smtpd - SMTP Server

Scopo: Include classi per implementare server SMTP

Il modulo smtpd include classi per la costruzione di semplici server SMTP. Rappresenta il lato server del protocollo usato da smtplib.

Classe Base del Server Mail

La classe base per tutti gli esempi di server forniti è SMTPServer. Gestisce la comunicazione con il client, riceve dati in arrivo, e fornisce un comodo aggancio di sovrascrittura per elaborare il messaggio quando lo stesso è completamente disponibile.

Gli argomenti del costruttore sono l'indirizzo locale per rilevare le connessioni e l'indirizzo remoto dove messaggi in transito dovrebbero essere consegnati. Il metodo process_message() viene fornito come aggancio perchè possa essere sovrascritto da una classe derivata. Viene chiamato quando il messaggio è completamente ricevuto, e viene corredato dei seguenti argomenti:

NOME ARGOMENTO DESCRIZIONE
peer indirizzo del client, una tupla che contiene IP e porta in arrivo
mailfrom informazioni della parte "from" della busta del messaggio, dato al server dal client quando il messaggio viene consegnato. Non necess ariamente trova sempre corrispondenza con l'intestazione From
rcpttos lista dei destinatari dalla busta del messaggio. Non necessariamente trova corrispondenza con l'intestazione To, specialmente se il destinatario è in copia conoscenza nascosta (bcc)
data Il corpo del messaggio completo RFC 5322

L'implementazione predefinita di process_message() solleva NotImplementedError. L'esempio che segue definisce una sottoclasse che sovrascrive il metodo per stampare informazioni circa i messaggi ricevuti.

# smtpd_custom.py

import smtpd
import asyncore


class CustomSMTPServer(smtpd.SMTPServer):

    def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
        print('Messaggio in ricezione da:', peer)
        print('Mittente del messaggio   :', mailfrom)
        print('Messaggio indirizzato a  :', rcpttos)
        print('Lunghezza del messaggio  :', len(data))


server = CustomSMTPServer(('127.0.0.1', 1025), None)

asyncore.loop()

SMTPServer usa asyncore, quindi per eseguire il server si chiami asyncore.loop().

Per la dimostrazione del server occorre un client. Si può adattare uno degli esempi da smtplib per creare un client per inviare dati al server di test, in esecuzione localmente sulla porta 1025.

# smtpd_senddata.py

import smtplib
import email.utils
from email.mime.text import MIMEText

# Create the message
msg = MIMEText('Questo il corpo del messaggio.')
msg['To'] = email.utils.formataddr(('Recipient',
                                    'destinatario@esempio.com'))
msg['From'] = email.utils.formataddr(('Author',
                                      'autore@esempio.com'))
msg['Subject'] = 'Semplice messaggio di test'

server = smtplib.SMTP('127.0.0.1', 1025)
server.set_debuglevel(True)  # show communication with the server
try:
    server.sendmail('autore@esempio.com',
                    ['destinatario@esempio.com'],
                    msg.as_string())
finally:
    server.quit()

Per verificare i programmi, si esegua smtpd_custom.py in un terminale, quindi smtpd_senddata.py in un altro.

$ python3 smtpd_custom.py

Messaggio in ricezione da: ('127.0.0.1', 43994)
Mittente del messaggio   : autore@esempio.com
Messaggio indirizzato a  : ['destinatario@esempio.com']
Lunghezza del messaggio  : 237

Il risultato di debug da smtpd_senddata.py mostra tutte le comunicazioni con il server.

$ python3 smtpd_senddata.py

send: 'ehlo [127.0.1.1]\r\n'
reply: b'250-robby-desktop\r\n'
reply: b'250-SIZE 33554432\r\n'
reply: b'250-8BITMIME\r\n'
reply: b'250 HELP\r\n'
reply: retcode (250); Msg: b'robby-desktop\nSIZE 33554432\n8BITMIME\nHELP'
send: 'mail FROM:<autore@esempio.com> size=244\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'rcpt TO:<destinatario@esempio.com>\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'data\r\n'
reply: b'354 End data with <CR><LF>.<CR><LF>\r\n'
reply: retcode (354); Msg: b'End data with <CR><LF>.<CR><LF>'
data: (354, b'End data with <CR><LF>.<CR><LF>')
send: b'Content-Type: text/plain; charset="us-ascii"\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo: Recipient <destinatario@esempio.com>\r\nFrom: Author <autore@esempio.com>\r\nSubject: Semplice messaggio di test\r\n\r\nQuesto il corpo del messaggio.\r\n.\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
data: (250, b'OK')
send: 'quit\r\n'
reply: b'221 Bye\r\n'
reply: retcode (221); Msg: b'Bye'

Per interrompere il server, si prema Ctrl-C.

Server di Debug

L'esempio precedente mostra gli argomenti a process_message(), ma smtpd include anche un server progettato specificamente per un debug più completo: DebuggingServer. Stampa l'intero messaggio in arrivo alla console, quindi interrompe l'elaborazione (non smista il messaggio a un server mail reale).

# smtpd_debug.py

import smtpd
import asyncore

server = smtpd.DebuggingServer(('127.0.0.1', 1025), None)

asyncore.loop()

Usando il programma client smtpd_senddata.py qui sopra, il risultato è:

---------- MESSAGE FOLLOWS ----------
mail options: ['SIZE=244']
b'Content-Type: text/plain; charset="us-ascii"'
b'MIME-Version: 1.0'
b'Content-Transfer-Encoding: 7bit'
b'To: Recipient <destinatario@esempio.com>'
b'From: Author <autore@esempio.com>'
b'Subject: Semplice messaggio di test'
b'X-Peer: 127.0.0.1'
b''
b'Questo il corpo del messaggio.'
------------ END MESSAGE ------------

Proxy Server

La classe PureProxy implementa un proxy server. I messaggi in arrivo vengono inoltrati a monte al server passato come argomento al costruttore.

La documentazione della libreria standard per smtpd dice, "eseguendo questo è probabile che si entri in un relay aperto, usare cautela"

I passi per impostare il proxy server sono simili a quelli del server di debug.

# smtpd_proxy.py

import smtpd
import asyncore

server = smtpd.PureProxy(('127.0.0.1', 1025), ('mail', 25))

asyncore.loop()

Non viene stampato alcun risultato, quindi per verificarne il funzionamento occorre cercare nei log del server mail.

Vedere anche:

smtpd
La documentazione della libreria standard per questo modulo.
smtplib
Fornisce una interfaccia client
email
Elabora messaggi email
asyncore
Modulo base per scrivere server asincroni
RFC 2822
Internet Message Format, definisce il formato del messaggio email
RFC 5322
Sostituisce RFC 2822