packet-ldap: fix regression for SASL handling
[wireshark-sm.git] / epan / print.c
blobd7ae606564c6959b8781642219bc2f005dedf166
1 /* print.c
2 * Routines for printing packet analysis trees.
4 * Gilbert Ramirez <gram@alumni.rice.edu>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include "config.h"
15 #include <stdio.h>
16 #include <string.h>
18 #include <epan/packet.h>
19 #include <epan/epan.h>
20 #include <epan/epan_dissect.h>
21 #include <epan/to_str.h>
22 #include <epan/to_str-int.h>
23 #include <epan/expert.h>
24 #include <epan/column.h>
25 #include <epan/column-info.h>
26 #include <epan/color_filters.h>
27 #include <epan/prefs.h>
28 #include <epan/print.h>
29 #include <epan/charsets.h>
30 #include <wsutil/json_dumper.h>
31 #include <wsutil/filesystem.h>
32 #include <version_info.h>
33 #include <wsutil/utf8_entities.h>
34 #include <ftypes/ftypes-int.h>
36 #define PDML_VERSION "0"
37 #define PSML_VERSION "0"
39 typedef struct {
40 int level;
41 print_stream_t *stream;
42 gboolean success;
43 GSList *src_list;
44 print_dissections_e print_dissections;
45 gboolean print_hex_for_data;
46 packet_char_enc encoding;
47 GHashTable *output_only_tables; /* output only these protocols */
48 } print_data;
50 typedef struct {
51 int level;
52 FILE *fh;
53 GSList *src_list;
54 gchar **filter;
55 pf_flags filter_flags;
56 } write_pdml_data;
58 typedef struct {
59 GSList *src_list;
60 gchar **filter;
61 pf_flags filter_flags;
62 gboolean print_hex;
63 gboolean print_text;
64 proto_node_children_grouper_func node_children_grouper;
65 json_dumper *dumper;
66 } write_json_data;
68 typedef struct {
69 output_fields_t *fields;
70 epan_dissect_t *edt;
71 } write_field_data_t;
73 struct _output_fields {
74 gboolean print_bom;
75 gboolean print_header;
76 gchar separator;
77 gchar occurrence;
78 gchar aggregator;
79 GPtrArray *fields;
80 GHashTable *field_indicies;
81 GPtrArray **field_values;
82 gchar quote;
83 gboolean includes_col_fields;
86 static gchar *get_field_hex_value(GSList *src_list, field_info *fi);
87 static void proto_tree_print_node(proto_node *node, gpointer data);
88 static void proto_tree_write_node_pdml(proto_node *node, gpointer data);
89 static void proto_tree_write_node_ek(proto_node *node, write_json_data *data);
90 static const guint8 *get_field_data(GSList *src_list, field_info *fi);
91 static void pdml_write_field_hex_value(write_pdml_data *pdata, field_info *fi);
92 static void json_write_field_hex_value(write_json_data *pdata, field_info *fi);
93 static gboolean print_hex_data_buffer(print_stream_t *stream, const guchar *cp,
94 guint length, packet_char_enc encoding);
95 static void write_specified_fields(fields_format format,
96 output_fields_t *fields,
97 epan_dissect_t *edt, column_info *cinfo,
98 FILE *fh,
99 json_dumper *dumper);
100 static void print_escaped_xml(FILE *fh, const char *unescaped_string);
101 static void print_escaped_csv(FILE *fh, const char *unescaped_string);
103 typedef void (*proto_node_value_writer)(proto_node *, write_json_data *);
104 static void write_json_index(json_dumper *dumper, epan_dissect_t *edt);
105 static void write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *data);
106 static void write_json_proto_node(GSList *node_values_head,
107 const char *suffix,
108 proto_node_value_writer value_writer,
109 write_json_data *data);
110 static void write_json_proto_node_value_list(GSList *node_values_head,
111 proto_node_value_writer value_writer,
112 write_json_data *data);
113 static void write_json_proto_node_filtered(proto_node *node, write_json_data *data);
114 static void write_json_proto_node_hex_dump(proto_node *node, write_json_data *data);
115 static void write_json_proto_node_children(proto_node *node, write_json_data *data);
116 static void write_json_proto_node_value(proto_node *node, write_json_data *data);
117 static void write_json_proto_node_no_value(proto_node *node, write_json_data *data);
118 static const char *proto_node_to_json_key(proto_node *node);
120 static void print_pdml_geninfo(epan_dissect_t *edt, FILE *fh);
121 static void write_ek_summary(column_info *cinfo, write_json_data *pdata);
123 static void proto_tree_get_node_field_values(proto_node *node, gpointer data);
125 /* Cache the protocols and field handles that the print functionality needs
126 This helps break explicit dependency on the dissectors. */
127 static int proto_data = -1;
128 static int proto_frame = -1;
130 void print_cache_field_handles(void)
132 proto_data = proto_get_id_by_short_name("Data");
133 proto_frame = proto_get_id_by_short_name("Frame");
136 gboolean
137 proto_tree_print(print_dissections_e print_dissections, gboolean print_hex,
138 epan_dissect_t *edt, GHashTable *output_only_tables,
139 print_stream_t *stream)
141 print_data data;
143 /* Create the output */
144 data.level = 0;
145 data.stream = stream;
146 data.success = TRUE;
147 data.src_list = edt->pi.data_src;
148 data.encoding = (packet_char_enc)edt->pi.fd->encoding;
149 data.print_dissections = print_dissections;
150 /* If we're printing the entire packet in hex, don't
151 print uninterpreted data fields in hex as well. */
152 data.print_hex_for_data = !print_hex;
153 data.output_only_tables = output_only_tables;
155 proto_tree_children_foreach(edt->tree, proto_tree_print_node, &data);
156 return data.success;
159 /* Print a tree's data, and any child nodes. */
160 static void
161 proto_tree_print_node(proto_node *node, gpointer data)
163 field_info *fi = PNODE_FINFO(node);
164 print_data *pdata = (print_data*) data;
165 const guint8 *pd;
166 gchar label_str[ITEM_LABEL_LENGTH];
167 gchar *label_ptr;
169 /* dissection with an invisible proto tree? */
170 g_assert(fi);
172 /* Don't print invisible entries. */
173 if (proto_item_is_hidden(node) && (prefs.display_hidden_proto_items == FALSE))
174 return;
176 /* Give up if we've already gotten an error. */
177 if (!pdata->success)
178 return;
180 /* was a free format label produced? */
181 if (fi->rep) {
182 label_ptr = fi->rep->representation;
184 else { /* no, make a generic label */
185 label_ptr = label_str;
186 proto_item_fill_label(fi, label_str);
189 if (proto_item_is_generated(node))
190 label_ptr = g_strconcat("[", label_ptr, "]", NULL);
192 pdata->success = print_line(pdata->stream, pdata->level, label_ptr);
194 if (proto_item_is_generated(node))
195 g_free(label_ptr);
197 if (!pdata->success)
198 return;
201 * If -O is specified, only display the protocols which are in the
202 * lookup table. Only check on the first level: once we start printing
203 * a tree, print the rest of the subtree. Otherwise we won't print
204 * subitems whose abbreviation doesn't match the protocol--for example
205 * text items (whose abbreviation is simply "text").
207 if ((pdata->output_only_tables != NULL) && (pdata->level == 0)
208 && (g_hash_table_lookup(pdata->output_only_tables, fi->hfinfo->abbrev) == NULL)) {
209 return;
212 /* If it's uninterpreted data, dump it (unless our caller will
213 be printing the entire packet in hex). */
214 if ((fi->hfinfo->id == proto_data) && (pdata->print_hex_for_data)) {
216 * Find the data for this field.
218 pd = get_field_data(pdata->src_list, fi);
219 if (pd) {
220 if (!print_line(pdata->stream, 0, "")) {
221 pdata->success = FALSE;
222 return;
224 if (!print_hex_data_buffer(pdata->stream, pd,
225 fi->length, pdata->encoding)) {
226 pdata->success = FALSE;
227 return;
232 /* If we're printing all levels, or if this node is one with a
233 subtree and its subtree is expanded, recurse into the subtree,
234 if it exists. */
235 g_assert((fi->tree_type >= -1) && (fi->tree_type < num_tree_types));
236 if ((pdata->print_dissections == print_dissections_expanded) ||
237 ((pdata->print_dissections == print_dissections_as_displayed) &&
238 (fi->tree_type >= 0) && tree_expanded(fi->tree_type))) {
239 if (node->first_child != NULL) {
240 pdata->level++;
241 proto_tree_children_foreach(node,
242 proto_tree_print_node, pdata);
243 pdata->level--;
244 if (!pdata->success)
245 return;
250 #define PDML2HTML_XSL "pdml2html.xsl"
251 void
252 write_pdml_preamble(FILE *fh, const gchar *filename)
254 time_t t = time(NULL);
255 struct tm * timeinfo;
256 char *fmt_ts;
257 const char *ts;
259 /* Create the output */
260 timeinfo = localtime(&t);
261 if (timeinfo != NULL) {
262 fmt_ts = asctime(timeinfo);
263 fmt_ts[strlen(fmt_ts)-1] = 0; /* overwrite \n */
264 ts = fmt_ts;
265 } else
266 ts = "Not representable";
268 fprintf(fh, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
269 fprintf(fh, "<?xml-stylesheet type=\"text/xsl\" href=\"" PDML2HTML_XSL "\"?>\n");
270 fprintf(fh, "<!-- You can find " PDML2HTML_XSL " in %s or at https://gitlab.com/wireshark/wireshark/-/raw/master/" PDML2HTML_XSL ". -->\n", get_datafile_dir());
271 fprintf(fh, "<pdml version=\"" PDML_VERSION "\" creator=\"%s/%s\" time=\"%s\" capture_file=\"", PACKAGE, VERSION, ts);
272 if (filename) {
273 /* \todo filename should be converted to UTF-8. */
274 print_escaped_xml(fh, filename);
276 fprintf(fh, "\">\n");
279 /* Check if the str match the protocolfilter. json_filter is space
280 delimited string and str need to exact-match to one of the value. */
281 static gboolean check_protocolfilter(gchar **protocolfilter, const char *str)
283 gboolean res = FALSE;
284 gchar **ptr;
286 if (str == NULL || protocolfilter == NULL) {
287 return FALSE;
290 for (ptr = protocolfilter; *ptr; ptr++) {
291 if (strcmp(*ptr, str) == 0) {
292 res = TRUE;
293 break;
297 return res;
300 void
301 write_pdml_proto_tree(output_fields_t* fields, gchar **protocolfilter, pf_flags protocolfilter_flags, epan_dissect_t *edt, column_info *cinfo, FILE *fh, gboolean use_color)
303 write_pdml_data data;
304 const color_filter_t *cfp;
306 g_assert(edt);
307 g_assert(fh);
309 cfp = edt->pi.fd->color_filter;
311 /* Create the output */
312 if (use_color && (cfp != NULL)) {
313 fprintf(fh, "<packet foreground='#%06x' background='#%06x'>\n",
314 color_t_to_rgb(&cfp->fg_color),
315 color_t_to_rgb(&cfp->bg_color));
316 } else {
317 fprintf(fh, "<packet>\n");
320 /* Print a "geninfo" protocol as required by PDML */
321 print_pdml_geninfo(edt, fh);
323 if (fields == NULL || fields->fields == NULL) {
324 /* Write out all fields */
325 data.level = 0;
326 data.fh = fh;
327 data.src_list = edt->pi.data_src;
328 data.filter = protocolfilter;
329 data.filter_flags = protocolfilter_flags;
331 proto_tree_children_foreach(edt->tree, proto_tree_write_node_pdml,
332 &data);
333 } else {
334 /* Write out specified fields */
335 write_specified_fields(FORMAT_XML, fields, edt, cinfo, fh, NULL);
338 fprintf(fh, "</packet>\n\n");
341 void
342 write_ek_proto_tree(output_fields_t* fields,
343 gboolean print_summary, gboolean print_hex,
344 gchar **protocolfilter,
345 pf_flags protocolfilter_flags, epan_dissect_t *edt,
346 column_info *cinfo,
347 FILE *fh)
349 g_assert(edt);
350 g_assert(fh);
352 write_json_data data;
354 json_dumper dumper = {
355 .output_file = fh,
356 .flags = JSON_DUMPER_DOT_TO_UNDERSCORE
359 data.dumper = &dumper;
361 json_dumper_begin_object(&dumper);
362 json_dumper_set_member_name(&dumper, "index");
363 json_dumper_begin_object(&dumper);
364 write_json_index(&dumper, edt);
365 json_dumper_set_member_name(&dumper, "_type");
366 json_dumper_value_string(&dumper, "doc");
367 json_dumper_end_object(&dumper);
368 json_dumper_end_object(&dumper);
369 json_dumper_finish(&dumper);
370 json_dumper_begin_object(&dumper);
372 /* Timestamp added for time indexing in Elasticsearch */
373 json_dumper_set_member_name(&dumper, "timestamp");
374 json_dumper_value_anyf(&dumper, "\"%" G_GUINT64_FORMAT "%03d\"", (guint64)edt->pi.abs_ts.secs, edt->pi.abs_ts.nsecs/1000000);
376 if (print_summary)
377 write_ek_summary(edt->pi.cinfo, &data);
379 if (edt->tree) {
380 json_dumper_set_member_name(&dumper, "layers");
381 json_dumper_begin_object(&dumper);
383 if (fields == NULL || fields->fields == NULL) {
384 /* Write out all fields */
385 data.src_list = edt->pi.data_src;
386 data.filter = protocolfilter;
387 data.filter_flags = protocolfilter_flags;
388 data.print_hex = print_hex;
389 proto_tree_write_node_ek(edt->tree, &data);
390 } else {
391 /* Write out specified fields */
392 write_specified_fields(FORMAT_EK, fields, edt, cinfo, NULL, data.dumper);
395 json_dumper_end_object(&dumper);
397 json_dumper_end_object(&dumper);
398 json_dumper_finish(&dumper);
401 void
402 write_fields_proto_tree(output_fields_t* fields, epan_dissect_t *edt, column_info *cinfo, FILE *fh)
404 g_assert(edt);
405 g_assert(fh);
407 /* Create the output */
408 write_specified_fields(FORMAT_CSV, fields, edt, cinfo, fh, NULL);
411 /* Indent to the correct level */
412 static void print_indent(int level, FILE *fh)
414 /* Use a buffer pre-filed with spaces */
415 #define MAX_INDENT 2048
416 static char spaces[MAX_INDENT];
417 static gboolean inited = FALSE;
418 if (!inited) {
419 for (int n=0; n < MAX_INDENT; n++) {
420 spaces[n] = ' ';
422 inited = TRUE;
425 if (fh == NULL) {
426 return;
429 /* Temp terminate at right length and write to fh. */
430 spaces[MIN(level*2, MAX_INDENT-1)] ='\0';
431 fputs(spaces, fh);
432 spaces[MIN(level*2, MAX_INDENT-1)] =' ';
435 /* Write out a tree's data, and any child nodes, as PDML */
436 static void
437 proto_tree_write_node_pdml(proto_node *node, gpointer data)
439 field_info *fi = PNODE_FINFO(node);
440 write_pdml_data *pdata = (write_pdml_data*) data;
441 const gchar *label_ptr;
442 gchar label_str[ITEM_LABEL_LENGTH];
443 char *dfilter_string;
444 gboolean wrap_in_fake_protocol;
446 /* dissection with an invisible proto tree? */
447 g_assert(fi);
449 /* Will wrap up top-level field items inside a fake protocol wrapper to
450 preserve the PDML schema */
451 wrap_in_fake_protocol =
452 (((fi->hfinfo->type != FT_PROTOCOL) ||
453 (fi->hfinfo->id == proto_data)) &&
454 (pdata->level == 0));
456 print_indent(pdata->level + 1, pdata->fh);
458 if (wrap_in_fake_protocol) {
459 /* Open fake protocol wrapper */
460 fputs("<proto name=\"fake-field-wrapper\">\n", pdata->fh);
461 pdata->level++;
463 print_indent(pdata->level + 1, pdata->fh);
466 /* Text label. It's printed as a field with no name. */
467 if (fi->hfinfo->id == hf_text_only) {
468 /* Get the text */
469 if (fi->rep) {
470 label_ptr = fi->rep->representation;
471 } else {
472 label_ptr = "";
475 /* Show empty name since it is a required field */
476 fputs("<field name=\"", pdata->fh);
477 fputs("\" show=\"", pdata->fh);
478 print_escaped_xml(pdata->fh, label_ptr);
480 fprintf(pdata->fh, "\" size=\"%d", fi->length);
481 if (node->parent && node->parent->finfo && (fi->start < node->parent->finfo->start)) {
482 fprintf(pdata->fh, "\" pos=\"%d", node->parent->finfo->start + fi->start);
483 } else {
484 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
487 if (fi->length > 0) {
488 fputs("\" value=\"", pdata->fh);
489 pdml_write_field_hex_value(pdata, fi);
492 if (node->first_child != NULL) {
493 fputs("\">\n", pdata->fh);
494 } else {
495 fputs("\"/>\n", pdata->fh);
499 /* Uninterpreted data, i.e., the "Data" protocol, is
500 * printed as a field instead of a protocol. */
501 else if (fi->hfinfo->id == proto_data) {
502 /* Write out field with data */
503 fputs("<field name=\"data\" value=\"", pdata->fh);
504 pdml_write_field_hex_value(pdata, fi);
505 fputs("\">\n", pdata->fh);
506 } else {
507 /* Normal protocols and fields */
508 if ((fi->hfinfo->type == FT_PROTOCOL) && (fi->hfinfo->id != proto_expert)) {
509 fputs("<proto name=\"", pdata->fh);
510 } else {
511 fputs("<field name=\"", pdata->fh);
513 print_escaped_xml(pdata->fh, fi->hfinfo->abbrev);
515 #if 0
516 /* PDML spec, see:
517 * https://wayback.archive.org/web/20150330045501/http://www.nbee.org/doku.php?id=netpdl:pdml_specification
519 * the show fields contains things in 'human readable' format
520 * showname: contains only the name of the field
521 * show: contains only the data of the field
522 * showdtl: contains additional details of the field data
523 * showmap: contains mappings of the field data (e.g. the hostname to an IP address)
525 * XXX - the showname shouldn't contain the field data itself
526 * (like it's contained in the fi->rep->representation).
527 * Unfortunately, we don't have the field data representation for
528 * all fields, so this isn't currently possible */
529 fputs("\" showname=\"", pdata->fh);
530 print_escaped_xml(pdata->fh, fi->hfinfo->name);
531 #endif
533 if (fi->rep) {
534 fputs("\" showname=\"", pdata->fh);
535 print_escaped_xml(pdata->fh, fi->rep->representation);
536 } else {
537 label_ptr = label_str;
538 proto_item_fill_label(fi, label_str);
539 fputs("\" showname=\"", pdata->fh);
540 print_escaped_xml(pdata->fh, label_ptr);
543 if (proto_item_is_hidden(node) && (prefs.display_hidden_proto_items == FALSE))
544 fprintf(pdata->fh, "\" hide=\"yes");
546 fprintf(pdata->fh, "\" size=\"%d", fi->length);
547 if (node->parent && node->parent->finfo && (fi->start < node->parent->finfo->start)) {
548 fprintf(pdata->fh, "\" pos=\"%d", node->parent->finfo->start + fi->start);
549 } else {
550 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
552 /* fprintf(pdata->fh, "\" id=\"%d", fi->hfinfo->id);*/
554 /* show, value, and unmaskedvalue attributes */
555 switch (fi->hfinfo->type)
557 case FT_PROTOCOL:
558 break;
559 case FT_NONE:
560 fputs("\" show=\"\" value=\"", pdata->fh);
561 break;
562 default:
563 dfilter_string = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
564 if (dfilter_string != NULL) {
566 fputs("\" show=\"", pdata->fh);
567 print_escaped_xml(pdata->fh, dfilter_string);
569 wmem_free(NULL, dfilter_string);
572 * XXX - should we omit "value" for any fields?
573 * What should we do for fields whose length is 0?
574 * They might come from a pseudo-header or from
575 * the capture header (e.g., time stamps), or
576 * they might be generated fields.
578 if (fi->length > 0) {
579 fputs("\" value=\"", pdata->fh);
581 if (fi->hfinfo->bitmask!=0) {
582 switch (fi->value.ftype->ftype) {
583 case FT_INT8:
584 case FT_INT16:
585 case FT_INT24:
586 case FT_INT32:
587 fprintf(pdata->fh, "%X", (guint) fvalue_get_sinteger(&fi->value));
588 break;
589 case FT_CHAR:
590 case FT_UINT8:
591 case FT_UINT16:
592 case FT_UINT24:
593 case FT_UINT32:
594 fprintf(pdata->fh, "%X", fvalue_get_uinteger(&fi->value));
595 break;
596 case FT_INT40:
597 case FT_INT48:
598 case FT_INT56:
599 case FT_INT64:
600 fprintf(pdata->fh, "%" G_GINT64_MODIFIER "X", fvalue_get_sinteger64(&fi->value));
601 break;
602 case FT_UINT40:
603 case FT_UINT48:
604 case FT_UINT56:
605 case FT_UINT64:
606 case FT_BOOLEAN:
607 fprintf(pdata->fh, "%" G_GINT64_MODIFIER "X", fvalue_get_uinteger64(&fi->value));
608 break;
609 default:
610 g_assert_not_reached();
612 fputs("\" unmaskedvalue=\"", pdata->fh);
613 pdml_write_field_hex_value(pdata, fi);
614 } else {
615 pdml_write_field_hex_value(pdata, fi);
620 if (node->first_child != NULL) {
621 fputs("\">\n", pdata->fh);
622 } else if (fi->hfinfo->id == proto_data) {
623 fputs("\">\n", pdata->fh);
624 } else {
625 fputs("\"/>\n", pdata->fh);
629 /* We print some levels for PDML. Recurse here. */
630 if (node->first_child != NULL) {
631 if (pdata->filter == NULL || check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
632 gchar **_filter = NULL;
633 /* Remove protocol filter for children, if children should be included */
634 if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
635 _filter = pdata->filter;
636 pdata->filter = NULL;
639 pdata->level++;
640 proto_tree_children_foreach(node,
641 proto_tree_write_node_pdml, pdata);
642 pdata->level--;
644 /* Put protocol filter back */
645 if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
646 pdata->filter = _filter;
648 } else {
649 print_indent(pdata->level + 2, pdata->fh);
651 /* print dummy field */
652 fputs("<field name=\"filtered\" value=\"", pdata->fh);
653 print_escaped_xml(pdata->fh, fi->hfinfo->abbrev);
654 fputs("\" />\n", pdata->fh);
658 /* Take back the extra level we added for fake wrapper protocol */
659 if (wrap_in_fake_protocol) {
660 pdata->level--;
663 if (node->first_child != NULL) {
664 print_indent(pdata->level + 1, pdata->fh);
666 /* Close off current element */
667 /* Data and expert "protocols" use simple tags */
668 if ((fi->hfinfo->id != proto_data) && (fi->hfinfo->id != proto_expert)) {
669 if (fi->hfinfo->type == FT_PROTOCOL) {
670 fputs("</proto>\n", pdata->fh);
671 } else {
672 fputs("</field>\n", pdata->fh);
674 } else {
675 fputs("</field>\n", pdata->fh);
679 /* Close off fake wrapper protocol */
680 if (wrap_in_fake_protocol) {
681 print_indent(pdata->level + 1, pdata->fh);
682 fputs("</proto>\n", pdata->fh);
686 json_dumper
687 write_json_preamble(FILE *fh)
689 json_dumper dumper = {
690 .output_file = fh,
691 .flags = JSON_DUMPER_FLAGS_PRETTY_PRINT
693 json_dumper_begin_array(&dumper);
694 return dumper;
697 void
698 write_json_finale(json_dumper *dumper)
700 json_dumper_end_array(dumper);
701 json_dumper_finish(dumper);
704 static void
705 write_json_index(json_dumper *dumper, epan_dissect_t *edt)
707 char ts[30];
708 struct tm * timeinfo;
709 gchar* str;
711 timeinfo = localtime(&edt->pi.abs_ts.secs);
712 if (timeinfo != NULL) {
713 strftime(ts, sizeof(ts), "%Y-%m-%d", timeinfo);
714 } else {
715 g_strlcpy(ts, "XXXX-XX-XX", sizeof(ts)); /* XXX - better way of saying "Not representable"? */
717 json_dumper_set_member_name(dumper, "_index");
718 str = g_strdup_printf("packets-%s", ts);
719 json_dumper_value_string(dumper, str);
720 g_free(str);
723 void
724 write_json_proto_tree(output_fields_t* fields,
725 print_dissections_e print_dissections,
726 gboolean print_hex, gchar **protocolfilter,
727 pf_flags protocolfilter_flags, epan_dissect_t *edt,
728 column_info *cinfo,
729 proto_node_children_grouper_func node_children_grouper,
730 json_dumper *dumper)
732 write_json_data data;
734 data.dumper = dumper;
736 json_dumper_begin_object(dumper);
737 write_json_index(dumper, edt);
738 json_dumper_set_member_name(dumper, "_type");
739 json_dumper_value_string(dumper, "doc");
740 json_dumper_set_member_name(dumper, "_score");
741 json_dumper_value_string(dumper, NULL);
742 json_dumper_set_member_name(dumper, "_source");
743 json_dumper_begin_object(dumper);
744 json_dumper_set_member_name(dumper, "layers");
746 if (fields == NULL || fields->fields == NULL) {
747 /* Write out all fields */
748 data.src_list = edt->pi.data_src;
749 data.filter = protocolfilter;
750 data.filter_flags = protocolfilter_flags;
751 data.print_hex = print_hex;
752 data.print_text = TRUE;
753 if (print_dissections == print_dissections_none) {
754 data.print_text = FALSE;
756 data.node_children_grouper = node_children_grouper;
758 write_json_proto_node_children(edt->tree, &data);
759 } else {
760 write_specified_fields(FORMAT_JSON, fields, edt, cinfo, NULL, dumper);
763 json_dumper_end_object(dumper);
764 json_dumper_end_object(dumper);
768 * Write a json object containing a list of key:value pairs where each key:value pair corresponds to a different json
769 * key and its associated nodes in the proto_tree.
770 * @param proto_node_list_head A 2-dimensional list containing a list of values for each different node json key. The
771 * elements themselves are a linked list of values associated with the same json key.
772 * @param pdata json writing metadata
774 static void
775 write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *pdata)
777 GSList *current_node = proto_node_list_head;
779 json_dumper_begin_object(pdata->dumper);
781 // Loop over each list of nodes (differentiated by json key) and write the associated json key:value pair in the
782 // output.
783 while (current_node != NULL) {
784 // Get the list of values for the current json key.
785 GSList *node_values_list = (GSList *) current_node->data;
787 // Retrieve the json key from the first value.
788 proto_node *first_value = (proto_node *) node_values_list->data;
789 const char *json_key = proto_node_to_json_key(first_value);
790 // Check if the current json key is filtered from the output with the "-j" cli option.
791 gboolean is_filtered = pdata->filter != NULL && !check_protocolfilter(pdata->filter, json_key);
793 field_info *fi = first_value->finfo;
794 char *value_string_repr = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
796 // We assume all values of a json key have roughly the same layout. Thus we can use the first value to derive
797 // attributes of all the values.
798 gboolean has_value = value_string_repr != NULL;
799 gboolean has_children = first_value->first_child != NULL;
800 gboolean is_pseudo_text_field = fi->hfinfo->id == 0;
802 wmem_free(NULL, value_string_repr); // fvalue_to_string_repr returns allocated buffer
804 // "-x" command line option. A "_raw" suffix is added to the json key so the textual value can be printed
805 // with the original json key. If both hex and text writing are enabled the raw information of fields whose
806 // length is equal to 0 is not written to the output. If the field is a special text pseudo field no raw
807 // information is written either.
808 if (pdata->print_hex && (!pdata->print_text || fi->length > 0) && !is_pseudo_text_field) {
809 write_json_proto_node(node_values_list, "_raw", write_json_proto_node_hex_dump, pdata);
812 if (pdata->print_text && has_value) {
813 write_json_proto_node(node_values_list, "", write_json_proto_node_value, pdata);
816 if (has_children) {
817 // If a node has both a value and a set of children we print the value and the children in separate
818 // key:value pairs. These can't have the same key so whenever a value is already printed with the node
819 // json key we print the children with the same key with a "_tree" suffix added.
820 char *suffix = has_value ? "_tree": "";
822 if (is_filtered) {
823 write_json_proto_node(node_values_list, suffix, write_json_proto_node_filtered, pdata);
824 } else {
825 // Remove protocol filter for children, if children should be included. This functionality is enabled
826 // with the "-J" command line option. We save the filter so it can be reenabled when we are done with
827 // the current key:value pair.
828 gchar **_filter = NULL;
829 if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
830 _filter = pdata->filter;
831 pdata->filter = NULL;
834 write_json_proto_node(node_values_list, suffix, write_json_proto_node_children, pdata);
836 // Put protocol filter back
837 if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
838 pdata->filter = _filter;
843 if (!has_value && !has_children && (pdata->print_text || (pdata->print_hex && is_pseudo_text_field))) {
844 write_json_proto_node(node_values_list, "", write_json_proto_node_no_value, pdata);
847 current_node = current_node->next;
849 json_dumper_end_object(pdata->dumper);
853 * Writes a single node as a key:value pair. The value_writer param can be used to specify how the node's value should
854 * be written.
855 * @param node_values_head Linked list containing all nodes associated with the same json key in this object.
856 * @param suffix Suffix that should be added to the json key.
857 * @param value_writer A function which writes the actual values of the node json key.
858 * @param pdata json writing metadata
860 static void
861 write_json_proto_node(GSList *node_values_head,
862 const char *suffix,
863 proto_node_value_writer value_writer,
864 write_json_data *pdata)
866 // Retrieve json key from first value.
867 proto_node *first_value = (proto_node *) node_values_head->data;
868 const char *json_key = proto_node_to_json_key(first_value);
869 gchar* json_key_suffix = g_strdup_printf("%s%s", json_key, suffix);
870 json_dumper_set_member_name(pdata->dumper, json_key_suffix);
871 g_free(json_key_suffix);
872 write_json_proto_node_value_list(node_values_head, value_writer, pdata);
876 * Writes a list of values of a single json key. If multiple values are passed they are wrapped in a json array.
877 * @param node_values_head Linked list containing all values that should be written.
878 * @param value_writer Function which writes the separate values.
879 * @param pdata json writing metadata
881 static void
882 write_json_proto_node_value_list(GSList *node_values_head, proto_node_value_writer value_writer, write_json_data *pdata)
884 GSList *current_value = node_values_head;
886 // Write directly if only a single value is passed. Wrap in json array otherwise.
887 if (current_value->next == NULL) {
888 value_writer((proto_node *) current_value->data, pdata);
889 } else {
890 json_dumper_begin_array(pdata->dumper);
892 while (current_value != NULL) {
893 value_writer((proto_node *) current_value->data, pdata);
894 current_value = current_value->next;
896 json_dumper_end_array(pdata->dumper);
901 * Writes the value for a node that's filtered from the output.
903 static void
904 write_json_proto_node_filtered(proto_node *node, write_json_data *pdata)
906 const char *json_key = proto_node_to_json_key(node);
908 json_dumper_begin_object(pdata->dumper);
909 json_dumper_set_member_name(pdata->dumper, "filtered");
910 json_dumper_value_string(pdata->dumper, json_key);
911 json_dumper_end_object(pdata->dumper);
915 * Writes the hex dump of a node. A json array is written containing the hex dump, position, length, bitmask and type of
916 * the node.
918 static void
919 write_json_proto_node_hex_dump(proto_node *node, write_json_data *pdata)
921 field_info *fi = node->finfo;
923 json_dumper_begin_array(pdata->dumper);
925 if (fi->hfinfo->bitmask!=0) {
926 switch (fi->value.ftype->ftype) {
927 case FT_INT8:
928 case FT_INT16:
929 case FT_INT24:
930 case FT_INT32:
931 json_dumper_value_anyf(pdata->dumper, "\"%X\"", (guint) fvalue_get_sinteger(&fi->value));
932 break;
933 case FT_CHAR:
934 case FT_UINT8:
935 case FT_UINT16:
936 case FT_UINT24:
937 case FT_UINT32:
938 json_dumper_value_anyf(pdata->dumper, "\"%X\"", fvalue_get_uinteger(&fi->value));
939 break;
940 case FT_INT40:
941 case FT_INT48:
942 case FT_INT56:
943 case FT_INT64:
944 json_dumper_value_anyf(pdata->dumper, "\"%" G_GINT64_MODIFIER "X\"", fvalue_get_sinteger64(&fi->value));
945 break;
946 case FT_UINT40:
947 case FT_UINT48:
948 case FT_UINT56:
949 case FT_UINT64:
950 case FT_BOOLEAN:
951 json_dumper_value_anyf(pdata->dumper, "\"%" G_GINT64_MODIFIER "X\"", fvalue_get_uinteger64(&fi->value));
952 break;
953 default:
954 g_assert_not_reached();
956 } else {
957 json_write_field_hex_value(pdata, fi);
960 /* Dump raw hex-encoded dissected information including position, length, bitmask, type */
961 json_dumper_value_anyf(pdata->dumper, "%" G_GINT32_MODIFIER "d", fi->start);
962 json_dumper_value_anyf(pdata->dumper, "%" G_GINT32_MODIFIER "d", fi->length);
963 json_dumper_value_anyf(pdata->dumper, "%" G_GUINT64_FORMAT, fi->hfinfo->bitmask);
964 json_dumper_value_anyf(pdata->dumper, "%" G_GINT32_MODIFIER "d", (gint32)fi->value.ftype->ftype);
966 json_dumper_end_array(pdata->dumper);
970 * Writes the children of a node. Calls write_json_proto_node_list internally which recursively writes children of nodes
971 * to the output.
973 static void
974 write_json_proto_node_children(proto_node *node, write_json_data *data)
976 GSList *grouped_children_list = data->node_children_grouper(node);
977 write_json_proto_node_list(grouped_children_list, data);
978 g_slist_free_full(grouped_children_list, (GDestroyNotify) g_slist_free);
982 * Writes the value of a node to the output.
984 static void
985 write_json_proto_node_value(proto_node *node, write_json_data *pdata)
987 field_info *fi = node->finfo;
988 // Get the actual value of the node as a string.
989 char *value_string_repr = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
991 json_dumper_value_string(pdata->dumper, value_string_repr);
993 wmem_free(NULL, value_string_repr);
997 * Write the value for a node that has no value and no children. This is the empty string for all nodes except those of
998 * type FT_PROTOCOL for which the full name is written instead.
1000 static void
1001 write_json_proto_node_no_value(proto_node *node, write_json_data *pdata)
1003 field_info *fi = node->finfo;
1005 if (fi->hfinfo->type == FT_PROTOCOL) {
1006 if (fi->rep) {
1007 json_dumper_value_string(pdata->dumper, fi->rep->representation);
1008 } else {
1009 gchar label_str[ITEM_LABEL_LENGTH];
1010 proto_item_fill_label(fi, label_str);
1011 json_dumper_value_string(pdata->dumper, label_str);
1013 } else {
1014 json_dumper_value_string(pdata->dumper, "");
1019 * Groups each child of the node separately.
1020 * @return Linked list where each element is another linked list containing a single node.
1022 GSList *
1023 proto_node_group_children_by_unique(proto_node *node) {
1024 GSList *unique_nodes_list = NULL;
1025 proto_node *current_child = node->first_child;
1027 while (current_child != NULL) {
1028 GSList *unique_node = g_slist_prepend(NULL, current_child);
1029 unique_nodes_list = g_slist_prepend(unique_nodes_list, unique_node);
1030 current_child = current_child->next;
1033 return g_slist_reverse(unique_nodes_list);
1037 * Groups the children of a node by their json key. Children are put in the same group if they have the same json key.
1038 * @return Linked list where each element is another linked list of nodes associated with the same json key.
1040 GSList *
1041 proto_node_group_children_by_json_key(proto_node *node)
1044 * For each different json key we store a linked list of values corresponding to that json key. These lists are kept
1045 * in both a linked list and a hashmap. The hashmap is used to quickly retrieve the values of a json key. The linked
1046 * list is used to preserve the ordering of keys as they are encountered which is not guaranteed when only using a
1047 * hashmap.
1049 GSList *same_key_nodes_list = NULL;
1050 GHashTable *lookup_by_json_key = g_hash_table_new(g_str_hash, g_str_equal);
1051 proto_node *current_child = node->first_child;
1054 * For each child of the node get the key and get the list of values already associated with that key from the
1055 * 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
1056 * list already exists add the node to that list.
1058 while (current_child != NULL) {
1059 char *json_key = (char *) proto_node_to_json_key(current_child);
1060 GSList *json_key_nodes = (GSList *) g_hash_table_lookup(lookup_by_json_key, json_key);
1062 if (json_key_nodes == NULL) {
1063 json_key_nodes = g_slist_append(json_key_nodes, current_child);
1064 // Prepending in single linked list is O(1), appending is O(n). Better to prepend here and reverse at the
1065 // end than potentially looping to the end of the linked list for each child.
1066 same_key_nodes_list = g_slist_prepend(same_key_nodes_list, json_key_nodes);
1067 g_hash_table_insert(lookup_by_json_key, json_key, json_key_nodes);
1068 } else {
1069 // Store and insert value again to circumvent unused_variable warning.
1070 // Append in this case since most value lists will only have a single value.
1071 json_key_nodes = g_slist_append(json_key_nodes, current_child);
1072 g_hash_table_insert(lookup_by_json_key, json_key, json_key_nodes);
1075 current_child = current_child->next;
1078 // Hash table is not needed anymore since the linked list with the correct ordering is returned.
1079 g_hash_table_destroy(lookup_by_json_key);
1081 return g_slist_reverse(same_key_nodes_list);
1085 * Returns the json key of a node. Tries to use the node's abbreviated name. If the abbreviated name is not available
1086 * the representation is used instead.
1088 static const char *
1089 proto_node_to_json_key(proto_node *node)
1091 const char *json_key;
1092 // Check if node has abbreviated name.
1093 if (node->finfo->hfinfo->id != hf_text_only) {
1094 json_key = node->finfo->hfinfo->abbrev;
1095 } else if (node->finfo->rep != NULL) {
1096 json_key = node->finfo->rep->representation;
1097 } else {
1098 json_key = "";
1101 return json_key;
1104 static gboolean
1105 ek_check_protocolfilter(gchar **protocolfilter, const char *str)
1107 gchar *str_escaped = NULL;
1108 gboolean check;
1109 int i;
1111 if (check_protocolfilter(protocolfilter, str))
1112 return TRUE;
1114 /* to to thread the '.' and '_' equally. The '.' is replace by print_escaped_ek for '_' */
1115 if (str != NULL && strlen(str) > 0) {
1116 str_escaped = g_strdup(str);
1118 i = 0;
1119 while (str_escaped[i] != '\0') {
1120 if (str_escaped[i] == '.') {
1121 str_escaped[i] = '_';
1123 i++;
1127 check = check_protocolfilter(protocolfilter, str_escaped);
1128 g_free(str_escaped);
1129 return check;
1133 * Finds a node's descendants to be printed as EK/JSON attributes.
1135 static void
1136 write_ek_summary(column_info *cinfo, write_json_data* pdata)
1138 gint i;
1140 for (i = 0; i < cinfo->num_cols; i++) {
1141 if (!get_column_visible(i))
1142 continue;
1143 json_dumper_set_member_name(pdata->dumper, g_ascii_strdown(cinfo->columns[i].col_title, -1));
1144 json_dumper_value_string(pdata->dumper, cinfo->columns[i].col_data);
1148 /* Write out a tree's data, and any child nodes, as JSON for EK */
1149 static void
1150 ek_fill_attr(proto_node *node, GSList **attr_list, GHashTable *attr_table, write_json_data *pdata)
1152 field_info *fi = NULL;
1153 field_info *fi_parent = NULL;
1154 gchar *node_name = NULL;
1155 GSList *attr_instances = NULL;
1157 proto_node *current_node = node->first_child;
1158 while (current_node != NULL) {
1159 fi = PNODE_FINFO(current_node);
1160 fi_parent = PNODE_FINFO(current_node->parent);
1162 /* dissection with an invisible proto tree? */
1163 g_assert(fi);
1165 if (fi_parent == NULL) {
1166 node_name = g_strdup(fi->hfinfo->abbrev);
1167 } else {
1168 node_name = g_strconcat(fi_parent->hfinfo->abbrev, "_", fi->hfinfo->abbrev, NULL);
1171 attr_instances = (GSList *) g_hash_table_lookup(attr_table, node_name);
1172 // First time we encounter this attr
1173 if (attr_instances == NULL) {
1174 attr_instances = g_slist_append(attr_instances, current_node);
1175 *attr_list = g_slist_prepend(*attr_list, attr_instances);
1176 } else {
1177 attr_instances = g_slist_append(attr_instances, current_node);
1180 // Update instance list for this attr in hash table
1181 g_hash_table_insert(attr_table, node_name, attr_instances);
1183 /* Field, recurse through children*/
1184 if (fi->hfinfo->type != FT_PROTOCOL && current_node->first_child != NULL) {
1185 if (pdata->filter != NULL) {
1186 if (ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
1187 gchar **_filter = NULL;
1188 /* Remove protocol filter for children, if children should be included */
1189 if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1190 _filter = pdata->filter;
1191 pdata->filter = NULL;
1194 ek_fill_attr(current_node, attr_list, attr_table, pdata);
1196 /* Put protocol filter back */
1197 if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1198 pdata->filter = _filter;
1200 } else {
1201 // Don't traverse children if filtered out
1203 } else {
1204 ek_fill_attr(current_node, attr_list, attr_table, pdata);
1206 } else {
1207 // Will descend into object at another point
1210 current_node = current_node->next;
1214 static void
1215 ek_write_name(proto_node *pnode, gchar* suffix, write_json_data* pdata)
1217 field_info *fi = PNODE_FINFO(pnode);
1218 gchar *str;
1220 if (fi->hfinfo->parent != -1) {
1221 header_field_info* parent = proto_registrar_get_nth(fi->hfinfo->parent);
1222 str = g_strdup_printf("%s_%s%s", parent->abbrev, fi->hfinfo->abbrev, suffix ? suffix : "");
1223 json_dumper_set_member_name(pdata->dumper, str);
1224 } else {
1225 str = g_strdup_printf("%s%s", fi->hfinfo->abbrev, suffix ? suffix : "");
1226 json_dumper_set_member_name(pdata->dumper, str);
1228 g_free(str);
1231 static void
1232 ek_write_hex(field_info *fi, write_json_data *pdata)
1234 if (fi->hfinfo->bitmask != 0) {
1235 switch (fi->value.ftype->ftype) {
1236 case FT_INT8:
1237 case FT_INT16:
1238 case FT_INT24:
1239 case FT_INT32:
1240 json_dumper_value_anyf(pdata->dumper, "\"%X\"", (guint) fvalue_get_sinteger(&fi->value));
1241 break;
1242 case FT_CHAR:
1243 case FT_UINT8:
1244 case FT_UINT16:
1245 case FT_UINT24:
1246 case FT_UINT32:
1247 json_dumper_value_anyf(pdata->dumper, "\"%X\"", fvalue_get_uinteger(&fi->value));
1248 break;
1249 case FT_INT40:
1250 case FT_INT48:
1251 case FT_INT56:
1252 case FT_INT64:
1253 json_dumper_value_anyf(pdata->dumper, "\"%" G_GINT64_MODIFIER "X\"", fvalue_get_sinteger64(&fi->value));
1254 break;
1255 case FT_UINT40:
1256 case FT_UINT48:
1257 case FT_UINT56:
1258 case FT_UINT64:
1259 case FT_BOOLEAN:
1260 json_dumper_value_anyf(pdata->dumper, "\"%" G_GINT64_MODIFIER "X\"", fvalue_get_uinteger64(&fi->value));
1261 break;
1262 default:
1263 g_assert_not_reached();
1265 } else {
1266 json_write_field_hex_value(pdata, fi);
1270 static void
1271 ek_write_field_value(field_info *fi, write_json_data* pdata)
1273 gchar label_str[ITEM_LABEL_LENGTH];
1274 char *dfilter_string;
1275 const nstime_t *t;
1276 struct tm *tm;
1277 #ifndef _WIN32
1278 struct tm tm_time;
1279 #endif
1280 char time_string[sizeof("YYYY-MM-DDTHH:MM:SS")];
1282 /* Text label */
1283 if (fi->hfinfo->id == hf_text_only && fi->rep) {
1284 json_dumper_value_string(pdata->dumper, fi->rep->representation);
1285 } else {
1286 /* show, value, and unmaskedvalue attributes */
1287 switch(fi->hfinfo->type) {
1288 case FT_PROTOCOL:
1289 if (fi->rep) {
1290 json_dumper_value_string(pdata->dumper, fi->rep->representation);
1292 else {
1293 proto_item_fill_label(fi, label_str);
1294 json_dumper_value_string(pdata->dumper, label_str);
1296 break;
1297 case FT_NONE:
1298 json_dumper_value_string(pdata->dumper, NULL);
1299 break;
1300 case FT_BOOLEAN:
1301 if (fi->value.value.uinteger64)
1302 json_dumper_value_anyf(pdata->dumper, "true");
1303 else
1304 json_dumper_value_anyf(pdata->dumper, "false");
1305 break;
1306 case FT_ABSOLUTE_TIME:
1307 t = (const nstime_t *)fvalue_get(&fi->value);
1308 #ifdef _WIN32
1310 * Do not use gmtime_s(), as it will call and
1311 * exception handler if the time we're providing
1312 * is < 0, and that will, by default, exit.
1313 * ("Programmers not bothering to check return
1314 * values? Try new Microsoft Visual Studio,
1315 * with Parameter Validation(R)! Kill insufficiently
1316 * careful programs - *and* the processes running them -
1317 * fast!")
1319 * We just want to report this as an unrepresentable
1320 * time. It fills in a per-thread structure, which
1321 * is sufficiently thread-safe for our purposes.
1323 tm = gmtime(&t->secs);
1324 #else
1326 * Use gmtime_r(), because the Single UNIX Specification
1327 * does *not* guarantee that gmtime() is thread-safe.
1328 * Perhaps it is on all platforms on which we run, but
1329 * this way we don't have to check.
1331 tm = gmtime_r(&t->secs, &tm_time);
1332 #endif
1333 if (tm != NULL) {
1334 strftime(time_string, sizeof(time_string), "%FT%T", tm);
1335 json_dumper_value_anyf(pdata->dumper, "\"%s.%09uZ\"", time_string, t->nsecs);
1336 } else {
1337 json_dumper_value_anyf(pdata->dumper, "\"Not representable\"");
1339 break;
1340 default:
1341 dfilter_string = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
1342 if (dfilter_string != NULL) {
1343 json_dumper_value_string(pdata->dumper, dfilter_string);
1345 wmem_free(NULL, dfilter_string);
1346 break;
1351 static void
1352 ek_write_attr_hex(GSList *attr_instances, write_json_data *pdata)
1354 GSList *current_node = attr_instances;
1355 proto_node *pnode = (proto_node *) current_node->data;
1356 field_info *fi = NULL;
1358 // Raw name
1359 ek_write_name(pnode, "_raw", pdata);
1361 if (g_slist_length(attr_instances) > 1) {
1362 json_dumper_begin_array(pdata->dumper);
1365 // Raw value(s)
1366 while (current_node != NULL) {
1367 pnode = (proto_node *) current_node->data;
1368 fi = PNODE_FINFO(pnode);
1370 ek_write_hex(fi, pdata);
1372 current_node = current_node->next;
1375 if (g_slist_length(attr_instances) > 1) {
1376 json_dumper_end_array(pdata->dumper);
1380 static void
1381 ek_write_attr(GSList *attr_instances, write_json_data *pdata)
1383 GSList *current_node = attr_instances;
1384 proto_node *pnode = (proto_node *) current_node->data;
1385 field_info *fi = PNODE_FINFO(pnode);
1387 // Hex dump -x
1388 if (pdata->print_hex && fi && fi->length > 0 && fi->hfinfo->id != hf_text_only) {
1389 ek_write_attr_hex(attr_instances, pdata);
1392 // Print attr name
1393 ek_write_name(pnode, NULL, pdata);
1395 if (g_slist_length(attr_instances) > 1) {
1396 json_dumper_begin_array(pdata->dumper);
1399 while (current_node != NULL) {
1400 pnode = (proto_node *) current_node->data;
1401 fi = PNODE_FINFO(pnode);
1403 /* Field */
1404 if (fi->hfinfo->type != FT_PROTOCOL) {
1405 if (pdata->filter != NULL
1406 && !ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
1408 /* print dummy field */
1409 json_dumper_begin_object(pdata->dumper);
1410 json_dumper_set_member_name(pdata->dumper, "filtered");
1411 json_dumper_value_string(pdata->dumper, fi->hfinfo->abbrev);
1412 json_dumper_end_object(pdata->dumper);
1413 } else {
1414 ek_write_field_value(fi, pdata);
1416 } else {
1417 /* Object */
1418 json_dumper_begin_object(pdata->dumper);
1420 if (pdata->filter != NULL) {
1421 if (ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
1422 gchar **_filter = NULL;
1423 /* Remove protocol filter for children, if children should be included */
1424 if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1425 _filter = pdata->filter;
1426 pdata->filter = NULL;
1429 proto_tree_write_node_ek(pnode, pdata);
1431 /* Put protocol filter back */
1432 if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
1433 pdata->filter = _filter;
1435 } else {
1436 /* print dummy field */
1437 json_dumper_set_member_name(pdata->dumper, "filtered");
1438 json_dumper_value_string(pdata->dumper, fi->hfinfo->abbrev);
1440 } else {
1441 proto_tree_write_node_ek(pnode, pdata);
1444 json_dumper_end_object(pdata->dumper);
1447 current_node = current_node->next;
1450 if (g_slist_length(attr_instances) > 1) {
1451 json_dumper_end_array(pdata->dumper);
1455 /* Write out a tree's data, and any child nodes, as JSON for EK */
1456 static void
1457 proto_tree_write_node_ek(proto_node *node, write_json_data *pdata)
1459 GSList *attr_list = NULL;
1460 GHashTable *attr_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1462 ek_fill_attr(node, &attr_list, attr_table, pdata);
1464 g_hash_table_destroy(attr_table);
1466 // Print attributes
1467 attr_list = g_slist_reverse(attr_list);
1468 GSList *current_attr = attr_list;
1469 while (current_attr != NULL) {
1470 GSList *attr_instances = (GSList *) current_attr->data;
1472 ek_write_attr(attr_instances, pdata);
1474 current_attr = current_attr->next;
1477 g_slist_free_full(attr_list, (GDestroyNotify) g_slist_free);
1480 /* Print info for a 'geninfo' pseudo-protocol. This is required by
1481 * the PDML spec. The information is contained in Wireshark's 'frame' protocol,
1482 * but we produce a 'geninfo' protocol in the PDML to conform to spec.
1483 * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */
1484 static void
1485 print_pdml_geninfo(epan_dissect_t *edt, FILE *fh)
1487 guint32 num, len, caplen;
1488 GPtrArray *finfo_array;
1489 field_info *frame_finfo;
1490 gchar *tmp;
1492 /* Get frame protocol's finfo. */
1493 finfo_array = proto_find_first_finfo(edt->tree, proto_frame);
1494 if (g_ptr_array_len(finfo_array) < 1) {
1495 return;
1497 frame_finfo = (field_info *)finfo_array->pdata[0];
1498 g_ptr_array_free(finfo_array, TRUE);
1500 /* frame.number, packet_info.num */
1501 num = edt->pi.num;
1503 /* frame.frame_len, packet_info.frame_data->pkt_len */
1504 len = edt->pi.fd->pkt_len;
1506 /* frame.cap_len --> packet_info.frame_data->cap_len */
1507 caplen = edt->pi.fd->cap_len;
1509 /* Print geninfo start */
1510 fprintf(fh,
1511 " <proto name=\"geninfo\" pos=\"0\" showname=\"General information\" size=\"%d\">\n",
1512 frame_finfo->length);
1514 /* Print geninfo.num */
1515 fprintf(fh,
1516 " <field name=\"num\" pos=\"0\" show=\"%u\" showname=\"Number\" value=\"%x\" size=\"%d\"/>\n",
1517 num, num, frame_finfo->length);
1519 /* Print geninfo.len */
1520 fprintf(fh,
1521 " <field name=\"len\" pos=\"0\" show=\"%u\" showname=\"Frame Length\" value=\"%x\" size=\"%d\"/>\n",
1522 len, len, frame_finfo->length);
1524 /* Print geninfo.caplen */
1525 fprintf(fh,
1526 " <field name=\"caplen\" pos=\"0\" show=\"%u\" showname=\"Captured Length\" value=\"%x\" size=\"%d\"/>\n",
1527 caplen, caplen, frame_finfo->length);
1529 tmp = abs_time_to_str(NULL, &edt->pi.abs_ts, ABSOLUTE_TIME_LOCAL, TRUE);
1531 /* Print geninfo.timestamp */
1532 fprintf(fh,
1533 " <field name=\"timestamp\" pos=\"0\" show=\"%s\" showname=\"Captured Time\" value=\"%d.%09d\" size=\"%d\"/>\n",
1534 tmp, (int)edt->pi.abs_ts.secs, edt->pi.abs_ts.nsecs, frame_finfo->length);
1536 wmem_free(NULL, tmp);
1538 /* Print geninfo end */
1539 fprintf(fh,
1540 " </proto>\n");
1543 void
1544 write_pdml_finale(FILE *fh)
1546 fputs("</pdml>\n", fh);
1549 void
1550 write_psml_preamble(column_info *cinfo, FILE *fh)
1552 gint i;
1554 fprintf(fh, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
1555 fprintf(fh, "<psml version=\"" PSML_VERSION "\" creator=\"%s/%s\">\n", PACKAGE, VERSION);
1556 fprintf(fh, "<structure>\n");
1558 for (i = 0; i < cinfo->num_cols; i++) {
1559 if (!get_column_visible(i))
1560 continue;
1561 fprintf(fh, "<section>");
1562 print_escaped_xml(fh, cinfo->columns[i].col_title);
1563 fprintf(fh, "</section>\n");
1566 fprintf(fh, "</structure>\n\n");
1569 void
1570 write_psml_columns(epan_dissect_t *edt, FILE *fh, gboolean use_color)
1572 gint i;
1573 const color_filter_t *cfp = edt->pi.fd->color_filter;
1575 if (use_color && (cfp != NULL)) {
1576 fprintf(fh, "<packet foreground='#%06x' background='#%06x'>\n",
1577 color_t_to_rgb(&cfp->fg_color),
1578 color_t_to_rgb(&cfp->bg_color));
1579 } else {
1580 fprintf(fh, "<packet>\n");
1583 for (i = 0; i < edt->pi.cinfo->num_cols; i++) {
1584 if (!get_column_visible(i))
1585 continue;
1586 fprintf(fh, "<section>");
1587 print_escaped_xml(fh, edt->pi.cinfo->columns[i].col_data);
1588 fprintf(fh, "</section>\n");
1591 fprintf(fh, "</packet>\n\n");
1594 void
1595 write_psml_finale(FILE *fh)
1597 fputs("</psml>\n", fh);
1600 static gchar *csv_massage_str(const gchar *source, const gchar *exceptions)
1602 gchar *csv_str;
1603 gchar *tmp_str;
1605 /* In general, our output for any field can contain Unicode characters,
1606 so g_strescape (which escapes any non-ASCII) is the wrong thing to do.
1607 Unfortunately glib doesn't appear to provide g_unicode_strescape()... */
1608 csv_str = g_strescape(source, exceptions);
1609 tmp_str = csv_str;
1610 /* Locate the UTF-8 right arrow character and replace it by an ASCII equivalent */
1611 while ( (tmp_str = strstr(tmp_str, UTF8_RIGHTWARDS_ARROW)) != NULL ) {
1612 tmp_str[0] = ' ';
1613 tmp_str[1] = '>';
1614 tmp_str[2] = ' ';
1616 tmp_str = csv_str;
1617 while ( (tmp_str = strstr(tmp_str, "\\\"")) != NULL )
1618 *tmp_str = '\"';
1619 return csv_str;
1622 static void csv_write_str(const char *str, char sep, FILE *fh)
1624 gchar *csv_str;
1626 /* Do not escape the UTF-8 right arrow character */
1627 csv_str = csv_massage_str(str, UTF8_RIGHTWARDS_ARROW);
1628 fprintf(fh, "\"%s\"%c", csv_str, sep);
1629 g_free(csv_str);
1632 void
1633 write_csv_column_titles(column_info *cinfo, FILE *fh)
1635 gint i;
1637 for (i = 0; i < cinfo->num_cols - 1; i++) {
1638 if (!get_column_visible(i))
1639 continue;
1640 csv_write_str(cinfo->columns[i].col_title, ',', fh);
1642 csv_write_str(cinfo->columns[i].col_title, '\n', fh);
1645 void
1646 write_csv_columns(epan_dissect_t *edt, FILE *fh)
1648 gint i;
1650 for (i = 0; i < edt->pi.cinfo->num_cols - 1; i++) {
1651 if (!get_column_visible(i))
1652 continue;
1653 csv_write_str(edt->pi.cinfo->columns[i].col_data, ',', fh);
1655 csv_write_str(edt->pi.cinfo->columns[i].col_data, '\n', fh);
1658 void
1659 write_carrays_hex_data(guint32 num, FILE *fh, epan_dissect_t *edt)
1661 guint32 i = 0, src_num = 0;
1662 GSList *src_le;
1663 tvbuff_t *tvb;
1664 char *name;
1665 const guchar *cp;
1666 guint length;
1667 char ascii[9];
1668 struct data_source *src;
1670 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
1671 memset(ascii, 0, sizeof(ascii));
1672 src = (struct data_source *)src_le->data;
1673 tvb = get_data_source_tvb(src);
1674 length = tvb_captured_length(tvb);
1675 if (length == 0)
1676 continue;
1678 cp = tvb_get_ptr(tvb, 0, length);
1680 name = get_data_source_name(src);
1681 if (name) {
1682 fprintf(fh, "/* %s */\n", name);
1683 wmem_free(NULL, name);
1685 if (src_num) {
1686 fprintf(fh, "static const unsigned char pkt%u_%u[%u] = {\n",
1687 num, src_num, length);
1688 } else {
1689 fprintf(fh, "static const unsigned char pkt%u[%u] = {\n",
1690 num, length);
1692 src_num++;
1694 for (i = 0; i < length; i++) {
1695 fprintf(fh, "0x%02x", *(cp + i));
1696 ascii[i % 8] = g_ascii_isprint(*(cp + i)) ? *(cp + i) : '.';
1698 if (i == (length - 1)) {
1699 guint rem;
1700 rem = length % 8;
1701 if (rem) {
1702 guint j;
1703 for ( j = 0; j < 8 - rem; j++ )
1704 fprintf(fh, " ");
1706 fprintf(fh, " /* %s */\n};\n\n", ascii);
1707 break;
1710 if (!((i + 1) % 8)) {
1711 fprintf(fh, ", /* %s */\n", ascii);
1712 memset(ascii, 0, sizeof(ascii));
1713 } else {
1714 fprintf(fh, ", ");
1721 * Find the data source for a specified field, and return a pointer
1722 * to the data in it. Returns NULL if the data is out of bounds.
1724 /* XXX: What am I missing ?
1725 * Why bother searching for fi->ds_tvb for the matching tvb
1726 * in the data_source list ?
1727 * IOW: Why not just use fi->ds_tvb for the arg to tvb_get_ptr() ?
1730 static const guint8 *
1731 get_field_data(GSList *src_list, field_info *fi)
1733 GSList *src_le;
1734 tvbuff_t *src_tvb;
1735 gint length, tvbuff_length;
1736 struct data_source *src;
1738 for (src_le = src_list; src_le != NULL; src_le = src_le->next) {
1739 src = (struct data_source *)src_le->data;
1740 src_tvb = get_data_source_tvb(src);
1741 if (fi->ds_tvb == src_tvb) {
1743 * Found it.
1745 * XXX - a field can have a length that runs past
1746 * the end of the tvbuff. Ideally, that should
1747 * be fixed when adding an item to the protocol
1748 * tree, but checking the length when doing
1749 * that could be expensive. Until we fix that,
1750 * we'll do the check here.
1752 tvbuff_length = tvb_captured_length_remaining(src_tvb,
1753 fi->start);
1754 if (tvbuff_length < 0) {
1755 return NULL;
1757 length = fi->length;
1758 if (length > tvbuff_length)
1759 length = tvbuff_length;
1760 return tvb_get_ptr(src_tvb, fi->start, length);
1763 return NULL; /* not found */
1766 /* Print a string, escaping out certain characters that need to
1767 * escaped out for XML. */
1768 static void
1769 print_escaped_xml(FILE *fh, const char *unescaped_string)
1771 const char *p;
1773 #define ESCAPED_BUFFER_MAX 256
1774 static char temp_buffer[ESCAPED_BUFFER_MAX];
1775 gint offset = 0;
1777 if (fh == NULL || unescaped_string == NULL) {
1778 return;
1781 for (p = unescaped_string; *p != '\0'; p++) {
1782 switch (*p) {
1783 case '&':
1784 g_strlcpy(&temp_buffer[offset], "&amp;", ESCAPED_BUFFER_MAX-offset);
1785 offset += 5;
1786 break;
1787 case '<':
1788 g_strlcpy(&temp_buffer[offset], "&lt;", ESCAPED_BUFFER_MAX-offset);
1789 offset += 4;
1790 break;
1791 case '>':
1792 g_strlcpy(&temp_buffer[offset], "&gt;", ESCAPED_BUFFER_MAX-offset);
1793 offset += 4;
1794 break;
1795 case '"':
1796 g_strlcpy(&temp_buffer[offset], "&quot;", ESCAPED_BUFFER_MAX-offset);
1797 offset += 6;
1798 break;
1799 case '\'':
1800 g_strlcpy(&temp_buffer[offset], "&#x27;", ESCAPED_BUFFER_MAX-offset);
1801 offset += 6;
1802 break;
1803 default:
1804 temp_buffer[offset++] = *p;
1806 if (offset > ESCAPED_BUFFER_MAX-8) {
1807 /* Getting close to end of buffer so flush to fh */
1808 temp_buffer[offset] = '\0';
1809 fputs(temp_buffer, fh);
1810 offset = 0;
1813 if (offset) {
1814 /* Flush any outstanding data */
1815 temp_buffer[offset] = '\0';
1816 fputs(temp_buffer, fh);
1820 static void
1821 print_escaped_csv(FILE *fh, const char *unescaped_string)
1823 const char *p;
1825 if (fh == NULL || unescaped_string == NULL) {
1826 return;
1829 for (p = unescaped_string; *p != '\0'; p++) {
1830 switch (*p) {
1831 case '\b':
1832 fputs("\\b", fh);
1833 break;
1834 case '\f':
1835 fputs("\\f", fh);
1836 break;
1837 case '\n':
1838 fputs("\\n", fh);
1839 break;
1840 case '\r':
1841 fputs("\\r", fh);
1842 break;
1843 case '\t':
1844 fputs("\\t", fh);
1845 break;
1846 default:
1847 fputc(*p, fh);
1852 static void
1853 pdml_write_field_hex_value(write_pdml_data *pdata, field_info *fi)
1855 int i;
1856 const guint8 *pd;
1858 if (!fi->ds_tvb)
1859 return;
1861 if (fi->length > tvb_captured_length_remaining(fi->ds_tvb, fi->start)) {
1862 fprintf(pdata->fh, "field length invalid!");
1863 return;
1866 /* Find the data for this field. */
1867 pd = get_field_data(pdata->src_list, fi);
1869 if (pd) {
1870 /* Used fixed buffer where can, otherwise temp malloc */
1871 static gchar str_static[129];
1872 gchar *str = str_static;
1873 gchar* str_heap = NULL;
1874 if (fi->length > 64) {
1875 str_heap = (gchar*)g_malloc0(fi->length*2+1);
1876 str = str_heap;
1879 static const char hex[] = "0123456789abcdef";
1881 /* Print a simple hex dump */
1882 for (i = 0 ; i < fi->length; i++) {
1883 str[2*i] = hex[pd[i] >> 4];
1884 str[2*i+1] = hex[pd[i] & 0xf];
1886 str[2 * fi->length] = '\0';
1887 fputs(str, pdata->fh);
1888 g_free(str_heap);
1893 static void
1894 json_write_field_hex_value(write_json_data *pdata, field_info *fi)
1896 const guint8 *pd;
1898 if (!fi->ds_tvb)
1899 return;
1901 if (fi->length > tvb_captured_length_remaining(fi->ds_tvb, fi->start)) {
1902 json_dumper_value_string(pdata->dumper, "field length invalid!");
1903 return;
1906 /* Find the data for this field. */
1907 pd = get_field_data(pdata->src_list, fi);
1909 if (pd) {
1910 gint i;
1911 guint len = fi->length * 2 + 1;
1912 gchar* str = (gchar*)g_malloc0(len);
1913 static const char hex[] = "0123456789abcdef";
1914 /* Print a simple hex dump */
1915 for (i = 0; i < fi->length; i++) {
1916 guint8 c = pd[i];
1917 str[2 * i] = hex[c >> 4];
1918 str[2 * i + 1] = hex[c & 0xf];
1920 str[2 * fi->length] = '\0';
1921 json_dumper_value_string(pdata->dumper, str);
1922 g_free(str);
1923 } else {
1924 json_dumper_value_string(pdata->dumper, "");
1928 gboolean
1929 print_hex_data(print_stream_t *stream, epan_dissect_t *edt)
1931 gboolean multiple_sources;
1932 GSList *src_le;
1933 tvbuff_t *tvb;
1934 char *line, *name;
1935 const guchar *cp;
1936 guint length;
1937 struct data_source *src;
1940 * Set "multiple_sources" iff this frame has more than one
1941 * data source; if it does, we need to print the name of
1942 * the data source before printing the data from the
1943 * data source.
1945 multiple_sources = (edt->pi.data_src->next != NULL);
1947 for (src_le = edt->pi.data_src; src_le != NULL;
1948 src_le = src_le->next) {
1949 src = (struct data_source *)src_le->data;
1950 tvb = get_data_source_tvb(src);
1951 if (multiple_sources) {
1952 name = get_data_source_name(src);
1953 line = g_strdup_printf("%s:", name);
1954 wmem_free(NULL, name);
1955 print_line(stream, 0, line);
1956 g_free(line);
1958 length = tvb_captured_length(tvb);
1959 if (length == 0)
1960 return TRUE;
1961 cp = tvb_get_ptr(tvb, 0, length);
1962 if (!print_hex_data_buffer(stream, cp, length,
1963 (packet_char_enc)edt->pi.fd->encoding))
1964 return FALSE;
1966 return TRUE;
1970 * This routine is based on a routine created by Dan Lasley
1971 * <DLASLEY@PROMUS.com>.
1973 * It was modified for Wireshark by Gilbert Ramirez and others.
1976 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
1977 #define BYTES_PER_LINE 16 /* max byte values printed on a line */
1978 #define HEX_DUMP_LEN (BYTES_PER_LINE*3)
1979 /* max number of characters hex dump takes -
1980 2 digits plus trailing blank */
1981 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
1982 /* number of characters those bytes take;
1983 3 characters per byte of hex dump,
1984 2 blanks separating hex from ASCII,
1985 1 character per byte of ASCII dump */
1986 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
1987 /* number of characters per line;
1988 offset, 2 blanks separating offset
1989 from data dump, data dump */
1991 static gboolean
1992 print_hex_data_buffer(print_stream_t *stream, const guchar *cp,
1993 guint length, packet_char_enc encoding)
1995 register unsigned int ad, i, j, k, l;
1996 guchar c;
1997 gchar line[MAX_LINE_LEN + 1];
1998 unsigned int use_digits;
2000 static gchar binhex[16] = {
2001 '0', '1', '2', '3', '4', '5', '6', '7',
2002 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
2005 * How many of the leading digits of the offset will we supply?
2006 * We always supply at least 4 digits, but if the maximum offset
2007 * won't fit in 4 digits, we use as many digits as will be needed.
2009 if (((length - 1) & 0xF0000000) != 0)
2010 use_digits = 8; /* need all 8 digits */
2011 else if (((length - 1) & 0x0F000000) != 0)
2012 use_digits = 7; /* need 7 digits */
2013 else if (((length - 1) & 0x00F00000) != 0)
2014 use_digits = 6; /* need 6 digits */
2015 else if (((length - 1) & 0x000F0000) != 0)
2016 use_digits = 5; /* need 5 digits */
2017 else
2018 use_digits = 4; /* we'll supply 4 digits */
2020 ad = 0;
2021 i = 0;
2022 j = 0;
2023 k = 0;
2024 while (i < length) {
2025 if ((i & 15) == 0) {
2027 * Start of a new line.
2029 j = 0;
2030 l = use_digits;
2031 do {
2032 l--;
2033 c = (ad >> (l*4)) & 0xF;
2034 line[j++] = binhex[c];
2035 } while (l != 0);
2036 line[j++] = ' ';
2037 line[j++] = ' ';
2038 memset(line+j, ' ', DATA_DUMP_LEN);
2041 * Offset in line of ASCII dump.
2043 k = j + HEX_DUMP_LEN + 2;
2045 c = *cp++;
2046 line[j++] = binhex[c>>4];
2047 line[j++] = binhex[c&0xf];
2048 j++;
2049 if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) {
2050 c = EBCDIC_to_ASCII1(c);
2052 line[k++] = ((c >= ' ') && (c < 0x7f)) ? c : '.';
2053 i++;
2054 if (((i & 15) == 0) || (i == length)) {
2056 * We'll be starting a new line, or
2057 * we're finished printing this buffer;
2058 * dump out the line we've constructed,
2059 * and advance the offset.
2061 line[k] = '\0';
2062 if (!print_line(stream, 0, line))
2063 return FALSE;
2064 ad += 16;
2067 return TRUE;
2070 gsize output_fields_num_fields(output_fields_t* fields)
2072 g_assert(fields);
2074 if (NULL == fields->fields) {
2075 return 0;
2076 } else {
2077 return fields->fields->len;
2081 void output_fields_free(output_fields_t* fields)
2083 g_assert(fields);
2085 if (NULL != fields->fields) {
2086 gsize i;
2088 if (NULL != fields->field_indicies) {
2089 /* Keys are stored in fields->fields, values are
2090 * integers.
2092 g_hash_table_destroy(fields->field_indicies);
2095 if (NULL != fields->field_values) {
2096 g_free(fields->field_values);
2099 for (i = 0; i < fields->fields->len; ++i) {
2100 gchar* field = (gchar *)g_ptr_array_index(fields->fields,i);
2101 g_free(field);
2103 g_ptr_array_free(fields->fields, TRUE);
2106 g_free(fields);
2109 #define COLUMN_FIELD_FILTER "_ws.col."
2111 void output_fields_add(output_fields_t *fields, const gchar *field)
2113 gchar *field_copy;
2115 g_assert(fields);
2116 g_assert(field);
2119 if (NULL == fields->fields) {
2120 fields->fields = g_ptr_array_new();
2123 field_copy = g_strdup(field);
2125 g_ptr_array_add(fields->fields, field_copy);
2127 /* See if we have a column as a field entry */
2128 if (!strncmp(field, COLUMN_FIELD_FILTER, strlen(COLUMN_FIELD_FILTER)))
2129 fields->includes_col_fields = TRUE;
2133 static void
2134 output_field_check(void *data, void *user_data)
2136 gchar *field = (gchar *)data;
2137 GSList **invalid_fields = (GSList **)user_data;
2139 if (!strncmp(field, COLUMN_FIELD_FILTER, strlen(COLUMN_FIELD_FILTER)))
2140 return;
2142 if (!proto_registrar_get_byname(field)) {
2143 *invalid_fields = g_slist_prepend(*invalid_fields, field);
2148 GSList *
2149 output_fields_valid(output_fields_t *fields)
2151 GSList *invalid_fields = NULL;
2152 if (fields->fields == NULL) {
2153 return NULL;
2156 g_ptr_array_foreach(fields->fields, output_field_check, &invalid_fields);
2158 return invalid_fields;
2161 gboolean output_fields_set_option(output_fields_t *info, gchar *option)
2163 const gchar *option_name;
2164 const gchar *option_value;
2166 g_assert(info);
2167 g_assert(option);
2169 if ('\0' == *option) {
2170 return FALSE; /* this happens if we're called from tshark -E '' */
2172 option_name = strtok(option, "=");
2173 if (!option_name) {
2174 return FALSE;
2176 option_value = option + strlen(option_name) + 1;
2177 if (*option_value == '\0') {
2178 return FALSE;
2181 if (0 == strcmp(option_name, "header")) {
2182 switch (*option_value) {
2183 case 'n':
2184 info->print_header = FALSE;
2185 break;
2186 case 'y':
2187 info->print_header = TRUE;
2188 break;
2189 default:
2190 return FALSE;
2192 return TRUE;
2194 else if (0 == strcmp(option_name, "separator")) {
2195 switch (*option_value) {
2196 case '/':
2197 switch (*++option_value) {
2198 case 't':
2199 info->separator = '\t';
2200 break;
2201 case 's':
2202 info->separator = ' ';
2203 break;
2204 default:
2205 info->separator = '\\';
2207 break;
2208 default:
2209 info->separator = *option_value;
2210 break;
2212 return TRUE;
2214 else if (0 == strcmp(option_name, "occurrence")) {
2215 switch (*option_value) {
2216 case 'f':
2217 case 'l':
2218 case 'a':
2219 info->occurrence = *option_value;
2220 break;
2221 default:
2222 return FALSE;
2224 return TRUE;
2226 else if (0 == strcmp(option_name, "aggregator")) {
2227 switch (*option_value) {
2228 case '/':
2229 switch (*++option_value) {
2230 case 's':
2231 info->aggregator = ' ';
2232 break;
2233 default:
2234 info->aggregator = '\\';
2236 break;
2237 default:
2238 info->aggregator = *option_value;
2239 break;
2241 return TRUE;
2243 else if (0 == strcmp(option_name, "quote")) {
2244 switch (*option_value) {
2245 case 'd':
2246 info->quote = '"';
2247 break;
2248 case 's':
2249 info->quote = '\'';
2250 break;
2251 case 'n':
2252 info->quote = '\0';
2253 break;
2254 default:
2255 info->quote = '\0';
2256 return FALSE;
2258 return TRUE;
2260 else if (0 == strcmp(option_name, "bom")) {
2261 switch (*option_value) {
2262 case 'n':
2263 info->print_bom = FALSE;
2264 break;
2265 case 'y':
2266 info->print_bom = TRUE;
2267 break;
2268 default:
2269 return FALSE;
2271 return TRUE;
2274 return FALSE;
2277 void output_fields_list_options(FILE *fh)
2279 fprintf(fh, "TShark: The available options for field output \"E\" are:\n");
2280 fputs("bom=y|n Prepend output with the UTF-8 BOM (def: N: no)\n", fh);
2281 fputs("header=y|n Print field abbreviations as first line of output (def: N: no)\n", fh);
2282 fputs("separator=/t|/s|<character> Set the separator to use;\n \"/t\" = tab, \"/s\" = space (def: /t: tab)\n", fh);
2283 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);
2284 fputs("aggregator=,|/s|<character> Set the aggregator to use;\n \",\" = comma, \"/s\" = space (def: ,: comma)\n", fh);
2285 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);
2288 gboolean output_fields_has_cols(output_fields_t* fields)
2290 g_assert(fields);
2291 return fields->includes_col_fields;
2294 void write_fields_preamble(output_fields_t* fields, FILE *fh)
2296 gsize i;
2298 g_assert(fields);
2299 g_assert(fh);
2300 g_assert(fields->fields);
2302 if (fields->print_bom) {
2303 fputs(UTF8_BOM, fh);
2307 if (!fields->print_header) {
2308 return;
2311 for(i = 0; i < fields->fields->len; ++i) {
2312 const gchar* field = (const gchar *)g_ptr_array_index(fields->fields,i);
2313 if (i != 0 ) {
2314 fputc(fields->separator, fh);
2316 fputs(field, fh);
2318 fputc('\n', fh);
2321 static void format_field_values(output_fields_t* fields, gpointer field_index, gchar* value)
2323 guint indx;
2324 GPtrArray* fv_p;
2326 if (NULL == value)
2327 return;
2329 /* Unwrap change made to disambiguiate zero / null */
2330 indx = GPOINTER_TO_UINT(field_index) - 1;
2332 if (fields->field_values[indx] == NULL) {
2333 fields->field_values[indx] = g_ptr_array_new();
2336 /* Essentially: fieldvalues[indx] is a 'GPtrArray *' with each array entry */
2337 /* pointing to a string which is (part of) the final output string. */
2339 fv_p = fields->field_values[indx];
2341 switch (fields->occurrence) {
2342 case 'f':
2343 /* print the value of only the first occurrence of the field */
2344 if (g_ptr_array_len(fv_p) != 0) {
2346 * This isn't the first occurrence, so the value won't be used;
2347 * free it.
2349 g_free(value);
2350 return;
2352 break;
2353 case 'l':
2354 /* print the value of only the last occurrence of the field */
2355 if (g_ptr_array_len(fv_p) != 0) {
2357 * This isn't the first occurrence, so there's already a
2358 * value in the array, which won't be used; free the
2359 * first (only) element in the array, and then remove
2360 * it - this value will replace it.
2362 g_free(g_ptr_array_index(fv_p, 0));
2363 g_ptr_array_set_size(fv_p, 0);
2365 break;
2366 case 'a':
2367 /* print the value of all accurrences of the field */
2368 if (g_ptr_array_len(fv_p) != 0) {
2370 * This isn't the first occurrence. so add the "aggregator"
2371 * character as a separator between the previous element
2372 * and this element.
2374 g_ptr_array_add(fv_p, (gpointer)g_strdup_printf("%c", fields->aggregator));
2376 break;
2377 default:
2378 g_assert_not_reached();
2379 break;
2382 g_ptr_array_add(fv_p, (gpointer)value);
2385 static void proto_tree_get_node_field_values(proto_node *node, gpointer data)
2387 write_field_data_t *call_data;
2388 field_info *fi;
2389 gpointer field_index;
2391 call_data = (write_field_data_t *)data;
2392 fi = PNODE_FINFO(node);
2394 /* dissection with an invisible proto tree? */
2395 g_assert(fi);
2397 field_index = g_hash_table_lookup(call_data->fields->field_indicies, fi->hfinfo->abbrev);
2398 if (NULL != field_index) {
2399 format_field_values(call_data->fields, field_index,
2400 get_node_field_value(fi, call_data->edt) /* g_ alloc'd string */
2404 /* Recurse here. */
2405 if (node->first_child != NULL) {
2406 proto_tree_children_foreach(node, proto_tree_get_node_field_values,
2407 call_data);
2411 static void write_specified_fields(fields_format format, output_fields_t *fields, epan_dissect_t *edt, column_info *cinfo, FILE *fh, json_dumper *dumper)
2413 gsize i;
2414 gint col;
2415 gchar *col_name;
2416 gpointer field_index;
2418 write_field_data_t data;
2420 g_assert(fields);
2421 g_assert(fields->fields);
2422 g_assert(edt);
2423 /* JSON formats must go through json_dumper */
2424 if (format == FORMAT_JSON || format == FORMAT_EK) {
2425 g_assert(!fh && dumper);
2426 } else {
2427 g_assert(fh && !dumper);
2430 data.fields = fields;
2431 data.edt = edt;
2433 if (NULL == fields->field_indicies) {
2434 /* Prepare a lookup table from string abbreviation for field to its index. */
2435 fields->field_indicies = g_hash_table_new(g_str_hash, g_str_equal);
2437 i = 0;
2438 while (i < fields->fields->len) {
2439 gchar *field = (gchar *)g_ptr_array_index(fields->fields, i);
2440 /* Store field indicies +1 so that zero is not a valid value,
2441 * and can be distinguished from NULL as a pointer.
2443 ++i;
2444 g_hash_table_insert(fields->field_indicies, field, GUINT_TO_POINTER(i));
2448 /* Array buffer to store values for this packet */
2449 /* Allocate an array for the 'GPtrarray *' the first time */
2450 /* ths function is invoked for a file; */
2451 /* Any and all 'GPtrArray *' are freed (after use) each */
2452 /* time (each packet) this function is invoked for a flle. */
2453 /* XXX: ToDo: use packet-scope'd memory & (if/when implemented) wmem ptr_array */
2454 if (NULL == fields->field_values)
2455 fields->field_values = g_new0(GPtrArray*, fields->fields->len); /* free'd in output_fields_free() */
2457 proto_tree_children_foreach(edt->tree, proto_tree_get_node_field_values,
2458 &data);
2460 /* Add columns to fields */
2461 if (fields->includes_col_fields) {
2462 for (col = 0; col < cinfo->num_cols; col++) {
2463 if (!get_column_visible(col))
2464 continue;
2465 /* Prepend COLUMN_FIELD_FILTER as the field name */
2466 col_name = g_strdup_printf("%s%s", COLUMN_FIELD_FILTER, cinfo->columns[col].col_title);
2467 field_index = g_hash_table_lookup(fields->field_indicies, col_name);
2468 g_free(col_name);
2470 if (NULL != field_index) {
2471 format_field_values(fields, field_index, g_strdup(cinfo->columns[col].col_data));
2476 switch (format) {
2477 case FORMAT_CSV:
2478 for(i = 0; i < fields->fields->len; ++i) {
2479 if (0 != i) {
2480 fputc(fields->separator, fh);
2482 if (NULL != fields->field_values[i]) {
2483 GPtrArray *fv_p;
2484 gchar * str;
2485 gsize j;
2486 fv_p = fields->field_values[i];
2487 if (fields->quote != '\0') {
2488 fputc(fields->quote, fh);
2491 /* Output the array of (partial) field values */
2492 for (j = 0; j < g_ptr_array_len(fv_p); j++ ) {
2493 str = (gchar *)g_ptr_array_index(fv_p, j);
2494 print_escaped_csv(fh, str);
2495 g_free(str);
2497 if (fields->quote != '\0') {
2498 fputc(fields->quote, fh);
2500 g_ptr_array_free(fv_p, TRUE); /* get ready for the next packet */
2501 fields->field_values[i] = NULL;
2504 break;
2505 case FORMAT_XML:
2506 for(i = 0; i < fields->fields->len; ++i) {
2507 gchar *field = (gchar *)g_ptr_array_index(fields->fields, i);
2509 if (NULL != fields->field_values[i]) {
2510 GPtrArray *fv_p;
2511 gchar * str;
2512 gsize j;
2513 fv_p = fields->field_values[i];
2515 /* Output the array of (partial) field values */
2516 for (j = 0; j < (g_ptr_array_len(fv_p)); j+=2 ) {
2517 str = (gchar *)g_ptr_array_index(fv_p, j);
2519 fprintf(fh, " <field name=\"%s\" value=", field);
2520 fputs("\"", fh);
2521 print_escaped_xml(fh, str);
2522 fputs("\"/>\n", fh);
2523 g_free(str);
2525 g_ptr_array_free(fv_p, TRUE); /* get ready for the next packet */
2526 fields->field_values[i] = NULL;
2529 break;
2530 case FORMAT_JSON:
2531 json_dumper_begin_object(dumper);
2532 for(i = 0; i < fields->fields->len; ++i) {
2533 gchar *field = (gchar *)g_ptr_array_index(fields->fields, i);
2535 if (NULL != fields->field_values[i]) {
2536 GPtrArray *fv_p;
2537 gchar * str;
2538 gsize j;
2539 fv_p = fields->field_values[i];
2541 json_dumper_set_member_name(dumper, field);
2542 json_dumper_begin_array(dumper);
2544 /* Output the array of (partial) field values */
2545 for (j = 0; j < (g_ptr_array_len(fv_p)); j += 2) {
2546 str = (gchar *) g_ptr_array_index(fv_p, j);
2547 json_dumper_value_string(dumper, str);
2548 g_free(str);
2551 json_dumper_end_array(dumper);
2553 g_ptr_array_free(fv_p, TRUE); /* get ready for the next packet */
2554 fields->field_values[i] = NULL;
2557 json_dumper_end_object(dumper);
2558 break;
2559 case FORMAT_EK:
2560 for(i = 0; i < fields->fields->len; ++i) {
2561 gchar *field = (gchar *)g_ptr_array_index(fields->fields, i);
2563 if (NULL != fields->field_values[i]) {
2564 GPtrArray *fv_p;
2565 gchar * str;
2566 gsize j;
2567 fv_p = fields->field_values[i];
2569 json_dumper_set_member_name(dumper, field);
2570 json_dumper_begin_array(dumper);
2572 /* Output the array of (partial) field values */
2573 for (j = 0; j < (g_ptr_array_len(fv_p)); j += 2) {
2574 str = (gchar *)g_ptr_array_index(fv_p, j);
2575 json_dumper_value_string(dumper, str);
2576 g_free(str);
2579 json_dumper_end_array(dumper);
2581 g_ptr_array_free(fv_p, TRUE); /* get ready for the next packet */
2582 fields->field_values[i] = NULL;
2585 break;
2587 default:
2588 fprintf(stderr, "Unknown fields format %d\n", format);
2589 g_assert_not_reached();
2590 break;
2594 void write_fields_finale(output_fields_t* fields _U_ , FILE *fh _U_)
2596 /* Nothing to do */
2599 /* Returns an g_malloced string */
2600 gchar* get_node_field_value(field_info* fi, epan_dissect_t* edt)
2602 if (fi->hfinfo->id == hf_text_only) {
2603 /* Text label.
2604 * Get the text */
2605 if (fi->rep) {
2606 return g_strdup(fi->rep->representation);
2608 else {
2609 return get_field_hex_value(edt->pi.data_src, fi);
2612 else if (fi->hfinfo->id == proto_data) {
2613 /* Uninterpreted data, i.e., the "Data" protocol, is
2614 * printed as a field instead of a protocol. */
2615 return get_field_hex_value(edt->pi.data_src, fi);
2617 else {
2618 /* Normal protocols and fields */
2619 gchar *dfilter_string;
2621 switch (fi->hfinfo->type)
2623 case FT_PROTOCOL:
2624 /* Print out the full details for the protocol. */
2625 if (fi->rep) {
2626 return g_strdup(fi->rep->representation);
2627 } else {
2628 /* Just print out the protocol abbreviation */
2629 return g_strdup(fi->hfinfo->abbrev);
2631 case FT_NONE:
2632 /* Return "1" so that the presence of a field of type
2633 * FT_NONE can be checked when using -T fields */
2634 return g_strdup("1");
2635 case FT_UINT_BYTES:
2636 case FT_BYTES:
2638 gchar *ret;
2639 guint8 *bytes = (guint8 *)fvalue_get(&fi->value);
2640 if (bytes) {
2641 dfilter_string = (gchar *)wmem_alloc(NULL, 3*fvalue_length(&fi->value));
2642 switch (fi->hfinfo->display) {
2643 case SEP_DOT:
2644 ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length(&fi->value), '.');
2645 break;
2646 case SEP_DASH:
2647 ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length(&fi->value), '-');
2648 break;
2649 case SEP_COLON:
2650 ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length(&fi->value), ':');
2651 break;
2652 case SEP_SPACE:
2653 ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length(&fi->value), ' ');
2654 break;
2655 case BASE_NONE:
2656 default:
2657 ret = bytes_to_hexstr(dfilter_string, bytes, fvalue_length(&fi->value));
2658 break;
2660 *ret = '\0';
2661 ret = g_strdup(dfilter_string);
2662 wmem_free(NULL, dfilter_string);
2663 } else {
2664 if (fi->hfinfo->display & BASE_ALLOW_ZERO) {
2665 ret = g_strdup("<none>");
2666 } else {
2667 ret = g_strdup("<MISSING>");
2670 return ret;
2672 break;
2673 default:
2674 dfilter_string = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
2675 if (dfilter_string != NULL) {
2676 gchar* ret = g_strdup(dfilter_string);
2677 wmem_free(NULL, dfilter_string);
2678 return ret;
2679 } else {
2680 return get_field_hex_value(edt->pi.data_src, fi);
2686 static gchar*
2687 get_field_hex_value(GSList *src_list, field_info *fi)
2689 const guint8 *pd;
2691 if (!fi->ds_tvb)
2692 return NULL;
2694 if (fi->length > tvb_captured_length_remaining(fi->ds_tvb, fi->start)) {
2695 return g_strdup("field length invalid!");
2698 /* Find the data for this field. */
2699 pd = get_field_data(src_list, fi);
2701 if (pd) {
2702 int i;
2703 gchar *buffer;
2704 gchar *p;
2705 int len;
2706 const int chars_per_byte = 2;
2708 len = chars_per_byte * fi->length;
2709 buffer = (gchar *)g_malloc(sizeof(gchar)*(len + 1));
2710 buffer[len] = '\0'; /* Ensure NULL termination in bad cases */
2711 p = buffer;
2712 /* Print a simple hex dump */
2713 for (i = 0 ; i < fi->length; i++) {
2714 g_snprintf(p, chars_per_byte+1, "%02x", pd[i]);
2715 p += chars_per_byte;
2717 return buffer;
2718 } else {
2719 return NULL;
2723 output_fields_t* output_fields_new(void)
2725 output_fields_t* fields = g_new(output_fields_t, 1);
2726 fields->print_bom = FALSE;
2727 fields->print_header = FALSE;
2728 fields->separator = '\t';
2729 fields->occurrence = 'a';
2730 fields->aggregator = ',';
2731 fields->fields = NULL; /*Do lazy initialisation */
2732 fields->field_indicies = NULL;
2733 fields->field_values = NULL;
2734 fields->quote ='\0';
2735 fields->includes_col_fields = FALSE;
2736 return fields;
2740 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2742 * Local variables:
2743 * c-basic-offset: 4
2744 * tab-width: 8
2745 * indent-tabs-mode: nil
2746 * End:
2748 * vi: set shiftwidth=4 tabstop=8 expandtab:
2749 * :indentSize=4:tabSize=8:noTabs=true: