ich versuch mich gerade in die Abläufe von SMTP einzuarbeiten und dachte womit geht das besser als selber einen kleinen Server zu schreiben.
ich hab heute mal ein bisschen zeit gehabt wobei unten stehender Code herausgekommen ist.
Momentan nimmt es nur Mails entgegen und leg diese in die Mail-Que. zur zeit noch ohne jegliche Überprüfung. (super open relay wenn er den versenden würde). Als Authtype ist CRAM-MD5 implementiert. Wie gesagt nimmt das Script momentan allerdings noch alle Mails an.
bei mir mit Evolution geht es Outlook ua. sollten aufgrund der etwas anderen Syntax noch Probleme machen.
das zweite Script ist zur Generierung der Passwörter.
Vielleicht habt Ihr Anregungen Das Konstrukt zu verbessern oder einfach nur ein paar allgemeine Hinweise.
Bin über jeden Kommentar Dankbar

PS: Das ist wirklich ein Entwurf und als SMTP Server so mehr als unbrauchbar
Code: Alles auswählen
#! /usr/bin/env python
# -*- encoding: UTF-8 -*-
# YPS SMTP mit Gimmick
from socket import *
import string,time, os, random, string
from stat import *
import md5, base64
import threading
import array
# Base Config
SMTP_PORT = 25
BUFSIZE = 1024
HOSTNAME = gethostname()
HOSTIP = gethostbyname(HOSTNAME)
GREATING = 'Welcome to alphalog Mail-Service'
DATA = ''
MAILQDIR = './mailq/'
LOGFILE = 'mail.log'
# Local Mail Users (ipad, opad in base64)
# daniel = testpw1
# sandra = testpw2
LOCALUSERS = {
'daniel' : ['QlNFQkZBBzY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Ng==','KDkvKCwrbVxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXA=='],
'sandra' : ['QlNFQkZBBDY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Ng==','KDkvKCwrblxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXA==']
}
class SmtpSession(threading.Thread):
def __init__ ( self, channel, remotehost ):
self.channel = channel
self.remotehost = remotehost
threading.Thread.__init__ ( self )
def log( self, logline ):
log=open(LOGFILE,'a')
log.write(logline)
log.close()
def cdigest(self, mailuser, data):
ipad = md5.md5(base64.b64decode(LOCALUSERS[mailuser][0]))
opad = md5.md5(base64.b64decode(LOCALUSERS[mailuser][1]))
ipad.update(data)
opad.update(ipad.digest())
return opad.hexdigest()
def run( self ):
STATUS = 0
self.log('connect from %s\n' %(self.remotehost))
self.channel.send('220 %s\n' %(GREATING))
sessionid = str(int(time.time())) + '.'
for i in range(14):
sessionid = sessionid + random.choice(string.letters + string.digits)
while 1:
# Empfange daten vom Client
DATA = self.channel.recv(BUFSIZE).strip()
print "Echo:" + DATA # Debug
# HELO Befehl
if 'HELO' in DATA:
try:
self.channel.send('250 Hello %s, nice to meet you\n' %(DATA.split()[1]))
except:
self.channel.send('501 Syntax: HELO hostname\n')
# EHLO Befehl
elif 'EHLO' in DATA:
self.channel.send('250 AUTH CRAM-MD5\n')
# AUTH CRAM-MD5 Befehl
elif 'AUTH CRAM-MD5' in DATA:
try:
#callenge erzeugen random.time@hostname und senden
rand = ''
for i in range(5):
rand = rand + random.choice(string.digits)
challenge = rand + '.' + str(int(time.time())) + '@' + HOSTNAME
challengeb64 = base64.b64encode(challenge)
self.channel.send('334 ' + challengeb64 + '\n')
# Authstring vom Client lessen und Pruefen
authstring = self.channel.recv(BUFSIZE).strip()
authstring = base64.b64decode(authstring).split()
mailuser = authstring[0]
mailpwdigest = authstring[1]
signature = self.cdigest(mailuser,challenge)
print 'signature: ' + signature
if mailpwdigest == signature:
self.channel.send('235 ok\n')
self.log('Login from %s User: %s\n' %(self.remotehost, mailuser))
else:
self.channel.send('535 Incorrect username or password\n')
self.log('FAILD Login from %s User: %s\n' %(self.remotehost, mailuser))
except:
#Wenn nicht implementiertes Syntax bzw. ander Fehler
self.channel.send('535 Incorrect username or password\n')
# MAIL Befehl
elif 'MAIL' in DATA:
if 'MAIL FROM:' in DATA:
try:
MAILFROM = DATA.split(':')[1].strip()
self.channel.send('250 %s Sender OK \n' %(MAILFROM))
STATUS = 1
except:
self.channel.send('501 Syntax: MAIL FROM: <address>\n')
else:
self.channel.send('501 Syntax: MAIL FROM: <address>\n')
# RCPT Befehl
elif 'RCPT' in DATA:
if STATUS != 1:
self.channel.send('503 Error: need MAIL command\n')
elif 'RCPT TO:' in DATA:
try:
RCPTTO = DATA.split(':')[1].strip()
self.channel.send('250 %s RE OK \n' %(RCPTTO))
STATUS = 2
print 'ok RCPTTO'
except:
self.channel.send('501 Syntax: RCPT TO: <address>\n')
else:
self.channel.send('501 Syntax: RCPT TO: <address>\n')
# DATA Befehl
elif 'DATA' in DATA:
if STATUS != 2:
self.channel.send('503 Error: need RCPT command\n')
else:
self.channel.send('354 End DATA with <CR><LF>.<CR><LF>\n')
DATA = ''
#MAIL lessen bis <CR><LF>.<CR><LF>
while not '\n.\n' in DATA:
DATA = DATA + conn.recv(BUFSIZE).strip() + '\n'
# Speichern
fd=open(MAILQDIR + sessionid,'w')
fd.write(DATA)
fd.close()
self.log('%s: Mail from %s to %s stored in mailq\n' %(sessionid, MAILFROM,RCPTTO))
self.channel.send('250 Message accepted for delivery\n')
# RSET Befehl
elif 'RSET' in DATA:
self.channel.send('250 Ok \n')
# NOOP Befehl
elif 'NOOP' in DATA:
self.channel.send('250 Ok \n')
# QUIT Befehl
elif 'QUIT' in DATA:
self.channel.send('221 See you later \n')
break
# ka
else:
self.channel.send('502 Error: command not implemented\n')
# Session Beenden
self.channel.close()
self.log('disconnect from %s\n' %(self.remotehost))
print('Verbindung beendet')
if __name__ == '__main__':
# TCP-Socket erstellen und an Port binden
s = socket(AF_INET, SOCK_STREAM)
print "Socket initiert"
s.bind(('', SMTP_PORT))
s.listen(500)
print "Server gestartet"
# Warte auf eine Verbindung zum Server
while True:
print 'Warte auf eine Verbindung zum Server'
conn, (remotehost, remoteport) = s.accept()
print 'Verbunden mit %s:%s' % (remotehost, remoteport)
SmtpSession(conn,remotehost).start()
s.close()
Code: Alles auswählen
import array, string, md5, base64
key = 'testpw2'
ipada = array.array("B", [0x36] * 64)
opada = array.array("B", [0x5C] * 64)
for i in range(len(key)):
ipada[i] = ipada[i] ^ ord(key[i])
opada[i] = opada[i] ^ ord(key[i])
ipada = ipada.tostring()
opada = opada.tostring()
print base64.b64encode(ipada)
print base64.b64encode(opada)