1 /*-
2  * SSLsplit - transparent SSL/TLS interception
3  * https://www.roe.ch/SSLsplit
4  *
5  * Copyright (c) 2009-2019, Daniel Roethlisberger <daniel@roe.ch>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "cache.h"
30 
31 #include "log.h"
32 #include "khash.h"
33 
34 #include <pthread.h>
35 
36 /*
37  * Generic, thread-safe cache.
38  */
39 
40 /*
41  * Create a new cache based on the initializer callback init_cb.
42  */
43 cache_t *
cache_new(cache_init_cb_t init_cb)44 cache_new(cache_init_cb_t init_cb)
45 {
46 	cache_t *cache;
47 
48 	if (!(cache = malloc(sizeof(cache_t))))
49 		return NULL;
50 
51 	if (pthread_mutex_init(&cache->mutex, NULL)) {
52 		free(cache);
53 		return NULL;
54 	}
55 
56 	init_cb(cache);
57 	return cache;
58 }
59 
60 /*
61  * Reinitialize cache after fork().  Returns 0 on success, -1 on failure.
62  */
63 int
cache_reinit(cache_t * cache)64 cache_reinit(cache_t *cache)
65 {
66 	return pthread_mutex_init(&cache->mutex, NULL) ? -1 : 0;
67 }
68 
69 /*
70  * Free a cache and all associated resources.
71  * This function is not thread-safe.
72  */
73 void
cache_free(cache_t * cache)74 cache_free(cache_t *cache)
75 {
76 	khiter_t it;
77 
78 	for (it = cache->begin_cb(); it != cache->end_cb(); it++) {
79 		if (cache->exist_cb(it)) {
80 			cache->free_key_cb(cache->get_key_cb(it));
81 			cache->free_val_cb(cache->get_val_cb(it));
82 		}
83 	}
84 	cache->fini_cb();
85 	pthread_mutex_destroy(&cache->mutex);
86 	free(cache);
87 }
88 
89 void
cache_gc(cache_t * cache)90 cache_gc(cache_t *cache)
91 {
92 	khiter_t it;
93 	cache_val_t val;
94 
95 	pthread_mutex_lock(&cache->mutex);
96 	for (it = cache->begin_cb(); it != cache->end_cb(); it++) {
97 		if (cache->exist_cb(it)) {
98 			val = cache->get_val_cb(it);
99 			if (!cache->unpackverify_val_cb(val, 0)) {
100 				cache->free_val_cb(val);
101 				cache->free_key_cb(cache->get_key_cb(it));
102 				cache->del_cb(it);
103 			}
104 		}
105 	}
106 	pthread_mutex_unlock(&cache->mutex);
107 }
108 
109 cache_val_t
cache_get(cache_t * cache,cache_key_t key)110 cache_get(cache_t *cache, cache_key_t key)
111 {
112 	cache_val_t rval = NULL;
113 	khiter_t it;
114 
115 	if (!key)
116 		return NULL;
117 
118 	pthread_mutex_lock(&cache->mutex);
119 	it = cache->get_cb(key);
120 	if (it != cache->end_cb()) {
121 		cache_val_t val;
122 		val = cache->get_val_cb(it);
123 		if (!(rval = cache->unpackverify_val_cb(val, 1))) {
124 			cache->free_val_cb(val);
125 			cache->free_key_cb(cache->get_key_cb(it));
126 			cache->del_cb(it);
127 		}
128 	}
129 	cache->free_key_cb(key);
130 	pthread_mutex_unlock(&cache->mutex);
131 	return rval;
132 }
133 
134 void
cache_set(cache_t * cache,cache_key_t key,cache_val_t val)135 cache_set(cache_t *cache, cache_key_t key, cache_val_t val)
136 {
137 	khiter_t it;
138 	int ret;
139 
140 	if (!key || !val)
141 		return;
142 
143 	pthread_mutex_lock(&cache->mutex);
144 	it = cache->put_cb(key, &ret);
145 	if (!ret) {
146 		cache->free_key_cb(key);
147 		cache->free_val_cb(cache->get_val_cb(it));
148 	}
149 	cache->set_val_cb(it, val);
150 	pthread_mutex_unlock(&cache->mutex);
151 }
152 
153 void
cache_del(cache_t * cache,cache_key_t key)154 cache_del(cache_t *cache, cache_key_t key)
155 {
156 	khiter_t it;
157 
158 	pthread_mutex_lock(&cache->mutex);
159 	it = cache->get_cb(key);
160 	if (it != cache->end_cb()) {
161 		cache->free_val_cb(cache->get_val_cb(it));
162 		cache->free_key_cb(cache->get_key_cb(it));
163 		cache->del_cb(it);
164 	}
165 	cache->free_key_cb(key);
166 	pthread_mutex_unlock(&cache->mutex);
167 }
168 
169 /* vim: set noet ft=c: */
170