【SMTP】C语言实现smtp客户端发送mail+附件_c语言解析smtp协议内容及附件内容

'=' ,

};


#### IV.smtp加密连接


##### 1. smtp端口号


摘自知乎:[smtp端口号]( )



> 
> **163.com:**
> 
> 
> 接收邮件服务器: [http://pop.163.com]( )
> 
> 
> 接收端口: 110或995(使用ssl时)
> 
> 
> 接收邮件服务器: [http://imap.163.com]( )
> 
> 
> 接收端口: 143或993(使用ssl时)
> 
> 
> 发送邮件服务器: [http://smtp.163.com]( )
> 
> 
> 发送端口: 25或465/994(使用ssl时)
> 
> 
> **126邮箱:**
> 
> 
> 接收邮件服务器: [http://pop.126.com]( )
> 
> 
> 接收端口: 110
> 
> 
> 发送邮件服务器: [http://smtp.126.com]( )
> 
> 
> 发送端口: 25
> 
> 
> **139邮箱:**
> 
> 
> POP3服务器地址:[http://POP.139.com]( )(端口:110)
> 
> 
> SMTP服务器地址:[http://SMTP.139.com]( )(端口:25)
> 
> 
> **QQ邮箱**:
> 
> 
> 接收邮件服务器: [http://pop.qq.com]( )
> 
> 
> 接收端口: 110或995(使用ssl时)
> 
> 
> 接收邮件服务器: [http://imap.qq.com]( )
> 
> 
> 接收端口: 143或993(使用ssl时)
> 
> 
> 发送邮件服务器: [http://smtp.qq.com]( )
> 
> 
> 发送端口: 25或465/587(使用ssl时)
> 
> 
> **QQ企业邮箱 :**
> 
> 
> POP3服务器地址:[http://pop.exmail.qq.com]( ) (SSL启用 端口:995)
> 
> 
> SMTP服务器地址:[http://smtp.exmail.qq.com]( )【端口:25或465/587(使用ssl时)】
> 
> 
> **gmail([http://google.com]( )) :**
> 
> 
> POP3服务器地址:[http://pop.gmail.com]( )(SSL启用 端口:995)
> 
> 
> SMTP服务器地址:[http://smtp.gmail.com]( )(SSL启用 端口:587)
> 
> 
> **Foxmail:**
> 
> 
> POP3服务器地址:[http://POP.foxmail.com]( )(端口:110)
> 
> 
> SMTP服务器地址:[http://SMTP.foxmail.com]( )(端口:25)
> 
> 
> **sina.com:**
> 
> 
> 接收邮件服务器: [http://pop.sina.com]( )
> 
> 
> 接收端口: 110
> 
> 
> 发送邮件服务器: [http://smtp.sina.com]( )
> 
> 
> 发送端口: 25
> 
> 
> **sinaVIP:**
> 
> 
> POP3服务器:[http://pop3.vip.sina.com]( ) (端口:110)
> 
> 
> SMTP服务器:[http://smtp.vip.sina.com]( ) (端口:25)
> 
> 
> **sohu.com:**
> 
> 
> POP3服务器地址:[http://pop3.sohu.com]( )(端口:110)
> 
> 
> SMTP服务器地址:[http://smtp.sohu.com]( )(端口:25)
> 
> 
> 雅虎邮箱:
> 
> 
> 接收邮件服务器: [http://pop.mail.yahoo.cn]( )
> 
> 
> 接收端口: 110或995(使用ssl时)
> 
> 
> 接收邮件服务器: [http://imap.mail.yahoo.cn]( )
> 
> 
> 接收端口: 143或993(使用ssl时)
> 
> 
> 发送邮件服务器: [http://smtp.mail.yahoo.cn]( )
> 
> 
> 发送端口: 25或465(使用ssl时)
> 
> 
> **HotMail :**
> 
> 
> 接收邮件服务器: [http://pop3.live.com]( )
> 
> 
> 接收端口: 995
> 
> 
> 发送邮件服务器: [http://smtp.live.com]( )
> 
> 
> 发送端口: 25
> 
> 
> **263.net:**
> 
> 
> POP3服务器地址:[http://pop3.263.net]( )(端口:110)
> 
> 
> SMTP服务器地址:[http://smtp.263.net]( )(端口:25)
> 
> 
> **263.net.cn:**
> 
> 
> POP3服务器地址:[http://pop.263.net.cn]( )(端口:110)
> 
> 
> SMTP服务器地址:[http://smtp.263.net.cn]( )(端口:25)
> 
> 
> **x263.net:**
> 
> 
> POP3服务器地址:[http://pop.x263.net]( )(端口:110)
> 
> 
> SMTP服务器地址:[http://smtp.x263.net]( )(端口:25)
> 
> 
> **21cn.com:**
> 
> 
> POP3服务器地址:[http://pop.21cn.com]( )(端口:110)
> 
> 
> SMTP服务器地址:[http://smtp.21cn.com]( )(端口:25)
> 
> 
> **china.com:**
> 
> 
> POP3服务器地址:[http://pop.china.com]( )(端口:110)
> 
> 
> SMTP服务器地址:[http://smtp.china.com]( )(端口:25)
> 
> 
>   
> **tom.com:**
> 
> 
> POP3服务器地址:[http://pop.tom.com]( )(端口:110)
> 
> 
> SMTP服务器地址:[http://smtp.tom.com]( )(端口:25)
> 
> 
>   
> **etang.com:**
> 
> 
> POP3服务器地址:[http://pop.etang.com]( )
> 
> 
> SMTP服务器地址:[http://smtp.etang.com]( )
> 
> 
> 


##### 2. smtp跳转加密端口


根据以上各服务器的端口号能看出,非加密端口都是25,加密端口有465,587,994等。


以smtp.qq.com为例,如果直接连465端口,需要进行ssl连接。


如果连接25端口,但需要加密连接,则可以发送STARTTLS跳转到加密端口。



C:telnet smtp.qq.com 25
S:220 newxmesmtplogicsvrszb1-0.qq.com XMail Esmtp QQ Mail Server.
C:HELO SMTP
S:250 OK
C:STARTTLS
S:220 Ready to start TLS from 180.108.251.243 to newxmesmtplogicsvrszb1-0.qq.com.

之后要发送数字签名等,没法手写,所以telnet只能到这一步


##### 3.SSL编程基础


我们通常进行tcp连接后都会返回一个sockfd,在之后读写数据时通过sockfd即可向远端进行通信。ssl加密连接相当于在sock上套了一层加密解密协议。简单来说,我们将sockfd作为参数传入SSL\_Connect函数后,之后会返回一个(SSL \*)pConnId,作用与sockfd一样,读写时使用SSL\_Read和SSL\_Write即可。以下给出SSL\_Connect代码:



VOID* SmtpSslConnect(int i4SockFd)
{
SSL_CTX *pSslCtx;
SSL *pSslConnId = NULL;
BIO *pSockBio;
X509 *X509_cert_peer;
const char *pu1Cafile = NULL; //这里不需要Cafile

SSL_library_init();

/* 初始化ssl ctx */
pSmtpSslCtx = SSL_CTX_new(SSLv23_client_method());

/* 选择版本,去掉SSLv2,SSLv3,TLSv1版本 */
SSL_CTX_set_options(pSmtpSslCtx,
                    SSL_OP_NO_SSLv2 |
                    SSL_OP_NO_SSLv3 |
                    SSL_OP_NO_TLSv1);

/* 配置数字签名等 */
SSL_CTX_set_mode(SslCtx, SSL_MODE_AUTO_RETRY);
if(SMTP_SSL_CERT_VERIFY)  /* 该宏需要自定义,这里未定义可视为0 */
{
    SSL_CTX_set_verify(SslCtx, SSL_VERIFY_PEER, NULL);
}

/*
 * Set the path to the user-provided CA file or use the default cert paths
 * if not provided.
 */
if(pu1Cafile)
{
    if(SSL_CTX_load_verify_locations(SslCtx, NULL, NULL) != 1)
    {
        SSL_CTX_free(SslCtx);
        return (NULL);
    }
}
else
{
    X509_STORE_set_default_paths(SSL_CTX_get_cert_store(SslCtx));
    if(ERR_peek_error() != 0)
    {
        SSL_CTX_free(SslCtx);
        return (NULL);
    }
}

/* 创建BIO socket */
pSockBio = BIO_new_socket(i4SockFd, 0))

SSL_set_bio (pSslConnId, pSockBio);
SSL_set_connect_state(pSslConnId);

/* ssl connect */
SSL_connect(pSslConnId)

return (VOID *)pSslConnId;

}


#### V. c语言实现smtp客户端代码


**注意:非可直接运行的代码,只有函数实现,仅提供思路。**


##### 1.smtp客户端



#define SMTP_SRV_READY_CODE “220”
#define SMTP_SRV_CLOSE_CODE “221”
#define SMTP_SRV_OK_CODE “250”
#define SMTP_SRV_START_MAIL “354”
#define SMTP_SRV_AUTH_CODE “334”
#define SMTP_SRV_AUTH_SUCCESS “235”

int SmtpSslReadResponse (void *pConnId, const char *pu1Code)
{
char au1RecvBuf[SMTP_RCV_BUF_LEN];

memset(au1RecvBuf, 0, SMTP_RCV_BUF_LEN);
if ((SSL_read(pConnId, (UINT1 *)au1RecvBuf, SMTP_RCV_BUF_LEN)) > 0)
{
    if ((strstr (au1RecvBuf, pu1CheckCode)) != NULL)
    {
        return (SMTP_SUCCESS);
    }
    else
    {
        return (SMTP_FAILURE);
    }
}

strcpy (au1ErrorBuf, "Connection Closed ");
return (SMTP_FAILURE);

}

int SmtpSslSendCmd (void *pConnId, int u4Len)
{
int i4BytesWritten;

if ((i4BytesWritten = SSL_write(pConnId, (const char *)au1CmdBuf, u4Len)) > 0)
{
    return i4BytesWritten;
}
else
{
    return SMTP_FAILURE;
}

}

void SmtpSendEmail(void)
{
int i4SockFd;
void *pConnId;

//TCP Connect to Server
MailServAddr.sin_addr.s_addr = OSIX_HTONL (gsSmtpAlertParams.u4MailServerIp);
MailServAddr.sin_family = AF_INET;
MailServAddr.sin_port = OSIX_HTONS (gsSmtpAlertParams.u4SMTPPort);

i4SockFd = (socket (AF_INET, SOCK_STREAM, IPPROTO_TCP));

connect (i4SockFd, (struct sockaddr *) &MailServAddr, sizeof (MailServAddr);

/* 如果不是25端口,就认为是加密端口 */
if (gsSmtpAlertParams.u4SMTPPort != 25)
{
    pConnId = SmtpSslConnect(i4SockFd);

    if (SmtpSslReadResponse(pConnId,SMTP_SRV_READY_CODE) != SMTP_SUCCESS)
    {
        /* 连接之后会收到220消息,如果不是说明连接发生错误 */
        close (i4SockFd);
        return ;
    }
}
else  /* 发生STARTTLS */
{
    /* 这里还没有加密,使用read而不是ssl_read */
    if (SmtpReadResponse (i4SockFd, (const UINT1 *)SMTP_SRV_READY_CODE) != SMTP_SUCCESS)
    {
        close (i4SockFd);
        return ;
    }

    memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
    u4Len = sprintf ((char *) au1CmdBuf, "STARTTLS\r\n");
    SmtpSendCmd (i4SockFd, u4Len);

    if (SmtpReadResponse (i4SockFd,
                          const char *)SMTP_SRV_READY_CODE) == SMTP_FAILURE)
    {
        close (i4SockFd);
        return;
    }

    pConnId = SmtpSslConnect(i4SockFd);
}

/* HELO SMTP */
memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
u4Len = sprintf ((CHR1 *) au1CmdBuf, "HELO SMTP\r\n");
SmtpSslSendCmd (pConnId, u4Len);

if (SmtpSslReadResponse(pConnId, SMTP_SRV_OK_CODE) == SMTP_FAILURE)
{
    close (i4SockFd);
    return SMTP_FAILURE;
}

/* AUTH LOGIN */
memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
u4Len = sprintf ((char *) au1CmdBuf, "AUTH LOGIN\r\n");
SmtpSslSendCmd (pConnId, u4Len);

if (SmtpSslReadResponse(pConnId, SMTP_SRV_AUTH_CODE) == SMTP_FAILURE)
{
    close (i4SockFd);
    return SMTP_FAILURE;
}

/* Username(base64) */
memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
memset (au1BaseBuf,0, SMTP_DOMAIN_NAME_LEN);
strcpy(au1BaseBuf, gsSmtpAlertParams.au1SysDomainName);
b64_ntop(au1BaseBuf, strlen(au1BaseBuf), (char*)au1CmdBuf, SMTP_CMD_BUF_LEN);
strcat(au1CmdBuf,"\r\n");
u4Len = STRLEN(au1CmdBuf);
SmtpSslSendCmd (pConnId, u4Len);

if (SmtpSslReadResponse(pConnId, SMTP_SRV_AUTH_CODE) == SMTP_FAILURE)
{
    close (i4SockFd);
    return SMTP_FAILURE;
}

/* Key(base64) */
memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
memset (au1BaseBuf,0, SMTP_DOMAIN_NAME_LEN);
strcpy(au1BaseBuf, gsSmtpAlertParams.au1SysDomainNameKey);
b64_ntop(au1BaseBuf, strlen(au1BaseBuf), (char*)au1CmdBuf, SMTP_CMD_BUF_LEN);
strcat(au1CmdBuf,"\r\n");
u4Len = strlen(au1CmdBuf);
SmtpSslSendCmd (pConnId, u4Len);

if (SmtpSslReadResponse(pConnId, SMTP_SRV_AUTH_SUCCESS) == SMTP_FAILURE)
{
    close (i4SockFd);
    return SMTP_FAILURE;
}

/* MAIL FROM:<> */
memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
u4Len = sprintf ((CHR1 *) au1CmdBuf, "MAIL FROM:<%s> \r\n",
                 gsSmtpAlertParams.au1SysDomainName);
SmtpSslSendCmd (pConnId, u4Len);

if (SmtpSslReadResponse(pConnId, SMTP_SRV_OK_CODE) == SMTP_FAILURE)
{
    close (i4SockFd);
    return SMTP_FAILURE;
}

/* RCPT TO:<> */
memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
u4Len = sprintf ((char *) au1CmdBuf, "RCPT TO:<%s> \r\n",
                 gsSmtpAlertParams.au1RcvDomainName);
SmtpSslSendCmd (pConnId, u4Len);

if (SmtpSslReadResponse(pConnId, SMTP_SRV_OK_CODE) == SMTP_FAILURE)
{
    close (i4SockFd);
    return SMTP_FAILURE;
}

/* DATA */
memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
u4Len = sprintf ((char *) au1CmdBuf, "DATA\r\n");
SysLogSmtpSslSendCmd (pConnId, u4Len);

if (SysLogSmtpSslReadResponse(pConnId, SMTP_SRV_START_MAIL) == SMTP_FAILURE)
{
    close (i4SockFd);
    return SMTP_FAILURE;
}

/* data从flash中读取 */
SmtpSslSendData(pConnId, NULL, SYSLOG_ZERO);

/* QUIT */
memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
u4Len = sprintf ((char *) au1CmdBuf, "QUIT\r\n");
SysLogSmtpSslSendCmd (pConnId, u4Len);

if (SysLogSmtpSslReadResponse(pConnId, SMTP_SRV_CLOSE_CODE) == SMTP_FAILURE)
{
    close (i4SockFd);
    return SMTP_FAILURE;
}

return;

}


##### 2.正文及附件处理



#difine TEST_FILE “test.txt”
enum {
UUENC_SRC_BUF_SIZE = 15*3,
UUENC_DST_BUF_SIZE = 4 * (UUENC_SRC_BUF_SIZE + 2) / 3,
}

int SmtpSslSendData (void *pConnId, char *pu1Data, int u4DataLen)
{
int i4Len;
char au1Subject[100];
char au1SendBuf[MAX_FLASH_FILESIZE];
char au1Str[SMTP_DOMAIN_NAME_LEN + SMTP_DOMAIN_NAME_LEN + SYSLOG_FIFTY];
char au1SrcBuf[UUENC_SRC_BUF_SIZE];
char au1DstBuf[UUENC_DST_BUF_SIZE + 1];
int i4Filefd = 0;
int i4Size = 0;
int u4Pos = 0;
int au1DataTermStr[] = “\r\n.\r\n”;

/* From:
 * To: */
memset (au1Str, 0, sizeof (au1Str));
sprintf ((char *) au1Str, "From: %s\r\nTo: %s\r\n",
         gsSmtpAlertParams.au1SysDomainName,
         gsSmtpAlertParams.au1RcvDomainName);
if (SSL_write (pConnId, (const char *)au1Str, (int) strlen (au1Str), 0) <= 0)
{
    return SMTP_FAILURE;
}

/* Subject */
memset (au1Subject, 0, sizeof (au1Subject));
sprinft ((char *) au1Subject, "Subject: Email Alert \r\n");
if (SSL_write (pConnId, (const char *)au1Subject, (int) strlen (au1Subject), 0) <= 0)
{
    return SMTP_FAILURE;
}

/* Main text and annexes */
memset (au1SendBuf, 0, MAX_FLASH_FILESIZE);
u4Pos = sprintf((char *)au1SendBuf, "Syslog.\r\n");
if (u4DataLen == 0)
{
    u4Pos += sprintf((char *)au1SendBuf + u4Pos, "begin 644 %s", TEST_FILE);
    i4Filefd = fileopen(TEST_FILE, FILE_RO);
    while (1)

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新!!

2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-ocoYKRx1-1715608361630)]

[外链图片转存中…(img-53tZjH8o-1715608361630)]

[外链图片转存中…(img-QqLpCthr-1715608361631)]

[外链图片转存中…(img-kYY72nO5-1715608361632)]

[外链图片转存中…(img-glFZFpLP-1715608361632)]

[外链图片转存中…(img-wzLUyPn3-1715608361633)]

[外链图片转存中…(img-1WKcqsUU-1715608361633)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新!!

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
主要功能: 1、可以发送附件的邮件,附件可以是多个,附件大小限制由发送方服务器而定,暂未测试具体为多少MB 2、邮件内容和主题可以是空,但当有附件时,主题取第一个附件的文件名(不含扩展名) 3、密码验证均为base64加密 4、邮件正文和附件的数据传送方式,均为base64 5、自动解析发件箱的SMTP服务器 压缩包文件简介: base.c:包含一些基本的函数,其中有一些在此程序中并未用到,只要使用了其中的base64加密算法 mail.c:包含邮件发送、数据读取、编码转换、smtp服务器连接、ip解析等函数 mailsend.c:包含main的c源文件,mail.exe则是根据mailsend.c、mail.c、base.c编译成的,具体编译方 法可参考makefile libbase.a:make之后生成的静态库 moontalk.cfg:base.c用到的配置文件,可能没用,放在这里进攻阅读参考 mail.cfg:自定义用户的配置文件,可用可不用,用作读代码的参考 mail.exe:邮件发送的执行文件,仅有命令行模式完善了,逐步输入(直接双击)的方式还不完善 b64.exe:base64加密解密的小工具,仅供参考,mail.cfg中用到密码的地方,可以使这个工具得到。 makefile:工程编译链接文件 注意:在本地使用mingw环境开发,遵循ANSI C标准,本地有系统的工程库,但是上传的时候,把这些文件 都放在一起了,可以先参考makefile进行工程调整,如果有任何问题,请发送到邮箱moontalk@yeah.net, 技术交流,不胜感激。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值