xref: /freebsd/contrib/wpa/src/utils/json.c (revision 71e72c9e91c4b8007a4292e09669e8b549c29e97)
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 #define JSON_MAX_DEPTH 10
16 #define JSON_MAX_TOKENS 500
17 
18 
json_escape_string(char * txt,size_t maxlen,const char * data,size_t len)19 void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len)
20 {
21 	char *end = txt + maxlen;
22 	size_t i;
23 
24 	for (i = 0; i < len; i++) {
25 		if (txt + 4 >= end)
26 			break;
27 
28 		switch (data[i]) {
29 		case '\"':
30 			*txt++ = '\\';
31 			*txt++ = '\"';
32 			break;
33 		case '\\':
34 			*txt++ = '\\';
35 			*txt++ = '\\';
36 			break;
37 		case '\n':
38 			*txt++ = '\\';
39 			*txt++ = 'n';
40 			break;
41 		case '\r':
42 			*txt++ = '\\';
43 			*txt++ = 'r';
44 			break;
45 		case '\t':
46 			*txt++ = '\\';
47 			*txt++ = 't';
48 			break;
49 		default:
50 			if (data[i] >= 32 && data[i] <= 126) {
51 				*txt++ = data[i];
52 			} else {
53 				txt += os_snprintf(txt, end - txt, "\\u%04x",
54 						   (unsigned char) data[i]);
55 			}
56 			break;
57 		}
58 	}
59 
60 	*txt = '\0';
61 }
62 
63 
json_parse_string(const char ** json_pos,const char * end)64 static char * json_parse_string(const char **json_pos, const char *end)
65 {
66 	const char *pos = *json_pos;
67 	char *str, *spos, *s_end;
68 	size_t max_len, buf_len;
69 	u8 bin[2];
70 
71 	pos++; /* skip starting quote */
72 
73 	max_len = end - pos + 1;
74 	buf_len = max_len > 10 ? 10 : max_len;
75 	str = os_malloc(buf_len);
76 	if (!str)
77 		return NULL;
78 	spos = str;
79 	s_end = str + buf_len;
80 
81 	for (; pos < end; pos++) {
82 		if (buf_len < max_len && s_end - spos < 3) {
83 			char *tmp;
84 			int idx;
85 
86 			idx = spos - str;
87 			buf_len *= 2;
88 			if (buf_len > max_len)
89 				buf_len = max_len;
90 			tmp = os_realloc(str, buf_len);
91 			if (!tmp)
92 				goto fail;
93 			str = tmp;
94 			spos = str + idx;
95 			s_end = str + buf_len;
96 		}
97 
98 		switch (*pos) {
99 		case '\"': /* end string */
100 			*spos = '\0';
101 			/* caller will move to the next position */
102 			*json_pos = pos;
103 			return str;
104 		case '\\':
105 			pos++;
106 			if (pos >= end) {
107 				wpa_printf(MSG_DEBUG,
108 					   "JSON: Truncated \\ escape");
109 				goto fail;
110 			}
111 			switch (*pos) {
112 			case '"':
113 			case '\\':
114 			case '/':
115 				*spos++ = *pos;
116 				break;
117 			case 'n':
118 				*spos++ = '\n';
119 				break;
120 			case 'r':
121 				*spos++ = '\r';
122 				break;
123 			case 't':
124 				*spos++ = '\t';
125 				break;
126 			case 'u':
127 				if (end - pos < 5 ||
128 				    hexstr2bin(pos + 1, bin, 2) < 0 ||
129 				    bin[1] == 0x00) {
130 					wpa_printf(MSG_DEBUG,
131 						   "JSON: Invalid \\u escape");
132 					goto fail;
133 				}
134 				if (bin[0] == 0x00) {
135 					*spos++ = bin[1];
136 				} else {
137 					*spos++ = bin[0];
138 					*spos++ = bin[1];
139 				}
140 				pos += 4;
141 				break;
142 			default:
143 				wpa_printf(MSG_DEBUG,
144 					   "JSON: Unknown escape '%c'", *pos);
145 				goto fail;
146 			}
147 			break;
148 		default:
149 			*spos++ = *pos;
150 			break;
151 		}
152 	}
153 
154 fail:
155 	os_free(str);
156 	return NULL;
157 }
158 
159 
json_get_number_len(const char * json_pos,const char * end,bool * is_double)160 static size_t json_get_number_len(const char *json_pos, const char *end,
161 				  bool *is_double)
162 {
163 	const char *pos = json_pos;
164 	size_t len;
165 
166 	for (; pos < end; pos++) {
167 		switch (*pos) {
168 		case '.':
169 		case 'e':
170 		case 'E':
171 			*is_double = true;
172 		case '-':
173 		case '0':
174 		case '1':
175 		case '2':
176 		case '3':
177 		case '4':
178 		case '5':
179 		case '6':
180 		case '7':
181 		case '8':
182 		case '9':
183 			continue;
184 		default:
185 			pos--;
186 			break;
187 		}
188 		break;
189 	}
190 	if (pos == end)
191 		pos--;
192 	if (pos < json_pos)
193 		return 0;
194 	len = pos - json_pos + 1;
195 
196 	return len;
197 }
198 
199 
json_parse_double(const char ** json_pos,size_t len,double * ret_val)200 static int json_parse_double(const char **json_pos, size_t len, double *ret_val)
201 {
202 	char *str;
203 	char *endptr;
204 
205 	str = os_malloc(len + 1);
206 	if (!str)
207 		return -1;
208 	os_memcpy(str, *json_pos, len);
209 	str[len] = '\0';
210 
211 	*ret_val = strtod(str, &endptr);
212 	if (endptr == str) {
213 		os_free(str);
214 		return -1;
215 	}
216 	os_free(str);
217 	*json_pos += len - 1;
218 	return 0;
219 }
220 
221 
json_parse_number(const char ** json_pos,size_t len,int * ret_val)222 static int json_parse_number(const char **json_pos, size_t len, int *ret_val)
223 {
224 	char *str;
225 
226 	str = os_malloc(len + 1);
227 	if (!str)
228 		return -1;
229 	os_memcpy(str, *json_pos, len);
230 	str[len] = '\0';
231 
232 	*ret_val = atoi(str);
233 	os_free(str);
234 	*json_pos += len - 1;
235 	return 0;
236 }
237 
238 
json_check_tree_state(struct json_token * token)239 static int json_check_tree_state(struct json_token *token)
240 {
241 	if (!token)
242 		return 0;
243 	if (json_check_tree_state(token->child) < 0 ||
244 	    json_check_tree_state(token->sibling) < 0)
245 		return -1;
246 	if (token->state != JSON_COMPLETED) {
247 		wpa_printf(MSG_DEBUG,
248 			   "JSON: Unexpected token state %d (name=%s type=%d)",
249 			   token->state, token->name ? token->name : "N/A",
250 			   token->type);
251 		return -1;
252 	}
253 	return 0;
254 }
255 
256 
json_alloc_token(unsigned int * tokens)257 static struct json_token * json_alloc_token(unsigned int *tokens)
258 {
259 	(*tokens)++;
260 	if (*tokens > JSON_MAX_TOKENS) {
261 		wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded");
262 		return NULL;
263 	}
264 	return os_zalloc(sizeof(struct json_token));
265 }
266 
267 
json_parse(const char * data,size_t data_len)268 struct json_token * json_parse(const char *data, size_t data_len)
269 {
270 	struct json_token *root = NULL, *curr_token = NULL, *token = NULL;
271 	const char *pos, *end;
272 	char *str;
273 	int num;
274 	double dnum;
275 	bool is_double;
276 	unsigned int depth = 0;
277 	unsigned int tokens = 0;
278 	size_t len;
279 
280 	pos = data;
281 	end = data + data_len;
282 
283 	for (; pos < end; pos++) {
284 		switch (*pos) {
285 		case '[': /* start array */
286 		case '{': /* start object */
287 			if (!curr_token) {
288 				token = json_alloc_token(&tokens);
289 				if (!token)
290 					goto fail;
291 				if (!root)
292 					root = token;
293 			} else if (curr_token->state == JSON_WAITING_VALUE) {
294 				token = curr_token;
295 			} else if (curr_token->parent &&
296 				   curr_token->parent->type == JSON_ARRAY &&
297 				   curr_token->parent->state == JSON_STARTED &&
298 				   curr_token->state == JSON_EMPTY) {
299 				token = curr_token;
300 			} else {
301 				wpa_printf(MSG_DEBUG,
302 					   "JSON: Invalid state for start array/object");
303 				goto fail;
304 			}
305 			depth++;
306 			if (depth > JSON_MAX_DEPTH) {
307 				wpa_printf(MSG_DEBUG,
308 					   "JSON: Max depth exceeded");
309 				goto fail;
310 			}
311 			token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT;
312 			token->state = JSON_STARTED;
313 			token->child = json_alloc_token(&tokens);
314 			if (!token->child)
315 				goto fail;
316 			curr_token = token->child;
317 			curr_token->parent = token;
318 			curr_token->state = JSON_EMPTY;
319 			break;
320 		case ']': /* end array */
321 		case '}': /* end object */
322 			if (!curr_token || !curr_token->parent ||
323 			    curr_token->parent->state != JSON_STARTED ||
324 			    depth == 0) {
325 				wpa_printf(MSG_DEBUG,
326 					   "JSON: Invalid state for end array/object");
327 				goto fail;
328 			}
329 			depth--;
330 			curr_token = curr_token->parent;
331 			if ((*pos == ']' &&
332 			     curr_token->type != JSON_ARRAY) ||
333 			    (*pos == '}' &&
334 			     curr_token->type != JSON_OBJECT)) {
335 				wpa_printf(MSG_DEBUG,
336 					   "JSON: Array/Object mismatch");
337 				goto fail;
338 			}
339 			if (curr_token->child->state == JSON_EMPTY &&
340 			    !curr_token->child->child &&
341 			    !curr_token->child->sibling) {
342 				/* Remove pending child token since the
343 				 * array/object was empty. */
344 				json_free(curr_token->child);
345 				curr_token->child = NULL;
346 			}
347 			curr_token->state = JSON_COMPLETED;
348 			break;
349 		case '\"': /* string */
350 			str = json_parse_string(&pos, end);
351 			if (!str)
352 				goto fail;
353 			if (!curr_token) {
354 				token = json_alloc_token(&tokens);
355 				if (!token) {
356 					os_free(str);
357 					goto fail;
358 				}
359 				token->type = JSON_STRING;
360 				token->string = str;
361 				token->state = JSON_COMPLETED;
362 			} else if (curr_token->parent &&
363 				   curr_token->parent->type == JSON_ARRAY &&
364 				   curr_token->parent->state == JSON_STARTED &&
365 				   curr_token->state == JSON_EMPTY) {
366 				curr_token->string = str;
367 				curr_token->state = JSON_COMPLETED;
368 				curr_token->type = JSON_STRING;
369 				wpa_printf(MSG_MSGDUMP,
370 					   "JSON: String value: '%s'",
371 					   curr_token->string);
372 			} else if (curr_token->state == JSON_EMPTY) {
373 				curr_token->type = JSON_VALUE;
374 				curr_token->name = str;
375 				curr_token->state = JSON_STARTED;
376 			} else if (curr_token->state == JSON_WAITING_VALUE) {
377 				curr_token->string = str;
378 				curr_token->state = JSON_COMPLETED;
379 				curr_token->type = JSON_STRING;
380 				wpa_printf(MSG_MSGDUMP,
381 					   "JSON: String value: '%s' = '%s'",
382 					   curr_token->name,
383 					   curr_token->string);
384 			} else {
385 				wpa_printf(MSG_DEBUG,
386 					   "JSON: Invalid state for a string");
387 				os_free(str);
388 				goto fail;
389 			}
390 			break;
391 		case ' ':
392 		case '\t':
393 		case '\r':
394 		case '\n':
395 			/* ignore whitespace */
396 			break;
397 		case ':': /* name/value separator */
398 			if (!curr_token || curr_token->state != JSON_STARTED)
399 				goto fail;
400 			curr_token->state = JSON_WAITING_VALUE;
401 			break;
402 		case ',': /* member separator */
403 			if (!curr_token)
404 				goto fail;
405 			curr_token->sibling = json_alloc_token(&tokens);
406 			if (!curr_token->sibling)
407 				goto fail;
408 			curr_token->sibling->parent = curr_token->parent;
409 			curr_token = curr_token->sibling;
410 			curr_token->state = JSON_EMPTY;
411 			break;
412 		case 't': /* true */
413 		case 'f': /* false */
414 		case 'n': /* null */
415 			if (!((end - pos >= 4 &&
416 			       os_strncmp(pos, "true", 4) == 0) ||
417 			      (end - pos >= 5 &&
418 			       os_strncmp(pos, "false", 5) == 0) ||
419 			      (end - pos >= 4 &&
420 			       os_strncmp(pos, "null", 4) == 0))) {
421 				wpa_printf(MSG_DEBUG,
422 					   "JSON: Invalid literal name");
423 				goto fail;
424 			}
425 			if (!curr_token) {
426 				token = json_alloc_token(&tokens);
427 				if (!token)
428 					goto fail;
429 				curr_token = token;
430 			} else if (curr_token->state == JSON_WAITING_VALUE) {
431 				wpa_printf(MSG_MSGDUMP,
432 					   "JSON: Literal name: '%s' = %c",
433 					   curr_token->name, *pos);
434 			} else if (curr_token->parent &&
435 				   curr_token->parent->type == JSON_ARRAY &&
436 				   curr_token->parent->state == JSON_STARTED &&
437 				   curr_token->state == JSON_EMPTY) {
438 				wpa_printf(MSG_MSGDUMP,
439 					   "JSON: Literal name: %c", *pos);
440 			} else {
441 				wpa_printf(MSG_DEBUG,
442 					   "JSON: Invalid state for a literal name");
443 				goto fail;
444 			}
445 			switch (*pos) {
446 			case 't':
447 				curr_token->type = JSON_BOOLEAN;
448 				curr_token->number = 1;
449 				pos += 3;
450 				break;
451 			case 'f':
452 				curr_token->type = JSON_BOOLEAN;
453 				curr_token->number = 0;
454 				pos += 4;
455 				break;
456 			case 'n':
457 				curr_token->type = JSON_NULL;
458 				pos += 3;
459 				break;
460 			}
461 			curr_token->state = JSON_COMPLETED;
462 			break;
463 		case '-':
464 		case '0':
465 		case '1':
466 		case '2':
467 		case '3':
468 		case '4':
469 		case '5':
470 		case '6':
471 		case '7':
472 		case '8':
473 		case '9':
474 			/* number */
475 			is_double = false;
476 			len = json_get_number_len(pos, end, &is_double);
477 			if (!len)
478 				goto fail;
479 			if (is_double) {
480 				if (json_parse_double(&pos, len, &dnum) < 0)
481 					goto fail;
482 			} else {
483 				if (json_parse_number(&pos, len, &num) < 0)
484 					goto fail;
485 			}
486 
487 			if (!curr_token) {
488 				token = json_alloc_token(&tokens);
489 				if (!token)
490 					goto fail;
491 				if (is_double) {
492 					token->dnumber = dnum;
493 					token->type = JSON_DOUBLE;
494 				} else {
495 					token->number = num;
496 					token->type = JSON_NUMBER;
497 				}
498 				token->state = JSON_COMPLETED;
499 			} else if (curr_token->state == JSON_WAITING_VALUE) {
500 				curr_token->state = JSON_COMPLETED;
501 				if (is_double) {
502 					curr_token->dnumber = dnum;
503 					curr_token->type = JSON_DOUBLE;
504 					wpa_printf(MSG_MSGDUMP,
505 						   "JSON: Double value: '%s' = '%f'",
506 						   curr_token->name,
507 						   curr_token->dnumber);
508 				} else {
509 					curr_token->number = num;
510 					curr_token->type = JSON_NUMBER;
511 					wpa_printf(MSG_MSGDUMP,
512 						   "JSON: Number value: '%s' = '%d'",
513 						   curr_token->name,
514 						   curr_token->number);
515 				}
516 			} else if (curr_token->parent &&
517 				   curr_token->parent->type == JSON_ARRAY &&
518 				   curr_token->parent->state == JSON_STARTED &&
519 				   curr_token->state == JSON_EMPTY) {
520 				curr_token->state = JSON_COMPLETED;
521 				if (is_double) {
522 					curr_token->dnumber = dnum;
523 					curr_token->type = JSON_DOUBLE;
524 					wpa_printf(MSG_MSGDUMP,
525 						   "JSON: Double value: %f",
526 						   curr_token->dnumber);
527 				} else {
528 					curr_token->number = num;
529 					curr_token->type = JSON_NUMBER;
530 					wpa_printf(MSG_MSGDUMP,
531 						   "JSON: Number value: %d",
532 						   curr_token->number);
533 				}
534 			} else {
535 				wpa_printf(MSG_DEBUG,
536 					   "JSON: Invalid state for a number");
537 				goto fail;
538 			}
539 			break;
540 		default:
541 			wpa_printf(MSG_DEBUG,
542 				   "JSON: Unexpected JSON character: %c", *pos);
543 			goto fail;
544 		}
545 
546 		if (!root)
547 			root = token;
548 		if (!curr_token)
549 			curr_token = token;
550 	}
551 
552 	if (json_check_tree_state(root) < 0) {
553 		wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
554 		goto fail;
555 	}
556 
557 	return root;
558 fail:
559 	wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
560 	json_free(root);
561 	return NULL;
562 }
563 
564 
json_free(struct json_token * json)565 void json_free(struct json_token *json)
566 {
567 	if (!json)
568 		return;
569 	json_free(json->child);
570 	json_free(json->sibling);
571 	os_free(json->name);
572 	os_free(json->string);
573 	os_free(json);
574 }
575 
576 
json_get_member(struct json_token * json,const char * name)577 struct json_token * json_get_member(struct json_token *json, const char *name)
578 {
579 	struct json_token *token, *ret = NULL;
580 
581 	if (!json || json->type != JSON_OBJECT)
582 		return NULL;
583 	/* Return last matching entry */
584 	for (token = json->child; token; token = token->sibling) {
585 		if (token->name && os_strcmp(token->name, name) == 0)
586 			ret = token;
587 	}
588 	return ret;
589 }
590 
591 
json_get_member_base64url(struct json_token * json,const char * name)592 struct wpabuf * json_get_member_base64url(struct json_token *json,
593 					  const char *name)
594 {
595 	struct json_token *token;
596 	unsigned char *buf;
597 	size_t buflen;
598 	struct wpabuf *ret;
599 
600 	token = json_get_member(json, name);
601 	if (!token || token->type != JSON_STRING)
602 		return NULL;
603 	buf = base64_url_decode(token->string, os_strlen(token->string),
604 				&buflen);
605 	if (!buf)
606 		return NULL;
607 	ret = wpabuf_alloc_ext_data(buf, buflen);
608 	if (!ret)
609 		os_free(buf);
610 
611 	return ret;
612 }
613 
614 
json_get_member_base64(struct json_token * json,const char * name)615 struct wpabuf * json_get_member_base64(struct json_token *json,
616 				       const char *name)
617 {
618 	struct json_token *token;
619 	unsigned char *buf;
620 	size_t buflen;
621 	struct wpabuf *ret;
622 
623 	token = json_get_member(json, name);
624 	if (!token || token->type != JSON_STRING)
625 		return NULL;
626 	buf = base64_decode(token->string, os_strlen(token->string), &buflen);
627 	if (!buf)
628 		return NULL;
629 	ret = wpabuf_alloc_ext_data(buf, buflen);
630 	if (!ret)
631 		os_free(buf);
632 
633 	return ret;
634 }
635 
636 
json_type_str(enum json_type type)637 static const char * json_type_str(enum json_type type)
638 {
639 	switch (type) {
640 	case JSON_VALUE:
641 		return "VALUE";
642 	case JSON_OBJECT:
643 		return "OBJECT";
644 	case JSON_ARRAY:
645 		return "ARRAY";
646 	case JSON_STRING:
647 		return "STRING";
648 	case JSON_NUMBER:
649 		return "NUMBER";
650 	case JSON_DOUBLE:
651 		return "DOUBLE";
652 	case JSON_BOOLEAN:
653 		return "BOOLEAN";
654 	case JSON_NULL:
655 		return "NULL";
656 	}
657 	return "??";
658 }
659 
660 
json_print_token(struct json_token * token,int depth,char * buf,size_t buflen)661 static void json_print_token(struct json_token *token, int depth,
662 			     char *buf, size_t buflen)
663 {
664 	size_t len;
665 	int ret;
666 
667 	if (!token)
668 		return;
669 	len = os_strlen(buf);
670 	ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
671 			  depth, json_type_str(token->type),
672 			  token->name ? token->name : "");
673 	if (os_snprintf_error(buflen - len, ret)) {
674 		buf[len] = '\0';
675 		return;
676 	}
677 	json_print_token(token->child, depth + 1, buf, buflen);
678 	json_print_token(token->sibling, depth, buf, buflen);
679 }
680 
681 
json_print_tree(struct json_token * root,char * buf,size_t buflen)682 void json_print_tree(struct json_token *root, char *buf, size_t buflen)
683 {
684 	buf[0] = '\0';
685 	json_print_token(root, 1, buf, buflen);
686 }
687 
688 
json_add_int(struct wpabuf * json,const char * name,int val)689 void json_add_int(struct wpabuf *json, const char *name, int val)
690 {
691 	wpabuf_printf(json, "\"%s\":%d", name, val);
692 }
693 
694 
json_add_double(struct wpabuf * json,const char * name,double val)695 void json_add_double(struct wpabuf *json, const char *name, double val)
696 {
697 	wpabuf_printf(json, "\"%s\":%f", name, val);
698 }
699 
700 
json_add_string(struct wpabuf * json,const char * name,const char * val)701 void json_add_string(struct wpabuf *json, const char *name, const char *val)
702 {
703 	wpabuf_printf(json, "\"%s\":\"%s\"", name, val);
704 }
705 
706 
json_add_string_escape(struct wpabuf * json,const char * name,const void * val,size_t len)707 int json_add_string_escape(struct wpabuf *json, const char *name,
708 			   const void *val, size_t len)
709 {
710 	char *tmp;
711 	size_t tmp_len = 6 * len + 1;
712 
713 	tmp = os_malloc(tmp_len);
714 	if (!tmp)
715 		return -1;
716 	json_escape_string(tmp, tmp_len, val, len);
717 	json_add_string(json, name, tmp);
718 	bin_clear_free(tmp, tmp_len);
719 	return 0;
720 }
721 
722 
json_add_base64url(struct wpabuf * json,const char * name,const void * val,size_t len)723 int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
724 		       size_t len)
725 {
726 	char *b64;
727 
728 	b64 = base64_url_encode(val, len, NULL);
729 	if (!b64)
730 		return -1;
731 	json_add_string(json, name, b64);
732 	os_free(b64);
733 	return 0;
734 }
735 
736 
json_add_base64(struct wpabuf * json,const char * name,const void * val,size_t len)737 int json_add_base64(struct wpabuf *json, const char *name, const void *val,
738 		    size_t len)
739 {
740 	char *b64;
741 
742 	b64 = base64_encode_no_lf(val, len, NULL);
743 	if (!b64)
744 		return -1;
745 	json_add_string(json, name, b64);
746 	os_free(b64);
747 	return 0;
748 }
749 
750 
json_start_object(struct wpabuf * json,const char * name)751 void json_start_object(struct wpabuf *json, const char *name)
752 {
753 	if (name)
754 		wpabuf_printf(json, "\"%s\":", name);
755 	wpabuf_put_u8(json, '{');
756 }
757 
758 
json_end_object(struct wpabuf * json)759 void json_end_object(struct wpabuf *json)
760 {
761 	wpabuf_put_u8(json, '}');
762 }
763 
764 
json_start_array(struct wpabuf * json,const char * name)765 void json_start_array(struct wpabuf *json, const char *name)
766 {
767 	if (name)
768 		wpabuf_printf(json, "\"%s\":", name);
769 	wpabuf_put_u8(json, '[');
770 }
771 
772 
json_end_array(struct wpabuf * json)773 void json_end_array(struct wpabuf *json)
774 {
775 	wpabuf_put_u8(json, ']');
776 }
777 
778 
json_value_sep(struct wpabuf * json)779 void json_value_sep(struct wpabuf *json)
780 {
781 	wpabuf_put_u8(json, ',');
782 }
783