Reputation: 115
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(
"[email protected]", // from - put an email address here
"[email protected]", // to - put an email address here
"subject",
"body",
"smtp.gmail.com",
"[email protected]",
"password",
587
);
if (ret != 0)
fprintf(stderr, "Failed to send mail (code: %i).\n", ret);
else
fprintf(stdout, "\nMail successfully sent.\n");
return ret;
}
Upvotes: 1
Views: 941
Reputation: 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.
Upvotes: 1