smtpd — Servidores de correo de muestra

Propósito:Incluye clases para implementar servidores SMTP.

El módulo smtpd incluye clases para construir servidores simples de protocolo de transporte de correo. Es el lado del servidor del protocolo utilizado por smtplib.

Clase base del servidor de correo

La clase base para todos los servidores de ejemplo proporcionados es SMTPServer. Se encarga de comunicarse con el cliente, recibir datos entrantes y proporcionar un enlace conveniente para procesar el mensaje una vez que esté completamente disponible.

Los argumentos del constructor son la dirección local para escuchar conexiones y la dirección remota donde deben ser entregados los mensajes proxy. El método process_message() se proporciona como un gancho ser anulado por una clase derivada. El llamado cuando el mensaje se recibe completamente, y con estos argumentos:

peer

La dirección del cliente, una tupla que contiene IP y puerto entrante.

mailfrom

La información «de» del sobre del mensaje, dada al servidor por el cliente cuando se entrega el mensaje. Esto no lo hace necesariamente coincide con el encabezado From en todos los casos.

rcpttos

La lista de destinatarios del sobre del mensaje. De nuevo, esta siempre coincide con el encabezado To, especialmente si un destinatario está recibiendo una copia ciega.

data

El cuerpo completo del mensaje RFC 5322.

La implementación predeterminada de process_message() plantea NotImplementedError. El siguiente ejemplo define una subclase que anula el método para imprimir información sobre los mensajes que recibe.

smtpd_custom.py
import smtpd
import asyncore


class CustomSMTPServer(smtpd.SMTPServer):

    def process_message(self, peer, mailfrom, rcpttos, data):
        print('Receiving message from:', peer)
        print('Message addressed from:', mailfrom)
        print('Message addressed to  :', rcpttos)
        print('Message length        :', len(data))


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

asyncore.loop()

SMTPServer usa asyncore, para ejecutar la llamada al servidor asyncore.loop().

Se necesita un cliente para demostrar el servidor. Uno de los ejemplos de la sección sobre smtplib puede adaptarse para crear un cliente para enviar datos al servidor de prueba que se ejecuta localmente en el puerto 1025.

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

# Create the message
msg = MIMEText('This is the body of the message.')
msg['To'] = email.utils.formataddr(('Recipient',
                                    'recipient@example.com'))
msg['From'] = email.utils.formataddr(('Author',
                                      'author@example.com'))
msg['Subject'] = 'Simple test message'

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

Para probar los programas, ejecuta smtpd_custom.py en una terminal y smtpd_senddata.py en otra.

$ python3 smtpd_custom.py

Receiving message from: ('127.0.0.1', 58541)
Message addressed from: author@example.com
Message addressed to  : ['recipient@example.com']
Message length        : 229

La salida de depuración de smtpd_senddata.py muestra toda la comunicación con el servidor.

$ python3 smtpd_senddata.py

send: 'ehlo 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-SIZE 33554432\r\n'
reply: b'250 HELP\r\n'
reply: retcode (250); Msg: b'1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa\nSIZE 33554432\nHELP'
send: 'mail FROM:<author@example.com> size=236\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'rcpt TO:<recipient@example.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-Ver
sion: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo: Recipient <r
ecipient@example.com>\r\nFrom: Author <author@example.com>\r\nSu
bject: Simple test message\r\n\r\nThis is the body of the messag
e.\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'

Para detener el servidor, presiona Ctrl-C.

Servidor de depuración

El ejemplo anterior muestra los argumentos de process_message(), pero smtpd también incluye un servidor diseñado específicamente para una depuración más completa, llamada DebuggingServer. Imprime el mensaje entrante completo a la consola y luego detiene el procesamiento ( no envía el mensaje a un servidor de correo real).

smtpd_debug.py
import smtpd
import asyncore

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

asyncore.loop()

Usando el programa cliente smtpd_senddata.py de antes, la salida del DebuggingServer es:

---------- MESSAGE FOLLOWS ----------
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
To: Recipient <recipient@example.com>
From: Author <author@example.com>
Subject: Simple test message
X-Peer: 127.0.0.1

This is the body of the message.
------------ END MESSAGE ------------

Servidor proxy

La clase PureProxy implementa un servidor proxy directo. Los mensajes entrantes se reenvían al servidor dado como argumento para el constructor.

Advertencia

La documentación de la biblioteca estándar para smtpd dice, «ejecutar esto tiene una buena oportunidad de convertirte en un relevo abierto, así que por favor ten cuidado.»

Los pasos para configurar el servidor proxy son similares al servidor de depuración.

smtpd_proxy.py
import smtpd
import asyncore

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

asyncore.loop()

Sin embargo, no imprime ningún resultado, así que para verificar que esté funcionando, observa los registros del servidor de correo.

Aug 20 19:16:34 homer sendmail[6785]: m9JNGXJb006785:
from=<author@example.com>, size=248, class=0, nrcpts=1,
msgid=<200810192316.m9JNGXJb006785@homer.example.com>,
proto=ESMTP, daemon=MTA, relay=[192.168.1.17]

Ver también