In this, I'll show a simple implementation of Simple Mail Transfer Protocol (SMTP) that is defined in RFC 821. According to the document - The objective of Simple Mail Transfer Protocol (SMTP) is to transfer mail reliably and efficiently. SMTP is independent of the particular transmission subsystem and requires only a reliable ordered data stream channel. In this article, we use TCP/IP only for message distribution.
The SMTP provides mechanisms for the transmission of mail; directly from the sending user's host to the receiving user's host when the two hosts are connected to the same transport service, or via one or more relay SMTP-servers when the source and destination hosts are not connected to the same transport service. To be able to provide the relay capability, the SMTP-server must be supplied with the name of the ultimate destination host as well as the destination mailbox name.
The protocol uses 7-bit ASCII characters. If the transport layer provides an 8-bit transmission channel, then the MSB is set to zero and used.
An SMTP client (Sender-SMTP) communicates with the SMTP server (Receiver-SMTP) using a predefined strict set of case insensitive commands. Among them
QUIT are always implemented by all SMTP servers. In response, the server sends some predefined response code to the client. For example,
250 is returned if everything goes OK. A short description may follow the reply code. All commands are ended with
CRLF characters. I'll describe the commands here sequentially.
In this article, I'll show how to implement a simple SMTP server. You can test it with any SMTP client like Outlook Express. To test the server, please stop the Windows SMTP Service first. You may need to create some folders also. For example, if you want to send mail to email@example.com you should have ./exampledomain.com/kuasha/mbox/ as a valid directory relative to the current directory of the server executable. Simply speaking, if you see an error message like "Cannot copy xxxx file to dddd directory" then you have to make dddd directory structure. I encourage you to see the code instead. And we may declare it to be stable SMTP if we can fix all errors.
There is also a POP3 server implementation here. If you keep two executables in a same directory and first setup SMTP and then setup POP3, you can send and receive mails between users with a mail client using the machine IP address of the machine.
When a transmission channel is established between client and server, the server sends a ready signal (
220 response) to the client:
220 kuashaonline.com Ready. <CRLF>
Client can then start the session issuing a
The HELO or EHLO command
These two commands (any one) are used to establish a dialog session between client and server. The client sends
HELO command in the following format to start a session:
HELO <SP> <domain> <CRLF>
<domain> is users domain who wish to send a message. If the server allows a user from this domain, it sends
250 OK You are not kicked off :) <CRLF>
Here is my simple implementation of
HELO request. Please note that for simplicity, I have not checked error conditions.
int CMailSession::ProcessHELO(char *buf, int len)
The session is now established and the client can now send a message.
The MAIL Command
Client starts with
MAIL command to send a mail message:
Format: MAIL <SP> FROM:<reverse-path> <CRLF>
Example MAIL FROM:<firstname.lastname@example.org> <CRLF>
Here client assigns the
FROM address. Let's assume that we do accept this
from address and send
It is possible to send invalid parameter response (501) from server if, for example,
from address format is not valid.
501 Syntax error in parameters or arguments <CRLF>
Here is my simple implementation:
int CMailSession::ProcessMAIL(char *buf, int len)
__w64 int alen;
from address is set. Now we want the
The RCPT Command
The client sets
to address with this command:
Format: RCPT <SP> TO:<forward-path> <CRLF>
Example: RCPT TO:email@example.com<CRLF>
Let's assume that our SMTP server can accept the message to store the message (if it is local) or relay the message to destination SMTP server somehow. So we accept it:
Here is my implementation:
int CMailSession::ProcessRCPT(char *buf, int len)
char *st,*en, *domain=tdom;
__w64 int alen;
printf("RCPT [%s] User [%s] Domain [%s]\n",address, user, domain);
printf("User MBox path [%s]\n",szUserPath);
printf("User not found on this domain\n");
OK. Now it's time to receive
DATA from client.
The DATA Command
DATA command to start sending data:
The server now sets its state to receive data and sends an affirmative result to client using
354 Start mail input; end with [CRLF].[CRLF] <CRLF>
When client receives this reply, client starts to send the mail body. At the end, client sends a
[CRLF].[CRLF] sequence to tell server that data sending is finished.
Here is my implementation to process
int CMailSession::ProcessDATA(char *buf, int len)
DWORD dwIn=len, dwOut;
When the terminator sequence (
[CRLF].CRLF]) is received, the mail reception is finished. The attachments are also received at this stage. The SMTP server may not separate the attachment part. It is the responsibility of the mail viewer. Everything is considered as data.
The QUIT Command
OK, we are finished with the sending mail. Client should now send
And server closes the session by sending
221 Service closing transmission channel.
OK. That's all about the good world. No error. But there may be some errors or failure. Server must send an appropriate response to notify that. Here is some example code in my response method:
int CMailSession::SendResponse(int nResponseType)
sprintf(buf,"220 %s Welcome to %s %s \r\n",
strcpy(buf,"221 Service closing transmission channel\r\n");
else if (nResponseType==250)
else if (nResponseType==354)
strcpy(buf,"354 Start mail input; end with <CRLF>.<CRLF>\r\n");
strcpy(buf,"501 Syntax error in parameters or arguments\r\n");
strcpy(buf,"502 Command not implemented\r\n");
strcpy(buf,"503 Bad sequence of commands\r\n");
strcpy(buf,"550 No such user\r\n");
strcpy(buf,"551 User not local. Can not forward the mail\r\n");
sprintf(buf,"%d No description\r\n",nResponseType);
- 23rd September, 2007: Article posted on CodeProject
This server was written for test purposes only when I was a 2nd year undergrad student back in the year 2003. So, it is not stable at all.