Moved mutex.c to a separate folder in order to cleanly wrap other threading primitive...
[strongswan.git] / src / libstrongswan / credentials / keys / key_encoding.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "key_encoding.h"
17
18 #include <stdint.h>
19
20 #include <utils/linked_list.h>
21 #include <utils/hashtable.h>
22 #include <threading.h>
23
24 typedef struct private_key_encoding_t private_key_encoding_t;
25
26 /**
27 * Private data of an key_encoding_t object.
28 */
29 struct private_key_encoding_t {
30
31 /**
32 * Public key_encoding_t interface.
33 */
34 key_encoding_t public;
35
36 /**
37 * cached encodings, a table for each encoding_type_t, containing chunk_t*
38 */
39 hashtable_t *cache[KEY_ENCODING_MAX];
40
41 /**
42 * Registered encoding fuctions, key_encoder_t
43 */
44 linked_list_t *encoders;
45
46 /**
47 * lock to access cache/encoders
48 */
49 rwlock_t *lock;
50 };
51
52 /**
53 * See header.
54 */
55 bool key_encoding_args(va_list args, ...)
56 {
57 va_list parts, copy;
58 bool failed = FALSE;
59
60 va_start(parts, args);
61
62 while (!failed)
63 {
64 key_encoding_part_t current, target;
65 chunk_t *out, data;
66
67 /* get the part we are looking for */
68 target = va_arg(parts, key_encoding_part_t);
69 if (target == KEY_PART_END)
70 {
71 break;
72 }
73 out = va_arg(parts, chunk_t*);
74
75 va_copy(copy, args);
76 while (!failed)
77 {
78 current = va_arg(copy, key_encoding_part_t);
79 if (current == KEY_PART_END)
80 {
81 failed = TRUE;
82 break;
83 }
84 data = va_arg(copy, chunk_t);
85 if (current == target)
86 {
87 *out = data;
88 break;
89 }
90 }
91 va_end(copy);
92 }
93 va_end(parts);
94 return !failed;
95 }
96
97 /**
98 * hashtable hash() function
99 */
100 static u_int hash(void *key)
101 {
102 return (uintptr_t)key;
103 }
104
105 /**
106 * hashtable equals() function
107 */
108 static bool equals(void *key1, void *key2)
109 {
110 return key1 == key2;
111 }
112
113 /**
114 * Implementation of key_encoding_t.get_cache
115 */
116 static bool get_cache(private_key_encoding_t *this, key_encoding_type_t type,
117 void *cache, chunk_t *encoding)
118 {
119 chunk_t *chunk;
120
121 if (type >= KEY_ENCODING_MAX || type < 0)
122 {
123 return FALSE;
124 }
125 this->lock->read_lock(this->lock);
126 chunk = this->cache[type]->get(this->cache[type], cache);
127 if (chunk)
128 {
129 *encoding = *chunk;
130 }
131 this->lock->unlock(this->lock);
132 return !!chunk;
133 }
134
135 /**
136 * Implementation of key_encoding_t.encode
137 */
138 static bool encode(private_key_encoding_t *this, key_encoding_type_t type,
139 void *cache, chunk_t *encoding, ...)
140 {
141 enumerator_t *enumerator;
142 va_list args, copy;
143 key_encoder_t encode;
144 bool success = FALSE;
145 chunk_t *chunk;
146
147 if (type >= KEY_ENCODING_MAX || type < 0)
148 {
149 return FALSE;
150 }
151 this->lock->read_lock(this->lock);
152 if (cache)
153 {
154 chunk = this->cache[type]->get(this->cache[type], cache);
155 if (chunk)
156 {
157 *encoding = *chunk;
158 this->lock->unlock(this->lock);
159 return TRUE;
160 }
161 }
162 va_start(args, encoding);
163 enumerator = this->encoders->create_enumerator(this->encoders);
164 while (enumerator->enumerate(enumerator, &encode))
165 {
166 va_copy(copy, args);
167 success = encode(type, encoding, copy);
168 va_end(copy);
169 if (success)
170 {
171 if (cache)
172 {
173 chunk = malloc_thing(chunk_t);
174 *chunk = *encoding;
175 this->lock->unlock(this->lock);
176 this->lock->write_lock(this->lock);
177 this->cache[type]->put(this->cache[type], cache, chunk);
178 }
179 break;
180 }
181 }
182 enumerator->destroy(enumerator);
183 va_end(args);
184 this->lock->unlock(this->lock);
185 return success;
186 }
187
188 /**
189 * Implementation of key_encoding_t.cache
190 */
191 static void cache(private_key_encoding_t *this, key_encoding_type_t type,
192 void *cache, chunk_t encoding)
193 {
194 chunk_t *chunk;
195
196 if (type >= KEY_ENCODING_MAX || type < 0)
197 {
198 return free(encoding.ptr);
199 }
200 chunk = malloc_thing(chunk_t);
201 *chunk = encoding;
202 this->lock->write_lock(this->lock);
203 chunk = this->cache[type]->put(this->cache[type], cache, chunk);
204 this->lock->unlock(this->lock);
205 /* free an encoding already associated to the cache */
206 if (chunk)
207 {
208 free(chunk->ptr);
209 free(chunk);
210 }
211 }
212
213 /**
214 * Implementation of key_encoding_t.clear_cache
215 */
216 static void clear_cache(private_key_encoding_t *this, void *cache)
217 {
218 key_encoding_type_t type;
219 chunk_t *chunk;
220
221 this->lock->write_lock(this->lock);
222 for (type = 0; type < KEY_ENCODING_MAX; type++)
223 {
224 chunk = this->cache[type]->remove(this->cache[type], cache);
225 if (chunk)
226 {
227 chunk_free(chunk);
228 free(chunk);
229 }
230 }
231 this->lock->unlock(this->lock);
232 }
233
234 /**
235 * Implementation of key_encoding_t.add_encoder
236 */
237 static void add_encoder(private_key_encoding_t *this, key_encoder_t encoder)
238 {
239 this->lock->write_lock(this->lock);
240 this->encoders->insert_last(this->encoders, encoder);
241 this->lock->unlock(this->lock);
242 }
243
244 /**
245 * Implementation of key_encoding_t.remove_encoder
246 */
247 static void remove_encoder(private_key_encoding_t *this, key_encoder_t encoder)
248 {
249 this->lock->write_lock(this->lock);
250 this->encoders->remove(this->encoders, encoder, NULL);
251 this->lock->unlock(this->lock);
252 }
253
254 /**
255 * Implementation of key_encoder_t.destroy.
256 */
257 static void destroy(private_key_encoding_t *this)
258 {
259 key_encoding_type_t type;
260
261 for (type = 0; type < KEY_ENCODING_MAX; type++)
262 {
263 /* We explicitly do not free remaining encodings. All keys should
264 * have gone now, and they are responsible for cleaning out their
265 * cache entries. Not flushing here allows the leak detective to
266 * complain if a key did not flush cached encodings. */
267 this->cache[type]->destroy(this->cache[type]);
268 }
269 this->encoders->destroy(this->encoders);
270 this->lock->destroy(this->lock);
271 free(this);
272 }
273
274 /**
275 * See header
276 */
277 key_encoding_t *key_encoding_create()
278 {
279 private_key_encoding_t *this = malloc_thing(private_key_encoding_t);
280 key_encoding_type_t type;
281
282 this->public.encode = (bool(*)(key_encoding_t*, key_encoding_type_t type, void *cache, chunk_t *encoding, ...))encode;
283 this->public.get_cache = (bool(*)(key_encoding_t*, key_encoding_type_t type, void *cache, chunk_t *encoding))get_cache;
284 this->public.cache = (void(*)(key_encoding_t*, key_encoding_type_t type, void *cache, chunk_t encoding))cache;
285 this->public.clear_cache = (void(*)(key_encoding_t*, void *cache))clear_cache;
286 this->public.add_encoder = (void(*)(key_encoding_t*, key_encoder_t encoder))add_encoder;
287 this->public.remove_encoder = (void(*)(key_encoding_t*, key_encoder_t encoder))remove_encoder;
288 this->public.destroy = (void(*)(key_encoding_t*))destroy;
289
290 for (type = 0; type < KEY_ENCODING_MAX; type++)
291 {
292 this->cache[type] = hashtable_create(hash, equals, 8);
293 }
294 this->encoders = linked_list_create();
295 this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
296
297 return &this->public;
298 }
299