概述
python之所以强大,是因为它支持的库很多,光标准库就满足了大部分的常用需求,工作中遇到收发邮件过防火墙的场景,一端发一个病毒穿过防火墙到达另一端,检测是否能够被防火墙拦住,这个功能是非常普通的,但又非常重要,基本上每次必测之内容
SMTP一般收发流程
发信人在用户代理如foxmail中编辑邮件,包括填写发信人,收信人,标题等
用户代理提取发信人编辑的信息,生成一封符合邮件标准格式的(RFC822)的邮件
用户代理用SMTP将邮件发送到发送邮件服务器上(发信人邮箱所对应的sendmail server)
发送端邮件服务器用SMTP将邮件发送到接收邮件服务器上(pop3 server)
收信人再调用用户代理,用pop3协议取回邮件
用户代理解解析收到的邮件 ,以适当的形式呈现在收信人面前
测试流程:
在LAN端搭一个SMTP server
在WAN端用foxmail客户端连接SMTP server发送一个病毒给LAN端的PC
此时病毒以附件的形式发送到LAN Server, 穿过防火墙,防火墙可以检测的到
python实现自动发送邮件思路
smtplib模块负责发送邮件
email模块负责发送的内容
发送邮件分三种形式
正文
附件
图片
smtplib模块介绍
server = smtplib.SMTP(smtp server) 实例化SMTP
server.docmd(cmd) 仿telenet 邮箱服务器中直接输入命令
server.login(username,password) SMTP服务器需要验证用户才能发邮件
server.sendmail(from_mail, to_mail,msg) 核心转发程序,把msg从from_mail发送到to_mail邮箱中
email模块介绍
MIMEMultipart类负责多种类消息载体
MIMEImage类负责图片载体
MIMEText类负责文字载体
MIMEBase类负责一般文件附件载体
MIMEImage
__init__(self, _imagedata, _subtype=None, _encoder=, **_params)
MIMEImage(file(filename,'rb').read())
MIMEText
__init__(self, _text, _subtype='plain', _charset='us-ascii')
MIMEText(body_text,'html','utf-8')
MIMEBase
__init__(self, _maintype, _subtype, **_params)
MIMEBase('text',filename).set_payload(fp.read())
SMTP 发送邮件命令模式,理论上都可以用smtplib.SMTP(host).docmd()完成
[root@hding qa]# telnet 10.8.116.6 25
Trying 10.8.116.6...
Connected to 10.8.116.6 (10.8.116.6).
Escape character is '^]'.
220 hding.com ESMTP Sendmail 8.13.8/8.13.8; Tue, 16 Aug 2016 20:00:57 +0800
ehlo 10.8.116.6 #服务器支持信息
250-hding.com Hello [10.8.116.6], pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-8BITMIME
250-SIZE
250-DSN
250-ETRN
250-AUTH DIGEST-MD5 CRAM-MD5 LOGIN PLAIN #验证方式
250-STARTTLS #支持STARTTLS
250-DELIVERBY
250 HELP
auth login
334 VXNlcm5hbWU6
aGRpbmc= #只接受base64编码
334 UGFzc3dvcmQ6
aGRpbmc= #只接受base64编码
235 2.0.0 OK Authenticated
mail from: hding@hding.com
250 2.1.0 hding@hding.com... Sender ok
rcpt to : qa@ding.com
250 2.1.5 qa@ding.com... Recipient ok
data
354 Enter mail, end with "." on a line by itself
"hello world"
I am terry
please welcome me
. #结束正文
250 2.0.0 u7GC0v9x027721 Message accepted for delivery
quit
221 2.0.0 hding.com closing connection
Connection closed by foreign host.
内部邮件服务器测试脚本
#!/usr/bin/env python
#coding:utf-8
from smtplib import SMTP
from smtplib import SMTP_SSL
from email import encoders
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
class DPI_SSL_SMTPS(object):
def __init__(self,from_mail, to_mail,subject,username='username',password='password', port=465,host='192.168.10.3'):
self.from_mail = from_mail
self.to_mail = to_mail
self.server = SMTP_SSL(host,port) #采取SMTP_SSL的方式,端口465
#self.server.starttls()
self.server.docmd('ehlo','192.168.10.3') #与服务器'ehlo'
self.server.login(username,password) #登录SMTP服务器
self.msg = MIMEMultipart() #定义消息载体
self.msg['From']= from_mail #消息from,to,subject字段不可省
self.msg['To']= to_mail
self.msg['Subject']=subject
def send_text(self,filename):
"send text as a body mail"
fp = open(filename,'r') #这边的正文内容是读取了一个文件中的内容写在正文中
content = fp.read()
fp.close()
con = MIMEText(content,'plain','utf-8')
self.msg.attach(con) #把正文内容加到消息载体中
def send_image(self,filename):
"send mail with image"
img = MIMEImage(file(filename,'rb').read()) #发送图片附件
img.add_header('Content-ID','1') #增加头信息cid,是为了以后如果要在正文中引用""'
img.add_header("Content-Disposition","attachment",filename=filename) #增加附件
self.msg.attach(img) #图片附件进入消息载体
def send_attachment(self,filename):
"send mail with attachment"
fp = open(filename,'rb')
mime = MIMEBase('text',filename)
mime.add_header('Content-Disposition','attachment',fiename=filename)
mime.set_payload(fp.read()) #把文件中的内容写进payload作为附件
encoders.encode_base64(mime)
fp.close()
self.msg.attach(mime)
def send_mail(self):
self.server.sendmail(self.from_mail,self.to_mail,self.msg.as_string())
self.server.quit()
if __name__ == '__main__':
server = DPI_SSL_SMTPS('hding@hding.com','qa@ding.com','test_mail')
server.send_attachment("test_file")
server.send_image('2.jpg')
server.send_text("test_file")
server.send_mail()
遇到的问题
smtplib.SMTPAuthenticationError: (535, 'Error: authentication failed')
这个问题是认证的问题,但是用命令行已经验证了用户口令正确,但执行时就会提示错误,原因在于认证的方式不同,命令行用的是AUTH_LOGIN,而smtplib时的login函数用的却是MD5,查看smtplib.py文件
def login(self, user, password):
"""Log in on an SMTP server that requires authentication.
The arguments are:
- user: The user name to authenticate with.
- password: The password for the authentication.
If there has been no previous EHLO or HELO command this session, this
method tries ESMTP EHLO first.
This method will return normally if the authentication was successful.
This method may raise the following exceptions:
SMTPHeloError The server didn't reply properly to
the helo greeting.
SMTPAuthenticationError The server didn't accept the username/
password combination.
SMTPException No suitable authentication method was
found.
"""
def encode_cram_md5(challenge, user, password):
challenge = base64.decodestring(challenge)
response = user + " " + hmac.HMAC(password, challenge).hexdigest()
return encode_base64(response, eol="")
def encode_plain(user, password):
return encode_base64("