具有SSL连接的SMTP客户端

时间:2015-03-12 22:32:05

标签: c++ email ssl smtp

我有下一个使用SMTP客户端的示例代码,当我尝试通过使用SSL的邮件发送邮件时,我得到以下内容:

  

550 SMTP仅在启用SSL或TLS连接时可用。

如何扩展此示例以实现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;
}

2 个答案:

答案 0 :(得分:2)

服务器告诉您发送了一个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 /库,为您完成所有艰苦的工作。要在现有套接字代码之上添加SSL / TLS,您可以使用OpenSSL或Microsoft的Crypto API(特别是其SChannel提供程序),甚至是WinSock' s拥有Secure Socket extensions。或者,您可以将第三方库替换为手动套接字代码,以便为您处理所有SMTP和SSL / TLS逻辑,例如libcurl

答案 1 :(得分:0)

解决此问题的另一种方法可能是在Scott Gifford的sslclient下运行您的C程序(请参阅http://www.superscript.com/ucspi-ssl/sslclient.html)。 sslclient将生成您的程序,并在您指定的端口上打开与服务器的SSL连接,并将程序的标准输出管道传送到服务器,并将服务器的输出管道输出到您的程序的标准输入。这样做的好处是你可以让sslclient完成所有繁重的工作,只要设置套接字和ssl等,你就可以专注于程序的核心功能。