From a66e1478c77fc8c8a7ab831916ab3d1c3aacb539 Mon Sep 17 00:00:00 2001 From: Paul Querna Date: Fri, 10 Dec 2004 06:08:52 +0000 Subject: working support for a ssl session cache via memcached. --- diff --git a/configure.ac b/configure.ac index 1a72b8c..001f145 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,8 @@ CHECK_LIBGNUTLS($MIN_TLS_VERSION) CHECK_APR_MEMCACHE() -MODULE_CFLAGS="${LIBGNUTLS_CFLAGS} ${APXS_CFLAGS} ${AP_INCLUDES} ${APR_INCLUDES} ${APU_INCLUDES}" -MODULE_LIBS="${LIBGNUTLS_LIBS} ${APR_MEMCACHE_LIBS}" +MODULE_CFLAGS="${LIBGNUTLS_CFLAGS} ${APR_MEMCACHE_CFLAGS} ${APXS_CFLAGS} ${AP_INCLUDES} ${APR_INCLUDES} ${APU_INCLUDES}" +MODULE_LIBS="${APR_MEMCACHE_LIBS} ${LIBGNUTLS_LIBS}" AC_SUBST(MODULE_CFLAGS) AC_SUBST(MODULE_LIBS) diff --git a/include/memcache.h b/include/memcache.h deleted file mode 100644 index b11bf4c..0000000 --- a/include/memcache.h +++ /dev/null @@ -1,402 +0,0 @@ -/* Copyright (c) 2004 Sean Chittenden - * - * All rights reserved until such time as this code is released with - * an official license. Use of this code for commerical, - * non-commercial, and personal purposes is encouraged. Public forks - * of this code is permitted so long as the fork and its decendents - * use this copyright/license. Use of this software in programs - * released under the GPL programs is expressly prohibited by the - * author (ie, BSD, closed source, or artistic license is okay, but - * GPL is not). */ - -#ifndef MEMCACHE_H -#define MEMCACHE_H - -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Our initial read(2) buffer has to be long enough to read the - * first line of the response. ie: - * - * "VALUE #{'k' * 250} #{2 ** 15} #{2 ** 32}\r\n.length => 275 - * - * However, since we want to avoid the number of system calls - * necessary, include trailing part of the protocol in our estimate: - * - * "\r\nEND\r\n".length => 7 - * - * Which yields a manditory limit of 282 bytes for a successful - * response. If we wish to try and get lucky with our first read(2) - * call and be able to read(2) in small values without making a second - * read(2) call, pad this number with a sufficiently large byte value. - * If most of your keys are 512B, then a GET_INIT_BUF_SIZE of 794 - * would be prudent (512 + 282). - * - * The default value of 1024 means that values less than 724 bytes - * will always be read(2) via the first read(2) call. Increasing this - * value to large values is not beneficial. If a second read(2) call - * is necessary, the read(2) will be made with a sufficiently large - * buffer already allocated. */ -#define GET_INIT_BUF_SIZE ((size_t)1024) - -/* Enables extra protocol checking. It's not strickly necesary if the - * server's sending the right data. This should be on by default, - * but, for those that don't want every check done to make sure things - * are right, undef this. 99% of the universe should leave this as - * is. If you think you're than 1% who doesn't need it, you're on - * crack and should definately leave PEDANTIC on. %*/ -#define PEDANTIC - -#define MAX_KEY_LEN 250 - -#define HAVE_SELECT 1 - -#define USE_CRC32_HASH 1 -/* #define USE_ELF_HASH 1 */ -/* #define USE_PERL_HASH 1 */ - -/* Various values for _flags */ -#define MC_RES_FREE_ON_DELETE 0x01 -#define MC_RES_NO_FREE_ON_DELETE 0x02 -#define MC_RES_FOUND 0x04 -struct memcache_res { - const char *key; /* key */ - size_t len; /* length of key */ - u_int32_t hash; /* hash of the key */ - void *val; /* the value */ - size_t bytes; /* length of val */ - - /* If size is zero (default), the memory for val is automatically - * allocated using mcMalloc(3). If size is zero, _flags has its - * MC_RES_FREE_ON_DELETE bit set. - * - * If size is non-zero, libmemcache(3) assumes that the caller has - * set val to an available portion of memory that is size bytes - * long. libmemcache(3) will only populate val with as many bytes - * as are specified by size (ie, it will trim the value in order to - * fit into val). If size is non-zero, _flags has its - * MC_RES_NO_FREE_ON_DELETE bit set by default. */ - size_t size; - TAILQ_ENTRY(memcache_res) entries; - - /* Note: this flags is very different than _flags. */ - u_int16_t flags; - - /* If _flags has 0x01 set, val will be free(3)'ed on when this - * struct is cleaned up via mc_res_free() or the request is cleaned - * up via mc_req_free(). - * - * If _flags has is 0x02 set, val will not be free(3)'ed when this - * response or its parent request are cleaned up. - * - * Note: Use mc_res_free_on_delete() to set the "free on delete" - * bits. */ - char _flags; -}; - -struct memcache_req { - TAILQ_HEAD(memcache_res_list, memcache_res) query; - u_int16_t num_keys; -}; - -struct memcache_server { - /* The hostname of the server. */ - char *hostname; - - /* Port number of the host we're connecting to. */ - char *port; - - /* The file descriptor for this server */ - int fd; - - /* The file descriptor flags */ - int flags; - - /* The timeout for this server */ - struct timeval tv; - - /* Is this particular server active or not? - * - * 'd' == Down Last request was unsuccessful - * 'n' == No host The hostname doesn't exist - * 't' == Try Haven't connected to it yet, will attempt - * 'u' == Up Has been connected to successfully - */ - char active; - - /* A cached copy of the looked up host. */ - struct addrinfo *hostinfo; - - /* The number of addresses in the cached copy. If there is more - * than one per DNS entry (discouraged), we establish a connection - * to them all. */ - u_int32_t num_addrs; - -#ifdef HAVE_SELECT - /* Reduces the amount of user time required when reading data. */ - fd_set fds; - struct timeval select_tv; -#endif - - /* Misc list bits */ - TAILQ_ENTRY(memcache_server) entries; -}; - - -struct memcache_server_stats { - pid_t pid; - time_t uptime; - time_t time; - char *version; - struct timeval rusage_user; - struct timeval rusage_system; - u_int32_t curr_items; - u_int64_t total_items; - u_int64_t bytes; - u_int32_t curr_connections; - u_int64_t total_connections; - u_int32_t connection_structures; - u_int64_t cmd_get; - u_int64_t cmd_refresh; - u_int64_t cmd_set; - u_int64_t get_hits; - u_int64_t get_misses; - u_int64_t refresh_hits; - u_int64_t refresh_misses; - u_int64_t bytes_read; - u_int64_t bytes_written; - u_int64_t limit_maxbytes; -}; - - -struct memcache { - /* The default timeout for all servers */ - struct timeval tv; - - /* The default read(2) size when reading a response. */ - size_t read_size; - - /* The complete list of servers */ - TAILQ_HEAD(memcache_server_list, memcache_server) server_list; - - /* A buffer for data */ - char *buf; - - /* A cursor for where we are in the buffer */ - char *cur; - - /* A pointer to where data should be appended with future read(2) - * calls. */ - char *read_cur; - - /* A pointer to the start of the current line in the buffer. */ - char *start; - - /* The allocated size of the buffer */ - size_t size; - - /* The number of servers in live_servers */ - u_int32_t num_live_servers; - - /* An array of usable memcache_servers */ - struct memcache_server **live_servers; -}; - - -/* Adds a given key to the cache */ -int mc_add(struct memcache *mc, - const char *key, const size_t key_len, - const void *val, const size_t bytes, - const time_t expire, const u_int16_t flags); - -/* Gets the value from memcache and allocates the data for the caller. - * It is the caller's responsibility to free the returned value. - * mc_get() is the preferred interface, however. */ -void *mc_aget(struct memcache *mc, const char *key, const size_t len); - -/* Gets the value from memcache and allocates the data for the caller. - * It is the caller's responsibility to free the returned value. - * mc_refresh() is the preferred interface, however. */ -void *mc_arefresh(struct memcache *mc, const char *key, const size_t len); - -/* Disconnects from a given server and marks it as down. */ -void mc_deactivate_server(struct memcache *mc, struct memcache_server *ms); - -/* Decrements a given key */ -u_int32_t mc_decr(struct memcache *mc, const char *key, const size_t key_len, const u_int32_t val); - -/* Deletes a given key */ -int mc_delete(struct memcache *mc, const char *key, const size_t key_len, const time_t hold); - -/* When given a hash value, this function returns the appropriate - * server to connect to in order to find the key. */ -struct memcache_server *mc_find_server(struct memcache *mc, const u_int32_t hash); - -/* Flushes all keys */ -int mc_flush_all(struct memcache *mc, const char *key, const size_t key_len); - -/* cleans up a memcache object. */ -void mc_free(struct memcache *mc); - -/* Tells the response object to free the allocated memory when it gets - * cleaned up or to let the caller manage the memory. */ -void mc_res_free_on_delete(struct memcache_res *res, int free_on_delete); - -/* mc_get() is the preferred method of accessing memcache. It - * accepts multiple keys and lets a user (should they so choose) - * perform memory caching to reduce the number of mcMalloc(3) calls - * mades. */ -void mc_get(struct memcache *mc, struct memcache_req *req); - -/* Generates a hash value from a given key */ -u_int32_t mc_hash_key(const char *key, const size_t len); - -/* Increments a given key */ -u_int32_t mc_incr(struct memcache *mc, const char *key, const size_t key_len, const u_int32_t val); - -/* Allocates a new memcache object */ -struct memcache *mc_new(void); - -/* mc_refresh() is the preferred method of accessing memcache. It - * accepts multiple keys and lets a user (should they so choose) - * perform memory caching to reduce the number of mcMalloc(3) calls - * mades. mc_refresh() differs from mc_get() in that mc_refresh - * updates the expiration time to be now + whatever the expiration for - * the item was set to. Sessions should use this as a way of noting - * sessions expiring. */ -void mc_refresh(struct memcache *mc, struct memcache_req *req); - -/* Replaces a given key to the cache */ -int mc_replace(struct memcache *mc, - const char *key, const size_t key_len, - const void *val, const size_t bytes, - const time_t expire, const u_int16_t flags); - -/* Adds a key to a given request */ -struct memcache_res *mc_req_add(struct memcache_req *req, const char *key, size_t len); - -/* Cleans up a given request and its subsequent responses. If _flags - * has the MC_RES_FREE_ON_DELETE bit set (default), it will clean up - * the value too. If _flags has MC_RES_NO_FREE_ON_DELETE set, - * however, it is the caller's responsibility to free the value. To - * prevent double free(3) errors, if a value is free(3)'ed before - * mc_req_free() is called, set val to NULL. */ -void mc_req_free(struct memcache_req *req); - -/* Allocates a new memcache request object. */ -struct memcache_req *mc_req_new(void); - -/* Cleans up an individual response object. Normally this is not - * necessary as a call to mc_req_free() will clean up its response - * objects. */ -void mc_res_free(struct memcache_req *req, struct memcache_res *res); - -/* Adds a server to the list of available servers. By default, - * servers are assumed to be available. Return codes: - * - * 0: success - * -1: Unable to allocate a new server instance - * -2: Unable to strdup hostname - * -3: Unable to strdup port - * -4: Unable to Unable to resolve the host, server deactivated, but added to list - * -5: Unable to realloc(3) the server list, server list unchanged */ -int mc_server_add(struct memcache *mc, const char *hostname, const char *port); -int mc_server_add2(struct memcache *mc, - const char *hostname, const size_t hostname_len, - const char *port, const size_t port_len); - -/* Cleans up a given stat's object */ -void mc_server_stats_free(struct memcache_server_stats *s); - -/* Gets a stats object from the given server. It is the caller's - * responsibility to cleanup the resulting object via - * mc_server_stats_free(). */ -struct memcache_server_stats *mc_server_stats(struct memcache *mc, struct memcache_server *ms); - -/* Sets a given key */ -int mc_set(struct memcache *mc, - const char *key, const size_t key_len, - const void *val, const size_t bytes, - const time_t expire, const u_int16_t flags); - -/* Creates a stats object for all available servers and returns the - * cumulative stats. Per host-specific data is generally the same as - * the last server querried. */ -struct memcache_server_stats *mc_stats(struct memcache *mc); - -/* Sets the default timeout for new servers. */ -void mc_timeout(struct memcache *mc, const int sec, const int usec); - - - -/* BEGIN memory management API functions */ - -/* The memcache API allows callers to provide their own memory - * allocation routines to aid in embedability with existing programs, - * libraries, programming languages, and environments that have their - * own memory handling routines. This interface was inspired by - * libxml. */ -typedef void (*mcFreeFunc)(void *mem); -typedef void *(*mcMallocFunc)(const size_t size); -typedef void *(*mcReallocFunc)(void *mem, const size_t size); -typedef char *(*mcStrdupFunc)(const char *str); - -extern mcFreeFunc mcFree; -extern mcMallocFunc mcMalloc; -extern mcMallocFunc mcMallocAtomic; -extern mcReallocFunc mcRealloc; -extern mcStrdupFunc mcStrdup; - -int mcMemSetup(mcFreeFunc freeFunc, mcMallocFunc mallocFunc, - mcReallocFunc reallocFunc, mcStrdupFunc strdupFunc); -int mcMemGet(mcFreeFunc *freeFunc, mcMallocFunc *mallocFunc, - mcReallocFunc *reallocFunc, mcStrdupFunc *strdupFunc); -/* END memory management API functions */ - - -/* APIs that should be implemented: */ - -/* Resets all hosts that are down to try */ -void mc_server_reset_all_active(struct memcache *mc); - -/* Resets a given host back to a try state */ -void mc_server_reset_active(struct memcache *mc, const char *hostname, const int port); - -/* Resets all dns entries */ -void mc_server_reset_all_dns(struct memcache *mc); - -/* Resets only one host's DNS cache */ -void mc_server_reset_dns(struct memcache *mc, const char *hostname, const int port); - -/* Disconnects from all memcache servers */ -void mc_server_disconnect_all(struct memcache *mc); - -/* Disconnects from one memcache server */ -void mc_server_disconnect(struct memcache *mc, const char *hostname, const int port); - -#ifdef TCP_NODELAY -/* Enable/disable TCP_NODELAY */ -void mc_nodelay_enable(struct memcache *mc, const int enable); - -/* Enable/disable TCP_NODELAY for a given server */ -void mc_server_nodelay_enable(struct memcache_server *ms, const int enable); -#endif - -/* Set the timeout on a per server basis */ -void mc_server_timeout(struct memcache_server *ms, const int sec, const int usec); - -/* Set the number of seconds you're willing to wait in total for a - * response. ??? */ - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/mod_gnutls.h b/include/mod_gnutls.h index 0f3433c..743a43f 100644 --- a/include/mod_gnutls.h +++ b/include/mod_gnutls.h @@ -29,6 +29,8 @@ #include "apr_strings.h" #include "apr_tables.h" +#include "apr_memcache.h" + #include #include @@ -68,6 +70,7 @@ typedef struct int macs[16]; int protocol[16]; int compression[16]; + const char* cache_config; } mod_gnutls_srvconf_rec; typedef struct { @@ -155,4 +158,13 @@ ssize_t mod_gnutls_transport_write(gnutls_transport_ptr_t ptr, const void *buffer, size_t len); +/** + * Init the Cache inside each Process + */ +int mod_gnutls_cache_child_init(apr_pool_t *p, server_rec *s, + mod_gnutls_srvconf_rec *sc); +/** + * Setup the Session Caching + */ +int mod_gnutls_cache_session_init(mod_gnutls_handle_t *ctxt); #endif /* __mod_gnutls_h_inc */ diff --git a/src/Makefile.am b/src/Makefile.am index 05d2fab..1f1860b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ CLEANFILES = .libs/libmod_gnutls *~ libmod_gnutls_la_SOURCES = mod_gnutls.c gnutls_io.c gnutls_cache.c libmod_gnutls_la_CFLAGS = -Wall ${MODULE_CFLAGS} -libmod_gnutls_la_LDFLAGS = ${MODULE_LIBS} +libmod_gnutls_la_LDFLAGS = -rpath ${AP_LIBEXECDIR} -module -avoid-version ${MODULE_LIBS} lib_LTLIBRARIES = libmod_gnutls.la diff --git a/src/gnutls_cache.c b/src/gnutls_cache.c index 683cdf4..bc13440 100644 --- a/src/gnutls_cache.c +++ b/src/gnutls_cache.c @@ -16,46 +16,212 @@ */ #include "mod_gnutls.h" +#include "ap_mpm.h" /** * GnuTLS Session Cache using libmemcached * */ -/* -#include "memcache.h" -int mod_gnutls_cache_init() +/* The underlying apr_memcache system is thread safe... woohoo */ +static apr_memcache_t* mc; + +int mod_gnutls_cache_child_init(apr_pool_t *p, server_rec *s, + mod_gnutls_srvconf_rec *sc) { - return 0; + apr_status_t rv = APR_SUCCESS; + int thread_limit = 0; + int nservers = 0; + char* cache_config; + char* split; + char* tok; + + ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit); + + /* Find all the servers in the first run to get a total count */ + cache_config = apr_pstrdup(p, sc->cache_config); + split = apr_strtok(cache_config, " ", &tok); + while (split) { + nservers++; + split = apr_strtok(NULL," ", &tok); + } + + rv = apr_memcache_create(p, nservers, 0, &mc); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "[gnutls_cache] Failed to create Memcache Object of '%d' size.", + nservers); + return rv; + } + + /* Now add each server to the memcache */ + cache_config = apr_pstrdup(p, sc->cache_config); + split = apr_strtok(cache_config, " ", &tok); + while (split) { + apr_memcache_server_t* st; + char* split2; + char* host_str; + char* port_str; + int port; + + host_str = apr_strtok(split,":", &split2); + port_str = apr_strtok(NULL,":", &split2); + if (!port_str) { + port = 11211; /* default port */ + } + else { + port = atoi(port_str); + } + + /* Should Max Conns be (thread_limit / nservers) ? */ + rv = apr_memcache_server_create(p, + host_str, port, + 0, + 1, + thread_limit, + 600, + &st); + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "[gnutls_cache] Failed to Create Server: %s:%d", + host_str, port); + return rv; + } + + rv = apr_memcache_add_server(mc, st); + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "[gnutls_cache] Failed to Add Server: %s:%d", + host_str, port); + return rv; + } + + split = apr_strtok(NULL," ", &tok); + } + return rv; } -static int cache_store((void* baton, gnutls_datum_t key, gnutls_datum_t data) + +/* thanks mod_ssl */ +#define GNUTLS_SESSION_ID_STRING_LEN \ + ((GNUTLS_MAX_SESSION_ID + 1) * 2) +#define MC_TAG "mod_gnutls:" +#define MC_TAG_LEN \ + (sizeof(MC_TAG)) +#define STR_SESSION_LEN (GNUTLS_SESSION_ID_STRING_LEN + MC_TAG_LEN) + + +static char *gnutls_session_id2sz(unsigned char *id, int idlen, + char *str, int strsize) { - mc_set(struct memcache *mc, - key->data, key->size, - data->data, data->size, - 3600, 0); - return 0; + char *cp; + int n; + + cp = apr_cpystrn(str, MC_TAG, MC_TAG_LEN); + for (n = 0; n < idlen && n < GNUTLS_MAX_SESSION_ID; n++) { + apr_snprintf(cp, strsize - (cp-str), "%02X", id[n]); + cp += 2; + } + *cp = '\0'; + return str; } -static int cache_fetch(void* baton, gnutls_datum_t key) + +static int cache_store(void* baton, gnutls_datum_t key, gnutls_datum_t data) { + apr_status_t rv = APR_SUCCESS; mod_gnutls_handle_t *ctxt = baton; - return 0; + char buf[STR_SESSION_LEN]; + char* strkey = NULL; + apr_uint32_t timeout; + + strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); + if(!strkey) + return -1; + + timeout = 3600; + + rv = apr_memcache_set(mc, strkey, data.data, data.size, timeout, 0); + + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, + ctxt->c->base_server, + "[gnutls_cache] error setting key '%s' " + "with %d bytes of data", strkey, data.size); + return -1; + } + + return 0; +} + +static gnutls_datum_t cache_fetch(void* baton, gnutls_datum_t key) +{ + apr_status_t rv = APR_SUCCESS; + mod_gnutls_handle_t *ctxt = baton; + char buf[STR_SESSION_LEN]; + char* strkey = NULL; + char* value; + apr_size_t value_len; + gnutls_datum_t data = { NULL, 0 }; + + strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); + if(!strkey) { + return data; + } + + rv = apr_memcache_getp(mc, ctxt->c->pool, strkey, + &value, &value_len, NULL); + + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, + ctxt->c->base_server, + "[gnutls_cache] error fetching key '%s' ", + strkey); + + data.size = 0; + data.data = NULL; + return data; + } + + /* TODO: Eliminate this memcpy. ffs. gnutls-- */ + data.data = gnutls_malloc(value_len); + if (data.data == NULL) + return data; + + data.size = value_len; + memcpy(data.data, value, value_len); + + return data; } static int cache_delete(void* baton, gnutls_datum_t key) { + apr_status_t rv = APR_SUCCESS; mod_gnutls_handle_t *ctxt = baton; - return 0; + char buf[STR_SESSION_LEN]; + char* strkey = NULL; + + strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); + if(!strkey) + return -1; + + rv = apr_memcache_delete(mc, strkey, 0); + + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, + ctxt->c->base_server, + "[gnutls_cache] error deleting key '%s' ", + strkey); + return -1; + } + + return 0; } int mod_gnutls_cache_session_init(mod_gnutls_handle_t *ctxt) { - gnutls_db_set_cache_expiration - gnutls_db_set_retrieve_function(session, cache_fetch); - gnutls_db_set_remove_function(session, cache_delete); - gnutls_db_set_store_function(session, cache_store); - gnutls_db_set_ptr(session, NULL); - return 0; + gnutls_db_set_retrieve_function(ctxt->session, cache_fetch); + gnutls_db_set_remove_function(ctxt->session, cache_delete); + gnutls_db_set_store_function(ctxt->session, cache_store); + gnutls_db_set_ptr(ctxt->session, ctxt); + return 0; } -*/ diff --git a/src/gnutls_io.c b/src/gnutls_io.c index e1c84be..e92646b 100644 --- a/src/gnutls_io.c +++ b/src/gnutls_io.c @@ -273,10 +273,16 @@ static apr_status_t gnutls_io_input_read(mod_gnutls_handle_t * ctxt, } else { /* Some Other Error. Report it. Die. */ - ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc, - ctxt->c->base_server, - "GnuTLS: Error reading data. (%d) '%s'", rc, - gnutls_strerror(rc)); + if(gnutls_error_is_fatal(rc)) { + ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc, + ctxt->c->base_server, + "GnuTLS: Error reading data. (%d) '%s'", rc, + gnutls_strerror(rc)); + } + else if(*len > 0) { + ctxt->input_rc = APR_SUCCESS; + break; + } } if (ctxt->input_rc == APR_SUCCESS) { @@ -449,9 +455,10 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f, while (!APR_BRIGADE_EMPTY(bb)) { apr_bucket *bucket = APR_BRIGADE_FIRST(bb); - if (APR_BUCKET_IS_EOS(bucket)) { + if (APR_BUCKET_IS_EOS(bucket) || AP_BUCKET_IS_EOC(bucket)) { - /* gnutls_bye(ctxt->session, GNUTLS_SHUT_RDWR); */ + gnutls_bye(ctxt->session, GNUTLS_SHUT_WR); + gnutls_deinit(ctxt->session); if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { return status; @@ -464,17 +471,6 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f, return status; } break; - - } - else if (AP_BUCKET_IS_EOC(bucket)) { - - gnutls_bye(ctxt->session, GNUTLS_SHUT_WR); - gnutls_deinit(ctxt->session); - if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { - return status; - } - break; - } else { /* filter output */ diff --git a/src/mod_gnutls.c b/src/mod_gnutls.c index 3dfbd9a..833edc2 100644 --- a/src/mod_gnutls.c +++ b/src/mod_gnutls.c @@ -70,7 +70,7 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog, } - if(first_run) { +// if(first_run) { /* TODO: Should we regenerate these after X requests / X time ? */ gnutls_dh_params_init(&dh_params); gnutls_dh_params_generate2(dh_params, DH_BITS); @@ -78,7 +78,7 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog, gnutls_rsa_params_init(&rsa_params); gnutls_rsa_params_generate2(rsa_params, RSA_BITS); #endif - } +// } for (s = base_server; s; s = s->next) { sc = (mod_gnutls_srvconf_rec *) ap_get_module_config(s->module_config, @@ -105,6 +105,25 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog, return OK; } +static void mod_gnutls_hook_child_init(apr_pool_t *p, server_rec *s) +{ + apr_status_t rv = APR_SUCCESS; + mod_gnutls_srvconf_rec *sc = ap_get_module_config(s->module_config, + &gnutls_module); + + if(sc->cache_config != NULL) { + rv = mod_gnutls_cache_child_init(p, s, sc); + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, + "[GnuTLS] - Failed to run Cache Init"); + } + } + else { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, + "[GnuTLS] - No Cache Configured. Hint: GnuTLSCache"); + } +} + static const char *mod_gnutls_hook_http_method(const request_rec * r) { mod_gnutls_srvconf_rec *sc = @@ -172,6 +191,7 @@ static mod_gnutls_handle_t* create_gnutls_handle(apr_pool_t* pool, conn_rec * c) gnutls_dh_set_prime_bits(ctxt->session, DH_BITS); + mod_gnutls_cache_session_init(ctxt); return ctxt; } @@ -250,6 +270,21 @@ static const char *gnutls_set_key_file(cmd_parms * parms, void *dummy, return NULL; } +static const char *gnutls_set_cache(cmd_parms * parms, void *dummy, + const char *arg) +{ + const char* err; + mod_gnutls_srvconf_rec *sc = ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + if ((err = ap_check_cmd_context(parms, GLOBAL_ONLY))) { + return err; + } + + sc->cache_config = apr_pstrdup(parms->pool, arg); + return NULL; +} + static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy, const char *arg) { @@ -279,6 +314,10 @@ static const command_rec gnutls_cmds[] = { NULL, RSRC_CONF, "SSL Server Certificate file"), + AP_INIT_TAKE1("GnuTLSCache", gnutls_set_cache, + NULL, + RSRC_CONF, + "SSL Server Certificate file"), AP_INIT_TAKE1("GnuTLSEnable", gnutls_set_enabled, NULL, RSRC_CONF, "Whether this server has GnuTLS Enabled. Default: Off"), @@ -299,6 +338,8 @@ static void gnutls_hooks(apr_pool_t * p) APR_HOOK_MIDDLE); ap_hook_post_config(mod_gnutls_hook_post_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(mod_gnutls_hook_child_init, NULL, NULL, + APR_HOOK_MIDDLE); ap_hook_http_method(mod_gnutls_hook_http_method, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_default_port(mod_gnutls_hook_default_port, NULL, NULL, @@ -331,6 +372,7 @@ static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s) gnutls_anon_allocate_server_credentials(&sc->anoncred); sc->key_file = NULL; sc->cert_file = NULL; + sc->cache_config = NULL; i = 0; sc->ciphers[i++] = GNUTLS_CIPHER_AES_256_CBC; -- cgit v0.9.2