Fix IXFR may end prematurely (#512) (#507)

Fix make IXFR work with single line answers (#507)
This commit is contained in:
kahnert 2017-10-13 17:21:31 +02:00 committed by Miek Gieben
parent 5f64fb22f9
commit be519e51ff
1 changed files with 32 additions and 27 deletions

59
xfr.go
View File

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