问题
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