Documentation!

Make the packet documentation much nicer.
This commit is contained in:
Miek Gieben 2010-12-25 11:14:11 +01:00
parent 7d4a26deac
commit 65caf6f891
11 changed files with 160 additions and 188 deletions

View File

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

View File

@ -5,23 +5,22 @@ import "time"
// All DNSSEC verification
const (
Year68 = 2 << (32 - 1)
year68 = 2 << (32 - 1)
)
// Translate the RRSIG's incep. and expir. time
// to the correct date, taking into account serial
// arithmetic
// 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
mod := (int64(t) - utc) / year68
// If needed assume wrap around(s)
ti := time.SecondsToUTC(int64(t) + (mod * Year68)) // abs()? TODO
// If needed assume wrap around(s)
ti := time.SecondsToUTC(int64(t) + (mod * year68)) // abs()? TODO
return ti.Format("20060102030405")
}
// Is the signature (RRSIG) valid?
// 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)
utc := time.UTC().Seconds() // maybe as parameter?? TODO MG
return int64(start) <= utc && utc <= int64(end)
}

View File

@ -1,10 +1,10 @@
package dns
// Implementation of EDNS0, RFC 2671
// EDNS0 option codes
const (
OptionCodeLLQ = 1
OptionCodeUL = 2
OptionCodeNSID = 3
OptionCodeLLQ = 1 // Not used
OptionCodeUL = 2 // Not used
OptionCodeNSID = 3 // NSID, RFC5001
// EDNS flag bits (put in Z section)
_DO = 1 << 15 // dnssec ok
)

View File

@ -2,77 +2,78 @@ package main
// Print the version.bind and hostname.bind for each
// address of NAMESERVER
// (c) Miek Gieben - 2011
import (
"dns"
"os"
"fmt"
"net"
"os"
"fmt"
"net"
)
func main() {
r := new(dns.Resolver)
qr := dns.NewQuerier(r)
r.Servers = []string{"127.0.0.1"}
r.Timeout = 2
r.Attempts = 1
var in dns.DnsMsg
r.Servers = []string{"127.0.0.1"}
r.Timeout = 2
r.Attempts = 1
var in dns.DnsMsg
if len(os.Args) != 2 {
fmt.Printf("%s NAMESERVER\n", os.Args[0])
os.Exit(1)
}
if len(os.Args) != 2 {
fmt.Printf("%s NAMESERVER\n", os.Args[0])
os.Exit(1)
}
m := new(dns.Msg)
m.Question = make([]dns.Question, 1)
for _, a := range addresses(qr, os.Args[0]) {
// 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}
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}
in = <-qr
if in.Dns != nil && in.Dns.Answer != nil {
fmt.Printf("%v\n", in.Dns.Answer[0])
}
}
m := new(dns.Msg)
m.Question = make([]dns.Question, 1)
for _, a := range addresses(qr, os.Args[0]) {
// 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}
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}
in = <-qr
if in.Dns != nil && in.Dns.Answer != nil {
fmt.Printf("%v\n", in.Dns.Answer[0])
}
}
// Stop the resolver, send it a null mesg
qr <- dns.DnsMsg{nil, nil}
<-qr
// Stop the resolver, send it a null mesg
qr <- dns.DnsMsg{nil, nil}
<-qr
}
func addresses(qr chan dns.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 := 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}
in := <-qr
m.Question[0] = dns.Question{os.Args[1], dns.TypeA, dns.ClassINET}
qr <- dns.DnsMsg{m, nil}
in := <-qr
if in.Dns.Rcode != dns.RcodeSuccess {
return nil
}
// Stuff must be in the answer section
for _, a := range in.Dns.Answer {
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}
in = <-qr
if in.Dns.Rcode != dns.RcodeSuccess {
return nil
}
// Stuff must be in the answer section
for _, a := range in.Dns.Answer {
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}
in = <-qr
if in.Dns.Rcode != dns.RcodeSuccess {
return nil
}
// Stuff must be in the answer section
for _, a := range in.Dns.Answer {
ips = append(ips, a.(*dns.RR_AAAA).AAAA)
}
return ips
if in.Dns.Rcode != dns.RcodeSuccess {
return nil
}
// Stuff must be in the answer section
for _, a := range in.Dns.Answer {
ips = append(ips, a.(*dns.RR_AAAA).AAAA)
}
return ips
}

View File

@ -1,6 +1,7 @@
package main
// Print the MX records of a domain
// (c) Miek Gieben - 2011
import (
"dns"
"os"

View File

@ -1,54 +0,0 @@
package main
import (
"dns"
"time"
"fmt"
)
const (
NLOOP = 5
)
func identity(msg []byte) []byte {
return msg
}
func byteflip(msg []byte) []byte {
msg[len(msg) - 1] = 0
msg[2] = 0 // See what happens
return msg
}
func bitflip(msg []byte) []byte {
return msg
}
func main() {
res := new(dns.Resolver)
ch := dns.NewQuerier(res)
// configure the resolver
res.Servers = []string{"192.168.1.2"}
res.Timeout = 2
res.Attempts = 1
res.Mangle = byteflip
// Setup done, now for some real work
// Create a new message
m := new(dns.Msg)
m.MsgHdr.Recursion_desired = true //only set this bit
m.Question = make([]dns.Question, 1)
// ask something
m.Question[0] = dns.Question{"miek.nl", dns.TypeSOA, dns.ClassINET}
ch <- dns.DnsMsg{m, nil}
// wait for an reply
in := <-ch
fmt.Printf("%v\n", in.Dns)
ch <- dns.DnsMsg{nil, nil}
time.Sleep(1.0e9) // wait for Go routine to do something
}

18
msg.go
View File

@ -4,8 +4,7 @@
// DNS packet assembly. See RFC 1035.
//
// This is intended to support name resolution during net.Dial.
// It doesn't have to be blazing fast.
// This is not (yet) optimized for speed.
//
// Rather than write the usual handful of routines to pack and
// unpack every message that can appear on the wire, we use
@ -13,13 +12,7 @@
// use it. Thus, if in the future we need to define new message
// structs, no new pack/unpack/printing code needs to be written.
//
// The first half of this file defines the DNS message formats.
// The second half implements the conversion to and from wire format.
// A few of the structure elements have string tags to aid the
// generic pack/unpack routines.
//
// TODO(miekg):
//
package dns
import (
@ -171,7 +164,6 @@ Loop:
return s, off1, true
}
// TODO(rsc): Move into generic library?
// Pack a reflect.StructValue into msg. Struct members can only be uint8, uint16, uint32, string,
// slices and other (often anonymous) structs.
func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) {
@ -503,6 +495,8 @@ type MsgHdr struct {
Rcode int
}
// Convert a MsgHdr to a string, mimic the way dig displays
// headers:
//;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48404
//;; flags: qr aa rd ra;
func (h *MsgHdr) String() string {
@ -544,6 +538,7 @@ func (h *MsgHdr) String() string {
return s
}
// The layout of a DNS message.
type Msg struct {
MsgHdr
Question []Question
@ -598,7 +593,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, 4096) // TODO, calculate REAL size
msg = make([]byte, defaultSize) // TODO, calculate REAL size
// Pack it in: header and then the pieces.
off := 0
@ -665,6 +660,7 @@ func (dns *Msg) Unpack(msg []byte) bool {
return true
}
// Convert a complete message to a string. Again use dig-like output
func (dns *Msg) String() string {
if dns == nil {
return "<nil> MsgHdr"

View File

@ -5,9 +5,23 @@
// DNS resolver client: see RFC 1035.
// A dns resolver is to be run as a seperate goroutine.
// For every reply the resolver answers by sending the
// received packet back on the channel.
// TODO: resolverFromConf (/etc/resolv.conf) parsing??
// received packet (with a possible error) back on the channel.
// A simple resolver can be setup with the following code:
//
// res := 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.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
//
package dns
import (
@ -17,37 +31,40 @@ import (
"net"
)
// For communicating with a resolver
// A nil msg ends the resolver goroutine
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.
// Sending a nil message instructs to resolver to stop.
type DnsMsg struct {
Dns *Msg
Dns *Msg
Error os.Error
}
type Resolver struct {
Servers []string // servers to use
rtt []int // round trip times for each NS (TODO)
Search []string // suffixes to append to local name
Port string // what port to use
Ndots int // number of dots in name to trigger absolute lookup
Timeout int // seconds before giving up on packet
Attempts int // lost packets before giving up on server
Rotate bool // round robin among servers
Tcp bool // use TCP
Mangle func([]byte) []byte // Mangle the packet
Servers []string // servers to use
Search []string // suffixes to append to local name
Port string // what port to use
Ndots int // number of dots in name to trigger absolute lookup
Timeout int // seconds before giving up on packet
Attempts int // lost packets before giving up on server
Rotate bool // round robin among servers
Tcp bool // use TCP
Mangle func([]byte) []byte // Mangle the packet
}
// Start a new querier as a goroutine, return
// the communication channel
// Start a new resolver as a goroutine, return the communication channel
func NewQuerier(res *Resolver) (ch chan DnsMsg) {
ch = make(chan DnsMsg)
go query(res, ch)
return
}
// do it
// The query function.
func query(res *Resolver, msg chan DnsMsg) {
// TODO port number, error checking, robustness
var c net.Conn
var err os.Error
var in *Msg
@ -56,8 +73,8 @@ func query(res *Resolver, msg chan DnsMsg) {
case out := <-msg: //msg received
if out.Dns == nil {
// nil message, quit the goroutine
msg <- DnsMsg{nil, nil}
close(msg)
msg <- DnsMsg{nil, nil}
close(msg)
return
}
@ -71,7 +88,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] + ":" + res.Port
server := res.Servers[i] + ":53"
if res.Tcp == true {
c, cerr = net.Dial("tcp", "", server)
@ -100,8 +117,6 @@ func query(res *Resolver, msg chan DnsMsg) {
return
}
// Use Pack to create a DNS question, from a msg
// 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) {
@ -117,7 +132,7 @@ func exchange(c net.Conn, m []byte, r *Resolver) (*Msg, os.Error) {
c.SetReadTimeout(int64(r.Timeout) * 1e9) // nanoseconds
// EDNS TODO
buf := make([]byte, 2000) // More than enough.
buf := make([]byte, defaultSize) // More than enough.
n, err = c.Read(buf)
if err != nil {
// More Go foo needed

View File

@ -21,8 +21,13 @@ func TestResolverEdns(t *testing.T) {
edns := new(RR_OPT)
edns.Hdr.Name = "." // must . be for edns
edns.Hdr.Rrtype = TypeOPT
edns.Hdr.Class = ClassINET
edns.Hdr.Ttl = 3600
// You can handle an OTP RR as any other, but there
// are some convience functions
ends.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
@ -30,7 +35,7 @@ func TestResolverEdns(t *testing.T) {
// ask something
m.Question[0] = Question{"miek.nl", TypeSOA, ClassINET}
m.Ns[0] = edns
m.Extra[0] = edns
ch <- DnsMsg{m, nil}
in := <-ch
@ -41,5 +46,5 @@ func TestResolverEdns(t *testing.T) {
t.Fail()
}
ch <- DnsMsg{nil, nil}
<-ch
<-ch // wait for ch to close channel
}

View File

@ -1,21 +1,18 @@
package dns
// subpackage?
// Convert a string to an resource record
// The string must fit on one line and must be fully formatted
// IN A 192.168.1.1 // not ok
// Convert a string to an resource record. The string must fir on one line.
// miek.nl. 3600 IN A 192.168.1.1 // ok
// miek.nl. IN A 192.168.1.1 // ok, ttl may be omitted
// miek.nl. A 192.168.1.1 // ok, ttl and class omitted
// miek.nl. 3600 A 192.168.1.1 // ok, class omitted
// IN A 192.168.1.1 // not ok
func AtoRR(s string) *RR {
// up to first whitespace is domainname
// next word is:
// <number> -> TTL
// IN|CH|HS -> Class
// <rest> -> Type
// When the type is seen, we can read the rest
// of the string in an rr-specific manner
return nil
// up to first whitespace is domainname
// next word is:
// <number> -> TTL
// IN|CH|HS -> Class
// <rest> -> Type
// When the type is seen, we can read the rest
// of the string in an rr-specific manner
return nil
}

View File

@ -1,10 +1,25 @@
// 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
// DNS Resource Records Types. See RFC 1035 and ...
// Package dns implements a full featured interface to the DNS.
// Supported RFCs 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.
//
// 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
//
// 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}
//
package dns
import (
@ -88,8 +103,7 @@ const (
_CD = 1 << 4 // checking disabled
)
// Why not use the official cryptstuff here too???
// or at least map them
// DNSSEC encryption algorithm codes.
const (
// DNSSEC algorithms
AlgRSAMD5 = 1
@ -102,6 +116,7 @@ const (
AlgECCGOST = 12
)
// DNSSEC hashing codes.
const (
HashSHA1 = 1 //?
HashSHA256 = 2 //?
@ -457,7 +472,7 @@ type RR_NSEC3 struct {
Salt string "hex"
HashLength uint8
NextDomain string "domain-name"
TypeBitMap []int "NSEC3" // &{TypeSOA,TypeDS,etc}
TypeBitMap []int "NSEC3" // &{TypeSOA,TypeDS,etc}
}
func (rr *RR_NSEC3) Header() *RR_Header {