使用smtpd搭建一个简易的smtp server

公司要求搭建一个smtp server 拦截邮箱发送的内容,但是还要让邮箱不报错, 使用python自带的SMTPD 搭建一个简单的smtp server

SMTPD

代码如下
#-*- coding: UTF-8 -*-
import threading
import asyncore
import datetime
from email.utils import parseaddr
from email.header import decode_header
from smtpd import SMTPServer
from email.parser import Parser#SMTPD 服务
class SSServer(SMTPServer):def guess_charset(self, msg):charset = msg.get_charset()if charset is None:content_type = msg.get('Content-Type', '').lower()pos = content_type.find('charset=')if pos >= 0:charset = content_type[pos + 8:].strip()return charsetdef decode_str(self,s):value, charset = decode_header(s)[0]if charset:value = value.decode(charset)return valuedef print_info(self,msg, indent=0):value_dict = {}if indent == 0:for header in ['From', 'To', 'Subject']:value = msg.get(header, '')if value:if header == 'Subject':value = self.decode_str(value)else:hdr, addr = parseaddr(value)value = self.decode_str(addr)value_dict[header] = value# print('%s%s: %s' % ('  ' * indent, header, value))if (msg.is_multipart()):parts = msg.get_payload()for n, part in enumerate(parts):print('%s part %s' % ('  ' * indent, n))print('%s--------------------' % ('  ' * indent))self.print_info(part, indent + 1)else:content_type = msg.get_content_type()if content_type == 'text/plain':content = msg.get_payload(decode=True)charset = self.guess_charset(msg)if charset:content = content.decode(charset)value_dict['Text'] = content# print('%sText: %s' % ('  ' * indent, content))elif content_type == 'text/html':content = msg.get_payload(decode=True)charset = self.guess_charset(msg)if charset:content = content.decode(charset)value_dict['Html'] = content#print('%sHtml: %s' % ('  ' * indent, content))else:print('%sAttachment: %s' % ('  ' * indent, content_type))return value_dict# API for "doing something useful with the message"def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):try:peer = peermailfrom = mailfromrcpttos = rcpttosdata = data# peer('127.0.0.1', 44612)# mailfrom hezhaoning@foxmail.com# rcpttos['hezhaoning333@163.com']# data = b'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: base64\nFrom: =?utf-8?b?6I+c6bif5pWZ56iL?=\nTo: =?utf-8?b?5rWL6K+V?=\nSubject: =?utf-8?b?UHl0aG9uIFNNVFAg6YKu5Lu25rWL6K+V?=\n\nUHl0aG9uIOmCruS7tuWPkemAgea1i+ivlS4uLg=='data = data.decode()# print(data)#print(type(data))# msg = Parser().parsestr(data)#  print(peer)#  print(mailfrom)#  print(rcpttos)#  print(msg)now_time = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d_%H%M%S')with open('./emailData-{}.txt'.format(now_time), 'w', encoding='utf-8') as f:  # 使用with open()新建对象ff.write( 'peer : '.format(str(peer)))f.write('mailfrom : '.format(mailfrom))f.write('rcpttos : '.format(', '.format(rcpttos)))f.write(data)#解析邮件内容# value = self.print_info(msg)except Exception as e:print(str(e))return#多线程的方式启动SMTPD服务器
class SMTPServerw():def __init__(self):#服务器端口self.port = 1025def start(self):'''Start listening on self.port'''# create an instance of the SMTP server, derived from  asyncore.dispatcher#创建一个SMTP服务器的实例,它派生自asyncore.dispatcheself.smtp = SSServer(('0.0.0.0', self.port),None)# self.smtp = SSSsever(('127.0.0.1', self.port), ( 'smtp.qq.com', 25))print(self.smtp)# start the asyncore loop, listening for SMTP connection, within a thread# timeout parameter is important, otherwise code will block 30 seconds# after the smtp channel has been closedkwargs = {'timeout':1, 'use_poll': True}self.thread = threading.Thread(target=asyncore.loop, kwargs=kwargs)self.thread.start()def stop(self):'''Stop listening to self.port'''# close the SMTPserver to ensure no channels connect to asyncoreself.smtp.close()# now it is safe to wait for asyncore.loop() to exitself.thread.join()
smtpd个人理解

该模块提供了几个类来实现SMTP服务器。一种是通用的无所作为的实现,可以被覆盖,另外两种提供特定的邮件发送策略

1. SMTPServer对象

class SMTPServer(localaddr, remoteaddr)
在前文的代码种我们引用的也是这个类,是源码中提供的示例服务器的基类,该对象继承自asyncore.dispatcher,并将自身插入asyncore实例化的事件循环中。它处理与客户端的通信,接收数据,并提供了一个方便的钩子函数,可在消息完全可用后覆盖它以处理消息。localaddr:传入两个参数(主机,端口)元组,用于侦听连接发送到服务器的邮箱信息,如果只侦听本地则是(‘127.0.0.1’,1025)如果是侦听所有连接则是(‘0.0.0.0’, 1025),我暂时remoteaddr 还没有用到过可以填 None

该对象有个方法process_message(peer, mailfrom, rcpttos, data)当完全接收到邮箱消息时,将调用该方法:
peer :客户端的地址,一个包含IP和传入端口的元组。
mailfrom :邮箱中的的“From”信息,在消息传递时由客户机提供给服务器。这并不一定在所有情况下都与From头匹配。
rcpttos :邮箱中的收件人列表。同样,这并不总是与To头匹配,特别是当某人是盲目复制的时候。
data :邮箱内容数据,完整的RFC 2822消息正文
可以在这个方法中打印出这些邮箱内容来 并对他们进行下一步操作做,具体参考代码

2. 调试服务器对象

class smtpd.DebuggingServer(localaddr, remoteaddr)

创建一个新的调试服务器。参数是按照SMTPServer。消息将被丢弃,并打印在stdout上。

3. PureProxy对象

class smtpd.PureProxy(localaddr, remoteaddr)

创建一个新的纯代理服务器。参数是按照SMTPServer。一切都会被传送到remoteaddr。请注意,运行这是一个很好的机会让你成为一个开放的继电器,所以请小心。

4. MailmanProxy对象

class smtpd.MailmanProxy(localaddr, remoteaddr)

创建一个新的纯代理服务器。参数是按照SMTPServer。除非本地邮递员配置知道地址,否则一切都会被转发到remoteaddr,在这种情况下,邮件将通过邮递员处理。请注意,运行这是一个很好的机会让你成为一个开放的继电器,所以请小心。

更多SMTPD的介绍可以看这个网站

https://pymotw.com/2/smtpd/

SMTPD 服务器运行

启动服务器

我使用的是 1025端口,我的服务器ip为192.168.99.204
启动服务器
邮箱我使用的foxmail, 修改邮箱的发件服务器的为 刚才启动的smtp 服务器的 ip跟端口
在这里插入图片描述
发送一封邮件到任意服务器
在这里插入图片描述
显示邮件发送成功
在这里插入图片描述
smtp 服务器收到打印出的邮件内容
在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部