Example #1
0
    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
Example #2
0
    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
Example #3
0
    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
Example #4
0
    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
Example #5
0
 def __validate_error(self, reason):
     '''
     Used as error callback below.
     '''
     logger.error(LIBDDNS_ZONE_INVALID_ERROR, self.__zname, self.__zclass,
                  reason)
Example #6
0
 def __validate_error(self, reason):
     '''
     Used as error callback below.
     '''
     logger.error(LIBDDNS_ZONE_INVALID_ERROR, self.__zname, self.__zclass,
                  reason)