def make_update(action, query): hostname = query.hostname.encode('ascii') D = dns.name.from_text(domain) H = dns.name.from_text(query.hostname) if H.is_subdomain(D): R = H.relativize(D) else: return "400 NOTAUTH %s\n" % H.to_text() keyring, algo = read_session_key("/etc/bind/keys/webapp.key") update = dns.update.Update(D, keyring=keyring, keyalgorithm=algo) if action == 'update': update.present(R, 'a') update.replace(R, 300, 'a', query.ip.encode('ascii')) elif action == 'delete': update.present(R, 'a') update.delete(R, 'a') elif action == 'add': update.absent(R, 'a') update.add(R, 300, 'a', query.ip.encode('ascii')) response = dns.query.tcp(update, '127.0.0.1') if response.rcode() == 0: return "NOERROR %s\n" % H.to_text() else: return "%s %s\n" % (dns.rcode.to_text(response.rcode()), H.to_text())
def record_exists(self): update = dns.update.Update(self.zone, keyring=self.keyring, keyalgorithm=self.algorithm) try: update.present(self.record, self.type) except dns.rdatatype.UnknownRdatatype as ee: self.module.fail_json(msg='Record error: {}'.format(str(ee))) response = self.__do_update(update) if dns.message.Message.rcode(response) == 0: if self.state == 'absent': return True try: update.present(self.record, self.type, self.value) except AttributeError: self.module.fail_json(msg='value needed when state=present') except dns.exception.SyntaxError: self.module.fail_json(msg='Invalid/malformed value') response = self.__do_update(update) if dns.message.Message.rcode(response) == 0: return True else: return 2 else: return False
def record_exists(self): update = dns.update.Update(self.zone, keyring=self.keyring, keyalgorithm=self.algorithm) try: update.present(self.module.params['record'], self.module.params['type']) except dns.rdatatype.UnknownRdatatype as e: self.module.fail_json(msg='Record error: {0}'.format(to_native(e))) response = self.__do_update(update) self.dns_rc = dns.message.Message.rcode(response) if self.dns_rc == 0: if self.module.params['state'] == 'absent': return 1 for entry in self.module.params['value']: try: update.present(self.module.params['record'], self.module.params['type'], entry) except AttributeError: self.module.fail_json(msg='value needed when state=present') except dns.exception.SyntaxError: self.module.fail_json(msg='Invalid/malformed value') response = self.__do_update(update) self.dns_rc = dns.message.Message.rcode(response) if self.dns_rc == 0: return 1 else: return 2 else: return 0
def record_exists(self): update = dns.update.Update(self.zone, keyring=self.keyring, keyalgorithm=self.algorithm) try: update.present(self.module.params['record'], self.module.params['type']) except dns.rdatatype.UnknownRdatatype as e: self.module.fail_json(msg='Record error: {0}'.format(to_native(e))) response = self.__do_update(update) self.dns_rc = dns.message.Message.rcode(response) if self.dns_rc == 0: if self.module.params['state'] == 'absent': return 1 for entry in self.value: try: update.present(self.module.params['record'], self.module.params['type'], entry) except AttributeError: self.module.fail_json( msg='value needed when state=present') except dns.exception.SyntaxError: self.module.fail_json(msg='Invalid/malformed value') response = self.__do_update(update) self.dns_rc = dns.message.Message.rcode(response) if self.dns_rc == 0: if self.ttl_changed(): return 2 else: return 1 else: return 2 else: return 0
def delete_a(self): try: update = dns.update.Update(self.get_domain_fqdn(), keyring=self.__keyring) update.present(self.__hostname, 'TXT', self.__txt_value) update.delete(self.__hostname, 'A') return dns.query.tcp(update, self.__server_ip).rcode() except Exception, error: raise Exception("Error deleting DNS A Entry: %s" % (error))
def update_a(self): try: update = dns.update.Update(self.get_domain_fqdn(), keyring=self.__keyring) # Só realiza o update se tiver uma entrada TXT e o valor dela bater com o calculado pelo servidor update.present(self.__hostname, 'TXT', self.__txt_value) update.replace(self.__hostname, self.__ttl, 'A', self.__ip) return dns.query.tcp(update, self.__server_ip).rcode() except Exception, error: raise Exception("Error updating DNS A Entry: %s" % (error))
def delete_rev(self): try: name = self.validate_rev_name() if not name: return None update = dns.update.Update(self.get_rev_domain_fqdn(), keyring=self.__keyring) update.present(name, 'PTR', self.get_fqdn()) update.delete(name, 'PTR') return dns.query.tcp(update, self.__server_ip).rcode() except Exception, error: raise Exception("Error deleting DNS PTR Entry: %s" % (error))
def _prepare_dns_updates(add_rrsets, delete_rrsets, my_zones): """Prepare a set of DNS updates for the specified rrset additions and deletions. One update will be created for each zone mentioned in the rrsets. Constrints will be added to the DNS update message: * when deleting the record, ensure that it existed * when adding a record, ensure that it did not exist * when modifying (deleting and readding) a record, ensure that it existed Returns a dict mapping zone names to dnspython Update objects. """ updates = {} for rrset in delete_rrsets: zone = _get_zone(rrset.name, my_zones) # Create a new update for this zone if necessary if zone not in updates: updates[zone] = dns.update.Update(zone, keyring=_create_keyring(zone)) update = updates[zone] # Require the record exist before deleting it. update.present(rrset.name, *rrset.items) # Delete the record. update.delete(rrset.name, rrset) for rrset in add_rrsets: zone = _get_zone(rrset.name, my_zones) # Create a new update for this zone if necessary if zone not in updates: updates[zone] = dns.update.Update(zone, keyring=_create_keyring(zone)) update = updates[zone] # For additions only, require that the record not exist before adding # it. We're processing each modification as a delete/add pair, so # it will exist before the update (and we ensure this above). if rrset.name not in [delete.name for delete in delete_rrsets]: update.absent(rrset.name, rrset.rdtype) update.add(rrset.name, rrset) return updates
def record_exists(self): update = dns.update.Update(self.zone, keyring=self.keyring) update.present(self.record, self.type) try: response = dns.query.tcp(update, self.server, timeout=10) if dns.message.Message.rcode(response) == 0: update.present(self.record, self.type, self.value) response = dns.query.tcp(update, self.server, timeout=10) if dns.message.Message.rcode(response) == 0: return True else: return 2 else: return False except: self.module.fail_json(msg='Connection to DNS server failed')
def record_exists(self): update = dns.update.Update(self.zone, keyring=self.keyring) update.present(self.record, self.type) try: response = dns.query.tcp(update, self.server, timeout=10) if dns.message.Message.rcode(response) == 0: update.present(self.record, self.type, self.value) response = dns.query.tcp(update, self.server, timeout=10) if dns.message.Message.rcode(response) == 0: return True else: return 2 else: return False except: self.module.fail_json(msg="Connection to DNS server failed")
def generate_update_from_diff(zonename, added, removed, oldsoa, keyring, keyalgo, force_conflicts): update = dns.update.Update(zonename, keyring=keyring, keyalgorithm=keyalgo) if (not force_conflicts): # Require the old SOA to still be present # (Essentially requires that the zone hasn't changed while editing) update.present(oldsoa[0], oldsoa[2]) for (name, ttl, rdata) in removed: update.delete(name, rdata) for (name, ttl, rdata) in added: update.add(name, ttl, rdata) return update
def generate_update_from_diff(zonename, original_zone, updated_zone, keyring, keyalgo, force_conflicts): update = dns.update.Update(zonename, keyring = keyring, keyalgorithm = keyalgo) if (not force_conflicts): # Require the old SOA to still be present # (Essentially requires that the zone hasn't changed while editing) oldsoa = get_single_record(original_zone.iterate_rdatas(), dns.rdatatype.SOA) update.present(oldsoa[0], oldsoa[2]) added, removed = get_zone_diff(original_zone, updated_zone) for (name, ttl, rdata) in removed: update.delete(name, rdata) for (name, ttl, rdata) in added: update.add(name, ttl, rdata) return [update, len(added), len(removed)]
def test_to_wire1(self): # type: () -> None update = dns.update.Update('example') update.id = 1 update.present('foo') update.present('foo', 'a') update.present('bar', 'a', '10.0.0.5') update.absent('blaz2') update.absent('blaz2', 'a') update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') update.add('bar', 300, 'a', '10.0.0.3') update.delete('bar', 'a', '10.0.0.4') update.delete('blaz', 'a') update.delete('blaz2') self.assertTrue(update.to_wire() == goodwire)
def test_to_wire3(self): update = dns.update.Update('example') update.id = 1 update.present('foo') update.present('foo', 'a') update.present('bar', 'a', '10.0.0.5') update.absent('blaz2') update.absent('blaz2', 'a') update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') update.add('bar', dns.rdataset.from_text(1, 1, 300, '10.0.0.3')) update.delete('bar', 'a', '10.0.0.4') update.delete('blaz', 'a') update.delete('blaz2') self.failUnless(update.to_wire() == goodwire)
def test_to_wire3(self): # type: () -> None update = dns.update.Update('example') update.id = 1 update.present('foo') update.present('foo', 'a') update.present('bar', 'a', '10.0.0.5') update.absent('blaz2') update.absent('blaz2', 'a') update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') update.add('bar', dns.rdataset.from_text(1, 1, 300, '10.0.0.3')) update.delete('bar', 'a', '10.0.0.4') update.delete('blaz', 'a') update.delete('blaz2') self.assertEqual(update.to_wire(), goodwire)
def test_to_wire1(self): # type: () -> None update = dns.update.Update('example') update.id = 1 update.present('foo') update.present('foo', 'a') update.present('bar', 'a', '10.0.0.5') update.absent('blaz2') update.absent('blaz2', 'a') update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') update.add('bar', 300, 'a', '10.0.0.3') update.delete('bar', 'a', '10.0.0.4') update.delete('blaz', 'a') update.delete('blaz2') self.failUnless(update.to_wire() == goodwire)
def test_to_wire1(self): update = dns.update.Update('example') update.id = 1 update.present('foo') update.present('foo', 'a') update.present('bar', 'a', '10.0.0.5') update.absent('blaz2') update.absent('blaz2', 'a') update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') update.add('bar', 300, 'a', '10.0.0.3') update.delete('bar', 'a', '10.0.0.4') update.delete('blaz', 'a') update.delete('blaz2') self.failUnless(update.to_wire() == goodwire)
def test_to_wire3(self): # type: () -> None update = dns.update.Update("example") update.id = 1 update.present("foo") update.present("foo", "a") update.present("bar", "a", "10.0.0.5") update.absent("blaz2") update.absent("blaz2", "a") update.replace("foo", 300, "a", "10.0.0.1", "10.0.0.2") update.add("bar", dns.rdataset.from_text(1, 1, 300, "10.0.0.3")) update.delete("bar", "a", "10.0.0.4") update.delete("blaz", "a") update.delete("blaz2") self.assertEqual(update.to_wire(), goodwire)
def test_to_wire2(self): # type: () -> None update = dns.update.Update('example') update.id = 1 update.present('foo') update.present('foo', 'a') update.present('bar', 'a', '10.0.0.5') update.absent('blaz2') update.absent('blaz2', 'a') update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') update.add('bar', 300, dns.rdata.from_text(1, 1, '10.0.0.3')) update.delete('bar', 'a', '10.0.0.4') update.delete('blaz', 'a') update.delete('blaz2') self.failUnless(update.to_wire() == goodwire)
def update_zone(self, zone_name, zi, db_soa_serial=None, candidate_soa_serial=None, force_soa_serial_update=False, wrap_serial_next_time=False, date_stamp=None, nsec3_seed=False, clear_dnskey=False, clear_nsec3=False): """ Use dnspython to update a Zone in the DNS server Use wrap_serial_next_time to 'fix' SOA serial numbers grossly not in the operations format YYYYMMDDnn. date is a datetime object in localtime. """ # Read in via AXFR zone for comparison purposes try: zone, dnskey_flag, nsec3param_flag = self.read_zone(zone_name) update_info = {'dnskey_flag': dnskey_flag, 'nsec3param_flag': nsec3param_flag} except NoSuchZoneOnServerError as exc: msg = str(exc) #return (RCODE_FATAL, msg, None, None) # Send RESET as server not configured yet. return (RCODE_RESET, msg, None, None) except (dns.query.UnexpectedSource, dns.query.BadResponse) as exc: msg = ("Zone '%s', - server %s not operating correctly." % (zone_name, server.server_name)) return (RCODE_FATAL, msg, None, None) except (IOError, OSError) as exc: if exc.errno in (errno.EACCES, errno.EPERM, errno.ECONNREFUSED, errno.ENETUNREACH, errno.ETIMEDOUT): msg = ("Zone '%s' - server %s:%s not available - %s" % (zone_name, self.server, self.port, exc.strerror)) return (RCODE_ERROR, msg, None, None) msg = ("Zone '%s' - server %s:%s, fatal error %s." % (zone_name, self.server, self.port, exc.strerror)) return (RCODE_FATAL, msg, None, None) # Get current SOA record for zone to include as prerequiste in update # Makes update transaction idempotent current_soa_rr = zone.find_rdataset(zone.origin, RRTYPE_SOA).items[0] update_soa_serial_flag = False curr_serial_no = self.get_serial_no(zone) # In case of a DR failover, our DB can have a more recent serial number # than in name server try: new_serial_no = new_soa_serial_no(curr_serial_no, zone_name, db_soa_serial=db_soa_serial, candidate=candidate_soa_serial, wrap_serial_next_time=wrap_serial_next_time, date_stamp=date_stamp) except SOASerialError as exc: msg = str(exc) if (not sys.stdin.isatty()): log_critical(msg) return (RCODE_FATAL, msg, None, None) if wrap_serial_next_time or force_soa_serial_update: # Apply serial number to SOA record. zi.update_soa_serial(new_serial_no) else: # An increment should only be performed after difference update_soa_serial_flag = True # Compare server_zone with zi.rrs # Find additions and deletions del_rrs = [rr for rr in zone.iterate_rdatas() if rr not in zi.iterate_dnspython_rrs()] add_rrs = [rr for rr in zi.iterate_dnspython_rrs() if rr not in zone.iterate_rdatas()] # Check if DNSSEC settings need to be changed do_clear_nsec3 = clear_nsec3 and nsec3param_flag do_clear_dnskey = clear_dnskey and dnskey_flag do_nsec3_seed = nsec3_seed and not nsec3param_flag if (not del_rrs and not add_rrs and not do_clear_nsec3 and not do_clear_dnskey and not do_nsec3_seed): msg = ("Domain '%s' not updated as no change detected" % (zone_name)) return (RCODE_NOCHANGE, msg, curr_serial_no, update_info) # Incremental update of SOA serial number soa_rdtype = dns.rdatatype.from_text(RRTYPE_SOA) if update_soa_serial_flag: # Apply serial number to SOA record. zi.update_soa_serial(new_serial_no) # recalculate add_rrs - got to be done or else updates will be # missed add_rrs = [rr for rr in zi.iterate_dnspython_rrs() if rr not in zone.iterate_rdatas()] # Groom updates for DynDNS update perculiarities # SOA can never be deleted RFC 2136 Section 3.4.2.3 and 3.4.2.4 # so skip this. del_rrs = [rr for rr in del_rrs if (rr[2].rdtype != soa_rdtype)] # Can never delete the last NS on the root of a zone, # so pre add all '@' NS records (RFC 2136 Sec # 3.4.2.4) tl_label = dns.name.from_text('@', origin=dns.name.empty) ns_rdtype = dns.rdatatype.from_text(RRTYPE_NS) pre_add_rrs = [rr for rr in add_rrs if (rr[0] == tl_label and rr[2].rdtype == ns_rdtype)] tl_ns_rdata = [rr[2] for rr in pre_add_rrs] add_rrs = [rr for rr in add_rrs if rr not in pre_add_rrs] # Remove '@' NS delete from del_rrs if record in pre_add_rrs # ie, we are just doing a TTL update! del_rrs = [rr for rr in del_rrs if (not(rr[0] == tl_label and rr[2] in tl_ns_rdata))] # CNAMEs can only be added to vacant nodes, or totally replace # RRSET on a node RFC 2136 Section 3.4.2.2 # Choose to enforce this at zi API level. # DNSSEC processing - prepare NSEC3PARM rdata if do_nsec3_seed: rn = random.getrandbits(int(settings['nsec3_salt_bit_length'])) hash_alg = settings['nsec3_hash_algorithm'] flags = settings['nsec3_flags'] iterations = settings['nsec3_iterations'] nsec3param_rdata = ("%s %s %s %016x" % (hash_alg, flags, iterations, rn)) # Test rn as random can produce garbage sometimes... rdata_list = nsec3param_rdata.split() try: # This is the piece of code where dnspython blows up... stuff = bytes.fromhex(rdata_list[-1]) except Exception: msg = ("Failed to seed NSEC3 salt - SM reset required") return (RCODE_RESET, msg, None, update_info) # Prepare dnspython tsigkeyring keyring = dns.tsigkeyring.from_text({ self.key_name : self.tsig_key['secret'] }) if (self.tsig_key['algorithm'] == 'hmac-md5'): key_algorithm = dns.tsig.HMAC_MD5 else: key_algorithm = dns.name.from_text(self.tsig_key['algorithm']) # Create update # We have to use absolute FQDNs on LHS and RHS to make sure updates # to NS etc happen # While doing this also handle wee things for DNSSEC processing origin = dns.name.from_text(zone_name) update = dns.update.Update(origin, keyring=keyring, keyname = self.key_name, keyalgorithm=key_algorithm) update.present(origin, current_soa_rr) for rr in pre_add_rrs: update.add(rr[0], rr[1], rr[2]) for rr in del_rrs: update.delete(rr[0], rr[2]) # Add DNSSEC clearance stuff to end of delete section of update if do_clear_nsec3: update.delete(origin, RRTYPE_NSEC3PARAM) if do_clear_dnskey: update.delete(origin, RRTYPE_DNSKEY) for rr in add_rrs: update.add(rr[0], rr[1], rr[2]) # NSEC3PARAM seeding if do_nsec3_seed: update.add(origin, '0', RRTYPE_NSEC3PARAM, nsec3param_rdata) # Do dee TING! response = dns.query.tcp(update, self.server, port=self.port) # Process reply rcode = response.rcode() rcode_text = dns.rcode.to_text(response.rcode()) success_rcodes = (dns.rcode.NOERROR) if (rcode in self.success_rcodes): msg = ("Update '%s' to domain '%s' succeeded" % (new_serial_no, zone_name)) return (RCODE_OK, msg, new_serial_no, update_info) elif (rcode in self.retry_rcodes): msg = ("Update '%s' to domain '%s' failed: %s - will retry" % (new_serial_no, zone_name, rcode_text)) return (RCODE_ERROR, msg, None, update_info) elif (rcode in self.reset_rcodes): msg = ("Update '%s' to domain '%s' failed: %s - SM reset required" % (new_serial_no, zone_name, rcode_text)) return (RCODE_RESET, msg, None, update_info) elif (rcode in self.fatal_rcodes): msg = ("Update '%s' to domain '%s' permanently failed: %s" % (new_serial_no, zone_name, rcode_text)) return (RCODE_FATAL, msg, None, update_info) else: msg = ("Update '%s' to domain '%s' permanently failed: '%s'" " - unknown response" % (new_serial_no, zone_name, response.rcode())) return (RCODE_FATAL, msg, None, update_info)
def update_zone(self, zone_name, zi, db_soa_serial=None, candidate_soa_serial=None, force_soa_serial_update=False, wrap_serial_next_time=False, date_stamp=None, nsec3_seed=False, clear_dnskey=False, clear_nsec3=False): """ Use dnspython to update a Zone in the DNS server Use wrap_serial_next_time to 'fix' SOA serial numbers grossly not in the operations format YYYYMMDDnn. date is a datetime object in localtime. """ # Read in via AXFR zone for comparison purposes try: zone, dnskey_flag, nsec3param_flag = self.read_zone(zone_name) update_info = { 'dnskey_flag': dnskey_flag, 'nsec3param_flag': nsec3param_flag } except NoSuchZoneOnServerError as exc: msg = str(exc) #return (RCODE_FATAL, msg, None, None) # Send RESET as server not configured yet. return (RCODE_RESET, msg, None, None) except (dns.query.UnexpectedSource, dns.query.BadResponse) as exc: msg = ("Zone '%s', - server %s not operating correctly." % (zone_name, server.server_name)) return (RCODE_FATAL, msg, None, None) except (IOError, OSError) as exc: if exc.errno in (errno.EACCES, errno.EPERM, errno.ECONNREFUSED, errno.ENETUNREACH, errno.ETIMEDOUT): msg = ("Zone '%s' - server %s:%s not available - %s" % (zone_name, self.server, self.port, exc.strerror)) return (RCODE_ERROR, msg, None, None) msg = ("Zone '%s' - server %s:%s, fatal error %s." % (zone_name, self.server, self.port, exc.strerror)) return (RCODE_FATAL, msg, None, None) # Get current SOA record for zone to include as prerequiste in update # Makes update transaction idempotent current_soa_rr = zone.find_rdataset(zone.origin, RRTYPE_SOA).items[0] update_soa_serial_flag = False curr_serial_no = self.get_serial_no(zone) # In case of a DR failover, our DB can have a more recent serial number # than in name server try: new_serial_no = new_soa_serial_no( curr_serial_no, zone_name, db_soa_serial=db_soa_serial, candidate=candidate_soa_serial, wrap_serial_next_time=wrap_serial_next_time, date_stamp=date_stamp) except SOASerialError as exc: msg = str(exc) if (not sys.stdin.isatty()): log_critical(msg) return (RCODE_FATAL, msg, None, None) if wrap_serial_next_time or force_soa_serial_update: # Apply serial number to SOA record. zi.update_soa_serial(new_serial_no) else: # An increment should only be performed after difference update_soa_serial_flag = True # Compare server_zone with zi.rrs # Find additions and deletions del_rrs = [ rr for rr in zone.iterate_rdatas() if rr not in zi.iterate_dnspython_rrs() ] add_rrs = [ rr for rr in zi.iterate_dnspython_rrs() if rr not in zone.iterate_rdatas() ] # Check if DNSSEC settings need to be changed do_clear_nsec3 = clear_nsec3 and nsec3param_flag do_clear_dnskey = clear_dnskey and dnskey_flag do_nsec3_seed = nsec3_seed and not nsec3param_flag if (not del_rrs and not add_rrs and not do_clear_nsec3 and not do_clear_dnskey and not do_nsec3_seed): msg = ("Domain '%s' not updated as no change detected" % (zone_name)) return (RCODE_NOCHANGE, msg, curr_serial_no, update_info) # Incremental update of SOA serial number soa_rdtype = dns.rdatatype.from_text(RRTYPE_SOA) if update_soa_serial_flag: # Apply serial number to SOA record. zi.update_soa_serial(new_serial_no) # recalculate add_rrs - got to be done or else updates will be # missed add_rrs = [ rr for rr in zi.iterate_dnspython_rrs() if rr not in zone.iterate_rdatas() ] # Groom updates for DynDNS update perculiarities # SOA can never be deleted RFC 2136 Section 3.4.2.3 and 3.4.2.4 # so skip this. del_rrs = [rr for rr in del_rrs if (rr[2].rdtype != soa_rdtype)] # Can never delete the last NS on the root of a zone, # so pre add all '@' NS records (RFC 2136 Sec # 3.4.2.4) tl_label = dns.name.from_text('@', origin=dns.name.empty) ns_rdtype = dns.rdatatype.from_text(RRTYPE_NS) pre_add_rrs = [ rr for rr in add_rrs if (rr[0] == tl_label and rr[2].rdtype == ns_rdtype) ] tl_ns_rdata = [rr[2] for rr in pre_add_rrs] add_rrs = [rr for rr in add_rrs if rr not in pre_add_rrs] # Remove '@' NS delete from del_rrs if record in pre_add_rrs # ie, we are just doing a TTL update! del_rrs = [ rr for rr in del_rrs if (not (rr[0] == tl_label and rr[2] in tl_ns_rdata)) ] # CNAMEs can only be added to vacant nodes, or totally replace # RRSET on a node RFC 2136 Section 3.4.2.2 # Choose to enforce this at zi API level. # DNSSEC processing - prepare NSEC3PARM rdata if do_nsec3_seed: rn = random.getrandbits(int(settings['nsec3_salt_bit_length'])) hash_alg = settings['nsec3_hash_algorithm'] flags = settings['nsec3_flags'] iterations = settings['nsec3_iterations'] nsec3param_rdata = ("%s %s %s %016x" % (hash_alg, flags, iterations, rn)) # Test rn as random can produce garbage sometimes... rdata_list = nsec3param_rdata.split() try: # This is the piece of code where dnspython blows up... stuff = bytes.fromhex(rdata_list[-1]) except Exception: msg = ("Failed to seed NSEC3 salt - SM reset required") return (RCODE_RESET, msg, None, update_info) # Prepare dnspython tsigkeyring keyring = dns.tsigkeyring.from_text( {self.key_name: self.tsig_key['secret']}) if (self.tsig_key['algorithm'] == 'hmac-md5'): key_algorithm = dns.tsig.HMAC_MD5 else: key_algorithm = dns.name.from_text(self.tsig_key['algorithm']) # Create update # We have to use absolute FQDNs on LHS and RHS to make sure updates # to NS etc happen # While doing this also handle wee things for DNSSEC processing origin = dns.name.from_text(zone_name) update = dns.update.Update(origin, keyring=keyring, keyname=self.key_name, keyalgorithm=key_algorithm) update.present(origin, current_soa_rr) for rr in pre_add_rrs: update.add(rr[0], rr[1], rr[2]) for rr in del_rrs: update.delete(rr[0], rr[2]) # Add DNSSEC clearance stuff to end of delete section of update if do_clear_nsec3: update.delete(origin, RRTYPE_NSEC3PARAM) if do_clear_dnskey: update.delete(origin, RRTYPE_DNSKEY) for rr in add_rrs: update.add(rr[0], rr[1], rr[2]) # NSEC3PARAM seeding if do_nsec3_seed: update.add(origin, '0', RRTYPE_NSEC3PARAM, nsec3param_rdata) # Do dee TING! response = dns.query.tcp(update, self.server, port=self.port) # Process reply rcode = response.rcode() rcode_text = dns.rcode.to_text(response.rcode()) success_rcodes = (dns.rcode.NOERROR) if (rcode in self.success_rcodes): msg = ("Update '%s' to domain '%s' succeeded" % (new_serial_no, zone_name)) return (RCODE_OK, msg, new_serial_no, update_info) elif (rcode in self.retry_rcodes): msg = ("Update '%s' to domain '%s' failed: %s - will retry" % (new_serial_no, zone_name, rcode_text)) return (RCODE_ERROR, msg, None, update_info) elif (rcode in self.reset_rcodes): msg = ( "Update '%s' to domain '%s' failed: %s - SM reset required" % (new_serial_no, zone_name, rcode_text)) return (RCODE_RESET, msg, None, update_info) elif (rcode in self.fatal_rcodes): msg = ("Update '%s' to domain '%s' permanently failed: %s" % (new_serial_no, zone_name, rcode_text)) return (RCODE_FATAL, msg, None, update_info) else: msg = ("Update '%s' to domain '%s' permanently failed: '%s'" " - unknown response" % (new_serial_no, zone_name, response.rcode())) return (RCODE_FATAL, msg, None, update_info)