Sending e-mail using pure WinSock

I’ve spend quite a bit of time these days searching for a simple way to send email from C++.

There sure is a lot of stuff on this on the Net, but there was one trouble – the program I’m writting is an NT service. Thus, all those MAPI-based solutions didn’t work for me (there are simply too many small things you have to take care of, that disable simple installation of software).

So, I just looked up “SMTP” on Wikipedia, and believe it or not, it was enough. I just wrote the whole thing as a pure plain-text communication with SMTP server, at it worked at first shot, haha!

The following code is a simplified version of what I used in the end – I got rid of almost all the error handling in the code, since everyone has different way to do it, and the posted version supports only 1 email address as “To:” address. In my original code, the “_To” string can be comma separated list of email addresses, but since I wrote it using my helpers library, I cut down the code this way. To support more “To:” addresses, simply repeat the sending of “RCPT TO: <address>” to SMTP server as many times as needed.

One last note – the “From:” address has to be an email address with a valid domain name, otherwise most of SMTP servers will refuse to send the mail.

So, without further ado, here’s the code (the code looks long, but it’s because SMTP server likes to talk a lot 😉 ):

bool SendMail(const char* _From, const char* _To, const char* _SMTP,
              const char* _Subject, const char* _MessageFormat, ...)
{
    int iProtocolPort = 0;
    char szBuffer[4096] = "";
    SOCKET hServer;
    LPHOSTENT lpHostEntry;
    LPSERVENT lpServEntry;
    SOCKADDR_IN SockAddr;

    // lookup SMTP server's IP address
    lpHostEntry = gethostbyname(_SMTP);
    if(!lpHostEntry)
    {
        // SMTP server not found!
        return false;
    }

    // create a TCP/IP socket, no specific protocol
    hServer = socket(PF_INET, SOCK_STREAM, 0);
    if (hServer == INVALID_SOCKET)
    {
        // Cannot open mail server socket!
        return false;
    }

    // get the mail service port
    lpServEntry = getservbyname("mail", 0);

    // use the SMTP default port if no other port is specified
    if (!lpServEntry)
        iProtocolPort = htons(IPPORT_SMTP);
    else
        iProtocolPort = lpServEntry->s_port;

    // setup a Socket Address structure
    SockAddr.sin_family = AF_INET;
    SockAddr.sin_port = iProtocolPort;
    SockAddr.sin_addr = *((LPIN_ADDR) *lpHostEntry->h_addr_list);

    // connect the Socket
    if(connect(hServer, (PSOCKADDR) &SockAddr, sizeof(SockAddr)))
    {
        // Error connecting to Server socket!
        return false;
    }

    // receive initial response from SMTP server
    recv(hServer, szBuffer, sizeof(szBuffer), 0);

    // send HELO smtp_server.com
    sprintf(szBuffer, "HELO %s\r\n", _SMTP);
    send(hServer, szBuffer, strlen(szBuffer), 0);
    recv(hServer, szBuffer, sizeof(szBuffer), 0);

    // send MAIL FROM: <sender@mydomain.com>
    sprintf(szBuffer, "MAIL FROM:<%s>\r\n", _From);
    send(hServer, szBuffer, strlen(szBuffer), 0);
    recv(hServer, szBuffer, sizeof(szBuffer), 0);

    // send RCPT TO: <receiver@domain.com>
    // repeat for each to-address

    sprintf(szBuffer, "RCPT TO:<%s>\r\n", _To);
    send(hServer, szBuffer, strlen(szBuffer), 0);
    recv(hServer, szBuffer, sizeof(szBuffer), 0);

    // send DATA
    sprintf(szBuffer, "DATA\r\n");
    send(hServer, szBuffer, strlen(szBuffer), 0);
    recv(hServer, szBuffer, sizeof(szBuffer), 0);

    // send Subject
    sprintf(szBuffer, "Subject: %s\n", _Subject);
    send(hServer, szBuffer, strlen(szBuffer), 0);

    // send From and To
    sprintf(szBuffer, "From: %s\n", _From);
    send(hServer, szBuffer, strlen(szBuffer), 0);

    sprintf(szBuffer, "To: %s\n\n", _To);
    send(hServer, szBuffer, strlen(szBuffer), 0);

    // prepare the string that will be sent as message body
    va_list message_parts;
    va_start(message_parts, _MessageFormat);
    char message[65536];
    vsprintf(message, _MessageFormat, message_parts);
    va_end(message_parts);

    send(hServer, message, strlen(message), 0);

    // send blank line and a period to end data transmission
    sprintf(szBuffer, "\r\n.\r\n");
    send(hServer, szBuffer, strlen(szBuffer), 0);
    recv(hServer, szBuffer, sizeof(szBuffer), 0);

    // Send QUIT
    sprintf(szBuffer, "QUIT\r\n");
    send(hServer, szBuffer, strlen(szBuffer), 0);
    recv(hServer, szBuffer, sizeof(szBuffer), 0);

    // clean up - close server socket
    closesocket(hServer);

    return true;
}

9 responses to “Sending e-mail using pure WinSock

  1. Just ‘google’ this blog, and this article was helpfull for me, thanks.

    My english maybe seems weird, because i barely use it, but you have got the point 😀

  2. good work!!
    i see DATA some where in the code, can i use it to send attached file?. i just want to make sure. if not, how can i attach a file to the message.

  3. Thanks 🙂

    The program presented here is a simple plain-text email sender, if used out-of-box; the DATA section of email simply includes content of message; to send emails containing html, unicode and/or attachments, you have to format the message (that is submitted in the DATA part) according to the MIME standard.

    See wikipedia.org – MIME and the links there-in.

  4. I read your blog for quite a long time and must tell you that your posts are always valuable to readers.

  5. “One last note – the “From:” address has to be an email address with a valid domain name, otherwise most of SMTP servers will refuse to send the mail.”

    To send emails with winsock then,do I need a smtp server running in the machine that sends the mail?

    Sorry bad english.

    Thanks!

  6. somehow this isn’t working for me! i never receive an answer of the SMTP Server after i have sent the MAIL FROM line.
    why? everythink works fine for the HELO command btw…
    sprintf(outBuf, “MAIL FROM:\r\n”);
    rg = send(s, outBuf, sizeof(outBuf),0);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s