Commit 41032a69 authored by Antoine Pitrou's avatar Antoine Pitrou

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.
parent b5cab85d
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment