ESP8226 micropython ssl connection issue with SMTP

829 views Asked by At

So after doing a lot of research I have been able to send SMTP email using Google's gmail server. I am using the same code without issue in CPython on both Windows and Linux however when I try to use the code on Micropython 1.9.2 (Latest as of Sept 11th) on the ESP8266 or Unix port the code locks up on line 63 but I cannot figure out why.

Any recommendations on how to correct this would be greatly appreciated as I the only other thing I can think of is that Micropython's implementation of SSL is not sufficient and that I will need to attempt to port CPython SSL over.

Thank you.

Offending code is:

61    heloCommand = 'EHLO Alice\r\n'
62    ssl_clientSocket.write(heloCommand.encode())
63    recv1 = ssl_clientSocket.read(1024)
64    print(recv1)

CODE (Yes, I know it is ugly and plan to clean it up once working in micropython):

    # Micropython
try:
  import usocket as socket
  #import base64
  import ussl as ssl

except:
# Python version 3
  import socket
  #import base64
  import ssl


msg = """From: [email protected]
To: [email protected]
Subject: Testing

Testing transmission thru python
"""
endmsg = "\r\n.\r\n"

recipient = "[email protected]"
sender = "[email protected]"
username = "[email protected]"
password = 'Mary_Had_A_Password_of_123'

# Choose a mail server (e.g. Google mail server) and call it mailserver
mailserver = "smtp.gmail.com"
port = 587

# Create socket called clientSocket and establish a TCP connection with mailserver
clientSocket = socket.socket()
clientSocket.connect(socket.getaddrinfo(mailserver, port)[0][-1])
recv = clientSocket.recv(1024)
print(recv)
print(recv[:3])
if recv[:3] != b'220':
    print('220 reply not received from server.')

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

# Request an encrypted connection
startTlsCommand = 'STARTTLS\r\n'
clientSocket.send(startTlsCommand.encode())
tls_recv = clientSocket.recv(1024)
print(tls_recv)
if tls_recv[:3] != b'220':
    print('220 reply not received from server')

# Encrypt the socket
#ssl_clientSocket = ssl.wrap_socket(clientSocket, ssl_version=ssl.PROTOCOL_TLSv1)
ssl_clientSocket = ssl.wrap_socket(clientSocket)
print("Secure socket created")

heloCommand = 'EHLO Alice\r\n'
ssl_clientSocket.write(heloCommand.encode())
recv1 = ssl_clientSocket.read(1024)
print(recv1)

# Send the AUTH LOGIN command and print server response.
authCommand = 'AUTH LOGIN\r\n'
ssl_clientSocket.write(authCommand.encode())
auth_recv = ssl_clientSocket.read(1024)
print(auth_recv)
if auth_recv[:3] != b'334':
    print('334 reply not received from server')

print("Sending username / password")
# Send username and print server response.
#uname = base64.b64encode((username).encode())
uname=b'Base64EncryptedUser=='
pword=b'Base64EncryptedPassword'
print(str(uname))
ssl_clientSocket.write(uname)
ssl_clientSocket.write('\r\n'.encode())
uname_recv = ssl_clientSocket.read(1024)
print(uname_recv)
if uname_recv[:3] != b'334':
    print('334 reply not received from server')

# Send password and print server response.
#pword = base64.b64encode((password).encode())
print(str(pword))
ssl_clientSocket.write(pword)
ssl_clientSocket.write('\r\n'.encode())
pword_recv = ssl_clientSocket.read(1024)

print(pword_recv)
if pword_recv[:3] != b'235':
    print('235 reply not received from server')

# Send MAIL FROM command and print server response.
mailFromCommand = 'MAIL FROM: <' + sender + '>\r\n'
ssl_clientSocket.write(mailFromCommand.encode())
recv2 = ssl_clientSocket.read(1024)
print(recv2)
if recv2[:3] != b'250':
    print('250 reply not received from server.')

# Send RCPT TO command and print server response.
rcptToCommand = 'RCPT TO: <' + recipient + '>\r\n'
ssl_clientSocket.write(rcptToCommand.encode())
recv3 = ssl_clientSocket.read(1024)
print(recv3)
if recv3[:3] != b'250':
    print('250 reply not received from server.')

# Send DATA command and print server response.
dataCommand = 'DATA\r\n'
ssl_clientSocket.write(dataCommand.encode())
recv4 = ssl_clientSocket.read(1024)
print(recv4)
if recv4[:3] != b'354':
    print('354 reply not received from server.')

# Send message data.
ssl_clientSocket.write(msg.encode())

# Message ends with a single period.
ssl_clientSocket.write(endmsg.encode())
recv5 = ssl_clientSocket.read(1024)
print(recv5)
if recv5[:3] != b'250':
    print('250 reply not received from server.')

# Send QUIT command and get server response.
quitCommand = 'QUIT\r\n'
ssl_clientSocket.write(quitCommand.encode())
recv6 = ssl_clientSocket.read(1024)
print(recv6)
if recv6[:3] != b'221':
    print('221 reply not received from server.')

clientSocket.close()

UPDATED CODE WITH ACCEPTED ANSWER (Yes, I know it is ugly) with read(1024) being replaced with readline() for ssl socket. Also needed to add a way to clean the buffer out after ssl EHLO command so added a "recvCount=recv1.decode().count('\n')" in first EHLO then a loop in the ssl EHLO for the same count:

    # Micropython
try:
  import usocket as socket
  #import base64
  import ussl as ssl

except:
# Python version 3
  import socket
  #import base64
  import ssl


msg = """From: [email protected]
To: [email protected]
Subject: Testing

Testing transmission thru python
"""
endmsg = "\r\n.\r\n"

recipient = "[email protected]"
sender = "[email protected]"
username = "[email protected]"
password = 'Mary_Had_A_Password_of_123'

# Choose a mail server (e.g. Google mail server) and call it mailserver
mailserver = "smtp.gmail.com"
port = 587

# Create socket called clientSocket and establish a TCP connection with mailserver
clientSocket = socket.socket()
clientSocket.connect(socket.getaddrinfo(mailserver, port)[0][-1])
recv = clientSocket.recv(1024)
print(recv)
print(recv[:3])
if recv[:3] != b'220':
    print('220 reply not received from server.')

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

# Request an encrypted connection
startTlsCommand = 'STARTTLS\r\n'
clientSocket.send(startTlsCommand.encode())
tls_recv = clientSocket.recv(1024)
print(tls_recv)
if tls_recv[:3] != b'220':
    print('220 reply not received from server')

# Encrypt the socket
#ssl_clientSocket = ssl.wrap_socket(clientSocket, ssl_version=ssl.PROTOCOL_TLSv1)
ssl_clientSocket = ssl.wrap_socket(clientSocket)
print("Secure socket created")

heloCommand = 'EHLO Alice\r\n'
ssl_clientSocket.write(heloCommand.encode())
recv1=''
for index in range(0,recvCount):
  recv1 = recv1+ssl_clientSocket.readline().decode()
print(recv1)

# Send the AUTH LOGIN command and print server response.
authCommand = 'AUTH LOGIN\r\n'
ssl_clientSocket.write(authCommand.encode())
auth_recv = ssl_clientSocket.readline()
print(auth_recv)
if auth_recv[:3] != b'334':
    print('334 reply not received from server')

print("Sending username / password")
# Send username and print server response.
#uname = base64.b64encode((username).encode())
uname=b'Base64EncryptedUser=='
pword=b'Base64EncryptedPassword'
print(str(uname))
ssl_clientSocket.write(uname)
ssl_clientSocket.write('\r\n'.encode())
uname_recv = ssl_clientSocket.readline()
print(uname_recv)
if uname_recv[:3] != b'334':
    print('334 reply not received from server')

# Send password and print server response.
#pword = base64.b64encode((password).encode())
print(str(pword))
ssl_clientSocket.write(pword)
ssl_clientSocket.write('\r\n'.encode())
pword_recv = ssl_clientSocket.readline()

print(pword_recv)
if pword_recv[:3] != b'235':
    print('235 reply not received from server')

# Send MAIL FROM command and print server response.
mailFromCommand = 'MAIL FROM: <' + sender + '>\r\n'
ssl_clientSocket.write(mailFromCommand.encode())
recv2 = ssl_clientSocket.readline()
print(recv2)
if recv2[:3] != b'250':
    print('250 reply not received from server.')

# Send RCPT TO command and print server response.
rcptToCommand = 'RCPT TO: <' + recipient + '>\r\n'
ssl_clientSocket.write(rcptToCommand.encode())
recv3 = ssl_clientSocket.readline()
print(recv3)
if recv3[:3] != b'250':
    print('250 reply not received from server.')

# Send DATA command and print server response.
dataCommand = 'DATA\r\n'
ssl_clientSocket.write(dataCommand.encode())
recv4 = ssl_clientSocket.readline()
print(recv4)
if recv4[:3] != b'354':
    print('354 reply not received from server.')

# Send message data.
ssl_clientSocket.write(msg.encode())

# Message ends with a single period.
ssl_clientSocket.write(endmsg.encode())
recv5 = ssl_clientSocket.readline()
print(recv5)
if recv5[:3] != b'250':
    print('250 reply not received from server.')

# Send QUIT command and get server response.
quitCommand = 'QUIT\r\n'
ssl_clientSocket.write(quitCommand.encode())
recv6 = ssl_clientSocket.readline()
print(recv6)
if recv6[:3] != b'221':
    print('221 reply not received from server.')

clientSocket.close()
1

There are 1 answers

1
pfalcon On BEST ANSWER

recv1 = ssl_clientSocket.read(1024)

Please read about about MicroPython stream semantics (which closely matches Python stream semantics, just with some simplifications): http://docs.micropython.org/en/latest/pyboard/library/uio.html?#conceptual-hierarchy

What the quoted statement does is requesting exactly 1024 of data (MicroPython follows "buffered stream" Python semantics by default). If there's not that much of data, .read() will patiently wait until enough arrives (or until EOF or error happens).

SMTP protocol is line-oriented, so you need to use .readline() instead.