Merge branch 'master' into fixed-505

This commit is contained in:
Roland Shoemaker 2015-08-13 22:50:13 -07:00
commit 3d5185d0db
22 changed files with 669 additions and 382 deletions

2
Godeps/Godeps.json generated
View File

@ -82,7 +82,7 @@
}, },
{ {
"ImportPath": "github.com/miekg/dns", "ImportPath": "github.com/miekg/dns",
"Rev": "259969e797348d20e8c144a7573c23f06fa962f5" "Rev": "7ff8d29c8b70b10f383a11f03b7bf5b7408bf41a"
}, },
{ {
"ImportPath": "github.com/miekg/pkcs11", "ImportPath": "github.com/miekg/pkcs11",

View File

@ -1,6 +1,6 @@
language: go language: go
go: go:
- 1.3
- 1.4 - 1.4
- 1.5rc1
script: script:
- go test -short -bench=. - go test -short -bench=.

View File

@ -24,6 +24,7 @@ If you like this, you may also be interested in:
A not-so-up-to-date-list-that-may-be-actually-current: A not-so-up-to-date-list-that-may-be-actually-current:
* https://cloudflare.com
* https://github.com/abh/geodns * https://github.com/abh/geodns
* http://www.statdns.com/ * http://www.statdns.com/
* http://www.dnsinspect.com/ * http://www.dnsinspect.com/
@ -38,6 +39,7 @@ A not-so-up-to-date-list-that-may-be-actually-current:
* https://github.com/tianon/rawdns * https://github.com/tianon/rawdns
* https://mesosphere.github.io/mesos-dns/ * https://mesosphere.github.io/mesos-dns/
* https://pulse.turbobytes.com/ * https://pulse.turbobytes.com/
* https://play.google.com/store/apps/details?id=com.turbobytes.dig
Send pull request if you want to be listed here. Send pull request if you want to be listed here.
@ -81,7 +83,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
*all of them* *all of them*
* 103{4,5} - DNS standard * 103{4,5} - DNS standard
* 1348 - NSAP record * 1348 - NSAP record (removed the record)
* 1982 - Serial Arithmetic * 1982 - Serial Arithmetic
* 1876 - LOC record * 1876 - LOC record
* 1995 - IXFR * 1995 - IXFR
@ -125,7 +127,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 6975 - Algorithm Understanding in DNSSEC * 6975 - Algorithm Understanding in DNSSEC
* 7043 - EUI48/EUI64 records * 7043 - EUI48/EUI64 records
* 7314 - DNS (EDNS) EXPIRE Option * 7314 - DNS (EDNS) EXPIRE Option
* xxxx - URI record (draft) * 7553 - URI record
* xxxx - EDNS0 DNS Update Lease (draft) * xxxx - EDNS0 DNS Update Lease (draft)
## Loosely based upon ## Loosely based upon
@ -138,7 +140,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
## TODO ## TODO
* privatekey.Precompute() when signing? * privatekey.Precompute() when signing?
* Last remaining RRs: APL, ATMA, A6 and NXT and IPSECKEY; * Last remaining RRs: APL, ATMA, A6, NSAP and NXT.
* Missing in parsing: ISDN, UNSPEC, ATMA; * Missing in parsing: ISDN, UNSPEC, NSAP and ATMA.
* NSEC(3) cover/match/closest enclose; * NSEC(3) cover/match/closest enclose.
* Replies with TC bit are not parsed to the end; * Replies with TC bit are not parsed to the end.

View File

@ -188,6 +188,33 @@ func IsFqdn(s string) bool {
return s[l-1] == '.' return s[l-1] == '.'
} }
// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181.
// This means the RRs need to have the same type, name, and class. Returns true
// if the RR set is valid, otherwise false.
func IsRRset(rrset []RR) bool {
if len(rrset) == 0 {
return false
}
if len(rrset) == 1 {
return true
}
rrHeader := rrset[0].Header()
rrType := rrHeader.Rrtype
rrClass := rrHeader.Class
rrName := rrHeader.Name
for _, rr := range rrset[1:] {
curRRHeader := rr.Header()
if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName {
// Mismatch between the records, so this is not a valid rrset for
//signing/verifying
return false
}
}
return true
}
// Fqdn return the fully qualified domain name from s. // Fqdn return the fully qualified domain name from s.
// If s is already fully qualified, it behaves as the identity function. // If s is already fully qualified, it behaves as the identity function.
func Fqdn(s string) string { func Fqdn(s string) string {

View File

@ -296,7 +296,7 @@ func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error {
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work. // This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
// First the easy checks // First the easy checks
if len(rrset) == 0 { if !IsRRset(rrset) {
return ErrRRset return ErrRRset
} }
if rr.KeyTag != k.KeyTag() { if rr.KeyTag != k.KeyTag() {
@ -314,14 +314,17 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
if k.Protocol != 3 { if k.Protocol != 3 {
return ErrKey return ErrKey
} }
for _, r := range rrset {
if r.Header().Class != rr.Hdr.Class { // IsRRset checked that we have at least one RR and that the RRs in
return ErrRRset // the set have consistent type, class, and name. Also check that type and
} // class matches the RRSIG record.
if r.Header().Rrtype != rr.TypeCovered { if rrset[0].Header().Class != rr.Hdr.Class {
return ErrRRset return ErrRRset
}
} }
if rrset[0].Header().Rrtype != rr.TypeCovered {
return ErrRRset
}
// RFC 4035 5.3.2. Reconstructing the Signed Data // RFC 4035 5.3.2. Reconstructing the Signed Data
// Copy the sig, except the rrsig data // Copy the sig, except the rrsig data
sigwire := new(rrsigWireFmt) sigwire := new(rrsigWireFmt)
@ -409,7 +412,8 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
// ValidityPeriod uses RFC1982 serial arithmetic to calculate // ValidityPeriod uses RFC1982 serial arithmetic to calculate
// if a signature period is valid. If t is the zero time, the // if a signature period is valid. If t is the zero time, the
// current time is taken other t is. // current time is taken other t is. Returns true if the signature
// is valid at the given time, otherwise returns false.
func (rr *RRSIG) ValidityPeriod(t time.Time) bool { func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
var utc int64 var utc int64
if t.IsZero() { if t.IsZero() {

View File

@ -656,3 +656,64 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG)) t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
} }
} }
func TestInvalidRRSet(t *testing.T) {
goodRecords := make([]RR, 2)
goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
goodRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
// Generate key
keyname := "cloudflare.com."
key := &DNSKEY{
Hdr: RR_Header{Name: keyname, Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 0},
Algorithm: ECDSAP256SHA256,
Flags: ZONE,
Protocol: 3,
}
privatekey, err := key.Generate(256)
if err != nil {
t.Fatal(err.Error())
}
// Need to fill in: Inception, Expiration, KeyTag, SignerName and Algorithm
curTime := time.Now()
signature := &RRSIG{
Inception: uint32(curTime.Unix()),
Expiration: uint32(curTime.Add(time.Hour).Unix()),
KeyTag: key.KeyTag(),
SignerName: keyname,
Algorithm: ECDSAP256SHA256,
}
// Inconsistent name between records
badRecords := make([]RR, 2)
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
badRecords[1] = &TXT{Hdr: RR_Header{Name: "nama.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
if IsRRset(badRecords) {
t.Fatal("Record set with inconsistent names considered valid")
}
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
badRecords[1] = &A{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeA, Class: ClassINET, Ttl: 0}}
if IsRRset(badRecords) {
t.Fatal("Record set with inconsistent record types considered valid")
}
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
badRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassCHAOS, Ttl: 0}, Txt: []string{"_o/"}}
if IsRRset(badRecords) {
t.Fatal("Record set with inconsistent record class considered valid")
}
// Sign the good record set and then make sure verification fails on the bad record set
if err := signature.Sign(privatekey, goodRecords); err != nil {
t.Fatal("Signing good records failed")
}
if err := signature.Verify(key, badRecords); err != ErrRRset {
t.Fatal("Verification did not return ErrRRset with inconsistent records")
}
}

View File

@ -192,6 +192,11 @@ func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
// e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4 // e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4
// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6 // // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6
// o.Option = append(o.Option, e) // o.Option = append(o.Option, e)
//
// Note: the spec (draft-ietf-dnsop-edns-client-subnet-00) has some insane logic
// for which netmask applies to the address. This code will parse all the
// available bits when unpacking (up to optlen). When packing it will apply
// SourceNetmask. If you need more advanced logic, patches welcome and good luck.
type EDNS0_SUBNET struct { type EDNS0_SUBNET struct {
Code uint16 // Always EDNS0SUBNET Code uint16 // Always EDNS0SUBNET
Family uint16 // 1 for IP, 2 for IP6 Family uint16 // 1 for IP, 2 for IP6
@ -218,38 +223,22 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) {
if e.SourceNetmask > net.IPv4len*8 { if e.SourceNetmask > net.IPv4len*8 {
return nil, errors.New("dns: bad netmask") return nil, errors.New("dns: bad netmask")
} }
ip := make([]byte, net.IPv4len) if len(e.Address.To4()) != net.IPv4len {
a := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8)) return nil, errors.New("dns: bad address")
for i := 0; i < net.IPv4len; i++ {
if i+1 > len(e.Address) {
break
}
ip[i] = a[i]
} }
needLength := e.SourceNetmask / 8 ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
if e.SourceNetmask%8 > 0 { needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
needLength++ b = append(b, ip[:needLength]...)
}
ip = ip[:needLength]
b = append(b, ip...)
case 2: case 2:
if e.SourceNetmask > net.IPv6len*8 { if e.SourceNetmask > net.IPv6len*8 {
return nil, errors.New("dns: bad netmask") return nil, errors.New("dns: bad netmask")
} }
ip := make([]byte, net.IPv6len) if len(e.Address) != net.IPv6len {
a := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8)) return nil, errors.New("dns: bad address")
for i := 0; i < net.IPv6len; i++ {
if i+1 > len(e.Address) {
break
}
ip[i] = a[i]
} }
needLength := e.SourceNetmask / 8 ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
if e.SourceNetmask%8 > 0 { needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
needLength++ b = append(b, ip[:needLength]...)
}
ip = ip[:needLength]
b = append(b, ip...)
default: default:
return nil, errors.New("dns: bad address family") return nil, errors.New("dns: bad address family")
} }
@ -257,8 +246,7 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) {
} }
func (e *EDNS0_SUBNET) unpack(b []byte) error { func (e *EDNS0_SUBNET) unpack(b []byte) error {
lb := len(b) if len(b) < 4 {
if lb < 4 {
return ErrBuf return ErrBuf
} }
e.Family, _ = unpackUint16(b, 0) e.Family, _ = unpackUint16(b, 0)
@ -266,25 +254,27 @@ func (e *EDNS0_SUBNET) unpack(b []byte) error {
e.SourceScope = b[3] e.SourceScope = b[3]
switch e.Family { switch e.Family {
case 1: case 1:
addr := make([]byte, 4) if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
for i := 0; i < int(e.SourceNetmask/8); i++ { return errors.New("dns: bad netmask")
if i >= len(addr) || 4+i >= len(b) { }
return ErrBuf addr := make([]byte, net.IPv4len)
} for i := 0; i < net.IPv4len && 4+i < len(b); i++ {
addr[i] = b[4+i] addr[i] = b[4+i]
} }
e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3]) e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3])
case 2: case 2:
addr := make([]byte, 16) if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
for i := 0; i < int(e.SourceNetmask/8); i++ { return errors.New("dns: bad netmask")
if i >= len(addr) || 4+i >= len(b) { }
return ErrBuf addr := make([]byte, net.IPv6len)
} for i := 0; i < net.IPv6len && 4+i < len(b); i++ {
addr[i] = b[4+i] addr[i] = b[4+i]
} }
e.Address = net.IP{addr[0], addr[1], addr[2], addr[3], addr[4], e.Address = net.IP{addr[0], addr[1], addr[2], addr[3], addr[4],
addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10],
addr[11], addr[12], addr[13], addr[14], addr[15]} addr[11], addr[12], addr[13], addr[14], addr[15]}
default:
return errors.New("dns: bad address family")
} }
return nil return nil
} }

View File

@ -5,6 +5,7 @@ import (
"bytes" "bytes"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
) )
@ -27,9 +28,15 @@ const (
) )
// ToPunycode converts unicode domain names to DNS-appropriate punycode names. // ToPunycode converts unicode domain names to DNS-appropriate punycode names.
// This function would return an empty string result for domain names with // This function will return an empty string result for domain names with
// invalid unicode strings. This function expects domain names in lowercase. // invalid unicode strings. This function expects domain names in lowercase.
func ToPunycode(s string) string { func ToPunycode(s string) string {
// Early check to see if encoding is needed.
// This will prevent making heap allocations when not needed.
if !needToPunycode(s) {
return s
}
tokens := dns.SplitDomainName(s) tokens := dns.SplitDomainName(s)
switch { switch {
case s == "": case s == "":
@ -51,7 +58,14 @@ func ToPunycode(s string) string {
} }
// FromPunycode returns unicode domain name from provided punycode string. // FromPunycode returns unicode domain name from provided punycode string.
// This function expects punycode strings in lowercase.
func FromPunycode(s string) string { func FromPunycode(s string) string {
// Early check to see if decoding is needed.
// This will prevent making heap allocations when not needed.
if !needFromPunycode(s) {
return s
}
tokens := dns.SplitDomainName(s) tokens := dns.SplitDomainName(s)
switch { switch {
case s == "": case s == "":
@ -124,7 +138,7 @@ func next(b []rune, boundary rune) rune {
} }
// preprune converts unicode rune to lower case. At this time it's not // preprune converts unicode rune to lower case. At this time it's not
// supporting all things described in RFCs // supporting all things described in RFCs.
func preprune(r rune) rune { func preprune(r rune) rune {
if unicode.IsUpper(r) { if unicode.IsUpper(r) {
r = unicode.ToLower(r) r = unicode.ToLower(r)
@ -132,7 +146,7 @@ func preprune(r rune) rune {
return r return r
} }
// tfunc is a function that helps calculate each character weight // tfunc is a function that helps calculate each character weight.
func tfunc(k, bias rune) rune { func tfunc(k, bias rune) rune {
switch { switch {
case k <= bias: case k <= bias:
@ -143,6 +157,51 @@ func tfunc(k, bias rune) rune {
return k - bias return k - bias
} }
// needToPunycode returns true for strings that require punycode encoding
// (contain unicode characters).
func needToPunycode(s string) bool {
// This function is very similar to bytes.Runes. We don't use bytes.Runes
// because it makes a heap allocation that's not needed here.
for i := 0; len(s) > 0; i++ {
r, l := utf8.DecodeRuneInString(s)
if r > 0x7f {
return true
}
s = s[l:]
}
return false
}
// needFromPunycode returns true for strings that require punycode decoding.
func needFromPunycode(s string) bool {
if s == "." {
return false
}
off := 0
end := false
pl := len(_PREFIX)
sl := len(s)
// If s starts with _PREFIX.
if sl > pl && s[off:off+pl] == _PREFIX {
return true
}
for {
// Find the part after the next ".".
off, end = dns.NextLabel(s, off)
if end {
return false
}
// If this parts starts with _PREFIX.
if sl-off > pl && s[off:off+pl] == _PREFIX {
return true
}
}
panic("dns: not reached")
}
// encode transforms Unicode input bytes (that represent DNS label) into // encode transforms Unicode input bytes (that represent DNS label) into
// punycode bytestream. This function would return nil if there's an invalid // punycode bytestream. This function would return nil if there's an invalid
// character in the label. // character in the label.
@ -217,7 +276,7 @@ func encode(input []byte) []byte {
return out.Bytes() return out.Bytes()
} }
// decode transforms punycode input bytes (that represent DNS label) into Unicode bytestream // decode transforms punycode input bytes (that represent DNS label) into Unicode bytestream.
func decode(b []byte) []byte { func decode(b []byte) []byte {
src := b // b would move and we need to keep it src := b // b would move and we need to keep it
@ -254,6 +313,10 @@ func decode(b []byte) []byte {
return src return src
} }
i += digit * w i += digit * w
if i < 0 {
// safety check for rune overflow
return src
}
t = tfunc(k, bias) t = tfunc(k, bias)
if digit < t { if digit < t {

View File

@ -8,9 +8,9 @@ import (
var testcases = [][2]string{ var testcases = [][2]string{
{"", ""}, {"", ""},
{"a", "a"}, {"a", "a"},
{"A-B", "a-b"}, {"a-b", "a-b"},
{"A-B-C", "a-b-c"}, {"a-b-c", "a-b-c"},
{"AbC", "abc"}, {"abc", "abc"},
{"я", "xn--41a"}, {"я", "xn--41a"},
{"zя", "xn--z-0ub"}, {"zя", "xn--z-0ub"},
{"яZ", "xn--z-zub"}, {"яZ", "xn--z-zub"},
@ -86,6 +86,7 @@ var invalidACEs = []string{
"xn--*", "xn--*",
"xn--", "xn--",
"xn---", "xn---",
"xn--a000000000",
} }
func TestInvalidPunycode(t *testing.T) { func TestInvalidPunycode(t *testing.T) {

View File

@ -29,8 +29,6 @@ var (
ErrAuth error = &Error{err: "bad authentication"} ErrAuth error = &Error{err: "bad authentication"}
// ErrBuf indicates that the buffer used it too small for the message. // ErrBuf indicates that the buffer used it too small for the message.
ErrBuf error = &Error{err: "buffer size too small"} ErrBuf error = &Error{err: "buffer size too small"}
// ErrConn indicates that a connection has both a TCP and UDP socket.
ErrConn error = &Error{err: "conn holds both UDP and TCP connection"}
// ErrConnEmpty indicates a connection is being uses before it is initialized. // ErrConnEmpty indicates a connection is being uses before it is initialized.
ErrConnEmpty error = &Error{err: "conn has no connection"} ErrConnEmpty error = &Error{err: "conn has no connection"}
// ErrExtendedRcode ... // ErrExtendedRcode ...
@ -51,8 +49,6 @@ var (
ErrShortRead error = &Error{err: "short read"} ErrShortRead error = &Error{err: "short read"}
// ErrSig indicates that a signature can not be cryptographically validated. // ErrSig indicates that a signature can not be cryptographically validated.
ErrSig error = &Error{err: "bad signature"} ErrSig error = &Error{err: "bad signature"}
// ErrSigGen indicates a faulure to generate a signature.
ErrSigGen error = &Error{err: "bad signature generation"}
// ErrSOA indicates that no SOA RR was seen when doing zone transfers. // ErrSOA indicates that no SOA RR was seen when doing zone transfers.
ErrSoa error = &Error{err: "no SOA"} ErrSoa error = &Error{err: "no SOA"}
// ErrTime indicates a timing error in TSIG authentication. // ErrTime indicates a timing error in TSIG authentication.
@ -138,7 +134,6 @@ var TypeToString = map[uint16]string{
TypeNINFO: "NINFO", TypeNINFO: "NINFO",
TypeNIMLOC: "NIMLOC", TypeNIMLOC: "NIMLOC",
TypeNS: "NS", TypeNS: "NS",
TypeNSAP: "NSAP",
TypeNSAPPTR: "NSAP-PTR", TypeNSAPPTR: "NSAP-PTR",
TypeNSEC3: "NSEC3", TypeNSEC3: "NSEC3",
TypeNSEC3PARAM: "NSEC3PARAM", TypeNSEC3PARAM: "NSEC3PARAM",
@ -237,7 +232,7 @@ var RcodeToString = map[int]string{
// PackDomainName packs a domain name s into msg[off:]. // PackDomainName packs a domain name s into msg[off:].
// If compression is wanted compress must be true and the compression // If compression is wanted compress must be true and the compression
// map needs to hold a mapping between domain names and offsets // map needs to hold a mapping between domain names and offsets
// pointing into msg[]. // pointing into msg.
func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
off1, _, err = packDomainName(s, msg, off, compression, compress) off1, _, err = packDomainName(s, msg, off, compression, compress)
return return
@ -413,9 +408,6 @@ Loop:
case 0x00: case 0x00:
if c == 0x00 { if c == 0x00 {
// end of name // end of name
if len(s) == 0 {
return ".", off, nil
}
break Loop break Loop
} }
// literal string // literal string
@ -476,6 +468,9 @@ Loop:
if ptr == 0 { if ptr == 0 {
off1 = off off1 = off
} }
if len(s) == 0 {
s = []byte(".")
}
return string(s), off1, nil return string(s), off1, nil
} }
@ -573,17 +568,16 @@ func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error)
return offset, nil return offset, nil
} }
func unpackTxt(msg []byte, offset, rdend int) ([]string, int, error) { func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) {
var err error off = off0
var ss []string
var s string var s string
for offset < rdend && err == nil { for off < len(msg) && err == nil {
s, offset, err = unpackTxtString(msg, offset) s, off, err = unpackTxtString(msg, off)
if err == nil { if err == nil {
ss = append(ss, s) ss = append(ss, s)
} }
} }
return ss, offset, err return
} }
func unpackTxtString(msg []byte, offset int) (string, int, error) { func unpackTxtString(msg []byte, offset int) (string, int, error) {
@ -748,58 +742,49 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str
if val.Field(i).Len() == 0 { if val.Field(i).Len() == 0 {
break break
} }
var bitmapbyte uint16 off1 := off
for j := 0; j < val.Field(i).Len(); j++ { for j := 0; j < val.Field(i).Len(); j++ {
serv := uint16((fv.Index(j).Uint())) serv := int(fv.Index(j).Uint())
bitmapbyte = uint16(serv / 8) if off+serv/8+1 > len(msg) {
if int(bitmapbyte) > lenmsg { return len(msg), &Error{err: "overflow packing wks"}
return lenmsg, &Error{err: "overflow packing wks"} }
msg[off+serv/8] |= byte(1 << (7 - uint(serv%8)))
if off+serv/8+1 > off1 {
off1 = off + serv/8 + 1
} }
bit := uint16(serv) - bitmapbyte*8
msg[bitmapbyte] = byte(1 << (7 - bit))
} }
off += int(bitmapbyte) off = off1
case `dns:"nsec"`: // NSEC/NSEC3 case `dns:"nsec"`: // NSEC/NSEC3
// This is the uint16 type bitmap // This is the uint16 type bitmap
if val.Field(i).Len() == 0 { if val.Field(i).Len() == 0 {
// Do absolutely nothing // Do absolutely nothing
break break
} }
var lastwindow, lastlength uint16
lastwindow := uint16(0)
length := uint16(0)
if off+2 > lenmsg {
return lenmsg, &Error{err: "overflow packing nsecx"}
}
for j := 0; j < val.Field(i).Len(); j++ { for j := 0; j < val.Field(i).Len(); j++ {
t := uint16((fv.Index(j).Uint())) t := uint16(fv.Index(j).Uint())
window := uint16(t / 256) window := t / 256
if lastwindow != window { length := (t-window*256)/8 + 1
if window > lastwindow && lastlength != 0 {
// New window, jump to the new offset // New window, jump to the new offset
off += int(length) + 3 off += int(lastlength) + 2
if off > lenmsg { lastlength = 0
return lenmsg, &Error{err: "overflow packing nsecx bitmap"}
}
} }
length = (t - window*256) / 8 if window < lastwindow || length < lastlength {
bit := t - (window * 256) - (length * 8) return len(msg), &Error{err: "nsec bits out of order"}
if off+2+int(length) > lenmsg { }
return lenmsg, &Error{err: "overflow packing nsecx bitmap"} if off+2+int(length) > len(msg) {
return len(msg), &Error{err: "overflow packing nsec"}
} }
// Setting the window # // Setting the window #
msg[off] = byte(window) msg[off] = byte(window)
// Setting the octets length // Setting the octets length
msg[off+1] = byte(length + 1) msg[off+1] = byte(length)
// Setting the bit value for the type in the right octet // Setting the bit value for the type in the right octet
msg[off+2+int(length)] |= byte(1 << (7 - bit)) msg[off+1+int(length)] |= byte(1 << (7 - (t % 8)))
lastwindow = window lastwindow, lastlength = window, length
}
off += 2 + int(length)
off++
if off > lenmsg {
return lenmsg, &Error{err: "overflow packing nsecx bitmap"}
} }
off += int(lastlength) + 2
} }
case reflect.Struct: case reflect.Struct:
off, err = packStructValue(fv, msg, off, compression, compress) off, err = packStructValue(fv, msg, off, compression, compress)
@ -957,17 +942,11 @@ func packStructCompress(any interface{}, msg []byte, off int, compression map[st
return off, err return off, err
} }
// TODO(miek): Fix use of rdlength here
// Unpack a reflect.StructValue from msg. // Unpack a reflect.StructValue from msg.
// Same restrictions as packStructValue. // Same restrictions as packStructValue.
func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err error) { func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err error) {
var lenrd int
lenmsg := len(msg) lenmsg := len(msg)
for i := 0; i < val.NumField(); i++ { for i := 0; i < val.NumField(); i++ {
if lenrd != 0 && lenrd == off {
break
}
if off > lenmsg { if off > lenmsg {
return lenmsg, &Error{"bad offset unpacking"} return lenmsg, &Error{"bad offset unpacking"}
} }
@ -979,7 +958,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
// therefore it's expected that this interface would be PrivateRdata // therefore it's expected that this interface would be PrivateRdata
switch data := fv.Interface().(type) { switch data := fv.Interface().(type) {
case PrivateRdata: case PrivateRdata:
n, err := data.Unpack(msg[off:lenrd]) n, err := data.Unpack(msg[off:])
if err != nil { if err != nil {
return lenmsg, err return lenmsg, err
} }
@ -995,7 +974,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
// HIP record slice of name (or none) // HIP record slice of name (or none)
var servers []string var servers []string
var s string var s string
for off < lenrd { for off < lenmsg {
s, off, err = UnpackDomainName(msg, off) s, off, err = UnpackDomainName(msg, off)
if err != nil { if err != nil {
return lenmsg, err return lenmsg, err
@ -1004,17 +983,17 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
} }
fv.Set(reflect.ValueOf(servers)) fv.Set(reflect.ValueOf(servers))
case `dns:"txt"`: case `dns:"txt"`:
if off == lenmsg || lenrd == off { if off == lenmsg {
break break
} }
var txt []string var txt []string
txt, off, err = unpackTxt(msg, off, lenrd) txt, off, err = unpackTxt(msg, off)
if err != nil { if err != nil {
return lenmsg, err return lenmsg, err
} }
fv.Set(reflect.ValueOf(txt)) fv.Set(reflect.ValueOf(txt))
case `dns:"opt"`: // edns0 case `dns:"opt"`: // edns0
if off == lenrd { if off == lenmsg {
// This is an EDNS0 (OPT Record) with no rdata // This is an EDNS0 (OPT Record) with no rdata
// We can safely return here. // We can safely return here.
break break
@ -1022,12 +1001,12 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
var edns []EDNS0 var edns []EDNS0
Option: Option:
code := uint16(0) code := uint16(0)
if off+2 > lenmsg { if off+4 > lenmsg {
return lenmsg, &Error{err: "overflow unpacking opt"} return lenmsg, &Error{err: "overflow unpacking opt"}
} }
code, off = unpackUint16(msg, off) code, off = unpackUint16(msg, off)
optlen, off1 := unpackUint16(msg, off) optlen, off1 := unpackUint16(msg, off)
if off1+int(optlen) > lenrd { if off1+int(optlen) > lenmsg {
return lenmsg, &Error{err: "overflow unpacking opt"} return lenmsg, &Error{err: "overflow unpacking opt"}
} }
switch code { switch code {
@ -1092,7 +1071,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
edns = append(edns, e) edns = append(edns, e)
off = off1 + int(optlen) off = off1 + int(optlen)
} }
if off < lenrd { if off < lenmsg {
goto Option goto Option
} }
fv.Set(reflect.ValueOf(edns)) fv.Set(reflect.ValueOf(edns))
@ -1103,10 +1082,10 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
continue continue
} }
} }
if off == lenrd { if off == lenmsg {
break // dyn. update break // dyn. update
} }
if off+net.IPv4len > lenrd || off+net.IPv4len > lenmsg { if off+net.IPv4len > lenmsg {
return lenmsg, &Error{err: "overflow unpacking a"} return lenmsg, &Error{err: "overflow unpacking a"}
} }
fv.Set(reflect.ValueOf(net.IPv4(msg[off], msg[off+1], msg[off+2], msg[off+3]))) fv.Set(reflect.ValueOf(net.IPv4(msg[off], msg[off+1], msg[off+2], msg[off+3])))
@ -1118,10 +1097,10 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
continue continue
} }
} }
if off == lenrd { if off == lenmsg {
break break
} }
if off+net.IPv6len > lenrd || off+net.IPv6len > lenmsg { if off+net.IPv6len > lenmsg {
return lenmsg, &Error{err: "overflow unpacking aaaa"} return lenmsg, &Error{err: "overflow unpacking aaaa"}
} }
fv.Set(reflect.ValueOf(net.IP{msg[off], msg[off+1], msg[off+2], msg[off+3], msg[off+4], fv.Set(reflect.ValueOf(net.IP{msg[off], msg[off+1], msg[off+2], msg[off+3], msg[off+4],
@ -1132,7 +1111,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
// Rest of the record is the bitmap // Rest of the record is the bitmap
var serv []uint16 var serv []uint16
j := 0 j := 0
for off < lenrd { for off < lenmsg {
if off+1 > lenmsg { if off+1 > lenmsg {
return lenmsg, &Error{err: "overflow unpacking wks"} return lenmsg, &Error{err: "overflow unpacking wks"}
} }
@ -1167,36 +1146,39 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
} }
fv.Set(reflect.ValueOf(serv)) fv.Set(reflect.ValueOf(serv))
case `dns:"nsec"`: // NSEC/NSEC3 case `dns:"nsec"`: // NSEC/NSEC3
if off == lenrd { if off == len(msg) {
break break
} }
// Rest of the record is the type bitmap // Rest of the record is the type bitmap
if off+2 > lenrd || off+2 > lenmsg {
return lenmsg, &Error{err: "overflow unpacking nsecx"}
}
var nsec []uint16 var nsec []uint16
length := 0 length := 0
window := 0 window := 0
for off+2 < lenrd { lastwindow := -1
for off < len(msg) {
if off+2 > len(msg) {
return len(msg), &Error{err: "overflow unpacking nsecx"}
}
window = int(msg[off]) window = int(msg[off])
length = int(msg[off+1]) length = int(msg[off+1])
//println("off, windows, length, end", off, window, length, endrr) off += 2
if window <= lastwindow {
// RFC 4034: Blocks are present in the NSEC RR RDATA in
// increasing numerical order.
return len(msg), &Error{err: "out of order NSEC block"}
}
if length == 0 { if length == 0 {
// A length window of zero is strange. If there // RFC 4034: Blocks with no types present MUST NOT be included.
// the window should not have been specified. Bail out return len(msg), &Error{err: "empty NSEC block"}
// println("dns: length == 0 when unpacking NSEC")
return lenmsg, &Error{err: "overflow unpacking nsecx"}
} }
if length > 32 { if length > 32 {
return lenmsg, &Error{err: "overflow unpacking nsecx"} return len(msg), &Error{err: "NSEC block too long"}
}
if off+length > len(msg) {
return len(msg), &Error{err: "overflowing NSEC block"}
} }
// Walk the bytes in the window - and check the bit settings... // Walk the bytes in the window and extract the type bits
off += 2
for j := 0; j < length; j++ { for j := 0; j < length; j++ {
if off+j+1 > lenmsg {
return lenmsg, &Error{err: "overflow unpacking nsecx"}
}
b := msg[off+j] b := msg[off+j]
// Check the bits one by one, and set the type // Check the bits one by one, and set the type
if b&0x80 == 0x80 { if b&0x80 == 0x80 {
@ -1225,6 +1207,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
} }
} }
off += length off += length
lastwindow = window
} }
fv.Set(reflect.ValueOf(nsec)) fv.Set(reflect.ValueOf(nsec))
} }
@ -1234,7 +1217,12 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
return lenmsg, err return lenmsg, err
} }
if val.Type().Field(i).Name == "Hdr" { if val.Type().Field(i).Name == "Hdr" {
lenrd = off + int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) lenrd := off + int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint())
if lenrd > lenmsg {
return lenmsg, &Error{err: "overflowing header size"}
}
msg = msg[:lenrd]
lenmsg = len(msg)
} }
case reflect.Uint8: case reflect.Uint8:
if off == lenmsg { if off == lenmsg {
@ -1265,6 +1253,9 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
fv.SetUint(uint64(uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]))) fv.SetUint(uint64(uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3])))
off += 4 off += 4
case reflect.Uint64: case reflect.Uint64:
if off == lenmsg {
break
}
switch val.Type().Field(i).Tag { switch val.Type().Field(i).Tag {
default: default:
if off+8 > lenmsg { if off+8 > lenmsg {
@ -1291,30 +1282,26 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
default: default:
return lenmsg, &Error{"bad tag unpacking string: " + val.Type().Field(i).Tag.Get("dns")} return lenmsg, &Error{"bad tag unpacking string: " + val.Type().Field(i).Tag.Get("dns")}
case `dns:"octet"`: case `dns:"octet"`:
strend := lenrd s = string(msg[off:])
if strend > lenmsg { off = lenmsg
return lenmsg, &Error{err: "overflow unpacking octet"}
}
s = string(msg[off:strend])
off = strend
case `dns:"hex"`: case `dns:"hex"`:
hexend := lenrd hexend := lenmsg
if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) { if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) {
hexend = off + int(val.FieldByName("HitLength").Uint()) hexend = off + int(val.FieldByName("HitLength").Uint())
} }
if hexend > lenrd || hexend > lenmsg { if hexend > lenmsg {
return lenmsg, &Error{err: "overflow unpacking hex"} return lenmsg, &Error{err: "overflow unpacking HIP hex"}
} }
s = hex.EncodeToString(msg[off:hexend]) s = hex.EncodeToString(msg[off:hexend])
off = hexend off = hexend
case `dns:"base64"`: case `dns:"base64"`:
// Rest of the RR is base64 encoded value // Rest of the RR is base64 encoded value
b64end := lenrd b64end := lenmsg
if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) { if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) {
b64end = off + int(val.FieldByName("PublicKeyLength").Uint()) b64end = off + int(val.FieldByName("PublicKeyLength").Uint())
} }
if b64end > lenrd || b64end > lenmsg { if b64end > lenmsg {
return lenmsg, &Error{err: "overflow unpacking base64"} return lenmsg, &Error{err: "overflow unpacking HIP base64"}
} }
s = toBase64(msg[off:b64end]) s = toBase64(msg[off:b64end])
off = b64end off = b64end
@ -1684,45 +1671,77 @@ func (dns *Msg) Unpack(msg []byte) (err error) {
dns.CheckingDisabled = (dh.Bits & _CD) != 0 dns.CheckingDisabled = (dh.Bits & _CD) != 0
dns.Rcode = int(dh.Bits & 0xF) dns.Rcode = int(dh.Bits & 0xF)
// Arrays. // Don't pre-alloc these arrays, the incoming lengths are from the network.
dns.Question = make([]Question, dh.Qdcount) dns.Question = make([]Question, 0, 1)
dns.Answer = make([]RR, dh.Ancount) dns.Answer = make([]RR, 0, 10)
dns.Ns = make([]RR, dh.Nscount) dns.Ns = make([]RR, 0, 10)
dns.Extra = make([]RR, dh.Arcount) dns.Extra = make([]RR, 0, 10)
for i := 0; i < len(dns.Question); i++ { var q Question
off, err = UnpackStruct(&dns.Question[i], msg, off) for i := 0; i < int(dh.Qdcount); i++ {
off1 := off
off, err = UnpackStruct(&q, msg, off)
if err != nil { if err != nil {
return err return err
} }
if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!
dh.Qdcount = uint16(i)
break
}
dns.Question = append(dns.Question, q)
} }
// If we see a TC bit being set we return here, without // If we see a TC bit being set we return here, without
// an error, because technically it isn't an error. So return // an error, because technically it isn't an error. So return
// without parsing the potentially corrupt packet and hitting an error. // without parsing the potentially corrupt packet and hitting an error.
// TODO(miek): this isn't the best strategy! // TODO(miek): this isn't the best strategy!
// Better stragey would be: set boolean indicating truncated message, go forth and parse
// until we hit an error, return the message without the latest parsed rr if this boolean
// is true.
if dns.Truncated { if dns.Truncated {
dns.Answer = nil dns.Answer = nil
dns.Ns = nil dns.Ns = nil
dns.Extra = nil dns.Extra = nil
return nil return nil
} }
for i := 0; i < len(dns.Answer); i++ {
dns.Answer[i], off, err = UnpackRR(msg, off) var r RR
for i := 0; i < int(dh.Ancount); i++ {
off1 := off
r, off, err = UnpackRR(msg, off)
if err != nil { if err != nil {
return err return err
} }
if off1 == off { // Offset does not increase anymore, dh.Ancount is a lie!
dh.Ancount = uint16(i)
break
}
dns.Answer = append(dns.Answer, r)
} }
for i := 0; i < len(dns.Ns); i++ { for i := 0; i < int(dh.Nscount); i++ {
dns.Ns[i], off, err = UnpackRR(msg, off) off1 := off
r, off, err = UnpackRR(msg, off)
if err != nil { if err != nil {
return err return err
} }
if off1 == off { // Offset does not increase anymore, dh.Nscount is a lie!
dh.Nscount = uint16(i)
break
}
dns.Ns = append(dns.Ns, r)
} }
for i := 0; i < len(dns.Extra); i++ { for i := 0; i < int(dh.Arcount); i++ {
dns.Extra[i], off, err = UnpackRR(msg, off) off1 := off
r, off, err = UnpackRR(msg, off)
if err != nil { if err != nil {
return err return err
} }
if off1 == off { // Offset does not increase anymore, dh.Arcount is a lie!
dh.Arcount = uint16(i)
break
}
dns.Extra = append(dns.Extra, r)
} }
if off != len(msg) { if off != len(msg) {
// TODO(miek) make this an error? // TODO(miek) make this an error?

View File

@ -905,11 +905,8 @@ func TestILNP(t *testing.T) {
} }
} }
func TestNsapGposEidNimloc(t *testing.T) { func TestGposEidNimloc(t *testing.T) {
dt := map[string]string{ dt := map[string]string{
"foo.bar.com. IN NSAP 21 47000580ffff000000321099991111222233334444": "foo.bar.com.\t3600\tIN\tNSAP\t0x47000580ffff000000321099991111222233334444",
"foo.bar.com. IN NSAP 0x47000580ffff000000321099991111222233334444": "foo.bar.com.\t3600\tIN\tNSAP\t0x47000580ffff000000321099991111222233334444",
"host.school.de IN NSAP 17 39276f3100111100002222333344449876": "host.school.de.\t3600\tIN\tNSAP\t0x39276f3100111100002222333344449876",
"444433332222111199990123000000ff. NSAP-PTR foo.bar.com.": "444433332222111199990123000000ff.\t3600\tIN\tNSAP-PTR\tfoo.bar.com.", "444433332222111199990123000000ff. NSAP-PTR foo.bar.com.": "444433332222111199990123000000ff.\t3600\tIN\tNSAP-PTR\tfoo.bar.com.",
"lillee. IN GPOS -32.6882 116.8652 10.0": "lillee.\t3600\tIN\tGPOS\t-32.6882 116.8652 10.0", "lillee. IN GPOS -32.6882 116.8652 10.0": "lillee.\t3600\tIN\tGPOS\t-32.6882 116.8652 10.0",
"hinault. IN GPOS -22.6882 116.8652 250.0": "hinault.\t3600\tIN\tGPOS\t-22.6882 116.8652 250.0", "hinault. IN GPOS -22.6882 116.8652 250.0": "hinault.\t3600\tIN\tGPOS\t-22.6882 116.8652 250.0",
@ -1506,3 +1503,22 @@ func TestPackCAA(t *testing.T) {
t.Fatalf("invalid flag for unpacked answer") t.Fatalf("invalid flag for unpacked answer")
} }
} }
func TestParseURI(t *testing.T) {
lt := map[string]string{
"_http._tcp. IN URI 10 1 \"http://www.example.com/path\"": "_http._tcp.\t3600\tIN\tURI\t10 1 \"http://www.example.com/path\"",
"_http._tcp. IN URI 10 1 \"\"": "_http._tcp.\t3600\tIN\tURI\t10 1 \"\"",
}
for i, o := range lt {
rr, err := NewRR(i)
if err != nil {
t.Error("failed to parse RR: ", err)
continue
}
if rr.String() != o {
t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String())
} else {
t.Logf("RR is OK: `%s'", rr.String())
}
}
}

View File

@ -47,6 +47,7 @@ type response struct {
tcp *net.TCPConn // i/o connection if TCP was used tcp *net.TCPConn // i/o connection if TCP was used
udpSession *SessionUDP // oob data to get egress interface right udpSession *SessionUDP // oob data to get egress interface right
remoteAddr net.Addr // address of the client remoteAddr net.Addr // address of the client
writer Writer // writer to output the raw DNS bits
} }
// ServeMux is an DNS request multiplexer. It matches the // ServeMux is an DNS request multiplexer. It matches the
@ -197,6 +198,43 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
DefaultServeMux.HandleFunc(pattern, handler) DefaultServeMux.HandleFunc(pattern, handler)
} }
// Writer writes raw DNS messages; each call to Write should send an entire message.
type Writer interface {
io.Writer
}
// Reader reads raw DNS messages; each call to ReadTCP or ReadUDP should return an entire message.
type Reader interface {
// ReadTCP reads a raw message from a TCP connection. Implementations may alter
// connection properties, for example the read-deadline.
ReadTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error)
// ReadUDP reads a raw message from a UDP connection. Implementations may alter
// connection properties, for example the read-deadline.
ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
}
// defaultReader is an adapter for the Server struct that implements the Reader interface
// using the readTCP and readUDP func of the embedded Server.
type defaultReader struct {
*Server
}
func (dr *defaultReader) ReadTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error) {
return dr.readTCP(conn, timeout)
}
func (dr *defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
return dr.readUDP(conn, timeout)
}
// DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader.
// Implementations should never return a nil Reader.
type DecorateReader func(Reader) Reader
// DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer.
// Implementations should never return a nil Writer.
type DecorateWriter func(Writer) Writer
// A Server defines parameters for running an DNS server. // A Server defines parameters for running an DNS server.
type Server struct { type Server struct {
// Address to listen on, ":dns" if empty. // Address to listen on, ":dns" if empty.
@ -223,8 +261,12 @@ type Server struct {
// Unsafe instructs the server to disregard any sanity checks and directly hand the message to // Unsafe instructs the server to disregard any sanity checks and directly hand the message to
// the handler. It will specfically not check if the query has the QR bit not set. // the handler. It will specfically not check if the query has the QR bit not set.
Unsafe bool Unsafe bool
// If NotifyStartedFunc is set is is called, once the server has started listening. // If NotifyStartedFunc is set it is called once the server has started listening.
NotifyStartedFunc func() NotifyStartedFunc func()
// DecorateReader is optional, allows customization of the process that reads raw DNS messages.
DecorateReader DecorateReader
// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
DecorateWriter DecorateWriter
// For graceful shutdown. // For graceful shutdown.
stopUDP chan bool stopUDP chan bool
@ -244,9 +286,9 @@ func (srv *Server) ListenAndServe() error {
srv.lock.Unlock() srv.lock.Unlock()
return &Error{err: "server already started"} return &Error{err: "server already started"}
} }
defer srv.lock.Unlock()
srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool) srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
srv.started = true srv.started = true
srv.lock.Unlock()
addr := srv.Addr addr := srv.Addr
if addr == "" { if addr == "" {
addr = ":domain" addr = ":domain"
@ -294,20 +336,22 @@ func (srv *Server) ActivateAndServe() error {
} }
srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool) srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
srv.started = true srv.started = true
pConn := srv.PacketConn
l := srv.Listener
srv.lock.Unlock() srv.lock.Unlock()
if srv.PacketConn != nil { if pConn != nil {
if srv.UDPSize == 0 { if srv.UDPSize == 0 {
srv.UDPSize = MinMsgSize srv.UDPSize = MinMsgSize
} }
if t, ok := srv.PacketConn.(*net.UDPConn); ok { if t, ok := pConn.(*net.UDPConn); ok {
if e := setUDPSocketOptions(t); e != nil { if e := setUDPSocketOptions(t); e != nil {
return e return e
} }
return srv.serveUDP(t) return srv.serveUDP(t)
} }
} }
if srv.Listener != nil { if l != nil {
if t, ok := srv.Listener.(*net.TCPListener); ok { if t, ok := l.(*net.TCPListener); ok {
return srv.serveTCP(t) return srv.serveTCP(t)
} }
} }
@ -316,7 +360,7 @@ func (srv *Server) ActivateAndServe() error {
// Shutdown gracefully shuts down a server. After a call to Shutdown, ListenAndServe and // Shutdown gracefully shuts down a server. After a call to Shutdown, ListenAndServe and
// ActivateAndServe will return. All in progress queries are completed before the server // ActivateAndServe will return. All in progress queries are completed before the server
// is taken down. If the Shutdown is taking longer than the reading timeout and error // is taken down. If the Shutdown is taking longer than the reading timeout an error
// is returned. // is returned.
func (srv *Server) Shutdown() error { func (srv *Server) Shutdown() error {
srv.lock.Lock() srv.lock.Lock()
@ -325,7 +369,6 @@ func (srv *Server) Shutdown() error {
return &Error{err: "server not started"} return &Error{err: "server not started"}
} }
srv.started = false srv.started = false
srv.lock.Unlock()
net, addr := srv.Net, srv.Addr net, addr := srv.Net, srv.Addr
switch { switch {
case srv.Listener != nil: case srv.Listener != nil:
@ -335,6 +378,7 @@ func (srv *Server) Shutdown() error {
a := srv.PacketConn.LocalAddr() a := srv.PacketConn.LocalAddr()
net, addr = a.Network(), a.String() net, addr = a.Network(), a.String()
} }
srv.lock.Unlock()
fin := make(chan bool) fin := make(chan bool)
switch net { switch net {
@ -382,6 +426,11 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
srv.NotifyStartedFunc() srv.NotifyStartedFunc()
} }
reader := Reader(&defaultReader{srv})
if srv.DecorateReader != nil {
reader = srv.DecorateReader(reader)
}
handler := srv.Handler handler := srv.Handler
if handler == nil { if handler == nil {
handler = DefaultServeMux handler = DefaultServeMux
@ -393,7 +442,7 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
if e != nil { if e != nil {
continue continue
} }
m, e := srv.readTCP(rw, rtimeout) m, e := reader.ReadTCP(rw, rtimeout)
select { select {
case <-srv.stopTCP: case <-srv.stopTCP:
return nil return nil
@ -417,6 +466,11 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
srv.NotifyStartedFunc() srv.NotifyStartedFunc()
} }
reader := Reader(&defaultReader{srv})
if srv.DecorateReader != nil {
reader = srv.DecorateReader(reader)
}
handler := srv.Handler handler := srv.Handler
if handler == nil { if handler == nil {
handler = DefaultServeMux handler = DefaultServeMux
@ -424,7 +478,7 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
rtimeout := srv.getReadTimeout() rtimeout := srv.getReadTimeout()
// deadline is not used here // deadline is not used here
for { for {
m, s, e := srv.readUDP(l, rtimeout) m, s, e := reader.ReadUDP(l, rtimeout)
select { select {
case <-srv.stopUDP: case <-srv.stopUDP:
return nil return nil
@ -442,6 +496,12 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
// Serve a new connection. // Serve a new connection.
func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t *net.TCPConn) { func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t *net.TCPConn) {
w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s} w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s}
if srv.DecorateWriter != nil {
w.writer = srv.DecorateWriter(w)
} else {
w.writer = w
}
q := 0 q := 0
defer func() { defer func() {
if u != nil { if u != nil {
@ -451,6 +511,11 @@ func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *Ses
srv.wgTCP.Done() srv.wgTCP.Done()
} }
}() }()
reader := Reader(&defaultReader{srv})
if srv.DecorateReader != nil {
reader = srv.DecorateReader(reader)
}
Redo: Redo:
req := new(Msg) req := new(Msg)
err := req.Unpack(m) err := req.Unpack(m)
@ -490,7 +555,7 @@ Exit:
if srv.IdleTimeout != nil { if srv.IdleTimeout != nil {
idleTimeout = srv.IdleTimeout() idleTimeout = srv.IdleTimeout()
} }
m, e := srv.readTCP(w.tcp, idleTimeout) m, e := reader.ReadTCP(w.tcp, idleTimeout)
if e == nil { if e == nil {
q++ q++
// TODO(miek): make this number configurable? // TODO(miek): make this number configurable?
@ -562,7 +627,7 @@ func (w *response) WriteMsg(m *Msg) (err error) {
if err != nil { if err != nil {
return err return err
} }
_, err = w.Write(data) _, err = w.writer.Write(data)
return err return err
} }
} }
@ -570,7 +635,7 @@ func (w *response) WriteMsg(m *Msg) (err error) {
if err != nil { if err != nil {
return err return err
} }
_, err = w.Write(data) _, err = w.writer.Write(data)
return err return err
} }

View File

@ -397,3 +397,54 @@ func TestShutdownUDP(t *testing.T) {
t.Errorf("Could not shutdown test UDP server, %v", err) t.Errorf("Could not shutdown test UDP server, %v", err)
} }
} }
type ExampleFrameLengthWriter struct {
Writer
}
func (e *ExampleFrameLengthWriter) Write(m []byte) (int, error) {
fmt.Println("writing raw DNS message of length", len(m))
return e.Writer.Write(m)
}
func ExampleDecorateWriter() {
// instrument raw DNS message writing
wf := DecorateWriter(func(w Writer) Writer {
return &ExampleFrameLengthWriter{w}
})
// simple UDP server
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
if err != nil {
fmt.Println(err.Error())
return
}
server := &Server{
PacketConn: pc,
DecorateWriter: wf,
}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
defer server.Shutdown()
go func() {
server.ActivateAndServe()
pc.Close()
}()
waitLock.Lock()
HandleFunc("miek.nl.", HelloServer)
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
_, _, err = c.Exchange(m, pc.LocalAddr().String())
if err != nil {
fmt.Println("failed to exchange", err.Error())
return
}
// Output: writing raw DNS message of length 56
}

View File

@ -46,7 +46,6 @@ const (
TypeX25 uint16 = 19 TypeX25 uint16 = 19
TypeISDN uint16 = 20 TypeISDN uint16 = 20
TypeRT uint16 = 21 TypeRT uint16 = 21
TypeNSAP uint16 = 22
TypeNSAPPTR uint16 = 23 TypeNSAPPTR uint16 = 23
TypeSIG uint16 = 24 TypeSIG uint16 = 24
TypeKEY uint16 = 25 TypeKEY uint16 = 25
@ -503,7 +502,7 @@ func sprintName(s string) string {
return string(dst) return string(dst)
} }
func sprintCAAValue(s string) string { func sprintTxtOctet(s string) string {
src := []byte(s) src := []byte(s)
dst := make([]byte, 0, len(src)) dst := make([]byte, 0, len(src))
dst = append(dst, '"') dst = append(dst, '"')
@ -850,7 +849,6 @@ func cmToM(m, e uint8) string {
return s return s
} }
// String returns a string version of a LOC
func (rr *LOC) String() string { func (rr *LOC) String() string {
s := rr.Hdr.String() s := rr.Hdr.String()
@ -1180,16 +1178,6 @@ func (rr *RKEY) String() string {
" " + rr.PublicKey " " + rr.PublicKey
} }
type NSAP struct {
Hdr RR_Header
Nsap string
}
func (rr *NSAP) Header() *RR_Header { return &rr.Hdr }
func (rr *NSAP) copy() RR { return &NSAP{*rr.Hdr.copyHeader(), rr.Nsap} }
func (rr *NSAP) String() string { return rr.Hdr.String() + "0x" + rr.Nsap }
func (rr *NSAP) len() int { return rr.Hdr.len() + 1 + len(rr.Nsap) + 1 }
type NSAPPTR struct { type NSAPPTR struct {
Hdr RR_Header Hdr RR_Header
Ptr string `dns:"domain-name"` Ptr string `dns:"domain-name"`
@ -1329,27 +1317,15 @@ type URI struct {
Hdr RR_Header Hdr RR_Header
Priority uint16 Priority uint16
Weight uint16 Weight uint16
Target []string `dns:"txt"` Target string `dns:"octet"`
} }
func (rr *URI) Header() *RR_Header { return &rr.Hdr } func (rr *URI) Header() *RR_Header { return &rr.Hdr }
func (rr *URI) copy() RR { func (rr *URI) copy() RR { return &URI{*rr.Hdr.copyHeader(), rr.Weight, rr.Priority, rr.Target} }
cp := make([]string, len(rr.Target), cap(rr.Target)) func (rr *URI) len() int { return rr.Hdr.len() + 4 + len(rr.Target) }
copy(cp, rr.Target)
return &URI{*rr.Hdr.copyHeader(), rr.Weight, rr.Priority, cp}
}
func (rr *URI) String() string { func (rr *URI) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +
" " + strconv.Itoa(int(rr.Weight)) + sprintTxt(rr.Target) " " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target)
}
func (rr *URI) len() int {
l := rr.Hdr.len() + 4
for _, t := range rr.Target {
l += len(t) + 1
}
return l
} }
type DHCID struct { type DHCID struct {
@ -1571,7 +1547,7 @@ func (rr *CAA) Header() *RR_Header { return &rr.Hdr }
func (rr *CAA) copy() RR { return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value} } func (rr *CAA) copy() RR { return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value} }
func (rr *CAA) len() int { return rr.Hdr.len() + 2 + len(rr.Tag) + len(rr.Value) } func (rr *CAA) len() int { return rr.Hdr.len() + 2 + len(rr.Tag) + len(rr.Value) }
func (rr *CAA) String() string { func (rr *CAA) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintCAAValue(rr.Value) return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value)
} }
type UID struct { type UID struct {
@ -1733,7 +1709,6 @@ var typeToRR = map[uint16]func() RR{
TypeNINFO: func() RR { return new(NINFO) }, TypeNINFO: func() RR { return new(NINFO) },
TypeNIMLOC: func() RR { return new(NIMLOC) }, TypeNIMLOC: func() RR { return new(NIMLOC) },
TypeNS: func() RR { return new(NS) }, TypeNS: func() RR { return new(NS) },
TypeNSAP: func() RR { return new(NSAP) },
TypeNSAPPTR: func() RR { return new(NSAPPTR) }, TypeNSAPPTR: func() RR { return new(NSAPPTR) },
TypeNSEC3: func() RR { return new(NSEC3) }, TypeNSEC3: func() RR { return new(NSEC3) },
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) }, TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },

View File

@ -141,11 +141,11 @@ func modToPrintf(s string) (string, int, string) {
return "", 0, "bad base in $GENERATE" return "", 0, "bad base in $GENERATE"
} }
offset, err := strconv.Atoi(xs[0]) offset, err := strconv.Atoi(xs[0])
if err != nil { if err != nil || offset > 255 {
return "", 0, "bad offset in $GENERATE" return "", 0, "bad offset in $GENERATE"
} }
width, err := strconv.Atoi(xs[1]) width, err := strconv.Atoi(xs[1])
if err != nil { if err != nil || width > 255 {
return "", offset, "bad width in $GENERATE" return "", offset, "bad width in $GENERATE"
} }
switch { switch {

View File

@ -131,11 +131,11 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
return r.RR, nil return r.RR, nil
} }
// ParseZone reads a RFC 1035 style one 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, which consist out the parsed RR, a potential comment or an error.
// If there is an error the RR is nil. The string file is only used // If there is an error the RR is nil. 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: $ORIGIN origin .
// 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.
// //
@ -152,7 +152,7 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
// //
// foo. IN A 10.0.0.1 ; this is a comment // foo. IN A 10.0.0.1 ; this is a comment
// //
// 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, file, 10000)
@ -281,7 +281,7 @@ 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 { if _, ok := IsDomainName(l.token); !ok || l.length == 0 || l.err {
t <- &Token{Error: &ParseError{f, "bad origin name", l}} t <- &Token{Error: &ParseError{f, "bad origin name", l}}
return return
} }
@ -806,7 +806,11 @@ func zlexer(s *scan, c chan lex) {
// Extract the class number from CLASSxx // Extract the class number from CLASSxx
func classToInt(token string) (uint16, bool) { func classToInt(token string) (uint16, bool) {
class, ok := strconv.Atoi(token[5:]) offset := 5
if len(token) < offset+1 {
return 0, false
}
class, ok := strconv.Atoi(token[offset:])
if ok != nil || class > maxUint16 { if ok != nil || class > maxUint16 {
return 0, false return 0, false
} }
@ -815,7 +819,11 @@ func classToInt(token string) (uint16, bool) {
// Extract the rr number from TYPExxx // Extract the rr number from TYPExxx
func typeToInt(token string) (uint16, bool) { func typeToInt(token string) (uint16, bool) {
typ, ok := strconv.Atoi(token[4:]) offset := 4
if len(token) < offset+1 {
return 0, false
}
typ, ok := strconv.Atoi(token[offset:])
if ok != nil || typ > maxUint16 { if ok != nil || typ > maxUint16 {
return 0, false return 0, false
} }

View File

@ -145,7 +145,7 @@ func setA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
rr.A = net.ParseIP(l.token) rr.A = net.ParseIP(l.token)
if rr.A == nil { if rr.A == nil || l.err {
return nil, &ParseError{f, "bad A A", l}, "" return nil, &ParseError{f, "bad A A", l}, ""
} }
return rr, nil, "" return rr, nil, ""
@ -160,7 +160,7 @@ func setAAAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
rr.AAAA = net.ParseIP(l.token) rr.AAAA = net.ParseIP(l.token)
if rr.AAAA == nil { if rr.AAAA == nil || l.err {
return nil, &ParseError{f, "bad AAAA AAAA", l}, "" return nil, &ParseError{f, "bad AAAA AAAA", l}, ""
} }
return rr, nil, "" return rr, nil, ""
@ -180,7 +180,7 @@ func setNS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad NS Ns", l}, "" return nil, &ParseError{f, "bad NS Ns", l}, ""
} }
if rr.Ns[l.length-1] != '.' { if rr.Ns[l.length-1] != '.' {
@ -203,7 +203,7 @@ func setPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad PTR Ptr", l}, "" return nil, &ParseError{f, "bad PTR Ptr", l}, ""
} }
if rr.Ptr[l.length-1] != '.' { if rr.Ptr[l.length-1] != '.' {
@ -226,7 +226,7 @@ func setNSAPPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string)
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad NSAP-PTR Ptr", l}, "" return nil, &ParseError{f, "bad NSAP-PTR Ptr", l}, ""
} }
if rr.Ptr[l.length-1] != '.' { if rr.Ptr[l.length-1] != '.' {
@ -248,7 +248,7 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.Mbox = o rr.Mbox = o
} else { } else {
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad RP Mbox", l}, "" return nil, &ParseError{f, "bad RP Mbox", l}, ""
} }
if rr.Mbox[l.length-1] != '.' { if rr.Mbox[l.length-1] != '.' {
@ -263,7 +263,7 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad RP Txt", l}, "" return nil, &ParseError{f, "bad RP Txt", l}, ""
} }
if rr.Txt[l.length-1] != '.' { if rr.Txt[l.length-1] != '.' {
@ -286,7 +286,7 @@ func setMR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad MR Mr", l}, "" return nil, &ParseError{f, "bad MR Mr", l}, ""
} }
if rr.Mr[l.length-1] != '.' { if rr.Mr[l.length-1] != '.' {
@ -309,7 +309,7 @@ func setMB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad MB Mb", l}, "" return nil, &ParseError{f, "bad MB Mb", l}, ""
} }
if rr.Mb[l.length-1] != '.' { if rr.Mb[l.length-1] != '.' {
@ -332,7 +332,7 @@ func setMG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad MG Mg", l}, "" return nil, &ParseError{f, "bad MG Mg", l}, ""
} }
if rr.Mg[l.length-1] != '.' { if rr.Mg[l.length-1] != '.' {
@ -380,7 +380,7 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.Rmail = o rr.Rmail = o
} else { } else {
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad MINFO Rmail", l}, "" return nil, &ParseError{f, "bad MINFO Rmail", l}, ""
} }
if rr.Rmail[l.length-1] != '.' { if rr.Rmail[l.length-1] != '.' {
@ -395,7 +395,7 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad MINFO Email", l}, "" return nil, &ParseError{f, "bad MINFO Email", l}, ""
} }
if rr.Email[l.length-1] != '.' { if rr.Email[l.length-1] != '.' {
@ -418,7 +418,7 @@ func setMF(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad MF Mf", l}, "" return nil, &ParseError{f, "bad MF Mf", l}, ""
} }
if rr.Mf[l.length-1] != '.' { if rr.Mf[l.length-1] != '.' {
@ -441,7 +441,7 @@ func setMD(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad MD Md", l}, "" return nil, &ParseError{f, "bad MD Md", l}, ""
} }
if rr.Md[l.length-1] != '.' { if rr.Md[l.length-1] != '.' {
@ -459,7 +459,7 @@ func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad MX Pref", l}, "" return nil, &ParseError{f, "bad MX Pref", l}, ""
} }
rr.Preference = uint16(i) rr.Preference = uint16(i)
@ -471,7 +471,7 @@ func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad MX Mx", l}, "" return nil, &ParseError{f, "bad MX Mx", l}, ""
} }
if rr.Mx[l.length-1] != '.' { if rr.Mx[l.length-1] != '.' {
@ -500,7 +500,7 @@ func setRT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad RT Host", l}, "" return nil, &ParseError{f, "bad RT Host", l}, ""
} }
if rr.Host[l.length-1] != '.' { if rr.Host[l.length-1] != '.' {
@ -518,7 +518,7 @@ func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad AFSDB Subtype", l}, "" return nil, &ParseError{f, "bad AFSDB Subtype", l}, ""
} }
rr.Subtype = uint16(i) rr.Subtype = uint16(i)
@ -530,7 +530,7 @@ func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad AFSDB Hostname", l}, "" return nil, &ParseError{f, "bad AFSDB Hostname", l}, ""
} }
if rr.Hostname[l.length-1] != '.' { if rr.Hostname[l.length-1] != '.' {
@ -544,6 +544,12 @@ func setX25(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.Hdr = h rr.Hdr = h
l := <-c l := <-c
if l.length == 0 {
return rr, nil, ""
}
if l.err {
return nil, &ParseError{f, "bad X25 PSDNAddress", l}, ""
}
rr.PSDNAddress = l.token rr.PSDNAddress = l.token
return rr, nil, "" return rr, nil, ""
} }
@ -557,7 +563,7 @@ func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad KX Pref", l}, "" return nil, &ParseError{f, "bad KX Pref", l}, ""
} }
rr.Preference = uint16(i) rr.Preference = uint16(i)
@ -569,7 +575,7 @@ func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad KX Exchanger", l}, "" return nil, &ParseError{f, "bad KX Exchanger", l}, ""
} }
if rr.Exchanger[l.length-1] != '.' { if rr.Exchanger[l.length-1] != '.' {
@ -592,7 +598,7 @@ func setCNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad CNAME Target", l}, "" return nil, &ParseError{f, "bad CNAME Target", l}, ""
} }
if rr.Target[l.length-1] != '.' { if rr.Target[l.length-1] != '.' {
@ -615,7 +621,7 @@ func setDNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad CNAME Target", l}, "" return nil, &ParseError{f, "bad CNAME Target", l}, ""
} }
if rr.Target[l.length-1] != '.' { if rr.Target[l.length-1] != '.' {
@ -638,7 +644,7 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.Ns = o rr.Ns = o
} else { } else {
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad SOA Ns", l}, "" return nil, &ParseError{f, "bad SOA Ns", l}, ""
} }
if rr.Ns[l.length-1] != '.' { if rr.Ns[l.length-1] != '.' {
@ -652,7 +658,7 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.Mbox = o rr.Mbox = o
} else { } else {
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad SOA Mbox", l}, "" return nil, &ParseError{f, "bad SOA Mbox", l}, ""
} }
if rr.Mbox[l.length-1] != '.' { if rr.Mbox[l.length-1] != '.' {
@ -667,6 +673,9 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
) )
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
l = <-c l = <-c
if l.err {
return nil, &ParseError{f, "bad SOA zone parameter", l}, ""
}
if j, e := strconv.Atoi(l.token); e != nil { if j, e := strconv.Atoi(l.token); e != nil {
if i == 0 { if i == 0 {
// Serial should be a number // Serial should be a number
@ -708,21 +717,21 @@ func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad SRV Priority", l}, "" return nil, &ParseError{f, "bad SRV Priority", l}, ""
} }
rr.Priority = uint16(i) rr.Priority = uint16(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad SRV Weight", l}, "" return nil, &ParseError{f, "bad SRV Weight", l}, ""
} }
rr.Weight = uint16(i) rr.Weight = uint16(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad SRV Port", l}, "" return nil, &ParseError{f, "bad SRV Port", l}, ""
} }
rr.Port = uint16(i) rr.Port = uint16(i)
@ -734,7 +743,7 @@ func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad SRV Target", l}, "" return nil, &ParseError{f, "bad SRV Target", l}, ""
} }
if rr.Target[l.length-1] != '.' { if rr.Target[l.length-1] != '.' {
@ -752,14 +761,14 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad NAPTR Order", l}, "" return nil, &ParseError{f, "bad NAPTR Order", l}, ""
} }
rr.Order = uint16(i) rr.Order = uint16(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad NAPTR Preference", l}, "" return nil, &ParseError{f, "bad NAPTR Preference", l}, ""
} }
rr.Preference = uint16(i) rr.Preference = uint16(i)
@ -828,7 +837,7 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad NAPTR Replacement", l}, "" return nil, &ParseError{f, "bad NAPTR Replacement", l}, ""
} }
if rr.Replacement[l.length-1] != '.' { if rr.Replacement[l.length-1] != '.' {
@ -850,7 +859,7 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.PreviousName = o rr.PreviousName = o
} else { } else {
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad TALINK PreviousName", l}, "" return nil, &ParseError{f, "bad TALINK PreviousName", l}, ""
} }
if rr.PreviousName[l.length-1] != '.' { if rr.PreviousName[l.length-1] != '.' {
@ -865,7 +874,7 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad TALINK NextName", l}, "" return nil, &ParseError{f, "bad TALINK NextName", l}, ""
} }
if rr.NextName[l.length-1] != '.' { if rr.NextName[l.length-1] != '.' {
@ -887,7 +896,7 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
if l.length == 0 { if l.length == 0 {
return rr, nil, "" return rr, nil, ""
} }
if i, e := strconv.Atoi(l.token); e != nil { if i, e := strconv.Atoi(l.token); e != nil || l.err {
return nil, &ParseError{f, "bad LOC Latitude", l}, "" return nil, &ParseError{f, "bad LOC Latitude", l}, ""
} else { } else {
rr.Latitude = 1000 * 60 * 60 * uint32(i) rr.Latitude = 1000 * 60 * 60 * uint32(i)
@ -898,14 +907,14 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
goto East goto East
} }
if i, e := strconv.Atoi(l.token); e != nil { if i, e := strconv.Atoi(l.token); e != nil || l.err {
return nil, &ParseError{f, "bad LOC Latitude minutes", l}, "" return nil, &ParseError{f, "bad LOC Latitude minutes", l}, ""
} else { } else {
rr.Latitude += 1000 * 60 * uint32(i) rr.Latitude += 1000 * 60 * uint32(i)
} }
<-c // zBlank <-c // zBlank
l = <-c l = <-c
if i, e := strconv.ParseFloat(l.token, 32); e != nil { if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
return nil, &ParseError{f, "bad LOC Latitude seconds", l}, "" return nil, &ParseError{f, "bad LOC Latitude seconds", l}, ""
} else { } else {
rr.Latitude += uint32(1000 * i) rr.Latitude += uint32(1000 * i)
@ -923,7 +932,7 @@ East:
// East // East
<-c // zBlank <-c // zBlank
l = <-c l = <-c
if i, e := strconv.Atoi(l.token); e != nil { if i, e := strconv.Atoi(l.token); e != nil || l.err {
return nil, &ParseError{f, "bad LOC Longitude", l}, "" return nil, &ParseError{f, "bad LOC Longitude", l}, ""
} else { } else {
rr.Longitude = 1000 * 60 * 60 * uint32(i) rr.Longitude = 1000 * 60 * 60 * uint32(i)
@ -934,14 +943,14 @@ East:
if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
goto Altitude goto Altitude
} }
if i, e := strconv.Atoi(l.token); e != nil { if i, e := strconv.Atoi(l.token); e != nil || l.err {
return nil, &ParseError{f, "bad LOC Longitude minutes", l}, "" return nil, &ParseError{f, "bad LOC Longitude minutes", l}, ""
} else { } else {
rr.Longitude += 1000 * 60 * uint32(i) rr.Longitude += 1000 * 60 * uint32(i)
} }
<-c // zBlank <-c // zBlank
l = <-c l = <-c
if i, e := strconv.ParseFloat(l.token, 32); e != nil { if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
return nil, &ParseError{f, "bad LOC Longitude seconds", l}, "" return nil, &ParseError{f, "bad LOC Longitude seconds", l}, ""
} else { } else {
rr.Longitude += uint32(1000 * i) rr.Longitude += uint32(1000 * i)
@ -958,6 +967,9 @@ East:
Altitude: Altitude:
<-c // zBlank <-c // zBlank
l = <-c l = <-c
if l.length == 0 || l.err {
return nil, &ParseError{f, "bad LOC Altitude", l}, ""
}
if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' {
l.token = l.token[0 : len(l.token)-1] l.token = l.token[0 : len(l.token)-1]
} }
@ -1014,17 +1026,23 @@ func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, l.comment return rr, nil, l.comment
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad HIP PublicKeyAlgorithm", l}, "" return nil, &ParseError{f, "bad HIP PublicKeyAlgorithm", l}, ""
} }
rr.PublicKeyAlgorithm = uint8(i) rr.PublicKeyAlgorithm = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
if l.length == 0 || l.err {
return nil, &ParseError{f, "bad HIP Hit", l}, ""
}
rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6.
rr.HitLength = uint8(len(rr.Hit)) / 2 rr.HitLength = uint8(len(rr.Hit)) / 2
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
if l.length == 0 || l.err {
return nil, &ParseError{f, "bad HIP PublicKey", l}, ""
}
rr.PublicKey = l.token // This cannot contain spaces rr.PublicKey = l.token // This cannot contain spaces
rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey))) rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey)))
@ -1036,10 +1054,11 @@ func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
case zString: case zString:
if l.token == "@" { if l.token == "@" {
xs = append(xs, o) xs = append(xs, o)
l = <-c
continue continue
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad HIP RendezvousServers", l}, "" return nil, &ParseError{f, "bad HIP RendezvousServers", l}, ""
} }
if l.token[l.length-1] != '.' { if l.token[l.length-1] != '.' {
@ -1075,7 +1094,7 @@ func setCERT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad CERT KeyTag", l}, "" return nil, &ParseError{f, "bad CERT KeyTag", l}, ""
} }
rr.KeyTag = uint16(i) rr.KeyTag = uint16(i)
@ -1139,21 +1158,21 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, err := strconv.Atoi(l.token) i, err := strconv.Atoi(l.token)
if err != nil { if err != nil || l.err {
return nil, &ParseError{f, "bad RRSIG Algorithm", l}, "" return nil, &ParseError{f, "bad RRSIG Algorithm", l}, ""
} }
rr.Algorithm = uint8(i) rr.Algorithm = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, err = strconv.Atoi(l.token) i, err = strconv.Atoi(l.token)
if err != nil { if err != nil || l.err {
return nil, &ParseError{f, "bad RRSIG Labels", l}, "" return nil, &ParseError{f, "bad RRSIG Labels", l}, ""
} }
rr.Labels = uint8(i) rr.Labels = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, err = strconv.Atoi(l.token) i, err = strconv.Atoi(l.token)
if err != nil { if err != nil || l.err {
return nil, &ParseError{f, "bad RRSIG OrigTtl", l}, "" return nil, &ParseError{f, "bad RRSIG OrigTtl", l}, ""
} }
rr.OrigTtl = uint32(i) rr.OrigTtl = uint32(i)
@ -1184,7 +1203,7 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, err = strconv.Atoi(l.token) i, err = strconv.Atoi(l.token)
if err != nil { if err != nil || l.err {
return nil, &ParseError{f, "bad RRSIG KeyTag", l}, "" return nil, &ParseError{f, "bad RRSIG KeyTag", l}, ""
} }
rr.KeyTag = uint16(i) rr.KeyTag = uint16(i)
@ -1195,7 +1214,7 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.SignerName = o rr.SignerName = o
} else { } else {
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad RRSIG SignerName", l}, "" return nil, &ParseError{f, "bad RRSIG SignerName", l}, ""
} }
if rr.SignerName[l.length-1] != '.' { if rr.SignerName[l.length-1] != '.' {
@ -1223,7 +1242,7 @@ func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.NextDomain = o rr.NextDomain = o
} else { } else {
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad NSEC NextDomain", l}, "" return nil, &ParseError{f, "bad NSEC NextDomain", l}, ""
} }
if rr.NextDomain[l.length-1] != '.' { if rr.NextDomain[l.length-1] != '.' {
@ -1265,27 +1284,27 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, l.comment return rr, nil, l.comment
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad NSEC3 Hash", l}, "" return nil, &ParseError{f, "bad NSEC3 Hash", l}, ""
} }
rr.Hash = uint8(i) rr.Hash = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad NSEC3 Flags", l}, "" return nil, &ParseError{f, "bad NSEC3 Flags", l}, ""
} }
rr.Flags = uint8(i) rr.Flags = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad NSEC3 Iterations", l}, "" return nil, &ParseError{f, "bad NSEC3 Iterations", l}, ""
} }
rr.Iterations = uint16(i) rr.Iterations = uint16(i)
<-c <-c
l = <-c l = <-c
if len(l.token) == 0 { if len(l.token) == 0 || l.err {
return nil, &ParseError{f, "bad NSEC3 Salt", l}, "" return nil, &ParseError{f, "bad NSEC3 Salt", l}, ""
} }
rr.SaltLength = uint8(len(l.token)) / 2 rr.SaltLength = uint8(len(l.token)) / 2
@ -1293,6 +1312,9 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
<-c <-c
l = <-c l = <-c
if len(l.token) == 0 || l.err {
return nil, &ParseError{f, "bad NSEC3 NextDomain", l}, ""
}
rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits) rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits)
rr.NextDomain = l.token rr.NextDomain = l.token
@ -1330,21 +1352,21 @@ func setNSEC3PARAM(h RR_Header, c chan lex, o, f string) (RR, *ParseError, strin
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad NSEC3PARAM Hash", l}, "" return nil, &ParseError{f, "bad NSEC3PARAM Hash", l}, ""
} }
rr.Hash = uint8(i) rr.Hash = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad NSEC3PARAM Flags", l}, "" return nil, &ParseError{f, "bad NSEC3PARAM Flags", l}, ""
} }
rr.Flags = uint8(i) rr.Flags = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad NSEC3PARAM Iterations", l}, "" return nil, &ParseError{f, "bad NSEC3PARAM Iterations", l}, ""
} }
rr.Iterations = uint16(i) rr.Iterations = uint16(i)
@ -1363,7 +1385,7 @@ func setEUI48(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
if l.length == 0 { if l.length == 0 {
return rr, nil, "" return rr, nil, ""
} }
if l.length != 17 { if l.length != 17 || l.err {
return nil, &ParseError{f, "bad EUI48 Address", l}, "" return nil, &ParseError{f, "bad EUI48 Address", l}, ""
} }
addr := make([]byte, 12) addr := make([]byte, 12)
@ -1395,7 +1417,7 @@ func setEUI64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
if l.length == 0 { if l.length == 0 {
return rr, nil, "" return rr, nil, ""
} }
if l.length != 23 { if l.length != 23 || l.err {
return nil, &ParseError{f, "bad EUI64 Address", l}, "" return nil, &ParseError{f, "bad EUI64 Address", l}, ""
} }
addr := make([]byte, 16) addr := make([]byte, 16)
@ -1428,7 +1450,7 @@ func setWKS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, l.comment return rr, nil, l.comment
} }
rr.Address = net.ParseIP(l.token) rr.Address = net.ParseIP(l.token)
if rr.Address == nil { if rr.Address == nil || l.err {
return nil, &ParseError{f, "bad WKS Address", l}, "" return nil, &ParseError{f, "bad WKS Address", l}, ""
} }
@ -1436,7 +1458,7 @@ func setWKS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
l = <-c l = <-c
proto := "tcp" proto := "tcp"
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad WKS Protocol", l}, "" return nil, &ParseError{f, "bad WKS Protocol", l}, ""
} }
rr.Protocol = uint8(i) rr.Protocol = uint8(i)
@ -1486,14 +1508,14 @@ func setSSHFP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad SSHFP Algorithm", l}, "" return nil, &ParseError{f, "bad SSHFP Algorithm", l}, ""
} }
rr.Algorithm = uint8(i) rr.Algorithm = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad SSHFP Type", l}, "" return nil, &ParseError{f, "bad SSHFP Type", l}, ""
} }
rr.Type = uint8(i) rr.Type = uint8(i)
@ -1515,21 +1537,21 @@ func setDNSKEYs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, str
return rr, nil, l.comment return rr, nil, l.comment
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad " + typ + " Flags", l}, "" return nil, &ParseError{f, "bad " + typ + " Flags", l}, ""
} }
rr.Flags = uint16(i) rr.Flags = uint16(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad " + typ + " Protocol", l}, "" return nil, &ParseError{f, "bad " + typ + " Protocol", l}, ""
} }
rr.Protocol = uint8(i) rr.Protocol = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, "" return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, ""
} }
rr.Algorithm = uint8(i) rr.Algorithm = uint8(i)
@ -1571,21 +1593,21 @@ func setRKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, l.comment return rr, nil, l.comment
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad RKEY Flags", l}, "" return nil, &ParseError{f, "bad RKEY Flags", l}, ""
} }
rr.Flags = uint16(i) rr.Flags = uint16(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad RKEY Protocol", l}, "" return nil, &ParseError{f, "bad RKEY Protocol", l}, ""
} }
rr.Protocol = uint8(i) rr.Protocol = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad RKEY Algorithm", l}, "" return nil, &ParseError{f, "bad RKEY Algorithm", l}, ""
} }
rr.Algorithm = uint8(i) rr.Algorithm = uint8(i)
@ -1619,34 +1641,6 @@ func setNIMLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, c1 return rr, nil, c1
} }
func setNSAP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr := new(NSAP)
rr.Hdr = h
chunks, e1, c1 := endingToTxtSlice(c, "bad NSAP Nsap", f)
if e1 != nil {
return nil, e1, c1
}
// data would come as one string or multiple... Just to ignore possible
// variety let's merge things back together and split to actual "words"
s := strings.Fields(strings.Join(chunks, " "))
if len(s) == 0 {
return rr, nil, c1
}
if len(s[0]) >= 2 && s[0][0:2] == "0x" || s[0][0:2] == "0X" {
// although RFC only suggests 0x there is no clarification that X is not allowed
rr.Nsap = strings.Join(s, "")[2:]
} else {
// since we do not know what to do with this data, and, we would not use original length
// in formatting, it's moot to check correctness of the length
_, err := strconv.Atoi(s[0])
if err != nil {
return nil, &ParseError{f, "bad NSAP Length", lex{token: s[0]}}, ""
}
rr.Nsap = strings.Join(s[1:], "")
}
return rr, nil, c1
}
func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr := new(GPOS) rr := new(GPOS)
rr.Hdr = h rr.Hdr = h
@ -1655,21 +1649,21 @@ func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, e := strconv.ParseFloat(l.token, 64) _, e := strconv.ParseFloat(l.token, 64)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad GPOS Longitude", l}, "" return nil, &ParseError{f, "bad GPOS Longitude", l}, ""
} }
rr.Longitude = l.token rr.Longitude = l.token
<-c // zBlank <-c // zBlank
l = <-c l = <-c
_, e = strconv.ParseFloat(l.token, 64) _, e = strconv.ParseFloat(l.token, 64)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad GPOS Latitude", l}, "" return nil, &ParseError{f, "bad GPOS Latitude", l}, ""
} }
rr.Latitude = l.token rr.Latitude = l.token
<-c // zBlank <-c // zBlank
l = <-c l = <-c
_, e = strconv.ParseFloat(l.token, 64) _, e = strconv.ParseFloat(l.token, 64)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad GPOS Altitude", l}, "" return nil, &ParseError{f, "bad GPOS Altitude", l}, ""
} }
rr.Altitude = l.token rr.Altitude = l.token
@ -1684,7 +1678,7 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string)
return rr, nil, l.comment return rr, nil, l.comment
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad " + typ + " KeyTag", l}, "" return nil, &ParseError{f, "bad " + typ + " KeyTag", l}, ""
} }
rr.KeyTag = uint16(i) rr.KeyTag = uint16(i)
@ -1692,7 +1686,7 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string)
l = <-c l = <-c
if i, e := strconv.Atoi(l.token); e != nil { if i, e := strconv.Atoi(l.token); e != nil {
i, ok := StringToAlgorithm[l.tokenUpper] i, ok := StringToAlgorithm[l.tokenUpper]
if !ok { if !ok || l.err {
return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, "" return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, ""
} }
rr.Algorithm = i rr.Algorithm = i
@ -1702,7 +1696,7 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad " + typ + " DigestType", l}, "" return nil, &ParseError{f, "bad " + typ + " DigestType", l}, ""
} }
rr.DigestType = uint8(i) rr.DigestType = uint8(i)
@ -1743,7 +1737,7 @@ func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, l.comment return rr, nil, l.comment
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad TA KeyTag", l}, "" return nil, &ParseError{f, "bad TA KeyTag", l}, ""
} }
rr.KeyTag = uint16(i) rr.KeyTag = uint16(i)
@ -1751,7 +1745,7 @@ func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
l = <-c l = <-c
if i, e := strconv.Atoi(l.token); e != nil { if i, e := strconv.Atoi(l.token); e != nil {
i, ok := StringToAlgorithm[l.tokenUpper] i, ok := StringToAlgorithm[l.tokenUpper]
if !ok { if !ok || l.err {
return nil, &ParseError{f, "bad TA Algorithm", l}, "" return nil, &ParseError{f, "bad TA Algorithm", l}, ""
} }
rr.Algorithm = i rr.Algorithm = i
@ -1761,7 +1755,7 @@ func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad TA DigestType", l}, "" return nil, &ParseError{f, "bad TA DigestType", l}, ""
} }
rr.DigestType = uint8(i) rr.DigestType = uint8(i)
@ -1781,21 +1775,21 @@ func setTLSA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, l.comment return rr, nil, l.comment
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad TLSA Usage", l}, "" return nil, &ParseError{f, "bad TLSA Usage", l}, ""
} }
rr.Usage = uint8(i) rr.Usage = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad TLSA Selector", l}, "" return nil, &ParseError{f, "bad TLSA Selector", l}, ""
} }
rr.Selector = uint8(i) rr.Selector = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad TLSA MatchingType", l}, "" return nil, &ParseError{f, "bad TLSA MatchingType", l}, ""
} }
rr.MatchingType = uint8(i) rr.MatchingType = uint8(i)
@ -1818,7 +1812,7 @@ func setRFC3597(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
rdlength, e := strconv.Atoi(l.token) rdlength, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad RFC3597 Rdata ", l}, "" return nil, &ParseError{f, "bad RFC3597 Rdata ", l}, ""
} }
@ -1876,28 +1870,32 @@ func setURI(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.Hdr = h rr.Hdr = h
l := <-c l := <-c
if l.length == 0 { if l.length == 0 { // Dynamic updates.
return rr, nil, l.comment return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad URI Priority", l}, "" return nil, &ParseError{f, "bad URI Priority", l}, ""
} }
rr.Priority = uint16(i) rr.Priority = uint16(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, e = strconv.Atoi(l.token) i, e = strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad URI Weight", l}, "" return nil, &ParseError{f, "bad URI Weight", l}, ""
} }
rr.Weight = uint16(i) rr.Weight = uint16(i)
<-c // zBlank <-c // zBlank
s, e, c1 := endingToTxtSlice(c, "bad URI Target", f) s, err, c1 := endingToTxtSlice(c, "bad URI Target", f)
if e != nil { if err != nil {
return nil, e.(*ParseError), "" return nil, err, ""
} }
rr.Target = s if len(s) > 1 {
return nil, &ParseError{f, "bad URI Target", l}, ""
}
rr.Target = s[0]
return rr, nil, c1 return rr, nil, c1
} }
@ -1923,14 +1921,14 @@ func setNID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad NID Preference", l}, "" return nil, &ParseError{f, "bad NID Preference", l}, ""
} }
rr.Preference = uint16(i) rr.Preference = uint16(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
u, err := stringToNodeID(l) u, err := stringToNodeID(l)
if err != nil { if err != nil || l.err {
return nil, err, "" return nil, err, ""
} }
rr.NodeID = u rr.NodeID = u
@ -1946,14 +1944,14 @@ func setL32(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad L32 Preference", l}, "" return nil, &ParseError{f, "bad L32 Preference", l}, ""
} }
rr.Preference = uint16(i) rr.Preference = uint16(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
rr.Locator32 = net.ParseIP(l.token) rr.Locator32 = net.ParseIP(l.token)
if rr.Locator32 == nil { if rr.Locator32 == nil || l.err {
return nil, &ParseError{f, "bad L32 Locator", l}, "" return nil, &ParseError{f, "bad L32 Locator", l}, ""
} }
return rr, nil, "" return rr, nil, ""
@ -1968,7 +1966,7 @@ func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad LP Preference", l}, "" return nil, &ParseError{f, "bad LP Preference", l}, ""
} }
rr.Preference = uint16(i) rr.Preference = uint16(i)
@ -1983,7 +1981,7 @@ func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad LP Fqdn", l}, "" return nil, &ParseError{f, "bad LP Fqdn", l}, ""
} }
if rr.Fqdn[l.length-1] != '.' { if rr.Fqdn[l.length-1] != '.' {
@ -2001,14 +1999,14 @@ func setL64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad L64 Preference", l}, "" return nil, &ParseError{f, "bad L64 Preference", l}, ""
} }
rr.Preference = uint16(i) rr.Preference = uint16(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
u, err := stringToNodeID(l) u, err := stringToNodeID(l)
if err != nil { if err != nil || l.err {
return nil, err, "" return nil, err, ""
} }
rr.Locator64 = u rr.Locator64 = u
@ -2023,7 +2021,7 @@ func setUID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad UID Uid", l}, "" return nil, &ParseError{f, "bad UID Uid", l}, ""
} }
rr.Uid = uint32(i) rr.Uid = uint32(i)
@ -2038,7 +2036,7 @@ func setGID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad GID Gid", l}, "" return nil, &ParseError{f, "bad GID Gid", l}, ""
} }
rr.Gid = uint32(i) rr.Gid = uint32(i)
@ -2065,7 +2063,7 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
i, e := strconv.Atoi(l.token) i, e := strconv.Atoi(l.token)
if e != nil { if e != nil || l.err {
return nil, &ParseError{f, "bad PX Preference", l}, "" return nil, &ParseError{f, "bad PX Preference", l}, ""
} }
rr.Preference = uint16(i) rr.Preference = uint16(i)
@ -2080,7 +2078,7 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad PX Map822", l}, "" return nil, &ParseError{f, "bad PX Map822", l}, ""
} }
if rr.Map822[l.length-1] != '.' { if rr.Map822[l.length-1] != '.' {
@ -2094,7 +2092,7 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, "" return rr, nil, ""
} }
_, ok = IsDomainName(l.token) _, ok = IsDomainName(l.token)
if !ok || l.length == 0 { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad PX Mapx400", l}, "" return nil, &ParseError{f, "bad PX Mapx400", l}, ""
} }
if rr.Mapx400[l.length-1] != '.' { if rr.Mapx400[l.length-1] != '.' {
@ -2111,21 +2109,21 @@ func setIPSECKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string)
return rr, nil, l.comment return rr, nil, l.comment
} }
i, err := strconv.Atoi(l.token) i, err := strconv.Atoi(l.token)
if err != nil { if err != nil || l.err {
return nil, &ParseError{f, "bad IPSECKEY Precedence", l}, "" return nil, &ParseError{f, "bad IPSECKEY Precedence", l}, ""
} }
rr.Precedence = uint8(i) rr.Precedence = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, err = strconv.Atoi(l.token) i, err = strconv.Atoi(l.token)
if err != nil { if err != nil || l.err {
return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, "" return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, ""
} }
rr.GatewayType = uint8(i) rr.GatewayType = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c l = <-c
i, err = strconv.Atoi(l.token) i, err = strconv.Atoi(l.token)
if err != nil { if err != nil || l.err {
return nil, &ParseError{f, "bad IPSECKEY Algorithm", l}, "" return nil, &ParseError{f, "bad IPSECKEY Algorithm", l}, ""
} }
rr.Algorithm = uint8(i) rr.Algorithm = uint8(i)
@ -2142,7 +2140,7 @@ func setIPSECKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string)
rr.GatewayName = o rr.GatewayName = o
} }
_, ok := IsDomainName(l.token) _, ok := IsDomainName(l.token)
if !ok { if !ok || l.length == 0 || l.err {
return nil, &ParseError{f, "bad IPSECKEY GatewayName", l}, "" return nil, &ParseError{f, "bad IPSECKEY GatewayName", l}, ""
} }
if rr.GatewayName[l.length-1] != '.' { if rr.GatewayName[l.length-1] != '.' {
@ -2178,12 +2176,12 @@ func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
return rr, nil, l.comment return rr, nil, l.comment
} }
i, err := strconv.Atoi(l.token) i, err := strconv.Atoi(l.token)
if err != nil { if err != nil || l.err {
return nil, &ParseError{f, "bad CAA Flag", l}, "" return nil, &ParseError{f, "bad CAA Flag", l}, ""
} }
rr.Flag = uint8(i) rr.Flag = uint8(i)
<-c // zBlank <-c // zBlank
l = <-c // zString l = <-c // zString
if l.value != zString { if l.value != zString {
return nil, &ParseError{f, "bad CAA Tag", l}, "" return nil, &ParseError{f, "bad CAA Tag", l}, ""
@ -2197,9 +2195,8 @@ func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
} }
if len(s) > 1 { if len(s) > 1 {
return nil, &ParseError{f, "bad CAA Value", l}, "" return nil, &ParseError{f, "bad CAA Value", l}, ""
} else {
rr.Value = s[0]
} }
rr.Value = s[0]
return rr, nil, c1 return rr, nil, c1
} }
@ -2242,7 +2239,6 @@ var typeToparserFunc = map[uint16]parserFunc{
TypeNID: parserFunc{setNID, false}, TypeNID: parserFunc{setNID, false},
TypeNIMLOC: parserFunc{setNIMLOC, true}, TypeNIMLOC: parserFunc{setNIMLOC, true},
TypeNINFO: parserFunc{setNINFO, true}, TypeNINFO: parserFunc{setNINFO, true},
TypeNSAP: parserFunc{setNSAP, true},
TypeNSAPPTR: parserFunc{setNSAPPTR, false}, TypeNSAPPTR: parserFunc{setNSAPPTR, false},
TypeNSEC3PARAM: parserFunc{setNSEC3PARAM, false}, TypeNSEC3PARAM: parserFunc{setNSEC3PARAM, false},
TypeNSEC3: parserFunc{setNSEC3, true}, TypeNSEC3: parserFunc{setNSEC3, true},

View File

@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
"strings"
"time" "time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
@ -88,9 +89,7 @@ func (dnsResolver *DNSResolverImpl) LookupTXT(hostname string) ([]string, time.D
for _, answer := range r.Answer { for _, answer := range r.Answer {
if answer.Header().Rrtype == dns.TypeTXT { if answer.Header().Rrtype == dns.TypeTXT {
if txtRec, ok := answer.(*dns.TXT); ok { if txtRec, ok := answer.(*dns.TXT); ok {
for _, field := range txtRec.Txt { txt = append(txt, strings.Join(txtRec.Txt, ""))
txt = append(txt, field)
}
} }
} }
} }

View File

@ -98,6 +98,13 @@ func mockDNSQuery(w dns.ResponseWriter, r *dns.Msg) {
record.Flag = 1 record.Flag = 1
appendAnswer(record) appendAnswer(record)
} }
case dns.TypeTXT:
if q.Name == "split-txt.letsencrypt.org." {
record := new(dns.TXT)
record.Hdr = dns.RR_Header{Name: "split-txt.letsencrypt.org.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}
record.Txt = []string{"a", "b", "c"}
appendAnswer(record)
}
} }
} }
@ -200,9 +207,14 @@ func TestDNSLookupTXT(t *testing.T) {
obj := NewDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr}) obj := NewDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr})
a, rtt, err := obj.LookupTXT("letsencrypt.org") a, rtt, err := obj.LookupTXT("letsencrypt.org")
t.Logf("A: %v RTT %s", a, rtt) t.Logf("A: %v RTT %s", a, rtt)
test.AssertNotError(t, err, "No message") test.AssertNotError(t, err, "No message")
a, rtt, err = obj.LookupTXT("split-txt.letsencrypt.org")
t.Logf("A: %v RTT %s", a, rtt)
test.AssertNotError(t, err, "No message")
test.AssertEquals(t, len(a), 1)
test.AssertEquals(t, a[0], "abc")
} }
func TestDNSLookupHost(t *testing.T) { func TestDNSLookupHost(t *testing.T) {

View File

@ -201,13 +201,13 @@ type Registration struct {
ID int64 `json:"id" db:"id"` ID int64 `json:"id" db:"id"`
// Account key to which the details are attached // Account key to which the details are attached
Key jose.JsonWebKey `json:"key" db:"jwk"` Key jose.JsonWebKey `json:"key"`
// Contact URIs // Contact URIs
Contact []*AcmeURL `json:"contact,omitempty" db:"contact"` Contact []*AcmeURL `json:"contact,omitempty"`
// Agreement with terms of service // Agreement with terms of service
Agreement string `json:"agreement,omitempty" db:"agreement"` Agreement string `json:"agreement,omitempty"`
} }
// MergeUpdate copies a subset of information from the input Registration // MergeUpdate copies a subset of information from the input Registration

View File

@ -38,8 +38,6 @@ func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) {
return string(t), nil return string(t), nil
case core.OCSPStatus: case core.OCSPStatus:
return string(t), nil return string(t), nil
case core.JSONBuffer:
return []byte(t), nil
default: default:
return val, nil return val, nil
} }
@ -48,7 +46,7 @@ func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) {
// FromDb converts a DB representation back into a Boulder object. // FromDb converts a DB representation back into a Boulder object.
func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) { func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
switch target.(type) { switch target.(type) {
case *core.AcmeIdentifier, *[]core.Challenge, *[]*core.AcmeURL, *[][]int, core.JSONBuffer: case *core.AcmeIdentifier, *[]core.Challenge, *[]*core.AcmeURL, *[][]int:
binder := func(holder, target interface{}) error { binder := func(holder, target interface{}) error {
s, ok := holder.(*string) s, ok := holder.(*string)
if !ok { if !ok {

View File

@ -118,7 +118,7 @@ function run_unit_tests() {
if [ "${TRAVIS}" == "true" ]; then if [ "${TRAVIS}" == "true" ]; then
# Run each test by itself for Travis, so we can get coverage # Run each test by itself for Travis, so we can get coverage
for dir in ${TESTDIRS}; do for dir in ${TESTDIRS}; do
run go test -race -covermode=count -coverprofile=${dir}.coverprofile ./${dir}/ run go test -race -cover -coverprofile=${dir}.coverprofile ./${dir}/
done done
# Gather all the coverprofiles # Gather all the coverprofiles