Handle perverse MFROM quoting.
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.24 2005/08/18 03:36:54 customdesigned
|
||||||
|
# Don't innoculate with SCREENED mail.
|
||||||
|
#
|
||||||
# Revision 1.23 2005/08/17 19:35:27 customdesigned
|
# Revision 1.23 2005/08/17 19:35:27 customdesigned
|
||||||
# Send DSN before adding message to quarantine.
|
# Send DSN before adding message to quarantine.
|
||||||
#
|
#
|
||||||
@@ -334,7 +337,7 @@ time_format = '%Y%b%d %H:%M:%S %Z'
|
|||||||
timeout = 600
|
timeout = 600
|
||||||
cbv_cache = {}
|
cbv_cache = {}
|
||||||
try:
|
try:
|
||||||
too_old = time.time() - 30*24*60*60 # 30 days
|
too_old = time.time() - 7*24*60*60 # 7 days
|
||||||
for ln in open('send_dsn.log'):
|
for ln in open('send_dsn.log'):
|
||||||
try:
|
try:
|
||||||
rcpt,ts = ln.strip().split(None,1)
|
rcpt,ts = ln.strip().split(None,1)
|
||||||
@@ -515,7 +518,22 @@ def read_config(list):
|
|||||||
#print srs_domain
|
#print srs_domain
|
||||||
|
|
||||||
def parse_addr(t):
|
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('<') 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('@')
|
return t.split('@')
|
||||||
|
|
||||||
def parse_header(val):
|
def parse_header(val):
|
||||||
@@ -733,7 +751,7 @@ class bmsMilter(Milter.Milter):
|
|||||||
'SPF fail: see http://openspf.com/why.html?sender=%s&ip=%s' % (q.s,q.i))
|
'SPF fail: see http://openspf.com/why.html?sender=%s&ip=%s' % (q.s,q.i))
|
||||||
res,code,txt = q.check()
|
res,code,txt = q.check()
|
||||||
q.result = res
|
q.result = res
|
||||||
if res == 'unknown' and q.perm_error:
|
if res == 'unknown' and q.perm_error and q.perm_error.ext:
|
||||||
self.cbv_needed = q # report SPF syntax error to sender
|
self.cbv_needed = q # 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
|
||||||
@@ -999,7 +1017,21 @@ 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
|
||||||
self.fp.seek(0)
|
self.fp.seek(0)
|
||||||
|
# log when neither sender nor from domains matches mail from domain
|
||||||
|
mf_domain = self.canon_from.split('@')[-1]
|
||||||
|
msg = rfc822.Message(self.fp)
|
||||||
|
for rn,hf in msg.getaddrlist('from')+msg.getaddrlist('sender'):
|
||||||
|
t = parse_addr(hf)
|
||||||
|
if len(t) == 2 and t[1].lower() == mf_domain:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.log("NOTE: MFROM domain doesn't match From or Sender");
|
||||||
|
for f in msg.getallmatchingheaders('from') \
|
||||||
|
+ msg.getallmatchingheaders('sender'):
|
||||||
|
self.log(f)
|
||||||
|
del msg
|
||||||
# copy headers to a temp file for scanning the body
|
# copy headers to a temp file for scanning the body
|
||||||
|
self.fp.seek(0)
|
||||||
headers = self.fp.getvalue()
|
headers = self.fp.getvalue()
|
||||||
self.fp.close()
|
self.fp.close()
|
||||||
fd,fname = tempfile.mkstemp(".defang")
|
fd,fname = tempfile.mkstemp(".defang")
|
||||||
|
|||||||
+6
-2
@@ -1,4 +1,5 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
import doctest
|
||||||
import Milter
|
import Milter
|
||||||
import bms
|
import bms
|
||||||
import mime
|
import mime
|
||||||
@@ -22,7 +23,7 @@ class TestMilter(bms.bmsMilter):
|
|||||||
|
|
||||||
def getsymval(self,name):
|
def getsymval(self,name):
|
||||||
if name == 'j': return 'test.milter.org'
|
if name == 'j': return 'test.milter.org'
|
||||||
return bms.bmsMilter.getsymval(self,name)
|
return ''
|
||||||
|
|
||||||
def replacebody(self,chunk):
|
def replacebody(self,chunk):
|
||||||
if self._body:
|
if self._body:
|
||||||
@@ -284,7 +285,10 @@ class BMSMilterTestCase(unittest.TestCase):
|
|||||||
# self.failUnless(rc == Milter.REJECT)
|
# self.failUnless(rc == Milter.REJECT)
|
||||||
# milter.close();
|
# milter.close();
|
||||||
|
|
||||||
def suite(): return unittest.makeSuite(BMSMilterTestCase,'test')
|
def suite():
|
||||||
|
s = unittest.makeSuite(BMSMilterTestCase,'test')
|
||||||
|
s.addTest(doctest.DocTestSuite(bms))
|
||||||
|
return s
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
|
|||||||
Reference in New Issue
Block a user