diff --git a/.travis.yml b/.travis.yml index 4139754..2fd7aed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,3 @@ language: go go: - 1.3 - tip -# COVERALLS_TOKEN -secure: "K748GqgZz2I4yHcS49Zi78MWx/xWR79zl1YIGoFxHU4TS6JtGJF9gjqDdSv8DBvBEZRXFMq3n5M+/CtYkIf2jQeQawT6I+PKxHJvpFlpqseWxN/aJRHfVsd0NxbWq6Icx7qzUOvgwKDxMSgXqdndh3reHkyTgkou5XIDlU2zf7g=" diff --git a/backend/backend.go b/backend/backend.go index 677423a..84ad9c1 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -118,7 +118,7 @@ func (b *Backend) getNamecoinEntryLL(name string) (*domain, error) { log.Info("namecoin query (", name, ") succeeded: ", v) - d, err := jsonToDomain(v) + d, err := b.jsonToDomain(v) if err != nil { log.Infoe(err, "cannot convert JSON to domain") return nil, err @@ -127,10 +127,10 @@ func (b *Backend) getNamecoinEntryLL(name string) (*domain, error) { return d, nil } -func jsonToDomain(jsonValue string) (*domain, error) { +func (b *Backend) jsonToDomain(jsonValue string) (*domain, error) { d := &domain{} - v, err := ncdomain.ParseValue(jsonValue, nil) + v, err := ncdomain.ParseValue(jsonValue, b.resolveExtraName) if err != nil { return nil, err } @@ -140,6 +140,16 @@ func jsonToDomain(jsonValue string) (*domain, error) { return d, nil } +func (b *Backend) resolveExtraName(name string) (jsonValue string, err error) { + v, err := b.nc.Query(name) + if err != nil { + log.Infoe(err, "namecoin subquery failed: ", err) + return "", err + } + + return v, nil +} + type btx struct { b *Backend qname string diff --git a/ncdomain/convert.go b/ncdomain/convert.go index 7817f89..6d2600a 100644 --- a/ncdomain/convert.go +++ b/ncdomain/convert.go @@ -30,19 +30,30 @@ func (v *Value) RRs(out []dns.RR, suffix string) ([]dns.RR, error) { il := len(out) suffix = dns.Fqdn(suffix) - out, _ = v.appendIPs(out, suffix) - out, _ = v.appendIP6s(out, suffix) out, _ = v.appendNSs(out, suffix) - out, _ = v.appendTXTs(out, suffix) + if len(v.NS) == 0 { + out, _ = v.appendTranslate(out, suffix) + if v.Translate == "" { + out, _ = v.appendAlias(out, suffix) + if v.Alias == "" { + out, _ = v.appendIPs(out, suffix) + out, _ = v.appendIP6s(out, suffix) + out, _ = v.appendTXTs(out, suffix) + out, _ = v.appendServices(out, suffix) + out, _ = v.appendMXs(out, suffix) + } + } + } out, _ = v.appendDSs(out, suffix) - out, _ = v.appendServices(out, suffix) - out, _ = v.appendMXs(out, suffix) - out, _ = v.appendAlias(out, suffix) - out, _ = v.appendTranslate(out, suffix) xout := out[il:] for i := range xout { - xout[i].Header().Name = suffix + h := xout[i].Header() + if h.Rrtype == dns.TypeSRV { + h.Name += "." + suffix + } else { + h.Name = suffix + } } return out, nil @@ -175,10 +186,14 @@ func (v *Value) RRsRecursive(out []dns.RR, suffix string) ([]dns.RR, error) { } for mk, mv := range v.Map { - out, err = mv.RRsRecursive(out, mk+"."+suffix) - if err != nil { - return nil, err + if !validateLabel(mk) && mk != "" && mk != "*" { + continue } + + out, err = mv.RRsRecursive(out, mk+"."+suffix) + //if err != nil { + // return nil, err + //} } return out, nil @@ -188,6 +203,7 @@ type rawValue struct { IP interface{} `json:"ip"` IP6 interface{} `json:"ip6"` NS interface{} `json:"ns"` + nsSet map[string]struct{} DNS interface{} `json:"dns"` // actually an alias for NS Alias interface{} `json:"alias"` Translate interface{} `json:"translate"` @@ -326,30 +342,42 @@ func (rv *rawValue) parseNS(v *Value) error { v.NS = nil + if rv.nsSet == nil { + rv.nsSet = map[string]struct{}{} + } + switch rv.NS.(type) { case []interface{}: for _, si := range rv.NS.([]interface{}) { s, ok := si.(string) - if !ok || !validateHostName(s) { + if !ok { continue } - - v.NS = append(v.NS, s) + rv.addNS(v, s) } return nil case string: s := rv.NS.(string) - if !validateHostName(s) { - return fmt.Errorf("malformed NS hostname") - } - - v.NS = append(v.NS, s) + rv.addNS(v, s) return nil default: return fmt.Errorf("unknown NS field format") } } +func (rv *rawValue) addNS(v *Value, s string) error { + if !validateHostName(s) { + return fmt.Errorf("malformed NS hostname") + } + + if _, ok := rv.nsSet[s]; !ok { + v.NS = append(v.NS, s) + rv.nsSet[s] = struct{}{} + } + + return nil +} + func (rv *rawValue) parseAlias(v *Value) error { if rv.Alias == nil { return nil @@ -417,7 +445,7 @@ func (rv *rawValue) parseDelegate(v *Value, resolve ResolveFunc, depth, mergeDep } func (rv *rawValue) parseHostmaster(v *Value) error { - if rv.Translate == nil { + if rv.Hostmaster == nil { return nil } @@ -498,11 +526,13 @@ func (rv *rawValue) parseTXT(v *Value) error { // [["...", "..."], ["...", "..."]] a := []string{} for _, x := range sa { - if xs, ok := x.(string); ok { + if xs, ok := x.(string); ok && len(xs) <= 255 { a = append(a, xs) } } - v.TXT = append(v.TXT, a) + if len(a) > 0 { + v.TXT = append(v.TXT, a) + } } else if s, ok := vv.(string); ok { v.TXT = append(v.TXT, segmentizeTXT(s)) } else { @@ -518,6 +548,24 @@ func (rv *rawValue) parseTXT(v *Value) error { } } + // Make sure the content of each TXT record does not exceed 65535 bytes. + for i := range v.TXT { + for { + L := 0 + + for j := range v.TXT[i] { + L += len(v.TXT[i][j]) + 1 + } + + if L <= 65535 { + break + } + + // Pop segments until under the limit. + v.TXT[i] = v.TXT[i][0 : len(v.TXT[i])-1] + } + } + return nil } @@ -725,8 +773,12 @@ func (v *Value) moveEmptyMapItems() error { // Validation functions -var re_hostName = regexp.MustCompilePOSIX(`^([a-z0-9_-]+\.)*[a-z0-9_-]+\.?$`) +// This is used to validate NS records, targets in SRV records, etc. In these cases +// an IP address is not allowed. Therefore this regex must exclude all-numeric domain names. +// This is done by requiring the final part to start with an alphabetic character. +var re_hostName = regexp.MustCompilePOSIX(`^([a-z0-9_][a-z0-9_-]{0,62}\.)*[a-z_][a-z0-9_-]{0,62}\.?$`) var re_serviceName = regexp.MustCompilePOSIX(`^[a-z_][a-z0-9_-]*$`) +var re_label = regexp.MustCompilePOSIX(`^[a-z0-9_][a-z0-9_-]*$`) func validateHostName(name string) bool { name = dns.Fqdn(name) @@ -737,6 +789,10 @@ func validateServiceName(name string) bool { return len(name) < 63 && re_serviceName.MatchString(name) } +func validateLabel(name string) bool { + return len(name) <= 63 && re_label.MatchString(name) +} + func validateEmail(email string) bool { addr, err := mail.ParseAddress(email) if addr == nil || err != nil { diff --git a/ncdomain/convert_test.go b/ncdomain/convert_test.go index 55265ff..973102d 100644 --- a/ncdomain/convert_test.go +++ b/ncdomain/convert_test.go @@ -5,51 +5,53 @@ import "github.com/miekg/dns" import "testing" import "net" import "fmt" +import "strings" type item struct { jsonValue string value *ncdomain.Value expectedError error merges map[string]string + rrstr string } var suite = []item{ - item{`{}`, &ncdomain.Value{}, nil, nil}, - item{`{"ip":"1.2.3.4"}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}, nil, nil}, - item{`{"ip":["1.2.3.4"]}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}, nil, nil}, - item{`{"ip":["1.2.3.4","200.200.200.200"]}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4"), net.ParseIP("200.200.200.200")}}, nil, nil}, - item{`{"ip6":"dead:b33f::deca:fbad"}`, &ncdomain.Value{IP6: []net.IP{net.ParseIP("dead:b33f::deca:fbad")}}, nil, nil}, - item{`{"ip6":["dead:b33f::deca:fbad"]}`, &ncdomain.Value{IP6: []net.IP{net.ParseIP("dead:b33f::deca:fbad")}}, nil, nil}, - item{`{"ip6":["dead:b33f::deca:fbad","1234:abcd:5678:bcde:9876:fedc:5432:ba98"]}`, &ncdomain.Value{IP6: []net.IP{net.ParseIP("dead:b33f::deca:fbad"), net.ParseIP("1234:abcd:5678:bcde:9876:fedc:5432:ba98")}}, nil, nil}, - item{`{"ns":"alpha.beta.gamma.delta"}`, &ncdomain.Value{NS: []string{"alpha.beta.gamma.delta"}}, nil, nil}, - item{`{"ns":["alpha.beta.gamma.delta"]}`, &ncdomain.Value{NS: []string{"alpha.beta.gamma.delta"}}, nil, nil}, - item{`{"ns":["alpha.beta.gamma.delta","delta.gamma.beta.alpha"]}`, &ncdomain.Value{NS: []string{"alpha.beta.gamma.delta", "delta.gamma.beta.alpha"}}, nil, nil}, - item{`{"mx":[[10,"alpha.beta.gamma.delta"]]}`, &ncdomain.Value{MX: []*dns.MX{&dns.MX{Hdr: dns.RR_Header{Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 600}, Preference: 10, Mx: "alpha.beta.gamma.delta"}}}, nil, nil}, - item{`{"mx":[[10,"alpha.beta.gamma.delta"],[20,"epsilon.example"]]}`, &ncdomain.Value{MX: []*dns.MX{&dns.MX{Hdr: dns.RR_Header{Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 600}, Preference: 10, Mx: "alpha.beta.gamma.delta"}, &dns.MX{Hdr: dns.RR_Header{Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 600}, Preference: 20, Mx: "epsilon.example"}}}, nil, nil}, - item{`{"alias":"alpha.beta.gamma.delta"}`, &ncdomain.Value{Alias: "alpha.beta.gamma.delta"}, nil, nil}, - item{`{"translate":"alpha.beta.gamma.delta"}`, &ncdomain.Value{Translate: "alpha.beta.gamma.delta"}, nil, nil}, - item{`{"txt":"text record"}`, &ncdomain.Value{TXT: [][]string{[]string{"text record"}}}, nil, nil}, - item{`{"txt":"[text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record]"}`, &ncdomain.Value{TXT: [][]string{[]string{"[text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record]", "[text ... record]"}}}, nil, nil}, - item{`{"txt":["text record"]}`, &ncdomain.Value{TXT: [][]string{[]string{"text record"}}}, nil, nil}, - item{`{"txt":["text record","text record 2"]}`, &ncdomain.Value{TXT: [][]string{[]string{"text record"}, []string{"text record 2"}}}, nil, nil}, - item{`{"txt":[["text", "record"]]}`, &ncdomain.Value{TXT: [][]string{[]string{"text", "record"}}}, nil, nil}, - item{`{"txt":[["text", "record"],["text", "record", "2"]]}`, &ncdomain.Value{TXT: [][]string{[]string{"text", "record"}, []string{"text", "record", "2"}}}, nil, nil}, - item{`{"service":[ ["http","tcp",1,2,80,"alpha.beta.gamma.delta"] ]}`, &ncdomain.Value{Service: []*dns.SRV{&dns.SRV{Hdr: dns.RR_Header{Name: "_http._tcp", Ttl: 600, Rrtype: dns.TypeSRV, Class: dns.ClassINET}, Priority: 1, Weight: 2, Port: 80, Target: "alpha.beta.gamma.delta"}}}, nil, nil}, - item{`{"service":[ ["http","tcp",1,2,80,"alpha.beta.gamma.delta"], ["https","tcp",1,2,443,"alpha.beta.gamma.delta"] ]}`, &ncdomain.Value{Service: []*dns.SRV{&dns.SRV{Hdr: dns.RR_Header{Name: "_http._tcp", Ttl: 600, Rrtype: dns.TypeSRV, Class: dns.ClassINET}, Priority: 1, Weight: 2, Port: 80, Target: "alpha.beta.gamma.delta"}, &dns.SRV{Hdr: dns.RR_Header{Name: "_https._tcp", Ttl: 600, Rrtype: dns.TypeSRV, Class: dns.ClassINET}, Priority: 1, Weight: 2, Port: 443, Target: "alpha.beta.gamma.delta"}}}, nil, nil}, - item{`{"map":{ "": { } }}`, &ncdomain.Value{}, nil, nil}, - item{`{"map":{ "": { "ip": "1.2.3.4" } }}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}, nil, nil}, - item{`{"map":{ "www": { "ip": "1.2.3.4" } }}`, &ncdomain.Value{Map: map[string]*ncdomain.Value{"www": &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}}}, nil, nil}, - item{`{"map":{ "": "1.2.3.4" }}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}, nil, nil}, - item{`{"map":{ "www": "1.2.3.4" }}`, &ncdomain.Value{Map: map[string]*ncdomain.Value{"www": &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}}}, nil, nil}, - item{`{"ds":[[12345,8,2,"4tPJFvbe6scylOgmj7WIUESoM/xUWViPSpGEz8QaV2Y="]]}`, &ncdomain.Value{DS: []*dns.DS{&dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 12345, Algorithm: 8, DigestType: 2, Digest: "e2d3c916f6deeac73294e8268fb5885044a833fc5459588f4a9184cfc41a5766"}}}, nil, nil}, - item{`{"ds":[[54321,8,1,"5sFxbPtr3IToTOGrVRDaxpFztbI="],[12345,8,2,"4tPJFvbe6scylOgmj7WIUESoM/xUWViPSpGEz8QaV2Y="]]}`, &ncdomain.Value{DS: []*dns.DS{&dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 54321, Algorithm: 8, DigestType: 1, Digest: "e6c1716cfb6bdc84e84ce1ab5510dac69173b5b2"}, &dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 12345, Algorithm: 8, DigestType: 2, Digest: "e2d3c916f6deeac73294e8268fb5885044a833fc5459588f4a9184cfc41a5766"}}}, nil, nil}, - item{`{"email":"hostmaster@example.com"}`, &ncdomain.Value{Hostmaster: "hostmaster@example.com"}, nil, nil}, - item{`{"ip":["1.2.3.4"],"import":"d/example"}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}, IP6: []net.IP{net.ParseIP("::beef")}}, nil, map[string]string{"d/example": `{"ip6":["::beef"]}`}}, - item{`{"ip":["1.2.3.4"],"import":"d/example"}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}, IP6: []net.IP{net.ParseIP("::beef")}}, nil, map[string]string{"d/example": `{"ip":["2.3.4.5"],"ip6":["::beef"]}`}}, - item{`{"ns":["alpha.beta"],"import":"d/example"}`, &ncdomain.Value{NS: []string{"alpha.beta"}, IP6: []net.IP{net.ParseIP("::beef")}}, nil, map[string]string{"d/example": `{"ns":["gamma.delta"],"ip6":["::beef"]}`}}, - item{`{"ds":[[12345,8,2,"4tPJFvbe6scylOgmj7WIUESoM/xUWViPSpGEz8QaV2Y="]],"import":"d/example"}`, &ncdomain.Value{DS: []*dns.DS{&dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 12345, Algorithm: 8, DigestType: 2, Digest: "e2d3c916f6deeac73294e8268fb5885044a833fc5459588f4a9184cfc41a5766"}}}, nil, map[string]string{"d/example": `{"ds":[ [54321,8,1,"5sFxbPtr3IToTOGrVRDaxpFztbI="] ]}`}}, - item{`{"import":"d/example"}`, &ncdomain.Value{DS: []*dns.DS{&dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 54321, Algorithm: 8, DigestType: 1, Digest: "e6c1716cfb6bdc84e84ce1ab5510dac69173b5b2"}}}, nil, map[string]string{"d/example": `{"ds":[ [54321,8,1,"5sFxbPtr3IToTOGrVRDaxpFztbI="] ]}`}}, - item{`{"ip":["1.2.3.4"],"delegate":"d/example"}`, &ncdomain.Value{IP6: []net.IP{net.ParseIP("::beef")}}, nil, map[string]string{"d/example": `{"ip6":["::beef"]}`}}, + item{`{}`, &ncdomain.Value{}, nil, nil, ""}, + item{`{"ip":"1.2.3.4"}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}, nil, nil, "bit. 600 IN A 1.2.3.4"}, + item{`{"ip":["1.2.3.4"]}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}, nil, nil, "bit. 600 IN A 1.2.3.4"}, + item{`{"ip":["1.2.3.4","200.200.200.200"]}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4"), net.ParseIP("200.200.200.200")}}, nil, nil, "bit. 600 IN A 1.2.3.4\nbit. 600 IN A 200.200.200.200"}, + item{`{"ip6":"dead:b33f::deca:fbad"}`, &ncdomain.Value{IP6: []net.IP{net.ParseIP("dead:b33f::deca:fbad")}}, nil, nil, "bit. 600 IN AAAA dead:b33f::deca:fbad"}, + item{`{"ip6":["dead:b33f::deca:fbad"]}`, &ncdomain.Value{IP6: []net.IP{net.ParseIP("dead:b33f::deca:fbad")}}, nil, nil, "bit. 600 IN AAAA dead:b33f::deca:fbad"}, + item{`{"ip6":["dead:b33f::deca:fbad","1234:abcd:5678:bcde:9876:fedc:5432:ba98"]}`, &ncdomain.Value{IP6: []net.IP{net.ParseIP("dead:b33f::deca:fbad"), net.ParseIP("1234:abcd:5678:bcde:9876:fedc:5432:ba98")}}, nil, nil, "bit. 600 IN AAAA dead:b33f::deca:fbad\nbit. 600 IN AAAA 1234:abcd:5678:bcde:9876:fedc:5432:ba98"}, + item{`{"ns":"alpha.beta.gamma.delta"}`, &ncdomain.Value{NS: []string{"alpha.beta.gamma.delta"}}, nil, nil, "bit. 600 IN NS alpha.beta.gamma.delta."}, + item{`{"ns":["alpha.beta.gamma.delta"]}`, &ncdomain.Value{NS: []string{"alpha.beta.gamma.delta"}}, nil, nil, "bit. 600 IN NS alpha.beta.gamma.delta."}, + item{`{"ns":["alpha.beta.gamma.delta","delta.gamma.beta.alpha"]}`, &ncdomain.Value{NS: []string{"alpha.beta.gamma.delta", "delta.gamma.beta.alpha"}}, nil, nil, "bit. 600 IN NS alpha.beta.gamma.delta.\nbit. 600 IN NS delta.gamma.beta.alpha."}, + item{`{"mx":[[10,"alpha.beta.gamma.delta"]]}`, &ncdomain.Value{MX: []*dns.MX{&dns.MX{Hdr: dns.RR_Header{Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 600}, Preference: 10, Mx: "alpha.beta.gamma.delta"}}}, nil, nil, "bit. 600 IN MX 10 alpha.beta.gamma.delta."}, + item{`{"mx":[[10,"alpha.beta.gamma.delta"],[20,"epsilon.example"]]}`, &ncdomain.Value{MX: []*dns.MX{&dns.MX{Hdr: dns.RR_Header{Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 600}, Preference: 10, Mx: "alpha.beta.gamma.delta"}, &dns.MX{Hdr: dns.RR_Header{Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 600}, Preference: 20, Mx: "epsilon.example"}}}, nil, nil, "bit. 600 IN MX 10 alpha.beta.gamma.delta.\nbit. 600 IN MX 20 epsilon.example."}, + item{`{"alias":"alpha.beta.gamma.delta"}`, &ncdomain.Value{Alias: "alpha.beta.gamma.delta"}, nil, nil, "bit. 600 IN CNAME alpha.beta.gamma.delta."}, + item{`{"translate":"alpha.beta.gamma.delta"}`, &ncdomain.Value{Translate: "alpha.beta.gamma.delta"}, nil, nil, "bit. 600 IN DNAME alpha.beta.gamma.delta."}, + item{`{"txt":"text record"}`, &ncdomain.Value{TXT: [][]string{[]string{"text record"}}}, nil, nil, "bit. 600 IN TXT \"text record\""}, + item{`{"txt":"[text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record]"}`, &ncdomain.Value{TXT: [][]string{[]string{"[text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record][text ... record]", "[text ... record]"}}}, nil, nil, ""}, + item{`{"txt":["text record"]}`, &ncdomain.Value{TXT: [][]string{[]string{"text record"}}}, nil, nil, "bit. 600 IN TXT \"text record\""}, + item{`{"txt":["text record","text record 2"]}`, &ncdomain.Value{TXT: [][]string{[]string{"text record"}, []string{"text record 2"}}}, nil, nil, "bit. 600 IN TXT \"text record\"\nbit. 600 IN TXT \"text record 2\""}, + item{`{"txt":[["text", "record"]]}`, &ncdomain.Value{TXT: [][]string{[]string{"text", "record"}}}, nil, nil, "bit. 600 IN TXT \"text\" \"record\""}, + item{`{"txt":[["text", "record"],["text", "record", "2"]]}`, &ncdomain.Value{TXT: [][]string{[]string{"text", "record"}, []string{"text", "record", "2"}}}, nil, nil, "bit. 600 IN TXT \"text\" \"record\"\nbit. 600 IN TXT \"text\" \"record\" \"2\""}, + item{`{"service":[ ["http","tcp",1,2,80,"alpha.beta.gamma.delta"] ]}`, &ncdomain.Value{Service: []*dns.SRV{&dns.SRV{Hdr: dns.RR_Header{Name: "_http._tcp", Ttl: 600, Rrtype: dns.TypeSRV, Class: dns.ClassINET}, Priority: 1, Weight: 2, Port: 80, Target: "alpha.beta.gamma.delta"}}}, nil, nil, "_http._tcp.bit. IN SRV 1 2 80 alpha.beta.gamma.delta."}, + item{`{"service":[ ["http","tcp",1,2,80,"alpha.beta.gamma.delta"], ["https","tcp",1,2,443,"alpha.beta.gamma.delta"] ]}`, &ncdomain.Value{Service: []*dns.SRV{&dns.SRV{Hdr: dns.RR_Header{Name: "_http._tcp", Ttl: 600, Rrtype: dns.TypeSRV, Class: dns.ClassINET}, Priority: 1, Weight: 2, Port: 80, Target: "alpha.beta.gamma.delta"}, &dns.SRV{Hdr: dns.RR_Header{Name: "_https._tcp", Ttl: 600, Rrtype: dns.TypeSRV, Class: dns.ClassINET}, Priority: 1, Weight: 2, Port: 443, Target: "alpha.beta.gamma.delta"}}}, nil, nil, "_http._tcp.bit. 600 IN SRV 1 2 80 alpha.beta.gamma.delta.\n_https._tcp.bit. 600 IN SRV 1 2 443 alpha.beta.gamma.delta."}, + item{`{"map":{ "": { } }}`, &ncdomain.Value{}, nil, nil, ""}, + item{`{"map":{ "": { "ip": "1.2.3.4" } }}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}, nil, nil, "bit. 600 IN A 1.2.3.4"}, + item{`{"map":{ "www": { "ip": "1.2.3.4" } }}`, &ncdomain.Value{Map: map[string]*ncdomain.Value{"www": &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}}}, nil, nil, "www.bit. 600 IN A 1.2.3.4"}, + item{`{"map":{ "": "1.2.3.4" }}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}, nil, nil, "bit. 600 IN A 1.2.3.4"}, + item{`{"map":{ "www": "1.2.3.4" }}`, &ncdomain.Value{Map: map[string]*ncdomain.Value{"www": &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}}}}, nil, nil, "www.bit. 600 IN A 1.2.3.4"}, + item{`{"ds":[[12345,8,2,"4tPJFvbe6scylOgmj7WIUESoM/xUWViPSpGEz8QaV2Y="]]}`, &ncdomain.Value{DS: []*dns.DS{&dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 12345, Algorithm: 8, DigestType: 2, Digest: "e2d3c916f6deeac73294e8268fb5885044a833fc5459588f4a9184cfc41a5766"}}}, nil, nil, "bit. 600 IN DS 12345 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CFC41A5766"}, + item{`{"ds":[[54321,8,1,"5sFxbPtr3IToTOGrVRDaxpFztbI="],[12345,8,2,"4tPJFvbe6scylOgmj7WIUESoM/xUWViPSpGEz8QaV2Y="]]}`, &ncdomain.Value{DS: []*dns.DS{&dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 54321, Algorithm: 8, DigestType: 1, Digest: "e6c1716cfb6bdc84e84ce1ab5510dac69173b5b2"}, &dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 12345, Algorithm: 8, DigestType: 2, Digest: "e2d3c916f6deeac73294e8268fb5885044a833fc5459588f4a9184cfc41a5766"}}}, nil, nil, "bit. 600 IN DS 54321 8 1 E6C1716CFB6BDC84E84CE1AB5510DAC69173B5B2\nbit. 600 IN DS 12345 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CFC41A5766"}, + item{`{"email":"hostmaster@example.com"}`, &ncdomain.Value{Hostmaster: "hostmaster@example.com"}, nil, nil, ""}, + item{`{"ip":["1.2.3.4"],"import":"d/example"}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}, IP6: []net.IP{net.ParseIP("::beef")}}, nil, map[string]string{"d/example": `{"ip6":["::beef"]}`}, "bit. 600 IN A 1.2.3.4\nbit. 600 IN AAAA ::beef"}, + item{`{"ip":["1.2.3.4"],"import":"d/example"}`, &ncdomain.Value{IP: []net.IP{net.ParseIP("1.2.3.4")}, IP6: []net.IP{net.ParseIP("::beef")}}, nil, map[string]string{"d/example": `{"ip":["2.3.4.5"],"ip6":["::beef"]}`}, "bit. 600 IN A 1.2.3.4\nbit. 600 IN AAAA ::beef"}, + item{`{"ns":["alpha.beta"],"import":"d/example"}`, &ncdomain.Value{NS: []string{"alpha.beta"}, IP6: []net.IP{net.ParseIP("::beef")}}, nil, map[string]string{"d/example": `{"ns":["gamma.delta"],"ip6":["::beef"]}`}, "bit. 600 IN NS alpha.beta."}, + item{`{"ds":[[12345,8,2,"4tPJFvbe6scylOgmj7WIUESoM/xUWViPSpGEz8QaV2Y="]],"import":"d/example"}`, &ncdomain.Value{DS: []*dns.DS{&dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 12345, Algorithm: 8, DigestType: 2, Digest: "e2d3c916f6deeac73294e8268fb5885044a833fc5459588f4a9184cfc41a5766"}}}, nil, map[string]string{"d/example": `{"ds":[ [54321,8,1,"5sFxbPtr3IToTOGrVRDaxpFztbI="] ]}`}, "bit. 600 IN DS 12345 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CFC41A5766"}, + item{`{"import":"d/example"}`, &ncdomain.Value{DS: []*dns.DS{&dns.DS{Hdr: dns.RR_Header{Rrtype: dns.TypeDS, Class: dns.ClassINET, Ttl: 600}, KeyTag: 54321, Algorithm: 8, DigestType: 1, Digest: "e6c1716cfb6bdc84e84ce1ab5510dac69173b5b2"}}}, nil, map[string]string{"d/example": `{"ds":[ [54321,8,1,"5sFxbPtr3IToTOGrVRDaxpFztbI="] ]}`}, "bit. 600 IN DS 54321 8 1 E6C1716CFB6BDC84E84CE1AB5510DAC69173B5B2"}, + item{`{"ip":["1.2.3.4"],"delegate":"d/example"}`, &ncdomain.Value{IP6: []net.IP{net.ParseIP("::beef")}}, nil, map[string]string{"d/example": `{"ip6":["::beef"]}`}, "bit. 600 IN AAAA ::beef"}, } func TestConversion(t *testing.T) { @@ -72,6 +74,18 @@ func TestConversion(t *testing.T) { if !equals(v, item.value) { t.Errorf("Item %d value did not match expected value: got %+v but expected %+v", i, v, item.value) } + if item.rrstr != "" { + rrstr := "" + rrs, _ := v.RRsRecursive(nil, "bit.") + for _, rr := range rrs { + rrstr += strings.Replace(rr.String(), "\t", " ", -1) + rrstr += "\n" + } + rrstr = strings.Trim(rrstr, "\n") + if item.rrstr != rrstr { + t.Errorf("Item %d rrstr did not match the expected value: got %+v but expected %+v", i, rrstr, item.rrstr) + } + } } } @@ -83,10 +97,12 @@ func equals(v1 *ncdomain.Value, v2 *ncdomain.Value) bool { eqIPArray(v1.IP6, v2.IP6) && eqStringArray(v1.NS, v2.NS) && v1.Alias == v2.Alias && + v1.Translate == v2.Translate && eqDSArray(v1.DS, v2.DS) && eqStringArrayArray(v1.TXT, v2.TXT) && eqServiceArray(v1.Service, v2.Service) && - eqValueMap(v1, v2) + eqValueMap(v1, v2) && + v1.Hostmaster == v2.Hostmaster } func eqIPArray(a []net.IP, b []net.IP) bool { diff --git a/util/util.go b/util/util.go index ffe52a2..a7e30db 100644 --- a/util/util.go +++ b/util/util.go @@ -2,8 +2,12 @@ package util import "strings" -// Split a domain name a.b.c.d.e into parts a (the head) and b.c.d.e (the rest). +// Split a domain name a.b.c.d.e into parts e (the head) and a.b.c.d (the rest). func SplitDomainHead(name string) (head string, rest string, err error) { + if len(name) > 0 && name[len(name)-1] == '.' { + name = name[0 : len(name)-1] + } + parts := strings.Split(name, ".") head = parts[len(parts)-1]