一、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;
}
}