From 22977491c373548dab11361529c2faea7af8c31c Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 28 Sep 2013 20:31:29 +0100 Subject: [PATCH 01/31] Try to use Conn --- client.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/client.go b/client.go index 02c17396..ea437afb 100644 --- a/client.go +++ b/client.go @@ -15,12 +15,14 @@ import ( // Order of events: // *client -> *reply -> Exchange() -> dial()/send()->write()/receive()->read() -// Do I want make this an interface thingy? -type reply struct { + +// A Conn represents a connection (which may be short lived) to a DNS +// server. +type Conn struct { + net.Conn client *Client addr string req *Msg - conn net.Conn tsigRequestMAC string tsigTimersOnly bool tsigStatus error @@ -39,6 +41,10 @@ type Client struct { group singleflight } +func Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err) { + +} + func (c *Client) exchangeMerge(m *Msg, a string, s net.Conn) (r *Msg, rtt time.Duration, err error) { if !c.SingleInflight { if s == nil { From 4bde528be5c29a9f8bfd5e941d76d66dbf2b21db Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 28 Sep 2013 21:58:08 +0100 Subject: [PATCH 02/31] Export dns.Conn and make it more like net.Conn Export lowlevel function and types so that they may be used. They higher level Exchange function is still there. ExchangeConn is gone, because it is not needed. --- client.go | 213 ++++++++++++++++++++++-------------------------------- ex/q/q.go | 2 + xfr.go | 2 + 3 files changed, 91 insertions(+), 126 deletions(-) diff --git a/client.go b/client.go index ea437afb..10bbdce2 100644 --- a/client.go +++ b/client.go @@ -12,17 +12,9 @@ import ( "time" ) -// Order of events: -// *client -> *reply -> Exchange() -> dial()/send()->write()/receive()->read() - - -// A Conn represents a connection (which may be short lived) to a DNS -// server. +// A Conn represents a connection (which may be short lived) to a DNS server. type Conn struct { net.Conn - client *Client - addr string - req *Msg tsigRequestMAC string tsigTimersOnly bool tsigStatus error @@ -30,8 +22,7 @@ type Conn struct { t time.Time } -// A Client defines parameter for a DNS client. A nil -// Client is usable for sending queries. +// A Client defines parameters for a DNS client. A nil Client is usable for sending queries. type Client struct { Net string // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections (ns), defaults to 2 * 1e9 @@ -41,16 +32,31 @@ type Client struct { group singleflight } -func Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err) { - +// Exchange performs an synchronous UDP query. It sends the message m to the address +// contained in a and waits for an reply. +func Exchange(m *Msg, a string) (r *Msg, err error) { + co := new(Conn) + co.Conn, err = net.DialTimeout("udp", a, 5*1e9) + if err != nil { + return nil, err + } + defer co.Close() + if err = co.WriteMsg(m); err != nil { + return nil, err + } + r, err = co.ReadMsg() + return r, err } -func (c *Client) exchangeMerge(m *Msg, a string, s net.Conn) (r *Msg, rtt time.Duration, err error) { +// Exchange performs an synchronous query. It sends the message m to the address +// contained in a and waits for an reply. Basic use pattern with a *dns.Client: +// +// c := new(dns.Client) +// in, rtt, err := c.Exchange(message, "127.0.0.1:53") +// +func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { if !c.SingleInflight { - if s == nil { - return c.exchange(m, a) - } - return c.exchangeConn(m, s) + return c.exchange(m, a) } // This adds a bunch of garbage, TODO(miek). t := "nop" @@ -62,86 +68,47 @@ func (c *Client) exchangeMerge(m *Msg, a string, s net.Conn) (r *Msg, rtt time.D cl = cl1 } r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) { - if s == nil { - return c.exchange(m, a) - } - return c.exchangeConn(m, s) + return c.exchange(m, a) }) if err != nil { return r, rtt, err } if shared { r1 := r.copy() - r1.Id = r.Id // Copy Id! +// not needed r1.Id = r.Id // Copy Id! r = r1 } return r, rtt, nil } -// Exchange performs an synchronous query. It sends the message m to the address -// contained in a and waits for an reply. Basic use pattern with a *dns.Client: -// -// c := new(dns.Client) -// in, rtt, err := c.Exchange(message, "127.0.0.1:53") -// -func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { - return c.exchangeMerge(m, a, nil) -} - func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { - w := &reply{client: c, addr: a} - if err = w.dial(); err != nil { - return nil, 0, err - } - defer w.conn.Close() - if err = w.send(m); err != nil { - return nil, 0, err - } - r, err = w.receive() - return r, w.rtt, err -} - -// ExchangeConn performs an synchronous query. It sends the message m trough the -// connection s and waits for a reply. -func (c *Client) ExchangeConn(m *Msg, s net.Conn) (r *Msg, rtt time.Duration, err error) { - return c.exchangeMerge(m, "", s) -} - -func (c *Client) exchangeConn(m *Msg, s net.Conn) (r *Msg, rtt time.Duration, err error) { - w := &reply{client: c, conn: s} - if err = w.send(m); err != nil { - return nil, 0, err - } - r, err = w.receive() - return r, w.rtt, err -} - -// dial connects to the address addr for the network set in c.Net -func (w *reply) dial() (err error) { - var conn net.Conn - if w.client.Net == "" { - conn, err = net.DialTimeout("udp", w.addr, 5*1e9) + co := new(Conn) + if c.Net == "" { + co.Conn, err = net.DialTimeout("udp", a, 5*1e9) } else { - conn, err = net.DialTimeout(w.client.Net, w.addr, 5*1e9) + co.Conn, err = net.DialTimeout(c.Net, a, 5*1e9) } if err != nil { - return err + return nil, 0, err } - w.conn = conn - return + defer co.Close() + if err = co.WriteMsg(m); err != nil { + return nil, 0, err + } + r, err = co.ReadMsg() + return r, co.rtt, err } -func (w *reply) receive() (*Msg, error) { +func (co *Conn) ReadMsg() (*Msg, error) { var p []byte m := new(Msg) - switch w.client.Net { - case "tcp", "tcp4", "tcp6": + if _, ok := co.Conn.(*net.TCPConn); ok { p = make([]byte, MaxMsgSize) - case "", "udp", "udp4", "udp6": + } else { // OPT! TODO(mg) p = make([]byte, DefaultMsgSize) } - n, err := w.read(p) + n, err := co.Read(p) if err != nil && n == 0 { return nil, err } @@ -149,30 +116,28 @@ func (w *reply) receive() (*Msg, error) { if err := m.Unpack(p); err != nil { return nil, err } - w.rtt = time.Since(w.t) - if t := m.IsTsig(); t != nil { - secret := t.Hdr.Name - if _, ok := w.client.TsigSecret[secret]; !ok { - w.tsigStatus = ErrSecret - return m, ErrSecret - } - // Need to work on the original message p, as that was used to calculate the tsig. - w.tsigStatus = TsigVerify(p, w.client.TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly) - } - return m, w.tsigStatus + co.rtt = time.Since(co.t) +// if t := m.IsTsig(); t != nil { +// secret := t.Hdr.Name +// if _, ok := w.client.TsigSecret[secret]; !ok { +// w.tsigStatus = ErrSecret +// return m, ErrSecret +// } +// // Need to work on the original message p, as that was used to calculate the tsig. +// w.tsigStatus = TsigVerify(p, w.client.TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly) +// } + return m, nil } -func (w *reply) read(p []byte) (n int, err error) { - if w.conn == nil { +func (co *Conn) Read(p []byte) (n int, err error) { + if co.Conn == nil { return 0, ErrConnEmpty } if len(p) < 2 { return 0, io.ErrShortBuffer } - switch w.client.Net { - case "tcp", "tcp4", "tcp6": - setTimeouts(w) - n, err = w.conn.(*net.TCPConn).Read(p[0:2]) + if t, ok := co.Conn.(*net.TCPConn); ok { + n, err = t.Read(p[0:2]) if err != nil || n != 2 { return n, err } @@ -183,25 +148,25 @@ func (w *reply) read(p []byte) (n int, err error) { if int(l) > len(p) { return int(l), io.ErrShortBuffer } - n, err = w.conn.(*net.TCPConn).Read(p[:l]) + n, err = t.Read(p[:l]) if err != nil { return n, err } i := n for i < int(l) { - j, err := w.conn.(*net.TCPConn).Read(p[i:int(l)]) + j, err := t.Read(p[i:int(l)]) if err != nil { return i, err } i += j } n = i - case "", "udp", "udp4", "udp6": - setTimeouts(w) - n, _, err = w.conn.(*net.UDPConn).ReadFromUDP(p) - if err != nil { - return n, err - } + return n, err + } + // assume udp connection + n, _, err = co.Conn.(*net.UDPConn).ReadFromUDP(p) + if err != nil { + return n, err } return n, err } @@ -209,62 +174,57 @@ func (w *reply) read(p []byte) (n int, err error) { // send sends a dns msg to the address specified in w. // If the message m contains a TSIG record the transaction // signature is calculated. -func (w *reply) send(m *Msg) (err error) { +func (co *Conn) WriteMsg(m *Msg) (err error) { var out []byte - if t := m.IsTsig(); t != nil { - mac := "" - name := t.Hdr.Name - if _, ok := w.client.TsigSecret[name]; !ok { - return ErrSecret - } - out, mac, err = TsigGenerate(m, w.client.TsigSecret[name], w.tsigRequestMAC, w.tsigTimersOnly) - w.tsigRequestMAC = mac - } else { - out, err = m.Pack() - } +// if t := m.IsTsig(); t != nil { +// mac := "" +// name := t.Hdr.Name +// if _, ok := w.client.TsigSecret[name]; !ok { +// return ErrSecret +// } +// out, mac, err = TsigGenerate(m, w.client.TsigSecret[name], w.tsigRequestMAC, w.tsigTimersOnly) +// w.tsigRequestMAC = mac +// } else { + out, err = m.Pack() +// } if err != nil { return err } - w.t = time.Now() - if _, err = w.write(out); err != nil { + co.t = time.Now() + if _, err = co.Write(out); err != nil { return err } return nil } -func (w *reply) write(p []byte) (n int, err error) { - switch w.client.Net { - case "tcp", "tcp4", "tcp6": +func (co *Conn) Write(p []byte) (n int, err error) { + if t, ok := co.Conn.(*net.TCPConn); ok { if len(p) < 2 { return 0, io.ErrShortBuffer } - setTimeouts(w) l := make([]byte, 2) l[0], l[1] = packUint16(uint16(len(p))) p = append(l, p...) - n, err := w.conn.Write(p) + n, err := t.Write(p) if err != nil { return n, err } i := n if i < len(p) { - j, err := w.conn.Write(p[i:len(p)]) + j, err := t.Write(p[i:len(p)]) if err != nil { return i, err } i += j } n = i - case "", "udp", "udp4", "udp6": - setTimeouts(w) - n, err = w.conn.(*net.UDPConn).Write(p) - if err != nil { - return n, err - } + return n, err } - return + n, err = co.Conn.(*net.UDPConn).Write(p) + return n, err } +/* func setTimeouts(w *reply) { if w.client.ReadTimeout == 0 { w.conn.SetReadDeadline(time.Now().Add(2 * 1e9)) @@ -278,3 +238,4 @@ func setTimeouts(w *reply) { w.conn.SetWriteDeadline(time.Now().Add(w.client.WriteTimeout)) } } +*/ diff --git a/ex/q/q.go b/ex/q/q.go index 04f6b86e..22031298 100644 --- a/ex/q/q.go +++ b/ex/q/q.go @@ -396,6 +396,7 @@ func shortRR(r dns.RR) dns.RR { } func doXfr(c *dns.Client, m *dns.Msg, nameserver string) { + /* if t, e := c.TransferIn(m, nameserver); e == nil { for r := range t { if r.Error == nil { @@ -412,4 +413,5 @@ func doXfr(c *dns.Client, m *dns.Msg, nameserver string) { } else { fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", e.Error()) } + */ } diff --git a/xfr.go b/xfr.go index 77ea7e0f..a2101ee4 100644 --- a/xfr.go +++ b/xfr.go @@ -10,6 +10,7 @@ type Envelope struct { Error error // If something went wrong, this contains the error. } +/* // TransferIn performs a [AI]XFR request (depends on the message's Qtype). It returns // a channel of *Envelope on which the replies from the server are sent. At the end of // the transfer the channel is closed. @@ -201,3 +202,4 @@ func xfrOut(w ResponseWriter, req *Msg, c chan *Envelope, e *error) { rep.Answer = nil } } +*/ From 69dcbaeece0822159341685741616e7c6914a545 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 28 Sep 2013 22:01:02 +0100 Subject: [PATCH 03/31] Remove xfr --- ex/reflect/reflect.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/ex/reflect/reflect.go b/ex/reflect/reflect.go index bc33226b..90a49d4c 100644 --- a/ex/reflect/reflect.go +++ b/ex/reflect/reflect.go @@ -103,24 +103,6 @@ func handleReflect(w dns.ResponseWriter, r *dns.Msg) { t.Txt = []string{str} switch r.Question[0].Qtype { - case dns.TypeAXFR: - c := make(chan *dns.Envelope) - var e *error - if err := dns.TransferOut(w, r, c, e); err != nil { - close(c) - return - } - soa, _ := dns.NewRR(`whoami.miek.nl. IN SOA elektron.atoom.net. miekg.atoom.net. ( - 2009032802 - 21600 - 7200 - 604800 - 3600)`) - c <- &dns.Envelope{RR: []dns.RR{soa, t, rr, soa}} - close(c) - w.Hijack() - // w.Close() // Client closes - return case dns.TypeTXT: m.Answer = append(m.Answer, t) m.Extra = append(m.Extra, rr) From 140e525195b85f911ae0361d3693bc46c68dc94d Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 28 Sep 2013 22:05:48 +0100 Subject: [PATCH 04/31] More documentation --- dns.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dns.go b/dns.go index cc442f17..4e606d79 100644 --- a/dns.go +++ b/dns.go @@ -66,6 +66,11 @@ // // c.SingleInflight = true // +// If these "advanced" features are not needed, a simple UDP query can be send, +// with: +// +// in, err := dns.Exchange(m1, "127.0.0.1:53") +// // A dns message consists out of four sections. // The question section: in.Question, the answer section: in.Answer, // the authority section: in.Ns and the additional section: in.Extra. From a743ae8b687523354f752f86db8ecbf0e15bc6d1 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 28 Sep 2013 22:16:15 +0100 Subject: [PATCH 05/31] Some more changes. Still pondering dns.Conn and a dns.Dial --- client_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client_test.go b/client_test.go index 0d93e961..90ac03a7 100644 --- a/client_test.go +++ b/client_test.go @@ -79,6 +79,7 @@ Loop: } } +/* func TestClientTsigAXFR(t *testing.T) { m := new(Msg) m.SetAxfr("miek.nl.") @@ -104,7 +105,9 @@ func TestClientTsigAXFR(t *testing.T) { } } } +*/ +/* func TestClientAXFRMultipleMessages(t *testing.T) { m := new(Msg) m.SetAxfr("dnsex.nl.") @@ -126,7 +129,9 @@ func TestClientAXFRMultipleMessages(t *testing.T) { } } } +*/ +/* // not really a test, but shows how to use update leases func TestUpdateLeaseTSIG(t *testing.T) { m := new(Msg) @@ -149,7 +154,7 @@ func TestUpdateLeaseTSIG(t *testing.T) { m.SetTsig("polvi.", HmacMD5, 300, time.Now().Unix()) c.TsigSecret = map[string]string{"polvi.": "pRZgBrBvI4NAHZYhxmhs/Q=="} - w := new(reply) + co := new(Conn) w.client = c w.addr = "127.0.0.1:53" w.req = m @@ -160,5 +165,5 @@ func TestUpdateLeaseTSIG(t *testing.T) { if err := w.send(m); err != nil { t.Fail() } - } +*/ From b97b3340fb572352a4c749263ce9fe3ff4bf355a Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 29 Sep 2013 10:22:27 +0100 Subject: [PATCH 06/31] Make TSIG work again Start fixing and using the UDP bufsize option. --- client.go | 66 ++++++++++++++++++++++++++----------------------------- edns.go | 1 - 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/client.go b/client.go index 10bbdce2..c8d720dc 100644 --- a/client.go +++ b/client.go @@ -15,11 +15,9 @@ import ( // A Conn represents a connection (which may be short lived) to a DNS server. type Conn struct { net.Conn - tsigRequestMAC string - tsigTimersOnly bool - tsigStatus error - rtt time.Duration - t time.Time + rtt time.Duration + t time.Time + requestMAC string } // A Client defines parameters for a DNS client. A nil Client is usable for sending queries. @@ -32,7 +30,7 @@ type Client struct { group singleflight } -// Exchange performs an synchronous UDP query. It sends the message m to the address +// Exchange performs a synchronous UDP query. It sends the message m to the address // contained in a and waits for an reply. func Exchange(m *Msg, a string) (r *Msg, err error) { co := new(Conn) @@ -41,10 +39,10 @@ func Exchange(m *Msg, a string) (r *Msg, err error) { return nil, err } defer co.Close() - if err = co.WriteMsg(m); err != nil { + if err = co.WriteMsg(m, nil); err != nil { return nil, err } - r, err = co.ReadMsg() + r, err = co.ReadMsg(nil) return r, err } @@ -75,7 +73,6 @@ func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro } if shared { r1 := r.copy() -// not needed r1.Id = r.Id // Copy Id! r = r1 } return r, rtt, nil @@ -92,20 +89,21 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro return nil, 0, err } defer co.Close() - if err = co.WriteMsg(m); err != nil { + if err = co.WriteMsg(m, c.TsigSecret); err != nil { return nil, 0, err } - r, err = co.ReadMsg() + r, err = co.ReadMsg(c.TsigSecret) return r, co.rtt, err } -func (co *Conn) ReadMsg() (*Msg, error) { +// Add bufsize +func (co *Conn) ReadMsg(tsigSecret map[string]string) (*Msg, error) { var p []byte m := new(Msg) if _, ok := co.Conn.(*net.TCPConn); ok { p = make([]byte, MaxMsgSize) } else { - // OPT! TODO(mg) + // OPT! TODO(miek): needs function change p = make([]byte, DefaultMsgSize) } n, err := co.Read(p) @@ -117,16 +115,14 @@ func (co *Conn) ReadMsg() (*Msg, error) { return nil, err } co.rtt = time.Since(co.t) -// if t := m.IsTsig(); t != nil { -// secret := t.Hdr.Name -// if _, ok := w.client.TsigSecret[secret]; !ok { -// w.tsigStatus = ErrSecret -// return m, ErrSecret -// } -// // Need to work on the original message p, as that was used to calculate the tsig. -// w.tsigStatus = TsigVerify(p, w.client.TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly) -// } - return m, nil + if t := m.IsTsig(); t != nil { + if _, ok := tsigSecret[t.Hdr.Name]; !ok { + return m, ErrSecret + } + // Need to work on the original message p, as that was used to calculate the tsig. + err = TsigVerify(p, tsigSecret[t.Hdr.Name], co.requestMAC, false) + } + return m, err } func (co *Conn) Read(p []byte) (n int, err error) { @@ -174,19 +170,19 @@ func (co *Conn) Read(p []byte) (n int, err error) { // send sends a dns msg to the address specified in w. // If the message m contains a TSIG record the transaction // signature is calculated. -func (co *Conn) WriteMsg(m *Msg) (err error) { +func (co *Conn) WriteMsg(m *Msg, tsigSecret map[string]string) (err error) { var out []byte -// if t := m.IsTsig(); t != nil { -// mac := "" -// name := t.Hdr.Name -// if _, ok := w.client.TsigSecret[name]; !ok { -// return ErrSecret -// } -// out, mac, err = TsigGenerate(m, w.client.TsigSecret[name], w.tsigRequestMAC, w.tsigTimersOnly) -// w.tsigRequestMAC = mac -// } else { - out, err = m.Pack() -// } + if t := m.IsTsig(); t != nil { + mac := "" + if _, ok := tsigSecret[t.Hdr.Name]; !ok { + return ErrSecret + } + out, mac, err = TsigGenerate(m, tsigSecret[t.Hdr.Name], co.requestMAC, false) + // Set for the next read + co.requestMAC = mac + } else { + out, err = m.Pack() + } if err != nil { return err } diff --git a/edns.go b/edns.go index 7e716a6b..8852ae28 100644 --- a/edns.go +++ b/edns.go @@ -443,7 +443,6 @@ func (e *EDNS0_DAU) String() string { } } return s - } type EDNS0_DHU struct { From 9a38f973919b39fc0821d75dc0eb062f7a81730b Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 29 Sep 2013 11:21:18 +0100 Subject: [PATCH 07/31] Fix timeouts, Tsig and EDNS0 update size --- client.go | 89 ++++++++++++++++++++++++++++++++++--------------------- dns.go | 2 +- server.go | 2 +- 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/client.go b/client.go index c8d720dc..d6739257 100644 --- a/client.go +++ b/client.go @@ -12,9 +12,13 @@ import ( "time" ) +const dnsTimeout time.Duration = 2 * 1e9 + // A Conn represents a connection (which may be short lived) to a DNS server. type Conn struct { net.Conn + UDPSize uint16 // Minimum reveive buffer for UDP messages, if > 512 EDNS0, 0 MinMsgSize. + TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be fully qualified rtt time.Duration t time.Time requestMAC string @@ -23,8 +27,9 @@ type Conn struct { // A Client defines parameters for a DNS client. A nil Client is usable for sending queries. type Client struct { Net string // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) - ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections (ns), defaults to 2 * 1e9 - WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections (ns), defaults to 2 * 1e9 + DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for new connections (ns), defaults to 2 * 1e9 + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for new connections (ns), defaults to 2 * 1e9 TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass group singleflight @@ -34,15 +39,15 @@ type Client struct { // contained in a and waits for an reply. func Exchange(m *Msg, a string) (r *Msg, err error) { co := new(Conn) - co.Conn, err = net.DialTimeout("udp", a, 5*1e9) + co.Conn, err = net.DialTimeout("udp", a, dnsTimeout) if err != nil { return nil, err } defer co.Close() - if err = co.WriteMsg(m, nil); err != nil { + if err = co.WriteMsg(m); err != nil { return nil, err } - r, err = co.ReadMsg(nil) + r, err = co.ReadMsg() return r, err } @@ -80,31 +85,43 @@ func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { co := new(Conn) + timeout := dnsTimeout + if c.DialTimeout != 0 { + timeout = c.DialTimeout + } if c.Net == "" { - co.Conn, err = net.DialTimeout("udp", a, 5*1e9) + co.Conn, err = net.DialTimeout("udp", a, timeout) } else { - co.Conn, err = net.DialTimeout(c.Net, a, 5*1e9) + co.Conn, err = net.DialTimeout(c.Net, a, timeout) } if err != nil { return nil, 0, err } defer co.Close() - if err = co.WriteMsg(m, c.TsigSecret); err != nil { + opt := m.IsEdns0() + if opt != nil && opt.UDPSize() >= MinMsgSize { + co.UDPSize = opt.UDPSize() + } + co.TsigSecret = c.TsigSecret + if err = co.WriteMsg(m); err != nil { return nil, 0, err } - r, err = co.ReadMsg(c.TsigSecret) + r, err = co.ReadMsg() return r, co.rtt, err } -// Add bufsize -func (co *Conn) ReadMsg(tsigSecret map[string]string) (*Msg, error) { +// ReadMsg reads a message from the connection co. +func (co *Conn) ReadMsg() (*Msg, error) { var p []byte m := new(Msg) if _, ok := co.Conn.(*net.TCPConn); ok { p = make([]byte, MaxMsgSize) } else { - // OPT! TODO(miek): needs function change - p = make([]byte, DefaultMsgSize) + if co.UDPSize >= 512 { + p = make([]byte, co.UDPSize) + } else { + p = make([]byte, MinMsgSize) + } } n, err := co.Read(p) if err != nil && n == 0 { @@ -116,15 +133,16 @@ func (co *Conn) ReadMsg(tsigSecret map[string]string) (*Msg, error) { } co.rtt = time.Since(co.t) if t := m.IsTsig(); t != nil { - if _, ok := tsigSecret[t.Hdr.Name]; !ok { + if _, ok := co.TsigSecret[t.Hdr.Name]; !ok { return m, ErrSecret } // Need to work on the original message p, as that was used to calculate the tsig. - err = TsigVerify(p, tsigSecret[t.Hdr.Name], co.requestMAC, false) + err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.requestMAC, false) } return m, err } +// Read implements the net.Conn read method. func (co *Conn) Read(p []byte) (n int, err error) { if co.Conn == nil { return 0, ErrConnEmpty @@ -167,18 +185,18 @@ func (co *Conn) Read(p []byte) (n int, err error) { return n, err } -// send sends a dns msg to the address specified in w. +// WriteMsg send a dns message throught the connection co. // If the message m contains a TSIG record the transaction // signature is calculated. -func (co *Conn) WriteMsg(m *Msg, tsigSecret map[string]string) (err error) { +func (co *Conn) WriteMsg(m *Msg) (err error) { var out []byte if t := m.IsTsig(); t != nil { mac := "" - if _, ok := tsigSecret[t.Hdr.Name]; !ok { + if _, ok := co.TsigSecret[t.Hdr.Name]; !ok { return ErrSecret } - out, mac, err = TsigGenerate(m, tsigSecret[t.Hdr.Name], co.requestMAC, false) - // Set for the next read + out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.requestMAC, false) + // Set for the next read, allthough only used in zone transfers co.requestMAC = mac } else { out, err = m.Pack() @@ -193,6 +211,7 @@ func (co *Conn) WriteMsg(m *Msg, tsigSecret map[string]string) (err error) { return nil } +// Write implements the net.Conn Write method. func (co *Conn) Write(p []byte) (n int, err error) { if t, ok := co.Conn.(*net.TCPConn); ok { if len(p) < 2 { @@ -220,18 +239,20 @@ func (co *Conn) Write(p []byte) (n int, err error) { return n, err } -/* -func setTimeouts(w *reply) { - if w.client.ReadTimeout == 0 { - w.conn.SetReadDeadline(time.Now().Add(2 * 1e9)) - } else { - w.conn.SetReadDeadline(time.Now().Add(w.client.ReadTimeout)) - } +// Close implements the net.Conn Close method. +func (co *Conn) Close() error { return co.Conn.Close() } - if w.client.WriteTimeout == 0 { - w.conn.SetWriteDeadline(time.Now().Add(2 * 1e9)) - } else { - w.conn.SetWriteDeadline(time.Now().Add(w.client.WriteTimeout)) - } -} -*/ +// LocalAddr implements the net.Conn LocalAddr method. +func (co *Conn) LocalAddr() net.Addr { return co.Conn.LocalAddr() } + +// RemoteAddr implements the net.Conn RemoteAddr method. +func (co *Conn) RemoteAddr() net.Addr { return co.Conn.RemoteAddr() } + +// SetDeadline implements the net.Conn SetDeadline method. +func (co *Conn) SetDeadline(t time.Time) error { return co.Conn.SetDeadline(t) } + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (co *Conn) SetReadDeadline(t time.Time) error { return co.Conn.SetReadDeadline(t) } + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (co *Conn) SetWriteDeadline(t time.Time) error { return co.Conn.SetWriteDeadline(t) } diff --git a/dns.go b/dns.go index 4e606d79..13776d92 100644 --- a/dns.go +++ b/dns.go @@ -91,7 +91,7 @@ import ( const ( year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. DefaultMsgSize = 4096 // Standard default for larger than 512 packets. - udpMsgSize = 512 // Default buffer size for servers receiving UDP packets. + MinMsgSize = 512 // Minimal size of a DNS packet. MaxMsgSize = 65536 // Largest possible DNS packet. defaultTtl = 3600 // Default TTL. ) diff --git a/server.go b/server.go index a6eeff43..33a5fe90 100644 --- a/server.go +++ b/server.go @@ -355,7 +355,7 @@ func (srv *Server) serveUDP(l *net.UDPConn) error { handler = DefaultServeMux } if srv.UDPSize == 0 { - srv.UDPSize = udpMsgSize + srv.UDPSize = MinMsgSize } for { if srv.ReadTimeout != 0 { From 500a32e64fe629ccbc090079b8301ceaf3baffb7 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 29 Sep 2013 11:26:02 +0100 Subject: [PATCH 08/31] Update all the tests --- client.go | 2 +- client_test.go | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/client.go b/client.go index d6739257..4872418b 100644 --- a/client.go +++ b/client.go @@ -17,7 +17,7 @@ const dnsTimeout time.Duration = 2 * 1e9 // A Conn represents a connection (which may be short lived) to a DNS server. type Conn struct { net.Conn - UDPSize uint16 // Minimum reveive buffer for UDP messages, if > 512 EDNS0, 0 MinMsgSize. + UDPSize uint16 // Minimum reveive buffer for UDP messages, if > 512 EDNS0, 0 MinMsgSize. TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be fully qualified rtt time.Duration t time.Time diff --git a/client_test.go b/client_test.go index 90ac03a7..848f9716 100644 --- a/client_test.go +++ b/client_test.go @@ -131,7 +131,6 @@ func TestClientAXFRMultipleMessages(t *testing.T) { } */ -/* // not really a test, but shows how to use update leases func TestUpdateLeaseTSIG(t *testing.T) { m := new(Msg) @@ -154,16 +153,8 @@ func TestUpdateLeaseTSIG(t *testing.T) { m.SetTsig("polvi.", HmacMD5, 300, time.Now().Unix()) c.TsigSecret = map[string]string{"polvi.": "pRZgBrBvI4NAHZYhxmhs/Q=="} - co := new(Conn) - w.client = c - w.addr = "127.0.0.1:53" - w.req = m - - if err := w.dial(); err != nil { - t.Fail() - } - if err := w.send(m); err != nil { + _, _, err := c.Exchange(m, "127.0.0.1:53") + if err != nil { t.Fail() } } -*/ From bf37f92d4aea1134fcb7aa0d276b22ac6a73b344 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 29 Sep 2013 11:30:03 +0100 Subject: [PATCH 09/31] Some test tweaking --- client_test.go | 1 + dns_test.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/client_test.go b/client_test.go index 848f9716..12430449 100644 --- a/client_test.go +++ b/client_test.go @@ -155,6 +155,7 @@ func TestUpdateLeaseTSIG(t *testing.T) { _, _, err := c.Exchange(m, "127.0.0.1:53") if err != nil { + t.Log(err.Error()) t.Fail() } } diff --git a/dns_test.go b/dns_test.go index 129963c1..94b5bae1 100644 --- a/dns_test.go +++ b/dns_test.go @@ -244,6 +244,9 @@ func TestToRFC3597(t *testing.T) { func TestNoRdataPack(t *testing.T) { data := make([]byte, 1024) for typ, fn := range rr_mk { + if typ == TypeCAA { + continue // TODO(miek): known ommision + } r := fn() *r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600} _, e := PackRR(r, data, 0, nil, false) From b02f1b5203d60fa2cf77da5f92bcd4139788c9fc Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 29 Sep 2013 11:46:39 +0100 Subject: [PATCH 10/31] Add Timeouts back in. Fix the timeouts --- client.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client.go b/client.go index 4872418b..2902a84a 100644 --- a/client.go +++ b/client.go @@ -43,7 +43,10 @@ func Exchange(m *Msg, a string) (r *Msg, err error) { if err != nil { return nil, err } + defer co.Close() + co.SetReadDeadline(time.Now().Add(dnsTimeout)) + co.SetWriteDeadline(time.Now().Add(dnsTimeout)) if err = co.WriteMsg(m); err != nil { return nil, err } @@ -97,6 +100,16 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro if err != nil { return nil, 0, err } + timeout = dnsTimeout + if c.ReadTimeout != 0 { + timeout = c.ReadTimeout + } + co.SetReadDeadline(time.Now().Add(dnsTimeout)) + timeout = dnsTimeout + if c.WriteTimeout != 0 { + timeout = c.ReadTimeout + } + co.SetWriteDeadline(time.Now().Add(dnsTimeout)) defer co.Close() opt := m.IsEdns0() if opt != nil && opt.UDPSize() >= MinMsgSize { From b06d42f1d720595a0e23713d6b0ecd664eb557f3 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 29 Sep 2013 11:50:09 +0100 Subject: [PATCH 11/31] correct documentation --- dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns.go b/dns.go index 13776d92..8c746264 100644 --- a/dns.go +++ b/dns.go @@ -36,7 +36,7 @@ // In the DNS messages are exchanged, these messages contain resource // records (sets). Use pattern for creating a message: // -// m := dns.new(Msg) +// m := new(dns.Msg) // m.SetQuestion("miek.nl.", dns.TypeMX) // // Or when not certain if the domain name is fully qualified: From db3de29edc9ba5309a6610b697d3b5e8ea7e1619 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 29 Sep 2013 20:30:04 +0100 Subject: [PATCH 12/31] doc updates --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index 2902a84a..a78f8976 100644 --- a/client.go +++ b/client.go @@ -17,7 +17,7 @@ const dnsTimeout time.Duration = 2 * 1e9 // A Conn represents a connection (which may be short lived) to a DNS server. type Conn struct { net.Conn - UDPSize uint16 // Minimum reveive buffer for UDP messages, if > 512 EDNS0, 0 MinMsgSize. + UDPSize uint16 // Minimum receive buffer for UDP messages TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be fully qualified rtt time.Duration t time.Time From f938bc70df22017c20891cce77230597ff2aaeb5 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 30 Sep 2013 20:24:18 +0100 Subject: [PATCH 13/31] Remove long deprecated functions too --- labels.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/labels.go b/labels.go index 7742dbd9..e7707b2e 100644 --- a/labels.go +++ b/labels.go @@ -160,17 +160,3 @@ func PrevLabel(s string, n int) (i int, start bool) { } return lab[len(lab)-n], false } - -func LenLabels(s string) int { - println("LenLabels is to be removed in future versions, for the better named CountLabel") - return CountLabel(s) -} -func SplitLabels(s string) []string { - println("SplitLabels is to be removed in future versions, for the better named SplitDomainName") - return SplitDomainName(s) -} - -func CompareLabels(s1, s2 string) (n int) { - println("CompareLabels is to be removed in future versions, for better named CompareDomainName") - return CompareDomainName(s1, s2) -} From 403baeb73a4346ed4381a8cf0e6be240cb186ee9 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Wed, 2 Oct 2013 20:35:13 +0100 Subject: [PATCH 14/31] Rework the transfers stuff --- client.go | 10 ++-- xfr.go | 172 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 130 insertions(+), 52 deletions(-) diff --git a/client.go b/client.go index a78f8976..d36d3322 100644 --- a/client.go +++ b/client.go @@ -16,7 +16,7 @@ const dnsTimeout time.Duration = 2 * 1e9 // A Conn represents a connection (which may be short lived) to a DNS server. type Conn struct { - net.Conn + net.Conn // a net.Conn holding the connection UDPSize uint16 // Minimum receive buffer for UDP messages TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be fully qualified rtt time.Duration @@ -28,8 +28,8 @@ type Conn struct { type Client struct { Net string // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for new connections (ns), defaults to 2 * 1e9 - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for new connections (ns), defaults to 2 * 1e9 + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass group singleflight @@ -107,7 +107,7 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro co.SetReadDeadline(time.Now().Add(dnsTimeout)) timeout = dnsTimeout if c.WriteTimeout != 0 { - timeout = c.ReadTimeout + timeout = c.WriteTimeout } co.SetWriteDeadline(time.Now().Add(dnsTimeout)) defer co.Close() @@ -198,7 +198,7 @@ func (co *Conn) Read(p []byte) (n int, err error) { return n, err } -// WriteMsg send a dns message throught the connection co. +// WriteMsg send a message throught the connection co. // If the message m contains a TSIG record the transaction // signature is calculated. func (co *Conn) WriteMsg(m *Msg) (err error) { diff --git a/xfr.go b/xfr.go index a2101ee4..c8e78cf7 100644 --- a/xfr.go +++ b/xfr.go @@ -4,38 +4,138 @@ package dns +import ( + "net" + "time" +) + // Envelope is used when doing [IA]XFR with a remote server. type Envelope struct { RR []RR // The set of RRs in the answer section of the AXFR reply message. Error error // If something went wrong, this contains the error. } -/* -// TransferIn performs a [AI]XFR request (depends on the message's Qtype). It returns -// a channel of *Envelope on which the replies from the server are sent. At the end of -// the transfer the channel is closed. -// The messages are TSIG checked if -// needed, no other post-processing is performed. The caller must dissect the returned -// messages. +type Transfer struct { + Conn + DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 + TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified + tsigTimersOnly bool +} + +// In performs a [AI]XFR request (depends on the message's Qtype). It returns +// a channel of *Envelope on which the replies from the server are sent. +// At the end of the transfer the channel is closed. +// The messages are TSIG checked if needed, no other post-processing is performed. +// The caller must dissect the returned messages. // // Basic use pattern for receiving an AXFR: // // // m contains the AXFR request -// t, e := c.TransferIn(m, "127.0.0.1:53") -// for r := range t { -// // ... deal with r.RR or r.Error +// t := new(dns.Transfer) +// c, e := t.In(m, "127.0.0.1:53") +// for env := range c +// // ... deal with env.RR or env.Error // } + +func (t *Transfer) In(q *Msg, a string, env chan *Envelope) (err error) { + co := new(Conn) + timeout := dnsTimeout + if t.DialTimeout != 0 { + timeout = t.DialTimeout + } + co.Conn, err = net.DialTimeout("tcp", a, timeout) + if err != nil { + return err + } + // re-read 'n stuff must be pushed down + timeout = dnsTimeout + if t.ReadTimeout != 0 { + timeout = t.ReadTimeout + } + co.SetReadDeadline(time.Now().Add(dnsTimeout)) + timeout = dnsTimeout + if t.WriteTimeout != 0 { + timeout = t.WriteTimeout + } + co.SetWriteDeadline(time.Now().Add(dnsTimeout)) + defer co.Close() + return nil +} + +// Out performs an outgoing [AI]XFR depending on the request message. The +// caller is responsible for sending the correct sequence of RR sets through +// the channel c. For reasons of symmetry Envelope is re-used. +// Errors are signaled via the error pointer, when an error occurs the function +// sets the error and returns (it does not close the channel). +// TSIG and enveloping is handled by TransferOut. +// +// Basic use pattern for sending an AXFR: +// +// // m contains the AXFR request +// t := new(dns.Transfer) +// env := make(chan *dns.Envelope) +// err := t.Out(m, c, e) +// for rrset := range rrsets { // rrsets is a []RR +// c <- &{Envelope{RR: rrset} +// if e != nil { +// close(c) +// break +// } +// } +// // w.Close() // Don't! Let the client close the connection +func (t *Transfer) Out(q *Msg, a string) (chan *Envelope, error) { + return nil, nil +} + +// ReadMsg reads a message from the transfer connection t. +func (t *Transfer) ReadMsg() (*Msg, error) { + m := new(Msg) + p := make([]byte, MaxMsgSize) + n, err := t.Conn.Read(p) + if err != nil && n == 0 { + return nil, err + } + p = p[:n] + if err := m.Unpack(p); err != nil { + return nil, err + } + if ts := m.IsTsig(); t != nil { + if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { + return m, ErrSecret + } + // Need to work on the original message p, as that was used to calculate the tsig. + err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.requestMAC, false) + } + return m, err +} + +// WriteMsg write a message throught the transfer connection t. +func (t *Transfer) WriteMsg(m *Msg) (err error) { + var out []byte + if ts := m.IsTsig(); t != nil { + mac := "" + if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { + return ErrSecret + } + out, mac, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.requestMAC, false) + // Set for the next read, allthough only used in zone transfers + t.requestMAC = mac + } else { + out, err = m.Pack() + } + if err != nil { + return err + } + if _, err = t.Conn.Write(out); err != nil { + return err + } + return nil +} + +/* func (c *Client) TransferIn(q *Msg, a string) (chan *Envelope, error) { - w := new(reply) - w.client = c - w.addr = a - w.req = q - if err := w.dial(); err != nil { - return nil, err - } - if err := w.send(q); err != nil { - return nil, err - } e := make(chan *Envelope) switch q.Question[0].Qtype { case TypeAXFR: @@ -65,7 +165,7 @@ func (w *reply) axfrIn(q *Msg, c chan *Envelope) { return } if first { - if !checkXfrSOA(in, true) { + if !checkSOA(in, true) { c <- &Envelope{in.Answer, ErrSoa} return } @@ -80,7 +180,7 @@ func (w *reply) axfrIn(q *Msg, c chan *Envelope) { if !first { w.tsigTimersOnly = true // Subsequent envelopes use this. - if checkXfrSOA(in, false) { + if checkSOA(in, false) { c <- &Envelope{in.Answer, nil} return } @@ -107,13 +207,13 @@ func (w *reply) ixfrIn(q *Msg, c chan *Envelope) { } if first { // A single SOA RR signals "no changes" - if len(in.Answer) == 1 && checkXfrSOA(in, true) { + if len(in.Answer) == 1 && checkSOA(in, true) { c <- &Envelope{in.Answer, nil} return } // Check if the returned answer is ok - if !checkXfrSOA(in, true) { + if !checkSOA(in, true) { c <- &Envelope{in.Answer, ErrSoa} return } @@ -141,7 +241,7 @@ func (w *reply) ixfrIn(q *Msg, c chan *Envelope) { // Check if he SOA record exists in the Answer section of // the packet. If first is true the first RR must be a SOA // if false, the last one should be a SOA. -func checkXfrSOA(in *Msg, first bool) bool { +func checkSOA(in *Msg, first bool) bool { if len(in.Answer) > 0 { if first { return in.Answer[0].Header().Rrtype == TypeSOA @@ -152,28 +252,6 @@ func checkXfrSOA(in *Msg, first bool) bool { return false } -// TransferOut performs an outgoing [AI]XFR depending on the request message. The -// caller is responsible for sending the correct sequence of RR sets through -// the channel c. For reasons of symmetry Envelope is re-used. -// Errors are signaled via the error pointer, when an error occurs the function -// sets the error and returns (it does not close the channel). -// TSIG and enveloping is handled by TransferOut. -// -// Basic use pattern for sending an AXFR: -// -// // q contains the AXFR request -// c := make(chan *Envelope) -// var e *error -// err := TransferOut(w, q, c, e) -// w.Hijack() // hijack the connection so that the package doesn't close it -// for _, rrset := range rrsets { // rrsets is a []RR -// c <- &{Envelope{RR: rrset} -// if e != nil { -// close(c) -// break -// } -// } -// // w.Close() // Don't! Let the client close the connection func TransferOut(w ResponseWriter, q *Msg, c chan *Envelope, e *error) error { switch q.Question[0].Qtype { case TypeAXFR, TypeIXFR: From 5868ba4cc89611ab01d647c794f2e44d83005980 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Thu, 10 Oct 2013 19:01:35 +0000 Subject: [PATCH 15/31] Partial changes for xfr --- xfr.go | 210 ++++++++++++++++++++++++++------------------------------- 1 file changed, 94 insertions(+), 116 deletions(-) diff --git a/xfr.go b/xfr.go index c8e78cf7..155572ae 100644 --- a/xfr.go +++ b/xfr.go @@ -9,7 +9,7 @@ import ( "time" ) -// Envelope is used when doing [IA]XFR with a remote server. +// Envelope is used when doing a transfer with a remote server. type Envelope struct { RR []RR // The set of RRs in the answer section of the AXFR reply message. Error error // If something went wrong, this contains the error. @@ -17,11 +17,10 @@ type Envelope struct { type Transfer struct { Conn - DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 - TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified - tsigTimersOnly bool + DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 + timersOnly bool } // In performs a [AI]XFR request (depends on the message's Qtype). It returns @@ -49,118 +48,28 @@ func (t *Transfer) In(q *Msg, a string, env chan *Envelope) (err error) { if err != nil { return err } - // re-read 'n stuff must be pushed down - timeout = dnsTimeout - if t.ReadTimeout != 0 { - timeout = t.ReadTimeout + if q.Question[0].Qtype == TypeAXFR { + go t.InAxfr(q.Id, env) + return nil } - co.SetReadDeadline(time.Now().Add(dnsTimeout)) - timeout = dnsTimeout - if t.WriteTimeout != 0 { - timeout = t.WriteTimeout + if q.Question[0].Qtype == TypeIXFR { + go t.InAxfr(q.Id, env) + return nil } - co.SetWriteDeadline(time.Now().Add(dnsTimeout)) - defer co.Close() - return nil + return nil // TODO(miek): some error } -// Out performs an outgoing [AI]XFR depending on the request message. The -// caller is responsible for sending the correct sequence of RR sets through -// the channel c. For reasons of symmetry Envelope is re-used. -// Errors are signaled via the error pointer, when an error occurs the function -// sets the error and returns (it does not close the channel). -// TSIG and enveloping is handled by TransferOut. -// -// Basic use pattern for sending an AXFR: -// -// // m contains the AXFR request -// t := new(dns.Transfer) -// env := make(chan *dns.Envelope) -// err := t.Out(m, c, e) -// for rrset := range rrsets { // rrsets is a []RR -// c <- &{Envelope{RR: rrset} -// if e != nil { -// close(c) -// break -// } -// } -// // w.Close() // Don't! Let the client close the connection -func (t *Transfer) Out(q *Msg, a string) (chan *Envelope, error) { - return nil, nil -} - -// ReadMsg reads a message from the transfer connection t. -func (t *Transfer) ReadMsg() (*Msg, error) { - m := new(Msg) - p := make([]byte, MaxMsgSize) - n, err := t.Conn.Read(p) - if err != nil && n == 0 { - return nil, err - } - p = p[:n] - if err := m.Unpack(p); err != nil { - return nil, err - } - if ts := m.IsTsig(); t != nil { - if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { - return m, ErrSecret - } - // Need to work on the original message p, as that was used to calculate the tsig. - err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.requestMAC, false) - } - return m, err -} - -// WriteMsg write a message throught the transfer connection t. -func (t *Transfer) WriteMsg(m *Msg) (err error) { - var out []byte - if ts := m.IsTsig(); t != nil { - mac := "" - if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { - return ErrSecret - } - out, mac, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.requestMAC, false) - // Set for the next read, allthough only used in zone transfers - t.requestMAC = mac - } else { - out, err = m.Pack() - } - if err != nil { - return err - } - if _, err = t.Conn.Write(out); err != nil { - return err - } - return nil -} - -/* -func (c *Client) TransferIn(q *Msg, a string) (chan *Envelope, error) { - e := make(chan *Envelope) - switch q.Question[0].Qtype { - case TypeAXFR: - go w.axfrIn(q, e) - return e, nil - case TypeIXFR: - go w.ixfrIn(q, e) - return e, nil - default: - return nil, nil - } - panic("dns: not reached") -} - -func (w *reply) axfrIn(q *Msg, c chan *Envelope) { +func (t *Transfer) InAxfr(id uint16, c chan *Envelope) { first := true - defer w.conn.Close() + defer t.Close() defer close(c) for { - in, err := w.receive() + in, err := t.ReadMsg() if err != nil { c <- &Envelope{nil, err} return } - if in.Id != q.Id { + if id != q.Id { c <- &Envelope{in.Answer, ErrId} return } @@ -190,6 +99,71 @@ func (w *reply) axfrIn(q *Msg, c chan *Envelope) { panic("dns: not reached") } + // re-read 'n stuff must be pushed down + timeout = dnsTimeout + if t.ReadTimeout != 0 { + timeout = t.ReadTimeout + } + co.SetReadDeadline(time.Now().Add(dnsTimeout)) + timeout = dnsTimeout + if t.WriteTimeout != 0 { + timeout = t.WriteTimeout + } + co.SetWriteDeadline(time.Now().Add(dnsTimeout)) + defer co.Close() + return nil +} + +func (t *Transfer) Out(w ResponseWriter, q *Msg, a string) (chan *Envelope, error) { + ch := make(chan *Envelope) + + return ch, nil +} + +// ReadMsg reads a message from the transfer connection t. +func (t *Transfer) ReadMsg() (*Msg, error) { + m := new(Msg) + p := make([]byte, MaxMsgSize) + n, err := t.Read(p) + if err != nil && n == 0 { + return nil, err + } + p = p[:n] + if err := m.Unpack(p); err != nil { + return nil, err + } + if ts := m.IsTsig(); t != nil { + if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { + return m, ErrSecret + } + // Need to work on the original message p, as that was used to calculate the tsig. + err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.requestMAC, t.timersOnly) + } + return m, err +} + +// WriteMsg write a message throught the transfer connection t. +func (t *Transfer) WriteMsg(m *Msg) (err error) { + var out []byte + if ts := m.IsTsig(); t != nil { + if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { + return ErrSecret + } + out, t.requestMAC, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.requestMAC, t.timersOnly) + } else { + out, err = m.Pack() + } + if err != nil { + return err + } + if _, err = t.Write(out); err != nil { + return err + } + return nil +} + +/* + func (w *reply) ixfrIn(q *Msg, c chan *Envelope) { var serial uint32 // The first serial seen is the current server serial first := true @@ -238,20 +212,24 @@ func (w *reply) ixfrIn(q *Msg, c chan *Envelope) { panic("dns: not reached") } -// Check if he SOA record exists in the Answer section of -// the packet. If first is true the first RR must be a SOA -// if false, the last one should be a SOA. -func checkSOA(in *Msg, first bool) bool { +/* + +func checkFirstSOA(in *Msg) bool { if len(in.Answer) > 0 { - if first { - return in.Answer[0].Header().Rrtype == TypeSOA - } else { - return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA - } + return in.Answer[0].Header().Rrtype == TypeSOA } return false } +func checkLastSOA(in *Msg) bool { + if len(in.Answer) > 0 { + return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA + } + return false +} + + +/* func TransferOut(w ResponseWriter, q *Msg, c chan *Envelope, e *error) error { switch q.Question[0].Qtype { case TypeAXFR, TypeIXFR: From b987dc6246c166b53616e94a51846cf7938725b2 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 11 Oct 2013 16:18:37 +0000 Subject: [PATCH 16/31] More transfer stuff --- client.go | 18 +++++++------- xfr.go | 73 ++++++++++++++++++++----------------------------------- 2 files changed, 36 insertions(+), 55 deletions(-) diff --git a/client.go b/client.go index d36d3322..f085e4e0 100644 --- a/client.go +++ b/client.go @@ -16,12 +16,12 @@ const dnsTimeout time.Duration = 2 * 1e9 // A Conn represents a connection (which may be short lived) to a DNS server. type Conn struct { - net.Conn // a net.Conn holding the connection - UDPSize uint16 // Minimum receive buffer for UDP messages - TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be fully qualified - rtt time.Duration - t time.Time - requestMAC string + net.Conn // a net.Conn holding the connection + UDPSize uint16 // Minimum receive buffer for UDP messages + TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be fully qualified + rtt time.Duration + t time.Time + tsigRequestMAC string } // A Client defines parameters for a DNS client. A nil Client is usable for sending queries. @@ -150,7 +150,7 @@ func (co *Conn) ReadMsg() (*Msg, error) { return m, ErrSecret } // Need to work on the original message p, as that was used to calculate the tsig. - err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.requestMAC, false) + err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false) } return m, err } @@ -208,9 +208,9 @@ func (co *Conn) WriteMsg(m *Msg) (err error) { if _, ok := co.TsigSecret[t.Hdr.Name]; !ok { return ErrSecret } - out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.requestMAC, false) + out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false) // Set for the next read, allthough only used in zone transfers - co.requestMAC = mac + co.tsigRequestMAC = mac } else { out, err = m.Pack() } diff --git a/xfr.go b/xfr.go index 155572ae..50ef9652 100644 --- a/xfr.go +++ b/xfr.go @@ -20,7 +20,7 @@ type Transfer struct { DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 - timersOnly bool + tsigTimersOnly bool } // In performs a [AI]XFR request (depends on the message's Qtype). It returns @@ -69,27 +69,27 @@ func (t *Transfer) InAxfr(id uint16, c chan *Envelope) { c <- &Envelope{nil, err} return } - if id != q.Id { + if id != in.Id { c <- &Envelope{in.Answer, ErrId} return } if first { - if !checkSOA(in, true) { + if !isSOAFirst(in) { c <- &Envelope{in.Answer, ErrSoa} return } first = !first // only one answer that is SOA, receive more if len(in.Answer) == 1 { - w.tsigTimersOnly = true + t.tsigTimersOnly = true c <- &Envelope{in.Answer, nil} continue } } if !first { - w.tsigTimersOnly = true // Subsequent envelopes use this. - if checkSOA(in, false) { + t.tsigTimersOnly = true // Subsequent envelopes use this. + if isSOALast(in) { c <- &Envelope{in.Answer, nil} return } @@ -99,6 +99,7 @@ func (t *Transfer) InAxfr(id uint16, c chan *Envelope) { panic("dns: not reached") } +/* // re-read 'n stuff must be pushed down timeout = dnsTimeout if t.ReadTimeout != 0 { @@ -112,11 +113,24 @@ func (t *Transfer) InAxfr(id uint16, c chan *Envelope) { co.SetWriteDeadline(time.Now().Add(dnsTimeout)) defer co.Close() return nil -} +*/ func (t *Transfer) Out(w ResponseWriter, q *Msg, a string) (chan *Envelope, error) { ch := make(chan *Envelope) - + r := new(Msg) + r.SetReply(q) + r.Authoritative = true + go func() { + for x := range ch { + // assume it fits TODO(miek): fix + r.Answer = append(r.Answer, x.RR...) + if err := w.WriteMsg(r); err != nil { + return + } + } +// w.TsigTimersOnly(true) +// rep.Answer = nil + }() return ch, nil } @@ -137,7 +151,7 @@ func (t *Transfer) ReadMsg() (*Msg, error) { return m, ErrSecret } // Need to work on the original message p, as that was used to calculate the tsig. - err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.requestMAC, t.timersOnly) + err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly) } return m, err } @@ -149,7 +163,7 @@ func (t *Transfer) WriteMsg(m *Msg) (err error) { if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { return ErrSecret } - out, t.requestMAC, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.requestMAC, t.timersOnly) + out, t.tsigRequestMAC, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly) } else { out, err = m.Pack() } @@ -211,51 +225,18 @@ func (w *reply) ixfrIn(q *Msg, c chan *Envelope) { } panic("dns: not reached") } +*/ -/* - -func checkFirstSOA(in *Msg) bool { +func isSOAFirst(in *Msg) bool { if len(in.Answer) > 0 { return in.Answer[0].Header().Rrtype == TypeSOA } return false } -func checkLastSOA(in *Msg) bool { +func isSOALast(in *Msg) bool { if len(in.Answer) > 0 { return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA } return false } - - -/* -func TransferOut(w ResponseWriter, q *Msg, c chan *Envelope, e *error) error { - switch q.Question[0].Qtype { - case TypeAXFR, TypeIXFR: - go xfrOut(w, q, c, e) - return nil - default: - return nil - } - panic("dns: not reached") -} - -// TODO(mg): count the RRs and the resulting size. -func xfrOut(w ResponseWriter, req *Msg, c chan *Envelope, e *error) { - rep := new(Msg) - rep.SetReply(req) - rep.Authoritative = true - - for x := range c { - // assume it fits - rep.Answer = append(rep.Answer, x.RR...) - if err := w.WriteMsg(rep); e != nil { - *e = err - return - } - w.TsigTimersOnly(true) - rep.Answer = nil - } -} -*/ From ca3c488ad8dbad40f92fa34a388aec2b415fa668 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 11 Oct 2013 16:36:37 +0000 Subject: [PATCH 17/31] Fix transfer --- client.go | 4 +- xfr.go | 136 ++++++++++++++++++++++++++---------------------------- 2 files changed, 68 insertions(+), 72 deletions(-) diff --git a/client.go b/client.go index f085e4e0..562eea91 100644 --- a/client.go +++ b/client.go @@ -104,12 +104,12 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro if c.ReadTimeout != 0 { timeout = c.ReadTimeout } - co.SetReadDeadline(time.Now().Add(dnsTimeout)) + co.SetReadDeadline(time.Now().Add(timeout)) timeout = dnsTimeout if c.WriteTimeout != 0 { timeout = c.WriteTimeout } - co.SetWriteDeadline(time.Now().Add(dnsTimeout)) + co.SetWriteDeadline(time.Now().Add(timeout)) defer co.Close() opt := m.IsEdns0() if opt != nil && opt.UDPSize() >= MinMsgSize { diff --git a/xfr.go b/xfr.go index 50ef9652..1dc90559 100644 --- a/xfr.go +++ b/xfr.go @@ -17,10 +17,10 @@ type Envelope struct { type Transfer struct { Conn - DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 - tsigTimersOnly bool + DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 + tsigTimersOnly bool } // In performs a [AI]XFR request (depends on the message's Qtype). It returns @@ -63,7 +63,12 @@ func (t *Transfer) InAxfr(id uint16, c chan *Envelope) { first := true defer t.Close() defer close(c) + timeout := dnsTimeout + if t.ReadTimeout != 0 { + timeout = t.ReadTimeout + } for { + t.SetReadDeadline(time.Now().Add(timeout)) in, err := t.ReadMsg() if err != nil { c <- &Envelope{nil, err} @@ -99,21 +104,59 @@ func (t *Transfer) InAxfr(id uint16, c chan *Envelope) { panic("dns: not reached") } -/* - // re-read 'n stuff must be pushed down - timeout = dnsTimeout +func (t *Transfer) InIxfr(id uint16, c chan *Envelope) { + serial := uint32(0) // The first serial seen is the current server serial + first := true + defer t.Close() + defer close(c) + timeout := dnsTimeout if t.ReadTimeout != 0 { timeout = t.ReadTimeout } - co.SetReadDeadline(time.Now().Add(dnsTimeout)) - timeout = dnsTimeout - if t.WriteTimeout != 0 { - timeout = t.WriteTimeout + for { + // re-read 'n stuff must be pushed down + t.SetReadDeadline(time.Now().Add(timeout)) + in, err := t.ReadMsg() + if err != nil { + c <- &Envelope{in.Answer, err} + return + } + if id != in.Id { + c <- &Envelope{in.Answer, ErrId} + return + } + if first { + // A single SOA RR signals "no changes" + if len(in.Answer) == 1 && isSOAFirst(in) { + c <- &Envelope{in.Answer, nil} + return + } + + // Check if the returned answer is ok + if !isSOAFirst(in) { + c <- &Envelope{in.Answer, ErrSoa} + return + } + // This serial is important + serial = in.Answer[0].(*SOA).Serial + first = !first + // continue // TODO(miek) + } + + // Now we need to check each message for SOA records, to see what we need to do + if !first { + t.tsigTimersOnly = true + // If the last record in the IXFR contains the servers' SOA, we should quit + if v, ok := in.Answer[len(in.Answer)-1].(*SOA); ok { + if v.Serial == serial { + c <- &Envelope{in.Answer, nil} + return + } + } + c <- &Envelope{in.Answer, nil} + } } - co.SetWriteDeadline(time.Now().Add(dnsTimeout)) - defer co.Close() - return nil -*/ +} func (t *Transfer) Out(w ResponseWriter, q *Msg, a string) (chan *Envelope, error) { ch := make(chan *Envelope) @@ -121,15 +164,15 @@ func (t *Transfer) Out(w ResponseWriter, q *Msg, a string) (chan *Envelope, erro r.SetReply(q) r.Authoritative = true go func() { - for x := range ch { - // assume it fits TODO(miek): fix - r.Answer = append(r.Answer, x.RR...) - if err := w.WriteMsg(r); err != nil { - return + for x := range ch { + // assume it fits TODO(miek): fix + r.Answer = append(r.Answer, x.RR...) + if err := w.WriteMsg(r); err != nil { + return + } } - } -// w.TsigTimersOnly(true) -// rep.Answer = nil + // w.TsigTimersOnly(true) + // rep.Answer = nil }() return ch, nil } @@ -178,53 +221,6 @@ func (t *Transfer) WriteMsg(m *Msg) (err error) { /* -func (w *reply) ixfrIn(q *Msg, c chan *Envelope) { - var serial uint32 // The first serial seen is the current server serial - first := true - defer w.conn.Close() - defer close(c) - for { - in, err := w.receive() - if err != nil { - c <- &Envelope{in.Answer, err} - return - } - if q.Id != in.Id { - c <- &Envelope{in.Answer, ErrId} - return - } - if first { - // A single SOA RR signals "no changes" - if len(in.Answer) == 1 && checkSOA(in, true) { - c <- &Envelope{in.Answer, nil} - return - } - - // Check if the returned answer is ok - if !checkSOA(in, true) { - c <- &Envelope{in.Answer, ErrSoa} - return - } - // This serial is important - serial = in.Answer[0].(*SOA).Serial - first = !first - } - - // Now we need to check each message for SOA records, to see what we need to do - if !first { - w.tsigTimersOnly = true - // If the last record in the IXFR contains the servers' SOA, we should quit - if v, ok := in.Answer[len(in.Answer)-1].(*SOA); ok { - if v.Serial == serial { - c <- &Envelope{in.Answer, nil} - return - } - } - c <- &Envelope{in.Answer, nil} - } - } - panic("dns: not reached") -} */ func isSOAFirst(in *Msg) bool { From f9fa2d2c0cdd247c5afe3dc2fecc284e21bf4a2f Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 11 Oct 2013 16:40:15 +0000 Subject: [PATCH 18/31] make these methods privates --- xfr.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xfr.go b/xfr.go index 1dc90559..f31e17a4 100644 --- a/xfr.go +++ b/xfr.go @@ -49,17 +49,17 @@ func (t *Transfer) In(q *Msg, a string, env chan *Envelope) (err error) { return err } if q.Question[0].Qtype == TypeAXFR { - go t.InAxfr(q.Id, env) + go t.inAxfr(q.Id, env) return nil } if q.Question[0].Qtype == TypeIXFR { - go t.InAxfr(q.Id, env) + go t.inIxfr(q.Id, env) return nil } return nil // TODO(miek): some error } -func (t *Transfer) InAxfr(id uint16, c chan *Envelope) { +func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { first := true defer t.Close() defer close(c) @@ -104,7 +104,7 @@ func (t *Transfer) InAxfr(id uint16, c chan *Envelope) { panic("dns: not reached") } -func (t *Transfer) InIxfr(id uint16, c chan *Envelope) { +func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { serial := uint32(0) // The first serial seen is the current server serial first := true defer t.Close() From 05e5e586b5aa9b4c5c069fa56477d46f19abf22d Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 11 Oct 2013 20:10:57 +0100 Subject: [PATCH 19/31] Fix xfr some more --- xfr.go | 46 +++++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/xfr.go b/xfr.go index f31e17a4..674e68b6 100644 --- a/xfr.go +++ b/xfr.go @@ -23,22 +23,8 @@ type Transfer struct { tsigTimersOnly bool } -// In performs a [AI]XFR request (depends on the message's Qtype). It returns -// a channel of *Envelope on which the replies from the server are sent. -// At the end of the transfer the channel is closed. -// The messages are TSIG checked if needed, no other post-processing is performed. -// The caller must dissect the returned messages. -// -// Basic use pattern for receiving an AXFR: -// -// // m contains the AXFR request -// t := new(dns.Transfer) -// c, e := t.In(m, "127.0.0.1:53") -// for env := range c -// // ... deal with env.RR or env.Error -// } - -func (t *Transfer) In(q *Msg, a string, env chan *Envelope) (err error) { +// In performs an incoming transfer with the server in a. +func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { co := new(Conn) timeout := dnsTimeout if t.DialTimeout != 0 { @@ -46,17 +32,20 @@ func (t *Transfer) In(q *Msg, a string, env chan *Envelope) (err error) { } co.Conn, err = net.DialTimeout("tcp", a, timeout) if err != nil { - return err + return nil, err } - if q.Question[0].Qtype == TypeAXFR { - go t.inAxfr(q.Id, env) - return nil - } - if q.Question[0].Qtype == TypeIXFR { - go t.inIxfr(q.Id, env) - return nil - } - return nil // TODO(miek): some error + env = make(chan *Envelope) + go func() { + if q.Question[0].Qtype == TypeAXFR { + go t.inAxfr(q.Id, env) + return + } + if q.Question[0].Qtype == TypeIXFR { + go t.inIxfr(q.Id, env) + return + } + }() + return env, nil } func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { @@ -114,7 +103,6 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { timeout = t.ReadTimeout } for { - // re-read 'n stuff must be pushed down t.SetReadDeadline(time.Now().Add(timeout)) in, err := t.ReadMsg() if err != nil { @@ -171,8 +159,8 @@ func (t *Transfer) Out(w ResponseWriter, q *Msg, a string) (chan *Envelope, erro return } } - // w.TsigTimersOnly(true) - // rep.Answer = nil + w.TsigTimersOnly(true) + r.Answer = nil }() return ch, nil } From 77d78f321851f528049d8a7914e06c36384888d1 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 11 Oct 2013 22:34:04 +0100 Subject: [PATCH 20/31] More xfr fixes, does not work yet --- client.go | 6 ++++-- ex/q/q.go | 57 +++++++++++++++++++++++++++++-------------------------- xfr.go | 12 ++++-------- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/client.go b/client.go index 562eea91..de759fbd 100644 --- a/client.go +++ b/client.go @@ -14,7 +14,7 @@ import ( const dnsTimeout time.Duration = 2 * 1e9 -// A Conn represents a connection (which may be short lived) to a DNS server. +// A Conn represents a connection to a DNS server. type Conn struct { net.Conn // a net.Conn holding the connection UDPSize uint16 // Minimum receive buffer for UDP messages @@ -124,6 +124,8 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro } // ReadMsg reads a message from the connection co. +// If the received message contains a TSIG record the transaction +// signature is verified. func (co *Conn) ReadMsg() (*Msg, error) { var p []byte m := new(Msg) @@ -198,7 +200,7 @@ func (co *Conn) Read(p []byte) (n int, err error) { return n, err } -// WriteMsg send a message throught the connection co. +// WriteMsg sends a message throught the connection co. // If the message m contains a TSIG record the transaction // signature is calculated. func (co *Conn) WriteMsg(m *Msg) (err error) { diff --git a/ex/q/q.go b/ex/q/q.go index 22031298..eba8d8ee 100644 --- a/ex/q/q.go +++ b/ex/q/q.go @@ -135,6 +135,14 @@ Flags: nameserver = dns.Fqdn(nameserver) + ":" + strconv.Itoa(*port) } c := new(dns.Client) + t := new(dns.Transfer) + c.Net = "udp" + if *four { + c.Net = "udp4" + } + if *six { + c.Net = "udp6" + } if *tcp { c.Net = "tcp" if *four { @@ -143,14 +151,6 @@ Flags: if *six { c.Net = "tcp6" } - } else { - c.Net = "udp" - if *four { - c.Net = "udp4" - } - if *six { - c.Net = "udp6" - } } m := new(dns.Msg) @@ -206,6 +206,7 @@ Flags: if algo, name, secret, ok := tsigKeyParse(*tsig); ok { m.SetTsig(name, algo, 300, time.Now().Unix()) c.TsigSecret = map[string]string{name: secret} + t.TsigSecret = map[string]string{name: secret} } else { fmt.Fprintf(os.Stderr, "TSIG key data error\n") return @@ -215,13 +216,15 @@ Flags: fmt.Printf("%s", m.String()) fmt.Printf("\n;; size: %d bytes\n\n", m.Len()) } - if qtype == dns.TypeAXFR { - c.Net = "tcp" - doXfr(c, m, nameserver) - continue - } - if qtype == dns.TypeIXFR { - doXfr(c, m, nameserver) + if qtype == dns.TypeAXFR || qtype == dns.TypeIXFR { + env, err := t.In(m, nameserver) + if err != nil { + fmt.Printf(";; %s\n", err.Error()) + continue + } + for e := range env { + fmt.Printf("%s\n", e.RR) + } continue } r, rtt, e := c.Exchange(m, nameserver) @@ -397,21 +400,21 @@ func shortRR(r dns.RR) dns.RR { func doXfr(c *dns.Client, m *dns.Msg, nameserver string) { /* - if t, e := c.TransferIn(m, nameserver); e == nil { - for r := range t { - if r.Error == nil { - for _, rr := range r.RR { - if *short { - rr = shortRR(rr) + if t, e := c.TransferIn(m, nameserver); e == nil { + for r := range t { + if r.Error == nil { + for _, rr := range r.RR { + if *short { + rr = shortRR(rr) + } + fmt.Printf("%v\n", rr) } - fmt.Printf("%v\n", rr) + } else { + fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", r.Error.Error()) } - } else { - fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", r.Error.Error()) } + } else { + fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", e.Error()) } - } else { - fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", e.Error()) - } */ } diff --git a/xfr.go b/xfr.go index 674e68b6..5bd6f79b 100644 --- a/xfr.go +++ b/xfr.go @@ -16,7 +16,7 @@ type Envelope struct { } type Transfer struct { - Conn + *Conn DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 @@ -25,12 +25,12 @@ type Transfer struct { // In performs an incoming transfer with the server in a. func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { - co := new(Conn) + t.Conn = new(Conn) timeout := dnsTimeout if t.DialTimeout != 0 { timeout = t.DialTimeout } - co.Conn, err = net.DialTimeout("tcp", a, timeout) + t.Conn, err = net.DialTimeout("tcp", a, timeout) if err != nil { return nil, err } @@ -128,7 +128,7 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { // This serial is important serial = in.Answer[0].(*SOA).Serial first = !first - // continue // TODO(miek) + // continue // TODO(miek): ? } // Now we need to check each message for SOA records, to see what we need to do @@ -207,10 +207,6 @@ func (t *Transfer) WriteMsg(m *Msg) (err error) { return nil } -/* - -*/ - func isSOAFirst(in *Msg) bool { if len(in.Answer) > 0 { return in.Answer[0].Header().Rrtype == TypeSOA From 3ca767d2eb2de405065279bd178afd39bf0dc603 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 12:32:14 +0100 Subject: [PATCH 21/31] Fix incoming axfr --- ex/q/q.go | 8 +++++++- xfr.go | 31 +++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/ex/q/q.go b/ex/q/q.go index eba8d8ee..579033e9 100644 --- a/ex/q/q.go +++ b/ex/q/q.go @@ -223,7 +223,13 @@ Flags: continue } for e := range env { - fmt.Printf("%s\n", e.RR) + if e.Error != nil { + fmt.Printf(";; %s\n", e.Error.Error()) + break + } + for _, r := range e.RR { + fmt.Printf("%s\n", r) + } } continue } diff --git a/xfr.go b/xfr.go index 5bd6f79b..0604a53f 100644 --- a/xfr.go +++ b/xfr.go @@ -30,10 +30,13 @@ func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { if t.DialTimeout != 0 { timeout = t.DialTimeout } - t.Conn, err = net.DialTimeout("tcp", a, timeout) + t.Conn.Conn, err = net.DialTimeout("tcp", a, timeout) if err != nil { return nil, err } + if err := t.WriteMsg(q); err != nil { + return nil, err + } env = make(chan *Envelope) go func() { if q.Question[0].Qtype == TypeAXFR { @@ -57,7 +60,7 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { timeout = t.ReadTimeout } for { - t.SetReadDeadline(time.Now().Add(timeout)) + t.Conn.SetReadDeadline(time.Now().Add(timeout)) in, err := t.ReadMsg() if err != nil { c <- &Envelope{nil, err} @@ -67,6 +70,7 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { c <- &Envelope{in.Answer, ErrId} return } + println("ok") if first { if !isSOAFirst(in) { c <- &Envelope{in.Answer, ErrSoa} @@ -177,7 +181,7 @@ func (t *Transfer) ReadMsg() (*Msg, error) { if err := m.Unpack(p); err != nil { return nil, err } - if ts := m.IsTsig(); t != nil { + if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil { if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { return m, ErrSecret } @@ -190,7 +194,7 @@ func (t *Transfer) ReadMsg() (*Msg, error) { // WriteMsg write a message throught the transfer connection t. func (t *Transfer) WriteMsg(m *Msg) (err error) { var out []byte - if ts := m.IsTsig(); t != nil { + if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil { if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { return ErrSecret } @@ -220,3 +224,22 @@ func isSOALast(in *Msg) bool { } return false } +/* +// Close implements the net.Conn Close method. +func (t *Transfer) Close() error { return t.Conn.Close() } + +// LocalAddr implements the net.Conn LocalAddr method. +func (t *Transfer) LocalAddr() net.Addr { return t.Conn.LocalAddr() } + +// RemoteAddr implements the net.Conn RemoteAddr method. +func (t *Transfer) RemoteAddr() net.Addr { return t.Conn.RemoteAddr() } + +// SetDeadline implements the net.Conn SetDeadline method. +func (t *Transfer) SetDeadline(t1 time.Time) error { return t.Conn.SetDeadline(t1) } + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (t *Transfer) SetReadDeadline(t1 time.Time) error { return t.Conn.SetReadDeadline(t1) } + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (t *Transfer) SetWriteDeadline(t1 time.Time) error { return t.Conn.SetWriteDeadline(t1) } +*/ From 97603e3f6298713c33647e1d7e872b754bdd366f Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 12:35:09 +0100 Subject: [PATCH 22/31] Readd ExchangeConn --- client.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client.go b/client.go index de759fbd..dc57e199 100644 --- a/client.go +++ b/client.go @@ -54,6 +54,15 @@ func Exchange(m *Msg, a string) (r *Msg, err error) { return r, err } +// ExchangeConn performs a sync +func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { + if err = co.WriteMsg(m); err != nil { + return nil, err + } + r, err = co.ReadMsg() + return r, err +} + // Exchange performs an synchronous query. It sends the message m to the address // contained in a and waits for an reply. Basic use pattern with a *dns.Client: // From 7691523300c7a7e75575752aa164cc41d2b16394 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 12:44:02 +0100 Subject: [PATCH 23/31] Re-add ExchangeConn ExchangeConn is back, but with a warning. Other various improvements. --- client.go | 13 ++++++++++++- xfr.go | 19 ------------------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/client.go b/client.go index dc57e199..6b90610c 100644 --- a/client.go +++ b/client.go @@ -54,8 +54,19 @@ func Exchange(m *Msg, a string) (r *Msg, err error) { return r, err } -// ExchangeConn performs a sync +// ExchangeConn performs a synchronous query. It sends the message m via the connection +// c and waits for a reply. The connection c is not closed by ExchangeConn. +// This function is going away, but can easily be mimicked: +// +// co := new(dns.Conn) +// co.Conn = c // c is your net.Conn +// co.WriteMsg(m) +// in, _ := co.ReadMsg() +// func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { + println("dns: this function is deprecated") + co := new(Conn) + co.Conn = c if err = co.WriteMsg(m); err != nil { return nil, err } diff --git a/xfr.go b/xfr.go index 0604a53f..9b76ab6e 100644 --- a/xfr.go +++ b/xfr.go @@ -224,22 +224,3 @@ func isSOALast(in *Msg) bool { } return false } -/* -// Close implements the net.Conn Close method. -func (t *Transfer) Close() error { return t.Conn.Close() } - -// LocalAddr implements the net.Conn LocalAddr method. -func (t *Transfer) LocalAddr() net.Addr { return t.Conn.LocalAddr() } - -// RemoteAddr implements the net.Conn RemoteAddr method. -func (t *Transfer) RemoteAddr() net.Addr { return t.Conn.RemoteAddr() } - -// SetDeadline implements the net.Conn SetDeadline method. -func (t *Transfer) SetDeadline(t1 time.Time) error { return t.Conn.SetDeadline(t1) } - -// SetReadDeadline implements the net.Conn SetReadDeadline method. -func (t *Transfer) SetReadDeadline(t1 time.Time) error { return t.Conn.SetReadDeadline(t1) } - -// SetWriteDeadline implements the net.Conn SetWriteDeadline method. -func (t *Transfer) SetWriteDeadline(t1 time.Time) error { return t.Conn.SetWriteDeadline(t1) } -*/ From d0818e23216e18dbb2a8a84ea8ca0356fadf51b7 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 13:27:03 +0100 Subject: [PATCH 24/31] Make q print number of envelopes when doing axfr --- ex/q/q.go | 5 +++++ xfr.go | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ex/q/q.go b/ex/q/q.go index 579033e9..170e6200 100644 --- a/ex/q/q.go +++ b/ex/q/q.go @@ -222,6 +222,8 @@ Flags: fmt.Printf(";; %s\n", err.Error()) continue } + envelope := 0 + record := 0 for e := range env { if e.Error != nil { fmt.Printf(";; %s\n", e.Error.Error()) @@ -230,7 +232,10 @@ Flags: for _, r := range e.RR { fmt.Printf("%s\n", r) } + record+=len(e.RR) + envelope++ } + fmt.Printf("\n;; xfr size: %d records (envelopes %d)\n", record, envelope) continue } r, rtt, e := c.Exchange(m, nameserver) diff --git a/xfr.go b/xfr.go index 9b76ab6e..ce78c5c9 100644 --- a/xfr.go +++ b/xfr.go @@ -132,7 +132,6 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { // This serial is important serial = in.Answer[0].(*SOA).Serial first = !first - // continue // TODO(miek): ? } // Now we need to check each message for SOA records, to see what we need to do From 4ba209c81a8456d922507cf07b1c46a8a7f81c42 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 13:44:44 +0100 Subject: [PATCH 25/31] Fully qualify the axfr name --- ex/q/q.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ex/q/q.go b/ex/q/q.go index 170e6200..15530db2 100644 --- a/ex/q/q.go +++ b/ex/q/q.go @@ -201,7 +201,6 @@ Flags: for _, v := range qname { m.Question[0] = dns.Question{dns.Fqdn(v), qtype, qclass} m.Id = dns.Id() - // Add tsig if *tsig != "" { if algo, name, secret, ok := tsigKeyParse(*tsig); ok { m.SetTsig(name, algo, 300, time.Now().Unix()) @@ -288,15 +287,15 @@ func tsigKeyParse(s string) (algo, name, secret string, ok bool) { s1 := strings.SplitN(s, ":", 3) switch len(s1) { case 2: - return "hmac-md5.sig-alg.reg.int.", s1[0], s1[1], true + return "hmac-md5.sig-alg.reg.int.", s1[0], dns.Fqdn(s1[1]), true case 3: switch s1[0] { case "hmac-md5": - return "hmac-md5.sig-alg.reg.int.", s1[1], s1[2], true + return "hmac-md5.sig-alg.reg.int.", dns.Fqdn(s1[1]), s1[2], true case "hmac-sha1": - return "hmac-sha1.", s1[1], s1[2], true + return "hmac-sha1.", dns.Fqdn(s1[1]), s1[2], true case "hmac-sha256": - return "hmac-sha256.", s1[1], s1[2], true + return "hmac-sha256.", dns.Fqdn(s1[1]), s1[2], true } } return From 66aa54ec211b41f3c6a91d6ae1440c32fa7c0340 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 13:45:35 +0100 Subject: [PATCH 26/31] Fully qualify the axfr name --- ex/q/q.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ex/q/q.go b/ex/q/q.go index 15530db2..78f439c0 100644 --- a/ex/q/q.go +++ b/ex/q/q.go @@ -287,7 +287,7 @@ func tsigKeyParse(s string) (algo, name, secret string, ok bool) { s1 := strings.SplitN(s, ":", 3) switch len(s1) { case 2: - return "hmac-md5.sig-alg.reg.int.", s1[0], dns.Fqdn(s1[1]), true + return "hmac-md5.sig-alg.reg.int.", dns.Fqdn(s1[0]), s1[1], true case 3: switch s1[0] { case "hmac-md5": From 675f170a3d351687182b14cd4ee0a999a24a451f Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 14:00:10 +0100 Subject: [PATCH 27/31] Fix axfr tsig --- client_test.go | 8 +++----- dns_test.go | 3 --- xfr.go | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/client_test.go b/client_test.go index 12430449..4e5b8762 100644 --- a/client_test.go +++ b/client_test.go @@ -79,17 +79,15 @@ Loop: } } -/* func TestClientTsigAXFR(t *testing.T) { m := new(Msg) m.SetAxfr("miek.nl.") m.SetTsig("axfr.", HmacMD5, 300, time.Now().Unix()) - c := new(Client) - c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} - c.Net = "tcp" + tr := new(Transfer) + tr.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} - if a, err := c.TransferIn(m, "37.251.95.53:53"); err != nil { + if a, err := tr.n(m, "176.58.119.54:53"); err != nil { t.Log("Failed to setup axfr: " + err.Error()) t.Fatal() } else { diff --git a/dns_test.go b/dns_test.go index 94b5bae1..aa208a99 100644 --- a/dns_test.go +++ b/dns_test.go @@ -9,9 +9,6 @@ import ( "testing" ) -// Query with way to long name -//./q mx bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.miek.nl.miek.nl.miek123.nl. - func TestPackUnpack(t *testing.T) { out := new(Msg) out.Answer = make([]RR, 1) diff --git a/xfr.go b/xfr.go index ce78c5c9..a7c53dc3 100644 --- a/xfr.go +++ b/xfr.go @@ -20,6 +20,7 @@ type Transfer struct { DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 + TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be fully qualified tsigTimersOnly bool } @@ -70,7 +71,6 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { c <- &Envelope{in.Answer, ErrId} return } - println("ok") if first { if !isSOAFirst(in) { c <- &Envelope{in.Answer, ErrSoa} From f3d8fc6c34c98c29827d7519ba36b871d835b1b3 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 15:35:26 +0100 Subject: [PATCH 28/31] Fix tsig tests and tweak q --- client_test.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/client_test.go b/client_test.go index 4e5b8762..31f7b2f0 100644 --- a/client_test.go +++ b/client_test.go @@ -81,13 +81,13 @@ Loop: func TestClientTsigAXFR(t *testing.T) { m := new(Msg) - m.SetAxfr("miek.nl.") + m.SetAxfr("example.nl.") m.SetTsig("axfr.", HmacMD5, 300, time.Now().Unix()) tr := new(Transfer) tr.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} - if a, err := tr.n(m, "176.58.119.54:53"); err != nil { + if a, err := tr.In(m, "176.58.119.54:53"); err != nil { t.Log("Failed to setup axfr: " + err.Error()) t.Fatal() } else { @@ -103,17 +103,13 @@ func TestClientTsigAXFR(t *testing.T) { } } } -*/ -/* -func TestClientAXFRMultipleMessages(t *testing.T) { +func TestClientAXFRMultipleEnvelopes(t *testing.T) { m := new(Msg) - m.SetAxfr("dnsex.nl.") + m.SetAxfr("nlnetlabs.nl.") - c := new(Client) - c.Net = "tcp" - - if a, err := c.TransferIn(m, "37.251.95.53:53"); err != nil { + tr := new(Transfer) + if a, err := tr.In(m, "213.154.224.1:53"); err != nil { t.Log("Failed to setup axfr" + err.Error()) t.Fail() return @@ -127,10 +123,9 @@ func TestClientAXFRMultipleMessages(t *testing.T) { } } } -*/ // not really a test, but shows how to use update leases -func TestUpdateLeaseTSIG(t *testing.T) { +func ExampleUpdateLeaseTSIG(t *testing.T) { m := new(Msg) m.SetUpdate("t.local.ip6.io.") rr, _ := NewRR("t.local.ip6.io. 30 A 127.0.0.1") From 6dc58c11bdf67fe2159729520188ffeb01ecbac9 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 16:12:21 +0100 Subject: [PATCH 29/31] Fix outgoing axfr and make reflect use it. --- ex/reflect/reflect.go | 17 ++++++++++++++++- xfr.go | 7 ++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ex/reflect/reflect.go b/ex/reflect/reflect.go index 90a49d4c..b41f43f7 100644 --- a/ex/reflect/reflect.go +++ b/ex/reflect/reflect.go @@ -111,6 +111,21 @@ func handleReflect(w dns.ResponseWriter, r *dns.Msg) { case dns.TypeAAAA, dns.TypeA: m.Answer = append(m.Answer, rr) m.Extra = append(m.Extra, t) + + case dns.TypeAXFR, dns.TypeIXFR: + c := make(chan *dns.Envelope) + tr := new(dns.Transfer) + defer close(c) + err := tr.Out(w, r, c) + if err != nil { + return + } + soa, _ := dns.NewRR(`whoami.miek.nl. 0 IN SOA linode.atoom.net. miek.miek.nl. 2009032802 21600 7200 604800 3600`) + c <- &dns.Envelope{RR: []dns.RR{soa, t, rr, soa}} + w.Hijack() + // w.Close() // Client closes connection + return + } if r.IsTsig() != nil { @@ -143,7 +158,7 @@ func serve(net, name, secret string) { } func main() { - runtime.GOMAXPROCS(runtime.NumCPU()*4) + runtime.GOMAXPROCS(runtime.NumCPU() * 4) cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") printf = flag.Bool("print", false, "print replies") compress = flag.Bool("compress", false, "compress replies") diff --git a/xfr.go b/xfr.go index a7c53dc3..4b29d893 100644 --- a/xfr.go +++ b/xfr.go @@ -149,11 +149,12 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { } } -func (t *Transfer) Out(w ResponseWriter, q *Msg, a string) (chan *Envelope, error) { - ch := make(chan *Envelope) +func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error { r := new(Msg) + // Compress? r.SetReply(q) r.Authoritative = true + go func() { for x := range ch { // assume it fits TODO(miek): fix @@ -165,7 +166,7 @@ func (t *Transfer) Out(w ResponseWriter, q *Msg, a string) (chan *Envelope, erro w.TsigTimersOnly(true) r.Answer = nil }() - return ch, nil + return nil } // ReadMsg reads a message from the transfer connection t. From 5b8fa0f1f68dcf360cda2498cedaf54cf6714581 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 16:26:25 +0100 Subject: [PATCH 30/31] Update the documentation --- xfr.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/xfr.go b/xfr.go index 4b29d893..2cd4aee1 100644 --- a/xfr.go +++ b/xfr.go @@ -15,6 +15,7 @@ type Envelope struct { Error error // If something went wrong, this contains the error. } +// A Transfer defines parameters that are used during a zone transfer. type Transfer struct { *Conn DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 @@ -149,6 +150,21 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { } } + + +// Out performs an outgoing transfer with the client connecting in w. +// Basic use pattern: +// +// ch := make(chan *dns.Envelope) +// tr := new(dns.Transfer) +// tr.Out(w, r, ch) +// c <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}} +// close(ch) +// w.Hijack() +// // w.Close() // Client closes connection +// +// The server is responsible for sending the correct sequence of RRs through the +// channel ch. func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error { r := new(Msg) // Compress? From b0d5dadf17b826f11499cf7fcb58ab8612b186aa Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 12 Oct 2013 16:30:02 +0100 Subject: [PATCH 31/31] Dont print status when there was an xfr error --- ex/q/q.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ex/q/q.go b/ex/q/q.go index 78f439c0..43292ffd 100644 --- a/ex/q/q.go +++ b/ex/q/q.go @@ -198,6 +198,7 @@ Flags: m.Extra = append(m.Extra, o) } +query: for _, v := range qname { m.Question[0] = dns.Question{dns.Fqdn(v), qtype, qclass} m.Id = dns.Id() @@ -226,7 +227,7 @@ Flags: for e := range env { if e.Error != nil { fmt.Printf(";; %s\n", e.Error.Error()) - break + continue query } for _, r := range e.RR { fmt.Printf("%s\n", r)