Fix incoming [IA]xfr

Make the function return a new channel, which is closed at the
end of the transfer. This way you can just use 'range' to loop
over the records.
This commit is contained in:
Miek Gieben 2012-05-18 12:06:31 +02:00
parent d0e4f0409a
commit f26963f4c3
2 changed files with 41 additions and 36 deletions

View File

@ -30,17 +30,13 @@ func main() {
m.SetTsig(name, dns.HmacMD5, 300, m.MsgHdr.Id, time.Now().Unix()) m.SetTsig(name, dns.HmacMD5, 300, m.MsgHdr.Id, time.Now().Unix())
} }
if err := client.XfrReceive(m, *nameserver); err == nil { if t, e := client.XfrReceive(m, *nameserver); e == nil {
for r := range client.ReplyChan { for r := range t {
if r.Error != nil { if r.Error == nil {
if r.Error == dns.ErrXfrLast { fmt.Printf("%v\n", r.Reply)
fmt.Printf("%v\n", r.Reply)
}
break
} }
fmt.Printf("%v\n", r.Reply)
} }
} else { } else {
fmt.Printf("Error %v\n", err) fmt.Printf("Error %v\n", e)
} }
} }

63
xfr.go
View File

@ -1,48 +1,59 @@
package dns package dns
// XfrReceives requests an incoming Ixfr or Axfr. If the message q's question // 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. // section has type TypeAXFR an Axfr is performed, if it is TypeIXFR it does an Ixfr.
// Each message will be send along the Client's reply channel as it is received. // The [AI]xfr's records are returned on the channel. Note the with an IXFR the client
// The last message send has Exchange.Error set to ErrXfrLast // needs to determine if records are to be removed are added.
// to signal there is nothing more to come. // The channel is closed when the transfer is terminated.
func (c *Client) XfrReceive(q *Msg, a string) error { //
// 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 := new(reply)
w.client = c w.client = c
w.addr = a w.addr = a
w.req = q w.req = q
if err := w.Dial(); err != nil { if err := w.Dial(); err != nil {
return err return nil, err
} }
if err := w.Send(q); err != nil { if err := w.Send(q); err != nil {
return err return nil, err
} }
e := make(chan *Exchange)
switch q.Question[0].Qtype { switch q.Question[0].Qtype {
case TypeAXFR: case TypeAXFR:
go w.axfrReceive() go w.axfrReceive(e)
return e, nil
case TypeIXFR: case TypeIXFR:
go w.ixfrReceive() go w.ixfrReceive(e)
return e, nil
default: default:
return ErrXfrType return nil, ErrXfrType
} }
return nil panic("not reached")
} }
func (w *reply) axfrReceive() { func (w *reply) axfrReceive(c chan *Exchange) {
first := true first := true
defer w.Close() defer w.Close()
defer close(c)
for { for {
in, err := w.Receive() in, err := w.Receive()
if err != nil { 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 return
} }
if w.req.Id != in.Id { 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 return
} }
if first { if first {
if !checkXfrSOA(in, true) { 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 return
} }
first = !first first = !first
@ -51,41 +62,40 @@ func (w *reply) axfrReceive() {
if !first { if !first {
w.tsigTimersOnly = true // Subsequent envelopes use this. w.tsigTimersOnly = true // Subsequent envelopes use this.
if checkXfrSOA(in, false) { 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 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") 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 var serial uint32 // The first serial seen is the current server serial
first := true first := true
defer w.Close() defer w.Close()
defer close(c)
for { for {
in, err := w.Receive() in, err := w.Receive()
if err != nil { 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 return
} }
if w.req.Id != in.Id { 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 return
} }
if first { if first {
// A single SOA RR signals "no changes" // A single SOA RR signals "no changes"
if len(in.Answer) == 1 && checkXfrSOA(in, true) { 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 return
} }
// Check if the returned answer is ok // Check if the returned answer is ok
if !checkXfrSOA(in, true) { 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 return
} }
// This serial is important // 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 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, ok := in.Answer[len(in.Answer)-1].(*RR_SOA); ok {
if v.Serial == serial { 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 return
} }
} }
w.Client().ReplyChan <- &Exchange{Request: w.req, Reply: in} c <- &Exchange{Request: w.req, Reply: in}
} }
} }
panic("not reached") panic("not reached")
return
} }
// XfrSend performs an outgoing Ixfr or Axfr. The function is xfr agnostic, it is // XfrSend performs an outgoing Ixfr or Axfr. The function is xfr agnostic, it is