Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9c3efe3

Browse files
committed
[Patch #945642] Fix non-blocking SSL sockets, which blocked on reads/writes in Python 2.3.
(It turns out that the Debian unstable packaging of Python 2.3.4 includes this patch.) Patch by Tino Lange.
1 parent 91cc5cd commit 9c3efe3

1 file changed

Lines changed: 60 additions & 26 deletions

File tree

Modules/_ssl.c

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,19 @@ typedef struct {
6464
static PyTypeObject PySSL_Type;
6565
static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args);
6666
static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args);
67-
static int wait_for_timeout(PySocketSockObject *s, int writing);
67+
static int check_socket_and_wait_for_timeout(PySocketSockObject *s,
68+
int writing);
6869

6970
#define PySSLObject_Check(v) ((v)->ob_type == &PySSL_Type)
7071

72+
typedef enum {
73+
SOCKET_IS_NONBLOCKING,
74+
SOCKET_IS_BLOCKING,
75+
SOCKET_HAS_TIMED_OUT,
76+
SOCKET_HAS_BEEN_CLOSED,
77+
SOCKET_OPERATION_OK
78+
} timeout_state;
79+
7180
/* XXX It might be helpful to augment the error message generated
7281
below with the name of the SSL function that generated the error.
7382
I expect it's obvious most of the time.
@@ -170,7 +179,7 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file)
170179
char *errstr = NULL;
171180
int ret;
172181
int err;
173-
int timedout;
182+
int sockstate;
174183

175184
self = PyObject_New(PySSLObject, &PySSL_Type); /* Create new object */
176185
if (self == NULL){
@@ -239,7 +248,7 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file)
239248

240249
/* Actually negotiate SSL connection */
241250
/* XXX If SSL_connect() returns 0, it's also a failure. */
242-
timedout = 0;
251+
sockstate = 0;
243252
do {
244253
Py_BEGIN_ALLOW_THREADS
245254
ret = SSL_connect(self->ssl);
@@ -249,13 +258,20 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file)
249258
goto fail;
250259
}
251260
if (err == SSL_ERROR_WANT_READ) {
252-
timedout = wait_for_timeout(Sock, 0);
261+
sockstate = check_socket_and_wait_for_timeout(Sock, 0);
253262
} else if (err == SSL_ERROR_WANT_WRITE) {
254-
timedout = wait_for_timeout(Sock, 1);
263+
sockstate = check_socket_and_wait_for_timeout(Sock, 1);
264+
} else {
265+
sockstate = SOCKET_OPERATION_OK;
255266
}
256-
if (timedout) {
257-
errstr = "The connect operation timed out";
267+
if (sockstate == SOCKET_HAS_TIMED_OUT) {
268+
PyErr_SetString(PySSLErrorObject, "The connect operation timed out");
258269
goto fail;
270+
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
271+
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
272+
goto fail;
273+
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
274+
break;
259275
}
260276
} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE);
261277
if (ret <= 0) {
@@ -334,22 +350,25 @@ static void PySSL_dealloc(PySSLObject *self)
334350

335351
/* If the socket has a timeout, do a select() on the socket.
336352
The argument writing indicates the direction.
337-
Return non-zero if the socket timed out, zero otherwise.
353+
Returns one of the possibilities in the timeout_state enum (above).
338354
*/
355+
339356
static int
340-
wait_for_timeout(PySocketSockObject *s, int writing)
357+
check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing)
341358
{
342359
fd_set fds;
343360
struct timeval tv;
344361
int rc;
345362

346363
/* Nothing to do unless we're in timeout mode (not non-blocking) */
347-
if (s->sock_timeout <= 0.0)
348-
return 0;
364+
if (s->sock_timeout < 0.0)
365+
return SOCKET_IS_BLOCKING;
366+
else if (s->sock_timeout == 0.0)
367+
return SOCKET_IS_NONBLOCKING;
349368

350369
/* Guard against closed socket */
351370
if (s->sock_fd < 0)
352-
return 0;
371+
return SOCKET_HAS_BEEN_CLOSED;
353372

354373
/* Construct the arguments to select */
355374
tv.tv_sec = (int)s->sock_timeout;
@@ -365,25 +384,29 @@ wait_for_timeout(PySocketSockObject *s, int writing)
365384
rc = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
366385
Py_END_ALLOW_THREADS
367386

368-
/* Return 1 on timeout, 0 otherwise */
369-
return rc == 0;
387+
/* Return SOCKET_TIMED_OUT on timeout, SOCKET_OPERATION_OK otherwise
388+
(when we are able to write or when there's something to read) */
389+
return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK;
370390
}
371391

372392
static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args)
373393
{
374394
char *data;
375395
int len;
376396
int count;
377-
int timedout;
397+
int sockstate;
378398
int err;
379399

380400
if (!PyArg_ParseTuple(args, "s#:write", &data, &count))
381401
return NULL;
382402

383-
timedout = wait_for_timeout(self->Socket, 1);
384-
if (timedout) {
403+
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1);
404+
if (sockstate == SOCKET_HAS_TIMED_OUT) {
385405
PyErr_SetString(PySSLErrorObject, "The write operation timed out");
386406
return NULL;
407+
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
408+
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
409+
return NULL;
387410
}
388411
do {
389412
err = 0;
@@ -395,13 +418,20 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args)
395418
return NULL;
396419
}
397420
if (err == SSL_ERROR_WANT_READ) {
398-
timedout = wait_for_timeout(self->Socket, 0);
421+
sockstate = check_socket_and_wait_for_timeout(self->Socket, 0);
399422
} else if (err == SSL_ERROR_WANT_WRITE) {
400-
timedout = wait_for_timeout(self->Socket, 1);
423+
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1);
424+
} else {
425+
sockstate = SOCKET_OPERATION_OK;
401426
}
402-
if (timedout) {
427+
if (sockstate == SOCKET_HAS_TIMED_OUT) {
403428
PyErr_SetString(PySSLErrorObject, "The write operation timed out");
404429
return NULL;
430+
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
431+
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
432+
return NULL;
433+
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
434+
break;
405435
}
406436
} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE);
407437
if (len > 0)
@@ -421,7 +451,7 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
421451
PyObject *buf;
422452
int count = 0;
423453
int len = 1024;
424-
int timedout;
454+
int sockstate;
425455
int err;
426456

427457
if (!PyArg_ParseTuple(args, "|i:read", &len))
@@ -430,8 +460,8 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
430460
if (!(buf = PyString_FromStringAndSize((char *) 0, len)))
431461
return NULL;
432462

433-
timedout = wait_for_timeout(self->Socket, 0);
434-
if (timedout) {
463+
sockstate = check_socket_and_wait_for_timeout(self->Socket, 0);
464+
if (sockstate == SOCKET_HAS_TIMED_OUT) {
435465
PyErr_SetString(PySSLErrorObject, "The read operation timed out");
436466
Py_DECREF(buf);
437467
return NULL;
@@ -447,14 +477,18 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
447477
return NULL;
448478
}
449479
if (err == SSL_ERROR_WANT_READ) {
450-
timedout = wait_for_timeout(self->Socket, 0);
480+
sockstate = check_socket_and_wait_for_timeout(self->Socket, 0);
451481
} else if (err == SSL_ERROR_WANT_WRITE) {
452-
timedout = wait_for_timeout(self->Socket, 1);
482+
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1);
483+
} else {
484+
sockstate = SOCKET_OPERATION_OK;
453485
}
454-
if (timedout) {
486+
if (sockstate == SOCKET_HAS_TIMED_OUT) {
455487
PyErr_SetString(PySSLErrorObject, "The read operation timed out");
456488
Py_DECREF(buf);
457489
return NULL;
490+
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
491+
break;
458492
}
459493
} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE);
460494
if (count <= 0) {

0 commit comments

Comments
 (0)