SMTP protocol (Python Implementation)

Keywords: Python Back-end computer networks

SMTP introduction

I won't talk about too many basic concepts. Just turn to the book or Wikipedia,
In short, SMTP is a simple mail transport protocol. Since it is a transport protocol, it is both sending and receiving mail
It is different from POP3 and IMAP
The difference can be seen simply from here

  • working process
    SMTP is an application layer protocol based on TCP connection and port 25. It is mainly non real-time, because some customers only ask the agent mailbox regularly (15min) for new mail

    There is no need to repeat the text here. Wait, the actual operation code uses pictures to analyze the process

SMTP command and answer codes

Response code, return message of the server

codeexplainSolution
220This message is usually returned after the connection is established
250Message returned after completing a requested action / command
354Transmission instructions for specific contents of mail
503Command sequence error / authorization requiredGenerally, check the sequence of several key commands first, and then check the authorization / login error
502Command not implemented
commandparameter
HELOHost name / opposite name
MAIL FROMOwn mailbox
RCPT TOOpposite mailbox
DATAStart transferring data, the body of the message
AUTH LOGINGenerally, LOGIN with LOGIN is used for authorization

Message format of mail

Definition of message format in RFC document

  1. All messages are composed of ASCII codes
  2. The message consists of message lines, which are separated by carriage return (CR) and line feed (LF)
  3. The length of the message cannot exceed 998 characters
  4. Length of message line ≤ 78 characters (excluding carriage return line feed character)
  5. The message can include multiple header fields and header contents
  6. The message may include a body, which must be separated from its header by a blank line
  7. Unless carriage return and line feed are required, carriage return and line feed are not used in the message

Specific transmission process

Give the code and wireshark first

from socket import *
import base64

# Mail content
subject = "I love computer networks!"
contenttype = "text/plain"
msg = "I love computer networks!"
endmsg = "\r\n.\r\n"

# Choose a mail server (e.g. Google mail server) and call it mailserver 
# If you don't know, just search the University / enterprise mailbox server directly, most of which are 25 port sending ports
mailserver = "***.mail"

# Sender and reciever
fromaddress = "****@mail"
toaddress = "****@qq.com"

# Auth information (Encode with base64)
username = base64.b64encode(fromaddress.encode()).decode()
password = base64.b64encode("******".encode()).decode()


# Create socket called clientSocket and establish a TCP connection with mailserver
clientSocket = socket(AF_INET, SOCK_STREAM) 
clientSocket.connect((mailserver, 25))

recv = clientSocket.recv(1024).decode()
print(recv)
if recv[:3] != '220':
    print('220 reply not received from server.')

# Send HELO command and print server response.
heloCommand = 'HELO Alice\r\n'
clientSocket.send(heloCommand.encode())
recv1 = clientSocket.recv(1024).decode()
print(recv1)
if recv1[:3] != '250':
    print('250 reply not received from server.')


# Auth must be authorized after hello
clientSocket.sendall('AUTH LOGIN\r\n'.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
	print('334 reply not received from server')

clientSocket.sendall((username + '\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
	print('334 reply not received from server')

clientSocket.sendall((password + '\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '235'):
	print('235 reply not received from server')

# Send MAIL FROM command and print server response.
# Fill in start
#sendall will send all the data until the error or all the data are sent, and send will send less than the required number of bytes
clientSocket.sendall(('MAIL FROM: <'+fromaddress+'>\r\n').encode())
recv2 = clientSocket.recv(1024).decode()
print(recv2)
if (recv2[:3] != '250'):
    print('250 reply not received from server.')
# Fill in end

# Send RCPT TO command and print server response. 
# Fill in start the command here is wrong. It's not MAIL, it's RCPT
clientSocket.sendall(('RCPT TO: <'+toaddress+'>\r\n').encode())
recv3 = clientSocket.recv(1024).decode()
print(recv3)
if (recv3[:3] != '250'):
    print('250 reply not received from server.')

# Fill in end
# Send DATA command and print server response. 
# Fill in start
clientSocket.send(('DATA\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '354'):
    print('354 reply not received from server')
# Fill in end

# Send message data.
# Fill in start
message = 'from:' + fromaddress + '\r\n'
message += 'to:' + toaddress + '\r\n'
message += 'subject:' + subject + '\r\n'
message += 'Content-Type:' + contenttype + '\r\n'
message += '\r\n' + msg
clientSocket.sendall(message.encode())

# Fill in end
# Message ends with a single period.
# Fill in start
clientSocket.sendall(endmsg.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
	print('250 reply not received from server')
# Fill in end
# Send QUIT command and get server response.
# Fill in start
clientSocket.sendall('QUIT\r\n'.encode())
# Fill in end

clientSocket.close()

Establish a connection first

First establish a TCP connection with socket

Use the mail server to shake hands with the port number to establish a TCP connection, and the server returns 220
After receiving 220, the client sends a HELO command to initiate a session
The server returns 250, which is OK

After the connection is established, you usually need to log in,

Because it is not a self built mail server, it is owned by an enterprise / someone else and requires authorization to log in

The client sends the command AUTH LOGIN. I'm going to log in
The server answers 334 dXNlcm5hbWU6, which means enter username: the latter is base64 encryption code
The customer sends the encrypted account name and password
Server authorization succeeded

Send mail

  1. The client uses "Mailfrom" to report the sender's mailbox and domain name to the server
  2. The server responds to the customer with the response code "250", which represents the completion of the request command
  3. The client reports the recipient's mailbox and domain name to the server with the "RCPT TO" command
  4. The server responds to the customer with the response code "250", which represents the completion of the request command
  5. The customer initializes the message transmission with "DTAT" command
  6. The server responded "354", indicating that mail input is available
  7. The client transmits the contents of the message to the server in consecutive lines, and each line is terminated with a two character line end identifier (CR and LF). The message ends with a line with only one "."
  8. The server responds to the customer with the response code "250", which represents the completion of the request command

Connection termination

  • Client sends "QUIT" command
  • After receiving the command, the server responds to the response code "221" and ends the session

Posted by Catharsis on Tue, 26 Oct 2021 01:40:42 -0700