More cleansup and robustness

* make the resolver more robust
* more dnssec functions
This commit is contained in:
Miek Gieben 2010-12-28 09:41:54 +01:00
parent 73cc848e00
commit 4ed14b9785
16 changed files with 128 additions and 137 deletions

View File

@ -18,15 +18,3 @@ include $(GOROOT)/src/Make.pkg
examples:
(cd examples; make)
progs: dnssectest keytest readtest
# too lazy to lookup how this works again in Makefiles
dnssectest: dnssectest.go $(GOFILES)
6g -I _obj dnssectest.go && 6l -L _obj -o dnssectest dnssectest.6
keytest: keytest.go $(GOFILES)
6g -I _obj keytest.go && 6l -L _obj -o keytest keytest.6
readtest: readtest.go $(GOFILES)
6g -I _obj readtest.go && 6l -L _obj -o readtest readtest.6

13
README
View File

@ -1,19 +1,14 @@
Alternative aproach to a DNS library
Much more control as you prepare a dns packet and then call the resolver
to send it.
The library is asynchronise (thanks to Go) from the get go.
Alternative (more granular) approach to a DNS library
Implemented RFCS:
* RFC2671, EDNS
* RFC1034/1035
* RFC4033/4034/4035 (todo: validation)
* RFC5155 (NSEC)
* RFC4033/4034/4035
* RFC5155 (NSEC3)
Loosly based upon:
Loosely based upon:
* ldns
* NSD
* Net::DNS

4
TODO
View File

@ -1,9 +1,9 @@
Todo:
* parse RRs from strings AToRR()
* DNSSEC validation
* NSEC(3) secure denial of existence
* Unknown RRs
* fix os.Erros usage
* AXFR/IXFR support
Tesing:
* EDNS0
@ -12,4 +12,4 @@ Tesing:
Issues:
* shortened ipv6 addresses are not parsed correctly
* quoted quotes in txt records
* divide the code in multiple packages? dns, dnssec
* divide the code in multiple packages? dns, dnssec?

View File

@ -2,6 +2,7 @@ package dns
import (
"crypto/sha1"
"crypto/sha256"
"encoding/hex"
"time"
"io"
@ -59,7 +60,9 @@ func (k *RR_DNSKEY) ToDS(hash int) *RR_DS {
io.WriteString(s, string(digest))
ds.Digest = hex.EncodeToString(s.Sum())
case HashSHA256:
s := sha256.New()
io.WriteString(s, string(digest))
ds.Digest = hex.EncodeToString(s.Sum())
case HashGOST94:
default:
@ -69,7 +72,7 @@ func (k *RR_DNSKEY) ToDS(hash int) *RR_DS {
return ds
}
// Calculate the keytag of the DNSKEY
// Calculate the keytag of the DNSKEY.
func (k *RR_DNSKEY) KeyTag() uint16 {
var keytag int
switch k.Algorithm {
@ -101,13 +104,22 @@ func (k *RR_DNSKEY) KeyTag() uint16 {
return uint16(keytag)
}
// Validate an rrset with the signature and key. Note the
// signature validate period is NOT checked. Used
// ValidSignaturePeriod for that
func (s *RR_RRSIG) Valid(rrset []RR, key *RR_DNSKEY) bool {
// Validate an rrset with the signature and key. This is the
// cryptographic test, the validity period most be check separately.
func (s *RR_RRSIG) Secure(rrset []RR, key *RR_DNSKEY) bool {
return false
}
// Using RFC1982 calculate if a signature period is valid
func (s *RR_RRSIG) PeriodOK() 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)
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 {
@ -119,9 +131,3 @@ func timeToDate(t uint32) string {
return ti.Format("20060102030405")
}
// Work on a signature RR_RRSIG
// Using RFC1982 calculate if a signature is valid
func ValidSignaturePeriod(start, end uint32) bool {
utc := time.UTC().Seconds() // maybe as parameter?? TODO MG
return int64(start) <= utc && utc <= int64(end)
}

35
dnssec_test.go Normal file
View File

@ -0,0 +1,35 @@
package dns
import (
"testing"
)
func TestSignature(t *testing.T) {
sig := new(RR_RRSIG)
sig.Hdr.Name = "miek.nl."
sig.Hdr.Rrtype = TypeRRSIG
sig.Hdr.Class = ClassINET
sig.Hdr.Ttl = 3600
sig.TypeCovered = TypeDNSKEY
sig.Algorithm = AlgRSASHA1
sig.Labels = 2
sig.OrigTtl = 4000
sig.Expiration = 1000 //Thu Jan 1 02:06:40 CET 1970
sig.Inception = 800 //Thu Jan 1 01:13:20 CET 1970
sig.KeyTag = 34641
sig.SignerName = "miek.nl."
sig.Sig = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
// Should not be valid
if sig.PeriodOK() {
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() {
t.Log("Should be valid")
t.Fail()
}
}

23
edns.go
View File

@ -1,5 +1,3 @@
// EDNS0 OTP RR implementation. Define the OPT RR and some
// convience functions to operate on it.
package dns
import (
@ -21,16 +19,17 @@ type Option struct {
Data string "hex"
}
/* EDNS extended RR.
This is the EDNS0 Header
Name string "domain-name"
Opt uint16 // was type, but is always TypeOPT
UDPSize uint16 // was class
ExtendedRcode uint8 // was TTL
Version uint8 // was TTL
Z uint16 // was TTL (all flags should be put here)
Rdlength uint16 // length of data after the header
*/
/*
* EDNS extended RR.
* This is the EDNS0 Header
* Name string "domain-name"
* Opt uint16 // was type, but is always TypeOPT
* UDPSize uint16 // was class
* ExtendedRcode uint8 // was TTL
* Version uint8 // was TTL
* Z uint16 // was TTL (all flags should be put here)
* Rdlength uint16 // length of data after the header
*/
type RR_OPT struct {
Hdr RR_Header

View File

@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
all: mx chaos
all: mx chaos dnssectest
# too lazy to lookup how this works again in Makefiles
mx: mx.go
@ -10,3 +10,7 @@ mx: mx.go
chaos: chaos.go
6g -I ../_obj chaos.go && 6l -L ../_obj -o chaos chaos.6
dnssectest: dnssectest.go
6g -I ../_obj dnssectest.go && 6l -L ../_obj -o dnssectest dnssectest.6

View File

@ -1,27 +0,0 @@
package main
import (
"dns"
"fmt"
)
func main() {
key := new(dns.RR_DNSKEY)
key.Hdr.Name = "miek.nl"
key.Hdr.Rrtype = dns.TypeDNSKEY
key.Hdr.Class = dns.ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = dns.AlgRSASHA256
key.PubKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
tag := key.KeyTag()
fmt.Printf("%v\n", key)
fmt.Printf("Wrong key tag: %d\n", tag)
m := new(dns.Msg)
m.Ns = make([]dns.RR, 1)
m.Ns[0] = key
m.Pack()
}

4
msg.go
View File

@ -25,6 +25,8 @@ import (
"encoding/hex"
)
const defaultMsgSize = 4096
// Packing and unpacking.
//
// All the packers and unpackers take a (msg []byte, off int)
@ -614,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, defaultSize) // TODO, calculate REAL size
msg = make([]byte, defaultMsgSize) // TODO, calculate REAL size
// Pack it in: header and then the pieces.
off := 0

View File

@ -1,23 +0,0 @@
package main
import (
"dns"
"fmt"
)
func main() {
key := new(dns.RR_DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = dns.TypeDNSKEY
key.Hdr.Class = dns.ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = dns.AlgRSASHA256
key.PubKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
fmt.Printf("%v\n", key)
s := "miek.nl. 3600 IN DNSKEY 256 3 8 AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
dns.ParseString(s)
}

View File

@ -7,21 +7,18 @@
// For every reply the resolver answers by sending the
// received packet (with a possible error) back on the channel.
//
// Basic usage pattern:
// Basic usage pattern for setting up a resolver:
//
// res := new(Resolver)
// ch := NewQuerier(res) // start new resolver
//
// ch := NewQuerier(res) // start new resolver
// res.Servers = []string{"127.0.0.1"} // set the nameserver
// res.Timeout = 2 // some optional extra config
// res.Attempts = 1
//
// m := new(Msg) // prepare a new message
// m := new(Msg) // prepare a new message
// m.MsgHdr.Recursion_desired = true // header bits
// m.Question = make([]Question, 1) // 1 RR in question sec.
// m.Question[0] = Question{"miek.nl", TypeSOA, ClassINET}
// ch <- DnsMsg{m, nil} // send the query
// in := <-ch // wait for reply
// ch <- DnsMsg{m, nil} // send the query
// in := <-ch // wait for reply
//
package dns
@ -32,12 +29,9 @@ import (
"net"
)
const defaultSize = 4096
// When communicating with a resolver, we use this structure
// to send packets to it, when sending Error must be nil.
// A resolver responds with a simular message and a possible
// error.
// to send packets to it, for sending Error must be nil.
// 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
@ -69,6 +63,17 @@ func query(res *Resolver, msg chan DnsMsg) {
var c net.Conn
var err os.Error
var in *Msg
var port string
if len(res.Servers) == 0 {
msg <- DnsMsg{nil, nil}
return
}
if res.Port == "" {
port = "53"
} else {
port = res.Port
}
for {
select {
case out := <-msg: //msg received
@ -89,8 +94,7 @@ func query(res *Resolver, msg chan DnsMsg) {
}
for i := 0; i < len(res.Servers); i++ {
// server := res.Servers[i] + ":" + res.Port
server := res.Servers[i] + ":53"
server := res.Servers[i] + ":" + port
if res.Tcp == true {
c, cerr = net.Dial("tcp", "", server)
} else {
@ -121,19 +125,30 @@ 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) {
var timeout int64
var attempts int
if r.Mangle != nil {
m = r.Mangle(m)
}
if r.Timeout == 0 {
timeout = 1
} else {
timeout = int64(r.Timeout)
}
if r.Attempts == 0 {
attempts = 1
} else {
attempts = r.Attempts
}
for attempt := 0; attempt < r.Attempts; attempt++ {
for a:= 0; a < attempts; a++ {
n, err := c.Write(m)
if err != nil {
return nil, err
}
c.SetReadTimeout(int64(r.Timeout) * 1e9) // nanoseconds
// EDNS TODO
buf := make([]byte, defaultSize) // More than enough.
c.SetReadTimeout(timeout * 1e9) // nanoseconds
buf := make([]byte, defaultMsgSize) // More than enough.
n, err = c.Read(buf)
if err != nil {
// More Go foo needed

View File

@ -26,8 +26,6 @@ func TestResolverEdns(t *testing.T) {
edns.UDPSize(4096, true)
edns.DoBit(true, true)
// edns.Nsid("mieks-server", true)
// edns.Hdr.Class = ClassINET
// edns.Hdr.Ttl = 3600
// no options for now
// edns.Option = make([]Option, 1)
// edns.Option[0].Code = OptionCodeNSID

View File

@ -10,8 +10,6 @@ func TestResolver(t *testing.T) {
ch := NewQuerier(res)
res.Servers = []string{"127.0.0.1"}
res.Timeout = 2
res.Attempts = 1
m := new(Msg)
m.MsgHdr.Recursion_desired = true //only set this bit

View File

@ -14,17 +14,22 @@ func TestSignature(t *testing.T) {
sig.Algorithm = AlgRSASHA1
sig.Labels = 2
sig.OrigTtl = 4000
sig.Expiration = 1000
sig.Inception = 800
sig.Expiration = 1000 //Thu Jan 1 02:06:40 CET 1970
sig.Inception = 800 //Thu Jan 1 01:13:20 CET 1970
sig.KeyTag = 34641
sig.SignerName = "miek.nl."
sig.Sig = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
// Should not be valid
if ValidSignaturePeriod(sig.Inception, sig.Expiration) {
if sig.PeriodOK() {
t.Log("Should not be valid")
t.Fail()
} else {
t.Logf("Valid sig period:\n%v\n", sig)
}
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() {
t.Log("Should be valid")
t.Fail()
}
}

View File

@ -4,23 +4,19 @@
// Extended and bugfixes by Miek Gieben
// Package dns implements a full featured interface to the DNS.
// Supported RFCs include:
// Supported RFCs and features include:
// * 1034/1035
// * 2671 - EDNS
// * 4033/4034/4035 - DNSSEC + validation functions
// * 1982 - Serial Arithmetic
// * IP6 support
// The package allow full control over what is send out to the DNS.
// The package allows full control over what is send out to the DNS.
//
// DNS RR types definitions. See RFC 1035/.../4034 and many more.
// To create quad-A record: "a.miek.nl" IN AAAA 2001:7b8:206:1:200:39ff:fe59:b187
// Basic usage pattern for creating new Resource Record:
//
// Basic usage pattern:
//
// import "net" // for IP functions
// r := new(RR_AAAA)
// r.AAAA = net.ParseIP("2001:7b8:206:1:200:39ff:fe59:b187").To16()
// r.Hdr = RR_Header{Name: "a.miek.nl", Rrtype: TypeAAAA, Class: ClassINET, Ttl: 3600}
// 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