Fix $ORIGIN in zonefiles
also check if a dname isn't fq, and add $ORIGIN is not.
This commit is contained in:
parent
822c8c66e2
commit
0322d3a9da
32
defaults.go
32
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.
|
||||
|
|
21
dns.go
21
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] == '.'
|
||||
}
|
||||
|
|
|
@ -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
104
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
|
||||
}
|
||||
|
|
77
zscan_rr.go
77
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
|
||||
|
|
Loading…
Reference in New Issue