-
Notifications
You must be signed in to change notification settings - Fork 0
/
deepspam4.py
executable file
·186 lines (147 loc) · 5.45 KB
/
deepspam4.py
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#! /usr/bin/python3
import asyncio
import logging
import struct
import time
import traceback
from io import BytesIO
############################################################################################################################################
from eml2str import eml2str
from model import DeepSpam
ds=DeepSpam()
def do_eml(msg,addr):
t=time.time()
text=eml2str(msg,True) # extract subject,bodytext
tokens=ds.tokenized([text])[0]
print(tokens[:160])
res=ds(text)
t=time.time()-t
print("--- %22s result: %8.5f (%5.3f ms)"%(addr,res,1000*t))
if res<0: return b"toosmall"
res+=0.1
# print(res)
# print("%d%%"%(res))
try:
f=open("deepspam2.res","at")
f.write("%3d%%: %s\n"%(res,tokens))
f.close()
except:
pass
if res<2:
return b"ham %d%%"%(res)
if res<10:
return b"maybeham %d%%"%(res)
if res<20:
return b"20ham %d%%"%(res)
if res>98:
return b"spam %d%%"%(res)
if res>90:
return b"maybespam %d%%"%(res)
if res>80:
return b"80spam %d%%"%(res)
return b"dunno %d%%"%(res)
############################################################################################################################################
milterinfo={
b'A': "Abort",
b'B': "Body chunk",
b'C': "Connection information",
b'D': "Define macro",
b'E': "final body chunk (End)",
b'H': "HELO/EHLO",
b'L': "Header",
b'M': "MAIL from",
b'N': "EOH",
b'O': "Option negotation",
b'R': "RCPT to",
b'Q': "QUIT",
b'T': "DATA",
b'U': "Unknown cmd",
b'+': "add recipient",
b'-': "remove recipient",
b'a': "accept",
b'b': "replace body (chunk)",
b'c': "continue",
b'd': "discard",
b'f': "cause a connection failure",
b'h': "add header",
b'i': "insert header",
b'm': "change header",
b'p': "progress",
b'q': "quarantine",
b'r': "reject",
b's': "set-sender",
b't': "tempfail",
b'y': "reply code etc" }
thread_cnt=0
thread_id=1000000000
async def handle_milter(reader, writer):
global thread_id
myid=(thread_id:=thread_id+1)
global thread_cnt
thread_cnt+=1
t0=time.time()
history=[]
eml=[]
addr = writer.get_extra_info('peername')
addrs=str(addr[0])+":"+str(addr[1])
print('%d[%3d] %22s Connect'%(myid,thread_cnt, addrs) )
while True:
try:
# print("# waiting...")
data = await reader.readexactly(4)
packetlen = int(struct.unpack('!I', data)[0])
# print("# pktlen=",packetlen)
data = await reader.readexactly(packetlen)
cmd=data[:1]
if cmd==b'D': continue # ignore macros
history.append(cmd)
# print(myid,"#data(%d)={%s}"%(len(data),milterinfo.get(cmd,"???")),data[:64])
if cmd==b'Q': # 'Q' SMFIC_QUIT Quit milter communication
break # Expected response: Close milter connection
if cmd==b'A': # 'A' SMFIC_ABORT Abort current filter checks Expected response: NONE
eml=[] # reset Resets internal state of milter program to before SMFIC_HELO
continue
# https://github.com/phalaaxx/milter/blob/master/milter-protocol.txt
response=[b'c'] if cmd in b'BCEHLMNR' else [] # Expected response: Accept/reject action
if cmd==b'O': # 'O' SMFIC_OPTNEG Option negotiation Expected response: SMFIC_OPTNEG packet
response=[b'O\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0F'] # version=2 actions=1 (ACTION_ADDHDRS) proto=0x0F (ignore conn+helo+from+rcpt)
# capture headers:
if cmd==b'L': # 'L' SMFIC_HEADER Mail header
hdr=data[1:].split(b'\x00')
eml.append(hdr[0]+b': '+hdr[1]+b'\n')
if cmd==b'N': # 'N' SMFIC_EOH End of headers marker
eml.append(b'\n')
# capture body:
if cmd==b'B': # 'B' SMFIC_BODY Body chunk
eml.append(data[1:]) # Up to MILTER_CHUNK_SIZE (65535) bytes
if cmd==b'E': # 'E' SMFIC_BODYEOB End of body marker
# response=[b'a',b'c'] # Expected response: Zero or more modification actions, then accept/reject action
# 'h' SMFIR_ADDHEADER Add header (modification action)
# 'a' SMFIR_ACCEPT Accept message completely (accept/reject action)
ret=do_eml(b''.join(eml),addrs)
response=[b'hX-deepspam\x00'+ret+b'\x00',b'a']
eml=[] # reset
for r in response:
writer.write(struct.pack('!I', len(r))+r)
cmd=r[:1]
history.append(cmd)
# print(myid,"#resp(%d)={%s}"%(len(r),milterinfo.get(cmd,"???")),r[:64])
await writer.drain()
except Exception as ex:
print(myid,"Exception!!!",repr(ex))
break
writer.close()
await writer.wait_closed()
t=time.time()-t0
print('%d[%3d] %22s Done %6.3fs cmds:'%(myid,thread_cnt, addrs, t ),b''.join(history))
thread_cnt-=1
async def main():
server = await asyncio.start_server(handle_milter, '127.0.0.1', 1081)
addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
print(f'Serving on {addrs}')
async with server:
await server.serve_forever()
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(message)s',
datefmt='%Y-%m-%d@%H:%M:%S')
asyncio.run(main())