Disclaimer: I hesitated on the title, due to the broad nature of this question (see below), other options included:
- How to send a mail from localhost, using Python code only?
- How to send email from Python code, without usage of external SMTP server?
- Is it possible to send an email DIRECTLY to it's destination, using localhost and Python only?
First, a little bit of context:
For the sake of learning I am building a website with user registration feature. The idea is that after registration user will receive an email with activation link. I would like to compose & send email from Python code, and that is the part where I would like to ask for some clarifications.
My understanding, before I began (obviously naive =) can be illustrated like this (given that there is a legitimate email address user@example.com):
After searching for examples, I bumped into some questions & answers on stackoverflow (1, 2, 3, 4). From these I've distilled the following snippet, to compose and send an email from Python code:
import smtplib
from email.message import EmailMessage
message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = 'me@example.com'
message['To'] = 'user@example.com'
smtp_server = smtplib.SMTP('smtp.server.address:587')
smtp_server.send_message(message)
smtp_server.quit()
Next (obvious) question was what to pass to smtplib.SMTP()
instead of 'smtp.server.address:587'
. From the comments to this answer, I discovered that local SMTP server (just for testing purposes though) could be started via python3 -m smtpd -c DebuggingServer -n localhost:1025
, then smtp_server = smtplib.SMTP('smtp.server.address:587')
could be changed to smtp_server = smtplib.SMTP('localhost:1025')
and all the sent emails will be displayed in the console (from where python3 -m smtpd -c DebuggingServer -n localhost:1025
command was executed), being enough for testing — it was not what I wanted (my aim was — the ability to send a mail to 'real-world' email address from local machine, using Python code only).
So, the next step would be to setup a local SMTP server, capable of sending an email to external 'real-world' email-address (as I wanted to do it all from Python code, so the server itself would better be implemented in Python too). I recalled reading in some magazine (in early 2000), that spammers use local servers for sending mails (that particular article was talking about Sambar, development for which have ended in 2007, and which was not written in Python :-) I thought there should be some present-day solution with similar functionality. So I started searching, my hope was to find (on stackoverflow or elsewhere) a reasonably short code snippet, which will do what I wanted. I haven't found such a code snippet, but I came across a snippet titled (Python) Send Email without Mail Server (which uses chilkat API), though all I needed (supposedly) was right there, in the comments to code, the first line clearly stated:
Is it really possible to send email without connecting to a mail server? Not really.
and a few lines below:
Here's what happens inside those other components that claim to not need a mail server: The component does a DNS MX lookup using the intended recipient's email address to find the mail server (i.e. SMTP server) for that domain. It then connects to that server and delivers the email. You're still connecting to an SMTP server — just not YOUR server.
Reading that, made me understand — I, clearly, was lacking some details in my understanding (reflected on picture above) of the process. To correct this I have read the whole RFC on SMTP.
After reading the RFC, my improved understanding of the process, might be pictured like this:
From this understanding, came the actual questions I'd like to clarify:
- Can my "improved understanding" be considered correct, as a general picture?
- What addresses, exactly, are returned by MX lookup?
using
host -t mx gmail.com
command (suggested by this answer), I was able to retrieve the following:gmail.com mail is handled by 10 alt1.gmail-smtp-in.l.google.com. gmail.com mail is handled by 20 alt2.gmail-smtp-in.l.google.com. gmail.com mail is handled by 40 alt4.gmail-smtp-in.l.google.com. gmail.com mail is handled by 30 alt3.gmail-smtp-in.l.google.com. gmail.com mail is handled by 5 gmail-smtp-in.l.google.com.
- but none of these are mentioned in the [official docs][13] (ones that are there: `smtp-relay.gmail.com`, `smtp.gmail.com`, `aspmx.l.google.com`)
- Is authentication always needed to pass an email to SMTP-server of an established mail service (say gmail)?
I understand that to use, say
smtp.gmail.com
for mail submission, you'll need, regardless if the recipient has a@gmail
address or not (as it stated in docs):Your full Gmail or G Suite email address is required for authentication.
But, if an email to
user@gmail.com
is submitted to SMTP-server not owned by gmail, then it'll be redirected to one of the gmail servers (directly or via gateway/relay). In this case (I assume) sender of an email will only need to authenticate on mail submission, so after that gmail server will accept the mail without authentication?- If yes, what is preventing me from "pretending" to be such a gateway/relay and hand over emails directly to their designated SMTPs? Then it, also should be pretty easy to write a "proxy-SMTP", which will just search for an appropriate server via MX lookup, and hand an email to it, sort of, directly.
- Documentation on gmail SMTP, also mentions
aspmx.l.google.com
server, which does not require authentication, though:
Mail can only be sent to Gmail or G Suite users.
With that being said, I assume the following snippet should work, for submitting a mail to ExistingUser@gmail.com
mailbox:
<!-- language: lang-python -->
import smtplib
from email.message import EmailMessage
message = EmailMessage()
message.set_content('Message test content')
message['Subject'] = 'Test mail!'
message['From'] = 'me@whatever.com'
message['To'] = 'ExistingUser@gmail.com'
smtp_server = smtplib.SMTP('aspmx.l.google.com:25')
smtp_server.send_message(message)
smtp_server.quit()
When ran, the code above (with ExistingUser@gmail.com
replaced by the valid mail) throws OSError: [Errno 65] No route to host
. All I want to confirm here is that the communication to aspmx.l.google.com
is handled correctly in code.