diff --git a/TODO b/TODO index eea26402..d9c2e953 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,8 @@ Todo: * NSEC3 - need base32 for Nsec3 -* Documentation -* Cleanup the code -* Cleanup the API * Tsig testing -* Private key file parsing, exponent 65536 does not work -* Parsing from /etc/resolv.conf - clean up the code +* Private key file parsing use io.Reader (or the like) +* Parsing from /etc/resolv.conf - clean up the code, use normal packages Longer term: * Parsing from strings, going with goyacc and own lexer @@ -18,13 +15,12 @@ Issues: * Check the network order, it works now, but this is on Intel * Make the testsuite work with public DNS servers * shortened ipv6 addresses are not parsed correctly (maybe net issue) -* Convenience functions? +* Convenience functions? Not sure if I want them?? - NXDomain pkt - FormErr pkt - for new(RR*) - nsupdate - - printing?? * query-time, server in string ouput of resolver.Msg - resolver.Msg when doing resolver querying, extend msg...? - also for the server? -* Fix remaining records: LOC, NAPTR, ..., ... +* Fix remaining records: LOC and others diff --git a/dns.go b/dns.go index 1eaf6d9d..b247ac08 100644 --- a/dns.go +++ b/dns.go @@ -3,7 +3,6 @@ // 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 - DNS standard // * 1982 - Serial Arithmetic @@ -24,8 +23,10 @@ // * 5011 - NSID // * 5936 - AXFR // * IP6 support -// -// The package allows full control over what is send out to the DNS. + +// 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. // package dns @@ -33,8 +34,10 @@ import ( "strconv" ) +// For RFC1982 (Serial Arithmetic) calculations. const Year68 = 2 << (32 - 1) +// Error represents a DNS error type Error struct { Error string Name string @@ -61,7 +64,7 @@ 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] } -// Check if the RRset is 2181 compliant +// Check if the RRset is RFC 2181 compliant func (r RRset) Ok() bool { ttl := r[0].Header().Ttl name := r[0].Header().Name diff --git a/dnssec.go b/dnssec.go index d2d6999c..9e42332f 100644 --- a/dnssec.go +++ b/dnssec.go @@ -19,7 +19,6 @@ import ( // DNSSEC encryption algorithm codes. const ( - // DNSSEC algorithms AlgRSAMD5 = 1 AlgDH = 2 AlgDSA = 3 @@ -58,6 +57,7 @@ type dnskeyWireFmt struct { Protocol uint8 Algorithm uint8 PubKey string "base64" + /* Nothing is left out */ } // Calculate the keytag of the DNSKEY. @@ -139,19 +139,18 @@ func (k *RR_DNSKEY) ToDS(h int) *RR_DS { io.WriteString(s, string(digest)) ds.Digest = hex.EncodeToString(s.Sum()) case HashGOST94: - + /* I have no clue */ default: - // wrong hash value return nil } return ds } // Sign an RRSet. The Signature needs to be filled in with -// all the values: Inception, Expiration, KeyTag(?), SignerName -// the rest is copied from the RRset. Return true when all ok. -// The Signature data is filled by this method -// There is no check if rrset is a proper (RFC 2181) RRSet +// all the values: Inception, Expiration, KeyTag and SignerName +// 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. +// 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 { return false @@ -161,14 +160,13 @@ func (s *RR_RRSIG) Sign(k PrivateKey, rrset RRset) bool { s.Hdr.Class = rrset[0].Header().Class s.Hdr.Rrtype = TypeRRSIG s.Hdr.Ttl = rrset[0].Header().Ttl // re-use TTL of RRset - /* Check that these are there */ - /* - s.Inception = inception - s.Expiration = expiration - s.KeyTag = k.KeyTag() - s.SignerName = k.Hdr.Name - s.Algorithm = ?? - */ + + if s.KeyTag == 0 || len(s.SignerName) == 0 { + // Must be set + return false + } + // Algorithm is check below + // s.Inception and s.Expiration may be 0 (rollover etc.) s.Labels = LabelCount(rrset[0].Header().Name) s.TypeCovered = rrset[0].Header().Rrtype @@ -247,6 +245,9 @@ func (s *RR_RRSIG) Sign(k PrivateKey, rrset RRset) bool { case AlgRSASHA512: h = sha512.New() ch = rsa.HashSHA512 + default: + // Illegal Alg + return false } // Need privakey representation in godns TODO(mg) see keygen.go io.WriteString(h, string(signdata)) @@ -273,26 +274,20 @@ func (s *RR_RRSIG) Sign(k PrivateKey, rrset RRset) bool { return true } -// Validate an rrset with the signature and key. This is the -// cryptographic test, the 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 check separately. func (s *RR_RRSIG) Verify(k *RR_DNSKEY, rrset RRset) bool { // Frist the easy checks if s.KeyTag != k.KeyTag() { - println(s.KeyTag) - println(k.KeyTag()) return false } if s.Hdr.Class != k.Hdr.Class { - println("Class") return false } if s.Algorithm != k.Algorithm { - println("Class") return false } if s.SignerName != k.Hdr.Name { - println(s.SignerName) - println(k.Hdr.Name) return false } for _, r := range rrset { @@ -390,12 +385,13 @@ func (s *RR_RRSIG) Verify(k *RR_DNSKEY, rrset RRset) bool { case AlgDSA: case AlgECC: case AlgECCGOST: + default: + // Unknown Alg + return false } - return err == nil } -// Beter name for this function // Using RFC1982 calculate if a signature period is valid func (s *RR_RRSIG) PeriodOK() bool { utc := time.UTC().Seconds() diff --git a/edns.go b/edns.go index c7358c43..02d73264 100644 --- a/edns.go +++ b/edns.go @@ -33,7 +33,7 @@ type Option struct { type RR_OPT struct { Hdr RR_Header - Option []Option "OPT" // Tag is used in pack and unpack + Option []Option "OPT" // Tag is used in Pack and Unpack } func (rr *RR_OPT) Header() *RR_Header { @@ -66,12 +66,12 @@ func (rr *RR_OPT) String() string { return s } -// Get the version +// Get the EDNS version (always 0 currently) func (rr *RR_OPT) Version() uint8 { return 0 } -// Set the version of edns +// Set the version of EDNS func (rr *RR_OPT) SetVersion(v uint8) { return } @@ -81,7 +81,7 @@ func (rr *RR_OPT) UDPSize() uint16 { return rr.Hdr.Class } -// Set/Get the UDP buffer size +// Set the UDP buffer size func (rr *RR_OPT) SetUDPSize(size uint16) { rr.Hdr.Class = size } @@ -116,15 +116,8 @@ func (rr *RR_OPT) Nsid() string { return "NSID: " + rr.Option[0].Data } -// Representation of NSID is in Hex - -// Set the NSID -func (rr *RR_OPT) SetNsidToHex(hexnsid string) { +// Set the NSID from a string which is represented as hex characters. +func (rr *RR_OPT) SetNsid(hexnsid string) { rr.Option[0].Code = OptionCodeNSID rr.Option[0].Data = hexnsid } - -func (rr *RR_OPT) SetNsidToString(nsid string) { - rr.Option[0].Code = OptionCodeNSID - rr.Option[0].Data = hex.EncodeToString([]byte(nsid)) -} diff --git a/keygen.go b/keygen.go index 8b4658b3..a706a32b 100644 --- a/keygen.go +++ b/keygen.go @@ -12,7 +12,7 @@ import ( ) // Empty interface that is used a wrapper around all possible -// private key implementation from the crypto package. +// private key implementations from the crypto package. type PrivateKey interface{} // io.Reader @@ -53,20 +53,14 @@ func (r *RR_DNSKEY) Generate(bits int) (PrivateKey, os.Error) { } // Convert a PrivateKey to a string. This -// string has the same format as the private-key-file -// of BIND9 (Private-key-format: v1.3). It needs some -// info from the key (hashing, keytag), so its a method -// of the RR_DNSKEY. +// string has the same format as the private-key-file of BIND9 (Private-key-format: v1.3). +// It needs some info from the key (hashing, keytag), so its a method of the RR_DNSKEY. func (r *RR_DNSKEY) PrivateKeyString(p PrivateKey) (s string) { switch t := p.(type) { case *rsa.PrivateKey: algorithm := strconv.Itoa(int(r.Algorithm)) + " (" + alg_str[r.Algorithm] + ")" modulus := unpackBase64(t.PublicKey.N.Bytes()) e := big.NewInt(int64(t.PublicKey.E)) - /* - pub := make([]byte, 1) - pub[0] = uint8(t.PublicKey.E) // Todo does not fit with binds 65537 exp! - */ publicExponent := unpackBase64(e.Bytes()) privateExponent := unpackBase64(t.D.Bytes()) prime1 := unpackBase64(t.P.Bytes()) @@ -99,8 +93,7 @@ func (r *RR_DNSKEY) PrivateKeyString(p PrivateKey) (s string) { return } -// Read a private key file and create a public key and -// return a private key +// Read a private key (file) string and create a public key. Return a 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 4a9f18c0..1d3d040a 100644 --- a/msg.go +++ b/msg.go @@ -675,6 +675,7 @@ func unpackRR(msg []byte, off int) (rr RR, off1 int, ok bool) { return rr, off, ok } +// Reverse a map func reverse(m map[uint16]string) map[string]uint16 { n := make(map[string]uint16) for u, s := range m { diff --git a/resolver/resolver.go b/resolver/resolver.go index dc3b7d58..efedb8b0 100644 --- a/resolver/resolver.go +++ b/resolver/resolver.go @@ -41,10 +41,6 @@ type Msg struct { Dns *dns.Msg Error os.Error } -// ^ prolly extend this so that servers can use this too. We need -// to also encode some client side stuff in here, like client addr -// I think that is the *only* thing we need. - type Resolver struct { Servers []string // servers to use @@ -138,7 +134,7 @@ func query(res *Resolver, msg chan Msg) { return } -// Start a new xfr as a goroutine, return a channel. +// Start a new xfr as a goroutine, return a channel of Msg. // Channel will be closed when the axfr is finished, until // that time new messages will appear on the channel func (res *Resolver) NewXfer() (ch chan Msg) { diff --git a/responder/responder.go b/responder/responder.go index eae65d47..edc134ed 100644 --- a/responder/responder.go +++ b/responder/responder.go @@ -4,12 +4,15 @@ // DNS server implementation -// Package responder implements a DNS server. A nameserver needs to implement -// the Responder interface: +// Package responder implements a DNS server. Any nameserver needs to implement +// the Responder interface to get things going. +// +// Typical usage of the package: // // type myserv Server // func (s *myserv) ResponderUDP(c *net.UDPConn, a net.Addr, in []byte) { /* UDP reply */ } // func (s *myserv) ResponderTCP(c *net.TCPConn, in []byte) { /* TCP reply */} +// // su := new(Server) // create new sever // su.Address = "127.0.0.1" // listen address // su.Port = "8053" // listen port @@ -44,17 +47,19 @@ type msg struct { // the kind of nameserver type Responder interface { // Receives the raw message content and writes back - // an udp response. An UDP connection needs a remote - // address to write to. + // an UDP response. An UDP connection needs a remote + // address to write to. ResponderUDP() must take care of sending + // any response back to the requestor. ResponderUDP(c *net.UDPConn, a net.Addr, in []byte) // Receives the raw message content and writes back - // a tcp response. A TCP connection does need to - // know explicitly be told the remote address. + // a TCP response. A TCP connection does need to + // know explicitly be told the remote address. ResponderTCP() must + // take care of sending back a response to the requestor. ResponderTCP(c *net.TCPConn, in []byte) } -// Start a new responder. The returned channel is only used -// to stop the responder. +// Start a new responder. The returned channel is only used to stop the responder. +// Send 'true' to make it stop func (res *Server) NewResponder(h Responder, ch chan bool) os.Error { var port string if len(res.Address) == 0 { @@ -109,6 +114,7 @@ func (res *Server) NewResponder(h Responder, ch chan bool) os.Error { return nil } +// Listen for UDP requests. func listenerUDP(a *net.UDPAddr, ch chan msg) { c, err := net.ListenUDP("udp", a) if err != nil { @@ -128,6 +134,7 @@ func listenerUDP(a *net.UDPAddr, ch chan msg) { } } +// Listen for TCP requests. func listenerTCP(a *net.TCPAddr, ch chan msg) { t, err := net.ListenTCP("tcp", a) if err != nil { @@ -171,7 +178,7 @@ func listenerTCP(a *net.TCPAddr, ch chan msg) { } } -// Send a buffer on the TCP connection +// Send a buffer on the TCP connection. func SendTCP(m []byte, c *net.TCPConn) os.Error { l := make([]byte, 2) l[0] = byte(len(m) >> 8) @@ -189,8 +196,8 @@ func SendTCP(m []byte, c *net.TCPConn) os.Error { return nil } -// Small helper function to help sending UDP packets. Mostly -// done for the symmetry. see SendTCP. +// Send a buffer to the remove address. Only here because +// of the symmetry with SendTCP(). func SendUDP(m []byte, c *net.UDPConn, a net.Addr) os.Error { _, err := c.WriteTo(m, a) if err != nil { diff --git a/string.go b/string.go index 2a686a79..43072ff7 100644 --- a/string.go +++ b/string.go @@ -12,8 +12,8 @@ const ( w = 7 * d ) -// Convert a Ttl to a value. Supported value 'm' for minutes, 'h' for hours -// 'w' for week and 'd' for days. Stuff like '1d1d' is legal and return the value of '2d' +// 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' func StringToSeconds(ttl string) (sec uint32, ok bool) { num := "" for _, k := range ttl { @@ -41,6 +41,7 @@ func StringToSeconds(ttl string) (sec uint32, ok bool) { return } +// Convert a value to a (string) Ttl. Reverse of StringToSeconds() func SecondsToString(val uint32) (str string) { mod := val / w if mod > 0 { diff --git a/tsig.go b/tsig.go index 04489a6b..3211558f 100644 --- a/tsig.go +++ b/tsig.go @@ -1,13 +1,12 @@ package dns -// Implementation of TSIG -// Generation an Validation +// Implementation of TSIG: generation and validation import ( - "crypto/hmac" + "io" "strconv" "strings" - "io" + "crypto/hmac" "encoding/hex" ) @@ -24,7 +23,7 @@ type RR_TSIG struct { Fudge uint16 MACSize uint16 MAC string - OrigId uint16 // msg id + OrigId uint16 Error uint16 OtherLen uint16 OtherData string @@ -46,9 +45,8 @@ func (rr *RR_TSIG) String() string { " " + rr.OtherData } -// The following values must be put in wireformat, so that -// the MAC can be calculated -// RFC 2845, section 3.4.2. TSIG Variables +// The following values must be put in wireformat, so that the MAC can be calculated. +// RFC 2845, section 3.4.2. TSIG Variables. type tsigWireFmt struct { // From RR_HEADER Name string "domain-name" @@ -66,8 +64,8 @@ type tsigWireFmt struct { // Generate the HMAC for msg. 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 -// secret is encoded in base64 in BIND9 +// be set, otherwise the MAC is not correct. +// The string 'secret' must be encoded in base64 func (rr *RR_TSIG) Generate(msg *Msg, secret string) bool { buf := make([]byte, 4096) // TODO(mg) bufsize! tsig := new(tsigWireFmt) @@ -104,7 +102,7 @@ func (rr *RR_TSIG) Generate(msg *Msg, secret string) bool { // Verify a TSIG. The msg should be the complete message with // the TSIG record still attached (as the last rr in the Additional -// section) +// section) TODO(mg) func (rr *RR_TSIG) Verify(msg *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 2700230e..68e5c584 100644 --- a/types.go +++ b/types.go @@ -6,8 +6,8 @@ // 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} +// r.TXT = "This is the content of the TXT record" // package dns