aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--include/memcache.h402
-rw-r--r--include/mod_gnutls.h12
-rw-r--r--src/Makefile.am2
-rw-r--r--src/gnutls_cache.c206
-rw-r--r--src/gnutls_io.c30
-rw-r--r--src/mod_gnutls.c46
7 files changed, 258 insertions, 444 deletions
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)
24 24
25CHECK_APR_MEMCACHE() 25CHECK_APR_MEMCACHE()
26 26
27MODULE_CFLAGS="${LIBGNUTLS_CFLAGS} ${APXS_CFLAGS} ${AP_INCLUDES} ${APR_INCLUDES} ${APU_INCLUDES}" 27MODULE_CFLAGS="${LIBGNUTLS_CFLAGS} ${APR_MEMCACHE_CFLAGS} ${APXS_CFLAGS} ${AP_INCLUDES} ${APR_INCLUDES} ${APU_INCLUDES}"
28MODULE_LIBS="${LIBGNUTLS_LIBS} ${APR_MEMCACHE_LIBS}" 28MODULE_LIBS="${APR_MEMCACHE_LIBS} ${LIBGNUTLS_LIBS}"
29 29
30AC_SUBST(MODULE_CFLAGS) 30AC_SUBST(MODULE_CFLAGS)
31AC_SUBST(MODULE_LIBS) 31AC_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 @@
1/* Copyright (c) 2004 Sean Chittenden <sean@chittenden.org>
2 *
3 * All rights reserved until such time as this code is released with
4 * an official license. Use of this code for commerical,
5 * non-commercial, and personal purposes is encouraged. Public forks
6 * of this code is permitted so long as the fork and its decendents
7 * use this copyright/license. Use of this software in programs
8 * released under the GPL programs is expressly prohibited by the
9 * author (ie, BSD, closed source, or artistic license is okay, but
10 * GPL is not). */
11
12#ifndef MEMCACHE_H
13#define MEMCACHE_H
14
15#include <netdb.h>
16#include <sys/queue.h>
17#include <sys/types.h>
18#include <sys/time.h>
19#include <unistd.h>
20
21#ifdef __cplusplus
22extern "C" {
23#endif
24
25/* Our initial read(2) buffer has to be long enough to read the
26 * first line of the response. ie:
27 *
28 * "VALUE #{'k' * 250} #{2 ** 15} #{2 ** 32}\r\n.length => 275
29 *
30 * However, since we want to avoid the number of system calls
31 * necessary, include trailing part of the protocol in our estimate:
32 *
33 * "\r\nEND\r\n".length => 7
34 *
35 * Which yields a manditory limit of 282 bytes for a successful
36 * response. If we wish to try and get lucky with our first read(2)
37 * call and be able to read(2) in small values without making a second
38 * read(2) call, pad this number with a sufficiently large byte value.
39 * If most of your keys are 512B, then a GET_INIT_BUF_SIZE of 794
40 * would be prudent (512 + 282).
41 *
42 * The default value of 1024 means that values less than 724 bytes
43 * will always be read(2) via the first read(2) call. Increasing this
44 * value to large values is not beneficial. If a second read(2) call
45 * is necessary, the read(2) will be made with a sufficiently large
46 * buffer already allocated. */
47#define GET_INIT_BUF_SIZE ((size_t)1024)
48
49/* Enables extra protocol checking. It's not strickly necesary if the
50 * server's sending the right data. This should be on by default,
51 * but, for those that don't want every check done to make sure things
52 * are right, undef this. 99% of the universe should leave this as
53 * is. If you think you're than 1% who doesn't need it, you're on
54 * crack and should definately leave PEDANTIC on. %*/
55#define PEDANTIC
56
57#define MAX_KEY_LEN 250
58
59#define HAVE_SELECT 1
60
61#define USE_CRC32_HASH 1
62/* #define USE_ELF_HASH 1 */
63/* #define USE_PERL_HASH 1 */
64
65/* Various values for _flags */
66#define MC_RES_FREE_ON_DELETE 0x01
67#define MC_RES_NO_FREE_ON_DELETE 0x02
68#define MC_RES_FOUND 0x04
69struct memcache_res {
70 const char *key; /* key */
71 size_t len; /* length of key */
72 u_int32_t hash; /* hash of the key */
73 void *val; /* the value */
74 size_t bytes; /* length of val */
75
76 /* If size is zero (default), the memory for val is automatically
77 * allocated using mcMalloc(3). If size is zero, _flags has its
78 * MC_RES_FREE_ON_DELETE bit set.
79 *
80 * If size is non-zero, libmemcache(3) assumes that the caller has
81 * set val to an available portion of memory that is size bytes
82 * long. libmemcache(3) will only populate val with as many bytes
83 * as are specified by size (ie, it will trim the value in order to
84 * fit into val). If size is non-zero, _flags has its
85 * MC_RES_NO_FREE_ON_DELETE bit set by default. */
86 size_t size;
87 TAILQ_ENTRY(memcache_res) entries;
88
89 /* Note: this flags is very different than _flags. */
90 u_int16_t flags;
91
92 /* If _flags has 0x01 set, val will be free(3)'ed on when this
93 * struct is cleaned up via mc_res_free() or the request is cleaned
94 * up via mc_req_free().
95 *
96 * If _flags has is 0x02 set, val will not be free(3)'ed when this
97 * response or its parent request are cleaned up.
98 *
99 * Note: Use mc_res_free_on_delete() to set the "free on delete"
100 * bits. */
101 char _flags;
102};
103
104struct memcache_req {
105 TAILQ_HEAD(memcache_res_list, memcache_res) query;
106 u_int16_t num_keys;
107};
108
109struct memcache_server {
110 /* The hostname of the server. */
111 char *hostname;
112
113 /* Port number of the host we're connecting to. */
114 char *port;
115
116 /* The file descriptor for this server */
117 int fd;
118
119 /* The file descriptor flags */
120 int flags;
121
122 /* The timeout for this server */
123 struct timeval tv;
124
125 /* Is this particular server active or not?
126 *
127 * 'd' == Down Last request was unsuccessful
128 * 'n' == No host The hostname doesn't exist
129 * 't' == Try Haven't connected to it yet, will attempt
130 * 'u' == Up Has been connected to successfully
131 */
132 char active;
133
134 /* A cached copy of the looked up host. */
135 struct addrinfo *hostinfo;
136
137 /* The number of addresses in the cached copy. If there is more
138 * than one per DNS entry (discouraged), we establish a connection
139 * to them all. */
140 u_int32_t num_addrs;
141
142#ifdef HAVE_SELECT
143 /* Reduces the amount of user time required when reading data. */
144 fd_set fds;
145 struct timeval select_tv;
146#endif
147
148 /* Misc list bits */
149 TAILQ_ENTRY(memcache_server) entries;
150};
151
152
153struct memcache_server_stats {
154 pid_t pid;
155 time_t uptime;
156 time_t time;
157 char *version;
158 struct timeval rusage_user;
159 struct timeval rusage_system;
160 u_int32_t curr_items;
161 u_int64_t total_items;
162 u_int64_t bytes;
163 u_int32_t curr_connections;
164 u_int64_t total_connections;
165 u_int32_t connection_structures;
166 u_int64_t cmd_get;
167 u_int64_t cmd_refresh;
168 u_int64_t cmd_set;
169 u_int64_t get_hits;
170 u_int64_t get_misses;
171 u_int64_t refresh_hits;
172 u_int64_t refresh_misses;
173 u_int64_t bytes_read;
174 u_int64_t bytes_written;
175 u_int64_t limit_maxbytes;
176};
177
178
179struct memcache {
180 /* The default timeout for all servers */
181 struct timeval tv;
182
183 /* The default read(2) size when reading a response. */
184 size_t read_size;
185
186 /* The complete list of servers */
187 TAILQ_HEAD(memcache_server_list, memcache_server) server_list;
188
189 /* A buffer for data */
190 char *buf;
191
192 /* A cursor for where we are in the buffer */
193 char *cur;
194
195 /* A pointer to where data should be appended with future read(2)
196 * calls. */
197 char *read_cur;
198
199 /* A pointer to the start of the current line in the buffer. */
200 char *start;
201
202 /* The allocated size of the buffer */
203 size_t size;
204
205 /* The number of servers in live_servers */
206 u_int32_t num_live_servers;
207
208 /* An array of usable memcache_servers */
209 struct memcache_server **live_servers;
210};
211
212
213/* Adds a given key to the cache */
214int mc_add(struct memcache *mc,
215 const char *key, const size_t key_len,
216 const void *val, const size_t bytes,
217 const time_t expire, const u_int16_t flags);
218
219/* Gets the value from memcache and allocates the data for the caller.
220 * It is the caller's responsibility to free the returned value.
221 * mc_get() is the preferred interface, however. */
222void *mc_aget(struct memcache *mc, const char *key, const size_t len);
223
224/* Gets the value from memcache and allocates the data for the caller.
225 * It is the caller's responsibility to free the returned value.
226 * mc_refresh() is the preferred interface, however. */
227void *mc_arefresh(struct memcache *mc, const char *key, const size_t len);
228
229/* Disconnects from a given server and marks it as down. */
230void mc_deactivate_server(struct memcache *mc, struct memcache_server *ms);
231
232/* Decrements a given key */
233u_int32_t mc_decr(struct memcache *mc, const char *key, const size_t key_len, const u_int32_t val);
234
235/* Deletes a given key */
236int mc_delete(struct memcache *mc, const char *key, const size_t key_len, const time_t hold);
237
238/* When given a hash value, this function returns the appropriate
239 * server to connect to in order to find the key. */
240struct memcache_server *mc_find_server(struct memcache *mc, const u_int32_t hash);
241
242/* Flushes all keys */
243int mc_flush_all(struct memcache *mc, const char *key, const size_t key_len);
244
245/* cleans up a memcache object. */
246void mc_free(struct memcache *mc);
247
248/* Tells the response object to free the allocated memory when it gets
249 * cleaned up or to let the caller manage the memory. */
250void mc_res_free_on_delete(struct memcache_res *res, int free_on_delete);
251
252/* mc_get() is the preferred method of accessing memcache. It
253 * accepts multiple keys and lets a user (should they so choose)
254 * perform memory caching to reduce the number of mcMalloc(3) calls
255 * mades. */
256void mc_get(struct memcache *mc, struct memcache_req *req);
257
258/* Generates a hash value from a given key */
259u_int32_t mc_hash_key(const char *key, const size_t len);
260
261/* Increments a given key */
262u_int32_t mc_incr(struct memcache *mc, const char *key, const size_t key_len, const u_int32_t val);
263
264/* Allocates a new memcache object */
265struct memcache *mc_new(void);
266
267/* mc_refresh() is the preferred method of accessing memcache. It
268 * accepts multiple keys and lets a user (should they so choose)
269 * perform memory caching to reduce the number of mcMalloc(3) calls
270 * mades. mc_refresh() differs from mc_get() in that mc_refresh
271 * updates the expiration time to be now + whatever the expiration for
272 * the item was set to. Sessions should use this as a way of noting
273 * sessions expiring. */
274void mc_refresh(struct memcache *mc, struct memcache_req *req);
275
276/* Replaces a given key to the cache */
277int mc_replace(struct memcache *mc,
278 const char *key, const size_t key_len,
279 const void *val, const size_t bytes,
280 const time_t expire, const u_int16_t flags);
281
282/* Adds a key to a given request */
283struct memcache_res *mc_req_add(struct memcache_req *req, const char *key, size_t len);
284
285/* Cleans up a given request and its subsequent responses. If _flags
286 * has the MC_RES_FREE_ON_DELETE bit set (default), it will clean up
287 * the value too. If _flags has MC_RES_NO_FREE_ON_DELETE set,
288 * however, it is the caller's responsibility to free the value. To
289 * prevent double free(3) errors, if a value is free(3)'ed before
290 * mc_req_free() is called, set val to NULL. */
291void mc_req_free(struct memcache_req *req);
292
293/* Allocates a new memcache request object. */
294struct memcache_req *mc_req_new(void);
295
296/* Cleans up an individual response object. Normally this is not
297 * necessary as a call to mc_req_free() will clean up its response
298 * objects. */
299void mc_res_free(struct memcache_req *req, struct memcache_res *res);
300
301/* Adds a server to the list of available servers. By default,
302 * servers are assumed to be available. Return codes:
303 *
304 * 0: success
305 * -1: Unable to allocate a new server instance
306 * -2: Unable to strdup hostname
307 * -3: Unable to strdup port
308 * -4: Unable to Unable to resolve the host, server deactivated, but added to list
309 * -5: Unable to realloc(3) the server list, server list unchanged */
310int mc_server_add(struct memcache *mc, const char *hostname, const char *port);
311int mc_server_add2(struct memcache *mc,
312 const char *hostname, const size_t hostname_len,
313 const char *port, const size_t port_len);
314
315/* Cleans up a given stat's object */
316void mc_server_stats_free(struct memcache_server_stats *s);
317
318/* Gets a stats object from the given server. It is the caller's
319 * responsibility to cleanup the resulting object via
320 * mc_server_stats_free(). */
321struct memcache_server_stats *mc_server_stats(struct memcache *mc, struct memcache_server *ms);
322
323/* Sets a given key */
324int mc_set(struct memcache *mc,
325 const char *key, const size_t key_len,
326 const void *val, const size_t bytes,
327 const time_t expire, const u_int16_t flags);
328
329/* Creates a stats object for all available servers and returns the
330 * cumulative stats. Per host-specific data is generally the same as
331 * the last server querried. */
332struct memcache_server_stats *mc_stats(struct memcache *mc);
333
334/* Sets the default timeout for new servers. */
335void mc_timeout(struct memcache *mc, const int sec, const int usec);
336
337
338
339/* BEGIN memory management API functions */
340
341/* The memcache API allows callers to provide their own memory
342 * allocation routines to aid in embedability with existing programs,
343 * libraries, programming languages, and environments that have their
344 * own memory handling routines. This interface was inspired by
345 * libxml. */
346typedef void (*mcFreeFunc)(void *mem);
347typedef void *(*mcMallocFunc)(const size_t size);
348typedef void *(*mcReallocFunc)(void *mem, const size_t size);
349typedef char *(*mcStrdupFunc)(const char *str);
350
351extern mcFreeFunc mcFree;
352extern mcMallocFunc mcMalloc;
353extern mcMallocFunc mcMallocAtomic;
354extern mcReallocFunc mcRealloc;
355extern mcStrdupFunc mcStrdup;
356
357int mcMemSetup(mcFreeFunc freeFunc, mcMallocFunc mallocFunc,
358 mcReallocFunc reallocFunc, mcStrdupFunc strdupFunc);
359int mcMemGet(mcFreeFunc *freeFunc, mcMallocFunc *mallocFunc,
360 mcReallocFunc *reallocFunc, mcStrdupFunc *strdupFunc);
361/* END memory management API functions */
362
363
364/* APIs that should be implemented: */
365
366/* Resets all hosts that are down to try */
367void mc_server_reset_all_active(struct memcache *mc);
368
369/* Resets a given host back to a try state */
370void mc_server_reset_active(struct memcache *mc, const char *hostname, const int port);
371
372/* Resets all dns entries */
373void mc_server_reset_all_dns(struct memcache *mc);
374
375/* Resets only one host's DNS cache */
376void mc_server_reset_dns(struct memcache *mc, const char *hostname, const int port);
377
378/* Disconnects from all memcache servers */
379void mc_server_disconnect_all(struct memcache *mc);
380
381/* Disconnects from one memcache server */
382void mc_server_disconnect(struct memcache *mc, const char *hostname, const int port);
383
384#ifdef TCP_NODELAY
385/* Enable/disable TCP_NODELAY */
386void mc_nodelay_enable(struct memcache *mc, const int enable);
387
388/* Enable/disable TCP_NODELAY for a given server */
389void mc_server_nodelay_enable(struct memcache_server *ms, const int enable);
390#endif
391
392/* Set the timeout on a per server basis */
393void mc_server_timeout(struct memcache_server *ms, const int sec, const int usec);
394
395/* Set the number of seconds you're willing to wait in total for a
396 * response. ??? */
397
398#ifdef __cplusplus
399}
400#endif
401
402#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 @@
29#include "apr_strings.h" 29#include "apr_strings.h"
30#include "apr_tables.h" 30#include "apr_tables.h"
31 31
32#include "apr_memcache.h"
33
32#include <gcrypt.h> 34#include <gcrypt.h>
33#include <gnutls/gnutls.h> 35#include <gnutls/gnutls.h>
34 36
@@ -68,6 +70,7 @@ typedef struct
68 int macs[16]; 70 int macs[16];
69 int protocol[16]; 71 int protocol[16];
70 int compression[16]; 72 int compression[16];
73 const char* cache_config;
71} mod_gnutls_srvconf_rec; 74} mod_gnutls_srvconf_rec;
72 75
73typedef struct { 76typedef struct {
@@ -155,4 +158,13 @@ ssize_t mod_gnutls_transport_write(gnutls_transport_ptr_t ptr,
155 const void *buffer, size_t len); 158 const void *buffer, size_t len);
156 159
157 160
161/**
162 * Init the Cache inside each Process
163 */
164int mod_gnutls_cache_child_init(apr_pool_t *p, server_rec *s,
165 mod_gnutls_srvconf_rec *sc);
166/**
167 * Setup the Session Caching
168 */
169int mod_gnutls_cache_session_init(mod_gnutls_handle_t *ctxt);
158#endif /* __mod_gnutls_h_inc */ 170#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 *~
2 2
3libmod_gnutls_la_SOURCES = mod_gnutls.c gnutls_io.c gnutls_cache.c 3libmod_gnutls_la_SOURCES = mod_gnutls.c gnutls_io.c gnutls_cache.c
4libmod_gnutls_la_CFLAGS = -Wall ${MODULE_CFLAGS} 4libmod_gnutls_la_CFLAGS = -Wall ${MODULE_CFLAGS}
5libmod_gnutls_la_LDFLAGS = ${MODULE_LIBS} 5libmod_gnutls_la_LDFLAGS = -rpath ${AP_LIBEXECDIR} -module -avoid-version ${MODULE_LIBS}
6 6
7lib_LTLIBRARIES = libmod_gnutls.la 7lib_LTLIBRARIES = libmod_gnutls.la
8 8
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 @@
16 */ 16 */
17 17
18#include "mod_gnutls.h" 18#include "mod_gnutls.h"
19#include "ap_mpm.h"
19 20
20/** 21/**
21 * GnuTLS Session Cache using libmemcached 22 * GnuTLS Session Cache using libmemcached
22 * 23 *
23 */ 24 */
24/*
25#include "memcache.h"
26 25
27int mod_gnutls_cache_init() 26/* The underlying apr_memcache system is thread safe... woohoo */
27static apr_memcache_t* mc;
28
29int mod_gnutls_cache_child_init(apr_pool_t *p, server_rec *s,
30 mod_gnutls_srvconf_rec *sc)
28{ 31{
29 return 0; 32 apr_status_t rv = APR_SUCCESS;
33 int thread_limit = 0;
34 int nservers = 0;
35 char* cache_config;
36 char* split;
37 char* tok;
38
39 ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
40
41 /* Find all the servers in the first run to get a total count */
42 cache_config = apr_pstrdup(p, sc->cache_config);
43 split = apr_strtok(cache_config, " ", &tok);
44 while (split) {
45 nservers++;
46 split = apr_strtok(NULL," ", &tok);
47 }
48
49 rv = apr_memcache_create(p, nservers, 0, &mc);
50 if (rv != APR_SUCCESS) {
51 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
52 "[gnutls_cache] Failed to create Memcache Object of '%d' size.",
53 nservers);
54 return rv;
55 }
56
57 /* Now add each server to the memcache */
58 cache_config = apr_pstrdup(p, sc->cache_config);
59 split = apr_strtok(cache_config, " ", &tok);
60 while (split) {
61 apr_memcache_server_t* st;
62 char* split2;
63 char* host_str;
64 char* port_str;
65 int port;
66
67 host_str = apr_strtok(split,":", &split2);
68 port_str = apr_strtok(NULL,":", &split2);
69 if (!port_str) {
70 port = 11211; /* default port */
71 }
72 else {
73 port = atoi(port_str);
74 }
75
76 /* Should Max Conns be (thread_limit / nservers) ? */
77 rv = apr_memcache_server_create(p,
78 host_str, port,
79 0,
80 1,
81 thread_limit,
82 600,
83 &st);
84 if(rv != APR_SUCCESS) {
85 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
86 "[gnutls_cache] Failed to Create Server: %s:%d",
87 host_str, port);
88 return rv;
89 }
90
91 rv = apr_memcache_add_server(mc, st);
92 if(rv != APR_SUCCESS) {
93 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
94 "[gnutls_cache] Failed to Add Server: %s:%d",
95 host_str, port);
96 return rv;
97 }
98
99 split = apr_strtok(NULL," ", &tok);
100 }
101 return rv;
30} 102}
31static int cache_store((void* baton, gnutls_datum_t key, gnutls_datum_t data) 103
104/* thanks mod_ssl */
105#define GNUTLS_SESSION_ID_STRING_LEN \
106 ((GNUTLS_MAX_SESSION_ID + 1) * 2)
107#define MC_TAG "mod_gnutls:"
108#define MC_TAG_LEN \
109 (sizeof(MC_TAG))
110#define STR_SESSION_LEN (GNUTLS_SESSION_ID_STRING_LEN + MC_TAG_LEN)
111
112
113static char *gnutls_session_id2sz(unsigned char *id, int idlen,
114 char *str, int strsize)
32{ 115{
33 mc_set(struct memcache *mc, 116 char *cp;
34 key->data, key->size, 117 int n;
35 data->data, data->size, 118
36 3600, 0); 119 cp = apr_cpystrn(str, MC_TAG, MC_TAG_LEN);
37 return 0; 120 for (n = 0; n < idlen && n < GNUTLS_MAX_SESSION_ID; n++) {
121 apr_snprintf(cp, strsize - (cp-str), "%02X", id[n]);
122 cp += 2;
123 }
124 *cp = '\0';
125 return str;
38} 126}
39 127
40static int cache_fetch(void* baton, gnutls_datum_t key) 128
129static int cache_store(void* baton, gnutls_datum_t key, gnutls_datum_t data)
41{ 130{
131 apr_status_t rv = APR_SUCCESS;
42 mod_gnutls_handle_t *ctxt = baton; 132 mod_gnutls_handle_t *ctxt = baton;
43 return 0; 133 char buf[STR_SESSION_LEN];
134 char* strkey = NULL;
135 apr_uint32_t timeout;
136
137 strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf));
138 if(!strkey)
139 return -1;
140
141 timeout = 3600;
142
143 rv = apr_memcache_set(mc, strkey, data.data, data.size, timeout, 0);
144
145 if(rv != APR_SUCCESS) {
146 ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
147 ctxt->c->base_server,
148 "[gnutls_cache] error setting key '%s' "
149 "with %d bytes of data", strkey, data.size);
150 return -1;
151 }
152
153 return 0;
154}
155
156static gnutls_datum_t cache_fetch(void* baton, gnutls_datum_t key)
157{
158 apr_status_t rv = APR_SUCCESS;
159 mod_gnutls_handle_t *ctxt = baton;
160 char buf[STR_SESSION_LEN];
161 char* strkey = NULL;
162 char* value;
163 apr_size_t value_len;
164 gnutls_datum_t data = { NULL, 0 };
165
166 strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf));
167 if(!strkey) {
168 return data;
169 }
170
171 rv = apr_memcache_getp(mc, ctxt->c->pool, strkey,
172 &value, &value_len, NULL);
173
174 if(rv != APR_SUCCESS) {
175 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
176 ctxt->c->base_server,
177 "[gnutls_cache] error fetching key '%s' ",
178 strkey);
179
180 data.size = 0;
181 data.data = NULL;
182 return data;
183 }
184
185 /* TODO: Eliminate this memcpy. ffs. gnutls-- */
186 data.data = gnutls_malloc(value_len);
187 if (data.data == NULL)
188 return data;
189
190 data.size = value_len;
191 memcpy(data.data, value, value_len);
192
193 return data;
44} 194}
45 195
46static int cache_delete(void* baton, gnutls_datum_t key) 196static int cache_delete(void* baton, gnutls_datum_t key)
47{ 197{
198 apr_status_t rv = APR_SUCCESS;
48 mod_gnutls_handle_t *ctxt = baton; 199 mod_gnutls_handle_t *ctxt = baton;
49 return 0; 200 char buf[STR_SESSION_LEN];
201 char* strkey = NULL;
202
203 strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf));
204 if(!strkey)
205 return -1;
206
207 rv = apr_memcache_delete(mc, strkey, 0);
208
209 if(rv != APR_SUCCESS) {
210 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
211 ctxt->c->base_server,
212 "[gnutls_cache] error deleting key '%s' ",
213 strkey);
214 return -1;
215 }
216
217 return 0;
50} 218}
51 219
52int mod_gnutls_cache_session_init(mod_gnutls_handle_t *ctxt) 220int mod_gnutls_cache_session_init(mod_gnutls_handle_t *ctxt)
53{ 221{
54 gnutls_db_set_cache_expiration 222 gnutls_db_set_retrieve_function(ctxt->session, cache_fetch);
55 gnutls_db_set_retrieve_function(session, cache_fetch); 223 gnutls_db_set_remove_function(ctxt->session, cache_delete);
56 gnutls_db_set_remove_function(session, cache_delete); 224 gnutls_db_set_store_function(ctxt->session, cache_store);
57 gnutls_db_set_store_function(session, cache_store); 225 gnutls_db_set_ptr(ctxt->session, ctxt);
58 gnutls_db_set_ptr(session, NULL); 226 return 0;
59 return 0;
60} 227}
61*/
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,
273 } 273 }
274 else { 274 else {
275 /* Some Other Error. Report it. Die. */ 275 /* Some Other Error. Report it. Die. */
276 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc, 276 if(gnutls_error_is_fatal(rc)) {
277 ctxt->c->base_server, 277 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc,
278 "GnuTLS: Error reading data. (%d) '%s'", rc, 278 ctxt->c->base_server,
279 gnutls_strerror(rc)); 279 "GnuTLS: Error reading data. (%d) '%s'", rc,
280 gnutls_strerror(rc));
281 }
282 else if(*len > 0) {
283 ctxt->input_rc = APR_SUCCESS;
284 break;
285 }
280 } 286 }
281 287
282 if (ctxt->input_rc == APR_SUCCESS) { 288 if (ctxt->input_rc == APR_SUCCESS) {
@@ -449,9 +455,10 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f,
449 455
450 while (!APR_BRIGADE_EMPTY(bb)) { 456 while (!APR_BRIGADE_EMPTY(bb)) {
451 apr_bucket *bucket = APR_BRIGADE_FIRST(bb); 457 apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
452 if (APR_BUCKET_IS_EOS(bucket)) { 458 if (APR_BUCKET_IS_EOS(bucket) || AP_BUCKET_IS_EOC(bucket)) {
453 459
454 /* gnutls_bye(ctxt->session, GNUTLS_SHUT_RDWR); */ 460 gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
461 gnutls_deinit(ctxt->session);
455 462
456 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { 463 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
457 return status; 464 return status;
@@ -464,17 +471,6 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f,
464 return status; 471 return status;
465 } 472 }
466 break; 473 break;
467
468 }
469 else if (AP_BUCKET_IS_EOC(bucket)) {
470
471 gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
472 gnutls_deinit(ctxt->session);
473 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
474 return status;
475 }
476 break;
477
478 } 474 }
479 else { 475 else {
480 /* filter output */ 476 /* 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,
70 } 70 }
71 71
72 72
73 if(first_run) { 73// if(first_run) {
74 /* TODO: Should we regenerate these after X requests / X time ? */ 74 /* TODO: Should we regenerate these after X requests / X time ? */
75 gnutls_dh_params_init(&dh_params); 75 gnutls_dh_params_init(&dh_params);
76 gnutls_dh_params_generate2(dh_params, DH_BITS); 76 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,
78 gnutls_rsa_params_init(&rsa_params); 78 gnutls_rsa_params_init(&rsa_params);
79 gnutls_rsa_params_generate2(rsa_params, RSA_BITS); 79 gnutls_rsa_params_generate2(rsa_params, RSA_BITS);
80#endif 80#endif
81 } 81// }
82 82
83 for (s = base_server; s; s = s->next) { 83 for (s = base_server; s; s = s->next) {
84 sc = (mod_gnutls_srvconf_rec *) ap_get_module_config(s->module_config, 84 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,
105 return OK; 105 return OK;
106} 106}
107 107
108static void mod_gnutls_hook_child_init(apr_pool_t *p, server_rec *s)
109{
110 apr_status_t rv = APR_SUCCESS;
111 mod_gnutls_srvconf_rec *sc = ap_get_module_config(s->module_config,
112 &gnutls_module);
113
114 if(sc->cache_config != NULL) {
115 rv = mod_gnutls_cache_child_init(p, s, sc);
116 if(rv != APR_SUCCESS) {
117 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
118 "[GnuTLS] - Failed to run Cache Init");
119 }
120 }
121 else {
122 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
123 "[GnuTLS] - No Cache Configured. Hint: GnuTLSCache");
124 }
125}
126
108static const char *mod_gnutls_hook_http_method(const request_rec * r) 127static const char *mod_gnutls_hook_http_method(const request_rec * r)
109{ 128{
110 mod_gnutls_srvconf_rec *sc = 129 mod_gnutls_srvconf_rec *sc =
@@ -172,6 +191,7 @@ static mod_gnutls_handle_t* create_gnutls_handle(apr_pool_t* pool, conn_rec * c)
172 191
173 gnutls_dh_set_prime_bits(ctxt->session, DH_BITS); 192 gnutls_dh_set_prime_bits(ctxt->session, DH_BITS);
174 193
194 mod_gnutls_cache_session_init(ctxt);
175 return ctxt; 195 return ctxt;
176} 196}
177 197
@@ -250,6 +270,21 @@ static const char *gnutls_set_key_file(cmd_parms * parms, void *dummy,
250 return NULL; 270 return NULL;
251} 271}
252 272
273static const char *gnutls_set_cache(cmd_parms * parms, void *dummy,
274 const char *arg)
275{
276 const char* err;
277 mod_gnutls_srvconf_rec *sc = ap_get_module_config(parms->server->
278 module_config,
279 &gnutls_module);
280 if ((err = ap_check_cmd_context(parms, GLOBAL_ONLY))) {
281 return err;
282 }
283
284 sc->cache_config = apr_pstrdup(parms->pool, arg);
285 return NULL;
286}
287
253static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy, 288static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy,
254 const char *arg) 289 const char *arg)
255{ 290{
@@ -279,6 +314,10 @@ static const command_rec gnutls_cmds[] = {
279 NULL, 314 NULL,
280 RSRC_CONF, 315 RSRC_CONF,
281 "SSL Server Certificate file"), 316 "SSL Server Certificate file"),
317 AP_INIT_TAKE1("GnuTLSCache", gnutls_set_cache,
318 NULL,
319 RSRC_CONF,
320 "SSL Server Certificate file"),
282 AP_INIT_TAKE1("GnuTLSEnable", gnutls_set_enabled, 321 AP_INIT_TAKE1("GnuTLSEnable", gnutls_set_enabled,
283 NULL, RSRC_CONF, 322 NULL, RSRC_CONF,
284 "Whether this server has GnuTLS Enabled. Default: Off"), 323 "Whether this server has GnuTLS Enabled. Default: Off"),
@@ -299,6 +338,8 @@ static void gnutls_hooks(apr_pool_t * p)
299 APR_HOOK_MIDDLE); 338 APR_HOOK_MIDDLE);
300 ap_hook_post_config(mod_gnutls_hook_post_config, NULL, NULL, 339 ap_hook_post_config(mod_gnutls_hook_post_config, NULL, NULL,
301 APR_HOOK_MIDDLE); 340 APR_HOOK_MIDDLE);
341 ap_hook_child_init(mod_gnutls_hook_child_init, NULL, NULL,
342 APR_HOOK_MIDDLE);
302 ap_hook_http_method(mod_gnutls_hook_http_method, NULL, NULL, 343 ap_hook_http_method(mod_gnutls_hook_http_method, NULL, NULL,
303 APR_HOOK_MIDDLE); 344 APR_HOOK_MIDDLE);
304 ap_hook_default_port(mod_gnutls_hook_default_port, NULL, NULL, 345 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)
331 gnutls_anon_allocate_server_credentials(&sc->anoncred); 372 gnutls_anon_allocate_server_credentials(&sc->anoncred);
332 sc->key_file = NULL; 373 sc->key_file = NULL;
333 sc->cert_file = NULL; 374 sc->cert_file = NULL;
375 sc->cache_config = NULL;
334 376
335 i = 0; 377 i = 0;
336 sc->ciphers[i++] = GNUTLS_CIPHER_AES_256_CBC; 378 sc->ciphers[i++] = GNUTLS_CIPHER_AES_256_CBC;