Sending Email with SMTP AUTH

How to Authenticate and Forward Messages Using TCP/IP Sockets

© Guy Lecky-Thompson

Tutorial showing use of WinSock programming to authenticate with an SMTP server, compose and forward an email message with error checking.

WinSock Programming

Programming Windows Sockets is relatively easy. Before compiling any WinSock enabled applications, however, there are a few things that need to be done:

For the second item, those using the Borland C Compiler need only adjust the LIBFILES entry in the .mak file according to their installation settings. These will commonly look something like:

LIBFILES = c:\borland\bcc55\lib\psdk\wsock32.lib

Like many external libraries (GDI+, communications drivers, etc.) WinSock has a certain preamble overhead to initialise the WinSock library and closedown sequence to release resources. While this is not the place to discuss these in detail, the two functions in the WinSock library can be called, thus:

WSADATA wsa_Data;
int wsa_ReturnCode = WSAStartup(0x101,&wsa_Data);

Of course, we need to check the return value in wsa_ReturnCode to be sure that the library has initialized itself properly, and a list of these can be found on the Microsoft website, or in the WinSock header file. The two parameters indicate the version of WinSock we are requesting (here 1.1), and a pointer to the WSADATA structure.

When we have finished with WinSock, be it at the end of the program (during the WM_DESTROY message processing) or after we have sent an email, we should call the cleanup function:

WSACleanup();

Once the library has been initialized, we can begin to send and receive data. Essentially, this requires that we:

The function to create a socket is very simple, and takes three parameters indicating the address format we intend to use to connect the socket to a server, a type specification for the data stream, and the protocol. For the majority of connections we will use the following:

SOCKET connection = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

This is a socket that will be addressed with a standard ARPA addressing scheme (xxx.xxx.xxx.xxx), full duplex stream, and using the TCP/IP protocol. The value of the variable connection can be tested against the value INVALID_SOCKET to check it's validity.

To attach a server to the socket, we have to do a bit of advanced data manipulation. It is not vital to understand all of the following, but it is necessary to grasp what we are trying to achieve. Firstly, some data structures:

struct hostent *host_entry;
struct sockaddr_in server_address;

The hostent structure is populated by the WinSock library, and as such needs to be treated as read only. The sockaddr_in structure, on the other hand, is read-write; we will set some parts of it to specific values, and pass those to WinSock so that it can establish communications with a server using the IP address and port.

Assuming that we have the server name in a variable szServerName, we need to retrieve the IP address by performing a DNS lookup. Or, rather, we need WinSock to do it for us:

host_entry=gethostbyname(szServerName);

The next piece of code assigns the various values (IP address and port) to the server_address structure:

server_address.sin_addr.s_addr
=*((unsigned long*)host_entry-h_addr);
server_address.sin_family=AF_INET;
server_address.sin_port=htons(nPort);

The sin_family has to match that which we intend to use with the socket, and nPort is usually obtainable by a quick search of the ISPs FAQs for connecting email clients (such as Outlook Express).

Finally, we can attempt to connect the socket to the server:

if (connect(connection,
(struct sockaddr*)&server_address,
sizeof(server_address))
== SOCKET_ERROR)

So long as the function does not return SOCKET_ERROR, we can proceed. It is not important to follow all the above code - a quick look at the WinSock header file will reveal what data type each parameter passed to connect is.

SMTP Commands

Having (hopefully) now connected to the SMTP server, we can proceed to send some commands. We should clear the incoming buffer by calling the recv function to receive any data that the server would like to send:

y=recv(connection,szBuffer,1024,0);

This is the easiest call to recv we can make - it just receives the data, and places the number of bytes received in y. The szBuffer variable, in this case, is 1024 bytes long. Having received the data, we could check it to see what was returned, but since we know what we want to do, we shall just proceed:

(Note that we have removed any checking of the received buffer for simplicity - in a real implementation this would be very bad manners, and one should always check to see that the server is 'ready' to talk with us.)

sprintf(szBuffer,"ehlo\r\n");
send(connection,szBuffer,strlen(szBuffer),0);
y=recv(connection,szBuffer,1024,0);

Having send the 'ehlo' burst, the server will return a list of commands that it is willing to support. One of those should indicate that it is ready to allow us to authenticate using Base64 encoding. In the following code segments, we assume that the username and passwords have been Base64 encoded (see, for example, the DillFrog Online Encoder) before use.

To start the login sequence, we send the string 'auth login\r\n' using the logic above. Now, it becomes important to check that the server is ready to allow this by receiving the buffer as normal, but checking for the string '334 '. This is a code that indicates that the server is willing to talk.

We can then send the username, again followed by '\r\n', and checking that '334 ' is returned. This is followed by the password, and if everything has been completed correctly, the server will reply with '235 ' in the received buffer.

We can now send the email.

Composing and Sending the Email

The email sending sequence is simple:

Each command should be terminated with a '\r\n' sequence, and the return value from the server will be '250 ' if everything is going according to plan. The commands are:

mail from:<email_address>\r\n

rcpt to:<email_address<\r\n

DATA\r\n

The message itself is composed of a subject and message body:

Subject: <subject>\r\n

<message_text>\r\n

.\r\n

This last can be sent as one block of formatted text:

sprintf(req,"Subject: %s\r\n\r\n%s\r\n.\r\n",
szSubject, szMessageBody);

Assuming that the return code is '250 ', we're done, and can close the socket, but not until we have said goodbye to the server:

sprintf(szBuffer,"quit\r\n");
send(connection,szBuffer,strlen(szBuffer),0);
y=recv(connection,szBuffer,1024,0);
closesocket(connection);

One point to note is that the email address of the sender should belong to the user specified in the Username. This method can be used at most ISPs, and some free email providers, but using it to send UCE (Unsolicited Commercial Email, or SPAM) is both illegal and a waste of bandwidth for the internet community.

Links


The copyright of the article Sending Email with SMTP AUTH in Computer Programming Tutorials is owned by Guy Lecky-Thompson. Permission to republish Sending Email with SMTP AUTH must be granted by the author in writing.




Post this Article to facebook Add this Article to del.icio.us! Digg this Article furl this Article Add this Article to Reddit Add this Article to Technorati Add this Article to Newsvine Add this Article to Windows Live Add this Article to Yahoo Add this Article to StumbleUpon Add this Article to BlinkLists Add this Article to Spurl Add this Article to Google Add this Article to Ask Add this Article to Squidoo