使用 SMTP 在 Linux 中使用 C 语言发送电子邮件
Posted
技术标签:
【中文标题】使用 SMTP 在 Linux 中使用 C 语言发送电子邮件【英文标题】:Using SMTP for sending e-mail with C in Linux 【发布时间】:2016-04-17 03:57:48 【问题描述】:我试图用 c 语言发送一封电子邮件,但在握手、确认消息和打印消息发送后,它没有将邮件发送到我的收件箱。我的整个代码成功执行。能帮忙吗,谢谢。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define BUFFEr_SIZE 4096
void error(char *msg)
perror(msg);
exit(0);
int main(int argc, char *argv[])
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[BUFFEr_SIZE];
if (argc < 3)
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL)
fprintf(stderr,"ERROR, no such host\n");
exit(0);
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
n = read(sockfd,buffer,BUFFEr_SIZE-1);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
bzero(buffer,BUFFEr_SIZE);
/*------------------------------*/
printf("\nDONE\n");
printf("EHLO");
strcpy(buffer,"ehlo smtp.gmail.com\n");
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,BUFFEr_SIZE);
n = read(sockfd,buffer,BUFFEr_SIZE-1);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
/*------------------------------*/
/*------------------------------*/
printf("\nDONE EHLO\n");
printf("AUTH");
strcpy(buffer,"AUTH LOGIN\n");
n = write(sockfd,buffer,strlen(buffer)+1);
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,BUFFEr_SIZE);
n = read(sockfd,buffer,BUFFEr_SIZE-1);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
/*------------------------------*/
/*------------------------------*/
printf("\nDONE AUTH\n");
printf("AUTH UID");
strcpy(buffer,"xxxx@gmail.com");
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,BUFFEr_SIZE);
n = read(sockfd,buffer,BUFFEr_SIZE-1);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
/*------------------------------*/
/*------------------------------*/
printf("\nDONE UID\n");
printf("AUTH PWD");
strcpy(buffer,"xxxxxx");
n = write(sockfd,buffer,strlen(buffer)+1);
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,BUFFEr_SIZE);
n = read(sockfd,buffer,BUFFEr_SIZE-1);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
/*------------------------------*/
/*------------------------------*/
printf("MAIL FROM");
strcpy(buffer,"MAIL FROM: xxxxx@gmail.com");
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,BUFFEr_SIZE);
n = read(sockfd,buffer,BUFFEr_SIZE-1);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
/*------------------------------*/
/*------------------------------*/
printf("MAIL TO");
strcpy(buffer,"RCPT TO: aaaaa@gmail.com");
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,BUFFEr_SIZE);
n = read(sockfd,buffer,BUFFEr_SIZE-1);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
/*------------------------------*/
/*------------------------------*/
printf("DONE MAILTO\n");
printf("DATA");
strcpy(buffer,"DATA\r\n");
n = write(sockfd,buffer,strlen(buffer));
strcpy(buffer,"Subject: test\r\n");
n = write(sockfd,buffer,strlen(buffer));
strcpy(buffer,"SMTP MAIL TOOL TEST WORKS!!!\r\n");
n = write(sockfd,buffer,strlen(buffer));
strcpy(buffer,"\n\n");
n = write(sockfd,buffer,strlen(buffer));
strcpy(buffer,".\n");
n = write(sockfd,buffer,strlen(buffer));
/*------------------------------*/
/*------------------------------*/
printf("SON DONE");
strcpy(buffer,"quit\n");
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,BUFFEr_SIZE);
n = read(sockfd,buffer,BUFFEr_SIZE-1);
if (n < 0)
error("ERROR reading from socket");
puts(buffer);
/*------------------------------*/
return 0;
输出在这里:
220 smtp.gmail.com ESMTP bh6sm124854736wjb.0 - gsmtp
DONE EHLO250-smtp.gmail.com 为您服务,[85.98.184.204]
250 尺寸 35882577
250-8BITMIME
250-STARTTLS
250-增强状态代码
250-流水线
250 分块
250 SMTPUTF8
DONE EHLO AUTH451 4.5.0 SMTP 协议违规,请参阅 RFC 2821 bh6sm124854736wjb.0 - gsmtp
完成认证
身份验证 UID
完成 UID
身份验证密码
发件人
邮寄给
邮寄完成
数据完成
【问题讨论】:
如果你的代码“执行成功”,为什么要问? 程序运行时输出什么?此外,您应该将send
和recv
用于套接字,而不是write
和read
。
并学习正确的缩进。该代码非常难以阅读。
你能显示你的程序的输出吗?您建议它可以工作(或未检测到错误),这意味着您的 C 代码没有明显问题,需要调试的是 SMTP。
@dbush 我试过这些,但还是不行。
【参考方案1】:
发送AUTH LOGIN
命令后,SMTP 服务器返回451 4.5.0 SMTP protocol violation, see RFC 2821
。查看RFC 2181,这个命令不存在。
此命令是 SMTP 的扩展,此邮件服务器不支持。如果您删除身份验证命令,它应该可以工作。
此外,在继续测试时,请务必查看程序的输出,以确保服务器不再返回任何错误。如果是这样,请检查标准以了解为什么会出现这些错误。
【讨论】:
服务器的EHLO
回复报告支持STARTTLS
。如果您发送 STARTTLS
命令,完成 SSL/TLS 握手,然后再次发送 EHLO
,则允许服务器更新其功能,这通常包括启用对其他不安全的 AUTH
命令的支持,例如 @987654330 @.
我删除了 AUTH LOGIN 部分,但服务器没有回复其他部分。
@UtkuGökcan:未经身份验证,您无法通过 Gmail 发送电子邮件,并且如果不先创建安全的 SSL/TLS 会话,您将无法进行身份验证。这就是 Gmail 服务器的工作方式。也就是说,您的代码在做任何事情来验证服务器的响应时,您所做的只是发送任意缓冲区和读取任意缓冲区,但这不是 SMTP 的工作方式。您忽略了一个已定义的协议。
@RemyLebeau 我明白了你的想法,这是合乎逻辑的,但我应该在哪里添加 STARTTLS 命令?
@UtkuGökcan:阅读RFC 3207。您在EHLO
之后发送STARTTLS
(它会告诉您是否支持STARTTLS
)以及在您第一次需要发送敏感信息(例如身份验证凭据)之前的任何时间。【参考方案2】:
您需要发送地址为:
MAIL FROM:<userx@y.foo.org>
RCPT TO:<userc@d.bar.org>
【讨论】:
我在代码块中有这些地址,但我在这里写为 xxx@gmail.com。不过谢谢你的回答。 对不起。我不明白你的意思。但改变之后,什么都没有改变。【参考方案3】:协议违反仅仅是因为第二行中的+1:
strcpy(buffer,"AUTH LOGIN\n");
n = write(sockfd,buffer,strlen(buffer)+1);
您正在向 google 发送“AUTH LOGIN\n\0” - \0 不应该在那里。
您仍然会发现 google 不允许您通过普通的未加密 SMTP 连接(即没有 STARTTLS 并且实际上通过 TLS 连接)发送密码,但这是另一回事。
【讨论】:
【参考方案4】:这可能会对您有所帮助。我有意将所有代码放在“主”函数中。我知道这是不好的做法,也不专业,但这只是为了演示。
#include <sys/socket.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include "base64_codec.h"
#define BUFLEN 4096
/*void InitOpenSSL()
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
*/
int main()
int sock;
char *host, *cmd, *ip;
char* enc_cmd;
char recvbuf[BUFLEN];
int iResult;
size_t out_len;
struct hostent* hent;
struct sockaddr_in sin;
host = "smtp-mail.outlook.com";
printf("Attempting to connect to %s...\n", host);
//
hent = gethostbyname(host);
if(hent == NULL)
printf("gethostbyname failed: %d\n", errno);
return -1;
printf("gethostbyname succeeded!\n");
ip = inet_ntoa(*(struct in_addr*)hent->h_addr_list[0]);
printf("Host IP: %s\n", ip);
//
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1)
printf("socket failed: %d\n", errno);
return -1;
printf("socket succeeded!\n");
//
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(587);
sin.sin_addr.s_addr = inet_addr(ip);
//
iResult = connect(sock, (struct sockaddr*)&sin, sizeof(sin));
if(iResult < 0)
printf("connect failed: %d\n", errno);
return -1;
printf("connect succeeded\n");
//
bzero(recvbuf, BUFLEN);
iResult = recv(sock, recvbuf, BUFLEN - 1, 0);
if(iResult <= 0)
printf("recv failed: %d\n", errno);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "EHLO smtp-mail.outlook.com\r\n";
iResult = send(sock, cmd, strlen(cmd), 0);
if(iResult <= 0)
printf("send failed: %d\n", errno);
return -1;
printf("Byte(s) sent: %d\n", iResult);
//
bzero(recvbuf, BUFLEN);
iResult = recv(sock, recvbuf, BUFLEN - 1, 0);
if(iResult <= 0)
printf("recv failed: %d\n", errno);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "STARTTLS\r\n";
iResult = send(sock, cmd, strlen(cmd), 0);
if(iResult <= 0)
printf("send failed: %d\n", errno);
return -1;
printf("Byte(s) sent: %d\n", iResult);
//
bzero(recvbuf, BUFLEN);
iResult = recv(sock, recvbuf, BUFLEN - 1, 0);
if(iResult <= 0)
printf("recv failed: %d\n", errno);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
// OpenSLL region
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
if(SSL_library_init() < 0)
printf("Could not initialise SSL library!\n");
ERR_print_errors_fp(stderr);
return -1;
printf("SSL Library initialised\n");
SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method());
if(ctx == NULL)
printf("ctx failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("ctx done!\n");
SSL* ssl = SSL_new(ctx);
if(ssl == NULL)
printf("ssl failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("ssl done!\n");
SSL_set_fd(ssl, sock);
iResult = SSL_connect(ssl);
if(iResult < 0)
printf("SSL connect failed!\n");
ERR_print_errors_fp(stderr);
return -1;
printf("SSL connect succeeded!\n");
cmd = "EHLO smtp-mail.outlook.com\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) sent: %d\n", iResult);
//
bzero(recvbuf, BUFLEN);
iResult = SSL_read(ssl, recvbuf, BUFLEN - 1);
if(iResult <= 0)
printf("SSL_read failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "AUTH LOGIN\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write faile\n");
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) sent: %d\n", iResult);
bzero(recvbuf, BUFLEN);
iResult = SSL_read(ssl, recvbuf, BUFLEN - 1);
if(iResult <= 0)
printf("SSL_read failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "[Your-email]";
enc_cmd = base64_encode(cmd, strlen(cmd), &out_len);
iResult = SSL_write(ssl, enc_cmd, out_len);
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) sent: %d\n", iResult);
cmd = "\r\n";
SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) sent: %d\n", iResult);
bzero(recvbuf, BUFLEN);
iResult = SSL_read(ssl, recvbuf, BUFLEN - 1);
if(iResult <= 0)
printf("SSL_read failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "[Your password]";
enc_cmd = base64_encode(cmd, strlen(cmd), &out_len);
iResult = SSL_write(ssl, enc_cmd, out_len);
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) sent: %d\n", iResult);
cmd = "\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) sent: %d\n", iResult);
bzero(recvbuf, BUFLEN);
iResult = SSL_read(ssl, recvbuf, BUFLEN - 1);
if(iResult <= 0)
printf("SSL_read failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "MAIL FROM: <Your mail>\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) sent: %d\n", iResult);
bzero(recvbuf, BUFLEN);
iResult = SSL_read(ssl, recvbuf, BUFLEN - 1);
if(iResult <= 0)
printf("SSL_read failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "RCPT TO: <recipient email>\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
bzero(recvbuf, BUFLEN);
iResult = SSL_read(ssl, recvbuf, BUFLEN - 1);
if(iResult <= 0)
printf("SSL_read failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "DATA\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
bzero(recvbuf, BUFLEN);
iResult = SSL_read(ssl, recvbuf, BUFLEN - 1);
if(iResult <= 0)
printf("SSL_read failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "MIME-Version: 1.0\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "Content-Type:multipart/mixed;boundary=\"977d81ff9d852ab2a0cad646f8058349\"\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "Subject: Test Mail\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "--977d81ff9d852ab2a0cad646f8058349\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "Content-Type: text/plain; charset=\"utf-8\"\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "Content-Transfer-Encoding: quoted-printable\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "Hi Me,=0A=0AThis is an empty file.=0A=0ARegards,=0A<ME>=0A=0A---- =0ASent using Guerrillamail.com =0ABlock or report abuse : https://www.guerrillamail.com//abuse/?a=3DUVJzDA8SW6Q1mwa14nUTcwfCX9ne0dhd=0A\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "--977d81ff9d852ab2a0cad646f8058349\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "Content-Type: text/plain\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "Content-Transfer-Encoding: base64\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "Content-Disposition: attachment; filename=\"details.txt\"\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "U2FtcGxlIFRleHQu\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "\r\n--977d81ff9d852ab2a0cad646f8058349--\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
//
cmd = "\r\n.\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
bzero(recvbuf, BUFLEN);
iResult = SSL_read(ssl, recvbuf, BUFLEN - 1);
if(iResult <= 0)
printf("SSL_read failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
cmd = "QUIT\r\n";
iResult = SSL_write(ssl, cmd, strlen(cmd));
if(iResult <= 0)
printf("SSL_write failed\n");
ERR_print_errors_fp(stderr);
return -1;
bzero(recvbuf, BUFLEN);
iResult = SSL_read(ssl, recvbuf, BUFLEN - 1);
if(iResult <= 0)
printf("SSL_read failed: %d\n", errno);
ERR_print_errors_fp(stderr);
return -1;
printf("Byte(s) received: %d\n", iResult);
printf("%s\n", recvbuf);
//
SSL_CTX_free(ctx);
printf("SSL closed!\n");
iResult = SSL_shutdown(ssl);
if(iResult == 0)
printf("SSL shutdown in progress...\n");
iResult = SSL_shutdown(ssl);
if(iResult == 1)
printf("SSL shutdown succeeded\n");
if(iResult == -1)
printf("SSL shutdown failed!\n");
//
iResult = shutdown(sock, SHUT_RDWR);
if(iResult == -1)
printf("shutdown failed: %d\n", errno);
//return -1;
printf("shutdown succeeded\n");
iResult = close(sock);
if(iResult < 0)
printf("Error occurred while closing socket\n");
return -1;
//
return 0;
【讨论】:
很难调试一个 5 岁的涂鸦。基本上,即使在 DATA 之后,程序也应该至少检查响应的第一位以知道是否继续。现在大多数服务器都接受 EHLO,GMail 肯定接受。 (尽管程序接受主机名,但我们从 220 称呼中知道它是 GMail。) EHLO 参数应该是客户端的名称,而不是服务器的名称。无论如何,通过一些更正,一个天真的服务器可以接受以这种方式发送的消息。具有良好反垃圾邮件规定的服务器不会。以上是关于使用 SMTP 在 Linux 中使用 C 语言发送电子邮件的主要内容,如果未能解决你的问题,请参考以下文章
LINUX下的mail\mailx为啥无法使用外部SMTP发邮件