def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (algorithm, flags, iterations, slen) = struct.unpack('!BBHB', wire[current:current + 5]) current += 5 rdlen -= 5 salt = wire[current:current + slen].unwrap() current += slen rdlen -= slen (nlen, ) = struct.unpack('!B', wire[current]) current += 1 rdlen -= 1 next = wire[current:current + nlen].unwrap() current += nlen rdlen -= nlen windows = [] while rdlen > 0: if rdlen < 3: raise exception.FormError("NSEC3 too short") window = ord(wire[current]) octets = ord(wire[current + 1]) if octets == 0 or octets > 32: raise exception.FormError("bad NSEC3 octets") current += 2 rdlen -= 2 if rdlen < octets: raise exception.FormError("bad NSEC3 bitmap length") bitmap = wire[current:current + octets].unwrap() current += octets rdlen -= octets windows.append((window, bitmap)) return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows)
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): if rdlen < 3: raise exception.FormError header = struct.unpack('!BBB', wire[current:current + 3]) gateway_type = header[1] current += 3 rdlen -= 3 if gateway_type == 0: gateway = None elif gateway_type == 1: gateway = inet.inet_ntop(inet.AF_INET, wire[current:current + 4]) current += 4 rdlen -= 4 elif gateway_type == 2: gateway = inet.inet_ntop(inet.AF_INET6, wire[current:current + 16]) current += 16 rdlen -= 16 elif gateway_type == 3: (gateway, cused) = name.from_wire(wire[:current + rdlen], current) current += cused rdlen -= cused else: raise exception.FormError('invalid IPSECKEY gateway type') key = wire[current:current + rdlen].unwrap() return cls(rdclass, rdtype, header[0], gateway_type, header[2], gateway, key)
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): (version, size, hprec, vprec, latitude, longitude, altitude) = \ struct.unpack("!BBBBIII", wire[current : current + rdlen]) if latitude > 0x80000000L: latitude = float(latitude - 0x80000000L) / 3600000 else: latitude = -1 * float(0x80000000L - latitude) / 3600000 if latitude < -90.0 or latitude > 90.0: raise exception.FormError("bad latitude") if longitude > 0x80000000L: longitude = float(longitude - 0x80000000L) / 3600000 else: longitude = -1 * float(0x80000000L - longitude) / 3600000 if longitude < -180.0 or longitude > 180.0: raise exception.FormError("bad longitude") altitude = float(altitude) - 10000000.0 size = _decode_size(size, "size") hprec = _decode_size(hprec, "horizontal precision") vprec = _decode_size(vprec, "vertical precision") return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec)
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (next, cused) = name.from_wire(wire[:current + rdlen], current) current += cused rdlen -= cused windows = [] while rdlen > 0: if rdlen < 3: raise exception.FormError("NSEC too short") window = ord(wire[current]) octets = ord(wire[current + 1]) if octets == 0 or octets > 32: raise exception.FormError("bad NSEC octets") current += 2 rdlen -= 2 if rdlen < octets: raise exception.FormError("bad NSEC bitmap length") bitmap = wire[current:current + octets].unwrap() current += octets rdlen -= octets windows.append((window, bitmap)) if not origin is None: next = next.relativize(origin) return cls(rdclass, rdtype, next, windows)
def make_response(query, recursion_available=False, our_payload=8192, fudge=300): """Make a message which is a response for the specified query. The message returned is really a response skeleton; it has all of the infrastructure required of a response, but none of the content. The response's question section is a shallow copy of the query's question section, so the query's question RRsets should not be changed. @param query: the query to respond to @type query: message.Message object @param recursion_available: should RA be set in the response? @type recursion_available: bool @param our_payload: payload size to advertise in EDNS responses; default is 8192. @type our_payload: int @param fudge: TSIG time fudge; default is 300 seconds. @type fudge: int @rtype: message.Message object""" if query.flags & flags.QR: raise exception.FormError('specified query message is not a query') response = message.Message(query.id) response.flags = flags.QR | (query.flags & flags.RD) if recursion_available: response.flags |= flags.RA response.set_opcode(query.opcode()) response.question = list(query.question) if query.edns >= 0: response.use_edns(0, 0, our_payload, query.payload) if query.had_tsig: response.use_tsig(query.keyring, query.keyname, fudge, None, 0, '', query.keyalgorithm) response.request_mac = query.mac return response
def xfr(where, zone, rdtype=rdatatype.AXFR, rdclass=rdataclass.IN, timeout=None, port=53, keyring=None, keyname=None, relativize=True, af=None, lifetime=None, source=None, source_port=0, serial=0, use_udp=False, keyalgorithm=tsig.default_algorithm): """Return a generator for the responses to a zone transfer. @param where: where to send the message @type where: string containing an IPv4 or IPv6 address @param zone: The name of the zone to transfer @type zone: name.Name object or string @param rdtype: The type of zone transfer. The default is rdatatype.AXFR. @type rdtype: int or string @param rdclass: The class of the zone transfer. The default is rdataclass.IN. @type rdclass: int or string @param timeout: The number of seconds to wait for each response message. If None, the default, wait forever. @type timeout: float @param port: The port to which to send the message. The default is 53. @type port: int @param keyring: The TSIG keyring to use @type keyring: dict @param keyname: The name of the TSIG key to use @type keyname: name.Name object or string @param relativize: If True, all names in the zone will be relativized to the zone origin. It is essential that the relativize setting matches the one specified to zone.from_xfr(). @type relativize: bool @param af: the address family to use. The default is None, which causes the address family to use to be inferred from the form of of where. If the inference attempt fails, AF_INET is used. @type af: int @param lifetime: The total number of seconds to spend doing the transfer. If None, the default, then there is no limit on the time the transfer may take. @type lifetime: float @rtype: generator of message.Message objects. @param source: source address. The default is the wildcard address. @type source: string @param source_port: The port from which to send the message. The default is 0. @type source_port: int @param serial: The SOA serial number to use as the base for an IXFR diff sequence (only meaningful if rdtype == rdatatype.IXFR). @type serial: int @param use_udp: Use UDP (only meaningful for IXFR) @type use_udp: bool @param keyalgorithm: The TSIG algorithm to use; defaults to tsig.default_algorithm @type keyalgorithm: string """ if isinstance(zone, (str, unicode)): zone = name.from_text(zone) if isinstance(rdtype, (str, unicode)): rdtype = rdatatype.from_text(rdtype) q = message.make_query(zone, rdtype, rdclass) if rdtype == rdatatype.IXFR: rrset = 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, algorithm=keyalgorithm) wire = q.to_wire() (af, destination, source) = _destination_and_source(af, where, port, source, source_port) if use_udp: if rdtype != rdatatype.IXFR: raise ValueError('cannot do a UDP AXFR') s = socket.socket(af, socket.SOCK_DGRAM, 0) else: s = socket.socket(af, socket.SOCK_STREAM, 0) s.setblocking(0) if source is not None: s.bind(source) expiration = _compute_expiration(lifetime) _connect(s, destination) l = len(wire) if use_udp: _wait_for_writable(s, expiration) s.send(wire) else: tcpmsg = struct.pack("!H", l) + wire _net_write(s, tcpmsg, expiration) done = False delete_mode = True expecting_SOA = False soa_rrset = None soa_count = 0 if relativize: origin = zone oname = name.empty else: origin = None oname = zone tsig_ctx = None first = True while not done: mexpiration = _compute_expiration(timeout) if mexpiration is None or mexpiration > expiration: mexpiration = expiration if use_udp: _wait_for_readable(s, expiration) (wire, from_address) = s.recvfrom(65535) else: ldata = _net_read(s, 2, mexpiration) (l, ) = struct.unpack("!H", ldata) wire = _net_read(s, l, mexpiration) r = message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, xfr=True, origin=origin, tsig_ctx=tsig_ctx, multi=True, first=first, one_rr_per_rrset=(rdtype == rdatatype.IXFR)) tsig_ctx = r.tsig_ctx first = False answer_index = 0 if soa_rrset is None: if not r.answer or r.answer[0].name != oname: raise exception.FormError("No answer or RRset not for qname") rrset = r.answer[0] if rrset.rdtype != rdatatype.SOA: raise exception.FormError("first RRset is not an SOA") answer_index = 1 soa_rrset = rrset.copy() if rdtype == rdatatype.IXFR: if soa_rrset[0].serial <= serial: # # We're already up-to-date. # done = True else: expecting_SOA = True # # Process SOAs in the answer section (other than the initial # SOA in the first message). # for rrset in r.answer[answer_index:]: if done: raise exception.FormError("answers after final SOA") if rrset.rdtype == rdatatype.SOA and rrset.name == oname: if expecting_SOA: if rrset[0].serial != serial: raise exception.FormError("IXFR base serial mismatch") expecting_SOA = False elif rdtype == rdatatype.IXFR: delete_mode = not delete_mode # # If this SOA RRset is equal to the first we saw then we're # finished. If this is an IXFR we also check that we're seeing # the record in the expected part of the response. # if rrset == soa_rrset and \ (rdtype == rdatatype.AXFR or \ (rdtype == rdatatype.IXFR and delete_mode)): done = True elif expecting_SOA: # # We made an IXFR request and are expecting another # SOA RR, but saw something else, so this must be an # AXFR response. # rdtype = rdatatype.AXFR expecting_SOA = False if done and q.keyring and not r.had_tsig: raise exception.FormError("missing TSIG") yield r s.close()