Fix $ORIGIN in zonefiles

also check if a dname isn't fq, and add $ORIGIN is not.
This commit is contained in:
Miek Gieben 2012-01-08 14:06:58 +01:00
parent 822c8c66e2
commit 0322d3a9da
5 changed files with 145 additions and 92 deletions

View File

@ -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.

21
dns.go
View File

@ -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] == '.'
}

View File

@ -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

104
zscan.go
View File

@ -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
}

View File

@ -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