def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
timeout=None, port=53, keyring=None, keyname=None, relativize=True,
- af=None, lifetime=None, source=None, source_port=0):
+ af=None, lifetime=None, source=None, source_port=0, serial=0):
"""Return a generator for the responses to a zone transfer.
@param where: where to send the message
@type source: string
@param source_port: The port from which to send the message.
The default is 0.
- @type source_port: int"""
+ @type source_port: int
+ @param serial: The SOA serial number to use as the base for an IXFR diff
+ sequence (only meaningful if rdtype == dns.rdatatype.IXFR).
+ @type serial: int"""
if isinstance(zone, (str, unicode)):
zone = dns.name.from_text(zone)
q = dns.message.make_query(zone, rdtype, rdclass)
+ if rdtype == dns.rdatatype.IXFR:
+ rrset = dns.rrset.from_text(zone, 0, 'IN', 'SOA',
+ '. . %u 0 0 0 0' % serial)
+ q.authority.append(rrset)
if not keyring is None:
q.use_tsig(keyring, keyname)
wire = q.to_wire()
tcpmsg = struct.pack("!H", l) + wire
_net_write(s, tcpmsg, expiration)
done = False
- seen_soa = False
+ soa_rrset = None
+ soa_count = 0
if relativize:
origin = zone
oname = dns.name.empty
multi=True, first=first)
tsig_ctx = r.tsig_ctx
first = False
- if not seen_soa:
+ if soa_rrset is None:
if not r.answer or r.answer[0].name != oname:
raise dns.exception.FormError
rrset = r.answer[0]
if rrset.rdtype != dns.rdatatype.SOA:
raise dns.exception.FormError
- seen_soa = True
- if len(r.answer) > 1 and r.answer[-1].name == oname:
- rrset = r.answer[-1]
- if rrset.rdtype == dns.rdatatype.SOA:
- if q.keyring and not r.had_tsig:
- raise dns.exception.FormError, "missing TSIG"
- done = True
- elif r.answer and r.answer[-1].name == oname:
+ soa_rrset = rrset.copy()
+ #
+ # Count the number of origin SOA RRs in this message
+ #
+ for rrset in r.answer:
+ if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname:
+ soa_count += 1
+ #
+ # Are we done?
+ #
+ if len(r.answer) > 1 and r.answer[-1].name == oname:
rrset = r.answer[-1]
- if rrset.rdtype == dns.rdatatype.SOA:
- if q.keyring and not r.had_tsig:
- raise dns.exception.FormError, "missing TSIG"
+ if rrset == soa_rrset and \
+ (rdtype == dns.rdatatype.AXFR or \
+ (rdtype == dns.rdatatype.IXFR and soa_count % 2 == 0)):
done = True
+ if done and q.keyring and not r.had_tsig:
+ raise dns.exception.FormError, "missing TSIG"
yield r
s.close()