Reputation: 2443
from OpenSSL.SSL import SSLv3_METHOD, TLSv1_METHOD
from twisted.mail.smtp import ESMTPSenderFactory
from twisted.python.usage import Options, UsageError
from twisted.internet.ssl import ClientContextFactory
from twisted.internet.defer import Deferred
from twisted.internet import reactor
def sendmail(
authenticationUsername, authenticationSecret,
fromAddress, toAddress,
messageFile,
smtpHost="email-smtp.us-east-1.amazonaws.com", smtpPort=587
):
"""
@param authenticationUsername: The username with which to authenticate.
@param authenticationSecret: The password with which to authenticate.
@param fromAddress: The SMTP reverse path (ie, MAIL FROM)
@param toAddress: The SMTP forward path (ie, RCPT TO)
@param messageFile: A file-like object containing the headers and body of
the message to send.
@param smtpHost: The MX host to which to connect.
@param smtpPort: The port number to which to connect.
@return: A Deferred which will be called back when the message has been
sent or which will errback if it cannot be sent.
"""
# Create a context factory which only allows SSLv3 and does not verify
# the peer's certificate.
contextFactory = ClientContextFactory()
contextFactory.method = TLSv1_METHOD
resultDeferred = Deferred()
senderFactory = ESMTPSenderFactory(
authenticationUsername,
authenticationSecret,
fromAddress,
toAddress,
messageFile,
resultDeferred,
contextFactory=contextFactory,heloFallback=True
)
reactor.connectTCP(smtpHost, smtpPort, senderFactory)
return resultDeferred
Note I have tried both SSLv3 and TLSv1 so you will both imported, but that is not the issue. The error I keep getting is this. Traceback:
2013-05-23 01:19:17+0800 [ESMTPSender,client] SMTP Client retrying server. Retry: 5
2013-05-23 01:19:20+0800 [ESMTPSender,client] SMTP Client retrying server. Retry: 4
2013-05-23 01:19:22+0800 [ESMTPSender,client] SMTP Client retrying server. Retry: 3
2013-05-23 01:19:25+0800 [ESMTPSender,client] SMTP Client retrying server. Retry: 2
2013-05-23 01:19:28+0800 [ESMTPSender,client] SMTP Client retrying server. Retry: 1
2013-05-23 01:19:30+0800 [ESMTPSender,client] Failed to deliver mail [Failure instance: Traceback (failure with no frames): <class 'twisted.mail.smtp.TLSError'>: 454 Could not complete the SSL/TLS handshake
2013-05-23 01:19:30+0800 [ESMTPSender,client] <<< 250-AUTH PLAIN LOGIN
2013-05-23 01:19:30+0800 [ESMTPSender,client] <<< 250 Ok
2013-05-23 01:19:30+0800 [ESMTPSender,client] >>> STARTTLS
2013-05-23 01:19:30+0800 [ESMTPSender,client] <<< 454 TLS not available due to temporary reason: TLS already active
2013-05-23 01:19:30+0800 [ESMTPSender,client]
2013-05-23 01:19:30+0800 [ESMTPSender,client] ]
2013-05-23 01:19:30+0800 [ESMTPSender,client] Stopping factory <twisted.mail.smtp.ESMTPSenderFactory instance at 0x2119950>
Amazon Ses supports wrapper and TLS but on different ports. SImilar to how GMail behaves.
I tried to just remove ContextFactory altogether. The error is same.
I have tried with smtplib to ensure it's not my system or auth etc. It works fine.
To be honest I do not fully understand twisted so I may be doing something dumb. The above code is similar to examples elsewhere and should work. Btw, I do not call reactor.stop() as I just ctrl-c after test. Any clues?
Update for completion: I call the method above like so
sendmail('username','password',from, to,StringIO.StringIO(mail)).addCallbacks(self.delivered, self.failed)
Upvotes: 1
Views: 1560
Reputation: 48325
Host email-smtp.us-east-1.amazonaws.com port 587 speaks unencrypted ESMTP (call this, perhaps, "TCP"). It supports negotiating up to encrypted ESMTP via the STARTTLS command.
Testing this manually, I find that it works as expected.
The error from the log you pasted (TLS already active) suggests that instead you have a connection over which TLS has already been negotiated (either because STARTTLS was used over a TCP connection or because you connected to a different server where TLS is negotiated automatically at the start of the connection).
The server is refusing to run TLS over TLS over TCP, which is probably sensible. However, from the code you pasted, I don't see how TLS could be negotiated twice. Perhaps if you can include more details about the context the answer will become clear.
It is possible that you're experiencing http://tm.tl/3989. If that is the case, upgrading to Twisted 13.0.0 or newer will resolve the issue. However, I don't see how this could be the case since I don't see how TLS could be negotiated twice by your code.
Actually, on further investigation, it seems you're experiencing a regression introduced by http://tm.tl/3989. I've filed http://tm.tl/6524 to track this.
Upvotes: 1