From be519e51ffa443ebfed8777a0caaa26fdfcbc0a2 Mon Sep 17 00:00:00 2001 From: kahnert Date: Fri, 13 Oct 2017 17:21:31 +0200 Subject: [PATCH] Fix IXFR may end prematurely (#512) (#507) Fix make IXFR work with single line answers (#507) --- xfr.go | 59 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/xfr.go b/xfr.go index 576c5590..2e892ea3 100644 --- a/xfr.go +++ b/xfr.go @@ -51,18 +51,18 @@ func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { env = make(chan *Envelope) go func() { if q.Question[0].Qtype == TypeAXFR { - go t.inAxfr(q.Id, env) + go t.inAxfr(q, env) return } if q.Question[0].Qtype == TypeIXFR { - go t.inIxfr(q.Id, env) + go t.inIxfr(q, env) return } }() return env, nil } -func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { +func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) { first := true defer t.Close() defer close(c) @@ -77,7 +77,7 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { c <- &Envelope{nil, err} return } - if id != in.Id { + if q.Id != in.Id { c <- &Envelope{in.Answer, ErrId} return } @@ -110,9 +110,11 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { } } -func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { +func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) { serial := uint32(0) // The first serial seen is the current server serial - first := true + axfr := true + n := 0 + qser := q.Ns[0].(*SOA).Serial defer t.Close() defer close(c) timeout := dnsTimeout @@ -126,21 +128,15 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { c <- &Envelope{nil, err} return } - if id != in.Id { + if q.Id != in.Id { c <- &Envelope{in.Answer, ErrId} return } - if first { - if in.Rcode != RcodeSuccess { - c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}} - return - } - // A single SOA RR signals "no changes" - if len(in.Answer) == 1 && isSOAFirst(in) { - c <- &Envelope{in.Answer, nil} - return - } - + if in.Rcode != RcodeSuccess { + c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}} + return + } + if n == 0 { // Check if the returned answer is ok if !isSOAFirst(in) { c <- &Envelope{in.Answer, ErrSoa} @@ -148,21 +144,30 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { } // This serial is important serial = in.Answer[0].(*SOA).Serial - first = !first + // Check if there are no changes in zone + if qser >= serial { + c <- &Envelope{in.Answer, nil} + return + } } - // 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 { + t.tsigTimersOnly = true + for _, rr := range in.Answer { + if v, ok := rr.(*SOA); ok { if v.Serial == serial { - c <- &Envelope{in.Answer, nil} - return + n++ + // quit if it's a full axfr or the the servers' SOA is repeated the third time + if axfr && n == 2 || n == 3 { + c <- &Envelope{in.Answer, nil} + return + } + } else if axfr { + // it's an ixfr + axfr = false } } - c <- &Envelope{in.Answer, nil} } + c <- &Envelope{in.Answer, nil} } }