Migrated cred_encoding to INIT/METHOD macros
[strongswan.git] / src / libstrongswan / credentials / cred_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 "cred_encoding.h"
17
18 #include <stdint.h>
19
20 #include <utils/linked_list.h>
21 #include <utils/hashtable.h>
22 #include <threading/rwlock.h>
23
24 typedef struct private_cred_encoding_t private_cred_encoding_t;
25
26 /**
27 * Private data of an cred_encoding_t object.
28 */
29 struct private_cred_encoding_t {
30
31 /**
32 * Public cred_encoding_t interface.
33 */
34 cred_encoding_t public;
35
36 /**
37 * cached encodings, a table for each encoding_type_t, containing chunk_t*
38 */
39 hashtable_t *cache[CRED_ENCODING_MAX];
40
41 /**
42 * Registered encoding fuctions, cred_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 cred_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 cred_encoding_part_t current, target;
65 chunk_t *out, data;
66
67 /* get the part we are looking for */
68 target = va_arg(parts, cred_encoding_part_t);
69 if (target == CRED_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, cred_encoding_part_t);
79 if (current == CRED_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 METHOD(cred_encoding_t, get_cache, bool,
114 private_cred_encoding_t *this, cred_encoding_type_t type, void *cache,
115 chunk_t *encoding)
116 {
117 chunk_t *chunk;
118
119 if (type >= CRED_ENCODING_MAX || type < 0)
120 {
121 return FALSE;
122 }
123 this->lock->read_lock(this->lock);
124 chunk = this->cache[type]->get(this->cache[type], cache);
125 if (chunk)
126 {
127 *encoding = *chunk;
128 }
129 this->lock->unlock(this->lock);
130 return !!chunk;
131 }
132
133 /**
134 * Implementation of cred_encoding_t.encode
135 */
136 static bool encode(private_cred_encoding_t *this, cred_encoding_type_t type,
137 void *cache, chunk_t *encoding, ...)
138 {
139 enumerator_t *enumerator;
140 va_list args, copy;
141 cred_encoder_t encode;
142 bool success = FALSE;
143 chunk_t *chunk;
144
145 if (type >= CRED_ENCODING_MAX || type < 0)
146 {
147 return FALSE;
148 }
149 this->lock->read_lock(this->lock);
150 if (cache)
151 {
152 chunk = this->cache[type]->get(this->cache[type], cache);
153 if (chunk)
154 {
155 *encoding = *chunk;
156 this->lock->unlock(this->lock);
157 return TRUE;
158 }
159 }
160 va_start(args, encoding);
161 enumerator = this->encoders->create_enumerator(this->encoders);
162 while (enumerator->enumerate(enumerator, &encode))
163 {
164 va_copy(copy, args);
165 success = encode(type, encoding, copy);
166 va_end(copy);
167 if (success)
168 {
169 break;
170 }
171 }
172 enumerator->destroy(enumerator);
173 this->lock->unlock(this->lock);
174 va_end(args);
175
176 if (success && cache)
177 {
178 chunk = malloc_thing(chunk_t);
179 *chunk = *encoding;
180 this->lock->write_lock(this->lock);
181 chunk = this->cache[type]->put(this->cache[type], cache, chunk);
182 this->lock->unlock(this->lock);
183 if (chunk)
184 {
185 free(chunk->ptr);
186 free(chunk);
187 }
188 }
189 return success;
190 }
191
192 METHOD(cred_encoding_t, cache, void,
193 private_cred_encoding_t *this, cred_encoding_type_t type, void *cache,
194 chunk_t encoding)
195 {
196 chunk_t *chunk;
197
198 if (type >= CRED_ENCODING_MAX || type < 0)
199 {
200 return free(encoding.ptr);
201 }
202 chunk = malloc_thing(chunk_t);
203 *chunk = encoding;
204 this->lock->write_lock(this->lock);
205 chunk = this->cache[type]->put(this->cache[type], cache, chunk);
206 this->lock->unlock(this->lock);
207 /* free an encoding already associated to the cache */
208 if (chunk)
209 {
210 free(chunk->ptr);
211 free(chunk);
212 }
213 }
214
215 METHOD(cred_encoding_t, clear_cache, void,
216 private_cred_encoding_t *this, void *cache)
217 {
218 cred_encoding_type_t type;
219 chunk_t *chunk;
220
221 this->lock->write_lock(this->lock);
222 for (type = 0; type < CRED_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 METHOD(cred_encoding_t, add_encoder, void,
235 private_cred_encoding_t *this, cred_encoder_t encoder)
236 {
237 this->lock->write_lock(this->lock);
238 this->encoders->insert_last(this->encoders, encoder);
239 this->lock->unlock(this->lock);
240 }
241
242 METHOD(cred_encoding_t, remove_encoder, void,
243 private_cred_encoding_t *this, cred_encoder_t encoder)
244 {
245 this->lock->write_lock(this->lock);
246 this->encoders->remove(this->encoders, encoder, NULL);
247 this->lock->unlock(this->lock);
248 }
249
250 METHOD(cred_encoding_t, destroy, void,
251 private_cred_encoding_t *this)
252 {
253 cred_encoding_type_t type;
254
255 for (type = 0; type < CRED_ENCODING_MAX; type++)
256 {
257 /* We explicitly do not free remaining encodings. All creds should
258 * have gone now, and they are responsible for cleaning out their
259 * cache entries. Not flushing here allows the leak detective to
260 * complain if a credential did not flush cached encodings. */
261 this->cache[type]->destroy(this->cache[type]);
262 }
263 this->encoders->destroy(this->encoders);
264 this->lock->destroy(this->lock);
265 free(this);
266 }
267
268 /**
269 * See header
270 */
271 cred_encoding_t *cred_encoding_create()
272 {
273 private_cred_encoding_t *this;
274 cred_encoding_type_t type;
275
276 INIT(this,
277 .public = {
278 .encode = (bool(*)(cred_encoding_t*, cred_encoding_type_t type, void *cache, chunk_t *encoding, ...))encode,
279 .get_cache = _get_cache,
280 .cache = _cache,
281 .clear_cache = _clear_cache,
282 .add_encoder = _add_encoder,
283 .remove_encoder = _remove_encoder,
284 .destroy = _destroy,
285 },
286 .encoders = linked_list_create(),
287 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
288 );
289
290 for (type = 0; type < CRED_ENCODING_MAX; type++)
291 {
292 this->cache[type] = hashtable_create(hash, equals, 8);
293 }
294
295 return &this->public;
296 }
297