starter: Add new bison/flex based parser for ipsec.conf
[strongswan.git] / src / starter / parser / conf_parser.c
1 /*
2 * Copyright (C) 2013-2014 Tobias Brunner
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 "conf_parser.h"
17
18 #include <collections/array.h>
19 #include <collections/hashtable.h>
20
21 /**
22 * Provided by the generated parser
23 */
24 bool conf_parser_parse_file(conf_parser_t *this, char *file);
25
26 typedef struct private_conf_parser_t private_conf_parser_t;
27 typedef struct section_t section_t;
28
29 /**
30 * Private data
31 */
32 struct private_conf_parser_t {
33
34 /**
35 * Public interface
36 */
37 conf_parser_t public;
38
39 /**
40 * Path to config file
41 */
42 char *file;
43
44 /**
45 * TRUE while parsing the config file
46 */
47 bool parsing;
48
49 /**
50 * Hashtable for ca sections, as section_t
51 */
52 hashtable_t *cas;
53
54 /**
55 * Hashtable for conn sections, as section_t
56 */
57 hashtable_t *conns;
58
59 /**
60 * Array to keep track of the order of conn sections, as section_t
61 */
62 array_t *conns_order;
63
64 /**
65 * config setup section
66 */
67 section_t *config_setup;
68
69 /**
70 * Pointer to the current section (the one added last) during parsing
71 */
72 section_t *current_section;
73
74 /**
75 * Refcount for this parser instance (also used by dictionaries)
76 */
77 refcount_t ref;
78 };
79
80 typedef struct {
81 /** Key of the setting */
82 char *key;
83 /** Value of the setting */
84 char *value;
85 } setting_t;
86
87 int setting_find(const void *a, const void *b)
88 {
89 const char *key = a;
90 const setting_t *setting = b;
91 return strcmp(key, setting->key);
92 }
93
94 int setting_sort(const void *a, const void *b, void *user)
95 {
96 const setting_t *sa = a, *sb = b;
97 return strcmp(sa->key, sb->key);
98 }
99
100 static void setting_destroy(setting_t *this)
101 {
102 free(this->key);
103 free(this->value);
104 free(this);
105 }
106
107 struct section_t {
108 /** Name of the section */
109 char *name;
110 /** Sorted array of settings, as setting_t */
111 array_t *settings;
112 /** Array of also= settings (in reversed order, i.e. most important to least
113 * important), as setting_t */
114 array_t *also;
115 /** Array of linearized parent objects derived from also= settings, in their
116 * order of importance (most to least, i.e. %default) as section_t
117 * NULL if not yet determined */
118 array_t *parents;
119 };
120
121 static section_t *section_create(char *name)
122 {
123 section_t *this;
124
125 INIT(this,
126 .name = name,
127 );
128 return this;
129 }
130
131 static void section_destroy(section_t *this)
132 {
133 array_destroy_function(this->settings, (void*)setting_destroy, NULL);
134 array_destroy_function(this->also, (void*)setting_destroy, NULL);
135 array_destroy(this->parents);
136 free(this->name);
137 free(this);
138 }
139
140 typedef struct {
141 /** Public interface */
142 dictionary_t public;
143 /** Parser object */
144 private_conf_parser_t *parser;
145 /** Section object */
146 section_t *section;
147 } section_dictionary_t;
148
149 typedef struct {
150 /** Public interface */
151 enumerator_t public;
152 /** Current settings enumerator */
153 enumerator_t *settings;
154 /** Enumerator into parent list */
155 enumerator_t *parents;
156 /** Hashtable to keep track of already enumerated settings */
157 hashtable_t *seen;
158 } dictionary_enumerator_t;
159
160 METHOD(enumerator_t, dictionary_enumerate, bool,
161 dictionary_enumerator_t *this, char **key, char **value)
162 {
163 setting_t *setting;
164 section_t *parent;
165
166 while (TRUE)
167 {
168 if (this->settings &&
169 this->settings->enumerate(this->settings, &setting))
170 {
171 if (this->seen->get(this->seen, setting->key))
172 {
173 continue;
174 }
175 this->seen->put(this->seen, setting->key, setting->key);
176 if (!setting->value)
177 {
178 continue;
179 }
180 if (key)
181 {
182 *key = setting->key;
183 }
184 if (value)
185 {
186 *value = setting->value;
187 }
188 return TRUE;
189 }
190 DESTROY_IF(this->settings);
191 this->settings = NULL;
192 if (this->parents &&
193 this->parents->enumerate(this->parents, &parent))
194 {
195 if (parent->settings)
196 {
197 this->settings = array_create_enumerator(parent->settings);
198 }
199 continue;
200 }
201 DESTROY_IF(this->parents);
202 this->parents = NULL;
203 break;
204 }
205 return FALSE;
206 }
207
208 METHOD(enumerator_t, dictionary_enumerator_destroy, void,
209 dictionary_enumerator_t *this)
210 {
211 DESTROY_IF(this->settings);
212 DESTROY_IF(this->parents);
213 this->seen->destroy(this->seen);
214 free(this);
215 }
216
217 METHOD(dictionary_t, dictionary_create_enumerator, enumerator_t*,
218 section_dictionary_t *this)
219 {
220 dictionary_enumerator_t *enumerator;
221
222 INIT(enumerator,
223 .public = {
224 .enumerate = (void*)_dictionary_enumerate,
225 .destroy = _dictionary_enumerator_destroy,
226 },
227 .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
228 );
229 if (this->section->settings)
230 {
231 enumerator->settings = array_create_enumerator(this->section->settings);
232 }
233 if (this->section->parents)
234 {
235 enumerator->parents = array_create_enumerator(this->section->parents);
236 }
237 return &enumerator->public;
238 }
239
240 METHOD(dictionary_t, dictionary_get, void*,
241 section_dictionary_t *this, const void *key)
242 {
243 enumerator_t *parents;
244 section_t *section;
245 setting_t *setting;
246 char *value = NULL;
247
248 section = this->section;
249 if (array_bsearch(section->settings, key, setting_find, &setting) != -1)
250 {
251 return setting->value;
252 }
253 parents = array_create_enumerator(section->parents);
254 while (parents->enumerate(parents, &section))
255 {
256 if (array_bsearch(section->settings, key, setting_find, &setting) != -1)
257 {
258 value = setting->value;
259 break;
260 }
261 }
262 parents->destroy(parents);
263 return value;
264 }
265
266 METHOD(dictionary_t, dictionary_destroy, void,
267 section_dictionary_t *this)
268 {
269 this->parser->public.destroy(&this->parser->public);
270 free(this);
271 }
272
273 static dictionary_t *section_dictionary_create(private_conf_parser_t *parser,
274 section_t *section)
275 {
276 section_dictionary_t *this;
277
278 INIT(this,
279 .public = {
280 .create_enumerator = _dictionary_create_enumerator,
281 .get = _dictionary_get,
282 .destroy = _dictionary_destroy,
283 },
284 .parser = parser,
285 .section = section,
286 );
287
288 ref_get(&parser->ref);
289
290 return &this->public;
291 }
292
293 static bool conn_filter(void *unused, section_t **section, char **name)
294 {
295 *name = (*section)->name;
296 return TRUE;
297 }
298
299 static bool ca_filter(void *unused, void *key, char **name, section_t **section)
300 {
301 *name = (*section)->name;
302 return TRUE;
303 }
304
305 METHOD(conf_parser_t, get_sections, enumerator_t*,
306 private_conf_parser_t *this, conf_parser_section_t type)
307 {
308 switch (type)
309 {
310 case CONF_PARSER_CONN:
311 return enumerator_create_filter(
312 array_create_enumerator(this->conns_order),
313 (void*)conn_filter, NULL, NULL);
314 case CONF_PARSER_CA:
315 return enumerator_create_filter(
316 this->cas->create_enumerator(this->cas),
317 (void*)ca_filter, NULL, NULL);
318 case CONF_PARSER_CONFIG_SETUP:
319 default:
320 return enumerator_create_empty();
321 }
322 }
323
324 METHOD(conf_parser_t, get_section, dictionary_t*,
325 private_conf_parser_t *this, conf_parser_section_t type, char *name)
326 {
327 section_t *section = NULL;
328
329 switch (type)
330 {
331 case CONF_PARSER_CONFIG_SETUP:
332 section = this->config_setup;
333 break;
334 case CONF_PARSER_CONN:
335 section = this->conns->get(this->conns, name);
336 break;
337 case CONF_PARSER_CA:
338 section = this->cas->get(this->cas, name);
339 break;
340 default:
341 break;
342 }
343 return section ? section_dictionary_create(this, section) : NULL;
344 }
345
346 METHOD(conf_parser_t, add_section, bool,
347 private_conf_parser_t *this, conf_parser_section_t type, char *name)
348 {
349 hashtable_t *sections = this->conns;
350 array_t *order = this->conns_order;
351 section_t *section = NULL;
352 bool exists = FALSE;
353
354 if (!this->parsing)
355 {
356 free(name);
357 return exists;
358 }
359 switch (type)
360 {
361 case CONF_PARSER_CONFIG_SETUP:
362 section = this->config_setup;
363 /* we don't expect a name, but just in case */
364 free(name);
365 break;
366 case CONF_PARSER_CA:
367 sections = this->cas;
368 order = NULL;
369 /* fall-through */
370 case CONF_PARSER_CONN:
371 section = sections->get(sections, name);
372 if (!section)
373 {
374 section = section_create(name);
375 sections->put(sections, name, section);
376 if (order)
377 {
378 array_insert(order, ARRAY_TAIL, section);
379 }
380 }
381 else
382 {
383 exists = TRUE;
384 free(name);
385 }
386 break;
387
388 }
389 this->current_section = section;
390 return exists;
391 }
392
393 METHOD(conf_parser_t, add_setting, void,
394 private_conf_parser_t *this, char *key, char *value)
395 {
396 section_t *section = this->current_section;
397 setting_t *setting;
398
399 if (!this->parsing || !this->current_section)
400 {
401 free(key);
402 free(value);
403 return;
404 }
405 if (streq(key, "also"))
406 {
407 if (!value || !strlen(value) || streq(value, "%default"))
408 { /* we require a name, but all sections inherit from %default */
409 free(key);
410 free(value);
411 return;
412 }
413 INIT(setting,
414 .key = key,
415 .value = value,
416 );
417 array_insert_create(&section->also, ARRAY_HEAD, setting);
418 return;
419 }
420 if (array_bsearch(section->settings, key, setting_find, &setting) == -1)
421 {
422 INIT(setting,
423 .key = key,
424 .value = value,
425 );
426 array_insert_create(&section->settings, ARRAY_TAIL, setting);
427 array_sort(section->settings, setting_sort, NULL);
428 }
429 else
430 {
431 free(setting->value);
432 setting->value = value;
433 free(key);
434 }
435 }
436
437 /**
438 * Check if the given section is contained in the given array. The search
439 * starts at the given index.
440 */
441 static bool is_contained_in(array_t *arr, section_t *section)
442 {
443 section_t *current;
444 int i;
445
446 for (i = 0; i < array_count(arr); i++)
447 {
448 array_get(arr, i, &current);
449 if (streq(section->name, current->name))
450 {
451 return TRUE;
452 }
453 }
454 return FALSE;
455 }
456
457 /**
458 * This algorithm to linearize sections uses a bottom-first depth-first
459 * semantic, with an additional elimination step that removes all but the
460 * last occurrence of each section.
461 *
462 * Consider this configuration:
463 *
464 * conn A
465 * conn B
466 * also=A
467 * conn C
468 * also=A
469 * conn D
470 * also=C
471 * also=B
472 *
473 * The linearization would yield D B A C A, which gets reduced to D B C A.
474 *
475 * Ambiguous configurations are handled pragmatically.
476 *
477 * Consider the following configuration:
478 *
479 * conn A
480 * conn B
481 * conn C
482 * also=A
483 * also=B
484 * conn D
485 * also=B
486 * also=A
487 * conn E
488 * also=C
489 * also=D
490 *
491 * It is ambiguous because D and C include the same two sections but in
492 * a different order.
493 *
494 * The linearization would yield E D A B C B A which gets reduced to E D C B A.
495 */
496 static bool resolve_also_single(hashtable_t *sections,
497 section_t *section, section_t *def, array_t *stack)
498 {
499 enumerator_t *enumerator;
500 array_t *parents;
501 section_t *parent, *grandparent;
502 setting_t *also;
503 bool success = TRUE;
504 int i;
505
506 array_insert(stack, ARRAY_HEAD, section);
507 parents = array_create(0, 0);
508 enumerator = array_create_enumerator(section->also);
509 while (enumerator->enumerate(enumerator, &also))
510 {
511 parent = sections->get(sections, also->value);
512 if (!parent || is_contained_in(stack, parent))
513 {
514 if (!parent)
515 {
516 DBG1(DBG_CFG, "section '%s' referenced in section '%s' not "
517 "found", also->value, section->name);
518 }
519 else
520 {
521 DBG1(DBG_CFG, "section '%s' referenced in section '%s' causes "
522 "a loop", parent->name, section->name);
523 }
524 array_remove_at(section->also, enumerator);
525 setting_destroy(also);
526 success = FALSE;
527 continue;
528 }
529 if (!parent->parents)
530 {
531 if (!resolve_also_single(sections, parent, def, stack))
532 {
533 success = FALSE;
534 continue;
535 }
536 }
537 /* add the grandparents and the parent to the list */
538 array_insert(parents, ARRAY_TAIL, parent);
539 for (i = 0; i < array_count(parent->parents); i++)
540 {
541 array_get(parent->parents, i, &grandparent);
542 array_insert(parents, ARRAY_TAIL, grandparent);
543 }
544 }
545 enumerator->destroy(enumerator);
546 array_remove(stack, ARRAY_HEAD, NULL);
547
548 if (success && def && !array_count(parents))
549 {
550 array_insert(parents, ARRAY_TAIL, def);
551 }
552
553 while (success && array_remove(parents, ARRAY_HEAD, &parent))
554 {
555 if (!is_contained_in(parents, parent))
556 { /* last occurrence of this section */
557 array_insert_create(&section->parents, ARRAY_TAIL, parent);
558 }
559 }
560 array_destroy(parents);
561 return success;
562 }
563
564 /**
565 * Resolve also= statements. The functions returns TRUE if everything is fine,
566 * or FALSE if either a referenced section does not exist, or if the section
567 * inheritance can't be determined properly (e.g. if there are loops or if a
568 * section inherits from multiple sections - perhaps over several levels - in
569 * an ambiguous way).
570 */
571 static bool resolve_also(hashtable_t *sections)
572 {
573 enumerator_t *enumerator;
574 section_t *def, *section;
575 array_t *stack;
576 bool success = TRUE;
577
578 stack = array_create(0, 0);
579
580 def = sections->get(sections, "%default");
581 if (def)
582 { /* the default section is the only one with an empty parents list */
583 def->parents = array_create(0, 0);
584 }
585 enumerator = sections->create_enumerator(sections);
586 while (enumerator->enumerate(enumerator, NULL, &section))
587 {
588 if (section->parents)
589 { /* already determined */
590 continue;
591 }
592 success = resolve_also_single(sections, section, def, stack) && success;
593 }
594 enumerator->destroy(enumerator);
595 array_destroy(stack);
596 return success;
597 }
598
599 METHOD(conf_parser_t, parse, bool,
600 private_conf_parser_t *this)
601 {
602 bool success;
603
604 if (!this->file)
605 { /* no file, lets assume this is OK */
606 return TRUE;
607 }
608 this->parsing = TRUE;
609 success = conf_parser_parse_file(&this->public, this->file);
610 this->parsing = FALSE;
611 return success && resolve_also(this->conns) && resolve_also(this->cas);
612 }
613
614 METHOD(conf_parser_t, destroy, void,
615 private_conf_parser_t *this)
616 {
617 if (ref_put(&this->ref))
618 {
619 this->cas->destroy_function(this->cas, (void*)section_destroy);
620 this->conns->destroy_function(this->conns, (void*)section_destroy);
621 section_destroy(this->config_setup);
622 array_destroy(this->conns_order);
623 free(this->file);
624 free(this);
625 }
626 }
627
628 /*
629 * Described in header
630 */
631 conf_parser_t *conf_parser_create(const char *file)
632 {
633 private_conf_parser_t *this;
634
635 INIT(this,
636 .public = {
637 .parse = _parse,
638 .get_sections = _get_sections,
639 .get_section = _get_section,
640 .add_section = _add_section,
641 .add_setting = _add_setting,
642 .destroy = _destroy,
643 },
644 .file = strdupnull(file),
645 .cas = hashtable_create(hashtable_hash_str,
646 hashtable_equals_str, 8),
647 .conns = hashtable_create(hashtable_hash_str,
648 hashtable_equals_str, 8),
649 .conns_order = array_create(0, 0),
650 .config_setup = section_create(NULL),
651 .ref = 1,
652 );
653
654 return &this->public;
655 }