vici: Return default value for get_int() if message value is empty string
[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 (value.len == 0)
359 {
360 return def;
361 }
362 if (chunk_printable(value, NULL, 0))
363 {
364 snprintf(buf, sizeof(buf), "%.*s", (int)value.len, value.ptr);
365 errno = 0;
366 ret = strtol(buf, &pos, 0);
367 if (errno == 0 && pos == buf + strlen(buf))
368 {
369 return ret;
370 }
371 }
372 }
373 return def;
374 }
375
376 METHOD(vici_message_t, get_int, int,
377 private_vici_message_t *this, int def, char *fmt, ...)
378 {
379 va_list args;
380 int val;
381
382 va_start(args, fmt);
383 val = vget_int(this, def, fmt, args);
384 va_end(args);
385 return val;
386 }
387
388 METHOD(vici_message_t, vget_value, chunk_t,
389 private_vici_message_t *this, chunk_t def, char *fmt, va_list args)
390 {
391 chunk_t value;
392 bool found;
393
394 found = find_value(this, &value, fmt, args);
395 if (found)
396 {
397 return value;
398 }
399 return def;
400 }
401
402 METHOD(vici_message_t, get_value, chunk_t,
403 private_vici_message_t *this, chunk_t def, char *fmt, ...)
404 {
405 va_list args;
406 chunk_t value;
407
408 va_start(args, fmt);
409 value = vget_value(this, def, fmt, args);
410 va_end(args);
411 return value;
412 }
413
414 METHOD(vici_message_t, get_encoding, chunk_t,
415 private_vici_message_t *this)
416 {
417 return this->encoding;
418 }
419
420 /**
421 * Private parse context data
422 */
423 struct vici_parse_context_t {
424 /** current section nesting level */
425 int level;
426 /** parse enumerator */
427 enumerator_t *e;
428 };
429
430 METHOD(vici_message_t, parse, bool,
431 private_vici_message_t *this, vici_parse_context_t *ctx,
432 vici_section_cb_t section, vici_value_cb_t kv, vici_value_cb_t li,
433 void *user)
434 {
435 vici_parse_context_t root = {};
436 char *name, *list = NULL;
437 vici_type_t type;
438 chunk_t value;
439 int base;
440 bool ok = TRUE;
441
442 if (!ctx)
443 {
444 ctx = &root;
445 root.e = create_enumerator(this);
446 }
447
448 base = ctx->level;
449
450 while (ok)
451 {
452 ok = ctx->e->enumerate(ctx->e, &type, &name, &value);
453 if (ok)
454 {
455 switch (type)
456 {
457 case VICI_START:
458 /* should never occur */
459 continue;
460 case VICI_KEY_VALUE:
461 if (ctx->level == base && kv)
462 {
463 name = strdup(name);
464 this->strings->insert_last(this->strings, name);
465 ok = kv(user, &this->public, name, value);
466 }
467 continue;
468 case VICI_LIST_START:
469 if (ctx->level == base)
470 {
471 list = strdup(name);
472 this->strings->insert_last(this->strings, list);
473 }
474 continue;
475 case VICI_LIST_ITEM:
476 if (list && li)
477 {
478 name = strdup(name);
479 this->strings->insert_last(this->strings, name);
480 ok = li(user, &this->public, list, value);
481 }
482 continue;
483 case VICI_LIST_END:
484 if (ctx->level == base)
485 {
486 list = NULL;
487 }
488 continue;
489 case VICI_SECTION_START:
490 if (ctx->level++ == base && section)
491 {
492 name = strdup(name);
493 this->strings->insert_last(this->strings, name);
494 ok = section(user, &this->public, ctx, name);
495 }
496 continue;
497 case VICI_SECTION_END:
498 if (ctx->level-- == base)
499 {
500 break;
501 }
502 continue;
503 case VICI_END:
504 break;
505 }
506 }
507 break;
508 }
509
510 if (ctx == &root)
511 {
512 root.e->destroy(root.e);
513 }
514 return ok;
515 }
516
517 METHOD(vici_message_t, dump, bool,
518 private_vici_message_t *this, char *label, bool pretty, FILE *out)
519 {
520 enumerator_t *enumerator;
521 int ident = 0, delta;
522 vici_type_t type, last_type = VICI_START;
523 char *name, *term, *sep, *separ, *assign;
524 chunk_t value;
525
526 /* pretty print uses indentation on multiple lines */
527 if (pretty)
528 {
529 delta = 2;
530 term = "\n";
531 separ = "";
532 assign = " = ";
533 }
534 else
535 {
536 delta = 0;
537 term = "";
538 separ = " ";
539 assign = "=";
540 }
541
542 fprintf(out, "%s {%s", label, term);
543 ident += delta;
544
545 enumerator = create_enumerator(this);
546 while (enumerator->enumerate(enumerator, &type, &name, &value))
547 {
548 switch (type)
549 {
550 case VICI_START:
551 /* should never occur */
552 break;
553 case VICI_SECTION_START:
554 sep = (last_type != VICI_SECTION_START &&
555 last_type != VICI_START) ? separ : "";
556 fprintf(out, "%*s%s%s {%s", ident, "", sep, name, term);
557 ident += delta;
558 break;
559 case VICI_SECTION_END:
560 ident -= delta;
561 fprintf(out, "%*s}%s", ident, "", term);
562 break;
563 case VICI_KEY_VALUE:
564 sep = (last_type != VICI_SECTION_START &&
565 last_type != VICI_START) ? separ : "";
566 if (chunk_printable(value, NULL, ' '))
567 {
568 fprintf(out, "%*s%s%s%s%.*s%s", ident, "", sep, name,
569 assign, (int)value.len, value.ptr, term);
570 }
571 else
572 {
573 fprintf(out, "%*s%s%s%s0x%+#B%s", ident, "", sep, name,
574 assign, &value, term);
575 }
576 break;
577 case VICI_LIST_START:
578 sep = (last_type != VICI_SECTION_START &&
579 last_type != VICI_START) ? separ : "";
580 fprintf(out, "%*s%s%s%s[%s", ident, "", sep, name, assign, term);
581 ident += delta;
582 break;
583 case VICI_LIST_END:
584 ident -= delta;
585 fprintf(out, "%*s]%s", ident, "", term);
586 break;
587 case VICI_LIST_ITEM:
588 sep = (last_type != VICI_LIST_START) ? separ : "";
589 if (chunk_printable(value, NULL, ' '))
590 {
591 fprintf(out, "%*s%s%.*s%s", ident, "", sep,
592 (int)value.len, value.ptr, term);
593 }
594 else
595 {
596 fprintf(out, "%*s%s0x%+#B%s", ident, "", sep,
597 &value, term);
598 }
599 break;
600 case VICI_END:
601 fprintf(out, "}\n");
602 enumerator->destroy(enumerator);
603 return TRUE;
604 }
605 last_type = type;
606 }
607 enumerator->destroy(enumerator);
608 return FALSE;
609 }
610
611 METHOD(vici_message_t, destroy, void,
612 private_vici_message_t *this)
613 {
614 if (this->cleanup)
615 {
616 chunk_clear(&this->encoding);
617 }
618 this->strings->destroy_function(this->strings, free);
619 free(this);
620 }
621
622 /**
623 * See header
624 */
625 vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup)
626 {
627 private_vici_message_t *this;
628
629 INIT(this,
630 .public = {
631 .create_enumerator = _create_enumerator,
632 .get_str = _get_str,
633 .vget_str = _vget_str,
634 .get_int = _get_int,
635 .vget_int = _vget_int,
636 .get_value = _get_value,
637 .vget_value = _vget_value,
638 .get_encoding = _get_encoding,
639 .parse = _parse,
640 .dump = _dump,
641 .destroy = _destroy,
642 },
643 .strings = linked_list_create(),
644 .encoding = data,
645 .cleanup = cleanup,
646 );
647
648 return &this->public;
649 }
650
651 /**
652 * See header
653 */
654 vici_message_t *vici_message_create_from_enumerator(enumerator_t *enumerator)
655 {
656 vici_builder_t *builder;
657 vici_type_t type;
658 char *name;
659 chunk_t value;
660
661 builder = vici_builder_create();
662 while (enumerator->enumerate(enumerator, &type, &name, &value))
663 {
664 switch (type)
665 {
666 case VICI_SECTION_START:
667 case VICI_LIST_START:
668 builder->add(builder, type, name);
669 continue;
670 case VICI_KEY_VALUE:
671 builder->add(builder, type, name, value);
672 continue;
673 case VICI_LIST_ITEM:
674 builder->add(builder, type, value);
675 continue;
676 case VICI_SECTION_END:
677 case VICI_LIST_END:
678 default:
679 builder->add(builder, type);
680 continue;
681 case VICI_END:
682 break;
683 }
684 break;
685 }
686 enumerator->destroy(enumerator);
687
688 return builder->finalize(builder);
689 }
690
691 /**
692 * See header
693 */
694 vici_message_t *vici_message_create_from_args(vici_type_t type, ...)
695 {
696 vici_builder_t *builder;
697 va_list args;
698 char *name;
699 chunk_t value;
700
701 builder = vici_builder_create();
702 va_start(args, type);
703 while (type != VICI_END)
704 {
705 switch (type)
706 {
707 case VICI_LIST_START:
708 case VICI_SECTION_START:
709 name = va_arg(args, char*);
710 builder->add(builder, type, name);
711 break;
712 case VICI_KEY_VALUE:
713 name = va_arg(args, char*);
714 value = va_arg(args, chunk_t);
715 builder->add(builder, type, name, value);
716 break;
717 case VICI_LIST_ITEM:
718 value = va_arg(args, chunk_t);
719 builder->add(builder, type, value);
720 break;
721 case VICI_SECTION_END:
722 case VICI_LIST_END:
723 default:
724 builder->add(builder, type);
725 break;
726 }
727 type = va_arg(args, vici_type_t);
728 }
729 va_end(args);
730 return builder->finalize(builder);
731 }