Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/file-file.c
Line
Count
Source
1
/* file-file.c
2
 *
3
 * Top-most file dissector. Decides dissector based on Filetap Encapsulation Type.
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 2000 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include "config.h"
13
14
#ifdef _MSC_VER
15
#include <windows.h>
16
#endif
17
18
#include <epan/packet.h>
19
#include <epan/exceptions.h>
20
#include <epan/show_exception.h>
21
#include <epan/tap.h>
22
#include <epan/proto_data.h>
23
#include <epan/color_filters.h>
24
#include <wiretap/wtap.h>
25
#include <wsutil/str_util.h>
26
#include <wsutil/array.h>
27
28
#include "file-file.h"
29
30
void proto_register_file(void);
31
void event_register_file(void);
32
33
static int proto_file;
34
static int hf_file_record_number;
35
static int hf_file_record_len;
36
static int hf_file_ftap_encap;
37
static int hf_file_marked;
38
static int hf_file_ignored;
39
static int hf_file_protocols;
40
static int hf_file_num_p_prot_data;
41
static int hf_file_proto_name_and_key;
42
static int hf_file_color_filter_name;
43
static int hf_file_color_filter_text;
44
45
static int ett_file;
46
47
static int file_tap;
48
49
static dissector_table_t file_encap_dissector_table;
50
51
/*
52
 * Routine used to register record end routine.  The routine should only
53
 * be registered when the dissector is used in the record, not in the
54
 * proto_register_XXX function.
55
 */
56
void
57
register_file_record_end_routine(packet_info *pinfo, void (*func)(void))
58
0
{
59
0
  pinfo->frame_end_routines = g_slist_append(pinfo->frame_end_routines, (void *)func);
60
0
}
61
62
typedef void (*void_func_t)(void);
63
64
static void
65
call_file_record_end_routine(void *routine, void *dummy _U_)
66
0
{
67
0
  void_func_t func = (void_func_t)routine;
68
0
  (*func)();
69
0
}
70
71
/* XXX - "packet comment" is passed into dissector as data, but currently doesn't have a use */
72
static int
73
dissect_file_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data)
74
0
{
75
0
  proto_item  *volatile ti = NULL;
76
0
  proto_tree  *volatile fh_tree = NULL;
77
0
  proto_tree  *volatile tree;
78
0
  proto_item  *item;
79
0
  const char *cap_plurality, *frame_plurality;
80
0
  const color_filter_t *color_filter;
81
0
  file_data_t *file_data = (file_data_t*)data;
82
83
0
  tree=parent_tree;
84
85
0
  pinfo->current_proto = "File";
86
87
  /* if FILE is not referenced from any filters we don't need to worry about
88
     generating any tree items.  */
89
0
  if(!proto_field_is_referenced(tree, proto_file)) {
90
0
    tree=NULL;
91
0
  } else {
92
0
    unsigned       cap_len, frame_len;
93
94
    /* Put in frame header information. */
95
0
    cap_len = tvb_captured_length(tvb);
96
0
    frame_len = tvb_reported_length(tvb);
97
98
0
    cap_plurality = plurality(cap_len, "", "s");
99
0
    frame_plurality = plurality(frame_len, "", "s");
100
101
0
    ti = proto_tree_add_protocol_format(tree, proto_file, tvb, 0, -1,
102
0
        "File record %u: %u byte%s",
103
0
        pinfo->num, frame_len, frame_plurality);
104
0
    proto_item_append_text(ti, ", %u byte%s",
105
0
        cap_len, cap_plurality);
106
107
0
    fh_tree = proto_item_add_subtree(ti, ett_file);
108
109
0
    if (pinfo->rec->rec_type == REC_TYPE_PACKET)
110
0
      proto_tree_add_int(fh_tree, hf_file_ftap_encap, tvb, 0, 0, pinfo->rec->rec_header.packet_header.pkt_encap);
111
112
0
    proto_tree_add_uint(fh_tree, hf_file_record_number, tvb, 0, 0, pinfo->num);
113
114
0
    proto_tree_add_uint_format(fh_tree, hf_file_record_len, tvb,
115
0
             0, 0, frame_len, "Record Length: %u byte%s (%u bits)",
116
0
             frame_len, frame_plurality, frame_len * 8);
117
118
0
    ti = proto_tree_add_boolean(fh_tree, hf_file_marked, tvb, 0, 0,pinfo->fd->marked);
119
0
    proto_item_set_generated(ti);
120
121
0
    ti = proto_tree_add_boolean(fh_tree, hf_file_ignored, tvb, 0, 0,pinfo->fd->ignored);
122
0
    proto_item_set_generated(ti);
123
124
0
    GPtrArray *ppd_array = p_get_proto_names_and_keys(wmem_file_scope(), pinfo);
125
0
    if(ppd_array){
126
0
      proto_item *ppd_item;
127
0
      unsigned num_entries = ppd_array->len;
128
0
      ppd_item = proto_tree_add_uint(fh_tree, hf_file_num_p_prot_data, tvb, 0, 0, num_entries);
129
0
      proto_item_set_generated(ppd_item);
130
0
      for(unsigned i=0; i<num_entries; i++){
131
0
        char* str = ppd_array->pdata[i];
132
0
        proto_tree_add_string_format(fh_tree, hf_file_proto_name_and_key, tvb, 0, 0, str, "%s", str);
133
0
      }
134
0
      g_ptr_array_unref(ppd_array);
135
0
    }
136
137
#if 0
138
    if (show_file_off) {
139
      proto_tree_add_int64_format_value(fh_tree, hf_frame_file_off, tvb,
140
                0, 0, pinfo->fd->file_off,
141
                "%" PRId64 " (0x%" PRIx64 ")",
142
                pinfo->fd->file_off, pinfo->fd->file_off);
143
    }
144
#endif
145
0
  }
146
147
0
  if (pinfo->fd->ignored) {
148
    /* Ignored package, stop handling here */
149
0
    col_set_str(pinfo->cinfo, COL_INFO, "<Ignored>");
150
0
    proto_tree_add_boolean_format(tree, hf_file_ignored, tvb, 0, 0, true, "This record is marked as ignored");
151
0
    return tvb_captured_length(tvb);
152
0
  }
153
154
  /* Portable Exception Handling to trap Wireshark specific exceptions like BoundsError exceptions */
155
0
  TRY {
156
#ifdef _MSC_VER
157
    /* Win32: Visual-C Structured Exception Handling (SEH) to trap hardware exceptions
158
       like memory access violations.
159
       (a running debugger will be called before the except part below) */
160
    /* Note: A Windows "exceptional exception" may leave the kazlib's (Portable Exception Handling)
161
       stack in an inconsistent state thus causing a crash at some point in the
162
       handling of the exception.
163
       See: https://lists.wireshark.org/archives/wireshark-dev/200704/msg00243.html
164
    */
165
    __try {
166
#endif
167
0
      if (pinfo->rec->rec_type != REC_TYPE_PACKET ||
168
0
          !dissector_try_uint(file_encap_dissector_table, pinfo->rec->rec_header.packet_header.pkt_encap,
169
0
            tvb, pinfo, parent_tree)) {
170
171
0
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "UNKNOWN");
172
0
        col_add_fstr(pinfo->cinfo, COL_INFO, "FTAP_ENCAP = %d",
173
0
               pinfo->rec->rec_header.packet_header.pkt_encap);
174
0
        call_data_dissector(tvb, pinfo, parent_tree);
175
0
      }
176
#ifdef _MSC_VER
177
    } __except(EXCEPTION_EXECUTE_HANDLER /* handle all exceptions */) {
178
      switch(GetExceptionCode()) {
179
      case(STATUS_ACCESS_VIOLATION):
180
        show_exception(tvb, pinfo, parent_tree, DissectorError,
181
                 "STATUS_ACCESS_VIOLATION: dissector accessed an invalid memory address");
182
        break;
183
      case(STATUS_INTEGER_DIVIDE_BY_ZERO):
184
        show_exception(tvb, pinfo, parent_tree, DissectorError,
185
                 "STATUS_INTEGER_DIVIDE_BY_ZERO: dissector tried an integer division by zero");
186
        break;
187
      case(STATUS_STACK_OVERFLOW):
188
        show_exception(tvb, pinfo, parent_tree, DissectorError,
189
                 "STATUS_STACK_OVERFLOW: dissector overflowed the stack (e.g. endless loop)");
190
        /* XXX - this will have probably corrupted the stack,
191
           which makes problems later in the exception code */
192
        break;
193
        /* XXX - add other hardware exception codes as required */
194
      default:
195
        show_exception(tvb, pinfo, parent_tree, DissectorError,
196
                 ws_strdup_printf("dissector caused an unknown exception: 0x%x", GetExceptionCode()));
197
      }
198
    }
199
#endif
200
0
  }
201
0
  CATCH_BOUNDS_AND_DISSECTOR_ERRORS {
202
0
    show_exception(tvb, pinfo, parent_tree, EXCEPT_CODE, GET_MESSAGE);
203
0
  }
204
0
  ENDTRY;
205
206
0
  if(proto_field_is_referenced(tree, hf_file_protocols)) {
207
0
    wmem_strbuf_t *val = wmem_strbuf_new(pinfo->pool, "");
208
0
    wmem_list_frame_t *frame;
209
    /* skip the first entry, it's always the "frame" protocol */
210
0
    frame = wmem_list_frame_next(wmem_list_head(pinfo->layers));
211
0
    if (frame) {
212
0
      wmem_strbuf_append(val, proto_get_protocol_filter_name(GPOINTER_TO_UINT(wmem_list_frame_data(frame))));
213
0
      frame = wmem_list_frame_next(frame);
214
0
    }
215
0
    while (frame) {
216
0
      wmem_strbuf_append_c(val, ':');
217
0
      wmem_strbuf_append(val, proto_get_protocol_filter_name(GPOINTER_TO_UINT(wmem_list_frame_data(frame))));
218
0
      frame = wmem_list_frame_next(frame);
219
0
    }
220
0
    ti = proto_tree_add_string(fh_tree, hf_file_protocols, tvb, 0, 0, wmem_strbuf_get_str(val));
221
0
    proto_item_set_generated(ti);
222
0
  }
223
224
  /*  Call postdissectors if we have any (while trying to avoid another
225
   *  TRY/CATCH)
226
   */
227
0
  if (have_postdissector()) {
228
0
    TRY {
229
#ifdef _MSC_VER
230
      /* Win32: Visual-C Structured Exception Handling (SEH)
231
         to trap hardware exceptions like memory access violations */
232
      /* (a running debugger will be called before the except part below) */
233
      /* Note: A Windows "exceptional exception" may leave the kazlib's (Portable Exception Handling)
234
         stack in an inconsistent state thus causing a crash at some point in the
235
         handling of the exception.
236
         See: https://lists.wireshark.org/archives/wireshark-dev/200704/msg00243.html
237
      */
238
      __try {
239
#endif
240
0
        call_all_postdissectors(tvb, pinfo, parent_tree);
241
#ifdef _MSC_VER
242
      } __except(EXCEPTION_EXECUTE_HANDLER /* handle all exceptions */) {
243
        switch(GetExceptionCode()) {
244
        case(STATUS_ACCESS_VIOLATION):
245
          show_exception(tvb, pinfo, parent_tree, DissectorError,
246
                   "STATUS_ACCESS_VIOLATION: dissector accessed an invalid memory address");
247
          break;
248
        case(STATUS_INTEGER_DIVIDE_BY_ZERO):
249
          show_exception(tvb, pinfo, parent_tree, DissectorError,
250
                   "STATUS_INTEGER_DIVIDE_BY_ZERO: dissector tried an integer division by zero");
251
          break;
252
        case(STATUS_STACK_OVERFLOW):
253
          show_exception(tvb, pinfo, parent_tree, DissectorError,
254
                   "STATUS_STACK_OVERFLOW: dissector overflowed the stack (e.g. endless loop)");
255
          /* XXX - this will have probably corrupted the stack,
256
             which makes problems later in the exception code */
257
          break;
258
          /* XXX - add other hardware exception codes as required */
259
        default:
260
          show_exception(tvb, pinfo, parent_tree, DissectorError,
261
                   ws_strdup_printf("dissector caused an unknown exception: 0x%x", GetExceptionCode()));
262
        }
263
      }
264
#endif
265
0
    }
266
0
    CATCH_BOUNDS_AND_DISSECTOR_ERRORS {
267
0
      show_exception(tvb, pinfo, parent_tree, EXCEPT_CODE, GET_MESSAGE);
268
0
    }
269
0
    ENDTRY;
270
0
  }
271
272
  /* Attempt to (re-)calculate color filters (if any). */
273
0
  if (pinfo->fd->need_colorize) {
274
0
    wmem_list_t *matches = NULL;
275
276
    /* Get ALL matching color filters (not just first).
277
     * Store matches in proto_data so the display code below can show
278
     * multiple matching rules in the frame tree. This enables multi-color
279
     * support in TShark when --color flag is used. */
280
0
    color_filter = color_filters_colorize_packet_all(file_data->color_edt, wmem_file_scope(), &matches, false);
281
0
    pinfo->fd->color_filter = color_filter;
282
0
    pinfo->fd->need_colorize = 0;
283
284
    /* Store matches in proto_data for display code below to access.
285
     * free any previously stored list (from a prior dissect pass) first. */
286
0
    if (matches) {
287
0
      wmem_list_t *old_matches = (wmem_list_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_file, 0);
288
0
      if (old_matches) {
289
0
        wmem_destroy_list(old_matches);
290
0
        p_remove_proto_data(wmem_file_scope(), pinfo, proto_file, 0);
291
0
      }
292
0
      p_add_proto_data(wmem_file_scope(), pinfo, proto_file, 0, matches);
293
0
    }
294
0
  } else {
295
0
    color_filter = pinfo->fd->color_filter;
296
0
  }
297
  /* Retrieve all matching filters from proto_data (stored during colorization above).
298
   * This enables multi-color display for both GUI and TShark. */
299
0
  wmem_list_t *matches = NULL;
300
0
  if (fh_tree) {
301
0
    matches = (wmem_list_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_file, 0);
302
0
  }
303
304
  /* Show all matching color filters if packet details multi-color is enabled.
305
   * This is controlled independently from packet list and scrollbar display. */
306
0
  if (matches && prefs.gui_packet_list_multi_color_details) {
307
    /* Show all matching color filters from stored list */
308
0
    for (wmem_list_frame_t *lf = wmem_list_head(matches); lf != NULL; lf = wmem_list_frame_next(lf)) {
309
0
      const color_filter_t *colorf = (const color_filter_t *)wmem_list_frame_data(lf);
310
311
0
      item = proto_tree_add_string(fh_tree, hf_file_color_filter_name, tvb,
312
0
                 0, 0, colorf->filter_name);
313
0
      proto_item_set_generated(item);
314
0
      item = proto_tree_add_string(fh_tree, hf_file_color_filter_text, tvb,
315
0
                 0, 0, colorf->filter_text);
316
0
      proto_item_set_generated(item);
317
0
    }
318
0
  } else if (color_filter) {
319
    /* Fallback to single filter if no stored matches */
320
0
    pinfo->fd->color_filter = color_filter;
321
0
    item = proto_tree_add_string(fh_tree, hf_file_color_filter_name, tvb,
322
0
               0, 0, color_filter->filter_name);
323
0
    proto_item_set_generated(item);
324
0
    item = proto_tree_add_string(fh_tree, hf_file_color_filter_text, tvb,
325
0
               0, 0, color_filter->filter_text);
326
0
    proto_item_set_generated(item);
327
0
  }
328
329
0
  tap_queue_packet(file_tap, pinfo, NULL);
330
331
332
0
  if (pinfo->frame_end_routines) {
333
0
    g_slist_foreach(pinfo->frame_end_routines, &call_file_record_end_routine, NULL);
334
0
    g_slist_free(pinfo->frame_end_routines);
335
0
    pinfo->frame_end_routines = NULL;
336
0
  }
337
338
0
  return tvb_captured_length(tvb);
339
0
}
340
341
static void
342
common_register_file(void)
343
16
{
344
16
  static hf_register_info hf[] = {
345
16
    { &hf_file_record_number,
346
16
      { "Record Number", "file.record_number",
347
16
        FT_UINT32, BASE_DEC, NULL, 0x0,
348
16
        NULL, HFILL }},
349
350
16
    { &hf_file_record_len,
351
16
      { "Record length", "file.record_len",
352
16
        FT_UINT32, BASE_DEC, NULL, 0x0,
353
16
        NULL, HFILL }},
354
#if 0
355
    { &hf_frame_file_off,
356
      { "File Offset", "file.offset",
357
        FT_INT64, BASE_DEC, NULL, 0x0,
358
        NULL, HFILL }},
359
#endif
360
16
    { &hf_file_marked,
361
16
      { "File record is marked", "file.marked",
362
16
        FT_BOOLEAN, BASE_NONE, NULL, 0x0,
363
16
        "File record is marked in the GUI", HFILL }},
364
365
16
    { &hf_file_ignored,
366
16
      { "File record is ignored", "file.ignored",
367
16
        FT_BOOLEAN, BASE_NONE, NULL, 0x0,
368
16
        "File record is ignored by the dissectors", HFILL }},
369
370
16
    { &hf_file_protocols,
371
16
      { "File record types in frame", "file.record_types",
372
16
        FT_STRING, BASE_NONE, NULL, 0x0,
373
16
        "File record types carried by this frame", HFILL }},
374
375
16
    { &hf_file_color_filter_name,
376
16
      { "Coloring Rule Name", "file.coloring_rule.name",
377
16
        FT_STRING, BASE_NONE, NULL, 0x0,
378
16
        "The file record matched the coloring rule with this name", HFILL }},
379
380
16
    { &hf_file_color_filter_text,
381
16
      { "Coloring Rule String", "file.coloring_rule.string",
382
16
        FT_STRING, BASE_NONE, NULL, 0x0,
383
16
        "The file record matched this coloring rule string", HFILL }},
384
385
16
    { &hf_file_num_p_prot_data,
386
16
      { "Number of per-record-data", "file.p_record_data",
387
16
        FT_UINT32, BASE_DEC, NULL, 0x0,
388
16
        NULL, HFILL }},
389
390
16
    { &hf_file_proto_name_and_key,
391
16
      { "Protocol Name and Key", "file.proto_name_and_key",
392
16
        FT_STRING, BASE_NONE, NULL, 0x0,
393
16
        NULL, HFILL }},
394
395
16
    { &hf_file_ftap_encap,
396
16
      { "Encapsulation type", "file.encap_type",
397
16
        FT_INT16, BASE_DEC, NULL, 0x0,
398
16
        NULL, HFILL }},
399
16
  };
400
401
16
  static int *ett[] = {
402
16
    &ett_file
403
16
  };
404
405
#if 0
406
  module_t *file_module;
407
#endif
408
409
16
  proto_file = proto_register_protocol("File", "File", "file");
410
16
  proto_register_field_array(proto_file, hf, array_length(hf));
411
16
  proto_register_subtree_array(ett, array_length(ett));
412
16
  register_dissector("file",dissect_file_record,proto_file);
413
414
16
  file_encap_dissector_table = register_dissector_table("ftap_encap",
415
16
      "Filetap encapsulation type", proto_file, FT_UINT32, BASE_DEC);
416
417
  /* You can't disable dissection of "Frame", as that would be
418
     tantamount to not doing any dissection whatsoever. */
419
16
  proto_set_cant_toggle(proto_file);
420
421
  /* Our preferences */
422
#if 0
423
  frame_module = prefs_register_protocol(proto_frame, NULL);
424
  prefs_register_bool_preference(frame_module, "show_file_off",
425
      "Show File Offset", "Show offset of frame in capture file", &show_file_off);
426
#endif
427
428
16
  file_tap=register_tap("file");
429
16
}
430
431
void
432
proto_register_file(void)
433
16
{
434
16
  common_register_file();
435
16
}
436
437
void
438
event_register_file(void)
439
0
{
440
0
  common_register_file();
441
0
}
442
443
/*
444
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
445
 *
446
 * Local variables:
447
 * c-basic-offset: 8
448
 * tab-width: 8
449
 * indent-tabs-mode: t
450
 * End:
451
 *
452
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
453
 * :indentSize=8:tabSize=8:noTabs=false:
454
 */