Add Meta data to the resolver

This commit is contained in:
Miek Gieben 2011-01-18 15:44:30 +01:00
parent fc0ffa20f7
commit ec441e36e1
9 changed files with 87 additions and 50 deletions

View File

@ -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)
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}

18
dns.go
View File

@ -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

1
msg.go
View File

@ -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)

View File

@ -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 {

View File

@ -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 {

View File

@ -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