vici: Remove unreachable code
[strongswan.git] / src / libcharon / plugins / vici / vici_message.c
1 /*
2 * Copyright (C) 2015 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2014 Martin Willi
6 * Copyright (C) 2014 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "vici_message.h"
20 #include "vici_builder.h"
21
22 #include <bio/bio_reader.h>
23 #include <bio/bio_writer.h>
24
25 #include <errno.h>
26
27 typedef struct private_vici_message_t private_vici_message_t;
28
29 /**
30 * Private data of an vici_message_t object.
31 */
32 struct private_vici_message_t {
33
34 /**
35 * Public vici_message_t interface.
36 */
37 vici_message_t public;
38
39 /**
40 * Message encoding
41 */
42 chunk_t encoding;
43
44 /**
45 * Free encoding during destruction?
46 */
47 bool cleanup;
48
49 /**
50 * Allocated strings we maintain for get_str()
51 */
52 linked_list_t *strings;
53 };
54
55 ENUM(vici_type_names, VICI_START, VICI_END,
56 "start",
57 "section-start",
58 "section-end",
59 "key-value",
60 "list-start",
61 "list-item",
62 "list-end",
63 "end"
64 );
65
66 /**
67 * See header.
68 */
69 bool vici_stringify(chunk_t chunk, char *buf, size_t size)
70 {
71 if (!chunk_printable(chunk, NULL, 0))
72 {
73 return FALSE;
74 }
75 snprintf(buf, size, "%.*s", (int)chunk.len, chunk.ptr);
76 return TRUE;
77 }
78
79 /**
80 * See header.
81 */
82 bool vici_verify_type(vici_type_t type, u_int section, bool list)
83 {
84 if (list)
85 {
86 if (type != VICI_LIST_END && type != VICI_LIST_ITEM)
87 {
88 DBG1(DBG_ENC, "'%N' within list", vici_type_names, type);
89 return FALSE;
90 }
91 }
92 else
93 {
94 if (type == VICI_LIST_ITEM || type == VICI_LIST_END)
95 {
96 DBG1(DBG_ENC, "'%N' outside list", vici_type_names, type);
97 return FALSE;
98 }
99 }
100 if (type == VICI_SECTION_END && section == 0)
101 {
102 DBG1(DBG_ENC, "'%N' outside of section", vici_type_names, type);
103 return FALSE;
104 }
105 if (type == VICI_END && section)
106 {
107 DBG1(DBG_ENC, "'%N' within section", vici_type_names, type);
108 return FALSE;
109 }
110 return TRUE;
111 }
112
113 /**
114 * Enumerator parsing message
115 */
116 typedef struct {
117 /* implements enumerator */
118 enumerator_t public;
119 /** reader to parse from */
120 bio_reader_t *reader;
121 /** section nesting level */
122 int section;
123 /** currently parsing list? */
124 bool list;
125 /** string currently enumerating */
126 char name[257];
127 } parse_enumerator_t;
128
129 METHOD(enumerator_t, parse_enumerate, bool,
130 parse_enumerator_t *this, va_list args)
131 {
132 vici_type_t *out;
133 chunk_t *value;
134 char **name;
135 uint8_t type;
136 chunk_t data;
137
138 VA_ARGS_VGET(args, out, name, value);
139
140 if (!this->reader->remaining(this->reader) ||
141 !this->reader->read_uint8(this->reader, &type))
142 {
143 *out = VICI_END;
144 return TRUE;
145 }
146 if (!vici_verify_type(type, this->section, this->list))
147 {
148 return FALSE;
149 }
150
151 switch (type)
152 {
153 case VICI_SECTION_START:
154 if (!this->reader->read_data8(this->reader, &data) ||
155 !vici_stringify(data, this->name, sizeof(this->name)))
156 {
157 DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type);
158 return FALSE;
159 }
160 *name = this->name;
161 this->section++;
162 break;
163 case VICI_SECTION_END:
164 this->section--;
165 break;
166 case VICI_KEY_VALUE:
167 if (!this->reader->read_data8(this->reader, &data) ||
168 !vici_stringify(data, this->name, sizeof(this->name)) ||
169 !this->reader->read_data16(this->reader, value))
170 {
171 DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type);
172 return FALSE;
173 }
174 *name = this->name;
175 break;
176 case VICI_LIST_START:
177 if (!this->reader->read_data8(this->reader, &data) ||
178 !vici_stringify(data, this->name, sizeof(this->name)))
179 {
180 DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type);
181 return FALSE;
182 }
183 *name = this->name;
184 this->list = TRUE;
185 break;
186 case VICI_LIST_ITEM:
187 this->reader->read_data16(this->reader, value);
188 break;
189 case VICI_LIST_END:
190 this->list = FALSE;
191 break;
192 case VICI_END:
193 return TRUE;
194 default:
195 DBG1(DBG_ENC, "unknown encoding type: %u", type);
196 return FALSE;
197 }
198
199 *out = type;
200
201 return TRUE;
202 }
203
204 METHOD(enumerator_t, parse_destroy, void,
205 parse_enumerator_t *this)
206 {
207 this->reader->destroy(this->reader);
208 free(this);
209 }
210
211 METHOD(vici_message_t, create_enumerator, enumerator_t*,
212 private_vici_message_t *this)
213 {
214 parse_enumerator_t *enumerator;
215
216 INIT(enumerator,
217 .public = {
218 .enumerate = enumerator_enumerate_default,
219 .venumerate = _parse_enumerate,
220 .destroy = _parse_destroy,
221 },
222 .reader = bio_reader_create(this->encoding),
223 );
224
225 return &enumerator->public;
226 }
227
228 /**
229 * Find a value for given vararg key
230 */
231 static bool find_value(private_vici_message_t *this, chunk_t *value,
232 char *fmt, va_list args)
233 {
234 enumerator_t *enumerator;
235 char buf[128], *name, *key, *dot, *next;
236 int section = 0, keysection = 0;
237 bool found = FALSE;
238 chunk_t current;
239 vici_type_t type;
240
241 vsnprintf(buf, sizeof(buf), fmt, args);
242 next = buf;
243
244 enumerator = create_enumerator(this);
245
246 /* descent into section */
247 while (TRUE)
248 {
249 dot = strchr(next, '.');
250 if (!dot)
251 {
252 key = next;
253 break;
254 }
255 *dot = '\0';
256 key = next;
257 next = dot + 1;
258 keysection++;
259
260 while (enumerator->enumerate(enumerator, &type, &name, &current))
261 {
262 switch (type)
263 {
264 case VICI_SECTION_START:
265 section++;
266 if (section == keysection && streq(name, key))
267 {
268 break;
269 }
270 continue;
271 case VICI_SECTION_END:
272 section--;
273 continue;
274 case VICI_END:
275 break;
276 default:
277 continue;
278 }
279 break;
280 }
281 }
282
283 /* find key/value in current section */
284 while (enumerator->enumerate(enumerator, &type, &name, &current))
285 {
286 switch (type)
287 {
288 case VICI_KEY_VALUE:
289 if (section == keysection && streq(key, name))
290 {
291 *value = current;
292 found = TRUE;
293 break;
294 }
295 continue;
296 case VICI_SECTION_START:
297 section++;
298 continue;
299 case VICI_SECTION_END:
300 section--;
301 continue;
302 case VICI_END:
303 break;
304 default:
305 continue;
306 }
307 break;
308 }
309
310 enumerator->destroy(enumerator);
311
312 return found;
313 }
314
315 METHOD(vici_message_t, vget_str, char*,
316 private_vici_message_t *this, char *def, char *fmt, va_list args)
317 {
318 chunk_t value;
319 bool found;
320 char *str;
321
322 found = find_value(this, &value, fmt, args);
323 if (found)
324 {
325 if (chunk_printable(value, NULL, 0))
326 {
327 str = strndup(value.ptr, value.len);
328 /* keep a reference to string, so caller doesn't have to care */
329 this->strings->insert_last(this->strings, str);
330 return str;
331 }
332 }
333 return def;
334 }
335
336 METHOD(vici_message_t, get_str, char*,
337 private_vici_message_t *this, char *def, char *fmt, ...)
338 {
339 va_list args;
340 char *str;
341
342 va_start(args, fmt);
343 str = vget_str(this, def, fmt, args);
344 va_end(args);
345 return str;
346 }
347
348 METHOD(vici_message_t, vget_int, int,
349 private_vici_message_t *this, int def, char *fmt, va_list args)
350 {
351 chunk_t value;
352 bool found;
353 char buf[32], *pos;
354 int ret;
355
356 found = find_value(this, &value, fmt, args);
357 if (found)
358 {
359 if (value.len == 0)
360 {
361 return def;
362 }
363 if (chunk_printable(value, NULL, 0))
364 {
365 snprintf(buf, sizeof(buf), "%.*s", (int)value.len, value.ptr);
366 errno = 0;
367 ret = strtol(buf, &pos, 0);
368 if (errno == 0 && pos == buf + strlen(buf))
369 {
370 return ret;
371 }
372 }
373 }
374 return def;
375 }
376
377 METHOD(vici_message_t, get_int, int,
378 private_vici_message_t *this, int def, char *fmt, ...)
379 {
380 va_list args;
381 int val;
382
383 va_start(args, fmt);
384 val = vget_int(this, def, fmt, args);
385 va_end(args);
386 return val;
387 }
388
389 METHOD(vici_message_t, vget_bool, bool,
390 private_vici_message_t *this, bool def, char *fmt, va_list args)
391 {
392 chunk_t value;
393 bool found;
394 char buf[16];
395
396 found = find_value(this, &value, fmt, args);
397 if (found)
398 {
399 if (value.len == 0)
400 {
401 return def;
402 }
403 if (chunk_printable(value, NULL, 0))
404 {
405 snprintf(buf, sizeof(buf), "%.*s", (int)value.len, value.ptr);
406 return settings_value_as_bool(buf, def);
407 }
408 }
409 return def;
410 }
411
412 METHOD(vici_message_t, get_bool, bool,
413 private_vici_message_t *this, bool def, char *fmt, ...)
414 {
415 va_list args;
416 bool val;
417
418 va_start(args, fmt);
419 val = vget_bool(this, def, fmt, args);
420 va_end(args);
421 return val;
422 }
423
424 METHOD(vici_message_t, vget_value, chunk_t,
425 private_vici_message_t *this, chunk_t def, char *fmt, va_list args)
426 {
427 chunk_t value;
428 bool found;
429
430 found = find_value(this, &value, fmt, args);
431 if (found)
432 {
433 return value;
434 }
435 return def;
436 }
437
438 METHOD(vici_message_t, get_value, chunk_t,
439 private_vici_message_t *this, chunk_t def, char *fmt, ...)
440 {
441 va_list args;
442 chunk_t value;
443
444 va_start(args, fmt);
445 value = vget_value(this, def, fmt, args);
446 va_end(args);
447 return value;
448 }
449
450 METHOD(vici_message_t, get_encoding, chunk_t,
451 private_vici_message_t *this)
452 {
453 return this->encoding;
454 }
455
456 /**
457 * Private parse context data
458 */
459 struct vici_parse_context_t {
460 /** current section nesting level */
461 int level;
462 /** parse enumerator */
463 enumerator_t *e;
464 };
465
466 METHOD(vici_message_t, parse, bool,
467 private_vici_message_t *this, vici_parse_context_t *ctx,
468 vici_section_cb_t section, vici_value_cb_t kv, vici_value_cb_t li,
469 void *user)
470 {
471 vici_parse_context_t root = {};
472 char *name, *list = NULL;
473 vici_type_t type;
474 chunk_t value;
475 int base;
476 bool ok = TRUE;
477
478 if (!ctx)
479 {
480 ctx = &root;
481 root.e = create_enumerator(this);
482 }
483
484 base = ctx->level;
485
486 while (ok)
487 {
488 ok = ctx->e->enumerate(ctx->e, &type, &name, &value);
489 if (ok)
490 {
491 switch (type)
492 {
493 case VICI_START:
494 /* should never occur */
495 continue;
496 case VICI_KEY_VALUE:
497 if (ctx->level == base && kv)
498 {
499 name = strdup(name);
500 this->strings->insert_last(this->strings, name);
501 ok = kv(user, &this->public, name, value);
502 }
503 continue;
504 case VICI_LIST_START:
505 if (ctx->level == base)
506 {
507 list = strdup(name);
508 this->strings->insert_last(this->strings, list);
509 }
510 continue;
511 case VICI_LIST_ITEM:
512 if (list && li)
513 {
514 name = strdup(name);
515 this->strings->insert_last(this->strings, name);
516 ok = li(user, &this->public, list, value);
517 }
518 continue;
519 case VICI_LIST_END:
520 if (ctx->level == base)
521 {
522 list = NULL;
523 }
524 continue;
525 case VICI_SECTION_START:
526 if (ctx->level++ == base && section)
527 {
528 name = strdup(name);
529 this->strings->insert_last(this->strings, name);
530 ok = section(user, &this->public, ctx, name);
531 }
532 continue;
533 case VICI_SECTION_END:
534 if (ctx->level-- == base)
535 {
536 break;
537 }
538 continue;
539 case VICI_END:
540 break;
541 }
542 }
543 break;
544 }
545
546 if (ctx == &root)
547 {
548 root.e->destroy(root.e);
549 }
550 return ok;
551 }
552
553 METHOD(vici_message_t, dump, bool,
554 private_vici_message_t *this, char *label, bool pretty, FILE *out)
555 {
556 enumerator_t *enumerator;
557 int ident = 0, delta;
558 vici_type_t type, last_type = VICI_START;
559 char *name, *term, *sep, *separ, *assign;
560 chunk_t value;
561
562 /* pretty print uses indentation on multiple lines */
563 if (pretty)
564 {
565 delta = 2;
566 term = "\n";
567 separ = "";
568 assign = " = ";
569 }
570 else
571 {
572 delta = 0;
573 term = "";
574 separ = " ";
575 assign = "=";
576 }
577
578 fprintf(out, "%s {%s", label, term);
579 ident += delta;
580
581 enumerator = create_enumerator(this);
582 while (enumerator->enumerate(enumerator, &type, &name, &value))
583 {
584 switch (type)
585 {
586 case VICI_START:
587 /* should never occur */
588 break;
589 case VICI_SECTION_START:
590 sep = (last_type != VICI_SECTION_START &&
591 last_type != VICI_START) ? separ : "";
592 fprintf(out, "%*s%s%s {%s", ident, "", sep, name, term);
593 ident += delta;
594 break;
595 case VICI_SECTION_END:
596 ident -= delta;
597 fprintf(out, "%*s}%s", ident, "", term);
598 break;
599 case VICI_KEY_VALUE:
600 sep = (last_type != VICI_SECTION_START &&
601 last_type != VICI_START) ? separ : "";
602 if (chunk_printable(value, NULL, ' '))
603 {
604 fprintf(out, "%*s%s%s%s%.*s%s", ident, "", sep, name,
605 assign, (int)value.len, value.ptr, term);
606 }
607 else
608 {
609 fprintf(out, "%*s%s%s%s0x%+#B%s", ident, "", sep, name,
610 assign, &value, term);
611 }
612 break;
613 case VICI_LIST_START:
614 sep = (last_type != VICI_SECTION_START &&
615 last_type != VICI_START) ? separ : "";
616 fprintf(out, "%*s%s%s%s[%s", ident, "", sep, name, assign, term);
617 ident += delta;
618 break;
619 case VICI_LIST_END:
620 ident -= delta;
621 fprintf(out, "%*s]%s", ident, "", term);
622 break;
623 case VICI_LIST_ITEM:
624 sep = (last_type != VICI_LIST_START) ? separ : "";
625 if (chunk_printable(value, NULL, ' '))
626 {
627 fprintf(out, "%*s%s%.*s%s", ident, "", sep,
628 (int)value.len, value.ptr, term);
629 }
630 else
631 {
632 fprintf(out, "%*s%s0x%+#B%s", ident, "", sep,
633 &value, term);
634 }
635 break;
636 case VICI_END:
637 fprintf(out, "}\n");
638 enumerator->destroy(enumerator);
639 return TRUE;
640 }
641 last_type = type;
642 }
643 enumerator->destroy(enumerator);
644 return FALSE;
645 }
646
647 METHOD(vici_message_t, destroy, void,
648 private_vici_message_t *this)
649 {
650 if (this->cleanup)
651 {
652 chunk_clear(&this->encoding);
653 }
654 this->strings->destroy_function(this->strings, free);
655 free(this);
656 }
657
658 /**
659 * See header
660 */
661 vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup)
662 {
663 private_vici_message_t *this;
664
665 INIT(this,
666 .public = {
667 .create_enumerator = _create_enumerator,
668 .get_str = _get_str,
669 .vget_str = _vget_str,
670 .get_int = _get_int,
671 .vget_int = _vget_int,
672 .get_bool = _get_bool,
673 .vget_bool = _vget_bool,
674 .get_value = _get_value,
675 .vget_value = _vget_value,
676 .get_encoding = _get_encoding,
677 .parse = _parse,
678 .dump = _dump,
679 .destroy = _destroy,
680 },
681 .strings = linked_list_create(),
682 .encoding = data,
683 .cleanup = cleanup,
684 );
685
686 return &this->public;
687 }
688
689 /**
690 * See header
691 */
692 vici_message_t *vici_message_create_from_enumerator(enumerator_t *enumerator)
693 {
694 vici_builder_t *builder;
695 vici_type_t type;
696 char *name;
697 chunk_t value;
698
699 builder = vici_builder_create();
700 while (enumerator->enumerate(enumerator, &type, &name, &value))
701 {
702 switch (type)
703 {
704 case VICI_SECTION_START:
705 case VICI_LIST_START:
706 builder->add(builder, type, name);
707 continue;
708 case VICI_KEY_VALUE:
709 builder->add(builder, type, name, value);
710 continue;
711 case VICI_LIST_ITEM:
712 builder->add(builder, type, value);
713 continue;
714 case VICI_SECTION_END:
715 case VICI_LIST_END:
716 default:
717 builder->add(builder, type);
718 continue;
719 case VICI_END:
720 break;
721 }
722 break;
723 }
724 enumerator->destroy(enumerator);
725
726 return builder->finalize(builder);
727 }
728
729 /**
730 * See header
731 */
732 vici_message_t *vici_message_create_from_args(vici_type_t type, ...)
733 {
734 vici_builder_t *builder;
735 va_list args;
736 char *name;
737 chunk_t value;
738
739 builder = vici_builder_create();
740 va_start(args, type);
741 while (type != VICI_END)
742 {
743 switch (type)
744 {
745 case VICI_LIST_START:
746 case VICI_SECTION_START:
747 name = va_arg(args, char*);
748 builder->add(builder, type, name);
749 break;
750 case VICI_KEY_VALUE:
751 name = va_arg(args, char*);
752 value = va_arg(args, chunk_t);
753 builder->add(builder, type, name, value);
754 break;
755 case VICI_LIST_ITEM:
756 value = va_arg(args, chunk_t);
757 builder->add(builder, type, value);
758 break;
759 case VICI_SECTION_END:
760 case VICI_LIST_END:
761 default:
762 builder->add(builder, type);
763 break;
764 }
765 type = va_arg(args, vici_type_t);
766 }
767 va_end(args);
768 return builder->finalize(builder);
769 }