From 15dd65171b7ba9e5554f51969cb785b6d407251a Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Thu, 30 Dec 2010 13:42:52 +0100 Subject: [PATCH] Split the package in multiple packages dns - the standard rrtypes and such dnssec - validation, keytag calculation, etc resolver - for talking to servers --- Makefile | 18 ++- dns.go | 107 ++++++++++++++ dnssec/Makefile | 11 ++ dnssec.go => dnssec/dnssec.go | 107 +++++++------- dnssec_test.go => dnssec/dnssec_test.go | 20 +-- ds_test.go => dnssec/ds_test.go | 11 +- dnskey_test.go => dnssec/keytag_test.go | 11 +- signature_test.go => dnssec/signature_test.go | 14 +- edns_test.go | 2 +- examples/chaos.go | 19 +-- examples/dnssectest.go | 20 +-- examples/mx.go | 9 +- msg.go | 4 +- pack_test.go | 35 ----- resolver/Makefile | 12 ++ resolver.go => resolver/resolver.go | 13 +- .../resolverEdns_test.go | 17 ++- resolver_test.go => resolver/resolver_test.go | 15 +- types.go | 137 +++++------------- 19 files changed, 311 insertions(+), 271 deletions(-) create mode 100644 dns.go create mode 100644 dnssec/Makefile rename dnssec.go => dnssec/dnssec.go (67%) rename dnssec_test.go => dnssec/dnssec_test.go (77%) rename ds_test.go => dnssec/ds_test.go (77%) rename dnskey_test.go => dnssec/keytag_test.go (76%) rename signature_test.go => dnssec/signature_test.go (82%) create mode 100644 resolver/Makefile rename resolver.go => resolver/resolver.go (95%) rename resolverEdns_test.go => resolver/resolverEdns_test.go (76%) rename resolver_test.go => resolver/resolver_test.go (61%) diff --git a/Makefile b/Makefile index b7725f07..88cf8c34 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,28 @@ # Copyright 2009 The Go Authors. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. +.PHONY: dnssec resolver examples include $(GOROOT)/src/Make.inc TARG=dns GOFILES=\ + dns.go\ msg.go\ - resolver.go \ types.go\ edns.go\ - dnssec.go\ include $(GOROOT)/src/Make.pkg -.PHONY: examples +all: package + gomake -C dnssec package + gomake -C resolver package -examples: - (cd examples; make) +install: $(INSTALLFILES) + gomake -C dnssec install + gomake -C resolver install + +dnstest: + gotest + gomake -C dnssec test + gomake -C resolver test diff --git a/dns.go b/dns.go new file mode 100644 index 00000000..fd2e2759 --- /dev/null +++ b/dns.go @@ -0,0 +1,107 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Extended and bugfixes by Miek Gieben + +// Package dns implements a full featured interface to the DNS. +// Supported RFCs and features include: +// * 1034/1035 +// * 2671 - EDNS +// * 4033/4034/4035 - DNSSEC + validation functions +// * 1982 - Serial Arithmetic +// * IP6 support +// The package allows full control over what is send out to the DNS. +// +// Basic usage pattern for creating new Resource Record: +// +// r := new(RR_TXT) +// r.TXT = "This is the content of the TXT record" +// r.Hdr = RR_Header{Name: "a.miek.nl", Rrtype: TypeTXT, Class: ClassINET, Ttl: 3600} +// +package dns + +import ( "strconv") + +const Year68 = 2 << (32 - 1) + +type RR interface { + Header() *RR_Header + String() string +} + +// An RRset is a slice of RRs. +type RRset []RR + +func (r RRset) Len() int { return len(r) } +func (r RRset) Less(i, j int) bool { return r[i].Header().Name < r[j].Header().Name } +func (r RRset) Swap(i, j int) { r[i], r[j] = r[j], r[i] } + +// DNS resource records. +// There are many types of messages, +// but they all share the same header. +type RR_Header struct { + Name string "domain-name" + Rrtype uint16 + Class uint16 + Ttl uint32 + Rdlength uint16 // length of data after header +} + +func (h *RR_Header) Header() *RR_Header { + return h +} + +func (h *RR_Header) String() string { + var s string + + if h.Rrtype == TypeOPT { + s = ";" + // and maybe other things + } + + if len(h.Name) == 0 { + s += ".\t" + } else { + s += h.Name + "\t" + } + s = s + strconv.Itoa(int(h.Ttl)) + "\t" + s = s + class_str[h.Class] + "\t" + s = s + rr_str[h.Rrtype] + "\t" + return s +} + +// Or expose the pack/unpack functions?? + +// Return the rdata of the RR in wireform. +func WireRdata(r RR) ([]byte, bool) { + buf := make([]byte, 4096) // Too large, need to FIX TODO(mg) + off1, ok := packRR(r, buf, 0) + if !ok { + return nil, false + } + start := off1 - int(r.Header().Rdlength) + end := start + int(r.Header().Rdlength) + buf = buf[start:end] + return buf, true +} + +// Return the wiredata of a domainname (sans compressions) +func WireDomainName(s string) ([]byte, bool) { + buf := make([]byte, 255) + off, ok := packDomainName(s, buf, 0) + if !ok { + return nil, ok + } + buf = buf[:off] + return buf, ok +} + +func WireRR(r RR) ([]byte, bool) { + buf := make([]byte, 4096) + off, ok := packRR(r, buf, 0) + if !ok { + return nil, false + } + buf = buf[:off] + return buf, ok +} diff --git a/dnssec/Makefile b/dnssec/Makefile new file mode 100644 index 00000000..788b9058 --- /dev/null +++ b/dnssec/Makefile @@ -0,0 +1,11 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include $(GOROOT)/src/Make.inc + +TARG=dns/dnssec +GOFILES=\ + dnssec.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/dnssec.go b/dnssec/dnssec.go similarity index 67% rename from dnssec.go rename to dnssec/dnssec.go index 124f78f8..6d564427 100644 --- a/dnssec.go +++ b/dnssec/dnssec.go @@ -1,4 +1,4 @@ -package dns +package dnssec import ( "crypto/sha1" @@ -13,47 +13,46 @@ import ( "strings" "fmt" //tmp "os" //tmp + "dns" ) +// DNSSEC encryption algorithm codes. const ( - // RFC1982 serial arithmetic - year68 = 2 << (32 - 1) + // DNSSEC algorithms + AlgRSAMD5 = 1 + AlgDH = 2 + AlgDSA = 3 + AlgECC = 4 + AlgRSASHA1 = 5 + AlgRSASHA256 = 8 + AlgRSASHA512 = 10 + AlgECCGOST = 12 ) -// An RRset is just a bunch a RRs. No restrictions -type RRset []RR - -func (r RRset) Len() int { return len(r) } -func (r RRset) Less(i, j int) bool { return r[i].Header().Name < r[j].Header().Name } -func (r RRset) Swap(i, j int) { r[i], r[j] = r[j], r[i] } +// DNSSEC hashing codes. +const ( + HashSHA1 = iota + HashSHA256 + HashGOST94 +) // Convert an DNSKEY record to a DS record. -func (k *RR_DNSKEY) ToDS(hash int) *RR_DS { - ds := new(RR_DS) +func ToDS(k *dns.RR_DNSKEY, hash int) *dns.RR_DS { + ds := new(dns.RR_DS) ds.Hdr.Name = k.Hdr.Name ds.Hdr.Class = k.Hdr.Class ds.Hdr.Ttl = k.Hdr.Ttl - ds.Hdr.Rrtype = TypeDS - ds.KeyTag = k.KeyTag() ds.Algorithm = k.Algorithm ds.DigestType = uint8(hash) + ds.KeyTag = KeyTag(k) - // Generic function that gives back a buffer with the rdata?? TODO(MG) - // Find the rdata portion for the key (again) - // (keytag does this too) - buf := make([]byte, 4096) - off1, ok := packRR(k, buf, 0) + wire, ok := dns.WireRdata(k) if !ok { return nil } - start := off1 - int(k.Header().Rdlength) - end := start + int(k.Header().Rdlength) - // buf[start:end] is the rdata of the key - buf = buf[start:end] - owner := make([]byte, 255) - off1, ok = packDomainName(k.Hdr.Name, owner, 0) - if !ok { + owner,ok1 := dns.WireDomainName(k.Hdr.Name) + if !ok1 { return nil } /* @@ -62,9 +61,8 @@ func (k *RR_DNSKEY) ToDS(hash int) *RR_DS { * "|" denotes concatenation * DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. */ - owner = owner[:off1] // digest buffer - digest := append(owner, buf...) + digest := append(owner, wire...) // another copy TODO(mg) switch hash { case HashSHA1: @@ -85,7 +83,7 @@ func (k *RR_DNSKEY) ToDS(hash int) *RR_DS { } // Calculate the keytag of the DNSKEY. -func (k *RR_DNSKEY) KeyTag() uint16 { +func KeyTag(k *dns.RR_DNSKEY) uint16 { var keytag int switch k.Algorithm { case AlgRSAMD5: @@ -95,7 +93,7 @@ func (k *RR_DNSKEY) KeyTag() uint16 { // Might encode header length too, so that // we dont need to pack/unpack all the time // Or a shadow structure, with the wiredata and header - wire, ok := wireRdata(k) + wire, ok := dns.WireRdata(k) if !ok { return 0 } @@ -114,11 +112,11 @@ func (k *RR_DNSKEY) KeyTag() uint16 { // Validate an rrset with the signature and key. This is the // cryptographic test, the validity period most be check separately. -func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool { +func Verify(s *dns.RR_RRSIG, k *dns.RR_DNSKEY, rrset dns.RRset) bool { // Frist the easy checks - if s.KeyTag != k.KeyTag() { + if s.KeyTag != KeyTag(k) { println(s.KeyTag) - println(k.KeyTag()) + println(KeyTag(k)) return false } if s.Hdr.Class != k.Hdr.Class { @@ -149,8 +147,8 @@ func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool { signeddata := make([]byte, 10240) // 10 Kb?? // Copy the sig, except the rrsig data // Can this be done easier? TODO(mg) - s1 := &RR_RRSIG{s.Hdr, s.TypeCovered, s.Algorithm, s.Labels, s.OrigTtl, s.Expiration, s.Inception, s.KeyTag, s.SignerName, ""} - buf, ok := wireRdata(s1) + s1 := &dns.RR_RRSIG{s.Hdr, s.TypeCovered, s.Algorithm, s.Labels, s.OrigTtl, s.Expiration, s.Inception, s.KeyTag, s.SignerName, ""} + buf, ok := dns.WireRdata(s1) if !ok { return false } @@ -165,10 +163,10 @@ func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool { h.Name = strings.ToLower(h.Name) // 6.2. Canonical RR Form. (3) - domain rdata to lowercaser switch h.Rrtype { - case TypeNS, TypeCNAME, TypeSOA, TypeMB, TypeMG, TypeMR, TypePTR: - case TypeHINFO, TypeMINFO, TypeMX /* TypeRP, TypeAFSDB, TypeRT */ : - case TypeSIG /* TypePX, TypeNXT /* TypeNAPTR, TypeKX */ : - case TypeSRV, /* TypeDNAME, TypeA6 */ TypeRRSIG, TypeNSEC: + case dns.TypeNS, dns.TypeCNAME, dns.TypeSOA, dns.TypeMB, dns.TypeMG, dns.TypeMR, dns.TypePTR: + case dns.TypeHINFO, dns.TypeMINFO, dns.TypeMX /* dns.TypeRP, dns.TypeAFSDB, dns.TypeRT */ : + case dns.TypeSIG /* dns.TypePX, dns.TypeNXT /* dns.TypeNAPTR, dns.TypeKX */ : + case dns.TypeSRV, /* dns.TypeDNAME, dns.TypeA6 */ dns.TypeRRSIG, dns.TypeNSEC: /* do something */ // lower case the strings rdata // @@ -177,10 +175,11 @@ func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool { // 6.2. Canonical RR Form. (5) - origTTL ttl := h.Ttl h.Ttl = s.OrigTtl - off, ok = packRR(r, signeddata, off) + wire, ok1 := dns.WireRR(r) h.Ttl = ttl // restore the order in the universe h.Name = name - if !ok { + wire = wire // fix this + if !ok1 { println("Failure to pack") return false } @@ -223,22 +222,22 @@ func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool { } // Using RFC1982 calculate if a signature period is valid -func (s *RR_RRSIG) PeriodOK() bool { +func PeriodOK(s *dns.RR_RRSIG) bool { utc := time.UTC().Seconds() - modi := (int64(s.Inception) - utc) / year68 - mode := (int64(s.Expiration) - utc) / year68 - ti := int64(s.Inception) + (modi * year68) - te := int64(s.Expiration) + (mode * year68) + modi := (int64(s.Inception) - utc) / dns.Year68 + mode := (int64(s.Expiration) - utc) / dns.Year68 + ti := int64(s.Inception) + (modi * dns.Year68) + te := int64(s.Expiration) + (mode * dns.Year68) return ti <= utc && utc <= te } -// Translate the RRSIG's incep. and expir. time to the correct date. -// Taking into account serial arithmetic (RFC 1982) -func timeToDate(t uint32) string { - utc := time.UTC().Seconds() - mod := (int64(t) - utc) / year68 - - // If needed assume wrap around(s) - ti := time.SecondsToUTC(int64(t) + (mod * year68)) // abs()? TODO - return ti.Format("20060102030405") +// Map for algorithm names. +var alg_str = map[uint8]string{ + AlgRSAMD5: "RSAMD5", + AlgDH: "DH", + AlgDSA: "DSA", + AlgRSASHA1: "RSASHA1", + AlgRSASHA256: "RSASHA256", + AlgRSASHA512: "RSASHA512", + AlgECCGOST: "ECC-GOST", } diff --git a/dnssec_test.go b/dnssec/dnssec_test.go similarity index 77% rename from dnssec_test.go rename to dnssec/dnssec_test.go index 7a2a19d3..81b7f8b1 100644 --- a/dnssec_test.go +++ b/dnssec/dnssec_test.go @@ -1,15 +1,16 @@ -package dns +package dnssec import ( "testing" "fmt" "os" + "dns" ) func TestSecure(t *testing.T) { // once this was valid - soa := new(RR_SOA) - soa.Hdr = RR_Header{"Miek.nl.", TypeSOA, ClassINET, 875, 0} + soa := new(dns.RR_SOA) + soa.Hdr = dns.RR_Header{"Miek.nl.", dns.TypeSOA, dns.ClassINET, 875, 0} soa.Ns = "open.nlnetlabs.nl." soa.Mbox = "miekg.atoom.net." soa.Serial = 1293513905 @@ -18,9 +19,9 @@ func TestSecure(t *testing.T) { soa.Expire = 604800 soa.Minttl = 86400 - sig := new(RR_RRSIG) - sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} - sig.TypeCovered = TypeSOA + sig := new(dns.RR_RRSIG) + sig.Hdr = dns.RR_Header{"miek.nl.", dns.TypeRRSIG, dns.ClassINET, 14400, 0} + sig.TypeCovered = dns.TypeSOA sig.Algorithm = AlgRSASHA256 sig.Labels = 2 sig.Expiration = 1296098705 // date '+%s' -d"2011-01-27 04:25:05 @@ -30,10 +31,9 @@ func TestSecure(t *testing.T) { sig.SignerName = "miek.nl." sig.Signature = "kLq/5oFy3Sh5ZxPGFMCyHq8MtN6E17R1Ln9+bJ2Q76YYAxFE8Xlie33A1GFctH2uhzRzJKuP/JSjUkrvGk2rjBm32z9zXtZsKx/4yV0da2nLRm44NOmX6gsP4Yia8mdqPUajjkyLzAzU2bevtesJm0Z65AcmPdq3tUZODdRAcng=" - key := new(RR_DNSKEY) + key := new(dns.RR_DNSKEY) key.Hdr.Name = "miek.nl." - key.Hdr.Rrtype = TypeDNSKEY - key.Hdr.Class = ClassINET + key.Hdr.Class = dns.ClassINET key.Hdr.Ttl = 14400 key.Flags = 256 key.Protocol = 3 @@ -42,7 +42,7 @@ func TestSecure(t *testing.T) { fmt.Fprintf(os.Stderr, "%v\n%v\n", sig, soa) // It should validate, at least this month dec 2010 - if ! sig.Verify([]RR{soa}, key) { + if ! Verify(sig, key, []dns.RR{soa}) { t.Log("Failure to validate") t.Fail() } diff --git a/ds_test.go b/dnssec/ds_test.go similarity index 77% rename from ds_test.go rename to dnssec/ds_test.go index 572e4369..43ef73a5 100644 --- a/ds_test.go +++ b/dnssec/ds_test.go @@ -1,22 +1,23 @@ -package dns +package dnssec import ( "testing" "strings" + "dns" ) func TestKeyToDS(t *testing.T) { - key := new(RR_DNSKEY) + key := new(dns.RR_DNSKEY) key.Hdr.Name = "miek.nl" - key.Hdr.Rrtype = TypeDNSKEY - key.Hdr.Class = ClassINET + key.Hdr.Rrtype = dns.TypeDNSKEY + key.Hdr.Class = dns.ClassINET key.Hdr.Ttl = 3600 key.Flags = 256 key.Protocol = 3 key.Algorithm = AlgRSASHA256 key.PubKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz" - ds := key.ToDS(HashSHA1) + ds := ToDS(key, HashSHA1) if strings.ToUpper(ds.Digest) != "B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1" { t.Logf("Wrong DS digest for Sha1\n%v\n", ds) t.Fail() diff --git a/dnskey_test.go b/dnssec/keytag_test.go similarity index 76% rename from dnskey_test.go rename to dnssec/keytag_test.go index b1280678..27073bf9 100644 --- a/dnskey_test.go +++ b/dnssec/keytag_test.go @@ -1,21 +1,22 @@ -package dns +package dnssec import ( "testing" + "dns" ) func TestTag(t *testing.T) { - key := new(RR_DNSKEY) + key := new(dns.RR_DNSKEY) key.Hdr.Name = "miek.nl" - key.Hdr.Rrtype = TypeDNSKEY - key.Hdr.Class = ClassINET + key.Hdr.Rrtype = dns.TypeDNSKEY + key.Hdr.Class = dns.ClassINET key.Hdr.Ttl = 3600 key.Flags = 256 key.Protocol = 3 key.Algorithm = AlgRSASHA256 key.PubKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz" - tag := key.KeyTag() + tag := KeyTag(key) if tag != 12051 { t.Logf("%v\n", key) t.Logf("Wrong key tag: %d\n", tag) diff --git a/signature_test.go b/dnssec/signature_test.go similarity index 82% rename from signature_test.go rename to dnssec/signature_test.go index 9f471d6b..4082f407 100644 --- a/signature_test.go +++ b/dnssec/signature_test.go @@ -1,16 +1,16 @@ -package dns +package dnssec import ( + "dns" "testing" ) func TestSignature(t *testing.T) { - sig := new(RR_RRSIG) + sig := new(dns.RR_RRSIG) sig.Hdr.Name = "miek.nl." - sig.Hdr.Rrtype = TypeRRSIG - sig.Hdr.Class = ClassINET + sig.Hdr.Class = dns.ClassINET sig.Hdr.Ttl = 3600 - sig.TypeCovered = TypeDNSKEY + sig.TypeCovered = dns.TypeDNSKEY sig.Algorithm = AlgRSASHA1 sig.Labels = 2 sig.OrigTtl = 4000 @@ -21,14 +21,14 @@ func TestSignature(t *testing.T) { sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ" // Should not be valid - if sig.PeriodOK() { + if PeriodOK(sig) { t.Log("Should not be valid") t.Fail() } sig.Inception = 315565800 //Tue Jan 1 10:10:00 CET 1980 sig.Expiration = 4102477800 //Fri Jan 1 10:10:00 CET 2100 - if ! sig.PeriodOK() { + if ! PeriodOK(sig) { t.Log("Should be valid") t.Fail() } diff --git a/edns_test.go b/edns_test.go index 8e1fd0ee..bb7c2b01 100644 --- a/edns_test.go +++ b/edns_test.go @@ -8,7 +8,7 @@ import ( func TestEDNS_RR(t *testing.T) { edns := new(RR_OPT) edns.Hdr.Name = "." // must . be for edns - edns.Hdr.Rrtype = TypeOPT + edns.Hdr.Rrtype = TypeOPT edns.Hdr.Class = ClassINET edns.Hdr.Ttl = 3600 edns.Option = make([]Option, 1) diff --git a/examples/chaos.go b/examples/chaos.go index e44c259a..f426c55d 100644 --- a/examples/chaos.go +++ b/examples/chaos.go @@ -5,18 +5,19 @@ package main // (c) Miek Gieben - 2011 import ( "dns" + "dns/resolver" "os" "fmt" "net" ) func main() { - r := new(dns.Resolver) - qr := dns.NewQuerier(r) + r := new(resolver.Resolver) + qr := resolver.NewQuerier(r) r.Servers = []string{"127.0.0.1"} r.Timeout = 2 r.Attempts = 1 - var in dns.DnsMsg + var in resolver.DnsMsg if len(os.Args) != 2 { fmt.Printf("%s NAMESERVER\n", os.Args[0]) @@ -29,13 +30,13 @@ func main() { // set the resolver to query the NS directly r.Servers = []string{a.String()} m.Question[0] = dns.Question{"version.bind.", dns.TypeTXT, dns.ClassCHAOS} - qr <- dns.DnsMsg{m, nil} + qr <- resolver.DnsMsg{m, nil} in = <-qr if in.Dns != nil && in.Dns.Answer != nil { fmt.Printf("%v\n", in.Dns.Answer[0]) } m.Question[0] = dns.Question{"hostname.bind.", dns.TypeTXT, dns.ClassCHAOS} - qr <- dns.DnsMsg{m, nil} + qr <- resolver.DnsMsg{m, nil} in = <-qr if in.Dns != nil && in.Dns.Answer != nil { fmt.Printf("%v\n", in.Dns.Answer[0]) @@ -43,18 +44,18 @@ func main() { } // Stop the resolver, send it a null mesg - qr <- dns.DnsMsg{nil, nil} + qr <- resolver.DnsMsg{nil, nil} <-qr } -func addresses(qr chan dns.DnsMsg, name string) []net.IP { +func addresses(qr chan resolver.DnsMsg, name string) []net.IP { m := new(dns.Msg) m.MsgHdr.Recursion_desired = true //only set this bit m.Question = make([]dns.Question, 1) var ips []net.IP m.Question[0] = dns.Question{os.Args[1], dns.TypeA, dns.ClassINET} - qr <- dns.DnsMsg{m, nil} + qr <- resolver.DnsMsg{m, nil} in := <-qr if in.Dns.Rcode != dns.RcodeSuccess { @@ -65,7 +66,7 @@ func addresses(qr chan dns.DnsMsg, name string) []net.IP { ips = append(ips, a.(*dns.RR_A).A) } m.Question[0] = dns.Question{os.Args[1], dns.TypeAAAA, dns.ClassINET} - qr <- dns.DnsMsg{m, nil} + qr <- resolver.DnsMsg{m, nil} in = <-qr if in.Dns.Rcode != dns.RcodeSuccess { diff --git a/examples/dnssectest.go b/examples/dnssectest.go index 58ea2933..7de2bdaf 100644 --- a/examples/dnssectest.go +++ b/examples/dnssectest.go @@ -2,6 +2,8 @@ package main import ( "dns" + "dns/dnssec" + "dns/resolver" "fmt" ) @@ -15,7 +17,7 @@ func main() { key.Hdr.Ttl = 3600 key.Flags = 257 key.Protocol = 3 - key.Algorithm = dns.AlgRSASHA1 + key.Algorithm = dnssec.AlgRSASHA1 key.PubKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFq NDzr//kZ" sig := new(dns.RR_RRSIG) @@ -24,18 +26,18 @@ func main() { sig.Hdr.Class = dns.ClassINET sig.Hdr.Ttl = 3600 sig.TypeCovered = dns.TypeDNSKEY - sig.Algorithm = dns.AlgRSASHA1 + sig.Algorithm = dnssec.AlgRSASHA1 sig.OrigTtl = 4000 sig.Expiration = 1000 sig.Inception = 800 sig.KeyTag = 34641 sig.SignerName = "miek.nl." - sig.Sig = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFq NDzr//kZ" + sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFq NDzr//kZ" fmt.Printf("%v", sig) - res := new(dns.Resolver) - ch := dns.NewQuerier(res) + res := new(resolver.Resolver) + ch := resolver.NewQuerier(res) // configure the resolver res.Servers = []string{"192.168.1.2"} @@ -49,20 +51,20 @@ func main() { m.Question = make([]dns.Question, 1) m.Question[0] = dns.Question{"miek.nl", dns.TypeDS, dns.ClassINET} - ch <- dns.DnsMsg{m, nil} + ch <- resolver.DnsMsg{m, nil} in := <-ch fmt.Printf("%v\n", in.Dns) m.Question[0] = dns.Question{"www.nlnetlabs.nl", dns.TypeRRSIG, dns.ClassINET} - ch <- dns.DnsMsg{m, nil} + ch <- resolver.DnsMsg{m, nil} in = <-ch fmt.Printf("%v\n", in.Dns) m.Question[0] = dns.Question{"xxxx.nlnetlabs.nl", dns.TypeDNSKEY, dns.ClassINET} - ch <- dns.DnsMsg{m, nil} + ch <- resolver.DnsMsg{m, nil} in = <-ch fmt.Printf("%v\n", in.Dns) - ch <- dns.DnsMsg{nil, nil} + ch <- resolver.DnsMsg{nil, nil} <-ch } diff --git a/examples/mx.go b/examples/mx.go index b9638d1e..2838340a 100644 --- a/examples/mx.go +++ b/examples/mx.go @@ -4,13 +4,14 @@ package main // (c) Miek Gieben - 2011 import ( "dns" + "dns/resolver" "os" "fmt" ) func main() { - r := new(dns.Resolver) - qr := dns.NewQuerier(r) + r := new(resolver.Resolver) + qr := resolver.NewQuerier(r) r.Servers = []string{"127.0.0.1"} r.Timeout = 2 r.Attempts = 1 @@ -25,7 +26,7 @@ func main() { m.Question = make([]dns.Question, 1) m.Question[0] = dns.Question{os.Args[1], dns.TypeMX, dns.ClassINET} - qr <- dns.DnsMsg{m, nil} + qr <- resolver.DnsMsg{m, nil} in := <-qr if in.Dns.Rcode != dns.RcodeSuccess { @@ -38,6 +39,6 @@ func main() { } // Stop the resolver, send it a null mesg - qr <- dns.DnsMsg{nil, nil} + qr <- resolver.DnsMsg{nil, nil} <-qr } diff --git a/msg.go b/msg.go index c8fbe1d1..92f13e11 100644 --- a/msg.go +++ b/msg.go @@ -25,7 +25,7 @@ import ( "encoding/hex" ) -const defaultMsgSize = 4096 +const DefaultMsgSize = 4096 // Packing and unpacking. // @@ -616,7 +616,7 @@ func (dns *Msg) Pack() (msg []byte, ok bool) { // 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 + msg = make([]byte, DefaultMsgSize) // TODO, calculate REAL size // Pack it in: header and then the pieces. off := 0 diff --git a/pack_test.go b/pack_test.go index 8cb0ff9b..69259211 100644 --- a/pack_test.go +++ b/pack_test.go @@ -25,41 +25,6 @@ func TestPackUnpack(t *testing.T) { t.Fail() } - key := new(RR_DNSKEY) - key.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600} - key = &RR_DNSKEY{Flags: 257, Protocol: 3, Algorithm: AlgRSASHA1} - key.PubKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ" - - out.Answer[0] = key - msg, ok = out.Pack() - if !ok { - t.Log("Failed to pack msg with DNSKEY") - t.Fail() - } - - if !in.Unpack(msg) { - t.Log("Failed to unpack msg with DNSKEY") - t.Fail() - } - - sig := new(RR_RRSIG) - sig.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600} - sig = &RR_RRSIG{TypeCovered: TypeDNSKEY, Algorithm: AlgRSASHA1, Labels: 2, - OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.", - Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"} - - out.Answer[0] = sig - msg, ok = out.Pack() - if !ok { - t.Log("Failed to pack msg with RRSIG") - t.Fail() - } - - if !in.Unpack(msg) { - t.Log("Failed to unpack msg with RRSIG") - t.Fail() - } - edns := new(RR_OPT) edns.Hdr.Name = "." edns.Hdr.Rrtype = TypeOPT diff --git a/resolver/Makefile b/resolver/Makefile new file mode 100644 index 00000000..41ac0287 --- /dev/null +++ b/resolver/Makefile @@ -0,0 +1,12 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include $(GOROOT)/src/Make.inc + +TARG=dns/resolver +GOFILES=\ + resolver.go\ + +#DEPS=../ +include $(GOROOT)/src/Make.pkg diff --git a/resolver.go b/resolver/resolver.go similarity index 95% rename from resolver.go rename to resolver/resolver.go index 7826cd1f..400e2173 100644 --- a/resolver.go +++ b/resolver/resolver.go @@ -20,13 +20,14 @@ // ch <- DnsMsg{m, nil} // send the query // in := <-ch // wait for reply // -package dns +package resolver import ( "os" "rand" "time" "net" + "dns" ) // When communicating with a resolver, we use this structure @@ -34,7 +35,7 @@ import ( // A resolver responds with a reply packet and a possible error. // Sending a nil message instructs to resolver to stop. type DnsMsg struct { - Dns *Msg + Dns *dns.Msg Error os.Error } @@ -62,7 +63,7 @@ func query(res *Resolver, msg chan DnsMsg) { // TODO port number, error checking, robustness var c net.Conn var err os.Error - var in *Msg + var in *dns.Msg var port string if len(res.Servers) == 0 { msg <- DnsMsg{nil, nil} @@ -124,7 +125,7 @@ func query(res *Resolver, msg chan DnsMsg) { // Send a request on the connection and hope for a reply. // Up to res.Attempts attempts. -func exchange(c net.Conn, m []byte, r *Resolver) (*Msg, os.Error) { +func exchange(c net.Conn, m []byte, r *Resolver) (*dns.Msg, os.Error) { var timeout int64 var attempts int if r.Mangle != nil { @@ -148,7 +149,7 @@ func exchange(c net.Conn, m []byte, r *Resolver) (*Msg, os.Error) { } c.SetReadTimeout(timeout * 1e9) // nanoseconds - buf := make([]byte, defaultMsgSize) // More than enough. + buf := make([]byte, dns.DefaultMsgSize) // More than enough. n, err = c.Read(buf) if err != nil { // More Go foo needed @@ -158,7 +159,7 @@ func exchange(c net.Conn, m []byte, r *Resolver) (*Msg, os.Error) { return nil, err } buf = buf[0:n] - in := new(Msg) + in := new(dns.Msg) if !in.Unpack(buf) { continue } diff --git a/resolverEdns_test.go b/resolver/resolverEdns_test.go similarity index 76% rename from resolverEdns_test.go rename to resolver/resolverEdns_test.go index e584deda..91cc746a 100644 --- a/resolverEdns_test.go +++ b/resolver/resolverEdns_test.go @@ -1,7 +1,8 @@ -package dns +package resolver import ( "testing" + "dns" ) func TestResolverEdns(t *testing.T) { @@ -12,15 +13,15 @@ func TestResolverEdns(t *testing.T) { res.Timeout = 2 res.Attempts = 1 - m := new(Msg) + m := new(dns.Msg) m.MsgHdr.Recursion_desired = true //only set this bit - m.Question = make([]Question, 1) - m.Extra = make([]RR, 1) + m.Question = make([]dns.Question, 1) + m.Extra = make([]dns.RR, 1) // Add EDNS rr - edns := new(RR_OPT) + edns := new(dns.RR_OPT) edns.Hdr.Name = "." // must . be for edns - edns.Hdr.Rrtype = TypeOPT + edns.Hdr.Rrtype = dns.TypeOPT // You can handle an OTP RR as any other, but there // are some convience functions edns.UDPSize(4096, true) @@ -32,7 +33,7 @@ func TestResolverEdns(t *testing.T) { // edns.Option[0].Data = "lalalala" // ask something - m.Question[0] = Question{"nlnetlabs.nl", TypeSOA, ClassINET} + m.Question[0] = dns.Question{"nlnetlabs.nl", dns.TypeSOA, dns.ClassINET} m.Extra[0] = edns ch <- DnsMsg{m, nil} @@ -40,7 +41,7 @@ func TestResolverEdns(t *testing.T) { //// t.Fail() // t.Log("%v\n", in.Dns) - if in.Dns.Rcode != RcodeSuccess { + if in.Dns.Rcode != dns.RcodeSuccess { t.Log("Failed to get an valid answer") t.Fail() } diff --git a/resolver_test.go b/resolver/resolver_test.go similarity index 61% rename from resolver_test.go rename to resolver/resolver_test.go index c63310ac..50d2ffaa 100644 --- a/resolver_test.go +++ b/resolver/resolver_test.go @@ -1,7 +1,8 @@ -package dns +package resolver import ( "testing" + "dns" ) @@ -11,27 +12,27 @@ func TestResolver(t *testing.T) { res.Servers = []string{"127.0.0.1"} - m := new(Msg) + m := new(dns.Msg) m.MsgHdr.Recursion_desired = true //only set this bit - m.Question = make([]Question, 1) + m.Question = make([]dns.Question, 1) // ask something - m.Question[0] = Question{"miek.nl", TypeSOA, ClassINET} + m.Question[0] = dns.Question{"miek.nl", dns.TypeSOA, dns.ClassINET} ch <- DnsMsg{m, nil} in := <-ch - if in.Dns.Rcode != RcodeSuccess { + if in.Dns.Rcode != dns.RcodeSuccess { t.Log("Failed to get an valid answer") t.Fail() t.Logf("%v\n", in) } // ask something - m.Question[0] = Question{"www.nlnetlabs.nl", TypeRRSIG, ClassINET} + m.Question[0] = dns.Question{"www.nlnetlabs.nl", dns.TypeRRSIG, dns.ClassINET} ch <- DnsMsg{m, nil} in = <-ch - if in.Dns.Rcode != RcodeSuccess { + if in.Dns.Rcode != dns.RcodeSuccess { t.Log("Failed to get an valid answer") t.Fail() t.Logf("%v\n", in) diff --git a/types.go b/types.go index 67ab37ae..7b070cc8 100644 --- a/types.go +++ b/types.go @@ -24,6 +24,7 @@ import ( "net" "strconv" "strings" + "time" ) // Packet formats @@ -106,26 +107,6 @@ const ( _CD = 1 << 4 // checking disabled ) -// DNSSEC encryption algorithm codes. -const ( - // DNSSEC algorithms - AlgRSAMD5 = 1 - AlgDH = 2 - AlgDSA = 3 - AlgECC = 4 - AlgRSASHA1 = 5 - AlgRSASHA256 = 8 - AlgRSASHA512 = 10 - AlgECCGOST = 12 -) - -// DNSSEC hashing codes. -const ( - HashSHA1 = iota - HashSHA256 - HashGOST94 -) - // DNS queries. type Question struct { Name string "domain-name" // "domain-name" specifies encoding; see packers below @@ -141,45 +122,6 @@ func (q *Question) String() string { return s } -// DNS responses (resource records). -// There are many types of messages, -// but they all share the same header. -type RR_Header struct { - Name string "domain-name" - Rrtype uint16 - Class uint16 - Ttl uint32 - Rdlength uint16 // length of data after header -} - -func (h *RR_Header) Header() *RR_Header { - return h -} - -func (h *RR_Header) String() string { - var s string - - if h.Rrtype == TypeOPT { - s = ";" - // and maybe other things - } - - if len(h.Name) == 0 { - s += ".\t" - } else { - s += h.Name + "\t" - } - s = s + strconv.Itoa(int(h.Ttl)) + "\t" - s = s + class_str[h.Class] + "\t" - s = s + rr_str[h.Rrtype] + "\t" - return s -} - -type RR interface { - Header() *RR_Header - String() string -} - type RR_CNAME struct { Hdr RR_Header Cname string "domain-name" @@ -506,29 +448,40 @@ func (rr *RR_NSEC3PARAM) String() string { // Salt with strings.ToUpper() } +// Translate the RRSIG's incep. and expir. time to the correct date. +// Taking into account serial arithmetic (RFC 1982) +func timeToDate(t uint32) string { + utc := time.UTC().Seconds() + mod := (int64(t) - utc) / Year68 + + // If needed assume wrap around(s) + ti := time.SecondsToUTC(int64(t) + (mod * Year68)) // abs()? TODO + return ti.Format("20060102030405") +} + // Map of constructors for each RR wire type. var rr_mk = map[int]func() RR{ - TypeCNAME: func() RR { return new(RR_CNAME) }, - TypeHINFO: func() RR { return new(RR_HINFO) }, - TypeMB: func() RR { return new(RR_MB) }, - TypeMG: func() RR { return new(RR_MG) }, - TypeMINFO: func() RR { return new(RR_MINFO) }, - TypeMR: func() RR { return new(RR_MR) }, - TypeMX: func() RR { return new(RR_MX) }, - TypeNS: func() RR { return new(RR_NS) }, - TypePTR: func() RR { return new(RR_PTR) }, - TypeSOA: func() RR { return new(RR_SOA) }, - TypeTXT: func() RR { return new(RR_TXT) }, - TypeSRV: func() RR { return new(RR_SRV) }, - TypeA: func() RR { return new(RR_A) }, - TypeAAAA: func() RR { return new(RR_AAAA) }, - TypeOPT: func() RR { return new(RR_OPT) }, - TypeDS: func() RR { return new(RR_DS) }, - TypeRRSIG: func() RR { return new(RR_RRSIG) }, - TypeNSEC: func() RR { return new(RR_NSEC) }, - TypeDNSKEY: func() RR { return new(RR_DNSKEY) }, - TypeNSEC3: func() RR { return new(RR_NSEC3) }, - TypeNSEC3PARAM: func() RR { return new(RR_NSEC3PARAM) }, + TypeCNAME: func() RR { return new(RR_CNAME) }, + TypeHINFO: func() RR { return new(RR_HINFO) }, + TypeMB: func() RR { return new(RR_MB) }, + TypeMG: func() RR { return new(RR_MG) }, + TypeMINFO: func() RR { return new(RR_MINFO) }, + TypeMR: func() RR { return new(RR_MR) }, + TypeMX: func() RR { return new(RR_MX) }, + TypeNS: func() RR { return new(RR_NS) }, + TypePTR: func() RR { return new(RR_PTR) }, + TypeSOA: func() RR { return new(RR_SOA) }, + TypeTXT: func() RR { return new(RR_TXT) }, + TypeSRV: func() RR { return new(RR_SRV) }, + TypeA: func() RR { return new(RR_A) }, + TypeAAAA: func() RR { return new(RR_AAAA) }, + TypeOPT: func() RR { return new(RR_OPT) }, + TypeDS: func() RR { return new(RR_DS) }, + TypeRRSIG: func() RR { return new(RR_RRSIG) }, + TypeNSEC: func() RR { return new(RR_NSEC) }, + TypeDNSKEY: func() RR { return new(RR_DNSKEY) }, + TypeNSEC3: func() RR { return new(RR_NSEC3) }, + TypeNSEC3PARAM: func() RR { return new(RR_NSEC3PARAM) }, } // Map of strings for each RR wire type. @@ -555,27 +508,3 @@ var rr_str = map[uint16]string{ TypeNSEC3: "NSEC3", TypeNSEC3PARAM: "NSEC3PARAM", } - -// Map for algorithm names. -var alg_str = map[uint8]string{ - AlgRSAMD5: "RSAMD5", - AlgDH: "DH", - AlgDSA: "DSA", - AlgRSASHA1: "RSASHA1", - AlgRSASHA256: "RSASHA256", - AlgRSASHA512: "RSASHA512", - AlgECCGOST: "ECC-GOST", -} - -// Return the rdata of the RR in wireform. -func wireRdata(r RR) ([]byte, bool) { - buf := make([]byte, 4096) // Too large, need to FIX TODO(mg) - off1, ok := packRR(r, buf, 0) - if !ok { - return nil, false - } - start := off1 - int(r.Header().Rdlength) - end := start + int(r.Header().Rdlength) - buf = buf[start:end] - return buf, true -}