Auto whitelist feature.
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# A simple milter that has grown quite a bit.
|
# A simple milter that has grown quite a bit.
|
||||||
# $Log$
|
# $Log$
|
||||||
|
# Revision 1.30 2005/10/12 16:36:30 customdesigned
|
||||||
|
# Release 0.8.3
|
||||||
|
#
|
||||||
# Revision 1.29 2005/10/11 22:50:07 customdesigned
|
# Revision 1.29 2005/10/11 22:50:07 customdesigned
|
||||||
# Always check HELO except for SPF pass, temperror.
|
# Always check HELO except for SPF pass, temperror.
|
||||||
#
|
#
|
||||||
@@ -352,9 +355,8 @@ spf_best_guess = False
|
|||||||
spf_reject_noptr = False
|
spf_reject_noptr = False
|
||||||
supply_sender = False
|
supply_sender = False
|
||||||
access_file = None
|
access_file = None
|
||||||
time_format = '%Y%b%d %H:%M:%S %Z'
|
|
||||||
timeout = 600
|
timeout = 600
|
||||||
cbv_cache = {}
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
stream=sys.stdout,
|
stream=sys.stdout,
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
@@ -363,19 +365,6 @@ logging.basicConfig(
|
|||||||
)
|
)
|
||||||
milter_log = logging.getLogger('milter')
|
milter_log = logging.getLogger('milter')
|
||||||
|
|
||||||
try:
|
|
||||||
too_old = time.time() - 7*24*60*60 # 7 days
|
|
||||||
for ln in open('send_dsn.log'):
|
|
||||||
try:
|
|
||||||
rcpt,ts = ln.strip().split(None,1)
|
|
||||||
l = time.strptime(ts,time_format)
|
|
||||||
t = time.mktime(l)
|
|
||||||
if t > too_old:
|
|
||||||
cbv_cache[rcpt] = None
|
|
||||||
except:
|
|
||||||
cbv_cache[ln.strip()] = None
|
|
||||||
except IOError: pass
|
|
||||||
|
|
||||||
class MilterConfigParser(ConfigParser.ConfigParser):
|
class MilterConfigParser(ConfigParser.ConfigParser):
|
||||||
|
|
||||||
def getlist(self,sect,opt):
|
def getlist(self,sect,opt):
|
||||||
@@ -662,6 +651,47 @@ class SPFPolicy(object):
|
|||||||
policy = 'OK'
|
policy = 'OK'
|
||||||
return policy
|
return policy
|
||||||
|
|
||||||
|
class AddrCache(object):
|
||||||
|
time_format = '%Y%b%d %H:%M:%S %Z'
|
||||||
|
|
||||||
|
def load(self,fname,age=7):
|
||||||
|
self.fname = fname
|
||||||
|
cache = {}
|
||||||
|
self.cache = cache
|
||||||
|
try:
|
||||||
|
too_old = time.time() - age*24*60*60 # max age in days
|
||||||
|
for ln in open(self.fname):
|
||||||
|
try:
|
||||||
|
rcpt,ts = ln.strip().split(None,1)
|
||||||
|
l = time.strptime(ts,AddrCache.time_format)
|
||||||
|
t = time.mktime(l)
|
||||||
|
if t > too_old:
|
||||||
|
cache[rcpt] = None
|
||||||
|
except:
|
||||||
|
cache[ln.strip()] = None
|
||||||
|
except IOError: pass
|
||||||
|
|
||||||
|
def has_key(self,sender):
|
||||||
|
return self.cache.has_key(sender)
|
||||||
|
|
||||||
|
def __getitem__(self,sender):
|
||||||
|
return self.cache[sender]
|
||||||
|
|
||||||
|
def __setitem__(self,sender,res):
|
||||||
|
cached = sender in self.cache
|
||||||
|
self.cache[sender] = res
|
||||||
|
if not cached and not res:
|
||||||
|
s = time.strftime(AddrCache.time_format,time.localtime())
|
||||||
|
print >>open(self.fname,'a'),sender,s # log who we sent DSNs to
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.cache)
|
||||||
|
|
||||||
|
cbv_cache = AddrCache()
|
||||||
|
cbv_cache.load('send_dsn.log',age=7)
|
||||||
|
auto_whitelist = AddrCache()
|
||||||
|
auto_whitelist.load('auto_whitelist.log',age=30)
|
||||||
|
|
||||||
class bmsMilter(Milter.Milter):
|
class bmsMilter(Milter.Milter):
|
||||||
"""Milter to replace attachments poisonous to Windows with a WARNING message,
|
"""Milter to replace attachments poisonous to Windows with a WARNING message,
|
||||||
check SPF, and other anti-forgery features, and implement wiretapping
|
check SPF, and other anti-forgery features, and implement wiretapping
|
||||||
@@ -987,6 +1017,9 @@ class bmsMilter(Milter.Milter):
|
|||||||
return Milter.TEMPFAIL
|
return Milter.TEMPFAIL
|
||||||
self.add_header('Received-SPF',q.get_header(res,receiver))
|
self.add_header('Received-SPF',q.get_header(res,receiver))
|
||||||
self.spf = q
|
self.spf = q
|
||||||
|
if self.dspam and not self.internal_connection and res == 'pass':
|
||||||
|
if auto_whitelist.has_key(self.canon_from):
|
||||||
|
self.dspam = False
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
# hide_path causes a copy of the message to be saved - until we
|
# hide_path causes a copy of the message to be saved - until we
|
||||||
@@ -998,8 +1031,9 @@ class bmsMilter(Milter.Milter):
|
|||||||
self.log('DISCARD: RCPT TO:',to,str)
|
self.log('DISCARD: RCPT TO:',to,str)
|
||||||
return Milter.DISCARD
|
return Milter.DISCARD
|
||||||
self.log("rcpt to",to,str)
|
self.log("rcpt to",to,str)
|
||||||
t = parse_addr(to.lower())
|
t = parse_addr(to)
|
||||||
if len(t) == 2:
|
if len(t) == 2:
|
||||||
|
t[1] = t[1].lower()
|
||||||
user,domain = t
|
user,domain = t
|
||||||
if self.is_bounce and srs and domain in srs_domain:
|
if self.is_bounce and srs and domain in srs_domain:
|
||||||
oldaddr = '@'.join(parse_addr(to))
|
oldaddr = '@'.join(parse_addr(to))
|
||||||
@@ -1027,7 +1061,8 @@ class bmsMilter(Milter.Milter):
|
|||||||
self.data_allowed = not srs_reject_spoofed
|
self.data_allowed = not srs_reject_spoofed
|
||||||
|
|
||||||
# non DSN mail to SRS address will bounce due to invalid local part
|
# non DSN mail to SRS address will bounce due to invalid local part
|
||||||
self.recipients.append('@'.join(t))
|
canon_to = '@'.join(t)
|
||||||
|
self.recipients.append(canon_to)
|
||||||
users = check_user.get(domain)
|
users = check_user.get(domain)
|
||||||
if self.discard:
|
if self.discard:
|
||||||
self.del_recipient(to)
|
self.del_recipient(to)
|
||||||
@@ -1043,6 +1078,14 @@ class bmsMilter(Milter.Milter):
|
|||||||
self.hidepath = True
|
self.hidepath = True
|
||||||
if not domain in dspam_reject:
|
if not domain in dspam_reject:
|
||||||
self.reject_spam = False
|
self.reject_spam = False
|
||||||
|
if self.internal_connection:
|
||||||
|
if internal_domains:
|
||||||
|
for pat in internal_domains:
|
||||||
|
if fnmatchcase(domain,pat): break
|
||||||
|
else:
|
||||||
|
auto_whitelist[canon_to] = None
|
||||||
|
else:
|
||||||
|
auto_whitelist[canon_to] = None
|
||||||
self.smart_alias(to)
|
self.smart_alias(to)
|
||||||
#rcpt = self.getsymval("{rcpt_addr}")
|
#rcpt = self.getsymval("{rcpt_addr}")
|
||||||
#self.log("rcpt-addr",rcpt);
|
#self.log("rcpt-addr",rcpt);
|
||||||
@@ -1161,7 +1204,7 @@ class bmsMilter(Milter.Milter):
|
|||||||
self.fp.write("%s: %s\n" % (name,val)) # add new headers to buffer
|
self.fp.write("%s: %s\n" % (name,val)) # add new headers to buffer
|
||||||
self.fp.write("\n") # terminate headers
|
self.fp.write("\n") # terminate headers
|
||||||
# log when neither sender nor from domains matches mail from domain
|
# log when neither sender nor from domains matches mail from domain
|
||||||
if supply_sender and self.mailfrom != '<>':
|
if supply_sender and self.mailfrom != '<>' and not self.internal_connection:
|
||||||
mf_domain = self.canon_from.split('@')[-1]
|
mf_domain = self.canon_from.split('@')[-1]
|
||||||
self.fp.seek(0)
|
self.fp.seek(0)
|
||||||
msg = rfc822.Message(self.fp)
|
msg = rfc822.Message(self.fp)
|
||||||
@@ -1315,6 +1358,8 @@ class bmsMilter(Milter.Milter):
|
|||||||
force_result=dspam.DSR_ISSPAM)
|
force_result=dspam.DSR_ISSPAM)
|
||||||
self.log("HONEYPOT:",rcpt)
|
self.log("HONEYPOT:",rcpt)
|
||||||
return Milter.DISCARD
|
return Milter.DISCARD
|
||||||
|
#if not self.dspam:
|
||||||
|
# FIXME: tag, but force as ham
|
||||||
txt = ds.check_spam(user,txt,self.recipients)
|
txt = ds.check_spam(user,txt,self.recipients)
|
||||||
if not txt:
|
if not txt:
|
||||||
# DISCARD if quarrantined for any recipient. It
|
# DISCARD if quarrantined for any recipient. It
|
||||||
@@ -1507,9 +1552,6 @@ class bmsMilter(Milter.Milter):
|
|||||||
self.setreply('550','5.7.1',*desc.splitlines())
|
self.setreply('550','5.7.1',*desc.splitlines())
|
||||||
return Milter.REJECT
|
return Milter.REJECT
|
||||||
cbv_cache[sender] = res
|
cbv_cache[sender] = res
|
||||||
if not cached:
|
|
||||||
s = time.strftime(time_format,time.localtime())
|
|
||||||
print >>open('send_dsn.log','a'),sender,s # log who we sent DSNs to
|
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user