Coverage Report

Created: 2026-03-30 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/range.c
Line
Count
Source
1
/* range.c
2
 * Range routines
3
 *
4
 * Dick Gooris <gooris@lucent.com>
5
 * Ulf Lamping <ulf.lamping@web.de>
6
 *
7
 * Wireshark - Network traffic analyzer
8
 * By Gerald Combs <gerald@wireshark.org>
9
 * Copyright 1998 Gerald Combs
10
 *
11
 * SPDX-License-Identifier: GPL-2.0-or-later
12
 */
13
14
#include "config.h"
15
16
#include <stdio.h>
17
#include <string.h>
18
#include <stdlib.h>
19
#include <errno.h>
20
21
#include <glib.h>
22
23
#include <epan/frame_data.h>
24
25
#include <epan/range.h>
26
27
#include <wsutil/strtoi.h>
28
29
/*
30
 * Size of the header of a range_t.
31
 */
32
22.1k
#define RANGE_HDR_SIZE (sizeof (range_t))
33
34
/* Allocate an empty range. */
35
range_t *range_empty(wmem_allocator_t *scope)
36
28
{
37
28
   range_t *range;
38
39
28
   range = (range_t *)wmem_alloc(scope, RANGE_HDR_SIZE);
40
28
   range->nranges = 0;
41
28
   return range;
42
28
}
43
44
/******************** Range Entry Parser *********************************/
45
46
/* Converts a range string to a fast comparable array of ranges.
47
 * The parameter 'es' points to the string to be converted.
48
 * The parameter 'max_value' specifies the maximum value in a
49
 * range.
50
 *
51
 * This function allocates a range_t large enough to hold the number
52
 * of ranges specified, and fills the array range->ranges containing
53
 * low and high values with the number of ranges being range->nranges.
54
 * After having called this function, the function value_is_in_range()
55
 * determines whether a given number is within the range or not.
56
 *
57
 * In case of a single number, we make a range where low is equal to high.
58
 * We take care on wrongly entered ranges; opposite order will be taken
59
 * care of.
60
 *
61
 * The following syntax is accepted :
62
 *
63
 *   1-20,30-40     Range from 1 to 20, and packets 30 to 40
64
 *   -20,30         Range from 1 to 20, and packet 30
65
 *   20,30,40-      20, 30, and the range from 40 to the end
66
 *   20-10,30-25    Range from 10 to 20, and from 25 to 30
67
 *   -              All values
68
 */
69
70
convert_ret_t
71
range_convert_str(wmem_allocator_t *scope, range_t **rangep, const char *es, uint32_t max_value)
72
10.5k
{
73
10.5k
   return range_convert_str_work(scope, rangep, es, max_value, true);
74
10.5k
}
75
76
/*  This version of range_convert_str() allows the caller to specify whether
77
 *  values in excess of the range's specified maximum should cause an error or
78
 *  be silently lowered.
79
 *  XXX - both the function and the variable could probably use better names.
80
 */
81
convert_ret_t
82
range_convert_str_work(wmem_allocator_t *scope, range_t **rangep, const char *es, uint32_t max_value,
83
                       bool err_on_max)
84
10.5k
{
85
86
10.5k
   range_t       *range;
87
10.5k
   unsigned      nranges;
88
10.5k
   const char    *p;
89
10.5k
   const char    *endp;
90
10.5k
   char          c;
91
10.5k
   unsigned      i;
92
10.5k
   uint32_t      tmp;
93
10.5k
   uint32_t      val;
94
95
10.5k
   if (rangep == NULL)
96
0
      return CVT_SYNTAX_ERROR;
97
98
   /* Allocate a range; this has room for one subrange. */
99
10.5k
   range = (range_t *)wmem_alloc(scope, RANGE_HDR_SIZE + sizeof (range_admin_t));
100
10.5k
   range->nranges = 0;
101
10.5k
   nranges = 1;
102
103
   /* Process the ranges separately until we get a comma or end of string.
104
    *
105
    * We build a structure array called ranges of high and low values. After the
106
    * following loop, we have the nranges variable which tells how many ranges
107
    * were found. The number of individual ranges is limited to 'MaxRanges'
108
    */
109
110
19.2k
   for (p = es; p; range->nranges++) {
111
      /* Skip white space. */
112
19.2k
      while ((c = *p) == ' ' || c == '\t')
113
14
         p++;
114
19.2k
      if (c == '\0')
115
10.5k
         break;
116
117
      /* This must be a subrange.  Make sure we have room for it. */
118
8.73k
      if (range->nranges >= nranges) {
119
         /* Grow the structure.
120
          * 4 is an arbitrarily chosen number.
121
          * We start with 1, under the assumption that people
122
          * will often give a single number or range, and then
123
          * proceed to keep it a multiple of 4.
124
          */
125
868
         if (nranges == 1)
126
686
            nranges = 4;
127
182
         else
128
182
            nranges += 4;
129
868
         range = (range_t *)wmem_realloc(scope, range, RANGE_HDR_SIZE +
130
868
                                      nranges*sizeof (range_admin_t));
131
868
      }
132
133
8.73k
      if (c == '-') {
134
         /* Subrange starts with 1. */
135
0
         range->ranges[range->nranges].low = 1;
136
8.73k
      } else if (g_ascii_isdigit(c)) {
137
         /* Subrange starts with the specified number */
138
8.73k
         errno = 0;
139
8.73k
         ws_basestrtou32(p, &endp, &val, 0);
140
8.73k
         if (errno == EINVAL) {
141
            /* That wasn't a valid number. */
142
0
            wmem_free(scope, range);
143
0
            return CVT_SYNTAX_ERROR;
144
0
         }
145
8.73k
         if (errno == ERANGE || val > max_value) {
146
            /* That was valid, but it's too big.  Return an error if requested
147
             * (e.g., except when reading from the preferences file).
148
             */
149
0
            if (err_on_max) {
150
0
               wmem_free(scope, range);
151
0
               return CVT_NUMBER_TOO_BIG;
152
0
            } else {
153
               /* Silently use the range's maximum value */
154
0
               val = max_value;
155
0
            }
156
0
         }
157
8.73k
         p = endp;
158
8.73k
         range->ranges[range->nranges].low = val;
159
160
         /* Skip white space. */
161
8.73k
         while ((c = *p) == ' ' || c == '\t')
162
0
            p++;
163
8.73k
      } else {
164
         /* Neither empty nor a number. */
165
0
         wmem_free(scope, range);
166
0
         return CVT_SYNTAX_ERROR;
167
0
      }
168
169
8.73k
      if (c == '-') {
170
         /* There's a hyphen in the range.  Skip past it. */
171
840
         p++;
172
173
         /* Skip white space. */
174
840
         while ((c = *p) == ' ' || c == '\t')
175
0
            p++;
176
177
840
         if (c == ',' || c == '\0') {
178
            /* End of subrange string; that means the subrange ends
179
             * with max_value.
180
             */
181
14
            range->ranges[range->nranges].high = max_value;
182
826
         } else if (g_ascii_isdigit(c)) {
183
            /* Subrange ends with the specified number. */
184
826
            errno = 0;
185
826
            ws_basestrtou32(p, &endp, &val, 0);
186
826
            if (errno == EINVAL) {
187
               /* That wasn't a valid number. */
188
0
               wmem_free(scope, range);
189
0
               return CVT_SYNTAX_ERROR;
190
0
            }
191
826
            if (errno == ERANGE || val > max_value) {
192
               /* That was valid, but it's too big.  Return an error if requested
193
                * (e.g., except when reading from the preferences file).
194
                */
195
0
               if (err_on_max) {
196
0
                  wmem_free(scope, range);
197
0
                  return CVT_NUMBER_TOO_BIG;
198
0
               } else {
199
                  /* Silently use the range's maximum value */
200
0
                  val = max_value;
201
0
               }
202
0
            }
203
826
            p = endp;
204
826
            range->ranges[range->nranges].high = val;
205
206
            /* Skip white space. */
207
826
            while ((c = *p) == ' ' || c == '\t')
208
0
               p++;
209
826
         } else {
210
            /* Neither empty nor a number. */
211
0
            wmem_free(scope, range);
212
0
            return CVT_SYNTAX_ERROR;
213
0
         }
214
7.89k
      } else if (c == ',' || c == '\0') {
215
         /* End of subrange string; that means there's no hyphen
216
          * in the subrange, so the start and the end are the same.
217
          */
218
7.89k
         range->ranges[range->nranges].high = range->ranges[range->nranges].low;
219
7.89k
      } else {
220
         /* Invalid character. */
221
0
         wmem_free(scope, range);
222
0
         return CVT_SYNTAX_ERROR;
223
0
      }
224
225
8.73k
      if (c == ',') {
226
         /* Subrange is followed by a comma; skip it. */
227
1.61k
         p++;
228
1.61k
      }
229
8.73k
   }
230
231
   /* Now we are going through the low and high values, and check
232
    * whether they are in a proper order. Low should be equal or lower
233
    * than high. So, go through the loop and swap if needed.
234
    */
235
19.2k
   for (i=0; i < range->nranges; i++) {
236
8.73k
      if (range->ranges[i].low > range->ranges[i].high) {
237
0
         tmp = range->ranges[i].low;
238
0
         range->ranges[i].low  = range->ranges[i].high;
239
0
         range->ranges[i].high = tmp;
240
0
      }
241
8.73k
   }
242
243
   /* In case we want to know what the result ranges are :
244
    *
245
    * for (i=0; i < range->nranges; i++) {
246
    *  printf("Function : range_convert_str L=%u \t H=%u\n",range->ranges[i].low,range->ranges[i].high);
247
    * }
248
    *
249
    */
250
10.5k
   *rangep = range;
251
10.5k
   return CVT_NO_ERROR;
252
10.5k
} /* range_convert_str */
253
254
/* This function returns true if a given value is within one of the ranges
255
 * stored in the ranges array.
256
 */
257
bool
258
value_is_in_range(const range_t *range, uint32_t val)
259
17.8k
{
260
17.8k
   unsigned i;
261
262
17.8k
   if (range) {
263
35.3k
      for (i=0; i < range->nranges; i++) {
264
18.6k
         if (val >= range->ranges[i].low && val <= range->ranges[i].high)
265
1.20k
            return true;
266
18.6k
      }
267
17.8k
   }
268
16.6k
   return false;
269
17.8k
}
270
271
/* This function returns true if val has successfully been added to
272
 * a range.  This may extend an existing range or create a new one
273
 */
274
bool
275
range_add_value(wmem_allocator_t *scope, range_t **range, uint32_t val)
276
0
{
277
0
   unsigned i;
278
279
0
   if ((range) && (*range)) {
280
0
      for (i=0; i < (*range)->nranges; i++) {
281
0
         if (val >= (*range)->ranges[i].low && val <= (*range)->ranges[i].high)
282
0
            return true;
283
284
0
         if (val == (*range)->ranges[i].low-1)
285
0
         {
286
             /* Sink to a new low */
287
0
             (*range)->ranges[i].low = val;
288
0
             return true;
289
0
         }
290
291
0
         if (val == (*range)->ranges[i].high+1)
292
0
         {
293
             /* Reach a new high */
294
0
             (*range)->ranges[i].high = val;
295
0
             return true;
296
0
         }
297
0
      }
298
299
0
      (*range) = (range_t *)wmem_realloc(scope, (*range), RANGE_HDR_SIZE +
300
0
                                ((*range)->nranges+1)*sizeof (range_admin_t));
301
0
      (*range)->nranges++;
302
0
      (*range)->ranges[i].low = (*range)->ranges[i].high = val;
303
0
      return true;
304
0
   }
305
0
   return false;
306
0
}
307
308
/* This function returns true if val has successfully been removed from
309
 * a range.  This may delete an existing range
310
 */
311
bool
312
range_remove_value(wmem_allocator_t *scope, range_t **range, uint32_t val)
313
0
{
314
0
   unsigned i, j, new_j;
315
0
   range_t *new_range;
316
317
0
   if ((range) && (*range)) {
318
0
      for (i=0; i < (*range)->nranges; i++) {
319
320
          /* value is in the middle of the range, so it can't really be removed */
321
0
         if (val > (*range)->ranges[i].low && val < (*range)->ranges[i].high)
322
0
            return true;
323
324
0
         if ((val ==  (*range)->ranges[i].low) && (val == (*range)->ranges[i].high))
325
0
         {
326
             /* Remove the range item entirely */
327
0
             new_range = (range_t*)wmem_alloc(scope, RANGE_HDR_SIZE + ((*range)->nranges-1)*sizeof (range_admin_t));
328
0
             new_range->nranges = (*range)->nranges-1;
329
0
             for (j=0, new_j = 0; j < (*range)->nranges; j++) {
330
331
                 /* Skip the current range */
332
0
                 if (j == i)
333
0
                     continue;
334
335
0
                 new_range->ranges[new_j].low = (*range)->ranges[j].low;
336
0
                 new_range->ranges[new_j].high = (*range)->ranges[j].high;
337
0
                 new_j++;
338
0
             }
339
340
0
             wmem_free(scope, *range);
341
0
             *range = new_range;
342
0
             return true;
343
0
         }
344
345
0
         if (val == (*range)->ranges[i].low)
346
0
         {
347
             /* Raise low */
348
0
             (*range)->ranges[i].low++;
349
0
             return true;
350
0
         }
351
352
0
         if (val == (*range)->ranges[i].high)
353
0
         {
354
             /* Reach a new high */
355
0
             (*range)->ranges[i].high--;
356
0
             return true;
357
0
         }
358
0
      }
359
0
      return true;
360
0
   }
361
0
   return false;
362
0
}
363
364
/* This function returns true if the two given range_t's are equal.
365
 */
366
bool
367
ranges_are_equal(const range_t *a, const range_t *b)
368
0
{
369
0
   unsigned i;
370
371
0
   if ( (a == NULL) || (b == NULL) )
372
0
       return false;
373
374
0
   if (a->nranges != b->nranges)
375
0
      return false;
376
377
0
   for (i=0; i < a->nranges; i++) {
378
0
      if (a->ranges[i].low != b->ranges[i].low)
379
0
         return false;
380
381
0
      if (a->ranges[i].high != b->ranges[i].high)
382
0
         return false;
383
0
   }
384
385
0
   return true;
386
387
0
}
388
389
/* This function calls the provided callback function for each value in
390
 * in the range.
391
 */
392
void
393
range_foreach(range_t *range, void (*callback)(uint32_t val, void *ptr), void *ptr)
394
224
{
395
224
   uint32_t i, j;
396
397
224
   if (range && callback) {
398
462
      for (i=0; i < range->nranges; i++) {
399
784
         for (j = range->ranges[i].low; j <= range->ranges[i].high; j++)
400
518
            callback(j, ptr);
401
266
      }
402
196
   }
403
224
}
404
405
/* This function converts a range_t to a (wmem-allocated) string.  */
406
char *
407
range_convert_range(wmem_allocator_t *scope, const range_t *range)
408
0
{
409
0
   uint32_t i;
410
0
   bool prepend_comma = false;
411
0
   wmem_strbuf_t *strbuf;
412
413
0
   strbuf=wmem_strbuf_new(scope, NULL);
414
415
0
   if (range) {
416
0
      for (i=0; i < range->nranges; i++) {
417
0
         if (range->ranges[i].low == range->ranges[i].high) {
418
0
            wmem_strbuf_append_printf(strbuf, "%s%u", prepend_comma?",":"", range->ranges[i].low);
419
0
         } else {
420
0
            wmem_strbuf_append_printf(strbuf, "%s%u-%u", prepend_comma?",":"", range->ranges[i].low, range->ranges[i].high);
421
0
         }
422
0
         prepend_comma = true;
423
0
      }
424
0
   }
425
0
   return wmem_strbuf_finalize(strbuf);
426
0
}
427
428
/* Create a copy of a range. */
429
range_t *
430
range_copy(wmem_allocator_t *scope, const range_t *src)
431
10.7k
{
432
10.7k
   range_t *dst;
433
10.7k
   size_t range_size;
434
435
10.7k
   if (src == NULL)
436
0
       return NULL;
437
438
10.7k
   range_size = RANGE_HDR_SIZE + src->nranges*sizeof (range_admin_t);
439
10.7k
   dst = (range_t *)wmem_memdup(scope, src, range_size);
440
10.7k
   return dst;
441
10.7k
}
442
443
#if 0
444
/* This is a debug function to check the range functionality */
445
static void
446
value_is_in_range_check(range_t *range, uint32_t val)
447
{
448
   /* Print the result for a given value */
449
   printf("Function : value_is_in_range_check Number %u\t",val);
450
451
   if (value_is_in_range(range, val)) {
452
      printf("is in range\n");
453
   } else {
454
      printf("is not in range\n");
455
   }
456
}
457
#endif
458
459
/*
460
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
461
 *
462
 * Local Variables:
463
 * c-basic-offset: 3
464
 * tab-width: 8
465
 * indent-tabs-mode: nil
466
 * End:
467
 *
468
 * ex: set shiftwidth=3 tabstop=8 expandtab:
469
 * :indentSize=3:tabSize=8:noTabs=true:
470
 */