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