Qualify relative RDATA names in the zone-file parser
A domain name in the RDATA section written without a trailing dot is relative to the zone origin (RFC 1035 §5.1), so "www CNAME host" in example.com. means host.example.com. The qualifier ran on the record after dns.NewRR had already resolved every RDATA name against the root, which turns "host" into "host." and makes the two indistinguishable -- relative() saw the trailing dot and left it alone. Every relative CNAME, NS, MX, PTR, SRV, NAPTR, SOA and SVCB target therefore pointed at the DNS root, which showed up as a local zone answering a CNAME with "host." and the alias target holding no local answer. Qualification now happens in assemble on the token stream, before the record is built, using a table of the RDATA positions that hold a name per type; names that already end in a dot stay absolute, so the RPZ targets "." and "rpz-drop." and the null MX targets are unaffected. Fix three wrong expectations in the localzone tests while here. The loop fixture was built with dns.NewRR on "alias.example.com", which cannot parse because the RR library wants a TTL and class column, so the expected record now spells them out. The origin-derivation fixture used "a" as an owner name, and a leading token that names an RR type reads as an omitted owner, which has no previous owner to inherit on the first line of a file; the owner is now a word. The merge test counted the SOA, but it is zone metadata rather than a queryable record, so two files contribute three records. The holder race test also looked up a name from the base zone while the holder was serving a store compiled from other.test., which no answer could satisfy; it now accepts either store's data.
This commit is contained in:
1 parent
ad9989b28c
commit
a5e54c09df
4 files changed
+1043
-49
No files matched your search
@@ -118,11 +118,8 @@ func Parse(r io.Reader, opt Options) (*Zone, error) {
|
||||
if !hasTTL && z.DefaultTTL > 0 {
|
||||
rr.Header().Ttl = z.DefaultTTL
|
||||
}
|
||||
// RFC 1035 §5.1: a domain name in the RDATA section that is written
|
||||
// without a trailing dot is relative to the zone origin, not to the
|
||||
// root. Left to the library it becomes "host.", which points a CNAME
|
||||
// at the root instead of at the zone.
|
||||
qualifyRdata(rr, z.Origin)
|
||||
// assemble has already qualified the RDATA names that were written
|
||||
// relative to the origin, so the record holds no relative name.
|
||||
rec := Record{
|
||||
Name: strings.ToLower(ensureDot(rr.Header().Name)),
|
||||
TTL: rr.Header().Ttl,
|
||||
@@ -229,6 +226,13 @@ func (z *Zone) assemble(line, lastOwner string) (string, bool, error) {
|
||||
if len(rest) == 0 {
|
||||
return "", false, fmt.Errorf("record %q has no type", head)
|
||||
}
|
||||
// RFC 1035 §5.1: a domain name in the RDATA section that is written
|
||||
// without a trailing dot is relative to the zone origin, not to the
|
||||
// root. It has to be qualified here, while it is still a token: the RR
|
||||
// library resolves RDATA names against the root, so once dns.NewRR has
|
||||
// run, "host" and "host." are indistinguishable and the origin would be
|
||||
// lost.
|
||||
qualifyRdata(rest[1:], rest[0], z.Origin)
|
||||
out = append(out, rest...)
|
||||
return strings.Join(out, " "), hasTTL, nil
|
||||
}
|
||||
@@ -290,45 +294,35 @@ func cloneSOA(in *dns.SOA) *dns.SOA {
|
||||
return &out
|
||||
}
|
||||
|
||||
// qualifyRdata appends the origin to the domain names in a record's RDATA
|
||||
// that were written without a trailing dot. RFC 1035 §5.1 makes those names
|
||||
// relative to the zone origin, so "www CNAME host" in example.com. means
|
||||
// rdataNameIndex maps an RR type to the positions of the domain names in its
|
||||
// presentation-format RDATA, counted from the first token after the type. The
|
||||
// position of a name follows from the type's field layout, so a relative name
|
||||
// can be qualified while the line is still a token stream. A type that carries
|
||||
// no name in its RDATA is absent, and so is a type assemble does not
|
||||
// recognise, since such a record never reaches this point.
|
||||
var rdataNameIndex = map[string][]int{
|
||||
"CNAME": {0},
|
||||
"HTTPS": {1}, // SvcPriority, TargetName, SvcParams...
|
||||
"MX": {1}, // Preference, Exchange
|
||||
"NAPTR": {5}, // Order, Preference, Flags, Service, Regexp, Replacement
|
||||
"NS": {0},
|
||||
"PTR": {0},
|
||||
"SOA": {0, 1}, // MName, RName
|
||||
"SRV": {3}, // Priority, Weight, Port, Target
|
||||
"SVCB": {1},
|
||||
}
|
||||
|
||||
// qualifyRdata appends the origin to the domain names in a record's RDATA that
|
||||
// were written without a trailing dot: "www CNAME host" in example.com. means
|
||||
// host.example.com. and not host. A name that already ends in a dot is
|
||||
// absolute and left alone, which keeps the RPZ targets "." and "rpz-drop."
|
||||
// and the null MX/SRV targets intact.
|
||||
func qualifyRdata(rr dns.RR, origin string) {
|
||||
switch v := rr.(type) {
|
||||
case *dns.CNAME:
|
||||
v.Target = relative(v.Target, origin)
|
||||
case *dns.DNAME:
|
||||
v.Target = relative(v.Target, origin)
|
||||
case *dns.NS:
|
||||
v.Ns = relative(v.Ns, origin)
|
||||
case *dns.PTR:
|
||||
v.Ptr = relative(v.Ptr, origin)
|
||||
case *dns.MX:
|
||||
v.Mx = relative(v.Mx, origin)
|
||||
case *dns.SRV:
|
||||
v.Target = relative(v.Target, origin)
|
||||
case *dns.SOA:
|
||||
v.Ns = relative(v.Ns, origin)
|
||||
v.Mbox = relative(v.Mbox, origin)
|
||||
case *dns.NAPTR:
|
||||
v.Replacement = relative(v.Replacement, origin)
|
||||
case *dns.RP:
|
||||
v.Mbox = relative(v.Mbox, origin)
|
||||
v.Txt = relative(v.Txt, origin)
|
||||
case *dns.MINFO:
|
||||
v.Rmail = relative(v.Rmail, origin)
|
||||
v.Email = relative(v.Email, origin)
|
||||
case *dns.MB:
|
||||
v.Mb = relative(v.Mb, origin)
|
||||
case *dns.MG:
|
||||
v.Mg = relative(v.Mg, origin)
|
||||
case *dns.MR:
|
||||
v.Mr = relative(v.Mr, origin)
|
||||
case *dns.KX:
|
||||
v.Exchanger = relative(v.Exchanger, origin)
|
||||
// and the null MX/SRV targets intact. rdata is the token slice after the type
|
||||
// and is qualified in place.
|
||||
func qualifyRdata(rdata []string, rrtype, origin string) {
|
||||
for _, i := range rdataNameIndex[strings.ToUpper(rrtype)] {
|
||||
if i < len(rdata) {
|
||||
rdata[i] = relative(rdata[i], origin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user