From b722229700fb04ca77adff284ea3a4b179e9fbb8 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 18 Mar 2011 14:13:42 +0100 Subject: [PATCH 01/24] start of server side (sending) AXFR --- TODO | 11 +++++++++++ server.go | 1 + xfr.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/TODO b/TODO index 6ff81661..2d8f6505 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,20 @@ +Guidelines for the API: + +o symmetrical, client side stuff should be mirrored in the server stuff +o clean, small API +o fast data structures (rb-tree, when they come available) +o api-use should lead to self documenting code +o compression (only ownernames?) + + + Todo: * Parsing from strings, going with goyacc and .cz lexer? * encoding NSEC3/NSEC bitmaps, DEcoding works * HIP RR (needs list of domain names, need slice stuff for that) * Resolver can see if you want ixfr or axfr from the msg no need for seperate functions +* Is subdomain, is glue helper functions Issues: * Check the network order, it works now, but this is on Intel?? diff --git a/server.go b/server.go index 62241139..57ab678e 100644 --- a/server.go +++ b/server.go @@ -11,6 +11,7 @@ import ( "net" ) +// Do I want this type Server struct { ServeUDP func(*net.UDPConn, net.Addr, *Msg) os.Error ServeTCP func(*net.TCPConn, net.Addr, *Msg) os.Error diff --git a/xfr.go b/xfr.go index 530accbd..ca2071de 100644 --- a/xfr.go +++ b/xfr.go @@ -1,3 +1,37 @@ package dns +import ( + "net" + ) + // Outgoing AXFR and IXFR implementations + +// Read from m until it is closed. Group the RRs until +// no space is left and send the messages. +// How do I know the message still fits in 64 K??? +func AxfrTCP(c *net.TCPConn, a net.Addr, m chan Xfr) { + msg := new(Msg) + msg.Answer = make([]RR, 1000) + i := 0 + var soa *RR_SOA + for r := range m { + msg.Answer[i] = r.RR + if soa == nil { + if r.RR.Header().Rrtype != TypeSOA { + // helegaar geen SOA + } else { + soa = r.RR.(*RR_SOA) + } + } + i++ + if i > 1000 { + // send it + msg.Answer = msg.Answer[:0] + i = 0 + } + } + // Last one, what if was 1000 send lonely soa?? No matter + // what, add the SOA and send the msg + msg.Answer[i] = soa + // send it +} From de9a1da6aa43f2052375ee8c609259801e575d45 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 20 Mar 2011 16:52:10 +0100 Subject: [PATCH 02/24] Add lowlevel read/write primatives - and make it much more Go-like --- dns.go | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++- resolver.go | 32 ++++------ 2 files changed, 178 insertions(+), 21 deletions(-) diff --git a/dns.go b/dns.go index 8b04e843..37eaaff7 100644 --- a/dns.go +++ b/dns.go @@ -15,7 +15,11 @@ // package dns +// ErrShortWrite is defined in io, use that! + import ( + "os" + "net" "strconv" ) @@ -30,7 +34,7 @@ const ( type Error struct { Error string Name string - Server string + Server net.Addr Timeout bool } @@ -41,6 +45,167 @@ func (e *Error) String() string { return e.Error } +// A Conn is the lowest primative in this DNS library +// A hold both the UDP and TCP connection, but only one +// can be active at any one time. +type Conn struct { + // The current UDP connection. + UDP *net.UDPConn + // The current TCP connection. + TCP *net.TCPConn + // The remote side of the connection. + Addr net.Addr + + // Timeout in sec + Timeout int + + // Number of attempts to try + Attempts int +} + +func (d *Conn) Read(p []byte) (n int, err os.Error) { + if d.UDP != nil && d.TCP != nil { + return 0, &Error{Error: "UDP and TCP or both non-nil"} + } + switch { + case d.UDP != nil: + n, err = d.UDP.Read(p) + if err != nil { + return n, err + } + case d.TCP != nil: + n, err = d.TCP.Read(p[0:1]) + if err != nil || n != 2 { + return n, err + } + l, _ := unpackUint16(p[0:1], 0) + if l == 0 { + return 0, &Error{Error: "received nil msg length", Server: d.Addr} + } + if int(l) > len(p) { + return int(l), &Error{Error: "Buffer too small to read"} + } + n, err = d.TCP.Read(p) + if err != nil { + return n, err + } + i := n + for i < int(l) { + n, err = d.TCP.Read(p[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return +} + +func (d *Conn) Write(p []byte) (n int, err os.Error) { + if d.UDP != nil && d.TCP != nil { + return 0, &Error{Error: "UDP and TCP or both non-nil"} + } + + var attempts int + if d.Attempts == 0 { + attempts = 1 + } else { + attempts = d.Attempts + } + d.SetTimeout() + + switch { + case d.UDP != nil: + for a := 0; a < attempts; a++ { + n, err = d.UDP.WriteTo(p, d.Addr) + if err != nil { + if e, ok := err.(net.Error); ok && e.Timeout() { + continue + } + return 0, err + } + } + case d.TCP != nil: + for a := 0; a < attempts; a++ { + l := make([]byte, 2) + l[0], l[1] = packUint16(uint16(len(p))) + n, err = d.TCP.Write(l) + if err != nil { + if e, ok := err.(net.Error); ok && e.Timeout() { + continue + } + return n, err + } + if n != 2 { + return n, &Error{Error: "Write failure"} + } + n, err = d.TCP.Write(p) + if err != nil { + if e, ok := err.(net.Error); ok && e.Timeout() { + continue + } + return n, err + } + } + } + return +} + +func (d *Conn) Close() (err os.Error) { + if d.UDP != nil && d.TCP != nil { + return &Error{Error: "UDP and TCP or both non-nil"} + } + switch { + case d.UDP != nil: + err = d.UDP.Close() + case d.TCP != nil: + err = d.TCP.Close() + } + return +} + +func (d *Conn) SetTimeout() (err os.Error) { + var sec int64 + if d.UDP != nil && d.TCP != nil { + return &Error{Error: "UDP and TCP or both non-nil"} + } + sec = int64(d.Timeout) + if sec == 0 { + sec = 1 + } + if d.UDP != nil { + err = d.TCP.SetTimeout(sec * 1e9) + } + if d.TCP != nil { + err = d.TCP.SetTimeout(sec * 1e9) + } + return +} + +// Fix those here...! +// ReadTsig +// WriteTsig + +func (d *Conn) Exchange(request []byte, nosend bool) (reply []byte, err os.Error) { + var n int + n, err = d.Write(request) + if err != nil { + return nil, err + } + // Layer violation to safe memory. (Its okay then.) + if d.UDP == nil { + reply = make([]byte, MaxMsgSize) + } else { + reply = make([]byte, DefaultMsgSize) + } + n, err = d.Read(reply) + if err != nil { + return nil, err + } + reply = reply[:n] + return +} + type RR interface { Header() *RR_Header diff --git a/resolver.go b/resolver.go index 8b2701fc..c5d500d1 100644 --- a/resolver.go +++ b/resolver.go @@ -32,10 +32,6 @@ type Resolver struct { Rrb int // Last used server (for round robin) } -func (res *Resolver) QueryTSIG(q *Msg, secret *string) (d *Msg, err os.Error) { - return nil,nil -} - // Basic usage pattern for setting up a resolver: // // res := new(Resolver) @@ -49,7 +45,6 @@ func (res *Resolver) QueryTSIG(q *Msg, secret *string) (d *Msg, err os.Error) { // // Note that message id checking is left to the caller. func (res *Resolver) Query(q *Msg) (d *Msg, err os.Error) { - // Check if there is a TSIG appended, if so, check it var ( c net.Conn port string @@ -78,31 +73,28 @@ func (res *Resolver) Query(q *Msg) (d *Msg, err os.Error) { } for i := 0; i < len(res.Servers); i++ { + d := new(Conn) server := res.Servers[i] + ":" + port t := time.Nanoseconds() if res.Tcp { c, err = net.Dial("tcp", "", server) + d.TCP = c.(*net.TCPConn) + d.Addr = d.TCP.RemoteAddr() } else { c, err = net.Dial("udp", "", server) + d.UDP = c.(*net.UDPConn) + d.Addr = d.UDP.RemoteAddr() } if err != nil { continue - } - if res.Tcp { - inb, err = exchangeTCP(c, sending, res, true) - in.Unpack(inb) - - } else { - inb, err = exchangeUDP(c, sending, res, true) - in.Unpack(inb) - } + } + inb, err = d.Exchange(sending, false) + if err != nil { + continue + } + in.Unpack(inb) res.Rtt[server] = time.Nanoseconds() - t - - // Check id in.id != out.id, should be checked in the client! c.Close() - if err != nil { - continue - } break } if err != nil { @@ -550,7 +542,7 @@ func recvTCP(c net.Conn) ([]byte, os.Error) { } length := uint16(l[0])<<8 | uint16(l[1]) if length == 0 { - return nil, &Error{Error: "received nil msg length", Server: c.RemoteAddr().String()} + return nil, &Error{Error: "received nil msg length", Server: c.RemoteAddr()} } m := make([]byte, length) n, cerr := c.Read(m) From b1f63f57c4c7d6cdaa3f22a92c95f2f78a7097dd Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 20 Mar 2011 19:58:55 +0100 Subject: [PATCH 03/24] Read/Write works, but axfr still fails --- defaults.go | 1 - dns.go | 32 +++-- msg.go | 5 +- resolver.go | 378 ++++++++++++++-------------------------------------- server.go | 95 ++++--------- tsig.go | 14 +- 6 files changed, 162 insertions(+), 363 deletions(-) diff --git a/defaults.go b/defaults.go index 719e3e88..66ed8f78 100644 --- a/defaults.go +++ b/defaults.go @@ -37,5 +37,4 @@ func (dns *Msg) SetAxfr(z string, class uint16) { dns.Question = make([]Question, 1) dns.Question[0] = Question{z, TypeAXFR, class} } - // IsIxfr/IsAxfr? diff --git a/dns.go b/dns.go index 37eaaff7..8fb865e1 100644 --- a/dns.go +++ b/dns.go @@ -51,11 +51,16 @@ func (e *Error) String() string { type Conn struct { // The current UDP connection. UDP *net.UDPConn + // The current TCP connection. TCP *net.TCPConn + // The remote side of the connection. Addr net.Addr + // If TSIG is used, this holds all the information + Signature *Tsig + // Timeout in sec Timeout int @@ -69,16 +74,21 @@ func (d *Conn) Read(p []byte) (n int, err os.Error) { } switch { case d.UDP != nil: - n, err = d.UDP.Read(p) + var addr net.Addr + n, addr, err = d.UDP.ReadFromUDP(p) if err != nil { return n, err } + d.Addr = addr case d.TCP != nil: - n, err = d.TCP.Read(p[0:1]) + if len(p) < 1 { + return 0, &Error{Error: "Buffer too small to read"} + } + n, err = d.TCP.Read(p[0:2]) if err != nil || n != 2 { return n, err } - l, _ := unpackUint16(p[0:1], 0) + l, _ := unpackUint16(p[0:2], 0) if l == 0 { return 0, &Error{Error: "received nil msg length", Server: d.Addr} } @@ -182,17 +192,15 @@ func (d *Conn) SetTimeout() (err os.Error) { return } -// Fix those here...! -// ReadTsig -// WriteTsig - func (d *Conn) Exchange(request []byte, nosend bool) (reply []byte, err os.Error) { var n int - n, err = d.Write(request) - if err != nil { - return nil, err - } - // Layer violation to safe memory. (Its okay then.) + if !nosend { + n, err = d.Write(request) + if err != nil { + return nil, err + } + } + // Layer violation to save memory. Its okay then... if d.UDP == nil { reply = make([]byte, MaxMsgSize) } else { diff --git a/msg.go b/msg.go index cc1ab7c0..906e6bf6 100644 --- a/msg.go +++ b/msg.go @@ -16,7 +16,7 @@ package dns import ( "os" - // "fmt" + "fmt" "reflect" "net" "rand" @@ -210,6 +210,7 @@ func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { Loop: for { if off >= len(msg) { + println(off, len(msg)) return "", len(msg), false } c := int(msg[off]) @@ -657,7 +658,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, case "domain-name": s, off, ok = unpackDomainName(msg, off) if !ok { - //fmt.Fprintf(os.Stderr, "dns: failure unpacking domain-name") + fmt.Fprintf(os.Stderr, "dns: failure unpacking domain-name") return len(msg), false } case "size-base32": diff --git a/resolver.go b/resolver.go index c5d500d1..cc228514 100644 --- a/resolver.go +++ b/resolver.go @@ -45,54 +45,40 @@ type Resolver struct { // // Note that message id checking is left to the caller. func (res *Resolver) Query(q *Msg) (d *Msg, err os.Error) { - var ( - c net.Conn - port string - inb []byte - ) - in := new(Msg) - if len(res.Servers) == 0 { - return nil, &Error{Error: "No servers defined"} - } - if res.Rtt == nil { - res.Rtt = make(map[string]int64) - } - if res.Port == "" { - port = "53" - } else { - port = res.Port + var c net.Conn + var inb []byte + in := new(Msg) + port, err := check(res, q) + if err != nil { + return nil, err } - if q.Id == 0 { - // No Id sed, set it - q.Id = Id() - } sending, ok := q.Pack() if !ok { return nil, &Error{Error: ErrPack} } for i := 0; i < len(res.Servers); i++ { - d := new(Conn) + d := new(Conn) server := res.Servers[i] + ":" + port t := time.Nanoseconds() if res.Tcp { c, err = net.Dial("tcp", "", server) - d.TCP = c.(*net.TCPConn) - d.Addr = d.TCP.RemoteAddr() + d.TCP = c.(*net.TCPConn) + d.Addr = d.TCP.RemoteAddr() } else { c, err = net.Dial("udp", "", server) - d.UDP = c.(*net.UDPConn) - d.Addr = d.UDP.RemoteAddr() + d.UDP = c.(*net.UDPConn) + d.Addr = d.UDP.RemoteAddr() } if err != nil { continue - } - inb, err = d.Exchange(sending, false) - if err != nil { - continue - } - in.Unpack(inb) + } + inb, err = d.Exchange(sending, false) + if err != nil { + continue + } + in.Unpack(inb) res.Rtt[server] = time.Nanoseconds() - t c.Close() break @@ -118,22 +104,13 @@ type Xfr struct { func (res *Resolver) Ixfr(q *Msg, m chan Xfr) { // TSIG var ( - port string - x Xfr - inb []byte - ) + x Xfr + inb []byte + ) in := new(Msg) - if res.Port == "" { - port = "53" - } else { - port = res.Port - } - if res.Rtt == nil { - res.Rtt = make(map[string]int64) - } - - if q.Id == 0 { - q.Id = Id() + port, err := check(res, q) + if err != nil { + return } defer close(m) @@ -149,36 +126,37 @@ Server: if err != nil { continue Server } - first := true var serial uint32 // The first serial seen is the current server serial + d := new(Conn) + d.TCP = c.(*net.TCPConn) + d.Addr = d.TCP.RemoteAddr() + first := true defer c.Close() for { if first { - inb, err = exchangeTCP(c, sending, res, true) - in.Unpack(inb) + inb, err = d.Exchange(sending, false) } else { - inb, err = exchangeTCP(c, sending, res, false) - in.Unpack(inb) + inb, err = d.Exchange(sending, true) } - if err != nil { - // Failed to send, try the next c.Close() continue Server } + + in.Unpack(inb) // error! if in.Id != q.Id { return } if first { // A single SOA RR signals "no changes" - if len(in.Answer) == 1 && checkAxfrSOA(in, true) { + if len(in.Answer) == 1 && checkXfrSOA(in, true) { return } // But still check if the returned answer is ok - if !checkAxfrSOA(in, true) { + if !checkXfrSOA(in, true) { c.Close() continue Server } @@ -228,22 +206,11 @@ Server: // the zone as-is. Xfr.Add is always true. // The channel is closed to signal the end of the AXFR. func (res *Resolver) AxfrTSIG(q *Msg, m chan Xfr, secret string) { - var ( - port string - inb []byte - ) + var inb []byte in := new(Msg) - if res.Port == "" { - port = "53" - } else { - port = res.Port - } - if res.Rtt == nil { - res.Rtt = make(map[string]int64) - } - - if q.Id == 0 { - q.Id = Id() + port, err := check(res, q) + if err != nil { + return } defer close(m) @@ -252,14 +219,14 @@ func (res *Resolver) AxfrTSIG(q *Msg, m chan Xfr, secret string) { return } - var tsig bool - var reqmac string + var tsig bool + var reqmac string // Check if there is a TSIG added to the request msg if len(q.Extra) > 0 { - tsig = q.Extra[len(q.Extra)-1].Header().Rrtype == TypeTSIG - if tsig { - reqmac = q.Extra[len(q.Extra)-1].(*RR_TSIG).MAC - } + tsig = q.Extra[len(q.Extra)-1].Header().Rrtype == TypeTSIG + if tsig { + reqmac = q.Extra[len(q.Extra)-1].(*RR_TSIG).MAC + } } Server: @@ -269,42 +236,44 @@ Server: if err != nil { continue Server } + d := new(Conn) + d.TCP = c.(*net.TCPConn) + d.Addr = d.TCP.RemoteAddr() + first := true defer c.Close() // TODO(mg): if not open? for { if first { - inb, err = exchangeTCP(c, sending, res, true) - in.Unpack(inb) + inb, err = d.Exchange(sending, false) } else { - inb, err = exchangeTCP(c, sending, res, false) - in.Unpack(inb) + inb, err = d.Exchange(sending, true) } - if err != nil { - // Failed to send, try the next c.Close() continue Server } + + in.Unpack(inb) if in.Id != q.Id { c.Close() return } - if tsig && len(in.Extra) > 0 { // What if not included? - t := in.Extra[len(in.Extra)-1] - if t.Header().Rrtype == TypeTSIG { - if t.(*RR_TSIG).Verify(inb, secret, reqmac, first) { - // Set the MAC for the next round. - reqmac = t.(*RR_TSIG).MAC - } else { - c.Close() - return - } - } - } + if tsig && len(in.Extra) > 0 { // What if not included? + t := in.Extra[len(in.Extra)-1] + if t.Header().Rrtype == TypeTSIG { + if t.(*RR_TSIG).Verify(inb, secret, reqmac, first) { + // Set the MAC for the next round. + reqmac = t.(*RR_TSIG).MAC + } else { + c.Close() + return + } + } + } if first { - if !checkAxfrSOA(in, true) { + if !checkXfrSOA(in, true) { c.Close() continue Server } @@ -312,12 +281,12 @@ Server: } if !first { - if !checkAxfrSOA(in, false) { + if !checkXfrSOA(in, false) { // Soa record not the last one - sendFromMsg(in, m, false) + sendMsg(in, m, false) continue } else { - sendFromMsg(in, m, true) + sendMsg(in, m, true) return } } @@ -335,23 +304,12 @@ Server: // the zone as-is. Xfr.Add is always true. // The channel is closed to signal the end of the AXFR. func (res *Resolver) Axfr(q *Msg, m chan Xfr) { - var ( - port string - inb []byte - ) + var inb []byte + port, err := check(res, q) + if err != nil { + return + } in := new(Msg) - if res.Port == "" { - port = "53" - } else { - port = res.Port - } - if res.Rtt == nil { - res.Rtt = make(map[string]int64) - } - - if q.Id == 0 { - q.Id = Id() - } defer close(m) sending, ok := q.Pack() @@ -366,29 +324,31 @@ Server: if err != nil { continue Server } + d := new(Conn) + d.TCP = c.(*net.TCPConn) + d.Addr = d.TCP.RemoteAddr() + first := true defer c.Close() // TODO(mg): if not open? for { if first { - inb, err = exchangeTCP(c, sending, res, true) - in.Unpack(inb) + inb, err = d.Exchange(sending, false) } else { - inb, err = exchangeTCP(c, sending, res, false) - in.Unpack(inb) + inb, err = d.Exchange(sending, true) } - if err != nil { - // Failed to send, try the next c.Close() continue Server } + if !in.Unpack(inb) { + println("Failed to unpack") + } if in.Id != q.Id { c.Close() return } - if first { - if !checkAxfrSOA(in, true) { + if !checkXfrSOA(in, true) { c.Close() continue Server } @@ -396,12 +356,12 @@ Server: } if !first { - if !checkAxfrSOA(in, false) { + if !checkXfrSOA(in, false) { // Soa record not the last one - sendFromMsg(in, m, false) + sendMsg(in, m, false) continue } else { - sendFromMsg(in, m, true) + sendMsg(in, m, true) return } } @@ -412,158 +372,10 @@ Server: return } -// Send a request on the connection and hope for a reply. -// Up to res.Attempts attempts. If send is false, nothing -// is send. -func exchangeUDP(c net.Conn, m []byte, r *Resolver, send bool) ([]byte, 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 a := 0; a < attempts; a++ { - if send { - err := sendUDP(m, c) - if err != nil { - if e, ok := err.(net.Error); ok && e.Timeout() { - continue - } - return nil, err - } - } - - c.SetReadTimeout(timeout * 1e9) // nanoseconds - buf, err := recvUDP(c) - if err != nil { - if e, ok := err.(net.Error); ok && e.Timeout() { - continue - } - return nil, err - } - return buf, nil - } - return nil, &Error{Error: ErrServ} -} - -// Up to res.Attempts attempts. -func exchangeTCP(c net.Conn, m []byte, r *Resolver, send bool) ([]byte, 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 a := 0; a < attempts; a++ { - // only send something when told so - if send { - err := sendTCP(m, c) - if err != nil { - if e, ok := err.(net.Error); ok && e.Timeout() { - continue - } - return nil, err - } - } - - c.SetReadTimeout(timeout * 1e9) // nanoseconds - // The server replies with two bytes length. - buf, err := recvTCP(c) - if err != nil { - if e, ok := err.(net.Error); ok && e.Timeout() { - continue - } - return nil, err - } - return buf, nil - } - return nil, &Error{Error: ErrServ} -} - -func sendUDP(m []byte, c net.Conn) os.Error { - _, err := c.Write(m) - if err != nil { - return err - } - return nil -} - -func recvUDP(c net.Conn) ([]byte, os.Error) { - m := make([]byte, DefaultMsgSize) - n, err := c.Read(m) - if err != nil { - return nil, err - } - m = m[:n] - return m, nil -} - -func sendTCP(m []byte, c net.Conn) os.Error { - l := make([]byte, 2) - l[0] = byte(len(m) >> 8) - l[1] = byte(len(m)) - // First we send the length - _, err := c.Write(l) - if err != nil { - return err - } - // And the the message - _, err = c.Write(m) - if err != nil { - return err - } - return nil -} - -func recvTCP(c net.Conn) ([]byte, os.Error) { - l := make([]byte, 2) // The server replies with two bytes length. - _, err := c.Read(l) - if err != nil { - return nil, err - } - length := uint16(l[0])<<8 | uint16(l[1]) - if length == 0 { - return nil, &Error{Error: "received nil msg length", Server: c.RemoteAddr()} - } - m := make([]byte, length) - n, cerr := c.Read(m) - if cerr != nil { - return nil, cerr - } - i := n - for i < int(length) { - n, err = c.Read(m[i:]) - if err != nil { - return nil, err - } - i += n - } - return m, nil -} - // Check if he SOA record exists in the Answer section of // the packet. If first is true the first RR must be a soa // if false, the last one should be a SOA -func checkAxfrSOA(in *Msg, first bool) bool { +func checkXfrSOA(in *Msg, first bool) bool { if len(in.Answer) > 0 { if first { return in.Answer[0].Header().Rrtype == TypeSOA @@ -575,7 +387,7 @@ func checkAxfrSOA(in *Msg, first bool) bool { } // Send the answer section to the channel -func sendFromMsg(in *Msg, c chan Xfr, nosoa bool) { +func sendMsg(in *Msg, c chan Xfr, nosoa bool) { x := Xfr{Add: true} for k, r := range in.Answer { if nosoa && k == len(in.Answer)-1 { @@ -585,3 +397,19 @@ func sendFromMsg(in *Msg, c chan Xfr, nosoa bool) { c <- x } } + +// Some assorted checks on the resolver +func check(res *Resolver, q *Msg) (port string, err os.Error) { + if res.Port == "" { + port = "53" + } else { + port = res.Port + } + if res.Rtt == nil { + res.Rtt = make(map[string]int64) + } + if q.Id == 0 { + q.Id = Id() + } + return +} diff --git a/server.go b/server.go index 57ab678e..2ff1b31f 100644 --- a/server.go +++ b/server.go @@ -11,71 +11,58 @@ import ( "net" ) -// Do I want this -type Server struct { - ServeUDP func(*net.UDPConn, net.Addr, *Msg) os.Error - ServeTCP func(*net.TCPConn, net.Addr, *Msg) os.Error - /* notify stuff here? */ - /* tsig here */ -} +// For both -> logging -func ServeUDP(l *net.UDPConn, f func(*net.UDPConn, net.Addr, *Msg)) os.Error { +func ServeUDP(l *net.UDPConn, f func(*Conn, *Msg)) os.Error { for { m := make([]byte, DefaultMsgSize) - n, radd, e := l.ReadFromUDP(m) + n, addr, e := l.ReadFromUDP(m) if e != nil { continue } m = m[:n] + + d := new(Conn) + d.UDP = l + d.Addr = addr + msg := new(Msg) if !msg.Unpack(m) { continue } - go f(l, radd, msg) + go f(d, msg) } panic("not reached") } -func ServeTCP(l *net.TCPListener, f func(*net.TCPConn, net.Addr, *Msg)) os.Error { - b := make([]byte, 2) +func ServeTCP(l *net.TCPListener, f func(*Conn, *Msg)) os.Error { for { c, e := l.AcceptTCP() if e != nil { return e } - n, e := c.Read(b) - if e != nil { - continue - } + d := new(Conn) + d.TCP = c + d.Addr = c.RemoteAddr() - length := uint16(b[0])<<8 | uint16(b[1]) - if length == 0 { - return &Error{Error: "received nil msg length"} - } - m := make([]byte, length) + m := make([]byte, MaxMsgSize) // This may start to hurt someday. + n, e := d.Read(m) + if e != nil { + continue + } + m = m[:n] - n, e = c.Read(m) - if e != nil { - continue - } - i := n - if i < int(length) { - n, e = c.Read(m[i:]) - if e != nil { - continue - } - i += n - } msg := new(Msg) if !msg.Unpack(m) { + // Logging?? continue } - go f(c, c.RemoteAddr(), msg) + go f(d, msg) } panic("not reached") } -func ListenAndServeTCP(addr string, f func(*net.TCPConn, net.Addr, *Msg)) os.Error { +func ListenAndServeTCP(addr string, f func(*Conn, *Msg)) os.Error { a, err := net.ResolveTCPAddr(addr) if err != nil { return err @@ -88,7 +75,7 @@ func ListenAndServeTCP(addr string, f func(*net.TCPConn, net.Addr, *Msg)) os.Err return err } -func ListenAndServeUDP(addr string, f func(*net.UDPConn, net.Addr, *Msg)) os.Error { +func ListenAndServeUDP(addr string, f func(*Conn, *Msg)) os.Error { a, err := net.ResolveUDPAddr(addr) if err != nil { return err @@ -100,39 +87,3 @@ func ListenAndServeUDP(addr string, f func(*net.UDPConn, net.Addr, *Msg)) os.Err err = ServeUDP(l, f) return err } - -// Send a buffer on the TCP connection. -func SendTCP(m []byte, c *net.TCPConn, a net.Addr) os.Error { - l := make([]byte, 2) - l[0] = byte(len(m) >> 8) - l[1] = byte(len(m)) - // First we send the length - n, err := c.Write(l) - if err != nil { - return err - } - // And the the message - n, err = c.Write(m) - if err != nil { - return err - } - i := n - for i < len(m) { - n, err = c.Write(m) - if err != nil { - return err - } - i += n - } - return nil -} - -// Send a buffer to the remove address. Only here because -// of the symmetry with SendTCP(). -func SendUDP(m []byte, c *net.UDPConn, a net.Addr) os.Error { - _, err := c.WriteTo(m, a) - if err != nil { - return err - } - return nil -} diff --git a/tsig.go b/tsig.go index 0c16032d..0ebfd5ad 100644 --- a/tsig.go +++ b/tsig.go @@ -10,6 +10,19 @@ import ( "encoding/hex" ) +type Tsig struct { + // The name of the key. + Name string + Fudge uint16 + TimeSigned uint64 + Algorithm string + // Tsig secret encoded in base64. + Secret string + // MAC (if known) + MAC string +} + + // HMAC hashing codes. These are transmitted as domain names. const ( HmacMD5 = "hmac-md5.sig-alg.reg.int." @@ -34,7 +47,6 @@ func (rr *RR_TSIG) Header() *RR_Header { return &rr.Hdr } -// move to defaults.go? func (rr *RR_TSIG) SetDefaults() { rr.Header().Ttl = 0 rr.Header().Class = ClassANY From ab035e636fd1d4b56ef7a53ee98b32a3c6a4adad Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 20 Mar 2011 20:16:10 +0100 Subject: [PATCH 04/24] debugging --- dns.go | 2 ++ msg.go | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dns.go b/dns.go index 8fb865e1..d97eb92f 100644 --- a/dns.go +++ b/dns.go @@ -207,7 +207,9 @@ func (d *Conn) Exchange(request []byte, nosend bool) (reply []byte, err os.Error reply = make([]byte, DefaultMsgSize) } n, err = d.Read(reply) + println("READ ", n) if err != nil { + println(err.String()) return nil, err } reply = reply[:n] diff --git a/msg.go b/msg.go index 906e6bf6..cc1ab7c0 100644 --- a/msg.go +++ b/msg.go @@ -16,7 +16,7 @@ package dns import ( "os" - "fmt" + // "fmt" "reflect" "net" "rand" @@ -210,7 +210,6 @@ func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { Loop: for { if off >= len(msg) { - println(off, len(msg)) return "", len(msg), false } c := int(msg[off]) @@ -658,7 +657,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, case "domain-name": s, off, ok = unpackDomainName(msg, off) if !ok { - fmt.Fprintf(os.Stderr, "dns: failure unpacking domain-name") + //fmt.Fprintf(os.Stderr, "dns: failure unpacking domain-name") return len(msg), false } case "size-base32": From 2c5184ff110ea43fd61b0afeae3a60f595bbb32f Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 20 Mar 2011 20:55:27 +0100 Subject: [PATCH 05/24] Add tsig stuff --- dns.go | 6 ++- tsig.go | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/dns.go b/dns.go index d97eb92f..79c04d70 100644 --- a/dns.go +++ b/dns.go @@ -59,7 +59,7 @@ type Conn struct { Addr net.Addr // If TSIG is used, this holds all the information - Signature *Tsig + Tsig *Tsig // Timeout in sec Timeout int @@ -108,6 +108,10 @@ func (d *Conn) Read(p []byte) (n int, err os.Error) { i += n } } + if d.Tsig != nil { + // Check the TSIG that we should be read + d.Tsig.Verify(p) + } return } diff --git a/tsig.go b/tsig.go index 0ebfd5ad..bf6a2b03 100644 --- a/tsig.go +++ b/tsig.go @@ -10,6 +10,8 @@ import ( "encoding/hex" ) +// Structure used in Read/Write lowlevel functions +// for TSIG generation and verification. type Tsig struct { // The name of the key. Name string @@ -20,9 +22,12 @@ type Tsig struct { Secret string // MAC (if known) MAC string + // Request MAC + RequestMAC string + // Include the timers if true + Timers bool } - // HMAC hashing codes. These are transmitted as domain names. const ( HmacMD5 = "hmac-md5.sig-alg.reg.int." @@ -99,6 +104,29 @@ type timerWireFmt struct { Fudge uint16 } +// In a message and out a new message with the tsig +// added +func (t *Tsig) Generate(msg []byte) ([]byte, bool) { + rawsecret, err := packBase64([]byte(t.Secret)) + if err != nil { + return nil, false + } + buf, ok := t.Buffer(msg) + if !ok { + return nil, false + } + + h := hmac.NewMD5([]byte(rawsecret)) + io.WriteString(h, string(buf)) + + t.MAC = hex.EncodeToString(h.Sum()) // Size is half! + if !ok { + return nil, false + } + // okay, create TSIG, add to message + return nil, true +} + // Generate the HMAC for message. The TSIG RR is modified // to include the MAC and MACSize. Note the the msg Id must // already be set, otherwise the MAC will not be correct when @@ -130,6 +158,29 @@ func (t *RR_TSIG) Generate(m *Msg, secret string) bool { return true } +// Verify a TSIG on a message. All relevant data should +// be set in the Tsig structure. +func (t *Tsig) Verify(msg []byte) bool { + rawsecret, err := packBase64([]byte(t.Secret)) + if err != nil { + return false + } + // Stipped the TSIG from the incoming msg + stripped, ok := stripTsig(msg) + if !ok { + return false + } + + buf, ok := t.Buffer(stripped) + if !ok { + return false + } + + h := hmac.NewMD5([]byte(rawsecret)) + io.WriteString(h, string(buf)) + return strings.ToUpper(hex.EncodeToString(h.Sum())) == strings.ToUpper(t.MAC) +} + // Verify a TSIG. The message should be the complete with // the TSIG record still attached (as the last rr in the Additional // section). Return true on success. @@ -145,7 +196,7 @@ func (t *RR_TSIG) Verify(msg []byte, secret, reqmac string, timers bool) bool { } // t.OrigId -- need to check - stripped, ok := stripTSIG(msg) + stripped, ok := stripTsig(msg) if !ok { return false } @@ -159,6 +210,61 @@ func (t *RR_TSIG) Verify(msg []byte, secret, reqmac string, timers bool) bool { return strings.ToUpper(hex.EncodeToString(h.Sum())) == strings.ToUpper(t.MAC) } +// Create a wiredata buffer for the MAC calculation +func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { + var ( + macbuf []byte + buf []byte + ) + + if t.RequestMAC != "" { + m := new(macWireFmt) + m.MACSize = uint16(len(t.RequestMAC) / 2) + m.MAC = t.RequestMAC + macbuf = make([]byte, len(t.RequestMAC)) // reqmac should be twice as long + n, ok := packStruct(m, macbuf, 0) + if !ok { + return nil, false + } + macbuf = macbuf[:n] + } + + tsigvar := make([]byte, DefaultMsgSize) + if t.Timers { + tsig := new(tsigWireFmt) + tsig.Name = strings.ToLower(t.Name) + tsig.Class = ClassANY + tsig.Ttl = 0 + tsig.Algorithm = strings.ToLower(t.Algorithm) + tsig.TimeSigned = t.TimeSigned + tsig.Fudge = t.Fudge + tsig.Error = 0 + tsig.OtherLen = 0 + tsig.OtherData = "" + n, ok1 := packStruct(tsig, tsigvar, 0) + if !ok1 { + return nil, false + } + tsigvar = tsigvar[:n] + } else { + tsig := new(timerWireFmt) + tsig.TimeSigned = t.TimeSigned + tsig.Fudge = t.Fudge + n, ok1 := packStruct(tsig, tsigvar, 0) + if !ok1 { + return nil, false + } + tsigvar = tsigvar[:n] + } + if t.RequestMAC != "" { + x := append(macbuf, msg...) + buf = append(x, tsigvar...) + } else { + buf = append(msg, tsigvar...) + } + return buf, true +} + // Create the buffer which we use for the MAC calculation. func tsigToBuf(rr *RR_TSIG, msg []byte, reqmac string, timers bool) ([]byte, bool) { var ( @@ -215,7 +321,7 @@ func tsigToBuf(rr *RR_TSIG, msg []byte, reqmac string, timers bool) ([]byte, boo } // Strip the TSIG from the pkt. -func stripTSIG(orig []byte) ([]byte, bool) { +func stripTsig(orig []byte) ([]byte, bool) { // Copied from msg.go's Unpack() // Header. var dh Header From 101efce4346dfa6904b2b3f831a55008fff670f4 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 20 Mar 2011 21:40:10 +0100 Subject: [PATCH 06/24] Tsig updates --- resolver.go | 4 ++-- tsig.go | 68 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/resolver.go b/resolver.go index cc228514..3d1c6b26 100644 --- a/resolver.go +++ b/resolver.go @@ -41,10 +41,10 @@ type Resolver struct { // m.MsgHdr.Recursion_desired = true // header bits // m.Question = make([]Question, 1) // 1 RR in question section // m.Question[0] = Question{"miek.nl", TypeSOA, ClassINET} -// in, err := res.Query(m) // Ask the question +// in, err := res.Query(m, nil) // Ask the question // // Note that message id checking is left to the caller. -func (res *Resolver) Query(q *Msg) (d *Msg, err os.Error) { +func (res *Resolver) Query(q *Msg, tsig *Tsig) (d *Msg, err os.Error) { var c net.Conn var inb []byte in := new(Msg) diff --git a/tsig.go b/tsig.go index bf6a2b03..375af43c 100644 --- a/tsig.go +++ b/tsig.go @@ -4,28 +4,31 @@ package dns // RFC 2845 and RFC 4635 import ( "io" + "time" "strconv" "strings" "crypto/hmac" "encoding/hex" ) +// Return os.Error with real tsig errors + // Structure used in Read/Write lowlevel functions // for TSIG generation and verification. type Tsig struct { - // The name of the key. - Name string - Fudge uint16 - TimeSigned uint64 - Algorithm string - // Tsig secret encoded in base64. - Secret string - // MAC (if known) - MAC string - // Request MAC - RequestMAC string - // Include the timers if true - Timers bool + // The name of the key. + Name string + Fudge uint16 + TimeSigned uint64 + Algorithm string + // Tsig secret encoded in base64. + Secret string + // MAC (if known) + MAC string + // Request MAC + RequestMAC string + // Only include the timers if true. + Timers bool } // HMAC hashing codes. These are transmitted as domain names. @@ -104,18 +107,23 @@ type timerWireFmt struct { Fudge uint16 } -// In a message and out a new message with the tsig -// added +// In a message and out a new message with the tsig added func (t *Tsig) Generate(msg []byte) ([]byte, bool) { rawsecret, err := packBase64([]byte(t.Secret)) if err != nil { return nil, false } - buf, ok := t.Buffer(msg) - if !ok { - return nil, false + if t.Fudge == 0 { + t.Fudge = 300 + } + if t.TimeSigned == 0 { + t.TimeSigned = uint64(time.Seconds()) } + buf, ok := t.Buffer(msg) + if !ok { + return nil, false + } h := hmac.NewMD5([]byte(rawsecret)) io.WriteString(h, string(buf)) @@ -123,8 +131,22 @@ func (t *Tsig) Generate(msg []byte) ([]byte, bool) { if !ok { return nil, false } - // okay, create TSIG, add to message - return nil, true + + // okay, create TSIG, add to message + rr := new(RR_TSIG) + rr.Hdr = RR_Header{Name: t.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0} + rr.Fudge = t.Fudge + rr.TimeSigned = t.TimeSigned + rr.Algorithm = t.Algorithm + rr.MAC = t.MAC + rr.MACSize = uint16(len(t.MAC) / 2) + + q := new(Msg) + q.Unpack(msg) + + q.Extra = append(q.Extra, rr) + send, ok := q.Pack() + return send, ok } // Generate the HMAC for message. The TSIG RR is modified @@ -165,7 +187,7 @@ func (t *Tsig) Verify(msg []byte) bool { if err != nil { return false } - // Stipped the TSIG from the incoming msg + // Stipped the TSIG from the incoming msg stripped, ok := stripTsig(msg) if !ok { return false @@ -217,7 +239,7 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { buf []byte ) - if t.RequestMAC != "" { + if t.RequestMAC != "" { m := new(macWireFmt) m.MACSize = uint16(len(t.RequestMAC) / 2) m.MAC = t.RequestMAC @@ -256,7 +278,7 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { } tsigvar = tsigvar[:n] } - if t.RequestMAC != "" { + if t.RequestMAC != "" { x := append(macbuf, msg...) buf = append(x, tsigvar...) } else { From 9bc0f3ed6b6338d30bf1708aab385338b8c646a8 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 10:51:28 +0100 Subject: [PATCH 07/24] More TSIG stuff --- dns.go | 20 +++++-- resolver.go | 27 +-------- tsig.go | 166 ++-------------------------------------------------- types.go | 32 ++++++++++ 4 files changed, 56 insertions(+), 189 deletions(-) diff --git a/dns.go b/dns.go index 79c04d70..4d45bc7f 100644 --- a/dns.go +++ b/dns.go @@ -110,7 +110,7 @@ func (d *Conn) Read(p []byte) (n int, err os.Error) { } if d.Tsig != nil { // Check the TSIG that we should be read - d.Tsig.Verify(p) + _, err = d.Tsig.Verify(p) } return } @@ -121,6 +121,7 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { } var attempts int + var q []byte if d.Attempts == 0 { attempts = 1 } else { @@ -128,10 +129,21 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { } d.SetTimeout() + if d.Tsig != nil { + // Create a new buffer with the TSIG added. + var ok bool + q, ok = d.Tsig.Generate(p) + if !ok { + // dikke shit + } + } else { + q = p + } + switch { case d.UDP != nil: for a := 0; a < attempts; a++ { - n, err = d.UDP.WriteTo(p, d.Addr) + n, err = d.UDP.WriteTo(q, d.Addr) if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { continue @@ -142,7 +154,7 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { case d.TCP != nil: for a := 0; a < attempts; a++ { l := make([]byte, 2) - l[0], l[1] = packUint16(uint16(len(p))) + l[0], l[1] = packUint16(uint16(len(q))) n, err = d.TCP.Write(l) if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { @@ -153,7 +165,7 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { if n != 2 { return n, &Error{Error: "Write failure"} } - n, err = d.TCP.Write(p) + n, err = d.TCP.Write(q) if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { continue diff --git a/resolver.go b/resolver.go index 3d1c6b26..4b44bda2 100644 --- a/resolver.go +++ b/resolver.go @@ -102,7 +102,6 @@ type Xfr struct { // have Xfr.Add set to true otherwise it is false. // Channel m is closed when the IXFR ends. func (res *Resolver) Ixfr(q *Msg, m chan Xfr) { - // TSIG var ( x Xfr inb []byte @@ -205,7 +204,7 @@ Server: // returned over the channel, so the caller will receive // the zone as-is. Xfr.Add is always true. // The channel is closed to signal the end of the AXFR. -func (res *Resolver) AxfrTSIG(q *Msg, m chan Xfr, secret string) { +func (res *Resolver) AxfrTSIG(q *Msg, m chan Xfr, t *Tsig) { var inb []byte in := new(Msg) port, err := check(res, q) @@ -219,16 +218,6 @@ func (res *Resolver) AxfrTSIG(q *Msg, m chan Xfr, secret string) { return } - var tsig bool - var reqmac string - // Check if there is a TSIG added to the request msg - if len(q.Extra) > 0 { - tsig = q.Extra[len(q.Extra)-1].Header().Rrtype == TypeTSIG - if tsig { - reqmac = q.Extra[len(q.Extra)-1].(*RR_TSIG).MAC - } - } - Server: for i := 0; i < len(res.Servers); i++ { server := res.Servers[i] + ":" + port @@ -239,6 +228,7 @@ Server: d := new(Conn) d.TCP = c.(*net.TCPConn) d.Addr = d.TCP.RemoteAddr() + d.Tsig = t first := true defer c.Close() // TODO(mg): if not open? @@ -259,19 +249,6 @@ Server: return } - if tsig && len(in.Extra) > 0 { // What if not included? - t := in.Extra[len(in.Extra)-1] - if t.Header().Rrtype == TypeTSIG { - if t.(*RR_TSIG).Verify(inb, secret, reqmac, first) { - // Set the MAC for the next round. - reqmac = t.(*RR_TSIG).MAC - } else { - c.Close() - return - } - } - } - if first { if !checkXfrSOA(in, true) { c.Close() diff --git a/tsig.go b/tsig.go index 375af43c..403c0c13 100644 --- a/tsig.go +++ b/tsig.go @@ -4,8 +4,8 @@ package dns // RFC 2845 and RFC 4635 import ( "io" + "os" "time" - "strconv" "strings" "crypto/hmac" "encoding/hex" @@ -38,45 +38,6 @@ const ( HmacSHA256 = "hmac-sha256." ) -type RR_TSIG struct { - Hdr RR_Header - Algorithm string "domain-name" - TimeSigned uint64 - Fudge uint16 - MACSize uint16 - MAC string "size-hex" - OrigId uint16 - Error uint16 - OtherLen uint16 - OtherData string "size-hex" -} - -func (rr *RR_TSIG) Header() *RR_Header { - return &rr.Hdr -} - -func (rr *RR_TSIG) SetDefaults() { - rr.Header().Ttl = 0 - rr.Header().Class = ClassANY - rr.Header().Rrtype = TypeTSIG - rr.Fudge = 300 - rr.Algorithm = HmacMD5 -} - -// TSIG has no official presentation format, but this will suffice. -func (rr *RR_TSIG) String() string { - return rr.Hdr.String() + - " " + rr.Algorithm + - " " + tsigTimeToDate(rr.TimeSigned) + - " " + strconv.Itoa(int(rr.Fudge)) + - " " + strconv.Itoa(int(rr.MACSize)) + - " " + strings.ToUpper(rr.MAC) + - " " + strconv.Itoa(int(rr.OrigId)) + - " " + strconv.Itoa(int(rr.Error)) + - " " + strconv.Itoa(int(rr.OtherLen)) + - " " + rr.OtherData -} - // The following values must be put in wireformat, so that the MAC can be calculated. // RFC 2845, section 3.4.2. TSIG Variables. type tsigWireFmt struct { @@ -149,87 +110,27 @@ func (t *Tsig) Generate(msg []byte) ([]byte, bool) { return send, ok } -// Generate the HMAC for message. The TSIG RR is modified -// to include the MAC and MACSize. Note the the msg Id must -// already be set, otherwise the MAC will not be correct when -// the message is send. -// The string 'secret' must be encoded in base64. -func (t *RR_TSIG) Generate(m *Msg, secret string) bool { - rawsecret, err := packBase64([]byte(secret)) - if err != nil { - return false - } - t.OrigId = m.MsgHdr.Id - - msg, ok := m.Pack() - if !ok { - return false - } - buf, ok1 := tsigToBuf(t, msg, "", true) - if !ok1 { - return false - } - h := hmac.NewMD5([]byte(rawsecret)) - io.WriteString(h, string(buf)) - - t.MAC = hex.EncodeToString(h.Sum()) - t.MACSize = uint16(len(h.Sum())) // Needs to be "on-the-wire" size. - if !ok { - return false - } - return true -} - // Verify a TSIG on a message. All relevant data should // be set in the Tsig structure. -func (t *Tsig) Verify(msg []byte) bool { +func (t *Tsig) Verify(msg []byte) (bool, os.Error) { rawsecret, err := packBase64([]byte(t.Secret)) if err != nil { - return false + return false, err } // Stipped the TSIG from the incoming msg stripped, ok := stripTsig(msg) if !ok { - return false + return false, &Error{Error: "Failed to strip tsig"} } buf, ok := t.Buffer(stripped) if !ok { - return false + return false, &Error{Error: "Failed to convert to raw buffer"} } h := hmac.NewMD5([]byte(rawsecret)) io.WriteString(h, string(buf)) - return strings.ToUpper(hex.EncodeToString(h.Sum())) == strings.ToUpper(t.MAC) -} - -// Verify a TSIG. The message should be the complete with -// the TSIG record still attached (as the last rr in the Additional -// section). Return true on success. -// The secret is a base64 encoded string with the secret. -func (t *RR_TSIG) Verify(msg []byte, secret, reqmac string, timers bool) bool { - rawsecret, err := packBase64([]byte(secret)) - if err != nil { - return false - } - - if t.Header().Rrtype != TypeTSIG { - return false - } - - // t.OrigId -- need to check - stripped, ok := stripTsig(msg) - if !ok { - return false - } - buf, ok := tsigToBuf(t, stripped, reqmac, timers) - if !ok { - return false - } - - h := hmac.NewMD5([]byte(rawsecret)) - io.WriteString(h, string(buf)) - return strings.ToUpper(hex.EncodeToString(h.Sum())) == strings.ToUpper(t.MAC) + return strings.ToUpper(hex.EncodeToString(h.Sum())) == strings.ToUpper(t.MAC), nil } // Create a wiredata buffer for the MAC calculation @@ -287,61 +188,6 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { return buf, true } -// Create the buffer which we use for the MAC calculation. -func tsigToBuf(rr *RR_TSIG, msg []byte, reqmac string, timers bool) ([]byte, bool) { - var ( - macbuf []byte - buf []byte - ) - - if reqmac != "" { - m := new(macWireFmt) - m.MACSize = uint16(len(reqmac) / 2) - m.MAC = reqmac - macbuf = make([]byte, len(reqmac)) // reqmac should be twice as long - n, ok := packStruct(m, macbuf, 0) - if !ok { - return nil, false - } - macbuf = macbuf[:n] - } - - tsigvar := make([]byte, DefaultMsgSize) - if timers { - tsig := new(tsigWireFmt) - tsig.Name = strings.ToLower(rr.Header().Name) - tsig.Class = rr.Header().Class - tsig.Ttl = rr.Header().Ttl - tsig.Algorithm = strings.ToLower(rr.Algorithm) - tsig.TimeSigned = rr.TimeSigned - tsig.Fudge = rr.Fudge - tsig.Error = rr.Error - tsig.OtherLen = rr.OtherLen - tsig.OtherData = rr.OtherData - n, ok1 := packStruct(tsig, tsigvar, 0) - if !ok1 { - return nil, false - } - tsigvar = tsigvar[:n] - } else { - tsig := new(timerWireFmt) - tsig.TimeSigned = rr.TimeSigned - tsig.Fudge = rr.Fudge - n, ok1 := packStruct(tsig, tsigvar, 0) - if !ok1 { - return nil, false - } - tsigvar = tsigvar[:n] - } - if reqmac != "" { - x := append(macbuf, msg...) - buf = append(x, tsigvar...) - } else { - buf = append(msg, tsigvar...) - } - return buf, true -} - // Strip the TSIG from the pkt. func stripTsig(orig []byte) ([]byte, bool) { // Copied from msg.go's Unpack() diff --git a/types.go b/types.go index 774bf4a7..cdf3b620 100644 --- a/types.go +++ b/types.go @@ -739,6 +739,38 @@ func (rr *RR_DHCID) String() string { return rr.Hdr.String() + rr.Digest } +// RFC 2845. +type RR_TSIG struct { + Hdr RR_Header + Algorithm string "domain-name" + TimeSigned uint64 + Fudge uint16 + MACSize uint16 + MAC string "size-hex" + OrigId uint16 + Error uint16 + OtherLen uint16 + OtherData string "size-hex" +} + +func (rr *RR_TSIG) Header() *RR_Header { + return &rr.Hdr +} + +// TSIG has no official presentation format, but this will suffice. +func (rr *RR_TSIG) String() string { + return rr.Hdr.String() + + " " + rr.Algorithm + + " " + tsigTimeToDate(rr.TimeSigned) + + " " + strconv.Itoa(int(rr.Fudge)) + + " " + strconv.Itoa(int(rr.MACSize)) + + " " + strings.ToUpper(rr.MAC) + + " " + strconv.Itoa(int(rr.OrigId)) + + " " + strconv.Itoa(int(rr.Error)) + + " " + strconv.Itoa(int(rr.OtherLen)) + + " " + rr.OtherData +} + // Translate the RRSIG's incep. and expir. time to the correct date. // Taking into account serial arithmetic (RFC 1982) func timeToDate(t uint32) string { From 6aaa5acd76cf9a547b9804af80b6c36fce392ac6 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 11:39:04 +0100 Subject: [PATCH 08/24] tsig nicely abstracted. Still bugs present though --- _examples/axfr/axfr.go | 5 +-- dns.go | 2 - resolver.go | 94 +++++++----------------------------------- tsig.go | 31 +++++++------- 4 files changed, 34 insertions(+), 98 deletions(-) diff --git a/_examples/axfr/axfr.go b/_examples/axfr/axfr.go index 0c52ddc2..98a81348 100644 --- a/_examples/axfr/axfr.go +++ b/_examples/axfr/axfr.go @@ -18,9 +18,9 @@ func main() { res.Servers[0] = *nameserver c := make(chan dns.Xfr) - m := new(dns.Msg) m.Question = make([]dns.Question, 1) + if *serial > 0 { m.Question[0] = dns.Question{zone, dns.TypeIXFR, dns.ClassINET} soa := new(dns.RR_SOA) @@ -28,11 +28,10 @@ func main() { soa.Serial = uint32(*serial) m.Ns = make([]dns.RR, 1) m.Ns[0] = soa - go res.Ixfr(m, c) } else { m.Question[0] = dns.Question{zone, dns.TypeAXFR, dns.ClassINET} - go res.Axfr(m, c) } + go res.Xfr(m, nil, c) for x := range c { fmt.Printf("%v %v\n",x.Add, x.RR) } diff --git a/dns.go b/dns.go index 4d45bc7f..df835fa6 100644 --- a/dns.go +++ b/dns.go @@ -223,9 +223,7 @@ func (d *Conn) Exchange(request []byte, nosend bool) (reply []byte, err os.Error reply = make([]byte, DefaultMsgSize) } n, err = d.Read(reply) - println("READ ", n) if err != nil { - println(err.String()) return nil, err } reply = reply[:n] diff --git a/resolver.go b/resolver.go index 4b44bda2..be8539b7 100644 --- a/resolver.go +++ b/resolver.go @@ -97,11 +97,23 @@ type Xfr struct { Err os.Error } +func (res *Resolver) Xfr(q *Msg, t *Tsig, m chan Xfr) { + switch q.Question[0].Qtype { + case TypeAXFR: + res.axfr(q, t, m) + case TypeIXFR: + res.ixfr(q, t, m) + default: + // wrong request + return + } +} + // Start an IXFR, q should contain a *Msg with the question // for an IXFR: "miek.nl" ANY IXFR. RRs that should be added // have Xfr.Add set to true otherwise it is false. // Channel m is closed when the IXFR ends. -func (res *Resolver) Ixfr(q *Msg, m chan Xfr) { +func (res *Resolver) ixfr(q *Msg, t *Tsig, m chan Xfr) { var ( x Xfr inb []byte @@ -204,7 +216,7 @@ Server: // returned over the channel, so the caller will receive // the zone as-is. Xfr.Add is always true. // The channel is closed to signal the end of the AXFR. -func (res *Resolver) AxfrTSIG(q *Msg, m chan Xfr, t *Tsig) { +func (res *Resolver) axfr(q *Msg, t *Tsig, m chan Xfr) { var inb []byte in := new(Msg) port, err := check(res, q) @@ -243,7 +255,7 @@ Server: continue Server } - in.Unpack(inb) + in.Unpack(inb) // TODO(mg): error handling if in.Id != q.Id { c.Close() return @@ -266,81 +278,7 @@ Server: sendMsg(in, m, true) return } - } - } - panic("not reached") - return - } - return -} - - -// Start an AXFR, q should contain a message with the question -// for an AXFR: "miek.nl" ANY AXFR. The closing SOA isn't -// returned over the channel, so the caller will receive -// the zone as-is. Xfr.Add is always true. -// The channel is closed to signal the end of the AXFR. -func (res *Resolver) Axfr(q *Msg, m chan Xfr) { - var inb []byte - port, err := check(res, q) - if err != nil { - return - } - in := new(Msg) - - defer close(m) - sending, ok := q.Pack() - if !ok { - return - } - -Server: - for i := 0; i < len(res.Servers); i++ { - server := res.Servers[i] + ":" + port - c, err := net.Dial("tcp", "", server) - if err != nil { - continue Server - } - d := new(Conn) - d.TCP = c.(*net.TCPConn) - d.Addr = d.TCP.RemoteAddr() - - first := true - defer c.Close() // TODO(mg): if not open? - for { - if first { - inb, err = d.Exchange(sending, false) - } else { - inb, err = d.Exchange(sending, true) - } - if err != nil { - c.Close() - continue Server - } - if !in.Unpack(inb) { - println("Failed to unpack") - } - if in.Id != q.Id { - c.Close() - return - } - if first { - if !checkXfrSOA(in, true) { - c.Close() - continue Server - } - first = !first - } - - if !first { - if !checkXfrSOA(in, false) { - // Soa record not the last one - sendMsg(in, m, false) - continue - } else { - sendMsg(in, m, true) - return - } + d.Tsig.TimersOnly = true // } } panic("not reached") diff --git a/tsig.go b/tsig.go index 403c0c13..febc2fd3 100644 --- a/tsig.go +++ b/tsig.go @@ -28,7 +28,7 @@ type Tsig struct { // Request MAC RequestMAC string // Only include the timers if true. - Timers bool + TimersOnly bool } // HMAC hashing codes. These are transmitted as domain names. @@ -93,18 +93,19 @@ func (t *Tsig) Generate(msg []byte) ([]byte, bool) { return nil, false } - // okay, create TSIG, add to message + // Create TSIG and add it to the message. + q := new(Msg) + q.Unpack(msg) // TODO(mg): error handling + rr := new(RR_TSIG) rr.Hdr = RR_Header{Name: t.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0} rr.Fudge = t.Fudge rr.TimeSigned = t.TimeSigned rr.Algorithm = t.Algorithm + rr.OrigId = q.Id rr.MAC = t.MAC rr.MACSize = uint16(len(t.MAC) / 2) - q := new(Msg) - q.Unpack(msg) - q.Extra = append(q.Extra, rr) send, ok := q.Pack() return send, ok @@ -153,7 +154,16 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { } tsigvar := make([]byte, DefaultMsgSize) - if t.Timers { + if t.TimersOnly { + tsig := new(timerWireFmt) + tsig.TimeSigned = t.TimeSigned + tsig.Fudge = t.Fudge + n, ok1 := packStruct(tsig, tsigvar, 0) + if !ok1 { + return nil, false + } + tsigvar = tsigvar[:n] + } else { tsig := new(tsigWireFmt) tsig.Name = strings.ToLower(t.Name) tsig.Class = ClassANY @@ -169,15 +179,6 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { return nil, false } tsigvar = tsigvar[:n] - } else { - tsig := new(timerWireFmt) - tsig.TimeSigned = t.TimeSigned - tsig.Fudge = t.Fudge - n, ok1 := packStruct(tsig, tsigvar, 0) - if !ok1 { - return nil, false - } - tsigvar = tsigvar[:n] } if t.RequestMAC != "" { x := append(macbuf, msg...) From 397c22f212616914eadb69507eb22af94b6719d0 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 11:40:06 +0100 Subject: [PATCH 09/24] update todo --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 2d8f6505..950f490e 100644 --- a/TODO +++ b/TODO @@ -5,6 +5,7 @@ o clean, small API o fast data structures (rb-tree, when they come available) o api-use should lead to self documenting code o compression (only ownernames?) +o Xfr, part of (d *Conn).Xfr(m, channel) From 90a1d813d9b2080f62c48239bb2900ae64847abd Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 14:20:46 +0100 Subject: [PATCH 10/24] Fix rereading tcp --- dns.go | 17 ++++++++++++++++- resolver.go | 10 +++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/dns.go b/dns.go index df835fa6..5523b0ee 100644 --- a/dns.go +++ b/dns.go @@ -103,10 +103,11 @@ func (d *Conn) Read(p []byte) (n int, err os.Error) { for i < int(l) { n, err = d.TCP.Read(p[i:]) if err != nil { - return 0, err + return i, err } i += n } + n = i } if d.Tsig != nil { // Check the TSIG that we should be read @@ -135,6 +136,7 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { q, ok = d.Tsig.Generate(p) if !ok { // dikke shit + // Generate should return os.Error } } else { q = p @@ -172,6 +174,19 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { } return n, err } + i := n + if i < len(q) { + n, err = d.TCP.Write(q) + if err != nil { + if e, ok := err.(net.Error); ok && e.Timeout() { + // We are half way in our write... + continue + } + return n, err + } + i += n + } + n = i } } return diff --git a/resolver.go b/resolver.go index be8539b7..d68ac5ee 100644 --- a/resolver.go +++ b/resolver.go @@ -78,7 +78,7 @@ func (res *Resolver) Query(q *Msg, tsig *Tsig) (d *Msg, err os.Error) { if err != nil { continue } - in.Unpack(inb) + in.Unpack(inb) // Discard error. res.Rtt[server] = time.Nanoseconds() - t c.Close() break @@ -155,7 +155,7 @@ Server: continue Server } - in.Unpack(inb) // error! + in.Unpack(inb) if in.Id != q.Id { return } @@ -254,10 +254,10 @@ Server: c.Close() continue Server } - - in.Unpack(inb) // TODO(mg): error handling + if !in.Unpack(inb) { + return + } if in.Id != q.Id { - c.Close() return } From d67c4279534e7287626b1924ad00fc00b3abd169 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 15:44:51 +0100 Subject: [PATCH 11/24] Abstract xfr nicely away --- resolver.go | 3 +- xfr.go | 180 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 161 insertions(+), 22 deletions(-) diff --git a/resolver.go b/resolver.go index d68ac5ee..54cb91ef 100644 --- a/resolver.go +++ b/resolver.go @@ -5,7 +5,6 @@ // DNS resolver client: see RFC 1035. package dns -// TODO: refacter this import ( "os" @@ -270,6 +269,7 @@ Server: } if !first { + d.Tsig.TimersOnly = true if !checkXfrSOA(in, false) { // Soa record not the last one sendMsg(in, m, false) @@ -278,7 +278,6 @@ Server: sendMsg(in, m, true) return } - d.Tsig.TimersOnly = true // } } panic("not reached") diff --git a/xfr.go b/xfr.go index ca2071de..03fd528e 100644 --- a/xfr.go +++ b/xfr.go @@ -1,37 +1,177 @@ package dns -import ( - "net" - ) - // Outgoing AXFR and IXFR implementations +// error handling?? -// Read from m until it is closed. Group the RRs until -// no space is left and send the messages. -// How do I know the message still fits in 64 K??? -func AxfrTCP(c *net.TCPConn, a net.Addr, m chan Xfr) { - msg := new(Msg) - msg.Answer = make([]RR, 1000) +// Msg tells use what to do +func (d *Conn) XfrRead(q *Msg, m chan Xfr) { + switch q.Question[0].Qtype { + case TypeAXFR: + d.axfrRead(q, m) + case TypeIXFR: + d.ixfrRead(q, m) + } +} + +func (d *Conn) XfrWrite(q *Msg, m chan Xfr) { + switch q.Question[0].Qtype { + case TypeAXFR: + d.axfrWrite(q, m) + case TypeIXFR: +// d.ixfrWrite(q, m) + } +} + +func (d *Conn) axfrRead(q *Msg, m chan Xfr) { + defer close(m) + first := true + in := new(Msg) + for { + inb := make([]byte, MaxMsgSize) + n, err := d.Read(inb) + if err != nil { + return + } + inb = inb[:n] + + if !in.Unpack(inb) { + return + } + if in.Id != q.Id { + return + } + + if first { + if !checkXfrSOA(in, true) { + return + } + first = !first + } + + if !first { + if !checkXfrSOA(in, false) { + // Soa record not the last one + sendMsg(in, m, false) + continue + } else { + sendMsg(in, m, true) + return + } + d.Tsig.TimersOnly = true // Subsequent envelopes use this + } + } + panic("not reached") + return +} + +// Just send the zone +func (d *Conn) axfrWrite(q *Msg, m chan Xfr) { + out := new(Msg) + out.Id = q.Id + out.Question = q.Question + out.Answer = make([]RR, 1000) + var soa *RR_SOA; i := 0 - var soa *RR_SOA - for r := range m { - msg.Answer[i] = r.RR + for r := range m { + out.Answer[i] = r.RR if soa == nil { if r.RR.Header().Rrtype != TypeSOA { - // helegaar geen SOA + return } else { soa = r.RR.(*RR_SOA) } } i++ if i > 1000 { - // send it - msg.Answer = msg.Answer[:0] + // Send it + send, _ := out.Pack() + _, err := d.Write(send) + if err != nil { + /* ... */ + } i = 0 + out.Answer = out.Answer[:0] } } - // Last one, what if was 1000 send lonely soa?? No matter - // what, add the SOA and send the msg - msg.Answer[i] = soa - // send it + // Everything is send, only the closing soa is left. + out.Answer[i] = soa + send, _ := out.Pack() + _, err := d.Write(send) + if err != nil { + /* ... */ + } +} + +func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { + defer close(m) + var serial uint32 // The first serial seen is the current server serial + var x Xfr + first := true + in := new(Msg) + for { + var inb []byte + if d.TCP != nil { + inb = make([]byte, MaxMsgSize) + } else { + inb = make([]byte, DefaultMsgSize) + } + n, err := d.Read(inb) + if err != nil { + return + } + inb = inb[:n] + + if !in.Unpack(inb) { + return + } + if in.Id != q.Id { + return + } + + if first { + // A single SOA RR signals "no changes" + if len(in.Answer) == 1 && checkXfrSOA(in, true) { + return + } + + // But still check if the returned answer is ok + if !checkXfrSOA(in, true) { + return + } + // This serial is important + serial = in.Answer[0].(*RR_SOA).Serial + first = !first + } + + // Now we need to check each message for SOA records, to see what we need to do + x.Add = true + if !first { + d.Tsig.TimersOnly = true + for k, r := range in.Answer { + // If the last record in the IXFR contains the servers' SOA, we should quit + if r.Header().Rrtype == TypeSOA { + switch { + case r.(*RR_SOA).Serial == serial: + if k == len(in.Answer)-1 { + // last rr is SOA with correct serial + //m <- r dont' send it + return + } + x.Add = true + if k != 0 { + // Intermediate SOA + continue + } + case r.(*RR_SOA).Serial != serial: + x.Add = false + continue // Don't need to see this SOA + } + } + x.RR = r + m <- x + } + } + } + panic("not reached") + return } From e47ebb2e4c75c1d8104da14f9e794e77d7e0a9c3 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 16:28:13 +0100 Subject: [PATCH 12/24] it works very nicely --- Makefile | 2 +- TODO | 2 - resolver.go | 205 +++------------------------------------------------- xfr.go | 125 ++++++++++++++++++++------------ 4 files changed, 89 insertions(+), 245 deletions(-) diff --git a/Makefile b/Makefile index 7f873bc8..c034b904 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,6 @@ include $(GOROOT)/src/Make.inc TARG=dns GOFILES=\ - xfr.go\ config.go\ defaults.go\ dns.go\ @@ -21,6 +20,7 @@ GOFILES=\ string.go\ tsig.go\ types.go\ + xfr.go\ # y.go\ include $(GOROOT)/src/Make.pkg diff --git a/TODO b/TODO index 950f490e..62a4a1fe 100644 --- a/TODO +++ b/TODO @@ -5,8 +5,6 @@ o clean, small API o fast data structures (rb-tree, when they come available) o api-use should lead to self documenting code o compression (only ownernames?) -o Xfr, part of (d *Conn).Xfr(m, channel) - Todo: diff --git a/resolver.go b/resolver.go index 54cb91ef..2afd3605 100644 --- a/resolver.go +++ b/resolver.go @@ -77,7 +77,7 @@ func (res *Resolver) Query(q *Msg, tsig *Tsig) (d *Msg, err os.Error) { if err != nil { continue } - in.Unpack(inb) // Discard error. + in.Unpack(inb) // Discard error. res.Rtt[server] = time.Nanoseconds() - t c.Close() break @@ -97,33 +97,10 @@ type Xfr struct { } func (res *Resolver) Xfr(q *Msg, t *Tsig, m chan Xfr) { - switch q.Question[0].Qtype { - case TypeAXFR: - res.axfr(q, t, m) - case TypeIXFR: - res.ixfr(q, t, m) - default: - // wrong request - return - } -} - -// Start an IXFR, q should contain a *Msg with the question -// for an IXFR: "miek.nl" ANY IXFR. RRs that should be added -// have Xfr.Add set to true otherwise it is false. -// Channel m is closed when the IXFR ends. -func (res *Resolver) ixfr(q *Msg, t *Tsig, m chan Xfr) { - var ( - x Xfr - inb []byte - ) - in := new(Msg) port, err := check(res, q) if err != nil { return } - - defer close(m) sending, ok := q.Pack() if !ok { return @@ -136,182 +113,20 @@ Server: if err != nil { continue Server } - var serial uint32 // The first serial seen is the current server serial - d := new(Conn) - d.TCP = c.(*net.TCPConn) - d.Addr = d.TCP.RemoteAddr() + d := new(Conn) + d.TCP = c.(*net.TCPConn) + d.Addr = d.TCP.RemoteAddr() + d.Tsig = t - first := true - defer c.Close() - for { - if first { - inb, err = d.Exchange(sending, false) - } else { - inb, err = d.Exchange(sending, true) - } - if err != nil { - c.Close() - continue Server - } - - in.Unpack(inb) - if in.Id != q.Id { - return - } - - if first { - // A single SOA RR signals "no changes" - if len(in.Answer) == 1 && checkXfrSOA(in, true) { - return - } - - // But still check if the returned answer is ok - if !checkXfrSOA(in, true) { - c.Close() - continue Server - } - // This serial is important - serial = in.Answer[0].(*RR_SOA).Serial - first = !first - } - - // Now we need to check each message for SOA records, to see what we need to do - x.Add = true - if !first { - for k, r := range in.Answer { - // If the last record in the IXFR contains the servers' SOA, we should quit - if r.Header().Rrtype == TypeSOA { - switch { - case r.(*RR_SOA).Serial == serial: - if k == len(in.Answer)-1 { - // last rr is SOA with correct serial - //m <- r dont' send it - return - } - x.Add = true - if k != 0 { - // Intermediate SOA - continue - } - case r.(*RR_SOA).Serial != serial: - x.Add = false - continue // Don't need to see this SOA - } - } - x.RR = r - m <- x - } - } - return - } - panic("not reached") - return + _, err = d.Write(sending) + if err != nil { + println(err.String()) + } + d.XfrRead(q, m) // check } return } -// Start an AXFR, q should contain a message with the question -// for an AXFR: "miek.nl" ANY AXFR. The closing SOA isn't -// returned over the channel, so the caller will receive -// the zone as-is. Xfr.Add is always true. -// The channel is closed to signal the end of the AXFR. -func (res *Resolver) axfr(q *Msg, t *Tsig, m chan Xfr) { - var inb []byte - in := new(Msg) - port, err := check(res, q) - if err != nil { - return - } - - defer close(m) - sending, ok := q.Pack() - if !ok { - return - } - -Server: - for i := 0; i < len(res.Servers); i++ { - server := res.Servers[i] + ":" + port - c, err := net.Dial("tcp", "", server) - if err != nil { - continue Server - } - d := new(Conn) - d.TCP = c.(*net.TCPConn) - d.Addr = d.TCP.RemoteAddr() - d.Tsig = t - - first := true - defer c.Close() // TODO(mg): if not open? - for { - if first { - inb, err = d.Exchange(sending, false) - } else { - inb, err = d.Exchange(sending, true) - } - if err != nil { - c.Close() - continue Server - } - if !in.Unpack(inb) { - return - } - if in.Id != q.Id { - return - } - - if first { - if !checkXfrSOA(in, true) { - c.Close() - continue Server - } - first = !first - } - - if !first { - d.Tsig.TimersOnly = true - if !checkXfrSOA(in, false) { - // Soa record not the last one - sendMsg(in, m, false) - continue - } else { - sendMsg(in, m, true) - return - } - } - } - panic("not reached") - return - } - return -} - -// Check if he SOA record exists in the Answer section of -// the packet. If first is true the first RR must be a soa -// if false, the last one should be a SOA -func checkXfrSOA(in *Msg, first bool) bool { - if len(in.Answer) > 0 { - if first { - return in.Answer[0].Header().Rrtype == TypeSOA - } else { - return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA - } - } - return false -} - -// Send the answer section to the channel -func sendMsg(in *Msg, c chan Xfr, nosoa bool) { - x := Xfr{Add: true} - for k, r := range in.Answer { - if nosoa && k == len(in.Answer)-1 { - continue - } - x.RR = r - c <- x - } -} - // Some assorted checks on the resolver func check(res *Resolver, q *Msg) (port string, err os.Error) { if res.Port == "" { diff --git a/xfr.go b/xfr.go index 03fd528e..5117dfee 100644 --- a/xfr.go +++ b/xfr.go @@ -5,21 +5,21 @@ package dns // Msg tells use what to do func (d *Conn) XfrRead(q *Msg, m chan Xfr) { - switch q.Question[0].Qtype { - case TypeAXFR: - d.axfrRead(q, m) - case TypeIXFR: - d.ixfrRead(q, m) - } + switch q.Question[0].Qtype { + case TypeAXFR: + d.axfrRead(q, m) + case TypeIXFR: + d.ixfrRead(q, m) + } } func (d *Conn) XfrWrite(q *Msg, m chan Xfr) { - switch q.Question[0].Qtype { - case TypeAXFR: - d.axfrWrite(q, m) - case TypeIXFR: -// d.ixfrWrite(q, m) - } + switch q.Question[0].Qtype { + case TypeAXFR: + d.axfrWrite(q, m) + case TypeIXFR: + // d.ixfrWrite(q, m) + } } func (d *Conn) axfrRead(q *Msg, m chan Xfr) { @@ -49,6 +49,9 @@ func (d *Conn) axfrRead(q *Msg, m chan Xfr) { } if !first { + if d.Tsig != nil { + d.Tsig.TimersOnly = true // Subsequent envelopes use this + } if !checkXfrSOA(in, false) { // Soa record not the last one sendMsg(in, m, false) @@ -57,7 +60,6 @@ func (d *Conn) axfrRead(q *Msg, m chan Xfr) { sendMsg(in, m, true) return } - d.Tsig.TimersOnly = true // Subsequent envelopes use this } } panic("not reached") @@ -67,39 +69,40 @@ func (d *Conn) axfrRead(q *Msg, m chan Xfr) { // Just send the zone func (d *Conn) axfrWrite(q *Msg, m chan Xfr) { out := new(Msg) - out.Id = q.Id - out.Question = q.Question - out.Answer = make([]RR, 1000) - var soa *RR_SOA; - i := 0 - for r := range m { - out.Answer[i] = r.RR - if soa == nil { - if r.RR.Header().Rrtype != TypeSOA { - return - } else { - soa = r.RR.(*RR_SOA) - } - } - i++ - if i > 1000 { - // Send it - send, _ := out.Pack() - _, err := d.Write(send) - if err != nil { - /* ... */ - } - i = 0 - out.Answer = out.Answer[:0] - } - } - // Everything is send, only the closing soa is left. - out.Answer[i] = soa - send, _ := out.Pack() - _, err := d.Write(send) - if err != nil { - /* ... */ - } + out.Id = q.Id + out.Question = q.Question + out.Answer = make([]RR, 1000) + var soa *RR_SOA + i := 0 + for r := range m { + out.Answer[i] = r.RR + if soa == nil { + if r.RR.Header().Rrtype != TypeSOA { + return + } else { + soa = r.RR.(*RR_SOA) + } + } + i++ + if i > 1000 { + // Send it + send, _ := out.Pack() + _, err := d.Write(send) + if err != nil { + /* ... */ + } + i = 0 + out.Answer = out.Answer[:0] + } + // TimersOnly foo + } + // Everything is send, only the closing soa is left. + out.Answer[i] = soa + send, _ := out.Pack() + _, err := d.Write(send) + if err != nil { + /* ... */ + } } func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { @@ -146,7 +149,9 @@ func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { // Now we need to check each message for SOA records, to see what we need to do x.Add = true if !first { - d.Tsig.TimersOnly = true + if d.Tsig != nil { + d.Tsig.TimersOnly = true + } for k, r := range in.Answer { // If the last record in the IXFR contains the servers' SOA, we should quit if r.Header().Rrtype == TypeSOA { @@ -175,3 +180,29 @@ func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { panic("not reached") return } + +// Check if he SOA record exists in the Answer section of +// the packet. If first is true the first RR must be a soa +// if false, the last one should be a SOA +func checkXfrSOA(in *Msg, first bool) bool { + if len(in.Answer) > 0 { + if first { + return in.Answer[0].Header().Rrtype == TypeSOA + } else { + return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA + } + } + return false +} + +// Send the answer section to the channel +func sendMsg(in *Msg, c chan Xfr, nosoa bool) { + x := Xfr{Add: true} + for k, r := range in.Answer { + if nosoa && k == len(in.Answer)-1 { + continue + } + x.RR = r + c <- x + } +} From 72c6ff37eb35e4a1fed06bfa5549ca3cea8fb73f Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 17:43:03 +0100 Subject: [PATCH 13/24] Fix error handling --- dns.go | 11 ++++++----- tsig.go | 42 +++++++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/dns.go b/dns.go index 5523b0ee..f4f8cbbd 100644 --- a/dns.go +++ b/dns.go @@ -112,6 +112,9 @@ func (d *Conn) Read(p []byte) (n int, err os.Error) { if d.Tsig != nil { // Check the TSIG that we should be read _, err = d.Tsig.Verify(p) + if err != nil { + return + } } return } @@ -132,11 +135,9 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { if d.Tsig != nil { // Create a new buffer with the TSIG added. - var ok bool - q, ok = d.Tsig.Generate(p) - if !ok { - // dikke shit - // Generate should return os.Error + q, err = d.Tsig.Generate(p) + if err != nil { + return 0, err } } else { q = p diff --git a/tsig.go b/tsig.go index febc2fd3..b1bfcd5e 100644 --- a/tsig.go +++ b/tsig.go @@ -69,10 +69,10 @@ type timerWireFmt struct { } // In a message and out a new message with the tsig added -func (t *Tsig) Generate(msg []byte) ([]byte, bool) { +func (t *Tsig) Generate(msg []byte) ([]byte, os.Error) { rawsecret, err := packBase64([]byte(t.Secret)) if err != nil { - return nil, false + return nil, err } if t.Fudge == 0 { t.Fudge = 300 @@ -81,21 +81,19 @@ func (t *Tsig) Generate(msg []byte) ([]byte, bool) { t.TimeSigned = uint64(time.Seconds()) } - buf, ok := t.Buffer(msg) - if !ok { - return nil, false + buf, err := t.Buffer(msg) + if err != nil { + return nil, err } h := hmac.NewMD5([]byte(rawsecret)) io.WriteString(h, string(buf)) - t.MAC = hex.EncodeToString(h.Sum()) // Size is half! - if !ok { - return nil, false - } // Create TSIG and add it to the message. q := new(Msg) - q.Unpack(msg) // TODO(mg): error handling + if !q.Unpack(msg) { + return nil, &Error{Error: "Failed to unpack"} + } rr := new(RR_TSIG) rr.Hdr = RR_Header{Name: t.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0} @@ -108,7 +106,10 @@ func (t *Tsig) Generate(msg []byte) ([]byte, bool) { q.Extra = append(q.Extra, rr) send, ok := q.Pack() - return send, ok + if !ok { + return send, &Error{Error: "Failed to pack"} + } + return send, nil } // Verify a TSIG on a message. All relevant data should @@ -124,18 +125,21 @@ func (t *Tsig) Verify(msg []byte) (bool, os.Error) { return false, &Error{Error: "Failed to strip tsig"} } - buf, ok := t.Buffer(stripped) - if !ok { - return false, &Error{Error: "Failed to convert to raw buffer"} + buf,err := t.Buffer(stripped) + if err != nil { + return false, err } + // Time needs to be checked */ + // Generic time error + h := hmac.NewMD5([]byte(rawsecret)) io.WriteString(h, string(buf)) return strings.ToUpper(hex.EncodeToString(h.Sum())) == strings.ToUpper(t.MAC), nil } // Create a wiredata buffer for the MAC calculation -func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { +func (t *Tsig) Buffer(msg []byte) ([]byte, os.Error) { var ( macbuf []byte buf []byte @@ -148,7 +152,7 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { macbuf = make([]byte, len(t.RequestMAC)) // reqmac should be twice as long n, ok := packStruct(m, macbuf, 0) if !ok { - return nil, false + return nil, &Error{Error: "Failed to pack request mac"} } macbuf = macbuf[:n] } @@ -160,7 +164,7 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { tsig.Fudge = t.Fudge n, ok1 := packStruct(tsig, tsigvar, 0) if !ok1 { - return nil, false + return nil, &Error{Error: "Failed to pack timers"} } tsigvar = tsigvar[:n] } else { @@ -176,7 +180,7 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { tsig.OtherData = "" n, ok1 := packStruct(tsig, tsigvar, 0) if !ok1 { - return nil, false + return nil, &Error{Error: "Failed to pack tsig variables"} } tsigvar = tsigvar[:n] } @@ -186,7 +190,7 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { } else { buf = append(msg, tsigvar...) } - return buf, true + return buf, nil } // Strip the TSIG from the pkt. From 7a466c2f73c1181fba7c48a83db6d7e63b7d0892 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 17:55:14 +0100 Subject: [PATCH 14/24] Better error handling --- resolver.go | 10 +--------- xfr.go | 32 ++++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/resolver.go b/resolver.go index 2afd3605..0fca67dc 100644 --- a/resolver.go +++ b/resolver.go @@ -88,14 +88,6 @@ func (res *Resolver) Query(q *Msg, tsig *Tsig) (d *Msg, err os.Error) { return in, nil } -// Xfr is used in communicating with *xfr functions. -// This structure is returned on the channel. -type Xfr struct { - Add bool // true is to be added, otherwise false - RR - Err os.Error -} - func (res *Resolver) Xfr(q *Msg, t *Tsig, m chan Xfr) { port, err := check(res, q) if err != nil { @@ -120,7 +112,7 @@ Server: _, err = d.Write(sending) if err != nil { - println(err.String()) + continue Server } d.XfrRead(q, m) // check } diff --git a/xfr.go b/xfr.go index 5117dfee..1047e6ec 100644 --- a/xfr.go +++ b/xfr.go @@ -1,8 +1,20 @@ package dns +import ( + "os" +) + // Outgoing AXFR and IXFR implementations // error handling?? +// Xfr is used in communicating with *xfr functions. +// This structure is returned on the channel. +type Xfr struct { + Add bool // true is to be added, otherwise false + RR + Err os.Error +} + // Msg tells use what to do func (d *Conn) XfrRead(q *Msg, m chan Xfr) { switch q.Question[0].Qtype { @@ -30,28 +42,32 @@ func (d *Conn) axfrRead(q *Msg, m chan Xfr) { inb := make([]byte, MaxMsgSize) n, err := d.Read(inb) if err != nil { + m <- Xfr{true, nil, err} return } inb = inb[:n] if !in.Unpack(inb) { + m <- Xfr{true, nil, &Error{Error: "Failed to unpack"}} return } if in.Id != q.Id { + m <- Xfr{true, nil, &Error{Error: "Id mismatch"}} return } if first { if !checkXfrSOA(in, true) { + m <- Xfr{true, nil, &Error{Error: "SOA not first record"}} return } first = !first } if !first { - if d.Tsig != nil { - d.Tsig.TimersOnly = true // Subsequent envelopes use this - } + if d.Tsig != nil { + d.Tsig.TimersOnly = true // Subsequent envelopes use this + } if !checkXfrSOA(in, false) { // Soa record not the last one sendMsg(in, m, false) @@ -120,14 +136,17 @@ func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { } n, err := d.Read(inb) if err != nil { + m <- Xfr{true, nil, err} return } inb = inb[:n] if !in.Unpack(inb) { + m <- Xfr{true, nil, &Error{Error: "Failed to unpack"}} return } if in.Id != q.Id { + m <- Xfr{true, nil, &Error{Error: "Id mismatch"}} return } @@ -139,6 +158,7 @@ func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { // But still check if the returned answer is ok if !checkXfrSOA(in, true) { + m <- Xfr{true, nil, &Error{Error: "SOA not first record"}} return } // This serial is important @@ -149,9 +169,9 @@ func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { // Now we need to check each message for SOA records, to see what we need to do x.Add = true if !first { - if d.Tsig != nil { - d.Tsig.TimersOnly = true - } + if d.Tsig != nil { + d.Tsig.TimersOnly = true + } for k, r := range in.Answer { // If the last record in the IXFR contains the servers' SOA, we should quit if r.Header().Rrtype == TypeSOA { From fd0704f111188a8c4076e1218211fc682b0e0cd4 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 18:37:11 +0100 Subject: [PATCH 15/24] Remove it --- Makefile | 1 - dns.y | 124 ------------------------------------------------------ server.go | 4 ++ xfr.go | 2 +- 4 files changed, 5 insertions(+), 126 deletions(-) delete mode 100644 dns.y diff --git a/Makefile b/Makefile index c034b904..e2a7c0b6 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,6 @@ GOFILES=\ tsig.go\ types.go\ xfr.go\ -# y.go\ include $(GOROOT)/src/Make.pkg diff --git a/dns.y b/dns.y deleted file mode 100644 index b4b8e5ce..00000000 --- a/dns.y +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright Miek Gieben 2011 -// Heavily influenced by the zone-parser from NSD - -%{ - -package dns - -import ( - "fmt" -) - -// A yacc parser for DNS Resource Records contained in strings - -%} - -%union { - val string - rrtype uint16 - class uint16 - ttl uint16 - -} - -/* - * Types known to package dns - */ -%token YA YNS - -/* - * Other elements of the Resource Records - */ -%token TTL -%token CLASS -%token VAL -%% -rr: name TTL CLASS rrtype - { - - }; - -name: label - | name '.' label - -label: VAL - -rrtype: - /* All supported RR types */ - YA - | YNS -%% - -type DnsLex int - -func (DnsLex) Lex(lval *yySymType) int { - - // yylval.rrtype = Str_rr($XX) //give back TypeA, TypeNS - // return Y_A this should be the token, another map? - -//func scan(s string) (string, int) { - if len(s) == 0 { - println("a bit short") - } - raw := []byte(s) - chunk := "" - off := 0 - brace := 0 -redo: - for off < len(raw) { - c := raw[off] -// println(c, string(c)) - switch c { - case '\n': - // normal case?? - if brace > 0 { - off++ - continue - } - case '.': -// println("off", off) - if off == 0 { - print("DOT") - return ".", off + 1 - } else { - return chunk, off - } - case ' ','\t': - if brace != 0 { - off++ - continue - } - // eat whitespace - // Look at next char - if raw[off+1] == ' ' { - off++ - continue - } else { - // if chunk is empty, we have skipped whitespace, and seen nothing - if len(chunk) == 0 { - off++ - goto redo - } - print("VAL ") - return chunk, off - } - case '(': - brace++ - off++ - continue - case ')': - brace-- - if brace < 0 { - println("syntax error") - } - off++ - continue - } - if c == ' ' { println("adding space") } - if c == '\t' { println("adding tab") } - chunk += string(c) - off++ - } - print("VAL ") - return chunk, off -} diff --git a/server.go b/server.go index 2ff1b31f..ef660272 100644 --- a/server.go +++ b/server.go @@ -62,6 +62,10 @@ func ServeTCP(l *net.TCPListener, f func(*Conn, *Msg)) os.Error { panic("not reached") } +// config functions Config +// ListenAndServeTCPTsig +// ListenAndServeUDPTsig + func ListenAndServeTCP(addr string, f func(*Conn, *Msg)) os.Error { a, err := net.ResolveTCPAddr(addr) if err != nil { diff --git a/xfr.go b/xfr.go index 1047e6ec..fe6a0c0a 100644 --- a/xfr.go +++ b/xfr.go @@ -112,7 +112,7 @@ func (d *Conn) axfrWrite(q *Msg, m chan Xfr) { } // TimersOnly foo } - // Everything is send, only the closing soa is left. + // Everything is sent, only the closing soa is left. out.Answer[i] = soa send, _ := out.Pack() _, err := d.Write(send) From a7a8b616f058b72eb96f68dcd1e5ef890959bb5e Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 22:53:15 +0100 Subject: [PATCH 16/24] fix reflect --- TODO | 2 + _examples/reflect/reflect.go | 107 ++++++++++++++--------------------- defaults.go | 9 +++ dns.go | 27 +++++++-- resolver.go | 12 +++- server.go | 13 +++-- xfr.go | 9 +-- 7 files changed, 95 insertions(+), 84 deletions(-) diff --git a/TODO b/TODO index 62a4a1fe..8b2c7bfa 100644 --- a/TODO +++ b/TODO @@ -5,6 +5,8 @@ o clean, small API o fast data structures (rb-tree, when they come available) o api-use should lead to self documenting code o compression (only ownernames?) +o Tsig will probably become an interface which has all configuration + stuff, but this will come later Todo: diff --git a/_examples/reflect/reflect.go b/_examples/reflect/reflect.go index 41461a9b..4d0bf134 100644 --- a/_examples/reflect/reflect.go +++ b/_examples/reflect/reflect.go @@ -18,99 +18,78 @@ package main import ( "os" - "os/signal" "net" "dns" "fmt" + "os/signal" "strconv" ) -func reply(a net.Addr, in *dns.Msg, tcp bool) *dns.Msg { - if in.MsgHdr.Response == true { - return nil // Don't answer responses - } +func reply(c *dns.Conn, in *dns.Msg) []byte { m := new(dns.Msg) - m.MsgHdr.Id = in.MsgHdr.Id - m.MsgHdr.Authoritative = true - m.MsgHdr.Response = true - m.MsgHdr.Opcode = dns.OpcodeQuery + m.SetReply(in.MsgHdr.Id) - m.MsgHdr.Rcode = dns.RcodeSuccess m.Question = make([]dns.Question, 1) m.Answer = make([]dns.RR, 1) m.Extra = make([]dns.RR, 1) - r := new(dns.RR_A) - r.Hdr = dns.RR_Header{Name: "whoami.miek.nl.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} - ip, _ := net.ResolveUDPAddr(a.String()) - r.A = ip.IP + m.Question[0] = in.Question[0] + + var ad net.IP + if c.UDP != nil { + ad = c.Addr.(*net.UDPAddr).IP + } else { + ad = c.Addr.(*net.TCPAddr).IP + } + if ad.To4() != nil { + r := new(dns.RR_A) + r.Hdr = dns.RR_Header{Name: "whoami.miek.nl.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} + if c.UDP != nil { + r.A = c.Addr.(*net.UDPAddr).IP + } else { + r.A = c.Addr.(*net.TCPAddr).IP + } + m.Answer[0] = r + } else { + r := new(dns.RR_AAAA) + r.Hdr = dns.RR_Header{Name: "whoami.miek.nl.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} + if c.UDP != nil { + r.AAAA = c.Addr.(*net.UDPAddr).IP + } else { + r.AAAA = c.Addr.(*net.TCPAddr).IP + } + m.Answer[0] = r + } t := new(dns.RR_TXT) t.Hdr = dns.RR_Header{Name: "whoami.miek.nl.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0} - if tcp { - t.Txt = "Port: " + strconv.Itoa(ip.Port) + " (tcp)" + if c.TCP != nil { + t.Txt = "Port: " + strconv.Itoa(c.Port) + " (tcp)" } else { - t.Txt = "Port: " + strconv.Itoa(ip.Port) + " (udp)" + t.Txt = "Port: " + strconv.Itoa(c.Port) + " (udp)" } - - m.Question[0] = in.Question[0] - m.Answer[0] = r m.Extra[0] = t - return m + + b, _ := m.Pack() + return b } -func replyUDP(c *net.UDPConn, a net.Addr, in *dns.Msg) { - m := reply(a, in, false) - if m == nil { - return +func handle(c *dns.Conn, in *dns.Msg) { + if in.MsgHdr.Response == true { + return // We don't do responses } - fmt.Fprintf(os.Stderr, "%v\n", m) - out, ok := m.Pack() - if !ok { - println("Failed to pack") - return - } - dns.SendUDP(out, c, a) -} - -func replyTCP(c *net.TCPConn, a net.Addr, in *dns.Msg) { - m := reply(a, in, true) - if m == nil { - return - } - fmt.Fprintf(os.Stderr, "%v\n", m) - out, ok := m.Pack() - if !ok { - println("Failed to pack") - return - } - dns.SendTCP(out, c, a) + answer := reply(c, in) + c.Write(answer) } func tcp(addr string, e chan os.Error) { - a, err := net.ResolveTCPAddr(addr) - if err != nil { - e <- err - } - l, err := net.ListenTCP("tcp", a) - if err != nil { - e <- err - } - err = dns.ServeTCP(l, replyTCP) + err := dns.ListenAndServeTCP(addr, handle) e <- err return } func udp(addr string, e chan os.Error) { - a, err := net.ResolveUDPAddr(addr) - if err != nil { - e <- err - } - l, err := net.ListenUDP("udp", a) - if err != nil { - e <- err - } - err = dns.ServeUDP(l, replyUDP) + err := dns.ListenAndServeUDP(addr, handle) e <- err return } diff --git a/defaults.go b/defaults.go index 66ed8f78..2eff77aa 100644 --- a/defaults.go +++ b/defaults.go @@ -1,5 +1,14 @@ package dns +// Create a reply packet. +func (dns *Msg) SetReply(id uint16) { + dns.MsgHdr.Id = id + dns.MsgHdr.Authoritative = true + dns.MsgHdr.Response = true + dns.MsgHdr.Opcode = OpcodeQuery + dns.MsgHdr.Rcode = RcodeSuccess +} + // Create a notify packet. func (dns *Msg) SetNotify(z string, class uint16) { dns.MsgHdr.Opcode = OpcodeNotify diff --git a/dns.go b/dns.go index f4f8cbbd..953e7bb4 100644 --- a/dns.go +++ b/dns.go @@ -58,6 +58,9 @@ type Conn struct { // The remote side of the connection. Addr net.Addr + // The remote port number of the connection. + Port int + // If TSIG is used, this holds all the information Tsig *Tsig @@ -68,6 +71,20 @@ type Conn struct { Attempts int } +// Create a new buffer of the appropiate size. +func (d *Conn) NewBuffer() []byte { + if d.TCP != nil { + b := make([]byte, MaxMsgSize) + return b + } + if d.UDP != nil { + b := make([]byte, DefaultMsgSize) + return b + } + return nil +} + + func (d *Conn) Read(p []byte) (n int, err os.Error) { if d.UDP != nil && d.TCP != nil { return 0, &Error{Error: "UDP and TCP or both non-nil"} @@ -80,6 +97,7 @@ func (d *Conn) Read(p []byte) (n int, err os.Error) { return n, err } d.Addr = addr + d.Port = addr.(*net.UDPAddr).Port case d.TCP != nil: if len(p) < 1 { return 0, &Error{Error: "Buffer too small to read"} @@ -88,6 +106,8 @@ func (d *Conn) Read(p []byte) (n int, err os.Error) { if err != nil || n != 2 { return n, err } + d.Addr = d.TCP.RemoteAddr() + d.Port = d.TCP.RemoteAddr().(*net.TCPAddr).Port l, _ := unpackUint16(p[0:2], 0) if l == 0 { return 0, &Error{Error: "received nil msg length", Server: d.Addr} @@ -132,7 +152,6 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { attempts = d.Attempts } d.SetTimeout() - if d.Tsig != nil { // Create a new buffer with the TSIG added. q, err = d.Tsig.Generate(p) @@ -233,11 +252,7 @@ func (d *Conn) Exchange(request []byte, nosend bool) (reply []byte, err os.Error } } // Layer violation to save memory. Its okay then... - if d.UDP == nil { - reply = make([]byte, MaxMsgSize) - } else { - reply = make([]byte, DefaultMsgSize) - } + reply = d.NewBuffer() n, err = d.Read(reply) if err != nil { return nil, err diff --git a/resolver.go b/resolver.go index 0fca67dc..61e1fb73 100644 --- a/resolver.go +++ b/resolver.go @@ -43,7 +43,11 @@ type Resolver struct { // in, err := res.Query(m, nil) // Ask the question // // Note that message id checking is left to the caller. -func (res *Resolver) Query(q *Msg, tsig *Tsig) (d *Msg, err os.Error) { +func (res *Resolver) Query(q *Msg) (d *Msg, err os.Error) { + return res.QueryTsig(q, nil) +} + +func (res *Resolver) QueryTsig(q *Msg, tsig *Tsig) (d *Msg, err os.Error) { var c net.Conn var inb []byte in := new(Msg) @@ -88,7 +92,11 @@ func (res *Resolver) Query(q *Msg, tsig *Tsig) (d *Msg, err os.Error) { return in, nil } -func (res *Resolver) Xfr(q *Msg, t *Tsig, m chan Xfr) { +func (res *Resolver) Xfr(q *Msg, m chan Xfr) { + res.XfrTsig(q, nil, m) +} + +func (res *Resolver) XfrTsig(q *Msg, t *Tsig, m chan Xfr) { port, err := check(res, q) if err != nil { return diff --git a/server.go b/server.go index ef660272..4ecdcc43 100644 --- a/server.go +++ b/server.go @@ -12,8 +12,9 @@ import ( ) // For both -> logging +// Add tsig stuff as in resolver.go -func ServeUDP(l *net.UDPConn, f func(*Conn, *Msg)) os.Error { +func HandleUDP(l *net.UDPConn, f func(*Conn, *Msg)) os.Error { for { m := make([]byte, DefaultMsgSize) n, addr, e := l.ReadFromUDP(m) @@ -25,6 +26,7 @@ func ServeUDP(l *net.UDPConn, f func(*Conn, *Msg)) os.Error { d := new(Conn) d.UDP = l d.Addr = addr + d.Port = addr.Port // Why not the same as in dns.go, line 96 msg := new(Msg) if !msg.Unpack(m) { @@ -35,7 +37,7 @@ func ServeUDP(l *net.UDPConn, f func(*Conn, *Msg)) os.Error { panic("not reached") } -func ServeTCP(l *net.TCPListener, f func(*Conn, *Msg)) os.Error { +func HandleTCP(l *net.TCPListener, f func(*Conn, *Msg)) os.Error { for { c, e := l.AcceptTCP() if e != nil { @@ -44,8 +46,9 @@ func ServeTCP(l *net.TCPListener, f func(*Conn, *Msg)) os.Error { d := new(Conn) d.TCP = c d.Addr = c.RemoteAddr() + d.Port = d.TCP.RemoteAddr().(*net.TCPAddr).Port - m := make([]byte, MaxMsgSize) // This may start to hurt someday. + m := d.NewBuffer() n, e := d.Read(m) if e != nil { continue @@ -75,7 +78,7 @@ func ListenAndServeTCP(addr string, f func(*Conn, *Msg)) os.Error { if err != nil { return err } - err = ServeTCP(l, f) + err = HandleTCP(l, f) return err } @@ -88,6 +91,6 @@ func ListenAndServeUDP(addr string, f func(*Conn, *Msg)) os.Error { if err != nil { return err } - err = ServeUDP(l, f) + err = HandleUDP(l, f) return err } diff --git a/xfr.go b/xfr.go index fe6a0c0a..4e5898a2 100644 --- a/xfr.go +++ b/xfr.go @@ -39,7 +39,7 @@ func (d *Conn) axfrRead(q *Msg, m chan Xfr) { first := true in := new(Msg) for { - inb := make([]byte, MaxMsgSize) + inb := d.NewBuffer() n, err := d.Read(inb) if err != nil { m <- Xfr{true, nil, err} @@ -128,12 +128,7 @@ func (d *Conn) ixfrRead(q *Msg, m chan Xfr) { first := true in := new(Msg) for { - var inb []byte - if d.TCP != nil { - inb = make([]byte, MaxMsgSize) - } else { - inb = make([]byte, DefaultMsgSize) - } + inb := d.NewBuffer() n, err := d.Read(inb) if err != nil { m <- Xfr{true, nil, err} From a15c82cf28b1762dac4b33263456ce7cc50283b8 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 22:59:50 +0100 Subject: [PATCH 17/24] Fix reflect --- _examples/reflect/reflect.go | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/_examples/reflect/reflect.go b/_examples/reflect/reflect.go index 4d0bf134..fb4580d2 100644 --- a/_examples/reflect/reflect.go +++ b/_examples/reflect/reflect.go @@ -33,31 +33,27 @@ func reply(c *dns.Conn, in *dns.Msg) []byte { m.Answer = make([]dns.RR, 1) m.Extra = make([]dns.RR, 1) + // Copy the question. m.Question[0] = in.Question[0] + // Some foo to check if we are called trough ip6 or ip4. + // We add the correct reply RR. var ad net.IP if c.UDP != nil { ad = c.Addr.(*net.UDPAddr).IP } else { ad = c.Addr.(*net.TCPAddr).IP } + if ad.To4() != nil { r := new(dns.RR_A) r.Hdr = dns.RR_Header{Name: "whoami.miek.nl.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} - if c.UDP != nil { - r.A = c.Addr.(*net.UDPAddr).IP - } else { - r.A = c.Addr.(*net.TCPAddr).IP - } + r.A = ad m.Answer[0] = r } else { r := new(dns.RR_AAAA) - r.Hdr = dns.RR_Header{Name: "whoami.miek.nl.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} - if c.UDP != nil { - r.AAAA = c.Addr.(*net.UDPAddr).IP - } else { - r.AAAA = c.Addr.(*net.TCPAddr).IP - } + r.Hdr = dns.RR_Header{Name: "whoami.miek.nl.", Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0} + r.AAAA = ad m.Answer[0] = r } From 2c82068ec8a9593ccf0ee257c97ee1812936e5eb Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 23:00:48 +0100 Subject: [PATCH 18/24] remove ns --- _examples/Makefile | 1 - _examples/ns/Makefile | 8 --- _examples/ns/ns.go | 130 ------------------------------------------ 3 files changed, 139 deletions(-) delete mode 100644 _examples/ns/Makefile delete mode 100644 _examples/ns/ns.go diff --git a/_examples/Makefile b/_examples/Makefile index 627a9602..330dccb3 100644 --- a/_examples/Makefile +++ b/_examples/Makefile @@ -4,7 +4,6 @@ chaos \ axfr \ reflect \ funkensturm \ -ns \ all: for i in $(EXAMPLES); do gomake -C $$i; done diff --git a/_examples/ns/Makefile b/_examples/ns/Makefile deleted file mode 100644 index 676dbf01..00000000 --- a/_examples/ns/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# 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=ns -GOFILES=ns.go -DEPS=../../ -include $(GOROOT)/src/Make.cmd diff --git a/_examples/ns/ns.go b/_examples/ns/ns.go deleted file mode 100644 index 47b61d7d..00000000 --- a/_examples/ns/ns.go +++ /dev/null @@ -1,130 +0,0 @@ -package main - -import ( - "os" - "dns" - "net" - "fmt" - "flag" - "os/signal" -// "json" -) - -var counter int - -func main() { -// var zone *string = flag.String("zone", "", "The zone to serve") - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s zone...\n", os.Args[0]) - flag.PrintDefaults() - } - flag.Parse() - - m := new(dns.Msg) - m.MsgHdr.Id = dns.Id() - m.MsgHdr.Authoritative = true - m.MsgHdr.AuthenticatedData = false - m.MsgHdr.RecursionAvailable = true - m.MsgHdr.Response = true - m.MsgHdr.Opcode = dns.OpcodeQuery - m.MsgHdr.Rcode = dns.RcodeSuccess - m.Question = make([]dns.Question, 1) - m.Question[0] = dns.Question{"miek.nl.", dns.TypeTXT, dns.ClassINET} - m.Answer = make([]dns.RR, 1) - t := new(dns.RR_TXT) - t.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 3600} - t.Txt = "Een antwoord" - m.Answer[0] = t - - errchan := make(chan os.Error) - go udp("127.0.0.1:8054", errchan) - go tcp("127.0.0.1:8054", errchan) - - forever: - for { - select { - case e := <-errchan: - fmt.Printf("Error received, stopping: %s\n", e.String()) - break forever - case <-signal.Incoming: - fmt.Printf("Signal received, stopping\n") - break forever - } - } - close(errchan) - fmt.Printf("Queries answered: %d\n", counter) -} - -func tcp(addr string, e chan os.Error) { - a, err := net.ResolveTCPAddr(addr) - if err != nil { - e <- err - } - l, err := net.ListenTCP("tcp", a) - if err != nil { - e <- err - } - err = dns.ServeTCP(l, replyTCP) - e <- err - return -} - -func udp(addr string, e chan os.Error) { - a, err := net.ResolveUDPAddr(addr) - if err != nil { - e <- err - } - l, err := net.ListenUDP("udp", a) - if err != nil { - e <- err - } - err = dns.ServeUDP(l, replyUDP) - e <- err - return -} - - -func createpkg(id uint16, tcp bool, remove net.Addr) []byte { - m := new(dns.Msg) - m.MsgHdr.Id = id - m.MsgHdr.Authoritative = true - m.MsgHdr.AuthenticatedData = false - m.MsgHdr.RecursionAvailable = true - m.MsgHdr.Response = true - m.MsgHdr.Opcode = dns.OpcodeQuery - m.MsgHdr.Rcode = dns.RcodeSuccess - m.Question = make([]dns.Question, 1) - m.Question[0] = dns.Question{"miek.nl.", dns.TypeTXT, dns.ClassINET} - m.Answer = make([]dns.RR, 1) - t := new(dns.RR_TXT) - t.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 3600} - if tcp { - t.Txt = "Dit is iets anders TCP" - } else { - t.Txt = "Dit is iets anders UDP" - } - m.Answer[0] = t - out, _ := m.Pack() - return out -} - -func replyUDP(c *net.UDPConn, a net.Addr, in *dns.Msg) { - if in.MsgHdr.Response == true { - // Uh... answering to an response?? - // dont think so - return - } - out := createpkg(in.MsgHdr.Id, false, a) - dns.SendUDP(out, c, a) - counter++ -} - -func replyTCP(c *net.TCPConn, a net.Addr, in *dns.Msg) { - if in.MsgHdr.Response == true { - return - } - out := createpkg(in.MsgHdr.Id, true, a) - dns.SendTCP(out, c, a) - counter++ -} - From f933c60b23a9ca8889a490a63954a5785f9890b9 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 23:02:26 +0100 Subject: [PATCH 19/24] Updates, reflect works --- TODO | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index 8b2c7bfa..eeb9c496 100644 --- a/TODO +++ b/TODO @@ -4,27 +4,25 @@ o symmetrical, client side stuff should be mirrored in the server stuff o clean, small API o fast data structures (rb-tree, when they come available) o api-use should lead to self documenting code + o compression (only ownernames?) + + +o Closing of tcp connections? o Tsig will probably become an interface which has all configuration stuff, but this will come later - Todo: * Parsing from strings, going with goyacc and .cz lexer? * encoding NSEC3/NSEC bitmaps, DEcoding works * HIP RR (needs list of domain names, need slice stuff for that) -* Resolver can see if you want ixfr or axfr from the msg no - need for seperate functions -* Is subdomain, is glue helper functions +* Is subdomain, is glue helper functions for this kind of stuff Issues: * Check the network order, it works now, but this is on Intel?? * Make the testsuite work with public DNS servers * pack/Unpack smaller. EDNS 'n stuff can be folded in * SetDefaults() for *all* types? -* Closing of tcp connections? - -* Refacter the IXFR/AXFR/TSIG code Examples: * Test impl of nameserver, with a small zone, 1 KSK and online signing From bc4d7ed74849bdb164e0b2f905804e276e6892ed Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Tue, 22 Mar 2011 09:13:25 +0100 Subject: [PATCH 20/24] add this example too --- _examples/key2ds/Makefile | 8 +++++++ _examples/key2ds/key2ds.go | 43 ++++++++++++++++++++++++++++++++++++ _examples/reflect/reflect.go | 2 +- dnssec.go | 4 +++- 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 _examples/key2ds/Makefile create mode 100644 _examples/key2ds/key2ds.go diff --git a/_examples/key2ds/Makefile b/_examples/key2ds/Makefile new file mode 100644 index 00000000..be182665 --- /dev/null +++ b/_examples/key2ds/Makefile @@ -0,0 +1,8 @@ +# 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=key2ds +GOFILES=key2ds.go +DEPS=../../ +include $(GOROOT)/src/Make.cmd diff --git a/_examples/key2ds/key2ds.go b/_examples/key2ds/key2ds.go new file mode 100644 index 00000000..84749626 --- /dev/null +++ b/_examples/key2ds/key2ds.go @@ -0,0 +1,43 @@ +package main + +// Print the MX records of a domain +// (c) Miek Gieben - 2011 +import ( + "dns" + "os" + "fmt" +) + +func main() { + r := new(dns.Resolver) + r.FromFile("/etc/resolv.conf") + if len(os.Args) != 2 { + fmt.Printf("%s DOMAIN\n", os.Args[0]) + os.Exit(1) + } + m := new(dns.Msg) + m.MsgHdr.RecursionDesired = true //only set this bit + m.Question = make([]dns.Question, 1) + m.Question[0] = dns.Question{os.Args[1], dns.TypeDNSKEY, dns.ClassINET} + + in, err := r.Query(m) + if in != nil { + if in.Rcode != dns.RcodeSuccess { + fmt.Printf(" *** invalid answer name %s after DNSKEY query for %s\n", os.Args[1], os.Args[1]) + os.Exit(1) + } + // Stuff must be in the answer section + for _, k := range in.Answer { + // Foreach key would need to provide a DS records, both sha1 and sha256 + if key, ok := k.(*dns.RR_DNSKEY); ok { + fmt.Printf("%v\n", key) + ds := key.ToDS(dns.HashSHA1) + fmt.Printf("\t%v\n", ds) + ds = key.ToDS(dns.HashSHA256) + fmt.Printf("\t%v\n", ds) + } + } + } else { + fmt.Printf("*** error: %s\n", err.String()) + } +} diff --git a/_examples/reflect/reflect.go b/_examples/reflect/reflect.go index fb4580d2..4cd7dbcc 100644 --- a/_examples/reflect/reflect.go +++ b/_examples/reflect/reflect.go @@ -36,7 +36,7 @@ func reply(c *dns.Conn, in *dns.Msg) []byte { // Copy the question. m.Question[0] = in.Question[0] - // Some foo to check if we are called trough ip6 or ip4. + // Some foo to check if we are called through ip6 or ip4. // We add the correct reply RR. var ad net.IP if c.UDP != nil { diff --git a/dnssec.go b/dnssec.go index 93422076..8733ccbf 100644 --- a/dnssec.go +++ b/dnssec.go @@ -32,7 +32,8 @@ const ( // DNSSEC hashing codes. const ( - HashSHA1 = iota + _ = iota + HashSHA1 HashSHA256 HashGOST94 ) @@ -104,6 +105,7 @@ func (k *RR_DNSKEY) ToDS(h int) *RR_DS { ds := new(RR_DS) ds.Hdr.Name = k.Hdr.Name ds.Hdr.Class = k.Hdr.Class + ds.Hdr.Rrtype = TypeDS ds.Hdr.Ttl = k.Hdr.Ttl ds.Algorithm = k.Algorithm ds.DigestType = uint8(h) From 7700ba5e9df80a5aeca82bac2df91bc29856eb92 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Tue, 22 Mar 2011 09:18:24 +0100 Subject: [PATCH 21/24] add another example --- _examples/Makefile | 1 + _examples/key2ds/key2ds.go | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/_examples/Makefile b/_examples/Makefile index 330dccb3..30c47f6b 100644 --- a/_examples/Makefile +++ b/_examples/Makefile @@ -4,6 +4,7 @@ chaos \ axfr \ reflect \ funkensturm \ +ds2key \ all: for i in $(EXAMPLES); do gomake -C $$i; done diff --git a/_examples/key2ds/key2ds.go b/_examples/key2ds/key2ds.go index 84749626..3431847c 100644 --- a/_examples/key2ds/key2ds.go +++ b/_examples/key2ds/key2ds.go @@ -30,11 +30,10 @@ func main() { for _, k := range in.Answer { // Foreach key would need to provide a DS records, both sha1 and sha256 if key, ok := k.(*dns.RR_DNSKEY); ok { - fmt.Printf("%v\n", key) ds := key.ToDS(dns.HashSHA1) - fmt.Printf("\t%v\n", ds) + fmt.Printf("%v\n", ds) ds = key.ToDS(dns.HashSHA256) - fmt.Printf("\t%v\n", ds) + fmt.Printf("%v\n", ds) } } } else { From bb47d77e04101ee103e921037ab31d3bc10d97d3 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Tue, 22 Mar 2011 09:35:25 +0100 Subject: [PATCH 22/24] Some more items --- TODO | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index eeb9c496..9d742bef 100644 --- a/TODO +++ b/TODO @@ -5,12 +5,14 @@ o clean, small API o fast data structures (rb-tree, when they come available) o api-use should lead to self documenting code +o zone structure -- only as rb-tree o compression (only ownernames?) +o Key2DS, also for offline keys -- need to parse them ofcourse o Closing of tcp connections? o Tsig will probably become an interface which has all configuration - stuff, but this will come later + stuff, but this will come later. Config which has Tsig function Todo: * Parsing from strings, going with goyacc and .cz lexer? From 28b008e05e28e4cff1b92614efc990ba5758d330 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Tue, 22 Mar 2011 09:44:33 +0100 Subject: [PATCH 23/24] tweaks --- README.markdown | 1 + edns.go | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index c59c9f09..085a05fb 100644 --- a/README.markdown +++ b/README.markdown @@ -38,6 +38,7 @@ Miek Gieben - 2010, 2011 - miek@miek.nl * 403{3,4,5} - DNSSEC + validation functions * 4255 - SSHFP * 4408 - SPF +* 4509 - SHA256 Hash in DS * 4635 - HMAC SHA TSIG * 5001 - NSID * 5155 - NSEC diff --git a/edns.go b/edns.go index 497d9431..0b19202e 100644 --- a/edns.go +++ b/edns.go @@ -7,9 +7,10 @@ import ( // EDNS0 Options const ( - OptionCodeLLQ = 1 // not used - OptionCodeUL = 2 // not used - OptionCodeNSID = 3 // NSID, RFC5001 + _ = iota + OptionCodeLLQ // not used + OptionCodeUL // not used + OptionCodeNSID // NSID, RFC5001 _DO = 1 << 7 // dnssec ok ) From 265bb3f79e23302e9a711c290f70d8ccf822a34c Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Tue, 22 Mar 2011 19:47:24 +0100 Subject: [PATCH 24/24] tweak comment --- _examples/key2ds/key2ds.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_examples/key2ds/key2ds.go b/_examples/key2ds/key2ds.go index 3431847c..f561fd61 100644 --- a/_examples/key2ds/key2ds.go +++ b/_examples/key2ds/key2ds.go @@ -1,6 +1,6 @@ package main -// Print the MX records of a domain +// Print the DNSKEY records of a domain as DS records // (c) Miek Gieben - 2011 import ( "dns"