This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
python-emails/emails/testsuite/contrib/local-smtpd/secure_smtpd/proxy_server.py

96 lines
3.6 KiB
Python
Executable File

import socket
import smtplib
import secure_smtpd
from .smtp_server import SMTPServer
from .store_credentials import StoreCredentials
class ProxyServer(SMTPServer):
"""Implements an open relay. Inherits from secure_smtpd, so can handle
SSL incoming. Modifies attributes slightly:
* if "ssl" is true accepts SSL connections inbound and connects via SSL
outbound
* adds "ssl_out_only", which can be set to True when "ssl" is False so that
inbound connections are in plain text but outbound are in SSL
* adds "debug", which if True copies all inbound messages to logger.info()
* ignores any credential validators, passing any credentials upstream
"""
def __init__(self, *args, **kwargs):
self.ssl_out_only = False
if 'ssl_out_only' in kwargs:
self.ssl_out_only = kwargs.pop('ssl_out_only')
self.debug = False
if 'debug' in kwargs:
self.debug = kwargs.pop('debug')
kwargs['credential_validator'] = StoreCredentials()
SMTPServer.__init__(self, *args, **kwargs)
def process_message(self, peer, mailfrom, rcpttos, data):
if self.debug:
# ------------------------
# stolen directly from stmpd.DebuggingServer
inheaders = 1
lines = data.split('\n')
self.logger.info('---------- MESSAGE FOLLOWS ----------')
for line in lines:
# headers first
if inheaders and not line:
self.logger.info('X-Peer: %s', peer[0])
inheaders = 0
self.logger.info(line)
self.logger.info('------------ END MESSAGE ------------')
# ------------------------
# following code is direct from smtpd.PureProxy
lines = data.split('\n')
# Look for the last header
i = 0
for line in lines:
if not line:
break
i += 1
lines.insert(i, 'X-Peer: %s' % peer[0])
data = '\n'.join(lines)
self._deliver(mailfrom, rcpttos, data)
def _deliver(self, mailfrom, rcpttos, data):
# ------------------------
# following code is adapted from smtpd.PureProxy with modifications to
# handle upstream SSL
refused = {}
try:
if self.ssl or self.ssl_out_only:
s = smtplib.SMTP_SSL()
else:
s = smtplib.SMTP()
s.connect(self._remoteaddr[0], self._remoteaddr[1])
if self.credential_validator.stored:
# we had credentials passed in, use them
s.login(
self.credential_validator.username,
self.credential_validator.password
)
try:
refused = s.sendmail(mailfrom, rcpttos, data)
if refused != {}:
self.logger.error('some connections refused %s', refused)
finally:
s.quit()
except smtplib.SMTPRecipientsRefused as e:
self.logger.exception('')
refused = e.recipients
except (socket.error, smtplib.SMTPException) as e:
self.logger.exception('')
# All recipients were refused. If the exception had an associated
# error code, use it. Otherwise,fake it with a non-triggering
# exception code.
errcode = getattr(e, 'smtp_code', -1)
errmsg = getattr(e, 'smtp_error', 'ignore')
for r in rcpttos:
refused[r] = (errcode, errmsg)
return refused