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())
}
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)
}
}

63
xfr.go
View File

@ -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