From: Bob Halley Date: Thu, 20 Jul 2006 01:40:28 +0000 (+0000) Subject: add IXFR support X-Git-Tag: v1.5.0~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c77a12960de59ef8e0f544dd339b3d15699d0168;p=thirdparty%2Fdnspython.git add IXFR support --- diff --git a/ChangeLog b/ChangeLog index 79fe31b8..3d011320 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2006-07-19 Bob Halley + + * dns/query.py (xfr): Add IXFR support. + 2006-06-22 Bob Halley * dns/rdtypes/IN/IPSECKEY.py: Added support for the IPSECKEY RR type. diff --git a/dns/query.py b/dns/query.py index 38bbcafd..fa79ad8e 100644 --- a/dns/query.py +++ b/dns/query.py @@ -222,7 +222,7 @@ def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0): 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 @@ -261,11 +261,18 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, @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() @@ -291,7 +298,8 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, 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 @@ -312,24 +320,29 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, 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()