def __init__(self, cname): """Create RecordData for CNAME type. Args: cname (Name): cname. """ self.cname = Name(cname)
def process_zone_file(origin, zonetext, owner, overwrite=False): """Imports zone to the database. No checks for existence are performed in this file. For form processing, see the ``import_zone_view`` view. """ if origin: origin = Name((origin.rstrip('.') + '.').split('.')) else: origin = None zonetext = clean_zonefile(str(zonetext)) try: zone = dns.zone.from_text(zonetext, origin=origin, relativize=False) if not str(zone.origin).rstrip('.'): raise UnknownOrigin process_and_import_zone_data(zone, owner, overwrite) except NoSOA: raise Exception('The zone has no SOA RR at its origin') except NoNS: raise Exception('The zone has no NS RRset at its origin') except UnknownOrigin: raise Exception('The zone\'s origin is unknown') except BadZone: raise Exception('The zone is malformed') except DNSException, e: #raise Exception(str(e)) raise Exception('The zone is malformed')
def process_axfr_response(origin, nameserver, owner, overwrite=False): """ origin: string domain name nameserver: IP of the DNS server """ origin = Name((origin.rstrip('.') + '.').split('.')) axfr_query = dns.query.xfr(nameserver, origin, timeout=5, relativize=False, lifetime=10) try: zone = dns.zone.from_xfr(axfr_query, relativize=False) if not str(zone.origin).rstrip('.'): raise UnknownOrigin process_and_import_zone_data(zone, owner, overwrite) except NoSOA: raise Exception('The zone has no SOA RR at its origin') except NoNS: raise Exception('The zone has no NS RRset at its origin') except UnknownOrigin: raise Exception('The zone\'s origin is unknown') except BadZone: raise Exception('The zone is malformed') except DNSException, e: if not str(e): raise Exception('Transfer Failed') raise Exception(str(e))
def process_zone_file(origin, zonetext, owner, overwrite=False): """Imports zone to the database. No checks for existence are performed in this file. For form processing, see the ``import_zone_view`` view. """ if origin: origin = Name((origin.rstrip('.') + '.').split('.')) else: origin = None zonetext = clean_zonefile(str(zonetext)) try: zone = dns.zone.from_text(zonetext, origin=origin, relativize=False) if not str(zone.origin).rstrip('.'): raise UnknownOrigin process_and_import_zone_data(zone, owner, overwrite) except NoSOA: raise Exception('The zone has no SOA RR at its origin') except NoNS: raise Exception('The zone has no NS RRset at its origin') except UnknownOrigin: raise Exception('The zone\'s origin is unknown') except BadZone: raise Exception('The zone is malformed') except DNSException as e: #raise Exception(str(e)) raise Exception('The zone is malformed')
def process_axfr_response(origin, nameserver, owner, overwrite=False): """ origin: string domain name nameserver: IP of the DNS server """ origin = Name((origin.rstrip('.') + '.').split('.')) axfr_query = dns.query.xfr(nameserver, origin, timeout=5, relativize=False, lifetime=10) try: zone = dns.zone.from_xfr(axfr_query, relativize=False) if not str(zone.origin).rstrip('.'): raise UnknownOrigin process_and_import_zone_data(zone, owner, overwrite) except NoSOA: raise Exception('The zone has no SOA RR at its origin') except NoNS: raise Exception('The zone has no NS RRset at its origin') except UnknownOrigin: raise Exception('The zone\'s origin is unknown') except BadZone: raise Exception('The zone is malformed') except DNSException as e: if not str(e): raise Exception('Transfer Failed') raise Exception(str(e))
def test_authority_domain(self): question = Question(Name("server1.gumpe"), Type.A, Class.IN) header = Header(1337, 0, 1, 0, 0, 0) query = Message(header, questions=[question]) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.sendto(query.to_bytes(), (SERVER, PORT)) data = s.recv(512) s.close() message = Message.from_bytes(data) self.assertCountEqual(message.answers, [ ResourceRecord( name=Name("server1.gumpe."), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("10.0.1.5"), ), ResourceRecord( name=Name("server1.gumpe."), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("10.0.1.4"), ), ])
def test_question_from_bytes(self, MockName): packet = b"\x07example\x03com\x00\x00\x02\x00\x01" MockName.from_bytes.return_value = (Name("example.com"), 13) question1, offset = Question.from_bytes(packet, 0) question2 = Question(Name("example.com"), Type.NS, Class.IN) self.assertEqual(question1, question2) self.assertEqual(offset, 17) MockName.from_bytes.assert_called_with(packet, 0)
def test_resource_from_bytes(self, MockName, MockRData): MockName.from_bytes.return_value = (Name("example.com"), 13) MockRData.create_from_bytes.return_value = ARecordData("1.1.1.1") packet = (b"\x07example\x03com\x00\x00\x01\x00\x02\x00\x00\x00\x03\x00" b"\x04\x01\x01\x01\x01") record1, offset = ResourceRecord.from_bytes(packet, 0) record2 = ResourceRecord(Name("example.com"), Type.A, Class.CS, 3, ARecordData("1.1.1.1")) self.assertEqual(record1, record2) MockName.from_bytes.assert_called_with(packet, 0) MockRData.create_from_bytes.assert_called_with(Type.A, packet, 23, 4)
def test_invalid_domain_from_cache(self): cache = RecordCache(0) record = ResourceRecord( name=Name("bonobo.putin"), type_=Type.A, class_=Class.IN, ttl=60, rdata=ARecordData("1.0.0.1"), ) cache.add_record(record) self.assertEqual(cache.lookup(Name("bonobo.putin"), Type.A, Class.IN), record)
def test_expired_cache_entry(self): cache = RecordCache(0) record = ResourceRecord( name=Name("bonobo.putin"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("1.0.0.1"), ) cache.add_record(record) self.assertEqual(cache.lookup(Name("bonobo.putin"), Type.A, Class.IN), None)
def test_ttl_overwrite(self): cache = RecordCache(60) record = ResourceRecord( name=Name("bonobo.putin"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("1.0.0.1"), ) cache.add_record(record) cache_entry = cache.lookup(Name("bonobo.putin"), Type.A, Class.IN) self.assertEqual(cache_entry, record) self.assertEqual(cache_entry.ttl, 60)
def matchByLabel(self, dname, type_, class_): """ Args: dname (Name): domain name type_ (Type): type class_ (Class): class """ dname = Name(dname) while dname.labels: rrs = self.lookup(str(dname), type_, class_) if rrs: return rrs dname.labels = dname.labels[1:] return []
def from_bytes(cls, packet, offset, rdlength): """Create a RecordData object from bytes. Args: packet (bytes): packet. offset (int): offset in message. rdlength (int): length of rdata. """ mname, offset = Name.from_bytes(packet, offset) rname, offset = Name.from_bytes(packet, offset) serial = struct.unpack_from("!I", packet, offset)[0] refresh = struct.unpack_from("!i", packet, offset + 4)[0] retry = struct.unpack_from("!i", packet, offset + 8)[0] expire = struct.unpack_from("!i", packet, offset + 12)[0] minimum = struct.unpack_from("!I", packet, offset + 16)[0] return cls(mname, rname, serial, refresh, retry, expire, minimum), offset + 20
def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) question = Question(Name("google.com."), Type.A, Class.IN) header = Header(9001, 0, 1, 0, 0, 0) header.qr = 0 # 0 for query header.opcode = 0 # standad query header.rd = 1 # recursive query = Message(header, [question]) ip = (([ ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.") ] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) + ["no IP found"])[0] sock.sendto(query.to_bytes(), (ip, 53)) data = sock.recv(1024) mess = Message.from_bytes(data) """ answer = ResourceRecord.to_dict(mess.answers[0]) auth = ResourceRecord.to_dict(mess.authorities[0]) addi = ResourceRecord.to_dict(mess.additionals[0]) """ rrs = [] rrs += mess.answers + mess.authorities + mess.additionals for r in rrs: print("R", r.to_dict())
def read_master_file(self, filename): """Read the zone from a master file See section 5 of RFC 1035. Args: filename (str): the filename of the master file """ records = re.finditer( Zone.record_re, open(filename, "r").read(), flags=re.MULTILINE ) for r in records: domain, ttl, class_, type_, rdata = r.groups() domain = domain or self.last_domain self.last_domain = domain record = ResourceRecord( name=Name(domain), type_=Type[type_], class_=Class[class_], ttl=int(ttl) if ttl is not None else Zone.default_ttl, rdata=RecordData.create_from_str(Type[type_], rdata) ) if domain in self.records: self.records[domain].append(record) else: self.records[domain] = [record]
def load_and_parse(self, content): content = re.sub(re.compile(";.*?\n"), "\n", content) #Remove comments content = re.sub(re.compile("\n\n*\n"), "\n", content) #Remove whitespaces content = re.sub(re.compile(" * "), " ", content) #Remove multiple spaces between words content = re.sub(re.compile("\t\t*\t"), " ", content) #Remove tabs between words content = re.sub(re.compile("\n *"), "\n", content) #Remove spaces at start of line content = re.sub(re.compile("\n\t*"), "\n", content) #Remove tabs at start of line content = re.compile(r'\(.*?\)', re.DOTALL)\ .sub(lambda x: x.group().replace('\n', ''), content) #Remove newlines between () content = re.sub(re.compile("\t+"), " ", content) #Remove tabs between words default_ttl = None origin = None recordSet = [] for line in content.split('\n'): if line[:4] == "$TTL": prev_ttl = self.time_to_seconds(line[4:].strip()) elif line[:7] == "$ORIGIN": origin = line[7:].strip() elif "SOA" not in line: parts = line.split(' ') rr_name = parts[0] rr_ttl = self.time_to_seconds(parts[1]) offset = int(rr_ttl == 0) if offset: rr_ttl = prev_ttl rr_class = Class[parts[1 + offset]] rr_type = parts[2 + offset] if Type[rr_type] == Type.CNAME or Type[rr_type] == Type.NS: rr_data = RecordData.create( Type[rr_type], Name(parts[3 + offset].rstrip('.'))) else: rr_data = RecordData.create(Type[rr_type], parts[3 + offset].rstrip('.')) self.add_node( rr_name, ResourceRecord(Name(rr_name), Type[rr_type], rr_class, rr_ttl, rr_data))
def test_outside_zone(self): question = Question(Name("gaia.cs.umass.edu"), Type.A, Class.IN) header = Header(1337, 0, 1, 0, 0, 0) header.rd = 1 query = Message(header, questions=[question]) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.sendto(query.to_bytes(), (SERVER, PORT)) data = s.recv(512) s.close() message = Message.from_bytes(data) self.assertEqual(message.answers, [ ResourceRecord(name=Name("gaia.cs.umass.edu"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("128.119.245.12")), ])
def test_name_to_bytes_compress4(self): name1 = Name("www.example.com") name2 = Name("example.com") compress = {} name1.to_bytes(0, compress) self.assertEqual(name2.to_bytes(17), b"\x07example\x03com\x00")
def test_name_to_bytes_compress5(self): name1 = Name("www.example.com") name2 = Name("WWW.example.com") compress = {} name1.to_bytes(0, compress) self.assertEqual(name2.to_bytes(17, compress), b"\xc0\x00")
def DnsName(*args): lst = [] for arg in args: if isinstance(arg, list): lst.extend(arg) else: lst.extend(arg.split('.')) return Name(lst)
def from_bytes(cls, packet, offset): """Convert ResourceRecord from bytes.""" name, offset = Name.from_bytes(packet, offset) type_ = Type(struct.unpack_from("!H", packet, offset)[0]) class_ = Class(struct.unpack_from("!H", packet, offset + 2)[0]) ttl, rdlength = struct.unpack_from("!iH", packet, offset + 4) offset += 10 rdata = RecordData.create_from_bytes(type_, packet, offset, rdlength) offset += rdlength return cls(name, type_, class_, ttl, rdata), offset
def from_bytes(cls, packet, offset, rdlength): """Create a RecordData object from bytes. Args: packet (bytes): packet. offset (int): offset in message. rdlength (int): length of rdata. """ nsdname, offset = Name.from_bytes(packet, offset) return cls(nsdname)
def test_concurrent_requests(self): queries = [] answers = [] question = Question(Name("gaia.cs.umass.edu"), Type.A, Class.IN) header = Header(1337, 0, 1, 0, 0, 0) header.rd = 1 queries.append(Message(header, questions=[question])) answers.append([ ResourceRecord(name=Name("gaia.cs.umass.edu"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("128.119.245.12")), ]) question = Question(Name("server2.gumpe"), Type.A, Class.IN) header = Header(420, 0, 1, 0, 0, 0) queries.append(Message(header, questions=[question])) answers.append([ ResourceRecord(name=Name("server2.gumpe"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("10.0.1.7")), ]) header = Header(69, 0, 1, 0, 0, 0) question = Question(Name("www.gumpe"), Type.A, Class.IN) queries.append(Message(header, questions=[question])) answers.append([ ResourceRecord(name=Name("www.gumpe"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("10.0.1.7")), ResourceRecord(name=Name("www.gumpe"), type_=Type.CNAME, class_=Class.IN, ttl=0, rdata=CNAMERecordData(Name("server2.gumpe"))), ]) sockets = [ socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for _ in range(len(queries)) ] for i in range(len(sockets)): sockets[i].sendto(queries[i].to_bytes(), (SERVER, PORT)) responses = [] for i in range(len(sockets)): responses.append(Message.from_bytes(sockets[i].recv(1024))) for i in range(len(sockets)): sockets[i].close() for i in range(len(queries)): self.assertCountEqual(responses[i].answers, answers[i])
def check_cache(self, hostname): iplist = self.cache.lookup(Name(hostname), Type.A, Class.IN) namelist = self.cache.lookup(Name(hostname), Type.CNAME, Class.IN) if not (iplist == [] and namelist == []): iplist = [x.rdata.address for x in iplist] namelist = [x.rdata.cname for x in namelist] return True, iplist, namelist hostname = hostname.split('.') for i in range(len(hostname)): test = '.'.join(hostname[i:]) namelist = self.cache.lookup(Name(test), Type.NS, Class.IN) namelist = [x.rdata.nsdname for x in namelist] if not namelist == []: for x in namelist: newips = self.cache.lookup(x, Type.A, Class.IN) iplist.extend(newips) iplist = [x.rdata.address for x in iplist] return False, iplist, namelist return False, [], []
def DnsAbsName(*args): lst = [] for arg in args: if isinstance(arg, list): lst.extend(arg) else: lst.extend(arg.split('.')) if lst[-1]: lst.append("") # make it an absolute name return Name(lst)
def test_invalid_domain_from_cache(self): cache = RecordCache(0) resolver = Resolver(5, cache) cache.add_record( ResourceRecord( name=Name("bonobo.putin"), type_=Type.A, class_=Class.IN, ttl=60, rdata=ARecordData("1.0.0.1"), )) cache.add_record( ResourceRecord( name=Name("bonobo.putin"), type_=Type.CNAME, class_=Class.IN, ttl=60, rdata=CNAMERecordData(Name("putin.bonobo")), )) self.assertEqual(resolver.gethostbyname("bonobo.putin"), ("bonobo.putin", ["putin.bonobo."], ["1.0.0.1"]))
def test_expired_cache_entry(self): cache = RecordCache(0) resolver = Resolver(5, cache) cache.add_record( ResourceRecord( name=Name("hw.gumpe"), type_=Type.A, class_=Class.IN, ttl=0, rdata=ARecordData("1.0.0.2"), )) cache.add_record( ResourceRecord( name=Name("hw.gumpe"), type_=Type.CNAME, class_=Class.IN, ttl=0, rdata=CNAMERecordData(Name("gumpe.hw")), )) self.assertEqual(resolver.gethostbyname("hw.gumpe"), ("hw.gumpe", [], []))
def mock_resolver(_, record_type): if record_type == 'SRV': return [ SRV(0, 0, 0, 0, 27017, Name(labels=hostname)) for hostname in [ b'mongo1.example.com'.split( b'.'), b'mongo2.example.com'.split(b'.'), b'mongo3.example.com'.split(b'.') ] ] elif record_type == 'TXT': return [TXT(0, 0, [b'replicaSet=rs0'])]
def send_query(sock, hostname, ip): # Create and send query question = Question(Name(hostname), Type.A, Class.IN) header = Header(randint(0, 2**16), 0, 1, 0, 0, 0) header.qr = 0 header.opcode = 0 header.rd = 0 # no recursion desired query = Message(header, [question]) sock.sendto(query.to_bytes(), (ip, 53)) # Receive response data = sock.recv(512) return Message.from_bytes(data)
def mock_resolver(_, rdtype, rdclass=None, lifetime=None, **kwargs): if rdtype == 'SRV': return [ SRV(0, 0, 0, 0, 27017, Name(labels=hostname)) for hostname in [ b'mongo1.example.com'.split( b'.'), b'mongo2.example.com'.split(b'.'), b'mongo3.example.com'.split(b'.') ] ] elif rdtype == 'TXT': return [TXT(0, 0, [b'replicaSet=rs0'])]
def query_recursive(self, sock, hostname, ip): if self.cache is not None: answer = [] record = self.cache.lookup(Name(hostname), Type.A, Class.IN) alias = self.cache.lookup(Name(hostname), Type.CNAME, Class.IN) if record is not None: answer.append(record) if alias is not None: answer.append(alias) if len(answer): return answer response = Resolver.send_query(sock, hostname, ip) if (response.header.an_count > 0 or response.header.rcode != 0): if self.cache is not None: self.cache.add_records(response.answers) return response.answers ips = [] for record in response.additionals: if record.type_ is Type.A: ips.append(record.rdata.address) if self.cache is not None: self.cache.add_record(record) if record.type_ is Type.CNAME and self.cache is not None: self.cache.add_record(record) if len(ips) == 0: for record in response.authorities: ipaddrlist = self.gethostbyname(record.rdata.nsdname)[2] for new_ip in ipaddrlist: res = self.query_recursive(sock, hostname, new_ip) if res is not None: return res for new_ip in ips: res = self.query_recursive(sock, hostname, new_ip) if res is not None: return res return []
class CNAMERecordData(RecordData): """Record data for CNAME type.""" def __init__(self, cname): """Create RecordData for CNAME type. Args: cname (Name): cname. """ self.cname = Name(cname) def __str__(self): return "Canonical Name: {}".format(self.cname) def __eq__(self, other): return (isinstance(other, CNAMERecordData) and self.cname == other.cname) def to_bytes(self, offset, compress): """Convert to bytes. Args: offset (int): offset in packet. compress (dict): dict from domain names to pointers. """ return self.cname.to_bytes(offset, compress) @classmethod def from_bytes(cls, packet, offset, rdlength): """Create a RecordData object from bytes. Args: packet (bytes): packet. offset (int): offset in message. rdlength (int): length of rdata. """ cname, offset = Name.from_bytes(packet, offset) return cls(cname) def to_dict(self): """Convert to dict.""" return {"cname": str(self.cname)} @classmethod def from_dict(cls, dct): """Create a RecordData object from dict.""" return cls(Name(dct["cname"]))
def pdnssec_hash_zone_record(zone_name, record_name): """Generates hash for ordername field in NSEC3 non-narrow mode. ***** Special kudos to the folks at the #powerdns IRC channel for the help, especially Maik for pointing me to dns.name.Name.to_digestable() method. ***** Notes from RFCs for easy reference ---------------------------------- NSEC3PARAM Presentation Format: http://tools.ietf.org/html/rfc5155#section-4.3 4.3. Presentation Format The presentation format of the RDATA portion is as follows: - The Hash Algorithm field is represented as an unsigned decimal integer. The value has a maximum of 255. - The Flags field is represented as an unsigned decimal integer. The value has a maximum value of 255. - The Iterations field is represented as an unsigned decimal integer. The value is between 0 and 65535, inclusive. - The Salt Length field is not represented. - The Salt field is represented as a sequence of case-insensitive hexadecimal digits. Whitespace is not allowed within the sequence. This field is represented as "-" (without the quotes) when the Salt Length field is zero. 5. Calculation of the Hash http://tools.ietf.org/html/rfc5155#section-5 The hash calculation uses three of the NSEC3 RDATA fields: Hash Algorithm, Salt, and Iterations. Define H(x) to be the hash of x using the Hash Algorithm selected by the NSEC3 RR, k to be the number of Iterations, and || to indicate concatenation. Then define: IH(salt, x, 0) = H(x || salt), and IH(salt, x, k) = H(IH(salt, x, k-1) || salt), if k > 0 Then the calculated hash of an owner name is IH(salt, owner name, iterations), where the owner name is in the canonical form, defined as: The wire format of the owner name where: 1. The owner name is fully expanded (no DNS name compression) and fully qualified; 2. All uppercase US-ASCII letters are replaced by the corresponding lowercase US-ASCII letters; 3. If the owner name is a wildcard name, the owner name is in its original unexpanded form, including the "*" label (no wildcard substitution); This form is as defined in Section 6.2 of [RFC4034]. The method to calculate the Hash is based on [RFC2898]. "DNSSEC NSEC3 Hash Algorithms". The initial contents of this registry are: 0 is Reserved. 1 is SHA-1. 2-255 Available for assignment. """ Domain = get_model('powerdns_manager', 'Domain') DomainMetadata = get_model('powerdns_manager', 'DomainMetadata') the_domain = Domain.objects.get(name__exact=zone_name) nsec3param = DomainMetadata.objects.get(domain=the_domain, kind='NSEC3PARAM') algo, flags, iterations, salt = nsec3param.content.split() # dns.name.NAME expects an absolute name (with trailing dot) record_name = '%s.' % record_name.rstrip('.') record_name = Name(record_name.split('.')) # Prepare salt salt = salt.decode('hex') hashed_name = sha1hash(record_name.to_digestable(), salt) i = 0 while i < int(iterations): hashed_name = sha1hash(hashed_name, salt) i += 1 # Do standard base32 encoding final_data = base64.b32encode(hashed_name) # Apply the translation table to convert to base32hex encoding. final_data = final_data.translate(b32_to_ext_hex) # Return lower case representation as required by PowerDNS return final_data.lower()
def generate_zone_file(origin): """Generates a zone file. Accepts the zone origin as string (no trailing dot). Returns the contents of a zone file that contains all the resource records associated with the domain with the provided origin. """ Domain = get_model('powerdns_manager', 'Domain') Record = get_model('powerdns_manager', 'Record') the_domain = Domain.objects.get(name__exact=origin) the_rrs = Record.objects.filter(domain=the_domain).order_by('-type') # Generate the zone file origin = Name((origin.rstrip('.') + '.').split('.')) # Create an empty dns.zone object. # We set check_origin=False because the zone contains no records. zone = dns.zone.from_text('', origin=origin, relativize=False, check_origin=False) rdclass = dns.rdataclass._by_text.get('IN') for rr in the_rrs: # Add trailing dot to rr.name record_name = rr.name.rstrip('.') + '.' if rr.type == 'SOA': # Add SOA Resource Record # SOA content: primary hostmaster serial refresh retry expire default_ttl bits = rr.content.split() # Primary nameserver of SOA record primary = bits[0].rstrip('.') + '.' mname = Name(primary.split('.')) # Responsible hostmaster from SOA record hostmaster = bits[1].rstrip('.') + '.' rname = Name(hostmaster.split('.')) rdtype = dns.rdatatype._by_text.get('SOA') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.ANY.SOA.SOA(rdclass, rdtype, mname = mname, rname = rname, serial = int(bits[2]), refresh = int(bits[3]), retry = int(bits[4]), expire = int(bits[5]), minimum = int(bits[6]) ) rdataset.add(rdata, ttl=int(rr.ttl)) elif rr.type == 'NS': # Add NS Resource Record rdtype = dns.rdatatype._by_text.get('NS') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.ANY.NS.NS(rdclass, rdtype, target = Name((rr.content.rstrip('.') + '.').split('.')) ) rdataset.add(rdata, ttl=int(rr.ttl)) elif rr.type == 'MX': # Add MX Resource Record rdtype = dns.rdatatype._by_text.get('MX') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.ANY.MX.MX(rdclass, rdtype, preference = int(rr.prio), exchange = Name((rr.content.rstrip('.') + '.').split('.')) ) rdataset.add(rdata, ttl=int(rr.ttl)) elif rr.type == 'TXT': # Add TXT Resource Record rdtype = dns.rdatatype._by_text.get('TXT') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.ANY.TXT.TXT(rdclass, rdtype, strings = [rr.content.strip('"')] ) rdataset.add(rdata, ttl=int(rr.ttl)) elif rr.type == 'CNAME': # Add CNAME Resource Record rdtype = dns.rdatatype._by_text.get('CNAME') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.ANY.CNAME.CNAME(rdclass, rdtype, target = Name((rr.content.rstrip('.') + '.').split('.')) ) rdataset.add(rdata, ttl=int(rr.ttl)) elif rr.type == 'A': # Add A Resource Record rdtype = dns.rdatatype._by_text.get('A') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.IN.A.A(rdclass, rdtype, address = rr.content ) rdataset.add(rdata, ttl=int(rr.ttl)) elif rr.type == 'AAAA': # Add AAAA Resource Record rdtype = dns.rdatatype._by_text.get('AAAA') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.IN.AAAA.AAAA(rdclass, rdtype, address = rr.content ) rdataset.add(rdata, ttl=int(rr.ttl)) elif rr.type == 'SPF': # Add SPF Resource Record rdtype = dns.rdatatype._by_text.get('SPF') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.ANY.SPF.SPF(rdclass, rdtype, strings = [rr.content.strip('"')] ) rdataset.add(rdata, ttl=int(rr.ttl)) elif rr.type == 'PTR': # Add PTR Resource Record rdtype = dns.rdatatype._by_text.get('PTR') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.ANY.PTR.PTR(rdclass, rdtype, target = Name((rr.content.rstrip('.') + '.').split('.')) ) rdataset.add(rdata, ttl=int(rr.ttl)) elif rr.type == 'SRV': # Add SRV Resource Record # weight port target weight, port, target = rr.content.split() rdtype = dns.rdatatype._by_text.get('SRV') rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True) rdata = dns.rdtypes.IN.SRV.SRV(rdclass, rdtype, priority = int(rr.prio), weight = int(weight), port = int(port), target = Name((target.rstrip('.') + '.').split('.')) ) rdataset.add(rdata, ttl=int(rr.ttl)) # Export text (from the source code of http://www.dnspython.org/docs/1.10.0/html/dns.zone.Zone-class.html#to_file) EOL = '\n' f = StringIO.StringIO() f.write('$ORIGIN %s%s' % (origin, EOL)) zone.to_file(f, sorted=True, relativize=False, nl=EOL) data = f.getvalue() f.close() return data