Twisted mail server with TLS - no portal?

≯℡__Kan透↙ 提交于 2019-12-13 17:38:37

问题


So thanks to a couple of users here, I now have a (almost) working SMTP Server that supports switching from plain text to TLS connection as required. Basic server code is:

from twisted.internet import ssl, protocol, defer, task, endpoints
from twisted.protocols.basic import LineReceiver
from twisted.python.modules import getModule
from OpenSSL.crypto import load_privatekey, load_certificate, FILETYPE_PEM
from custom_esmtp import mySMTP

def main(reactor):
    caCertFile = open("/opt/tesa/etc/certs/CA/cacert.pem","r")
    certFile = open("/opt/tesa/etc/certs/server/server.crt","r")
    keyFile = open("/opt/tesa/etc/certs/server/server.key","r")
    caCertData = caCertFile.read()
    pKeyData = keyFile.read()
    certData = certFile.read()
    caCert = ssl.Certificate.loadPEM(caCertData)
    cert = load_certificate(FILETYPE_PEM, certData)
    pKey = load_privatekey(FILETYPE_PEM, pKeyData)
    sslCtxFactory = ssl.CertificateOptions(privateKey=pKey, certificate=cert, trustRoot=caCert)
    myESMTP = mySMTP(contextFactory=sslCtxFactory)
    factory = protocol.Factory.forProtocol(lambda: mySMTP(contextFactory=sslCtxFactory))
    endpoint = endpoints.TCP4ServerEndpoint(reactor, 8001)
    endpoint.listen(factory)
    return defer.Deferred()

if __name__ == '__main__':
    import starttls_server
    task.react(starttls_server.main)

As you can see - I create an object instance (myESMTP) of class mySMTP. This as you can probably guess is derived (in custom_esmtp.py) from ESMTP in twisted's mail.py - which is in turn derived from the SMTP class - we have written a couple of function overloads for the myESMTP class, with more to come.

However, in twisted's mail.py, the SMTP class definition has a method "validateFrom":

def validateFrom(self, helo, origin):
    <<snip>>
    if self.portal:

        result = self.portal.login(
            cred.credentials.Anonymous(),
            None,
            IMessageDeliveryFactory, IMessageDelivery)

        def ebAuthentication(err):
            """
            Translate cred exceptions into SMTP exceptions so that the
            protocol code which invokes C{validateFrom} can properly report
            the failure.
            """
            if err.check(cred.error.UnauthorizedLogin):
                print ("Unauth Login")
                exc = SMTPBadSender(origin)
            elif err.check(cred.error.UnhandledCredentials):
                exc = SMTPBadSender(
                    origin, resp="Unauthenticated senders not allowed")
            else:
                return err
            return defer.fail(exc)

        result.addCallbacks(
            self._cbAnonymousAuthentication, ebAuthentication)

        def continueValidation(ignored):
            """
            Re-attempt from address validation.
            """
            return self.validateFrom(helo, origin)

        result.addCallback(continueValidation)
        return result

    raise SMTPBadSender(origin)

So if self.portal is defined, then the method returns before exiting the if condition - but if it is undefined, it will raise an SMTPBadSender error. And there is nowhere in the SMTP class definition that portal is defined.

I've noticed that self.portal does get defined in the init method for the SMTPFactory class - should we be using this somehow - if so, can someone explain how this would affect our server code? That said, even this doesn't seem to set self.portal to anything "meaningful"...

def init(self, portal = None): self.portal = portal

Perhaps this a bug in the SMTP class definition? Seems unlikely... Perhaps we just need to create our own overridden version of validateFrom, and remove the code to raise an error if self.portal is undefined? Again though, I've tried this - (removing the 2 lines of code that generate the error outside of the if block) - and the result was "unusual"....

mail from: me@localhost
250 Sender address accepted
rcpt to:you@somedomain.test
503 Must have sender before recipient

Thanks as ever!


回答1:


So I've found what seems to be a simple answer - simply create overloaded definitions for ValidateFrom and ValidateTo in our custom ESMTP class! Works nicely enough... but I'm not 100% convinced this is the most "correct" solution - I can now submit my ehlo, mail from, and rcpt to... but when I try to submit "data":

2014-10-15 09:49:40+0000 [mySMTP,0,127.0.0.1] Unhandled Error
        Traceback (most recent call last):
          File "/usr/lib64/python2.6/site-packages/twisted/internet/tcp.py", line 220, in _dataReceived
            rval = self.protocol.dataReceived(data)
          File "/usr/lib64/python2.6/site-packages/twisted/protocols/basic.py", line 454, in dataReceived
            self.lineReceived(line)
          File "/usr/lib64/python2.6/site-packages/twisted/mail/smtp.py", line 568, in lineReceived
            return getattr(self, 'state_' + self.mode)(line)
          File "/usr/lib64/python2.6/site-packages/twisted/mail/smtp.py", line 582, in state_COMMAND
            method('')
        --- <exception caught here> ---
          File "/usr/lib64/python2.6/site-packages/twisted/mail/smtp.py", line 733, in do_DATA
            msg = msgFunc()
        exceptions.AttributeError: User instance has no __call__ method

I still wonder if I should be using SMTPFactory in some way.... I know it's not good practice here to ask a question within an answer, so I'll just leave it as "if anyone has a better suggestion, please do say!"



来源:https://stackoverflow.com/questions/26338633/twisted-mail-server-with-tls-no-portal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!