dfilter: Add support for entering time in UTC

Add the option to enter a filter with an absolute time
value in UTC. Otherwise the value is interpreted in
local time.

The syntax used is an "UTC" suffix, for example:

    frame.time == "Dec 31, 2002 13:55:31.3 UTC"

This also changes the behavior of "Apply Selected as filter".
Fields using a local time display type will use local time
and fields using UTC display type will be applied using UTC.

Fixes #13268.
This commit is contained in:
João Valverde 2021-12-28 12:24:06 +00:00
parent 42084c2d17
commit 0047ca961f
5 changed files with 96 additions and 44 deletions

View File

@ -55,6 +55,7 @@ They previously shipped with Npcap 1.55.
** Adds a new strict equality operator "===" or "all_eq". The expression "a === b" is true if and only if all a's are equal to b.
The negation of "===" can now be written as "!==" (any_ne), in addition to "~=" (introduced in Wireshark 3.6.0).
** Adds the aliases "any_eq" for "==" and "all_ne" for "!=".
** Absolute times can be given in UTC by appending the suffix "UTC" to time values. Otherwise local time is used.
* text2pcap has been updated to use the new logging output options and the
"-d" flag has been removed. The "debug" log level corresponds to the old

View File

@ -27,11 +27,13 @@ cmp_order(const fvalue_t *a, const fvalue_t *b)
* Get a nanoseconds value, starting at "p".
*
* Returns true on success, false on failure.
*
* If successful endptr points to the first invalid character.
*/
static gboolean
get_nsecs(const char *startp, int *nsecs)
get_nsecs(const char *startp, int *nsecs, const char **endptr)
{
int ndigits;
int ndigits = 0;
int scale;
const char *p;
int val;
@ -39,9 +41,10 @@ get_nsecs(const char *startp, int *nsecs)
int i;
/*
* How many characters are in the string?
* How many digits are in the string?
*/
ndigits = (int)strlen(startp);
for (p = startp; g_ascii_isdigit(*p); p++)
ndigits++;
/*
* If there are N characters in the string, the last of the
@ -53,7 +56,6 @@ get_nsecs(const char *startp, int *nsecs)
/*
* Start at the last character, and work backwards.
*/
p = startp + ndigits;
val = 0;
while (p != startp) {
p--;
@ -83,6 +85,8 @@ get_nsecs(const char *startp, int *nsecs)
scale++;
}
*nsecs = val;
if (endptr)
*endptr = startp + ndigits;
return TRUE;
}
@ -130,7 +134,7 @@ relative_val_from_unparsed(fvalue_t *fv, const char *s, gboolean allow_partial_v
/*
* Get the nanoseconds value.
*/
if (!get_nsecs(curptr, &fv->value.time.nsecs))
if (!get_nsecs(curptr, &fv->value.time.nsecs, NULL))
goto fail;
} else {
/*
@ -169,8 +173,10 @@ parse_month_name(const char *s, int *tm_mon)
return FALSE;
}
/* Parses an absolute time value from a string. The string cannot have
* a time zone suffix and is always interpreted in local time.
/*
* Parses an absolute time value from a string. The string can have
* a UTC time zone suffix. In that case it is interpreted in UTC. Otherwise
* it is interpreted in local time.
*
* OS-dependent; e.g., on 32 bit versions of Windows when compiled to use
* _mktime32 treats dates before January 1, 1970 as invalid.
@ -183,7 +189,8 @@ static gboolean
absolute_val_from_string(fvalue_t *fv, const char *s, char **err_msg_ptr)
{
struct tm tm;
char *curptr = NULL;
const char *curptr = NULL;
const char *endptr;
gboolean has_seconds = TRUE;
char *err_msg = NULL;
@ -210,23 +217,7 @@ absolute_val_from_string(fvalue_t *fv, const char *s, char **err_msg_ptr)
curptr = ws_strptime(s,"%Y-%m-%d", &tm);
if (curptr == NULL)
goto fail;
tm.tm_isdst = -1; /* let the computer figure out if it's DST */
fv->value.time.secs = mktime(&tm);
if (fv->value.time.secs == (time_t)-1) {
/*
* XXX - should we supply an error message that mentions
* that the time specified might be syntactically valid
* but might not actually have occurred, e.g. a time in
* the non-existent time range after the clocks are
* set forward during daylight savings time (or possibly
* that it's in the time range after the clocks are set
* backward, so that there are two different times that
* it could be)?
*/
err_msg = ws_strdup("\"%s\" cannot be converted to a valid calendar time.");
goto fail;
}
if (*curptr == '.') {
/* Nanoseconds */
@ -240,14 +231,11 @@ absolute_val_from_string(fvalue_t *fv, const char *s, char **err_msg_ptr)
err_msg = ws_strdup("Subseconds value is not a number.");
goto fail;
}
if (!get_nsecs(curptr, &fv->value.time.nsecs)) {
if (!get_nsecs(curptr, &fv->value.time.nsecs, &endptr)) {
err_msg = ws_strdup("Subseconds value is invalid.");
goto fail;
}
}
else if (*curptr != '\0') {
err_msg = ws_strdup("Unexpected data after time value.");
goto fail;
curptr = endptr;
}
else {
/*
@ -256,6 +244,50 @@ absolute_val_from_string(fvalue_t *fv, const char *s, char **err_msg_ptr)
fv->value.time.nsecs = 0;
}
/* Skip whitespace */
while (g_ascii_isspace(*curptr)) {
curptr++;
}
/* Do we have a Timezone? */
if (strcmp(curptr, "UTC") == 0) {
curptr += strlen("UTC");
if (*curptr == '\0') {
/* It's UTC */
fv->value.time.secs = mktime_utc(&tm);
goto done;
}
else {
err_msg = ws_strdup("Unexpected data after time value.");
goto fail;
}
}
if (*curptr == '\0') {
/* Local time */
fv->value.time.secs = mktime(&tm);
goto done;
}
else {
err_msg = ws_strdup("Unexpected data after time value.");
goto fail;
}
done:
if (fv->value.time.secs == (time_t)-1) {
/*
* XXX - should we supply an error message that mentions
* that the time specified might be syntactically valid
* but might not actually have occurred, e.g. a time in
* the non-existent time range after the clocks are
* set forward during daylight savings time (or possibly
* that it's in the time range after the clocks are set
* backward, so that there are two different times that
* it could be)?
*/
err_msg = ws_strdup("\"%s\" cannot be converted to a valid calendar time.");
goto fail;
}
return TRUE;
fail:
@ -304,6 +336,9 @@ absolute_val_to_repr(wmem_allocator_t *scope, const fvalue_t *fv, ftrepr_t rtype
{
char *rep;
if (field_display == BASE_NONE)
field_display = ABSOLUTE_TIME_LOCAL;
switch (rtype) {
case FTREPR_DISPLAY:
rep = abs_time_to_str_ex(scope, &fv->value.time,
@ -311,10 +346,12 @@ absolute_val_to_repr(wmem_allocator_t *scope, const fvalue_t *fv, ftrepr_t rtype
break;
case FTREPR_DFILTER:
/* absolute_val_from_string only accepts local time,
* with no time zone, so match that. */
/* Only ABSOLUTE_TIME_LOCAL and ABSOLUTE_TIME_UTC
* are supported. Normalize the field_display value. */
if (field_display != ABSOLUTE_TIME_LOCAL)
field_display = ABSOLUTE_TIME_UTC;
rep = abs_time_to_str_ex(scope, &fv->value.time,
ABSOLUTE_TIME_LOCAL, ABS_TIME_TO_STR_ADD_DQUOTES);
field_display, ABS_TIME_TO_STR_SHOW_UTC_ONLY|ABS_TIME_TO_STR_ADD_DQUOTES);
break;
default:

View File

@ -110,19 +110,25 @@ get_fmt_broken_down_time(field_display_e fmt, const time_t *secs)
}
static const char *
get_fmt_zonename(field_display_e fmt, struct tm *tmp)
get_fmt_zonename(char *buf, size_t size, field_display_e fmt, struct tm *tmp, int flags)
{
switch (fmt) {
case ABSOLUTE_TIME_UTC:
case ABSOLUTE_TIME_DOY_UTC:
case ABSOLUTE_TIME_NTP_UTC:
return "UTC";
case ABSOLUTE_TIME_LOCAL:
return get_zonename(tmp);
default:
snprintf(buf, size, " UTC");
break;
case ABSOLUTE_TIME_LOCAL:
if (flags & ABS_TIME_TO_STR_SHOW_ZONE)
snprintf(buf, size, " %s", get_zonename(tmp));
else
*buf = '\0';
break;
default:
ws_assert_not_reached();
}
ws_assert_not_reached();
return buf;
}
static char *
@ -177,6 +183,9 @@ abs_time_to_str_ex(wmem_allocator_t *scope, const nstime_t *abs_time, field_disp
char buf_nsecs[32];
char buf_tzone[32];
if (fmt == BASE_NONE)
fmt = ABSOLUTE_TIME_LOCAL;
ws_assert(FIELD_DISPLAY_IS_ABSOLUTE_TIME(fmt));
if (fmt == ABSOLUTE_TIME_NTP_UTC && abs_time->secs == 0 &&
@ -195,8 +204,8 @@ abs_time_to_str_ex(wmem_allocator_t *scope, const nstime_t *abs_time, field_disp
}
*buf_tzone = '\0';
if (flags & ABS_TIME_TO_STR_SHOW_ZONE) {
snprintf(buf_tzone, sizeof(buf_tzone), " %s", get_fmt_zonename(fmt, tmp));
if (flags & ABS_TIME_TO_STR_SHOW_ZONE || flags & ABS_TIME_TO_STR_SHOW_UTC_ONLY) {
get_fmt_zonename(buf_tzone, sizeof(buf_tzone), fmt, tmp, flags);
}
return snprint_abs_time_secs(scope, fmt, tmp, buf_nsecs, buf_tzone, flags & ABS_TIME_TO_STR_ADD_DQUOTES);

View File

@ -132,6 +132,7 @@ WS_DLL_PUBLIC gchar* tvb_address_var_to_str(wmem_allocator_t *scope, tvbuff_t *t
#define ABS_TIME_TO_STR_SHOW_ZONE (1U << 0)
#define ABS_TIME_TO_STR_ADD_DQUOTES (1U << 1)
#define ABS_TIME_TO_STR_SHOW_UTC_ONLY (1U << 2)
WS_DLL_PUBLIC char *abs_time_to_str_ex(wmem_allocator_t *scope,
const nstime_t *, field_display_e fmt,

View File

@ -79,10 +79,14 @@ class case_time(unittest.TestCase):
dfilter = 'frame.time <= "Dec 31, 2002 13:56:31.3"'
checkDFilterCount(dfilter, 1)
def test_utc_time_1(self, checkDFilterCount):
dfilter = 'frame.time == "Dec 31, 2002 13:55:31.3 UTC"'
checkDFilterCount(dfilter, 1)
def test_bad_time_1(self, checkDFilterFail):
# No text is permitted after the time.
dfilter = 'frame.time == "Dec 31, 2002 13:56:31.3 UTC"'
error = 'value is invalid'
# This is an error, only UTC timezone can be used
dfilter = 'frame.time == "Dec 31, 2002 13:56:31.3 WET"'
error = 'Unexpected data after time value'
checkDFilterFail(dfilter, error)
def test_bad_time_2(self, checkDFilterFail):