smtp中使用SSL与starttls的问题

时间:2021-08-24 15:17:14
开发smtp的时候,有些网站需要SSL验证,有些网站需要STARTTLS验证。
我使用OPENSSL来进行SSL验证。但对于需要STARTTLS验证的,似乎总不能成功。
谁能给个使用OPENSSL进行STARTTLS验证的例子啊。

举例:
服务器:smtp.gmail.com
端口:587
要求STARTTLS ,
我的做法是,普通socket connect,然后发送 EHLO,然后STARTTLS,
然后使用OPENSSL按照普通SSL方式连接,失败!

但对于pop.gmail.com 995是可以使用SSL连接的。似乎OPENSSL的SSL连接
应该是可行的,但STARTTLS就不可以,是我少做了什么还是什么原因?
请帮忙,谢谢!

12 个解决方案

#1


使用OPENSSL直接连接。

#2


引用楼主 snowboy1124 的帖子:
开发smtp的时候,有些网站需要SSL验证,有些网站需要STARTTLS验证。 
我使用OPENSSL来进行SSL验证。但对于需要STARTTLS验证的,似乎总不能成功。 
谁能给个使用OPENSSL进行STARTTLS验证的例子啊。 

举例: 
服务器:smtp.gmail.com 
端口:587 
要求STARTTLS , 
我的做法是,普通socket connect,然后发送 EHLO,然后STARTTLS, 
然后使用OPENSSL按照普通SSL方式连接,失败! 

但对于pop.gmail.com 995是可以使用SSL…

google官网上写的是pop只需要ssl,默认情况下一般使用的是sslv23,这个是可以通过的
而smtp则要求使用tls,所以如果的ssl选项当中使用的还是sslv23就无法成功了,如果没有特别的话,应该是只需要修改ssl选项即可。

#3


如下代码供你参考,其中的有一些常量定义和类变量及编码函数,可以通过命名识别,凑合着看,应该由你需要的东东

int CSmtp::TestSSL()
{
//-- 初始化 --SSL--
int err=-1;
//char* str;
SSL_CTX* ctx;
SSL* ssl;
//X509* server_cert;
SSL_METHOD *meth;

SSLeay_add_ssl_algorithms();
meth = SSLv2_client_method();
SSL_load_error_strings();
ctx = SSL_CTX_new (meth);
if(ctx == NULL) 
{
SetLastError(ME_SSL);
return -20;
}

char *p,*b;
//--
sockaddr_in       sin;
    int sock = socket (AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) 
{
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_CREATE);
return -1;
}
    sin.sin_family = AF_INET;
    sin.sin_port = htons( (unsigned short)m_iPort);

    struct hostent * host_addr = gethostbyname(m_szHost);
    if(host_addr==NULL) 
{
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_HOST);
return -4;
    }
    sin.sin_addr.s_addr = *((int*)*host_addr->h_addr_list) ;

//******************************
int TimeOut=50000;
if(::setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
closesocket(sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}
if(::setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
closesocket(sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}
//设置非阻塞方式连接
unsigned long ul = 1;
int ret = ioctlsocket(sock, FIONBIO, (unsigned long*)&ul);
if(ret==SOCKET_ERROR)
{
closesocket(sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}

//连接
connect (sock,(const struct sockaddr *)&sin, sizeof(sockaddr_in) );//MAY ERROR
struct timeval timeout ;
fd_set r;

FD_ZERO(&r);
FD_SET(sock, &r);
timeout.tv_sec = SSL_CNT_TIMEOUT;
timeout.tv_usec =0;
ret = select(0, 0, &r, 0, &timeout);
if ( ret <= 0 )
{
::closesocket(sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}
unsigned long ul1= 0 ;
ret = ioctlsocket(sock, FIONBIO, (unsigned long*)&ul1);
if(ret==SOCKET_ERROR)
{
::closesocket (sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}

//----
unsigned int iLen=0;

char sz[512]={0};
char * pszPut;
int  iBufLen=m_nDataLen*2+1024;
try
{
pszPut =new char[iBufLen];
}
catch(CException * e)
{
e->Delete();
::closesocket (sock);
SSL_CTX_free (ctx);
SetLastError(ME_MOMERY);
return -7;
}

p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(recv(sock,p,1,0) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}

if(iLen < 1)
{
SetLastError(ME_SOCK_CONNECT);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -6;
}

if(atoi(b) != 220) 
{
SetLastError(ME_SOCK_CONNECT);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -100;
}

//-----
//握手信号;
strcpy(sz,"EHLO BigLee\r\n");
iLen=send(sock,sz,strlen(sz),0);
if(iLen == SOCKET_ERROR)
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -8;
}
p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;

while(recv(sock,p,1,0) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -8;
}

if(atoi(b) != 250) 
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -100;
}
//***************
//握手信号;
strcpy(sz,"STARTTLS\r\n");
iLen=send(sock,sz,strlen(sz),0);
if(iLen == SOCKET_ERROR)
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -8;
}
p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(recv(sock,p,1,0) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -8;
}

if(atoi(b) != 220) 
{
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
SetLastError(ME_HELLO);
return -8;
}

//************************
ssl = SSL_new (ctx);
if(ssl == NULL)
{
SetLastError(ME_SSL);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -20;
}
SSL_set_fd (ssl, sock);
err = SSL_connect (ssl);
if(err == -1) 
{
SetLastError(ME_SSL);
goto Error;
}

//--auth
strcpy(sz,"AUTH LOGIN\r\n");
err = SSL_write (ssl, sz,strlen(sz)); 
if(err == -1)
{
SetLastError(ME_AUTH);
goto Error;
}

p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(SSL_read (ssl, p,1) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_AUTH);
goto Error;
}

if(atoi(b) != 334) 
{
SetLastError(ME_AUTH);
goto Error;
}
//account
strcpy(sz,m_szAc);
memset(pszPut,'\0',iBufLen);
CXMail::Base64Encode(sz,strlen(sz),pszPut,iBufLen,&iLen,0);
strcat(pszPut,"\r\n");
err = SSL_write (ssl, pszPut,strlen(pszPut)); 
if(err == -1)
{
SetLastError(ME_AUTH_AC);
goto Error;
}

p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(SSL_read (ssl, p,1) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_AUTH_AC);
goto Error;
}

if(atoi(b) != 334) 
{
SetLastError(ME_AUTH_AC);
goto Error;
}

//password
strcpy(sz,m_szPwd);
memset(pszPut,'\0',256);
CXMail::Base64Encode(sz,strlen(sz),pszPut,256,&iLen,0);
strcat(pszPut,"\r\n");

err = SSL_write (ssl, pszPut,strlen(pszPut)); 
if(err == -1)
{
SetLastError(ME_AUTH_PWD);
goto Error;
}
p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(SSL_read (ssl, p,1) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_AUTH_PWD);
goto Error;
}

if(atoi(b) != 235) 
{
SetLastError(ME_AUTH_PWD);
goto Error;
}

//-- END OF AUTH --
strcpy(sz,"QUIT\r\n");
SSL_write (ssl, sz,strlen(sz)); 

p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(SSL_read (ssl, p,1) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}

delete pszPut;
SSL_shutdown (ssl); 
closesocket(sock);
SSL_free (ssl);
SSL_CTX_free (ctx);
SetLastError("邮箱测试连接成功。",0);
m_bTestOk = TRUE;
return 0;

Error:
delete pszPut;
SSL_shutdown (ssl); 
closesocket(sock);
SSL_free (ssl);
SSL_CTX_free (ctx);
return -100;

}

#4


up一个
学习一下

#5


MARK,学习!

#6


 学习

#7


http://blog.vkill.net/read.php/138.htm
这个是ruby的,参考下

#8


你发送starttls后用新的socket重新连接了?

#9


刚好也在做这个东西,应该是你初始化或是链接的地方不对吧.
你可以加我msn:ckt1120@hotmail.com

#10



mark

#11


引用 4 楼 blackcat242 的回复:
up一个
 学习一下

#12


找到一套提供SMTP STARTTLS以及SMTP PORT465 (SSL CONNECT)的MAIL SERVER軟件
可提供需要的人測試

EVO mail server

#1


使用OPENSSL直接连接。

#2


引用楼主 snowboy1124 的帖子:
开发smtp的时候,有些网站需要SSL验证,有些网站需要STARTTLS验证。 
我使用OPENSSL来进行SSL验证。但对于需要STARTTLS验证的,似乎总不能成功。 
谁能给个使用OPENSSL进行STARTTLS验证的例子啊。 

举例: 
服务器:smtp.gmail.com 
端口:587 
要求STARTTLS , 
我的做法是,普通socket connect,然后发送 EHLO,然后STARTTLS, 
然后使用OPENSSL按照普通SSL方式连接,失败! 

但对于pop.gmail.com 995是可以使用SSL…

google官网上写的是pop只需要ssl,默认情况下一般使用的是sslv23,这个是可以通过的
而smtp则要求使用tls,所以如果的ssl选项当中使用的还是sslv23就无法成功了,如果没有特别的话,应该是只需要修改ssl选项即可。

#3


如下代码供你参考,其中的有一些常量定义和类变量及编码函数,可以通过命名识别,凑合着看,应该由你需要的东东

int CSmtp::TestSSL()
{
//-- 初始化 --SSL--
int err=-1;
//char* str;
SSL_CTX* ctx;
SSL* ssl;
//X509* server_cert;
SSL_METHOD *meth;

SSLeay_add_ssl_algorithms();
meth = SSLv2_client_method();
SSL_load_error_strings();
ctx = SSL_CTX_new (meth);
if(ctx == NULL) 
{
SetLastError(ME_SSL);
return -20;
}

char *p,*b;
//--
sockaddr_in       sin;
    int sock = socket (AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) 
{
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_CREATE);
return -1;
}
    sin.sin_family = AF_INET;
    sin.sin_port = htons( (unsigned short)m_iPort);

    struct hostent * host_addr = gethostbyname(m_szHost);
    if(host_addr==NULL) 
{
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_HOST);
return -4;
    }
    sin.sin_addr.s_addr = *((int*)*host_addr->h_addr_list) ;

//******************************
int TimeOut=50000;
if(::setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
closesocket(sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}
if(::setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
closesocket(sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}
//设置非阻塞方式连接
unsigned long ul = 1;
int ret = ioctlsocket(sock, FIONBIO, (unsigned long*)&ul);
if(ret==SOCKET_ERROR)
{
closesocket(sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}

//连接
connect (sock,(const struct sockaddr *)&sin, sizeof(sockaddr_in) );//MAY ERROR
struct timeval timeout ;
fd_set r;

FD_ZERO(&r);
FD_SET(sock, &r);
timeout.tv_sec = SSL_CNT_TIMEOUT;
timeout.tv_usec =0;
ret = select(0, 0, &r, 0, &timeout);
if ( ret <= 0 )
{
::closesocket(sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}
unsigned long ul1= 0 ;
ret = ioctlsocket(sock, FIONBIO, (unsigned long*)&ul1);
if(ret==SOCKET_ERROR)
{
::closesocket (sock);
SSL_CTX_free (ctx);
SetLastError(ME_SOCK_OPT);
return -5;
}

//----
unsigned int iLen=0;

char sz[512]={0};
char * pszPut;
int  iBufLen=m_nDataLen*2+1024;
try
{
pszPut =new char[iBufLen];
}
catch(CException * e)
{
e->Delete();
::closesocket (sock);
SSL_CTX_free (ctx);
SetLastError(ME_MOMERY);
return -7;
}

p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(recv(sock,p,1,0) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}

if(iLen < 1)
{
SetLastError(ME_SOCK_CONNECT);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -6;
}

if(atoi(b) != 220) 
{
SetLastError(ME_SOCK_CONNECT);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -100;
}

//-----
//握手信号;
strcpy(sz,"EHLO BigLee\r\n");
iLen=send(sock,sz,strlen(sz),0);
if(iLen == SOCKET_ERROR)
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -8;
}
p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;

while(recv(sock,p,1,0) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -8;
}

if(atoi(b) != 250) 
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -100;
}
//***************
//握手信号;
strcpy(sz,"STARTTLS\r\n");
iLen=send(sock,sz,strlen(sz),0);
if(iLen == SOCKET_ERROR)
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -8;
}
p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(recv(sock,p,1,0) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_HELLO);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -8;
}

if(atoi(b) != 220) 
{
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
SetLastError(ME_HELLO);
return -8;
}

//************************
ssl = SSL_new (ctx);
if(ssl == NULL)
{
SetLastError(ME_SSL);
delete pszPut;
::closesocket (sock);
SSL_CTX_free (ctx);
return -20;
}
SSL_set_fd (ssl, sock);
err = SSL_connect (ssl);
if(err == -1) 
{
SetLastError(ME_SSL);
goto Error;
}

//--auth
strcpy(sz,"AUTH LOGIN\r\n");
err = SSL_write (ssl, sz,strlen(sz)); 
if(err == -1)
{
SetLastError(ME_AUTH);
goto Error;
}

p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(SSL_read (ssl, p,1) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_AUTH);
goto Error;
}

if(atoi(b) != 334) 
{
SetLastError(ME_AUTH);
goto Error;
}
//account
strcpy(sz,m_szAc);
memset(pszPut,'\0',iBufLen);
CXMail::Base64Encode(sz,strlen(sz),pszPut,iBufLen,&iLen,0);
strcat(pszPut,"\r\n");
err = SSL_write (ssl, pszPut,strlen(pszPut)); 
if(err == -1)
{
SetLastError(ME_AUTH_AC);
goto Error;
}

p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(SSL_read (ssl, p,1) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_AUTH_AC);
goto Error;
}

if(atoi(b) != 334) 
{
SetLastError(ME_AUTH_AC);
goto Error;
}

//password
strcpy(sz,m_szPwd);
memset(pszPut,'\0',256);
CXMail::Base64Encode(sz,strlen(sz),pszPut,256,&iLen,0);
strcat(pszPut,"\r\n");

err = SSL_write (ssl, pszPut,strlen(pszPut)); 
if(err == -1)
{
SetLastError(ME_AUTH_PWD);
goto Error;
}
p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(SSL_read (ssl, p,1) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}
if(iLen < 1)
{
SetLastError(ME_AUTH_PWD);
goto Error;
}

if(atoi(b) != 235) 
{
SetLastError(ME_AUTH_PWD);
goto Error;
}

//-- END OF AUTH --
strcpy(sz,"QUIT\r\n");
SSL_write (ssl, sz,strlen(sz)); 

p = m_sz;
iLen = 0;
memset(p,'\0',MAX_MAIL_GET_LEN);
b = p;
while(SSL_read (ssl, p,1) > 0)
{
if((iLen++) >= MAX_MAIL_GET_LEN) break;
if(*p == 10) 
{
if(b[3] == ' ') break;
b = p+1;
}
p++;
}

delete pszPut;
SSL_shutdown (ssl); 
closesocket(sock);
SSL_free (ssl);
SSL_CTX_free (ctx);
SetLastError("邮箱测试连接成功。",0);
m_bTestOk = TRUE;
return 0;

Error:
delete pszPut;
SSL_shutdown (ssl); 
closesocket(sock);
SSL_free (ssl);
SSL_CTX_free (ctx);
return -100;

}

#4


up一个
学习一下

#5


MARK,学习!

#6


 学习

#7


http://blog.vkill.net/read.php/138.htm
这个是ruby的,参考下

#8


你发送starttls后用新的socket重新连接了?

#9


刚好也在做这个东西,应该是你初始化或是链接的地方不对吧.
你可以加我msn:ckt1120@hotmail.com

#10



mark

#11


引用 4 楼 blackcat242 的回复:
up一个
 学习一下

#12


找到一套提供SMTP STARTTLS以及SMTP PORT465 (SSL CONNECT)的MAIL SERVER軟件
可提供需要的人測試

EVO mail server