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