Click here to Skip to main content
15,867,453 members
Articles / Web Development / HTML

SMTP Client with SSL/TLS

,
Rate me:
Please Sign up or sign in to vote.
4.93/5 (132 votes)
22 Oct 2015CPOL7 min read 1.8M   40.8K   225   496
C++ SMTP client, support SSL and TLS encrypted connection to SMTP server

Introduction

I needed to send emails in a product written in C++, so I searched the Internet and found a great article: SMTP Client written by Jakub Piwowarczyk. However, many of my clients use SMTP servers that require secure connection (TLS or SSL), and the SMTP Client does not support it. So I had to add SSL/TLS support to the CSmtp class from SMTP Client before I could use it in my product. As I was new to SSL/OpenSSL, it did take me quite some time to learn how to use it properly, and to make the code work with several popular SMTP servers. I have also seen people searching the internet looking for a C++ implementation of SMTP/SSL/TLS, but just could not find one. So I decided to share the one I wrote, in the hope that it will save people who are not familiar with SSL some time.

Note that this article does not cover the details of SMTP. Please go to the original article SMTP Client if you need to know more about SMTP.

Background

There are 2 kinds of secure connections for SMTP, one is SSL and the other is TLS. Some SMTP servers support only one kind and some support both. Generally speaking, the port for SSL is 465, and the port for TLS is 587, but this is not always the case. In addition to the ports being different, SMTP/SSL is different than SMTP/TLS in that, SMTP/SSL negotiates an encrypted connection directly after the underlying TCP connection has been established, while SMTP/TLS requires that the client send a STARTLS command to the server before they negotiate an encrypted connection.

The steps involved in SMTP/SSL are as follows:

  1. The client connects to the server using TCP.
  2. The client negotiates an encrypted connection with the server.
  3. The server sends a welcome message using the encrypted connection to the client.
  4. The client sends a EHLO command using the encrypted connection to the server.
  5. The server responds to the EHLO command using the encrypted connection.

The steps involved in SMTP/TLS are as follows:

  1. The client connects to the server using TCP.
  2. The server sends a welcome message using the un-encrypted connection to the client.
  3. The client sends a EHLO command using the un-encrypted connection to the server.
  4. The server responds to the EHLO command using the un-encrypted connection.
  5. The client sends a STARTTLS command using the un-encrypted connection to the server.
  6. The server responds to the STARTTLS command using the un-encrypted connection.
  7. The client negotiates an encrypted connection with the server.
  8. The client sends a EHLO command using the encrypted connection to the server.
  9. The server responds to the EHLO command using the encrypted connection.

Using the Code

I have used openssl (http://www.openssl.org) in the sample code. The directory "openssl-0.9.8l" in the sample code contains all the necessary header files and the two pre-built static openssl libraries. If you would also like to use this version of openssl in your code, be sure to copy the entire "openssl-0.9.8l" directory to the root directory of your project and add "openssl-0.9.8l\inc32" to "Additional Include Directories", and also add "openssl-0.9.8l\out32" to "Additional Library Directories".

If you would like to build your own openssl, please refer to http://www.openssl.org for detailed instructions.

C++
#define test_gmail_tls
    CSmtp mail;
#if defined(test_gmail_tls)
    mail.SetSMTPServer("smtp.gmail.com",587);
    mail.SetSecurityType(USE_TLS);
#elif defined(test_gmail_ssl)
    mail.SetSMTPServer("smtp.gmail.com",465);
    mail.SetSecurityType(USE_SSL);
#elif defined(test_hotmail_TLS)
    mail.SetSMTPServer("smtp.live.com",25);
    mail.SetSecurityType(USE_TLS);
#elif defined(test_aol_tls)
    mail.SetSMTPServer("smtp.aol.com",587);
    mail.SetSecurityType(USE_TLS);
#elif defined(test_yahoo_ssl)
    mail.SetSMTPServer("plus.smtp.mail.yahoo.com",465);
    mail.SetSecurityType(USE_SSL);
#endif
    mail.SetLogin("***");
    mail.SetPassword("***");
    mail.SetSenderName("User");
    // ......
    mail.Send();

If you use a non-privileged user account to test Yahoo, the mail will fail to send. And the error message returned by the Yahoo SMTP server is "530 Access denied: Free users cannot access this server".

Notes

  • The code does not verify the server's identity, that is, it does not check the server's certificate. This is usually not a big problem if we make sure we feed the program with correct server addresses. However, it is still worth mentioning that there is the chance that we are talking to an impersonator if we don't check the certificate.
  • You are not allowed to use the code in this article for spamming.

References

History

  • Rev 2.4, 2015/10/22

Thanks to everyone for the effective crowdsourcing! Please keep up the improvements to our library!

  • Fixed issues with files being left opened and buffer not being deleted if an error occurs as discussed here.  Thanks to Josep Solà!
  • Fixed issue with opening attachments as discussed here.  Thanks to Graham!
  • Fixed potential memory leak as discussed here.  Thanks to LahPo!
  • Made total message size limit larger as recommended here.  Thanks to Stanislav!
  • Fixed an issue with incomplete attachment file paths as discussed here.  Thanks to Member 11508846 and Member 11887128!

Thanks to everyone for the effective crowdsourcing! Please keep up the improvements to our library!

  • Added fix contributed GKarRacer for the improper checking of the MsgBody.size() before working with a line of the message body.
  • Moved memory allocation and checking if all attachments can be opened to before the MAIL command is issued so that if there is an issue with one of them, sending the email can be gracefully aborted without the email being sent without the attachments.
  • Changed all the sprintf commands to snprintf to add greater security. #define'd snprintf to sprintf_s for MS Visual C. Also changed most strcpy calls to snprintf since the MS Visual C version of that function strcpy_s has the arguments re-ordered so it wouldn't be possible to use it without affecting portability.
  • Added fix contributed by jcyangzh about a possible infinite loop in the SayQuit function.
  • Added fixes contributed by sbrytskyy required to make the AUTH PLAIN login work properly.
  • Rev 2.1, 2012/11/06
  • Rev 2.0, 2011/06/23
    • Added the m_bAuthenticate member variable to be able to disable authentication even though it may be supported by the server. It defaults to true so if it is not set, the library will act as it would have before the addition.
    • Added the ability to pass the security type, m_type, the new m_Authenticate flag, the login and password into the ConnectRemoteServer function. If these new arguments are not included in the call, the function will work as it did before.
    • Added the ability to pass the new m_Authenticate flag into the SetSMTPServer function. If not provided, the function will act as it would before the addition.
    • Added fixes contributed by Martin Kjallman
    • Added fixes contributed by Karpov Andrey
    • Added fixes contributed by Jakub Piwowarczyk
  • Rev 1.9, 2010/08/19
    • Added PLAIN, CRAM-MD5 and DIGESTMD5 authorization.
    • Added a DisconnectRemoteServer() function and reconfigured the Send() function such that if you have already called the ConnectRemoveServer() function, it will use the existing connection and leave the connection open. This allows you to call ConnectRemoteServer(), then send multiple messages on the same connection. If this approach is used, you have to call DisconnectRemoteServer() to close the connection. If you call Send() without calling ConnectRemoteServer(), it will close the connection after sending. This change should be fully backward compatible.
  • Rev 1.8, 2010/08/09
  • 2010/08/03
    • Modified Introduction
  • 2010/08/02
    • Added note
  • 2010/08/01
    • Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Written By
Software Developer
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionUnable to compile in Win7, Visual Studio 2015 Pin
fooloomanzoo23-Feb-17 4:09
fooloomanzoo23-Feb-17 4:09 
AnswerRe: Unable to compile in Win7, Visual Studio 2015 Pin
David Johns12-May-17 14:32
David Johns12-May-17 14:32 
GeneralMy vote of 5 Pin
eo_petrov4-Feb-17 21:06
eo_petrov4-Feb-17 21:06 
SuggestionDownload link to source code doesn't work. Pin
MaLinaM23-Dec-16 1:18
MaLinaM23-Dec-16 1:18 
QuestionCan someone who got this to work on VS2015 + W10 share their project? Pin
Danyil Minenko5-Sep-16 7:30
Danyil Minenko5-Sep-16 7:30 
AnswerRe: Can someone who got this to work on VS2015 + W10 share their project? Pin
UrbanBlues10-Sep-16 5:40
UrbanBlues10-Sep-16 5:40 
GeneralRe: Can someone who got this to work on VS2015 + W10 share their project? Pin
fooloomanzoo23-Feb-17 4:19
fooloomanzoo23-Feb-17 4:19 
Questionit doen't work on 64bit, win10, vs2015 Pin
e4w3wq22-Aug-16 23:43
e4w3wq22-Aug-16 23:43 
AnswerRe: it doen't work on 64bit, win10, vs2015 Pin
Portiella26-Aug-16 2:32
Portiella26-Aug-16 2:32 
GeneralRe: it doen't work on 64bit, win10, vs2015 Pin
Danyil Minenko4-Sep-16 12:11
Danyil Minenko4-Sep-16 12:11 
GeneralRe: it doen't work on 64bit, win10, vs2015 Pin
UrbanBlues10-Sep-16 6:06
UrbanBlues10-Sep-16 6:06 
GeneralRe: it doen't work on 64bit, win10, vs2015 Pin
fooloomanzoo23-Feb-17 4:18
fooloomanzoo23-Feb-17 4:18 
QuestionError: Bad login or password Pin
deepak madan3-Aug-16 1:29
deepak madan3-Aug-16 1:29 
AnswerRe: Error: Bad login or password Pin
David Johns3-Dec-16 3:48
David Johns3-Dec-16 3:48 
QuestionThe program runs in the problem on arm linux Pin
Member 1255422822-Jun-16 17:34
Member 1255422822-Jun-16 17:34 
AnswerRe: The program runs in the problem on arm linux Pin
David Johns3-Dec-16 3:46
David Johns3-Dec-16 3:46 
QuestionCSMTP up-to-date on github Pin
R00T8822-May-16 22:08
R00T8822-May-16 22:08 
Questionbug in CSmtp::AddRecipient Pin
Beiley18-May-16 8:44
Beiley18-May-16 8:44 
AnswerRe: bug in CSmtp::AddRecipient Pin
David Johns3-Dec-16 4:07
David Johns3-Dec-16 4:07 
BugMemory allocation issue with CSmtp::FormatHeader on 64bit machines Pin
Member 124713107-May-16 9:55
Member 124713107-May-16 9:55 
GeneralRe: Memory allocation issue with CSmtp::FormatHeader on 64bit machines Pin
David Johns3-Dec-16 4:03
David Johns3-Dec-16 4:03 
QuestionCSmtp::ConnectRemoteServer never returns false in case of connection failure Pin
Pavel Cherezov25-Apr-16 0:42
Pavel Cherezov25-Apr-16 0:42 
QuestionWorks great but OpenSSL not up to date security Pin
Michael B Pliam2-Apr-16 20:00
Michael B Pliam2-Apr-16 20:00 
AnswerRe: Works great but OpenSSL not up to date security Pin
David Johns2-Dec-16 16:44
David Johns2-Dec-16 16:44 
QuestionWon't compile with Visual Studio 2015 Pin
Michael B Pliam21-Mar-16 12:27
Michael B Pliam21-Mar-16 12:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.