SMTP Engine in Python
Vor kurzem war ich dabei einen Contentfilter für Postfix zu schreiben. Einfache Contentfilter lassen sich ja sogar mit Shell Scripts machen, diese werden dann aber per Pipe aufgerufen, was mir nicht wirklich gefällt…
Viel interessanter ist es einen Contentfilter per spawn einzubinden, was ein Program startet und es an einen Socket bindet, etwa so wie inetd. Die Kommunikation zwischen Postfix und dem Contentfilter läuft dann per STDIN bzw. STDOUT…
Der Contentfilter muss aber SMTP sprechen können, da Postfix per STDIN Smtp Kommandos schickt. Somit musste ich eine SMTPD Klasse in Python schreiben. Da es allgemein wenig Dokumentiert ist, wie so etwas programmiert werden kann, stelle ich hier die Basis-Klasse zur
Verfügung.
import sys, string, os
#---------------------------------------------------------------------------------------------
class SMTPServerEngine:
ST_INIT = 0
ST_HELO = 1
ST_MAIL = 2
ST_RCPT = 3
ST_DATA = 4
ST_QUIT = 5
def __init__(self):
self.state = SMTPServerEngine.ST_INIT
def chug(self):
while 1:
data = ''
completeLine = 0
while not completeLine:
lump = os.read(sys.stdin.fileno(), 1024)
if len(lump):
data += lump
if (len(data) >= 2) and data[-2:] == '\r\n':
completeLine = 1
if self.state != SMTPServerEngine.ST_DATA:
rsp, keep = self.doCommand(data)
else:
rsp = self.doData(data)
if rsp == None:
continue
sys.stdout.write(rsp + "\r\n")
sys.stdout.flush()
if keep == 0:
sys.exit(0)
return
else:
return
return
def doCommand(self, data):
"""Process a single SMTP Command"""
cmd = data[0:4]
cmd = string.upper(cmd)
keep = 1
if cmd == "HELO":
self.state = SMTPServerEngine.ST_HELO
elif cmd == "RSET":
self.dataAccum = ""
self.state = SMTPServerEngine.ST_INIT
elif cmd == "NOOP":
pass
elif cmd == "QUIT":
keep = 0
elif cmd == "MAIL":
if self.state != SMTPServerEngine.ST_HELO:
return ("503 Bad command sequence", 1)
self.state = SMTPServerEngine.ST_MAIL
elif cmd == "RCPT":
if (self.state != SMTPServerEngine.ST_MAIL) and (self.state != SMTPServerEngine.ST_RCPT):
return ("503 Bad command sequence", 1)
self.state = SMTPServerEngine.ST_RCPT
elif cmd == "DATA":
if self.state != SMTPServerEngine.ST_RCPT:
return ("503 Bad command sequence", 1)
self.state = SMTPServerEngine.ST_DATA
self.dataAccum = ""
return ("354 OK, Enter data, terminated with a \\r\\n.\\r\\n", 1)
else:
return ("505 Eh? WTF was that?", 1)
return("250 OK", keep)
def doData(self, data):
"""
Process SMTP Data. Accumulates client DATA until the
terminator is found.
"""
self.dataAccum = self.dataAccum + data
if len(self.dataAccum) > 4 and self.dataAccum[-5:] == '\r\n.\r\n':
self.dataAccum = self.dataAccum[:-5]
self.state = SMTPServerEngine.ST_HELO
return "250 OK - message accepted"
else:
return None
#---------------------------------------------------------------------------------------------
if __name__ == '__main__':
try:
sys.stdout.write("220 localhost\r\n")
sys.stdout.flush()
while 1:
engine = SMTPServerEngine()
engine.chug()
except SystemExit:
pass
|
Kommentar hinzufügen