tshark: Support multiple -j and -J options, including mixed

Store the field filter strings in a wmem_map pointing to the
field flags for each string. This allows specifying multiple
filter options (-j or -J) on the command line, including some
of both.

Fix #17470
This commit is contained in:
John Thacker 2023-02-13 20:55:36 -05:00
parent de2a2c5ddb
commit 01172f5a1d
5 changed files with 104 additions and 74 deletions

View File

@ -52,14 +52,12 @@ typedef struct {
int level;
FILE *fh;
GSList *src_list;
gchar **filter;
pf_flags filter_flags;
wmem_map_t *filter;
} write_pdml_data;
typedef struct {
GSList *src_list;
gchar **filter;
pf_flags filter_flags;
wmem_map_t *filter;
gboolean print_hex;
gboolean print_text;
proto_node_children_grouper_func node_children_grouper;
@ -280,29 +278,41 @@ write_pdml_preamble(FILE *fh, const gchar *filename)
fprintf(fh, "\">\n");
}
/* Check if the str match the protocolfilter. json_filter is space
delimited string and str need to exact-match to one of the value. */
static gboolean check_protocolfilter(gchar **protocolfilter, const char *str)
/* Check if the str matches the protocolfilter.
*
* @param[in] protocolfilter a map of field abbreviations that pass the filter
* to the flags for that field, or NULL if no filter (so all fields pass)
* @param[in] str the field abbreviation to lookup in the map.
* @param[out] flags if not NULL, gets set to the value in the map for
* the given key if found (undefined if return is FALSE.)
* @return TRUE if the filter passes the string, FALSE if the filter
* filters out the string.
*/
static gboolean check_protocolfilter(wmem_map_t *protocolfilter, const char *str, pf_flags *flags)
{
gboolean res = FALSE;
gchar **ptr;
void *value;
if (str == NULL || protocolfilter == NULL) {
if (protocolfilter == NULL) {
if (flags) {
*flags = PF_NONE;
}
return TRUE;
}
if (str == NULL) {
return FALSE;
}
for (ptr = protocolfilter; *ptr; ptr++) {
if (strcmp(*ptr, str) == 0) {
res = TRUE;
break;
}
res = wmem_map_lookup_extended(protocolfilter, str, NULL, &value);
if (res && flags) {
*flags = GPOINTER_TO_UINT(value);
}
return res;
}
void
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)
write_pdml_proto_tree(output_fields_t* fields, wmem_map_t *protocolfilter, epan_dissect_t *edt, column_info *cinfo, FILE *fh, gboolean use_color)
{
write_pdml_data data;
const color_filter_t *cfp;
@ -330,7 +340,6 @@ write_pdml_proto_tree(output_fields_t* fields, gchar **protocolfilter, pf_flags
data.fh = fh;
data.src_list = edt->pi.data_src;
data.filter = protocolfilter;
data.filter_flags = protocolfilter_flags;
proto_tree_children_foreach(edt->tree, proto_tree_write_node_pdml,
&data);
@ -345,8 +354,8 @@ write_pdml_proto_tree(output_fields_t* fields, gchar **protocolfilter, pf_flags
void
write_ek_proto_tree(output_fields_t* fields,
gboolean print_summary, gboolean print_hex,
gchar **protocolfilter,
pf_flags protocolfilter_flags, epan_dissect_t *edt,
wmem_map_t *protocolfilter,
epan_dissect_t *edt,
column_info *cinfo,
FILE *fh)
{
@ -388,7 +397,6 @@ write_ek_proto_tree(output_fields_t* fields,
/* Write out all fields */
data.src_list = edt->pi.data_src;
data.filter = protocolfilter;
data.filter_flags = protocolfilter_flags;
data.print_hex = print_hex;
proto_tree_write_node_ek(edt->tree, &data);
} else {
@ -632,10 +640,11 @@ proto_tree_write_node_pdml(proto_node *node, gpointer data)
/* We print some levels for PDML. Recurse here. */
if (node->first_child != NULL) {
if (pdata->filter == NULL || check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
gchar **_filter = NULL;
pf_flags filter_flags = PF_NONE;
if (pdata->filter == NULL || check_protocolfilter(pdata->filter, fi->hfinfo->abbrev, &filter_flags)) {
wmem_map_t *_filter = NULL;
/* Remove protocol filter for children, if children should be included */
if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
_filter = pdata->filter;
pdata->filter = NULL;
}
@ -646,7 +655,7 @@ proto_tree_write_node_pdml(proto_node *node, gpointer data)
pdata->level--;
/* Put protocol filter back */
if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
pdata->filter = _filter;
}
} else {
@ -727,9 +736,8 @@ write_json_index(json_dumper *dumper, epan_dissect_t *edt)
void
write_json_proto_tree(output_fields_t* fields,
print_dissections_e print_dissections,
gboolean print_hex, gchar **protocolfilter,
pf_flags protocolfilter_flags, epan_dissect_t *edt,
column_info *cinfo,
gboolean print_hex, wmem_map_t *protocolfilter,
epan_dissect_t *edt, column_info *cinfo,
proto_node_children_grouper_func node_children_grouper,
json_dumper *dumper)
{
@ -751,7 +759,6 @@ write_json_proto_tree(output_fields_t* fields,
/* Write out all fields */
data.src_list = edt->pi.data_src;
data.filter = protocolfilter;
data.filter_flags = protocolfilter_flags;
data.print_hex = print_hex;
data.print_text = TRUE;
if (print_dissections == print_dissections_none) {
@ -809,7 +816,8 @@ write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *pdata)
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 = pdata->filter != NULL && !check_protocolfilter(pdata->filter, json_key);
pf_flags filter_flags = PF_NONE;
gboolean is_filtered = pdata->filter != NULL && !check_protocolfilter(pdata->filter, json_key, &filter_flags);
field_info *fi = first_value->finfo;
char *value_string_repr = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
@ -846,8 +854,8 @@ write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *pdata)
// 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 ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
wmem_map_t *_filter = NULL;
if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
_filter = pdata->filter;
pdata->filter = NULL;
}
@ -857,7 +865,7 @@ write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *pdata)
write_json_proto_node(node_values_list, suffix, write_json_proto_node_dynamic, pdata);
// Put protocol filter back
if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
pdata->filter = _filter;
}
}
@ -1119,8 +1127,12 @@ proto_node_group_children_by_json_key(proto_node *node)
}
/**
* Returns the json key of a node. Tries to use the node's abbreviated name. If the abbreviated name is not available
* the representation is used instead.
* Returns the json key of a node. Tries to use the node's abbreviated name.
* If the abbreviated name is not available the representation is used instead.
*
* XXX: The representation can have spaces or differ depending on the content,
* which makes it difficult to match text-only fields with a -j/-J filter in tshark.
* (Issue #17125).
*/
static const char *
proto_node_to_json_key(proto_node *node)
@ -1139,13 +1151,13 @@ proto_node_to_json_key(proto_node *node)
}
static gboolean
ek_check_protocolfilter(gchar **protocolfilter, const char *str)
ek_check_protocolfilter(wmem_map_t *protocolfilter, const char *str, pf_flags *filter_flags)
{
gchar *str_escaped = NULL;
gboolean check;
int i;
if (check_protocolfilter(protocolfilter, str))
if (check_protocolfilter(protocolfilter, str, filter_flags))
return TRUE;
/* to to thread the '.' and '_' equally. The '.' is replace by print_escaped_ek for '_' */
@ -1161,7 +1173,7 @@ ek_check_protocolfilter(gchar **protocolfilter, const char *str)
}
}
check = check_protocolfilter(protocolfilter, str_escaped);
check = check_protocolfilter(protocolfilter, str_escaped, filter_flags);
g_free(str_escaped);
return check;
}
@ -1204,10 +1216,11 @@ ek_fill_attr(proto_node *node, GHashTable *attr_table, write_json_data *pdata)
/* Field, recurse through children*/
if (fi->hfinfo->type != FT_PROTOCOL && current_node->first_child != NULL) {
if (pdata->filter != NULL) {
if (ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
gchar **_filter = NULL;
pf_flags filter_flags = PF_NONE;
if (ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev, &filter_flags)) {
wmem_map_t *_filter = NULL;
/* Remove protocol filter for children, if children should be included */
if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
_filter = pdata->filter;
pdata->filter = NULL;
}
@ -1215,7 +1228,7 @@ ek_fill_attr(proto_node *node, GHashTable *attr_table, write_json_data *pdata)
ek_fill_attr(current_node, attr_table, pdata);
/* Put protocol filter back */
if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
pdata->filter = _filter;
}
} else {
@ -1402,9 +1415,10 @@ ek_write_attr_hex(GSList *attr_instances, write_json_data *pdata)
static void
ek_write_attr(GSList *attr_instances, write_json_data *pdata)
{
GSList *current_node = attr_instances;
proto_node *pnode = (proto_node *) current_node->data;
field_info *fi = PNODE_FINFO(pnode);
GSList *current_node = attr_instances;
proto_node *pnode = (proto_node *) current_node->data;
field_info *fi = PNODE_FINFO(pnode);
pf_flags filter_flags = PF_NONE;
// Hex dump -x
if (pdata->print_hex && fi && fi->length > 0 && fi->hfinfo->id != hf_text_only) {
@ -1425,7 +1439,7 @@ ek_write_attr(GSList *attr_instances, write_json_data *pdata)
/* Field */
if (fi->hfinfo->type != FT_PROTOCOL) {
if (pdata->filter != NULL
&& !ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
&& !ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev, &filter_flags)) {
/* print dummy field */
json_dumper_begin_object(pdata->dumper);
@ -1440,10 +1454,10 @@ ek_write_attr(GSList *attr_instances, write_json_data *pdata)
json_dumper_begin_object(pdata->dumper);
if (pdata->filter != NULL) {
if (ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev)) {
gchar **_filter = NULL;
if (ek_check_protocolfilter(pdata->filter, fi->hfinfo->abbrev, &filter_flags)) {
wmem_map_t *_filter = NULL;
/* Remove protocol filter for children, if children should be included */
if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
_filter = pdata->filter;
pdata->filter = NULL;
}
@ -1451,7 +1465,7 @@ ek_write_attr(GSList *attr_instances, write_json_data *pdata)
proto_tree_write_node_ek(pnode, pdata);
/* Put protocol filter back */
if ((pdata->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
if ((filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) {
pdata->filter = _filter;
}
} else {

View File

@ -94,7 +94,7 @@ WS_DLL_PUBLIC gboolean proto_tree_print(print_dissections_e print_dissections,
WS_DLL_PUBLIC gboolean print_hex_data(print_stream_t *stream, epan_dissect_t *edt, guint hexdump_options);
WS_DLL_PUBLIC void write_pdml_preamble(FILE *fh, const gchar* filename);
WS_DLL_PUBLIC void 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);
WS_DLL_PUBLIC void write_pdml_proto_tree(output_fields_t* fields, wmem_map_t *protocolfilter, epan_dissect_t *edt, column_info *cinfo, FILE *fh, gboolean use_color);
WS_DLL_PUBLIC void write_pdml_finale(FILE *fh);
// Implementations of proto_node_children_grouper_func
@ -107,8 +107,7 @@ 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,
gchar **protocolfilter,
pf_flags protocolfilter_flags,
wmem_map_t *protocolfilter,
epan_dissect_t *edt,
column_info *cinfo,
proto_node_children_grouper_func node_children_grouper,
@ -118,8 +117,7 @@ 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,
gboolean print_hex_data,
gchar **protocolfilter,
pf_flags protocolfilter_flags,
wmem_map_t *protocolfilter,
epan_dissect_t *edt,
column_info *cinfo, FILE *fh);

4
file.c
View File

@ -2694,7 +2694,7 @@ write_pdml_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
fdata, NULL);
/* Write out the information in that tree. */
write_pdml_proto_tree(NULL, NULL, PF_NONE, &args->edt, &cf->cinfo, args->fh, FALSE);
write_pdml_proto_tree(NULL, NULL, &args->edt, &cf->cinfo, args->fh, FALSE);
epan_dissect_reset(&args->edt);
@ -2993,7 +2993,7 @@ 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->print_args->print_hex, NULL,
&args->edt, &cf->cinfo, proto_node_group_children_by_unique,
&args->jdumper);

View File

@ -1964,7 +1964,7 @@ print_packet(capture_file *cf, epan_dissect_t *edt)
break;
case WRITE_XML:
write_pdml_proto_tree(NULL, NULL, PF_NONE, edt, &cf->cinfo, stdout, FALSE);
write_pdml_proto_tree(NULL, NULL, edt, &cf->cinfo, stdout, FALSE);
printf("\n");
return !ferror(stdout);
case WRITE_FIELDS:

View File

@ -186,8 +186,7 @@ static print_stream_t *print_stream = NULL;
static char *output_file_name;
static output_fields_t* output_fields = NULL;
static gchar **protocolfilter = NULL;
static pf_flags protocolfilter_flags = PF_NONE;
static wmem_map_t *protocolfilter = NULL;
static gboolean no_duplicate_keys = FALSE;
static proto_node_children_grouper_func node_children_grouper = proto_node_group_children_by_unique;
@ -572,6 +571,32 @@ hexdump_option_help(FILE *output)
fprintf(output, "\n");
}
static void
protocolfilter_add_opt(const char* arg, pf_flags filter_flags)
{
void* value;
gchar **newfilter = NULL;
for (newfilter = wmem_strsplit(wmem_epan_scope(), arg, " ", -1); *newfilter; newfilter++) {
if (strcmp(*newfilter, "") == 0) {
/* Don't treat the empty string as an intended field abbreviation
* to output, consecutive spaces on the command line probably
* aren't intentional.
*/
continue;
}
if (!protocolfilter) {
protocolfilter = wmem_map_new(wmem_epan_scope(), wmem_str_hash, g_str_equal);
}
if (wmem_map_lookup_extended(protocolfilter, *newfilter, NULL, &value)) {
if (GPOINTER_TO_UINT(value) != (guint)filter_flags) {
cmdarg_err("%s was already specified with different filter flags. Overwriting previous protocol filter.", *newfilter);
}
}
wmem_map_insert(protocolfilter, *newfilter, GINT_TO_POINTER(filter_flags));
}
}
static void
print_current_user(void)
{
@ -1318,17 +1343,10 @@ main(int argc, char *argv[])
goto clean_exit;
break;
case 'j':
if (protocolfilter) {
cmdarg_err("-j or -J was already specified. Overwriting previous protocol filter.");
}
protocolfilter = wmem_strsplit(wmem_epan_scope(), ws_optarg, " ", -1);
protocolfilter_add_opt(ws_optarg, PF_NONE);
break;
case 'J':
if (protocolfilter) {
cmdarg_err("-j or -J was already specified. Overwriting previous protocol filter.");
}
protocolfilter_flags = PF_INCLUDE_CHILDREN;
protocolfilter = wmem_strsplit(wmem_epan_scope(), ws_optarg, " ", -1);
protocolfilter_add_opt(ws_optarg, PF_INCLUDE_CHILDREN);
break;
case 'W': /* Select extra information to save in our capture file */
/* This is patterned after the -N flag which may not be the best idea. */
@ -4352,7 +4370,7 @@ print_packet(capture_file *cf, epan_dissect_t *edt)
return !ferror(stdout);
}
if (print_details) {
write_pdml_proto_tree(output_fields, protocolfilter, protocolfilter_flags, edt, &cf->cinfo, stdout, dissect_color);
write_pdml_proto_tree(output_fields, protocolfilter, edt, &cf->cinfo, stdout, dissect_color);
printf("\n");
return !ferror(stdout);
}
@ -4375,7 +4393,7 @@ print_packet(capture_file *cf, epan_dissect_t *edt)
ws_assert_not_reached();
if (print_details) {
write_json_proto_tree(output_fields, print_dissections_expanded,
print_hex, protocolfilter, protocolfilter_flags,
print_hex, protocolfilter,
edt, &cf->cinfo, node_children_grouper, &jdumper);
return !ferror(stdout);
}
@ -4385,16 +4403,16 @@ print_packet(capture_file *cf, epan_dissect_t *edt)
if (print_summary)
ws_assert_not_reached();
if (print_details) {
write_json_proto_tree(output_fields, print_dissections_none, TRUE,
protocolfilter, protocolfilter_flags,
write_json_proto_tree(output_fields, print_dissections_none,
TRUE, protocolfilter,
edt, &cf->cinfo, node_children_grouper, &jdumper);
return !ferror(stdout);
}
break;
case WRITE_EK:
write_ek_proto_tree(output_fields, print_summary, print_hex, protocolfilter,
protocolfilter_flags, edt, &cf->cinfo, stdout);
write_ek_proto_tree(output_fields, print_summary, print_hex,
protocolfilter, edt, &cf->cinfo, stdout);
return !ferror(stdout);
default: