Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wsutil/jsmn.c
Line
Count
Source
1
/*
2
Copyright (c) 2010 Serge A. Zaitsev
3
4
Permission is hereby granted, free of charge, to any person obtaining a copy
5
of this software and associated documentation files (the "Software"), to deal
6
in the Software without restriction, including without limitation the rights
7
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
copies of the Software, and to permit persons to whom the Software is
9
furnished to do so, subject to the following conditions:
10
11
The above copyright notice and this permission notice shall be included in
12
all copies or substantial portions of the Software.
13
14
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
THE SOFTWARE.
21
*/
22
23
#include "jsmn.h"
24
25
/**
26
 * Allocates a fresh unused token from the token pull.
27
 */
28
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
29
0
    jsmntok_t *tokens, size_t num_tokens) {
30
0
  jsmntok_t *tok;
31
0
  if (parser->toknext >= num_tokens) {
32
0
    return NULL;
33
0
  }
34
0
  tok = &tokens[parser->toknext++];
35
0
  tok->start = tok->end = -1;
36
0
  tok->size = 0;
37
0
#ifdef JSMN_PARENT_LINKS
38
0
  tok->parent = -1;
39
0
#endif
40
0
  return tok;
41
0
}
42
43
/**
44
 * Fills token type and boundaries.
45
 */
46
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
47
0
                            int start, int end) {
48
0
  token->type = type;
49
0
  token->start = start;
50
0
  token->end = end;
51
0
  token->size = 0;
52
0
}
53
54
/**
55
 * Fills next available token with JSON primitive.
56
 */
57
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
58
0
    size_t len, jsmntok_t *tokens, size_t num_tokens) {
59
0
  jsmntok_t *token;
60
0
  int start;
61
62
0
  start = parser->pos;
63
64
0
  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
65
0
    switch (js[parser->pos]) {
66
#ifndef JSMN_STRICT
67
      /* In strict mode primitive must be followed by "," or "}" or "]" */
68
      case ':':
69
#endif
70
0
      case '\t' : case '\r' : case '\n' : case ' ' :
71
0
      case ','  : case ']'  : case '}' :
72
0
        goto found;
73
0
    }
74
0
    if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
75
0
      parser->pos = start;
76
0
      return JSMN_ERROR_INVAL;
77
0
    }
78
0
  }
79
0
#ifdef JSMN_STRICT
80
  /* In strict mode primitive must be followed by a comma/object/array */
81
0
  parser->pos = start;
82
0
  return JSMN_ERROR_PART;
83
0
#endif
84
85
0
found:
86
0
  if (tokens == NULL) {
87
0
    parser->pos--;
88
0
    return 0;
89
0
  }
90
0
  token = jsmn_alloc_token(parser, tokens, num_tokens);
91
0
  if (token == NULL) {
92
0
    parser->pos = start;
93
0
    return JSMN_ERROR_NOMEM;
94
0
  }
95
0
  jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
96
0
#ifdef JSMN_PARENT_LINKS
97
0
  token->parent = parser->toksuper;
98
0
#endif
99
0
  parser->pos--;
100
0
  return 0;
101
0
}
102
103
/**
104
 * Fills next token with JSON string.
105
 */
106
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
107
0
    size_t len, jsmntok_t *tokens, size_t num_tokens) {
108
0
  jsmntok_t *token;
109
110
0
  int start = parser->pos;
111
112
0
  parser->pos++;
113
114
  /* Skip starting quote */
115
0
  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
116
0
    char c = js[parser->pos];
117
118
    /* Quote: end of string */
119
0
    if (c == '\"') {
120
0
      if (tokens == NULL) {
121
0
        return 0;
122
0
      }
123
0
      token = jsmn_alloc_token(parser, tokens, num_tokens);
124
0
      if (token == NULL) {
125
0
        parser->pos = start;
126
0
        return JSMN_ERROR_NOMEM;
127
0
      }
128
0
      jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
129
0
#ifdef JSMN_PARENT_LINKS
130
0
      token->parent = parser->toksuper;
131
0
#endif
132
0
      return 0;
133
0
    }
134
135
    /* Backslash: Quoted symbol expected */
136
0
    if (c == '\\' && parser->pos + 1 < len) {
137
0
      int i;
138
0
      parser->pos++;
139
0
      switch (js[parser->pos]) {
140
        /* Allowed escaped symbols */
141
0
        case '\"': case '/' : case '\\' : case 'b' :
142
0
        case 'f' : case 'r' : case 'n'  : case 't' :
143
0
          break;
144
        /* Allows escaped symbol \uXXXX */
145
0
        case 'u':
146
0
          parser->pos++;
147
0
          for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
148
            /* If it isn't a hex character we have an error */
149
0
            if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
150
0
                  (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
151
0
                  (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
152
0
              parser->pos = start;
153
0
              return JSMN_ERROR_INVAL;
154
0
            }
155
0
            parser->pos++;
156
0
          }
157
0
          parser->pos--;
158
0
          break;
159
        /* Unexpected symbol */
160
0
        default:
161
0
          parser->pos = start;
162
0
          return JSMN_ERROR_INVAL;
163
0
      }
164
0
    }
165
0
  }
166
0
  parser->pos = start;
167
0
  return JSMN_ERROR_PART;
168
0
}
169
170
/**
171
 * Parse JSON string and fill tokens.
172
 */
173
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
174
0
    jsmntok_t *tokens, unsigned int num_tokens) {
175
0
  int r;
176
0
  int i;
177
0
  jsmntok_t *token;
178
0
  int count = parser->toknext;
179
180
0
  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
181
0
    char c;
182
0
    jsmntype_t type;
183
184
0
    c = js[parser->pos];
185
0
    switch (c) {
186
0
      case '{': case '[':
187
0
        count++;
188
0
        if (tokens == NULL) {
189
0
          break;
190
0
        }
191
0
        token = jsmn_alloc_token(parser, tokens, num_tokens);
192
0
        if (token == NULL)
193
0
          return JSMN_ERROR_NOMEM;
194
0
        if (parser->toksuper != -1) {
195
0
          tokens[parser->toksuper].size++;
196
0
#ifdef JSMN_PARENT_LINKS
197
0
          token->parent = parser->toksuper;
198
0
#endif
199
0
        }
200
0
        token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
201
0
        token->start = parser->pos;
202
0
        parser->toksuper = parser->toknext - 1;
203
0
        break;
204
0
      case '}': case ']':
205
0
        if (tokens == NULL)
206
0
          break;
207
0
        type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
208
0
#ifdef JSMN_PARENT_LINKS
209
0
        if (parser->toknext < 1) {
210
0
          return JSMN_ERROR_INVAL;
211
0
        }
212
0
        token = &tokens[parser->toknext - 1];
213
0
        for (;;) {
214
0
          if (token->start != -1 && token->end == -1) {
215
0
            if (token->type != type) {
216
0
              return JSMN_ERROR_INVAL;
217
0
            }
218
0
            token->end = parser->pos + 1;
219
0
            parser->toksuper = token->parent;
220
0
            break;
221
0
          }
222
0
          if (token->parent == -1) {
223
0
            break;
224
0
          }
225
0
          token = &tokens[token->parent];
226
0
        }
227
#else
228
        for (i = parser->toknext - 1; i >= 0; i--) {
229
          token = &tokens[i];
230
          if (token->start != -1 && token->end == -1) {
231
            if (token->type != type) {
232
              return JSMN_ERROR_INVAL;
233
            }
234
            parser->toksuper = -1;
235
            token->end = parser->pos + 1;
236
            break;
237
          }
238
        }
239
        /* Error if unmatched closing bracket */
240
        if (i == -1) return JSMN_ERROR_INVAL;
241
        for (; i >= 0; i--) {
242
          token = &tokens[i];
243
          if (token->start != -1 && token->end == -1) {
244
            parser->toksuper = i;
245
            break;
246
          }
247
        }
248
#endif
249
0
        break;
250
0
      case '\"':
251
0
        r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
252
0
        if (r < 0) return r;
253
0
        count++;
254
0
        if (parser->toksuper != -1 && tokens != NULL)
255
0
          tokens[parser->toksuper].size++;
256
0
        break;
257
0
      case '\t' : case '\r' : case '\n' : case ' ':
258
0
        break;
259
0
      case ':':
260
0
        parser->toksuper = parser->toknext - 1;
261
0
        break;
262
0
      case ',':
263
0
        if (tokens != NULL && parser->toksuper != -1 &&
264
0
            tokens[parser->toksuper].type != JSMN_ARRAY &&
265
0
            tokens[parser->toksuper].type != JSMN_OBJECT) {
266
0
#ifdef JSMN_PARENT_LINKS
267
0
          parser->toksuper = tokens[parser->toksuper].parent;
268
#else
269
          for (i = parser->toknext - 1; i >= 0; i--) {
270
            if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
271
              if (tokens[i].start != -1 && tokens[i].end == -1) {
272
                parser->toksuper = i;
273
                break;
274
              }
275
            }
276
          }
277
#endif
278
0
        }
279
0
        break;
280
0
#ifdef JSMN_STRICT
281
      /* In strict mode primitives are: numbers and booleans */
282
0
      case '-': case '0': case '1' : case '2': case '3' : case '4':
283
0
      case '5': case '6': case '7' : case '8': case '9':
284
0
      case 't': case 'f': case 'n' :
285
        /* And they must not be keys of the object */
286
0
        if (tokens != NULL && parser->toksuper != -1) {
287
0
          jsmntok_t *t = &tokens[parser->toksuper];
288
0
          if (t->type == JSMN_OBJECT ||
289
0
              (t->type == JSMN_STRING && t->size != 0)) {
290
0
            return JSMN_ERROR_INVAL;
291
0
          }
292
0
        }
293
#else
294
      /* In non-strict mode every unquoted value is a primitive */
295
      default:
296
#endif
297
0
        r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
298
0
        if (r < 0) return r;
299
0
        count++;
300
0
        if (parser->toksuper != -1 && tokens != NULL)
301
0
          tokens[parser->toksuper].size++;
302
0
        break;
303
304
0
#ifdef JSMN_STRICT
305
      /* Unexpected char in strict mode */
306
0
      default:
307
0
        return JSMN_ERROR_INVAL;
308
0
#endif
309
0
    }
310
0
  }
311
312
0
  if (tokens != NULL) {
313
0
    for (i = parser->toknext - 1; i >= 0; i--) {
314
      /* Unmatched opened object or array */
315
0
      if (tokens[i].start != -1 && tokens[i].end == -1) {
316
0
        return JSMN_ERROR_PART;
317
0
      }
318
0
    }
319
0
  }
320
321
0
  return count;
322
0
}
323
324
/**
325
 * Creates a new parser based over a given  buffer with an array of tokens
326
 * available.
327
 */
328
0
void jsmn_init(jsmn_parser *parser) {
329
0
  parser->pos = 0;
330
0
  parser->toknext = 0;
331
0
  parser->toksuper = -1;
332
0
}