From 0322d3a9daf5602fb14bc91aea7ef04e05521f02 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 8 Jan 2012 14:06:58 +0100 Subject: [PATCH] Fix $ORIGIN in zonefiles also check if a dname isn't fq, and add $ORIGIN is not. --- defaults.go | 32 +++++++++------- dns.go | 21 +++------- parse_test.go | 3 ++ zscan.go | 104 ++++++++++++++++++++++++++++++++------------------ zscan_rr.go | 77 ++++++++++++++++++++++++------------- 5 files changed, 145 insertions(+), 92 deletions(-) diff --git a/defaults.go b/defaults.go index 9be25f0f..9fdc8fad 100644 --- a/defaults.go +++ b/defaults.go @@ -196,53 +196,59 @@ func (dns *Msg) IsEdns0() (ok bool) { return } -// IsDomainName checks if s is a valid domainname. -func IsDomainName(s string) bool { // copied from net package. +// IsDomainName checks if s is a valid domainname, it returns +// true and a length, when a domain name is valid. When false +// is return the length isn't specified. +func IsDomainName(s string) (bool, int) { // copied from net package. // See RFC 1035, RFC 3696. if len(s) == 0 { - return false + return false, 0 } - if len(s) > 255 { - return false - } - if s[len(s)-1] != '.' { // simplify checking loop: make name end in dot - s += "." + if len(s) > 255 { // Not true...? + return false, 0 } + if !Fqdn(s) { // simplify checking loop: make name end in dot + s += "." + } last := byte('.') ok := false // ok once we've seen a letter partlen := 0 + n := 0 for i := 0; i < len(s); i++ { c := s[i] switch { default: - return false + return false, 0 case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c == '*': ok = true partlen++ + n++ case '0' <= c && c <= '9': // fine partlen++ + n++ case c == '-': // byte before dash cannot be dot if last == '.' { - return false + return false, 0 } partlen++ case c == '.': // byte before dot cannot be dot, dash if last == '.' || last == '-' { - return false + return false, 0 } if partlen > 63 || partlen == 0 { - return false + return false, 0 } partlen = 0 + n++ } last = c } - return ok + return ok, n } // Return the number of labels in a domain name. diff --git a/dns.go b/dns.go index 9aa26a07..245dbbba 100644 --- a/dns.go +++ b/dns.go @@ -230,19 +230,10 @@ func zoneMatch(pattern, zone string) (ok bool) { return } -// DnameLength returns the length of a packed dname. -func DomainNameLength(s string) int { // TODO better name - // Special case for '.' - if s == "." { - return 1 - } - - // Add trailing dot to canonicalize name. - if n := len(s); n == 0 || s[n-1] != '.' { - return n + 1 - } else { - return n + 1 - } - panic("not reached") - return 0 +// Fqdn checks if a domain name is fully qualified +func Fqdn(s string) bool { + if len(s) == 0 { + return false // ? + } + return s[len(s)-1] == '.' } diff --git a/parse_test.go b/parse_test.go index 3c45020f..5fa138ab 100644 --- a/parse_test.go +++ b/parse_test.go @@ -137,6 +137,8 @@ func TestParseBrace(t *testing.T) { `miek.nl. IN ( 3600 A 127.0.0.1)`: "miek.nl.\t3600\tIN\tA\t127.0.0.1", "(miek.nl.) (A) (127.0.0.1)": "miek.nl.\t3600\tIN\tA\t127.0.0.1", + "miek.nl A 127.0.0.1": "miek.nl.\t3600\tIN\tA\t127.0.0.1", + "miek.nl. NS ns.miek.nl": "miek.nl.\t3600\tIN\tNS\tns.miek.nl.", `(miek.nl.) ( (IN) (AAAA) @@ -145,6 +147,7 @@ func TestParseBrace(t *testing.T) { (IN) (AAAA) (::1))`: "miek.nl.\t3600\tIN\tAAAA\t::1", + "miek\\.nl. IN A 127.0.0.1": "miek\\.nl.\t3600\tIN\tA\t127.0.0.1", "miek.nl. IN A 127.0.0.1": "miek.nl.\t3600\tIN\tA\t127.0.0.1", `miek.nl. 86400 IN SOA elektron.atoom.net. miekg.atoom.net. ( 2009032802 ; serial diff --git a/zscan.go b/zscan.go index 5f712183..cab4ded5 100644 --- a/zscan.go +++ b/zscan.go @@ -46,6 +46,8 @@ const ( _EXPECT_RDATA // The first element of the rdata _EXPECT_DIRTTL_BL // Space after directive $TTL _EXPECT_DIRTTL // Directive $TTL + _EXPECT_DIRORIGIN_BL // Space after directive $ORIGIN + _EXPECT_DIRORIGIN // Directive $ORIGIN ) // ParseError contains the parse error and the location in the io.Reader @@ -84,9 +86,9 @@ type Token struct { func NewRR(s string) (RR, error) { t := make(chan Token) if s[len(s)-1] != '\n' { // We need a closing newline - t = ParseZone(strings.NewReader(s+"\n")) + t = ParseZone(strings.NewReader(s + "\n")) } else { - t= ParseZone(strings.NewReader(s)) + t = ParseZone(strings.NewReader(s)) } r := <-t if r.Error != nil { @@ -97,10 +99,10 @@ func NewRR(s string) (RR, error) { // ParseZone reads a RFC 1035 zone from r. It returns each parsed RR or on error // on the returned channel. The channel t is closed by ParseZone when the end of r is reached. -func ParseZone(r io.Reader) (chan Token) { - t := make(chan Token) - go parseZone(r, t) - return t +func ParseZone(r io.Reader) chan Token { + t := make(chan Token) + go parseZone(r, t) + return t } func parseZone(r io.Reader, t chan Token) { @@ -124,6 +126,7 @@ func parseZone(r io.Reader, t chan Token) { var h RR_Header var ok bool var defttl uint32 = DefaultTtl + var origin string = "." for l := range c { if _DEBUG { fmt.Printf("[%v]\n", l) @@ -144,9 +147,18 @@ func parseZone(r io.Reader, t chan Token) { st = _EXPECT_OWNER_DIR case _OWNER: h.Name = l.token + if ok, _ := IsDomainName(l.token); !ok { + t <- Token{Error: &ParseError{"bad owner name", l}} + return + } + if !Fqdn(h.Name) { + h.Name += origin + } st = _EXPECT_OWNER_BL case _DIRTTL: st = _EXPECT_DIRTTL_BL + case _DIRORIGIN: + st = _EXPECT_DIRORIGIN_BL default: t <- Token{Error: &ParseError{"Error at the start", l}} return @@ -167,8 +179,23 @@ func parseZone(r io.Reader, t chan Token) { } else { defttl = ttl } - st = _EXPECT_OWNER_DIR + case _EXPECT_DIRORIGIN_BL: + if l.value != _BLANK { + t <- Token{Error: &ParseError{"No blank after $-directive", l}} + return + } + st = _EXPECT_DIRORIGIN + case _EXPECT_DIRORIGIN: + if l.value != _STRING { + t <- Token{Error: &ParseError{"Expecting $ORIGIN value, not this...", l}} + return + } + if !Fqdn(l.token) { + origin = l.token + origin // Append old origin if the new one isn't a fqdn + } else { + origin = l.token + } case _EXPECT_OWNER_BL: if l.value != _BLANK { t <- Token{Error: &ParseError{"No blank after owner", l}} @@ -254,7 +281,7 @@ func parseZone(r io.Reader, t chan Token) { st = _EXPECT_RDATA case _EXPECT_RDATA: // I could save my token here...? l - r, e := setRR(h, c) + r, e := setRR(h, c, origin) if e != nil { // If e.lex is nil than we have encounter a unknown RR type // in that case we substitute our current lex token @@ -308,7 +335,7 @@ func zlexer(s scanner.Scanner, c chan lex) { l.line = s.Position.Line switch x := s.TokenText(); x { case " ", "\t": - escape = false + escape = false if commt { break } @@ -319,7 +346,7 @@ func zlexer(s scanner.Scanner, c chan lex) { // If we have a string and its the first, make it an owner l.value = _OWNER l.token = str - // escape $... start with a \ not a $, so this will work + // escape $... start with a \ not a $, so this will work if str == "$TTL" { l.value = _DIRTTL } @@ -351,11 +378,11 @@ func zlexer(s scanner.Scanner, c chan lex) { owner = false space = true case ";": - if escape { - escape = false - str += ";" - break - } + if escape { + escape = false + str += ";" + break + } if quote { // Inside quoted text we allow ; str += ";" @@ -363,8 +390,8 @@ func zlexer(s scanner.Scanner, c chan lex) { } commt = true case "\n": - // Hmmm, escape newline - escape = false + // Hmmm, escape newline + escape = false if commt { // Reset a comment commt = false @@ -413,42 +440,43 @@ func zlexer(s scanner.Scanner, c chan lex) { if commt { break } - if escape { - str += "\\" - escape = false - break - } + if escape { + str += "\\" + escape = false + break + } + str += "\\" escape = true case "\"": if commt { break } - if escape { - str += "\"" - escape = false - break - } + if escape { + str += "\"" + escape = false + break + } // str += "\"" don't add quoted quotes quote = !quote case "(": if commt { break } - if escape { - str += "(" - escape = false - break - } + if escape { + str += "(" + escape = false + break + } brace++ case ")": if commt { break } - if escape { - str += ")" - escape = false - break - } + if escape { + str += ")" + escape = false + break + } brace-- if brace < 0 { l.err = "Extra closing brace" @@ -459,7 +487,7 @@ func zlexer(s scanner.Scanner, c chan lex) { if commt { break } - escape = false + escape = false str += x space = false } diff --git a/zscan_rr.go b/zscan_rr.go index 1fbbc2f7..1bea9522 100644 --- a/zscan_rr.go +++ b/zscan_rr.go @@ -13,7 +13,7 @@ import ( // or immediately a _NEWLINE. If this is not the case we flag // an *ParseError: garbage after rdata. -func setRR(h RR_Header, c chan lex) (RR, *ParseError) { +func setRR(h RR_Header, c chan lex, o string) (RR, *ParseError) { var r RR e := new(ParseError) switch h.Rrtype { @@ -24,16 +24,16 @@ func setRR(h RR_Header, c chan lex) (RR, *ParseError) { r, e = setAAAA(h, c) goto Slurp case TypeNS: - r, e = setNS(h, c) + r, e = setNS(h, c, o) goto Slurp case TypeMX: - r, e = setMX(h, c) + r, e = setMX(h, c, o) goto Slurp case TypeCNAME: - r, e = setCNAME(h, c) + r, e = setCNAME(h, c, o) goto Slurp case TypeSOA: - r, e = setSOA(h, c) + r, e = setSOA(h, c, o) goto Slurp case TypeSSHFP: r, e = setSSHFP(h, c) @@ -44,11 +44,11 @@ func setRR(h RR_Header, c chan lex) (RR, *ParseError) { // newline. Thus there is no need to slurp the remainder, because there is none. return setDNSKEY(h, c) case TypeRRSIG: - return setRRSIG(h, c) + return setRRSIG(h, c, o) case TypeNSEC: - return setNSEC(h, c) + return setNSEC(h, c, o) case TypeNSEC3: - return setNSEC3(h, c) + return setNSEC3(h, c, o) case TypeDS: return setDS(h, c) case TypeTXT: @@ -117,19 +117,22 @@ func setAAAA(h RR_Header, c chan lex) (RR, *ParseError) { return rr, nil } -func setNS(h RR_Header, c chan lex) (RR, *ParseError) { +func setNS(h RR_Header, c chan lex, o string) (RR, *ParseError) { rr := new(RR_NS) rr.Hdr = h l := <-c rr.Ns = l.token - if !IsDomainName(l.token) { + if ok, _ := IsDomainName(l.token); !ok { return nil, &ParseError{"bad NS Ns", l} } + if !Fqdn(rr.Ns) { + rr.Ns += o + } return rr, nil } -func setMX(h RR_Header, c chan lex) (RR, *ParseError) { +func setMX(h RR_Header, c chan lex, o string) (RR, *ParseError) { rr := new(RR_MX) rr.Hdr = h @@ -142,40 +145,52 @@ func setMX(h RR_Header, c chan lex) (RR, *ParseError) { <-c // _BLANK l = <-c // _STRING rr.Mx = l.token - if !IsDomainName(l.token) { + if ok, _ := IsDomainName(l.token); !ok { return nil, &ParseError{"bad MX Mx", l} } + if !Fqdn(rr.Mx) { + rr.Mx += o + } return rr, nil } -func setCNAME(h RR_Header, c chan lex) (RR, *ParseError) { +func setCNAME(h RR_Header, c chan lex, o string) (RR, *ParseError) { rr := new(RR_CNAME) rr.Hdr = h l := <-c rr.Cname = l.token - if !IsDomainName(l.token) { + if ok, _ := IsDomainName(l.token); !ok { return nil, &ParseError{"bad CNAME", l} } + if !Fqdn(rr.Cname) { + rr.Cname += o + } return rr, nil } -func setSOA(h RR_Header, c chan lex) (RR, *ParseError) { +func setSOA(h RR_Header, c chan lex, o string) (RR, *ParseError) { rr := new(RR_SOA) rr.Hdr = h l := <-c rr.Ns = l.token <-c // _BLANK - if !IsDomainName(l.token) { + if ok, _ := IsDomainName(l.token); !ok { return nil, &ParseError{"bad SOA mname", l} } + if !Fqdn(rr.Ns) { + rr.Ns += o + } l = <-c rr.Mbox = l.token - if !IsDomainName(l.token) { + if ok, _ := IsDomainName(l.token); !ok { return nil, &ParseError{"bad SOA rname", l} } + if !Fqdn(rr.Mbox) { + rr.Mbox += o + } <-c // _BLANK var j int @@ -205,7 +220,7 @@ func setSOA(h RR_Header, c chan lex) (RR, *ParseError) { return rr, nil } -func setRRSIG(h RR_Header, c chan lex) (RR, *ParseError) { +func setRRSIG(h RR_Header, c chan lex, o string) (RR, *ParseError) { rr := new(RR_RRSIG) rr.Hdr = h l := <-c @@ -258,11 +273,13 @@ func setRRSIG(h RR_Header, c chan lex) (RR, *ParseError) { } <-c // _BLANK l = <-c - if !IsDomainName(l.token) { + rr.SignerName = l.token + if ok, _ := IsDomainName(l.token); !ok { return nil, &ParseError{"bad RRSIG signername", l} - } else { - rr.SignerName = l.token } + if !Fqdn(rr.SignerName) { + rr.SignerName += o + } // Get the remaining data until we see a NEWLINE l = <-c s := "" @@ -281,16 +298,18 @@ func setRRSIG(h RR_Header, c chan lex) (RR, *ParseError) { return rr, nil } -func setNSEC(h RR_Header, c chan lex) (RR, *ParseError) { +func setNSEC(h RR_Header, c chan lex, o string) (RR, *ParseError) { rr := new(RR_NSEC) rr.Hdr = h l := <-c - if !IsDomainName(l.token) { + rr.NextDomain = l.token + if ok, _ := IsDomainName(l.token); !ok { return nil, &ParseError{"bad NSEC nextdomain", l} - } else { - rr.NextDomain = l.token } + if !Fqdn(rr.NextDomain) { + rr.NextDomain += o + } rr.TypeBitMap = make([]uint16, 0) l = <-c @@ -312,7 +331,7 @@ func setNSEC(h RR_Header, c chan lex) (RR, *ParseError) { return rr, nil } -func setNSEC3(h RR_Header, c chan lex) (RR, *ParseError) { +func setNSEC3(h RR_Header, c chan lex, o string) (RR, *ParseError) { rr := new(RR_NSEC3) rr.Hdr = h @@ -345,6 +364,12 @@ func setNSEC3(h RR_Header, c chan lex) (RR, *ParseError) { l = <-c rr.HashLength = uint8(len(l.token)) rr.NextDomain = l.token + if ok, _ := IsDomainName(l.token); !ok { + return nil, &ParseError{"bad NSEC nextdomain", l} + } + if !Fqdn(rr.NextDomain) { + rr.NextDomain += o + } rr.TypeBitMap = make([]uint16, 0) l = <-c