Issue #11183: Add finer-grained exceptions to the ssl module, so that
you don't have to inspect the exception's attributes in the common case.
This commit is contained in:
parent
b5cab85dc7
commit
41032a69c1
|
@ -59,6 +59,48 @@ Functions, Constants, and Exceptions
|
|||
.. versionchanged:: 3.3
|
||||
:exc:`SSLError` used to be a subtype of :exc:`socket.error`.
|
||||
|
||||
.. exception:: SSLZeroReturnError
|
||||
|
||||
A subclass of :exc:`SSLError` raised when trying to read or write and
|
||||
the SSL connection has been closed cleanly. Note that this doesn't
|
||||
mean that the underlying transport (read TCP) has been closed.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. exception:: SSLWantReadError
|
||||
|
||||
A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket
|
||||
<ssl-nonblocking>` when trying to read or write data, but more data needs
|
||||
to be received on the underlying TCP transport before the request can be
|
||||
fulfilled.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. exception:: SSLWantWriteError
|
||||
|
||||
A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket
|
||||
<ssl-nonblocking>` when trying to read or write data, but more data needs
|
||||
to be sent on the underlying TCP transport before the request can be
|
||||
fulfilled.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. exception:: SSLSyscallError
|
||||
|
||||
A subclass of :exc:`SSLError` raised when a system error was encountered
|
||||
while trying to fulfill an operation on a SSL socket. Unfortunately,
|
||||
there is no easy way to inspect the original errno number.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. exception:: SSLEOFError
|
||||
|
||||
A subclass of :exc:`SSLError` raised when the SSL connection has been
|
||||
terminated abrupted. Generally, you shouldn't try to reuse the underlying
|
||||
transport when this error is encountered.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. exception:: CertificateError
|
||||
|
||||
Raised to signal an error with a certificate (such as mismatching
|
||||
|
|
|
@ -60,7 +60,11 @@ import re
|
|||
import _ssl # if we can't import it, let the error propagate
|
||||
|
||||
from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
|
||||
from _ssl import _SSLContext, SSLError
|
||||
from _ssl import _SSLContext
|
||||
from _ssl import (
|
||||
SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError,
|
||||
SSLSyscallError, SSLEOFError,
|
||||
)
|
||||
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
||||
from _ssl import OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1
|
||||
from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
|
||||
|
|
|
@ -619,13 +619,10 @@ class NetworkedTests(unittest.TestCase):
|
|||
try:
|
||||
s.do_handshake()
|
||||
break
|
||||
except ssl.SSLError as err:
|
||||
if err.args[0] == ssl.SSL_ERROR_WANT_READ:
|
||||
select.select([s], [], [], 5.0)
|
||||
elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
|
||||
select.select([], [s], [], 5.0)
|
||||
else:
|
||||
raise
|
||||
except ssl.SSLWantReadError:
|
||||
select.select([s], [], [], 5.0)
|
||||
except ssl.SSLWantWriteError:
|
||||
select.select([], [s], [], 5.0)
|
||||
# SSL established
|
||||
self.assertTrue(s.getpeercert())
|
||||
finally:
|
||||
|
@ -745,13 +742,10 @@ class NetworkedTests(unittest.TestCase):
|
|||
count += 1
|
||||
s.do_handshake()
|
||||
break
|
||||
except ssl.SSLError as err:
|
||||
if err.args[0] == ssl.SSL_ERROR_WANT_READ:
|
||||
select.select([s], [], [])
|
||||
elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
|
||||
select.select([], [s], [])
|
||||
else:
|
||||
raise
|
||||
except ssl.SSLWantReadError:
|
||||
select.select([s], [], [])
|
||||
except ssl.SSLWantWriteError:
|
||||
select.select([], [s], [])
|
||||
s.close()
|
||||
if support.verbose:
|
||||
sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count)
|
||||
|
@ -1030,12 +1024,11 @@ else:
|
|||
def _do_ssl_handshake(self):
|
||||
try:
|
||||
self.socket.do_handshake()
|
||||
except ssl.SSLError as 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()
|
||||
except (ssl.SSLWantReadError, ssl.SSLWantWriteError):
|
||||
return
|
||||
except ssl.SSLEOFError:
|
||||
return self.handle_close()
|
||||
except ssl.SSLError:
|
||||
raise
|
||||
except socket.error as err:
|
||||
if err.args[0] == errno.ECONNABORTED:
|
||||
|
|
|
@ -341,6 +341,9 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #11183: Add finer-grained exceptions to the ssl module, so that
|
||||
you don't have to inspect the exception's attributes in the common case.
|
||||
|
||||
- Issue #13216: Add cp65001 codec, the Windows UTF-8 (CP_UTF8).
|
||||
|
||||
- Issue #13226: Add RTLD_xxx constants to the os module. These constants can be
|
||||
|
|
|
@ -99,6 +99,11 @@ static PySocketModule_APIObject PySocketModule;
|
|||
|
||||
/* SSL error object */
|
||||
static PyObject *PySSLErrorObject;
|
||||
static PyObject *PySSLZeroReturnErrorObject;
|
||||
static PyObject *PySSLWantReadErrorObject;
|
||||
static PyObject *PySSLWantWriteErrorObject;
|
||||
static PyObject *PySSLSyscallErrorObject;
|
||||
static PyObject *PySSLEOFErrorObject;
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
|
||||
|
@ -191,6 +196,7 @@ static PyObject *
|
|||
PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
||||
{
|
||||
PyObject *v;
|
||||
PyObject *type = PySSLErrorObject;
|
||||
char buf[2048];
|
||||
char *errstr;
|
||||
int err;
|
||||
|
@ -203,15 +209,18 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
|||
|
||||
switch (err) {
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
errstr = "TLS/SSL connection has been closed";
|
||||
errstr = "TLS/SSL connection has been closed (EOF)";
|
||||
type = PySSLZeroReturnErrorObject;
|
||||
p = PY_SSL_ERROR_ZERO_RETURN;
|
||||
break;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
errstr = "The operation did not complete (read)";
|
||||
type = PySSLWantReadErrorObject;
|
||||
p = PY_SSL_ERROR_WANT_READ;
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
p = PY_SSL_ERROR_WANT_WRITE;
|
||||
type = PySSLWantWriteErrorObject;
|
||||
errstr = "The operation did not complete (write)";
|
||||
break;
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
|
@ -230,6 +239,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
|||
= (PySocketSockObject *) PyWeakref_GetObject(obj->Socket);
|
||||
if (ret == 0 || (((PyObject *)s) == Py_None)) {
|
||||
p = PY_SSL_ERROR_EOF;
|
||||
type = PySSLEOFErrorObject;
|
||||
errstr = "EOF occurred in violation of protocol";
|
||||
} else if (ret == -1) {
|
||||
/* underlying BIO reported an I/O error */
|
||||
|
@ -240,6 +250,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
|||
return v;
|
||||
} else { /* possible? */
|
||||
p = PY_SSL_ERROR_SYSCALL;
|
||||
type = PySSLSyscallErrorObject;
|
||||
errstr = "Some I/O error occurred";
|
||||
}
|
||||
} else {
|
||||
|
@ -272,7 +283,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
|||
ERR_clear_error();
|
||||
v = Py_BuildValue("(is)", p, buf);
|
||||
if (v != NULL) {
|
||||
PyErr_SetObject(PySSLErrorObject, v);
|
||||
PyErr_SetObject(type, v);
|
||||
Py_DECREF(v);
|
||||
}
|
||||
return NULL;
|
||||
|
@ -2300,6 +2311,23 @@ parse_openssl_version(unsigned long libver,
|
|||
PyDoc_STRVAR(SSLError_doc,
|
||||
"An error occurred in the SSL implementation.");
|
||||
|
||||
PyDoc_STRVAR(SSLZeroReturnError_doc,
|
||||
"SSL/TLS session closed cleanly.");
|
||||
|
||||
PyDoc_STRVAR(SSLWantReadError_doc,
|
||||
"Non-blocking SSL socket needs to read more data\n"
|
||||
"before the requested operation can be completed.");
|
||||
|
||||
PyDoc_STRVAR(SSLWantWriteError_doc,
|
||||
"Non-blocking SSL socket needs to write more data\n"
|
||||
"before the requested operation can be completed.");
|
||||
|
||||
PyDoc_STRVAR(SSLSyscallError_doc,
|
||||
"System error when attempting SSL operation.");
|
||||
|
||||
PyDoc_STRVAR(SSLEOFError_doc,
|
||||
"SSL/TLS connection terminated abruptly.");
|
||||
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__ssl(void)
|
||||
|
@ -2343,7 +2371,33 @@ PyInit__ssl(void)
|
|||
NULL);
|
||||
if (PySSLErrorObject == NULL)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0)
|
||||
PySSLZeroReturnErrorObject = PyErr_NewExceptionWithDoc(
|
||||
"ssl.SSLZeroReturnError", SSLZeroReturnError_doc,
|
||||
PySSLErrorObject, NULL);
|
||||
PySSLWantReadErrorObject = PyErr_NewExceptionWithDoc(
|
||||
"ssl.SSLWantReadError", SSLWantReadError_doc,
|
||||
PySSLErrorObject, NULL);
|
||||
PySSLWantWriteErrorObject = PyErr_NewExceptionWithDoc(
|
||||
"ssl.SSLWantWriteError", SSLWantWriteError_doc,
|
||||
PySSLErrorObject, NULL);
|
||||
PySSLSyscallErrorObject = PyErr_NewExceptionWithDoc(
|
||||
"ssl.SSLSyscallError", SSLSyscallError_doc,
|
||||
PySSLErrorObject, NULL);
|
||||
PySSLEOFErrorObject = PyErr_NewExceptionWithDoc(
|
||||
"ssl.SSLEOFError", SSLEOFError_doc,
|
||||
PySSLErrorObject, NULL);
|
||||
if (PySSLZeroReturnErrorObject == NULL
|
||||
|| PySSLWantReadErrorObject == NULL
|
||||
|| PySSLWantWriteErrorObject == NULL
|
||||
|| PySSLSyscallErrorObject == NULL
|
||||
|| PySSLEOFErrorObject == NULL)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0
|
||||
|| PyDict_SetItemString(d, "SSLZeroReturnError", PySSLZeroReturnErrorObject) != 0
|
||||
|| PyDict_SetItemString(d, "SSLWantReadError", PySSLWantReadErrorObject) != 0
|
||||
|| PyDict_SetItemString(d, "SSLWantWriteError", PySSLWantWriteErrorObject) != 0
|
||||
|| PyDict_SetItemString(d, "SSLSyscallError", PySSLSyscallErrorObject) != 0
|
||||
|| PyDict_SetItemString(d, "SSLEOFError", PySSLEOFErrorObject) != 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(d, "_SSLContext",
|
||||
(PyObject *)&PySSLContext_Type) != 0)
|
||||
|
|
Loading…
Reference in New Issue