Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d3f8ab8

Browse files
committed
Merged revisions 80451-80452 via svnmerge from
svn+ssh://[email protected]/python/trunk ........ r80451 | antoine.pitrou | 2010-04-24 21:57:01 +0200 (sam., 24 avril 2010) | 4 lines The do_handshake() method of SSL objects now adjusts the blocking mode of the SSL structure if necessary (as other methods already do). ........ r80452 | antoine.pitrou | 2010-04-24 22:04:58 +0200 (sam., 24 avril 2010) | 4 lines Issue #5103: SSL handshake would ignore the socket timeout and block indefinitely if the other end didn't respond. ........
1 parent fb88636 commit d3f8ab8

4 files changed

Lines changed: 125 additions & 22 deletions

File tree

Lib/test/test_poplib.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import socket
1111
import os
1212
import time
13+
import errno
1314

1415
from unittest import TestCase
1516
from test import support as test_support
@@ -241,13 +242,39 @@ class DummyPOP3_SSLHandler(DummyPOP3Handler):
241242
def __init__(self, conn):
242243
asynchat.async_chat.__init__(self, conn)
243244
ssl_socket = ssl.wrap_socket(self.socket, certfile=CERTFILE,
244-
server_side=True)
245+
server_side=True,
246+
do_handshake_on_connect=False)
245247
self.del_channel()
246248
self.set_socket(ssl_socket)
249+
# Must try handshake before calling push()
250+
self._ssl_accepting = True
251+
self._do_ssl_handshake()
247252
self.set_terminator(b"\r\n")
248253
self.in_buffer = []
249254
self.push('+OK dummy pop3 server ready. <timestamp>')
250255

256+
def _do_ssl_handshake(self):
257+
try:
258+
self.socket.do_handshake()
259+
except ssl.SSLError as err:
260+
if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
261+
ssl.SSL_ERROR_WANT_WRITE):
262+
return
263+
elif err.args[0] == ssl.SSL_ERROR_EOF:
264+
return self.handle_close()
265+
raise
266+
except socket.error as err:
267+
if err.args[0] == errno.ECONNABORTED:
268+
return self.handle_close()
269+
else:
270+
self._ssl_accepting = False
271+
272+
def handle_read(self):
273+
if self._ssl_accepting:
274+
self._do_ssl_handshake()
275+
else:
276+
DummyPOP3Handler.handle_read(self)
277+
251278
class TestPOP3_SSLClass(TestPOP3Class):
252279
# repeat previous tests by using poplib.POP3_SSL
253280

Lib/test/test_ssl.py

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -616,30 +616,46 @@ def __init__(self, conn, certfile):
616616
certfile=certfile,
617617
do_handshake_on_connect=False)
618618
asyncore.dispatcher_with_send.__init__(self, self.socket)
619-
# now we have to do the handshake
620-
# we'll just do it the easy way, and block the connection
621-
# till it's finished. If we were doing it right, we'd
622-
# do this in multiple calls to handle_read...
623-
self.do_handshake(block=True)
619+
self._ssl_accepting = True
620+
self._do_ssl_handshake()
624621

625622
def readable(self):
626623
if isinstance(self.socket, ssl.SSLSocket):
627624
while self.socket.pending() > 0:
628625
self.handle_read_event()
629626
return True
630627

628+
def _do_ssl_handshake(self):
629+
try:
630+
self.socket.do_handshake()
631+
except ssl.SSLError as err:
632+
if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
633+
ssl.SSL_ERROR_WANT_WRITE):
634+
return
635+
elif err.args[0] == ssl.SSL_ERROR_EOF:
636+
return self.handle_close()
637+
raise
638+
except socket.error as err:
639+
if err.args[0] == errno.ECONNABORTED:
640+
return self.handle_close()
641+
else:
642+
self._ssl_accepting = False
643+
631644
def handle_read(self):
632-
data = self.recv(1024)
633-
if support.verbose:
634-
sys.stdout.write(" server: read %s from client\n" % repr(data))
635-
if not data:
636-
self.close()
645+
if self._ssl_accepting:
646+
self._do_ssl_handshake()
637647
else:
638-
self.send(str(data, 'ASCII', 'strict').lower().encode('ASCII', 'strict'))
648+
data = self.recv(1024)
649+
if support.verbose:
650+
sys.stdout.write(" server: read %s from client\n" % repr(data))
651+
if not data:
652+
self.close()
653+
else:
654+
self.send(str(data, 'ASCII', 'strict').lower().encode('ASCII', 'strict'))
639655

640656
def handle_close(self):
641657
self.close()
642-
if support.verbose:
658+
if test_support.verbose:
643659
sys.stdout.write(" server: closed connection %s\n" % self.socket)
644660

645661
def handle_error(self):
@@ -1266,6 +1282,55 @@ def _recvfrom_into():
12661282
server.stop()
12671283
server.join()
12681284

1285+
def test_handshake_timeout(self):
1286+
# Issue #5103: SSL handshake must respect the socket timeout
1287+
server = socket.socket(socket.AF_INET)
1288+
host = "127.0.0.1"
1289+
port = support.bind_port(server)
1290+
started = threading.Event()
1291+
finish = False
1292+
1293+
def serve():
1294+
server.listen(5)
1295+
started.set()
1296+
conns = []
1297+
while not finish:
1298+
r, w, e = select.select([server], [], [], 0.1)
1299+
if server in r:
1300+
# Let the socket hang around rather than having
1301+
# it closed by garbage collection.
1302+
conns.append(server.accept()[0])
1303+
1304+
t = threading.Thread(target=serve)
1305+
t.start()
1306+
started.wait()
1307+
1308+
try:
1309+
if 0:
1310+
# Disabled until #8524 finds a solution
1311+
try:
1312+
c = socket.socket(socket.AF_INET)
1313+
c.settimeout(1.0)
1314+
c.connect((host, port))
1315+
# Will attempt handshake and time out
1316+
self.assertRaisesRegexp(ssl.SSLError, "timed out",
1317+
ssl.wrap_socket, c)
1318+
finally:
1319+
c.close()
1320+
try:
1321+
c = socket.socket(socket.AF_INET)
1322+
c = ssl.wrap_socket(c)
1323+
c.settimeout(0.2)
1324+
# Will attempt handshake and time out
1325+
self.assertRaisesRegexp(ssl.SSLError, "timed out",
1326+
c.connect, (host, port))
1327+
finally:
1328+
c.close()
1329+
finally:
1330+
finish = True
1331+
t.join()
1332+
server.close()
1333+
12691334

12701335
def test_main(verbose=False):
12711336
if skip_expected:

Misc/NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@ C-API
339339
Library
340340
-------
341341

342+
- Issue #5103: SSL handshake would ignore the socket timeout and block
343+
indefinitely if the other end didn't respond.
344+
345+
- The do_handshake() method of SSL objects now adjusts the blocking mode of
346+
the SSL structure if necessary (as other methods already do).
347+
342348
- Issue #8391: os.execvpe() and os.getenv() supports unicode with surrogates
343349
and bytes strings for environment keys and values
344350

Modules/_ssl.c

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -455,20 +455,25 @@ static PyObject *PySSL_SSLdo_handshake(PySSLObject *self)
455455
{
456456
int ret;
457457
int err;
458-
int sockstate;
458+
int sockstate, nonblocking;
459+
PySocketSockObject *sock
460+
= (PySocketSockObject *) PyWeakref_GetObject(self->Socket);
461+
462+
if (((PyObject*)sock) == Py_None) {
463+
_setSSLError("Underlying socket connection gone",
464+
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);
465+
return NULL;
466+
}
467+
468+
/* just in case the blocking state of the socket has been changed */
469+
nonblocking = (sock->sock_timeout >= 0.0);
470+
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);
471+
BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);
459472

460473
/* Actually negotiate SSL connection */
461474
/* XXX If SSL_do_handshake() returns 0, it's also a failure. */
462475
sockstate = 0;
463476
do {
464-
PySocketSockObject *sock
465-
= (PySocketSockObject *) PyWeakref_GetObject(self->Socket);
466-
if (((PyObject*)sock) == Py_None) {
467-
_setSSLError("Underlying socket connection gone",
468-
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);
469-
return NULL;
470-
}
471-
472477
PySSL_BEGIN_ALLOW_THREADS
473478
ret = SSL_do_handshake(self->ssl);
474479
err = SSL_get_error(self->ssl, ret);

0 commit comments

Comments
 (0)