CBV policy sends no DSN. DSN policy sends DSN.

This commit is contained in:
Stuart Gathman
2008-08-06 00:52:38 +00:00
parent be3f463450
commit 28a0e551bd
+22 -45
View File
@@ -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.124 2008/08/05 18:04:06 customdesigned
# Send quarantine DSN to SPF PASS only.
#
# Revision 1.123 2008/07/29 21:59:29 customdesigned # Revision 1.123 2008/07/29 21:59:29 customdesigned
# Parse ESMTP params # Parse ESMTP params
# #
@@ -942,17 +945,13 @@ class bmsMilter(Milter.Milter):
res,code,txt = q.check() res,code,txt = q.check()
q.result = res q.result = res
if res in ('unknown','permerror') and q.perm_error and q.perm_error.ext: if res in ('unknown','permerror') and q.perm_error and q.perm_error.ext:
self.cbv_needed = (q,res) # report SPF syntax error to sender self.cbv_needed = (q,'permerror') # report SPF syntax error to sender
res,code,txt = q.perm_error.ext # extended (lax processing) result res,code,txt = q.perm_error.ext # extended (lax processing) result
txt = 'EXT: ' + txt txt = 'EXT: ' + txt
p = SPFPolicy(q.s) p = SPFPolicy(q.s)
# FIXME: try:finally to close policy db, or reuse with lock # FIXME: try:finally to close policy db, or reuse with lock
if res in ('error','temperror'): if res in ('error','temperror'):
policy = p.getTempErrorPolicy() if self.need_cbv(p.getTempErrorPolicy(),q,'temperror'):
if policy == 'CBV':
if self.mailfrom != '<>':
self.cbv_needed = (q,res)
elif policy != 'OK':
self.log('TEMPFAIL: SPF %s %i %s' % (res,code,txt)) self.log('TEMPFAIL: SPF %s %i %s' % (res,code,txt))
self.setreply(str(code),'4.3.0',txt, self.setreply(str(code),'4.3.0',txt,
'We cannot accept your email until the DNS server for %s' % q.o, 'We cannot accept your email until the DNS server for %s' % q.o,
@@ -1006,11 +1005,9 @@ class bmsMilter(Milter.Milter):
and hres != 'pass': and hres != 'pass':
# this bad boy has no credentials whatsoever # this bad boy has no credentials whatsoever
policy = p.getNonePolicy() policy = p.getNonePolicy()
if policy == 'CBV': if policy in ('CBV','DNS'):
if self.mailfrom != '<>':
self.cbv_needed = (q,ores) # accept, but inform sender via DSN
self.offenses = 3 # ban ip if any bad recipient self.offenses = 3 # ban ip if any bad recipient
elif policy != 'OK': if self.need_cbv(policy,q,'strike3'):
self.log('REJECT: no PTR, HELO or SPF') self.log('REJECT: no PTR, HELO or SPF')
self.setreply('550','5.7.1', self.setreply('550','5.7.1',
"You must have a valid HELO or publish SPF: http://www.openspf.org ", "You must have a valid HELO or publish SPF: http://www.openspf.org ",
@@ -1021,11 +1018,7 @@ class bmsMilter(Milter.Milter):
) )
return self.offense() # ban ip if too many bad MFROMs return self.offense() # ban ip if too many bad MFROMs
if res in ('deny', 'fail'): if res in ('deny', 'fail'):
policy = p.getFailPolicy() if self.need_cbv(p.getFailPolicy(),q,'fail'):
if policy == 'CBV':
if self.mailfrom != '<>':
self.cbv_needed = (q,res)
elif policy != 'OK':
self.log('REJECT: SPF %s %i %s' % (res,code,txt)) self.log('REJECT: SPF %s %i %s' % (res,code,txt))
self.setreply(str(code),'5.7.1',txt) self.setreply(str(code),'5.7.1',txt)
# A proper SPF fail error message would read: # A proper SPF fail error message would read:
@@ -1033,11 +1026,7 @@ class bmsMilter(Milter.Milter):
# "forged.org" in the sender address. Contact <postmaster@forged.org>. # "forged.org" in the sender address. Contact <postmaster@forged.org>.
return Milter.REJECT return Milter.REJECT
if res == 'softfail': if res == 'softfail':
policy = p.getSoftfailPolicy() if self.need_cbv(p.getSoftfailPolicy(),q,'softfail'):
if policy == 'CBV':
if self.mailfrom != '<>':
self.cbv_needed = (q,res)
elif policy != 'OK':
self.log('REJECT: SPF %s %i %s' % (res,code,txt)) self.log('REJECT: SPF %s %i %s' % (res,code,txt))
self.setreply('550','5.7.1', self.setreply('550','5.7.1',
'SPF softfail: If you get this Delivery Status Notice, your email', 'SPF softfail: If you get this Delivery Status Notice, your email',
@@ -1048,12 +1037,7 @@ class bmsMilter(Milter.Milter):
) )
return Milter.REJECT return Milter.REJECT
if res == 'neutral': if res == 'neutral':
policy = p.getNeutralPolicy() if self.need_cbv(p.getNeutralPolicy(),q,'neutral'):
if policy == 'CBV':
if self.mailfrom != '<>':
self.cbv_needed = (q,res)
# FIXME: this makes Received-SPF show wrong result
elif policy != 'OK':
self.log('REJECT: SPF neutral for',q.s) self.log('REJECT: SPF neutral for',q.s)
self.setreply('550','5.7.1', self.setreply('550','5.7.1',
'mail from %s must pass SPF: http://openspf.org/why.html' % q.o, 'mail from %s must pass SPF: http://openspf.org/why.html' % q.o,
@@ -1065,11 +1049,7 @@ class bmsMilter(Milter.Milter):
) )
return Milter.REJECT return Milter.REJECT
if res in ('unknown','permerror'): if res in ('unknown','permerror'):
policy = p.getPermErrorPolicy() if self.need_cbv(p.getPermErrorPolicy(),q,'permerror'):
if policy == 'CBV':
if self.mailfrom != '<>':
self.cbv_needed = (q,res)
elif policy != 'OK':
self.log('REJECT: SPF %s %i %s' % (res,code,txt)) self.log('REJECT: SPF %s %i %s' % (res,code,txt))
# latest SPF draft recommends 5.5.2 instead of 5.7.1 # latest SPF draft recommends 5.5.2 instead of 5.7.1
self.setreply(str(code),'5.5.2',txt, self.setreply(str(code),'5.5.2',txt,
@@ -1648,24 +1628,21 @@ class bmsMilter(Milter.Milter):
self.log("TRAINSPAM:",screener,'X-Dspam-Score: %f' % ds.probability) self.log("TRAINSPAM:",screener,'X-Dspam-Score: %f' % ds.probability)
def do_needed_cbv(self,msg): def do_needed_cbv(self,msg):
q,res = self.cbv_needed q,template_name = self.cbv_needed
if res == 'softfail':
template_name = 'softfail'
elif res in ('fail','deny'):
template_name = 'fail'
elif res in ('unknown','permerror'):
template_name = 'permerror'
elif res == 'neutral':
#template_name = 'neutral'
template_name = None
elif res in ('error','temperror'):
template_name = 'temperror'
else:
template_name = 'strike3'
rc = self.send_dsn(q,msg,template_name) rc = self.send_dsn(q,msg,template_name)
self.cbv_needed = None self.cbv_needed = None
return rc return rc
def need_cbv(self,policy,q,tname):
if policy == 'CBV':
if self.mailfrom != '<>' and not self.cbv_needed:
self.cbv_needed = (q,None)
elif policy == 'DSN':
if self.mailfrom != '<>' and not self.cbv_needed:
self.cbv_needed = (q,tname)
elif policy != 'OK': return True
return False
def eom(self): def eom(self):
if not self.fp: if not self.fp:
return Milter.ACCEPT # no message collected - so no eom processing return Milter.ACCEPT # no message collected - so no eom processing