class RBLProviderBase(object): """Baseclass for all rbl providers""" def __init__(self, rbldomain, timeout=3, lifetime=10): self.replycodes = {} self.rbldomain = rbldomain self.logger = logging.getLogger('%s.rbllookup.%s' % (__package__, self.rbldomain)) self.resolver = DNSLookup(timeout=timeout, lifetime=lifetime) self.descriptiontemplate = "${input} is listed on ${rbldomain} (${identifier})" self.lifetime = lifetime def add_replycode(self, mask, identifier=None): """add a replycode/bitmask. identifier is any object which will be returned if a dns result matches this replycode if identifier is not passed, the lookup domain will be used""" if identifier is None: identifier = self.rbldomain self.replycodes[mask] = identifier def _listed_identifiers(self, input, transform, dnsresult): listings = [] for code, identifier in self.replycodes.items(): if dnsresult == "%s" % code or dnsresult == "127.0.0.%s" % code: listings.append( (identifier, self.make_description(input=input, dnsresult=dnsresult, transform=transform, identifier=identifier, replycode=code))) return listings def make_description(self, **values): """create a human readable listing explanation""" template = Template(self.descriptiontemplate) values['rbldomain'] = self.rbldomain return template.safe_substitute(values) def accept_input(self, value): return re.match("^[a-zA-Z0-9.-]{2,256}$", value) is not None def transform_input(self, value): """transform input, eg, look up records or make md5 hashes here or whatever is needed for your specific provider and return a list of transformed values""" return [value, ] def make_lookup(self, transform): """some bls require additional modifications, even after input transformation, eg. ips must be reversed... by default we just fix trailing dots """ return add_trailing_dot(transform) + self.rbldomain def listed(self, input, parallel=False): listings = [] if not self.accept_input(input): return [] transforms = self.transform_input(input) if parallel: lookup_to_trans = {} for transform in transforms: lookup_to_trans[self.make_lookup(transform)] = transform logging.debug("lookup_to_trans=%s" % lookup_to_trans) multidnsresult = self.resolver.lookup_multi(lookup_to_trans.keys()) for lookup, arecordlist in multidnsresult.items(): if lookup not in lookup_to_trans: self.logger.error( "dns error: I asked for %s but got '%s' ?!" % (lookup_to_trans.keys(), lookup)) continue for ipresult in arecordlist: listings.extend( self._listed_identifiers(input, lookup_to_trans[lookup], ipresult.content)) else: loopstarttime = time.time() for transform in transforms: lookup = self.make_lookup(transform) arecordlist = self.resolver.lookup(lookup.encode('utf-8', 'ignore')) for ipresult in arecordlist: listings.extend( self._listed_identifiers( input, transform, ipresult.content)) runtime = time.time() - loopstarttime if runtime > self.lifetime: self.logger.debug('rbl lookup aborted due to timeout after %ss' % runtime) break return listings def __str__(self): return "<%s d=%s codes=%s>" % (self.__class__.__name__, self.rbldomain, self.replycodes) def __repr__(self): return str(self)
class RBLProviderBase(object): """Baseclass for all rbl providers""" def __init__(self, rbldomain, timeout=3, lifetime=10): self.replycodes = {} self.rbldomain = rbldomain self.logger = logging.getLogger('%s.rbllookup.%s' % (__package__, self.rbldomain)) self.resolver = DNSLookup(timeout=timeout, lifetime=lifetime) self.descriptiontemplate = "${input} is listed on ${rbldomain} (${identifier})" self.lifetime = lifetime def add_replycode(self, mask, identifier=None): """add a replycode/bitmask. identifier is any object which will be returned if a dns result matches this replycode if identifier is not passed, the lookup domain will be used""" if identifier is None: identifier = self.rbldomain self.replycodes[mask] = identifier def _listed_identifiers(self, input, transform, dnsresult): listings = [] for code, identifier in self.replycodes.items(): if dnsresult == "%s" % code or dnsresult == "127.0.0.%s" % code: listings.append((identifier, self.make_description(input=input, dnsresult=dnsresult, transform=transform, identifier=identifier, replycode=code))) return listings def make_description(self, **values): """create a human readable listing explanation""" template = Template(self.descriptiontemplate) values['rbldomain'] = self.rbldomain return template.safe_substitute(values) def accept_input(self, value): return re.match("^[a-zA-Z0-9.-]{2,256}$", value) is not None def transform_input(self, value): """transform input, eg, look up records or make md5 hashes here or whatever is needed for your specific provider and return a list of transformed values""" return [ value, ] def make_lookup(self, transform): """some bls require additional modifications, even after input transformation, eg. ips must be reversed... by default we just fix trailing dots """ return add_trailing_dot(transform) + self.rbldomain def listed(self, input, parallel=False): listings = [] if not self.accept_input(input): return [] transforms = self.transform_input(input) if parallel: lookup_to_trans = {} for transform in transforms: lookup_to_trans[self.make_lookup(transform)] = transform logging.debug("lookup_to_trans=%s" % lookup_to_trans) multidnsresult = self.resolver.lookup_multi(lookup_to_trans.keys()) for lookup, arecordlist in multidnsresult.items(): if lookup not in lookup_to_trans: self.logger.error( "dns error: I asked for %s but got '%s' ?!" % (lookup_to_trans.keys(), lookup)) continue for ipresult in arecordlist: listings.extend( self._listed_identifiers(input, lookup_to_trans[lookup], ipresult.content)) else: loopstarttime = time.time() for transform in transforms: lookup = self.make_lookup(transform) arecordlist = self.resolver.lookup( lookup.encode('utf-8', 'ignore')) for ipresult in arecordlist: listings.extend( self._listed_identifiers(input, transform, ipresult.content)) runtime = time.time() - loopstarttime if runtime > self.lifetime: self.logger.debug( 'rbl lookup aborted due to timeout after %ss' % runtime) break return listings def __str__(self): return "<%s d=%s codes=%s>" % (self.__class__.__name__, self.rbldomain, self.replycodes) def __repr__(self): return str(self)