case_sensitive_localpart option, more delayed bounce heuristics,
optional smart_alias section.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user