Monday, September 1, 2014

Send email to gmail in C++ with boost and openssl

It took some time for me to implement this program due to the length and complexity of it. But I am glad that I tried after all is done. I found that not many people tried c++ program that can send email to gmail. It involves two difficult steps:
  1. Understanding SMTP protocol
  2. Understanding how to use open-SSL library
  3. Understanding what Google Application specific passwords is
For SMTP protocol, I found the wikipedia page most helpful.
For OpenSSL, their webpage has its own wiki page.
For Google application specific passwords, there is an Google official support page that explains it.

When those SMTP and SSL are combined there are two different ways to communicate: SSL and TLS.
According to the official support page, GMail provide two different ports for each. Port number 465 is for SSL and 587 is for TLS. I found TLS little awkward hybrid so this page will focus only on SSL. But I don't think it will be too different.

I used two libraries: boost and openssl.
Boost is for network programming and open-ssl is for secure network socket layer, SSL.
You can install those libraries with a command: sudo apt-get install libboost-all-dev and sudo apt-get install openssl

Boost library is a kind of famous for the collection of interesting functionality. Good thing is that most of them work on most of platforms, which works like a platform independent standard library. But bad thing is that the library itself is gigantic. It will take long time to download and install. Not only that boost libraries take long time to compile even after they are compiled in the pre-compiled header.

Since C++11 didn't introduce any network related functions, boost was my best options to wrap the platform dependent network programming. It also helps other people to read the source code.

I made a class called, Socket; somehow I felt confident enough not to use "My" as the prefix. lol
The constructor takes server and port information and keeps the connection. I found the echo client example very helpful. Once I get the socket object, I can keep using the boost::asio functions or I can get the native socket file descriptor and use it with other libraries.



I made another class for OpenSSL. I tried hard to make the length of source code to be in one page but I couldn't make it. If I didn't have comments, I could make it but I found those comments very important for readability. It has two parts: the global initialization and the other part. The initialization has to happen only once. I thought that maybe I could have a static variable inside of the constructor but it will become problem in multi-thread programs so it will need to be called way before.

As the comments describe, OpenSSL is used by a process:

  1. At first the library must be initialized
  2. Then an SSL_CTX object is created as a framework to establish TLS/SSL enabled connections.
  3. Create SSL struct that hold data for the connection.
  4. When a network connection has been created, it can be assigned to an SSL object.
  5. After the SSL object has been created using SSL_new, SSL_set_fd or SSL_set_bio can be used to associate the network connection with the object.
  6. Then TLS/SSL handshake is performed using SSL_accept or SSL_connect respectively; I used SSL_connect.
  7. SSL_read and SSL_write are used to read and write data on the TLS/SSL connection.
  8. SSL_shutdown can be used to shut down the TLS/SSL connection.


The constructor takes the native socket file descriptor as an input argument. And optionally it takes SSL method, which can be one of SSLv2, SSLv3 TLSv1 or SSLv23. It seems that SSLv2 is deprecated version so there are actually two choices: SSLv3 or TLSv1. SSLv23 is for compatibility but SSLv2 is not expected to be used.

The writing data part was easy but reading data part was little more tricky. It is because when I send data, I have the data and the length of the data. But when I read data, there is no easy way to know how much data I need to read. There can be network delay and the function may return too early. Fortunately,  SMTP has a kind of simple protocol to detect whether or not the text messages are all read. The pattern is not the part of this class because this class is all about SSL not SMTP. So the function, "Read", takes a functor that can tell whether or not it should read more. This is very synchronous approach but it makes things easier to read and maintain. And this was enough for SMTP.

I made a class MySMTP. This is all about SMTP and it uses two classes above: Socket and MyOpenSSL. Speaking of dependency, Socket class is for the first 5 layers of OSI network 7-layer and MyOpenSSL class is for the 6th layer, representation. Now MySMTP is for the last layer, application. Therefore it makes sense for MySMTP to know about Socket class and MyOpenSSL class.

First I tried to make a class and I changed it to a static function. It was mainly because I couldn't get them all into one page. lol. Later I figured that it doesn't make sense to have a constructor and later the object is accessed by another function like "Send".  I couldn't find a clear reason to have a constructor because this process is just a single sequential process. It makes sense to have a class and object if the object holds data or state that later functions need to use.

The function "SendSSL" is a like of straight forward. It reads/waits for the server to say something. Once the expected messages are received proper messages are sent back. It is like woki-toki speaking protocol. More details about SMTP protocol can be found from the wikipedia page.

The functor, ReceiveFunctor, may look strange. I have developed my own pattern of creating a functor. Wait a second... hmm. I am not sure if it cause any trouble with STL if a functor has a const member variable; I will need to re-visit on this later. The functor has a bit complicated implementation in the function, operator(). As I put the contract comment on the function, it will:

  • return false if the input argument, msg, didn't contain the newline character; it will need to receive/wait more for the other data
  • return true if the input argument, msg, contains expected code number and there is a newline character following, or
  • throw if the input argument, msg, doesn't contain the expected code.
Here the expected code means the first three digit numbers that the SMTP server sends.

Now it is ready to connect to the SMTP server and send/receive SMTP data with SSL. But GMail added one more complexity: application specific password. You can check what it is from the official suppor
t page. Once you got the new password, you can use it for this program only. If you ignore this limitation, you will see the error message and the link URL from the SMTP server.

As you can see compiling the whole program took about one and half minutes, which is long time for the short program. I think it is mainly because of the boost library.

Every time I run this program, I get a new email; and I had to delete them. lol.
Depending on your demand, you will need to adjust the text messages in the email, which wouldn't be hard.

I am still not sure what the keyword, "gsmtp", means....

1 comment:

  1. Thank you very much ! Great code and explanations, it works very well .

    ReplyDelete

About Me

My photo
Tomorrow may not come, so I want to do my best now.