Coverage Report

Created: 2026-05-30 06:12

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