Esempio n. 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
Esempio n. 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
Esempio n. 3
0
    def _get_update_zone(self):
        '''Parse the zone section and find the zone to be updated.

        If the zone section is valid and the specified zone is found in
        the configuration, sets private member variables for this session:
        __datasrc_client: A matching data source that contains the specified
                          zone
        __zname: The zone name as a Name object
        __zclass: The zone class as an RRClass object
        If this method raises an exception, these members are not set.

        Note: This method is protected for ease of use in tests, where
        methods are tested that need the setup done here without calling
        the full handle() method.
        '''
        # Validation: the zone section must contain exactly one question,
        # and it must be of type SOA.
        n_zones = self.__message.get_rr_count(SECTION_ZONE)
        if n_zones != 1:
            raise UpdateError(
                'Invalid number of records in zone section: ' + str(n_zones),
                None, None, Rcode.FORMERR)
        zrecord = self.__message.get_question()[0]
        if zrecord.get_type() != RRType.SOA:
            raise UpdateError('update zone section contains non-SOA', None,
                              None, Rcode.FORMERR)

        # See if we're serving a primary zone specified in the zone section.
        zname = zrecord.get_name()
        zclass = zrecord.get_class()
        zone_type, datasrc_client = self.__zone_config.find_zone(zname, zclass)
        if zone_type == bundy.ddns.zone_config.ZONE_PRIMARY:
            self.__datasrc_client = datasrc_client
            self.__zname = zname
            self.__zclass = zclass
            return
        elif zone_type == bundy.ddns.zone_config.ZONE_SECONDARY:
            # We are a secondary server; since we don't yet support update
            # forwarding, we return 'not implemented'.
            logger.debug(DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_FORWARD_FAIL,
                         ClientFormatter(self.__client_addr, self.__tsig),
                         ZoneFormatter(zname, zclass))
            raise UpdateError('forward', zname, zclass, Rcode.NOTIMP, True)
        # zone wasn't found
        logger.debug(DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_NOTAUTH,
                     ClientFormatter(self.__client_addr, self.__tsig),
                     ZoneFormatter(zname, zclass))
        raise UpdateError('notauth', zname, zclass, Rcode.NOTAUTH, True)
Esempio n. 4
0
    def _get_update_zone(self):
        '''Parse the zone section and find the zone to be updated.

        If the zone section is valid and the specified zone is found in
        the configuration, sets private member variables for this session:
        __datasrc_client: A matching data source that contains the specified
                          zone
        __zname: The zone name as a Name object
        __zclass: The zone class as an RRClass object
        If this method raises an exception, these members are not set.

        Note: This method is protected for ease of use in tests, where
        methods are tested that need the setup done here without calling
        the full handle() method.
        '''
        # Validation: the zone section must contain exactly one question,
        # and it must be of type SOA.
        n_zones = self.__message.get_rr_count(SECTION_ZONE)
        if n_zones != 1:
            raise UpdateError('Invalid number of records in zone section: ' +
                              str(n_zones), None, None, Rcode.FORMERR)
        zrecord = self.__message.get_question()[0]
        if zrecord.get_type() != RRType.SOA:
            raise UpdateError('update zone section contains non-SOA',
                              None, None, Rcode.FORMERR)

        # See if we're serving a primary zone specified in the zone section.
        zname = zrecord.get_name()
        zclass = zrecord.get_class()
        zone_type, datasrc_client = self.__zone_config.find_zone(zname, zclass)
        if zone_type == bundy.ddns.zone_config.ZONE_PRIMARY:
            self.__datasrc_client = datasrc_client
            self.__zname = zname
            self.__zclass = zclass
            return
        elif zone_type == bundy.ddns.zone_config.ZONE_SECONDARY:
            # We are a secondary server; since we don't yet support update
            # forwarding, we return 'not implemented'.
            logger.debug(DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_FORWARD_FAIL,
                         ClientFormatter(self.__client_addr, self.__tsig),
                         ZoneFormatter(zname, zclass))
            raise UpdateError('forward', zname, zclass, Rcode.NOTIMP, True)
        # zone wasn't found
        logger.debug(DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_NOTAUTH,
                     ClientFormatter(self.__client_addr, self.__tsig),
                     ZoneFormatter(zname, zclass))
        raise UpdateError('notauth', zname, zclass, Rcode.NOTAUTH, True)
Esempio n. 5
0
 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))
Esempio n. 6
0
 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))