fix merge

This commit is contained in:
Miek Gieben 2012-08-31 15:05:59 +02:00
commit 4a4292c1b9
7 changed files with 161 additions and 137 deletions

View File

@ -1,7 +1,6 @@
# TODO
* outgoing [AI]xfr
* fix 'q' standardize ipv6 input with [::1]#53 ?
* 'q' standardize ipv6 input with [::1]#53 ?
## Nice to have

View File

@ -155,7 +155,7 @@ func (w *reply) receive() (*Msg, error) {
p = make([]byte, DefaultMsgSize)
}
n, err := w.readClient(p)
if err != nil || n == 0 {
if err != nil && n == 0 {
return nil, err
}
p = p[:n]
@ -180,7 +180,7 @@ func (w *reply) readClient(p []byte) (n int, err error) {
if w.conn == nil {
return 0, ErrConnEmpty
}
if len(p) < 1 {
if len(p) < 2 {
return 0, io.ErrShortBuffer
}
attempts := w.client.Attempts
@ -279,9 +279,6 @@ func (w *reply) writeClient(p []byte) (n int, err error) {
if attempts == 0 {
attempts = 1
}
if err = w.dial(); err != nil {
return 0, err
}
switch w.client.Net {
case "tcp", "tcp4", "tcp6":
if len(p) < 2 {

View File

@ -66,16 +66,14 @@ func TestClientTsigAXFR(t *testing.T) {
return
} else {
for ex := range a {
t.Log(ex.Reply.String())
if ex.Error != nil {
t.Logf("Error %s\n", ex.Error.Error())
t.Fail()
break
}
if ex.Reply.Rcode != RcodeSuccess {
break
for _, rr := range ex.RR {
t.Logf("%s\n", rr.String())
}
t.Logf("%s\n", ex.Reply.String())
}
}
}
@ -86,7 +84,6 @@ func TestClientAXFRMultipleMessages(t *testing.T) {
c := new(Client)
c.Net = "tcp"
// timeout?
if a, err := c.XfrReceive(m, "85.223.71.124:53"); err != nil {
t.Log("Failed to setup axfr" + err.Error())
@ -94,15 +91,11 @@ func TestClientAXFRMultipleMessages(t *testing.T) {
return
} else {
for ex := range a {
t.Log(ex.Reply.String())
if ex.Error != nil {
t.Logf("Error %s\n", ex.Error.Error())
t.Fail()
break
}
if ex.Reply.Rcode != RcodeSuccess {
break
}
}
}
}

View File

@ -1,9 +1,9 @@
package main
import (
"github.com/miekg/dns"
"flag"
"fmt"
"github.com/miekg/dns"
"strings"
"time"
)
@ -33,7 +33,11 @@ func main() {
if t, e := client.XfrReceive(m, *nameserver); e == nil {
for r := range t {
if r.Error == nil {
fmt.Printf("%v\n", r.Reply)
for _, rr := range r.RR {
fmt.Printf("%v\n", rr)
}
} else {
fmt.Printf("error: %s\n", r.Error.Error())
}
}
} else {

View File

@ -28,6 +28,7 @@ import (
"runtime/pprof"
"strconv"
"strings"
"syscall"
"time"
)
@ -76,6 +77,24 @@ func handleReflect(w dns.ResponseWriter, r *dns.Msg) {
t.Txt = []string{str}
switch r.Question[0].Qtype {
case dns.TypeAXFR:
c := make(chan *dns.XfrToken)
var e *error
if err := dns.XfrSend(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.XfrToken{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)
@ -86,7 +105,7 @@ func handleReflect(w dns.ResponseWriter, r *dns.Msg) {
m.Extra = append(m.Extra, t)
}
if r.IsTsig() {
if r.IsTsig() != nil {
if w.TsigStatus() == nil {
m.SetTsig(r.Extra[len(r.Extra)-1].(*dns.RR_TSIG).Hdr.Name, dns.HmacMD5, 300, time.Now().Unix())
} else {
@ -142,12 +161,12 @@ func main() {
go serve("tcp", name, secret)
go serve("udp", name, secret)
sig := make(chan os.Signal)
signal.Notify(sig)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
forever:
for {
select {
case <-sig:
fmt.Printf("Signal received, stopping\n")
case s:=<-sig:
fmt.Printf("Signal (%d) received, stopping\n", s)
break forever
}
}

View File

@ -22,12 +22,19 @@ type Handler interface {
type ResponseWriter interface {
// RemoteAddr returns the net.Addr of the client that sent the current request.
RemoteAddr() net.Addr
// TsigStatus returns the status of the Tsig.
TsigStatus() error
// Write writes a reply back to the client.
Write(*Msg) error
// WriteBuf writes a raw buffer back to the client.
WriteBuf([]byte) error
// Close closes the connection.
Close() error
// TsigStatus returns the status of the Tsig.
TsigStatus() error
// TsigTimersOnly sets the tsig timers only boolean.
TsigTimersOnly(bool)
// Hijack lets the caller take over the connection.
// After a call to Hijack(), the DNS package will not do anything with the connection
Hijack()
}
type conn struct {
@ -36,12 +43,16 @@ type conn struct {
request []byte // bytes read
_UDP *net.UDPConn // i/o connection if UDP was used
_TCP *net.TCPConn // i/o connection if TCP was used
hijacked bool // connection has been hijacked by hander TODO(mg)
tsigSecret map[string]string // the tsig secrets
}
type response struct {
conn *conn
<<<<<<< HEAD
=======
hijacked bool // connection has been hijacked by handler
req *Msg
>>>>>>> axfr
tsigStatus error
tsigTimersOnly bool
tsigRequestMAC string
@ -303,18 +314,6 @@ func newConn(t *net.TCPConn, u *net.UDPConn, a net.Addr, buf []byte, handler Han
return c, nil
}
// Close the connection.
func (c *conn) close() {
switch {
case c._UDP != nil:
c._UDP.Close()
c._UDP = nil
case c._TCP != nil:
c._TCP.Close()
c._TCP = nil
}
}
// Serve a new connection.
func (c *conn) serve() {
// for block to make it easy to break out to close the tcp connection
@ -338,17 +337,26 @@ func (c *conn) serve() {
w.tsigStatus = ErrKeyAlg
}
w.tsigStatus = TsigVerify(c.request, w.conn.tsigSecret[secret], "", false)
w.tsigTimersOnly = false // Will this ever be true?
w.tsigTimersOnly = false
w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*RR_TSIG).MAC
}
c.handler.ServeDNS(w, req) // this does the writing back to the client
if c.hijacked {
// client takes care of the connection, i.e. calls Close()
return
}
break
}
// quite elaborate, but this was the original c.close() function
if c._TCP != nil {
c.close() // Listen and Serve is closed then
switch {
case c._UDP != nil:
c._UDP.Close()
c._UDP = nil
case c._TCP != nil:
c._TCP.Close()
c._TCP = nil
}
}
}
@ -421,3 +429,25 @@ func (w *response) RemoteAddr() net.Addr { return w.conn.remoteAddr }
// TsigStatus implements the ResponseWriter.TsigStatus method.
func (w *response) TsigStatus() error { return w.tsigStatus }
// TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method.
func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b }
// Hijack implements the ResponseWriter.Hijack method.
func (w *response) Hijack() { w.hijacked = true }
// Close implements the ResponseWriter.Close method
func (w *response) Close() error {
if w.conn._UDP != nil {
e := w.conn._UDP.Close()
w.conn._UDP = nil
return e
}
if w.conn._TCP != nil {
e := w.conn._TCP.Close()
w.conn._TCP = nil
return e
}
// no-op
return nil
}

178
xfr.go
View File

@ -1,21 +1,13 @@
package dns
import (
"net"
"time"
)
// XfrMsg is used when doing [IA]xfr with a remote server.
type XfrMsg struct {
Request *Msg // the question sent
Reply *Msg // the answer to the question that was sent
Rtt time.Duration // round trip time
RemoteAddr net.Addr // address of the server
Error error // if something went wrong, this contains the error
// XfrToken is used when doing [IA]xfr with a remote server.
type XfrToken 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
}
// XfrReceive performs a [AI]xfr request (depends on the message's Qtype). It returns
// a channel of XfrMsg on which the replies from the server are sent. At the end of
// a channel of XfrToken on which the replies from the server are sent. At the end of
// the transfer the channel is closed.
// It panics if the Qtype does not equal TypeAXFR or TypeIXFR. The messages are TSIG checked if
// needed, no other post-processing is performed. The caller must dissect the returned
@ -23,12 +15,12 @@ type XfrMsg struct {
//
// Basic use pattern for receiving an AXFR:
//
// // m contains the [AI]xfr request
// // m contains the AXFR request
// t, e := client.XfrReceive(m, "127.0.0.1:53")
// for r := range t {
// // ... deal with r.Reply or r.Error
// // ... deal with r.RR or r.Error
// }
func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrMsg, error) {
func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrToken, error) {
w := new(reply)
w.client = c
w.addr = a
@ -39,13 +31,13 @@ func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrMsg, error) {
if err := w.send(q); err != nil {
return nil, err
}
e := make(chan *XfrMsg)
e := make(chan *XfrToken)
switch q.Question[0].Qtype {
case TypeAXFR:
go w.axfrReceive(e)
go w.axfrReceive(q, e)
return e, nil
case TypeIXFR:
go w.ixfrReceive(e)
go w.ixfrReceive(q, e)
return e, nil
default:
return nil, ErrXfrType
@ -53,23 +45,23 @@ func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrMsg, error) {
panic("dns: not reached")
}
func (w *reply) axfrReceive(c chan *XfrMsg) {
func (w *reply) axfrReceive(q *Msg, c chan *XfrToken) {
first := true
defer w.conn.Close()
defer close(c)
for {
in, err := w.receive()
if err != nil {
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: err}
c <- &XfrToken{in.Answer, err}
return
}
if w.req.Id != in.Id {
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: ErrId}
if in.Id != q.Id {
c <- &XfrToken{in.Answer, ErrId}
return
}
if first {
if !checkXfrSOA(in, true) {
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: ErrXfrSoa}
c <- &XfrToken{in.Answer, ErrXfrSoa}
return
}
first = !first
@ -78,16 +70,16 @@ func (w *reply) axfrReceive(c chan *XfrMsg) {
if !first {
w.tsigTimersOnly = true // Subsequent envelopes use this.
if checkXfrSOA(in, false) {
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil}
c <- &XfrToken{in.Answer, nil}
return
}
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil}
c <- &XfrToken{in.Answer, nil}
}
}
panic("dns: not reached")
}
func (w *reply) ixfrReceive(c chan *XfrMsg) {
func (w *reply) ixfrReceive(q *Msg, c chan *XfrToken) {
var serial uint32 // The first serial seen is the current server serial
first := true
defer w.conn.Close()
@ -95,23 +87,23 @@ func (w *reply) ixfrReceive(c chan *XfrMsg) {
for {
in, err := w.receive()
if err != nil {
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: err}
c <- &XfrToken{in.Answer, err}
return
}
if w.req.Id != in.Id {
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: ErrId}
if q.Id != in.Id {
c <- &XfrToken{in.Answer, ErrId}
return
}
if first {
// A single SOA RR signals "no changes"
if len(in.Answer) == 1 && checkXfrSOA(in, true) {
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil}
c <- &XfrToken{in.Answer, nil}
return
}
// Check if the returned answer is ok
if !checkXfrSOA(in, true) {
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: ErrXfrSoa}
c <- &XfrToken{in.Answer, ErrXfrSoa}
return
}
// This serial is important
@ -125,82 +117,19 @@ func (w *reply) ixfrReceive(c chan *XfrMsg) {
// If the last record in the IXFR contains the servers' SOA, we should quit
if v, ok := in.Answer[len(in.Answer)-1].(*RR_SOA); ok {
if v.Serial == serial {
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil}
c <- &XfrToken{in.Answer, nil}
return
}
}
c <- &XfrMsg{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr()}
c <- &XfrToken{in.Answer, nil}
}
}
panic("dns: not reached")
}
// XfrSend performs an outgoing Ixfr or Axfr. The function is [AI]xfr agnostic, it is
// up to the caller to correctly send the sequence of messages.
func XfrSend(w ResponseWriter, q *Msg, a string) error {
switch q.Question[0].Qtype {
case TypeAXFR, TypeIXFR:
// go d.xfrWrite(q, m, e)
default:
return ErrXfrType
}
return nil
}
/*
// Just send the zone
func (d *Conn) axfrSend(q *Msg, m chan *Xfr, e chan os.Error) {
out := new(Msg)
out.Id = q.Id
out.Question = q.Question
out.Answer = make([]RR, 1001) // TODO(mg) look at this number
out.MsgHdr.Response = true
out.MsgHdr.Authoritative = true
first := true
var soa *RR_SOA
i := 0
for r := range m {
out.Answer[i] = r.RR
if soa == nil {
if r.RR.Header().Rrtype != TypeSOA {
e <- ErrXfrSoa
return
} else {
soa = r.RR.(*RR_SOA)
}
}
i++
if i > 1000 {
// Send it
err := d.WriteMsg(out)
if err != nil {
e <- err
return
}
i = 0
// Gaat dit goed?
out.Answer = out.Answer[:0]
if first {
if d.Tsig != nil {
d.Tsig.TimersOnly = true
}
first = !first
}
}
}
// Everything is sent, only the closing soa is left.
out.Answer[i] = soa
out.Answer = out.Answer[:i+1]
err := d.WriteMsg(out)
if err != nil {
e <- err
}
}
*/
// 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
// if false, the last one should be a SOA.
func checkXfrSOA(in *Msg, first bool) bool {
if len(in.Answer) > 0 {
if first {
@ -211,3 +140,56 @@ func checkXfrSOA(in *Msg, first bool) bool {
}
return false
}
// XfrSend 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 XfrToken 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 XfrSend.
//
// Basic use pattern for sending an AXFR:
//
// // q contains the AXFR request
// c := make(chan *XfrToken)
// var e *error
// err := XfrSend(w, q, c, e)
// w.Hijack() // hijack the connection so that the library doesn't close it
// for _, rrset := range rrsets { // rrset is a []RR
// c <- &{XfrToken{RR: rrset}
// if e != nil {
// close(c)
// break
// }
// }
// // w.Close() // Don't! Let the client close the connection
func XfrSend(w ResponseWriter, q *Msg, c chan *XfrToken, e *error) error {
switch q.Question[0].Qtype {
case TypeAXFR, TypeIXFR:
go axfrSend(w, q, c, e)
return nil
default:
return ErrXfrType
}
panic("not reached")
}
// TODO(mg): count the RRs and the resulting size.
func axfrSend(w ResponseWriter, req *Msg, c chan *XfrToken, e *error) {
rep := new(Msg)
rep.SetReply(req)
rep.MsgHdr.Authoritative = true
for x := range c {
// assume it fits
rep.Answer = append(rep.Answer, x.RR...)
if err := w.Write(rep); e != nil {
*e = err
return
}
w.TsigTimersOnly(true)
rep.Answer = nil
}
}