wmem: allow wmem_destroy_list to ignore a NULL list.
[metze/wireshark/wip.git] / wsutil / wsjson.c
1 /* wsjson.c
2  * JSON parsing functions.
3  *
4  * Copyright 2016, Dario Lombardo
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12
13 #include "config.h"
14
15 #include "wsjson.h"
16
17 #include <string.h>
18 #include <wsutil/jsmn.h>
19 #include <wsutil/str_util.h>
20 #include <wsutil/unicode-utils.h>
21 #include "log.h"
22
23 gboolean
24 json_validate(const guint8 *buf, const size_t len)
25 {
26     gboolean ret = TRUE;
27     /* We expect no more than 1024 tokens */
28     guint max_tokens = 1024;
29     jsmntok_t* t;
30     jsmn_parser p;
31     int rcode;
32
33     t = g_new0(jsmntok_t, max_tokens);
34
35     if (!t)
36         return FALSE;
37
38     jsmn_init(&p);
39     rcode = jsmn_parse(&p, buf, len, t, max_tokens);
40     if (rcode < 0) {
41         switch (rcode) {
42             case JSMN_ERROR_NOMEM:
43                 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "jsmn: not enough tokens were provided");
44                 break;
45             case JSMN_ERROR_INVAL:
46                 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "jsmn: invalid character inside JSON string");
47                 break;
48             case JSMN_ERROR_PART:
49                 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "jsmn: the string is not a full JSON packet, "
50                     "more bytes expected");
51                 break;
52             default:
53                 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "jsmn: unexpected error");
54                 break;
55         }
56         ret = FALSE;
57     }
58
59     g_free(t);
60     return ret;
61 }
62
63 int
64 json_parse(const char *buf, jsmntok_t *tokens, unsigned int max_tokens)
65 {
66     jsmn_parser p;
67
68     jsmn_init(&p);
69     return jsmn_parse(&p, buf, strlen(buf), tokens, max_tokens);
70 }
71
72 gboolean
73 json_decode_string_inplace(char *text)
74 {
75     const char *input = text;
76     char *output = text;
77     while (*input) {
78         char ch = *input++;
79
80         if (ch == '\\') {
81             ch = *input++;
82
83             switch (ch) {
84                 case '\"':
85                 case '\\':
86                 case '/':
87                     *output++ = ch;
88                     break;
89
90                 case 'b':
91                     *output++ = '\b';
92                     break;
93                 case 'f':
94                     *output++ = '\f';
95                     break;
96                 case 'n':
97                     *output++ = '\n';
98                     break;
99                 case 'r':
100                     *output++ = '\r';
101                     break;
102                 case 't':
103                     *output++ = '\t';
104                     break;
105
106                 case 'u':
107                 {
108                     guint32 unicode_hex = 0;
109                     int k;
110                     int bin;
111
112                     for (k = 0; k < 4; k++) {
113                         unicode_hex <<= 4;
114
115                         ch = *input++;
116                         bin = ws_xton(ch);
117                         if (bin == -1)
118                             return FALSE;
119                         unicode_hex |= bin;
120                     }
121
122                     if ((IS_LEAD_SURROGATE(unicode_hex))) {
123                         guint16 lead_surrogate = unicode_hex;
124                         guint16 trail_surrogate = 0;
125
126                         if (input[0] != '\\' || input[1] != 'u')
127                             return FALSE;
128                         input += 2;
129
130                         for (k = 0; k < 4; k++) {
131                             trail_surrogate <<= 4;
132
133                             ch = *input++;
134                             bin = ws_xton(ch);
135                             if (bin == -1)
136                                 return FALSE;
137                             trail_surrogate |= bin;
138                         }
139
140                         if ((!IS_TRAIL_SURROGATE(trail_surrogate)))
141                             return FALSE;
142
143                         unicode_hex = SURROGATE_VALUE(lead_surrogate,trail_surrogate);
144
145                     } else if ((IS_TRAIL_SURROGATE(unicode_hex))) {
146                         return FALSE;
147                     }
148
149                     if (!g_unichar_validate(unicode_hex))
150                         return FALSE;
151
152                     /* Don't allow NUL byte injection. */
153                     if (unicode_hex == 0)
154                         return FALSE;
155
156                     /* \uXXXX => 6 bytes, and g_unichar_to_utf8() requires to have output buffer at least 6 bytes -> OK. */
157                     k = g_unichar_to_utf8(unicode_hex, output);
158                     output += k;
159                     break;
160                 }
161
162                 default:
163                     return FALSE;
164             }
165
166         } else {
167             *output = ch;
168             output++;
169         }
170     }
171
172     *output = '\0';
173     return TRUE;
174 }
175
176 /*
177  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
178  *
179  * Local variables:
180  * c-basic-offset: 4
181  * tab-width: 8
182  * indent-tabs-mode: nil
183  * End:
184  *
185  * vi: set shiftwidth=4 tabstop=8 expandtab:
186  * :indentSize=4:tabSize=8:noTabs=true:
187  */