diff --git a/_examples/axfr/axfr.go b/_examples/axfr/axfr.go index ffc6b9b0..702e8e18 100644 --- a/_examples/axfr/axfr.go +++ b/_examples/axfr/axfr.go @@ -15,7 +15,7 @@ func main() { m.Question = make([]dns.Question, 1) m.Question[0] = dns.Question{"atoom.net", dns.TypeAXFR, dns.ClassINET} - ch <- resolver.Msg{m, nil} + ch <- resolver.Msg{m, nil, nil} for dm := range ch { fmt.Printf("%v\n",dm.Dns) } diff --git a/_examples/chaos/chaos.go b/_examples/chaos/chaos.go index ee6b7db0..89482589 100644 --- a/_examples/chaos/chaos.go +++ b/_examples/chaos/chaos.go @@ -28,13 +28,13 @@ func main() { // set the resolver to query the NS directly r.Servers = []string{a.String()} m.Question[0] = dns.Question{"version.bind.", dns.TypeTXT, dns.ClassCHAOS} - qr <- resolver.Msg{m, nil} + qr <- resolver.Msg{m, nil, nil} in = <-qr if in.Dns != nil && in.Dns.Answer != nil { fmt.Printf("%v\n", in.Dns.Answer[0]) } m.Question[0] = dns.Question{"hostname.bind.", dns.TypeTXT, dns.ClassCHAOS} - qr <- resolver.Msg{m, nil} + qr <- resolver.Msg{m, nil, nil} in = <-qr if in.Dns != nil && in.Dns.Answer != nil { fmt.Printf("%v\n", in.Dns.Answer[0]) @@ -42,7 +42,7 @@ func main() { } // Stop the resolver, send it a null mesg - qr <- resolver.Msg{nil, nil} + qr <- resolver.Msg{nil, nil, nil} <-qr } @@ -53,7 +53,7 @@ func addresses(qr chan resolver.Msg, name string) []net.IP { var ips []net.IP m.Question[0] = dns.Question{os.Args[1], dns.TypeA, dns.ClassINET} - qr <- resolver.Msg{m, nil} + qr <- resolver.Msg{m, nil, nil} in := <-qr if in.Dns == nil { @@ -69,7 +69,7 @@ func addresses(qr chan resolver.Msg, name string) []net.IP { ips = append(ips, a.(*dns.RR_A).A) } m.Question[0] = dns.Question{os.Args[1], dns.TypeAAAA, dns.ClassINET} - qr <- resolver.Msg{m, nil} + qr <- resolver.Msg{m, nil, nil} in = <-qr if in.Dns == nil { diff --git a/_examples/mx/mx.go b/_examples/mx/mx.go index 62a157b1..b7e3fae4 100644 --- a/_examples/mx/mx.go +++ b/_examples/mx/mx.go @@ -23,7 +23,7 @@ func main() { m.Question = make([]dns.Question, 1) m.Question[0] = dns.Question{os.Args[1], dns.TypeMX, dns.ClassINET} - qr <- resolver.Msg{m, nil} + qr <- resolver.Msg{m, nil, nil} in := <-qr if in.Dns != nil { if in.Dns.Rcode != dns.RcodeSuccess { @@ -39,6 +39,6 @@ func main() { } // Stop the resolver, send it a null mesg - qr <- resolver.Msg{nil, nil} + qr <- resolver.Msg{nil, nil, nil} <-qr } diff --git a/_examples/q/q.go b/_examples/q/q.go index 65cdef48..35267936 100644 --- a/_examples/q/q.go +++ b/_examples/q/q.go @@ -38,13 +38,7 @@ FLAGS: nameserver = flag.Arg(i) continue FLAGS } - // If it looks like a class, it is a class - for k, v := range dns.Class_str { - if v == strings.ToUpper(flag.Arg(i)) { - qclass = k - continue FLAGS - } - } + // First class, then type, to make ANY queries possible // And if it looks like type, it is a type for k, v := range dns.Rr_str { if v == strings.ToUpper(flag.Arg(i)) { @@ -52,6 +46,13 @@ FLAGS: continue FLAGS } } + // If it looks like a class, it is a class + for k, v := range dns.Class_str { + if v == strings.ToUpper(flag.Arg(i)) { + qclass = k + continue FLAGS + } + } // Anything else is a qname qname = append(qname, flag.Arg(i)) } @@ -96,17 +97,18 @@ FLAGS: for _, v := range qname { m.Question[0] = dns.Question{v, qtype, qclass} m.SetId() - qr <- resolver.Msg{m, nil} + qr <- resolver.Msg{m, nil, nil} in := <-qr if in.Dns != nil { if m.Id != in.Dns.Id { fmt.Printf("Id mismatch\n") } fmt.Printf("%v\n", in.Dns) + fmt.Printf("%s\n", in.Meta) } else { fmt.Printf("%v\n", in.Error.String()) } } - qr <- resolver.Msg{nil, nil} + qr <- resolver.Msg{nil, nil, nil} <-qr } diff --git a/dns.go b/dns.go index b247ac08..5402ba46 100644 --- a/dns.go +++ b/dns.go @@ -52,6 +52,24 @@ func (e *Error) String() string { return e.Error } +// Meta data when querying +type Meta struct { + QLen int // query length in bytes + RLen int // reply length in bytes + QueryStart int64 // start of query in nanoseconds epoch + QueryEnd int64 // end of query in nanosecond epoch +} + +func (m *Meta) String() string { + s := ";; Query time: " + strconv.Itoa(int(m.QueryEnd - m.QueryStart)) + " nsec" + s += "\n;; MSG SIZE rcvd: " + strconv.Itoa(m.RLen) + ", sent: " + strconv.Itoa(m.QLen) + rf := float32(m.RLen) + qf := float32(m.QLen) + s += " (" + strconv.Ftoa32(rf/qf, 'f', 2) + ":1)" + // WHEN?? + return s +} + type RR interface { Header() *RR_Header String() string diff --git a/msg.go b/msg.go index 1d3d040a..c4e80258 100644 --- a/msg.go +++ b/msg.go @@ -92,6 +92,7 @@ var Rr_str = map[uint16]string{ TypeTSIG: "TSIG", // Meta RR TypeAXFR: "AXFR", // Meta RR TypeIXFR: "IXFR", // Meta RR + TypeALL: "ANY", // Meta RR } // Reverse of Rr_str (needed for parsing) diff --git a/resolver/resolver.go b/resolver/resolver.go index efedb8b0..cfce346f 100644 --- a/resolver/resolver.go +++ b/resolver/resolver.go @@ -28,6 +28,7 @@ import ( "os" "net" "dns" + "time" ) const packErr = "Failed to pack message" @@ -39,6 +40,7 @@ const servErr = "No servers could be reached" // Sending a nil message instructs to resolver to stop. type Msg struct { Dns *dns.Msg + Meta *dns.Meta Error os.Error } @@ -71,6 +73,7 @@ func query(res *Resolver, msg chan Msg) { err os.Error in *dns.Msg port string + meta *dns.Meta ) // len(res.Server) == 0 can be perfectly valid, when setting up the resolver if res.Port == "" { @@ -84,7 +87,7 @@ func query(res *Resolver, msg chan Msg) { case out := <-msg: //msg received if out.Dns == nil { // nil message, quit the goroutine - msg <- Msg{nil, nil} + msg <- Msg{nil, nil, nil} close(msg) return } @@ -97,7 +100,7 @@ func query(res *Resolver, msg chan Msg) { } sending, ok := out.Dns.Pack() if !ok { - msg <- Msg{nil, &dns.Error{Error: packErr}} + msg <- Msg{nil, nil, &dns.Error{Error: packErr}} continue } @@ -113,9 +116,9 @@ func query(res *Resolver, msg chan Msg) { continue } if res.Tcp { - in, err = exchangeTCP(c, sending, res, true) + in, meta, err = exchangeTCP(c, sending, res, true) } else { - in, err = exchangeUDP(c, sending, res, true) + in, meta, err = exchangeUDP(c, sending, res, true) } // Check id in.id != out.id, should be checked in the client! @@ -123,11 +126,13 @@ func query(res *Resolver, msg chan Msg) { if err != nil { continue } + break } + meta.QLen = len(sending) if err != nil { - msg <- Msg{nil, err} + msg <- Msg{nil, meta, err} } else { - msg <- Msg{in, nil} + msg <- Msg{in, meta, nil} } } } @@ -146,6 +151,7 @@ func (res *Resolver) NewXfer() (ch chan Msg) { func axfr(res *Resolver, msg chan Msg) { var port string var err os.Error + meta := new(dns.Meta) var in *dns.Msg if res.Port == "" { port = "53" @@ -158,7 +164,7 @@ func axfr(res *Resolver, msg chan Msg) { case out := <-msg: // msg received if out.Dns == nil { // stop - msg <- Msg{nil, nil} + msg <- Msg{nil, nil, nil} close(msg) return } @@ -166,8 +172,9 @@ func axfr(res *Resolver, msg chan Msg) { out.Dns.SetId() sending, ok := out.Dns.Pack() if !ok { - msg <- Msg{nil, &dns.Error{Error: packErr}} + msg <- Msg{nil, nil, &dns.Error{Error: packErr}} } + meta.QLen = len(sending) SERVER: for i := 0; i < len(res.Servers); i++ { server := res.Servers[i] + ":" + port @@ -180,9 +187,9 @@ func axfr(res *Resolver, msg chan Msg) { // Start the AXFR for { if first { - in, cerr = exchangeTCP(c, sending, res, true) + in, meta, cerr = exchangeTCP(c, sending, res, true) } else { - in, cerr = exchangeTCP(c, sending, res, false) + in, meta, cerr = exchangeTCP(c, sending, res, false) } if cerr != nil { @@ -204,12 +211,12 @@ func axfr(res *Resolver, msg chan Msg) { if !first { if !checkSOA(in, false) { // Soa record not the last one - msg <- Msg{in, nil} + msg <- Msg{in, meta, nil} continue // next } else { c.Close() - msg <- Msg{in, nil} + msg <- Msg{in, meta, nil} close(msg) return } @@ -218,7 +225,7 @@ func axfr(res *Resolver, msg chan Msg) { println("Should never be reached") return } - msg <- Msg{nil, err} + msg <- Msg{nil, meta, err} close(msg) return } @@ -228,9 +235,10 @@ func axfr(res *Resolver, msg chan Msg) { // Send a request on the connection and hope for a reply. // Up to res.Attempts attempts. -func exchangeUDP(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Error) { +func exchangeUDP(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, *dns.Meta, os.Error) { var timeout int64 var attempts int + meta := new(dns.Meta) if r.Mangle != nil { m = r.Mangle(m) } @@ -246,37 +254,41 @@ func exchangeUDP(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Err } for a := 0; a < attempts; a++ { if send { + meta.QueryStart = time.Nanoseconds() err := sendUDP(m, c) if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { continue } - return nil, err + return nil, meta, err } } c.SetReadTimeout(timeout * 1e9) // nanoseconds buf, err := recvUDP(c) + meta.QueryEnd = time.Nanoseconds() if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { continue } - return nil, err + return nil, meta, err } in := new(dns.Msg) + meta.RLen = len(buf) if !in.Unpack(buf) { continue } - return in, nil + return in, meta, nil } - return nil, &dns.Error{Error: servErr} + return nil, meta, &dns.Error{Error: servErr} } // Up to res.Attempts attempts. -func exchangeTCP(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Error) { +func exchangeTCP(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, *dns.Meta, os.Error) { var timeout int64 var attempts int + meta := new(dns.Meta) if r.Mangle != nil { m = r.Mangle(m) } @@ -294,31 +306,34 @@ func exchangeTCP(c net.Conn, m []byte, r *Resolver, send bool) (*dns.Msg, os.Err for a := 0; a < attempts; a++ { // only send something when told so if send { + meta.QueryStart = time.Nanoseconds() err := sendTCP(m,c) if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { continue } - return nil, err + return nil, meta, err } } c.SetReadTimeout(timeout * 1e9) // nanoseconds // The server replies with two bytes length buf, err := recvTCP(c) + meta.QueryEnd = time.Nanoseconds() if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { continue } - return nil, err + return nil, meta, err } in := new(dns.Msg) + meta.RLen = len(buf) if !in.Unpack(buf) { continue } - return in, nil + return in, meta, nil } - return nil, &dns.Error{Error: servErr} + return nil, meta, &dns.Error{Error: servErr} } func sendUDP(m []byte,c net.Conn) os.Error { diff --git a/resolver/resolver_test.go b/resolver/resolver_test.go index 109e8491..2bfe0a57 100644 --- a/resolver/resolver_test.go +++ b/resolver/resolver_test.go @@ -17,7 +17,7 @@ func TestResolver(t *testing.T) { // ask something m.Question[0] = dns.Question{"miek.nl", dns.TypeSOA, dns.ClassINET} - ch <- Msg{m, nil} + ch <- Msg{m, nil, nil} in := <-ch if in.Dns != nil && in.Dns.Rcode != dns.RcodeSuccess { @@ -28,7 +28,7 @@ func TestResolver(t *testing.T) { // ask something m.Question[0] = dns.Question{"www.nlnetlabs.nl", dns.TypeRRSIG, dns.ClassINET} - ch <- Msg{m, nil} + ch <- Msg{m, nil, nil} in = <-ch if in.Dns != nil && in.Dns.Rcode != dns.RcodeSuccess { @@ -37,7 +37,7 @@ func TestResolver(t *testing.T) { t.Logf("%v\n", in) } - ch <- Msg{nil, nil} + ch <- Msg{nil, nil, nil} <-ch } @@ -62,14 +62,14 @@ func TestResolverEdns(t *testing.T) { edns.SetUDPSize(2048) edns.SetDo() edns.Option = make([]dns.Option, 1) - edns.SetNsidToHex("") // Empty to request it + edns.SetNsid("") // Empty to request it // ask something m.Question[0] = dns.Question{"powerdns.nl", dns.TypeDNSKEY, dns.ClassINET} m.Extra = make([]dns.RR, 1) m.Extra[0] = edns - ch <- Msg{m, nil} + ch <- Msg{m, nil, nil} in := <-ch if in.Dns != nil { if in.Dns.Rcode != dns.RcodeSuccess { @@ -78,7 +78,7 @@ func TestResolverEdns(t *testing.T) { t.Fail() } } - ch <- Msg{nil, nil} + ch <- Msg{nil, nil, nil} <-ch // wait for ch to close channel } @@ -108,7 +108,7 @@ func TestResolverTsig(t *testing.T) { // Add it to the msg m.Extra[0] = tsig - ch <- Msg{m, nil} + ch <- Msg{m, nil, nil} in := <-ch if in.Dns != nil { if in.Dns.Rcode != dns.RcodeSuccess { @@ -117,7 +117,7 @@ func TestResolverTsig(t *testing.T) { t.Fail() } } - ch <- Msg{nil, nil} + ch <- Msg{nil, nil, nil} <-ch // wait for ch to close channel } @@ -131,7 +131,7 @@ func TestAXFR(t *testing.T) { m.Question[0] = dns.Question{"miek.nl", dns.TypeAXFR, dns.ClassINET} //m.Question[0] = dns.Question{"atoom.net", dns.TypeAXFR, dns.ClassINET} - ch <- Msg{m, nil} + ch <- Msg{m, nil, nil} for dm := range ch { var _ = dm /* fmt.Printf("%v\n",dm.Dns) */ @@ -147,7 +147,7 @@ func TestFromFile(t *testing.T) { m.Question = make([]dns.Question, 1) m.Question[0] = dns.Question{"a.miek.nl", dns.TypeA, dns.ClassINET} - ch <- Msg{m, nil} + ch <- Msg{m, nil, nil} in := <-ch if in.Dns != nil { if in.Dns.Rcode != dns.RcodeSuccess { diff --git a/responder/responder.go b/responder/responder.go index 3b0d9699..fbe0aba2 100644 --- a/responder/responder.go +++ b/responder/responder.go @@ -41,6 +41,7 @@ type msg struct { addr net.Addr // remote address msg []byte // raw dns message err os.Error // any errors + // Meta stuff } // Every nameserver implements the Responder interface. It defines