diff --git a/ex/axfr/axfr.go b/ex/axfr/axfr.go index a776a495..b5f9ac51 100644 --- a/ex/axfr/axfr.go +++ b/ex/axfr/axfr.go @@ -30,17 +30,13 @@ func main() { m.SetTsig(name, dns.HmacMD5, 300, m.MsgHdr.Id, time.Now().Unix()) } - if err := client.XfrReceive(m, *nameserver); err == nil { - for r := range client.ReplyChan { - if r.Error != nil { - if r.Error == dns.ErrXfrLast { - fmt.Printf("%v\n", r.Reply) - } - break + if t, e := client.XfrReceive(m, *nameserver); e == nil { + for r := range t { + if r.Error == nil { + fmt.Printf("%v\n", r.Reply) } - fmt.Printf("%v\n", r.Reply) } } else { - fmt.Printf("Error %v\n", err) + fmt.Printf("Error %v\n", e) } } diff --git a/xfr.go b/xfr.go index 3d3b908c..c7a477c1 100644 --- a/xfr.go +++ b/xfr.go @@ -1,48 +1,59 @@ package dns // XfrReceives requests an incoming Ixfr or Axfr. If the message q's question -// section contains an AXFR type an Axfr is performed, if it is IXFR it does an Ixfr. -// Each message will be send along the Client's reply channel as it is received. -// The last message send has Exchange.Error set to ErrXfrLast -// to signal there is nothing more to come. -func (c *Client) XfrReceive(q *Msg, a string) error { +// section has type TypeAXFR an Axfr is performed, if it is TypeIXFR it does an Ixfr. +// The [AI]xfr's records are returned on the channel. Note the with an IXFR the client +// needs to determine if records are to be removed are added. +// The channel is closed when the transfer is terminated. +// +// Basic use pattern for setting up a transfer: +// +// t, _ := client.XfrReceive(m, "127.0.0.1:53") +// for r := range t { +// // +// } +func (c *Client) XfrReceive(q *Msg, a string) (chan *Exchange, error) { w := new(reply) w.client = c w.addr = a w.req = q if err := w.Dial(); err != nil { - return err + return nil, err } if err := w.Send(q); err != nil { - return err + return nil, err } + e := make(chan *Exchange) switch q.Question[0].Qtype { case TypeAXFR: - go w.axfrReceive() + go w.axfrReceive(e) + return e, nil case TypeIXFR: - go w.ixfrReceive() + go w.ixfrReceive(e) + return e, nil default: - return ErrXfrType + return nil, ErrXfrType } - return nil + panic("not reached") } -func (w *reply) axfrReceive() { +func (w *reply) axfrReceive(c chan *Exchange) { first := true defer w.Close() + defer close(c) for { in, err := w.Receive() if err != nil { - w.Client().ReplyChan <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), err} + c <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), err} return } if w.req.Id != in.Id { - w.Client().ReplyChan <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrId} + c <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrId} return } if first { if !checkXfrSOA(in, true) { - w.Client().ReplyChan <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrXfrSoa} + c <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrXfrSoa} return } first = !first @@ -51,41 +62,40 @@ func (w *reply) axfrReceive() { if !first { w.tsigTimersOnly = true // Subsequent envelopes use this. if checkXfrSOA(in, false) { - w.Client().ReplyChan <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrXfrLast} + c <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrXfrLast} return } - w.Client().ReplyChan <- &Exchange{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil} + c <- &Exchange{Request: w.req, Reply: in, Rtt: w.rtt, RemoteAddr: w.conn.RemoteAddr(), Error: nil} } } panic("not reached") - return } -func (w *reply) ixfrReceive() { +func (w *reply) ixfrReceive(c chan *Exchange) { var serial uint32 // The first serial seen is the current server serial first := true defer w.Close() + defer close(c) for { in, err := w.Receive() if err != nil { - w.Client().ReplyChan <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), err} + c <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), err} return } if w.req.Id != in.Id { - w.Client().ReplyChan <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrId} + c <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrId} return } - if first { // A single SOA RR signals "no changes" if len(in.Answer) == 1 && checkXfrSOA(in, true) { - w.Client().ReplyChan <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrXfrLast} + c <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), nil} return } // Check if the returned answer is ok if !checkXfrSOA(in, true) { - w.Client().ReplyChan <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrXfrSoa} + c <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrXfrSoa} return } // This serial is important @@ -99,15 +109,14 @@ func (w *reply) ixfrReceive() { // 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 { - w.Client().ReplyChan <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), ErrXfrLast} + c <- &Exchange{w.req, in, w.rtt, w.conn.RemoteAddr(), nil} return } } - w.Client().ReplyChan <- &Exchange{Request: w.req, Reply: in} + c <- &Exchange{Request: w.req, Reply: in} } } panic("not reached") - return } // XfrSend performs an outgoing Ixfr or Axfr. The function is xfr agnostic, it is