aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDario Lombardo <lomato@gmail.com>2018-12-10 13:44:03 +0100
committerPeter Wu <peter@lekensteyn.nl>2019-01-03 14:33:48 +0000
commit30c90fa745af6b63da6aa6b59e4fbb24d2f195bd (patch)
tree02bdfdbc37fe7f9178167e1cef6bd71bcc66af7c
parent19d787d0514696eff4b723d23c7615155c23b245 (diff)
epan: use json_dumper for json outputs.
They include -Tjson, -Tjsonraw, -Tek. Change-Id: Ib3d700482ce5c29727c3f778cc3c46a1bf7756c4 Reviewed-on: https://code.wireshark.org/review/31000 Petri-Dish: Dario Lombardo <lomato@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu <peter@lekensteyn.nl>
-rw-r--r--epan/print.c539
-rw-r--r--epan/print.h9
-rw-r--r--file.c9
-rw-r--r--tshark.c15
4 files changed, 216 insertions, 356 deletions
diff --git a/epan/print.c b/epan/print.c
index ac4ce1db57..ad99f42168 100644
--- a/epan/print.c
+++ b/epan/print.c
@@ -27,6 +27,7 @@
#include <epan/prefs.h>
#include <epan/print.h>
#include <epan/charsets.h>
+#include <wsutil/json_dumper.h>
#include <wsutil/filesystem.h>
#include <version_info.h>
#include <wsutil/utf8_entities.h>
@@ -55,14 +56,13 @@ typedef struct {
} write_pdml_data;
typedef struct {
- int level;
- FILE *fh;
GSList *src_list;
gchar **filter;
pf_flags filter_flags;
gboolean print_hex;
gboolean print_text;
proto_node_children_grouper_func node_children_grouper;
+ json_dumper *dumper;
} write_json_data;
typedef struct {
@@ -95,13 +95,13 @@ static gboolean print_hex_data_buffer(print_stream_t *stream, const guchar *cp,
static void write_specified_fields(fields_format format,
output_fields_t *fields,
epan_dissect_t *edt, column_info *cinfo,
- FILE *fh);
+ FILE *fh,
+ json_dumper *dumper);
static void print_escaped_xml(FILE *fh, const char *unescaped_string);
-static void print_escaped_json(FILE *fh, const char *unescaped_string);
-static void print_escaped_ek(FILE *fh, const char *unescaped_string);
static void print_escaped_csv(FILE *fh, const char *unescaped_string);
typedef void (*proto_node_value_writer)(proto_node *, write_json_data *);
+static void write_json_index(json_dumper *dumper, epan_dissect_t *edt);
static void write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *data);
static void write_json_proto_node(GSList *node_values_head,
const char *suffix,
@@ -118,12 +118,10 @@ static void write_json_proto_node_no_value(proto_node *node, write_json_data *da
static const char *proto_node_to_json_key(proto_node *node);
static void print_pdml_geninfo(epan_dissect_t *edt, FILE *fh);
-static void write_ek_summary(column_info *cinfo, FILE *fh);
+static void write_ek_summary(column_info *cinfo, write_json_data *pdata);
static void proto_tree_get_node_field_values(proto_node *node, gpointer data);
-static gboolean json_is_first;
-
/* Cache the protocols and field handles that the print functionality needs
This helps break explicit dependency on the dissectors. */
static int proto_data = -1;
@@ -335,7 +333,7 @@ write_pdml_proto_tree(output_fields_t* fields, gchar **protocolfilter, pf_flags
&data);
} else {
/* Write out specified fields */
- write_specified_fields(FORMAT_XML, fields, edt, cinfo, fh);
+ write_specified_fields(FORMAT_XML, fields, edt, cinfo, fh, NULL);
}
fprintf(fh, "</packet>\n\n");
@@ -349,49 +347,56 @@ write_ek_proto_tree(output_fields_t* fields,
column_info *cinfo,
FILE *fh)
{
- write_json_data data;
- char ts[30];
- struct tm *timeinfo;
-
g_assert(edt);
g_assert(fh);
- /* Create the output */
- timeinfo = localtime(&edt->pi.abs_ts.secs);
- if (timeinfo != NULL)
- strftime(ts, sizeof ts, "%Y-%m-%d", timeinfo);
- else
- g_strlcpy(ts, "XXXX-XX-XX", sizeof ts); /* XXX - better way of saying "Not representable"? */
+ write_json_data data;
+
+ json_dumper dumper = {
+ .output_file = fh,
+ .flags = JSON_DUMPER_DOT_TO_UNDERSCORE
+ };
+
+ data.dumper = &dumper;
+
+ json_dumper_begin_object(&dumper);
+ json_dumper_set_member_name(&dumper, "index");
+ json_dumper_begin_object(&dumper);
+ write_json_index(&dumper, edt);
+ json_dumper_set_member_name(&dumper, "_type");
+ json_dumper_value_string(&dumper, "pcap_file");
+ json_dumper_end_object(&dumper);
+ json_dumper_end_object(&dumper);
+ json_dumper_finish(&dumper);
+ json_dumper_begin_object(&dumper);
- fprintf(fh, "{\"index\" : {\"_index\": \"packets-%s\", \"_type\": \"pcap_file\"}}\n", ts);
/* Timestamp added for time indexing in Elasticsearch */
- fprintf(fh, "{\"timestamp\" : \"%" G_GUINT64_FORMAT "%03d\"", (guint64)edt->pi.abs_ts.secs, edt->pi.abs_ts.nsecs/1000000);
+ json_dumper_set_member_name(&dumper, "timestamp");
+ json_dumper_value_anyf(&dumper, "\"%" G_GUINT64_FORMAT "%03d\"", (guint64)edt->pi.abs_ts.secs, edt->pi.abs_ts.nsecs/1000000);
if (print_summary)
- write_ek_summary(edt->pi.cinfo, fh);
+ write_ek_summary(edt->pi.cinfo, &data);
if (edt->tree) {
- fprintf(fh, ", \"layers\" : {");
+ json_dumper_set_member_name(&dumper, "layers");
+ json_dumper_begin_object(&dumper);
if (fields == NULL || fields->fields == NULL) {
/* Write out all fields */
- data.level = 0;
- data.fh = fh;
data.src_list = edt->pi.data_src;
- data.filter = protocolfilter;
+ data.filter = protocolfilter;
data.filter_flags = protocolfilter_flags;
data.print_hex = print_hex;
-
proto_tree_write_node_ek(edt->tree, &data);
} else {
/* Write out specified fields */
- write_specified_fields(FORMAT_EK, fields, edt, cinfo, fh);
+ write_specified_fields(FORMAT_EK, fields, edt, cinfo, fh, data.dumper);
}
- fputs("}", fh);
+ json_dumper_end_object(&dumper);
}
-
- fputs("}\n", fh);
+ json_dumper_end_object(&dumper);
+ json_dumper_finish(&dumper);
}
void
@@ -401,7 +406,7 @@ write_fields_proto_tree(output_fields_t* fields, epan_dissect_t *edt, column_inf
g_assert(fh);
/* Create the output */
- write_specified_fields(FORMAT_CSV, fields, edt, cinfo, fh);
+ write_specified_fields(FORMAT_CSV, fields, edt, cinfo, fh, NULL);
}
/* Indent to the correct level */
@@ -676,17 +681,41 @@ proto_tree_write_node_pdml(proto_node *node, gpointer data)
}
}
-void
+json_dumper
write_json_preamble(FILE *fh)
{
- fputs("[\n", fh);
- json_is_first = TRUE;
+ json_dumper dumper = {
+ .output_file = fh,
+ .flags = JSON_DUMPER_FLAGS_PRETTY_PRINT
+ };
+ json_dumper_begin_array(&dumper);
+ return dumper;
}
void
-write_json_finale(FILE *fh)
+write_json_finale(json_dumper *dumper)
{
- fputs("\n\n]\n", fh);
+ json_dumper_end_array(dumper);
+ json_dumper_finish(dumper);
+}
+
+static void
+write_json_index(json_dumper *dumper, epan_dissect_t *edt)
+{
+ char ts[30];
+ struct tm * timeinfo;
+ gchar* str;
+
+ timeinfo = localtime(&edt->pi.abs_ts.secs);
+ if (timeinfo != NULL) {
+ strftime(ts, sizeof ts, "%Y-%m-%d", timeinfo);
+ } else {
+ g_strlcpy(ts, "XXXX-XX-XX", sizeof ts); /* XXX - better way of saying "Not representable"? */
+ }
+ json_dumper_set_member_name(dumper, "_index");
+ str = g_strdup_printf("packets-%s", ts);
+ json_dumper_value_string(dumper, str);
+ g_free(str);
}
void
@@ -696,38 +725,26 @@ write_json_proto_tree(output_fields_t* fields,
pf_flags protocolfilter_flags, epan_dissect_t *edt,
column_info *cinfo,
proto_node_children_grouper_func node_children_grouper,
- FILE *fh)
+ json_dumper *dumper)
{
- char ts[30];
- struct tm *timeinfo;
write_json_data data;
- if (!json_is_first) {
- fputs("\n\n ,\n", fh);
- } else {
- json_is_first = FALSE;
- }
-
- timeinfo = localtime(&edt->pi.abs_ts.secs);
- if (timeinfo != NULL) {
- strftime(ts, sizeof ts, "%Y-%m-%d", timeinfo);
- } else {
- g_strlcpy(ts, "XXXX-XX-XX", sizeof ts); /* XXX - better way of saying "Not representable"? */
- }
+ data.dumper = dumper;
- fputs(" {\n", fh);
- fprintf(fh, " \"_index\": \"packets-%s\",\n", ts);
- fputs(" \"_type\": \"pcap_file\",\n", fh);
- fputs(" \"_score\": null,\n", fh);
- fputs(" \"_source\": {\n", fh);
- fputs(" \"layers\": ", fh);
+ json_dumper_begin_object(dumper);
+ write_json_index(dumper, edt);
+ json_dumper_set_member_name(dumper, "_type");
+ json_dumper_value_string(dumper, "pcap_file");
+ json_dumper_set_member_name(dumper, "_score");
+ json_dumper_value_string(dumper, NULL);
+ json_dumper_set_member_name(dumper, "_source");
+ json_dumper_begin_object(dumper);
+ json_dumper_set_member_name(dumper, "layers");
if (fields == NULL || fields->fields == NULL) {
/* Write out all fields */
- data.level = 3;
- data.fh = fh;
data.src_list = edt->pi.data_src;
- data.filter = protocolfilter;
+ data.filter = protocolfilter;
data.filter_flags = protocolfilter_flags;
data.print_hex = print_hex;
data.print_text = TRUE;
@@ -738,12 +755,11 @@ write_json_proto_tree(output_fields_t* fields,
write_json_proto_node_children(edt->tree, &data);
} else {
- write_specified_fields(FORMAT_JSON, fields, edt, cinfo, fh);
+ write_specified_fields(FORMAT_JSON, fields, edt, cinfo, NULL, dumper);
}
- fputs("\n", fh);
- fputs(" }\n", fh);
- fputs(" }", fh);
+ json_dumper_end_object(dumper);
+ json_dumper_end_object(dumper);
}
/**
@@ -754,24 +770,11 @@ write_json_proto_tree(output_fields_t* fields,
* @param data json writing metadata
*/
static void
-write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *data)
+write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *pdata)
{
GSList *current_node = proto_node_list_head;
- fputs("{\n", data->fh);
- data->level++;
-
- /*
- * In most of the following if statements we cannot be sure if its the first or last if statement to be
- * executed. Thus we need a way of knowing whether a key:value pair has already been printed in order to know
- * if a comma should be printed before the next key:value pair. We use the delimiter_needed variable to store
- * whether a comma needs to be written before a new key:value pair is written. Note that instead of checking
- * before writing a new key:value pair if a comma is needed we could also check after writing a key:value pair
- * whether a comma is needed but this would be considerably more complex since after each if statement a
- * different condition would have to be checked. After the first value is written a delimiter is always needed so
- * this value is never set back to FALSE after it has been set to TRUE.
- */
- gboolean delimiter_needed = FALSE;
+ json_dumper_begin_object(pdata->dumper);
// Loop over each list of nodes (differentiated by json key) and write the associated json key:value pair in the
// output.
@@ -783,7 +786,7 @@ write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *data)
proto_node *first_value = (proto_node *) node_values_list->data;
const char *json_key = proto_node_to_json_key(first_value);
// Check if the current json key is filtered from the output with the "-j" cli option.
- gboolean is_filtered = data->filter != NULL && !check_protocolfilter(data->filter, json_key);
+ gboolean is_filtered = pdata->filter != NULL && !check_protocolfilter(pdata->filter, json_key);
field_info *fi = first_value->finfo;
char *value_string_repr = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
@@ -800,62 +803,48 @@ write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *data)
// with the original json key. If both hex and text writing are enabled the raw information of fields whose
// length is equal to 0 is not written to the output. If the field is a special text pseudo field no raw
// information is written either.
- if (data->print_hex && (!data->print_text || fi->length > 0) && !is_pseudo_text_field) {
- if (delimiter_needed) fputs(",\n", data->fh);
- write_json_proto_node(node_values_list, "_raw", write_json_proto_node_hex_dump, data);
- delimiter_needed = TRUE;
+ if (pdata->print_hex && (!pdata->print_text || fi->length > 0) && !is_pseudo_text_field) {
+ write_json_proto_node(node_values_list, "_raw", write_json_proto_node_hex_dump, pdata);
}
- if (data->print_text && has_value) {
- if (delimiter_needed) fputs(",\n", data->fh);
- write_json_proto_node(node_values_list, "", write_json_proto_node_value, data);
- delimiter_needed = TRUE;
+ if (pdata->print_text && has_value) {
+ write_json_proto_node(node_values_list, "", write_json_proto_node_value, pdata);
}
if (has_children) {
- if (delimiter_needed) fputs(",\n", data->fh);
-
// If a node has both a value and a set of children we print the value and the children in separate
// key:value pairs. These can't have the same key so whenever a value is already printed with the node
// json key we print the children with the same key with a "_tree" suffix added.
char *suffix = has_value ? "_tree": "";
if (is_filtered) {
- write_json_proto_node(node_values_list, suffix, write_json_proto_node_filtered, data);
+ write_json_proto_node(node_values_list, suffix, write_json_proto_node_filtered, pdata);
} else {
// Remove protocol filter for children, if children should be included. This functionality is enabled
// with the "-J" command line option. We save the filter so it can be reenabled when we are done with
// the current key:value pair.
gchar **_filter = NULL;
- if ((data->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
- _filter = data->filter;
- data->filter = NULL;
+ if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
+ _filter = pdata->filter;
+ pdata->filter = NULL;
}
- write_json_proto_node(node_values_list, suffix, write_json_proto_node_children, data);
+ write_json_proto_node(node_values_list, suffix, write_json_proto_node_children, pdata);
// Put protocol filter back
- if ((data->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
- data->filter = _filter;
+ if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
+ pdata->filter = _filter;
}
}
-
- delimiter_needed = TRUE;
}
- if (!has_value && !has_children && (data->print_text || (data->print_hex && is_pseudo_text_field))) {
- if (delimiter_needed) fputs(",\n", data->fh);
- write_json_proto_node(node_values_list, "", write_json_proto_node_no_value, data);
- delimiter_needed = TRUE;
+ if (!has_value && !has_children && (pdata->print_text || (pdata->print_hex && is_pseudo_text_field))) {
+ write_json_proto_node(node_values_list, "", write_json_proto_node_no_value, pdata);
}
current_node = current_node->next;
}
-
- data->level--;
- fputs("\n", data->fh);
- print_indent(data->level, data->fh);
- fputs("}", data->fh);
+ json_dumper_end_object(pdata->dumper);
}
/**
@@ -870,19 +859,15 @@ static void
write_json_proto_node(GSList *node_values_head,
const char *suffix,
proto_node_value_writer value_writer,
- write_json_data *data)
+ write_json_data *pdata)
{
// Retrieve json key from first value.
proto_node *first_value = (proto_node *) node_values_head->data;
const char *json_key = proto_node_to_json_key(first_value);
-
- print_indent(data->level, data->fh);
- fputs("\"", data->fh);
- print_escaped_json(data->fh, json_key);
- print_escaped_json(data->fh, suffix);
- fputs("\": ", data->fh);
-
- write_json_proto_node_value_list(node_values_head, value_writer, data);
+ gchar* json_key_suffix = g_strdup_printf("%s%s", json_key, suffix);
+ json_dumper_set_member_name(pdata->dumper, json_key_suffix);
+ g_free(json_key_suffix);
+ write_json_proto_node_value_list(node_values_head, value_writer, pdata);
}
/**
@@ -892,30 +877,21 @@ write_json_proto_node(GSList *node_values_head,
* @param data json writing metadata
*/
static void
-write_json_proto_node_value_list(GSList *node_values_head, proto_node_value_writer value_writer, write_json_data *data)
+write_json_proto_node_value_list(GSList *node_values_head, proto_node_value_writer value_writer, write_json_data *pdata)
{
GSList *current_value = node_values_head;
// Write directly if only a single value is passed. Wrap in json array otherwise.
if (current_value->next == NULL) {
- value_writer((proto_node *) current_value->data, data);
+ value_writer((proto_node *) current_value->data, pdata);
} else {
- fputs("[\n", data->fh);
- data->level++;
+ json_dumper_begin_array(pdata->dumper);
while (current_value != NULL) {
- // Do not print delimiter before first value
- if (current_value != node_values_head) fputs(",\n", data->fh);
-
- print_indent(data->level, data->fh);
- value_writer((proto_node *) current_value->data, data);
+ value_writer((proto_node *) current_value->data, pdata);
current_value = current_value->next;
}
-
- data->level--;
- fputs("\n", data->fh);
- print_indent(data->level, data->fh);
- fputs("]", data->fh);
+ json_dumper_end_array(pdata->dumper);
}
}
@@ -923,22 +899,14 @@ write_json_proto_node_value_list(GSList *node_values_head, proto_node_value_writ
* Writes the value for a node that's filtered from the output.
*/
static void
-write_json_proto_node_filtered(proto_node *node, write_json_data *data)
+write_json_proto_node_filtered(proto_node *node, write_json_data *pdata)
{
const char *json_key = proto_node_to_json_key(node);
- fputs("{\n", data->fh);
- data->level++;
-
- print_indent(data->level, data->fh);
- fputs("\"filtered\": ", data->fh);
- fputs("\"", data->fh);
- print_escaped_json(data->fh, json_key);
- fputs("\"\n", data->fh);
-
- data->level--;
- print_indent(data->level, data->fh);
- fputs("}", data->fh);
+ json_dumper_begin_object(pdata->dumper);
+ json_dumper_set_member_name(pdata->dumper, "filtered");
+ json_dumper_value_string(pdata->dumper, json_key);
+ json_dumper_end_object(pdata->dumper);
}
/**
@@ -946,11 +914,11 @@ write_json_proto_node_filtered(proto_node *node, write_json_data *data)
* the node.
*/
static void
-write_json_proto_node_hex_dump(proto_node *node, write_json_data *data)
+write_json_proto_node_hex_dump(proto_node *node, write_json_data *pdata)
{
field_info *fi = node->finfo;
- fputs("[\"", data->fh);
+ json_dumper_begin_array(pdata->dumper);
if (fi->hfinfo->bitmask!=0) {
switch (fi->value.ftype->ftype) {
@@ -958,42 +926,42 @@ write_json_proto_node_hex_dump(proto_node *node, write_json_data *data)
case FT_INT16:
case FT_INT24:
case FT_INT32:
- fprintf(data->fh, "%X", (guint) fvalue_get_sinteger(&fi->value));
+ json_dumper_value_anyf(pdata->dumper, "\"%X\"", (guint) fvalue_get_sinteger(&fi->value));
break;
case FT_CHAR:
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
- fprintf(data->fh, "%X", fvalue_get_uinteger(&fi->value));
+ json_dumper_value_anyf(pdata->dumper, "\"%X\"", fvalue_get_uinteger(&fi->value));
break;
case FT_INT40:
case FT_INT48:
case FT_INT56:
case FT_INT64:
- fprintf(data->fh, "%" G_GINT64_MODIFIER "X", fvalue_get_sinteger64(&fi->value));
+ json_dumper_value_anyf(pdata->dumper, "\"%" G_GINT64_MODIFIER "X\"", fvalue_get_sinteger64(&fi->value));
break;
case FT_UINT40:
case FT_UINT48:
case FT_UINT56:
case FT_UINT64:
case FT_BOOLEAN:
- fprintf(data->fh, "%" G_GINT64_MODIFIER "X", fvalue_get_uinteger64(&fi->value));
+ json_dumper_value_anyf(pdata->dumper, "\"%" G_GINT64_MODIFIER "X\"", fvalue_get_uinteger64(&fi->value));
break;
default:
g_assert_not_reached();
}
} else {
- json_write_field_hex_value(data, fi);
+ json_write_field_hex_value(pdata, fi);
}
/* Dump raw hex-encoded dissected information including position, length, bitmask, type */
- fprintf(data->fh, "\", %" G_GINT32_MODIFIER "d", fi->start);
- fprintf(data->fh, ", %" G_GINT32_MODIFIER "d", fi->length);
- fprintf(data->fh, ", %" G_GUINT64_FORMAT, fi->hfinfo->bitmask);
- fprintf(data->fh, ", %" G_GINT32_MODIFIER "d", (gint32)fi->value.ftype->ftype);
+ json_dumper_value_anyf(pdata->dumper, "%" G_GINT32_MODIFIER "d", fi->start);
+ json_dumper_value_anyf(pdata->dumper, "%" G_GINT32_MODIFIER "d", fi->length);
+ json_dumper_value_anyf(pdata->dumper, "%" G_GUINT64_FORMAT, fi->hfinfo->bitmask);
+ json_dumper_value_anyf(pdata->dumper, "%" G_GINT32_MODIFIER "d", (gint32)fi->value.ftype->ftype);
- fputs("]", data->fh);
+ json_dumper_end_array(pdata->dumper);
}
/**
@@ -1012,15 +980,13 @@ write_json_proto_node_children(proto_node *node, write_json_data *data)
* Writes the value of a node to the output.
*/
static void
-write_json_proto_node_value(proto_node *node, write_json_data *data)
+write_json_proto_node_value(proto_node *node, write_json_data *pdata)
{
field_info *fi = node->finfo;
// Get the actual value of the node as a string.
char *value_string_repr = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
- fputs("\"", data->fh);
- print_escaped_json(data->fh, value_string_repr);
- fputs("\"", data->fh);
+ json_dumper_value_string(pdata->dumper, value_string_repr);
wmem_free(NULL, value_string_repr);
}
@@ -1030,23 +996,21 @@ write_json_proto_node_value(proto_node *node, write_json_data *data)
* type FT_PROTOCOL for which the full name is written instead.
*/
static void
-write_json_proto_node_no_value(proto_node *node, write_json_data *data)
+write_json_proto_node_no_value(proto_node *node, write_json_data *pdata)
{
field_info *fi = node->finfo;
- fputs("\"", data->fh);
-
if (fi->hfinfo->type == FT_PROTOCOL) {
if (fi->rep) {
- print_escaped_json(data->fh, fi->rep->representation);
+ json_dumper_value_string(pdata->dumper, fi->rep->representation);
} else {
gchar label_str[ITEM_LABEL_LENGTH];
proto_item_fill_label(fi, label_str);
- print_escaped_json(data->fh, label_str);
+ json_dumper_value_string(pdata->dumper, label_str);
}
+ } else {
+ json_dumper_value_string(pdata->dumper, "");
}
-
- fputs("\"", data->fh);
}
/**
@@ -1162,17 +1126,15 @@ ek_check_protocolfilter(gchar **protocolfilter, const char *str)
* Finds a node's descendants to be printed as EK/JSON attributes.
*/
static void
-write_ek_summary(column_info *cinfo, FILE *fh)
+write_ek_summary(column_info *cinfo, write_json_data* pdata)
{
gint i;
for (i = 0; i < cinfo->num_cols; i++) {
- if (!get_column_visible(i)) continue;
- fputs(", \"", fh);
- print_escaped_ek(fh, g_ascii_strdown(cinfo->columns[i].col_title, -1));
- fputs("\": \"", fh);
- print_escaped_json(fh, cinfo->columns[i].col_data);
- fputs("\"", fh);
+ if (!get_column_visible(i))
+ continue;
+ json_dumper_set_member_name(pdata->dumper, g_ascii_strdown(cinfo->columns[i].col_title, -1));
+ json_dumper_value_string(pdata->dumper, cinfo->columns[i].col_data);
}
}
@@ -1248,16 +1210,20 @@ ek_fill_attr(proto_node *node, GSList **attr_list, GHashTable *attr_table, write
}
static void
-ek_write_name(proto_node *pnode, write_json_data *pdata)
+ek_write_name(proto_node *pnode, gchar* suffix, write_json_data* pdata)
{
field_info *fi = PNODE_FINFO(pnode);
field_info *fi_parent = PNODE_FINFO(pnode->parent);
+ gchar *str;
if (fi_parent != NULL) {
- print_escaped_ek(pdata->fh, fi_parent->hfinfo->abbrev);
- fputs("_", pdata->fh);
+ str = g_strdup_printf("%s_%s%s", fi_parent->hfinfo->abbrev, fi->hfinfo->abbrev, suffix ? suffix : "");
+ json_dumper_set_member_name(pdata->dumper, str);
+ } else {
+ str = g_strdup_printf("%s%s", fi->hfinfo->abbrev, suffix ? suffix : "");
+ json_dumper_set_member_name(pdata->dumper, str);
}
- print_escaped_ek(pdata->fh, fi->hfinfo->abbrev);
+ g_free(str);
}
static void
@@ -1269,27 +1235,27 @@ ek_write_hex(field_info *fi, write_json_data *pdata)
case FT_INT16:
case FT_INT24:
case FT_INT32:
- fprintf(pdata->fh, "%X", (guint) fvalue_get_sinteger(&fi->value));
+ json_dumper_value_anyf(pdata->dumper, "%X", (guint) fvalue_get_sinteger(&fi->value));
break;
case FT_CHAR:
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
- fprintf(pdata->fh, "%X", fvalue_get_uinteger(&fi->value));
+ json_dumper_value_anyf(pdata->dumper, "%X", fvalue_get_uinteger(&fi->value));
break;
case FT_INT40:
case FT_INT48:
case FT_INT56:
case FT_INT64:
- fprintf(pdata->fh, "%" G_GINT64_MODIFIER "X", fvalue_get_sinteger64(&fi->value));
+ json_dumper_value_anyf(pdata->dumper, "%" G_GINT64_MODIFIER "X", fvalue_get_sinteger64(&fi->value));
break;
case FT_UINT40:
case FT_UINT48:
case FT_UINT56:
case FT_UINT64:
case FT_BOOLEAN:
- fprintf(pdata->fh, "%" G_GINT64_MODIFIER "X", fvalue_get_uinteger64(&fi->value));
+ json_dumper_value_anyf(pdata->dumper, "%" G_GINT64_MODIFIER "X", fvalue_get_uinteger64(&fi->value));
break;
default:
g_assert_not_reached();
@@ -1301,30 +1267,30 @@ ek_write_hex(field_info *fi, write_json_data *pdata)
}
static void
-ek_write_field_value(field_info *fi, write_json_data *pdata)
+ek_write_field_value(field_info *fi, write_json_data* pdata)
{
gchar label_str[ITEM_LABEL_LENGTH];
char *dfilter_string;
/* Text label */
if (fi->hfinfo->id == hf_text_only && fi->rep) {
- print_escaped_json(pdata->fh, fi->rep->representation);
+ json_dumper_value_string(pdata->dumper, fi->rep->representation);
}
else {
/* show, value, and unmaskedvalue attributes */
if (fi->hfinfo->type == FT_PROTOCOL) {
if (fi->rep) {
- print_escaped_json(pdata->fh, fi->rep->representation);
+ json_dumper_value_string(pdata->dumper, fi->rep->representation);
}
else {
proto_item_fill_label(fi, label_str);
- print_escaped_json(pdata->fh, label_str);
+ json_dumper_value_string(pdata->dumper, label_str);
}
}
else if (fi->hfinfo->type != FT_NONE) {
dfilter_string = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
if (dfilter_string != NULL) {
- print_escaped_json(pdata->fh, dfilter_string);
+ json_dumper_value_string(pdata->dumper, dfilter_string);
}
wmem_free(NULL, dfilter_string);
}
@@ -1339,12 +1305,10 @@ ek_write_attr_hex(GSList *attr_instances, write_json_data *pdata)
field_info *fi = NULL;
// Raw name
- fputs("\"", pdata->fh);
- ek_write_name(pnode, pdata);
- fputs("_raw\": ", pdata->fh);
+ ek_write_name(pnode, "_raw", pdata);
if (g_slist_length(attr_instances) > 1) {
- fputs("[", pdata->fh);
+ json_dumper_begin_array(pdata->dumper);
}
// Raw value(s)
@@ -1352,18 +1316,13 @@ ek_write_attr_hex(GSList *attr_instances, write_json_data *pdata)
pnode = (proto_node *) current_node->data;
fi = PNODE_FINFO(pnode);
- fputs("\"", pdata->fh);
ek_write_hex(fi, pdata);
- fputs("\"", pdata->fh);
current_node = current_node->next;
- if (current_node != NULL) {
- fputs(",", pdata->fh);
- }
}
if (g_slist_length(attr_instances) > 1) {
- fputs("]", pdata->fh);
+ json_dumper_end_array(pdata->dumper);
}
}
@@ -1377,17 +1336,13 @@ ek_write_attr(GSList *attr_instances, write_json_data *pdata)
// Hex dump -x
if (pdata->print_hex && fi && fi->length > 0 && fi->hfinfo->id != hf_text_only) {
ek_write_attr_hex(attr_instances, pdata);
-
- fputs(",", pdata->fh);
}
// Print attr name
- fputs("\"", pdata->fh);
- ek_write_name(pnode, pdata);
- fputs("\": ", pdata->fh);
+ ek_write_name(pnode, NULL, pdata);
if (g_slist_length(attr_instances) > 1) {
- fputs("[", pdata->fh);
+ json_dumper_begin_array(pdata->dumper);
}
while (current_node != NULL) {
@@ -1396,24 +1351,20 @@ ek_write_attr(GSList *attr_instances, write_json_data *pdata)
/* Field */
if (fi->hfinfo->type != FT_PROTOCOL) {
- fputs("\"", pdata->fh);
-
if (pdata->filter != NULL
&& !ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
/* print dummy field */
- fputs("\",\"filtered\": \"", pdata->fh);
- print_escaped_ek(pdata->fh, fi->hfinfo->abbrev);
+ json_dumper_set_member_name(pdata->dumper, "filtered");
+ json_dumper_value_string(pdata->dumper, fi->hfinfo->abbrev);
}
else {
ek_write_field_value(fi, pdata);
}
-
- fputs("\"", pdata->fh);
}
/* Object */
else {
- fputs("{", pdata->fh);
+ json_dumper_begin_object(pdata->dumper);
if (pdata->filter != NULL) {
if (ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
@@ -1432,26 +1383,22 @@ ek_write_attr(GSList *attr_instances, write_json_data *pdata)
}
} else {
/* print dummy field */
- fputs("\"filtered\": \"", pdata->fh);
- print_escaped_ek(pdata->fh, fi->hfinfo->abbrev);
- fputs("\"", pdata->fh);
+ json_dumper_set_member_name(pdata->dumper, "filtered");
+ json_dumper_value_string(pdata->dumper, fi->hfinfo->abbrev);
}
}
else {
proto_tree_write_node_ek(pnode, pdata);
}
- fputs("}", pdata->fh);
+ json_dumper_end_object(pdata->dumper);
}
current_node = current_node->next;
- if (current_node != NULL) {
- fputs(",", pdata->fh);
- }
}
if (g_slist_length(attr_instances) > 1) {
- fputs("]", pdata->fh);
+ json_dumper_end_array(pdata->dumper);
}
}
@@ -1474,9 +1421,6 @@ proto_tree_write_node_ek(proto_node *node, write_json_data *pdata)
ek_write_attr(attr_instances, pdata);
current_attr = current_attr->next;
- if (current_attr != NULL) {
- fputs(",", pdata->fh);
- }
}
g_slist_free_full(attr_list, (GDestroyNotify) g_slist_free);
@@ -1551,8 +1495,6 @@ write_pdml_finale(FILE *fh)
fputs("</pdml>\n", fh);
}
-
-
void
write_psml_preamble(column_info *cinfo, FILE *fh)
{
@@ -1810,59 +1752,6 @@ print_escaped_xml(FILE *fh, const char *unescaped_string)
}
static void
-print_escaped_bare(FILE *fh, const char *unescaped_string, gboolean change_dot)
-{
- const char *p;
- char temp_str[8];
-
- if (fh == NULL || unescaped_string == NULL) {
- return;
- }
-
- for (p = unescaped_string; *p != '\0'; p++) {
- switch (*p) {
- case '"':
- fputs("\\\"", fh);
- break;
- case '\\':
- fputs("\\\\", fh);
- break;
- case '/':
- fputs("\\/", fh);
- break;
- case '\b':
- fputs("\\b", fh);
- break;
- case '\f':
- fputs("\\f", fh);
- break;
- case '\n':
- fputs("\\n", fh);
- break;
- case '\r':
- fputs("\\r", fh);
- break;
- case '\t':
- fputs("\\t", fh);
- break;
- case '.':
- if (change_dot)
- fputs("_", fh);
- else
- fputs(".", fh);
- break;
- default:
- if (g_ascii_isprint(*p))
- fputc(*p, fh);
- else {
- g_snprintf(temp_str, sizeof(temp_str), "\\u00%02x", (guint8)*p);
- fputs(temp_str, fh);
- }
- }
- }
-}
-
-static void
print_escaped_csv(FILE *fh, const char *unescaped_string)
{
const char *p;
@@ -1894,23 +1783,6 @@ print_escaped_csv(FILE *fh, const char *unescaped_string)
}
}
-
-/* Print a string, escaping out certain characters that need to
- * escaped out for JSON. */
-static void
-print_escaped_json(FILE *fh, const char *unescaped_string)
-{
- print_escaped_bare(fh, unescaped_string, FALSE);
-}
-
-/* Print a string, escaping out certain characters that need to
- * escaped out for Elasticsearch title. */
-static void
-print_escaped_ek(FILE *fh, const char *unescaped_string)
-{
- print_escaped_bare(fh, unescaped_string, TRUE);
-}
-
static void
pdml_write_field_hex_value(write_pdml_data *pdata, field_info *fi)
{
@@ -1939,14 +1811,13 @@ pdml_write_field_hex_value(write_pdml_data *pdata, field_info *fi)
static void
json_write_field_hex_value(write_json_data *pdata, field_info *fi)
{
- int i;
const guint8 *pd;
if (!fi->ds_tvb)
return;
if (fi->length > tvb_captured_length_remaining(fi->ds_tvb, fi->start)) {
- fprintf(pdata->fh, "field length invalid!");
+ json_dumper_value_string(pdata->dumper, "field length invalid!");
return;
}
@@ -1954,10 +1825,21 @@ json_write_field_hex_value(write_json_data *pdata, field_info *fi)
pd = get_field_data(pdata->src_list, fi);
if (pd) {
+ gint i;
+ guint len = fi->length * 2 + 1;
+ gchar* str = (gchar*)g_malloc0(len);
+ static const char hex[] = "0123456789abcdef";
/* Print a simple hex dump */
- for (i = 0 ; i < fi->length; i++) {
- fprintf(pdata->fh, "%02x", pd[i]);
- }
+ for (i = 0; i < fi->length; i++) {
+ guint8 c = pd[i];
+ str[2 * i] = hex[c >> 4];
+ str[2 * i + 1] = hex[c & 0xf];
+ }
+ str[2 * fi->length] = '\0';
+ json_dumper_value_string(pdata->dumper, str);
+ g_free(str);
+ } else {
+ json_dumper_value_string(pdata->dumper, "");
}
}
@@ -2444,10 +2326,9 @@ static void proto_tree_get_node_field_values(proto_node *node, gpointer data)
}
}
-static void write_specified_fields(fields_format format, output_fields_t *fields, epan_dissect_t *edt, column_info *cinfo, FILE *fh)
+static void write_specified_fields(fields_format format, output_fields_t *fields, epan_dissect_t *edt, column_info *cinfo, FILE *fh, json_dumper *dumper)
{
gsize i;
- gboolean first = TRUE;
gint col;
gchar *col_name;
gpointer field_index;
@@ -2559,7 +2440,7 @@ static void write_specified_fields(fields_format format, output_fields_t *fields
}
break;
case FORMAT_JSON:
- fputs("{\n", fh);
+ json_dumper_begin_object(dumper);
for(i = 0; i < fields->fields->len; ++i) {
gchar *field = (gchar *)g_ptr_array_index(fields->fields, i);
@@ -2569,36 +2450,23 @@ static void write_specified_fields(fields_format format, output_fields_t *fields
gsize j;
fv_p = fields->field_values[i];
+ json_dumper_set_member_name(dumper, field);
+ json_dumper_begin_array(dumper);
+
/* Output the array of (partial) field values */
for (j = 0; j < (g_ptr_array_len(fv_p)); j += 2) {
str = (gchar *) g_ptr_array_index(fv_p, j);
-
- if (j == 0) {
- if (!first) {
- fputs(",\n", fh);
- }
- fprintf(fh, " \"%s\": [", field);
- }
- fputs("\"", fh);
- print_escaped_json(fh, str);
- fputs("\"", fh);
+ json_dumper_value_string(dumper, str);
g_free(str);
-
- if (j + 2 < (g_ptr_array_len(fv_p))) {
- fputs(",", fh);
- } else {
- fputs("]", fh);
- }
}
- first = FALSE;
+ json_dumper_end_array(dumper);
+
g_ptr_array_free(fv_p, TRUE); /* get ready for the next packet */
fields->field_values[i] = NULL;
}
}
- fputc('\n',fh);
-
- fputs(" }", fh);
+ json_dumper_end_object(dumper);
break;
case FORMAT_EK:
for(i = 0; i < fields->fields->len; ++i) {
@@ -2610,33 +2478,18 @@ static void write_specified_fields(fields_format format, output_fields_t *fields
gsize j;
fv_p = fields->field_values[i];
+ json_dumper_set_member_name(dumper, field);
+ json_dumper_begin_array(dumper);
+
/* Output the array of (partial) field values */
for (j = 0; j < (g_ptr_array_len(fv_p)); j += 2) {
str = (gchar *)g_ptr_array_index(fv_p, j);
-
- if (j == 0) {
- if (!first) {
- fputs(",", fh);
- }
- fputs("\"", fh);
- print_escaped_ek(fh, field);
- fputs("\": [", fh);
- }
- fputs("\"", fh);
- print_escaped_json(fh, str);
- fputs("\"", fh);
+ json_dumper_value_string(dumper, str);
g_free(str);
+ }
- if (j + 2 < (g_ptr_array_len(fv_p))) {
- fputs(",", fh);
- }
- else {
- fputs("]", fh);
-
- }
- }
+ json_dumper_end_array(dumper);
- first = FALSE;
g_ptr_array_free(fv_p, TRUE); /* get ready for the next packet */
fields->field_values[i] = NULL;
}
diff --git a/epan/print.h b/epan/print.h
index ad96942dbc..52da0ce2db 100644
--- a/epan/print.h
+++ b/epan/print.h
@@ -17,9 +17,10 @@
#include <epan/epan.h>
#include <epan/packet.h>
-
#include <epan/print_stream.h>
+#include <wsutil/json_dumper.h>
+
#include "ws_symbol_export.h"
#ifdef __cplusplus
@@ -91,7 +92,7 @@ WS_DLL_PUBLIC GSList *proto_node_group_children_by_unique(proto_node *node);
// Groups children by json key (children with the same json key get put in the same group
WS_DLL_PUBLIC GSList *proto_node_group_children_by_json_key(proto_node *node);
-WS_DLL_PUBLIC void write_json_preamble(FILE *fh);
+WS_DLL_PUBLIC json_dumper write_json_preamble(FILE *fh);
WS_DLL_PUBLIC void write_json_proto_tree(output_fields_t* fields,
print_dissections_e print_dissections,
gboolean print_hex_data,
@@ -100,8 +101,8 @@ WS_DLL_PUBLIC void write_json_proto_tree(output_fields_t* fields,
epan_dissect_t *edt,
column_info *cinfo,
proto_node_children_grouper_func node_children_grouper,
- FILE *fh);
-WS_DLL_PUBLIC void write_json_finale(FILE *fh);
+ json_dumper *dumper);
+WS_DLL_PUBLIC void write_json_finale(json_dumper *dumper);
WS_DLL_PUBLIC void write_ek_proto_tree(output_fields_t* fields,
gboolean print_summary,
diff --git a/file.c b/file.c
index e2fd943c25..8a3b120392 100644
--- a/file.c
+++ b/file.c
@@ -21,6 +21,7 @@
#include <wsutil/tempfile.h>
#include <wsutil/file_util.h>
#include <wsutil/filesystem.h>
+#include <wsutil/json_dumper.h>
#include <version_info.h>
#include <wiretap/merge.h>
@@ -2629,6 +2630,7 @@ typedef struct {
FILE *fh;
epan_dissect_t edt;
print_args_t *print_args;
+ json_dumper jdumper;
} write_packet_callback_args_t;
static gboolean
@@ -2940,7 +2942,8 @@ write_json_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
/* Write out the information in that tree. */
write_json_proto_tree(NULL, args->print_args->print_dissections,
args->print_args->print_hex, NULL, PF_NONE,
- &args->edt, &cf->cinfo, proto_node_group_children_by_unique, args->fh);
+ &args->edt, &cf->cinfo, proto_node_group_children_by_unique,
+ &args->jdumper);
epan_dissect_reset(&args->edt);
@@ -2958,7 +2961,7 @@ cf_write_json_packets(capture_file *cf, print_args_t *print_args)
if (fh == NULL)
return CF_PRINT_OPEN_ERROR; /* attempt to open destination failed */
- write_json_preamble(fh);
+ callback_args.jdumper = write_json_preamble(fh);
if (ferror(fh)) {
fclose(fh);
return CF_PRINT_WRITE_ERROR;
@@ -2992,7 +2995,7 @@ cf_write_json_packets(capture_file *cf, print_args_t *print_args)
return CF_PRINT_WRITE_ERROR;
}
- write_json_finale(fh);
+ write_json_finale(&callback_args.jdumper);
if (ferror(fh)) {
fclose(fh);
return CF_PRINT_WRITE_ERROR;
diff --git a/tshark.c b/tshark.c
index d6d935323f..2367f1aead 100644
--- a/tshark.c
+++ b/tshark.c
@@ -114,6 +114,7 @@
#include <wsutil/str_util.h>
#include <wsutil/utf8_entities.h>
+#include <wsutil/json_dumper.h>
#include "extcap.h"
@@ -193,6 +194,8 @@ static pf_flags protocolfilter_flags = PF_NONE;
static gboolean no_duplicate_keys = FALSE;
static proto_node_children_grouper_func node_children_grouper = proto_node_group_children_by_unique;
+static json_dumper jdumper;
+
/* The line separator used between packets, changeable via the -S option */
static const char *separator = "";
@@ -3559,11 +3562,11 @@ write_preamble(capture_file *cf)
case WRITE_JSON:
case WRITE_JSON_RAW:
- write_json_preamble(stdout);
+ jdumper = write_json_preamble(stdout);
return !ferror(stdout);
case WRITE_EK:
- return !ferror(stdout);
+ return TRUE;
default:
g_assert_not_reached();
@@ -3916,7 +3919,7 @@ print_packet(capture_file *cf, epan_dissect_t *edt)
if (print_details) {
write_json_proto_tree(output_fields, print_dissections_expanded,
print_hex, protocolfilter, protocolfilter_flags,
- edt, &cf->cinfo, node_children_grouper, stdout);
+ edt, &cf->cinfo, node_children_grouper, &jdumper);
return !ferror(stdout);
}
break;
@@ -3927,7 +3930,7 @@ print_packet(capture_file *cf, epan_dissect_t *edt)
if (print_details) {
write_json_proto_tree(output_fields, print_dissections_none, TRUE,
protocolfilter, protocolfilter_flags,
- edt, &cf->cinfo, node_children_grouper, stdout);
+ edt, &cf->cinfo, node_children_grouper, &jdumper);
return !ferror(stdout);
}
break;
@@ -3972,11 +3975,11 @@ write_finale(void)
case WRITE_JSON:
case WRITE_JSON_RAW:
- write_json_finale(stdout);
+ write_json_finale(&jdumper);
return !ferror(stdout);
case WRITE_EK:
- return !ferror(stdout);
+ return TRUE;
default:
g_assert_not_reached();