具有 SSL 连接的 SMTP 客户端
SMTP client with SSL connection
我有下一个使用 SMTP 客户端的示例代码,当我尝试通过使用 SSL 的邮件发送邮件时,我得到以下信息:
550 SMTP is available only with SSL or TLS connection enabled.
我如何扩展此示例以实现 SSL,例如通过 gmail 发送电子邮件等...我更喜欢套接字编码并且没有外部库:
int main()
{
if (FAILED (WSAStartup (MAKEWORD( 1,1 ), &ws)))
{
printf("Error in WSAStartup(...)\n");
return 1; }
// creating socket
s = socket (AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
printf("Error in socket(...)\n");
return 1; }
//get server address
d_addr = gethostbyname ("smtp.mail.ru");
if (d_addr==NULL)
{
printf("Error in gethostbyname(...)\n");
return 1; };
// fill address parameters
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = *((unsigned long *) d_addr->h_addr);
addr.sin_port = htons (25);
// connecting...
if (SOCKET_ERROR == (connect (s, (sockaddr *) &addr,
sizeof (addr))))
{
printf("Error in connect(...)\n");
return 1; }
// waiting from answer from server
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// sy hello to server
strcpy(text,"HELO smtp.mail.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// waiting approve from server
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// set sender
strcpy(text,"MAIL FROM: sender@mail.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// waiting for approve
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// set receiver
strcpy(text,"RCPT TO: receiver@mtu-net.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// waiting for approve
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// ready to start sending letter
strcpy(text,"DATA\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// waiting for approve
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// from whom letter
strcpy(text,"FROM: sender@mail.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// receiver
strcpy(text,"TO: receiver@mtu-net.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// letter subject
strcpy(text,"SUBJECT: test\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// letter text
strcpy(text,"Hi!\nIt is a message for you\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// telling that we finish
strcpy(text,"\r\n.\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// quit
strcpy(text,"QUIT");
send(s,text,strlen(text),0);
printf("send - %s", text);
// close socket
closesocket(s);
return 0;
}
服务器告诉您发送的 SMTP 命令要求套接字连接首先处于安全状态(尽管服务器应该为此目的使用回复代码 530
)。
您正在连接到端口 25,它通常是一个 未加密 SMTP 端口。 加密 SMTP 端口通常是 465(隐式 SSL)和 587(显式 TLS)。
您还在使用 HELO
命令,该命令已过时。您应该改用 EHLO
命令(参见 RFC 2821 Section 4.1.1.1)。这将允许服务器向您发送其功能列表(特别是其当前的安全和身份验证设置)。
在端口 25 和 587 上,SMTP 通信最初是未加密。您可以连接并立即收到服务器的 SMTP 问候语,并发送您的初始 HELO
/EHLO
命令。如果 EHLO
回复包含 STARTTLS
能力(参见 RFC 3207),您可以发送 STARTTLS
命令以启动 SSL/TLS 握手到 从那一刻起加密 通信。 STARTTLS
成功后,发送新的 EHLO
命令以获取更新的功能,然后再发送任何后续命令。
在端口 465 上,SMTP 通信始终加密。您必须在连接到服务器后立即开始 SSL/TLS 握手,然后它才能发送其 SMTP 问候语并且您发送初始 HELO
/EHLO
命令。
现在,关于 SSL/TLS 会话本身,有许多不同的方法可以实现它,但不要尝试从头开始实现 SSL/TLS!这是非常复杂的,你会弄错的。使用现有的外部 API/library 为您完成所有艰苦的工作。要在现有套接字代码之上添加 SSL/TLS,您可以使用 OpenSSL, or Microsoft's Crypto API (in particular, its SChannel provider), or even WinSock's own Secure Socket extensions. Or, you could replace your manual socket code with a 3rd party library that handles all of the SMTP and SSL/TLS logic for you, such as libcurl.
解决此问题的另一种方法可能是 运行 您的 C 程序在 Scott Gifford 的 sslclient 下(参见 http://www.superscript.com/ucspi-ssl/sslclient.html)。 sslclient 将生成您的程序并在您指定的端口上打开到服务器的 SSL 连接,并将程序的标准输出通过管道传输到服务器,并将服务器的输出通过管道传输到程序的标准输入。这样做的好处是,您可以让 sslclient 完成所有繁重的工作,例如设置套接字和 ssl 等,您可以专注于程序的核心功能。
我有下一个使用 SMTP 客户端的示例代码,当我尝试通过使用 SSL 的邮件发送邮件时,我得到以下信息:
550 SMTP is available only with SSL or TLS connection enabled.
我如何扩展此示例以实现 SSL,例如通过 gmail 发送电子邮件等...我更喜欢套接字编码并且没有外部库:
int main()
{
if (FAILED (WSAStartup (MAKEWORD( 1,1 ), &ws)))
{
printf("Error in WSAStartup(...)\n");
return 1; }
// creating socket
s = socket (AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
printf("Error in socket(...)\n");
return 1; }
//get server address
d_addr = gethostbyname ("smtp.mail.ru");
if (d_addr==NULL)
{
printf("Error in gethostbyname(...)\n");
return 1; };
// fill address parameters
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = *((unsigned long *) d_addr->h_addr);
addr.sin_port = htons (25);
// connecting...
if (SOCKET_ERROR == (connect (s, (sockaddr *) &addr,
sizeof (addr))))
{
printf("Error in connect(...)\n");
return 1; }
// waiting from answer from server
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// sy hello to server
strcpy(text,"HELO smtp.mail.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// waiting approve from server
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// set sender
strcpy(text,"MAIL FROM: sender@mail.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// waiting for approve
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// set receiver
strcpy(text,"RCPT TO: receiver@mtu-net.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// waiting for approve
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// ready to start sending letter
strcpy(text,"DATA\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// waiting for approve
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// from whom letter
strcpy(text,"FROM: sender@mail.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// receiver
strcpy(text,"TO: receiver@mtu-net.ru\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// letter subject
strcpy(text,"SUBJECT: test\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// letter text
strcpy(text,"Hi!\nIt is a message for you\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
// telling that we finish
strcpy(text,"\r\n.\r\n");
send(s,text,strlen(text),0);
printf("send - %s", text);
recv(s,text,sizeof(text),0);
printf("recv - %s", text);
// quit
strcpy(text,"QUIT");
send(s,text,strlen(text),0);
printf("send - %s", text);
// close socket
closesocket(s);
return 0;
}
服务器告诉您发送的 SMTP 命令要求套接字连接首先处于安全状态(尽管服务器应该为此目的使用回复代码 530
)。
您正在连接到端口 25,它通常是一个 未加密 SMTP 端口。 加密 SMTP 端口通常是 465(隐式 SSL)和 587(显式 TLS)。
您还在使用 HELO
命令,该命令已过时。您应该改用 EHLO
命令(参见 RFC 2821 Section 4.1.1.1)。这将允许服务器向您发送其功能列表(特别是其当前的安全和身份验证设置)。
在端口 25 和 587 上,SMTP 通信最初是未加密。您可以连接并立即收到服务器的 SMTP 问候语,并发送您的初始 HELO
/EHLO
命令。如果 EHLO
回复包含 STARTTLS
能力(参见 RFC 3207),您可以发送 STARTTLS
命令以启动 SSL/TLS 握手到 从那一刻起加密 通信。 STARTTLS
成功后,发送新的 EHLO
命令以获取更新的功能,然后再发送任何后续命令。
在端口 465 上,SMTP 通信始终加密。您必须在连接到服务器后立即开始 SSL/TLS 握手,然后它才能发送其 SMTP 问候语并且您发送初始 HELO
/EHLO
命令。
现在,关于 SSL/TLS 会话本身,有许多不同的方法可以实现它,但不要尝试从头开始实现 SSL/TLS!这是非常复杂的,你会弄错的。使用现有的外部 API/library 为您完成所有艰苦的工作。要在现有套接字代码之上添加 SSL/TLS,您可以使用 OpenSSL, or Microsoft's Crypto API (in particular, its SChannel provider), or even WinSock's own Secure Socket extensions. Or, you could replace your manual socket code with a 3rd party library that handles all of the SMTP and SSL/TLS logic for you, such as libcurl.
解决此问题的另一种方法可能是 运行 您的 C 程序在 Scott Gifford 的 sslclient 下(参见 http://www.superscript.com/ucspi-ssl/sslclient.html)。 sslclient 将生成您的程序并在您指定的端口上打开到服务器的 SSL 连接,并将程序的标准输出通过管道传输到服务器,并将服务器的输出通过管道传输到程序的标准输入。这样做的好处是,您可以让 sslclient 完成所有繁重的工作,例如设置套接字和 ssl 等,您可以专注于程序的核心功能。