Merged revisions 80451-80452 via svnmerge from

svn+ssh://pythondev@svn.python.org/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.
........
This commit is contained in:
Antoine Pitrou 2010-04-24 20:13:37 +00:00
parent 2ec87deede
commit c689d96044
4 changed files with 91 additions and 10 deletions

View File

@ -113,12 +113,7 @@ class SSLSocket(socket):
keyfile, certfile, keyfile, certfile,
cert_reqs, ssl_version, ca_certs) cert_reqs, ssl_version, ca_certs)
if do_handshake_on_connect: if do_handshake_on_connect:
timeout = self.gettimeout() self.do_handshake()
try:
self.settimeout(None)
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

View File

@ -457,7 +457,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):
@ -465,9 +466,28 @@ 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):
data = self.recv(1024) if self._ssl_accepting:
self.send(data.lower()) self._do_ssl_handshake()
else:
data = self.recv(1024)
self.send(data.lower())
def handle_close(self): def handle_close(self):
self.close() self.close()
@ -1226,6 +1246,61 @@ 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
try:
ssl.wrap_socket(c)
except ssl.SSLError, e:
self.assertTrue("timed out" in str(e), str(e))
else:
self.fail("SSLError wasn't raised")
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
try:
c.connect((host, port))
except ssl.SSLError, e:
self.assertTrue("timed out" in str(e), str(e))
else:
self.fail("SSLError wasn't raised")
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:

View File

@ -33,6 +33,12 @@ 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 SSL structure if necessary (as other methods already do).
- Issue #5238: Calling makefile() on an SSL object would prevent the - Issue #5238: Calling makefile() on an SSL object would prevent the
underlying socket from being closed until all objects get truely destroyed. underlying socket from being closed until all objects get truely destroyed.

View File

@ -445,7 +445,12 @@ static PyObject *PySSL_SSLdo_handshake(PySSLObject *self)
{ {
int ret; int ret;
int err; int err;
int sockstate; int sockstate, nonblocking;
/* just in case the blocking state of the socket has been changed */
nonblocking = (self->Socket->sock_timeout >= 0.0);
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);
BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);
/* Actually negotiate SSL connection */ /* Actually negotiate SSL connection */
/* XXX If SSL_do_handshake() returns 0, it's also a failure. */ /* XXX If SSL_do_handshake() returns 0, it's also a failure. */