mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
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
5 changed files with 120 additions and 24 deletions
|
@ -59,6 +59,48 @@ Functions, Constants, and Exceptions
|
||||||
.. versionchanged:: 3.3
|
.. versionchanged:: 3.3
|
||||||
:exc:`SSLError` used to be a subtype of :exc:`socket.error`.
|
: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
|
.. exception:: CertificateError
|
||||||
|
|
||||||
Raised to signal an error with a certificate (such as mismatching
|
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
|
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 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 CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
||||||
from _ssl import OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1
|
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
|
from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
|
||||||
|
|
|
@ -619,13 +619,10 @@ class NetworkedTests(unittest.TestCase):
|
||||||
try:
|
try:
|
||||||
s.do_handshake()
|
s.do_handshake()
|
||||||
break
|
break
|
||||||
except ssl.SSLError as err:
|
except ssl.SSLWantReadError:
|
||||||
if err.args[0] == ssl.SSL_ERROR_WANT_READ:
|
|
||||||
select.select([s], [], [], 5.0)
|
select.select([s], [], [], 5.0)
|
||||||
elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
|
except ssl.SSLWantWriteError:
|
||||||
select.select([], [s], [], 5.0)
|
select.select([], [s], [], 5.0)
|
||||||
else:
|
|
||||||
raise
|
|
||||||
# SSL established
|
# SSL established
|
||||||
self.assertTrue(s.getpeercert())
|
self.assertTrue(s.getpeercert())
|
||||||
finally:
|
finally:
|
||||||
|
@ -745,13 +742,10 @@ class NetworkedTests(unittest.TestCase):
|
||||||
count += 1
|
count += 1
|
||||||
s.do_handshake()
|
s.do_handshake()
|
||||||
break
|
break
|
||||||
except ssl.SSLError as err:
|
except ssl.SSLWantReadError:
|
||||||
if err.args[0] == ssl.SSL_ERROR_WANT_READ:
|
|
||||||
select.select([s], [], [])
|
select.select([s], [], [])
|
||||||
elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
|
except ssl.SSLWantWriteError:
|
||||||
select.select([], [s], [])
|
select.select([], [s], [])
|
||||||
else:
|
|
||||||
raise
|
|
||||||
s.close()
|
s.close()
|
||||||
if support.verbose:
|
if support.verbose:
|
||||||
sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count)
|
sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count)
|
||||||
|
@ -1030,12 +1024,11 @@ else:
|
||||||
def _do_ssl_handshake(self):
|
def _do_ssl_handshake(self):
|
||||||
try:
|
try:
|
||||||
self.socket.do_handshake()
|
self.socket.do_handshake()
|
||||||
except ssl.SSLError as err:
|
except (ssl.SSLWantReadError, ssl.SSLWantWriteError):
|
||||||
if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
|
|
||||||
ssl.SSL_ERROR_WANT_WRITE):
|
|
||||||
return
|
return
|
||||||
elif err.args[0] == ssl.SSL_ERROR_EOF:
|
except ssl.SSLEOFError:
|
||||||
return self.handle_close()
|
return self.handle_close()
|
||||||
|
except ssl.SSLError:
|
||||||
raise
|
raise
|
||||||
except socket.error as err:
|
except socket.error as err:
|
||||||
if err.args[0] == errno.ECONNABORTED:
|
if err.args[0] == errno.ECONNABORTED:
|
||||||
|
|
|
@ -341,6 +341,9 @@ Core and Builtins
|
||||||
Library
|
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 #13216: Add cp65001 codec, the Windows UTF-8 (CP_UTF8).
|
||||||
|
|
||||||
- Issue #13226: Add RTLD_xxx constants to the os module. These constants can be
|
- 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 */
|
/* SSL error object */
|
||||||
static PyObject *PySSLErrorObject;
|
static PyObject *PySSLErrorObject;
|
||||||
|
static PyObject *PySSLZeroReturnErrorObject;
|
||||||
|
static PyObject *PySSLWantReadErrorObject;
|
||||||
|
static PyObject *PySSLWantWriteErrorObject;
|
||||||
|
static PyObject *PySSLSyscallErrorObject;
|
||||||
|
static PyObject *PySSLEOFErrorObject;
|
||||||
|
|
||||||
#ifdef WITH_THREAD
|
#ifdef WITH_THREAD
|
||||||
|
|
||||||
|
@ -191,6 +196,7 @@ static PyObject *
|
||||||
PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
||||||
{
|
{
|
||||||
PyObject *v;
|
PyObject *v;
|
||||||
|
PyObject *type = PySSLErrorObject;
|
||||||
char buf[2048];
|
char buf[2048];
|
||||||
char *errstr;
|
char *errstr;
|
||||||
int err;
|
int err;
|
||||||
|
@ -203,15 +209,18 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
||||||
|
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case SSL_ERROR_ZERO_RETURN:
|
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;
|
p = PY_SSL_ERROR_ZERO_RETURN;
|
||||||
break;
|
break;
|
||||||
case SSL_ERROR_WANT_READ:
|
case SSL_ERROR_WANT_READ:
|
||||||
errstr = "The operation did not complete (read)";
|
errstr = "The operation did not complete (read)";
|
||||||
|
type = PySSLWantReadErrorObject;
|
||||||
p = PY_SSL_ERROR_WANT_READ;
|
p = PY_SSL_ERROR_WANT_READ;
|
||||||
break;
|
break;
|
||||||
case SSL_ERROR_WANT_WRITE:
|
case SSL_ERROR_WANT_WRITE:
|
||||||
p = PY_SSL_ERROR_WANT_WRITE;
|
p = PY_SSL_ERROR_WANT_WRITE;
|
||||||
|
type = PySSLWantWriteErrorObject;
|
||||||
errstr = "The operation did not complete (write)";
|
errstr = "The operation did not complete (write)";
|
||||||
break;
|
break;
|
||||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
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);
|
= (PySocketSockObject *) PyWeakref_GetObject(obj->Socket);
|
||||||
if (ret == 0 || (((PyObject *)s) == Py_None)) {
|
if (ret == 0 || (((PyObject *)s) == Py_None)) {
|
||||||
p = PY_SSL_ERROR_EOF;
|
p = PY_SSL_ERROR_EOF;
|
||||||
|
type = PySSLEOFErrorObject;
|
||||||
errstr = "EOF occurred in violation of protocol";
|
errstr = "EOF occurred in violation of protocol";
|
||||||
} else if (ret == -1) {
|
} else if (ret == -1) {
|
||||||
/* underlying BIO reported an I/O error */
|
/* underlying BIO reported an I/O error */
|
||||||
|
@ -240,6 +250,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
||||||
return v;
|
return v;
|
||||||
} else { /* possible? */
|
} else { /* possible? */
|
||||||
p = PY_SSL_ERROR_SYSCALL;
|
p = PY_SSL_ERROR_SYSCALL;
|
||||||
|
type = PySSLSyscallErrorObject;
|
||||||
errstr = "Some I/O error occurred";
|
errstr = "Some I/O error occurred";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -272,7 +283,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
v = Py_BuildValue("(is)", p, buf);
|
v = Py_BuildValue("(is)", p, buf);
|
||||||
if (v != NULL) {
|
if (v != NULL) {
|
||||||
PyErr_SetObject(PySSLErrorObject, v);
|
PyErr_SetObject(type, v);
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2300,6 +2311,23 @@ parse_openssl_version(unsigned long libver,
|
||||||
PyDoc_STRVAR(SSLError_doc,
|
PyDoc_STRVAR(SSLError_doc,
|
||||||
"An error occurred in the SSL implementation.");
|
"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
|
PyMODINIT_FUNC
|
||||||
PyInit__ssl(void)
|
PyInit__ssl(void)
|
||||||
|
@ -2343,7 +2371,33 @@ PyInit__ssl(void)
|
||||||
NULL);
|
NULL);
|
||||||
if (PySSLErrorObject == NULL)
|
if (PySSLErrorObject == NULL)
|
||||||
return 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;
|
return NULL;
|
||||||
if (PyDict_SetItemString(d, "_SSLContext",
|
if (PyDict_SetItemString(d, "_SSLContext",
|
||||||
(PyObject *)&PySSLContext_Type) != 0)
|
(PyObject *)&PySSLContext_Type) != 0)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue