1

When communicating with the "smtp.gmail.com" server I used the EHLO command to get the greet from gmail "at your service" response, then STARTTLS command and got "2.0.0 Ready to start TLS". After the "STARTTLS" command I start sending the email data like "MAIL FROM: %s\n", from" but when I do the server connection closes. I now realize that the server was waiting for the TLS negotiation, what TLS negotiation commands do I send to establish a handshake? I referenced Chapter 5. from the RFC document: [https://www.rfc-editor.org/rfc/rfc2487] for information about TLS Negotiation.

The C++ code I am using is from: [http://www.cplusplus.com/forum/windows/35333/]

#include <windows.h> 
#include <cstdio>
#include <iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib") //I added because of LNK2019 error
//#include <winsock2.h>  //on MSVC2008 - windows.h already includes winsock stuff    /* WSAGetLastError, WSAStartUp  */
#define snprintf _snprintf

static void sendmail_write(const int  sock,const char *str,const char *arg)
 {
    char buf[4096];

    if (arg != NULL)
        snprintf(buf, sizeof(buf), str, arg);
    else
        snprintf(buf, sizeof(buf), str);

    send(sock, buf, strlen(buf), 0);

// read a reply from server
    char outbuf[1024];
    int len=recv(sock,outbuf,1024,0);
    outbuf[len]='\0';
    cout <<outbuf;

}

static int sendmail(
                const char *from,
                const char *to,
                const char *subject,
                const char *body,
                const char *hostname,
        const char *user,
        const char *pass,
                const int   port
            ) {

    struct hostent *host;
    struct sockaddr_in saddr_in;
    int sock = 0;


    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        return -1;
    }


    sock = socket(AF_INET, SOCK_STREAM, 0);
    host = gethostbyname(hostname);

    saddr_in.sin_family      = AF_INET;
    saddr_in.sin_port        = htons((u_short)port);
    saddr_in.sin_addr.s_addr = 0;

    memcpy((char*)&(saddr_in.sin_addr), host->h_addr, host->h_length);

    if (connect(sock, (struct sockaddr*)&saddr_in, sizeof(saddr_in)) == -1) {
        return -2;
    }



    sendmail_write(sock, "EHLO %s\n",       "MyMailDomain");  // Should I use HELO or EHLO?

    sendmail_write(sock, "STARTTLS\n",""); // <----- starting TLS?

    sendmail_write(sock, "MAIL FROM: %s\n", from);    // from
    sendmail_write(sock, "RCPT TO: %s\n",   to);      // to
    sendmail_write(sock, "DATA\n",          NULL);    // begin data
    sendmail_write(sock, "From: %s\n",      from);
    sendmail_write(sock, "To: %s\n",        to);
    sendmail_write(sock, "Subject: %s\n",   subject);
    sendmail_write(sock, "\n",              NULL);
    sendmail_write(sock, "%s\n",            body);    // data
    sendmail_write(sock, ".\n",             NULL);    // end data
    sendmail_write(sock, "QUIT\n",          NULL);    // terminate

    closesocket(sock);

    return 0;
}


int main(int argc, char *argv[]) {
    int ret = sendmail(

        "user@gmail.com",  // from - put an email address here
        "touser@gmail.com", // to - put an email address here
        "subject",
        "body",
        "smtp.gmail.com",
    "user@gmail.com",
    "password",
        587
    );

    if (ret != 0)
        fprintf(stderr, "Failed to send mail (code: %i).\n", ret);
    else
        fprintf(stdout, "\nMail successfully sent.\n");

    return ret;
}
Community
  • 1
  • 1
Programminghobby
  • 105
  • 1
  • 6
  • 16
  • First, how do you know how many bytes you actually send? You're not bothering to check. Second, what SSL implementation are you using? Setting up an SSL connection requires a lot more than just sending TLS negotiation command.s – Andrew Henle Oct 11 '17 at 19:33
  • I am new to C++ and socket programming. All I know is that the gmail-smtp server requires STARTTLS and login Authentication(user,pass). I'm not sure where to start with SSL. I thought TLS is seperate from SSL? – Programminghobby Oct 11 '17 at 19:44
  • TLS is a later version of SSL. See [the Wiki page](https://en.wikipedia.org/wiki/Transport_Layer_Security) for details on the history. You probably should start by looking at OpenSSL for an implementation. See https://www.openssl.org/ – Andrew Henle Oct 11 '17 at 19:47
  • based on this @Eugene Mayevski 'Allied Bits https://stackoverflow.com/a/5540504/3576562 this suggest that gmail requires a implicit SSL/TLS where STARTTLS command is sent to initiate "SSL handshake" then application-level protocol where it requests a user/password? – Programminghobby Oct 11 '17 at 19:51
  • Assuming you're using OpenSSL, you'll have to modify a "normal" SSL connection to do STARTTLS. Start just like you are, but once you send `STARTTLS\r\n` (note the `\r\n` instead of just `\n`), you need to call `SSL_set_fd()` to set the socket to use for SSL/TLS to the one you've already connected to, then call `SSL_connect()` to do the SSL/TLS negotiation. Once that's successful, you switch to using `SSL_read()` and `SSL_write()` instead of `send()` and `recv()`. Be sure to read the documentation on those functions - they can return errors at any time such that you have to retry them. – Andrew Henle Oct 11 '17 at 20:03
  • Will using Openssl mean including openSSL like "#pragma comment(lib, "ws2_32.lib")" Does OpenSSL also feature a login-authentication function for gmail servers? – Programminghobby Oct 11 '17 at 20:57

1 Answers1

1

This is a quick, abbreviated "STARTTLS" example using OpenSSL, without any error checking (the proper header files are left as an exercise - that will help learn OpenSSL):

SSL_CTX *ctx = SSL_CTX_new( SSLv23_client_method() );
SSL *ssl = SSL_new( ctx );

connect( sock, ( struct sockaddr * ) &saddr_in, sizeof( saddr_in ) );

send( sock, "EHLO X\r\n", strlen( "EHLO X\r\n" );

// drain any reply - you can examine this - it can have useful data in it
recv( sock, buf, sizeof( buf ) );

// switch this plaintext connection over to SSL/TLS
send( sock, ". STARTTLS\r\n", strlen( ". STARTTLS\r\n" );
recv( sock, buf, sizeof( buf ) );

// now start the SSL/TLS negotiation
SSL_set_fd( ssl, sock );
int rc = SSL_connect( ssl );

...

After this, assuming the SSL_connect() call succeeds, you'd use SSL_write( ssl, void *buf, int num ) instead of send( sock, void *buf, size_t num ), and SSL_read() instead of recv() to read and write data such as user names and passwords. Note that you really need to pay attention to incomplete read and write operations, and also failures that return SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE from SSL_get_error(). For example, if SSL_read() fails, then SSL_get_error() returns SSL_ERROR_WANT_READ, you have to retry the SSL_read().

And you will want to add a lot of error checking - check everything, and actually do calls such as SSL_write() and SSL_read() from helper functions that handle short writes and errors, and properly retry.

Getting it working will take some trial-and-error on your part.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • Great so the openssl communicates with the gmail server using the SSL_write() and SSL_read() to initiate the secure certificate and read the feedback from the server instead of the native send() and recv() for sockets? – Programminghobby Oct 11 '17 at 21:04
  • I currently have a error: "crypto.h(659): fatal error C1070: mismatched #if/#endif pair in file" . Did you get this compilation error when including openssl? – Programminghobby Oct 12 '17 at 04:55
  • @ComputerScience How are you compiling it? You'd probably do better to find and use a precompiled, binary distribution. I do know it took me a bit to get it to compile on Windows - and I used the MSVC command-line compile method. I also remember that the version of Perl available really matters - only one really works, and I don't remember offhand which one. The OpenSSL official docs: https://wiki.openssl.org/index.php/Compilation_and_Installation See http://p-nand-q.com/programming/windows/building_openssl_with_visual_studio_2013.html for some binary distributions. – Andrew Henle Oct 12 '17 at 11:59
  • ,Not sure the version of Openssl library from CodeProject.com's implementation. It had openssl library, all changes required are in this post https://stackoverflow.com/questions/43553483/fatal-error-openssl-e-os2-h-no-such-file-or-directory. The official openssl website had openssl-1.0.2I.tar.gz but didn't use the install file to install the library. Instead I just copied the 'crypto.h' from the openssl-1.0.2I.tar.gz compressed folder and still same error as the 'crypto.h' from the CodeProject folder. I used MSVC command-line also to build/compile my c++ socket file, – Programminghobby Oct 13 '17 at 04:22
  • then built .exe socket program. After swapping the brackets for parentheses on every '#include openssl/', I stepped through the entire 'crypto.h' and every #ifndef was closed by a #endif , oddly '#ifndef HEADER_CRYPTO_H' starts at the beginning of the file and ends at end of file. Do I need a Perl installation to access a working openssl library? – Programminghobby Oct 13 '17 at 04:22
  • What Openssl version did you use and how do you find a precompiled version? Also what is the file type of a binary distribution openssl; .rar, .lib, .dll? – Programminghobby Oct 15 '17 at 19:56
  • what language did you use; native-C or C++ ? Also I noticed that IBM's implementation also changed the include libraries to parentheses like: #include "openssl/bio.h" . See the "Headers and initialization" in link: https://www.ibm.com/developerworks/library/l-openssl/ , the guy in the following link had similar issues compiling openssl with native-c or c++; https://stackoverflow.com/questions/41200177/https-with-openssl-on-linux-in-c-c – Programminghobby Oct 20 '17 at 20:32
  • Also did you use the TCP or UDP protocol when creating your socket? Any information on your socket initialization would be helpful. – Programminghobby Oct 22 '17 at 09:15