Python 2: SMTPServerDisconnected: Connection unexpectedly closed

I have a small problem with sending an email in Python:

#me == my email address
#you == recipient's email address
me = "[email protected]"
you = "[email protected]"

# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "Alert"
msg['From'] = me
msg['To'] = you

# Create the body of the message (a plain-text and an HTML version).
html = '<html><body><p>Hi, I have the following alerts for you!</p></body></html>'

# Record the MIME types of both parts - text/plain and text/html.
part2 = MIMEText(html, 'html')

# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part2)

# Send the message via local SMTP server.
s = smtplib.SMTP('aspmx.l.google.com')
# sendmail function takes 3 arguments: sender's address, recipient's address
# and message to send - here it is sent as one string.
s.sendmail(me, you, msg.as_string())
s.quit()

So before now, my program didn't give me an error, but it also didn't send me an email. And now python gives me an error:

SMTPServerDisconnected: Connection unexpectedly closed

How can I fix this?


Solution 1:

TLDR: switch to authenticated connection over TLS.

Most probably the gmail server rejected the connection after the data command (very nasty of them to do so at this stage :). The actual message is most probably this one:

    retcode (421); Msg: 4.7.0 [ip.octets.listed.here      15] Our system has detected an unusual rate of
    4.7.0 unsolicited mail originating from your IP address. To protect our
    4.7.0 users from spam, mail sent from your IP address has been temporarily
    4.7.0 rate limited. Please visit
    4.7.0  https://support.google.com/mail/answer/81126 to review our Bulk Email
    4.7.0 Senders Guidelines. qa9si9093954wjc.138 - gsmtp

How do I know that? Because I've tried it :) with the s.set_debuglevel(1), which prints the SMTP conversation and you can see firsthand what's the issue.

You've got two options here:

  1. Continue using that relay; as explained by Google, it's unencrypted gmail-to-gmail only, and you have to un-blacklist your ip through their procedure

  2. The most fool-proof option is to switch to TLS with authentication

Here's how the changed source looks like:

# skipped your comments for readability
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

me = "[email protected]"
my_password = r"your_actual_password"
you = "[email protected]"

msg = MIMEMultipart('alternative')
msg['Subject'] = "Alert"
msg['From'] = me
msg['To'] = you

html = '<html><body><p>Hi, I have the following alerts for you!</p></body></html>'
part2 = MIMEText(html, 'html')

msg.attach(part2)

# Send the message via gmail's regular server, over SSL - passwords are being sent, afterall
s = smtplib.SMTP_SSL('smtp.gmail.com')
# uncomment if interested in the actual smtp conversation
# s.set_debuglevel(1)
# do the smtp auth; sends ehlo if it hasn't been sent already
s.login(me, my_password)

s.sendmail(me, you, msg.as_string())
s.quit()

Now, if try to 'cheat' the system and send with a different (non-gmail) address it's gonna a) require you to connect to a different hostname (some of the MX records for gmail), then b) stop you and close the connection on the grounds of blacklisted ip, and c) do reverse DNS, DKIM and lots of other countermeasures to make sure you're actually in control of the domain you presented in the MAIL FROM: address.

Finally, there's also option 3) - use any other email relaying service, there are tons of good ones :)

Solution 2:

I Had the same issue and solved it just by specifying the right port like this:

smtplib.SMTP('smtp.gmail.com', 587)