smtpd — Sample Mail Servers

Purpose:Includes classes for implementing SMTP servers.

The smtpd module includes classes for building simple mail transport protocol servers. It is the server-side of the protocol used by smtplib.

Mail Server Base Class

The base class for all of the provided example servers is SMTPServer. It handles communicating with the client, receiving incoming data, and provides a convenient hook to override to process the message once it is fully available.

The constructor arguments are the local address to listen for connections and the remote address where proxied messages should be delivered. The method process_message() is provided as a hook to be overridden by a derived class. It is called when the message is completely received, and given these arguments:


The client’s address, a tuple containing IP and incoming port.


The “from” information out of the message envelope, given to the server by the client when the message is delivered. This does not necessarily match the From header in all cases.


The list of recipients from the message envelope. Again, this does not always match the To header, especially if a recipient is being blind carbon copied.


The full RFC 2822 message body.

The default implementation of process_message() raises NotImplementedError. The next example defines a subclass that overrides the method to print information about the messages it receives.

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(('', 1025), None)


SMTPServer uses asyncore, so to run the server call asyncore.loop().

A client is needed to demonstrate the server. One of the examples from the section on smtplib can be adapted to create a client to send data to the test server running locally on port 1025.

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',
msg['From'] = email.utils.formataddr(('Author',
msg['Subject'] = 'Simple test message'

server = smtplib.SMTP('', 1025)
server.set_debuglevel(True)  # show communication with the server

To test the programs, run in one terminal and in another.

$ python3

Receiving message from: ('', 58541)
Message addressed from:
Message addressed to  : ['']
Message length        : 229

The debug output from shows all of the communication with the server.

$ python3

send: 'ehlo\r\n'
reply: b'250-\r\n'
reply: b'250-SIZE 33554432\r\n'
reply: b'250 HELP\r\n'
reply: retcode (250); Msg: b'\nSIZE 33554432\nHELP'
send: 'mail FROM:<> size=236\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'rcpt TO:<>\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>\r\nFrom: Author <>\r\nSu
bject: Simple test message\r\n\r\nThis is the body of the messag
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'

To stop the server, press Ctrl-C.

Debugging Server

The previous example shows the arguments to process_message(), but smtpd also includes a server specifically designed for more complete debugging, called DebuggingServer. It prints the entire incoming message to the console and then stops processing (it does not proxy the message to a real mail server).

import smtpd
import asyncore

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


Using the client program from earlier, the output of the DebuggingServer is:

---------- MESSAGE FOLLOWS ----------
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
To: Recipient <>
From: Author <>
Subject: Simple test message

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

Proxy Server

The PureProxy class implements a straightforward proxy server. Incoming messages are forwarded upstream to the server given as argument to the constructor.


The standard library documentation for smtpd says, “running this has a good chance to make you into an open relay, so please be careful.”

The steps for setting up the proxy server are similar to the debug server.

import smtpd
import asyncore

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


It prints no output, though, so to verify that it is working look at the mail server logs.

Aug 20 19:16:34 homer sendmail[6785]: m9JNGXJb006785:
from=<>, size=248, class=0, nrcpts=1,
proto=ESMTP, daemon=MTA, relay=[]

See also