94 lines
3.5 KiB
Python
94 lines
3.5 KiB
Python
import secure_smtpd
|
|
import ssl, smtpd, asyncore, socket, logging, signal, time, sys
|
|
|
|
from .smtp_channel import SMTPChannel
|
|
from asyncore import ExitNow
|
|
from .process_pool import ProcessPool
|
|
from ssl import SSLError
|
|
try:
|
|
from Queue import Empty
|
|
except ImportError:
|
|
# We're on python3
|
|
from queue import Empty
|
|
|
|
class SMTPServer(smtpd.SMTPServer):
|
|
|
|
def __init__(self, localaddr, remoteaddr, ssl=False, certfile=None, keyfile=None, ssl_version=ssl.PROTOCOL_SSLv23, require_authentication=False, credential_validator=None, maximum_execution_time=30, process_count=5):
|
|
smtpd.SMTPServer.__init__(self, localaddr, remoteaddr)
|
|
self.logger = logging.getLogger( secure_smtpd.LOG_NAME )
|
|
self.certfile = certfile
|
|
self.keyfile = keyfile
|
|
self.ssl_version = ssl_version
|
|
self.subprocesses = []
|
|
self.require_authentication = require_authentication
|
|
self.credential_validator = credential_validator
|
|
self.ssl = ssl
|
|
self.maximum_execution_time = maximum_execution_time
|
|
self.process_count = process_count
|
|
self.process_pool = None
|
|
|
|
def handle_accept(self):
|
|
self.process_pool = ProcessPool(self._accept_subprocess, process_count=self.process_count)
|
|
self.close()
|
|
|
|
def _accept_subprocess(self, queue):
|
|
while True:
|
|
try:
|
|
self.socket.setblocking(1)
|
|
pair = self.accept()
|
|
map = {}
|
|
|
|
if pair is not None:
|
|
|
|
self.logger.info('_accept_subprocess(): smtp connection accepted within subprocess.')
|
|
|
|
newsocket, fromaddr = pair
|
|
newsocket.settimeout(self.maximum_execution_time)
|
|
|
|
if self.ssl:
|
|
newsocket = ssl.wrap_socket(
|
|
newsocket,
|
|
server_side=True,
|
|
certfile=self.certfile,
|
|
keyfile=self.keyfile,
|
|
ssl_version=self.ssl_version,
|
|
)
|
|
channel = SMTPChannel(
|
|
self,
|
|
newsocket,
|
|
fromaddr,
|
|
require_authentication=self.require_authentication,
|
|
credential_validator=self.credential_validator,
|
|
map=map
|
|
)
|
|
|
|
self.logger.info('_accept_subprocess(): starting asyncore within subprocess.')
|
|
|
|
asyncore.loop(map=map)
|
|
|
|
self.logger.error('_accept_subprocess(): asyncore loop exited.')
|
|
except (ExitNow, SSLError):
|
|
self._shutdown_socket(newsocket)
|
|
self.logger.info('_accept_subprocess(): smtp channel terminated asyncore.')
|
|
except Exception as e:
|
|
self._shutdown_socket(newsocket)
|
|
self.logger.error('_accept_subprocess(): uncaught exception: %s' % str(e))
|
|
|
|
def _shutdown_socket(self, s):
|
|
try:
|
|
s.shutdown(socket.SHUT_RDWR)
|
|
s.close()
|
|
except Exception as e:
|
|
self.logger.error('_shutdown_socket(): failed to cleanly shutdown socket: %s' % str(e))
|
|
|
|
|
|
def run(self):
|
|
asyncore.loop()
|
|
if hasattr(signal, 'SIGTERM'):
|
|
def sig_handler(signal,frame):
|
|
self.logger.info("Got signal %s, shutting down." % signal)
|
|
sys.exit(0)
|
|
signal.signal(signal.SIGTERM, sig_handler)
|
|
while 1:
|
|
time.sleep(1)
|