def handle(self): '''Handle the update request according to RFC2136. This method returns a tuple of the following elements that indicate the result of the request. - Result code of the request processing, which are: UPDATE_SUCCESS Update request granted and succeeded. UPDATE_ERROR Some error happened to be reported in the response. UPDATE_DROP Error happened and no response should be sent. Except the case of UPDATE_DROP, the UpdateSession object will have created a response that is to be returned to the request client, which can be retrieved by get_message(). If it's UPDATE_DROP, subsequent call to get_message() returns None. - The name of the updated zone (bundy.dns.Name object) in case of UPDATE_SUCCESS; otherwise None. - The RR class of the updated zone (bundy.dns.RRClass object) in case of UPDATE_SUCCESS; otherwise None. - The name of the used data source associated with DataSourceClient in case of UPDATE_SUCCESS; otherwise None. ''' try: self._get_update_zone() # Contrary to what RFC2136 specifies, we do ACL checks before # prerequisites. It's now generally considered to be a bad # idea, and actually does harm such as information # leak. It should make more sense to prevent any security issues # by performing ACL check as early as possible. self.__check_update_acl(self.__zname, self.__zclass) self._create_diff() prereq_result = self.__check_prerequisites() if prereq_result != Rcode.NOERROR: self.__make_response(prereq_result) return UPDATE_ERROR, self.__zname, self.__zclass, None update_result = self.__do_update() if update_result != Rcode.NOERROR: self.__make_response(update_result) return UPDATE_ERROR, self.__zname, self.__zclass, None self.__make_response(Rcode.NOERROR) datasrc_name = self.__datasrc_client.get_datasource_name() return UPDATE_SUCCESS, self.__zname, self.__zclass, datasrc_name except UpdateError as e: if not e.nolog: logger.debug(logger.DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_PROCESSING_FAILED, ClientFormatter(self.__client_addr, self.__tsig), ZoneFormatter(e.zname, e.zclass), e) # If RCODE is specified, create a corresponding resonse and return # ERROR; otherwise clear the message and return DROP. if e.rcode is not None: self.__make_response(e.rcode) return UPDATE_ERROR, None, None, None self.__message = None return UPDATE_DROP, None, None, None except bundy.datasrc.Error as e: logger.error(LIBDDNS_DATASRC_ERROR, ClientFormatter(self.__client_addr, self.__tsig), e) self.__make_response(Rcode.SERVFAIL) return UPDATE_ERROR, None, None, None
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 __validate_error(self, reason): ''' Used as error callback below. ''' logger.error(LIBDDNS_ZONE_INVALID_ERROR, self.__zname, self.__zclass, reason)