Exemple #1
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
Exemple #2
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
Exemple #3
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))
Exemple #4
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))
Exemple #5
0
 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
Exemple #6
0
    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
Exemple #7
0
 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
Exemple #8
0
    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