Revision: 1982
Author: chemodax
Date: Tue Jul 2 08:28:23 2013
Log: Add API for reusing SSL sessions.
* serf_bucket_types.h
(SERF_HAS_SSL_SESSION_API): New macro to detected serf API support in
compile time.
(serf_ssl_session_t, serf_ssl_session_export, serf_ssl_session_import,
serf_ssl_new_session_t, serf_ssl_new_session_callback_set,
ssl_resume_session): New.
* buckets/ssl_buckets.c
(serf_ssl_context_t): Add new_session_cb and new_session_cb_baton.
(serf_ssl_session_t): New.
(serf_ssl_new_session_callback_set, new_session, serf_ssl_session_export,
cleanup_session, serf_ssl_session_import, ssl_resume_session): New.
(ssl_init_context): Initialize new_session_cb to NULL. Enable
SSL_CTX_sess_set_new_cb callback and disable internal session management.
* test/serf_get.c
(app_baton_t): Add session_filename.
(new_ssl_session, read_ssl_session): New.
(conn_setup): Attempt to reuse existing SSL session if requested.
(print_usage: Mention new '-s' option.
(main): Parse new '-s' option.
http://code.google.com/p/serf/source/detail?r=1982
Modified:
/trunk/buckets/ssl_buckets.c
/trunk/serf_bucket_types.h
/trunk/test/serf_get.c
=======================================
--- /trunk/buckets/ssl_buckets.c Sun Apr 21 13:19:52 2013
+++ /trunk/buckets/ssl_buckets.c Tue Jul 2 08:28:23 2013
@@ -173,6 +173,9 @@
serf_ssl_server_cert_chain_cb_t server_cert_chain_callback;
void *server_cert_userdata;
+ serf_ssl_new_session_t new_session_cb;
+ void *new_session_cb_baton;
+
const char *cert_path;
X509 *cached_cert;
@@ -201,6 +204,10 @@
int depth;
};
+struct serf_ssl_session_t {
+ SSL_SESSION *session_obj;
+};
+
static void disable_compression(serf_ssl_context_t *ssl_ctx);
#if SSL_VERBOSE
@@ -1193,6 +1200,93 @@
context->server_cert_userdata = data;
}
+void serf_ssl_new_session_callback_set(
+ serf_ssl_context_t *context,
+ serf_ssl_new_session_t new_session_cb,
+ void *baton)
+{
+ context->new_session_cb = new_session_cb;
+ context->new_session_cb_baton = baton;
+}
+
+static int new_session(SSL *ssl, SSL_SESSION *sess)
+{
+ serf_ssl_context_t *ctx = SSL_get_app_data(ssl);
+
+ if (ctx->new_session_cb) {
+ serf_ssl_session_t session;
+ apr_pool_t *subpool;
+
+ session.session_obj = sess;
+ apr_pool_create(&subpool, ctx->pool);
+
+ ctx->new_session_cb(&session, ctx->new_session_cb_baton, subpool);
+
+ apr_pool_destroy(subpool);
+ }
+
+ return 0;
+}
+
+apr_status_t serf_ssl_session_export(void **data_p,
+ apr_size_t *len_p,
+ const serf_ssl_session_t *session,
+ apr_pool_t *pool)
+{
+ int sess_len;
+ void *sess_data;
+ unsigned char *p;
+
+ sess_len = i2d_SSL_SESSION(session->session_obj, NULL);
+ if (!sess_len) {
+ return APR_EGENERAL;
+ }
+
+ sess_data = apr_palloc(pool, sess_len);
+ p = sess_data;
+ sess_len = i2d_SSL_SESSION(session->session_obj, &p);
+ if (!sess_len) {
+ return APR_EGENERAL;
+ }
+
+ *data_p = sess_data;
+ *len_p = sess_len;
+ return APR_SUCCESS;
+}
+
+static apr_status_t cleanup_session(void *data)
+{
+ serf_ssl_session_t *session = data;
+
+ SSL_SESSION_free(session->session_obj);
+ session->session_obj = NULL;
+
+ return APR_SUCCESS;
+}
+
+apr_status_t serf_ssl_session_import(const serf_ssl_session_t **session_p,
+ void *data,
+ apr_size_t len,
+ apr_pool_t *pool)
+{
+ SSL_SESSION *sess;
+ serf_ssl_session_t *session;
+ const unsigned char *p = data;
+
+ sess = d2i_SSL_SESSION(NULL, &p, len);
+
+ if (!sess) {
+ return APR_EGENERAL;
+ }
+
+ session = apr_pcalloc(pool, sizeof(serf_ssl_session_t));
+ session->session_obj = sess;
+ apr_pool_cleanup_register(pool, session, cleanup_session,
cleanup_session);
+
+ *session_p = session;
+ return APR_SUCCESS;
+}
+
static serf_ssl_context_t *ssl_init_context(void)
{
serf_ssl_context_t *ssl_ctx;
@@ -1226,6 +1320,15 @@
SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER,
validate_server_certificate);
SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_ALL);
+
+ ssl_ctx->new_session_cb = NULL;
+
+ /* Enable SSL callback for new sessions and disable internal session
+ handling. */
+ SSL_CTX_set_session_cache_mode(
+ ssl_ctx->ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_new_cb(ssl_ctx->ctx, new_session);
+
/* Disable SSL compression by default. */
disable_compression(ssl_ctx);
@@ -1260,6 +1363,15 @@
return ssl_ctx;
}
+
+apr_status_t ssl_resume_session(
+ serf_ssl_context_t *ssl_ctx,
+ const serf_ssl_session_t *session,
+ apr_pool_t *pool)
+{
+ SSL_set_session(ssl_ctx->ssl, session->session_obj);
+ return APR_SUCCESS;
+}
static apr_status_t ssl_free_context(
serf_ssl_context_t *ssl_ctx)
=======================================
--- /trunk/serf_bucket_types.h Mon Jun 3 18:06:15 2013
+++ /trunk/serf_bucket_types.h Tue Jul 2 08:28:23 2013
@@ -553,6 +553,43 @@
serf_ssl_server_cert_chain_cb_t cert_chain_callback,
void *data);
+/* Define preprocessor macro for easy detection of serf capability.*/
+#define SERF_HAS_SSL_SESSION_API 1
+
+typedef struct serf_ssl_session_t serf_ssl_session_t;
+
+/* Exports @a session to continous memory block. */
+apr_status_t serf_ssl_session_export(void **data,
+ apr_size_t *len,
+ const serf_ssl_session_t *session,
+ apr_pool_t *pool);
+
+/* Restores previously saved session from continuous memory block @a data
with
+ * @a len length. */
+apr_status_t serf_ssl_session_import(const serf_ssl_session_t **session,
+ void *data,
+ apr_size_t len,
+ apr_pool_t *pool);
+
+/**
+ * Callback to notify when new SSL session is negotiated.
+ */
+typedef apr_status_t (*serf_ssl_new_session_t)(
+ serf_ssl_session_t *ssl_session,
+ void *baton,
+ apr_pool_t *pool);
+
+void serf_ssl_new_session_callback_set(
+ serf_ssl_context_t *context,
+ serf_ssl_new_session_t new_session_cb,
+ void *baton);
+
+/* Configure @a ssl_ctx to attempt resume exisiting @a ssl_session. */
+apr_status_t ssl_resume_session(
+ serf_ssl_context_t *ssl_ctx,
+ const serf_ssl_session_t *ssl_session,
+ apr_pool_t *pool);
+
/**
* Use the default root CA certificates as included with the OpenSSL
library.
*/
=======================================
--- /trunk/test/serf_get.c Wed Jun 26 04:47:21 2013
+++ /trunk/test/serf_get.c Tue Jul 2 08:28:23 2013
@@ -30,6 +30,7 @@
int using_ssl;
serf_ssl_context_t *ssl_ctx;
serf_bucket_alloc_t *bkt_alloc;
+ const char *session_filename;
} app_baton_t;
static void closed_connection(serf_connection_t *conn,
@@ -134,6 +135,67 @@
apr_pool_destroy(pool);
return APR_SUCCESS;
}
+
+static apr_status_t
+new_ssl_session(const serf_ssl_session_t *session,
+ void *baton,
+ apr_pool_t *pool)
+{
+ void *data;
+ apr_size_t len;
+ apr_status_t status;
+ apr_file_t *file;
+ app_baton_t *ctx = baton;
+
+ if (ctx->session_filename) {
+ status = serf_ssl_session_export(&data, &len, session, pool);
+
+ if (status) {
+ return status;
+ }
+
+ status = apr_file_open(&file, ctx->session_filename,
+ APR_WRITE|APR_CREATE, APR_OS_DEFAULT, pool);
+ if (status) {
+ return status;
+ }
+
+ apr_file_write_full(file, data, len, NULL);
+ apr_file_close(file);
+
+ fprintf(stderr, "Saved SSL session to '%s'\n",
ctx->session_filename);
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t read_ssl_session(const serf_ssl_session_t **session,
+ const char *filename,
+ apr_pool_t *pool)
+{
+ apr_status_t status;
+ char buf[32*1014];
+ apr_file_t *file;
+ apr_size_t len;
+
+ status = apr_file_open(&file, filename, APR_READ, APR_OS_DEFAULT,
pool);
+ if (status) {
+ return status;
+ }
+
+ status = apr_file_read_full(file, buf, sizeof(buf), &len);
+
+ /* We should reach EOF. */
+ if (status == APR_EOF) {
+ status = serf_ssl_session_import(session, buf, len, pool);
+ } else {
+ status = APR_EGENERAL;
+ }
+
+ apr_file_close(file);
+
+ return status;
+}
static apr_status_t conn_setup(apr_socket_t *skt,
serf_bucket_t **input_bkt,
@@ -155,6 +217,23 @@
print_certs, NULL);
serf_ssl_set_hostname(ctx->ssl_ctx, ctx->hostinfo);
+ if (ctx->session_filename) {
+ serf_ssl_session_t *session;
+ apr_status_t status;
+
+ serf_ssl_new_session_callback_set(ctx->ssl_ctx,
new_ssl_session, ctx);
+ status = read_ssl_session(&session, ctx->session_filename,
pool);
+ if (status == APR_SUCCESS) {
+ fprintf(stderr, "Using SSL session from '%s'\n",
+ ctx->session_filename);
+ ssl_resume_session(ctx->ssl_ctx, session, pool);
+ }
+ else {
+ fprintf(stderr, "Cannot read SSL session from '%s': %d\n",
+ ctx->session_filename, status);
+ }
+ }
+
*output_bkt = serf_bucket_ssl_encrypt_create(*output_bkt,
ctx->ssl_ctx,
ctx->bkt_alloc);
}
@@ -366,6 +445,7 @@
puts("-m <method> Use the <method> HTTP Method");
puts("-f <file> Use the <file> as the request body");
puts("-p <hostname:port> Use the <host:port> as proxy server");
+ puts("-s <filename> Read and write SSL session to specified file");
}
int main(int argc, const char **argv)
@@ -402,10 +482,11 @@
method = "GET";
/* Do not print headers by default. */
print_headers = 0;
+ app_ctx.session_filename = NULL;
apr_getopt_init(&opt, pool, argc, argv);
- while ((status = apr_getopt(opt, "U:P:f:hHm:n:vp:x:", &opt_c,
&opt_arg)) ==
+ while ((status = apr_getopt(opt, "U:P:f:hHm:n:vp:x:s:", &opt_c,
&opt_arg)) ==
APR_SUCCESS) {
switch (opt_c) {
@@ -452,6 +533,8 @@
case 'v':
puts("Serf version: " SERF_VERSION_STRING);
exit(0);
+ case 's':
+ app_ctx.session_filename = opt_arg;
default:
break;
}