What is the proper way to ACTUALLY SEND mail from (Python) code?
Thanks to these answers, to my additional questions: 1, 2, 3, as well as these two questions (and answers) of other people: one, two — I think I am now ready to answer the questions I have posted, on my own.
I will address the questions one by one:
Yes, as a general picture, sending of an email can be portrayed like this:
MX lookup returns address(es) of server(s) which receive email destined to the specified domain.
- As to "Why
smtp-relay.gmail.com
,smtp.gmail.com
,aspmx.l.google.com
are not returned byhost -t mx gmail.com
command?". This point is, pretty much, covered in another answer to this question. The main points to grasp here are:- servers returned by MX lookup are responsible for receiving of emails for the domain (gmail, in this particular case)
- servers listed in gmail docs are meant for the mail sending (i.e. mails that gmail user wants to send, to other gmail user or otherwise, are submitted to those servers)
- As to "Why
Authentication is not needed, for servers receiving emails (i.e. the ones returned by MX lookup).
- There are a couple things that prevent such servers from being abused:
- many ISPs block outbound connections to port
25
(which is default port for mail receiving servers), to prevent such "direct" mail sending - there are numerous measures taken on the side of receiving servers, which are, mainly, intended to prevent spamming, but as a result will, likely, prevent such "direct" mail sending, as well (some examples are: DNSBL — list of blocked IPs, DKIM — is an email authentication method designed to detect forged sender addresses in emails (if you do not have your own, legitimate, mail server, you will use someone other's domain for
From
field, that is where you might be hit by DKIM)
- many ISPs block outbound connections to port
- There are a couple things that prevent such servers from being abused:
Code snippet is OK. The error is produced, in all probability, due to the blocking on the ISP's side.
With all that being said, code snippet:
import smtplib
from email.message import EmailMessage
message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = '[email protected]'
message['To'] = '[email protected]'
smtp_server = smtplib.SMTP('smtp.server.address:25')
smtp_server.send_message(message)
smtp_server.quit()
will actually send an email (see this question, for real-world, working example) given that smtp.server.address:25
is legitimate server and there are no blockings on ISP's and/or smtp.server.address
side.
Your understanding of how mail works is roughly correct. Some additional notes that may clear things up:
SMTP is used for two distinct purposes. You seem to be confusing these two.:
The first use, typically called "submission", is to send a mail from an MUA (Mail User Agent, your mail program, Outlook, Thunderbird, ...) to an MTA (Mail Transfer Agent, typically called "mail server"). MTAs are run by your ISP, or by mail-providers such as GMail. Typically, their use is restricted by either IP address (only customers of said ISP can use it), or username/password.
The second use is to send mail from one MTA to another MTA. This part is, usually, wide open, since you are probably willing to accept inbound mail from anyone. This is also the location where anti-spam measures are taken.
In order to send a mail, you need, at least, the second part of SMTP: the ability to talk to another MTA to deliver the mail.
The typical way to send mails is to compose the mail in your application, then send it off to an MTA mail server for delivery. Depending on your setup, that MTA can be either installed on the same machine as your Python code is running on (localhost), or can be a more "central" mail server (possibly requiring authentication).
"Your" MTA will take care of all the nasty details of delivering mail such as:
Doing DNS lookups to find out the MTA's to contact to relay the mail. This includes MX-lookup, but also other fallback mechanisms such as A-records.
Retrying delivery, if the first attempt fails temporarily
Generating a bounce message, if the message fails permanently
Make multiple copies of the message, in case of multiple recipients on different domains
Signing the message with DKIM to reduce the chance of it being marked as SPAM.
...
You could, of course, re-implement all these features within your own Python code, and effectively combine an MTA with your application, but I strongly advise against it. Mail is surprisingly hard to get right...
Bottom line: Try to send the mail via SMTP to the mail server of your provider or another mail service. If that is not possible: think really hard if you want to run your own mail server. Being marked as a spammer happens easily; getting removed from spam-lists is much harder. Don't re-implement SMTP-code in your application.