Coverage Report

Created: 2026-05-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/decode_as.c
Line
Count
Source
1
/* decode_as.c
2
 * Routines for dissector Decode As handlers
3
 *
4
 * Wireshark - Network traffic analyzer
5
 * By Gerald Combs <gerald@wireshark.org>
6
 * Copyright 1998 Gerald Combs
7
 *
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 */
10
11
#include "config.h"
12
13
#include <glib.h>
14
15
#include "decode_as.h"
16
#include <epan/packet.h>
17
#include "prefs.h"
18
#include "prefs-int.h"
19
#include "wsutil/file_util.h"
20
#include "wsutil/filesystem.h"
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <errno.h>
24
#include <wsutil/ws_assert.h>
25
26
GList *decode_as_list;
27
28
void register_decode_as(decode_as_t* reg)
29
1.12k
{
30
1.12k
    dissector_table_t decode_table;
31
32
    /* Ensure valid functions */
33
1.12k
    ws_assert(reg->populate_list);
34
1.12k
    ws_assert(reg->reset_value);
35
1.12k
    ws_assert(reg->change_value);
36
37
1.12k
    decode_table = find_dissector_table(reg->table_name);
38
1.12k
    if (decode_table != NULL)
39
1.12k
    {
40
1.12k
        dissector_table_allow_decode_as(decode_table);
41
1.12k
    }
42
43
1.12k
    decode_as_list = g_list_prepend(decode_as_list, reg);
44
1.12k
}
45
46
static void next_proto_prompt(packet_info *pinfo _U_, char *result)
47
0
{
48
0
    snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "Next level protocol as");
49
0
}
50
51
static void *next_proto_value(packet_info *pinfo _U_)
52
0
{
53
0
    return 0;
54
0
}
55
56
static build_valid_func next_proto_values[] = { next_proto_value };
57
static decode_as_value_t next_proto_da_values =
58
                        { next_proto_prompt, 1, next_proto_values };
59
60
dissector_table_t register_decode_as_next_proto(int proto, const char *table_name, const char *ui_name, build_label_func label_func)
61
375
{
62
375
    decode_as_t *da;
63
64
375
    dissector_table_t dt = register_dissector_table(table_name, ui_name, proto, FT_NONE, BASE_NONE);
65
66
375
    da = wmem_new0(wmem_epan_scope(), decode_as_t);
67
375
    da->name = wmem_strdup(wmem_epan_scope(), proto_get_protocol_filter_name(proto));
68
375
    da->table_name = wmem_strdup(wmem_epan_scope(), table_name);
69
375
    da->num_items = 1;
70
375
    if (label_func == NULL)
71
180
    {
72
180
        da->values = &next_proto_da_values;
73
180
    }
74
195
    else
75
195
    {
76
195
        da->values = wmem_new(wmem_epan_scope(), decode_as_value_t);
77
195
        da->values->label_func = label_func;
78
195
        da->values->num_values = 1;
79
195
        da->values->build_values = next_proto_values;
80
195
    }
81
375
    da->populate_list = decode_as_default_populate_list;
82
375
    da->reset_value = decode_as_default_reset;
83
375
    da->change_value = decode_as_default_change;
84
85
375
    register_decode_as(da);
86
375
    return dt;
87
375
}
88
89
struct decode_as_default_populate
90
{
91
    decode_as_add_to_list_func add_to_list;
92
    void *ui_element;
93
};
94
95
static void
96
decode_proto_add_to_list (const char *table_name, void *value, void *user_data)
97
0
{
98
0
    struct decode_as_default_populate* populate = (struct decode_as_default_populate*)user_data;
99
0
    const char      *dissector_description;
100
0
    int        i;
101
0
    dissector_handle_t handle;
102
103
104
0
    handle = (dissector_handle_t)value;
105
0
    dissector_description = dissector_handle_get_description(handle);
106
107
0
    i = dissector_handle_get_protocol_index(handle);
108
0
    if (i >= 0 && !proto_is_protocol_enabled(find_protocol_by_id(i)))
109
0
        return;
110
111
0
    populate->add_to_list(table_name, dissector_description, value, populate->ui_element);
112
0
}
113
114
void decode_as_default_populate_list(const char *table_name, decode_as_add_to_list_func add_to_list, void *ui_element)
115
0
{
116
0
    struct decode_as_default_populate populate;
117
118
0
    populate.add_to_list = add_to_list;
119
0
    populate.ui_element = ui_element;
120
121
0
    dissector_table_foreach_handle(table_name, decode_proto_add_to_list, &populate);
122
0
}
123
124
bool decode_as_default_reset(const char *name, const void *pattern)
125
0
{
126
0
    switch (get_dissector_table_selector_type(name)) {
127
0
    case FT_UINT8:
128
0
    case FT_UINT16:
129
0
    case FT_UINT24:
130
0
    case FT_UINT32:
131
0
        dissector_reset_uint(name, GPOINTER_TO_UINT(pattern));
132
0
        return true;
133
0
    case FT_NONE:
134
0
        dissector_reset_payload(name);
135
0
        return true;
136
0
    case FT_STRING:
137
0
    case FT_STRINGZ:
138
0
    case FT_UINT_STRING:
139
0
    case FT_STRINGZPAD:
140
0
    case FT_STRINGZTRUNC:
141
0
        dissector_reset_string(name, (!pattern)?"":(const char *) pattern);
142
0
        return true;
143
0
    default:
144
0
        return false;
145
0
    };
146
147
0
    return true;
148
0
}
149
150
bool decode_as_default_change(const char *name, const void *pattern, const void *handle, const char *list_name _U_)
151
0
{
152
0
    const dissector_handle_t dissector = (const dissector_handle_t)handle;
153
0
    switch (get_dissector_table_selector_type(name)) {
154
0
    case FT_UINT8:
155
0
    case FT_UINT16:
156
0
    case FT_UINT24:
157
0
    case FT_UINT32:
158
0
        dissector_change_uint(name, GPOINTER_TO_UINT(pattern), dissector);
159
0
        return true;
160
0
    case FT_NONE:
161
0
        dissector_change_payload(name, dissector);
162
0
        return true;
163
0
    case FT_STRING:
164
0
    case FT_STRINGZ:
165
0
    case FT_UINT_STRING:
166
0
    case FT_STRINGZPAD:
167
0
    case FT_STRINGZTRUNC:
168
0
        dissector_change_string(name, (!pattern)?"":(const char *) pattern, dissector);
169
0
        return true;
170
0
    default:
171
0
        return false;
172
0
    };
173
174
0
    return true;
175
0
}
176
177
/* Some useful utilities for Decode As */
178
179
/*
180
 * A list of dissectors that need to be reset.
181
 */
182
static GSList *dissector_reset_list;
183
184
/*
185
 * A callback function to parse each "decode as" entry in the file and apply the change
186
 */
187
static prefs_set_pref_e
188
read_set_decode_as_entries(char *key, const char *value,
189
                           void *user_data,
190
                           bool return_range_errors _U_)
191
0
{
192
0
    char *values[4] = {NULL, NULL, NULL, NULL};
193
0
    char delimiter[4] = {',', ',', ',','\0'};
194
0
    char *pch;
195
0
    unsigned i, j;
196
0
    GHashTable* processed_entries = (GHashTable*)user_data;
197
0
    dissector_table_t sub_dissectors;
198
0
    prefs_set_pref_e retval = PREFS_SET_OK;
199
0
    bool is_valid = false;
200
201
0
    if (strcmp(key, DECODE_AS_ENTRY) == 0) {
202
        /* Parse csv into table, selector, initial, current */
203
0
        for (i = 0; i < 4; i++) {
204
0
            pch = strchr(value, delimiter[i]);
205
0
            if (pch == NULL) {
206
0
                for (j = 0; j < i; j++) {
207
0
                    g_free(values[j]);
208
0
                }
209
0
                return PREFS_SET_SYNTAX_ERR;
210
0
            }
211
0
            values[i] = g_strndup(value, pch - value);
212
0
            value = pch + 1;
213
0
        }
214
0
        sub_dissectors = find_dissector_table(values[0]);
215
0
        if (sub_dissectors != NULL) {
216
0
            dissector_handle_t handle;
217
0
            ftenum_t selector_type;
218
0
            pref_t* pref_value;
219
0
            module_t *module;
220
0
            const char* proto_name;
221
222
0
            selector_type = dissector_table_get_type(sub_dissectors);
223
224
0
            handle = dissector_table_get_dissector_handle(sub_dissectors, values[3]);
225
0
            if (handle != NULL || g_ascii_strcasecmp(values[3], DECODE_AS_NONE) == 0) {
226
0
                is_valid = true;
227
0
            }
228
229
0
            if (is_valid) {
230
0
                if (FT_IS_STRING(selector_type)) {
231
0
                    dissector_change_string(values[0], values[1], handle);
232
0
                } else {
233
0
                    char *p;
234
0
                    long long_value;
235
236
0
                    long_value = strtol(values[1], &p, 0);
237
0
                    if (p == values[0] || *p != '\0' || long_value < 0 ||
238
0
                          (unsigned long)long_value > UINT_MAX) {
239
0
                        retval = PREFS_SET_SYNTAX_ERR;
240
0
                        is_valid = false;
241
0
                    } else {
242
0
                        dissector_change_uint(values[0], (unsigned)long_value, handle);
243
0
                    }
244
245
                    /* Now apply the value data back to dissector table preference */
246
0
                    if (handle != NULL) {
247
0
                        proto_name = proto_get_protocol_filter_name(dissector_handle_get_protocol_index(handle));
248
0
                        module = prefs_find_module(proto_name);
249
                        // values[0] is the dissector table
250
0
                        char *pref_name = ws_strdup_printf("%s%s", values[0], dissector_handle_get_pref_suffix(handle));
251
0
                        pref_value = prefs_find_preference(module, pref_name);
252
0
                        g_free(pref_name);
253
0
                        if (pref_value != NULL) {
254
0
                            bool replace = false;
255
0
                            if (g_hash_table_lookup(processed_entries, proto_name) == NULL) {
256
                                /* First decode as entry for this protocol, ranges may be replaced */
257
0
                                replace = true;
258
259
                                /* Remember we've processed this protocol */
260
0
                                g_hash_table_insert(processed_entries, (void *)proto_name, (void *)proto_name);
261
0
                            }
262
263
0
                            prefs_add_decode_as_value(pref_value, (unsigned)long_value, replace);
264
0
                            module->prefs_changed_flags |= prefs_get_effect_flags(pref_value);
265
0
                        }
266
0
                    }
267
0
                }
268
0
            }
269
0
            if (is_valid) {
270
0
                decode_build_reset_list(values[0], selector_type, values[1], NULL, NULL);
271
0
            }
272
0
        } else {
273
0
            retval = PREFS_SET_SYNTAX_ERR;
274
0
        }
275
276
0
    } else {
277
0
        retval = PREFS_SET_NO_SUCH_PREF;
278
0
    }
279
280
0
    for (i = 0; i < 4; i++) {
281
0
        g_free(values[i]);
282
0
    }
283
0
    return retval;
284
0
}
285
286
void
287
load_decode_as_entries(const char* app_env_var_prefix)
288
15
{
289
15
    char   *daf_path;
290
15
    FILE   *daf;
291
292
15
    decode_clear_all();
293
294
15
    daf_path = get_persconffile_path(DECODE_AS_ENTRIES_FILE_NAME, true, app_env_var_prefix);
295
15
    if ((daf = ws_fopen(daf_path, "r")) != NULL) {
296
        /* Store saved entries for better range processing */
297
0
        GHashTable* processed_entries = g_hash_table_new(g_str_hash, g_str_equal);
298
0
        read_prefs_file(daf_path, daf, read_set_decode_as_entries, processed_entries);
299
0
        g_hash_table_destroy(processed_entries);
300
0
        fclose(daf);
301
0
    }
302
15
    g_free(daf_path);
303
15
}
304
305
306
/* Make a sorted list of the entries as we are fetching them from a hash table. Then write it out from the sorted list */
307
static void
308
decode_as_write_entry (const char *table_name, ftenum_t selector_type,
309
                       void *key, void *value, void *user_data)
310
0
{
311
0
    GList **decode_as_rows_list = (GList **)user_data;
312
0
    dissector_handle_t current, initial;
313
0
    const char *current_dissector_name, *initial_dissector_name, *decode_as_row;
314
315
0
    current = dtbl_entry_get_handle((dtbl_entry_t *)value);
316
0
    if (current == NULL)
317
0
        current_dissector_name = DECODE_AS_NONE;
318
0
    else
319
0
        current_dissector_name = dissector_handle_get_description(current);
320
0
    initial = dtbl_entry_get_initial_handle((dtbl_entry_t *)value);
321
0
    if (initial == NULL)
322
0
        initial_dissector_name = DECODE_AS_NONE;
323
0
    else
324
0
        initial_dissector_name = dissector_handle_get_description(initial);
325
326
0
    switch (selector_type) {
327
328
0
    case FT_UINT8:
329
0
    case FT_UINT16:
330
0
    case FT_UINT24:
331
0
    case FT_UINT32:
332
        /*
333
         * XXX - write these in decimal, regardless of the base of
334
         * the dissector table's selector, as older versions of
335
         * Wireshark used atoi() when reading this file, and
336
         * failed to handle hex or octal numbers.
337
         *
338
         * That will be fixed in future 1.10 and 1.12 releases,
339
         * but pre-1.10 releases are at end-of-life and won't
340
         * be fixed.
341
         */
342
0
        decode_as_row = ws_strdup_printf(
343
0
            DECODE_AS_ENTRY ": %s,%u,%s,%s\n",
344
0
            table_name, GPOINTER_TO_UINT(key), initial_dissector_name,
345
0
            current_dissector_name);
346
0
        break;
347
0
    case FT_NONE:
348
        /*
349
         * XXX - Just put a placeholder for the key value.  Currently
350
         * FT_NONE dissector table uses a single uint value for
351
         * a placeholder
352
         */
353
0
        decode_as_row = ws_strdup_printf(
354
0
            DECODE_AS_ENTRY ": %s,0,%s,%s\n",
355
0
            table_name, initial_dissector_name,
356
0
            current_dissector_name);
357
0
        break;
358
359
0
    case FT_STRING:
360
0
    case FT_STRINGZ:
361
0
    case FT_UINT_STRING:
362
0
    case FT_STRINGZPAD:
363
0
    case FT_STRINGZTRUNC:
364
0
        decode_as_row = ws_strdup_printf(
365
0
            DECODE_AS_ENTRY ": %s,%s,%s,%s\n",
366
0
            table_name, (char *)key, initial_dissector_name,
367
0
            current_dissector_name);
368
0
        break;
369
370
0
    default:
371
0
        ws_assert_not_reached();
372
0
        break;
373
0
    }
374
375
    /* Do we need a better sort function ???*/
376
0
    *decode_as_rows_list = g_list_insert_sorted (*decode_as_rows_list, (void *)decode_as_row,
377
0
        (GCompareFunc)g_ascii_strcasecmp);
378
379
0
}
380
381
/* Print the sorted rows to File */
382
static void
383
decode_as_print_rows(void *data, void *user_data)
384
0
{
385
0
    FILE *da_file = (FILE *)user_data;
386
0
    const char *decode_as_row = (const char *)data;
387
388
0
    fprintf(da_file, "%s",decode_as_row);
389
390
0
}
391
int
392
save_decode_as_entries(const char* app_name, const char* app_env_var_prefix, char** err)
393
0
{
394
0
    char *pf_dir_path;
395
0
    char *daf_path;
396
0
    FILE *da_file;
397
0
    GList *decode_as_rows_list = NULL;
398
399
0
    if (create_persconffile_dir(app_env_var_prefix, &pf_dir_path) == -1) {
400
0
        *err = ws_strdup_printf("Can't create directory\n\"%s\"\nfor recent file: %s.",
401
0
                                pf_dir_path, g_strerror(errno));
402
0
        g_free(pf_dir_path);
403
0
        return -1;
404
0
    }
405
406
0
    daf_path = get_persconffile_path(DECODE_AS_ENTRIES_FILE_NAME, true, app_env_var_prefix);
407
0
    if ((da_file = ws_fopen(daf_path, "w")) == NULL) {
408
0
        *err = ws_strdup_printf("Can't open decode_as_entries file\n\"%s\": %s.",
409
0
                                daf_path, g_strerror(errno));
410
0
        g_free(daf_path);
411
0
        return -1;
412
0
    }
413
414
0
    fprintf(da_file, "# \"Decode As\" entries file for %s " VERSION ".\n"
415
0
        "#\n"
416
0
        "# This file is regenerated each time \"Decode As\" preferences\n"
417
0
        "# are saved within %s. Making manual changes should be safe,\n"
418
0
        "# however.\n",
419
0
        app_name, app_name);
420
421
0
    dissector_all_tables_foreach_changed(decode_as_write_entry, &decode_as_rows_list);
422
423
0
    g_list_foreach(decode_as_rows_list, decode_as_print_rows, da_file);
424
425
0
    fclose(da_file);
426
0
    g_free(daf_path);
427
0
    g_list_free_full(decode_as_rows_list, g_free);
428
429
0
    return 0;
430
0
}
431
432
/*
433
 * Data structure for tracking which dissector need to be reset.  This
434
 * structure is necessary as a hash table entry cannot be removed
435
 * while a g_hash_table_foreach walk is in progress.
436
 */
437
typedef struct dissector_delete_item {
438
    /* The name of the dissector table */
439
    char *ddi_table_name;
440
    /* The type of the selector in that dissector table */
441
    ftenum_t ddi_selector_type;
442
    /* The selector in the dissector table */
443
    union {
444
        unsigned   sel_uint;
445
        char    *sel_string;
446
    } ddi_selector;
447
} dissector_delete_item_t;
448
449
void
450
decode_build_reset_list (const char *table_name, ftenum_t selector_type,
451
                         void *key, void *value _U_,
452
                         void *user_data _U_)
453
0
{
454
0
    dissector_delete_item_t *item;
455
456
0
    item = g_new(dissector_delete_item_t,1);
457
0
    item->ddi_table_name = g_strdup(table_name);
458
0
    item->ddi_selector_type = selector_type;
459
0
    switch (selector_type) {
460
461
0
    case FT_UINT8:
462
0
    case FT_UINT16:
463
0
    case FT_UINT24:
464
0
    case FT_UINT32:
465
0
        item->ddi_selector.sel_uint = GPOINTER_TO_UINT(key);
466
0
        break;
467
468
0
    case FT_NONE:
469
        /* Not really needed, but prevents the assert */
470
0
        item->ddi_selector.sel_uint = 0;
471
0
        break;
472
473
0
    case FT_STRING:
474
0
    case FT_STRINGZ:
475
0
    case FT_UINT_STRING:
476
0
    case FT_STRINGZPAD:
477
0
    case FT_STRINGZTRUNC:
478
0
        item->ddi_selector.sel_string = g_strdup((char *)key);
479
0
        break;
480
481
0
    default:
482
0
        ws_assert_not_reached();
483
0
    }
484
0
    dissector_reset_list = g_slist_prepend(dissector_reset_list, item);
485
0
}
486
487
/* clear all settings */
488
void
489
decode_clear_all(void)
490
15
{
491
15
    dissector_delete_item_t *item;
492
15
    GSList *tmp;
493
494
15
    dissector_all_tables_foreach_changed(decode_build_reset_list, NULL);
495
496
15
    for (tmp = dissector_reset_list; tmp; tmp = g_slist_next(tmp)) {
497
0
        item = (dissector_delete_item_t *)tmp->data;
498
0
        switch (item->ddi_selector_type) {
499
500
0
        case FT_UINT8:
501
0
        case FT_UINT16:
502
0
        case FT_UINT24:
503
0
        case FT_UINT32:
504
0
            dissector_reset_uint(item->ddi_table_name,
505
0
                                 item->ddi_selector.sel_uint);
506
0
            break;
507
508
0
        case FT_NONE:
509
0
            dissector_reset_payload(item->ddi_table_name);
510
0
            break;
511
512
0
        case FT_STRING:
513
0
        case FT_STRINGZ:
514
0
        case FT_UINT_STRING:
515
0
        case FT_STRINGZPAD:
516
0
        case FT_STRINGZTRUNC:
517
0
            dissector_reset_string(item->ddi_table_name,
518
0
                                   item->ddi_selector.sel_string);
519
0
            g_free(item->ddi_selector.sel_string);
520
0
            break;
521
522
0
        default:
523
0
            ws_assert_not_reached();
524
0
        }
525
0
        g_free(item->ddi_table_name);
526
0
        g_free(item);
527
0
    }
528
15
    g_slist_free(dissector_reset_list);
529
15
    dissector_reset_list = NULL;
530
531
1.14k
    for (GList* cur = decode_as_list; cur; cur = cur->next) {
532
1.12k
        decode_as_t* entry = (decode_as_t*)cur->data;
533
1.12k
        if (entry->reset_all != NULL) {
534
15
            entry->reset_all();
535
15
        }
536
1.12k
    }
537
15
}
538
539
void
540
decode_cleanup(void)
541
0
{
542
0
    g_list_free(decode_as_list);
543
    decode_as_list = NULL;
544
0
}
545
546
/*
547
 * Editor modelines
548
 *
549
 * Local Variables:
550
 * c-basic-offset: 4
551
 * tab-width: 8
552
 * indent-tabs-mode: nil
553
 * End:
554
 *
555
 * ex: set shiftwidth=4 tabstop=8 expandtab:
556
 * :indentSize=4:tabSize=8:noTabs=true:
557
 */