diff --git a/TODO.markdown b/TODO.markdown index 31236d51..ef404bad 100644 --- a/TODO.markdown +++ b/TODO.markdown @@ -6,6 +6,7 @@ things that need to be fixed. ## Todo * encoding NSEC3/NSEC bitmaps, DEcoding works; +* add functions to operate on []byte messages (raw packets) * HIP RR (needs list of domain names, need slice for that); * IsSubdomain, IsGlue helper functions; * Cleanup?; diff --git a/_examples/q/q.go b/_examples/q/q.go index 72a5be60..f0b82177 100644 --- a/_examples/q/q.go +++ b/_examples/q/q.go @@ -18,7 +18,7 @@ func q(w dns.RequestWriter, m *dns.Msg) { func main() { var dnssec *bool = flag.Bool("dnssec", false, "request DNSSEC records") var query *bool = flag.Bool("question", false, "show question") - var short *bool = flag.Bool("short", false, "abbriate long DNSKEY and RRSIG RRs") + var short *bool = flag.Bool("short", false, "abbreviate long DNSKEY and RRSIG RRs") var aa *bool = flag.Bool("aa", false, "set AA flag in query") var ad *bool = flag.Bool("ad", false, "set AD flag in query") var cd *bool = flag.Bool("cd", false, "set CD flag in query") diff --git a/_examples/qperf/Makefile b/_examples/qperf/Makefile new file mode 100644 index 00000000..f5159b3f --- /dev/null +++ b/_examples/qperf/Makefile @@ -0,0 +1,8 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. +include $(GOROOT)/src/Make.inc +TARG=qperf +GOFILES=qperf.go +DEPS=../../ +include $(GOROOT)/src/Make.cmd diff --git a/_examples/qperf/qperf.go b/_examples/qperf/qperf.go new file mode 100644 index 00000000..7f9b5703 --- /dev/null +++ b/_examples/qperf/qperf.go @@ -0,0 +1,149 @@ +package main + +import ( + "dns" + "os" + "flag" + "log" + "fmt" + "time" + "strconv" + "strings" + "runtime" + "runtime/pprof" +) + +func main() { + dnssec := flag.Bool("dnssec", false, "request DNSSEC records") + tcp := flag.Bool("tcp", false, "TCP mode") + nsid := flag.Bool("nsid", false, "ask for NSID") + queries := flag.Int("queries", 20, "number of concurrent queries to perform") + looptime := flag.Int("time", 2, "number of seconds to query") + cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage: %s [@server(:port)] [qtype] [qclass] [name ...]\n", os.Args[0]) + flag.PrintDefaults() + } + + queries_send := int64(0) + qid := uint16(1) + qtype := uint16(0) + qclass := uint16(dns.ClassINET) // Default qclass + nameserver := "@127.0.0.1:53" + var qname []string + + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + runtime.GOMAXPROCS(*queries) + +Flags: + for i := 0; i < flag.NArg(); i++ { + // If it starts with @ it is a nameserver + if flag.Arg(i)[0] == '@' { + nameserver = flag.Arg(i) + 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)) { + qtype = k + 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 + } + } + // If it starts with TYPExxx it is unknown rr + if strings.HasPrefix(flag.Arg(i), "TYPE") { + i, e := strconv.Atoi(string([]byte(flag.Arg(i))[4:])) + if e == nil { + qtype = uint16(i) + continue Flags + } + } + + // Anything else is a qname + qname = append(qname, flag.Arg(i)) + } + if len(qname) == 0 { + qname = make([]string, 1) + qname[0] = "." + qtype = dns.TypeNS + } + if qtype == 0 { + qtype = dns.TypeA + } + + nameserver = string([]byte(nameserver)[1:]) // chop off @ + if !strings.HasSuffix(nameserver, ":53") { + nameserver += ":53" + } + start := time.Nanoseconds() + for i := 0; i < *queries; i++ { + go func() { + println("starting querier") + c := dns.NewClient() + if *tcp { + c.Net = "tcp" + } + m := new(dns.Msg) + m.Question = make([]dns.Question, 1) + m.Question[0] = dns.Question{qname[0], qtype, qclass} + if *dnssec || *nsid { + opt := dns.NewRR(dns.TypeOPT).(*dns.RR_OPT) + opt.Hdr.Rrtype = 0 + opt.SetDo() + opt.SetVersion(0) + opt.SetUDPSize(dns.DefaultMsgSize) + if *nsid { + opt.SetNsid("") + } + m.Extra = make([]dns.RR, 1) + m.Extra[0] = opt + } + mbuf, _ := m.Pack() + for { + // set Id + mbuf[0], mbuf[1] = byte(qid >> 8), byte(qid) + c.ExchangeBuffer(mbuf, nameserver) + queries_send++ // global var...??? + qid++ // let is overflow and wrap + /* + if r == nil { + println("weird reply", qid) + } + */ + /* + if r != nil && r.Rcode != dns.RcodeSuccess { + println("not a success code") + } + its a raw buffer + */ + } + }() + } + + 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 7d9663b6..af6e0492 100644 --- a/client.go +++ b/client.go @@ -189,33 +189,46 @@ func (c *Client) Do(m *Msg, a string) { } } -// Exchange performs an synchronous query. It sends the message m to the address -// contained in a and waits for an reply. -func (c *Client) Exchange(m *Msg, a string) *Msg { +// ExchangeBuf performs a synchronous query. It sends the buffer m to the +// address (net.Addr?) contained in a +func (c *Client) ExchangeBuffer(m []byte, a string) []byte { w := new(reply) w.client = c w.addr = a - out, ok := m.Pack() - if !ok { - // - } - _, err := w.writeClient(out) + _, err := w.writeClient(m) + defer w.closeClient() // XXX here?? what about TCP which should remain open if err != nil { + println(err.String()) return nil } - // udp / tcp + // udp / tcp TODO p := make([]byte, DefaultMsgSize) n, err := w.readClient(p) if err != nil { return nil } p = p[:n] + return p + +} + +// Exchange performs an synchronous query. It sends the message m to the address +// contained in a and waits for an reply. +func (c *Client) Exchange(m *Msg, a string) *Msg { + out, ok := m.Pack() + if !ok { + panic("failed to pack message") + } + p := c.ExchangeBuffer(out, a) + if p == nil { + return nil + } r := new(Msg) if ok := r.Unpack(p); !ok { return nil } - return r + return r } func (w *reply) WriteMessages(m []*Msg) { @@ -395,3 +408,8 @@ func (w *reply) writeClient(p []byte) (n int, err os.Error) { } return 0, nil } + +// UDP/TCP stuff +func (w *reply) closeClient() (err os.Error) { + return w.conn.Close() +}