diff --git a/ChangeLog b/ChangeLog index fd66754b..0367507c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,8 @@ xx YYY 2011: 0.0.1 Miek Gieben * Most known RRs supported - * DNSSEC implemented (verify/signing) + * DNSSEC (verify/signing) * Key generation - * Private key file parsing from BIND + * Private key file parsing from BIND files * TSIG * EDNS0, NSID * Cherry picked GRONG for server side ideas diff --git a/README b/README index 17308ef6..1c9ea299 100644 --- a/README +++ b/README @@ -1,9 +1,9 @@ Alternative (more granular) approach to a DNS library. Completely usable DNS library. Most widely used Resource Records are -supported (more to come). DNSSEC types too (except NSEC3, for now). +supported. DNSSEC types too (except NSEC3, for now). EDNS0 is (see edns.go), UDP/TCP queries, TSIG, AXFR (and IXFR probably) -too. Both client and server side programming is supported. +too. Both client and server side programming is supported. Installation is done by running: ./install @@ -11,22 +11,36 @@ Sample programs can be found in the _examples directory. They can be build with: make examples (after the dns package has been installed) The major omission at the moment is parsing Resource Records from -strings. (I.e. supporting the 1035 zone file format). +strings. (i.e. supporting the RFC 1035 zone file format). +Also the IPv6 support needs to be tested, both in dns/resolver and +dns/responder. Everything else should be present and working. If not, drop me an email. Have fun! Miek Gieben - 2010, 2011 - miek@miek.nl -Implemented RFCS: +Supported RFCs and features include: -* RFC 1034/1035 -* RFC 2671, EDNS -* RRC 3225, DO bit -* RFC 3110, RSA in DNS -* RFC 4033/4034/4035 , DNSSEC -* RFC 5001, NSID -* RFC 5155, NSEC3 -- todo -* And all that I forgot +* 1034/1035 - DNS standard +* 1982 - Serial Arithmetic +* 1876 - LOC record (incomplete) +* 1995 - IXFR +* 1996 - DNS notify +* 2181 - RRset definition +* 2537 - RSAMD5 DNS keys +* 2065 - DNSSEC (updated in later RFCs) +* 2671 - EDNS +* 2782 - SRV +* 2845 - TSIG +* 2915 - NAPTR +* 3110 - RSASHA1 DNS keys +* 3225 - DO bit (DNSSEC OK) +* 4033/4034/4035 - DNSSEC + validation functions +* 4255 - SSHFP +* 4408 - SPF +* 5001 - NSID +* 5155 - NSEC -- todo +* 5936 - AXFR Loosely based upon: * ldns diff --git a/dns.go b/dns.go index 410e4d29..0c769a97 100644 --- a/dns.go +++ b/dns.go @@ -3,38 +3,25 @@ // license that can be found in the LICENSE file. // Extended and bugfixes by Miek Gieben -// Supported RFCs and features include: -// * 1034/1035 - DNS standard -// * 1982 - Serial Arithmetic -// * 1876 - LOC record (incomplete) -// * 1995 - IXFR -// * 1996 - DNS notify -// * 2181 - RRset definition -// * 2537 - RSAMD5 DNS keys -// * 2065 - DNSSEC (updated in later RFCs) -// * 2671 - EDNS -// * 2782 - SRV record -// * 2845 - TSIG -// * 2915 - NAPTR record -// * 3110 - RSASHA1 DNS keys -// * 3225 - DO bit (DNSSEC OK) -// * 4033/4034/4035 - DNSSEC + validation functions -// * 4255 - SSHFP record -// * 5011 - NSID -// * 5936 - AXFR -// * IP6 support - // Package dns implements a full featured interface to the DNS. -// The package allows full control over what is send out to the DNS. All RR types are converted -// to Go types. +// The package allows full control over what is send out to the DNS. // +// Resource Records are types in Go. They are not stored in wire format. +// Basic usage pattern for creating new Resource Record: +// +// r := new(RR_TXT) +// r.Hdr = RR_Header{Name: "a.miek.nl", Rrtype: TypeTXT, Class: ClassINET, Ttl: 3600} +// r.TXT = "This is the content of the TXT record" +// +// Convert from - Unpack() - and to - Pack() - wire format for +// Msgs or Resource Records. package dns import ( "strconv" ) -// For RFC1982 (Serial Arithmetic) calculations. +// For RFC1982 (Serial Arithmetic) calculations in 32 bits. const Year68 = 2 << (32 - 1) // Error represents a DNS error @@ -52,7 +39,7 @@ func (e *Error) String() string { return e.Error } -// Meta data when querying +// Meta data for queries type Meta struct { QLen int // query length in bytes RLen int // reply length in bytes diff --git a/dns.y b/dns.y index fc67667b..b9f3514b 100644 --- a/dns.y +++ b/dns.y @@ -3,7 +3,7 @@ %{ -package dns +package parse import ( "fmt" @@ -24,7 +24,7 @@ import ( /* * Types known to package dns */ -%token Y_A Y_NS +%token YA YNS /* * Other elements of the Resource Records @@ -45,8 +45,8 @@ label: VAL rrtype: /* All supported RR types */ - Y_A - | Y_NS + YA + | YNS %% type DnsLex int diff --git a/dnssec.go b/dnssec.go index f497e4a6..b7c7200d 100644 --- a/dnssec.go +++ b/dnssec.go @@ -148,8 +148,8 @@ func (k *RR_DNSKEY) ToDS(h int) *RR_DS { // Sign an RRSet. The Signature needs to be filled in with // the values: Inception, Expiration, KeyTag, SignerName and Algorithm. -// The rest is copied from the RRset. Return true when the signing went OK. -// The Signature data is the RRSIG is filled by this method. +// The rest is copied from the RRset. Returns true when the signing went OK. +// The Signature data in the RRSIG is filled by this method. // There is no check if rrset is a proper (RFC 2181) RRSet. func (s *RR_RRSIG) Sign(k PrivateKey, rrset RRset) bool { if k == nil { @@ -271,8 +271,8 @@ func (s *RR_RRSIG) Sign(k PrivateKey, rrset RRset) bool { return true } -// Validate an rrset with the signature and key. This is only the -// cryptographic test, the signature validity period most be check separately. +// Validate an RRSet with the signature and key. This is only the +// cryptographic test, the signature validity period most be checked separately. func (s *RR_RRSIG) Verify(k *RR_DNSKEY, rrset RRset) bool { // Frist the easy checks if s.KeyTag != k.KeyTag() { @@ -389,7 +389,7 @@ func (s *RR_RRSIG) Verify(k *RR_DNSKEY, rrset RRset) bool { return err == nil } -// Using RFC1982 calculate if a signature period is valid +// Use RFC1982 to calculate if a signature period is valid. func (s *RR_RRSIG) PeriodOK() bool { utc := time.UTC().Seconds() modi := (int64(s.Inception) - utc) / Year68 diff --git a/keygen.go b/keygen.go index 2aa4a4d0..f811f1ba 100644 --- a/keygen.go +++ b/keygen.go @@ -11,7 +11,7 @@ import ( "crypto/rand" ) -// Empty interface that is used a wrapper around all possible +// Empty interface that is used as a wrapper around all possible // private key implementations from the crypto package. type PrivateKey interface{} @@ -21,7 +21,7 @@ type PrivateKey interface{} // PrivateKeyToDNSKEY // Generate a key of the given bit size. -// The public part is directly put inside the DNSKEY record. +// The public part is put inside the DNSKEY record. // The Algorithm in the key must be set as this will define // what kind of DNSKEY will be generated. func (r *RR_DNSKEY) Generate(bits int) (PrivateKey, os.Error) { @@ -93,7 +93,7 @@ func (r *RR_DNSKEY) PrivateKeyString(p PrivateKey) (s string) { return } -// Read a private key (file) string and create a public key. Return a private key +// Read a private key (file) string and create a public key. Return the private key. func (k *RR_DNSKEY) PrivateKeySetString(s string) (PrivateKey, os.Error) { p := new(rsa.PrivateKey) r := bufio.NewReader(strings.NewReader(s)) diff --git a/msg.go b/msg.go index dab90b07..790a1c92 100644 --- a/msg.go +++ b/msg.go @@ -2,17 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// DNS packet assembly. See RFC 1035. -// // This is not (yet) optimized for speed. -// -// Rather than write the usual handful of routines to pack and -// unpack every message that can appear on the wire, we use -// reflection to write a generic pack/unpack for structs and then -// use it. Thus, if in the future we need to define new message -// structs, no new pack/unpack/printing code needs to be written. -// -// + +// DNS packet assembly, see RFC 1035. Converting from - Unpack() - +// and to - Pack() - wire format. +// All the packers and unpackers take a (msg []byte, off int) +// and return (off1 int, ok bool). If they return ok==false, they +// also return off1==len(msg), so that the next unpacker will +// also fail. This lets us avoid checks of ok until the end of a +// packing sequence. + package dns import ( @@ -27,6 +26,7 @@ import ( "encoding/hex" ) +// The size of the standard buffer. const DefaultMsgSize = 4096 // A manually-unpacked version of (id, bits). @@ -54,14 +54,6 @@ type Msg struct { Extra []RR } -// Packing and unpacking. -// -// All the packers and unpackers take a (msg []byte, off int) -// and return (off1 int, ok bool). If they return ok==false, they -// also return off1==len(msg), so that the next unpacker will -// also fail. This lets us avoid checks of ok until the end of a -// packing sequence. - // Map of strings for each RR wire type. var Rr_str = map[uint16]string{ TypeCNAME: "CNAME", @@ -89,7 +81,7 @@ var Rr_str = map[uint16]string{ TypeNSEC: "NSEC", TypeDNSKEY: "DNSKEY", TypeNSEC3: "NSEC3", - TypeNSEC3PARAM: "NSEC3PARAM", // DNSSEC's bitch + TypeNSEC3PARAM: "NSEC3PARAM", TypeSPF: "SPF", TypeTKEY: "TKEY", // Meta RR TypeTSIG: "TSIG", // Meta RR @@ -98,10 +90,10 @@ var Rr_str = map[uint16]string{ TypeALL: "ANY", // Meta RR } -// Reverse of Rr_str (needed for parsing) +// Reverse of Rr_str (needed for string parsing). var Str_rr = reverse(Rr_str) -// Map of strings for each RR wire type. +// Map of strings for each CLASS wire type. var Class_str = map[uint16]string{ ClassINET: "IN", ClassCSNET: "CS", @@ -134,6 +126,12 @@ var rcode_str = map[int]string{ RcodeNotZone: "NOTZONE", } +// Rather than write the usual handful of routines to pack and +// unpack every message that can appear on the wire, we use +// reflection to write a generic pack/unpack for structs and then +// use it. Thus, if in the future we need to define new message +// structs, no new pack/unpack/printing code needs to be written. + // Pack a domain name s into msg[off:]. // Domain names are a sequence of counted strings // split at the dots. They end with a zero-length string. @@ -747,7 +745,6 @@ func reverse(m map[uint16]string) map[string]uint16 { return n } - // Convert a MsgHdr to a string, mimic the way Dig displays headers: //;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48404 //;; flags: qr aa rd ra; @@ -904,7 +901,7 @@ func (dns *Msg) Unpack(msg []byte) bool { return true } -// Convert a complete message to a string using dig-like output. +// Convert a complete message to a string with dig-like output. func (dns *Msg) String() string { if dns == nil { return " MsgHdr" @@ -941,7 +938,7 @@ func (dns *Msg) String() string { return s } -// Set an Msg Id to a random value +// Set an Msg Id to a random value. func (m *Msg) SetId() { m.Id = uint16(rand.Int()) ^ uint16(time.Nanoseconds()) } diff --git a/string.go b/string.go index 43072ff7..e4f6a533 100644 --- a/string.go +++ b/string.go @@ -13,7 +13,7 @@ const ( ) // Convert a Ttl to a value. Supported values: 'm' for minutes, 'h' for hours -// 'w' for week and 'd' for days. Stuff like '1d1d' is legal and returns the value of '2d' +// 'w' for week and 'd' for days, '1d1d' is legal and returns the value of '2d'. func StringToSeconds(ttl string) (sec uint32, ok bool) { num := "" for _, k := range ttl { diff --git a/tsig.go b/tsig.go index c6663c8b..6a82dd4b 100644 --- a/tsig.go +++ b/tsig.go @@ -10,7 +10,7 @@ import ( "encoding/hex" ) -// Need to lookup the actual codes +// HMAC hashing codes. These are transmitted as domain names. const ( HmacMD5 = "HMAC-MD5.SIG-ALG.REG.INT" HmacSHA1 = "hmac-sha1" @@ -34,8 +34,8 @@ func (rr *RR_TSIG) Header() *RR_Header { return &rr.Hdr } +// TSIG has no official presentation format, but this will suffice. func (rr *RR_TSIG) String() string { - // It has no official presentation format return rr.Hdr.String() + " " + rr.Algorithm + " " + tsigTimeToDate(rr.TimeSigned) + @@ -63,11 +63,11 @@ type tsigWireFmt struct { OtherData string "fixed-size" } -// Return the RR with the TSIG AND include it in the message -// Generate the HMAC for msg. The TSIG RR is modified +// Generate the HMAC for message. The TSIG RR is modified // to include the MAC and MACSize. Note the the msg Id must -// be set, otherwise the MAC is not correct. -// The string 'secret' must be encoded in base64 +// already be set, otherwise the MAC will not be correct when +// the message is send. +// The string 'secret' must be encoded in base64. func (t *RR_TSIG) Generate(m *Msg, secret string) bool { rawsecret, err := packBase64([]byte(secret)) if err != nil { @@ -87,10 +87,10 @@ func (t *RR_TSIG) Generate(m *Msg, secret string) bool { return true } -// Verify a TSIG. The msg should be the complete message with +// Verify a TSIG. The message should be the complete with // the TSIG record still attached (as the last rr in the Additional -// section) TODO(mg) -// The secret is a base64 encoded string with a secret +// section). Return true on success. +// The secret is a base64 encoded string with the secret. func (t *RR_TSIG) Verify(m *Msg, secret string) bool { // copy the mesg, strip (and check) the tsig rr // perform the opposite of Generate() and then diff --git a/types.go b/types.go index 64e55c10..7aa2adf2 100644 --- a/types.go +++ b/types.go @@ -3,12 +3,14 @@ // license that can be found in the LICENSE file. // Extended and bugfixes by Miek Gieben +// Resource Records are types in Go. They are not stored in wire format. // Basic usage pattern for creating new Resource Record: // -// r := new(RR_TXT) -// r.Hdr = RR_Header{Name: "a.miek.nl", Rrtype: TypeTXT, Class: ClassINET, Ttl: 3600} -// r.TXT = "This is the content of the TXT record" +// r := new(RR_TXT) +// r.Hdr = RR_Header{Name: "a.miek.nl", Rrtype: TypeTXT, Class: ClassINET, Ttl: 3600} +// r.TXT = "This is the content of the TXT record" // + package dns import ( @@ -20,7 +22,7 @@ import ( // Packet formats -// Wire constants. +// Wire constants and supported types. const ( // valid RR_Header.Rrtype and Question.qtype TypeA = 1 @@ -361,7 +363,7 @@ func (rr *RR_CERT) String() string { " " + rr.Certificate } -// RFC 2672 +// See RFC 2672. type RR_DNAME struct { Hdr RR_Header Target string "domain-name" @@ -578,7 +580,7 @@ func (rr *RR_NSEC3PARAM) String() string { return s } -// RFC 4408 +// See RFC 4408 type RR_SPF struct { Hdr RR_Header Txt string