1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 
22 #include "zbxjson.h"
23 #include "json_parser.h"
24 #include "json.h"
25 
26 #include "log.h"
27 
28 static zbx_int64_t	json_parse_object(const char *start, char **error);
29 
30 /******************************************************************************
31  *                                                                            *
32  * Function: json_error                                                       *
33  *                                                                            *
34  * Purpose: Prepares JSON parsing error message                               *
35  *                                                                            *
36  * Parameters: message     - [IN] the error message                           *
37  *             json_buffer - [IN] the failing data fragment                   *
38  *             error       - [OUT] the parsing error message (can be NULL)    *
39  *                                                                            *
40  * Return value: 0 - the json_error() function always returns 0 value         *
41  *                      so it can be used to return from failed parses        *
42  *                                                                            *
43  * Author: Andris Zeila                                                       *
44  *                                                                            *
45  ******************************************************************************/
json_error(const char * message,const char * json_buffer,char ** error)46 static zbx_int64_t	json_error(const char *message, const char *json_buffer, char **error)
47 {
48 	if (NULL != error)
49 	{
50 		if (NULL != json_buffer)
51 			*error = zbx_dsprintf(*error, "%s at: '%s'", message, json_buffer);
52 		else
53 			*error = zbx_strdup(*error, message);
54 	}
55 
56 	return 0;
57 }
58 
59 /******************************************************************************
60  *                                                                            *
61  * Function: json_parse_string                                                *
62  *                                                                            *
63  * Purpose: Parses JSON string value or object name                           *
64  *                                                                            *
65  * Parameters: start - [IN] the JSON data without leading whitespace          *
66  *             error - [OUT] the parsing error message (can be NULL)          *
67  *                                                                            *
68  * Return value: The number of characters parsed. On error 0 is returned and  *
69  *               error parameter (if not NULL) contains allocated error       *
70  *               message.                                                     *
71  *                                                                            *
72  * Author: Andris Zeila                                                       *
73  *                                                                            *
74  ******************************************************************************/
json_parse_string(const char * start,char ** error)75 static zbx_int64_t	json_parse_string(const char *start, char **error)
76 {
77 	const char	*ptr = start;
78 
79 	/* skip starting '"' */
80 	ptr++;
81 
82 	while ('"' != *ptr)
83 	{
84 		/* unexpected end of string data, failing */
85 		if ('\0' == *ptr)
86 			return json_error("unexpected end of string data", NULL, error);
87 
88 		if ('\\' == *ptr)
89 		{
90 			const char	*escape_start = ptr;
91 			int		i;
92 
93 			/* unexpected end of string data, failing */
94 			if ('\0' == *(++ptr))
95 				return json_error("invalid escape sequence in string", escape_start, error);
96 
97 			switch (*ptr)
98 			{
99 				case '"':
100 				case '\\':
101 				case '/':
102 				case 'b':
103 				case 'f':
104 				case 'n':
105 				case 'r':
106 				case 't':
107 					break;
108 				case 'u':
109 					/* check if the \u is followed with 4 hex digits */
110 					for (i = 0; i < 4; i++)
111 					{
112 						if (0 == isxdigit((unsigned char)*(++ptr)))
113 						{
114 							return json_error("invalid escape sequence in string",
115 									escape_start, error);
116 						}
117 					}
118 
119 					break;
120 				default:
121 					return json_error("invalid escape sequence in string data",
122 							escape_start, error);
123 			}
124 		}
125 
126 		/* Control character U+0000 - U+001F? It should have been escaped according to RFC 8259. */
127 		if (0x1f >= (unsigned char)*ptr)
128 			return json_error("invalid control character in string data", ptr, error);
129 
130 		ptr++;
131 	}
132 
133 	return ptr - start + 1;
134 }
135 
136 /******************************************************************************
137  *                                                                            *
138  * Function: json_parse_array                                                 *
139  *                                                                            *
140  * Purpose: Parses JSON array value                                           *
141  *                                                                            *
142  * Parameters: start - [IN] the JSON data without leading whitespace          *
143  *             error - [OUT] the parsing error message (can be NULL)          *
144  *                                                                            *
145  * Return value: The number of characters parsed. On error 0 is returned and  *
146  *               error parameter (if not NULL) contains allocated error       *
147  *               message.                                                     *
148  *                                                                            *
149  * Author: Andris Zeila                                                       *
150  *                                                                            *
151  ******************************************************************************/
json_parse_array(const char * start,char ** error)152 static zbx_int64_t	json_parse_array(const char *start, char **error)
153 {
154 	const char	*ptr = start;
155 	zbx_int64_t	len;
156 
157 	ptr++;
158 	SKIP_WHITESPACE(ptr);
159 
160 	if (']' != *ptr)
161 	{
162 		while (1)
163 		{
164 			/* json_parse_value strips leading whitespace, so we don't have to do it here */
165 			if (0 == (len = json_parse_value(ptr, error)))
166 				return 0;
167 
168 			ptr += len;
169 			SKIP_WHITESPACE(ptr);
170 
171 			if (',' != *ptr)
172 				break;
173 
174 			ptr++;
175 		}
176 
177 		/* no closing ], failing */
178 		if (']' != *ptr)
179 			return json_error("invalid array format, expected closing character ']'", ptr, error);
180 	}
181 
182 	return ptr - start + 1;
183 }
184 
185 /******************************************************************************
186  *                                                                            *
187  * Function: json_parse_number                                                *
188  *                                                                            *
189  * Purpose: Parses JSON number value                                          *
190  *                                                                            *
191  * Parameters: start - [IN] the JSON data without leading whitespace          *
192  *             error - [OUT] the parsing error message (can be NULL)          *
193  *                                                                            *
194  * Return value: The number of characters parsed. On error 0 is returned and  *
195  *               error parameter (if not NULL) contains allocated error       *
196  *               message.                                                     *
197  *                                                                            *
198  * Author: Andris Zeila                                                       *
199  *                                                                            *
200  ******************************************************************************/
json_parse_number(const char * start,char ** error)201 static zbx_int64_t	json_parse_number(const char *start, char **error)
202 {
203 	const char	*ptr = start;
204 	char		first_digit;
205 	int		point = 0, digit = 0;
206 
207 	if ('-' == *ptr)
208 		ptr++;
209 
210 	first_digit = *ptr;
211 
212 	while ('\0' != *ptr)
213 	{
214 		if ('.' == *ptr)
215 		{
216 			if (0 != point)
217 				break;
218 			point = 1;
219 		}
220 		else if (0 == isdigit((unsigned char)*ptr))
221 			break;
222 
223 		ptr++;
224 		if (0 == point)
225 			digit++;
226 	}
227 
228 	/* number does not contain any digits, failing */
229 	if (0 == digit)
230 		return json_error("invalid numeric value format", start, error);
231 
232 	/* number has zero leading digit following by other digits, failing */
233 	if ('0' == first_digit && 1 < digit)
234 		return json_error("invalid numeric value format", start, error);
235 
236 	if ('e' == *ptr || 'E' == *ptr)
237 	{
238 		if ('\0' == *(++ptr))
239 			return json_error("unexpected end of numeric value", NULL, error);
240 
241 		if ('+' == *ptr || '-' == *ptr)
242 		{
243 			if ('\0' == *(++ptr))
244 				return json_error("unexpected end of numeric value", NULL, error);
245 		}
246 
247 		if (0 == isdigit((unsigned char)*ptr))
248 			return json_error("invalid power value of number in E notation", ptr, error);
249 
250 		while ('\0' != *(++ptr))
251 		{
252 			if (0 == isdigit((unsigned char)*ptr))
253 				break;
254 		}
255 	}
256 
257 	return ptr - start;
258 }
259 
260 /******************************************************************************
261  *                                                                            *
262  * Function: json_parse_literal                                               *
263  *                                                                            *
264  * Purpose: Parses the specified literal value                                *
265  *                                                                            *
266  * Parameters: start - [IN] the JSON data without leading whitespace          *
267  *             text  - [IN] the literal value to parse                        *
268  *             error - [OUT] the parsing error message (can be NULL)          *
269  *                                                                            *
270  * Return value: The number of characters parsed. On error 0 is returned and  *
271  *               error parameter (if not NULL) contains allocated error       *
272  *               message.                                                     *
273  *                                                                            *
274  * Author: Andris Zeila                                                       *
275  *                                                                            *
276  * Comments: This function is used to parse JSON literal values null, true    *
277  *           false.                                                           *
278  *                                                                            *
279  ******************************************************************************/
json_parse_literal(const char * start,const char * text,char ** error)280 static zbx_int64_t	json_parse_literal(const char *start, const char *text, char **error)
281 {
282 	const char	*ptr = start;
283 
284 	while ('\0' != *text)
285 	{
286 		if (*ptr != *text)
287 			return json_error("invalid literal value", start, error);
288 		ptr++;
289 		text++;
290 	}
291 
292 	return ptr - start;
293 }
294 
295 /******************************************************************************
296  *                                                                            *
297  * Function: json_parse_value                                                 *
298  *                                                                            *
299  * Purpose: Parses JSON object value                                          *
300  *                                                                            *
301  * Parameters: start - [IN] the JSON data                                     *
302  *             error - [OUT] the parsing error message (can be NULL)          *
303  *                                                                            *
304  * Return value: The number of characters parsed. On error 0 is returned and  *
305  *               error parameter (if not NULL) contains allocated error       *
306  *               message.                                                     *
307  *                                                                            *
308  * Author: Andris Zeila                                                       *
309  *                                                                            *
310  ******************************************************************************/
json_parse_value(const char * start,char ** error)311 zbx_int64_t	json_parse_value(const char *start, char **error)
312 {
313 	const char	*ptr = start;
314 	zbx_int64_t	len;
315 
316 	SKIP_WHITESPACE(ptr);
317 
318 	switch (*ptr)
319 	{
320 		case '\0':
321 			return json_error("unexpected end of object value", NULL, error);
322 		case '"':
323 			if (0 == (len = json_parse_string(ptr, error)))
324 				return 0;
325 			break;
326 		case '{':
327 			if (0 == (len = json_parse_object(ptr, error)))
328 				return 0;
329 			break;
330 		case '[':
331 			if (0 == (len = json_parse_array(ptr, error)))
332 				return 0;
333 			break;
334 		case 't':
335 			if (0 == (len = json_parse_literal(ptr, "true", error)))
336 				return 0;
337 			break;
338 		case 'f':
339 			if (0 == (len = json_parse_literal(ptr, "false", error)))
340 				return 0;
341 			break;
342 		case 'n':
343 			if (0 == (len = json_parse_literal(ptr, "null", error)))
344 				return 0;
345 			break;
346 		case '0':
347 		case '1':
348 		case '2':
349 		case '3':
350 		case '4':
351 		case '5':
352 		case '6':
353 		case '7':
354 		case '8':
355 		case '9':
356 		case '-':
357 			if (0 == (len = json_parse_number(ptr, error)))
358 				return 0;
359 			break;
360 		default:
361 			return json_error("invalid JSON object value starting character", ptr, error);
362 	}
363 
364 	return ptr - start + len;
365 }
366 
367 /******************************************************************************
368  *                                                                            *
369  * Function: json_parse_object                                                *
370  *                                                                            *
371  * Purpose: Parses JSON object                                                *
372  *                                                                            *
373  * Parameters: start - [IN] the JSON data                                     *
374  *             error - [OUT] the parsing error message (can be NULL)          *
375  *                                                                            *
376  * Return value: The number of characters parsed. On error 0 is returned and  *
377  *               error parameter (if not NULL) contains allocated error       *
378  *               message.                                                     *
379  *                                                                            *
380  * Author: Andris Zeila                                                       *
381  *                                                                            *
382  ******************************************************************************/
json_parse_object(const char * start,char ** error)383 static zbx_int64_t	json_parse_object(const char *start, char **error)
384 {
385 	const char	*ptr = start;
386 	zbx_int64_t	len;
387 
388 	/* parse object name */
389 	SKIP_WHITESPACE(ptr);
390 
391 	ptr++;
392 	SKIP_WHITESPACE(ptr);
393 
394 	if ('}' != *ptr)
395 	{
396 		while (1)
397 		{
398 			if ('"' != *ptr)
399 				return json_error("invalid object name", ptr, error);
400 
401 			/* cannot parse object name, failing */
402 			if (0 == (len = json_parse_string(ptr, error)))
403 				return 0;
404 
405 			ptr += len;
406 
407 			/* parse name:value separator */
408 			SKIP_WHITESPACE(ptr);
409 
410 			if (':' != *ptr)
411 				return json_error("invalid object name/value separator", ptr, error);
412 			ptr++;
413 
414 			if (0 == (len = json_parse_value(ptr, error)))
415 				return 0;
416 
417 			ptr += len;
418 
419 			SKIP_WHITESPACE(ptr);
420 
421 			if (',' != *ptr)
422 				break;
423 
424 			ptr++;
425 			SKIP_WHITESPACE(ptr);
426 		}
427 
428 		/* object is not properly closed, failing */
429 		if ('}' != *ptr)
430 			return json_error("invalid object format, expected closing character '}'", ptr, error);
431 	}
432 
433 	return ptr - start + 1;
434 }
435 
436 /******************************************************************************
437  *                                                                            *
438  * Function: zbx_json_validate                                                *
439  *                                                                            *
440  * Purpose: Validates JSON object                                             *
441  *                                                                            *
442  * Parameters: start - [IN]  the string to validate                           *
443  *             error - [OUT] the parse error message. If the error value is   *
444  *                           set it must be freed by caller after it has      *
445  *                           been used (can be NULL).                         *
446  *                                                                            *
447  * Return value: The number of characters parsed. On error 0 is returned and  *
448  *               error parameter (if not NULL) contains allocated error       *
449  *               message.                                                     *
450  *                                                                            *
451  * Author: Andris Zeila                                                       *
452  *                                                                            *
453  ******************************************************************************/
zbx_json_validate(const char * start,char ** error)454 zbx_int64_t	zbx_json_validate(const char *start, char **error)
455 {
456 	zbx_int64_t	len;
457 
458 	/* parse object name */
459 	SKIP_WHITESPACE(start);
460 
461 	switch (*start)
462 	{
463 		case '{':
464 			if (0 == (len = json_parse_object(start, error)))
465 				return 0;
466 			break;
467 		case '[':
468 			if (0 == (len = json_parse_array(start, error)))
469 				return 0;
470 			break;
471 		default:
472 			/* not an json data, failing */
473 			return json_error("invalid object format, expected opening character '{' or '['", start, error);
474 	}
475 
476 	start += len;
477 	SKIP_WHITESPACE(start);
478 
479 	if ('\0' != *start)
480 		return json_error("invalid character following JSON object", start, error);
481 
482 	return len;
483 }
484