Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-42854: Use SSL_read/write_ex() (GH-25468) #25468

Merged
merged 1 commit into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/library/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,11 @@ SSL Sockets
to create instances directly. This was never documented or officially
supported.

.. versionchanged:: 3.10
Python now uses ``SSL_read_ex`` and ``SSL_write_ex`` internally. The
functions support reading and writing of data larger than 2 GB. Writing
zero-length data no longer fails with a protocol violation error.

SSL sockets also have the following additional methods and attributes:

.. method:: SSLSocket.read(len=1024, buffer=None)
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,17 @@ def test_connect_ex_error(self):
)
self.assertIn(rc, errors)

def test_read_write_zero(self):
# empty reads and writes now work, bpo-42854, bpo-31711
client_context, server_context, hostname = testing_context()
server = ThreadedEchoServer(context=server_context)
with server:
with client_context.wrap_socket(socket.socket(),
server_hostname=hostname) as s:
s.connect((HOST, server.port))
self.assertEqual(s.recv(0), b"")
self.assertEqual(s.send(b""), 0)


class ContextTests(unittest.TestCase):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The :mod:`ssl` module now uses ``SSL_read_ex`` and ``SSL_write_ex``
internally. The functions support reading and writing of data larger
than 2 GB. Writing zero-length data no longer fails with a protocol
violation error.
32 changes: 14 additions & 18 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2186,7 +2186,8 @@ static PyObject *
_ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
/*[clinic end generated code: output=aa7a6be5527358d8 input=77262d994fe5100a]*/
{
int len;
size_t count = 0;
int retval;
int sockstate;
_PySSLError err;
int nonblocking;
Expand All @@ -2204,12 +2205,6 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
Py_INCREF(sock);
}

if (b->len > INT_MAX) {
PyErr_Format(PyExc_OverflowError,
"string longer than %d bytes", INT_MAX);
goto error;
}

if (sock != NULL) {
/* just in case the blocking state of the socket has been changed */
nonblocking = (sock->sock_timeout >= 0);
Expand Down Expand Up @@ -2239,8 +2234,8 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)

do {
PySSL_BEGIN_ALLOW_THREADS
len = SSL_write(self->ssl, b->buf, (int)b->len);
err = _PySSL_errno(len <= 0, self->ssl, len);
retval = SSL_write_ex(self->ssl, b->buf, (int)b->len, &count);
err = _PySSL_errno(retval == 0, self->ssl, retval);
PySSL_END_ALLOW_THREADS
self->err = err;

Expand Down Expand Up @@ -2273,11 +2268,11 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
err.ssl == SSL_ERROR_WANT_WRITE);

Py_XDECREF(sock);
if (len <= 0)
return PySSL_SetError(self, len, __FILE__, __LINE__);
if (retval == 0)
return PySSL_SetError(self, retval, __FILE__, __LINE__);
if (PySSL_ChainExceptions(self) < 0)
return NULL;
return PyLong_FromLong(len);
return PyLong_FromSize_t(count);
error:
Py_XDECREF(sock);
PySSL_ChainExceptions(self);
Expand Down Expand Up @@ -2327,7 +2322,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
{
PyObject *dest = NULL;
char *mem;
int count;
size_t count = 0;
int retval;
int sockstate;
_PySSLError err;
int nonblocking;
Expand Down Expand Up @@ -2390,8 +2386,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,

do {
PySSL_BEGIN_ALLOW_THREADS
count = SSL_read(self->ssl, mem, len);
err = _PySSL_errno(count <= 0, self->ssl, count);
retval = SSL_read_ex(self->ssl, mem, len, &count);
err = _PySSL_errno(retval == 0, self->ssl, retval);
PySSL_END_ALLOW_THREADS
self->err = err;

Expand Down Expand Up @@ -2424,8 +2420,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
} while (err.ssl == SSL_ERROR_WANT_READ ||
err.ssl == SSL_ERROR_WANT_WRITE);

if (count <= 0) {
PySSL_SetError(self, count, __FILE__, __LINE__);
if (retval == 0) {
PySSL_SetError(self, retval, __FILE__, __LINE__);
goto error;
}
if (self->exc_type != NULL)
Expand All @@ -2438,7 +2434,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
return dest;
}
else {
return PyLong_FromLong(count);
return PyLong_FromSize_t(count);
}

error:
Expand Down