case_sensitive_localpart option, more delayed bounce heuristics,

optional smart_alias section.
This commit is contained in:
Stuart Gathman
2006-10-01 01:44:06 +00:00
parent 2fe8fa8813
commit 33aeefa19f
5 changed files with 68 additions and 24 deletions
+7 -2
View File
@@ -1,6 +1,11 @@
Here is a history of user visible changes to Python milter.
0.8.6 Delay reject of unsigned RCPT for postmaster and abuse only
0.8.6 Support CBV timeout
Support fail template, headers in templates
Create GOSSiP record only when connection will procede to DATA.
More SPF lax heuristics
Don't require SPF pass for white/black listing mail from trusted relay.
Support localpart wildcard for white and black lists.
Delay reject of unsigned RCPT for postmaster and abuse only
Fix dsn reporting of hard permerror
Resolve FIXME for wrap_close in miltermodule.c
Add Message-ID to DSNs
+10 -4
View File
@@ -1,10 +1,13 @@
Reporting explanation for failure should show source of sender
provided explanation.
Reports PROBATION even when rejecting message (works, but confusing in log).
Bug in Auto-whitelist. Recent Auto-whitelist doesn't override expired entry.
Delayed_failure detection needs to handle multi-line header fields. Also,
delayed_failure should be recognized when addressed to postmaster@helodomain
Idea: load headers into message object, and use header array.
DONE Delayed_failure detection needs to handle multi-line header fields.
Also, delayed_failure should be recognized when addressed to
postmaster@helodomain
Need to use wildcards in blacklist.log: *.madcowsrecord.net
Need to exclude emails like !*-admin@example.com in whitelist_sender.
@@ -42,7 +45,10 @@ data structure as autowhitelist.log. Add emails blacklisted via CBV
so that they are remembered across milter restarts.
Make all dictionaries work like honeypot. Do not train as ham unless
whitelisted. Train on blacklisted messages, or spam feedback.
whitelisted. Train on blacklisted messages, or spam feedback. This
can be called Train On Error. Should be possible to startup
with training on everything to get dictionary built fast, then switch
to train on error to minimize labor.
Allow unsigned DSNs from selected domains (that don't accept signed MFROM,
e.g. verizon.net).
+42 -15
View File
@@ -1,6 +1,9 @@
#!/usr/bin/env python
# A simple milter that has grown quite a bit.
# $Log$
# Revision 1.66 2006/07/26 16:42:26 customdesigned
# Support CBV timeout
#
# Revision 1.65 2006/06/21 22:22:00 customdesigned
# Handle multi-line headers in delayed dsns.
#
@@ -215,7 +218,7 @@ import mime
import email.Errors
import Milter
import tempfile
import ConfigParser
from ConfigParser import ConfigParser
import time
import socket
import struct
@@ -254,6 +257,7 @@ ip4re = re.compile(r'^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$')
# If found, we blacklist that recipient.
subjpats = (
r'^failure notice',
r'^subjectbounce',
r'^returned mail',
r'^undeliver',
r'^delivery\b.*\bfailure',
@@ -281,6 +285,7 @@ block_forward = {}
hide_path = ()
log_headers = False
block_chinese = False
case_sensitive_localpart = False
spam_words = ()
porn_words = ()
banned_exts = mime.extlist.split(',')
@@ -328,8 +333,15 @@ milter_log = logging.getLogger('milter')
if gossip:
gossip_node = Gossip('gossip4.db',120)
class MilterConfigParser(ConfigParser.ConfigParser):
class MilterConfigParser(ConfigParser):
def __init__(self,defaults):
ConfigParser.__init__(self)
self.defaults = defaults
def get(self,sect,opt):
return ConfigParser.get(self,sect,opt,vars=self.defaults)
def getlist(self,sect,opt):
if self.has_option(sect,opt):
return [q.strip() for q in self.get(sect,opt).split(',')]
@@ -343,11 +355,11 @@ class MilterConfigParser(ConfigParser.ConfigParser):
for q in s.split(','):
q = q.strip()
if q.startswith('file:'):
domain = q[5:]
domain = q[5:].lower()
d[domain] = d.setdefault(domain,[]) + open(domain,'r').read().split()
else:
user,domain = q.split('@')
d.setdefault(domain,[]).append(user)
d.setdefault(domain.lower(),[]).append(user)
return d
def getaddrdict(self,sect,opt):
@@ -390,7 +402,8 @@ def read_config(list):
'reject_noptr': 'no',
'supply_sender': 'no',
'best_guess': 'no',
'dspam_internal': 'yes'
'dspam_internal': 'yes',
'case_sensitive_localpart': 'no'
})
cp.read(list)
@@ -398,6 +411,7 @@ def read_config(list):
tempfile.tempdir = cp.get('milter','tempdir')
global socketname, timeout, check_user, log_headers
global internal_connect, internal_domains, trusted_relay, hello_blacklist
global case_sensitive_localpart
socketname = cp.get('milter','socket')
timeout = cp.getint('milter','timeout')
check_user = cp.getaddrset('milter','check_user')
@@ -406,6 +420,7 @@ def read_config(list):
internal_domains = cp.getlist('milter','internal_domains')
trusted_relay = cp.getlist('milter','trusted_relay')
hello_blacklist = cp.getlist('milter','hello_blacklist')
case_sensitive_localpart = cp.getboolean('milter','case_sensitive_localpart')
# defang section
global scan_rfc822, scan_html, block_chinese, scan_zip, block_forward
@@ -439,13 +454,19 @@ def read_config(list):
if wiretap_dest: wiretap_dest = '<%s>' % wiretap_dest
global smart_alias
for sa in cp.getlist('wiretap','smart_alias'):
sm = cp.getlist('wiretap',sa)
for sa,v in [
(k,cp.get('wiretap',k)) for k in cp.getlist('wiretap','smart_alias')
] + (cp.has_section('smart_alias') and cp.items('smart_alias',True) or []):
print sa,v
sm = [q.strip() for q in v.split(',')]
if len(sm) < 2:
milter_log.warning('malformed smart alias: %s',sa)
continue
if len(sm) == 2: sm.append(sa)
key = (sm[0],sm[1])
if case_sensitive_localpart:
key = (sm[0],sm[1])
else:
key = (sm[0].lower(),sm[1].lower())
smart_alias[key] = sm[2:]
# dspam section
@@ -829,12 +850,18 @@ class bmsMilter(Milter.Milter):
def smart_alias(self,to):
if smart_alias:
t = parse_addr(to.lower())
if case_sensitive_localpart:
t = parse_addr(to)
else:
t = parse_addr(to.lower())
if len(t) == 2:
ct = '@'.join(t)
else:
ct = t[0]
cf = self.canon_from
if case_sensitive_localpart:
cf = self.canon_from
else:
cf = self.canon_from.lower()
cf0 = cf.split('@',1)
if len(cf0) == 2:
cf0 = '@' + cf0[1]
@@ -1582,10 +1609,10 @@ class bmsMilter(Milter.Milter):
if ln[0].isspace() and ln[0] != '\n':
lastln += ln
continue
name,val = lastln.rstrip().split(None,1)
pos = val.find('<SRS')
if pos >= 0:
try:
try:
name,val = lastln.rstrip().split(None,1)
pos = val.find('<SRS')
if pos >= 0:
sender = srs.reverse(val[pos+1:-1])
cbv_cache[sender] = 500,self.delayed_failure,time.time()
try:
@@ -1597,7 +1624,7 @@ class bmsMilter(Milter.Milter):
self.tempname = None
self.log('BLACKLIST:',sender,fname)
return Milter.DISCARD
except: continue
except: continue
lnl = ln.lower()
for k in ('message-id','x-mailer','sender'):
if lnl.startswith(k):
+8 -3
View File
@@ -31,6 +31,9 @@ log_headers = 0
# Reject mail for domains mentioned unless user is mentioned here also
;check_user = joe@mycorp.com, mary@mycorp.com, file:bigcorp.com
# Treat localparts in milter.cfg as case-insensitive
case_sensitive_localpart = true
# features intended to filter or block incoming mail
[defang]
@@ -125,11 +128,13 @@ blind = 1
# discard outgoing mail without alerting sender
# can be used in conjunction with wiretap to censor outgoing mail
;discard_users = canned@bigcorp.com
#
# smart aliases trigger on both sender and recipient
# alias = sender, recipient[, destination]
#
;smart_alias = copycust,walter,spy1,spy2
# multiple wiretap monitors
[smart_alias]
# multiple wiretap monitors. Smart aliases are applied after wiretap.
;spy1 = disloyal@bigcorp.com,spy@bigcorp.com
;spy2 = bigmouth@bigcorp.com,spy@bigcorp.com
# mail from client@clientcorp.com to sue@bigcorp.com is redirected to
@@ -142,7 +147,7 @@ blind = 1
;walter1 = cust@othercorp.com,walter@bigcorp.com,boss@bigcorp.com,
; walter@bigcorp.com
;bulk = soruce@telex.com,bob@jsconnor.com
;bulk = soruce@telex.com,larry@jsconnor.com
;bulk1 = soruce@telex.com,larry@jsconnor.com,bulk
# See http://bmsi.com/python/dspam.html
[dspam]
+1
View File
@@ -177,6 +177,7 @@ rm -rf $RPM_BUILD_ROOT
%changelog
* Tue May 23 2006 Stuart Gathman <stuart@bmsi.com> 0.8.6-2
- Support CBV timeout
- Support fail template, headers in templates
- Create GOSSiP record only when connection will procede to DATA.
- More SPF lax heuristics