diff --git a/MANIFEST.in b/MANIFEST.in index 9c9d3866fb3d15c32c4ed0853ae0c26854e8c6be..225b1c8e1677acfe345278a5916a2c07d91826a8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -18,6 +18,7 @@ include cid2spf.py include spfquery.py include test.py include sample.py +include spfmilter.py include test/* include doc/* include Milter/*.py diff --git a/Milter/utils.py b/Milter/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..8351cb7d40e38eb94f5ebc927a03b12fb32f185e --- /dev/null +++ b/Milter/utils.py @@ -0,0 +1,58 @@ +import re +import struct +import socket +from fnmatch import fnmatchcase + +ip4re = re.compile(r'^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$') + +# from spf.py +def addr2bin(str): + "Convert a string IPv4 address into an unsigned integer." + return struct.unpack("!L", socket.inet_aton(str))[0] + +MASK = 0xFFFFFFFFL + +def cidr(i,n): + return ~(MASK >> n) & MASK & i + +def iniplist(ipaddr,iplist): + """Return whether ip is in cidr list + >>> iniplist('66.179.26.146',['127.0.0.1','66.179.26.128/26']) + True + >>> iniplist('127.0.0.1',['127.0.0.1','66.179.26.128/26']) + True + >>> iniplist('192.168.0.45',['192.168.0.*']) + True + """ + ipnum = addr2bin(ipaddr) + for pat in iplist: + p = pat.split('/',1) + if ip4re.match(p[0]): + if len(p) > 1: + n = int(p[1]) + else: + n = 32 + if cidr(addr2bin(p[0]),n) == cidr(ipnum,n): + return True + elif fnmatchcase(ipaddr,pat): + return True + return False + +def parse_addr(t): + """Split email into user,domain. + + >>> parse_addr('user@example.com') + ['user', 'example.com'] + >>> parse_addr('"user@example.com"') + ['user@example.com'] + >>> parse_addr('"user@bar"@example.com') + ['user@bar', 'example.com'] + >>> parse_addr('foo') + ['foo'] + """ + if t.startswith('<') and t.endswith('>'): t = t[1:-1] + if t.startswith('"'): + if t.endswith('"'): return [t[1:-1]] + pos = t.find('"@') + if pos > 0: return [t[1:pos],t[pos+2:]] + return t.split('@') diff --git a/bms.py b/bms.py index 164f17370f51eaafca8dba60f46e34cc623c47c7..c102f2f1d0e374e661609075000b1102f9c8b00b 100644 --- a/bms.py +++ b/bms.py @@ -1,6 +1,9 @@ #!/usr/bin/env python # A simple milter that has grown quite a bit. # $Log$ +# Revision 1.79 2007/01/05 21:25:40 customdesigned +# Move AddrCache to Milter package. +# # Revision 1.78 2007/01/04 18:01:10 customdesigned # Do plain CBV when template missing. # @@ -53,6 +56,7 @@ import gc import anydbm import Milter.dsn as dsn from Milter.dynip import is_dynip as dynip +from Milter.utils import iniplist,parse_addr,ip4re from fnmatch import fnmatchcase from email.Header import decode_header @@ -77,8 +81,6 @@ except: SES = None try: import spf except: spf = None -ip4re = re.compile(r'^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$') - # Sometimes, MTAs reply to our DSN. We recognize this type of reply/DSN # and check for the original recipient SRS encoded in Message-ID. # If found, we blacklist that recipient. @@ -362,25 +364,6 @@ def read_config(list): srs_domain.add(cp.getdefault('srs','fwdomain')) banned_users = cp.getlist('srs','banned_users') -def parse_addr(t): - """Split email into user,domain. - - >>> parse_addr('user@example.com') - ['user', 'example.com'] - >>> parse_addr('"user@example.com"') - ['user@example.com'] - >>> parse_addr('"user@bar"@example.com') - ['user@bar', 'example.com'] - >>> parse_addr('foo') - ['foo'] - """ - if t.startswith('<') and t.endswith('>'): t = t[1:-1] - if t.startswith('"'): - if t.endswith('"'): return [t[1:-1]] - pos = t.find('"@') - if pos > 0: return [t[1:pos],t[pos+2:]] - return t.split('@') - def parse_header(val): """Decode headers gratuitously encoded to hide the content. """ @@ -480,39 +463,6 @@ class SPFPolicy(object): policy = 'OK' return policy -# from spf.py -def addr2bin(str): - "Convert a string IPv4 address into an unsigned integer." - return struct.unpack("!L", socket.inet_aton(str))[0] - -MASK = 0xFFFFFFFFL - -def cidr(i,n): - return ~(MASK >> n) & MASK & i - -def iniplist(ipaddr,iplist): - """Return whether ip is in cidr list - >>> iniplist('66.179.26.146',['127.0.0.1','66.179.26.128/26']) - True - >>> iniplist('127.0.0.1',['127.0.0.1','66.179.26.128/26']) - True - >>> iniplist('192.168.0.45',['192.168.0.*']) - True - """ - ipnum = addr2bin(ipaddr) - for pat in iplist: - p = pat.split('/',1) - if ip4re.match(p[0]): - if len(p) > 1: - n = int(p[1]) - else: - n = 32 - if cidr(addr2bin(p[0]),n) == cidr(ipnum,n): - return True - elif fnmatchcase(ipaddr,pat): - return True - return False - from Milter.cache import AddrCache cbv_cache = AddrCache(renew=7) diff --git a/spfmilter.py b/spfmilter.py index 4d9877a62ab2bab061031b894062f702c60f263b..b04b3175b991328f2434b144f9774d5ac46dfa44 100644 --- a/spfmilter.py +++ b/spfmilter.py @@ -14,6 +14,8 @@ import struct import socket import syslog +from Milter.utils import iniplist,parse_addr + syslog.openlog('spfmilter',0,syslog.LOG_MAIL) # list of trusted forwarder domains. An SPF record for a forwarder @@ -27,60 +29,7 @@ trusted_relay = [] socketname = "/var/run/milter/spfmiltersock" #socketname = os.getenv("HOME") + "/pythonsock" - -ip4re = re.compile(r'^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$') - -# from spf.py -def addr2bin(str): - "Convert a string IPv4 address into an unsigned integer." - return struct.unpack("!L", socket.inet_aton(str))[0] - -MASK = 0xFFFFFFFFL - -def cidr(i,n): - return ~(MASK >> n) & MASK & i - -def iniplist(ipaddr,iplist): - """Return whether ip is in cidr list - >>> iniplist('66.179.26.146',['127.0.0.1','66.179.26.128/26']) - True - >>> iniplist('127.0.0.1',['127.0.0.1','66.179.26.128/26']) - True - >>> iniplist('192.168.0.45',['192.168.0.*']) - True - """ - ipnum = addr2bin(ipaddr) - for pat in iplist: - p = pat.split('/',1) - if ip4re.match(p[0]): - if len(p) > 1: - n = int(p[1]) - else: - n = 32 - if cidr(addr2bin(p[0]),n) == cidr(ipnum,n): - return True - elif fnmatchcase(ipaddr,pat): - return True - return False - -def parse_addr(t): - """Split email into user,domain. - - >>> parse_addr('user@example.com') - ['user', 'example.com'] - >>> parse_addr('"user@example.com"') - ['user@example.com'] - >>> parse_addr('"user@bar"@example.com') - ['user@bar', 'example.com'] - >>> parse_addr('foo') - ['foo'] - """ - if t.startswith('<') and t.endswith('>'): t = t[1:-1] - if t.startswith('"'): - if t.endswith('"'): return [t[1:-1]] - pos = t.find('"@') - if pos > 0: return [t[1:pos],t[pos+2:]] - return t.split('@') +miltername = "pyspffilter" class spfMilter(Milter.Milter): "Milter to check SPF." @@ -221,11 +170,11 @@ if __name__ == "__main__": Milter.set_flags(Milter.CHGHDRS + Milter.ADDHDRS) print """To use this with sendmail, add the following to sendmail.cf: -O InputMailFilters=pyspffilter -Xpyspffilter, S=local:%s +O InputMailFilters=%s +X%s, S=local:%s See the sendmail README for libmilter. -sample spfmilter startup""" % socketname +sample spfmilter startup""" % (miltername,miltername,socketname) sys.stdout.flush() Milter.runmilter("pyspffilter",socketname,240) print "sample spfmilter shutdown"