Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/print.c
Line
Count
Source
1
/* print.c
2
 * Routines for printing packet analysis trees.
3
 *
4
 * Gilbert Ramirez <gram@alumni.rice.edu>
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 <stdio.h>
16
#include <string.h>
17
18
#include <epan/epan.h>
19
#include <epan/epan_dissect.h>
20
#include <epan/to_str.h>
21
#include <epan/expert.h>
22
#include <epan/column.h>
23
#include <epan/column-info.h>
24
#include <epan/color_filters.h>
25
#include <epan/dfilter/dfilter.h>
26
#include <epan/prefs.h>
27
#include <epan/print.h>
28
#include <wsutil/array.h>
29
#include <wsutil/json_dumper.h>
30
#include <wsutil/filesystem.h>
31
#include <wsutil/utf8_entities.h>
32
#include <wsutil/str_util.h>
33
#include <wsutil/ws_assert.h>
34
#include <epan/strutil.h>
35
#include <ftypes/ftypes.h>
36
37
38
#define PDML_VERSION "0"
39
#define PSML_VERSION "0"
40
41
typedef struct {
42
    int                  level;
43
    print_stream_t      *stream;
44
    bool                 success;
45
    GSList              *src_list;
46
    print_dissections_e  print_dissections;
47
    bool                 print_hex_for_data;
48
    packet_char_enc      encoding;
49
    GHashTable          *output_only_tables; /* output only these protocols */
50
} print_data;
51
52
typedef struct {
53
    int             level;
54
    FILE           *fh;
55
    GSList         *src_list;
56
    wmem_map_t     *filter;
57
} write_pdml_data;
58
59
typedef struct {
60
    GSList         *src_list;
61
    wmem_map_t     *filter;
62
    bool            print_hex;
63
    bool            print_text;
64
    proto_node_children_grouper_func node_children_grouper;
65
    json_dumper    *dumper;
66
    tvbuff_t       *cached_src_tvb;  /* last-hit data source tvb */
67
    struct data_source *cached_src;  /* last-hit data source */
68
    uint32_t        cached_src_idx;  /* last-hit data source index */
69
    GHashTable     *grouping_table; /* reusable hash table for key grouping */
70
} write_json_data;
71
72
typedef struct {
73
    output_fields_t *fields;
74
    epan_dissect_t  *edt;
75
} write_field_data_t;
76
77
struct _output_fields {
78
    bool          print_bom;
79
    bool          print_header;
80
    char          separator;
81
    char          occurrence;
82
    char          aggregator;
83
    GPtrArray    *fields;
84
    GPtrArray    *field_dfilters;
85
    GHashTable   *field_indicies;
86
    GPtrArray   **field_values;
87
    wmem_map_t   *protocolfilter;
88
    char          quote;
89
    bool          escape;
90
    bool          includes_col_fields;
91
    char         *split_by;       /* protocol abbreviation to split rows on */
92
};
93
94
static char *get_field_hex_value(GSList *src_list, field_info *fi);
95
static void proto_tree_print_node(proto_node *node, void *data);
96
static void proto_tree_write_node_pdml(proto_node *node, void *data);
97
static void proto_tree_write_node_ek(proto_node *node, write_json_data *data);
98
static struct data_source* get_field_data_source(GSList *src_list, field_info *fi, uint32_t *idx);
99
static struct data_source* get_field_data_source_cached(GSList *src_list, field_info *fi, uint32_t *idx, write_json_data *pdata);
100
static const uint8_t *get_field_data(GSList *src_list, field_info *fi);
101
static const uint8_t *get_field_data_cached(GSList *src_list, field_info *fi, write_json_data *pdata);
102
static void pdml_write_field_hex_value(write_pdml_data *pdata, field_info *fi);
103
static void json_write_field_hex_value(write_json_data *pdata, field_info *fi);
104
static bool print_hex_data_buffer(print_stream_t *stream, const unsigned char *cp,
105
                                      unsigned length, packet_char_enc encoding,
106
                                      unsigned hexdump_options);
107
static void write_specified_fields(fields_format format,
108
                                   output_fields_t *fields,
109
                                   epan_dissect_t *edt, column_info *cinfo,
110
                                   FILE *fh,
111
                                   json_dumper *dumper);
112
static void print_escaped_xml(FILE *fh, const char *unescaped_string);
113
static void print_escaped_csv(FILE *fh, const char *unescaped_string, char delimiter, char quote_char, bool escape_wsp);
114
115
typedef void (*proto_node_value_writer)(proto_node *, write_json_data *);
116
static void write_json_index(json_dumper *dumper, epan_dissect_t *edt);
117
static void write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *data);
118
static void write_json_proto_node(GSList *node_values_head,
119
                                  const char *suffix,
120
                                  proto_node_value_writer value_writer,
121
                                  write_json_data *data);
122
static void write_json_proto_node_value_list(GSList *node_values_head,
123
                                             proto_node_value_writer value_writer,
124
                                             write_json_data *data);
125
static void write_json_proto_node_filtered(proto_node *node, write_json_data *data);
126
static void write_json_proto_node_hex_dump(proto_node *node, write_json_data *data);
127
static void write_json_proto_node_dynamic(proto_node *node, write_json_data *data);
128
static void write_json_proto_node_children(proto_node *node, write_json_data *data);
129
static void write_json_proto_node_value(proto_node *node, write_json_data *data);
130
static void write_json_proto_node_no_value(proto_node *node, write_json_data *data);
131
static const char *proto_node_to_json_key(proto_node *node);
132
133
static void print_pdml_geninfo(epan_dissect_t *edt, FILE *fh);
134
static void write_ek_summary(column_info *cinfo, write_json_data *pdata);
135
136
static void proto_tree_get_node_field_values(proto_node *node, void *data);
137
138
/* Cache the protocols and field handles that the print functionality needs
139
   This helps break explicit dependency on the dissectors. */
140
static int proto_data;
141
static int proto_frame;
142
143
void print_cache_field_handles(void)
144
16
{
145
16
    proto_data = proto_get_id_by_short_name("Data");
146
16
    proto_frame = proto_get_id_by_short_name("Frame");
147
16
}
148
149
bool
150
proto_tree_print(print_dissections_e print_dissections, bool print_hex,
151
                 epan_dissect_t *edt, GHashTable *output_only_tables,
152
                 print_stream_t *stream)
153
0
{
154
0
    print_data data;
155
156
    /* Create the output */
157
0
    data.level              = 0;
158
0
    data.stream             = stream;
159
0
    data.success            = true;
160
0
    data.src_list           = edt->pi.data_src;
161
0
    data.encoding           = (packet_char_enc)edt->pi.fd->encoding;
162
0
    data.print_dissections  = print_dissections;
163
    /* If we're printing the entire packet in hex, don't
164
       print uninterpreted data fields in hex as well. */
165
0
    data.print_hex_for_data = !print_hex;
166
0
    data.output_only_tables = output_only_tables;
167
168
0
    proto_tree_children_foreach(edt->tree, proto_tree_print_node, &data);
169
0
    return data.success;
170
0
}
171
172
/* Print a tree's data, and any child nodes. */
173
static void
174
proto_tree_print_node(proto_node *node, void *data)
175
0
{
176
0
    field_info   *fi    = PNODE_FINFO(node);
177
0
    print_data   *pdata = (print_data*) data;
178
0
    const uint8_t *pd;
179
0
    char          label_str[ITEM_LABEL_LENGTH];
180
0
    char         *label_ptr;
181
182
    /* dissection with an invisible proto tree? */
183
0
    ws_assert(fi);
184
185
    /* Don't print invisible entries. */
186
0
    if (proto_item_is_hidden(node) && (prefs.display_hidden_proto_items == false))
187
0
        return;
188
189
    /* Give up if we've already gotten an error. */
190
0
    if (!pdata->success)
191
0
        return;
192
193
    /* was a free format label produced? */
194
0
    if (fi->rep) {
195
0
        label_ptr = fi->rep->representation;
196
0
    }
197
0
    else { /* no, make a generic label */
198
0
        label_ptr = label_str;
199
0
        proto_item_fill_label(fi, label_str, NULL);
200
0
    }
201
202
0
    if (proto_item_is_generated(node))
203
0
        label_ptr = g_strconcat("[", label_ptr, "]", NULL);
204
205
0
    pdata->success = print_line(pdata->stream, pdata->level, label_ptr);
206
207
0
    if (proto_item_is_generated(node))
208
0
        g_free(label_ptr);
209
210
0
    if (!pdata->success)
211
0
        return;
212
213
    /*
214
     * If -O is specified, only display the protocols which are in the
215
     * lookup table.  Only check on the first level: once we start printing
216
     * a tree, print the rest of the subtree.  Otherwise we won't print
217
     * subitems whose abbreviation doesn't match the protocol--for example
218
     * text items (whose abbreviation is simply "text").
219
     */
220
0
    if ((pdata->output_only_tables != NULL) && (pdata->level == 0)
221
0
        && (g_hash_table_lookup(pdata->output_only_tables, fi->hfinfo->abbrev) == NULL)) {
222
0
        return;
223
0
    }
224
225
    /* If it's uninterpreted data, dump it (unless our caller will
226
       be printing the entire packet in hex). */
227
0
    if ((fi->hfinfo->id == proto_data) && (pdata->print_hex_for_data)) {
228
        /*
229
         * Find the data for this field.
230
         */
231
0
        pd = get_field_data(pdata->src_list, fi);
232
0
        if (pd) {
233
0
            if (!print_line(pdata->stream, 0, "")) {
234
0
                pdata->success = false;
235
0
                return;
236
0
            }
237
0
            if (!print_hex_data_buffer(pdata->stream, pd,
238
0
                                       fi->length, pdata->encoding, HEXDUMP_ASCII_INCLUDE)) {
239
0
                pdata->success = false;
240
0
                return;
241
0
            }
242
0
        }
243
0
    }
244
245
    /* If we're printing all levels, or if this node is one with a
246
       subtree and its subtree is expanded, recurse into the subtree,
247
       if it exists. */
248
0
    ws_assert((fi->tree_type >= -1) && (fi->tree_type < num_tree_types));
249
0
    if ((pdata->print_dissections == print_dissections_expanded) ||
250
0
        ((pdata->print_dissections == print_dissections_as_displayed) &&
251
0
         (fi->tree_type >= 0) && tree_expanded(fi->tree_type))) {
252
0
        if (node->first_child != NULL) {
253
0
            pdata->level++;
254
0
            proto_tree_children_foreach(node,
255
0
                                        proto_tree_print_node, pdata);
256
0
            pdata->level--;
257
0
            if (!pdata->success)
258
0
                return;
259
0
        }
260
0
    }
261
0
}
262
263
#define PDML2HTML_XSL "pdml2html.xsl"
264
#define PDML2HTML_URL "https://gitlab.com/wireshark/wireshark/-/tree/master/resources/share/doc/wireshark/"
265
void
266
write_pdml_preamble(FILE *fh, const char *filename, const char* doc_dir)
267
0
{
268
0
    time_t t = time(NULL);
269
0
    struct tm * timeinfo;
270
0
    char *fmt_ts;
271
0
    const char *ts;
272
273
    /* Create the output */
274
0
    timeinfo = localtime(&t);
275
0
    if (timeinfo != NULL) {
276
0
        fmt_ts = asctime(timeinfo);
277
0
        fmt_ts[strlen(fmt_ts)-1] = 0; /* overwrite \n */
278
0
        ts = fmt_ts;
279
0
    } else
280
0
        ts = "Not representable";
281
282
0
    fprintf(fh, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
283
0
    fprintf(fh, "<?xml-stylesheet type=\"text/xsl\" href=\"" PDML2HTML_XSL "\"?>\n");
284
0
    fprintf(fh, "<!-- You can find " PDML2HTML_XSL " in %s or at "PDML2HTML_URL PDML2HTML_XSL ". -->\n", doc_dir);
285
0
    fprintf(fh, "<pdml version=\"" PDML_VERSION "\" creator=\"%s/%s\" time=\"%s\" capture_file=\"", PACKAGE, VERSION, ts);
286
0
    if (filename) {
287
        /* \todo filename should be converted to UTF-8. */
288
0
        print_escaped_xml(fh, filename);
289
0
    }
290
0
    fprintf(fh, "\">\n");
291
0
}
292
293
/* Check if the str matches the protocolfilter.
294
 *
295
 * @param[in]  protocolfilter a map of field abbreviations that pass the filter
296
 * to the flags for that field, or NULL if no filter (so all fields pass)
297
 * @param[in]  str the field abbreviation to lookup in the map.
298
 * @param[out] flags if not NULL, gets set to the value in the map for
299
 * the given key if found (undefined if return is false.)
300
 * @return     true if the filter passes the string, false if the filter
301
 * filters out the string.
302
 */
303
static bool check_protocolfilter(wmem_map_t *protocolfilter, const char *str, pf_flags *flags)
304
0
{
305
0
    bool res = false;
306
0
    void *value;
307
308
0
    if (protocolfilter == NULL) {
309
0
        if (flags) {
310
0
            *flags = PF_NONE;
311
0
        }
312
0
        return true;
313
0
    }
314
315
0
    if (str == NULL) {
316
0
        return false;
317
0
    }
318
319
0
    res = wmem_map_lookup_extended(protocolfilter, str, NULL, &value);
320
0
    if (res && flags) {
321
0
        *flags = GPOINTER_TO_UINT(value);
322
0
    }
323
0
    return res;
324
0
}
325
326
void
327
write_pdml_proto_tree(output_fields_t* fields, epan_dissect_t *edt, column_info *cinfo, FILE *fh, bool use_color)
328
0
{
329
0
    write_pdml_data data;
330
0
    const color_filter_t *cfp;
331
332
0
    ws_assert(edt);
333
0
    ws_assert(fh);
334
335
0
    cfp = edt->pi.fd->color_filter;
336
337
    /* Create the output */
338
0
    if (use_color && (cfp != NULL)) {
339
0
        fprintf(fh, "<packet foreground='#%06x' background='#%06x'>\n",
340
0
            color_t_to_rgb(&cfp->fg_color),
341
0
            color_t_to_rgb(&cfp->bg_color));
342
0
    } else {
343
0
        fprintf(fh, "<packet>\n");
344
0
    }
345
346
    /* Print a "geninfo" protocol as required by PDML */
347
0
    print_pdml_geninfo(edt, fh);
348
349
0
    if (fields == NULL || fields->fields == NULL) {
350
        /* Write out all fields */
351
0
        data.level    = 0;
352
0
        data.fh       = fh;
353
0
        data.src_list = edt->pi.data_src;
354
0
        data.filter   = fields ? fields->protocolfilter : NULL;
355
356
0
        proto_tree_children_foreach(edt->tree, proto_tree_write_node_pdml,
357
0
                                    &data);
358
0
    } else {
359
        /* Write out specified fields */
360
0
        write_specified_fields(FORMAT_XML, fields, edt, cinfo, fh, NULL);
361
0
    }
362
363
0
    fprintf(fh, "</packet>\n\n");
364
0
}
365
366
void
367
write_ek_proto_tree(output_fields_t* fields,
368
                    bool print_summary, bool print_hex,
369
                    epan_dissect_t *edt,
370
                    column_info *cinfo,
371
                    FILE *fh)
372
0
{
373
0
    ws_assert(edt);
374
0
    ws_assert(fh);
375
376
0
    write_json_data data;
377
378
0
    json_dumper dumper = {
379
0
        .output_file = fh,
380
0
        .flags = JSON_DUMPER_DOT_TO_UNDERSCORE
381
0
    };
382
383
0
    data.dumper = &dumper;
384
385
0
    json_dumper_begin_object(&dumper);
386
0
    json_dumper_set_member_name_const(&dumper, "index");
387
0
    json_dumper_begin_object(&dumper);
388
0
    write_json_index(&dumper, edt);
389
0
    json_dumper_end_object(&dumper);
390
0
    json_dumper_end_object(&dumper);
391
0
    json_dumper_finish(&dumper);
392
0
    json_dumper_begin_object(&dumper);
393
394
    /* Timestamp added for time indexing in Elasticsearch */
395
0
    json_dumper_set_member_name_const(&dumper, "timestamp");
396
0
    json_dumper_value_anyf(&dumper, "\"%" PRIu64 "%03d\"", (uint64_t)edt->pi.abs_ts.secs, edt->pi.abs_ts.nsecs/1000000);
397
398
0
    if (print_summary)
399
0
        write_ek_summary(edt->pi.cinfo, &data);
400
401
0
    if (edt->tree) {
402
0
        json_dumper_set_member_name_const(&dumper, "layers");
403
0
        json_dumper_begin_object(&dumper);
404
405
0
        if (fields == NULL || fields->fields == NULL) {
406
            /* Write out all fields */
407
0
            data.src_list = edt->pi.data_src;
408
0
            data.filter = fields ? fields->protocolfilter : NULL;
409
0
            data.print_hex = print_hex;
410
0
            data.cached_src_tvb = edt->pi.data_src ?
411
0
                get_data_source_tvb((struct data_source *)edt->pi.data_src->data) : NULL;
412
0
            data.cached_src = edt->pi.data_src ?
413
0
                (struct data_source *)edt->pi.data_src->data : NULL;
414
0
            data.cached_src_idx = 0;
415
0
            proto_tree_write_node_ek(edt->tree, &data);
416
0
        } else {
417
            /* Write out specified fields */
418
0
            write_specified_fields(FORMAT_EK, fields, edt, cinfo, NULL, data.dumper);
419
0
        }
420
421
0
        json_dumper_end_object(&dumper);
422
0
    }
423
0
    json_dumper_end_object(&dumper);
424
0
    json_dumper_finish(&dumper);
425
0
}
426
427
void
428
write_fields_proto_tree(output_fields_t* fields, epan_dissect_t *edt, column_info *cinfo, FILE *fh)
429
0
{
430
0
    ws_assert(edt);
431
0
    ws_assert(fh);
432
433
    /* Create the output */
434
0
    write_specified_fields(FORMAT_CSV, fields, edt, cinfo, fh, NULL);
435
0
}
436
437
/* Indent to the correct level */
438
static void print_indent(int level, FILE *fh)
439
0
{
440
    /* Use a buffer pre-filled with spaces */
441
0
#define MAX_INDENT 2048
442
0
    static char spaces[MAX_INDENT];
443
0
    static bool inited = false;
444
0
    if (!inited) {
445
0
        for (int n=0; n < MAX_INDENT; n++) {
446
0
            spaces[n] = ' ';
447
0
        }
448
0
        inited = true;
449
0
    }
450
451
0
    if (fh == NULL) {
452
0
        return;
453
0
    }
454
455
    /* Temp terminate at right length and write to fh. */
456
0
    spaces[MIN(level*2, MAX_INDENT-1)] ='\0';
457
0
    fputs(spaces, fh);
458
0
    spaces[MIN(level*2, MAX_INDENT-1)] =' ';
459
0
}
460
461
/* Write out a tree's data, and any child nodes, as PDML */
462
static void
463
proto_tree_write_node_pdml(proto_node *node, void *data)
464
0
{
465
0
    field_info      *fi    = PNODE_FINFO(node);
466
0
    write_pdml_data *pdata = (write_pdml_data*) data;
467
0
    const char      *label_ptr;
468
0
    char             label_str[ITEM_LABEL_LENGTH];
469
0
    char            *dfilter_string;
470
0
    bool             wrap_in_fake_protocol;
471
472
    /* dissection with an invisible proto tree? */
473
0
    ws_assert(fi);
474
475
    /* Will wrap up top-level field items inside a fake protocol wrapper to
476
       preserve the PDML schema */
477
0
    wrap_in_fake_protocol =
478
0
        (((fi->hfinfo->type != FT_PROTOCOL) ||
479
0
          (fi->hfinfo->id == proto_data)) &&
480
0
         (pdata->level == 0));
481
482
0
    print_indent(pdata->level + 1, pdata->fh);
483
484
0
    if (wrap_in_fake_protocol) {
485
        /* Open fake protocol wrapper */
486
0
        fputs("<proto name=\"fake-field-wrapper\">\n", pdata->fh);
487
0
        pdata->level++;
488
489
0
        print_indent(pdata->level + 1, pdata->fh);
490
0
    }
491
492
    /* Text label. It's printed as a field with no name. */
493
0
    if (fi->hfinfo->id == hf_text_only) {
494
        /* Get the text */
495
0
        if (fi->rep) {
496
0
            label_ptr = fi->rep->representation;
497
0
        } else {
498
0
            label_ptr = "";
499
0
        }
500
501
        /* Show empty name since it is a required field */
502
0
        fputs("<field name=\"", pdata->fh);
503
0
        fputs("\" show=\"", pdata->fh);
504
0
        print_escaped_xml(pdata->fh, label_ptr);
505
506
0
        fprintf(pdata->fh, "\" size=\"%d", fi->length);
507
0
        if (node->parent && node->parent->finfo && (fi->start < node->parent->finfo->start)) {
508
0
            fprintf(pdata->fh, "\" pos=\"%d", node->parent->finfo->start + fi->start);
509
0
        } else {
510
0
            fprintf(pdata->fh, "\" pos=\"%d", fi->start);
511
0
        }
512
513
0
        if (fi->length > 0) {
514
0
            fputs("\" value=\"", pdata->fh);
515
0
            pdml_write_field_hex_value(pdata, fi);
516
0
        }
517
518
0
        if (node->first_child != NULL) {
519
0
            fputs("\">\n", pdata->fh);
520
0
        } else {
521
0
            fputs("\"/>\n", pdata->fh);
522
0
        }
523
0
    }
524
525
    /* Uninterpreted data, i.e., the "Data" protocol, is
526
     * printed as a field instead of a protocol. */
527
0
    else if (fi->hfinfo->id == proto_data) {
528
        /* Write out field with data */
529
0
        fputs("<field name=\"data\" value=\"", pdata->fh);
530
0
        pdml_write_field_hex_value(pdata, fi);
531
0
        fputs("\">\n", pdata->fh);
532
0
    } else {
533
        /* Normal protocols and fields */
534
0
        if ((fi->hfinfo->type == FT_PROTOCOL) && (fi->hfinfo->id != proto_expert)) {
535
0
            fputs("<proto name=\"", pdata->fh);
536
0
        } else {
537
0
            fputs("<field name=\"", pdata->fh);
538
0
        }
539
0
        print_escaped_xml(pdata->fh, fi->hfinfo->abbrev);
540
541
#if 0
542
        /* PDML spec, see:
543
         * https://wayback.archive.org/web/20150330045501/http://www.nbee.org/doku.php?id=netpdl:pdml_specification
544
         *
545
         * the show fields contains things in 'human readable' format
546
         * showname: contains only the name of the field
547
         * show: contains only the data of the field
548
         * showdtl: contains additional details of the field data
549
         * showmap: contains mappings of the field data (e.g. the hostname to an IP address)
550
         *
551
         * XXX - the showname shouldn't contain the field data itself
552
         * (like it's contained in the fi->rep->representation).
553
         * Unfortunately, we don't have the field data representation for
554
         * all fields, so this isn't currently possible */
555
        fputs("\" showname=\"", pdata->fh);
556
        print_escaped_xml(pdata->fh, fi->hfinfo->name);
557
#endif
558
559
0
        if (fi->rep) {
560
0
            fputs("\" showname=\"", pdata->fh);
561
0
            print_escaped_xml(pdata->fh, fi->rep->representation);
562
0
        } else {
563
0
            label_ptr = label_str;
564
0
            proto_item_fill_label(fi, label_str, NULL);
565
0
            fputs("\" showname=\"", pdata->fh);
566
0
            print_escaped_xml(pdata->fh, label_ptr);
567
0
        }
568
569
0
        if (proto_item_is_hidden(node) && (prefs.display_hidden_proto_items == false))
570
0
            fprintf(pdata->fh, "\" hide=\"yes");
571
572
0
        fprintf(pdata->fh, "\" size=\"%d", fi->length);
573
0
        if (node->parent && node->parent->finfo && (fi->start < node->parent->finfo->start)) {
574
0
            fprintf(pdata->fh, "\" pos=\"%d", node->parent->finfo->start + fi->start);
575
0
        } else {
576
0
            fprintf(pdata->fh, "\" pos=\"%d", fi->start);
577
0
        }
578
/*      fprintf(pdata->fh, "\" id=\"%d", fi->hfinfo->id);*/
579
580
        /* show, value, and unmaskedvalue attributes */
581
0
        switch (fi->hfinfo->type)
582
0
        {
583
0
        case FT_PROTOCOL:
584
0
            break;
585
0
        case FT_NONE:
586
0
            fputs("\" show=\"\" value=\"",  pdata->fh);
587
0
            break;
588
0
        default:
589
0
            dfilter_string = fvalue_to_string_repr(NULL, fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
590
            /* XXX - doc/README.xml-output describes the show attribute as:
591
             * show - the representation of the packet data ('value') as it
592
             *        would appear in a display filter.
593
             *
594
             * which (along with the name of the variable) would argue for using
595
             * FTREPR_DFILTER. However, FTREPR_DFILTER adds quotes to some but
596
             * not all field types, so it could not be used. The treatment of
597
             * FT_ABSOLUTE_VALUE is particularly different between DISPLAY and
598
             * DFILTER, though.
599
             */
600
0
            if (dfilter_string != NULL) {
601
602
0
                fputs("\" show=\"", pdata->fh);
603
0
                print_escaped_xml(pdata->fh, dfilter_string);
604
0
            }
605
0
            wmem_free(NULL, dfilter_string);
606
607
            /*
608
             * XXX - should we omit "value" for any fields?
609
             * What should we do for fields whose length is 0?
610
             * They might come from a pseudo-header or from
611
             * the capture header (e.g., time stamps), or
612
             * they might be generated fields.
613
             */
614
0
            if (fi->length > 0) {
615
0
                fputs("\" value=\"", pdata->fh);
616
617
0
                if (fi->hfinfo->bitmask!=0) {
618
0
                    switch (fvalue_type_ftenum(fi->value)) {
619
0
                        case FT_INT8:
620
0
                        case FT_INT16:
621
0
                        case FT_INT24:
622
0
                        case FT_INT32:
623
0
                            fprintf(pdata->fh, "%X", (unsigned) fvalue_get_sinteger(fi->value));
624
0
                            break;
625
0
                        case FT_CHAR:
626
0
                        case FT_UINT8:
627
0
                        case FT_UINT16:
628
0
                        case FT_UINT24:
629
0
                        case FT_UINT32:
630
0
                            fprintf(pdata->fh, "%X", fvalue_get_uinteger(fi->value));
631
0
                            break;
632
0
                        case FT_INT40:
633
0
                        case FT_INT48:
634
0
                        case FT_INT56:
635
0
                        case FT_INT64:
636
0
                            fprintf(pdata->fh, "%" PRIX64, fvalue_get_sinteger64(fi->value));
637
0
                            break;
638
0
                        case FT_UINT40:
639
0
                        case FT_UINT48:
640
0
                        case FT_UINT56:
641
0
                        case FT_UINT64:
642
0
                        case FT_BOOLEAN:
643
0
                            fprintf(pdata->fh, "%" PRIX64, fvalue_get_uinteger64(fi->value));
644
0
                            break;
645
0
                        default:
646
0
                            ws_assert_not_reached();
647
0
                    }
648
0
                    fputs("\" unmaskedvalue=\"", pdata->fh);
649
0
                    pdml_write_field_hex_value(pdata, fi);
650
0
                } else {
651
0
                    pdml_write_field_hex_value(pdata, fi);
652
0
                }
653
0
            }
654
0
        }
655
656
0
        if (node->first_child != NULL) {
657
0
            fputs("\">\n", pdata->fh);
658
0
        } else if (fi->hfinfo->id == proto_data) {
659
0
            fputs("\">\n", pdata->fh);
660
0
        } else {
661
0
            fputs("\"/>\n", pdata->fh);
662
0
        }
663
0
    }
664
665
    /* We print some levels for PDML. Recurse here. */
666
0
    if (node->first_child != NULL) {
667
0
        pf_flags filter_flags = PF_NONE;
668
0
        if (pdata->filter == NULL || check_protocolfilter(pdata->filter, fi->hfinfo->abbrev, &filter_flags)) {
669
0
            wmem_map_t *_filter = NULL;
670
            /* Remove protocol filter for children, if children should be included */
671
0
            if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
672
0
                _filter = pdata->filter;
673
0
                pdata->filter = NULL;
674
0
            }
675
676
0
            pdata->level++;
677
0
            proto_tree_children_foreach(node,
678
0
                                        proto_tree_write_node_pdml, pdata);
679
0
            pdata->level--;
680
681
            /* Put protocol filter back */
682
0
            if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
683
0
                pdata->filter = _filter;
684
0
            }
685
0
        } else {
686
0
            print_indent(pdata->level + 2, pdata->fh);
687
688
            /* print dummy field */
689
0
            fputs("<field name=\"filtered\" value=\"", pdata->fh);
690
0
            print_escaped_xml(pdata->fh, fi->hfinfo->abbrev);
691
0
            fputs("\" />\n", pdata->fh);
692
0
        }
693
0
    }
694
695
    /* Take back the extra level we added for fake wrapper protocol */
696
0
    if (wrap_in_fake_protocol) {
697
0
        pdata->level--;
698
0
    }
699
700
0
    if (node->first_child != NULL) {
701
0
        print_indent(pdata->level + 1, pdata->fh);
702
703
        /* Close off current element */
704
        /* Data and expert "protocols" use simple tags */
705
0
        if ((fi->hfinfo->id != proto_data) && (fi->hfinfo->id != proto_expert)) {
706
0
            if (fi->hfinfo->type == FT_PROTOCOL) {
707
0
                fputs("</proto>\n", pdata->fh);
708
0
            } else {
709
0
                fputs("</field>\n", pdata->fh);
710
0
            }
711
0
        } else {
712
0
            fputs("</field>\n", pdata->fh);
713
0
        }
714
0
    }
715
716
    /* Close off fake wrapper protocol */
717
0
    if (wrap_in_fake_protocol) {
718
0
        print_indent(pdata->level + 1, pdata->fh);
719
0
        fputs("</proto>\n", pdata->fh);
720
0
    }
721
0
}
722
723
json_dumper
724
write_json_preamble(FILE *fh, bool compact)
725
0
{
726
0
    json_dumper dumper = {
727
0
        .output_file = fh,
728
0
        .flags = compact ? 0 : JSON_DUMPER_FLAGS_PRETTY_PRINT
729
0
    };
730
0
    json_dumper_begin_array(&dumper);
731
0
    return dumper;
732
0
}
733
734
void
735
write_json_finale(json_dumper *dumper)
736
0
{
737
0
    json_dumper_end_array(dumper);
738
0
    json_dumper_finish(dumper);
739
0
}
740
741
static void
742
write_json_index(json_dumper *dumper, epan_dissect_t *edt)
743
0
{
744
0
    char ts[30];
745
0
    struct tm * timeinfo;
746
0
    char* str;
747
748
0
    timeinfo = localtime(&edt->pi.abs_ts.secs);
749
0
    if (timeinfo != NULL) {
750
0
        strftime(ts, sizeof(ts), "%Y-%m-%d", timeinfo);
751
0
    } else {
752
0
        (void) g_strlcpy(ts, "XXXX-XX-XX", sizeof(ts)); /* XXX - better way of saying "Not representable"? */
753
0
    }
754
0
    json_dumper_set_member_name_const(dumper, "_index");
755
0
    str = ws_strdup_printf("packets-%s", ts);
756
0
    json_dumper_value_string_noesc(dumper, str, strlen(str));
757
0
    g_free(str);
758
0
}
759
760
void
761
write_json_proto_tree(output_fields_t* fields,
762
                      print_dissections_e print_dissections,
763
                      bool print_hex,
764
                      epan_dissect_t *edt, column_info *cinfo,
765
                      proto_node_children_grouper_func node_children_grouper,
766
                      json_dumper *dumper)
767
0
{
768
0
    write_json_data data;
769
770
0
    data.dumper = dumper;
771
772
0
    json_dumper_begin_object(dumper);
773
0
    write_json_index(dumper, edt);
774
0
    json_dumper_set_member_name_const(dumper, "_score");
775
0
    json_dumper_value_string(dumper, NULL);
776
0
    json_dumper_set_member_name_const(dumper, "_source");
777
0
    json_dumper_begin_object(dumper);
778
0
    json_dumper_set_member_name_const(dumper, "layers");
779
780
0
    if (fields == NULL || fields->fields == NULL) {
781
        /* Write out all fields */
782
0
        data.src_list = edt->pi.data_src;
783
0
        data.filter = fields ? fields->protocolfilter : NULL;
784
0
        data.print_hex = print_hex;
785
0
        data.print_text = true;
786
0
        if (print_dissections == print_dissections_none) {
787
0
            data.print_text = false;
788
0
        }
789
0
        data.node_children_grouper = node_children_grouper;
790
0
        data.cached_src_tvb = edt->pi.data_src ?
791
0
            get_data_source_tvb((struct data_source *)edt->pi.data_src->data) : NULL;
792
0
        data.cached_src = edt->pi.data_src ?
793
0
            (struct data_source *)edt->pi.data_src->data : NULL;
794
0
        data.cached_src_idx = 0;
795
0
        data.grouping_table = g_hash_table_new(g_direct_hash, g_direct_equal);
796
797
0
        write_json_proto_node_children(edt->tree, &data);
798
0
        g_hash_table_destroy(data.grouping_table);
799
0
    } else {
800
0
        write_specified_fields(FORMAT_JSON, fields, edt, cinfo, NULL, dumper);
801
0
    }
802
803
0
    json_dumper_end_object(dumper);
804
0
    json_dumper_end_object(dumper);
805
0
}
806
807
/**
808
 * Returns a boolean telling us whether that node list contains any node which has children
809
 */
810
static bool
811
any_has_children(GSList *node_values_list)
812
0
{
813
0
    GSList *current_node = node_values_list;
814
0
    while (current_node != NULL) {
815
0
        proto_node *current_value = (proto_node *) current_node->data;
816
0
        if (current_value->first_child != NULL) {
817
0
            return true;
818
0
        }
819
0
        current_node = current_node->next;
820
0
    }
821
0
    return false;
822
0
}
823
824
/**
825
 * Write a json object containing a list of key:value pairs where each key:value pair corresponds to a different json
826
 * key and its associated nodes in the proto_tree.
827
 * @param proto_node_list_head A 2-dimensional list containing a list of values for each different node json key. The
828
 * elements themselves are a linked list of values associated with the same json key.
829
 * @param pdata json writing metadata
830
 */
831
static void
832
write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *pdata)
833
0
{
834
0
    GSList *current_node = proto_node_list_head;
835
836
0
    json_dumper_begin_object(pdata->dumper);
837
838
    // Loop over each list of nodes (differentiated by json key) and write the associated json key:value pair in the
839
    // output.
840
0
    while (current_node != NULL) {
841
        // Get the list of values for the current json key.
842
0
        GSList *node_values_list = (GSList *) current_node->data;
843
844
        // Retrieve the json key from the first value.
845
0
        proto_node *first_value = (proto_node *) node_values_list->data;
846
0
        const char *json_key = proto_node_to_json_key(first_value);
847
        // Check if the current json key is filtered from the output with the "-j" cli option.
848
0
        pf_flags filter_flags = PF_NONE;
849
0
        bool is_filtered = pdata->filter != NULL && !check_protocolfilter(pdata->filter, json_key, &filter_flags);
850
851
0
        field_info *fi = first_value->finfo;
852
0
        char *value_string_repr = fvalue_to_string_repr(NULL, fi->value, FTREPR_JSON, fi->hfinfo->display);
853
0
        bool has_children = any_has_children(node_values_list);
854
855
        // We assume all values of a json key have roughly the same layout. Thus we can use the first value to derive
856
        // attributes of all the values.
857
0
        bool has_value = value_string_repr != NULL;
858
0
        bool is_pseudo_text_field = fi->hfinfo->id == hf_text_only;
859
860
0
        wmem_free(NULL, value_string_repr); // fvalue_to_string_repr returns allocated buffer
861
862
        // "-x" command line option. A "_raw" suffix is added to the json key so the textual value can be printed
863
        // with the original json key. If both hex and text writing are enabled the raw information of fields whose
864
        // length is equal to 0 is not written to the output. If the field is a special text pseudo field no raw
865
        // information is written either.
866
0
        if (pdata->print_hex && (!pdata->print_text || fi->length > 0) && !is_pseudo_text_field) {
867
0
            write_json_proto_node(node_values_list, "_raw", write_json_proto_node_hex_dump, pdata);
868
0
        }
869
870
0
        if (pdata->print_text && has_value) {
871
0
            write_json_proto_node(node_values_list, "", write_json_proto_node_value, pdata);
872
0
        }
873
874
0
        if (has_children) {
875
            // If a node has both a value and a set of children we print the value and the children in separate
876
            // key:value pairs. These can't have the same key so whenever a value is already printed with the node
877
            // json key we print the children with the same key with a "_tree" suffix added.
878
0
            char *suffix = has_value ? "_tree": "";
879
880
0
            if (is_filtered) {
881
0
                write_json_proto_node(node_values_list, suffix, write_json_proto_node_filtered, pdata);
882
0
            } else {
883
                // Remove protocol filter for children, if children should be included. This functionality is enabled
884
                // with the "-J" command line option. We save the filter so it can be reenabled when we are done with
885
                // the current key:value pair.
886
0
                wmem_map_t *_filter = NULL;
887
0
                if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
888
0
                    _filter = pdata->filter;
889
0
                    pdata->filter = NULL;
890
0
                }
891
892
                // has_children is true if any of the nodes have children. So we're not 100% sure whether this
893
                // particular node has children or not => use the 'dynamic' version of 'write_json_proto_node'
894
0
                write_json_proto_node(node_values_list, suffix, write_json_proto_node_dynamic, pdata);
895
896
                // Put protocol filter back
897
0
                if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
898
0
                    pdata->filter = _filter;
899
0
                }
900
0
            }
901
0
        }
902
903
0
        if (!has_value && !has_children && (pdata->print_text || (pdata->print_hex && is_pseudo_text_field))) {
904
0
            write_json_proto_node(node_values_list, "", write_json_proto_node_no_value, pdata);
905
0
        }
906
907
0
        current_node = current_node->next;
908
0
    }
909
0
    json_dumper_end_object(pdata->dumper);
910
0
}
911
912
/**
913
 * Writes a single node as a key:value pair. The value_writer param can be used to specify how the node's value should
914
 * be written.
915
 * @param node_values_head Linked list containing all nodes associated with the same json key in this object.
916
 * @param suffix Suffix that should be added to the json key.
917
 * @param value_writer A function which writes the actual values of the node json key.
918
 * @param pdata json writing metadata
919
 */
920
static void
921
write_json_proto_node(GSList *node_values_head,
922
                      const char *suffix,
923
                      proto_node_value_writer value_writer,
924
                      write_json_data *pdata)
925
0
{
926
    // Retrieve json key from first value.
927
0
    proto_node *first_value = (proto_node *) node_values_head->data;
928
0
    const char *json_key = proto_node_to_json_key(first_value);
929
930
0
    if (suffix[0] == '\0') {
931
0
        json_dumper_set_member_name(pdata->dumper, json_key);
932
0
    } else {
933
0
        size_t klen = strlen(json_key);
934
0
        size_t slen = strlen(suffix);
935
0
        char sbuf[256];
936
0
        char *key = (klen + slen + 1 <= sizeof(sbuf)) ? sbuf : (char *)g_malloc(klen + slen + 1);
937
0
        memcpy(key, json_key, klen);
938
0
        memcpy(key + klen, suffix, slen + 1);
939
0
        json_dumper_set_member_name(pdata->dumper, key);
940
0
        if (key != sbuf) g_free(key);
941
0
    }
942
0
    write_json_proto_node_value_list(node_values_head, value_writer, pdata);
943
0
}
944
945
/**
946
 * Writes a list of values of a single json key. If multiple values are passed they are wrapped in a json array.
947
 * @param node_values_head Linked list containing all values that should be written.
948
 * @param value_writer Function which writes the separate values.
949
 * @param pdata json writing metadata
950
 */
951
static void
952
write_json_proto_node_value_list(GSList *node_values_head, proto_node_value_writer value_writer, write_json_data *pdata)
953
0
{
954
0
    GSList *current_value = node_values_head;
955
956
    // Write directly if only a single value is passed. Wrap in json array otherwise.
957
0
    if (current_value->next == NULL) {
958
0
        value_writer((proto_node *) current_value->data, pdata);
959
0
    } else {
960
0
        json_dumper_begin_array(pdata->dumper);
961
962
0
        while (current_value != NULL) {
963
0
            value_writer((proto_node *) current_value->data, pdata);
964
0
            current_value = current_value->next;
965
0
        }
966
0
        json_dumper_end_array(pdata->dumper);
967
0
    }
968
0
}
969
970
/**
971
 * Writes the value for a node that's filtered from the output.
972
 */
973
static void
974
write_json_proto_node_filtered(proto_node *node, write_json_data *pdata)
975
0
{
976
0
    const char *json_key = proto_node_to_json_key(node);
977
978
0
    json_dumper_begin_object(pdata->dumper);
979
0
    json_dumper_set_member_name_const(pdata->dumper, "filtered");
980
0
    json_dumper_value_string(pdata->dumper, json_key);
981
0
    json_dumper_end_object(pdata->dumper);
982
0
}
983
984
/**
985
 * Writes the hex dump of a node. A json array is written containing the hex dump, position, length, bitmask and type of
986
 * the node.
987
 */
988
static void
989
write_json_proto_node_hex_dump(proto_node *node, write_json_data *pdata)
990
0
{
991
0
    field_info *fi = node->finfo;
992
0
    uint32_t src_idx;
993
994
0
    json_dumper_begin_array(pdata->dumper);
995
996
0
    json_write_field_hex_value(pdata, fi);
997
998
    /* Dump raw hex-encoded dissected information including position, length,
999
     * bitmask, type, and data source index. */
1000
    /* These were added for use by json2pcap, but might be useful for others. */
1001
0
    json_dumper_value_int(pdata->dumper, fi->start);
1002
0
    json_dumper_value_int(pdata->dumper, fi->length);
1003
0
    json_dumper_value_uint(pdata->dumper, fi->hfinfo->bitmask);
1004
0
    json_dumper_value_int(pdata->dumper, (int32_t)fvalue_type_ftenum(fi->value));
1005
1006
0
    if (get_field_data_source_cached(pdata->src_list, fi, &src_idx, pdata)) {
1007
0
        json_dumper_value_uint(pdata->dumper, src_idx);
1008
0
    } else {
1009
0
        json_dumper_value_literal(pdata->dumper, "null", 4);
1010
0
    }
1011
1012
0
    json_dumper_end_array(pdata->dumper);
1013
0
}
1014
1015
/**
1016
 * Writes the value of a node, which may be a simple node with no value and no children,
1017
 * or a node with children -- this will be determined dynamically
1018
 */
1019
static void
1020
write_json_proto_node_dynamic(proto_node *node, write_json_data *data) // NOLINT(misc-no-recursion)
1021
0
{
1022
0
    if (node->first_child == NULL) {
1023
0
        write_json_proto_node_no_value(node, data);
1024
0
    } else {
1025
0
        write_json_proto_node_children(node, data);
1026
0
    }
1027
0
}
1028
1029
/**
1030
 * Writes the children of a node. Calls write_json_proto_node_list internally which recursively writes children of nodes
1031
 * to the output.
1032
 */
1033
/* Write a single child node directly (no-duplicate fast path, no GSList allocation) */
1034
static void
1035
write_json_proto_node_single(proto_node *node, write_json_data *pdata) // NOLINT(misc-no-recursion)
1036
0
{
1037
0
    const char *json_key = proto_node_to_json_key(node);
1038
0
    field_info *fi = node->finfo;
1039
0
    char *value_string_repr = fvalue_to_string_repr(NULL, fi->value, FTREPR_JSON, fi->hfinfo->display);
1040
0
    bool has_children = node->first_child != NULL;
1041
0
    bool has_value = value_string_repr != NULL;
1042
0
    bool is_pseudo_text_field = fi->hfinfo->id == hf_text_only;
1043
0
    size_t klen = strlen(json_key);
1044
1045
0
    pf_flags filter_flags = PF_NONE;
1046
0
    bool is_filtered = pdata->filter != NULL && !check_protocolfilter(pdata->filter, json_key, &filter_flags);
1047
1048
0
    wmem_free(NULL, value_string_repr);
1049
1050
0
    if (pdata->print_hex && (!pdata->print_text || fi->length > 0) && !is_pseudo_text_field) {
1051
0
        char sbuf[256];
1052
0
        char *key_raw = (klen + 5 <= sizeof(sbuf)) ? sbuf : (char *)g_malloc(klen + 5);
1053
0
        memcpy(key_raw, json_key, klen);
1054
0
        memcpy(key_raw + klen, "_raw", 5);
1055
0
        json_dumper_set_member_name_noesc(pdata->dumper, key_raw, klen + 4);
1056
0
        if (key_raw != sbuf) g_free(key_raw);
1057
0
        write_json_proto_node_hex_dump(node, pdata);
1058
0
    }
1059
1060
0
    if (pdata->print_text && has_value) {
1061
0
        if (!is_pseudo_text_field)
1062
0
            json_dumper_set_member_name_noesc(pdata->dumper, json_key, klen);
1063
0
        else
1064
0
            json_dumper_set_member_name(pdata->dumper, json_key);
1065
0
        write_json_proto_node_value(node, pdata);
1066
0
    }
1067
1068
0
    if (has_children) {
1069
0
        if (has_value) {
1070
0
            char sbuf[256];
1071
0
            char *key_tree = (klen + 6 <= sizeof(sbuf)) ? sbuf : (char *)g_malloc(klen + 6);
1072
0
            memcpy(key_tree, json_key, klen);
1073
0
            memcpy(key_tree + klen, "_tree", 6);
1074
0
            if (!is_pseudo_text_field)
1075
0
                json_dumper_set_member_name_noesc(pdata->dumper, key_tree, klen + 5);
1076
0
            else
1077
0
                json_dumper_set_member_name(pdata->dumper, key_tree);
1078
0
            if (key_tree != sbuf) g_free(key_tree);
1079
0
        } else {
1080
0
            if (!is_pseudo_text_field)
1081
0
                json_dumper_set_member_name_noesc(pdata->dumper, json_key, klen);
1082
0
            else
1083
0
                json_dumper_set_member_name(pdata->dumper, json_key);
1084
0
        }
1085
1086
0
        if (is_filtered) {
1087
0
            write_json_proto_node_filtered(node, pdata);
1088
0
        } else {
1089
0
            wmem_map_t *_filter = NULL;
1090
0
            if ((filter_flags & PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1091
0
                _filter = pdata->filter;
1092
0
                pdata->filter = NULL;
1093
0
            }
1094
0
            write_json_proto_node_dynamic(node, pdata);
1095
0
            if ((filter_flags & PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1096
0
                pdata->filter = _filter;
1097
0
            }
1098
0
        }
1099
0
    }
1100
1101
0
    if (!has_value && !has_children && (pdata->print_text || (pdata->print_hex && is_pseudo_text_field))) {
1102
0
        if (!is_pseudo_text_field)
1103
0
            json_dumper_set_member_name_noesc(pdata->dumper, json_key, klen);
1104
0
        else
1105
0
            json_dumper_set_member_name(pdata->dumper, json_key);
1106
0
        write_json_proto_node_no_value(node, pdata);
1107
0
    }
1108
0
}
1109
1110
static void
1111
write_json_proto_node_children(proto_node *node, write_json_data *data) // NOLINT(misc-no-recursion)
1112
0
{
1113
0
    if (data->grouping_table && data->node_children_grouper == proto_node_group_children_by_json_key) {
1114
        /* Fast path: check if all keys are unique (common case) */
1115
0
        GHashTable *table = data->grouping_table;
1116
0
        g_hash_table_remove_all(table);
1117
0
        proto_node *current_child = node->first_child;
1118
0
        bool has_duplicates = false;
1119
1120
0
        while (current_child != NULL) {
1121
0
            char *json_key = (char *) proto_node_to_json_key(current_child);
1122
0
            if (g_hash_table_contains(table, json_key)) {
1123
0
                has_duplicates = true;
1124
0
                break;
1125
0
            }
1126
0
            g_hash_table_insert(table, json_key, json_key);
1127
0
            current_child = current_child->next;
1128
0
        }
1129
1130
0
        if (!has_duplicates) {
1131
            /* No duplicates: write children directly without GSList allocation */
1132
0
            json_dumper_begin_object(data->dumper);
1133
0
            current_child = node->first_child;
1134
0
            while (current_child != NULL) {
1135
0
                write_json_proto_node_single(current_child, data);
1136
0
                current_child = current_child->next;
1137
0
            }
1138
0
            json_dumper_end_object(data->dumper);
1139
0
            return;
1140
0
        }
1141
0
    }
1142
1143
0
    GSList *grouped_children_list = data->node_children_grouper(node);
1144
0
    write_json_proto_node_list(grouped_children_list, data);
1145
0
    g_slist_free_full(grouped_children_list, (GDestroyNotify) g_slist_free);
1146
0
}
1147
1148
/**
1149
 * Writes the value of a node to the output.
1150
 */
1151
static void
1152
write_json_proto_node_value(proto_node *node, write_json_data *pdata)
1153
0
{
1154
0
    field_info *fi = node->finfo;
1155
    // Get the actual value of the node as a string.
1156
0
    char *value_string_repr = fvalue_to_string_repr(NULL, fi->value, FTREPR_JSON, fi->hfinfo->display);
1157
1158
    //TODO: Have FTREPR_JSON include quotes where appropriate and use json_dumper_value_anyf() here,
1159
    // so we can output booleans and numbers and not only strings.
1160
0
    json_dumper_value_string(pdata->dumper, value_string_repr);
1161
1162
0
    wmem_free(NULL, value_string_repr);
1163
0
}
1164
1165
/**
1166
 * Write the value for a node that has no value and no children. This is the empty string for all nodes except those of
1167
 * type FT_PROTOCOL for which the full name is written instead.
1168
 */
1169
static void
1170
write_json_proto_node_no_value(proto_node *node, write_json_data *pdata)
1171
0
{
1172
0
    field_info *fi = node->finfo;
1173
1174
0
    if (fi->hfinfo->type == FT_PROTOCOL) {
1175
0
        if (fi->rep) {
1176
0
            json_dumper_value_string(pdata->dumper, fi->rep->representation);
1177
0
        } else {
1178
0
            char label_str[ITEM_LABEL_LENGTH];
1179
0
            proto_item_fill_label(fi, label_str, NULL);
1180
0
            json_dumper_value_string(pdata->dumper, label_str);
1181
0
        }
1182
0
    } else {
1183
0
        json_dumper_value_string_noesc(pdata->dumper, "", 0);
1184
0
    }
1185
0
}
1186
1187
/**
1188
 * Groups each child of the node separately.
1189
 * @return Linked list where each element is another linked list containing a single node.
1190
 */
1191
GSList *
1192
0
proto_node_group_children_by_unique(proto_node *node) {
1193
0
    GSList *unique_nodes_list = NULL;
1194
0
    proto_node *current_child = node->first_child;
1195
1196
0
    while (current_child != NULL) {
1197
0
        GSList *unique_node = g_slist_prepend(NULL, current_child);
1198
0
        unique_nodes_list = g_slist_prepend(unique_nodes_list, unique_node);
1199
0
        current_child = current_child->next;
1200
0
    }
1201
1202
0
    return g_slist_reverse(unique_nodes_list);
1203
0
}
1204
1205
/**
1206
 * Groups the children of a node by their json key. Children are put in the same group if they have the same json key.
1207
 * @return Linked list where each element is another linked list of nodes associated with the same json key.
1208
 */
1209
GSList *
1210
proto_node_group_children_by_json_key(proto_node *node)
1211
0
{
1212
    /**
1213
     * For each different json key we store a linked list of values corresponding to that json key. These lists are kept
1214
     * in both a linked list and a hashmap. The hashmap is used to quickly retrieve the values of a json key. The linked
1215
     * list is used to preserve the ordering of keys as they are encountered which is not guaranteed when only using a
1216
     * hashmap.
1217
     */
1218
0
    GSList *same_key_nodes_list = NULL;
1219
0
    GHashTable *lookup_by_json_key = g_hash_table_new(g_direct_hash, g_direct_equal);
1220
0
    proto_node *current_child = node->first_child;
1221
1222
    /**
1223
     * For each child of the node get the key and get the list of values already associated with that key from the
1224
     * hashmap. If no list exist yet for that key create a new one and add it to both the linked list and hashmap. If a
1225
     * list already exists add the node to that list.
1226
     */
1227
0
    while (current_child != NULL) {
1228
0
        char *json_key = (char *) proto_node_to_json_key(current_child);
1229
0
        GSList *json_key_nodes = (GSList *) g_hash_table_lookup(lookup_by_json_key, json_key);
1230
1231
0
        if (json_key_nodes == NULL) {
1232
0
            json_key_nodes = g_slist_append(json_key_nodes, current_child);
1233
            // Prepending in single linked list is O(1), appending is O(n). Better to prepend here and reverse at the
1234
            // end than potentially looping to the end of the linked list for each child.
1235
0
            same_key_nodes_list = g_slist_prepend(same_key_nodes_list, json_key_nodes);
1236
0
            g_hash_table_insert(lookup_by_json_key, json_key, json_key_nodes);
1237
0
        } else {
1238
            // Store and insert value again to circumvent unused_variable warning.
1239
            // Append in this case since most value lists will only have a single value.
1240
0
            json_key_nodes = g_slist_append(json_key_nodes, current_child);
1241
0
            g_hash_table_insert(lookup_by_json_key, json_key, json_key_nodes);
1242
0
        }
1243
1244
0
        current_child = current_child->next;
1245
0
    }
1246
1247
    // Hash table is not needed anymore since the linked list with the correct ordering is returned.
1248
0
    g_hash_table_destroy(lookup_by_json_key);
1249
1250
0
    return g_slist_reverse(same_key_nodes_list);
1251
0
}
1252
1253
/**
1254
 * Returns the json key of a node. Tries to use the node's abbreviated name.
1255
 * If the abbreviated name is not available the representation is used instead.
1256
 *
1257
 * XXX: The representation can have spaces or differ depending on the content,
1258
 * which makes it difficult to match text-only fields with a -j/-J filter in tshark.
1259
 * (Issue #17125).
1260
 */
1261
static const char *
1262
proto_node_to_json_key(proto_node *node)
1263
0
{
1264
0
    const char *json_key;
1265
    // Check if node has abbreviated name.
1266
0
    if (node->finfo->hfinfo->id != hf_text_only) {
1267
0
        json_key = node->finfo->hfinfo->abbrev;
1268
0
    } else if (node->finfo->rep != NULL) {
1269
0
        json_key = node->finfo->rep->representation;
1270
0
    } else {
1271
0
        json_key = "";
1272
0
    }
1273
1274
0
    return json_key;
1275
0
}
1276
1277
static bool
1278
ek_check_protocolfilter(wmem_map_t *protocolfilter, const char *str, pf_flags *filter_flags)
1279
0
{
1280
0
    char *str_escaped = NULL;
1281
0
    bool check;
1282
0
    int i;
1283
1284
0
    if (check_protocolfilter(protocolfilter, str, filter_flags))
1285
0
        return true;
1286
1287
    /* to to thread the '.' and '_' equally. The '.' is replace by print_escaped_ek for '_' */
1288
0
    if (str != NULL && strlen(str) > 0) {
1289
0
        str_escaped = g_strdup(str);
1290
1291
0
        i = 0;
1292
0
        while (str_escaped[i] != '\0') {
1293
0
            if (str_escaped[i] == '.') {
1294
0
                str_escaped[i] = '_';
1295
0
            }
1296
0
            i++;
1297
0
        }
1298
0
    }
1299
1300
0
    check = check_protocolfilter(protocolfilter, str_escaped, filter_flags);
1301
0
    g_free(str_escaped);
1302
0
    return check;
1303
0
}
1304
1305
/**
1306
 * Finds a node's descendants to be printed as EK/JSON attributes.
1307
 */
1308
static void
1309
write_ek_summary(column_info *cinfo, write_json_data* pdata)
1310
0
{
1311
0
    unsigned i;
1312
1313
0
    for (i = 0; i < cinfo->num_cols; i++) {
1314
0
        if (!get_column_visible(i))
1315
0
            continue;
1316
0
        json_dumper_set_member_name(pdata->dumper, g_ascii_strdown(cinfo->columns[i].col_title, -1));
1317
0
        json_dumper_value_string(pdata->dumper, get_column_text(cinfo, i));
1318
0
    }
1319
0
}
1320
1321
/* Write out a tree's data, and any child nodes, as JSON for EK */
1322
static void
1323
// NOLINTNEXTLINE(misc-no-recursion)
1324
ek_fill_attr(proto_node *node, GHashTable *attr_table, write_json_data *pdata)
1325
0
{
1326
0
    field_info *fi         = NULL;
1327
0
    GSList *attr_instances = NULL;
1328
1329
0
    proto_node *current_node = node->first_child;
1330
0
    while (current_node != NULL) {
1331
0
        fi        = PNODE_FINFO(current_node);
1332
1333
        /* dissection with an invisible proto tree? */
1334
0
        ws_assert(fi);
1335
1336
0
        attr_instances = (GSList *) g_hash_table_lookup(attr_table, fi->hfinfo->abbrev);
1337
0
        attr_instances = g_slist_append(attr_instances, current_node);
1338
        // Update instance list for this attr in hash table
1339
0
        g_hash_table_insert(attr_table, g_strdup(fi->hfinfo->abbrev), attr_instances);
1340
1341
        /* Field, recurse through children*/
1342
0
        if (fi->hfinfo->type != FT_PROTOCOL && current_node->first_child != NULL) {
1343
0
            if (pdata->filter != NULL) {
1344
0
                pf_flags filter_flags = PF_NONE;
1345
0
                if (ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev, &filter_flags)) {
1346
0
                    wmem_map_t *_filter = NULL;
1347
                    /* Remove protocol filter for children, if children should be included */
1348
0
                    if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1349
0
                        _filter = pdata->filter;
1350
0
                        pdata->filter = NULL;
1351
0
                    }
1352
1353
                    // We recurse here, but we're limited by our tree depth checks in proto.c
1354
0
                    ek_fill_attr(current_node, attr_table, pdata);
1355
1356
                    /* Put protocol filter back */
1357
0
                    if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1358
0
                        pdata->filter = _filter;
1359
0
                    }
1360
0
                } else {
1361
                    // Don't traverse children if filtered out
1362
0
                }
1363
0
            } else {
1364
                // We recurse here, but we're limited by our tree depth checks in proto.c
1365
0
                ek_fill_attr(current_node, attr_table, pdata);
1366
0
            }
1367
0
        } else {
1368
            // Will descend into object at another point
1369
0
        }
1370
1371
0
        current_node = current_node->next;
1372
0
    }
1373
0
}
1374
1375
static void
1376
ek_write_name(proto_node *pnode, char* suffix, write_json_data* pdata)
1377
0
{
1378
0
    field_info *fi = PNODE_FINFO(pnode);
1379
0
    char       *str;
1380
1381
0
    if (fi->hfinfo->parent != -1) {
1382
0
        header_field_info* parent = proto_registrar_get_nth(fi->hfinfo->parent);
1383
0
        str = ws_strdup_printf("%s_%s%s", parent->abbrev, fi->hfinfo->abbrev, suffix ? suffix : "");
1384
0
        json_dumper_set_member_name(pdata->dumper, str);
1385
0
    } else {
1386
0
        str = ws_strdup_printf("%s%s", fi->hfinfo->abbrev, suffix ? suffix : "");
1387
0
        json_dumper_set_member_name(pdata->dumper, str);
1388
0
    }
1389
0
    g_free(str);
1390
0
}
1391
1392
static void
1393
ek_write_hex(field_info *fi, write_json_data *pdata)
1394
0
{
1395
0
    json_write_field_hex_value(pdata, fi);
1396
0
}
1397
1398
static void
1399
ek_write_field_value(field_info *fi, write_json_data* pdata)
1400
0
{
1401
0
    char *dfilter_string;
1402
1403
    /* Text label */
1404
0
    if (fi->hfinfo->id == hf_text_only && fi->rep) {
1405
0
        json_dumper_value_string(pdata->dumper, fi->rep->representation);
1406
0
    } else {
1407
        /* show, value, and unmaskedvalue attributes */
1408
0
        switch(fi->hfinfo->type) {
1409
0
        case FT_PROTOCOL:
1410
0
            if (fi->rep) {
1411
0
                json_dumper_value_string(pdata->dumper, fi->rep->representation);
1412
0
            }
1413
0
            else {
1414
0
                json_dumper_value_string(pdata->dumper, fi->hfinfo->name);
1415
0
            }
1416
0
            break;
1417
0
        case FT_BOOLEAN:
1418
            /* XXX - This is to use a JSON boolean literal, though ElasticSearch
1419
             * supports automatic conversion from "true" and "false" for boolean
1420
             * types*, so this could be handled, albeit less efficiently due to
1421
             * the string allocation, by the general case. (*But not from other
1422
             * truthy or falsy strings like "1" and "0" since 6.0. Compare:
1423
             * https://www.elastic.co/guide/en/elasticsearch/reference/5.0/boolean.html
1424
             * https://www.elastic.co/guide/en/elasticsearch/reference/6.0/boolean.html
1425
             * https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/boolean
1426
             * )
1427
             */
1428
0
            if (fvalue_get_uinteger64(fi->value))
1429
0
                json_dumper_value_literal(pdata->dumper, "true", 4);
1430
0
            else
1431
0
                json_dumper_value_literal(pdata->dumper, "false", 5);
1432
0
            break;
1433
0
        default:
1434
0
            dfilter_string = fvalue_to_string_repr(NULL, fi->value, FTREPR_EK, fi->hfinfo->display);
1435
0
            json_dumper_value_string(pdata->dumper, dfilter_string);
1436
0
            wmem_free(NULL, dfilter_string);
1437
0
            break;
1438
0
        }
1439
0
    }
1440
0
}
1441
1442
static void
1443
ek_write_attr_hex(GSList *attr_instances, write_json_data *pdata)
1444
0
{
1445
0
    GSList *current_node = attr_instances;
1446
0
    proto_node *pnode    = (proto_node *) current_node->data;
1447
0
    field_info *fi       = NULL;
1448
1449
    // Raw name
1450
0
    ek_write_name(pnode, "_raw", pdata);
1451
1452
0
    if (g_slist_length(attr_instances) > 1) {
1453
0
        json_dumper_begin_array(pdata->dumper);
1454
0
    }
1455
1456
    // Raw value(s)
1457
0
    while (current_node != NULL) {
1458
0
        pnode = (proto_node *) current_node->data;
1459
0
        fi    = PNODE_FINFO(pnode);
1460
1461
0
        ek_write_hex(fi, pdata);
1462
1463
0
        current_node = current_node->next;
1464
0
    }
1465
1466
0
    if (g_slist_length(attr_instances) > 1) {
1467
0
        json_dumper_end_array(pdata->dumper);
1468
0
    }
1469
0
}
1470
1471
static void
1472
// NOLINTNEXTLINE(misc-no-recursion)
1473
ek_write_attr(GSList *attr_instances, write_json_data *pdata)
1474
0
{
1475
0
    GSList *current_node  = attr_instances;
1476
0
    proto_node *pnode     = (proto_node *) current_node->data;
1477
0
    field_info *fi        = PNODE_FINFO(pnode);
1478
0
    pf_flags filter_flags = PF_NONE;
1479
1480
    // Hex dump -x
1481
0
    if (pdata->print_hex && fi && fi->length > 0 && fi->hfinfo->id != hf_text_only) {
1482
0
        ek_write_attr_hex(attr_instances, pdata);
1483
0
    }
1484
1485
    // Print attr name
1486
0
    ek_write_name(pnode, NULL, pdata);
1487
1488
0
    if (g_slist_length(attr_instances) > 1) {
1489
0
        json_dumper_begin_array(pdata->dumper);
1490
0
    }
1491
1492
0
    while (current_node != NULL) {
1493
0
        pnode = (proto_node *) current_node->data;
1494
0
        fi    = PNODE_FINFO(pnode);
1495
1496
        /* Field */
1497
0
        if (fi->hfinfo->type != FT_PROTOCOL) {
1498
0
            if (pdata->filter != NULL
1499
0
                && !ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev, &filter_flags)) {
1500
1501
                /* print dummy field */
1502
0
                json_dumper_begin_object(pdata->dumper);
1503
0
                json_dumper_set_member_name_const(pdata->dumper, "filtered");
1504
0
                json_dumper_value_string_noesc(pdata->dumper, fi->hfinfo->abbrev, strlen(fi->hfinfo->abbrev));
1505
0
                json_dumper_end_object(pdata->dumper);
1506
0
            } else {
1507
0
                ek_write_field_value(fi, pdata);
1508
0
            }
1509
0
        } else {
1510
            /* Object */
1511
0
            json_dumper_begin_object(pdata->dumper);
1512
1513
0
            if (pdata->filter != NULL) {
1514
0
                if (ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev, &filter_flags)) {
1515
0
                    wmem_map_t *_filter = NULL;
1516
                    /* Remove protocol filter for children, if children should be included */
1517
0
                    if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1518
0
                        _filter = pdata->filter;
1519
0
                        pdata->filter = NULL;
1520
0
                    }
1521
1522
0
                    proto_tree_write_node_ek(pnode, pdata);
1523
1524
                    /* Put protocol filter back */
1525
0
                    if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1526
0
                        pdata->filter = _filter;
1527
0
                    }
1528
0
                } else {
1529
                    /* print dummy field */
1530
0
                    json_dumper_set_member_name_const(pdata->dumper, "filtered");
1531
0
                    json_dumper_value_string_noesc(pdata->dumper, fi->hfinfo->abbrev, strlen(fi->hfinfo->abbrev));
1532
0
                }
1533
0
            } else {
1534
0
                proto_tree_write_node_ek(pnode, pdata);
1535
0
            }
1536
1537
0
            json_dumper_end_object(pdata->dumper);
1538
0
        }
1539
1540
0
        current_node = current_node->next;
1541
0
    }
1542
1543
0
    if (g_slist_length(attr_instances) > 1) {
1544
0
        json_dumper_end_array(pdata->dumper);
1545
0
    }
1546
0
}
1547
1548
// NOLINTNEXTLINE(misc-no-recursion)
1549
void process_ek_attrs(void *key _U_, void *value, void *pdata)
1550
0
{
1551
0
    GSList *attr_instances = (GSList *) value;
1552
0
    ek_write_attr(attr_instances, pdata);
1553
0
}
1554
1555
/* Write out a tree's data, and any child nodes, as JSON for EK */
1556
static void
1557
// NOLINTNEXTLINE(misc-no-recursion)
1558
proto_tree_write_node_ek(proto_node *node, write_json_data *pdata)
1559
0
{
1560
0
    GHashTable *attr_table  = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1561
0
    GHashTableIter iter;
1562
0
    void *key, *value;
1563
0
    ek_fill_attr(node, attr_table, pdata);
1564
1565
    // Print attributes
1566
0
    g_hash_table_iter_init(&iter, attr_table);
1567
0
    while (g_hash_table_iter_next (&iter, &key, &value)) {
1568
0
        process_ek_attrs(key, value, pdata);
1569
0
        g_hash_table_iter_remove(&iter);
1570
        /* We lookup a list in the table, append to it, and re-insert it; as
1571
         * g_slist_append() can change the start pointer of the list we can't
1572
         * just append to the list without replacing the old value. In turn,
1573
         * that means we can't set the value_destroy_func when creating
1574
         * the hash table, because on re-insertion that would destroy the
1575
         * nodes of the old list, which are still being used by the new list.
1576
         * So free it here.
1577
         */
1578
0
        g_slist_free((GSList*)value);
1579
0
    }
1580
0
    g_hash_table_destroy(attr_table);
1581
0
}
1582
1583
/* Print info for a 'geninfo' pseudo-protocol. This is required by
1584
 * the PDML spec. The information is contained in Wireshark's 'frame' protocol,
1585
 * but we produce a 'geninfo' protocol in the PDML to conform to spec.
1586
 * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */
1587
static void
1588
print_pdml_geninfo(epan_dissect_t *edt, FILE *fh)
1589
0
{
1590
0
    uint32_t    num, len, caplen;
1591
0
    GPtrArray  *finfo_array;
1592
0
    field_info *frame_finfo;
1593
0
    char       *tmp;
1594
1595
    /* Get frame protocol's finfo. */
1596
0
    finfo_array = proto_find_first_finfo(edt->tree, proto_frame);
1597
0
    if (g_ptr_array_len(finfo_array) < 1) {
1598
0
        return;
1599
0
    }
1600
0
    frame_finfo = (field_info *)finfo_array->pdata[0];
1601
0
    g_ptr_array_free(finfo_array, true);
1602
1603
    /* frame.number, packet_info.num */
1604
0
    num = edt->pi.num;
1605
1606
    /* frame.frame_len, packet_info.frame_data->pkt_len */
1607
0
    len = edt->pi.fd->pkt_len;
1608
1609
    /* frame.cap_len --> packet_info.frame_data->cap_len */
1610
0
    caplen = edt->pi.fd->cap_len;
1611
1612
    /* Print geninfo start */
1613
0
    fprintf(fh,
1614
0
            "  <proto name=\"geninfo\" pos=\"0\" showname=\"General information\" size=\"%d\">\n",
1615
0
            frame_finfo->length);
1616
1617
    /* Print geninfo.num */
1618
0
    fprintf(fh,
1619
0
            "    <field name=\"num\" pos=\"0\" show=\"%u\" showname=\"Number\" value=\"%x\" size=\"%d\"/>\n",
1620
0
            num, num, frame_finfo->length);
1621
1622
    /* Print geninfo.len */
1623
0
    fprintf(fh,
1624
0
            "    <field name=\"len\" pos=\"0\" show=\"%u\" showname=\"Frame Length\" value=\"%x\" size=\"%d\"/>\n",
1625
0
            len, len, frame_finfo->length);
1626
1627
    /* Print geninfo.caplen */
1628
0
    fprintf(fh,
1629
0
            "    <field name=\"caplen\" pos=\"0\" show=\"%u\" showname=\"Captured Length\" value=\"%x\" size=\"%d\"/>\n",
1630
0
            caplen, caplen, frame_finfo->length);
1631
1632
0
    tmp = abs_time_to_str(NULL, &edt->pi.abs_ts, ABSOLUTE_TIME_LOCAL, true);
1633
1634
    /* Print geninfo.timestamp */
1635
0
    fprintf(fh,
1636
0
            "    <field name=\"timestamp\" pos=\"0\" show=\"%s\" showname=\"Captured Time\" value=\"%jd.%09d\" size=\"%d\"/>\n",
1637
0
            tmp, (intmax_t)edt->pi.abs_ts.secs, edt->pi.abs_ts.nsecs, frame_finfo->length);
1638
1639
0
    wmem_free(NULL, tmp);
1640
1641
    /* Print geninfo end */
1642
0
    fprintf(fh,
1643
0
            "  </proto>\n");
1644
0
}
1645
1646
void
1647
write_pdml_finale(FILE *fh)
1648
0
{
1649
0
    fputs("</pdml>\n", fh);
1650
0
}
1651
1652
void
1653
write_psml_preamble(column_info *cinfo, FILE *fh)
1654
0
{
1655
0
    unsigned i;
1656
1657
0
    fprintf(fh, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
1658
0
    fprintf(fh, "<psml version=\"" PSML_VERSION "\" creator=\"%s/%s\">\n", PACKAGE, VERSION);
1659
0
    fprintf(fh, "<structure>\n");
1660
1661
0
    for (i = 0; i < cinfo->num_cols; i++) {
1662
0
        if (!get_column_visible(i))
1663
0
            continue;
1664
0
        fprintf(fh, "<section>");
1665
0
        print_escaped_xml(fh, cinfo->columns[i].col_title);
1666
0
        fprintf(fh, "</section>\n");
1667
0
    }
1668
1669
0
    fprintf(fh, "</structure>\n\n");
1670
0
}
1671
1672
void
1673
write_psml_columns(epan_dissect_t *edt, FILE *fh, bool use_color)
1674
0
{
1675
0
    unsigned i;
1676
0
    const color_filter_t *cfp = edt->pi.fd->color_filter;
1677
1678
0
    if (use_color && (cfp != NULL)) {
1679
0
        fprintf(fh, "<packet foreground='#%06x' background='#%06x'>\n",
1680
0
            color_t_to_rgb(&cfp->fg_color),
1681
0
            color_t_to_rgb(&cfp->bg_color));
1682
0
    } else {
1683
0
        fprintf(fh, "<packet>\n");
1684
0
    }
1685
1686
0
    for (i = 0; i < edt->pi.cinfo->num_cols; i++) {
1687
0
        if (!get_column_visible(i))
1688
0
            continue;
1689
0
        fprintf(fh, "<section>");
1690
0
        print_escaped_xml(fh, get_column_text(edt->pi.cinfo, i));
1691
0
        fprintf(fh, "</section>\n");
1692
0
    }
1693
1694
0
    fprintf(fh, "</packet>\n\n");
1695
0
}
1696
1697
void
1698
write_psml_finale(FILE *fh)
1699
0
{
1700
0
    fputs("</psml>\n", fh);
1701
0
}
1702
1703
static char *csv_massage_str(const char *source, const char *exceptions)
1704
0
{
1705
0
    char *csv_str;
1706
0
    char *tmp_str;
1707
1708
    /* In general, our output for any field can contain Unicode characters,
1709
       so g_strescape (which escapes any non-ASCII) is the wrong thing to do.
1710
       Unfortunately glib doesn't appear to provide g_unicode_strescape()... */
1711
0
    csv_str = g_strescape(source, exceptions);
1712
0
    tmp_str = csv_str;
1713
    /* Locate the UTF-8 right arrow character and replace it by an ASCII equivalent */
1714
0
    while ( (tmp_str = strstr(tmp_str, UTF8_RIGHTWARDS_ARROW)) != NULL ) {
1715
0
        tmp_str[0] = ' ';
1716
0
        tmp_str[1] = '>';
1717
0
        tmp_str[2] = ' ';
1718
0
    }
1719
0
    tmp_str = csv_str;
1720
0
    while ( (tmp_str = strstr(tmp_str, "\\\"")) != NULL )
1721
0
        *tmp_str = '\"';
1722
0
    return csv_str;
1723
0
}
1724
1725
static void csv_write_str(const char *str, char sep, FILE *fh, bool print_separator)
1726
0
{
1727
0
    char *csv_str;
1728
1729
    /* Do not escape the UTF-8 right arrow character */
1730
0
    csv_str = csv_massage_str(str, UTF8_RIGHTWARDS_ARROW);
1731
0
    if (print_separator) {
1732
0
        fprintf(fh, "%c\"%s\"", sep, csv_str);
1733
0
    } else {
1734
0
        fprintf(fh, "\"%s\"", csv_str);
1735
0
    }
1736
0
    g_free(csv_str);
1737
0
}
1738
1739
static void csv_write_str_utf8(const char *str, char sep, FILE *fh, bool print_separator, bool escape_wsp)
1740
0
{
1741
0
    char *csv_str;
1742
1743
0
    csv_str = ws_escape_csv(NULL, str, true, '\"', true, escape_wsp);
1744
0
    if (print_separator) {
1745
0
        fprintf(fh, "%c%s", sep, csv_str);
1746
0
    } else {
1747
0
        fprintf(fh, "%s", csv_str);
1748
0
    }
1749
0
    g_free(csv_str);
1750
0
}
1751
1752
void
1753
write_csv_column_titles(column_info *cinfo, FILE *fh)
1754
0
{
1755
0
    print_args_csv_t csv_args = {0};
1756
0
    write_csv_column_titles_with_args(cinfo, fh, csv_args);
1757
0
}
1758
1759
void
1760
write_csv_columns(epan_dissect_t *edt, FILE *fh)
1761
0
{
1762
0
    print_args_csv_t csv_args = {0};
1763
0
    write_csv_columns_with_args(edt, fh, csv_args);
1764
0
}
1765
1766
void
1767
write_csv_column_titles_with_args(column_info *cinfo, FILE *fh, print_args_csv_t csv_args)
1768
0
{
1769
0
    unsigned i;
1770
0
    bool print_separator = false;
1771
    // Avoid printing separator for first column
1772
1773
0
    for (i = 0; i < cinfo->num_cols; i++) {
1774
0
        if (!get_column_visible(i))
1775
0
            continue;
1776
0
        if (csv_args.print_utf8) {
1777
0
            csv_write_str_utf8(cinfo->columns[i].col_title, ',', fh, print_separator, csv_args.escape_wsp);
1778
0
        } else {
1779
0
            csv_write_str(cinfo->columns[i].col_title, ',', fh, print_separator);
1780
0
        }
1781
0
        print_separator = true;
1782
0
    }
1783
0
    if (print_separator) { // Only add line break if anything was output
1784
0
        fprintf(fh, "\n");
1785
0
    }
1786
0
}
1787
1788
void
1789
write_csv_columns_with_args(epan_dissect_t *edt, FILE *fh, print_args_csv_t csv_args)
1790
0
{
1791
0
    unsigned i;
1792
0
    bool print_separator = false;
1793
    // Avoid printing separator for first column
1794
1795
0
    for (i = 0; i < edt->pi.cinfo->num_cols; i++) {
1796
0
        if (!get_column_visible(i))
1797
0
            continue;
1798
0
        if (csv_args.print_utf8) {
1799
0
            csv_write_str_utf8(get_column_text(edt->pi.cinfo, i), ',', fh, print_separator, csv_args.escape_wsp);
1800
0
        } else {
1801
0
            csv_write_str(get_column_text(edt->pi.cinfo, i), ',', fh, print_separator);
1802
0
        }
1803
0
        print_separator = true;
1804
0
    }
1805
0
    if (print_separator) { // Only add line break if anything was output
1806
0
        fprintf(fh, "\n");
1807
0
    }
1808
0
}
1809
1810
void
1811
write_carrays_preamble(FILE *fh _U_, GPtrArray *index, print_args_carrays_t args)
1812
0
{
1813
0
    if (args.print_index) {
1814
0
        if (args.print_secondary_data_sources) {
1815
0
            g_ptr_array_add(index, g_strdup("static struct { \n"
1816
0
                "    unsigned frame_num;\n"
1817
0
                "    unsigned data_src_num;\n"
1818
0
                "    unsigned data_size;\n"
1819
0
                "    const unsigned char *data;\n"
1820
0
                "    const char *data_src_name;\n"
1821
0
                "} data_source_list[] = {\n"));
1822
0
        } else {
1823
0
            g_ptr_array_add(index, g_strdup("static struct { \n"
1824
0
                "    unsigned frame_num;\n"
1825
0
                "    unsigned data_size;\n"
1826
0
                "    const unsigned char *data;\n"
1827
0
                "} frame_list[] = {\n"));
1828
0
        }
1829
0
    }
1830
0
}
1831
1832
void
1833
write_carrays_hex_data_with_args(uint32_t num, FILE *fh, epan_dissect_t *edt, GPtrArray *index, print_args_carrays_t args)
1834
0
{
1835
0
    uint32_t      i = 0, src_num = 0;
1836
0
    GSList       *src_le;
1837
0
    tvbuff_t     *tvb;
1838
0
    char         *description;
1839
0
    char         *pkt_name;
1840
0
    const unsigned char *cp;
1841
0
    unsigned      length;
1842
0
    char          ascii[9];
1843
0
    struct data_source *src;
1844
1845
0
    for (src_le = edt->pi.data_src; src_le != NULL; src_le = args.print_secondary_data_sources ? src_le->next : NULL) {
1846
0
        memset(ascii, 0, sizeof(ascii));
1847
0
        src = (struct data_source *)src_le->data;
1848
0
        tvb = get_data_source_tvb(src);
1849
0
        length = tvb_captured_length(tvb);
1850
0
        if (length == 0)
1851
0
            continue;
1852
1853
0
        cp = tvb_get_ptr(tvb, 0, length);
1854
1855
        // This cannot return NULL with non-NULL src
1856
0
        description = get_data_source_description(src);
1857
0
        fprintf(fh, "// %s\n", description);
1858
0
        if (src_num) {
1859
0
            pkt_name = ws_strdup_printf("pkt%u_%u", num, src_num);
1860
0
        } else {
1861
0
            pkt_name = ws_strdup_printf("pkt%u", num);
1862
0
        }
1863
1864
0
        fprintf(fh, "static const unsigned char %s[%u] = {\n", pkt_name, length);
1865
0
        if (index && args.print_index) {
1866
0
            if (args.print_secondary_data_sources) {
1867
0
                g_ptr_array_add(index, ws_strdup_printf("    { %u, %u, %4u, %s, \"%s\" },\n",
1868
0
                                                        num, src_num, length, pkt_name,
1869
0
                                                        get_data_source_name(src)));
1870
0
            } else {
1871
0
                g_ptr_array_add(index, ws_strdup_printf("    { %u, %4u, %s },\n",
1872
0
                                                        num, length, pkt_name));
1873
0
            }
1874
0
        }
1875
0
        wmem_free(NULL, pkt_name);
1876
0
        wmem_free(NULL, description);
1877
0
        src_num++;
1878
1879
0
        for (i = 0; i < length; i++) {
1880
0
            fprintf(fh, "0x%02x", *(cp + i));
1881
0
            ascii[i % 8] = g_ascii_isprint(*(cp + i)) ? *(cp + i) : '.';
1882
1883
0
            if (i == (length - 1)) {
1884
0
                unsigned rem;
1885
0
                rem = length % 8;
1886
0
                if (rem) {
1887
0
                    unsigned j;
1888
0
                    for ( j = 0; j < 8 - rem; j++ )
1889
0
                        fprintf(fh, "      ");
1890
0
                }
1891
0
                fprintf(fh, "  // |%s|\n};\n\n", ascii);
1892
0
                break;
1893
0
            }
1894
1895
0
            if (!((i + 1) % 8)) {
1896
0
                fprintf(fh, ", // |%s|\n", ascii);
1897
0
                memset(ascii, 0, sizeof(ascii));
1898
0
            } else {
1899
0
                fprintf(fh, ", ");
1900
0
            }
1901
0
        }
1902
0
    }
1903
0
}
1904
1905
void
1906
write_carrays_hex_data(uint32_t num, FILE *fh, epan_dissect_t *edt)
1907
0
{
1908
0
    print_args_carrays_t args = {.print_secondary_data_sources = true};
1909
0
    write_carrays_hex_data_with_args(num, fh, edt, NULL, args);
1910
0
}
1911
1912
void
1913
write_carrays_finale(FILE *fh, GPtrArray *index, print_args_carrays_t args)
1914
0
{
1915
0
    if (args.print_index) {
1916
0
        for (unsigned dsnum = 0; dsnum < index->len; dsnum++) {
1917
0
            fprintf(fh, "%s", (char*)g_ptr_array_index(index, dsnum));
1918
0
        }
1919
        /* Using 0L instead of NULL just to avoid any dependency on
1920
         * standard headers or versions of C/C++, but probably overkill. */
1921
0
        if (args.print_secondary_data_sources) {
1922
0
            fputs("    { 0, 0, 0, 0L, 0L },\n};\n", fh);
1923
0
        } else {
1924
0
            fputs("    { 0, 0, 0L },\n};\n", fh);
1925
0
        }
1926
0
    }
1927
0
}
1928
1929
/*
1930
 * Find the data source for a specified field, and return a pointer to it.
1931
 * Also returns the index of the data source in the list of data sources
1932
 * Returns NULL if the field's data source is not in the list of data sources,
1933
 * in which case idx is not valid.
1934
 */
1935
static struct data_source*
1936
get_field_data_source(GSList *src_list, field_info *fi, uint32_t *idx)
1937
0
{
1938
0
    GSList   *src_le;
1939
0
    struct data_source *src;
1940
0
    uint32_t  src_idx = 0;
1941
1942
0
    for (src_le = src_list; src_le != NULL; src_le = src_le->next) {
1943
0
        src = (struct data_source *)src_le->data;
1944
0
        if (fi->ds_tvb == get_data_source_tvb(src)) {
1945
            /*
1946
             * Found it.
1947
             */
1948
0
            if (idx) {
1949
0
                *idx = src_idx;
1950
0
            }
1951
0
            return src;
1952
0
        }
1953
0
        src_idx++;
1954
0
    }
1955
0
    return NULL;  /* not found */
1956
0
}
1957
1958
/* Fast variant using last-hit cache — consecutive fields often share the same data source */
1959
static struct data_source*
1960
get_field_data_source_cached(GSList *src_list, field_info *fi, uint32_t *idx, write_json_data *pdata)
1961
0
{
1962
    /* Fast path: check last-hit cache */
1963
0
    if (pdata->cached_src && fi->ds_tvb == pdata->cached_src_tvb) {
1964
0
        if (idx) {
1965
0
            *idx = pdata->cached_src_idx;
1966
0
        }
1967
0
        return pdata->cached_src;
1968
0
    }
1969
    /* Miss: do full search and update cache */
1970
0
    uint32_t found_idx = 0;
1971
0
    struct data_source *src = get_field_data_source(src_list, fi, &found_idx);
1972
0
    if (src) {
1973
0
        pdata->cached_src_tvb = fi->ds_tvb;
1974
0
        pdata->cached_src = src;
1975
0
        pdata->cached_src_idx = found_idx;
1976
0
        if (idx) {
1977
0
            *idx = found_idx;
1978
0
        }
1979
0
    }
1980
0
    return src;
1981
0
}
1982
1983
/*
1984
 * Find the data source for a specified field, and return a pointer
1985
 * to the data in it. Returns NULL if the data is out of bounds.
1986
 */
1987
/* XXX: What am I missing ?
1988
 *      Why bother searching for fi->ds_tvb for the matching tvb
1989
 *       in the data_source list ?
1990
 *      IOW: Why not just use fi->ds_tvb for the arg to tvb_get_ptr() ?
1991
 *
1992
 *      The effect is that if the field was added to the tree with a
1993
 *      a tvb whose data source tvb was *not* added to pinfo with
1994
 *      add_new_data_source, then it won't get printed. But why?
1995
 */
1996
static const uint8_t *
1997
get_field_data(GSList *src_list, field_info *fi)
1998
0
{
1999
0
    tvbuff_t *src_tvb;
2000
0
    int       length, tvbuff_length;
2001
0
    struct data_source *src;
2002
2003
0
    src = get_field_data_source(src_list, fi, NULL);
2004
0
    if (src) {
2005
0
        src_tvb = get_data_source_tvb(src);
2006
        /*
2007
         * Found it.
2008
         *
2009
         * XXX - a field can have a length that runs past
2010
         * the end of the tvbuff.  Ideally, that should
2011
         * be fixed when adding an item to the protocol
2012
         * tree, but checking the length when doing
2013
         * that could be expensive.  Until we fix that,
2014
         * we'll do the check here.
2015
         */
2016
0
        tvbuff_length = tvb_captured_length_remaining(src_tvb,
2017
0
                                             fi->start);
2018
0
        if (tvbuff_length < 0) {
2019
0
            return NULL;
2020
0
        }
2021
0
        length = fi->length;
2022
0
        if (length > tvbuff_length)
2023
0
            length = tvbuff_length;
2024
0
        return tvb_get_ptr(src_tvb, fi->start, length);
2025
0
    }
2026
0
    return NULL;  /* not found */
2027
0
}
2028
2029
/* Fast variant using cached first-source tvb */
2030
static const uint8_t *
2031
get_field_data_cached(GSList *src_list, field_info *fi, write_json_data *pdata)
2032
0
{
2033
0
    tvbuff_t *src_tvb;
2034
0
    int       length, tvbuff_length;
2035
0
    struct data_source *src;
2036
2037
0
    src = get_field_data_source_cached(src_list, fi, NULL, pdata);
2038
0
    if (src) {
2039
0
        src_tvb = get_data_source_tvb(src);
2040
0
        tvbuff_length = tvb_captured_length_remaining(src_tvb, fi->start);
2041
0
        if (tvbuff_length < 0) {
2042
0
            return NULL;
2043
0
        }
2044
0
        length = fi->length;
2045
0
        if (length > tvbuff_length)
2046
0
            length = tvbuff_length;
2047
0
        return tvb_get_ptr(src_tvb, fi->start, length);
2048
0
    }
2049
0
    return NULL;
2050
0
}
2051
2052
/* Print a string, escaping out certain characters that need to
2053
 * escaped out for XML. */
2054
static void
2055
print_escaped_xml(FILE *fh, const char *unescaped_string)
2056
0
{
2057
0
    char* buff;
2058
2059
0
    if (fh == NULL || unescaped_string == NULL) {
2060
0
        return;
2061
0
    }
2062
2063
0
    buff = xml_escape(unescaped_string);
2064
2065
0
    fputs(buff, fh);
2066
0
    g_free(buff);
2067
0
}
2068
2069
static void
2070
print_escaped_csv(FILE *fh, const char *unescaped_string, char delimiter, char quote_char, bool escape_wsp)
2071
0
{
2072
0
    if (fh == NULL || unescaped_string == NULL) {
2073
0
        return;
2074
0
    }
2075
2076
    /* XXX: What about the field aggregator? Should that be escaped?
2077
     * Should there be an "escape all non-printable" option?
2078
     * (Instead of or in addition to escape wsp?)
2079
     * Should there be a "escape all non ASCII?" option, similar
2080
     * to the Wireshark output?
2081
     */
2082
0
    char *escaped_string;
2083
0
    if (quote_char == '\0') {
2084
        /* Not quoting, so we must escape the delimiter */
2085
0
        escaped_string = ws_escape_csv(NULL, unescaped_string, false, delimiter, false, escape_wsp);
2086
0
    } else {
2087
0
        escaped_string = ws_escape_csv(NULL, unescaped_string, true, quote_char, true, escape_wsp);
2088
0
    }
2089
0
    fputs(escaped_string, fh);
2090
0
    wmem_free(NULL, escaped_string);
2091
0
}
2092
2093
static void
2094
pdml_write_field_hex_value(write_pdml_data *pdata, field_info *fi)
2095
0
{
2096
0
    unsigned       i;
2097
0
    const uint8_t *pd;
2098
2099
0
    if (!fi->ds_tvb)
2100
0
        return;
2101
2102
0
    if (fi->length > (unsigned)tvb_captured_length_remaining(fi->ds_tvb, fi->start)) {
2103
0
        fprintf(pdata->fh, "field length invalid!");
2104
0
        return;
2105
0
    }
2106
2107
    /* Find the data for this field. */
2108
0
    pd = get_field_data(pdata->src_list, fi);
2109
2110
0
    if (pd) {
2111
        /* Used fixed buffer where can, otherwise temp malloc */
2112
0
        static char str_static[513];
2113
0
        char *str = str_static;
2114
0
        char* str_heap = NULL;
2115
0
        if (fi->length > 256) {
2116
0
            str_heap = (char*)g_malloc(fi->length*2 + 1);  /* no need to zero */
2117
0
            str = str_heap;
2118
0
        }
2119
2120
0
        static const char hex[] = "0123456789abcdef";
2121
2122
        /* Print a simple hex dump */
2123
0
        for (i = 0 ; i < fi->length; i++) {
2124
0
            str[2*i] =   hex[pd[i] >> 4];
2125
0
            str[2*i+1] = hex[pd[i] & 0xf];
2126
0
        }
2127
0
        str[2 * fi->length] = '\0';
2128
0
        fputs(str, pdata->fh);
2129
0
        g_free(str_heap);            /* harmless/fast if NULL */
2130
0
    }
2131
0
}
2132
2133
static void
2134
json_write_field_hex_value(write_json_data *pdata, field_info *fi)
2135
0
{
2136
0
    const uint8_t *pd;
2137
2138
    // XXX - Why are uppercase hex digits used if the bitmask is non zero,
2139
    // and lowercase otherwise? To give a hint that there was a bitmask?
2140
0
    if (fi->hfinfo->bitmask!=0) {
2141
0
        switch (fvalue_type_ftenum(fi->value)) {
2142
0
            case FT_INT8:
2143
0
            case FT_INT16:
2144
0
            case FT_INT24:
2145
0
            case FT_INT32:
2146
0
                json_dumper_value_anyf(pdata->dumper, "\"%X\"", (unsigned) fvalue_get_sinteger(fi->value));
2147
0
                return;
2148
0
            case FT_CHAR:
2149
0
            case FT_UINT8:
2150
0
            case FT_UINT16:
2151
0
            case FT_UINT24:
2152
0
            case FT_UINT32:
2153
0
                json_dumper_value_anyf(pdata->dumper, "\"%X\"", fvalue_get_uinteger(fi->value));
2154
0
                return;
2155
0
            case FT_INT40:
2156
0
            case FT_INT48:
2157
0
            case FT_INT56:
2158
0
            case FT_INT64:
2159
0
                json_dumper_value_anyf(pdata->dumper, "\"%" PRIX64 "\"", fvalue_get_sinteger64(fi->value));
2160
0
                return;
2161
0
            case FT_UINT40:
2162
0
            case FT_UINT48:
2163
0
            case FT_UINT56:
2164
0
            case FT_UINT64:
2165
0
            case FT_BOOLEAN:
2166
0
                json_dumper_value_anyf(pdata->dumper, "\"%" PRIX64 "\"", fvalue_get_uinteger64(fi->value));
2167
0
                return;
2168
0
            default:
2169
0
                ws_assert_not_reached();
2170
0
        }
2171
0
    }
2172
2173
0
    if (!fi->ds_tvb) {
2174
        // Should this be null instead of the empty string?
2175
0
        json_dumper_value_string_noesc(pdata->dumper, "", 0);
2176
0
        return;
2177
0
    }
2178
2179
0
    if (fi->length > (unsigned)tvb_captured_length_remaining(fi->ds_tvb, fi->start)) {
2180
0
        json_dumper_value_string_noesc(pdata->dumper, "field length invalid!", 21);
2181
0
        return;
2182
0
    }
2183
2184
    /* Find the data for this field. */
2185
0
    pd = get_field_data_cached(pdata->src_list, fi, pdata);
2186
2187
0
    if (pd) {
2188
        /* Write hex directly using raw string output (no escape needed for hex). */
2189
0
        static const char hex[] = "0123456789abcdef";
2190
0
        unsigned len = fi->length;
2191
0
        char buf[512];
2192
0
        unsigned i = 0;
2193
0
        if (len <= (sizeof(buf) - 1) / 2) {
2194
0
            for (i = 0; i < len; i++) {
2195
0
                uint8_t c = pd[i];
2196
0
                buf[2 * i] = hex[c >> 4];
2197
0
                buf[2 * i + 1] = hex[c & 0xf];
2198
0
            }
2199
0
            json_dumper_value_string_noesc(pdata->dumper, buf, len * 2);
2200
0
        } else {
2201
0
            char *str = (char*)g_malloc(len * 2);
2202
0
            for (i = 0; i < len; i++) {
2203
0
                uint8_t c = pd[i];
2204
0
                str[2 * i] = hex[c >> 4];
2205
0
                str[2 * i + 1] = hex[c & 0xf];
2206
0
            }
2207
0
            json_dumper_value_string_noesc(pdata->dumper, str, len * 2);
2208
0
            g_free(str);
2209
0
        }
2210
0
    } else {
2211
        // Should this be null instead of the empty string?
2212
0
        json_dumper_value_string_noesc(pdata->dumper, "", 0);
2213
0
    }
2214
0
}
2215
2216
bool
2217
print_hex_data(print_stream_t *stream, epan_dissect_t *edt, unsigned hexdump_options)
2218
0
{
2219
0
    bool          multiple_sources;
2220
0
    GSList       *src_le;
2221
0
    tvbuff_t     *tvb;
2222
0
    char         *line, *description;
2223
0
    const unsigned char *cp;
2224
0
    unsigned      length;
2225
0
    struct data_source *src;
2226
0
    char          timebuf[NSTIME_ISO8601_BUFSIZE];
2227
2228
0
    if ((HEXDUMP_TIMESTAMP_OPTION(hexdump_options) == HEXDUMP_TIMESTAMP)) {
2229
0
        set_fd_time(edt->session, edt->pi.fd, timebuf);
2230
0
        print_line(stream, 0, timebuf);
2231
0
    }
2232
    /*
2233
     * Set "multiple_sources" iff this frame has more than one
2234
     * data source; if it does, we need to print the name of
2235
     * the data source before printing the data from the
2236
     * data source.
2237
     */
2238
0
    multiple_sources = (edt->pi.data_src->next != NULL);
2239
2240
0
    for (src_le = edt->pi.data_src; src_le != NULL;
2241
0
         src_le = src_le->next) {
2242
0
        src = (struct data_source *)src_le->data;
2243
0
        tvb = get_data_source_tvb(src);
2244
0
        if (multiple_sources && (HEXDUMP_SOURCE_OPTION(hexdump_options) == HEXDUMP_SOURCE_MULTI)) {
2245
0
            description = get_data_source_description(src);
2246
0
            line = ws_strdup_printf("%s:", description);
2247
0
            wmem_free(NULL, description);
2248
0
            print_line(stream, 0, line);
2249
0
            g_free(line);
2250
0
        }
2251
0
        length = tvb_captured_length(tvb);
2252
0
        if (length == 0)
2253
0
            return true;
2254
0
        cp = tvb_get_ptr(tvb, 0, length);
2255
0
        if (!print_hex_data_buffer(stream, cp, length,
2256
0
                                   (packet_char_enc)edt->pi.fd->encoding,
2257
0
                                   HEXDUMP_ASCII_OPTION(hexdump_options)))
2258
0
            return false;
2259
0
        if (HEXDUMP_SOURCE_OPTION(hexdump_options) == HEXDUMP_SOURCE_PRIMARY) {
2260
0
            return true;
2261
0
        }
2262
0
    }
2263
0
    return true;
2264
0
}
2265
2266
static bool print_hex_data_line(void *stream, const char *line)
2267
0
{
2268
0
    return print_line(stream, 0, line);
2269
0
}
2270
2271
static bool print_hex_data_buffer(print_stream_t *stream, const unsigned char *cp,
2272
                                      unsigned length, packet_char_enc encoding,
2273
                                      unsigned hexdump_options)
2274
0
{
2275
0
    return hex_dump_buffer(print_hex_data_line, stream, cp, length,
2276
0
                        encoding == PACKET_CHAR_ENC_CHAR_EBCDIC ? HEXDUMP_ENC_EBCDIC : HEXDUMP_ENC_ASCII,
2277
0
                        hexdump_options);
2278
0
}
2279
2280
size_t output_fields_num_fields(output_fields_t* fields)
2281
0
{
2282
0
    ws_assert(fields);
2283
2284
0
    if (NULL == fields->fields) {
2285
0
        return 0;
2286
0
    } else {
2287
0
        return fields->fields->len;
2288
0
    }
2289
0
}
2290
2291
void output_fields_free(output_fields_t* fields)
2292
0
{
2293
0
    ws_assert(fields);
2294
2295
0
    if (NULL != fields->fields) {
2296
0
        size_t i;
2297
2298
0
        if (NULL != fields->field_indicies) {
2299
            /* Keys are stored in fields->fields, values are
2300
             * integers.
2301
             */
2302
0
            g_hash_table_destroy(fields->field_indicies);
2303
0
        }
2304
2305
0
        if (NULL != fields->field_dfilters) {
2306
0
            g_ptr_array_unref(fields->field_dfilters);
2307
0
        }
2308
2309
0
        if (NULL != fields->field_values) {
2310
0
            g_free(fields->field_values);
2311
0
        }
2312
2313
0
        for (i = 0; i < fields->fields->len; ++i) {
2314
0
            char* field = (char *)g_ptr_array_index(fields->fields,i);
2315
0
            g_free(field);
2316
0
        }
2317
0
        g_ptr_array_free(fields->fields, true);
2318
0
    }
2319
2320
0
    g_free(fields->split_by);
2321
0
    g_free(fields);
2322
0
}
2323
2324
void output_fields_add(output_fields_t *fields, const char *field)
2325
0
{
2326
0
    char *field_copy;
2327
2328
0
    ws_assert(fields);
2329
0
    ws_assert(field);
2330
2331
2332
0
    if (NULL == fields->fields) {
2333
0
        fields->fields = g_ptr_array_new();
2334
0
    }
2335
2336
0
    field_copy = g_strdup(field);
2337
2338
0
    g_ptr_array_add(fields->fields, field_copy);
2339
2340
    /* See if we have a column as a field entry */
2341
0
    if (!strncmp(field, COLUMN_FIELD_FILTER, strlen(COLUMN_FIELD_FILTER)))
2342
0
        fields->includes_col_fields = true;
2343
2344
0
}
2345
2346
/*
2347
 * Returns true if the field did not exist yet (or existed with the same
2348
 * filter_flags value), false if the field was in the protocolfilter with
2349
 * a different flag.
2350
 */
2351
bool
2352
output_fields_add_protocolfilter(output_fields_t* fields, const char* field, pf_flags filter_flags)
2353
0
{
2354
0
    void* value;
2355
0
    bool ret = true;
2356
0
    if (!fields->protocolfilter) {
2357
0
        fields->protocolfilter = wmem_map_new(wmem_epan_scope(), wmem_str_hash, g_str_equal);
2358
0
    }
2359
0
    if (wmem_map_lookup_extended(fields->protocolfilter, field, NULL, &value)) {
2360
0
        if (GPOINTER_TO_UINT(value) != (unsigned)filter_flags) {
2361
0
            ret = false;
2362
0
        }
2363
0
    }
2364
0
    wmem_map_insert(fields->protocolfilter, field, GINT_TO_POINTER(filter_flags));
2365
2366
    /* See if we have a column as a field entry */
2367
0
    if (!strncmp(field, COLUMN_FIELD_FILTER, strlen(COLUMN_FIELD_FILTER)))
2368
0
        fields->includes_col_fields = true;
2369
2370
0
    return ret;
2371
0
}
2372
2373
static void
2374
output_field_check(void *data, void *user_data)
2375
0
{
2376
0
    char *field = (char *)data;
2377
0
    GSList **invalid_fields = (GSList **)user_data;
2378
2379
0
    dfilter_t *dfilter;
2380
0
    if (dfilter_compile(field, &dfilter, NULL)) {
2381
0
        dfilter_free(dfilter);
2382
0
    } else {
2383
0
        *invalid_fields = g_slist_prepend(*invalid_fields, field);
2384
0
    }
2385
2386
0
}
2387
2388
static void
2389
output_field_check_protocolfilter(void* key, void* value _U_, void* user_data)
2390
0
{
2391
0
    output_field_check(key, user_data);
2392
0
}
2393
2394
GSList *
2395
output_fields_valid(output_fields_t *fields)
2396
0
{
2397
0
    GSList *invalid_fields = NULL;
2398
0
    if (fields->fields != NULL) {
2399
0
        g_ptr_array_foreach(fields->fields, output_field_check, &invalid_fields);
2400
0
    }
2401
2402
0
    if (fields->protocolfilter != NULL) {
2403
0
        wmem_map_foreach(fields->protocolfilter, output_field_check_protocolfilter, &invalid_fields);
2404
0
    }
2405
2406
0
    return invalid_fields;
2407
0
}
2408
2409
bool output_fields_set_option(output_fields_t *info, char *option)
2410
0
{
2411
0
    const char *option_name;
2412
0
    const char *option_value;
2413
2414
0
    ws_assert(info);
2415
0
    ws_assert(option);
2416
2417
0
    if ('\0' == *option) {
2418
0
        return false; /* this happens if we're called from tshark -E '' */
2419
0
    }
2420
0
    option_name = strtok(option, "=");
2421
0
    if (!option_name) {
2422
0
        return false;
2423
0
    }
2424
0
    option_value = option + strlen(option_name) + 1;
2425
0
    if (*option_value == '\0') {
2426
0
        return false;
2427
0
    }
2428
2429
0
    if (0 == strcmp(option_name, "header")) {
2430
0
        switch (*option_value) {
2431
0
        case 'n':
2432
0
            info->print_header = false;
2433
0
            break;
2434
0
        case 'y':
2435
0
            info->print_header = true;
2436
0
            break;
2437
0
        default:
2438
0
            return false;
2439
0
        }
2440
0
        return true;
2441
0
    }
2442
0
    else if (0 == strcmp(option_name, "separator")) {
2443
0
        switch (*option_value) {
2444
0
        case '/':
2445
0
            switch (*++option_value) {
2446
0
            case 't':
2447
0
                info->separator = '\t';
2448
0
                break;
2449
0
            case 's':
2450
0
                info->separator = ' ';
2451
0
                break;
2452
0
            default:
2453
0
                info->separator = '\\';
2454
0
            }
2455
0
            break;
2456
0
        default:
2457
0
            info->separator = *option_value;
2458
0
            break;
2459
0
        }
2460
0
        return true;
2461
0
    }
2462
0
    else if (0 == strcmp(option_name, "occurrence")) {
2463
0
        switch (*option_value) {
2464
0
        case 'f':
2465
0
        case 'l':
2466
0
        case 'a':
2467
0
            info->occurrence = *option_value;
2468
0
            break;
2469
0
        default:
2470
0
            return false;
2471
0
        }
2472
0
        return true;
2473
0
    }
2474
0
    else if (0 == strcmp(option_name, "aggregator")) {
2475
0
        switch (*option_value) {
2476
0
        case '/':
2477
0
            switch (*++option_value) {
2478
0
            case 's':
2479
0
                info->aggregator = ' ';
2480
0
                break;
2481
0
            default:
2482
0
                info->aggregator = '\\';
2483
0
            }
2484
0
            break;
2485
0
        default:
2486
0
            info->aggregator = *option_value;
2487
0
            break;
2488
0
        }
2489
0
        return true;
2490
0
    }
2491
0
    else if (0 == strcmp(option_name, "quote")) {
2492
0
        switch (*option_value) {
2493
0
        case 'd':
2494
0
            info->quote = '"';
2495
0
            break;
2496
0
        case 's':
2497
0
            info->quote = '\'';
2498
0
            break;
2499
0
        case 'n':
2500
0
            info->quote = '\0';
2501
0
            break;
2502
0
        default:
2503
0
            info->quote = '\0';
2504
0
            return false;
2505
0
        }
2506
0
        return true;
2507
0
    }
2508
0
    else if (0 == strcmp(option_name, "bom")) {
2509
0
        switch (*option_value) {
2510
0
        case 'n':
2511
0
            info->print_bom = false;
2512
0
            break;
2513
0
        case 'y':
2514
0
            info->print_bom = true;
2515
0
            break;
2516
0
        default:
2517
0
            return false;
2518
0
        }
2519
0
        return true;
2520
0
    }
2521
0
    else if (0 == strcmp(option_name, "escape")) {
2522
0
        switch (*option_value) {
2523
0
        case 'n':
2524
0
            info->escape = false;
2525
0
            break;
2526
0
        case 'y':
2527
0
            info->escape = true;
2528
0
            break;
2529
0
        default:
2530
0
            return false;
2531
0
        }
2532
0
        return true;
2533
0
    }
2534
0
    else if (0 == strcmp(option_name, "split")) {
2535
0
        g_free(info->split_by);
2536
0
        info->split_by = g_strdup(option_value);
2537
0
        return true;
2538
0
    }
2539
2540
0
    return false;
2541
0
}
2542
2543
void output_fields_list_options(FILE *fh)
2544
0
{
2545
0
    fprintf(fh, "TShark: The available options for field output \"E\" are:\n");
2546
0
    fputs("bom=y|n    Prepend output with the UTF-8 BOM (def: N: no)\n", fh);
2547
0
    fputs("header=y|n    Print field abbreviations as first line of output (def: N: no)\n", fh);
2548
0
    fputs("separator=/t|/s|<character>   Set the separator to use;\n     \"/t\" = tab, \"/s\" = space (def: /t: tab)\n", fh);
2549
0
    fputs("occurrence=f|l|a  Select the occurrence of a field to use;\n     \"f\" = first, \"l\" = last, \"a\" = all (def: a: all)\n", fh);
2550
0
    fputs("aggregator=,|/s|<character>   Set the aggregator to use;\n     \",\" = comma, \"/s\" = space (def: ,: comma)\n", fh);
2551
0
    fputs("quote=d|s|n   Print either d: double-quotes, s: single quotes or \n     n: no quotes around field values (def: n: none)\n", fh);
2552
0
    fputs("split=<proto>   Split output into one row per message instance of <proto>\n     (e.g., split=diameter)\n", fh);
2553
0
}
2554
2555
bool output_fields_has_cols(output_fields_t* fields)
2556
0
{
2557
0
    ws_assert(fields);
2558
0
    return fields->includes_col_fields;
2559
0
}
2560
2561
static void
2562
output_field_prime_edt(void *data, void *user_data)
2563
0
{
2564
0
    char *field = (char *)data;
2565
0
    epan_dissect_t *edt = (epan_dissect_t*)user_data;
2566
2567
    /* Find a hf. Note in tshark we already converted the protocol from
2568
     * its alias, if any.
2569
     */
2570
0
    header_field_info *hfinfo = proto_registrar_get_byname(field);
2571
0
    if (hfinfo) {
2572
        /* Rewind to the first hf of that name. */
2573
0
        while (hfinfo->same_name_prev_id != -1) {
2574
0
            hfinfo = proto_registrar_get_nth(hfinfo->same_name_prev_id);
2575
0
        }
2576
2577
        /* Prime all hf's with that name. */
2578
0
        while (hfinfo) {
2579
0
            proto_tree_prime_with_hfid_print(edt->tree, hfinfo->id);
2580
0
            hfinfo = hfinfo->same_name_next;
2581
0
        }
2582
0
    }
2583
0
}
2584
2585
static void
2586
output_field_dfilter_prime_edt(void *data, void *user_data)
2587
0
{
2588
0
    dfilter_t *dfilter = (dfilter_t *)data;
2589
0
    epan_dissect_t *edt = (epan_dissect_t*)user_data;
2590
2591
0
    if (dfilter) {
2592
0
        epan_dissect_prime_with_dfilter(edt, dfilter);
2593
0
    }
2594
0
}
2595
2596
static void
2597
dfilter_free_cb(void *data)
2598
0
{
2599
0
    dfilter_t *dcode = (dfilter_t*)data;
2600
2601
0
    dfilter_free(dcode);
2602
0
}
2603
2604
void output_fields_prime_edt(epan_dissect_t *edt, output_fields_t* fields)
2605
0
{
2606
0
    if (fields->fields != NULL) {
2607
0
        g_ptr_array_foreach(fields->fields, output_field_prime_edt, edt);
2608
2609
0
        if (fields->field_dfilters == NULL) {
2610
0
            fields->field_dfilters = g_ptr_array_new_full(fields->fields->len, dfilter_free_cb);
2611
2612
0
            for (size_t i = 0; i < fields->fields->len; ++i) {
2613
0
                char *field = (char *)g_ptr_array_index(fields->fields, i);
2614
0
                dfilter_t *dfilter = NULL;
2615
2616
                /* For now, we only compile a filter for complex expressions.
2617
                 * If it's just a field name, use the previous method.
2618
                 */
2619
0
                if (!proto_registrar_get_byname(field)) {
2620
0
                    dfilter_compile_full(field, &dfilter, NULL, DF_EXPAND_MACROS|DF_OPTIMIZE|DF_RETURN_VALUES, __func__);
2621
0
                }
2622
0
                g_ptr_array_add(fields->field_dfilters, dfilter);
2623
0
            }
2624
0
        }
2625
2626
0
        g_ptr_array_foreach(fields->field_dfilters, output_field_dfilter_prime_edt, edt);
2627
0
    }
2628
0
}
2629
2630
void write_fields_preamble(output_fields_t* fields, FILE *fh)
2631
0
{
2632
0
    size_t i;
2633
2634
0
    ws_assert(fields);
2635
0
    ws_assert(fh);
2636
0
    ws_assert(fields->fields);
2637
2638
0
    if (fields->print_bom) {
2639
0
        fputs(UTF8_BOM, fh);
2640
0
    }
2641
2642
2643
0
    if (!fields->print_header) {
2644
0
        return;
2645
0
    }
2646
2647
0
    for(i = 0; i < fields->fields->len; ++i) {
2648
0
        const char* field = (const char *)g_ptr_array_index(fields->fields,i);
2649
0
        if (i != 0 ) {
2650
0
            fputc(fields->separator, fh);
2651
0
        }
2652
0
        fputs(field, fh);
2653
0
    }
2654
0
    fputc('\n', fh);
2655
0
}
2656
2657
static void format_field_values(output_fields_t* fields, void *field_index, char* value)
2658
0
{
2659
0
    unsigned   indx;
2660
0
    GPtrArray* fv_p;
2661
2662
0
    if (NULL == value)
2663
0
        return;
2664
2665
    /* Unwrap change made to disambiguate zero / null */
2666
0
    indx = GPOINTER_TO_UINT(field_index) - 1;
2667
2668
0
    if (fields->field_values[indx] == NULL) {
2669
0
        fields->field_values[indx] = g_ptr_array_new_with_free_func(g_free);
2670
0
    }
2671
2672
    /* Essentially: fieldvalues[indx] is a 'GPtrArray *' with each array entry */
2673
    /*  pointing to a string which is (part of) the final output string.       */
2674
2675
0
    fv_p = fields->field_values[indx];
2676
2677
0
    switch (fields->occurrence) {
2678
0
    case 'f':
2679
        /* print the value of only the first occurrence of the field */
2680
0
        if (g_ptr_array_len(fv_p) != 0) {
2681
            /*
2682
             * This isn't the first occurrence, so the value won't be used;
2683
             * free it.
2684
             */
2685
0
            g_free(value);
2686
0
            return;
2687
0
        }
2688
0
        break;
2689
0
    case 'l':
2690
        /* print the value of only the last occurrence of the field */
2691
0
        if (g_ptr_array_len(fv_p) != 0) {
2692
            /*
2693
             * This isn't the first occurrence, so there's already a
2694
             * value in the array, which won't be used; remove the
2695
             * first (only) element in the array (which will free it,
2696
             * as we created the GPtrArray with a free func) -
2697
             * this value will replace it.
2698
             */
2699
0
            g_ptr_array_set_size(fv_p, 0);
2700
0
        }
2701
0
        break;
2702
0
    case 'a':
2703
        /* print the value of all occurrences of the field */
2704
0
        break;
2705
0
    default:
2706
0
        ws_assert_not_reached();
2707
0
        break;
2708
0
    }
2709
2710
0
    g_ptr_array_add(fv_p, (void *)value);
2711
0
}
2712
2713
static void proto_tree_get_node_field_values(proto_node *node, void *data)
2714
0
{
2715
0
    write_field_data_t *call_data;
2716
0
    field_info *fi;
2717
0
    void *      field_index;
2718
2719
0
    call_data = (write_field_data_t *)data;
2720
0
    fi = PNODE_FINFO(node);
2721
2722
    /* check for a faked item with an invisible tree */
2723
0
    if (fi) {
2724
0
        field_index = g_hash_table_lookup(call_data->fields->field_indicies, fi->hfinfo->abbrev);
2725
0
        if (NULL != field_index) {
2726
0
            format_field_values(call_data->fields, field_index,
2727
0
                                get_node_field_value(fi, call_data->edt) /* g_ alloc'd string */
2728
0
                );
2729
0
        }
2730
0
    }
2731
2732
    /* Recurse here. */
2733
0
    if (node->first_child != NULL) {
2734
0
        proto_tree_children_foreach(node, proto_tree_get_node_field_values,
2735
0
                                    call_data);
2736
0
    }
2737
0
}
2738
2739
/* --- Message-instance split mode --- */
2740
2741
typedef struct {
2742
    output_fields_t *fields;
2743
    epan_dissect_t  *edt;
2744
    const char      *split_proto;    /* protocol abbreviation to split on */
2745
    GPtrArray       *instance_nodes; /* array of proto_node* for each message instance */
2746
    /* Per-instance field values: array of (GPtrArray **), each entry is
2747
     * an array[num_fields] of GPtrArray* (same layout as fields->field_values) */
2748
    GPtrArray       *instance_values;
2749
    GPtrArray      **global_values;  /* values for fields not under any split instance */
2750
    unsigned         num_fields;
2751
} split_field_data_t;
2752
2753
/* Find the nearest ancestor node that is one of the split instance nodes */
2754
static int find_split_instance_index(proto_node *node, split_field_data_t *sdata)
2755
0
{
2756
0
    for (proto_node *p = node->parent; p != NULL; p = p->parent) {
2757
0
        for (unsigned k = 0; k < sdata->instance_nodes->len; k++) {
2758
0
            if (g_ptr_array_index(sdata->instance_nodes, k) == p) {
2759
0
                return (int)k;
2760
0
            }
2761
0
        }
2762
0
    }
2763
0
    return -1;  /* not under any split instance */
2764
0
}
2765
2766
/* Collect split protocol instance nodes by iterating the tree */
2767
static void find_split_protocol_nodes(proto_node *node, const char *split_proto, GPtrArray *instances)
2768
0
{
2769
0
    GPtrArray *stack = g_ptr_array_new();
2770
0
    g_ptr_array_add(stack, node);
2771
2772
0
    while (stack->len > 0) {
2773
0
        proto_node *cur = (proto_node *)g_ptr_array_index(stack, stack->len - 1);
2774
0
        g_ptr_array_set_size(stack, stack->len - 1);
2775
2776
0
        for (proto_node *child = cur->first_child; child != NULL; child = child->next) {
2777
0
            if (child->hfinfo && child->hfinfo->type == FT_PROTOCOL &&
2778
0
                strcmp(child->hfinfo->abbrev, split_proto) == 0) {
2779
0
                g_ptr_array_add(instances, child);
2780
0
            } else {
2781
0
                g_ptr_array_add(stack, child);
2782
0
            }
2783
0
        }
2784
0
    }
2785
0
    g_ptr_array_free(stack, true);
2786
0
}
2787
2788
/* Recursively collect field values bucketed by message instance */
2789
static void proto_tree_get_node_field_values_split(proto_node *node, void *data)
2790
0
{
2791
0
    split_field_data_t *sdata = (split_field_data_t *)data;
2792
0
    field_info *fi = PNODE_FINFO(node);
2793
2794
0
    if (fi) {
2795
0
        void *field_index = g_hash_table_lookup(sdata->fields->field_indicies, fi->hfinfo->abbrev);
2796
0
        if (NULL != field_index) {
2797
0
            unsigned indx = GPOINTER_TO_UINT(field_index) - 1;
2798
0
            char *value = get_node_field_value(fi, sdata->edt);
2799
0
            if (value) {
2800
0
                int inst = find_split_instance_index(node, sdata);
2801
0
                GPtrArray **bucket;
2802
0
                if (inst >= 0) {
2803
0
                    bucket = (GPtrArray **)g_ptr_array_index(sdata->instance_values, inst);
2804
0
                } else {
2805
0
                    bucket = sdata->global_values;
2806
0
                }
2807
0
                if (bucket[indx] == NULL) {
2808
0
                    bucket[indx] = g_ptr_array_new_with_free_func(g_free);
2809
0
                }
2810
0
                g_ptr_array_add(bucket[indx], value);
2811
0
            }
2812
0
        }
2813
0
    }
2814
2815
0
    if (node->first_child != NULL) {
2816
0
        proto_tree_children_foreach(node, proto_tree_get_node_field_values_split, data);
2817
0
    }
2818
0
}
2819
2820
/* Output one CSV row from a field_values bucket */
2821
static void output_csv_row(output_fields_t *fields, GPtrArray **bucket,
2822
                           GPtrArray **global_values, FILE *fh)
2823
0
{
2824
0
    for (unsigned i = 0; i < fields->fields->len; ++i) {
2825
0
        if (0 != i) {
2826
0
            fputc(fields->separator, fh);
2827
0
        }
2828
        /* Use instance-specific value if present, else global */
2829
0
        GPtrArray *fv_p = bucket ? bucket[i] : NULL;
2830
0
        if (fv_p == NULL)
2831
0
            fv_p = global_values ? global_values[i] : NULL;
2832
0
        if (fv_p != NULL && g_ptr_array_len(fv_p) != 0) {
2833
0
            wmem_strbuf_t *buf = wmem_strbuf_new(NULL, g_ptr_array_index(fv_p, 0));
2834
0
            for (size_t j = 1; j < g_ptr_array_len(fv_p); j++) {
2835
0
                wmem_strbuf_append_c(buf, fields->aggregator);
2836
0
                wmem_strbuf_append(buf, (char *)g_ptr_array_index(fv_p, j));
2837
0
            }
2838
0
            print_escaped_csv(fh, wmem_strbuf_get_str(buf), fields->separator, fields->quote, fields->escape);
2839
0
            wmem_strbuf_destroy(buf);
2840
0
        }
2841
0
    }
2842
0
}
2843
2844
/* Write fields in split mode: one CSV row per message instance */
2845
static void write_split_fields_csv(output_fields_t *fields, epan_dissect_t *edt, FILE *fh)
2846
0
{
2847
0
    unsigned num_fields = fields->fields->len;
2848
2849
    /* Find all instances of the split protocol */
2850
0
    split_field_data_t sdata;
2851
0
    sdata.fields = fields;
2852
0
    sdata.edt = edt;
2853
0
    sdata.split_proto = fields->split_by;
2854
0
    sdata.instance_nodes = g_ptr_array_new();
2855
0
    sdata.instance_values = g_ptr_array_new();
2856
0
    sdata.global_values = g_new0(GPtrArray*, num_fields);
2857
0
    sdata.num_fields = num_fields;
2858
2859
0
    find_split_protocol_nodes(edt->tree, sdata.split_proto, sdata.instance_nodes);
2860
2861
    /* Allocate per-instance value arrays */
2862
0
    for (unsigned k = 0; k < sdata.instance_nodes->len; k++) {
2863
0
        GPtrArray **inst = g_new0(GPtrArray*, num_fields);
2864
0
        g_ptr_array_add(sdata.instance_values, inst);
2865
0
    }
2866
2867
    /* Collect field values */
2868
0
    proto_tree_children_foreach(edt->tree, proto_tree_get_node_field_values_split, &sdata);
2869
2870
    /* Output rows */
2871
0
    if (sdata.instance_nodes->len == 0) {
2872
        /* No split instances found, output global values as a single row */
2873
0
        output_csv_row(fields, NULL, sdata.global_values, fh);
2874
0
        fputc('\n', fh);
2875
0
    } else {
2876
0
        for (unsigned k = 0; k < sdata.instance_nodes->len; k++) {
2877
0
            GPtrArray **bucket = (GPtrArray **)g_ptr_array_index(sdata.instance_values, k);
2878
0
            output_csv_row(fields, bucket, sdata.global_values, fh);
2879
0
            fputc('\n', fh);
2880
            /* Free instance bucket */
2881
0
            for (unsigned i = 0; i < num_fields; i++) {
2882
0
                if (bucket[i]) {
2883
0
                    g_ptr_array_free(bucket[i], true);
2884
0
                }
2885
0
            }
2886
0
            g_free(bucket);
2887
0
        }
2888
0
    }
2889
2890
    /* Free global values */
2891
0
    for (unsigned i = 0; i < num_fields; i++) {
2892
0
        if (sdata.global_values[i]) {
2893
0
            g_ptr_array_free(sdata.global_values[i], true);
2894
0
        }
2895
0
    }
2896
0
    g_free(sdata.global_values);
2897
0
    g_ptr_array_free(sdata.instance_nodes, true);
2898
0
    g_ptr_array_free(sdata.instance_values, true);
2899
0
}
2900
2901
2902
static void write_specified_fields(fields_format format, output_fields_t *fields, epan_dissect_t *edt, column_info *cinfo _U_, FILE *fh, json_dumper *dumper)
2903
0
{
2904
0
    unsigned    i;
2905
2906
0
    write_field_data_t data;
2907
2908
0
    ws_assert(fields);
2909
0
    ws_assert(fields->fields);
2910
0
    ws_assert(edt);
2911
    /* JSON formats must go through json_dumper */
2912
0
    if (format == FORMAT_JSON || format == FORMAT_EK) {
2913
0
        ws_assert(!fh && dumper);
2914
0
    } else {
2915
0
        ws_assert(fh && !dumper);
2916
0
    }
2917
2918
0
    data.fields = fields;
2919
0
    data.edt = edt;
2920
2921
0
    if (NULL == fields->field_indicies) {
2922
        /* Prepare a lookup table from string abbreviation for field to its index. */
2923
0
        fields->field_indicies = g_hash_table_new(g_str_hash, g_str_equal);
2924
2925
0
        i = 0;
2926
0
        while (i < fields->fields->len) {
2927
0
            char *field = (char *)g_ptr_array_index(fields->fields, i);
2928
            /* Store field indicies +1 so that zero is not a valid value,
2929
             * and can be distinguished from NULL as a pointer.
2930
             */
2931
0
            ++i;
2932
0
            if (proto_registrar_get_byname(field)) {
2933
0
                g_hash_table_insert(fields->field_indicies, field, GUINT_TO_POINTER(i));
2934
0
            }
2935
0
        }
2936
0
    }
2937
2938
    /* Split mode: one row per message instance */
2939
0
    if (fields->split_by && format == FORMAT_CSV) {
2940
0
        write_split_fields_csv(fields, edt, fh);
2941
0
        return;
2942
0
    }
2943
2944
    /* Array buffer to store values for this packet              */
2945
    /*  Allocate an array for the 'GPtrarray *' the first time   */
2946
    /*   ths function is invoked for a file;                     */
2947
    /*  Any and all 'GPtrArray *' are freed (after use) each     */
2948
    /*   time (each packet) this function is invoked for a flle. */
2949
    /* XXX: ToDo: use packet-scope'd memory & (if/when implemented) wmem ptr_array */
2950
0
    if (NULL == fields->field_values)
2951
0
        fields->field_values = g_new0(GPtrArray*, fields->fields->len);  /* free'd in output_fields_free() */
2952
2953
0
    i = 0;
2954
0
    while(i < fields->fields->len) {
2955
0
        dfilter_t *dfilter = (dfilter_t *)g_ptr_array_index(fields->field_dfilters, i);
2956
2957
        /* Match how the field indices are treated. */
2958
0
        ++i;
2959
2960
0
        if (dfilter != NULL) {
2961
0
            GPtrArray *fvals = NULL;
2962
0
            bool passed = dfilter_apply_full(dfilter, edt->tree, &fvals);
2963
0
            char *str;
2964
0
            if (fvals != NULL) {
2965
0
                int len = g_ptr_array_len(fvals);
2966
0
                for (int j = 0; j < len; ++j) {
2967
0
                    str = fvalue_to_string_repr(NULL, fvals->pdata[j], FTREPR_DISPLAY, BASE_NONE);
2968
0
                    format_field_values(fields, GUINT_TO_POINTER(i), str);
2969
0
                }
2970
0
                g_ptr_array_unref(fvals);
2971
0
            } else if (passed) {
2972
                /* XXX - Should this be "1" (and "0" for !passed) like with
2973
                 * FT_NONE fields, or a check mark / nothing like the GUI ? */
2974
                //str = g_strdup("1");
2975
0
                str = g_strdup(UTF8_CHECK_MARK);
2976
0
                format_field_values(fields, GUINT_TO_POINTER(i), str);
2977
0
            }
2978
0
        }
2979
0
    }
2980
2981
0
    proto_tree_children_foreach(edt->tree, proto_tree_get_node_field_values,
2982
0
                                &data);
2983
2984
0
    switch (format) {
2985
0
    case FORMAT_CSV:
2986
0
        for(i = 0; i < fields->fields->len; ++i) {
2987
0
            if (0 != i) {
2988
0
                fputc(fields->separator, fh);
2989
0
            }
2990
0
            if (NULL != fields->field_values[i]) {
2991
0
                GPtrArray *fv_p;
2992
0
                size_t j;
2993
0
                fv_p = fields->field_values[i];
2994
2995
                /* Output the array of (partial) field values */
2996
0
                if (g_ptr_array_len(fv_p) != 0) {
2997
0
                    wmem_strbuf_t *buf = wmem_strbuf_new(NULL, g_ptr_array_index(fv_p, 0));
2998
0
                    for (j = 1; j < g_ptr_array_len(fv_p); j++ ) {
2999
0
                        wmem_strbuf_append_c(buf, fields->aggregator);
3000
0
                        wmem_strbuf_append(buf, (char *)g_ptr_array_index(fv_p, j));
3001
0
                    }
3002
0
                    print_escaped_csv(fh, wmem_strbuf_get_str(buf), fields->separator, fields->quote, fields->escape);
3003
0
                    wmem_strbuf_destroy(buf);
3004
0
                }
3005
0
                g_ptr_array_free(fv_p, true);  /* get ready for the next packet */
3006
0
                fields->field_values[i] = NULL;
3007
0
            }
3008
0
        }
3009
0
        break;
3010
0
    case FORMAT_XML:
3011
0
        for(i = 0; i < fields->fields->len; ++i) {
3012
0
            char *field = (char *)g_ptr_array_index(fields->fields, i);
3013
3014
0
            if (NULL != fields->field_values[i]) {
3015
0
                GPtrArray *fv_p;
3016
0
                char * str;
3017
0
                size_t j;
3018
0
                fv_p = fields->field_values[i];
3019
3020
                /* Output the array of (partial) field values */
3021
0
                for (j = 0; j < (g_ptr_array_len(fv_p)); j++ ) {
3022
0
                    str = (char *)g_ptr_array_index(fv_p, j);
3023
3024
0
                    fprintf(fh, "  <field name=\"%s\" value=", field);
3025
0
                    fputs("\"", fh);
3026
0
                    print_escaped_xml(fh, str);
3027
0
                    fputs("\"/>\n", fh);
3028
0
                }
3029
0
                g_ptr_array_free(fv_p, true);  /* get ready for the next packet */
3030
0
                fields->field_values[i] = NULL;
3031
0
            }
3032
0
        }
3033
0
        break;
3034
0
    case FORMAT_JSON:
3035
0
        json_dumper_begin_object(dumper);
3036
0
        for(i = 0; i < fields->fields->len; ++i) {
3037
0
            char *field = (char *)g_ptr_array_index(fields->fields, i);
3038
3039
0
            if (NULL != fields->field_values[i]) {
3040
0
                GPtrArray *fv_p;
3041
0
                char * str;
3042
0
                size_t j;
3043
0
                fv_p = fields->field_values[i];
3044
3045
0
                json_dumper_set_member_name(dumper, field);
3046
0
                json_dumper_begin_array(dumper);
3047
3048
                /* Output the array of (partial) field values */
3049
0
                for (j = 0; j < (g_ptr_array_len(fv_p)); j++ ) {
3050
0
                    str = (char *) g_ptr_array_index(fv_p, j);
3051
0
                    json_dumper_value_string(dumper, str);
3052
0
                }
3053
3054
0
                json_dumper_end_array(dumper);
3055
3056
0
                g_ptr_array_free(fv_p, true);  /* get ready for the next packet */
3057
0
                fields->field_values[i] = NULL;
3058
0
            }
3059
0
        }
3060
0
        json_dumper_end_object(dumper);
3061
0
        break;
3062
0
    case FORMAT_EK:
3063
0
        for(i = 0; i < fields->fields->len; ++i) {
3064
0
            char *field = (char *)g_ptr_array_index(fields->fields, i);
3065
3066
0
            if (NULL != fields->field_values[i]) {
3067
0
                GPtrArray *fv_p;
3068
0
                char * str;
3069
0
                size_t j;
3070
0
                fv_p = fields->field_values[i];
3071
3072
0
                json_dumper_set_member_name(dumper, field);
3073
0
                json_dumper_begin_array(dumper);
3074
3075
                /* Output the array of (partial) field values */
3076
0
                for (j = 0; j < (g_ptr_array_len(fv_p)); j++ ) {
3077
0
                    str = (char *)g_ptr_array_index(fv_p, j);
3078
0
                    json_dumper_value_string(dumper, str);
3079
0
                }
3080
3081
0
                json_dumper_end_array(dumper);
3082
3083
0
                g_ptr_array_free(fv_p, true);  /* get ready for the next packet */
3084
0
                fields->field_values[i] = NULL;
3085
0
            }
3086
0
        }
3087
0
        break;
3088
3089
0
    default:
3090
0
        fprintf(stderr, "Unknown fields format %d\n", format);
3091
0
        ws_assert_not_reached();
3092
0
        break;
3093
0
    }
3094
0
}
3095
3096
void write_fields_finale(output_fields_t* fields _U_ , FILE *fh _U_)
3097
0
{
3098
    /* Nothing to do */
3099
0
}
3100
3101
/* Returns an g_malloced string */
3102
char* get_node_field_value(field_info* fi, epan_dissect_t* edt)
3103
0
{
3104
0
    if (fi->hfinfo->id == hf_text_only) {
3105
        /* Text label.
3106
         * Get the text */
3107
0
        if (fi->rep) {
3108
0
            return g_strdup(fi->rep->representation);
3109
0
        }
3110
0
        else {
3111
0
            return get_field_hex_value(edt->pi.data_src, fi);
3112
0
        }
3113
0
    }
3114
0
    else if (fi->hfinfo->id == proto_data) {
3115
        /* Uninterpreted data, i.e., the "Data" protocol, is
3116
         * printed as a field instead of a protocol. */
3117
0
        return get_field_hex_value(edt->pi.data_src, fi);
3118
0
    }
3119
0
    else {
3120
        /* Normal protocols and fields */
3121
0
        char       *dfilter_string;
3122
3123
0
        switch (fi->hfinfo->type)
3124
0
        {
3125
0
        case FT_PROTOCOL:
3126
            /* Print out the full details for the protocol. */
3127
0
            if (fi->rep) {
3128
0
                return g_strdup(fi->rep->representation);
3129
0
            } else {
3130
                /* Just print out the protocol abbreviation */
3131
0
                return g_strdup(fi->hfinfo->abbrev);
3132
0
            }
3133
0
        case FT_NONE:
3134
            /* Return "1" so that the presence of a field of type
3135
             * FT_NONE can be checked when using -T fields */
3136
0
            return g_strdup("1");
3137
0
        case FT_UINT_BYTES:
3138
0
        case FT_BYTES:
3139
0
            {
3140
0
                char *ret;
3141
0
                const uint8_t *bytes = fvalue_get_bytes_data(fi->value);
3142
0
                if (bytes) {
3143
0
                    dfilter_string = (char *)wmem_alloc(NULL, 3*fvalue_length2(fi->value));
3144
0
                    switch (fi->hfinfo->display) {
3145
0
                    case SEP_DOT:
3146
0
                        ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length2(fi->value), '.');
3147
0
                        break;
3148
0
                    case SEP_DASH:
3149
0
                        ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length2(fi->value), '-');
3150
0
                        break;
3151
0
                    case SEP_COLON:
3152
0
                        ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length2(fi->value), ':');
3153
0
                        break;
3154
0
                    case SEP_SPACE:
3155
0
                        ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length2(fi->value), ' ');
3156
0
                        break;
3157
0
                    case BASE_NONE:
3158
0
                    default:
3159
0
                        ret = bytes_to_hexstr(dfilter_string, bytes, fvalue_length2(fi->value));
3160
0
                        break;
3161
0
                    }
3162
0
                    *ret = '\0';
3163
0
                    ret = g_strdup(dfilter_string);
3164
0
                    wmem_free(NULL, dfilter_string);
3165
0
                } else {
3166
0
                    if (fi->hfinfo->display & BASE_ALLOW_ZERO) {
3167
0
                        ret = g_strdup("<none>");
3168
0
                    } else {
3169
0
                        ret = g_strdup("<MISSING>");
3170
0
                    }
3171
0
                }
3172
0
                return ret;
3173
0
            }
3174
0
            break;
3175
0
        default:
3176
0
            dfilter_string = fvalue_to_string_repr(NULL, fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
3177
0
            if (dfilter_string != NULL) {
3178
0
                char* ret = g_strdup(dfilter_string);
3179
0
                wmem_free(NULL, dfilter_string);
3180
0
                return ret;
3181
0
            } else {
3182
0
                return get_field_hex_value(edt->pi.data_src, fi);
3183
0
            }
3184
0
        }
3185
0
    }
3186
0
}
3187
3188
static char*
3189
get_field_hex_value(GSList *src_list, field_info *fi)
3190
0
{
3191
0
    const uint8_t *pd;
3192
3193
0
    if (!fi->ds_tvb)
3194
0
        return NULL;
3195
3196
0
    if (fi->length > (unsigned)tvb_captured_length_remaining(fi->ds_tvb, fi->start)) {
3197
0
        return g_strdup("field length invalid!");
3198
0
    }
3199
3200
    /* Find the data for this field. */
3201
0
    pd = get_field_data(src_list, fi);
3202
3203
0
    if (pd) {
3204
0
        unsigned   i;
3205
0
        char      *buffer;
3206
0
        char      *p;
3207
0
        unsigned   len;
3208
0
        const int  chars_per_byte = 2;
3209
3210
0
        len    = chars_per_byte * fi->length;
3211
0
        buffer = (char *)g_malloc(sizeof(char)*(len + 1));
3212
0
        buffer[len] = '\0'; /* Ensure NULL termination in bad cases */
3213
0
        p = buffer;
3214
        /* Print a simple hex dump */
3215
0
        for (i = 0 ; i < fi->length; i++) {
3216
0
            snprintf(p, chars_per_byte+1, "%02x", pd[i]);
3217
0
            p += chars_per_byte;
3218
0
        }
3219
0
        return buffer;
3220
0
    } else {
3221
0
        return NULL;
3222
0
    }
3223
0
}
3224
3225
output_fields_t* output_fields_new(void)
3226
0
{
3227
0
    output_fields_t* fields     = g_new(output_fields_t, 1);
3228
0
    fields->print_bom           = false;
3229
0
    fields->print_header        = false;
3230
0
    fields->separator           = '\t';
3231
0
    fields->occurrence          = 'a';
3232
0
    fields->aggregator          = ',';
3233
0
    fields->fields              = NULL; /*Do lazy initialisation */
3234
0
    fields->field_dfilters      = NULL;
3235
0
    fields->field_indicies      = NULL;
3236
0
    fields->field_values        = NULL;
3237
0
    fields->protocolfilter      = NULL;
3238
0
    fields->quote               ='\0';
3239
0
    fields->escape              = true;
3240
0
    fields->includes_col_fields = false;
3241
0
    fields->split_by            = NULL;
3242
0
    return fields;
3243
0
}
3244
3245
bool output_fields_has_split(output_fields_t* fields)
3246
0
{
3247
    return fields->split_by != NULL;
3248
0
}
3249
3250
/*
3251
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
3252
 *
3253
 * Local variables:
3254
 * c-basic-offset: 4
3255
 * tab-width: 8
3256
 * indent-tabs-mode: nil
3257
 * End:
3258
 *
3259
 * vi: set shiftwidth=4 tabstop=8 expandtab:
3260
 * :indentSize=4:tabSize=8:noTabs=true:
3261
 */