1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
# Analyze milter log to find abusers
import traceback
import sys
def parse_addr(a):
beg = a.find('<')
end = a.find('>')
if beg >= 0:
if end > beg: return a[beg+1:end]
return a
class Connection(object):
def __init__(self,dt,tm,id,ip=None,conn=None):
self.dt = dt
self.tm = tm
self.id = id
if ip:
_,self.host,self.ip = ip.split(None,2)
elif conn:
self.ip = conn.ip
self.host = conn.host
self.helo = conn.helo
self.subject = None
self.rcpt = []
self.mfrom = None
self.helo = None
self.innoc = []
self.whitelist = False
def connections(fp):
conndict = {}
termdict = {}
for line in fp:
if line.startswith('{'): continue
a = line.split(None,4)
if len(a) < 4: continue
dt,tm,id,op = a[:4]
if (id,op) == ('bms','milter'):
# FIXME: optionally yield all partial connections in conndict
conndict = {}
termdict = {}
continue
if id[0] == '[' and id[-1] == ']':
try:
key = int(id[1:-1])
except:
print >>sys.stderr,'bad id:',line.rstrip()
continue
else: continue
if op == 'connect':
ip = a[4].rstrip()
conn = Connection(dt,tm,id,ip=ip)
conndict[key] = conn
elif op in (
'DISCARD:','TAG:','CBV:','Large','No',
'NOTE:','From:','Sender:','TRAIN:'):
continue
else:
op = op.lower()
try:
conn = conndict[key]
except KeyError:
try:
conn = termdict[key]
del termdict[key]
conndict[key] = conn
except KeyError:
print >>sys.stderr,'key error:',line.rstrip()
continue
try:
if op == 'subject:':
if len(a) > 4:
conn.subject = a[4].rstrip()
elif op == 'innoc:':
conn.innoc.append(a[4].rstrip())
elif op == 'whitelist':
conn.whitelist = True
elif op == 'x-mailer:':
if len(a) > 4:
conn.mailer = a[4].rstrip()
elif op == 'x-guessed-spf:':
conn.spfguess = a[4]
elif op == 'received-spf:':
conn.spfres,conn.spfmsg = a[4].rstrip().split(None,1)
elif op == 'received:':
conn.received = a[4].rstrip()
elif op == 'temp':
_,conn.tempfile = a[4].rstrip().split(None,1)
elif op == 'srs':
_,conn.srsrcpt = a[4].rstrip().split(None,1)
elif op == 'mail':
_,conn.mfrom = a[4].rstrip().split(None,1)
elif op == 'rcpt':
_,rcpt = a[4].rstrip().split(None,1)
conn.rcpt.append(rcpt)
elif op == 'hello':
_,conn.helo = a[4].rstrip().split(None,1)
elif op in ('eom','dspam','abort'):
del conndict[key]
conn.enddt = dt
conn.endtm = tm
conn.result = op
yield conn
termdict[key] = Connection(conn.dt,conn.tm,conn.id,conn=conn)
elif op in ('reject:','dspam:','tempfail:','reject','fail:','honeypot:'):
del conndict[key]
conn.enddt = dt
conn.endtm = tm
conn.result = op
conn.resmsg = a[4].rstrip()
yield conn
termdict[key] = Connection(conn.dt,conn.tm,conn.id,conn=conn)
elif op in ('fp:','spam:'):
del conndict[key]
termdict[key] = Connection(conn.dt,conn.tm,conn.id,conn=conn)
else:
print >>sys.stderr,'unknown op:',line.rstrip()
except Exception:
print >>sys.stderr,'error:',line.rstrip()
traceback.print_exc()
if __name__ == '__main__':
import gzip
for fn in sys.argv[1:]:
if fn.endswith('.gz'):
fp = gzip.open(fn)
else:
fp = open(fn)
for conn in connections(fp):
if conn.rcpt and conn.mfrom:
for r in conn.rcpt:
if r.lower().find('iancarter') > 0: break
else:
if conn.mfrom.lower().find('iancarter') < 0: continue
print >>sys.stderr,conn.result,conn.dt,conn.tm,conn.id,conn.subject,parse_addr(conn.mfrom),
for a in conn.rcpt:
print parse_addr(a),
print
|