libtnccs: Optionally use RTLD_NOW to load IMC/IMVs with dlopen()
[strongswan.git] / src / libtnccs / plugins / tnc_imv / tnc_imv.c
1 /*
2 * Copyright (C) 2010-2013 Andreas Steffen,
3 * HSR 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 "tnc_imv.h"
17
18 #ifndef WIN32
19 #include <dlfcn.h>
20 #endif
21
22 #include <tncif_pa_subtypes.h>
23
24 #include <utils/debug.h>
25 #include <library.h>
26 #include <collections/linked_list.h>
27 #include <threading/mutex.h>
28
29 typedef struct private_tnc_imv_t private_tnc_imv_t;
30
31 /**
32 * Private data of an imv_t object.
33 */
34 struct private_tnc_imv_t {
35
36 /**
37 * Public members of imv_t.
38 */
39 imv_t public;
40
41 /**
42 * Name of loaded IMV
43 */
44 char *name;
45
46 /**
47 * Handle of loaded IMV
48 */
49 void *handle;
50
51 /**
52 * ID of loaded IMV
53 */
54 TNC_IMVID id;
55
56 /**
57 * List of additional IMV IDs
58 */
59 linked_list_t *additional_ids;
60
61 /**
62 * List of message types supported by IMV - Vendor ID part
63 */
64 TNC_VendorIDList supported_vids;
65
66 /**
67 * List of message types supported by IMV - Subtype part
68 */
69 TNC_MessageSubtypeList supported_subtypes;
70
71 /**
72 * Number of supported message types
73 */
74 TNC_UInt32 type_count;
75
76 /**
77 * mutex to lock the imv_t object
78 */
79 mutex_t *mutex;
80 };
81
82 METHOD(imv_t, set_id, void,
83 private_tnc_imv_t *this, TNC_IMVID id)
84 {
85 this->id = id;
86 }
87
88 METHOD(imv_t, get_id, TNC_IMVID,
89 private_tnc_imv_t *this)
90 {
91 return this->id;
92 }
93
94 METHOD(imv_t, add_id, void,
95 private_tnc_imv_t *this, TNC_IMVID id)
96 {
97 TNC_IMVID *new_id;
98
99 new_id = malloc_thing(TNC_IMVID);
100 *new_id = id;
101 this->additional_ids->insert_last(this->additional_ids, new_id);
102 }
103
104 METHOD(imv_t, has_id, bool,
105 private_tnc_imv_t *this, TNC_IMVID id)
106 {
107 enumerator_t *enumerator;
108 TNC_IMVID *additional_id;
109 bool found = FALSE;
110
111 /* check primary IMV ID */
112 if (id == this->id)
113 {
114 return TRUE;
115 }
116
117 /* return if there are no additional IMV IDs */
118 if (this->additional_ids->get_count(this->additional_ids) == 0)
119 {
120 return FALSE;
121 }
122
123 /* check additional IMV IDs */
124 enumerator = this->additional_ids->create_enumerator(this->additional_ids);
125 while (enumerator->enumerate(enumerator, &additional_id))
126 {
127 if (id == *additional_id)
128 {
129 found = TRUE;
130 break;
131 }
132 }
133 enumerator->destroy(enumerator);
134
135 return found;
136 }
137
138 METHOD(imv_t, get_name, char*,
139 private_tnc_imv_t *this)
140 {
141 return this->name;
142 }
143
144 METHOD(imv_t, set_message_types, void,
145 private_tnc_imv_t *this, TNC_MessageTypeList supported_types,
146 TNC_UInt32 type_count)
147 {
148 char buf[BUF_LEN];
149 char *pos = buf;
150 int len = sizeof(buf);
151 int i, written;
152 size_t size;
153 TNC_VendorID vid;
154 TNC_MessageSubtype subtype;
155 enum_name_t *pa_subtype_names;
156
157 /* lock the imv_t instance */
158 this->mutex->lock(this->mutex);
159
160 /* Free existing VendorID and MessageSubtype lists */
161 free(this->supported_vids);
162 this->supported_vids = NULL;
163 free(this->supported_subtypes);
164 this->supported_subtypes = NULL;
165
166 /* Store the new MessageType list */
167 this->type_count = type_count;
168 if (type_count && supported_types)
169 {
170 size = type_count * sizeof(TNC_VendorID);
171 this->supported_vids = malloc(size);
172 size = type_count * sizeof(TNC_MessageSubtype);
173 this->supported_subtypes = malloc(size);
174
175 for (i = 0; i < type_count; i++)
176 {
177 vid = (supported_types[i] >> 8) & TNC_VENDORID_ANY;
178 subtype = supported_types[i] & TNC_SUBTYPE_ANY;
179
180 pa_subtype_names = get_pa_subtype_names(vid);
181 if (pa_subtype_names)
182 {
183 written = snprintf(pos, len," '%N/%N' 0x%06x/0x%02x",
184 pen_names, vid, pa_subtype_names, subtype,
185 vid, subtype);
186 }
187 else
188 {
189 written = snprintf(pos, len," '%N' 0x%06x/0x%02x",
190 pen_names, vid, vid, subtype);
191 }
192 if (written >= len)
193 {
194 break;
195 }
196 pos += written;
197 len -= written;
198
199 this->supported_vids[i] = vid;
200 this->supported_subtypes[i] = subtype;
201 }
202 }
203 *pos = '\0';
204 DBG2(DBG_TNC, "IMV %u supports %u message type%s:%s",
205 this->id, type_count, (type_count == 1) ? "":"s", buf);
206
207 /* unlock the imv_t instance */
208 this->mutex->unlock(this->mutex);
209 }
210
211 METHOD(imv_t, set_message_types_long, void,
212 private_tnc_imv_t *this, TNC_VendorIDList supported_vids,
213 TNC_MessageSubtypeList supported_subtypes, TNC_UInt32 type_count)
214 {
215 char buf[BUF_LEN];
216 char *pos = buf;
217 int len = sizeof(buf);
218 int i, written;
219 size_t size;
220 TNC_VendorID vid;
221 TNC_MessageSubtype subtype;
222 enum_name_t *pa_subtype_names;
223
224 /* lock the imv_t instance */
225 this->mutex->lock(this->mutex);
226
227 /* Free existing VendorID and MessageSubtype lists */
228 free(this->supported_vids);
229 this->supported_vids = NULL;
230 free(this->supported_subtypes);
231 this->supported_subtypes = NULL;
232
233 /* Store the new MessageType list */
234 this->type_count = type_count;
235 if (type_count && supported_vids && supported_subtypes)
236 {
237 size = type_count * sizeof(TNC_VendorID);
238 this->supported_vids = malloc(size);
239 memcpy(this->supported_vids, supported_vids, size);
240 size = type_count * sizeof(TNC_MessageSubtype);
241 this->supported_subtypes = malloc(size);
242 memcpy(this->supported_subtypes, supported_subtypes, size);
243
244 for (i = 0; i < type_count; i++)
245 {
246 vid = supported_vids[i];
247 subtype = supported_subtypes[i];
248
249 pa_subtype_names = get_pa_subtype_names(vid);
250 if (pa_subtype_names)
251 {
252 written = snprintf(pos, len," '%N/%N' 0x%06x/0x%08x",
253 pen_names, vid, pa_subtype_names, subtype,
254 vid, subtype);
255 }
256 else
257 {
258 written = snprintf(pos, len," '%N' 0x%06x/0x%08x",
259 pen_names, vid, vid, subtype);
260 }
261 if (written >= len)
262 {
263 break;
264 }
265 pos += written;
266 len -= written;
267 }
268 }
269 *pos = '\0';
270 DBG2(DBG_TNC, "IMV %u supports %u message type%s:%s",
271 this->id, type_count, (type_count == 1) ? "":"s", buf);
272
273 /* unlock the imv_t instance */
274 this->mutex->unlock(this->mutex);
275 }
276
277 METHOD(imv_t, type_supported, bool,
278 private_tnc_imv_t *this, TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype)
279 {
280 TNC_VendorID vid;
281 TNC_MessageSubtype subtype;
282 int i;
283
284 for (i = 0; i < this->type_count; i++)
285 {
286 vid = this->supported_vids[i];
287 subtype = this->supported_subtypes[i];
288
289 if ((vid == TNC_VENDORID_ANY && subtype == TNC_SUBTYPE_ANY) ||
290 (vid == msg_vid && (subtype == TNC_SUBTYPE_ANY ||
291 subtype == msg_subtype)))
292 {
293 return TRUE;
294 }
295 }
296 return FALSE;
297 }
298
299 METHOD(imv_t, destroy, void,
300 private_tnc_imv_t *this)
301 {
302 if (this->handle && lib->settings->get_bool(lib->settings,
303 "%s.plugins.tnc-imv.dlclose", TRUE, lib->ns))
304 {
305 dlclose(this->handle);
306 }
307 this->mutex->destroy(this->mutex);
308 this->additional_ids->destroy_function(this->additional_ids, free);
309 free(this->supported_vids);
310 free(this->supported_subtypes);
311 free(this->name);
312 free(this);
313 }
314
315 /**
316 * Generic constructor.
317 */
318 static private_tnc_imv_t* tnc_imv_create_empty(char *name)
319 {
320 private_tnc_imv_t *this;
321
322 INIT(this,
323 .public = {
324 .set_id = _set_id,
325 .get_id = _get_id,
326 .add_id = _add_id,
327 .has_id = _has_id,
328 .get_name = _get_name,
329 .set_message_types = _set_message_types,
330 .set_message_types_long = _set_message_types_long,
331 .type_supported = _type_supported,
332 .destroy = _destroy,
333 },
334 .name = strdup(name),
335 .additional_ids = linked_list_create(),
336 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
337 );
338
339 return this;
340 }
341
342 /**
343 * Described in header.
344 */
345 imv_t* tnc_imv_create(char *name, char *path)
346 {
347 private_tnc_imv_t *this;
348 int flag = RTLD_LAZY;
349
350 this = tnc_imv_create_empty(name);
351
352 if (lib->settings->get_bool(lib->settings, "%s.dlopen_use_rtld_now",
353 lib->ns, FALSE))
354 {
355 flag = RTLD_NOW;
356 }
357 this->handle = dlopen(path, flag);
358 if (!this->handle)
359 {
360 DBG1(DBG_TNC, "IMV \"%s\" failed to load: %s", name, dlerror());
361 destroy(this);
362 return NULL;
363 }
364
365 this->public.initialize = dlsym(this->handle, "TNC_IMV_Initialize");
366 if (!this->public.initialize)
367 {
368 DBG1(DBG_TNC, "could not resolve TNC_IMV_Initialize in %s: %s\n",
369 path, dlerror());
370 destroy(this);
371 return NULL;
372 }
373 this->public.notify_connection_change =
374 dlsym(this->handle, "TNC_IMV_NotifyConnectionChange");
375 this->public.solicit_recommendation =
376 dlsym(this->handle, "TNC_IMV_SolicitRecommendation");
377 if (!this->public.solicit_recommendation)
378 {
379 DBG1(DBG_TNC, "could not resolve TNC_IMV_SolicitRecommendation in %s: %s\n",
380 path, dlerror());
381 destroy(this);
382 return NULL;
383 }
384 this->public.receive_message =
385 dlsym(this->handle, "TNC_IMV_ReceiveMessage");
386 this->public.receive_message_long =
387 dlsym(this->handle, "TNC_IMV_ReceiveMessageLong");
388 this->public.batch_ending =
389 dlsym(this->handle, "TNC_IMV_BatchEnding");
390 this->public.terminate =
391 dlsym(this->handle, "TNC_IMV_Terminate");
392 this->public.provide_bind_function =
393 dlsym(this->handle, "TNC_IMV_ProvideBindFunction");
394 if (!this->public.provide_bind_function)
395 {
396 DBG1(DBG_TNC, "could not resolve TNC_IMV_ProvideBindFunction in %s: %s\n",
397 path, dlerror());
398 destroy(this);
399 return NULL;
400 }
401
402 return &this->public;
403 }
404
405 /**
406 * Described in header.
407 */
408 imv_t* tnc_imv_create_from_functions(char *name,
409 TNC_IMV_InitializePointer initialize,
410 TNC_IMV_NotifyConnectionChangePointer notify_connection_change,
411 TNC_IMV_ReceiveMessagePointer receive_message,
412 TNC_IMV_ReceiveMessageLongPointer receive_message_long,
413 TNC_IMV_SolicitRecommendationPointer solicit_recommendation,
414 TNC_IMV_BatchEndingPointer batch_ending,
415 TNC_IMV_TerminatePointer terminate,
416 TNC_IMV_ProvideBindFunctionPointer provide_bind_function)
417 {
418 private_tnc_imv_t *this;
419
420 this = tnc_imv_create_empty(name);
421
422 this->public.initialize = initialize;
423 this->public.notify_connection_change = notify_connection_change;
424 this->public.receive_message = receive_message;
425 this->public.receive_message_long = receive_message_long;
426 this->public.solicit_recommendation = solicit_recommendation;
427 this->public.batch_ending = batch_ending;
428 this->public.terminate = terminate;
429 this->public.provide_bind_function = provide_bind_function;
430
431 return &this->public;
432 }