SMTP 服务器在 STARTTLS 完成之前被丢弃



在与"smtp.gmail.com"服务器通信时,我使用 EHLO 命令从 gmail "at your service" 响应获取问候语,然后使用 STARTTLS 命令并得到"2.0.0 准备启动 TLS"。在"STARTTLS"命令之后,我开始发送电子邮件数据,如"MAIL FROM: %s", from",但是当我这样做时,服务器连接关闭。我现在意识到服务器正在等待 TLS 协商,我应该发送哪些 TLS 协商命令来建立握手?我参考了第5章。来自 RFC 文档:[https://www.rfc-editor.org/rfc/rfc2487] 以获取有关 TLS 协商的信息。

我正在使用的C++代码来自:[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]='';
    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 %sn",       "MyMailDomain");  // Should I use HELO or EHLO?
    sendmail_write(sock, "STARTTLSn",""); // <----- starting TLS?
    sendmail_write(sock, "MAIL FROM: %sn", from);    // from
    sendmail_write(sock, "RCPT TO: %sn",   to);      // to
    sendmail_write(sock, "DATAn",          NULL);    // begin data
    sendmail_write(sock, "From: %sn",      from);
    sendmail_write(sock, "To: %sn",        to);
    sendmail_write(sock, "Subject: %sn",   subject);
    sendmail_write(sock, "n",              NULL);
    sendmail_write(sock, "%sn",            body);    // data
    sendmail_write(sock, ".n",             NULL);    // end data
    sendmail_write(sock, "QUITn",          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;
}
这是一个

使用OpenSSL的快速缩写"STARTTLS"示例,没有任何错误检查(正确的头文件作为练习保留 - 这将有助于学习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 Xrn", strlen( "EHLO Xrn" );
// 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, ". STARTTLSrn", strlen( ". STARTTLSrn" );
recv( sock, buf, sizeof( buf ) );
// now start the SSL/TLS negotiation
SSL_set_fd( ssl, sock );
int rc = SSL_connect( ssl );
...

在此之后,假设SSL_connect()调用成功,您将使用 SSL_write( ssl, void *buf, int num ) 而不是 send( sock, void *buf, size_t num ) ,并使用SSL_read()而不是recv()来读取和写入用户名和密码等数据。 请注意,您确实需要注意不完整的读写操作,以及从SSL_get_error()返回SSL_ERROR_WANT_READSSL_ERROR_WANT_WRITE的故障。 例如,如果SSL_read()失败,则SSL_get_error()返回SSL_ERROR_WANT_READ,则必须重试SSL_read()

您将需要添加大量错误检查 - 检查所有内容,并实际从处理短写入和错误的帮助程序函数执行诸如SSL_write()SSL_read()之类的调用,并正确重试。

让它工作需要你做一些反复试验。

相关内容

最新更新