Merge branch 'master' of github.com:miekg/dns

This commit is contained in:
Miek Gieben 2014-01-24 09:25:37 +00:00
commit 7004ce03e8
12 changed files with 129 additions and 33 deletions

View File

@ -1,4 +1,4 @@
CONTRIBUTORS
# CONTRIBUTORS
* Miek Gieben - miek@miek.nl
* Ask Bjørn Hansen

View File

@ -1,4 +1,4 @@
# Alternative (more granular) approach to a DNS library.
# Alternative (more granular) approach to a DNS library
> Less is more.

View File

@ -105,7 +105,7 @@ func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
return r, rtt, err
}
if shared {
return r.copy(), rtt, nil
return r.Copy(), rtt, nil
}
return r, rtt, nil
}

View File

@ -24,8 +24,8 @@ type ClientConfig struct {
// ClientConfigFromFile parses a resolv.conf(5) like file and returns
// a *ClientConfig.
func ClientConfigFromFile(conf string) (*ClientConfig, error) {
file, err := os.Open(conf)
func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
file, err := os.Open(resolvconf)
if err != nil {
return nil, err
}

View File

@ -186,7 +186,7 @@ func Fqdn(s string) string {
// Copied from the official Go code.
// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
// address addr suitable for rDNS (PTR) record lookup or an error if it fails
// address suitable for reverse DNS (PTR) record lookups or an error if it fails
// to parse the IP address.
func ReverseAddr(addr string) (arpa string, err error) {
ip := net.ParseIP(addr)

2
dns.go
View File

@ -91,7 +91,7 @@ import (
const (
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
DefaultMsgSize = 4096 // Standard default for larger than 512 packets.
DefaultMsgSize = 4096 // Standard default for larger than 512 bytes.
MinMsgSize = 512 // Minimal size of a DNS packet.
MaxMsgSize = 65536 // Largest possible DNS packet.
defaultTtl = 3600 // Default TTL.

View File

@ -10,10 +10,12 @@ package dns
// www.miek.nl. returns []string{"www", "miek", "nl"}
// The root label (.) returns nil.
func SplitDomainName(s string) (labels []string) {
if len(s) == 0 {
return nil
}
fqdnEnd := 0 // offset of the final '.' or the length of the name
idx := Split(s)
begin := 0
if s[len(s)-1] == '.' {
fqdnEnd = len(s) - 1
} else {

31
msg.go
View File

@ -1290,6 +1290,12 @@ func (h *MsgHdr) String() string {
// Pack packs a Msg: it is converted to to wire format.
// If the dns.Compress is true the message will be in compressed wire format.
func (dns *Msg) Pack() (msg []byte, err error) {
return dns.PackBuffer(nil)
}
// PackBuffer packs a Msg, using the given buffer buf. If buf is too small
// a new buffer is allocated.
func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) {
var dh Header
var compression map[string]int
if dns.Compress {
@ -1335,7 +1341,11 @@ func (dns *Msg) Pack() (msg []byte, err error) {
dh.Nscount = uint16(len(ns))
dh.Arcount = uint16(len(extra))
msg = make([]byte, dns.packLen()+1)
msg = buf
if packLen := dns.packLen() + 1; len(msg) < packLen {
msg = make([]byte, packLen)
}
// Pack it in: header and then the pieces.
off := 0
off, err = packStructCompress(&dh, msg, off, compression, dns.Compress)
@ -1545,18 +1555,27 @@ func (dns *Msg) Len() int {
return l
}
func (dns *Msg) copy() *Msg {
// Copy returns a new *Msg which is a deep-copy of dns.
func (dns *Msg) Copy() *Msg {
r1 := new(Msg)
r1.MsgHdr = dns.MsgHdr
r1.Compress = dns.Compress
r1.Question = make([]Question, len(dns.Question))
copy(r1.Question, dns.Question) // TODO(miek): Question is an immutable value, ok to do a shallow-copy
r1.Answer = make([]RR, len(dns.Answer))
for i := 0; i < len(dns.Answer); i++ {
r1.Answer[i] = dns.Answer[i].copy()
}
r1.Ns = make([]RR, len(dns.Ns))
for i := 0; i < len(dns.Ns); i++ {
r1.Ns[i] = dns.Ns[i].copy()
}
r1.Extra = make([]RR, len(dns.Extra))
copy(r1.Question, dns.Question)
copy(r1.Answer, dns.Answer)
copy(r1.Ns, dns.Ns)
copy(r1.Extra, dns.Extra)
for i := 0; i < len(dns.Extra); i++ {
r1.Extra[i] = dns.Extra[i].copy()
}
return r1
}

View File

@ -179,6 +179,7 @@ func TestQuotes(t *testing.T) {
`t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a bc\"",
`t.example.com. IN TXT "a
bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a\\n bc\"",
`t.example.com. IN TXT ""`: "t.example.com.\t3600\tIN\tTXT\t\"\"",
`t.example.com. IN TXT "a"`: "t.example.com.\t3600\tIN\tTXT\t\"a\"",
`t.example.com. IN TXT "aa"`: "t.example.com.\t3600\tIN\tTXT\t\"aa\"",
`t.example.com. IN TXT "aaa" ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
@ -714,7 +715,7 @@ func TestTXT(t *testing.T) {
if rr.String() != `_raop._tcp.local. 60 IN TXT "single value"` {
t.Error("Bad representation of TXT record:", rr.String())
}
if rr.len() == 10 {
if rr.len() != 28+1+12 {
t.Error("Bad size of serialized record:", rr.len())
}
}
@ -734,10 +735,30 @@ func TestTXT(t *testing.T) {
if rr.String() != `_raop._tcp.local. 60 IN TXT "a=1" "b=2" "c=3" "d=4"` {
t.Error("Bad representation of TXT multi value record:", rr.String())
}
if rr.len() != 44 {
if rr.len() != 28+1+3+1+3+1+3+1+3 {
t.Error("Bad size of serialized multi value record:", rr.len())
}
}
// Test empty-string in TXT record
rr, err = NewRR(`_raop._tcp.local. 60 IN TXT ""`)
if err != nil {
t.Error("Failed to parse empty-string TXT record", err)
} else if rr, ok := rr.(*TXT); !ok {
t.Error("Wrong type, record should be of type TXT")
} else {
if len(rr.Txt) != 1 {
t.Error("Bad size of TXT empty-string value:", len(rr.Txt))
} else if rr.Txt[0] != "" {
t.Error("Bad value for empty-string TXT record")
}
if rr.String() != `_raop._tcp.local. 60 IN TXT ""` {
t.Error("Bad representation of empty-string TXT record:", rr.String())
}
if rr.len() != 28+1 {
t.Error("Bad size of serialized record:", rr.len())
}
}
}
func TestTypeXXXX(t *testing.T) {
@ -792,3 +813,29 @@ func TestDigit(t *testing.T) {
}
}
}
func TestParseRRSIGTimestamp(t *testing.T) {
tests := map[string]bool{
`miek.nl. IN RRSIG SOA 8 2 43200 20140210031301 20140111031301 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2BvuNiUJjSYlJAgzyAE6CF875BMvvZa+Sb0 RlSCL7WODQSQHhCx/fegHhVVF+Iz8N8kOLrmXD1+jO3Bm6Prl5UhcsPx WTBsg/kmxbp8sR1kvH4oZJtVfakG3iDerrxNaf0sQwhZzyfJQAqpC7pcBoc=`: true,
`miek.nl. IN RRSIG SOA 8 2 43200 315565800 4102477800 12051 miek.nl. MVZUyrYwq0iZhMFDDnVXD2BvuNiUJjSYlJAgzyAE6CF875BMvvZa+Sb0 RlSCL7WODQSQHhCx/fegHhVVF+Iz8N8kOLrmXD1+jO3Bm6Prl5UhcsPx WTBsg/kmxbp8sR1kvH4oZJtVfakG3iDerrxNaf0sQwhZzyfJQAqpC7pcBoc=`: true,
}
for r, _ := range tests {
_, e := NewRR(r)
if e != nil {
t.Fail()
t.Logf("%s\n", e.Error())
}
}
}
func TestTxtEqual(t *testing.T) {
rr1 := new(TXT)
rr1.Hdr = RR_Header{Name: ".", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
rr1.Txt = []string{"a\"a", "\"", "b"}
rr2, _ := NewRR(rr1.String())
if rr1.String() != rr2.String() {
// t.Fail() // This is not an error, but keep this test.
t.Logf("These two TXT records should match")
t.Logf("\n%s\n%s\n", rr1.String(), rr2.String())
}
}

19
tsig.go
View File

@ -29,7 +29,7 @@
// t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
// m.SetAxfr("miek.nl.")
// m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
// c, err := tr.In(m, "176.58.119.54:53")
// c, err := t.In(m, "176.58.119.54:53")
// for r := range c { /* r.RR */ }
//
// You can now read the records from the transfer as they come in. Each envelope is checked with TSIG.
@ -217,12 +217,17 @@ func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
if err != nil {
return err
}
// Srtip the TSIG from the incoming msg
// Strip the TSIG from the incoming msg
stripped, tsig, err := stripTsig(msg)
if err != nil {
return err
}
msgMAC, err := hex.DecodeString(tsig.MAC)
if err != nil {
return err
}
buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
ti := uint64(time.Now().Unix()) - tsig.TimeSigned
if uint64(tsig.Fudge) < ti {
@ -232,16 +237,16 @@ func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
var h hash.Hash
switch tsig.Algorithm {
case HmacMD5:
h = hmac.New(md5.New, []byte(rawsecret))
h = hmac.New(md5.New, rawsecret)
case HmacSHA1:
h = hmac.New(sha1.New, []byte(rawsecret))
h = hmac.New(sha1.New, rawsecret)
case HmacSHA256:
h = hmac.New(sha256.New, []byte(rawsecret))
h = hmac.New(sha256.New, rawsecret)
default:
return ErrKeyAlg
}
io.WriteString(h, string(buf))
if strings.ToUpper(hex.EncodeToString(h.Sum(nil))) != strings.ToUpper(tsig.MAC) {
h.Write(buf)
if !hmac.Equal(h.Sum(nil), msgMAC) {
return ErrSig
}
return nil

View File

@ -586,7 +586,7 @@ type A struct {
}
func (rr *A) Header() *RR_Header { return &rr.Hdr }
func (rr *A) copy() RR { return &A{*rr.Hdr.copyHeader(), rr.A} }
func (rr *A) copy() RR { return &A{*rr.Hdr.copyHeader(), copyIP(rr.A)} }
func (rr *A) len() int { return rr.Hdr.len() + net.IPv4len }
func (rr *A) String() string {
@ -602,7 +602,7 @@ type AAAA struct {
}
func (rr *AAAA) Header() *RR_Header { return &rr.Hdr }
func (rr *AAAA) copy() RR { return &AAAA{*rr.Hdr.copyHeader(), rr.AAAA} }
func (rr *AAAA) copy() RR { return &AAAA{*rr.Hdr.copyHeader(), copyIP(rr.AAAA)} }
func (rr *AAAA) len() int { return rr.Hdr.len() + net.IPv6len }
func (rr *AAAA) String() string {
@ -1264,7 +1264,7 @@ func (rr *WKS) len() int { return rr.Hdr.len() + net.IPv4len + 1 }
func (rr *WKS) copy() RR {
cp := make([]uint16, len(rr.BitMap), cap(rr.BitMap))
copy(cp, rr.BitMap)
return &WKS{*rr.Hdr.copyHeader(), rr.Address, rr.Protocol, cp}
return &WKS{*rr.Hdr.copyHeader(), copyIP(rr.Address), rr.Protocol, cp}
}
func (rr *WKS) String() (s string) {
@ -1303,7 +1303,7 @@ type L32 struct {
}
func (rr *L32) Header() *RR_Header { return &rr.Hdr }
func (rr *L32) copy() RR { return &L32{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator32} }
func (rr *L32) copy() RR { return &L32{*rr.Hdr.copyHeader(), rr.Preference, copyIP(rr.Locator32)} }
func (rr *L32) len() int { return rr.Hdr.len() + net.IPv4len }
func (rr *L32) String() string {
@ -1502,12 +1502,19 @@ func euiToString(eui uint64, bits int) (hex string) {
return
}
// copyIP returns a copy of ip.
func copyIP(ip net.IP) net.IP {
p := make(net.IP, len(ip))
copy(p, ip)
return p
}
// Map of constructors for each RR wire type.
var rr_mk = map[uint16]func() RR{
TypeA: func() RR { return new(A) },
TypeAAAA: func() RR { return new(AAAA) },
TypeAFSDB: func() RR { return new(AFSDB) },
// TypeCAA: func() RR { return new(CAA) },
TypeA: func() RR { return new(A) },
TypeAAAA: func() RR { return new(AAAA) },
TypeAFSDB: func() RR { return new(AFSDB) },
// TypeCAA: func() RR { return new(CAA) },
TypeCDS: func() RR { return new(CDS) },
TypeCERT: func() RR { return new(CERT) },
TypeCNAME: func() RR { return new(CNAME) },

View File

@ -223,9 +223,11 @@ func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, stri
switch l.value == _QUOTE {
case true: // A number of quoted string
s = make([]string, 0)
empty := true
for l.value != _NEWLINE && l.value != _EOF {
switch l.value {
case _STRING:
empty = false
s = append(s, l.token)
case _BLANK:
if quote {
@ -233,7 +235,11 @@ func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, stri
return nil, &ParseError{f, errstr, l}, ""
}
case _QUOTE:
if empty && quote {
s = append(s, "")
}
quote = !quote
empty = true
default:
return nil, &ParseError{f, errstr, l}, ""
}
@ -1162,14 +1168,24 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
<-c // _BLANK
l = <-c
if i, err := StringToTime(l.token); err != nil {
return nil, &ParseError{f, "bad RRSIG Expiration", l}, ""
// Try to see if all numeric and use it as epoch
if i, err := strconv.ParseInt(l.token, 10, 64); err == nil {
// TODO(miek): error out on > MAX_UINT32, same below
rr.Expiration = uint32(i)
} else {
return nil, &ParseError{f, "bad RRSIG Expiration", l}, ""
}
} else {
rr.Expiration = i
}
<-c // _BLANK
l = <-c
if i, err := StringToTime(l.token); err != nil {
return nil, &ParseError{f, "bad RRSIG Inception", l}, ""
if i, err := strconv.ParseInt(l.token, 10, 64); err == nil {
rr.Inception = uint32(i)
} else {
return nil, &ParseError{f, "bad RRSIG Inception", l}, ""
}
} else {
rr.Inception = i
}