使用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 charset

    def decode_str(self,s):
        value, charset = decode_header(s)[0]
        if charset:
            value = value.decode(charset)
        return value

    def 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 = peer
            mailfrom = mailfrom
            rcpttos = rcpttos
            data = 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()新建对象f
                f.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 = 1025
    def start(self):
        '''Start listening on self.port'''
        # create an instance of the SMTP server, derived from  asyncore.dispatcher
        #创建一个SMTP服务器的实例,它派生自asyncore.dispatche
        self.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 closed
        kwargs = {'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 asyncore
        self.smtp.close()
        # now it is safe to wait for asyncore.loop() to exit
        self.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 服务器收到打印出的邮件内容
在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值