diff --git a/Makefile b/Makefile index ea8aedc1..e1d63e2f 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ GOFILES=\ edns.go\ keygen.go\ kscan.go\ + labels.go\ msg.go\ nsec3.go \ rawmsg.go \ diff --git a/defaults.go b/defaults.go index 3e8e4069..84b12f87 100644 --- a/defaults.go +++ b/defaults.go @@ -268,24 +268,3 @@ func Fqdn(s string) string { } return s + "." } - -// SplitLabels splits a domainname string into its labels. -// No syntax checks are performed. -// TODO: labels functions... -func SplitLabels(s string) (labels []string) { - last := byte('.') - k := 0 - l := 0 - for i := 0; i < len(s); i++ { - if s[i] == '.' { - if last == '\\' { - // do nothing - break - } - labels[l] = s[k:i] - k = i+1 // + dot - } - last = s[i] - } - return -} diff --git a/dns.go b/dns.go index 4f3e3ed4..9f5c693c 100644 --- a/dns.go +++ b/dns.go @@ -91,6 +91,7 @@ func (e *Error) Error() string { type RR interface { Header() *RR_Header String() string + Len() int } // An RRset is a slice of RRs. @@ -212,15 +213,21 @@ func (h *RR_Header) String() string { return s } +func (h *RR_Header) Len() int { + l := len(h.Name) + 1 + l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2) + return l +} + func zoneMatch(pattern, zone string) (ok bool) { if len(pattern) == 0 { return } - if len(zone) == 0 { - zone = "." - } - pattern = Fqdn(pattern) - zone = Fqdn(zone) + if len(zone) == 0 { + zone = "." + } + pattern = Fqdn(pattern) + zone = Fqdn(zone) i := 0 for { ok = pattern[len(pattern)-1-i] == zone[len(zone)-1-i] diff --git a/edns.go b/edns.go index f003167d..6f6cecf0 100644 --- a/edns.go +++ b/edns.go @@ -75,6 +75,10 @@ func (rr *RR_OPT) String() string { return s } +func (rr *RR_OPT) Len() int { + return rr.Hdr.Len() + len(rr.Option) // TODO: to small/large? +} + // TODO(mg) // Get the EDNS version (always 0 currently). func (rr *RR_OPT) Version() uint8 { diff --git a/label_test.go b/label_test.go new file mode 100644 index 00000000..4749b556 --- /dev/null +++ b/label_test.go @@ -0,0 +1,30 @@ +package dns + +import ( + "testing" +) + +func TestCompareLabels(t *testing.T) { + s1 := "www.miek.nl." + s2 := "miek.nl." + s3 := "www.bla.nl." + s4 := "nl.www.bla." + s5 := "nl" + + if CompareLabels(s1, s2) != 2 { + t.Logf("%s with %s should be %d", s1, s2, 2) + t.Fail() + } + if CompareLabels(s1, s3) != 1 { + t.Logf("%s with %s should be %d", s1, s3, 1) + t.Fail() + } + if CompareLabels(s3, s4) != 0 { + t.Logf("%s with %s should be %d", s3, s4, 0) + t.Fail() + } + if CompareLabels(s1, s5) != 1 { + t.Logf("%s with %s should be %d", s1, s5, 1) + t.Fail() + } +} diff --git a/labels.go b/labels.go new file mode 100644 index 00000000..7979dad2 --- /dev/null +++ b/labels.go @@ -0,0 +1,50 @@ +package dns + +// Holds a bunch of helper functions for dealing with labels. + +// SplitLabels splits a domainname string into its labels. +func SplitLabels(s string) []string { + last := byte('.') + k := 0 + labels := make([]string, 0) + s = Fqdn(s) // Make fully qualified + for i := 0; i < len(s); i++ { + if s[i] == '.' { + if last == '\\' { + // do nothing + break + } + labels = append(labels,s[k:i]) + k = i + 1 // + dot + } + last = s[i] + } + return labels +} + +// CompareLabels compares the strings s1 and s2 and +// returns how many labels they have in common starting from the right. +// The comparison stops at the first inequality +// +// www.miek.nl and miek.nl have two labels in common: miek and nl +// www.miek.nl and www.bla.nl have one label in common: nl +func CompareLabels(s1, s2 string) (n int) { + l1 := SplitLabels(s1) + l2 := SplitLabels(s2) + + x1 := len(l1)-1 + x2 := len(l2)-1 + for { + if x1 < 0 || x2 < 0 { + break + } + if l1[x1] == l2[x2] { + n++ + } else { + break + } + x1-- + x2-- + } + return +} diff --git a/msg.go b/msg.go index c476eba7..b2744923 100644 --- a/msg.go +++ b/msg.go @@ -463,6 +463,10 @@ func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) //fmt.Fprintf(os.Stderr, "dns: overflow packing (size-)hex string") return lenmsg, false } + if off+hex.DecodedLen(len(s)) > lenmsg { + // Overflow + return lenmsg, false + } copy(msg[off:off+hex.DecodedLen(len(s))], h) off += hex.DecodedLen(len(s)) case "size": @@ -979,11 +983,8 @@ func (dns *Msg) Pack() (msg []byte, ok bool) { dh.Nscount = uint16(len(ns)) dh.Arcount = uint16(len(extra)) - // TODO: fix this MG - // Could work harder to calculate message size, - // but this is far more than we need and not - // big enough to hurt the allocator. - msg = make([]byte, DefaultMsgSize) // TODO, calculate REAL size + // TODO: still too much, but better than 64K + msg = make([]byte, dns.Len()*6) // Pack it in: header and then the pieces. off := 0 @@ -1099,6 +1100,33 @@ func (dns *Msg) String() string { return s } +// Len return the message length (in uncompressed wirefmt). +func (dns *Msg) Len() int { + // Message header is always 12 bytes + l := 12 + if len(dns.Question) > 0 { + for i := 0; i < len(dns.Question); i++ { + l += dns.Question[i].Len() + } + } + if len(dns.Answer) > 0 { + for i := 0; i < len(dns.Answer); i++ { + l += dns.Answer[i].Len() + } + } + if len(dns.Ns) > 0 { + for i := 0; i < len(dns.Ns); i++ { + l += dns.Ns[i].Len() + } + } + if len(dns.Extra) > 0 { + for i := 0; i < len(dns.Extra); i++ { + l += dns.Extra[i].Len() + } + } + return l +} + // Id return a 16 bits random number to be used as a // message id. The random provided should be good enough. func Id() uint16 { diff --git a/types.go b/types.go index 35ac3203..6f573314 100644 --- a/types.go +++ b/types.go @@ -136,11 +136,11 @@ type Question struct { func (q *Question) String() (s string) { // prefix with ; (as in dig) - if len(q.Name) == 0 { - s = ";.\t" // root label - } else { - s = ";" + q.Name + "\t" - } + if len(q.Name) == 0 { + s = ";.\t" // root label + } else { + s = ";" + q.Name + "\t" + } s = s + Class_str[q.Qclass] + "\t" if _, ok := Rr_str[q.Qtype]; ok { s += " " + Rr_str[q.Qtype] @@ -150,6 +150,11 @@ func (q *Question) String() (s string) { return s } +func (q *Question) Len() int { + l := len(q.Name)+1 + return l + 4 +} + type RR_ANY struct { Hdr RR_Header // Does not have any rdata @@ -163,6 +168,10 @@ func (rr *RR_ANY) String() string { return rr.Hdr.String() } +func (rr *RR_ANY) Len() int { + return rr.Hdr.Len() +} + type RR_CNAME struct { Hdr RR_Header Cname string "domain-name" @@ -176,6 +185,11 @@ func (rr *RR_CNAME) String() string { return rr.Hdr.String() + rr.Cname } +func (rr *RR_CNAME) Len() int { + l := len(rr.Cname)+1 + return rr.Hdr.Len() + l +} + type RR_HINFO struct { Hdr RR_Header Cpu string @@ -190,6 +204,10 @@ func (rr *RR_HINFO) String() string { return rr.Hdr.String() + rr.Cpu + " " + rr.Os } +func (rr *RR_HINFO) Len() int { + return rr.Hdr.Len() + len(rr.Cpu) + len(rr.Os) +} + type RR_MB struct { Hdr RR_Header Mb string "domain-name" @@ -203,6 +221,11 @@ func (rr *RR_MB) String() string { return rr.Hdr.String() + rr.Mb } +func (rr *RR_MB) Len() int { + l := len(rr.Mb)+1 + return rr.Hdr.Len() + l +} + type RR_MG struct { Hdr RR_Header Mg string "domain-name" @@ -216,6 +239,11 @@ func (rr *RR_MG) String() string { return rr.Hdr.String() + rr.Mg } +func (rr *RR_MG) Len() int { + l := len(rr.Mg)+1 + return rr.Hdr.Len() + l +} + type RR_MINFO struct { Hdr RR_Header Rmail string "domain-name" @@ -230,6 +258,12 @@ func (rr *RR_MINFO) String() string { return rr.Hdr.String() + rr.Rmail + " " + rr.Email } +func (rr *RR_MINFO) Len() int { + l := len(rr.Rmail)+1 + n := len(rr.Email)+1 + return rr.Hdr.Len() + l + n +} + type RR_MR struct { Hdr RR_Header Mr string "domain-name" @@ -243,6 +277,11 @@ func (rr *RR_MR) String() string { return rr.Hdr.String() + rr.Mr } +func (rr *RR_MR) Len() int { + l := len(rr.Mr)+1 + return rr.Hdr.Len() + l +} + type RR_MX struct { Hdr RR_Header Pref uint16 @@ -257,6 +296,11 @@ func (rr *RR_MX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Pref)) + " " + rr.Mx } +func (rr *RR_MX) Len() int { + l := len(rr.Mx)+1 + return rr.Hdr.Len() + l + 2 +} + type RR_NS struct { Hdr RR_Header Ns string "domain-name" @@ -270,6 +314,11 @@ func (rr *RR_NS) String() string { return rr.Hdr.String() + rr.Ns } +func (rr *RR_NS) Len() int { + l := len(rr.Ns)+1 + return rr.Hdr.Len() + l +} + type RR_PTR struct { Hdr RR_Header Ptr string "domain-name" @@ -283,6 +332,11 @@ func (rr *RR_PTR) String() string { return rr.Hdr.String() + rr.Ptr } +func (rr *RR_PTR) Len() int { + l := len(rr.Ptr)+1 + return rr.Hdr.Len() + l +} + type RR_SOA struct { Hdr RR_Header Ns string "domain-name" @@ -307,6 +361,12 @@ func (rr *RR_SOA) String() string { " " + strconv.Itoa(int(rr.Minttl)) } +func (rr *RR_SOA) Len() int { + l := len(rr.Ns)+1 + n := len(rr.Mbox)+1 + return rr.Hdr.Len() + l + n + 20 +} + type RR_TXT struct { Hdr RR_Header Txt string "txt" @@ -320,6 +380,10 @@ func (rr *RR_TXT) String() string { return rr.Hdr.String() + "\"" + rr.Txt + "\"" } +func (rr *RR_TXT) Len() int { + return rr.Hdr.Len() + len(rr.Txt) // TODO: always works? +} + type RR_SRV struct { Hdr RR_Header Priority uint16 @@ -339,6 +403,11 @@ func (rr *RR_SRV) String() string { strconv.Itoa(int(rr.Port)) + " " + rr.Target } +func (rr *RR_SRV) Len() int { + l := len(rr.Target)+1 + return rr.Hdr.Len() + l + 6 +} + type RR_NAPTR struct { Hdr RR_Header Order uint16 @@ -363,6 +432,10 @@ func (rr *RR_NAPTR) String() string { rr.Replacement } +func (rr *RR_NAPTR) Len() int { + return rr.Hdr.Len() + 0 +} + // See RFC 4398. type RR_CERT struct { Hdr RR_Header @@ -383,6 +456,10 @@ func (rr *RR_CERT) String() string { " " + rr.Certificate } +func (rr *RR_CERT) Len() int { + return rr.Hdr.Len() + 0 +} + // See RFC 2672. type RR_DNAME struct { Hdr RR_Header @@ -397,6 +474,11 @@ func (rr *RR_DNAME) String() string { return rr.Hdr.String() + rr.Target } +func (rr *RR_DNAME) Len() int { + l := len(rr.Target)+1 + return rr.Hdr.Len() + l +} + type RR_A struct { Hdr RR_Header A net.IP "A" @@ -410,6 +492,10 @@ func (rr *RR_A) String() string { return rr.Hdr.String() + rr.A.String() } +func (rr *RR_A) Len() int { + return rr.Hdr.Len() + 4 /// 4 bytes? +} + type RR_AAAA struct { Hdr RR_Header AAAA net.IP "AAAA" @@ -423,6 +509,10 @@ func (rr *RR_AAAA) String() string { return rr.Hdr.String() + rr.AAAA.String() } +func (rr *RR_AAAA) Len() int { + return rr.Hdr.Len() + 16 +} + type RR_LOC struct { Hdr RR_Header Version uint8 @@ -443,6 +533,10 @@ func (rr *RR_LOC) String() string { return rr.Hdr.String() + "TODO" } +func (rr *RR_LOC) Len() int { + return rr.Hdr.Len() + 0 +} + type RR_RRSIG struct { Hdr RR_Header TypeCovered uint16 @@ -472,6 +566,12 @@ func (rr *RR_RRSIG) String() string { " " + rr.Signature } +func (rr *RR_RRSIG) Len() int { + l := len(rr.SignerName)+1 + return rr.Hdr.Len() + l + len(rr.Signature) + 18 + // base64 string, wordt iets minder dus +} + type RR_NSEC struct { Hdr RR_Header NextDomain string "domain-name" @@ -494,6 +594,12 @@ func (rr *RR_NSEC) String() string { return s } +func (rr *RR_NSEC) Len() int { + l := len(rr.NextDomain)+1 + return rr.Hdr.Len() + l + len(rr.TypeBitMap) + // This is also shorter due to the windowing +} + type RR_DS struct { Hdr RR_Header KeyTag uint16 @@ -513,6 +619,10 @@ func (rr *RR_DS) String() string { " " + strings.ToUpper(rr.Digest) } +func (rr *RR_DS) Len() int { + return rr.Hdr.Len() + 4 + len(rr.Digest)/2 +} + type RR_DLV struct { Hdr RR_Header KeyTag uint16 @@ -532,6 +642,10 @@ func (rr *RR_DLV) String() string { " " + strings.ToUpper(rr.Digest) } +func (rr *RR_DLV) Len() int { + return rr.Hdr.Len() + 4 + len(rr.Digest)/2 +} + type RR_KX struct { Hdr RR_Header Preference uint16 @@ -547,6 +661,10 @@ func (rr *RR_KX) String() string { " " + rr.Exchanger } +func (rr *RR_KX) Len() int { + return 0 +} + type RR_TA struct { Hdr RR_Header KeyTag uint16 @@ -566,6 +684,10 @@ func (rr *RR_TA) String() string { " " + strings.ToUpper(rr.Digest) } +func (rr *RR_TA) Len() int { + return rr.Hdr.Len() + 4 + len(rr.Digest)/2 +} + type RR_TALINK struct { Hdr RR_Header PreviousName string "domain" @@ -581,6 +703,10 @@ func (rr *RR_TALINK) String() string { " " + rr.PreviousName + " " + rr.NextName } +func (rr *RR_TALINK) Len() int { + return rr.Hdr.Len() + len(rr.PreviousName) + len(rr.NextName) + 2 +} + type RR_SSHFP struct { Hdr RR_Header Algorithm uint8 @@ -598,6 +724,10 @@ func (rr *RR_SSHFP) String() string { " " + strings.ToUpper(rr.FingerPrint) } +func (rr *RR_SSHFP) Len() int { + return rr.Hdr.Len() + 2 + len(rr.FingerPrint)/2 +} + type RR_DNSKEY struct { Hdr RR_Header Flags uint16 @@ -617,6 +747,10 @@ func (rr *RR_DNSKEY) String() string { " " + rr.PublicKey } +func (rr *RR_DNSKEY) Len() int { + return rr.Hdr.Len() + 4 + len(rr.PublicKey) // todo: base64 +} + type RR_NSEC3 struct { Hdr RR_Header Hash uint8 @@ -650,6 +784,11 @@ func (rr *RR_NSEC3) String() string { return s } +func (rr *RR_NSEC3) Len() int { + return rr.Hdr.Len() + 6 + len(rr.Salt)/2+1 + len(rr.NextDomain)+1 + len(rr.TypeBitMap) + // TODO: size-base32 and typebitmap +} + type RR_NSEC3PARAM struct { Hdr RR_Header Hash uint8 @@ -672,13 +811,8 @@ func (rr *RR_NSEC3PARAM) String() string { return s } -// saltString converts a NSECX salt to uppercase and -// returns "-" when it is empty -func saltString(s string) string { - if len(s) == 0 { - return "-" - } - return strings.ToUpper(s) +func (rr *RR_NSEC3PARAM) Len() int { + return rr.Hdr.Len() + 0 } // See RFC 4408. @@ -695,6 +829,10 @@ func (rr *RR_SPF) String() string { return rr.Hdr.String() + "\"" + rr.Txt + "\"" } +func (rr *RR_SPF) Len() int { + return rr.Hdr.Len() + 0 +} + type RR_TKEY struct { Hdr RR_Header Algorithm string "domain-name" @@ -717,6 +855,10 @@ func (rr *RR_TKEY) String() string { return "" } +func (rr *RR_TKEY) Len() int { + return rr.Hdr.Len() + 0 +} + // Unknown RR representation type RR_RFC3597 struct { Hdr RR_Header @@ -733,6 +875,10 @@ func (rr *RR_RFC3597) String() string { return s } +func (rr *RR_RFC3597) Len() int { + return rr.Hdr.Len() + len(rr.Rdata)/2 +} + type RR_URI struct { Hdr RR_Header Priority uint16 @@ -750,6 +896,10 @@ func (rr *RR_URI) String() string { " " + rr.Target } +func (rr *RR_URI) Len() int { + return rr.Hdr.Len() + 0 +} + type RR_DHCID struct { Hdr RR_Header Digest string "base64" @@ -763,6 +913,10 @@ func (rr *RR_DHCID) String() string { return rr.Hdr.String() + rr.Digest } +func (rr *RR_DHCID) Len() int { + return rr.Hdr.Len() + 0 +} + // RFC 2845. type RR_TSIG struct { Hdr RR_Header @@ -795,11 +949,15 @@ func (rr *RR_TSIG) String() string { " " + rr.OtherData } +func (rr *RR_TSIG) Len() int { + return rr.Hdr.Len() + 0 +} + // Translate the RRSIG's incep. and expir. time to the correct date. // Taking into account serial arithmetic (RFC 1982) [TODO] func timeToDate(t uint32) string { -// utc := time.Now().UTC().Unix() -// mod := (int64(t) - utc) / Year68 + // utc := time.Now().UTC().Unix() + // mod := (int64(t) - utc) / Year68 ti := time.Unix(int64(t), 0).UTC() return ti.Format("20060102150405") } @@ -813,7 +971,7 @@ func dateToTime(s string) (uint32, error) { return 0, e } mod := t.Unix() / Year68 - ti := uint32(t.Unix() - (mod * Year68)) + ti := uint32(t.Unix() - (mod * Year68)) return ti, nil } @@ -828,6 +986,15 @@ func tsigTimeToDate(t uint64) string { */ } +// saltString converts a NSECX salt to uppercase and +// returns "-" when it is empty +func saltString(s string) string { + if len(s) == 0 { + return "-" + } + return strings.ToUpper(s) +} + // Map of constructors for each RR wire type. var rr_mk = map[uint16]func() RR{ TypeCNAME: func() RR { return new(RR_CNAME) },