diff --git a/Makefile b/Makefile index 548e1f5d..7506dbeb 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ GOFILES=\ nsec3.go\ resolver.go\ config.go\ + responder.go # y.go\ include $(GOROOT)/src/Make.pkg diff --git a/responder.go b/responder.go new file mode 100644 index 00000000..c9ab2548 --- /dev/null +++ b/responder.go @@ -0,0 +1,162 @@ +// Copyright 2011 Miek Gieben. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// DNS server implementation + +// Package responder implements a DNS server. Any nameserver needs to implement +// the Responder interface to get things going. Each incoming query is handled +// in a seperate goroutine. +// +// Typical usage of the package: +// +// type myserv Server +// func (s *myserv) ResponderUDP(c *net.UDPConn, a net.Addr, in []byte) { /* UDP reply */ } +// func (s *myserv) ResponderTCP(c *net.TCPConn, in []byte) { /* TCP reply */} +// +// s := new(Server) // create new sever +// s.Address = "127.0.0.1" // listen address +// s.Port = "8053" // listen port +// var m *myserv +// ch :=make(chan bool) +// go s.NewResponder(m, ch) // start the responder +package responder + +import ( + "os" + "net" + "dns" +) + +type msg struct { + udp *net.UDPConn // udp conn + tcp *net.TCPConn // tcp conn + addr net.Addr // remote address + msg []byte // raw dns message + err os.Error // any errors +} + +// Every nameserver implements the Hander interface. It defines +// the kind of nameserver +type Handler interface { + // Receives the raw message content and writes back + // an UDP response. An UDP connection needs a remote + // address to write to. ServeUDP() must take care of sending + // any response back to the requestor. + ServeUDP(c *net.UDPConn, a net.Addr, in []byte) + // Receives the raw message content and writes back + // a TCP response. A TCP connection does need to + // know explicitly be told the remote address. ServeTCP() must + // take care of sending back a response to the requestor. + ServeTCP(c *net.TCPConn, in []byte) +} + +func ServeUDP(l *net.UDPConn, handler Handler) os.Error { + if handler == nil { + // handler == DefaultServer + } + for { + m := make([]byte, dns.DefaultMsgSize) // TODO(mg) out of this loop? + n, radd, err := l.ReadFromUDP(m) + if err != nil { + return err + } + m = m[:n] + go handler.ServeUDP(l, radd, m) + } + panic("not reached") +} + +func ServeTCP(l *net.TCPListener, handler Handler) os.Error { + if handler == nil { + // handler = DefaultServer + } + for { + b := make([]byte, 2) // receiver length + c, err := l.AcceptTCP() + if err != nil { + return err + } + + n, cerr := c.Read(b) + if cerr != nil { + return cerr + } + length := uint16(b[0])<<8 | uint16(b[1]) + if length == 0 { + return &dns.Error{Error: "received nil msg length"} + } + m := make([]byte, length) + + n, cerr = c.Read(m) + if cerr != nil { + return cerr + } + i := n + if i < int(length) { + n, err = c.Read(m[i:]) + if err != nil { + return err + } + i += n + } + go handler.ServeTCP(c, m) + } + panic("not reached") +} + +func ListenAndServeTCP(addr string, handler Handler) os.Error { + ta, err := net.ResolveTCPAddr(addr) + if err != nil { + return err + } + l, err := net.ListenTCP("tcp", ta) + if err != nil { + return err + } + err = ServeTCP(l, handler) + l.Close() + return err +} + +func ListenAndServeUDP(addr string, handler Handler) os.Error { + ua, err := net.ResolveUDPAddr(addr) + if err != nil { + return err + } + l, err := net.ListenUDP("udp", ua) + if err != nil { + return err + } + err = ServeUDP(l, handler) + l.Close() + return err +} + +// Send a buffer on the TCP connection. +func SendTCP(m []byte, c *net.TCPConn) 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 +} + +// 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/responder_test.go b/responder_test.go new file mode 100644 index 00000000..d6d076f9 --- /dev/null +++ b/responder_test.go @@ -0,0 +1,189 @@ +package responder + +import ( + "os" + "testing" + "fmt" + "dns" + "net" + "time" +) + +type myserv Server + +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 (s *myserv) ResponderUDP(c *net.UDPConn, a net.Addr, in []byte) { + inmsg := new(dns.Msg) + inmsg.Unpack(in) + if inmsg.MsgHdr.Response == true { + // Uh... answering to an response?? + // dont think so + return + } + out := createpkg(inmsg.MsgHdr.Id, false, a) + SendUDP(out, c, a) + // Meta.QLen/RLen/QueryStart/QueryEnd can be filled in at + // this point for logging purposses or anything else +} + +func (s *myserv) ResponderTCP(c *net.TCPConn, in []byte) { + inmsg := new(dns.Msg) + inmsg.Unpack(in) + if inmsg.MsgHdr.Response == true { + // Uh... answering to an response?? + // dont think so + return + } + out := createpkg(inmsg.MsgHdr.Id, true, c.RemoteAddr()) + SendTCP(out, c) +} + +func TestResponder(t *testing.T) { + /* udp servertje */ + su := new(Server) + su.Address = "127.0.0.1" + su.Port = "8053" + var us *myserv + uch := make(chan os.Error) + go su.NewResponder(us, uch) + + /* tcp servertje */ + st := new(Server) + st.Address = "127.0.0.1" + st.Port = "8053" + st.Tcp = true + var ts *myserv + tch := make(chan os.Error) + go st.NewResponder(ts, tch) + time.Sleep(1 * 1e9) + uch <- nil + tch <- nil +} + +/* +func TestReflectorResponder(t *testing.T) { + stop := make(chan os.Error) + s := new(Server) + s.Port = "8053" + s.Address = "127.0.0.1" + + stoptcp := make(chan os.Error) + stcp := new(Server) + stcp.Port = "8053" + stcp.Address = "127.0.0.1" + stcp.Tcp = true + + go stcp.NewResponder(Reflector, stoptcp) + go s.NewResponder(Reflector, stop) + + time.Sleep(1 * 1e9) + stop <- nil + stoptcp <- nil +} +*/ + +type servtsig Server + +func createpkgtsig(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 (s *servtsig) ResponderUDP(c *net.UDPConn, a net.Addr, in []byte) { + inmsg := new(dns.Msg) + inmsg.Unpack(in) + fmt.Printf("%v\n", inmsg) + if inmsg.MsgHdr.Response == true { + // Uh... answering to an response?? + // dont think so + return + } + rr := inmsg.Extra[len(inmsg.Extra)-1] + switch t := rr.(type) { + case *dns.RR_TSIG: + v := t.Verify(inmsg, "awwLOtRfpGE+rRKF2+DEiw==") + println(v) + } + + + out := createpkgtsig(inmsg.MsgHdr.Id, false, a) + SendUDP(out, c, a) + // Meta.QLen/RLen/QueryStart/QueryEnd can be filled in at + // this point for logging purposses or anything else +} + +func (s *servtsig) ResponderTCP(c *net.TCPConn, in []byte) { + inmsg := new(dns.Msg) + inmsg.Unpack(in) + if inmsg.MsgHdr.Response == true { + // Uh... answering to an response?? + // dont think so + return + } + out := createpkgtsig(inmsg.MsgHdr.Id, true, c.RemoteAddr()) + SendTCP(out, c) +} + +func TestResponderTsig(t *testing.T) { + /* udp servertje */ + su := new(Server) + su.Address = "127.0.0.1" + su.Port = "8053" + var us *servtsig + uch := make(chan os.Error) + go su.NewResponder(us, uch) + + /* tcp servertje */ + st := new(Server) + st.Address = "127.0.0.1" + st.Port = "8053" + st.Tcp = true + var ts *servtsig + tch := make(chan os.Error) + go st.NewResponder(ts, tch) + time.Sleep(1 * 1e9) + uch <- nil + tch <- nil +}