diff --git a/client.go b/client.go index 7e80de1c..856655e1 100644 --- a/client.go +++ b/client.go @@ -13,10 +13,7 @@ import ( "time" ) -// Check incoming TSIG message. TODO(mg) -// Need a tsigstatus for that too? Don't know yet. TODO(mg) - -// Incoming (just as in os.Signal) +// Incoming (documentation just as in os.Signal) type QueryHandler interface { QueryDNS(w RequestWriter, q *Msg) } @@ -32,10 +29,12 @@ type RequestWriter interface { Receive() (*Msg, error) // Close closes the connection with the server. Close() error - // Dials calls the server + // Dials calls the server. Dial() error - // TsigStatus return the TSIG validation status. + // TsigStatus returns the TSIG validation status. TsigStatus() error + // Rtt returns the duration of the request. The value is only valid after a Send()/Receive() sequence. + Rtt() time.Duration } // hijacked connections...? @@ -47,6 +46,8 @@ type reply struct { tsigRequestMAC string tsigTimersOnly bool tsigStatus error + rtt time.Duration + t time.Time } // A Request is a incoming message from a Client. @@ -190,7 +191,6 @@ func (q *Query) Query() error { if handler == nil { handler = DefaultQueryMux } - //forever: for { select { case in := <-q.QueryChan: @@ -247,12 +247,14 @@ func (c *Client) ExchangeBuffer(inbuf []byte, a string, outbuf []byte) (n int, e if c.Hijacked != nil { w.conn = c.Hijacked } + w.t = time.Now() if n, err = w.writeClient(inbuf); err != nil { return 0, err } if n, err = w.readClient(outbuf); err != nil { return n, err } + w.rtt = time.Since(w.t) return n, nil } @@ -297,22 +299,6 @@ func (w *reply) Dial() error { return nil } -func (w *reply) Close() (err error) { - return w.conn.Close() -} - -func (w *reply) Client() *Client { - return w.client -} - -func (w *reply) Request() *Msg { - return w.req -} - -func (w *reply) TsigStatus() error { - return w.tsigStatus -} - func (w *reply) Receive() (*Msg, error) { var p []byte m := new(Msg) @@ -323,13 +309,14 @@ func (w *reply) Receive() (*Msg, error) { p = make([]byte, DefaultMsgSize) } n, err := w.readClient(p) - if err != nil { + if err != nil || n == 0 { return nil, err } p = p[:n] if ok := m.Unpack(p); !ok { return nil, ErrUnpack } + w.rtt = time.Since(w.t) if m.IsTsig() { secret := m.Extra[len(m.Extra)-1].(*RR_TSIG).Hdr.Name if _, ok := w.Client().TsigSecret[secret]; !ok { @@ -430,6 +417,7 @@ func (w *reply) Send(m *Msg) (err error) { return ErrPack } } + w.t = time.Now() if _, err = w.writeClient(out); err != nil { return err } @@ -505,3 +493,18 @@ func (w *reply) writeClient(p []byte) (n int, err error) { } return } + +// Close implents the RequestWriter.Close method +func (w *reply) Close() (err error) { return w.conn.Close() } + +// Client returns a pointer to the client +func (w *reply) Client() *Client { return w.client } + +// Request returns the request contained in reply +func (w *reply) Request() *Msg { return w.req } + +// TsigStatus implements the RequestWriter.TsigStatus method +func (w *reply) TsigStatus() error { return w.tsigStatus } + +// Rtt implements the RequestWriter.Rtt method +func (w *reply) Rtt() time.Duration { return w.rtt } diff --git a/ex/q/q.go b/ex/q/q.go index 0385b9a0..f7add2c3 100644 --- a/ex/q/q.go +++ b/ex/q/q.go @@ -27,6 +27,8 @@ func q(w dns.RequestWriter, m *dns.Msg) { if w.TsigStatus() != nil { fmt.Printf(";; Couldn't verify TSIG signature: %s\n", w.TsigStatus().Error()) } + // Save the Rtt in the message + r.Rtt = w.Rtt() w.Write(r) } @@ -222,6 +224,8 @@ forever: } fmt.Printf("%v", r.Reply) + fmt.Printf("\n;; Query time: %.3d µs\n", r.Reply.Rtt/1e3) + // Server maybe } i++ if i == len(qname) { diff --git a/msg.go b/msg.go index 219db803..d5400be0 100644 --- a/msg.go +++ b/msg.go @@ -80,11 +80,12 @@ type MsgHdr struct { // The layout of a DNS message. type Msg struct { MsgHdr - Compress bool // If true, the message will be compressed when converted to wire format. - Question []Question // Holds the RR(s) of the question section. - Answer []RR // Holds the RR(s) of the answer section. - Ns []RR // Holds the RR(s) of the authority section. - Extra []RR // Holds the RR(s) of the additional section. + Compress bool // If true, the message will be compressed when converted to wire format. + Rtt time.Duration // Round Trip Time, time it took between sending a reply and receiving the answer. + Question []Question // Holds the RR(s) of the question section. + Answer []RR // Holds the RR(s) of the answer section. + Ns []RR // Holds the RR(s) of the authority section. + Extra []RR // Holds the RR(s) of the additional section. } // Map of strings for each RR wire type. @@ -373,7 +374,7 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str case reflect.Slice: switch val.Type().Field(i).Tag.Get("dns") { default: - println("dns: unknown tag packing slice", val.Type().Field(i).Tag.Get("dns"), '"', val.Type().Field(i).Tag , '"') + println("dns: unknown tag packing slice", val.Type().Field(i).Tag.Get("dns"), '"', val.Type().Field(i).Tag, '"') return lenmsg, false case "domain-name": for j := 0; j < val.Field(i).Len(); j++ { diff --git a/server.go b/server.go index dc6adf8a..881a4125 100644 --- a/server.go +++ b/server.go @@ -23,7 +23,7 @@ type Handler interface { type ResponseWriter interface { // RemoteAddr returns the net.Addr of the client that sent the current request. RemoteAddr() net.Addr - // Return the status of the Tsig (TsigNone, TsigVerified or TsigBad) + // TsigSttus returns the status of the Tsig (TsigNone, TsigVerified or TsigBad). TsigStatus() error // Write writes a reply back to the client. Write(*Msg) error @@ -403,6 +403,4 @@ func (w *response) Write(m *Msg) (err error) { func (w *response) RemoteAddr() net.Addr { return w.conn.remoteAddr } // TsigStatus implements the ResponseWriter.TsigStatus method -func (w *response) TsigStatus() error { - return w.tsigStatus -} +func (w *response) TsigStatus() error { return w.tsigStatus }