From 62e3973395fb9fab2eb8f651bcd0fea4e695e1cf Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Tue, 14 Jan 2020 16:49:59 +0900 Subject: [PATCH] bpo-39259: smtp.SMTP/SMTP_SSL now reject timeout = 0 (GH-17958) --- Doc/library/smtplib.rst | 6 ++++++ Doc/whatsnew/3.9.rst | 7 +++++++ Lib/smtplib.py | 15 ++++++++------- Lib/test/test_smtplib.py | 5 +++++ .../2020-01-12-16-34-28.bpo-39259.J_yBVq.rst | 3 +++ 5 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-01-12-16-34-28.bpo-39259.J_yBVq.rst diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst index 6176c35a0e4..f6ac123823b 100644 --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -70,6 +70,9 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). .. versionadded:: 3.5 The SMTPUTF8 extension (:rfc:`6531`) is now supported. + .. versionchanged:: 3.9 + If the *timeout* parameter is set to be zero, it will raise a + :class:`ValueError` to prevent the creation of a non-blocking socket .. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, \ certfile=None [, timeout], context=None, \ @@ -108,6 +111,9 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). :func:`ssl.create_default_context` select the system's trusted CA certificates for you. + .. versionchanged:: 3.9 + If the *timeout* parameter is set to be zero, it will raise a + :class:`ValueError` to prevent the creation of a non-blocking socket .. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None, source_address=None) diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 859bf440f89..00409af4387 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -259,6 +259,13 @@ now raises :exc:`ImportError` instead of :exc:`ValueError` for invalid relative import attempts. (Contributed by Ngalim Siregar in :issue:`37444`.) +smtplib +------- + +:class:`~smtplib.SMTP` and :class:`~smtplib.SMTP_SSL` now raise a :class:`ValueError` +if the given timeout for their constructor is zero to prevent the creation of +a non-blocking socket. (Contributed by Dong-hee Na in :issue:`39259`.) + signal ------ diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 6513842eb6c..4d5cdb5ac0a 100755 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -303,6 +303,8 @@ class SMTP: def _get_socket(self, host, port, timeout): # This makes it simpler for SMTP_SSL to use the SMTP connect code # and just alter the socket connection bit. + if timeout is not None and not timeout: + raise ValueError('Non-blocking socket (timeout=0) is not supported') if self.debuglevel > 0: self._print_debug('connect: to', (host, port), self.source_address) return socket.create_connection((host, port), timeout, @@ -1030,13 +1032,12 @@ if _have_ssl: keyfile=keyfile) self.context = context SMTP.__init__(self, host, port, local_hostname, timeout, - source_address) + source_address) def _get_socket(self, host, port, timeout): if self.debuglevel > 0: self._print_debug('connect:', (host, port)) - new_socket = socket.create_connection((host, port), timeout, - self.source_address) + new_socket = super()._get_socket(host, port, timeout) new_socket = self.context.wrap_socket(new_socket, server_hostname=self._host) return new_socket @@ -1065,15 +1066,15 @@ class LMTP(SMTP): ehlo_msg = "lhlo" def __init__(self, host='', port=LMTP_PORT, local_hostname=None, - source_address=None): + source_address=None): """Initialize a new instance.""" - SMTP.__init__(self, host, port, local_hostname=local_hostname, - source_address=source_address) + super().__init__(host, port, local_hostname=local_hostname, + source_address=source_address) def connect(self, host='localhost', port=0, source_address=None): """Connect to the LMTP daemon, on either a Unix or a TCP socket.""" if host[0] != '/': - return SMTP.connect(self, host, port, source_address=source_address) + return super().connect(host, port, source_address=source_address) # Handle Unix-domain sockets. try: diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index faf013ac9a6..cc5c4b13464 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -122,6 +122,11 @@ class GeneralTests(unittest.TestCase): self.assertIsNone(smtp.sock.gettimeout()) smtp.close() + def testTimeoutZero(self): + mock_socket.reply_with(b"220 Hola mundo") + with self.assertRaises(ValueError): + smtplib.SMTP(HOST, self.port, timeout=0) + def testTimeoutValue(self): mock_socket.reply_with(b"220 Hola mundo") smtp = smtplib.SMTP(HOST, self.port, timeout=30) diff --git a/Misc/NEWS.d/next/Library/2020-01-12-16-34-28.bpo-39259.J_yBVq.rst b/Misc/NEWS.d/next/Library/2020-01-12-16-34-28.bpo-39259.J_yBVq.rst new file mode 100644 index 00000000000..6cc490eb35e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-01-12-16-34-28.bpo-39259.J_yBVq.rst @@ -0,0 +1,3 @@ +:class:`~smtplib.SMTP` and :class:`~smtplib.SMTP_SSL` now raise a +:class:`ValueError` if the given timeout for their constructor is zero to +prevent the creation of a non-blocking socket. Patch by Dong-hee Na.