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