| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "../libnetdata.h" |
| 4 | |
| 5 | // "JSON parser failed: " prefix (20 chars) + json-c error string (< 60 chars) + NUL |
| 6 | #define JSON_PARSER_ERROR_MSG_MAX 256 |
| 7 | #define JSON_PARSER_ERROR_PREFIX "JSON parser failed: " |
| 8 | #define JSON_PARSER_ERROR_TRUNCATION_SUFFIX "..." |
| 9 | #define JSON_PARSER_UNKNOWN_ERROR "unknown error" |
| 10 | |
| 11 | static void json_parser_format_error(char *dst, size_t dst_size, const char *error_msg, size_t error_len) { |
| 12 | if(unlikely(!dst_size)) |
| 13 | return; |
| 14 | |
| 15 | if(!error_msg || !*error_msg) { |
| 16 | error_msg = JSON_PARSER_UNKNOWN_ERROR; |
| 17 | error_len = sizeof(JSON_PARSER_UNKNOWN_ERROR) - 1; |
| 18 | } |
| 19 | |
| 20 | const size_t prefix_len = sizeof(JSON_PARSER_ERROR_PREFIX) - 1; |
| 21 | size_t available = dst_size - 1; |
| 22 | if(unlikely(available <= prefix_len)) { |
| 23 | dst[0] = '\0'; |
| 24 | return; |
| 25 | } |
| 26 | |
| 27 | available -= prefix_len; |
| 28 | |
| 29 | bool truncated = error_len > available; |
| 30 | if(truncated) { |
| 31 | size_t suffix_len = sizeof(JSON_PARSER_ERROR_TRUNCATION_SUFFIX) - 1; |
| 32 | if(available > suffix_len) |
| 33 | available -= suffix_len; |
| 34 | } |
| 35 | |
| 36 | snprintfz(dst, dst_size, "%s%.*s%s", |
| 37 | JSON_PARSER_ERROR_PREFIX, |
| 38 | (int)available, |
| 39 | error_msg, |
| 40 | truncated ? JSON_PARSER_ERROR_TRUNCATION_SUFFIX : ""); |
| 41 | } |
| 42 | |
| 43 | int rrd_call_function_error(BUFFER *wb, const char *msg, int code) { |
| 44 | buffer_reset(wb); |
| 45 | buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY); |
| 46 | buffer_json_member_add_int64(wb, "status", code); |
| 47 | buffer_json_member_add_string(wb, "errorMessage", msg); |
| 48 | buffer_json_finalize(wb); |
| 49 | wb->date = now_realtime_sec(); |
| 50 | wb->expires = wb->date + 1; |
| 51 | wb->response_code = code; |
| 52 | return code; |
| 53 | } |
| 54 | |
| 55 | struct json_object *json_parse_function_payload_or_error(BUFFER *output, BUFFER *payload, int *code, json_parse_function_payload_t cb, void *cb_data) { |
| 56 | if(!payload || !buffer_strlen(payload)) { |
| 57 | *code = rrd_call_function_error(output, "No payload given, but a payload is required for this feature.", HTTP_RESP_BAD_REQUEST); |
| 58 | return NULL; |
| 59 | } |
| 60 | |
| 61 | struct json_tokener *tokener = json_tokener_new(); |
| 62 | if (!tokener) { |
| 63 | *code = rrd_call_function_error(output, "Failed to initialize json parser.", HTTP_RESP_INTERNAL_SERVER_ERROR); |
| 64 | return NULL; |
| 65 | } |
| 66 | |
| 67 | struct json_object *jobj = json_tokener_parse_ex(tokener, buffer_tostring(payload), (int)buffer_strlen(payload)); |
| 68 | if (json_tokener_get_error(tokener) != json_tokener_success) { |
| 69 | const char *error_msg = json_tokener_error_desc(json_tokener_get_error(tokener)); |
| 70 | char tmp[JSON_PARSER_ERROR_MSG_MAX]; |
| 71 | json_parser_format_error(tmp, sizeof(tmp), error_msg, error_msg ? strnlen(error_msg, JSON_PARSER_ERROR_MSG_MAX) : 0); |
| 72 | json_tokener_free(tokener); |
| 73 | *code = rrd_call_function_error(output, tmp, HTTP_RESP_INTERNAL_SERVER_ERROR); |
| 74 | return NULL; |
| 75 | } |
| 76 | json_tokener_free(tokener); |
| 77 | |
| 78 | CLEAN_BUFFER *error = buffer_create(0, NULL); |
| 79 | if(!cb(jobj, cb_data, error)) { |
| 80 | char tmp[JSON_PARSER_ERROR_MSG_MAX]; |
| 81 | json_parser_format_error(tmp, sizeof(tmp), buffer_tostring(error), buffer_strlen(error)); |
| 82 | *code = rrd_call_function_error(output, tmp, HTTP_RESP_BAD_REQUEST); |
| 83 | json_object_put(jobj); |
| 84 | return NULL; |
| 85 | } |
| 86 | |
| 87 | *code = HTTP_RESP_OK; |
| 88 | |
| 89 | return jobj; |
| 90 | } |
| 91 | |
| 92 | int json_parse_payload_or_error(BUFFER *payload, BUFFER *error, json_parse_function_payload_t cb, void *cb_data) { |
| 93 | if(!payload || !buffer_strlen(payload)) { |
| 94 | buffer_strcat(error, "No payload given, but a payload is required for this feature."); |
| 95 | return HTTP_RESP_BAD_REQUEST; |
| 96 | } |
| 97 | |
| 98 | struct json_tokener *tokener = json_tokener_new(); |
| 99 | if (!tokener) { |
| 100 | buffer_strcat(error, "Failed to initialize json parser."); |
| 101 | return HTTP_RESP_INTERNAL_SERVER_ERROR; |
| 102 | } |
| 103 | |
| 104 | struct json_object *jobj = json_tokener_parse_ex(tokener, buffer_tostring(payload), (int)buffer_strlen(payload)); |
| 105 | if (json_tokener_get_error(tokener) != json_tokener_success) { |
| 106 | const char *error_msg = json_tokener_error_desc(json_tokener_get_error(tokener)); |
| 107 | char tmp[JSON_PARSER_ERROR_MSG_MAX]; |
| 108 | json_parser_format_error(tmp, sizeof(tmp), error_msg, error_msg ? strnlen(error_msg, JSON_PARSER_ERROR_MSG_MAX) : 0); |
| 109 | json_tokener_free(tokener); |
| 110 | buffer_strcat(error, tmp); |
| 111 | return HTTP_RESP_BAD_REQUEST; |
| 112 | } |
| 113 | json_tokener_free(tokener); |
| 114 | |
| 115 | if(!cb(jobj, cb_data, error)) { |
| 116 | if(!buffer_strlen(error)) |
| 117 | buffer_strcat(error, "Unknown error during parsing"); |
| 118 | json_object_put(jobj); |
| 119 | return HTTP_RESP_BAD_REQUEST; |
| 120 | } |
| 121 | |
| 122 | json_object_put(jobj); |
| 123 | return HTTP_RESP_OK; |
| 124 | } |