msg.Service: add HostType() method (#627)

This method parses the Host field in the service. It returns 1 or 3
things 1) it is a host 2) an IPv4 address or an 3) IPv6 address.
This simplifies some code a bit and allows for 1 way of parsing the Host
field.

This *only* parse the Host field, Mail and/or Text values should be
checked separately.

We reuse the dns.TypeXXX values for this as to not invent anything new.
This commit is contained in:
Miek Gieben 2017-04-22 07:58:30 +01:00 committed by GitHub
parent 320cf97868
commit 4c9351b0a3
3 changed files with 115 additions and 41 deletions

View file

@ -21,9 +21,11 @@ func A(b ServiceBackend, zone string, state request.Request, previousRecords []d
} }
for _, serv := range services { for _, serv := range services {
ip := net.ParseIP(serv.Host)
switch { what, ip := serv.HostType()
case ip == nil:
switch what {
case dns.TypeANY:
if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) { if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
// x CNAME x is a direct loop, don't add those // x CNAME x is a direct loop, don't add those
continue continue
@ -67,9 +69,11 @@ func A(b ServiceBackend, zone string, state request.Request, previousRecords []d
records = append(records, newRecord) records = append(records, newRecord)
records = append(records, m1.Answer...) records = append(records, m1.Answer...)
continue continue
case ip.To4() != nil:
records = append(records, serv.NewA(state.QName(), ip.To4())) case dns.TypeA:
case ip.To4() == nil: records = append(records, serv.NewA(state.QName(), ip))
case dns.TypeAAAA:
// nodata? // nodata?
} }
} }
@ -84,9 +88,11 @@ func AAAA(b ServiceBackend, zone string, state request.Request, previousRecords
} }
for _, serv := range services { for _, serv := range services {
ip := net.ParseIP(serv.Host)
switch { what, ip := serv.HostType()
case ip == nil:
switch what {
case dns.TypeANY:
// Try to resolve as CNAME if it's not an IP, but only if we don't create loops. // Try to resolve as CNAME if it's not an IP, but only if we don't create loops.
if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) { if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
// x CNAME x is a direct loop, don't add those // x CNAME x is a direct loop, don't add those
@ -131,10 +137,12 @@ func AAAA(b ServiceBackend, zone string, state request.Request, previousRecords
records = append(records, m1.Answer...) records = append(records, m1.Answer...)
continue continue
// both here again // both here again
case ip.To4() != nil:
case dns.TypeA:
// nada? // nada?
case ip.To4() == nil:
records = append(records, serv.NewAAAA(state.QName(), ip.To16())) case dns.TypeAAAA:
records = append(records, serv.NewAAAA(state.QName(), ip))
} }
} }
return records, debug, nil return records, debug, nil
@ -170,9 +178,11 @@ func SRV(b ServiceBackend, zone string, state request.Request, opt Options) (rec
w1 *= float64(serv.Weight) w1 *= float64(serv.Weight)
} }
weight := uint16(math.Floor(w1)) weight := uint16(math.Floor(w1))
ip := net.ParseIP(serv.Host)
switch { what, ip := serv.HostType()
case ip == nil:
switch what {
case dns.TypeANY:
srv := serv.NewSRV(state.QName(), weight) srv := serv.NewSRV(state.QName(), weight)
records = append(records, srv) records = append(records, srv)
@ -214,18 +224,13 @@ func SRV(b ServiceBackend, zone string, state request.Request, opt Options) (rec
debug = append(debug, debugAddr...) debug = append(debug, debugAddr...)
} }
// IPv6 lookups here as well? AAAA(zone, state1, nil). // IPv6 lookups here as well? AAAA(zone, state1, nil).
case ip.To4() != nil:
case dns.TypeA, dns.TypeAAAA:
serv.Host = msg.Domain(serv.Key) serv.Host = msg.Domain(serv.Key)
srv := serv.NewSRV(state.QName(), weight) srv := serv.NewSRV(state.QName(), weight)
records = append(records, srv) records = append(records, srv)
extra = append(extra, serv.NewA(srv.Target, ip.To4())) extra = append(extra, newAddress(serv, srv.Target, ip, what))
case ip.To4() == nil:
serv.Host = msg.Domain(serv.Key)
srv := serv.NewSRV(state.QName(), weight)
records = append(records, srv)
extra = append(extra, serv.NewAAAA(srv.Target, ip.To16()))
} }
} }
return records, extra, debug, nil return records, extra, debug, nil
@ -243,9 +248,9 @@ func MX(b ServiceBackend, zone string, state request.Request, opt Options) (reco
if !serv.Mail { if !serv.Mail {
continue continue
} }
ip := net.ParseIP(serv.Host) what, ip := serv.HostType()
switch { switch what {
case ip == nil: case dns.TypeANY:
mx := serv.NewMX(state.QName()) mx := serv.NewMX(state.QName())
records = append(records, mx) records = append(records, mx)
if _, ok := lookup[mx.Mx]; ok { if _, ok := lookup[mx.Mx]; ok {
@ -284,14 +289,11 @@ func MX(b ServiceBackend, zone string, state request.Request, opt Options) (reco
debug = append(debug, debugAddr...) debug = append(debug, debugAddr...)
} }
// e.AAAA as well // e.AAAA as well
case ip.To4() != nil:
case dns.TypeA, dns.TypeAAAA:
serv.Host = msg.Domain(serv.Key) serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewMX(state.QName())) records = append(records, serv.NewMX(state.QName()))
extra = append(extra, serv.NewA(serv.Host, ip.To4())) extra = append(extra, newAddress(serv, serv.Host, ip, what))
case ip.To4() == nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewMX(state.QName()))
extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
} }
} }
return records, extra, debug, nil return records, extra, debug, nil
@ -360,18 +362,15 @@ func NS(b ServiceBackend, zone string, state request.Request, opt Options) (reco
state.Req.Question[0].Name = old state.Req.Question[0].Name = old
for _, serv := range services { for _, serv := range services {
ip := net.ParseIP(serv.Host) what, ip := serv.HostType()
switch { switch what {
case ip == nil: case dns.TypeANY:
return nil, nil, debug, fmt.Errorf("NS record must be an IP address: %s", serv.Host) return nil, nil, debug, fmt.Errorf("NS record must be an IP address: %s", serv.Host)
case ip.To4() != nil:
case dns.TypeA, dns.TypeAAAA:
serv.Host = msg.Domain(serv.Key) serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewNS(state.QName())) records = append(records, serv.NewNS(state.QName()))
extra = append(extra, serv.NewA(serv.Host, ip.To4())) extra = append(extra, newAddress(serv, serv.Host, ip, what))
case ip.To4() == nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewNS(state.QName()))
extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
} }
} }
return records, extra, debug, nil return records, extra, debug, nil
@ -445,6 +444,17 @@ func ErrorToTxt(err error) dns.RR {
return t return t
} }
func newAddress(s msg.Service, name string, ip net.IP, what uint16) dns.RR {
hdr := dns.RR_Header{Name: name, Rrtype: what, Class: dns.ClassINET, Ttl: s.TTL}
if what == dns.TypeA {
return &dns.A{Hdr: hdr, A: ip}
}
// Should always be dns.TypeAAAA
return &dns.AAAA{Hdr: hdr, AAAA: ip}
}
const ( const (
minTTL = 60 minTTL = 60
hostmaster = "hostmaster" hostmaster = "hostmaster"

View file

@ -0,0 +1,33 @@
package msg
import (
"net"
"github.com/miekg/dns"
)
// HostType returns the DNS type of what is encoded in the Service Host field. We're reusing
// dns.TypeXXX to not reinvent a new set of identifiers.
//
// dns.TypeA: the service's Host field contains an A record.
// dns.TypeAAAA: the service's Host field contains an AAAA record.
// dns.TypeANY: the service's Host field contains a name.
//
// Note that a service can double/triple as a TXT record or MX record.
func (s *Service) HostType() (what uint16, normalized net.IP) {
ip := net.ParseIP(s.Host)
switch {
case ip == nil:
return dns.TypeANY, nil
case ip.To4() != nil:
return dns.TypeA, ip.To4()
case ip.To4() == nil:
return dns.TypeAAAA, ip.To16()
}
// This should never be reached.
return dns.TypeNone, nil
}

View file

@ -0,0 +1,31 @@
package msg
import (
"testing"
"github.com/miekg/dns"
)
func TestType(t *testing.T) {
tests := []struct {
serv Service
expectedType uint16
}{
{Service{Host: "example.org"}, dns.TypeANY},
{Service{Host: "127.0.0.1"}, dns.TypeA},
{Service{Host: "2000::3"}, dns.TypeAAAA},
{Service{Host: "2000..3"}, dns.TypeANY},
{Service{Host: "127.0.0.257"}, dns.TypeANY},
{Service{Host: "127.0.0.252", Mail: true}, dns.TypeA},
{Service{Host: "127.0.0.252", Mail: true, Text: "a"}, dns.TypeA},
{Service{Host: "127.0.0.254", Mail: false, Text: "a"}, dns.TypeA},
}
for i, tc := range tests {
what, _ := tc.serv.HostType()
if what != tc.expectedType {
t.Errorf("Test %d: Expected what %v, but got %v", i, tc.expectedType, what)
}
}
}