The python smtplib module sends SSL and TLS secure mail instances

  • 2020-05-07 19:55:24
  • OfStack

smtplib of python provides a very convenient way to send E-mail. It simply encapsulates the smtp protocol.

The basic commands of the smtp protocol include:

HELO identifies the user to the server
MAIL initializes mail transfer mail from:
RCPT identifies a single mail recipient; Often followed by the MAIL command, there may be more than one rcpt to:
After a single or multiple RCPT commands, DATA indicates that all mail recipients have been identified and the data transfer has been initiated, ending with
VRFY is used to verify the existence of the specified user/mailbox; The server often disables this command for security reasons
EXPN validates the existence of a given mailbox list, and extending the mailbox list is often disabled
What commands does the HELP query server support
NOOP no operation, the server should respond OK
QUIT ends the session
RSET resets the session and the current transfer is cancelled
MAIL FROM specifies the sender address
RCPT TO specified recipient address

There are two ways to use smtp, one is direct mail delivery, that is, if you want to send email to the 163.com 163.com email server, then directly connect to the 163.com email server and send the message to the 163.com email server. The other one is a verified email. The process is, for example, if you want to send an email to email:.zzz@163.com, you don't send it directly to 163.com. This means first connecting to sina.com's smtp server, then authenticating, and then Posting the letter to sina.com before sending the letter to 163.com. sina.com will deliver the letter to 163.com.

The command flow of type 1 is basically like this:
1. helo
2. mail from
3. rcpt to
4. data
5. quit

However, the first mode of sending, rcpt to, is limited in that the recipient specified by rcpt to must exist on the server or it will not be received. First look at the code:


#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib # Mail server address
mailserver = "smtp.163.com"
# smtp In conversation mail from address
from_addr = "asfgysg@zxsdf.com"
# smtp In conversation rcpt to address
to_addr = "zhaoweikid@163.com"
# letter
msg = "test mail" svr = smtplib.SMTP(mailserver)
# Set to debug mode, that is, there will be output during the session
svr.set_debuglevel(1)
# helo Command, docmd The method includes getting the return information from the other server
svr.docmd("HELO server")
# mail from, The sender of the message
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, Mail receiver
svr.docmd("RCPT TO: <%s>" % to_addr)
# data Command to start sending data
svr.docmd("DATA")
# Send text data
svr.send(msg)
# Such as to . A mark that ends the text transmission , with send Sent, so use getreply Get the return information
svr.send(" . ")
svr.getreply()
# Send over, exit
svr.quit()

Note that 163.com has an anti-spam function, and this method of mail delivery is not guaranteed to pass the detection of anti-spam system. So 1 is generally not recommended for individuals to send in this way.

  2 is a bit different:

1.ehlo
2.auth login
3.mail from
4.rcpt to
5.data
6.quit

Compared to the first one, there is one more authentication process, which is auth login.


#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib
import base64 # Mail server address
mailserver = "smtp.163.com"
# Mail username
username = "xxxxxx@163.com"
# password
password = "xxxxxxx"
# smtp In conversation mail from address
from_addr = "xxxxxx@163.com"
# smtp In conversation rcpt to address
to_addr = "yyyyyy@163.com"
# letter
msg = "my test mail" svr = smtplib.SMTP(mailserver)
# Set to debug mode, that is, there will be output during the session
svr.set_debuglevel(1)
# ehlo Command, docmd The method includes getting the return information from the other server
svr.docmd("EHLO server")
# auth login The command
svr.docmd("AUTH LOGIN")
# Send the username, yes base64 Coded. Used send Sent, so use getreply Get the return information
svr.send(base64.encodestring(username))
svr.getreply()
# Sending the password)
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, The sender of the message
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, Mail receiver
svr.docmd("RCPT TO: <%s>" % to_addr)
# data Command to start sending data
svr.docmd("DATA")
# Send text data
svr.send(msg)
# Such as to . A mark that ends the text transmission
svr.send(" . ")
svr.getreply()
# Send over, exit
svr.quit()
     
What is mentioned above is the most common case, but what can't be ignored is that many enterprise emails now support secure emails, that is, emails sent via SSL. How to send this? SMTP supports SSL secure mail in two ways. One is to open 1 465 port to receive ssl mail, and the other is to add 1 starttls command to the standard smtp port 25.

Let's see how the first one works:


#-*- encoding: gb2312 -*-
import os, sys, string, socket
import smtplib
class SMTP_SSL (smtplib.SMTP):
    def __init__(self, host='', port=465, local_hostname=None, key=None, cert=None):
        self.cert = cert
        self.key = key
        smtplib.SMTP.__init__(self, host, port, local_hostname)
       
    def connect(self, host='localhost', port=465):
        if not port and (host.find(':') == host.rfind(':')):
            i = host.rfind(':')
            if i >= 0:
                host, port = host[:i], host[i+1:]
                try: port = int(port)
                except ValueError:
                    raise socket.error, "nonnumeric port"
        if not port: port = 654
        if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
        msg = "getaddrinfo returns an empty list"
        self.sock = None
        for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
            af, socktype, proto, canonname, sa = res
            try:
                self.sock = socket.socket(af, socktype, proto)
                if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
                self.sock.connect(sa)
                # Newly added creation ssl The connection
                sslobj = socket.ssl(self.sock, self.key, self.cert)
            except socket.error, msg:
                if self.debuglevel > 0:
                    print>>stderr, 'connect fail:', (host, port)
                if self.sock:
                    self.sock.close()
                self.sock = None
                continue
            break
        if not self.sock:
            raise socket.error, msg         # Set up the ssl
        self.sock = smtplib.SSLFakeSocket(self.sock, sslobj)
        self.file = smtplib.SSLFakeFile(sslobj);         (code, msg) = self.getreply()
        if self.debuglevel > 0: print>>stderr, "connect:", msg
        return (code, msg)
       
if __name__ == '__main__':
    smtp = SMTP_SSL('192.168.2.10')
    smtp.set_debuglevel(1)
    smtp.sendmail("zzz@xxx.com", "zhaowei@zhaowei.com", "xxxxxxxxxxxxxxxxx")
    smtp.quit()
     

Here I have derived the new SMTP_SSL class from the original smtplib.SMTP, which handles ssl connections. The 192.168.2.10 I tested here is my own test server.

The second is the new starttls command, which is very simple. smtplib has this method called smtplib.starttls (). Of course, not all mail systems support secure mail, this needs to be confirmed from the return value of ehlo, if there is starttls, then support. Compared to the second method of sending regular mail, it only needs to add 1 line of code:


#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib
import base64 # Mail server address
mailserver = "smtp.163.com"
# Mail username
username = "xxxxxx@163.com"
# password
password = "xxxxxxx"
# smtp In conversation mail from address
from_addr = "xxxxxx@163.com"
# smtp In conversation rcpt to address
to_addr = "yyyyyy@163.com"
# letter
msg = "my test mail" svr = smtplib.SMTP(mailserver)
# Set to debug mode, that is, there will be output during the session
svr.set_debuglevel(1)
# ehlo Command, docmd Methods include getting the return information from the other server, which will be included in the return value if secure mail is supported starttls prompt
svr.docmd("EHLO server")
svr.starttls()  # <------ This line is the new support for secure mail code!
# auth login The command
svr.docmd("AUTH LOGIN")
# Send the username, yes base64 Coded. Used send Sent, so use getreply Get the return information
svr.send(base64.encodestring(username))
svr.getreply()
# Sending the password)
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, The sender of the message
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, Mail receiver
svr.docmd("RCPT TO: <%s>" % to_addr)
# data Command to start sending data
svr.docmd("DATA")
# Send text data
svr.send(msg)
# Such as to . A mark that ends the text transmission
svr.send(" . ")
svr.getreply()
# Send over, exit
svr.quit()

Note: in order to facilitate the above code, I did not judge the return value. Strictly speaking, I should judge the code returned under 1. In smtp protocol, only the return code is 2xx or 3xx can continue the next step.


Related articles: