PNG  IHDR;IDATxܻn0K )(pA 7LeG{ §㻢|ذaÆ 6lذaÆ 6lذaÆ 6lom$^yذag5bÆ 6lذaÆ 6lذa{ 6lذaÆ `}HFkm,mӪôô! x|'ܢ˟;E:9&ᶒ}{v]n&6 h_tڠ͵-ҫZ;Z$.Pkž)!o>}leQfJTu іچ\X=8Rن4`Vwl>nG^is"ms$ui?wbs[m6K4O.4%/bC%t Mז -lG6mrz2s%9s@-k9=)kB5\+͂Zsٲ Rn~GRC wIcIn7jJhۛNCS|j08yiHKֶۛkɈ+;SzL/F*\Ԕ#"5m2[S=gnaPeғL lذaÆ 6l^ḵaÆ 6lذaÆ 6lذa; _ذaÆ 6lذaÆ 6lذaÆ RIENDB` #!/usr/bin/env python3 # -*- encoding: utf-8; py-indent-offset: 4 -*- # Check_mk agent for blacklist # GPL (C) Lars Falk-Petersen , 2020 import os import sys import time import socket import ipaddress # Check if IP is private version = 3 verbose = False class Blacklists(object): """ A class to hold the data structure for blacklists. Avoid _ in names. """ def __init__(self, name, domain, response={}, results='', removal='', always_black='', always_white=''): self.name = name # Shortname for check self.domain = domain # Domain for check self.response = response # If response == key, value is the list we're on self.results = results self.removal = removal # Url for removal. will be replaced by blacklisted IP. self.always_black = always_black # https://tools.ietf.org/html/rfc5782#section-5 says "IPv4-based DNSxLs MUST contain an entry for 127.0.0.2 for testing purposes." but not everyone does. If they do (or have another test address) it's added here. self.always_white = always_white # https://tools.ietf.org/html/rfc5782#section-5 says "IPv4-based DNSxLs MUST NOT contain an entry for 127.0.0.1." # Sanity checks: if not self.domain.startswith('.'): raise ValueError('Domains must start with a dot, %s given.' % self.domain) if 2 > len(self.name): raise ValueError('Must have a name (over 1 character long), %s given.' % self.name) if self.removal and not self.removal.startswith('http'): raise ValueError('Removal must be a web address if not empty, %s given.' % self.removal) def check_list(self, external_ips, verbose = False): """ Look-up reversed ip + blacklist domain, and use result as key in response dict to add url to results. Then return string if lists we're blacklisted in. Args: external_ips - dict { IP: reversed_ip } """ if verbose: print('Checking list %s' % self.name) start_time = time.time() for ip in external_ips: if '.' in ip: # Check IPv4 address try: self.results += ('%s listed in %s%s, ' % ( ip, self.response[socket.gethostbyname(external_ips[ip] + self.domain)], '' if 0 == len(self.removal) else ' %s ' % self.removal )).replace('', ip) except socket.gaierror: pass # Empty DNS response means not listed (or dead service...) except KeyError as err: if verbose: print("Error: blacklist %s responds with a code that is not listed, %s." % (self.domain, ip)) else: # Check IPv6 address if verbose: print("No IPv6 support yet") if verbose and self.results: print('results %s' % self.results) print ("Done in %s. " % round(time.time()-start_time, 3)) return self.results def reverse_ipv4 (ipv4): ''' Reverse IPv4 address, i.e. 127.0.0.1 -> 1.0.0.127 ''' return ".".join(reversed(ipv4.split('.'))) def reverse_ipv6 (ipv4): ''' Reverse IPv6, remove :, add . for each character, i.e. 1234:0000:0000:0111 -> 1.1.1.0.0.0.0.0.0.0.0.0.4.3.2.1 ''' return ".".join("".join(reversed(ip.split(':')))) def is_ipv4 (ip): ''' Returns True if IPv4, False if IPv6. ''' if '.' in ip: return True if ':' in ip: return False raise ValueError('IP %s seems not to be IPv4 or IPv6.' % ip) def build_collection(): ''' Build blacklist collection and return list ''' collection = [] collection += [ Blacklists( name='abuse.ch', # No obvious way to loop-up, delist domain='.spam.abuse.ch', response={'127.0.0.2': 'spam.abuse.ch'}, always_white='127.0.0.2'), Blacklists( name='anti-spam.cn', domain='.cblplus.anti-spam.org.cn', response={ '127.0.8.2': 'cblplus.anti-spam.org.cn', '127.0.8.6': 'cblplus.anti-spam.org.cn'}, removal='http://anti-spam.cn/appeal.action', always_white='127.0.0.2'), Blacklists( name='barracudacentral.org', domain='.b.barracudacentral.org', response={'127.0.0.2': 'b.barracudacentral.org'}, removal='http://barracudacentral.org/rbl/removal-request', always_black='127.0.0.2'), Blacklists( name='cobion.com/IBM', domain='.dnsbl.cobion.com', response={ '127.0.0.1': 'dnsbl.cobion.com', '127.0.0.2': 'dnsbl.cobion.com'}, removal='https://exchange.xforce.ibmcloud.com/url/', always_white='127.0.0.2'), Blacklists( name='manitu.net', # No obvious way to loop-up, delist domain='.ix.dnsbl.manitu.net', response={'127.0.0.2': 'ix.dnsbl.manitu.net'}, always_black='127.0.0.2'), Blacklists( name='rbl.jp', # No obvious way to loop-up, delist domain='.all.rbl.jp', response={'127.0.0.2': 'all.rbl.jp'}, always_white='127.0.0.2'), Blacklists( name='realtimeblacklist.com', domain='.rbl.realtimeblacklist.com', response={'127.0.0.2': 'rbl.realtimeblacklist.com'}, removal='https://realtimeblacklist.com/lookup/?', always_black='127.0.0.2'), Blacklists( name='redhawk.org', domain='.access.redhawk.org', response={'127.0.0.2': 'access.redhawk.org'}, removal='https://www.redhawk.org/SpamHawk/query.php', always_black='127.0.0.2'), Blacklists( name='sorbs.net', domain='.dnsbl.sorbs.net', response={ '127.0.0.2': 'http.dnsbl.sorbs.net', '127.0.0.3': 'socks.dnsbl.sorbs.net', '127.0.0.4': 'misc.dnsbl.sorbs.net', '127.0.0.5': 'smtp.dnsbl.sorbs.net', '127.0.0.6': 'spam.dnsbl.sorbs.net', '127.0.0.7': 'web.dnsbl.sorbs.net', '127.0.0.8': 'block.dnsbl.sorbs.net', '127.0.0.9': 'zombie.dnsbl.sorbs.net', '127.0.0.10': 'dul.dnsbl.sorbs.net', '127.0.0.11': 'badconf.rhsbl.sorbs.net', '127.0.0.12': 'nomail.rhsbl.sorbs.net'}, removal='http://www.sorbs.net/cgi-bin/support', always_black='127.0.0.2'), Blacklists( name='spamhaus.org', domain='.zen.spamhaus.org', response={ '127.0.0.2': 'sbl.spamhaus.org', '127.0.0.3': 'css.spamhaus.org', '127.0.0.4': 'cbl.abuseat.org', '127.0.0.5': 'www.njabl.org', '127.0.0.6': 'xbl.spamhaus.org', '127.0.0.7': 'xbl.spamhaus.org', '127.0.0.10': 'pbl.spamhaus.org', '127.0.0.11': 'pbl.spamhaus.org'}, removal='https://www.spamhaus.org/query/ip/', always_black='127.0.0.2'), Blacklists( name='spamcop.net', domain='.bl.spamcop.net', response={'127.0.0.2': 'bl.spamcop.net'}, removal='https://www.spamcop.net/w3m?action=checkblock&ip=', always_black='127.0.0.2'), Blacklists( name='spamrats.com', domain='.spam.spamrats.com', response={'127.0.0.38': 'spam.spamrats.com'}, removal='http://spamrats.com/lookup.php?ip=', always_white='127.0.0.2'), Blacklists( name='spewsl2sorbs.net', domain='.l2.spews.dnsbl.sorbs.net', response={'127.0.0.2': 'l2.spews.dnsbl.sorbs.net'}, removal='http://www.sorbs.net/general/using.shtml', always_black='127.0.0.2'), Blacklists( name='swinog.ch', domain='.dnsrbl.swinog.ch', response={'127.0.0.3': 'dnsrbl.swinog.ch'}, removal='https://antispam.imp.ch/', always_white='127.0.0.2'), Blacklists( name='gbudb.net', domain='.truncate.gbudb.net', response={'127.0.0.2': 'truncate.gbudb.net'}, removal='http://www.gbudb.com/truncate/how-ips-are-removed.jsp', always_black='127.0.0.2'), Blacklists( name='lashback.com', domain='.ubl.unsubscore.com', response={'127.0.0.2': 'ubl.unsubscore.com'}, removal='http://blacklist.lashback.com/', always_black='127.0.0.2'), Blacklists( name='wbpl', domain='.db.wpbl.info', response={'127.0.0.2': 'db.wpbl.info'}, always_black='127.0.0.2', removal='http://wpbl.info/cgi-bin/detail.cgi?ip='), Blacklists( name='backscatterer.org', domain='.ips.backscatterer.org', response={'127.0.0.2': 'ips.backscatterer.org'}, always_black='127.0.0.2', always_white='127.0.0.1', removal='http://www.backscatterer.org/?target=test'), # Removed blacklists: # bad.psky.me https://glockapps.com/blacklist/bad-psky-me/ # psbl.surriel.com Listed as active in https://glockapps.com/blacklist/psbl-surriel-com/, but seems dead # virbl.dnsbl.bit.nl https://virbl.bit.nl/ # rbl-dns.com no removal without paying, blackmail. ] return collection def get_host_and_IPs(hostname=''): ''' Fetch hostname, a list of public IP addresses, and a list of reversed, public IP addresses. If hostname is given as argument, that one is used instead of the system one. ''' if hostname: try: # Check if given an IP address valid_ip = ipaddress.ip_address(hostname) #ipaddress_list = [str(valid_ip)] except ValueError as err: pass else: hostname = socket.gethostname() # Fetch IPv4 (_, _, ipaddress_list) = socket.gethostbyname_ex(hostname) # Fetch IPv6 try: ipaddress_list.append( ipaddress.ip_address( socket.getaddrinfo( host=hostname, port=None, family=socket.AF_INET6)[0][4][0] ).exploded ) except socket.gaierror as err: if verbose: print ("No IPv6 address found, %s" % err) # Build a dict of public IPs with reverse as value (IP given on CLI is accepted) external_ips = {} for ip in ipaddress_list: if not ipaddress.ip_address(ip).is_private or hostname == ip: if is_ipv4(ip): external_ips[ip] = reverse_ipv4(ip) else: # TODO: not handling IPv6 yet. if verbose: external_ips[ip] = reverse_ipv6(ip) if verbose: print('hostname %s, external_ips %s, revips %s' % (hostname, (','.join(external_ips.keys())), (','.join(external_ips.values())))) return hostname, external_ips def main(verbose=False): hostname = '' output = '<<>>\n' # Set verbosity or a specified hostname if len(sys.argv) > 1: for arg in sys.argv[1:]: if arg == 'verbose': verbose = True else: hostname = arg hostname, external_ips = get_host_and_IPs(hostname) # Ask all blacklist providers, and print results if external_ips: collection = build_collection() for provider in collection: result = provider.check_list(external_ips, verbose) output += 'blacklist_%s %s\n' % ( provider.name, result.count(',') ) if output.strip(): output = output.rstrip('\n') return output if __name__ == "__main__": print(main(verbose))