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) env = make(chan *Envelope)
go func() { go func() {
if q.Question[0].Qtype == TypeAXFR { if q.Question[0].Qtype == TypeAXFR {
go t.inAxfr(q.Id, env) go t.inAxfr(q, env)
return return
} }
if q.Question[0].Qtype == TypeIXFR { if q.Question[0].Qtype == TypeIXFR {
go t.inIxfr(q.Id, env) go t.inIxfr(q, env)
return return
} }
}() }()
return env, nil return env, nil
} }
func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) {
first := true first := true
defer t.Close() defer t.Close()
defer close(c) defer close(c)
@ -77,7 +77,7 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) {
c <- &Envelope{nil, err} c <- &Envelope{nil, err}
return return
} }
if id != in.Id { if q.Id != in.Id {
c <- &Envelope{in.Answer, ErrId} c <- &Envelope{in.Answer, ErrId}
return 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 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 t.Close()
defer close(c) defer close(c)
timeout := dnsTimeout timeout := dnsTimeout
@ -126,21 +128,15 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) {
c <- &Envelope{nil, err} c <- &Envelope{nil, err}
return return
} }
if id != in.Id { if q.Id != in.Id {
c <- &Envelope{in.Answer, ErrId} c <- &Envelope{in.Answer, ErrId}
return return
} }
if first { if in.Rcode != RcodeSuccess {
if in.Rcode != RcodeSuccess { c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}} return
return }
} if n == 0 {
// A single SOA RR signals "no changes"
if len(in.Answer) == 1 && isSOAFirst(in) {
c <- &Envelope{in.Answer, nil}
return
}
// Check if the returned answer is ok // Check if the returned answer is ok
if !isSOAFirst(in) { if !isSOAFirst(in) {
c <- &Envelope{in.Answer, ErrSoa} c <- &Envelope{in.Answer, ErrSoa}
@ -148,21 +144,30 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) {
} }
// This serial is important // This serial is important
serial = in.Answer[0].(*SOA).Serial 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 // Now we need to check each message for SOA records, to see what we need to do
if !first { t.tsigTimersOnly = true
t.tsigTimersOnly = true for _, rr := range in.Answer {
// If the last record in the IXFR contains the servers' SOA, we should quit if v, ok := rr.(*SOA); ok {
if v, ok := in.Answer[len(in.Answer)-1].(*SOA); ok {
if v.Serial == serial { if v.Serial == serial {
c <- &Envelope{in.Answer, nil} n++
return // 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}
} }
} }