=== modified file 'OpenSSL/ssl/context.c' | |
--- OpenSSL/ssl/context.c 2011-09-11 13:35:32 +0000 | |
+++ OpenSSL/ssl/context.c 2012-02-09 05:35:09 +0000 | |
@@ -238,6 +238,66 @@ | |
} | |
/* | |
+ * Globally defined next proto callback. This is called from OpenSSL internally. | |
+ * The GIL will not be held when this function is invoked. It must not be held | |
+ * when the function returns. | |
+ * | |
+ * Arguments: ssl - The Connection | |
+ * **out - handle to where we can put a new string to be returned | |
+ * *outlen - pointer to where we can put the len of the string we want to return | |
+ * Returns: SSL_TLSEXT_ERR_OK | |
+ */ | |
+static int | |
+global_next_proto_callback(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) | |
+{ | |
+ ssl_ConnectionObj *conn = (ssl_ConnectionObj *)SSL_get_app_data(ssl); | |
+ PyObject *argv, *ret, *item; | |
+ Py_ssize_t length, i, strlength; | |
+ unsigned char *outptr; | |
+ | |
+ /* | |
+ * GIL isn't held yet. First things first - acquire it, or any Python API | |
+ * we invoke might segfault or blow up the sun. The reverse will be done | |
+ * before returning. | |
+ */ | |
+ MY_END_ALLOW_THREADS(conn->tstate); | |
+ | |
+ argv = Py_BuildValue("(O)", (PyObject *)conn); | |
+ ret = PyEval_CallObject(conn->context->next_protos_advertised_callback, argv); | |
+ Py_DECREF(argv); | |
+ | |
+ length = PyList_Size(ret); | |
+ for (i = 0; i < length; i++) { | |
+ *outlen += PyBytes_Size(PyList_GetItem(ret, i)) + 1; | |
+ /* FIXME: need to test that all strings are less than 255 */ | |
+ } | |
+ *out = outptr = OPENSSL_malloc(*outlen); | |
+ for (i = 0; i < length; i++) { | |
+ item = PyList_GetItem(ret, i); | |
+ strlength = PyBytes_Size(item); | |
+ *outptr = Py_SAFE_DOWNCAST(strlength, Py_ssize_t, char); | |
+ outptr++; | |
+ strncpy((char *)outptr, PyString_AsString(item), strlength); | |
+ outptr += strlength; | |
+ } | |
+ | |
+ if (ret == NULL) { | |
+ /* | |
+ * XXX - This should be reported somehow. -exarkun | |
+ */ | |
+ PyErr_Clear(); | |
+ } else { | |
+ Py_DECREF(ret); | |
+ } | |
+ | |
+ /* | |
+ * This function is returning into OpenSSL. Release the GIL again. | |
+ */ | |
+ MY_BEGIN_ALLOW_THREADS(conn->tstate); | |
+ return SSL_TLSEXT_ERR_OK; | |
+} | |
+ | |
+/* | |
* Globally defined TLS extension server name callback. This is called from | |
* OpenSSL internally. The GIL will not be held when this function is invoked. | |
* It must not be held when the function returns. | |
@@ -1030,6 +1090,35 @@ | |
return Py_None; | |
} | |
+static char ssl_Context_set_next_protos_advertised_callback_doc[] = "\n\ | |
+Set the next protos advertised callback\n\ | |
+\n\ | |
+:param callback: The Python callback to use\n\ | |
+:return: None\n\ | |
+"; | |
+static PyObject * | |
+ssl_Context_set_next_protos_advertised_callback(ssl_ContextObj *self, PyObject *args) | |
+{ | |
+ PyObject *callback; | |
+ | |
+ if (!PyArg_ParseTuple(args, "O:set_next_protos_advertised_callback", &callback)) | |
+ return NULL; | |
+ | |
+ if (!PyCallable_Check(callback)) | |
+ { | |
+ PyErr_SetString(PyExc_TypeError, "expected PyCallable"); | |
+ return NULL; | |
+ } | |
+ | |
+ Py_DECREF(self->next_protos_advertised_callback); | |
+ Py_INCREF(callback); | |
+ self->next_protos_advertised_callback = callback; | |
+ SSL_CTX_set_next_protos_advertised_cb(self->ctx, global_next_proto_callback, NULL); | |
+ | |
+ Py_INCREF(Py_None); | |
+ return Py_None; | |
+} | |
+ | |
static char ssl_Context_get_app_data_doc[] = "\n\ | |
Get the application data (supplied via set_app_data())\n\ | |
\n\ | |
@@ -1187,6 +1276,7 @@ | |
ADD_METHOD(set_timeout), | |
ADD_METHOD(get_timeout), | |
ADD_METHOD(set_info_callback), | |
+ ADD_METHOD(set_next_protos_advertised_callback), | |
ADD_METHOD(get_app_data), | |
ADD_METHOD(set_app_data), | |
ADD_METHOD(get_cert_store), | |
@@ -1241,6 +1331,9 @@ | |
self->info_callback = Py_None; | |
Py_INCREF(Py_None); | |
+ self->next_protos_advertised_callback = Py_None; | |
+ | |
+ Py_INCREF(Py_None); | |
self->tlsext_servername_callback = Py_None; | |
Py_INCREF(Py_None); | |
@@ -1320,6 +1413,8 @@ | |
ret = visit((PyObject *)self->verify_callback, arg); | |
if (ret == 0 && self->info_callback != NULL) | |
ret = visit((PyObject *)self->info_callback, arg); | |
+ if (ret == 0 && self->next_protos_advertised_callback != NULL) | |
+ ret = visit((PyObject *)self->next_protos_advertised_callback, arg); | |
if (ret == 0 && self->app_data != NULL) | |
ret = visit(self->app_data, arg); | |
return ret; | |
@@ -1342,6 +1437,8 @@ | |
self->verify_callback = NULL; | |
Py_XDECREF(self->info_callback); | |
self->info_callback = NULL; | |
+ Py_XDECREF(self->next_protos_advertised_callback); | |
+ self->next_protos_advertised_callback = NULL; | |
Py_XDECREF(self->app_data); | |
self->app_data = NULL; | |
return 0; | |
=== modified file 'OpenSSL/ssl/context.h' | |
--- OpenSSL/ssl/context.h 2011-05-26 22:47:00 +0000 | |
+++ OpenSSL/ssl/context.h 2012-02-09 05:09:32 +0000 | |
@@ -29,6 +29,7 @@ | |
*passphrase_userdata, | |
*verify_callback, | |
*info_callback, | |
+ *next_protos_advertised_callback, | |
*tlsext_servername_callback, | |
*app_data; | |
PyThreadState *tstate; | |
=== modified file 'OpenSSL/test/test_ssl.py' | |
--- OpenSSL/test/test_ssl.py 2011-09-11 14:01:31 +0000 | |
+++ OpenSSL/test/test_ssl.py 2012-02-09 05:13:06 +0000 | |
@@ -587,6 +587,41 @@ | |
# Kind of lame. Just make sure it got called somehow. | |
self.assertTrue(called) | |
+ def FAIL_test_set_next_protos_advertised_callback_callback(self): | |
+ """ | |
+ :py:obj:`Context.set_info_callback` accepts a callable which will be invoked | |
+ when certain information about an SSL connection is available. | |
+ """ | |
+ (server, client) = socket_pair() | |
+ | |
+ clientSSL = Connection(Context(SSLv3_METHOD), client) | |
+ clientSSL.set_connect_state() | |
+ | |
+ called = [] | |
+ def next_protos_advertised(conn): | |
+ called.append(conn) | |
+ return ['spdy/2', 'http/1.1'] | |
+ context = Context(SSLv3_METHOD) | |
+ context.set_next_protos_advertised_callback(next_protos_advertised) | |
+ context.use_certificate( | |
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM)) | |
+ context.use_privatekey( | |
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)) | |
+ | |
+ serverSSL = Connection(context, server) | |
+ serverSSL.set_accept_state() | |
+ | |
+ while not called: | |
+ for ssl in clientSSL, serverSSL: | |
+ try: | |
+ ssl.do_handshake() | |
+ except WantReadError: | |
+ print 'r' | |
+ pass | |
+ | |
+ # Kind of lame. Just make sure it got called somehow. | |
+ self.assertTrue(called) | |
+ | |
def _load_verify_locations_test(self, *args): | |
""" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment