aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDario Lombardo <lomato@gmail.com>2019-01-06 10:34:32 +0100
committerPeter Wu <peter@lekensteyn.nl>2019-01-14 16:00:29 +0000
commitc3d198c401d5ec17289159cc88e2f891070e7779 (patch)
treefee286134f8799ecc84211baf6099ab204876338
parent252938ed253846671a101f6b3f3e90354be3e4b2 (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.txt1
-rw-r--r--doc/wireshark-filter.pod10
-rw-r--r--docbook/release-notes.asciidoc3
-rw-r--r--docbook/wsug_src/WSUG_chapter_work.asciidoc16
-rw-r--r--epan/dfilter/dfunctions.c139
-rw-r--r--test/suite_dfilter/group_dfunction_string.py39
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)