Coverage Report

Created: 2025-04-24 06:18

/src/hostap/src/utils/json.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * JavaScript Object Notation (JSON) parser (RFC7159)
3
 * Copyright (c) 2017, Qualcomm Atheros, Inc.
4
 *
5
 * This software may be distributed under the terms of the BSD license.
6
 * See README for more details.
7
 */
8
9
#include "includes.h"
10
11
#include "common.h"
12
#include "base64.h"
13
#include "json.h"
14
15
3.77k
#define JSON_MAX_DEPTH 10
16
40.1k
#define JSON_MAX_TOKENS 500
17
18
19
void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len)
20
0
{
21
0
  char *end = txt + maxlen;
22
0
  size_t i;
23
24
0
  for (i = 0; i < len; i++) {
25
0
    if (txt + 4 >= end)
26
0
      break;
27
28
0
    switch (data[i]) {
29
0
    case '\"':
30
0
      *txt++ = '\\';
31
0
      *txt++ = '\"';
32
0
      break;
33
0
    case '\\':
34
0
      *txt++ = '\\';
35
0
      *txt++ = '\\';
36
0
      break;
37
0
    case '\n':
38
0
      *txt++ = '\\';
39
0
      *txt++ = 'n';
40
0
      break;
41
0
    case '\r':
42
0
      *txt++ = '\\';
43
0
      *txt++ = 'r';
44
0
      break;
45
0
    case '\t':
46
0
      *txt++ = '\\';
47
0
      *txt++ = 't';
48
0
      break;
49
0
    default:
50
0
      if (data[i] >= 32 && data[i] <= 126) {
51
0
        *txt++ = data[i];
52
0
      } else {
53
0
        txt += os_snprintf(txt, end - txt, "\\u%04x",
54
0
               (unsigned char) data[i]);
55
0
      }
56
0
      break;
57
0
    }
58
0
  }
59
60
0
  *txt = '\0';
61
0
}
62
63
64
static char * json_parse_string(const char **json_pos, const char *end)
65
5.00k
{
66
5.00k
  const char *pos = *json_pos;
67
5.00k
  char *str, *spos, *s_end;
68
5.00k
  size_t max_len, buf_len;
69
5.00k
  u8 bin[2];
70
71
5.00k
  pos++; /* skip starting quote */
72
73
5.00k
  max_len = end - pos + 1;
74
5.00k
  buf_len = max_len > 10 ? 10 : max_len;
75
5.00k
  str = os_malloc(buf_len);
76
5.00k
  if (!str)
77
0
    return NULL;
78
5.00k
  spos = str;
79
5.00k
  s_end = str + buf_len;
80
81
16.6M
  for (; pos < end; pos++) {
82
16.6M
    if (buf_len < max_len && s_end - spos < 3) {
83
9.58k
      char *tmp;
84
9.58k
      int idx;
85
86
9.58k
      idx = spos - str;
87
9.58k
      buf_len *= 2;
88
9.58k
      if (buf_len > max_len)
89
149
        buf_len = max_len;
90
9.58k
      tmp = os_realloc(str, buf_len);
91
9.58k
      if (!tmp)
92
0
        goto fail;
93
9.58k
      str = tmp;
94
9.58k
      spos = str + idx;
95
9.58k
      s_end = str + buf_len;
96
9.58k
    }
97
98
16.6M
    switch (*pos) {
99
4.70k
    case '\"': /* end string */
100
4.70k
      *spos = '\0';
101
      /* caller will move to the next position */
102
4.70k
      *json_pos = pos;
103
4.70k
      return str;
104
29.6k
    case '\\':
105
29.6k
      pos++;
106
29.6k
      if (pos >= end) {
107
4
        wpa_printf(MSG_DEBUG,
108
4
             "JSON: Truncated \\ escape");
109
4
        goto fail;
110
4
      }
111
29.6k
      switch (*pos) {
112
3.42k
      case '"':
113
5.32k
      case '\\':
114
5.91k
      case '/':
115
5.91k
        *spos++ = *pos;
116
5.91k
        break;
117
751
      case 'n':
118
751
        *spos++ = '\n';
119
751
        break;
120
2.18k
      case 'r':
121
2.18k
        *spos++ = '\r';
122
2.18k
        break;
123
606
      case 't':
124
606
        *spos++ = '\t';
125
606
        break;
126
20.2k
      case 'u':
127
20.2k
        if (end - pos < 5 ||
128
20.2k
            hexstr2bin(pos + 1, bin, 2) < 0 ||
129
20.2k
            bin[1] == 0x00) {
130
88
          wpa_printf(MSG_DEBUG,
131
88
               "JSON: Invalid \\u escape");
132
88
          goto fail;
133
88
        }
134
20.1k
        if (bin[0] == 0x00) {
135
1.79k
          *spos++ = bin[1];
136
18.3k
        } else {
137
18.3k
          *spos++ = bin[0];
138
18.3k
          *spos++ = bin[1];
139
18.3k
        }
140
20.1k
        pos += 4;
141
20.1k
        break;
142
12
      default:
143
12
        wpa_printf(MSG_DEBUG,
144
12
             "JSON: Unknown escape '%c'", *pos);
145
12
        goto fail;
146
29.6k
      }
147
29.5k
      break;
148
16.6M
    default:
149
16.6M
      *spos++ = *pos;
150
16.6M
      break;
151
16.6M
    }
152
16.6M
  }
153
154
298
fail:
155
298
  os_free(str);
156
298
  return NULL;
157
5.00k
}
158
159
160
static int json_parse_number(const char **json_pos, const char *end,
161
           int *ret_val)
162
15.6k
{
163
15.6k
  const char *pos = *json_pos;
164
15.6k
  size_t len;
165
15.6k
  char *str;
166
167
31.9k
  for (; pos < end; pos++) {
168
31.7k
    if (*pos != '-' && (*pos < '0' || *pos > '9')) {
169
15.5k
      pos--;
170
15.5k
      break;
171
15.5k
    }
172
31.7k
  }
173
15.6k
  if (pos == end)
174
157
    pos--;
175
15.6k
  if (pos < *json_pos)
176
0
    return -1;
177
15.6k
  len = pos - *json_pos + 1;
178
15.6k
  str = os_malloc(len + 1);
179
15.6k
  if (!str)
180
0
    return -1;
181
15.6k
  os_memcpy(str, *json_pos, len);
182
15.6k
  str[len] = '\0';
183
184
15.6k
  *ret_val = atoi(str);
185
15.6k
  os_free(str);
186
15.6k
  *json_pos = pos;
187
15.6k
  return 0;
188
15.6k
}
189
190
191
static int json_check_tree_state(struct json_token *token)
192
64.5k
{
193
64.5k
  if (!token)
194
32.2k
    return 0;
195
32.2k
  if (json_check_tree_state(token->child) < 0 ||
196
32.2k
      json_check_tree_state(token->sibling) < 0)
197
15.6k
    return -1;
198
16.5k
  if (token->state != JSON_COMPLETED) {
199
268
    wpa_printf(MSG_DEBUG,
200
268
         "JSON: Unexpected token state %d (name=%s type=%d)",
201
268
         token->state, token->name ? token->name : "N/A",
202
268
         token->type);
203
268
    return -1;
204
268
  }
205
16.3k
  return 0;
206
16.5k
}
207
208
209
static struct json_token * json_alloc_token(unsigned int *tokens)
210
40.1k
{
211
40.1k
  (*tokens)++;
212
40.1k
  if (*tokens > JSON_MAX_TOKENS) {
213
2
    wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded");
214
2
    return NULL;
215
2
  }
216
40.1k
  return os_zalloc(sizeof(struct json_token));
217
40.1k
}
218
219
220
struct json_token * json_parse(const char *data, size_t data_len)
221
1.05k
{
222
1.05k
  struct json_token *root = NULL, *curr_token = NULL, *token = NULL;
223
1.05k
  const char *pos, *end;
224
1.05k
  char *str;
225
1.05k
  int num;
226
1.05k
  unsigned int depth = 0;
227
1.05k
  unsigned int tokens = 0;
228
229
1.05k
  pos = data;
230
1.05k
  end = data + data_len;
231
232
70.5k
  for (; pos < end; pos++) {
233
70.1k
    switch (*pos) {
234
2.59k
    case '[': /* start array */
235
3.78k
    case '{': /* start object */
236
3.78k
      if (!curr_token) {
237
425
        token = json_alloc_token(&tokens);
238
425
        if (!token)
239
0
          goto fail;
240
425
        if (!root)
241
425
          root = token;
242
3.35k
      } else if (curr_token->state == JSON_WAITING_VALUE) {
243
447
        token = curr_token;
244
2.91k
      } else if (curr_token->parent &&
245
2.91k
           curr_token->parent->type == JSON_ARRAY &&
246
2.91k
           curr_token->parent->state == JSON_STARTED &&
247
2.91k
           curr_token->state == JSON_EMPTY) {
248
2.90k
        token = curr_token;
249
2.90k
      } else {
250
10
        wpa_printf(MSG_DEBUG,
251
10
             "JSON: Invalid state for start array/object");
252
10
        goto fail;
253
10
      }
254
3.77k
      depth++;
255
3.77k
      if (depth > JSON_MAX_DEPTH) {
256
10
        wpa_printf(MSG_DEBUG,
257
10
             "JSON: Max depth exceeded");
258
10
        goto fail;
259
10
      }
260
3.76k
      token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT;
261
3.76k
      token->state = JSON_STARTED;
262
3.76k
      token->child = json_alloc_token(&tokens);
263
3.76k
      if (!token->child)
264
1
        goto fail;
265
3.76k
      curr_token = token->child;
266
3.76k
      curr_token->parent = token;
267
3.76k
      curr_token->state = JSON_EMPTY;
268
3.76k
      break;
269
1.94k
    case ']': /* end array */
270
2.99k
    case '}': /* end object */
271
2.99k
      if (!curr_token || !curr_token->parent ||
272
2.99k
          curr_token->parent->state != JSON_STARTED ||
273
2.99k
          depth == 0) {
274
11
        wpa_printf(MSG_DEBUG,
275
11
             "JSON: Invalid state for end array/object");
276
11
        goto fail;
277
11
      }
278
2.98k
      depth--;
279
2.98k
      curr_token = curr_token->parent;
280
2.98k
      if ((*pos == ']' &&
281
2.98k
           curr_token->type != JSON_ARRAY) ||
282
2.98k
          (*pos == '}' &&
283
2.98k
           curr_token->type != JSON_OBJECT)) {
284
9
        wpa_printf(MSG_DEBUG,
285
9
             "JSON: Array/Object mismatch");
286
9
        goto fail;
287
9
      }
288
2.97k
      if (curr_token->child->state == JSON_EMPTY &&
289
2.97k
          !curr_token->child->child &&
290
2.97k
          !curr_token->child->sibling) {
291
        /* Remove pending child token since the
292
         * array/object was empty. */
293
859
        json_free(curr_token->child);
294
859
        curr_token->child = NULL;
295
859
      }
296
2.97k
      curr_token->state = JSON_COMPLETED;
297
2.97k
      break;
298
5.00k
    case '\"': /* string */
299
5.00k
      str = json_parse_string(&pos, end);
300
5.00k
      if (!str)
301
298
        goto fail;
302
4.70k
      if (!curr_token) {
303
56
        token = json_alloc_token(&tokens);
304
56
        if (!token) {
305
0
          os_free(str);
306
0
          goto fail;
307
0
        }
308
56
        token->type = JSON_STRING;
309
56
        token->string = str;
310
56
        token->state = JSON_COMPLETED;
311
4.65k
      } else if (curr_token->parent &&
312
4.65k
           curr_token->parent->type == JSON_ARRAY &&
313
4.65k
           curr_token->parent->state == JSON_STARTED &&
314
4.65k
           curr_token->state == JSON_EMPTY) {
315
251
        curr_token->string = str;
316
251
        curr_token->state = JSON_COMPLETED;
317
251
        curr_token->type = JSON_STRING;
318
251
        wpa_printf(MSG_MSGDUMP,
319
251
             "JSON: String value: '%s'",
320
251
             curr_token->string);
321
4.40k
      } else if (curr_token->state == JSON_EMPTY) {
322
3.87k
        curr_token->type = JSON_VALUE;
323
3.87k
        curr_token->name = str;
324
3.87k
        curr_token->state = JSON_STARTED;
325
3.87k
      } else if (curr_token->state == JSON_WAITING_VALUE) {
326
490
        curr_token->string = str;
327
490
        curr_token->state = JSON_COMPLETED;
328
490
        curr_token->type = JSON_STRING;
329
490
        wpa_printf(MSG_MSGDUMP,
330
490
             "JSON: String value: '%s' = '%s'",
331
490
             curr_token->name,
332
490
             curr_token->string);
333
490
      } else {
334
34
        wpa_printf(MSG_DEBUG,
335
34
             "JSON: Invalid state for a string");
336
34
        os_free(str);
337
34
        goto fail;
338
34
      }
339
4.67k
      break;
340
4.67k
    case ' ':
341
529
    case '\t':
342
740
    case '\r':
343
1.06k
    case '\n':
344
      /* ignore whitespace */
345
1.06k
      break;
346
3.28k
    case ':': /* name/value separator */
347
3.28k
      if (!curr_token || curr_token->state != JSON_STARTED)
348
5
        goto fail;
349
3.27k
      curr_token->state = JSON_WAITING_VALUE;
350
3.27k
      break;
351
35.7k
    case ',': /* member separator */
352
35.7k
      if (!curr_token)
353
1
        goto fail;
354
35.7k
      curr_token->sibling = json_alloc_token(&tokens);
355
35.7k
      if (!curr_token->sibling)
356
1
        goto fail;
357
35.7k
      curr_token->sibling->parent = curr_token->parent;
358
35.7k
      curr_token = curr_token->sibling;
359
35.7k
      curr_token->state = JSON_EMPTY;
360
35.7k
      break;
361
826
    case 't': /* true */
362
1.48k
    case 'f': /* false */
363
2.54k
    case 'n': /* null */
364
2.54k
      if (!((end - pos >= 4 &&
365
2.54k
             os_strncmp(pos, "true", 4) == 0) ||
366
2.54k
            (end - pos >= 5 &&
367
1.76k
             os_strncmp(pos, "false", 5) == 0) ||
368
2.54k
            (end - pos >= 4 &&
369
1.16k
             os_strncmp(pos, "null", 4) == 0))) {
370
138
        wpa_printf(MSG_DEBUG,
371
138
             "JSON: Invalid literal name");
372
138
        goto fail;
373
138
      }
374
2.40k
      if (!curr_token) {
375
39
        token = json_alloc_token(&tokens);
376
39
        if (!token)
377
0
          goto fail;
378
39
        curr_token = token;
379
2.36k
      } else if (curr_token->state == JSON_WAITING_VALUE) {
380
450
        wpa_printf(MSG_MSGDUMP,
381
450
             "JSON: Literal name: '%s' = %c",
382
450
             curr_token->name, *pos);
383
1.91k
      } else if (curr_token->parent &&
384
1.91k
           curr_token->parent->type == JSON_ARRAY &&
385
1.91k
           curr_token->parent->state == JSON_STARTED &&
386
1.91k
           curr_token->state == JSON_EMPTY) {
387
1.88k
        wpa_printf(MSG_MSGDUMP,
388
1.88k
             "JSON: Literal name: %c", *pos);
389
1.88k
      } else {
390
31
        wpa_printf(MSG_DEBUG,
391
31
             "JSON: Invalid state for a literal name");
392
31
        goto fail;
393
31
      }
394
2.37k
      switch (*pos) {
395
770
      case 't':
396
770
        curr_token->type = JSON_BOOLEAN;
397
770
        curr_token->number = 1;
398
770
        pos += 3;
399
770
        break;
400
596
      case 'f':
401
596
        curr_token->type = JSON_BOOLEAN;
402
596
        curr_token->number = 0;
403
596
        pos += 4;
404
596
        break;
405
1.01k
      case 'n':
406
1.01k
        curr_token->type = JSON_NULL;
407
1.01k
        pos += 3;
408
1.01k
        break;
409
2.37k
      }
410
2.37k
      curr_token->state = JSON_COMPLETED;
411
2.37k
      break;
412
821
    case '-':
413
2.34k
    case '0':
414
3.86k
    case '1':
415
4.93k
    case '2':
416
5.95k
    case '3':
417
7.38k
    case '4':
418
9.05k
    case '5':
419
9.75k
    case '6':
420
13.8k
    case '7':
421
15.1k
    case '8':
422
15.6k
    case '9':
423
      /* number */
424
15.6k
      if (json_parse_number(&pos, end, &num) < 0)
425
0
        goto fail;
426
15.6k
      if (!curr_token) {
427
125
        token = json_alloc_token(&tokens);
428
125
        if (!token)
429
0
          goto fail;
430
125
        token->type = JSON_NUMBER;
431
125
        token->number = num;
432
125
        token->state = JSON_COMPLETED;
433
15.5k
      } else if (curr_token->state == JSON_WAITING_VALUE) {
434
1.72k
        curr_token->number = num;
435
1.72k
        curr_token->state = JSON_COMPLETED;
436
1.72k
        curr_token->type = JSON_NUMBER;
437
1.72k
        wpa_printf(MSG_MSGDUMP,
438
1.72k
             "JSON: Number value: '%s' = '%d'",
439
1.72k
             curr_token->name,
440
1.72k
             curr_token->number);
441
13.8k
      } else if (curr_token->parent &&
442
13.8k
           curr_token->parent->type == JSON_ARRAY &&
443
13.8k
           curr_token->parent->state == JSON_STARTED &&
444
13.8k
           curr_token->state == JSON_EMPTY) {
445
13.7k
        curr_token->number = num;
446
13.7k
        curr_token->state = JSON_COMPLETED;
447
13.7k
        curr_token->type = JSON_NUMBER;
448
13.7k
        wpa_printf(MSG_MSGDUMP,
449
13.7k
             "JSON: Number value: %d",
450
13.7k
             curr_token->number);
451
13.7k
      } else {
452
30
        wpa_printf(MSG_DEBUG,
453
30
             "JSON: Invalid state for a number");
454
30
        goto fail;
455
30
      }
456
15.6k
      break;
457
15.6k
    default:
458
38
      wpa_printf(MSG_DEBUG,
459
38
           "JSON: Unexpected JSON character: %c", *pos);
460
38
      goto fail;
461
70.1k
    }
462
463
69.5k
    if (!root)
464
1.14k
      root = token;
465
69.5k
    if (!curr_token)
466
1.11k
      curr_token = token;
467
69.5k
  }
468
469
434
  if (json_check_tree_state(root) < 0) {
470
268
    wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
471
268
    goto fail;
472
268
  }
473
474
166
  return root;
475
885
fail:
476
885
  wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
477
885
  json_free(root);
478
885
  return NULL;
479
434
}
480
481
482
void json_free(struct json_token *json)
483
82.2k
{
484
82.2k
  if (!json)
485
42.0k
    return;
486
40.1k
  json_free(json->child);
487
40.1k
  json_free(json->sibling);
488
40.1k
  os_free(json->name);
489
40.1k
  os_free(json->string);
490
40.1k
  os_free(json);
491
40.1k
}
492
493
494
struct json_token * json_get_member(struct json_token *json, const char *name)
495
0
{
496
0
  struct json_token *token, *ret = NULL;
497
498
0
  if (!json || json->type != JSON_OBJECT)
499
0
    return NULL;
500
  /* Return last matching entry */
501
0
  for (token = json->child; token; token = token->sibling) {
502
0
    if (token->name && os_strcmp(token->name, name) == 0)
503
0
      ret = token;
504
0
  }
505
0
  return ret;
506
0
}
507
508
509
struct wpabuf * json_get_member_base64url(struct json_token *json,
510
            const char *name)
511
0
{
512
0
  struct json_token *token;
513
0
  unsigned char *buf;
514
0
  size_t buflen;
515
0
  struct wpabuf *ret;
516
517
0
  token = json_get_member(json, name);
518
0
  if (!token || token->type != JSON_STRING)
519
0
    return NULL;
520
0
  buf = base64_url_decode(token->string, os_strlen(token->string),
521
0
        &buflen);
522
0
  if (!buf)
523
0
    return NULL;
524
0
  ret = wpabuf_alloc_ext_data(buf, buflen);
525
0
  if (!ret)
526
0
    os_free(buf);
527
528
0
  return ret;
529
0
}
530
531
532
struct wpabuf * json_get_member_base64(struct json_token *json,
533
               const char *name)
534
0
{
535
0
  struct json_token *token;
536
0
  unsigned char *buf;
537
0
  size_t buflen;
538
0
  struct wpabuf *ret;
539
540
0
  token = json_get_member(json, name);
541
0
  if (!token || token->type != JSON_STRING)
542
0
    return NULL;
543
0
  buf = base64_decode(token->string, os_strlen(token->string), &buflen);
544
0
  if (!buf)
545
0
    return NULL;
546
0
  ret = wpabuf_alloc_ext_data(buf, buflen);
547
0
  if (!ret)
548
0
    os_free(buf);
549
550
0
  return ret;
551
0
}
552
553
554
static const char * json_type_str(enum json_type type)
555
12.1k
{
556
12.1k
  switch (type) {
557
0
  case JSON_VALUE:
558
0
    return "VALUE";
559
288
  case JSON_OBJECT:
560
288
    return "OBJECT";
561
872
  case JSON_ARRAY:
562
872
    return "ARRAY";
563
87
  case JSON_STRING:
564
87
    return "STRING";
565
10.7k
  case JSON_NUMBER:
566
10.7k
    return "NUMBER";
567
5
  case JSON_BOOLEAN:
568
5
    return "BOOLEAN";
569
135
  case JSON_NULL:
570
135
    return "NULL";
571
12.1k
  }
572
0
  return "??";
573
12.1k
}
574
575
576
static void json_print_token(struct json_token *token, int depth,
577
           char *buf, size_t buflen)
578
24.0k
{
579
24.0k
  size_t len;
580
24.0k
  int ret;
581
582
24.0k
  if (!token)
583
11.8k
    return;
584
12.1k
  len = os_strlen(buf);
585
12.1k
  ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
586
12.1k
        depth, json_type_str(token->type),
587
12.1k
        token->name ? token->name : "");
588
12.1k
  if (os_snprintf_error(buflen - len, ret)) {
589
208
    buf[len] = '\0';
590
208
    return;
591
208
  }
592
11.9k
  json_print_token(token->child, depth + 1, buf, buflen);
593
11.9k
  json_print_token(token->sibling, depth, buf, buflen);
594
11.9k
}
595
596
597
void json_print_tree(struct json_token *root, char *buf, size_t buflen)
598
131
{
599
131
  buf[0] = '\0';
600
131
  json_print_token(root, 1, buf, buflen);
601
131
}
602
603
604
void json_add_int(struct wpabuf *json, const char *name, int val)
605
0
{
606
0
  wpabuf_printf(json, "\"%s\":%d", name, val);
607
0
}
608
609
610
void json_add_string(struct wpabuf *json, const char *name, const char *val)
611
0
{
612
0
  wpabuf_printf(json, "\"%s\":\"%s\"", name, val);
613
0
}
614
615
616
int json_add_string_escape(struct wpabuf *json, const char *name,
617
         const void *val, size_t len)
618
0
{
619
0
  char *tmp;
620
0
  size_t tmp_len = 6 * len + 1;
621
622
0
  tmp = os_malloc(tmp_len);
623
0
  if (!tmp)
624
0
    return -1;
625
0
  json_escape_string(tmp, tmp_len, val, len);
626
0
  json_add_string(json, name, tmp);
627
0
  bin_clear_free(tmp, tmp_len);
628
0
  return 0;
629
0
}
630
631
632
int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
633
           size_t len)
634
0
{
635
0
  char *b64;
636
637
0
  b64 = base64_url_encode(val, len, NULL);
638
0
  if (!b64)
639
0
    return -1;
640
0
  json_add_string(json, name, b64);
641
0
  os_free(b64);
642
0
  return 0;
643
0
}
644
645
646
int json_add_base64(struct wpabuf *json, const char *name, const void *val,
647
        size_t len)
648
0
{
649
0
  char *b64;
650
651
0
  b64 = base64_encode_no_lf(val, len, NULL);
652
0
  if (!b64)
653
0
    return -1;
654
0
  json_add_string(json, name, b64);
655
0
  os_free(b64);
656
0
  return 0;
657
0
}
658
659
660
void json_start_object(struct wpabuf *json, const char *name)
661
0
{
662
0
  if (name)
663
0
    wpabuf_printf(json, "\"%s\":", name);
664
0
  wpabuf_put_u8(json, '{');
665
0
}
666
667
668
void json_end_object(struct wpabuf *json)
669
0
{
670
0
  wpabuf_put_u8(json, '}');
671
0
}
672
673
674
void json_start_array(struct wpabuf *json, const char *name)
675
0
{
676
0
  if (name)
677
0
    wpabuf_printf(json, "\"%s\":", name);
678
0
  wpabuf_put_u8(json, '[');
679
0
}
680
681
682
void json_end_array(struct wpabuf *json)
683
0
{
684
0
  wpabuf_put_u8(json, ']');
685
0
}
686
687
688
void json_value_sep(struct wpabuf *json)
689
0
{
690
0
  wpabuf_put_u8(json, ',');
691
0
}