More cleansup and robustness
* make the resolver more robust * more dnssec functions
This commit is contained in:
parent
73cc848e00
commit
4ed14b9785
12
Makefile
12
Makefile
|
@ -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
13
README
|
@ -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
4
TODO
|
@ -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?
|
||||
|
|
30
dnssec.go
30
dnssec.go
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
23
edns.go
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
27
keytest.go
27
keytest.go
|
@ -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
4
msg.go
|
@ -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
|
||||
|
|
23
readtest.go
23
readtest.go
|
@ -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)
|
||||
}
|
53
resolver.go
53
resolver.go
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
16
types.go
16
types.go
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue