一、STMP基础知识
1、简介
- SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控
制信件的中转方式;
- SMTP协议属于TCP/IP协议族,它帮助每台计算机在发送或中转信件时找到下一个目的地,通过SMTP协议所指定的服务器,就可以
把E-mail寄到收信人的服务器上了,整个过程只要几分钟;而SMTP服务器则是遵循SMTP协议的发送邮件服务器,用来发送或中
转发出的电子邮件;
- MIME规范使得二进制文件能够通过SMTP来传输。目前大多数SMTP软件实现都支持8位MIME扩展,从而使二进制文件的传输变得
几乎和纯文本一样简单。
2、工作流程

- 当SMTP客户端有邮件要传送时,与SMTP服务器建立一个双向的传输通道;SMTP客户端负责邮件信息传送到一个或多个SMTP服
务器,如果失败则给出报告;
- SMTP服务器可能最终目的地,也可能是中间的中继或者网关。SMTP命令由SMTP客户端产生,发送到SMTP服务器。SMTP响应
由SMTP服务器发送给SMTP客户端,对命令做出回应;
- 邮件传输者可以出现在起始SMTP发送方与最终的SMTP接收方之间建立的连接上,或者出现在通过中间系统的一系列跃点上。一
旦传输通道建立和初始握手完成,SMTP客户端正常初始化邮件事务。这样的事务包括一系列命令,以定义邮件的发送方和目的
地,以邮件内容本身的传递;
- 发送者发送 MAIL 命令来指定发送者的邮件,如果接受者接收这个邮件,就回复OK ,接着发送者发送 RCPT命令来指定接收者的
邮箱,如果被接收同样回复OK,如果不接受则拒绝(不会终止整个通话)。接收者邮箱确定后,发送者用DATA命令指示要发送
数据,并用一个 . 结束发送。如果数据被接收,会收到OK ,然后用QUIT结束会话;
3、常用指令
【EHLO】:参数为SMTP客户端全称域名;与服务器确认通知其客户端使用的机器名称和地址,标识身份;
客户端发出后,若服务器支持SMTP服务,则会给出相应的回应;
【AUTH】:使用AUTH LOGIN与服务器进行登录验证;
【MAIL FROM】:发件人信息,若与认证信息不同则被定位为垃圾/恶意邮件;
【RCPT TO】:收件人地址;
【DATA】:邮件基本信息;
【FROM】:邮件基本信息;
【SUBJECT】:邮件标题;
【QUIT】:断开连接;
4、SMTP响应码
| |
|---|
| 211 | System status, or system help reply |
| 214 | Help message |
| 220 | <domain> Service ready |
| 221 | <domain> Service closing transmission channel |
| 235 | Authentication successful |
| 250 | Requested mail action okay, completed |
| 251 | User not local; will forward to <forward-path> |
| 252 | Cannot VRFY user, but will accept message and attempt delivery |
| 334 | AUTH input |
| 354 | Start mail input; end with <CRLF>.<CRLF> |
| 421 | <domain> Service not available, closing transmission channel |
| 432 | A password transition is needed |
| 450 | Requested mail action not taken: mailbox unavailable |
| 451 | Requested action aborted: local error in processing |
| 452 | Requested action not taken: insufficient system storage" |
| 454 | Temporary authentication failed |
| 500 | Syntax error, command unrecognized |
| 501 | Syntax error in parameters or arguments |
| 502 | Command not implemented |
| 503 | Bad sequence of commands |
| 504 | Command parameter not implemented |
| 530 | Authentication required |
| 534 | Authentication mechanism is too weak |
| 535 | Authentication credentials invalid |
| 538 | Encryption required for requested authentication mechanism |
| 550 | Requested action not taken: mailbox unavailable |
| 551 | User not local; please try <forward-path> |
| 552 | Requested mail action aborted: exceeded storage allocation |
| 553 | Requested action not taken: mailbox name not allowed |
| 554 | Transaction failed |
二、代码
添加链接描述
#include "smtp.h"
#include <iostream>
namespace Jxiepc {
Smtp::Smtp(int port, std::string domain, std::string user, std::string pwd,
std::string t_mail, std::string title, std::string content, std::string type)
: m_port(port), m_domain(domain), m_user(user), m_password(pwd), m_tmail(t_mail),
m_title(title), m_content(content), m_type(type) {
if(init() < 0) {
perror("init error");
}
}
Smtp::~Smtp() {
close(m_sockfd);
}
int Smtp::init() {
if(make_connect() == -1) {
return -1;
}
std::string str;
Recv();
if(strstr(m_buf, "220") == nullptr) { return -1; }
std::cout << "****: " << m_buf << std::endl;
Send("HELO " + m_user + "\r\n");
Recv();
if(strstr(m_buf, "250") == nullptr) { return -1; }
Send("AUTH LOGIN\r\n");
Recv();
if(strstr(m_buf, "334") == nullptr) { return -1; }
str = m_user.substr(0, m_user.find('@', 0));
str = enBase64(str.c_str());
str += "\r\n";
Send(str);
Recv();
if(strstr(m_buf, "334") == nullptr) { return -1; }
Send(enBase64(m_password.c_str()) + "\r\n");
Recv();
if(strstr(m_buf, "235") == nullptr) { return -1; }
std::cout << "AUTH SUCCESS..." << std::endl;
Send("MAIL FROM: <" + m_user + ">\r\n");
Recv();
if(strstr(m_buf, "250") == nullptr) { return -1; }
Send("RCPT TO: <" + m_tmail+ ">\r\n");
Recv();
if(strstr(m_buf, "250") == nullptr) { return -1; }
Send("DATA\r\n");
Recv();
str = "From: " + m_user + "\r\n";
str += "To: " + m_tmail + "\r\n";
str += "Subject: " + m_title + "\r\n";
str += "Content-Type: multipart/mixed;boundary=qwertyuiop\r\n";
str += "\r\n--qwertyuiop\r\n";
if(m_type == "html"){
str += "content-type:text/html;charset=utf-8\r\n";
}else{
str += "Content-Type: text/plain;charset=utf-8\r\n";
}
Send(str);
str = "\r\n" + m_content + "\r\n";
str += "\r\n--qwertyuiop--\r\n.\r\n";
Send(str);
Recv();
if(strstr(m_buf, "250") == nullptr) { return -1; }
std::cout << "send success..." << std::endl;
Send("QUIT\r\n");
return 0;
}
int Smtp::make_connect() {
m_sockfd = Socket(AF_INET, SOCK_STREAM, 0);
hostent *host_info = gethostbyname(m_domain.c_str());
if(host_info == nullptr){
perror("gethostbyname error");
return -1;
}
if(host_info->h_addrtype != AF_INET) {
perror("AF_INET error");
return -1;
}
char buf[128];
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = *((unsigned long*)host_info->h_addr_list[0]);
addr.sin_port = htons(m_port);
Connect(m_sockfd, (struct sockaddr*)&addr, sizeof(addr));
return 0;
}
void Smtp::Connect(int fd, const struct sockaddr *sa, socklen_t salen) {
if(connect(fd, sa, salen) == -1) {
perror("connect error");
exit(-1);
}
}
int Smtp::Socket(int family, int type, int protocol) {
int sockfd;
if((sockfd = socket(family, type, protocol)) == -1) {
perror("socket error");
exit(-1);
}
return sockfd;
}
ssize_t Smtp::Send(const std::string& str) {
ssize_t n;
std::cout << str;
if((n = send(m_sockfd, str.c_str(), str.length(), 0)) == -1) {
perror("write error");
exit(-1);
}
return n;
}
ssize_t Smtp::Recv() {
ssize_t n;
m_buf[0] = '\0';
if((n == recv(m_sockfd, m_buf, 0xFFF, 0)) == -1) {
perror("recv error");
exit(-1);
}
return n;
}
std::string Smtp::enBase64(const std::string& str)
{
std::string base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int str_len = str.length();
std::string res="";
for (int strp=0; strp<str_len/3*3; strp+=3)
{
res+=base64_table[str[strp]>>2];
res+=base64_table[(str[strp]&0x3)<<4 | (str[strp+1])>>4];
res+=base64_table[(str[strp+1]&0xf)<<2 | (str[strp+2])>>6];
res+=base64_table[(str[strp+2])&0x3f];
}
if (str_len%3==1)
{
int pos=str_len/3 * 3;
res += base64_table[str[pos]>>2];
res += base64_table[(str[pos]&0x3)<<4];
res += "=";
res += "=";
}
else if (str_len%3==2)
{
int pos=str_len/3 * 3;
res += base64_table[str[pos]>>2];
res += base64_table[(str[pos]&0x3)<<4 | (str[pos+1])>>4];
res += base64_table[(str[pos+1]&0xf)<<2];
res += "=";
}
return res;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184