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