Issue #13721: SSLSocket.getpeercert() and SSLSocket.do_handshake() now raise an OSError with ENOTCONN, instead of an AttributeError, when the SSLSocket is not connected.

This commit is contained in:
Antoine Pitrou 2013-05-01 20:52:07 +02:00
parent f6ca26fbff
commit 242db728e2
3 changed files with 41 additions and 12 deletions

View file

@ -299,7 +299,6 @@ class SSLSocket(socket):
self.server_hostname = server_hostname self.server_hostname = server_hostname
self.do_handshake_on_connect = do_handshake_on_connect self.do_handshake_on_connect = do_handshake_on_connect
self.suppress_ragged_eofs = suppress_ragged_eofs self.suppress_ragged_eofs = suppress_ragged_eofs
connected = False
if sock is not None: if sock is not None:
socket.__init__(self, socket.__init__(self,
family=sock.family, family=sock.family,
@ -307,20 +306,22 @@ class SSLSocket(socket):
proto=sock.proto, proto=sock.proto,
fileno=sock.fileno()) fileno=sock.fileno())
self.settimeout(sock.gettimeout()) self.settimeout(sock.gettimeout())
# see if it's connected
try:
sock.getpeername()
except OSError as e:
if e.errno != errno.ENOTCONN:
raise
else:
connected = True
sock.detach() sock.detach()
elif fileno is not None: elif fileno is not None:
socket.__init__(self, fileno=fileno) socket.__init__(self, fileno=fileno)
else: else:
socket.__init__(self, family=family, type=type, proto=proto) socket.__init__(self, family=family, type=type, proto=proto)
# See if we are connected
try:
self.getpeername()
except OSError as e:
if e.errno != errno.ENOTCONN:
raise
connected = False
else:
connected = True
self._closed = False self._closed = False
self._sslobj = None self._sslobj = None
self._connected = connected self._connected = connected
@ -339,6 +340,7 @@ class SSLSocket(socket):
except OSError as x: except OSError as x:
self.close() self.close()
raise x raise x
@property @property
def context(self): def context(self):
return self._context return self._context
@ -356,6 +358,14 @@ class SSLSocket(socket):
# raise an exception here if you wish to check for spurious closes # raise an exception here if you wish to check for spurious closes
pass pass
def _check_connected(self):
if not self._connected:
# getpeername() will raise ENOTCONN if the socket is really
# not connected; note that we can be connected even without
# _connected being set, e.g. if connect() first returned
# EAGAIN.
self.getpeername()
def read(self, len=0, buffer=None): def read(self, len=0, buffer=None):
"""Read up to LEN bytes and return them. """Read up to LEN bytes and return them.
Return zero-length string on EOF.""" Return zero-length string on EOF."""
@ -390,6 +400,7 @@ class SSLSocket(socket):
certificate was provided, but not validated.""" certificate was provided, but not validated."""
self._checkClosed() self._checkClosed()
self._check_connected()
return self._sslobj.peer_certificate(binary_form) return self._sslobj.peer_certificate(binary_form)
def selected_npn_protocol(self): def selected_npn_protocol(self):
@ -538,12 +549,11 @@ class SSLSocket(socket):
def _real_close(self): def _real_close(self):
self._sslobj = None self._sslobj = None
# self._closed = True
socket._real_close(self) socket._real_close(self)
def do_handshake(self, block=False): def do_handshake(self, block=False):
"""Perform a TLS/SSL handshake.""" """Perform a TLS/SSL handshake."""
self._check_connected()
timeout = self.gettimeout() timeout = self.gettimeout()
try: try:
if timeout == 0.0 and block: if timeout == 0.0 and block:
@ -567,9 +577,9 @@ class SSLSocket(socket):
rc = None rc = None
socket.connect(self, addr) socket.connect(self, addr)
if not rc: if not rc:
self._connected = True
if self.do_handshake_on_connect: if self.do_handshake_on_connect:
self.do_handshake() self.do_handshake()
self._connected = True
return rc return rc
except OSError: except OSError:
self._sslobj = None self._sslobj = None

View file

@ -17,6 +17,7 @@ import asyncore
import weakref import weakref
import platform import platform
import functools import functools
from unittest import mock
ssl = support.import_module("ssl") ssl = support.import_module("ssl")
@ -1931,6 +1932,20 @@ else:
self.assertIsInstance(remote, ssl.SSLSocket) self.assertIsInstance(remote, ssl.SSLSocket)
self.assertEqual(peer, client_addr) self.assertEqual(peer, client_addr)
def test_getpeercert_enotconn(self):
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
with context.wrap_socket(socket.socket()) as sock:
with self.assertRaises(OSError) as cm:
sock.getpeercert()
self.assertEqual(cm.exception.errno, errno.ENOTCONN)
def test_do_handshake_enotconn(self):
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
with context.wrap_socket(socket.socket()) as sock:
with self.assertRaises(OSError) as cm:
sock.do_handshake()
self.assertEqual(cm.exception.errno, errno.ENOTCONN)
def test_default_ciphers(self): def test_default_ciphers(self):
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
try: try:

View file

@ -60,6 +60,10 @@ Core and Builtins
Library Library
------- -------
- Issue #13721: SSLSocket.getpeercert() and SSLSocket.do_handshake() now
raise an OSError with ENOTCONN, instead of an AttributeError, when the
SSLSocket is not connected.
- Issue #14679: add an __all__ (that contains only HTMLParser) to html.parser. - Issue #14679: add an __all__ (that contains only HTMLParser) to html.parser.
- Issue #17802: Fix an UnboundLocalError in html.parser. Initial tests by - Issue #17802: Fix an UnboundLocalError in html.parser. Initial tests by