mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #5103: SSL handshake would ignore the socket timeout and block
indefinitely if the other end didn't respond.
This commit is contained in:
parent
4d3e372ff3
commit
fc69af1562
4 changed files with 102 additions and 10 deletions
|
@ -115,12 +115,7 @@ class SSLSocket(socket):
|
||||||
cert_reqs, ssl_version, ca_certs,
|
cert_reqs, ssl_version, ca_certs,
|
||||||
ciphers)
|
ciphers)
|
||||||
if do_handshake_on_connect:
|
if do_handshake_on_connect:
|
||||||
timeout = self.gettimeout()
|
|
||||||
try:
|
|
||||||
self.settimeout(None)
|
|
||||||
self.do_handshake()
|
self.do_handshake()
|
||||||
finally:
|
|
||||||
self.settimeout(timeout)
|
|
||||||
self.keyfile = keyfile
|
self.keyfile = keyfile
|
||||||
self.certfile = certfile
|
self.certfile = certfile
|
||||||
self.cert_reqs = cert_reqs
|
self.cert_reqs = cert_reqs
|
||||||
|
|
|
@ -10,6 +10,7 @@ import asynchat
|
||||||
import socket
|
import socket
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import errno
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from test import test_support
|
from test import test_support
|
||||||
|
@ -231,11 +232,37 @@ if hasattr(poplib, 'POP3_SSL'):
|
||||||
def __init__(self, conn):
|
def __init__(self, conn):
|
||||||
asynchat.async_chat.__init__(self, conn)
|
asynchat.async_chat.__init__(self, conn)
|
||||||
self.socket = ssl.wrap_socket(self.socket, certfile=CERTFILE,
|
self.socket = ssl.wrap_socket(self.socket, certfile=CERTFILE,
|
||||||
server_side=True)
|
server_side=True,
|
||||||
|
do_handshake_on_connect=False)
|
||||||
|
# Must try handshake before calling push()
|
||||||
|
self._ssl_accepting = True
|
||||||
|
self._do_ssl_handshake()
|
||||||
self.set_terminator("\r\n")
|
self.set_terminator("\r\n")
|
||||||
self.in_buffer = []
|
self.in_buffer = []
|
||||||
self.push('+OK dummy pop3 server ready.')
|
self.push('+OK dummy pop3 server ready.')
|
||||||
|
|
||||||
|
def _do_ssl_handshake(self):
|
||||||
|
try:
|
||||||
|
self.socket.do_handshake()
|
||||||
|
except ssl.SSLError, err:
|
||||||
|
if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
|
||||||
|
ssl.SSL_ERROR_WANT_WRITE):
|
||||||
|
return
|
||||||
|
elif err.args[0] == ssl.SSL_ERROR_EOF:
|
||||||
|
return self.handle_close()
|
||||||
|
raise
|
||||||
|
except socket.error, err:
|
||||||
|
if err.args[0] == errno.ECONNABORTED:
|
||||||
|
return self.handle_close()
|
||||||
|
else:
|
||||||
|
self._ssl_accepting = False
|
||||||
|
|
||||||
|
def handle_read(self):
|
||||||
|
if self._ssl_accepting:
|
||||||
|
self._do_ssl_handshake()
|
||||||
|
else:
|
||||||
|
DummyPOP3Handler.handle_read(self)
|
||||||
|
|
||||||
class TestPOP3_SSLClass(TestPOP3Class):
|
class TestPOP3_SSLClass(TestPOP3Class):
|
||||||
# repeat previous tests by using poplib.POP3_SSL
|
# repeat previous tests by using poplib.POP3_SSL
|
||||||
|
|
||||||
|
|
|
@ -494,7 +494,8 @@ else:
|
||||||
asyncore.dispatcher_with_send.__init__(self, conn)
|
asyncore.dispatcher_with_send.__init__(self, conn)
|
||||||
self.socket = ssl.wrap_socket(conn, server_side=True,
|
self.socket = ssl.wrap_socket(conn, server_side=True,
|
||||||
certfile=certfile,
|
certfile=certfile,
|
||||||
do_handshake_on_connect=True)
|
do_handshake_on_connect=False)
|
||||||
|
self._ssl_accepting = True
|
||||||
|
|
||||||
def readable(self):
|
def readable(self):
|
||||||
if isinstance(self.socket, ssl.SSLSocket):
|
if isinstance(self.socket, ssl.SSLSocket):
|
||||||
|
@ -502,7 +503,26 @@ else:
|
||||||
self.handle_read_event()
|
self.handle_read_event()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _do_ssl_handshake(self):
|
||||||
|
try:
|
||||||
|
self.socket.do_handshake()
|
||||||
|
except ssl.SSLError, err:
|
||||||
|
if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
|
||||||
|
ssl.SSL_ERROR_WANT_WRITE):
|
||||||
|
return
|
||||||
|
elif err.args[0] == ssl.SSL_ERROR_EOF:
|
||||||
|
return self.handle_close()
|
||||||
|
raise
|
||||||
|
except socket.error, err:
|
||||||
|
if err.args[0] == errno.ECONNABORTED:
|
||||||
|
return self.handle_close()
|
||||||
|
else:
|
||||||
|
self._ssl_accepting = False
|
||||||
|
|
||||||
def handle_read(self):
|
def handle_read(self):
|
||||||
|
if self._ssl_accepting:
|
||||||
|
self._do_ssl_handshake()
|
||||||
|
else:
|
||||||
data = self.recv(1024)
|
data = self.recv(1024)
|
||||||
self.send(data.lower())
|
self.send(data.lower())
|
||||||
|
|
||||||
|
@ -1271,6 +1291,53 @@ else:
|
||||||
server.stop()
|
server.stop()
|
||||||
server.join()
|
server.join()
|
||||||
|
|
||||||
|
def test_handshake_timeout(self):
|
||||||
|
# Issue #5103: SSL handshake must respect the socket timeout
|
||||||
|
server = socket.socket(socket.AF_INET)
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = test_support.bind_port(server)
|
||||||
|
started = threading.Event()
|
||||||
|
finish = False
|
||||||
|
|
||||||
|
def serve():
|
||||||
|
server.listen(5)
|
||||||
|
started.set()
|
||||||
|
conns = []
|
||||||
|
while not finish:
|
||||||
|
r, w, e = select.select([server], [], [], 0.1)
|
||||||
|
if server in r:
|
||||||
|
# Let the socket hang around rather than having
|
||||||
|
# it closed by garbage collection.
|
||||||
|
conns.append(server.accept()[0])
|
||||||
|
|
||||||
|
t = threading.Thread(target=serve)
|
||||||
|
t.start()
|
||||||
|
started.wait()
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
c = socket.socket(socket.AF_INET)
|
||||||
|
c.settimeout(0.2)
|
||||||
|
c.connect((host, port))
|
||||||
|
# Will attempt handshake and time out
|
||||||
|
self.assertRaisesRegexp(ssl.SSLError, "timed out",
|
||||||
|
ssl.wrap_socket, c)
|
||||||
|
finally:
|
||||||
|
c.close()
|
||||||
|
try:
|
||||||
|
c = socket.socket(socket.AF_INET)
|
||||||
|
c.settimeout(0.2)
|
||||||
|
c = ssl.wrap_socket(c)
|
||||||
|
# Will attempt handshake and time out
|
||||||
|
self.assertRaisesRegexp(ssl.SSLError, "timed out",
|
||||||
|
c.connect, (host, port))
|
||||||
|
finally:
|
||||||
|
c.close()
|
||||||
|
finally:
|
||||||
|
finish = True
|
||||||
|
t.join()
|
||||||
|
server.close()
|
||||||
|
|
||||||
|
|
||||||
def test_main(verbose=False):
|
def test_main(verbose=False):
|
||||||
if skip_expected:
|
if skip_expected:
|
||||||
|
|
|
@ -25,6 +25,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #5103: SSL handshake would ignore the socket timeout and block
|
||||||
|
indefinitely if the other end didn't respond.
|
||||||
|
|
||||||
- The do_handshake() method of SSL objects now adjusts the blocking mode of
|
- The do_handshake() method of SSL objects now adjusts the blocking mode of
|
||||||
the SSL structure if necessary (as other methods already do).
|
the SSL structure if necessary (as other methods already do).
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue