96 lines
3.6 KiB
Python
Executable File
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
|