documentation updates

This commit is contained in:
Miek Gieben 2011-01-27 09:29:11 +01:00
parent 9fdb9e0feb
commit d9dfd913a7
10 changed files with 91 additions and 91 deletions

View File

@ -1,8 +1,8 @@
xx YYY 2011: 0.0.1 Miek Gieben <miek@miek.nl> xx YYY 2011: 0.0.1 Miek Gieben <miek@miek.nl>
* Most known RRs supported * Most known RRs supported
* DNSSEC implemented (verify/signing) * DNSSEC (verify/signing)
* Key generation * Key generation
* Private key file parsing from BIND * Private key file parsing from BIND files
* TSIG * TSIG
* EDNS0, NSID * EDNS0, NSID
* Cherry picked GRONG for server side ideas * Cherry picked GRONG for server side ideas

38
README
View File

@ -1,9 +1,9 @@
Alternative (more granular) approach to a DNS library. Alternative (more granular) approach to a DNS library.
Completely usable DNS library. Most widely used Resource Records are 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) 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 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) be build with: make examples (after the dns package has been installed)
The major omission at the moment is parsing Resource Records from 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. Everything else should be present and working. If not, drop me an email.
Have fun! Have fun!
Miek Gieben - 2010, 2011 - miek@miek.nl Miek Gieben - 2010, 2011 - miek@miek.nl
Implemented RFCS: Supported RFCs and features include:
* RFC 1034/1035 * 1034/1035 - DNS standard
* RFC 2671, EDNS * 1982 - Serial Arithmetic
* RRC 3225, DO bit * 1876 - LOC record (incomplete)
* RFC 3110, RSA in DNS * 1995 - IXFR
* RFC 4033/4034/4035 , DNSSEC * 1996 - DNS notify
* RFC 5001, NSID * 2181 - RRset definition
* RFC 5155, NSEC3 -- todo * 2537 - RSAMD5 DNS keys
* And all that I forgot * 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: Loosely based upon:
* ldns * ldns

37
dns.go
View File

@ -3,38 +3,25 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Extended and bugfixes by Miek Gieben // 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. // 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 // The package allows full control over what is send out to the DNS.
// to Go types.
// //
// 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 package dns
import ( import (
"strconv" "strconv"
) )
// For RFC1982 (Serial Arithmetic) calculations. // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
const Year68 = 2 << (32 - 1) const Year68 = 2 << (32 - 1)
// Error represents a DNS error // Error represents a DNS error
@ -52,7 +39,7 @@ func (e *Error) String() string {
return e.Error return e.Error
} }
// Meta data when querying // Meta data for queries
type Meta struct { type Meta struct {
QLen int // query length in bytes QLen int // query length in bytes
RLen int // reply length in bytes RLen int // reply length in bytes

8
dns.y
View File

@ -3,7 +3,7 @@
%{ %{
package dns package parse
import ( import (
"fmt" "fmt"
@ -24,7 +24,7 @@ import (
/* /*
* Types known to package dns * Types known to package dns
*/ */
%token <rrtype> Y_A Y_NS %token <rrtype> YA YNS
/* /*
* Other elements of the Resource Records * Other elements of the Resource Records
@ -45,8 +45,8 @@ label: VAL
rrtype: rrtype:
/* All supported RR types */ /* All supported RR types */
Y_A YA
| Y_NS | YNS
%% %%
type DnsLex int type DnsLex int

View File

@ -148,8 +148,8 @@ func (k *RR_DNSKEY) ToDS(h int) *RR_DS {
// Sign an RRSet. The Signature needs to be filled in with // Sign an RRSet. The Signature needs to be filled in with
// the values: Inception, Expiration, KeyTag, SignerName and Algorithm. // the values: Inception, Expiration, KeyTag, SignerName and Algorithm.
// The rest is copied from the RRset. Return true when the signing went OK. // The rest is copied from the RRset. Returns true when the signing went OK.
// The Signature data is the RRSIG is filled by this method. // The Signature data in the RRSIG is filled by this method.
// There is no check if rrset is a proper (RFC 2181) RRSet. // There is no check if rrset is a proper (RFC 2181) RRSet.
func (s *RR_RRSIG) Sign(k PrivateKey, rrset RRset) bool { func (s *RR_RRSIG) Sign(k PrivateKey, rrset RRset) bool {
if k == nil { if k == nil {
@ -271,8 +271,8 @@ func (s *RR_RRSIG) Sign(k PrivateKey, rrset RRset) bool {
return true return true
} }
// Validate an rrset with the signature and key. This is only the // Validate an RRSet with the signature and key. This is only the
// cryptographic test, the signature validity period most be check separately. // cryptographic test, the signature validity period most be checked separately.
func (s *RR_RRSIG) Verify(k *RR_DNSKEY, rrset RRset) bool { func (s *RR_RRSIG) Verify(k *RR_DNSKEY, rrset RRset) bool {
// Frist the easy checks // Frist the easy checks
if s.KeyTag != k.KeyTag() { if s.KeyTag != k.KeyTag() {
@ -389,7 +389,7 @@ func (s *RR_RRSIG) Verify(k *RR_DNSKEY, rrset RRset) bool {
return err == nil 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 { func (s *RR_RRSIG) PeriodOK() bool {
utc := time.UTC().Seconds() utc := time.UTC().Seconds()
modi := (int64(s.Inception) - utc) / Year68 modi := (int64(s.Inception) - utc) / Year68

View File

@ -11,7 +11,7 @@ import (
"crypto/rand" "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. // private key implementations from the crypto package.
type PrivateKey interface{} type PrivateKey interface{}
@ -21,7 +21,7 @@ type PrivateKey interface{}
// PrivateKeyToDNSKEY // PrivateKeyToDNSKEY
// Generate a key of the given bit size. // 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 // The Algorithm in the key must be set as this will define
// what kind of DNSKEY will be generated. // what kind of DNSKEY will be generated.
func (r *RR_DNSKEY) Generate(bits int) (PrivateKey, os.Error) { func (r *RR_DNSKEY) Generate(bits int) (PrivateKey, os.Error) {
@ -93,7 +93,7 @@ func (r *RR_DNSKEY) PrivateKeyString(p PrivateKey) (s string) {
return 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) { func (k *RR_DNSKEY) PrivateKeySetString(s string) (PrivateKey, os.Error) {
p := new(rsa.PrivateKey) p := new(rsa.PrivateKey)
r := bufio.NewReader(strings.NewReader(s)) r := bufio.NewReader(strings.NewReader(s))

45
msg.go
View File

@ -2,17 +2,16 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// DNS packet assembly. See RFC 1035.
//
// This is not (yet) optimized for speed. // This is not (yet) optimized for speed.
//
// Rather than write the usual handful of routines to pack and // DNS packet assembly, see RFC 1035. Converting from - Unpack() -
// unpack every message that can appear on the wire, we use // and to - Pack() - wire format.
// reflection to write a generic pack/unpack for structs and then // All the packers and unpackers take a (msg []byte, off int)
// use it. Thus, if in the future we need to define new message // and return (off1 int, ok bool). If they return ok==false, they
// structs, no new pack/unpack/printing code needs to be written. // 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 package dns
import ( import (
@ -27,6 +26,7 @@ import (
"encoding/hex" "encoding/hex"
) )
// The size of the standard buffer.
const DefaultMsgSize = 4096 const DefaultMsgSize = 4096
// A manually-unpacked version of (id, bits). // A manually-unpacked version of (id, bits).
@ -54,14 +54,6 @@ type Msg struct {
Extra []RR 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. // Map of strings for each RR wire type.
var Rr_str = map[uint16]string{ var Rr_str = map[uint16]string{
TypeCNAME: "CNAME", TypeCNAME: "CNAME",
@ -89,7 +81,7 @@ var Rr_str = map[uint16]string{
TypeNSEC: "NSEC", TypeNSEC: "NSEC",
TypeDNSKEY: "DNSKEY", TypeDNSKEY: "DNSKEY",
TypeNSEC3: "NSEC3", TypeNSEC3: "NSEC3",
TypeNSEC3PARAM: "NSEC3PARAM", // DNSSEC's bitch TypeNSEC3PARAM: "NSEC3PARAM",
TypeSPF: "SPF", TypeSPF: "SPF",
TypeTKEY: "TKEY", // Meta RR TypeTKEY: "TKEY", // Meta RR
TypeTSIG: "TSIG", // Meta RR TypeTSIG: "TSIG", // Meta RR
@ -98,10 +90,10 @@ var Rr_str = map[uint16]string{
TypeALL: "ANY", // Meta RR 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) 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{ var Class_str = map[uint16]string{
ClassINET: "IN", ClassINET: "IN",
ClassCSNET: "CS", ClassCSNET: "CS",
@ -134,6 +126,12 @@ var rcode_str = map[int]string{
RcodeNotZone: "NOTZONE", 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:]. // Pack a domain name s into msg[off:].
// Domain names are a sequence of counted strings // Domain names are a sequence of counted strings
// split at the dots. They end with a zero-length string. // 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 return n
} }
// Convert a MsgHdr to a string, mimic the way Dig displays headers: // Convert a MsgHdr to a string, mimic the way Dig displays headers:
//;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48404 //;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48404
//;; flags: qr aa rd ra; //;; flags: qr aa rd ra;
@ -904,7 +901,7 @@ func (dns *Msg) Unpack(msg []byte) bool {
return true 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 { func (dns *Msg) String() string {
if dns == nil { if dns == nil {
return "<nil> MsgHdr" return "<nil> MsgHdr"
@ -941,7 +938,7 @@ func (dns *Msg) String() string {
return s return s
} }
// Set an Msg Id to a random value // Set an Msg Id to a random value.
func (m *Msg) SetId() { func (m *Msg) SetId() {
m.Id = uint16(rand.Int()) ^ uint16(time.Nanoseconds()) m.Id = uint16(rand.Int()) ^ uint16(time.Nanoseconds())
} }

View File

@ -13,7 +13,7 @@ const (
) )
// Convert a Ttl to a value. Supported values: 'm' for minutes, 'h' for hours // 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) { func StringToSeconds(ttl string) (sec uint32, ok bool) {
num := "" num := ""
for _, k := range ttl { for _, k := range ttl {

18
tsig.go
View File

@ -10,7 +10,7 @@ import (
"encoding/hex" "encoding/hex"
) )
// Need to lookup the actual codes // HMAC hashing codes. These are transmitted as domain names.
const ( const (
HmacMD5 = "HMAC-MD5.SIG-ALG.REG.INT" HmacMD5 = "HMAC-MD5.SIG-ALG.REG.INT"
HmacSHA1 = "hmac-sha1" HmacSHA1 = "hmac-sha1"
@ -34,8 +34,8 @@ func (rr *RR_TSIG) Header() *RR_Header {
return &rr.Hdr return &rr.Hdr
} }
// TSIG has no official presentation format, but this will suffice.
func (rr *RR_TSIG) String() string { func (rr *RR_TSIG) String() string {
// It has no official presentation format
return rr.Hdr.String() + return rr.Hdr.String() +
" " + rr.Algorithm + " " + rr.Algorithm +
" " + tsigTimeToDate(rr.TimeSigned) + " " + tsigTimeToDate(rr.TimeSigned) +
@ -63,11 +63,11 @@ type tsigWireFmt struct {
OtherData string "fixed-size" OtherData string "fixed-size"
} }
// Return the RR with the TSIG AND include it in the message // Generate the HMAC for message. The TSIG RR is modified
// Generate the HMAC for msg. The TSIG RR is modified
// to include the MAC and MACSize. Note the the msg Id must // to include the MAC and MACSize. Note the the msg Id must
// be set, otherwise the MAC is not correct. // already be set, otherwise the MAC will not be correct when
// The string 'secret' must be encoded in base64 // the message is send.
// The string 'secret' must be encoded in base64.
func (t *RR_TSIG) Generate(m *Msg, secret string) bool { func (t *RR_TSIG) Generate(m *Msg, secret string) bool {
rawsecret, err := packBase64([]byte(secret)) rawsecret, err := packBase64([]byte(secret))
if err != nil { if err != nil {
@ -87,10 +87,10 @@ func (t *RR_TSIG) Generate(m *Msg, secret string) bool {
return true 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 // the TSIG record still attached (as the last rr in the Additional
// section) TODO(mg) // section). Return true on success.
// The secret is a base64 encoded string with a secret // The secret is a base64 encoded string with the secret.
func (t *RR_TSIG) Verify(m *Msg, secret string) bool { func (t *RR_TSIG) Verify(m *Msg, secret string) bool {
// copy the mesg, strip (and check) the tsig rr // copy the mesg, strip (and check) the tsig rr
// perform the opposite of Generate() and then // perform the opposite of Generate() and then

View File

@ -3,12 +3,14 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Extended and bugfixes by Miek Gieben // 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: // Basic usage pattern for creating new Resource Record:
// //
// r := new(RR_TXT) // r := new(RR_TXT)
// r.Hdr = RR_Header{Name: "a.miek.nl", Rrtype: TypeTXT, Class: ClassINET, Ttl: 3600} // 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.TXT = "This is the content of the TXT record"
// //
package dns package dns
import ( import (
@ -20,7 +22,7 @@ import (
// Packet formats // Packet formats
// Wire constants. // Wire constants and supported types.
const ( const (
// valid RR_Header.Rrtype and Question.qtype // valid RR_Header.Rrtype and Question.qtype
TypeA = 1 TypeA = 1
@ -361,7 +363,7 @@ func (rr *RR_CERT) String() string {
" " + rr.Certificate " " + rr.Certificate
} }
// RFC 2672 // See RFC 2672.
type RR_DNAME struct { type RR_DNAME struct {
Hdr RR_Header Hdr RR_Header
Target string "domain-name" Target string "domain-name"
@ -578,7 +580,7 @@ func (rr *RR_NSEC3PARAM) String() string {
return s return s
} }
// RFC 4408 // See RFC 4408
type RR_SPF struct { type RR_SPF struct {
Hdr RR_Header Hdr RR_Header
Txt string Txt string