mirror of https://github.com/python/cpython
> Some of the code sets the error string in this directly before
> returning NULL, and other pieces of the code call PySSL_SetError, > which creates the error string. I think some of the places which set > the string directly probably shouldn't; instead, they should call > PySSL_SetError to cons up the error name directly from the err code. > However, PySSL_SetError only works after the construction of an ssl > object, which means it can't be used there... I'll take a longer look > at it and see if there's a reasonable fix. Here's a patch which addresses this. It also fixes the indentation in PySSL_SetError, bringing it into line with PEP 7, fixes a compile warning about one of the OpenSSL macros, and makes the namespace a bit more consistent. I've tested it on FC 7 and OS X 10.4. % ./python ./Lib/test/regrtest.py -R :1: -u all test_ssl test_ssl beginning 6 repetitions 123456 ...... 1 test OK. [29244 refs] % [GvR: slightly edited to enforce 79-char line length, even if it required violating the style guide.]
This commit is contained in:
parent
ba8c5653cc
commit
780b80dc08
196
Modules/_ssl.c
196
Modules/_ssl.c
|
@ -122,71 +122,77 @@ PySSL_SetError(PySSLObject *obj, int ret, char *filename, int lineno)
|
||||||
char buf[2048];
|
char buf[2048];
|
||||||
char *errstr;
|
char *errstr;
|
||||||
int err;
|
int err;
|
||||||
enum py_ssl_error p;
|
enum py_ssl_error p = PY_SSL_ERROR_NONE;
|
||||||
|
|
||||||
assert(ret <= 0);
|
assert(ret <= 0);
|
||||||
|
|
||||||
err = SSL_get_error(obj->ssl, ret);
|
if ((obj != NULL) && (obj->ssl != NULL)) {
|
||||||
|
err = SSL_get_error(obj->ssl, ret);
|
||||||
|
|
||||||
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";
|
||||||
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)";
|
||||||
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;
|
||||||
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:
|
||||||
p = PY_SSL_ERROR_WANT_X509_LOOKUP;
|
p = PY_SSL_ERROR_WANT_X509_LOOKUP;
|
||||||
errstr = "The operation did not complete (X509 lookup)";
|
errstr =
|
||||||
break;
|
"The operation did not complete (X509 lookup)";
|
||||||
case SSL_ERROR_WANT_CONNECT:
|
break;
|
||||||
p = PY_SSL_ERROR_WANT_CONNECT;
|
case SSL_ERROR_WANT_CONNECT:
|
||||||
errstr = "The operation did not complete (connect)";
|
p = PY_SSL_ERROR_WANT_CONNECT;
|
||||||
break;
|
errstr = "The operation did not complete (connect)";
|
||||||
case SSL_ERROR_SYSCALL:
|
break;
|
||||||
{
|
case SSL_ERROR_SYSCALL:
|
||||||
unsigned long e = ERR_get_error();
|
{
|
||||||
if (e == 0) {
|
unsigned long e = ERR_get_error();
|
||||||
if (ret == 0 || !obj->Socket) {
|
if (e == 0) {
|
||||||
p = PY_SSL_ERROR_EOF;
|
if (ret == 0 || !obj->Socket) {
|
||||||
errstr = "EOF occurred in violation of protocol";
|
p = PY_SSL_ERROR_EOF;
|
||||||
} else if (ret == -1) {
|
errstr =
|
||||||
/* the underlying BIO reported an I/O error */
|
"EOF occurred in violation of protocol";
|
||||||
return obj->Socket->errorhandler();
|
} else if (ret == -1) {
|
||||||
} else { /* possible? */
|
/* underlying BIO reported an I/O error */
|
||||||
|
return obj->Socket->errorhandler();
|
||||||
|
} else { /* possible? */
|
||||||
|
p = PY_SSL_ERROR_SYSCALL;
|
||||||
|
errstr = "Some I/O error occurred";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
p = PY_SSL_ERROR_SYSCALL;
|
p = PY_SSL_ERROR_SYSCALL;
|
||||||
errstr = "Some I/O error occurred";
|
/* XXX Protected by global interpreter lock */
|
||||||
|
errstr = ERR_error_string(e, NULL);
|
||||||
}
|
}
|
||||||
} else {
|
break;
|
||||||
p = PY_SSL_ERROR_SYSCALL;
|
|
||||||
/* XXX Protected by global interpreter lock */
|
|
||||||
errstr = ERR_error_string(e, NULL);
|
|
||||||
}
|
}
|
||||||
break;
|
case SSL_ERROR_SSL:
|
||||||
}
|
{
|
||||||
case SSL_ERROR_SSL:
|
unsigned long e = ERR_get_error();
|
||||||
{
|
p = PY_SSL_ERROR_SSL;
|
||||||
unsigned long e = ERR_get_error();
|
if (e != 0)
|
||||||
p = PY_SSL_ERROR_SSL;
|
/* XXX Protected by global interpreter lock */
|
||||||
if (e != 0)
|
errstr = ERR_error_string(e, NULL);
|
||||||
/* XXX Protected by global interpreter lock */
|
else { /* possible? */
|
||||||
errstr = ERR_error_string(e, NULL);
|
errstr =
|
||||||
else { /* possible? */
|
"A failure in the SSL library occurred";
|
||||||
errstr = "A failure in the SSL library occurred";
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
default:
|
||||||
|
p = PY_SSL_ERROR_INVALID_ERROR_CODE;
|
||||||
|
errstr = "Invalid error code";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errstr = ERR_error_string(ERR_peek_last_error(), NULL);
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
p = PY_SSL_ERROR_INVALID_ERROR_CODE;
|
|
||||||
errstr = "Invalid error code";
|
|
||||||
}
|
|
||||||
|
|
||||||
PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr);
|
PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr);
|
||||||
v = Py_BuildValue("(is)", p, buf);
|
v = Py_BuildValue("(is)", p, buf);
|
||||||
if (v != NULL) {
|
if (v != NULL) {
|
||||||
|
@ -221,13 +227,15 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
|
||||||
self->Socket = NULL;
|
self->Socket = NULL;
|
||||||
|
|
||||||
if ((key_file && !cert_file) || (!key_file && cert_file)) {
|
if ((key_file && !cert_file) || (!key_file && cert_file)) {
|
||||||
errstr = ERRSTR("Both the key & certificate files must be specified");
|
errstr = ERRSTR("Both the key & certificate files "
|
||||||
|
"must be specified");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((socket_type == PY_SSL_SERVER) &&
|
if ((socket_type == PY_SSL_SERVER) &&
|
||||||
((key_file == NULL) || (cert_file == NULL))) {
|
((key_file == NULL) || (cert_file == NULL))) {
|
||||||
errstr = ERRSTR("Both the key & certificate files must be specified for server-side operation");
|
errstr = ERRSTR("Both the key & certificate files "
|
||||||
|
"must be specified for server-side operation");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,15 +257,17 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
|
||||||
|
|
||||||
if (certreq != PY_SSL_CERT_NONE) {
|
if (certreq != PY_SSL_CERT_NONE) {
|
||||||
if (cacerts_file == NULL) {
|
if (cacerts_file == NULL) {
|
||||||
errstr = ERRSTR("No root certificates specified for verification of other-side certificates.");
|
errstr = ERRSTR("No root certificates specified for "
|
||||||
|
"verification of other-side certificates.");
|
||||||
goto fail;
|
goto fail;
|
||||||
} else {
|
} else {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
ret = SSL_CTX_load_verify_locations(self->ctx,
|
ret = SSL_CTX_load_verify_locations(self->ctx,
|
||||||
cacerts_file, NULL);
|
cacerts_file,
|
||||||
|
NULL);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
if (ret < 1) {
|
if (ret != 1) {
|
||||||
errstr = ERRSTR("SSL_CTX_load_verify_locations");
|
PySSL_SetError(NULL, 0, __FILE__, __LINE__);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,8 +277,8 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
|
||||||
ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file,
|
ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file,
|
||||||
SSL_FILETYPE_PEM);
|
SSL_FILETYPE_PEM);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
if (ret < 1) {
|
if (ret != 1) {
|
||||||
errstr = ERRSTR("SSL_CTX_use_PrivateKey_file error");
|
PySSL_SetError(NULL, 0, __FILE__, __LINE__);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,11 +286,12 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
|
||||||
ret = SSL_CTX_use_certificate_chain_file(self->ctx,
|
ret = SSL_CTX_use_certificate_chain_file(self->ctx,
|
||||||
cert_file);
|
cert_file);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
if (ret < 1) {
|
if (ret != 1) {
|
||||||
errstr = ERRSTR("SSL_CTX_use_certificate_chain_file error") ;
|
PySSL_SetError(NULL, 0, __FILE__, __LINE__);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
SSL_CTX_set_options(self->ctx, SSL_OP_ALL); /* ssl compatibility */
|
/* ssl compatibility */
|
||||||
|
SSL_CTX_set_options(self->ctx, SSL_OP_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
verification_mode = SSL_VERIFY_NONE;
|
verification_mode = SSL_VERIFY_NONE;
|
||||||
|
@ -375,7 +386,7 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
PySocket_ssl(PyObject *self, PyObject *args)
|
PySSL_sslwrap(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PySocketSockObject *Sock;
|
PySocketSockObject *Sock;
|
||||||
int server_side = 0;
|
int server_side = 0;
|
||||||
|
@ -431,6 +442,9 @@ _create_dict_for_X509_NAME (X509_NAME *xname)
|
||||||
PyObject *pd = PyDict_New();
|
PyObject *pd = PyDict_New();
|
||||||
int index_counter;
|
int index_counter;
|
||||||
|
|
||||||
|
if (pd == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
for (index_counter = 0;
|
for (index_counter = 0;
|
||||||
index_counter < X509_NAME_entry_count(xname);
|
index_counter < X509_NAME_entry_count(xname);
|
||||||
index_counter++)
|
index_counter++)
|
||||||
|
@ -520,7 +534,7 @@ PySSL_peercert(PySSLObject *self)
|
||||||
X509_get_issuer_name(self->peer_cert));
|
X509_get_issuer_name(self->peer_cert));
|
||||||
if (issuer == NULL)
|
if (issuer == NULL)
|
||||||
goto fail0;
|
goto fail0;
|
||||||
if (PyDict_SetItemString(retval, (const char *) "issuer", issuer) < 0) {
|
if (PyDict_SetItemString(retval, (const char *)"issuer", issuer) < 0) {
|
||||||
Py_DECREF(issuer);
|
Py_DECREF(issuer);
|
||||||
goto fail0;
|
goto fail0;
|
||||||
}
|
}
|
||||||
|
@ -548,7 +562,7 @@ PySSL_peercert(PySSLObject *self)
|
||||||
}
|
}
|
||||||
Py_DECREF(pnotBefore);
|
Py_DECREF(pnotBefore);
|
||||||
|
|
||||||
BIO_reset(biobuf);
|
(void) BIO_reset(biobuf);
|
||||||
notAfter = X509_get_notAfter(self->peer_cert);
|
notAfter = X509_get_notAfter(self->peer_cert);
|
||||||
ASN1_TIME_print(biobuf, notAfter);
|
ASN1_TIME_print(biobuf, notAfter);
|
||||||
len = BIO_gets(biobuf, buf, sizeof(buf)-1);
|
len = BIO_gets(biobuf, buf, sizeof(buf)-1);
|
||||||
|
@ -664,13 +678,16 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args)
|
||||||
|
|
||||||
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1);
|
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1);
|
||||||
if (sockstate == SOCKET_HAS_TIMED_OUT) {
|
if (sockstate == SOCKET_HAS_TIMED_OUT) {
|
||||||
PyErr_SetString(PySSLErrorObject, "The write operation timed out");
|
PyErr_SetString(PySSLErrorObject,
|
||||||
|
"The write operation timed out");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
|
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
|
||||||
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
|
PyErr_SetString(PySSLErrorObject,
|
||||||
|
"Underlying socket has been closed.");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) {
|
} else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) {
|
||||||
PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
|
PyErr_SetString(PySSLErrorObject,
|
||||||
|
"Underlying socket too large for select().");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
@ -683,17 +700,21 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (err == SSL_ERROR_WANT_READ) {
|
if (err == SSL_ERROR_WANT_READ) {
|
||||||
sockstate = check_socket_and_wait_for_timeout(self->Socket, 0);
|
sockstate =
|
||||||
|
check_socket_and_wait_for_timeout(self->Socket, 0);
|
||||||
} else if (err == SSL_ERROR_WANT_WRITE) {
|
} else if (err == SSL_ERROR_WANT_WRITE) {
|
||||||
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1);
|
sockstate =
|
||||||
|
check_socket_and_wait_for_timeout(self->Socket, 1);
|
||||||
} else {
|
} else {
|
||||||
sockstate = SOCKET_OPERATION_OK;
|
sockstate = SOCKET_OPERATION_OK;
|
||||||
}
|
}
|
||||||
if (sockstate == SOCKET_HAS_TIMED_OUT) {
|
if (sockstate == SOCKET_HAS_TIMED_OUT) {
|
||||||
PyErr_SetString(PySSLErrorObject, "The write operation timed out");
|
PyErr_SetString(PySSLErrorObject,
|
||||||
|
"The write operation timed out");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
|
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
|
||||||
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
|
PyErr_SetString(PySSLErrorObject,
|
||||||
|
"Underlying socket has been closed.");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
|
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
|
||||||
break;
|
break;
|
||||||
|
@ -746,9 +767,9 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
|
||||||
if (SSL_get_shutdown(self->ssl) !=
|
if (SSL_get_shutdown(self->ssl) !=
|
||||||
SSL_RECEIVED_SHUTDOWN)
|
SSL_RECEIVED_SHUTDOWN)
|
||||||
{
|
{
|
||||||
Py_DECREF(buf);
|
Py_DECREF(buf);
|
||||||
PyErr_SetString(PySSLErrorObject,
|
PyErr_SetString(PySSLErrorObject,
|
||||||
"Socket closed without SSL shutdown handshake");
|
"Socket closed without SSL shutdown handshake");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
/* should contain a zero-length string */
|
/* should contain a zero-length string */
|
||||||
|
@ -768,14 +789,14 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (err == SSL_ERROR_WANT_READ) {
|
if (err == SSL_ERROR_WANT_READ) {
|
||||||
sockstate =
|
sockstate =
|
||||||
check_socket_and_wait_for_timeout(self->Socket, 0);
|
check_socket_and_wait_for_timeout(self->Socket, 0);
|
||||||
} else if (err == SSL_ERROR_WANT_WRITE) {
|
} else if (err == SSL_ERROR_WANT_WRITE) {
|
||||||
sockstate =
|
sockstate =
|
||||||
check_socket_and_wait_for_timeout(self->Socket, 1);
|
check_socket_and_wait_for_timeout(self->Socket, 1);
|
||||||
} else if ((err == SSL_ERROR_ZERO_RETURN) &&
|
} else if ((err == SSL_ERROR_ZERO_RETURN) &&
|
||||||
(SSL_get_shutdown(self->ssl) ==
|
(SSL_get_shutdown(self->ssl) ==
|
||||||
SSL_RECEIVED_SHUTDOWN))
|
SSL_RECEIVED_SHUTDOWN))
|
||||||
{
|
{
|
||||||
_PyString_Resize(&buf, 0);
|
_PyString_Resize(&buf, 0);
|
||||||
return buf;
|
return buf;
|
||||||
|
@ -846,7 +867,8 @@ static PyMethodDef PySSLMethods[] = {
|
||||||
{"server", (PyCFunction)PySSL_server, METH_NOARGS},
|
{"server", (PyCFunction)PySSL_server, METH_NOARGS},
|
||||||
{"issuer", (PyCFunction)PySSL_issuer, METH_NOARGS},
|
{"issuer", (PyCFunction)PySSL_issuer, METH_NOARGS},
|
||||||
{"peer_certificate", (PyCFunction)PySSL_peercert, METH_NOARGS},
|
{"peer_certificate", (PyCFunction)PySSL_peercert, METH_NOARGS},
|
||||||
{"shutdown", (PyCFunction)PySSL_SSLshutdown, METH_NOARGS, PySSL_SSLshutdown_doc},
|
{"shutdown", (PyCFunction)PySSL_SSLshutdown, METH_NOARGS,
|
||||||
|
PySSL_SSLshutdown_doc},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -857,7 +879,7 @@ static PyObject *PySSL_getattr(PySSLObject *self, char *name)
|
||||||
|
|
||||||
static PyTypeObject PySSL_Type = {
|
static PyTypeObject PySSL_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
"socket.SSL", /*tp_name*/
|
"ssl.SSLContext", /*tp_name*/
|
||||||
sizeof(PySSLObject), /*tp_basicsize*/
|
sizeof(PySSLObject), /*tp_basicsize*/
|
||||||
0, /*tp_itemsize*/
|
0, /*tp_itemsize*/
|
||||||
/* methods */
|
/* methods */
|
||||||
|
@ -932,7 +954,7 @@ PyDoc_STRVAR(PySSL_RAND_egd_doc,
|
||||||
"RAND_egd(path) -> bytes\n\
|
"RAND_egd(path) -> bytes\n\
|
||||||
\n\
|
\n\
|
||||||
Queries the entropy gather daemon (EGD) on socket path. Returns number\n\
|
Queries the entropy gather daemon (EGD) on socket path. Returns number\n\
|
||||||
of bytes read. Raises socket.sslerror if connection to EGD fails or\n\
|
of bytes read. Raises ssl.sslerror if connection to EGD fails or\n\
|
||||||
if it does provide enough data to seed PRNG.");
|
if it does provide enough data to seed PRNG.");
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -940,7 +962,7 @@ if it does provide enough data to seed PRNG.");
|
||||||
/* List of functions exported by this module. */
|
/* List of functions exported by this module. */
|
||||||
|
|
||||||
static PyMethodDef PySSL_methods[] = {
|
static PyMethodDef PySSL_methods[] = {
|
||||||
{"sslwrap", PySocket_ssl,
|
{"sslwrap", PySSL_sslwrap,
|
||||||
METH_VARARGS, ssl_doc},
|
METH_VARARGS, ssl_doc},
|
||||||
#ifdef HAVE_OPENSSL_RAND
|
#ifdef HAVE_OPENSSL_RAND
|
||||||
{"RAND_add", PySSL_RAND_add, METH_VARARGS,
|
{"RAND_add", PySSL_RAND_add, METH_VARARGS,
|
||||||
|
@ -979,7 +1001,7 @@ init_ssl(void)
|
||||||
SSLeay_add_ssl_algorithms();
|
SSLeay_add_ssl_algorithms();
|
||||||
|
|
||||||
/* Add symbols to module dict */
|
/* Add symbols to module dict */
|
||||||
PySSLErrorObject = PyErr_NewException("socket.sslerror",
|
PySSLErrorObject = PyErr_NewException("ssl.sslerror",
|
||||||
PySocketModule.error,
|
PySocketModule.error,
|
||||||
NULL);
|
NULL);
|
||||||
if (PySSLErrorObject == NULL)
|
if (PySSLErrorObject == NULL)
|
||||||
|
|
Loading…
Reference in New Issue