diff options
author | Dario Lombardo <lomato@gmail.com> | 2019-01-06 10:34:32 +0100 |
---|---|---|
committer | Peter Wu <peter@lekensteyn.nl> | 2019-01-14 16:00:29 +0000 |
commit | c3d198c401d5ec17289159cc88e2f891070e7779 (patch) | |
tree | fee286134f8799ecc84211baf6099ab204876338 | |
parent | 252938ed253846671a101f6b3f3e90354be3e4b2 (diff) |
dfilter: add string() function.
This function can convert non-string fields into strings. This allows the
user to apply string functions (like contains and matches) to non-string fields.
Examples:
string(frame.number) matches "[13579]$" => for odd frames
string(eth.dst) matches "aa\.bb\.cc\.dd\.ee\..." => to match a group of stations
string(snmp.name) matches "^1.2.3.4" => for all OIDs under a specific node
Change-Id: I18173f50ba5314ecdcd1e4b66c7e8ba5b44257ee
Reviewed-on: https://code.wireshark.org/review/31427
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Peter Wu <peter@lekensteyn.nl>
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | doc/wireshark-filter.pod | 10 | ||||
-rw-r--r-- | docbook/release-notes.asciidoc | 3 | ||||
-rw-r--r-- | docbook/wsug_src/WSUG_chapter_work.asciidoc | 16 | ||||
-rw-r--r-- | epan/dfilter/dfunctions.c | 139 | ||||
-rw-r--r-- | test/suite_dfilter/group_dfunction_string.py | 39 |
6 files changed, 204 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f0853b9744..5f74e5f957 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3131,6 +3131,7 @@ set(_test_group_list suite_dfilter.group_bytes_ipv6 suite_dfilter.group_bytes_type suite_dfilter.group_double + suite_dfilter.group_dfunction_string suite_dfilter.group_integer suite_dfilter.group_integer_1byte suite_dfilter.group_ipv4 diff --git a/doc/wireshark-filter.pod b/doc/wireshark-filter.pod index 98e5c60383..b3d7d2cc04 100644 --- a/doc/wireshark-filter.pod +++ b/doc/wireshark-filter.pod @@ -103,6 +103,7 @@ The filter language has the following functions: lower(string-field) - converts a string field to lowercase len(field) - returns the byte length of a string or bytes field count(field) - returns the number of field occurrences in a frame + string(field) - converts a non-string field to string upper() and lower() are useful for performing case-insensitive string comparisons. For example: @@ -110,6 +111,15 @@ comparisons. For example: upper(ncp.nds_stream_name) contains "MACRO" lower(mount.dump.hostname) == "angel" +string() converts a field value to a string, suitable for use with operators like +"matches" or "contains". Integer fields are converted to their decimal representation. +It can be used with IP/Ethernet addresses (as well as others), but not with string or +byte fields. For example: + + string(frame.number) matches "[13579]$" + +gives you all the odd packets. + =head2 Protocol field types Each protocol field is typed. The types are: diff --git a/docbook/release-notes.asciidoc b/docbook/release-notes.asciidoc index c4acc98aa4..e66cde25f7 100644 --- a/docbook/release-notes.asciidoc +++ b/docbook/release-notes.asciidoc @@ -78,6 +78,9 @@ since version 2.6.0: Secrets Block (DSB) containing a TLS Key Log (wsbuglink:15252[]). * The editcap utility gained a new `--inject-secrets` option to inject an existing TLS Key Log file into a pcapng file. +* A new dfilter function string() has been added. It allows the conversion of + non-string fields to strings so string functions (as contains and matches) + can be used on them. === Removed Features and Support diff --git a/docbook/wsug_src/WSUG_chapter_work.asciidoc b/docbook/wsug_src/WSUG_chapter_work.asciidoc index e3b9dba139..6bf8c94832 100644 --- a/docbook/wsug_src/WSUG_chapter_work.asciidoc +++ b/docbook/wsug_src/WSUG_chapter_work.asciidoc @@ -677,6 +677,7 @@ The display filter language has a number of functions to convert fields, see |lower |Converts a string field to lowercase. |len |Returns the byte length of a string or bytes field. |count |Returns the number of field occurrences in a frame. +|string |Converts a non-string field to a string. |=============== The `upper` and `lower` functions can used to force case-insensitive matches: @@ -690,6 +691,21 @@ Usually an IP frame has only two addresses (source and destination), but in case of ICMP errors or tunneling, a single packet might contain even more addresses. These packets can be found with `count(ip.addr) > 2`. +The `string` function converts a field value to a string, suitable for use with operators +like "matches" or "contains". Integer fields are converted to their decimal representation. +It can be used with IP/Ethernet addresses (as well as others), but not with string or +byte fields. + +For example, to match odd frame numbers: +---- +string(frame.number) matches "[13579]$" +---- + +to match IP addresses ending in 255 in a block of subnets (172.16 to 172.31): +---- +string(ip.dst) matches "^172\.(1[6-9]|2[0-9]|3[0-1])\..{1,3}\.255" +---- + [[ChWorkBuildDisplayFilterMistake]] ==== A Common Mistake diff --git a/epan/dfilter/dfunctions.c b/epan/dfilter/dfunctions.c index 6f71c23d40..e8cfb33e26 100644 --- a/epan/dfilter/dfunctions.c +++ b/epan/dfilter/dfunctions.c @@ -99,6 +99,70 @@ df_func_count(GList* arg1list, GList *arg2junk _U_, GList **retval) return TRUE; } +/* dfilter function: string() */ +static gboolean +df_func_string(GList* arg1list, GList *arg2junk _U_, GList **retval) +{ + GList *arg1 = arg1list; + fvalue_t *arg_fvalue; + fvalue_t *new_ft_string; + char *s; + + while (arg1) { + arg_fvalue = (fvalue_t *)arg1->data; + switch (fvalue_type_ftenum(arg_fvalue)) + { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_UINT40: + case FT_UINT48: + case FT_UINT56: + case FT_UINT64: + case FT_INT8: + case FT_INT16: + case FT_INT32: + case FT_INT40: + case FT_INT48: + case FT_INT56: + case FT_INT64: + case FT_IPv4: + case FT_IPv6: + case FT_FLOAT: + case FT_DOUBLE: + case FT_ETHER: + case FT_FRAMENUM: + case FT_AX25: + case FT_IPXNET: + case FT_GUID: + case FT_OID: + case FT_EUI64: + case FT_VINES: + case FT_REL_OID: + case FT_SYSTEM_ID: + case FT_FCWWN: + case FT_IEEE_11073_SFLOAT: + case FT_IEEE_11073_FLOAT: + s = fvalue_to_string_repr(NULL, arg_fvalue, FTREPR_DFILTER, BASE_NONE); + /* Ensure we have an allocated string here */ + if (!s) + s = wmem_strdup(NULL, ""); + break; + default: + return TRUE; + } + + new_ft_string = fvalue_new(FT_STRING); + fvalue_set_string(new_ft_string, s); + wmem_free(NULL, s); + *retval = g_list_append(*retval, new_ft_string); + + arg1 = arg1->next; + } + + return TRUE; +} /* For upper() and lower() checks that the parameter passed to * it is an FT_STRING */ @@ -174,13 +238,80 @@ ul_semcheck_field_param(dfwork_t *dfw, int param_num, stnode_t *st_node) } } +static void +ul_semcheck_string_param(dfwork_t *dfw, int param_num, stnode_t *st_node) +{ + sttype_id_t type; + ftenum_t ftype; + header_field_info *hfinfo; + const char* msg = "To string conversion for this field is not supported"; + + type = stnode_type_id(st_node); + + if (param_num == 0) { + switch(type) { + case STTYPE_FIELD: + hfinfo = (header_field_info *)stnode_data(st_node); + ftype = hfinfo->type; + switch (ftype) + { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_UINT40: + case FT_UINT48: + case FT_UINT56: + case FT_UINT64: + case FT_INT8: + case FT_INT16: + case FT_INT32: + case FT_INT40: + case FT_INT48: + case FT_INT56: + case FT_INT64: + case FT_IPv4: + case FT_IPv6: + case FT_FLOAT: + case FT_DOUBLE: + case FT_ETHER: + case FT_FRAMENUM: + case FT_AX25: + case FT_IPXNET: + case FT_GUID: + case FT_OID: + case FT_EUI64: + case FT_VINES: + case FT_REL_OID: + case FT_SYSTEM_ID: + case FT_FCWWN: + case FT_IEEE_11073_SFLOAT: + case FT_IEEE_11073_FLOAT: + break; + default: + dfilter_fail(dfw, "%s", msg); + THROW(TypeError); + break; + } + break; + default: + dfilter_fail(dfw, "%s", msg); + THROW(TypeError); + } + } + else { + g_assert_not_reached(); + } +} + /* The table of all display-filter functions */ static df_func_def_t df_functions[] = { - { "lower", df_func_lower, FT_STRING, 1, 1, ul_semcheck_params }, - { "upper", df_func_upper, FT_STRING, 1, 1, ul_semcheck_params }, - { "len", df_func_len, FT_UINT32, 1, 1, ul_semcheck_len_params }, - { "count", df_func_count, FT_UINT32, 1, 1, ul_semcheck_field_param }, + { "lower", df_func_lower, FT_STRING, 1, 1, ul_semcheck_params }, + { "upper", df_func_upper, FT_STRING, 1, 1, ul_semcheck_params }, + { "len", df_func_len, FT_UINT32, 1, 1, ul_semcheck_len_params }, + { "count", df_func_count, FT_UINT32, 1, 1, ul_semcheck_field_param }, + { "string", df_func_string, FT_STRING, 1, 1, ul_semcheck_string_param }, { NULL, NULL, FT_NONE, 0, 0, NULL } }; diff --git a/test/suite_dfilter/group_dfunction_string.py b/test/suite_dfilter/group_dfunction_string.py new file mode 100644 index 0000000000..c1bd17342c --- /dev/null +++ b/test/suite_dfilter/group_dfunction_string.py @@ -0,0 +1,39 @@ +# Copyright (c) 2019 by Dario Lombardo <lomato@gmail.com> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import unittest +import fixtures +from suite_dfilter.dfiltertest import * + +@fixtures.uses_fixtures +class case_dfunction_string(unittest.TestCase): + trace_file = "dhcp.pcap" + + def test_matches_1(self, checkDFilterCount): + dfilter = "string(frame.number) matches \"[13579]$\"" + checkDFilterCount(dfilter, 2) + + def test_contains_1(self, checkDFilterCount): + dfilter = "string(eth.src) contains \"00:08:74\"" + checkDFilterCount(dfilter, 2) + + def test_fail_1(self, checkDFilterFail): + # Invalid filter (only non-string fields are supported) + dfilter = "string(dhcp.server) == hostname" + checkDFilterFail(dfilter) + + def test_fail_2(self, checkDFilterFail): + # Invalid field: value + dfilter = "string(123) == \"123\"" + checkDFilterFail(dfilter) + + def test_fail_3(self, checkDFilterFail): + # Invalid field: protocol + dfilter = "string(dhcp) == hostname" + checkDFilterFail(dfilter) + + def test_fail_4(self, checkDFilterFail): + # Invalid field: bytes + dfilter = "string(dhcp.option.value) == \"hostname\"" + checkDFilterFail(dfilter) |