Correctly parse omitted TTLs and relative domains (#513)
* Fix $TTL handling * Error when there is no TTL for an RR * Fix relative name handling * Error when a relative name is used without an origin (cf. https://tools.ietf.org/html/rfc1035#section-5.1 ) Fixes #484
This commit is contained in:
parent
689d334b01
commit
eccf8bbe83
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddDomain adds origin to s if s is not already a FQDN.
|
// AddOrigin adds origin to s if s is not already a FQDN.
|
||||||
// Note that the result may not be a FQDN. If origin does not end
|
// Note that the result may not be a FQDN. If origin does not end
|
||||||
// with a ".", the result won't either.
|
// with a ".", the result won't either.
|
||||||
// This implements the zonefile convention (specified in RFC 1035,
|
// This implements the zonefile convention (specified in RFC 1035,
|
||||||
|
|
4
doc.go
4
doc.go
|
@ -22,9 +22,9 @@ Or directly from a string:
|
||||||
|
|
||||||
mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
|
mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
|
||||||
|
|
||||||
Or when the default TTL (3600) and class (IN) suit you:
|
Or when the default origin (.) and TTL (3600) and class (IN) suit you:
|
||||||
|
|
||||||
mx, err := dns.NewRR("miek.nl. MX 10 mx.miek.nl.")
|
mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl")
|
||||||
|
|
||||||
Or even:
|
Or even:
|
||||||
|
|
||||||
|
|
188
parse_test.go
188
parse_test.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -571,81 +572,88 @@ test IN CNAME test.a.example.com.
|
||||||
t.Logf("%d RRs parsed in %.2f s (%.2f RR/s)", i, float32(delta)/1e9, float32(i)/(float32(delta)/1e9))
|
t.Logf("%d RRs parsed in %.2f s (%.2f RR/s)", i, float32(delta)/1e9, float32(i)/(float32(delta)/1e9))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleParseZone() {
|
func TestOmittedTTL(t *testing.T) {
|
||||||
zone := `$ORIGIN .
|
zone := `
|
||||||
$TTL 3600 ; 1 hour
|
$ORIGIN example.com.
|
||||||
name IN SOA a6.nstld.com. hostmaster.nic.name. (
|
example.com. 42 IN SOA ns1.example.com. hostmaster.example.com. 1 86400 60 86400 3600 ; TTL=42 SOA
|
||||||
203362132 ; serial
|
example.com. NS 2 ; TTL=42 absolute owner name
|
||||||
300 ; refresh (5 minutes)
|
@ MD 3 ; TTL=42 current-origin owner name
|
||||||
300 ; retry (5 minutes)
|
MF 4 ; TTL=42 leading-space implied owner name
|
||||||
1209600 ; expire (2 weeks)
|
43 TYPE65280 \# 1 05 ; TTL=43 implied owner name explicit TTL
|
||||||
300 ; minimum (5 minutes)
|
MB 6 ; TTL=43 leading-tab implied owner name
|
||||||
)
|
$TTL 1337
|
||||||
$TTL 10800 ; 3 hours
|
example.com. 88 MG 7 ; TTL=88 explicit TTL
|
||||||
name. 10800 IN NS name.
|
example.com. MR 8 ; TTL=1337 after first $TTL
|
||||||
IN NS g6.nstld.com.
|
$TTL 314
|
||||||
7200 NS h6.nstld.com.
|
1 TXT 9 ; TTL=1 implied owner name explicit TTL
|
||||||
3600 IN NS j6.nstld.com.
|
example.com. DNAME 10 ; TTL=314 after second $TTL
|
||||||
IN 3600 NS k6.nstld.com.
|
|
||||||
NS l6.nstld.com.
|
|
||||||
NS a6.nstld.com.
|
|
||||||
NS c6.nstld.com.
|
|
||||||
NS d6.nstld.com.
|
|
||||||
NS f6.nstld.com.
|
|
||||||
NS m6.nstld.com.
|
|
||||||
(
|
|
||||||
NS m7.nstld.com.
|
|
||||||
)
|
|
||||||
$ORIGIN name.
|
|
||||||
0-0onlus NS ns7.ehiweb.it.
|
|
||||||
NS ns8.ehiweb.it.
|
|
||||||
0-g MX 10 mx01.nic
|
|
||||||
MX 10 mx02.nic
|
|
||||||
MX 10 mx03.nic
|
|
||||||
MX 10 mx04.nic
|
|
||||||
$ORIGIN 0-g.name
|
|
||||||
moutamassey NS ns01.yahoodomains.jp.
|
|
||||||
NS ns02.yahoodomains.jp.
|
|
||||||
`
|
`
|
||||||
to := ParseZone(strings.NewReader(zone), "", "testzone")
|
reCaseFromComment := regexp.MustCompile(`TTL=(\d+)\s+(.*)`)
|
||||||
for x := range to {
|
records := ParseZone(strings.NewReader(zone), "", "")
|
||||||
fmt.Println(x.RR)
|
var i int
|
||||||
|
for record := range records {
|
||||||
|
i++
|
||||||
|
if record.Error != nil {
|
||||||
|
t.Error(record.Error)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expected := reCaseFromComment.FindStringSubmatch(record.Comment)
|
||||||
|
expectedTTL, _ := strconv.ParseUint(expected[1], 10, 32)
|
||||||
|
ttl := record.RR.Header().Ttl
|
||||||
|
if ttl != uint32(expectedTTL) {
|
||||||
|
t.Errorf("%s: expected TTL %d, got %d", expected[2], expectedTTL, ttl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i != 10 {
|
||||||
|
t.Errorf("expected %d records, got %d", 5, i)
|
||||||
}
|
}
|
||||||
// Output:
|
|
||||||
// name. 3600 IN SOA a6.nstld.com. hostmaster.nic.name. 203362132 300 300 1209600 300
|
|
||||||
// name. 10800 IN NS name.
|
|
||||||
// name. 10800 IN NS g6.nstld.com.
|
|
||||||
// name. 7200 IN NS h6.nstld.com.
|
|
||||||
// name. 3600 IN NS j6.nstld.com.
|
|
||||||
// name. 3600 IN NS k6.nstld.com.
|
|
||||||
// name. 10800 IN NS l6.nstld.com.
|
|
||||||
// name. 10800 IN NS a6.nstld.com.
|
|
||||||
// name. 10800 IN NS c6.nstld.com.
|
|
||||||
// name. 10800 IN NS d6.nstld.com.
|
|
||||||
// name. 10800 IN NS f6.nstld.com.
|
|
||||||
// name. 10800 IN NS m6.nstld.com.
|
|
||||||
// name. 10800 IN NS m7.nstld.com.
|
|
||||||
// 0-0onlus.name. 10800 IN NS ns7.ehiweb.it.
|
|
||||||
// 0-0onlus.name. 10800 IN NS ns8.ehiweb.it.
|
|
||||||
// 0-g.name. 10800 IN MX 10 mx01.nic.name.
|
|
||||||
// 0-g.name. 10800 IN MX 10 mx02.nic.name.
|
|
||||||
// 0-g.name. 10800 IN MX 10 mx03.nic.name.
|
|
||||||
// 0-g.name. 10800 IN MX 10 mx04.nic.name.
|
|
||||||
// moutamassey.0-g.name.name. 10800 IN NS ns01.yahoodomains.jp.
|
|
||||||
// moutamassey.0-g.name.name. 10800 IN NS ns02.yahoodomains.jp.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleHIP() {
|
func TestRelativeNameErrors(t *testing.T) {
|
||||||
h := `www.example.com IN HIP ( 2 200100107B1A74DF365639CC39F1D578
|
var badZones = []struct {
|
||||||
AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p
|
label string
|
||||||
9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQ
|
zoneContents string
|
||||||
b1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D
|
expectedErr string
|
||||||
rvs.example.com. )`
|
}{
|
||||||
if hip, err := NewRR(h); err == nil {
|
{
|
||||||
fmt.Println(hip.String())
|
"relative owner name without origin",
|
||||||
|
"example.com 3600 IN SOA ns.example.com. hostmaster.example.com. 1 86400 60 86400 3600",
|
||||||
|
"bad owner name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"relative owner name in RDATA",
|
||||||
|
"example.com. 3600 IN SOA ns hostmaster 1 86400 60 86400 3600",
|
||||||
|
"bad SOA Ns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"origin reference without origin",
|
||||||
|
"@ 3600 IN SOA ns.example.com. hostmaster.example.com. 1 86400 60 86400 3600",
|
||||||
|
"bad owner name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"relative owner name in $INCLUDE",
|
||||||
|
"$INCLUDE file.db example.com",
|
||||||
|
"bad origin name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"relative owner name in $ORIGIN",
|
||||||
|
"$ORIGIN example.com",
|
||||||
|
"bad origin name",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, errorCase := range badZones {
|
||||||
|
entries := ParseZone(strings.NewReader(errorCase.zoneContents), "", "")
|
||||||
|
for entry := range entries {
|
||||||
|
if entry.Error == nil {
|
||||||
|
t.Errorf("%s: expected error, got nil", errorCase.label)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := entry.Error.err
|
||||||
|
if err != errorCase.expectedErr {
|
||||||
|
t.Errorf("%s: expected error `%s`, got `%s`", errorCase.label, errorCase.expectedErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Output:
|
|
||||||
// www.example.com. 3600 IN HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHIP(t *testing.T) {
|
func TestHIP(t *testing.T) {
|
||||||
|
@ -686,24 +694,6 @@ b1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleSOA() {
|
|
||||||
s := "example.com. 1000 SOA master.example.com. admin.example.com. 1 4294967294 4294967293 4294967295 100"
|
|
||||||
if soa, err := NewRR(s); err == nil {
|
|
||||||
fmt.Println(soa.String())
|
|
||||||
}
|
|
||||||
// Output:
|
|
||||||
// example.com. 1000 IN SOA master.example.com. admin.example.com. 1 4294967294 4294967293 4294967295 100
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLineNumberError(t *testing.T) {
|
|
||||||
s := "example.com. 1000 SOA master.example.com. admin.example.com. monkey 4294967294 4294967293 4294967295 100"
|
|
||||||
if _, err := NewRR(s); err != nil {
|
|
||||||
if err.Error() != "dns: bad SOA zone parameter: \"monkey\" at line: 1:68" {
|
|
||||||
t.Error("not expecting this error: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with no known RR on the line
|
// Test with no known RR on the line
|
||||||
func TestLineNumberError2(t *testing.T) {
|
func TestLineNumberError2(t *testing.T) {
|
||||||
tests := map[string]string{
|
tests := map[string]string{
|
||||||
|
@ -801,28 +791,6 @@ func TestLowercaseTokens(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleParseZone_generate() {
|
|
||||||
// From the manual: http://www.bind9.net/manual/bind/9.3.2/Bv9ARM.ch06.html#id2566761
|
|
||||||
zone := "$GENERATE 1-2 0 NS SERVER$.EXAMPLE.\n$GENERATE 1-8 $ CNAME $.0"
|
|
||||||
to := ParseZone(strings.NewReader(zone), "0.0.192.IN-ADDR.ARPA.", "")
|
|
||||||
for x := range to {
|
|
||||||
if x.Error == nil {
|
|
||||||
fmt.Println(x.RR.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Output:
|
|
||||||
// 0.0.0.192.IN-ADDR.ARPA. 3600 IN NS SERVER1.EXAMPLE.
|
|
||||||
// 0.0.0.192.IN-ADDR.ARPA. 3600 IN NS SERVER2.EXAMPLE.
|
|
||||||
// 1.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 1.0.0.0.192.IN-ADDR.ARPA.
|
|
||||||
// 2.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 2.0.0.0.192.IN-ADDR.ARPA.
|
|
||||||
// 3.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 3.0.0.0.192.IN-ADDR.ARPA.
|
|
||||||
// 4.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 4.0.0.0.192.IN-ADDR.ARPA.
|
|
||||||
// 5.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 5.0.0.0.192.IN-ADDR.ARPA.
|
|
||||||
// 6.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 6.0.0.0.192.IN-ADDR.ARPA.
|
|
||||||
// 7.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 7.0.0.0.192.IN-ADDR.ARPA.
|
|
||||||
// 8.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 8.0.0.0.192.IN-ADDR.ARPA.
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSRVPacking(t *testing.T) {
|
func TestSRVPacking(t *testing.T) {
|
||||||
msg := Msg{}
|
msg := Msg{}
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ func (rd *VERSION) Len() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
var smallzone = `$ORIGIN example.org.
|
var smallzone = `$ORIGIN example.org.
|
||||||
@ SOA sns.dns.icann.org. noc.dns.icann.org. (
|
@ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. (
|
||||||
2014091518 7200 3600 1209600 3600
|
2014091518 7200 3600 1209600 3600
|
||||||
)
|
)
|
||||||
A 1.2.3.4
|
A 1.2.3.4
|
||||||
|
|
129
scan.go
129
scan.go
|
@ -105,6 +105,12 @@ type Token struct {
|
||||||
Comment string
|
Comment string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ttlState describes the state necessary to fill in an omitted RR TTL
|
||||||
|
type ttlState struct {
|
||||||
|
ttl uint32 // ttl is the current default TTL
|
||||||
|
isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive
|
||||||
|
}
|
||||||
|
|
||||||
// NewRR reads the RR contained in the string s. Only the first RR is
|
// NewRR reads the RR contained in the string s. Only the first RR is
|
||||||
// returned. If s contains no RR, return nil with no error. The class
|
// returned. If s contains no RR, return nil with no error. The class
|
||||||
// defaults to IN and TTL defaults to 3600. The full zone file syntax
|
// defaults to IN and TTL defaults to 3600. The full zone file syntax
|
||||||
|
@ -120,7 +126,8 @@ func NewRR(s string) (RR, error) {
|
||||||
// ReadRR reads the RR contained in q.
|
// ReadRR reads the RR contained in q.
|
||||||
// See NewRR for more documentation.
|
// See NewRR for more documentation.
|
||||||
func ReadRR(q io.Reader, filename string) (RR, error) {
|
func ReadRR(q io.Reader, filename string) (RR, error) {
|
||||||
r := <-parseZoneHelper(q, ".", filename, 1)
|
defttl := &ttlState{defaultTtl, false}
|
||||||
|
r := <-parseZoneHelper(q, ".", defttl, filename, 1)
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -132,10 +139,10 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseZone reads a RFC 1035 style zonefile from r. It returns *Tokens on the
|
// ParseZone reads a RFC 1035 style zonefile from r. It returns *Tokens on the
|
||||||
// returned channel, which consist out the parsed RR, a potential comment or an error.
|
// returned channel, each consisting of either a parsed RR and optional comment
|
||||||
// If there is an error the RR is nil. The string file is only used
|
// or a nil RR and an error. The string file is only used
|
||||||
// in error reporting. The string origin is used as the initial origin, as
|
// in error reporting. The string origin is used as the initial origin, as
|
||||||
// if the file would start with: $ORIGIN origin .
|
// if the file would start with an $ORIGIN directive.
|
||||||
// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are supported.
|
// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are supported.
|
||||||
// The channel t is closed by ParseZone when the end of r is reached.
|
// The channel t is closed by ParseZone when the end of r is reached.
|
||||||
//
|
//
|
||||||
|
@ -157,16 +164,16 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
|
||||||
// The text "; this is comment" is returned in Token.Comment. Comments inside the
|
// The text "; this is comment" is returned in Token.Comment. Comments inside the
|
||||||
// RR are discarded. Comments on a line by themselves are discarded too.
|
// RR are discarded. Comments on a line by themselves are discarded too.
|
||||||
func ParseZone(r io.Reader, origin, file string) chan *Token {
|
func ParseZone(r io.Reader, origin, file string) chan *Token {
|
||||||
return parseZoneHelper(r, origin, file, 10000)
|
return parseZoneHelper(r, origin, nil, file, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseZoneHelper(r io.Reader, origin, file string, chansize int) chan *Token {
|
func parseZoneHelper(r io.Reader, origin string, defttl *ttlState, file string, chansize int) chan *Token {
|
||||||
t := make(chan *Token, chansize)
|
t := make(chan *Token, chansize)
|
||||||
go parseZone(r, origin, file, t, 0)
|
go parseZone(r, origin, defttl, file, t, 0)
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *Token, include int) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if include == 0 {
|
if include == 0 {
|
||||||
close(t)
|
close(t)
|
||||||
|
@ -186,18 +193,16 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
// After detecting these, we know the zRrtype so we can jump to functions
|
// After detecting these, we know the zRrtype so we can jump to functions
|
||||||
// handling the rdata for each of these types.
|
// handling the rdata for each of these types.
|
||||||
|
|
||||||
if origin == "" {
|
if origin != "" {
|
||||||
origin = "."
|
origin = Fqdn(origin)
|
||||||
}
|
if _, ok := IsDomainName(origin); !ok {
|
||||||
origin = Fqdn(origin)
|
t <- &Token{Error: &ParseError{f, "bad initial origin name", lex{}}}
|
||||||
if _, ok := IsDomainName(origin); !ok {
|
return
|
||||||
t <- &Token{Error: &ParseError{f, "bad initial origin name", lex{}}}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
st := zExpectOwnerDir // initial state
|
st := zExpectOwnerDir // initial state
|
||||||
var h RR_Header
|
var h RR_Header
|
||||||
var defttl uint32 = defaultTtl
|
|
||||||
var prevName string
|
var prevName string
|
||||||
for l := range c {
|
for l := range c {
|
||||||
// Lexer spotted an error already
|
// Lexer spotted an error already
|
||||||
|
@ -209,27 +214,21 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
switch st {
|
switch st {
|
||||||
case zExpectOwnerDir:
|
case zExpectOwnerDir:
|
||||||
// We can also expect a directive, like $TTL or $ORIGIN
|
// We can also expect a directive, like $TTL or $ORIGIN
|
||||||
h.Ttl = defttl
|
if defttl != nil {
|
||||||
|
h.Ttl = defttl.ttl
|
||||||
|
}
|
||||||
h.Class = ClassINET
|
h.Class = ClassINET
|
||||||
switch l.value {
|
switch l.value {
|
||||||
case zNewline:
|
case zNewline:
|
||||||
st = zExpectOwnerDir
|
st = zExpectOwnerDir
|
||||||
case zOwner:
|
case zOwner:
|
||||||
h.Name = l.token
|
h.Name = l.token
|
||||||
if l.token[0] == '@' {
|
name, ok := toAbsoluteName(l.token, origin)
|
||||||
h.Name = origin
|
|
||||||
prevName = h.Name
|
|
||||||
st = zExpectOwnerBl
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if h.Name[l.length-1] != '.' {
|
|
||||||
h.Name = appendOrigin(h.Name, origin)
|
|
||||||
}
|
|
||||||
_, ok := IsDomainName(l.token)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
t <- &Token{Error: &ParseError{f, "bad owner name", l}}
|
t <- &Token{Error: &ParseError{f, "bad owner name", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
h.Name = name
|
||||||
prevName = h.Name
|
prevName = h.Name
|
||||||
st = zExpectOwnerBl
|
st = zExpectOwnerBl
|
||||||
case zDirTtl:
|
case zDirTtl:
|
||||||
|
@ -258,8 +257,9 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.Ttl = ttl
|
h.Ttl = ttl
|
||||||
// Don't about the defttl, we should take the $TTL value
|
if defttl == nil || !defttl.isByDirective {
|
||||||
// defttl = ttl
|
defttl = &ttlState{ttl, false}
|
||||||
|
}
|
||||||
st = zExpectAnyNoTtlBl
|
st = zExpectAnyNoTtlBl
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -282,20 +282,12 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
case zBlank:
|
case zBlank:
|
||||||
l := <-c
|
l := <-c
|
||||||
if l.value == zString {
|
if l.value == zString {
|
||||||
if _, ok := IsDomainName(l.token); !ok || l.length == 0 || l.err {
|
name, ok := toAbsoluteName(l.token, origin)
|
||||||
|
if !ok {
|
||||||
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
|
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// a new origin is specified.
|
neworigin = name
|
||||||
if l.token[l.length-1] != '.' {
|
|
||||||
if origin != "." { // Prevent .. endings
|
|
||||||
neworigin = l.token + "." + origin
|
|
||||||
} else {
|
|
||||||
neworigin = l.token + origin
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
neworigin = l.token
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case zNewline, zEOF:
|
case zNewline, zEOF:
|
||||||
// Ok
|
// Ok
|
||||||
|
@ -313,7 +305,7 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
t <- &Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}}
|
t <- &Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parseZone(r1, neworigin, l.token, t, include+1)
|
parseZone(r1, neworigin, defttl, l.token, t, include+1)
|
||||||
st = zExpectOwnerDir
|
st = zExpectOwnerDir
|
||||||
case zExpectDirTtlBl:
|
case zExpectDirTtlBl:
|
||||||
if l.value != zBlank {
|
if l.value != zBlank {
|
||||||
|
@ -335,7 +327,7 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defttl = ttl
|
defttl = &ttlState{ttl, true}
|
||||||
st = zExpectOwnerDir
|
st = zExpectOwnerDir
|
||||||
case zExpectDirOriginBl:
|
case zExpectDirOriginBl:
|
||||||
if l.value != zBlank {
|
if l.value != zBlank {
|
||||||
|
@ -351,19 +343,12 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
if e, _ := slurpRemainder(c, f); e != nil {
|
if e, _ := slurpRemainder(c, f); e != nil {
|
||||||
t <- &Token{Error: e}
|
t <- &Token{Error: e}
|
||||||
}
|
}
|
||||||
if _, ok := IsDomainName(l.token); !ok {
|
name, ok := toAbsoluteName(l.token, origin)
|
||||||
|
if !ok {
|
||||||
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
|
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if l.token[l.length-1] != '.' {
|
origin = name
|
||||||
if origin != "." { // Prevent .. endings
|
|
||||||
origin = l.token + "." + origin
|
|
||||||
} else {
|
|
||||||
origin = l.token + origin
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
origin = l.token
|
|
||||||
}
|
|
||||||
st = zExpectOwnerDir
|
st = zExpectOwnerDir
|
||||||
case zExpectDirGenerateBl:
|
case zExpectDirGenerateBl:
|
||||||
if l.value != zBlank {
|
if l.value != zBlank {
|
||||||
|
@ -390,6 +375,10 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
case zExpectAny:
|
case zExpectAny:
|
||||||
switch l.value {
|
switch l.value {
|
||||||
case zRrtpe:
|
case zRrtpe:
|
||||||
|
if defttl == nil {
|
||||||
|
t <- &Token{Error: &ParseError{f, "missing TTL with no previous value", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
h.Rrtype = l.torc
|
h.Rrtype = l.torc
|
||||||
st = zExpectRdata
|
st = zExpectRdata
|
||||||
case zClass:
|
case zClass:
|
||||||
|
@ -402,7 +391,9 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.Ttl = ttl
|
h.Ttl = ttl
|
||||||
// defttl = ttl // don't set the defttl here
|
if defttl == nil || !defttl.isByDirective {
|
||||||
|
defttl = &ttlState{ttl, false}
|
||||||
|
}
|
||||||
st = zExpectAnyNoTtlBl
|
st = zExpectAnyNoTtlBl
|
||||||
default:
|
default:
|
||||||
t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}}
|
t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}}
|
||||||
|
@ -441,7 +432,9 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.Ttl = ttl
|
h.Ttl = ttl
|
||||||
// defttl = ttl // don't set the def ttl anymore
|
if defttl == nil || !defttl.isByDirective {
|
||||||
|
defttl = &ttlState{ttl, false}
|
||||||
|
}
|
||||||
st = zExpectRrtypeBl
|
st = zExpectRrtypeBl
|
||||||
case zRrtpe:
|
case zRrtpe:
|
||||||
h.Rrtype = l.torc
|
h.Rrtype = l.torc
|
||||||
|
@ -918,6 +911,34 @@ func stringToCm(token string) (e, m uint8, ok bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toAbsoluteName(name, origin string) (absolute string, ok bool) {
|
||||||
|
// check for an explicit origin reference
|
||||||
|
if name == "@" {
|
||||||
|
// require a nonempty origin
|
||||||
|
if origin == "" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return origin, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// require a valid domain name
|
||||||
|
_, ok = IsDomainName(name)
|
||||||
|
if !ok || name == "" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if name is already absolute
|
||||||
|
if name[len(name)-1] == '.' {
|
||||||
|
return name, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// require a nonempty origin
|
||||||
|
if origin == "" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return appendOrigin(name, origin), true
|
||||||
|
}
|
||||||
|
|
||||||
func appendOrigin(name, origin string) string {
|
func appendOrigin(name, origin string) string {
|
||||||
if origin == "." {
|
if origin == "." {
|
||||||
return name + origin
|
return name + origin
|
||||||
|
|
592
scan_rr.go
592
scan_rr.go
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue