diff --git a/_examples/qperf/_go_.6 b/_examples/qperf/_go_.6 new file mode 100644 index 00000000..1b6ab8d8 Binary files /dev/null and b/_examples/qperf/_go_.6 differ diff --git a/_examples/qperf/qperf2 b/_examples/qperf/qperf2 new file mode 100755 index 00000000..4214a29e Binary files /dev/null and b/_examples/qperf/qperf2 differ diff --git a/_examples/qperf/qperf2.go b/_examples/qperf/qperf2.go new file mode 100644 index 00000000..62d837f7 --- /dev/null +++ b/_examples/qperf/qperf2.go @@ -0,0 +1,92 @@ +package main + +import ( + "dns" + "os" + "flag" + "log" + "fmt" + "time" + "runtime" + "runtime/pprof" +) + +func main() { + queries := flag.Int("queries", 20, "number of concurrent queries to perform") + maxproc := flag.Int("maxproc", 4, "set GOMAXPROCS to this value") + looptime := flag.Int("time", 2, "number of seconds to query") + nameserver := flag.String("ns", "127.0.0.1:53", "the nameserver to query") + cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage: %s [qtype] [qclass] [name]", os.Args[0]) + flag.PrintDefaults() + } + + queries_send := int64(0) + qid := uint16(1) + qtype := uint16(dns.TypeMX) + qclass := uint16(dns.ClassINET) // Default qclass + qname := "miek.nl" +// nameserver := "127.0.0.1:53" +// nameserver = "193.0.14.129:53" // k.root-server.net +// nameserver = "213.154.224.1:53" // open.nlnetlabs.nl +// nameserver = "193.110.157.135:53" // xelerance.com +// nameserver = "195.169.221.157:53" // Jelte, bind10-devel + + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + runtime.GOMAXPROCS(*maxproc) + start := time.Nanoseconds() + fmt.Printf("Starting %d query functions. GOMAXPROCS set to %d\n", *queries, *maxproc) + fmt.Printf("Querying %s\n", *nameserver) + for i := 0; i < *queries; i++ { + go func() { + pktbuf := make([]byte, dns.DefaultMsgSize) + m := new(dns.Msg) + m.Question = make([]dns.Question, 1) + m.Question[0] = dns.Question{qname, qtype, qclass} + qbuf, _ := m.Pack() + c := dns.NewClient() + if err := c.Dial(*nameserver); err != nil { + return + } + defer c.Close() + r := new(dns.Msg) + for { + // set Id + dns.RawSetId(qbuf, 0, qid) + n, err := c.ExchangeBuffer(qbuf, *nameserver, pktbuf) + if err != nil { + log.Print(err) + continue + } + r.Unpack(pktbuf[:n]) + //println(r.MsgHdr.String()) + n=n + r=r + queries_send++ + qid++ + //break // stop after 1 query + } + }() + } + + t := time.NewTicker(int64(*looptime) * 1e9) +wait: + for { + select { + case <-t.C: + // time is up + break wait + } + } + delta := float32(time.Nanoseconds() - start) / 1e9 + fmt.Printf("%d queries in %.4f s (%.4f qps)\n", queries_send, delta, float32(queries_send)/delta) +} diff --git a/client.go b/client.go index 13bff4ad..eb9f6e7c 100644 --- a/client.go +++ b/client.go @@ -125,7 +125,7 @@ type Client struct { ReadTimeout int64 // the net.Conn.SetReadTimeout value for new connections WriteTimeout int64 // the net.Conn.SetWriteTimeout value for new connections TsigSecret map[string]string // secret(s) for Tsig map[] - //Conn net.Conn // if set, use this connection, otherwise Dial again TODO + Hijacked net.Conn // if set the calling code takes care of the connection // LocalAddr string // Local address to use } @@ -182,6 +182,22 @@ func (w *reply) Write(m *Msg) { w.Client().ChannelReply <- []*Msg{w.req, m} } +func (c *Client) Dial(addr string) os.Error { + conn, err := net.Dial(c.Net, addr) + if err != nil { + return err + } + c.Hijacked = conn + return nil +} + +func (c *Client) Close() os.Error { + if c.Hijacked == nil { + return nil // TODO + } + return c.Hijacked.Close() +} + // Do performs an asynchronous query. The result is returned on the // channel set in the c. If no channel is set DefaultQueryChan is used. func (c *Client) Do(m *Msg, a string) { @@ -198,10 +214,15 @@ func (c *Client) ExchangeBuffer(inbuf []byte, a string, outbuf []byte) (n int, e w := new(reply) w.client = c w.addr = a - if err = w.Dial(); err != nil { - return 0, err + if c.Hijacked == nil { + if err = w.Dial(); err != nil { + return 0, err + } + defer w.Close() } - defer w.Close() // XXX here?? what about TCP which should remain open + if c.Hijacked != nil { + w.conn = c.Hijacked + } if n, err = w.writeClient(inbuf); err != nil { return 0, err } @@ -365,8 +386,7 @@ func (w *reply) writeClient(p []byte) (n int, err os.Error) { if w.Client().Net == "" { panic("c.Net empty") } - if w.conn == nil { - // No connection yet, dial it. impl. at this place? TODO + if w.Client().Hijacked == nil { if err = w.Dial(); err != nil { return 0, err } diff --git a/rawmsg.go b/rawmsg.go index 351a8d79..a4903b96 100644 --- a/rawmsg.go +++ b/rawmsg.go @@ -23,3 +23,8 @@ func (h *RR_Header) RawSetRdlength(buf []byte, off int) bool { buf[off+off1+2+2+4], buf[off+off1+2+2+4+1] = packUint16(h.Rdlength) return true } + +func RawSetId(buf []byte, off int, id uint16) bool { + buf[off], buf[off+1] = packUint16(id) + return true +}