SMTPServer with asyncore leave sockets open?

45 views Asked by At

I have implemented a basic SMTPServer which works great but I'm encountering an issue and I don't know how I can fix it.

When checking the file descriptors opened for the $PID of the process running my script (ls -l /proc/$PID/fd/ | grep "socket:" | wc -l), the quantity of file descriptors keep increasing.

Since I'm filtering by "socket:", this clearly some sockets left open, certainly because some mail providers doesn't quit clearly.

I've tested locally some Python scripts connecting to that SMTPServer code, and even if I don't close the connection, when the client process stops, the client close the connection, and the file descriptor is removed.

I thought about implementing a timeout for waiting, or restarting the process every N connections, but these are just bad patches for a bigger problem.

Do you know where that would come from?

For information, my process_message wrapper is surounded by a

try:
    # ...
except:
    return '450 Please try again later"

So an exception coming from my code should be well handled.

Here's my code (simplified):

class MyMXServer(SMTPServer):
    def __init__(self, host='127.0.0.1', port=25):
        self.srs = SRS.new(os.getenv('SRS_KEY', 'some passwd'))
        return SMTPServer.__init__(self, (host, port), None)

    def process_message(self, greeting, peer, mailfrom, recipients, data, **kwargs):
        try:
            return self._process_message(greeting, peer, mailfrom, recipients, data, **kwargs)
        except:
            logging.getLogger('err').exception('[EXCEPTION]')
            return '450 Please try again later.'

    def _process_message(self, greeting, peer, mailfrom, recipients, data, **kwargs):
        # Various things like checking DNSBL, SPF, Spam, etc
1

There are 1 answers

10
chrisharrel On

Hard to answer without seeing your code. However, you've already implemented the initial components of exception handling. else may not be necessary, but it sounds like a finally clause is a requirement in this situation. Finish the block to ensure the socket is closed:

try:
    # unreliable process
except:
    return '450 Please try again later"
else:
    # successful outcome
finallY:
    socket.close()