mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #19785: smtplib now supports SSLContext.check_hostname and server name
indication for TLS/SSL connections.
This commit is contained in:
parent
216d463b1f
commit
a5768f7292
4 changed files with 45 additions and 6 deletions
|
@ -90,6 +90,10 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions).
|
||||||
.. versionchanged:: 3.3
|
.. versionchanged:: 3.3
|
||||||
source_address argument was added.
|
source_address argument was added.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.4
|
||||||
|
The class now supports hostname check with
|
||||||
|
:attr:`SSLContext.check_hostname` and *Server Name Indicator* (see
|
||||||
|
:data:`~ssl.HAS_SNI`).
|
||||||
|
|
||||||
.. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None, source_address=None)
|
.. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None, source_address=None)
|
||||||
|
|
||||||
|
@ -316,6 +320,11 @@ An :class:`SMTP` instance has the following methods:
|
||||||
.. versionchanged:: 3.3
|
.. versionchanged:: 3.3
|
||||||
*context* was added.
|
*context* was added.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.4
|
||||||
|
The method now supports hostname check with
|
||||||
|
:attr:`SSLContext.check_hostname` and *Server Name Indicator* (see
|
||||||
|
:data:`~ssl.HAS_SNI`).
|
||||||
|
|
||||||
|
|
||||||
.. method:: SMTP.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
|
.. method:: SMTP.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
|
||||||
|
|
||||||
|
|
|
@ -232,6 +232,7 @@ class SMTP:
|
||||||
will be used.
|
will be used.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
self._host = host
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.esmtp_features = {}
|
self.esmtp_features = {}
|
||||||
self.source_address = source_address
|
self.source_address = source_address
|
||||||
|
@ -667,7 +668,9 @@ class SMTP:
|
||||||
if context is None:
|
if context is None:
|
||||||
context = ssl._create_stdlib_context(certfile=certfile,
|
context = ssl._create_stdlib_context(certfile=certfile,
|
||||||
keyfile=keyfile)
|
keyfile=keyfile)
|
||||||
self.sock = context.wrap_socket(self.sock)
|
server_hostname = self._host if ssl.HAS_SNI else None
|
||||||
|
self.sock = context.wrap_socket(self.sock,
|
||||||
|
server_hostname=server_hostname)
|
||||||
self.file = None
|
self.file = None
|
||||||
# RFC 3207:
|
# RFC 3207:
|
||||||
# The client MUST discard any knowledge obtained from
|
# The client MUST discard any knowledge obtained from
|
||||||
|
@ -892,7 +895,9 @@ if _have_ssl:
|
||||||
print('connect:', (host, port), file=stderr)
|
print('connect:', (host, port), file=stderr)
|
||||||
new_socket = socket.create_connection((host, port), timeout,
|
new_socket = socket.create_connection((host, port), timeout,
|
||||||
self.source_address)
|
self.source_address)
|
||||||
new_socket = self.context.wrap_socket(new_socket)
|
server_hostname = self._host if ssl.HAS_SNI else None
|
||||||
|
new_socket = self.context.wrap_socket(new_socket,
|
||||||
|
server_hostname=server_hostname)
|
||||||
return new_socket
|
return new_socket
|
||||||
|
|
||||||
__all__.append("SMTP_SSL")
|
__all__.append("SMTP_SSL")
|
||||||
|
|
|
@ -3,23 +3,35 @@
|
||||||
import unittest
|
import unittest
|
||||||
from test import support
|
from test import support
|
||||||
import smtplib
|
import smtplib
|
||||||
|
import socket
|
||||||
|
|
||||||
ssl = support.import_module("ssl")
|
ssl = support.import_module("ssl")
|
||||||
|
|
||||||
support.requires("network")
|
support.requires("network")
|
||||||
|
|
||||||
|
def check_ssl_verifiy(host, port):
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
with socket.create_connection((host, port)) as sock:
|
||||||
|
try:
|
||||||
|
sock = context.wrap_socket(sock, server_hostname=host)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
sock.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class SmtpTest(unittest.TestCase):
|
class SmtpTest(unittest.TestCase):
|
||||||
testServer = 'smtp.gmail.com'
|
testServer = 'smtp.gmail.com'
|
||||||
remotePort = 25
|
remotePort = 25
|
||||||
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
|
||||||
|
|
||||||
def test_connect_starttls(self):
|
def test_connect_starttls(self):
|
||||||
support.get_attribute(smtplib, 'SMTP_SSL')
|
support.get_attribute(smtplib, 'SMTP_SSL')
|
||||||
|
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
with support.transient_internet(self.testServer):
|
with support.transient_internet(self.testServer):
|
||||||
server = smtplib.SMTP(self.testServer, self.remotePort)
|
server = smtplib.SMTP(self.testServer, self.remotePort)
|
||||||
try:
|
try:
|
||||||
server.starttls(context=self.context)
|
server.starttls(context=context)
|
||||||
except smtplib.SMTPException as e:
|
except smtplib.SMTPException as e:
|
||||||
if e.args[0] == 'STARTTLS extension not supported by server.':
|
if e.args[0] == 'STARTTLS extension not supported by server.':
|
||||||
unittest.skip(e.args[0])
|
unittest.skip(e.args[0])
|
||||||
|
@ -32,7 +44,7 @@ class SmtpTest(unittest.TestCase):
|
||||||
class SmtpSSLTest(unittest.TestCase):
|
class SmtpSSLTest(unittest.TestCase):
|
||||||
testServer = 'smtp.gmail.com'
|
testServer = 'smtp.gmail.com'
|
||||||
remotePort = 465
|
remotePort = 465
|
||||||
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
can_verify = check_ssl_verifiy(testServer, remotePort)
|
||||||
|
|
||||||
def test_connect(self):
|
def test_connect(self):
|
||||||
support.get_attribute(smtplib, 'SMTP_SSL')
|
support.get_attribute(smtplib, 'SMTP_SSL')
|
||||||
|
@ -49,9 +61,19 @@ class SmtpSSLTest(unittest.TestCase):
|
||||||
server.quit()
|
server.quit()
|
||||||
|
|
||||||
def test_connect_using_sslcontext(self):
|
def test_connect_using_sslcontext(self):
|
||||||
|
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
support.get_attribute(smtplib, 'SMTP_SSL')
|
support.get_attribute(smtplib, 'SMTP_SSL')
|
||||||
with support.transient_internet(self.testServer):
|
with support.transient_internet(self.testServer):
|
||||||
server = smtplib.SMTP_SSL(self.testServer, self.remotePort, context=self.context)
|
server = smtplib.SMTP_SSL(self.testServer, self.remotePort, context=context)
|
||||||
|
server.ehlo()
|
||||||
|
server.quit()
|
||||||
|
|
||||||
|
@unittest.skipUnless(can_verify, "SSL certificate can't be verified")
|
||||||
|
def test_connect_using_sslcontext_verified(self):
|
||||||
|
support.get_attribute(smtplib, 'SMTP_SSL')
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
with support.transient_internet(self.testServer):
|
||||||
|
server = smtplib.SMTP_SSL(self.testServer, self.remotePort, context=context)
|
||||||
server.ehlo()
|
server.ehlo()
|
||||||
server.quit()
|
server.quit()
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #19785: smtplib now supports SSLContext.check_hostname and server name
|
||||||
|
indication for TLS/SSL connections.
|
||||||
|
|
||||||
- Issue #19784: poplib now supports SSLContext.check_hostname and server name
|
- Issue #19784: poplib now supports SSLContext.check_hostname and server name
|
||||||
indication for TLS/SSL connections.
|
indication for TLS/SSL connections.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue