Split the package in multiple packages

dns - the standard rrtypes and such
    dnssec - validation, keytag calculation, etc
    resolver - for talking to servers
This commit is contained in:
Miek Gieben 2010-12-30 13:42:52 +01:00
parent bc624181dc
commit 15dd65171b
19 changed files with 311 additions and 271 deletions

View File

@ -1,20 +1,28 @@
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
.PHONY: dnssec resolver examples
include $(GOROOT)/src/Make.inc
TARG=dns
GOFILES=\
dns.go\
msg.go\
resolver.go \
types.go\
edns.go\
dnssec.go\
include $(GOROOT)/src/Make.pkg
.PHONY: examples
all: package
gomake -C dnssec package
gomake -C resolver package
examples:
(cd examples; make)
install: $(INSTALLFILES)
gomake -C dnssec install
gomake -C resolver install
dnstest:
gotest
gomake -C dnssec test
gomake -C resolver test

107
dns.go Normal file
View File

@ -0,0 +1,107 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// 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
// * 2671 - EDNS
// * 4033/4034/4035 - DNSSEC + validation functions
// * 1982 - Serial Arithmetic
// * IP6 support
// The package allows full control over what is send out to the DNS.
//
// 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}
//
package dns
import ( "strconv")
const Year68 = 2 << (32 - 1)
type RR interface {
Header() *RR_Header
String() string
}
// An RRset is a slice of RRs.
type RRset []RR
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] }
// DNS resource records.
// There are many types of messages,
// but they all share the same header.
type RR_Header struct {
Name string "domain-name"
Rrtype uint16
Class uint16
Ttl uint32
Rdlength uint16 // length of data after header
}
func (h *RR_Header) Header() *RR_Header {
return h
}
func (h *RR_Header) String() string {
var s string
if h.Rrtype == TypeOPT {
s = ";"
// and maybe other things
}
if len(h.Name) == 0 {
s += ".\t"
} else {
s += h.Name + "\t"
}
s = s + strconv.Itoa(int(h.Ttl)) + "\t"
s = s + class_str[h.Class] + "\t"
s = s + rr_str[h.Rrtype] + "\t"
return s
}
// Or expose the pack/unpack functions??
// Return the rdata of the RR in wireform.
func WireRdata(r RR) ([]byte, bool) {
buf := make([]byte, 4096) // Too large, need to FIX TODO(mg)
off1, ok := packRR(r, buf, 0)
if !ok {
return nil, false
}
start := off1 - int(r.Header().Rdlength)
end := start + int(r.Header().Rdlength)
buf = buf[start:end]
return buf, true
}
// Return the wiredata of a domainname (sans compressions)
func WireDomainName(s string) ([]byte, bool) {
buf := make([]byte, 255)
off, ok := packDomainName(s, buf, 0)
if !ok {
return nil, ok
}
buf = buf[:off]
return buf, ok
}
func WireRR(r RR) ([]byte, bool) {
buf := make([]byte, 4096)
off, ok := packRR(r, buf, 0)
if !ok {
return nil, false
}
buf = buf[:off]
return buf, ok
}

11
dnssec/Makefile Normal file
View File

@ -0,0 +1,11 @@
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
TARG=dns/dnssec
GOFILES=\
dnssec.go\
include $(GOROOT)/src/Make.pkg

View File

@ -1,4 +1,4 @@
package dns
package dnssec
import (
"crypto/sha1"
@ -13,47 +13,46 @@ import (
"strings"
"fmt" //tmp
"os" //tmp
"dns"
)
// DNSSEC encryption algorithm codes.
const (
// RFC1982 serial arithmetic
year68 = 2 << (32 - 1)
// DNSSEC algorithms
AlgRSAMD5 = 1
AlgDH = 2
AlgDSA = 3
AlgECC = 4
AlgRSASHA1 = 5
AlgRSASHA256 = 8
AlgRSASHA512 = 10
AlgECCGOST = 12
)
// An RRset is just a bunch a RRs. No restrictions
type RRset []RR
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] }
// DNSSEC hashing codes.
const (
HashSHA1 = iota
HashSHA256
HashGOST94
)
// Convert an DNSKEY record to a DS record.
func (k *RR_DNSKEY) ToDS(hash int) *RR_DS {
ds := new(RR_DS)
func ToDS(k *dns.RR_DNSKEY, hash int) *dns.RR_DS {
ds := new(dns.RR_DS)
ds.Hdr.Name = k.Hdr.Name
ds.Hdr.Class = k.Hdr.Class
ds.Hdr.Ttl = k.Hdr.Ttl
ds.Hdr.Rrtype = TypeDS
ds.KeyTag = k.KeyTag()
ds.Algorithm = k.Algorithm
ds.DigestType = uint8(hash)
ds.KeyTag = KeyTag(k)
// Generic function that gives back a buffer with the rdata?? TODO(MG)
// Find the rdata portion for the key (again)
// (keytag does this too)
buf := make([]byte, 4096)
off1, ok := packRR(k, buf, 0)
wire, ok := dns.WireRdata(k)
if !ok {
return nil
}
start := off1 - int(k.Header().Rdlength)
end := start + int(k.Header().Rdlength)
// buf[start:end] is the rdata of the key
buf = buf[start:end]
owner := make([]byte, 255)
off1, ok = packDomainName(k.Hdr.Name, owner, 0)
if !ok {
owner,ok1 := dns.WireDomainName(k.Hdr.Name)
if !ok1 {
return nil
}
/*
@ -62,9 +61,8 @@ func (k *RR_DNSKEY) ToDS(hash int) *RR_DS {
* "|" denotes concatenation
* DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
*/
owner = owner[:off1]
// digest buffer
digest := append(owner, buf...)
digest := append(owner, wire...) // another copy TODO(mg)
switch hash {
case HashSHA1:
@ -85,7 +83,7 @@ func (k *RR_DNSKEY) ToDS(hash int) *RR_DS {
}
// Calculate the keytag of the DNSKEY.
func (k *RR_DNSKEY) KeyTag() uint16 {
func KeyTag(k *dns.RR_DNSKEY) uint16 {
var keytag int
switch k.Algorithm {
case AlgRSAMD5:
@ -95,7 +93,7 @@ func (k *RR_DNSKEY) KeyTag() uint16 {
// Might encode header length too, so that
// we dont need to pack/unpack all the time
// Or a shadow structure, with the wiredata and header
wire, ok := wireRdata(k)
wire, ok := dns.WireRdata(k)
if !ok {
return 0
}
@ -114,11 +112,11 @@ func (k *RR_DNSKEY) KeyTag() uint16 {
// Validate an rrset with the signature and key. This is the
// cryptographic test, the validity period most be check separately.
func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool {
func Verify(s *dns.RR_RRSIG, k *dns.RR_DNSKEY, rrset dns.RRset) bool {
// Frist the easy checks
if s.KeyTag != k.KeyTag() {
if s.KeyTag != KeyTag(k) {
println(s.KeyTag)
println(k.KeyTag())
println(KeyTag(k))
return false
}
if s.Hdr.Class != k.Hdr.Class {
@ -149,8 +147,8 @@ func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool {
signeddata := make([]byte, 10240) // 10 Kb??
// Copy the sig, except the rrsig data
// Can this be done easier? TODO(mg)
s1 := &RR_RRSIG{s.Hdr, s.TypeCovered, s.Algorithm, s.Labels, s.OrigTtl, s.Expiration, s.Inception, s.KeyTag, s.SignerName, ""}
buf, ok := wireRdata(s1)
s1 := &dns.RR_RRSIG{s.Hdr, s.TypeCovered, s.Algorithm, s.Labels, s.OrigTtl, s.Expiration, s.Inception, s.KeyTag, s.SignerName, ""}
buf, ok := dns.WireRdata(s1)
if !ok {
return false
}
@ -165,10 +163,10 @@ func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool {
h.Name = strings.ToLower(h.Name)
// 6.2. Canonical RR Form. (3) - domain rdata to lowercaser
switch h.Rrtype {
case TypeNS, TypeCNAME, TypeSOA, TypeMB, TypeMG, TypeMR, TypePTR:
case TypeHINFO, TypeMINFO, TypeMX /* TypeRP, TypeAFSDB, TypeRT */ :
case TypeSIG /* TypePX, TypeNXT /* TypeNAPTR, TypeKX */ :
case TypeSRV, /* TypeDNAME, TypeA6 */ TypeRRSIG, TypeNSEC:
case dns.TypeNS, dns.TypeCNAME, dns.TypeSOA, dns.TypeMB, dns.TypeMG, dns.TypeMR, dns.TypePTR:
case dns.TypeHINFO, dns.TypeMINFO, dns.TypeMX /* dns.TypeRP, dns.TypeAFSDB, dns.TypeRT */ :
case dns.TypeSIG /* dns.TypePX, dns.TypeNXT /* dns.TypeNAPTR, dns.TypeKX */ :
case dns.TypeSRV, /* dns.TypeDNAME, dns.TypeA6 */ dns.TypeRRSIG, dns.TypeNSEC:
/* do something */
// lower case the strings rdata //
@ -177,10 +175,11 @@ func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool {
// 6.2. Canonical RR Form. (5) - origTTL
ttl := h.Ttl
h.Ttl = s.OrigTtl
off, ok = packRR(r, signeddata, off)
wire, ok1 := dns.WireRR(r)
h.Ttl = ttl // restore the order in the universe
h.Name = name
if !ok {
wire = wire // fix this
if !ok1 {
println("Failure to pack")
return false
}
@ -223,22 +222,22 @@ func (s *RR_RRSIG) Verify(rrset RRset, k *RR_DNSKEY) bool {
}
// Using RFC1982 calculate if a signature period is valid
func (s *RR_RRSIG) PeriodOK() bool {
func PeriodOK(s *dns.RR_RRSIG) 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)
modi := (int64(s.Inception) - utc) / dns.Year68
mode := (int64(s.Expiration) - utc) / dns.Year68
ti := int64(s.Inception) + (modi * dns.Year68)
te := int64(s.Expiration) + (mode * dns.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 {
utc := time.UTC().Seconds()
mod := (int64(t) - utc) / year68
// If needed assume wrap around(s)
ti := time.SecondsToUTC(int64(t) + (mod * year68)) // abs()? TODO
return ti.Format("20060102030405")
// Map for algorithm names.
var alg_str = map[uint8]string{
AlgRSAMD5: "RSAMD5",
AlgDH: "DH",
AlgDSA: "DSA",
AlgRSASHA1: "RSASHA1",
AlgRSASHA256: "RSASHA256",
AlgRSASHA512: "RSASHA512",
AlgECCGOST: "ECC-GOST",
}

View File

@ -1,15 +1,16 @@
package dns
package dnssec
import (
"testing"
"fmt"
"os"
"dns"
)
func TestSecure(t *testing.T) {
// once this was valid
soa := new(RR_SOA)
soa.Hdr = RR_Header{"Miek.nl.", TypeSOA, ClassINET, 875, 0}
soa := new(dns.RR_SOA)
soa.Hdr = dns.RR_Header{"Miek.nl.", dns.TypeSOA, dns.ClassINET, 875, 0}
soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293513905
@ -18,9 +19,9 @@ func TestSecure(t *testing.T) {
soa.Expire = 604800
soa.Minttl = 86400
sig := new(RR_RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = TypeSOA
sig := new(dns.RR_RRSIG)
sig.Hdr = dns.RR_Header{"miek.nl.", dns.TypeRRSIG, dns.ClassINET, 14400, 0}
sig.TypeCovered = dns.TypeSOA
sig.Algorithm = AlgRSASHA256
sig.Labels = 2
sig.Expiration = 1296098705 // date '+%s' -d"2011-01-27 04:25:05
@ -30,10 +31,9 @@ func TestSecure(t *testing.T) {
sig.SignerName = "miek.nl."
sig.Signature = "kLq/5oFy3Sh5ZxPGFMCyHq8MtN6E17R1Ln9+bJ2Q76YYAxFE8Xlie33A1GFctH2uhzRzJKuP/JSjUkrvGk2rjBm32z9zXtZsKx/4yV0da2nLRm44NOmX6gsP4Yia8mdqPUajjkyLzAzU2bevtesJm0Z65AcmPdq3tUZODdRAcng="
key := new(RR_DNSKEY)
key := new(dns.RR_DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Class = dns.ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
@ -42,7 +42,7 @@ func TestSecure(t *testing.T) {
fmt.Fprintf(os.Stderr, "%v\n%v\n", sig, soa)
// It should validate, at least this month dec 2010
if ! sig.Verify([]RR{soa}, key) {
if ! Verify(sig, key, []dns.RR{soa}) {
t.Log("Failure to validate")
t.Fail()
}

View File

@ -1,22 +1,23 @@
package dns
package dnssec
import (
"testing"
"strings"
"dns"
)
func TestKeyToDS(t *testing.T) {
key := new(RR_DNSKEY)
key := new(dns.RR_DNSKEY)
key.Hdr.Name = "miek.nl"
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Rrtype = dns.TypeDNSKEY
key.Hdr.Class = dns.ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = AlgRSASHA256
key.PubKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
ds := key.ToDS(HashSHA1)
ds := ToDS(key, HashSHA1)
if strings.ToUpper(ds.Digest) != "B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1" {
t.Logf("Wrong DS digest for Sha1\n%v\n", ds)
t.Fail()

View File

@ -1,21 +1,22 @@
package dns
package dnssec
import (
"testing"
"dns"
)
func TestTag(t *testing.T) {
key := new(RR_DNSKEY)
key := new(dns.RR_DNSKEY)
key.Hdr.Name = "miek.nl"
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Rrtype = dns.TypeDNSKEY
key.Hdr.Class = dns.ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = AlgRSASHA256
key.PubKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
tag := key.KeyTag()
tag := KeyTag(key)
if tag != 12051 {
t.Logf("%v\n", key)
t.Logf("Wrong key tag: %d\n", tag)

View File

@ -1,16 +1,16 @@
package dns
package dnssec
import (
"dns"
"testing"
)
func TestSignature(t *testing.T) {
sig := new(RR_RRSIG)
sig := new(dns.RR_RRSIG)
sig.Hdr.Name = "miek.nl."
sig.Hdr.Rrtype = TypeRRSIG
sig.Hdr.Class = ClassINET
sig.Hdr.Class = dns.ClassINET
sig.Hdr.Ttl = 3600
sig.TypeCovered = TypeDNSKEY
sig.TypeCovered = dns.TypeDNSKEY
sig.Algorithm = AlgRSASHA1
sig.Labels = 2
sig.OrigTtl = 4000
@ -21,14 +21,14 @@ func TestSignature(t *testing.T) {
sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
// Should not be valid
if sig.PeriodOK() {
if PeriodOK(sig) {
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() {
if ! PeriodOK(sig) {
t.Log("Should be valid")
t.Fail()
}

View File

@ -8,7 +8,7 @@ import (
func TestEDNS_RR(t *testing.T) {
edns := new(RR_OPT)
edns.Hdr.Name = "." // must . be for edns
edns.Hdr.Rrtype = TypeOPT
edns.Hdr.Rrtype = TypeOPT
edns.Hdr.Class = ClassINET
edns.Hdr.Ttl = 3600
edns.Option = make([]Option, 1)

View File

@ -5,18 +5,19 @@ package main
// (c) Miek Gieben - 2011
import (
"dns"
"dns/resolver"
"os"
"fmt"
"net"
)
func main() {
r := new(dns.Resolver)
qr := dns.NewQuerier(r)
r := new(resolver.Resolver)
qr := resolver.NewQuerier(r)
r.Servers = []string{"127.0.0.1"}
r.Timeout = 2
r.Attempts = 1
var in dns.DnsMsg
var in resolver.DnsMsg
if len(os.Args) != 2 {
fmt.Printf("%s NAMESERVER\n", os.Args[0])
@ -29,13 +30,13 @@ func main() {
// set the resolver to query the NS directly
r.Servers = []string{a.String()}
m.Question[0] = dns.Question{"version.bind.", dns.TypeTXT, dns.ClassCHAOS}
qr <- dns.DnsMsg{m, nil}
qr <- resolver.DnsMsg{m, nil}
in = <-qr
if in.Dns != nil && in.Dns.Answer != nil {
fmt.Printf("%v\n", in.Dns.Answer[0])
}
m.Question[0] = dns.Question{"hostname.bind.", dns.TypeTXT, dns.ClassCHAOS}
qr <- dns.DnsMsg{m, nil}
qr <- resolver.DnsMsg{m, nil}
in = <-qr
if in.Dns != nil && in.Dns.Answer != nil {
fmt.Printf("%v\n", in.Dns.Answer[0])
@ -43,18 +44,18 @@ func main() {
}
// Stop the resolver, send it a null mesg
qr <- dns.DnsMsg{nil, nil}
qr <- resolver.DnsMsg{nil, nil}
<-qr
}
func addresses(qr chan dns.DnsMsg, name string) []net.IP {
func addresses(qr chan resolver.DnsMsg, name string) []net.IP {
m := new(dns.Msg)
m.MsgHdr.Recursion_desired = true //only set this bit
m.Question = make([]dns.Question, 1)
var ips []net.IP
m.Question[0] = dns.Question{os.Args[1], dns.TypeA, dns.ClassINET}
qr <- dns.DnsMsg{m, nil}
qr <- resolver.DnsMsg{m, nil}
in := <-qr
if in.Dns.Rcode != dns.RcodeSuccess {
@ -65,7 +66,7 @@ func addresses(qr chan dns.DnsMsg, name string) []net.IP {
ips = append(ips, a.(*dns.RR_A).A)
}
m.Question[0] = dns.Question{os.Args[1], dns.TypeAAAA, dns.ClassINET}
qr <- dns.DnsMsg{m, nil}
qr <- resolver.DnsMsg{m, nil}
in = <-qr
if in.Dns.Rcode != dns.RcodeSuccess {

View File

@ -2,6 +2,8 @@ package main
import (
"dns"
"dns/dnssec"
"dns/resolver"
"fmt"
)
@ -15,7 +17,7 @@ func main() {
key.Hdr.Ttl = 3600
key.Flags = 257
key.Protocol = 3
key.Algorithm = dns.AlgRSASHA1
key.Algorithm = dnssec.AlgRSASHA1
key.PubKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFq NDzr//kZ"
sig := new(dns.RR_RRSIG)
@ -24,18 +26,18 @@ func main() {
sig.Hdr.Class = dns.ClassINET
sig.Hdr.Ttl = 3600
sig.TypeCovered = dns.TypeDNSKEY
sig.Algorithm = dns.AlgRSASHA1
sig.Algorithm = dnssec.AlgRSASHA1
sig.OrigTtl = 4000
sig.Expiration = 1000
sig.Inception = 800
sig.KeyTag = 34641
sig.SignerName = "miek.nl."
sig.Sig = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFq NDzr//kZ"
sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFq NDzr//kZ"
fmt.Printf("%v", sig)
res := new(dns.Resolver)
ch := dns.NewQuerier(res)
res := new(resolver.Resolver)
ch := resolver.NewQuerier(res)
// configure the resolver
res.Servers = []string{"192.168.1.2"}
@ -49,20 +51,20 @@ func main() {
m.Question = make([]dns.Question, 1)
m.Question[0] = dns.Question{"miek.nl", dns.TypeDS, dns.ClassINET}
ch <- dns.DnsMsg{m, nil}
ch <- resolver.DnsMsg{m, nil}
in := <-ch
fmt.Printf("%v\n", in.Dns)
m.Question[0] = dns.Question{"www.nlnetlabs.nl", dns.TypeRRSIG, dns.ClassINET}
ch <- dns.DnsMsg{m, nil}
ch <- resolver.DnsMsg{m, nil}
in = <-ch
fmt.Printf("%v\n", in.Dns)
m.Question[0] = dns.Question{"xxxx.nlnetlabs.nl", dns.TypeDNSKEY, dns.ClassINET}
ch <- dns.DnsMsg{m, nil}
ch <- resolver.DnsMsg{m, nil}
in = <-ch
fmt.Printf("%v\n", in.Dns)
ch <- dns.DnsMsg{nil, nil}
ch <- resolver.DnsMsg{nil, nil}
<-ch
}

View File

@ -4,13 +4,14 @@ package main
// (c) Miek Gieben - 2011
import (
"dns"
"dns/resolver"
"os"
"fmt"
)
func main() {
r := new(dns.Resolver)
qr := dns.NewQuerier(r)
r := new(resolver.Resolver)
qr := resolver.NewQuerier(r)
r.Servers = []string{"127.0.0.1"}
r.Timeout = 2
r.Attempts = 1
@ -25,7 +26,7 @@ func main() {
m.Question = make([]dns.Question, 1)
m.Question[0] = dns.Question{os.Args[1], dns.TypeMX, dns.ClassINET}
qr <- dns.DnsMsg{m, nil}
qr <- resolver.DnsMsg{m, nil}
in := <-qr
if in.Dns.Rcode != dns.RcodeSuccess {
@ -38,6 +39,6 @@ func main() {
}
// Stop the resolver, send it a null mesg
qr <- dns.DnsMsg{nil, nil}
qr <- resolver.DnsMsg{nil, nil}
<-qr
}

4
msg.go
View File

@ -25,7 +25,7 @@ import (
"encoding/hex"
)
const defaultMsgSize = 4096
const DefaultMsgSize = 4096
// Packing and unpacking.
//
@ -616,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, defaultMsgSize) // TODO, calculate REAL size
msg = make([]byte, DefaultMsgSize) // TODO, calculate REAL size
// Pack it in: header and then the pieces.
off := 0

View File

@ -25,41 +25,6 @@ func TestPackUnpack(t *testing.T) {
t.Fail()
}
key := new(RR_DNSKEY)
key.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600}
key = &RR_DNSKEY{Flags: 257, Protocol: 3, Algorithm: AlgRSASHA1}
key.PubKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
out.Answer[0] = key
msg, ok = out.Pack()
if !ok {
t.Log("Failed to pack msg with DNSKEY")
t.Fail()
}
if !in.Unpack(msg) {
t.Log("Failed to unpack msg with DNSKEY")
t.Fail()
}
sig := new(RR_RRSIG)
sig.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600}
sig = &RR_RRSIG{TypeCovered: TypeDNSKEY, Algorithm: AlgRSASHA1, Labels: 2,
OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.",
Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"}
out.Answer[0] = sig
msg, ok = out.Pack()
if !ok {
t.Log("Failed to pack msg with RRSIG")
t.Fail()
}
if !in.Unpack(msg) {
t.Log("Failed to unpack msg with RRSIG")
t.Fail()
}
edns := new(RR_OPT)
edns.Hdr.Name = "."
edns.Hdr.Rrtype = TypeOPT

12
resolver/Makefile Normal file
View File

@ -0,0 +1,12 @@
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
TARG=dns/resolver
GOFILES=\
resolver.go\
#DEPS=../
include $(GOROOT)/src/Make.pkg

View File

@ -20,13 +20,14 @@
// ch <- DnsMsg{m, nil} // send the query
// in := <-ch // wait for reply
//
package dns
package resolver
import (
"os"
"rand"
"time"
"net"
"dns"
)
// When communicating with a resolver, we use this structure
@ -34,7 +35,7 @@ import (
// 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
Dns *dns.Msg
Error os.Error
}
@ -62,7 +63,7 @@ func query(res *Resolver, msg chan DnsMsg) {
// TODO port number, error checking, robustness
var c net.Conn
var err os.Error
var in *Msg
var in *dns.Msg
var port string
if len(res.Servers) == 0 {
msg <- DnsMsg{nil, nil}
@ -124,7 +125,7 @@ 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) {
func exchange(c net.Conn, m []byte, r *Resolver) (*dns.Msg, os.Error) {
var timeout int64
var attempts int
if r.Mangle != nil {
@ -148,7 +149,7 @@ func exchange(c net.Conn, m []byte, r *Resolver) (*Msg, os.Error) {
}
c.SetReadTimeout(timeout * 1e9) // nanoseconds
buf := make([]byte, defaultMsgSize) // More than enough.
buf := make([]byte, dns.DefaultMsgSize) // More than enough.
n, err = c.Read(buf)
if err != nil {
// More Go foo needed
@ -158,7 +159,7 @@ func exchange(c net.Conn, m []byte, r *Resolver) (*Msg, os.Error) {
return nil, err
}
buf = buf[0:n]
in := new(Msg)
in := new(dns.Msg)
if !in.Unpack(buf) {
continue
}

View File

@ -1,7 +1,8 @@
package dns
package resolver
import (
"testing"
"dns"
)
func TestResolverEdns(t *testing.T) {
@ -12,15 +13,15 @@ func TestResolverEdns(t *testing.T) {
res.Timeout = 2
res.Attempts = 1
m := new(Msg)
m := new(dns.Msg)
m.MsgHdr.Recursion_desired = true //only set this bit
m.Question = make([]Question, 1)
m.Extra = make([]RR, 1)
m.Question = make([]dns.Question, 1)
m.Extra = make([]dns.RR, 1)
// Add EDNS rr
edns := new(RR_OPT)
edns := new(dns.RR_OPT)
edns.Hdr.Name = "." // must . be for edns
edns.Hdr.Rrtype = TypeOPT
edns.Hdr.Rrtype = dns.TypeOPT
// You can handle an OTP RR as any other, but there
// are some convience functions
edns.UDPSize(4096, true)
@ -32,7 +33,7 @@ func TestResolverEdns(t *testing.T) {
// edns.Option[0].Data = "lalalala"
// ask something
m.Question[0] = Question{"nlnetlabs.nl", TypeSOA, ClassINET}
m.Question[0] = dns.Question{"nlnetlabs.nl", dns.TypeSOA, dns.ClassINET}
m.Extra[0] = edns
ch <- DnsMsg{m, nil}
@ -40,7 +41,7 @@ func TestResolverEdns(t *testing.T) {
//// t.Fail()
// t.Log("%v\n", in.Dns)
if in.Dns.Rcode != RcodeSuccess {
if in.Dns.Rcode != dns.RcodeSuccess {
t.Log("Failed to get an valid answer")
t.Fail()
}

View File

@ -1,7 +1,8 @@
package dns
package resolver
import (
"testing"
"dns"
)
@ -11,27 +12,27 @@ func TestResolver(t *testing.T) {
res.Servers = []string{"127.0.0.1"}
m := new(Msg)
m := new(dns.Msg)
m.MsgHdr.Recursion_desired = true //only set this bit
m.Question = make([]Question, 1)
m.Question = make([]dns.Question, 1)
// ask something
m.Question[0] = Question{"miek.nl", TypeSOA, ClassINET}
m.Question[0] = dns.Question{"miek.nl", dns.TypeSOA, dns.ClassINET}
ch <- DnsMsg{m, nil}
in := <-ch
if in.Dns.Rcode != RcodeSuccess {
if in.Dns.Rcode != dns.RcodeSuccess {
t.Log("Failed to get an valid answer")
t.Fail()
t.Logf("%v\n", in)
}
// ask something
m.Question[0] = Question{"www.nlnetlabs.nl", TypeRRSIG, ClassINET}
m.Question[0] = dns.Question{"www.nlnetlabs.nl", dns.TypeRRSIG, dns.ClassINET}
ch <- DnsMsg{m, nil}
in = <-ch
if in.Dns.Rcode != RcodeSuccess {
if in.Dns.Rcode != dns.RcodeSuccess {
t.Log("Failed to get an valid answer")
t.Fail()
t.Logf("%v\n", in)

137
types.go
View File

@ -24,6 +24,7 @@ import (
"net"
"strconv"
"strings"
"time"
)
// Packet formats
@ -106,26 +107,6 @@ const (
_CD = 1 << 4 // checking disabled
)
// DNSSEC encryption algorithm codes.
const (
// DNSSEC algorithms
AlgRSAMD5 = 1
AlgDH = 2
AlgDSA = 3
AlgECC = 4
AlgRSASHA1 = 5
AlgRSASHA256 = 8
AlgRSASHA512 = 10
AlgECCGOST = 12
)
// DNSSEC hashing codes.
const (
HashSHA1 = iota
HashSHA256
HashGOST94
)
// DNS queries.
type Question struct {
Name string "domain-name" // "domain-name" specifies encoding; see packers below
@ -141,45 +122,6 @@ func (q *Question) String() string {
return s
}
// DNS responses (resource records).
// There are many types of messages,
// but they all share the same header.
type RR_Header struct {
Name string "domain-name"
Rrtype uint16
Class uint16
Ttl uint32
Rdlength uint16 // length of data after header
}
func (h *RR_Header) Header() *RR_Header {
return h
}
func (h *RR_Header) String() string {
var s string
if h.Rrtype == TypeOPT {
s = ";"
// and maybe other things
}
if len(h.Name) == 0 {
s += ".\t"
} else {
s += h.Name + "\t"
}
s = s + strconv.Itoa(int(h.Ttl)) + "\t"
s = s + class_str[h.Class] + "\t"
s = s + rr_str[h.Rrtype] + "\t"
return s
}
type RR interface {
Header() *RR_Header
String() string
}
type RR_CNAME struct {
Hdr RR_Header
Cname string "domain-name"
@ -506,29 +448,40 @@ func (rr *RR_NSEC3PARAM) String() string {
// Salt with strings.ToUpper()
}
// Translate the RRSIG's incep. and expir. time to the correct date.
// Taking into account serial arithmetic (RFC 1982)
func timeToDate(t uint32) string {
utc := time.UTC().Seconds()
mod := (int64(t) - utc) / Year68
// If needed assume wrap around(s)
ti := time.SecondsToUTC(int64(t) + (mod * Year68)) // abs()? TODO
return ti.Format("20060102030405")
}
// Map of constructors for each RR wire type.
var rr_mk = map[int]func() RR{
TypeCNAME: func() RR { return new(RR_CNAME) },
TypeHINFO: func() RR { return new(RR_HINFO) },
TypeMB: func() RR { return new(RR_MB) },
TypeMG: func() RR { return new(RR_MG) },
TypeMINFO: func() RR { return new(RR_MINFO) },
TypeMR: func() RR { return new(RR_MR) },
TypeMX: func() RR { return new(RR_MX) },
TypeNS: func() RR { return new(RR_NS) },
TypePTR: func() RR { return new(RR_PTR) },
TypeSOA: func() RR { return new(RR_SOA) },
TypeTXT: func() RR { return new(RR_TXT) },
TypeSRV: func() RR { return new(RR_SRV) },
TypeA: func() RR { return new(RR_A) },
TypeAAAA: func() RR { return new(RR_AAAA) },
TypeOPT: func() RR { return new(RR_OPT) },
TypeDS: func() RR { return new(RR_DS) },
TypeRRSIG: func() RR { return new(RR_RRSIG) },
TypeNSEC: func() RR { return new(RR_NSEC) },
TypeDNSKEY: func() RR { return new(RR_DNSKEY) },
TypeNSEC3: func() RR { return new(RR_NSEC3) },
TypeNSEC3PARAM: func() RR { return new(RR_NSEC3PARAM) },
TypeCNAME: func() RR { return new(RR_CNAME) },
TypeHINFO: func() RR { return new(RR_HINFO) },
TypeMB: func() RR { return new(RR_MB) },
TypeMG: func() RR { return new(RR_MG) },
TypeMINFO: func() RR { return new(RR_MINFO) },
TypeMR: func() RR { return new(RR_MR) },
TypeMX: func() RR { return new(RR_MX) },
TypeNS: func() RR { return new(RR_NS) },
TypePTR: func() RR { return new(RR_PTR) },
TypeSOA: func() RR { return new(RR_SOA) },
TypeTXT: func() RR { return new(RR_TXT) },
TypeSRV: func() RR { return new(RR_SRV) },
TypeA: func() RR { return new(RR_A) },
TypeAAAA: func() RR { return new(RR_AAAA) },
TypeOPT: func() RR { return new(RR_OPT) },
TypeDS: func() RR { return new(RR_DS) },
TypeRRSIG: func() RR { return new(RR_RRSIG) },
TypeNSEC: func() RR { return new(RR_NSEC) },
TypeDNSKEY: func() RR { return new(RR_DNSKEY) },
TypeNSEC3: func() RR { return new(RR_NSEC3) },
TypeNSEC3PARAM: func() RR { return new(RR_NSEC3PARAM) },
}
// Map of strings for each RR wire type.
@ -555,27 +508,3 @@ var rr_str = map[uint16]string{
TypeNSEC3: "NSEC3",
TypeNSEC3PARAM: "NSEC3PARAM",
}
// Map for algorithm names.
var alg_str = map[uint8]string{
AlgRSAMD5: "RSAMD5",
AlgDH: "DH",
AlgDSA: "DSA",
AlgRSASHA1: "RSASHA1",
AlgRSASHA256: "RSASHA256",
AlgRSASHA512: "RSASHA512",
AlgECCGOST: "ECC-GOST",
}
// Return the rdata of the RR in wireform.
func wireRdata(r RR) ([]byte, bool) {
buf := make([]byte, 4096) // Too large, need to FIX TODO(mg)
off1, ok := packRR(r, buf, 0)
if !ok {
return nil, false
}
start := off1 - int(r.Header().Rdlength)
end := start + int(r.Header().Rdlength)
buf = buf[start:end]
return buf, true
}