def __do_update(self): '''Scan, check, and execute the Update section in the DDNS Update message. Returns an Rcode to signal the result (NOERROR upon success, any error result otherwise). ''' # prescan prescan_result = self.__do_prescan() if prescan_result != Rcode.NOERROR: return prescan_result # update try: # Do special handling for SOA first self.__update_soa() # Algorithm from RFC2136 Section 3.4 # Note that this works on full rrsets, not individual RRs. # Some checks might be easier with individual RRs, but only if we # would use the ZoneUpdater directly (so we can query the # 'zone-as-it-would-be-so-far'. However, due to the current use # of the Diff class, this is not the case, and therefore it # is easier to work with full rrsets for the most parts # (less lookups needed; conversion to individual rrs is # the same effort whether it is done here or in the several # do_update statements) for rrset in self.__message.get_section(SECTION_UPDATE): if rrset.get_class() == self.__zclass: self.__do_update_add_rrs_to_rrset(rrset) elif rrset.get_class() == RRClass.ANY: if rrset.get_type() == RRType.ANY: self.__do_update_delete_name(rrset) else: self.__do_update_delete_rrset(rrset) elif rrset.get_class() == RRClass.NONE: self.__do_update_delete_rrs_from_rrset(rrset) if not check_zone( self.__zname, self.__zclass, self.__diff.get_rrset_collection(), (self.__validate_error, self.__validate_warning)): raise UpdateError('Validation of the new zone failed', self.__zname, self.__zclass, Rcode.REFUSED) self.__diff.commit() return Rcode.NOERROR except UpdateError: # Propagate UpdateError exceptions (don't catch them in the # blocks below) raise except bundy.datasrc.Error as dse: logger.info(LIBDDNS_UPDATE_DATASRC_COMMIT_FAILED, dse) return Rcode.SERVFAIL except Exception as uce: logger.error(LIBDDNS_UPDATE_UNCAUGHT_EXCEPTION, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), uce) return Rcode.SERVFAIL
def __do_update(self): '''Scan, check, and execute the Update section in the DDNS Update message. Returns an Rcode to signal the result (NOERROR upon success, any error result otherwise). ''' # prescan prescan_result = self.__do_prescan() if prescan_result != Rcode.NOERROR: return prescan_result # update try: # Do special handling for SOA first self.__update_soa() # Algorithm from RFC2136 Section 3.4 # Note that this works on full rrsets, not individual RRs. # Some checks might be easier with individual RRs, but only if we # would use the ZoneUpdater directly (so we can query the # 'zone-as-it-would-be-so-far'. However, due to the current use # of the Diff class, this is not the case, and therefore it # is easier to work with full rrsets for the most parts # (less lookups needed; conversion to individual rrs is # the same effort whether it is done here or in the several # do_update statements) for rrset in self.__message.get_section(SECTION_UPDATE): if rrset.get_class() == self.__zclass: self.__do_update_add_rrs_to_rrset(rrset) elif rrset.get_class() == RRClass.ANY: if rrset.get_type() == RRType.ANY: self.__do_update_delete_name(rrset) else: self.__do_update_delete_rrset(rrset) elif rrset.get_class() == RRClass.NONE: self.__do_update_delete_rrs_from_rrset(rrset) if not check_zone(self.__zname, self.__zclass, self.__diff.get_rrset_collection(), (self.__validate_error, self.__validate_warning)): raise UpdateError('Validation of the new zone failed', self.__zname, self.__zclass, Rcode.REFUSED) self.__diff.commit() return Rcode.NOERROR except UpdateError: # Propagate UpdateError exceptions (don't catch them in the # blocks below) raise except bundy.datasrc.Error as dse: logger.info(LIBDDNS_UPDATE_DATASRC_COMMIT_FAILED, dse) return Rcode.SERVFAIL except Exception as uce: logger.error(LIBDDNS_UPDATE_UNCAUGHT_EXCEPTION, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), uce) return Rcode.SERVFAIL
def __check_update_acl(self, zname, zclass): '''Apply update ACL for the zone to be updated.''' acl = self.__zone_config.get_update_acl(zname, zclass) action = acl.execute(bundy.acl.dns.RequestContext( (self.__client_addr[0], self.__client_addr[1]), self.__tsig)) if action == REJECT: logger.info(LIBDDNS_UPDATE_DENIED, ClientFormatter(self.__client_addr, self.__tsig), ZoneFormatter(zname, zclass)) raise UpdateError('rejected', zname, zclass, Rcode.REFUSED, True) if action == DROP: logger.info(LIBDDNS_UPDATE_DROPPED, ClientFormatter(self.__client_addr, self.__tsig), ZoneFormatter(zname, zclass)) raise UpdateError('dropped', zname, zclass, None, True) logger.debug(logger.DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_APPROVED, ClientFormatter(self.__client_addr, self.__tsig), ZoneFormatter(zname, zclass))
def __check_update_acl(self, zname, zclass): '''Apply update ACL for the zone to be updated.''' acl = self.__zone_config.get_update_acl(zname, zclass) action = acl.execute( bundy.acl.dns.RequestContext( (self.__client_addr[0], self.__client_addr[1]), self.__tsig)) if action == REJECT: logger.info(LIBDDNS_UPDATE_DENIED, ClientFormatter(self.__client_addr, self.__tsig), ZoneFormatter(zname, zclass)) raise UpdateError('rejected', zname, zclass, Rcode.REFUSED, True) if action == DROP: logger.info(LIBDDNS_UPDATE_DROPPED, ClientFormatter(self.__client_addr, self.__tsig), ZoneFormatter(zname, zclass)) raise UpdateError('dropped', zname, zclass, None, True) logger.debug(logger.DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_APPROVED, ClientFormatter(self.__client_addr, self.__tsig), ZoneFormatter(zname, zclass))
def __do_prescan(self): '''Perform the prescan as defined in RFC2136 section 3.4.1. This method has a side-effect; it sets self._new_soa if it encounters the addition of a SOA record in the update list (so serial can be checked by update later, etc.). It puts the added SOA in self.__added_soa. ''' for rrset in self.__message.get_section(SECTION_UPDATE): if not self.__check_in_zone(rrset): logger.info(LIBDDNS_UPDATE_NOTZONE, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.NOTZONE if rrset.get_class() == self.__zclass: # In fact, all metatypes are in a specific range, # so one check can test TKEY to ANY # (some value check is needed anyway, since we do # not have defined RRtypes for MAILA and MAILB) if rrset.get_type().get_code() >= 249: logger.info(LIBDDNS_UPDATE_ADD_BAD_TYPE, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR if rrset.get_type() == RRType.SOA: # In case there's multiple soa records in the update # somehow, just take the last for rr in foreach_rr(rrset): self.__set_soa_rrset(rr) elif rrset.get_class() == RRClass.ANY: if rrset.get_ttl().get_value() != 0: logger.info(LIBDDNS_UPDATE_DELETE_NONZERO_TTL, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR if rrset.get_rdata_count() > 0: logger.info(LIBDDNS_UPDATE_DELETE_RRSET_NOT_EMPTY, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR if rrset.get_type().get_code() >= 249 and\ rrset.get_type().get_code() <= 254: logger.info(LIBDDNS_UPDATE_DELETE_BAD_TYPE, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR elif rrset.get_class() == RRClass.NONE: if rrset.get_ttl().get_value() != 0: logger.info(LIBDDNS_UPDATE_DELETE_RR_NONZERO_TTL, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR if rrset.get_type().get_code() >= 249: logger.info(LIBDDNS_UPDATE_DELETE_RR_BAD_TYPE, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR else: logger.info(LIBDDNS_UPDATE_BAD_CLASS, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR return Rcode.NOERROR
def __check_prerequisites(self): '''Check the prerequisites section of the UPDATE Message. RFC2136 Section 2.4. Returns a dns Rcode signaling either no error (Rcode.NOERROR) or that one of the prerequisites failed (any other Rcode). ''' # Temporary array to store exact-match RRsets exact_match_rrsets = [] for rrset in self.__message.get_section(SECTION_PREREQUISITE): # First check if the name is in the zone if not self.__check_in_zone(rrset): logger.info(LIBDDNS_PREREQ_NOTZONE, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.NOTZONE # Algorithm taken from RFC2136 Section 3.2 if rrset.get_class() == RRClass.ANY: if rrset.get_ttl().get_value() != 0 or\ rrset.get_rdata_count() != 0: logger.info(LIBDDNS_PREREQ_FORMERR_ANY, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR elif rrset.get_type() == RRType.ANY: if not self.__prereq_name_in_use(rrset): rcode = Rcode.NXDOMAIN logger.info(LIBDDNS_PREREQ_NAME_IN_USE_FAILED, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset), rcode) return rcode else: if not self.__prereq_rrset_exists(rrset): rcode = Rcode.NXRRSET logger.info(LIBDDNS_PREREQ_RRSET_EXISTS_FAILED, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset), rcode) return rcode elif rrset.get_class() == RRClass.NONE: if rrset.get_ttl().get_value() != 0 or\ rrset.get_rdata_count() != 0: logger.info(LIBDDNS_PREREQ_FORMERR_NONE, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR elif rrset.get_type() == RRType.ANY: if not self.__prereq_name_not_in_use(rrset): rcode = Rcode.YXDOMAIN logger.info(LIBDDNS_PREREQ_NAME_NOT_IN_USE_FAILED, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset), rcode) return rcode else: if not self.__prereq_rrset_does_not_exist(rrset): rcode = Rcode.YXRRSET logger.info(LIBDDNS_PREREQ_RRSET_DOES_NOT_EXIST_FAILED, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset), rcode) return rcode elif rrset.get_class() == self.__zclass: if rrset.get_ttl().get_value() != 0: logger.info(LIBDDNS_PREREQ_FORMERR, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR else: collect_rrsets(exact_match_rrsets, rrset) else: logger.info(LIBDDNS_PREREQ_FORMERR_CLASS, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(rrset)) return Rcode.FORMERR for collected_rrset in exact_match_rrsets: if not self.__prereq_rrset_exists_value(collected_rrset): rcode = Rcode.NXRRSET logger.info(LIBDDNS_PREREQ_RRSET_EXISTS_VAL_FAILED, ClientFormatter(self.__client_addr), ZoneFormatter(self.__zname, self.__zclass), RRsetFormatter(collected_rrset), rcode) return rcode # All prerequisites are satisfied return Rcode.NOERROR