diff --git a/Makefile b/Makefile index 32e3cb15..e8d9884f 100644 --- a/Makefile +++ b/Makefile @@ -17,17 +17,20 @@ include $(GOROOT)/src/Make.pkg all: package gomake -C dnssec package gomake -C resolver package +# gomake -C xfr package # gomake -C strconv package install: $(INSTALLFILES) gomake -C dnssec install gomake -C resolver install +# gomake -C xfr package # gomake -C strconv install dnstest: gotest gomake -C dnssec test gomake -C resolver test +# gomake -C xfr test # gomake -C strconv test _examples: diff --git a/TODO b/TODO index f2766fd2..550660d6 100644 --- a/TODO +++ b/TODO @@ -2,16 +2,21 @@ Todo: * DNSSEC validation * NSEC(3) secure denial of existence * fix os.Erros usage, add DNSSEC related errors -* AXFR/IXFR support +* AXFR/IXFR support +* Notify support (send/receive) +* TKEY -- RFC 2930 +* TSIG -- RFC 4635 * IDN? * Unknown RRs? * query-time, server in string ouput of dns.Msg * Parsing from strings * Server support (signing, receiving queries) +* Check the network order, it works now, but this is on Intel Issues: * Better sized buffers * TC bit handling +* Fix TCP: should read 2 bytes of length * shortened ipv6 addresses are not parsed correctly * quoted quotes in txt records * Convience functions? diff --git a/_examples/q/Makefile b/_examples/q/Makefile index 26d10ebd..f587d26c 100644 --- a/_examples/q/Makefile +++ b/_examples/q/Makefile @@ -4,4 +4,5 @@ include $(GOROOT)/src/Make.inc TARG=q GOFILES=q.go +DEPS=../../ include $(GOROOT)/src/Make.cmd diff --git a/dns.go b/dns.go index f5de0dc9..8a35310a 100644 --- a/dns.go +++ b/dns.go @@ -6,13 +6,15 @@ // Package dns implements a full featured interface to the DNS. // Supported RFCs and features include: // * 1982 - Serial Arithmetic -// * 1034/1035 +// * 1034/1035 // * 1876 - LOC record (incomplete) +// * 1995 - IXFR // * 2671 - EDNS // * 2915 - NAPTR record (incomplete) // * 3225 - DO bit (DNSSEC OK) // * 4033/4034/4035 - DNSSEC + validation functions // * 5011 - NSID +// * 5936 - AXFR // * IP6 support // The package allows full control over what is send out to the DNS. // diff --git a/msg.go b/msg.go index 4c553866..cfe99d2b 100644 --- a/msg.go +++ b/msg.go @@ -60,6 +60,8 @@ var Rr_str = map[uint16]string{ TypeDNSKEY: "DNSKEY", TypeNSEC3: "NSEC3", TypeNSEC3PARAM: "NSEC3PARAM", + TypeAXFR: "AXFR", // Not real RRs + TypeIXFR: "IXFR", } diff --git a/resolver/resolver.go b/resolver/resolver.go index 28665c26..6f6e2ca8 100644 --- a/resolver/resolver.go +++ b/resolver/resolver.go @@ -88,14 +88,14 @@ func query(res *Resolver, msg chan DnsMsg) { out.Dns.Id = uint16(rand.Int()) ^ uint16(time.Nanoseconds()) sending, ok := out.Dns.Pack() if !ok { - println("pack failed") + //println("pack failed") msg <- DnsMsg{nil, nil} // todo error continue; } for i := 0; i < len(res.Servers); i++ { server := res.Servers[i] + ":" + port - if res.Tcp == true { + if res.Tcp { c, cerr = net.Dial("tcp", "", server) } else { c, cerr = net.Dial("udp", "", server) @@ -104,8 +104,14 @@ func query(res *Resolver, msg chan DnsMsg) { err = cerr continue } - in, err = exchange(c, sending, res) + if res.Tcp { + in, err = exchange_tcp(c, sending, res) + } else { + in, err = exchange_udp(c, sending, res) + } + // Check id in.id != out.id + // TODO(mg) c.Close() if err != nil { @@ -124,7 +130,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) (*dns.Msg, os.Error) { +func exchange_udp(c net.Conn, m []byte, r *Resolver) (*dns.Msg, os.Error) { var timeout int64 var attempts int if r.Mangle != nil { @@ -140,17 +146,19 @@ func exchange(c net.Conn, m []byte, r *Resolver) (*dns.Msg, os.Error) { } else { attempts = r.Attempts } - for a:= 0; a < attempts; a++ { n, err := c.Write(m) if err != nil { + //println("error writing") return nil, err } c.SetReadTimeout(timeout * 1e9) // nanoseconds - buf := make([]byte, dns.DefaultMsgSize) // More than enough. + buf := make([]byte, dns.DefaultMsgSize) // More than enough??? n, err = c.Read(buf) if err != nil { + //println("error reading") + //println(err.String()) // More Go foo needed //if e, ok := err.(Error); ok && e.Timeout() { // continue @@ -166,3 +174,69 @@ func exchange(c net.Conn, m []byte, r *Resolver) (*dns.Msg, os.Error) { } return nil, nil // todo error } + +// Up to res.Attempts attempts. +func exchange_tcp(c net.Conn, m []byte, r *Resolver) (*dns.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 + } + + ls := make([]byte, 2) // sender length + lr := make([]byte, 2) // receiver length + var length uint16 + ls[0] = byte(len(m) >> 8) + ls[1] = byte(len(m)) + for a := 0; a < attempts; a++ { + // With DNS over TCP we first send the length + _, err := c.Write(ls) + if err != nil { + return nil, err + } + + // And then send the message + _, err = c.Write(m) + if err != nil { + return nil, err + } + + c.SetReadTimeout(timeout * 1e9) // nanoseconds + // The server replies with two bytes length + _, err = c.Read(lr) + if err != nil { + return nil, err + } + length = uint16(lr[0])<<8 | uint16(lr[1]) + // if length is 0?? + // And then the message + buf := make([]byte, length) + _, err = c.Read(buf) + if err != nil { + //println("error reading") + //println(err.String()) + // More Go foo needed + //if e, ok := err.(Error); ok && e.Timeout() { + // continue + //} + return nil, err + } + in := new(dns.Msg) + if !in.Unpack(buf) { +// println("unpacking went wrong") + continue + } + return in, nil + } + return nil, nil // todo error +} diff --git a/types.go b/types.go index 0e6930cb..ebf1d6fb 100644 --- a/types.go +++ b/types.go @@ -60,6 +60,7 @@ const ( TypeNSEC3PARAM = 51 // valid Question.qtype only + TypeIXFR = 251 TypeAXFR = 252 TypeMAILB = 253 TypeMAILA = 254