// There is no need to call SSL_shutdown() quickly twice in a row, // when you have a proper I/O sleep/wakeup loop. Since the 2nd call // to SSL_shutdown() will not do anything more than the 1st did, in // the situation where no new incomig I/O came in between the 2 // calls (which is a small window if time when you call them back to // back). // // My change to "if(err >= 0)" into "if(err > 0)" presumes you only // want this Python API call to return if SSL_shutdown() was // completely successful. Doing anything else sort of defeats the // point, it means an application wanting/needing a secure shutdown // could never achieve it because you denied them in your // SSL_shutdown() wrapper. // // However if this is the only SSL_shutdown() API call you have you // may find this isn't what all users want, since this forces them // to wait around until SSL_shutdown() is complete and some // application don't demand/require that. Some application only // wish to post their ends SSL_shutdown() but not wait around for // the other ends SSL_shutdown(). // // Alternatively you might like to consider: // 1) Making this call a 1:1 mapping with SSL_shutdown() pushes the // understanding of how to use SSL_shutdown() back to the // application. This can be bad too; so to fix that you create // another new Python API call which is a "helper" method that // applications can use (as a replacement for direct SSL_shutdown // calls). // 2) You might only want this function to block until it sees a 0 // return from SSL_shudown(). If this is the case you should // only perform an I/O wait if you see SSL_ERROR_WANT_WRITE. // Since SSL_ERROR_WANT_READ is synonymous with the return of // 0 from SSL_shutdown() (however you will always see 0 returned // on the first call). // // My vote would be to keep the SSL_shutdown as much of a 1:1 (thin // layer) as possible, and provide a helper function that does the // waiting and leg work. Update your documentation to recommend all // users use the helper function instead of direct SSL_shutdown() // access. The helper function understand's Python's I/O and // sleep/wait model (across different platforms). while (1) { char tmpbuf[64]; int n; PySSL_BEGIN_ALLOW_THREADS n = SSL_peek(self->ssl, tmpbuf, sizeof(tmpbuf)); if (n > 0) { /* Eek! There is still application data that the * application needs to read, we can't correctly * deal with a shutdown until that has been removed. */ // FIXME raise exception, soft-error indicating this problem } err = SSL_shutdown(self->ssl); PySSL_END_ALLOW_THREADS /* if err==1 then a secure shutdown with SSL_shutdown() is complete */ if (err > 0) break; /* Possibly retry shutdown until timeout or failure */ ssl_err = SSL_get_error(self->ssl, err); if (ssl_err == SSL_ERROR_WANT_READ) sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); else if (ssl_err == SSL_ERROR_WANT_WRITE) sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); else break; /* some other error that doesn't relate to waiting for I/O */ if (sockstate == SOCKET_HAS_TIMED_OUT) { if (ssl_err == SSL_ERROR_WANT_READ) PyErr_SetString(PySSLErrorObject, "The read operation timed out"); else PyErr_SetString(PySSLErrorObject, "The write operation timed out"); return NULL; } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select()."); return NULL; } else if (sockstate != SOCKET_OPERATION_OK) /* Retain the SSL error code */ break; }