From 60a9e1124b21ecee4be4e5fac5de281505ded05b Mon Sep 17 00:00:00 2001 From: John Thacker Date: Sun, 1 Oct 2023 11:37:21 -0400 Subject: [PATCH] Find: Matching multiple occurrences in Packet Bytes Match multiple occurrence in Find Packet Bytes, both forwards and backwards. Also fix an issue highlighting wide strings properly reintroduced by commit c0885fe390f1fba32986806383dd38a437c7681f For backwards searching in string and binary searches, use the memrchr and backwards mempbrk implementations. For regex, use PCRE2_ANCHORED to transform the user's regex expression into one that is anchored at the start byte, and progressively search backwards. Fix #11269 --- file.c | 601 +++++++++++++++++++++++-- file.h | 5 +- ui/logray/logray_main_window_slots.cpp | 16 + ui/qt/search_frame.cpp | 15 +- ui/qt/wireshark_main_window_slots.cpp | 16 + wsutil/regex.c | 2 + wsutil/regex.h | 1 + 7 files changed, 605 insertions(+), 51 deletions(-) diff --git a/file.c b/file.c index 97e457bebf..46159b89ef 100644 --- a/file.c +++ b/file.c @@ -94,18 +94,32 @@ static match_result match_summary_line(capture_file *cf, frame_data *fdata, wtap_rec *, Buffer *, void *criterion); static match_result match_narrow_and_wide(capture_file *cf, frame_data *fdata, wtap_rec *, Buffer *, void *criterion); +static match_result match_narrow_and_wide_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *, Buffer *, void *criterion); static match_result match_narrow_and_wide_case(capture_file *cf, frame_data *fdata, wtap_rec *, Buffer *, void *criterion); +static match_result match_narrow_and_wide_case_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *, Buffer *, void *criterion); static match_result match_narrow_case(capture_file *cf, frame_data *fdata, wtap_rec *, Buffer *, void *criterion); +static match_result match_narrow_case_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *, Buffer *, void *criterion); static match_result match_wide(capture_file *cf, frame_data *fdata, wtap_rec *, Buffer *, void *criterion); +static match_result match_wide_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *, Buffer *, void *criterion); static match_result match_wide_case(capture_file *cf, frame_data *fdata, wtap_rec *, Buffer *, void *criterion); +static match_result match_wide_case_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *, Buffer *, void *criterion); static match_result match_binary(capture_file *cf, frame_data *fdata, wtap_rec *, Buffer *, void *criterion); +static match_result match_binary_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *, Buffer *, void *criterion); static match_result match_regex(capture_file *cf, frame_data *fdata, wtap_rec *, Buffer *, void *criterion); +static match_result match_regex_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *, Buffer *, void *criterion); static match_result match_dfilter(capture_file *cf, frame_data *fdata, wtap_rec *, Buffer *, void *criterion); static match_result match_marked(capture_file *cf, frame_data *fdata, @@ -3536,11 +3550,12 @@ typedef struct { gboolean cf_find_packet_data(capture_file *cf, const guint8 *string, size_t string_size, - search_direction dir) + search_direction dir, bool multiple) { cbs_t info; guint8 needles[3]; - ws_mempbrk_pattern pattern; + ws_mempbrk_pattern pattern = {0}; + ws_match_function match_function; info.data = string; info.data_len = string_size; @@ -3548,7 +3563,7 @@ cf_find_packet_data(capture_file *cf, const guint8 *string, size_t string_size, /* Regex, String or hex search? */ if (cf->regex) { /* Regular Expression search */ - return find_packet(cf, match_regex, NULL, dir); + match_function = (cf->dir == SD_FORWARD) ? match_regex : match_regex_reverse; } else if (cf->string) { /* String search - what type of string? */ if (cf->case_type) { @@ -3560,13 +3575,16 @@ cf_find_packet_data(capture_file *cf, const guint8 *string, size_t string_size, switch (cf->scs_type) { case SCS_NARROW_AND_WIDE: - return find_packet(cf, match_narrow_and_wide_case, &info, dir); + match_function = (cf->dir == SD_FORWARD) ? match_narrow_and_wide_case : match_narrow_and_wide_case_reverse; + break; case SCS_NARROW: - return find_packet(cf, match_narrow_case, &info, dir); + match_function = (cf->dir == SD_FORWARD) ? match_narrow_case : match_narrow_case_reverse; + break; case SCS_WIDE: - return find_packet(cf, match_wide_case, &info, dir); + match_function = (cf->dir == SD_FORWARD) ? match_wide_case : match_wide_case_reverse; + break; default: ws_assert_not_reached(); @@ -3577,23 +3595,51 @@ cf_find_packet_data(capture_file *cf, const guint8 *string, size_t string_size, switch (cf->scs_type) { case SCS_NARROW_AND_WIDE: - return find_packet(cf, match_narrow_and_wide, &info, dir); + match_function = (cf->dir == SD_FORWARD) ? match_narrow_and_wide : match_narrow_and_wide_reverse; + break; case SCS_NARROW: /* Narrow, case-sensitive match is the same as looking * for a converted hexstring. */ - return find_packet(cf, match_binary, &info, dir); + match_function = (cf->dir == SD_FORWARD) ? match_binary : match_binary_reverse; + break; case SCS_WIDE: - return find_packet(cf, match_wide, &info, dir); + match_function = (cf->dir == SD_FORWARD) ? match_wide : match_wide_reverse; + break; default: ws_assert_not_reached(); return FALSE; } } - } else - return find_packet(cf, match_binary, &info, dir); + } else { + match_function = (cf->dir == SD_FORWARD) ? match_binary : match_binary_reverse; + } + + if (multiple && cf->current_frame && (cf->search_pos || cf->search_len)) { + /* Use the current frame (this will perform the equivalent of + * cf_read_current_record() in match_function). + */ + if (match_function(cf, cf->current_frame, &cf->rec, &cf->buf, &info)) { + cf->search_in_progress = TRUE; + if (cf->edt) { + field_info *fi = NULL; + /* The regex match can match an empty string. */ + if (cf->search_len) { + fi = proto_find_field_from_offset(cf->edt->tree, cf->search_pos + cf->search_len - 1, cf->edt->tvb); + } + packet_list_select_finfo(fi); + } else { + packet_list_select_row_from_data(cf->current_frame); + } + cf->search_in_progress = FALSE; + return TRUE; + } + } + cf->search_pos = 0; /* Reset the position */ + cf->search_len = 0; /* Reset length */ + return find_packet(cf, match_function, &info, dir); } static match_result @@ -3620,7 +3666,12 @@ match_narrow_and_wide(capture_file *cf, frame_data *fdata, buf_len = fdata->cap_len; buf_start = ws_buffer_start_ptr(buf); buf_end = buf_start + buf_len; - for (pd = buf_start; pd < buf_end; pd++) { + pd = buf_start; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte past the previous match start */ + pd += cf->search_pos + 1; + } + for (; pd < buf_end; pd++) { pd = (guint8 *)memchr(pd, ascii_text[0], buf_end - pd); if (pd == NULL) break; /* Try narrow match at this start location */ @@ -3632,8 +3683,8 @@ match_narrow_and_wide(capture_file *cf, frame_data *fdata, if (c_match == textlen) { result = MR_MATCHED; /* Save position and length for highlighting the field. */ - cf->search_pos = (guint32)(pd - buf_start); - cf->search_len = (guint32)(textlen); + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); goto done; } } else { @@ -3650,8 +3701,87 @@ match_narrow_and_wide(capture_file *cf, frame_data *fdata, if (c_match == textlen) { result = MR_MATCHED; /* Save position and length for highlighting the field. */ - cf->search_pos = (guint32)(pd - buf_start); - cf->search_len = (guint32)(textlen); + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); + goto done; + } + i++; + if (pd + i >= buf_end || pd[i] != '\0') break; + } else { + break; + } + } + } + +done: + return result; +} + +static match_result +match_narrow_and_wide_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *rec, Buffer *buf, void *criterion) +{ + cbs_t *info = (cbs_t *)criterion; + const guint8 *ascii_text = info->data; + size_t textlen = info->data_len; + match_result result; + guint32 buf_len; + guint8 *pd, *buf_start, *buf_end; + guint32 i; + guint8 c_char; + size_t c_match = 0; + + /* Load the frame's data. */ + if (!cf_read_record(cf, fdata, rec, buf)) { + /* Attempt to get the packet failed. */ + return MR_ERROR; + } + + result = MR_NOTMATCHED; + /* Has to be room to hold the sought data. */ + if (textlen > fdata->cap_len) { + return result; + } + buf_len = fdata->cap_len; + buf_start = ws_buffer_start_ptr(buf); + buf_end = buf_start + buf_len; + pd = buf_end - textlen; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte before the previous match start */ + pd = buf_start + cf->search_pos - 1; + } + for (; pd < buf_end; pd++) { + pd = (uint8_t *)ws_memrchr(buf_start, ascii_text[0], pd - buf_start + 1); + if (pd == NULL) break; + /* Try narrow match at this start location */ + c_match = 0; + for (i = 0; pd + i < buf_end; i++) { + c_char = pd[i]; + if (c_char == ascii_text[c_match]) { + c_match++; + if (c_match == textlen) { + result = MR_MATCHED; + /* Save position and length for highlighting the field. */ + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); + goto done; + } + } else { + break; + } + } + + /* Now try wide match at the same start location. */ + c_match = 0; + for (i = 0; pd + i < buf_end; i++) { + c_char = pd[i]; + if (c_char == ascii_text[c_match]) { + c_match++; + if (c_match == textlen) { + result = MR_MATCHED; + /* Save position and length for highlighting the field. */ + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); goto done; } i++; @@ -3694,7 +3824,12 @@ match_narrow_and_wide_case(capture_file *cf, frame_data *fdata, buf_len = fdata->cap_len; buf_start = ws_buffer_start_ptr(buf); buf_end = buf_start + buf_len; - for (pd = buf_start; pd < buf_end; pd++) { + pd = buf_start; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte past the previous match start */ + pd += cf->search_pos + 1; + } + for (; pd < buf_end; pd++) { pd = (guint8 *)ws_mempbrk_exec(pd, buf_end - pd, pattern, &c_char); if (pd == NULL) break; /* Try narrow match at this start location */ @@ -3706,8 +3841,8 @@ match_narrow_and_wide_case(capture_file *cf, frame_data *fdata, if (c_match == textlen) { result = MR_MATCHED; /* Save position and length for highlighting the field. */ - cf->search_pos = (guint32)(pd - buf_start); - cf->search_len = (guint32)(textlen); + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); goto done; } } else { @@ -3724,8 +3859,90 @@ match_narrow_and_wide_case(capture_file *cf, frame_data *fdata, if (c_match == textlen) { result = MR_MATCHED; /* Save position and length for highlighting the field. */ - cf->search_pos = (guint32)(pd - buf_start); - cf->search_len = (guint32)(textlen); + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); + goto done; + } + i++; + if (pd + i >= buf_end || pd[i] != '\0') break; + } else { + break; + } + } + } + +done: + return result; +} + +static match_result +match_narrow_and_wide_case_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *rec, Buffer *buf, void *criterion) +{ + cbs_t *info = (cbs_t *)criterion; + const guint8 *ascii_text = info->data; + size_t textlen = info->data_len; + ws_mempbrk_pattern *pattern = info->pattern; + match_result result; + guint32 buf_len; + guint8 *pd, *buf_start, *buf_end; + guint32 i; + guint8 c_char; + size_t c_match = 0; + + /* Load the frame's data. */ + if (!cf_read_record(cf, fdata, rec, buf)) { + /* Attempt to get the packet failed. */ + return MR_ERROR; + } + + ws_assert(pattern != NULL); + + result = MR_NOTMATCHED; + /* Has to be room to hold the sought data. */ + if (textlen > fdata->cap_len) { + return result; + } + buf_len = fdata->cap_len; + buf_start = ws_buffer_start_ptr(buf); + buf_end = buf_start + buf_len; + pd = buf_end - textlen; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte before the previous match start */ + pd = buf_start + cf->search_pos - 1; + } + for (; pd >= buf_start; pd--) { + pd = (guint8 *)ws_memrpbrk_exec(buf_start, pd - buf_start + 1, pattern, &c_char); + if (pd == NULL) break; + /* Try narrow match at this start location */ + c_match = 0; + for (i = 0; pd + i < buf_end; i++) { + c_char = g_ascii_toupper(pd[i]); + if (c_char == ascii_text[c_match]) { + c_match++; + if (c_match == textlen) { + result = MR_MATCHED; + /* Save position and length for highlighting the field. */ + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); + goto done; + } + } else { + break; + } + } + + /* Now try wide match at the same start location. */ + c_match = 0; + for (i = 0; pd + i < buf_end; i++) { + c_char = g_ascii_toupper(pd[i]); + if (c_char == ascii_text[c_match]) { + c_match++; + if (c_match == textlen) { + result = MR_MATCHED; + /* Save position and length for highlighting the field. */ + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); goto done; } i++; @@ -3768,7 +3985,12 @@ match_narrow_case(capture_file *cf, frame_data *fdata, buf_len = fdata->cap_len; buf_start = ws_buffer_start_ptr(buf); buf_end = buf_start + buf_len; - for (pd = buf_start; pd < buf_end; pd++) { + pd = buf_start; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte past the previous match start */ + pd += cf->search_pos + 1; + } + for (; pd < buf_end; pd++) { pd = (guint8 *)ws_mempbrk_exec(pd, buf_end - pd, pattern, &c_char); if (pd == NULL) break; c_match = 0; @@ -3779,8 +4001,69 @@ match_narrow_case(capture_file *cf, frame_data *fdata, if (c_match == textlen) { /* Save position and length for highlighting the field. */ result = MR_MATCHED; - cf->search_pos = (guint32)(pd - buf_start); - cf->search_len = (guint32)(textlen); + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); + goto done; + } + } else { + break; + } + } + } + +done: + return result; +} + +static match_result +match_narrow_case_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *rec, Buffer *buf, void *criterion) +{ + cbs_t *info = (cbs_t *)criterion; + const guint8 *ascii_text = info->data; + size_t textlen = info->data_len; + ws_mempbrk_pattern *pattern = info->pattern; + match_result result; + guint32 buf_len; + guint8 *pd, *buf_start, *buf_end; + guint32 i; + guint8 c_char; + size_t c_match = 0; + + /* Load the frame's data. */ + if (!cf_read_record(cf, fdata, rec, buf)) { + /* Attempt to get the packet failed. */ + return MR_ERROR; + } + + ws_assert(pattern != NULL); + + result = MR_NOTMATCHED; + /* Has to be room to hold the sought data. */ + if (textlen > fdata->cap_len) { + return result; + } + buf_len = fdata->cap_len; + buf_start = ws_buffer_start_ptr(buf); + buf_end = buf_start + buf_len; + pd = buf_end - textlen; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte before the previous match start */ + pd = buf_start + cf->search_pos - 1; + } + for (; pd >= buf_start; pd--) { + pd = (guint8 *)ws_memrpbrk_exec(buf_start, pd - buf_start + 1, pattern, &c_char); + if (pd == NULL) break; + c_match = 0; + for (i = 0; pd + i < buf_end; i++) { + c_char = g_ascii_toupper(pd[i]); + if (c_char == ascii_text[c_match]) { + c_match++; + if (c_match == textlen) { + /* Save position and length for highlighting the field. */ + result = MR_MATCHED; + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); goto done; } } else { @@ -3817,7 +4100,12 @@ match_wide(capture_file *cf, frame_data *fdata, buf_len = fdata->cap_len; buf_start = ws_buffer_start_ptr(buf); buf_end = buf_start + buf_len; - for (pd = buf_start; pd < buf_end; pd++) { + pd = buf_start; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte past the previous match start */ + pd += cf->search_pos + 1; + } + for (; pd < buf_end; pd++) { pd = (guint8 *)memchr(pd, ascii_text[0], buf_end - pd); if (pd == NULL) break; c_match = 0; @@ -3828,8 +4116,68 @@ match_wide(capture_file *cf, frame_data *fdata, if (c_match == textlen) { result = MR_MATCHED; /* Save position and length for highlighting the field. */ - cf->search_pos = (guint32)(pd - buf_start); - cf->search_len = (guint32)(textlen); + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); + goto done; + } + i++; + if (pd + i >= buf_end || pd[i] != '\0') break; + } else { + break; + } + } + } + +done: + return result; +} + +static match_result +match_wide_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *rec, Buffer *buf, void *criterion) +{ + cbs_t *info = (cbs_t *)criterion; + const guint8 *ascii_text = info->data; + size_t textlen = info->data_len; + match_result result; + guint32 buf_len; + guint8 *pd, *buf_start, *buf_end; + guint32 i; + guint8 c_char; + size_t c_match = 0; + + /* Load the frame's data. */ + if (!cf_read_record(cf, fdata, rec, buf)) { + /* Attempt to get the packet failed. */ + return MR_ERROR; + } + + result = MR_NOTMATCHED; + /* Has to be room to hold the sought data. */ + if (textlen > fdata->cap_len) { + return result; + } + buf_len = fdata->cap_len; + buf_start = ws_buffer_start_ptr(buf); + buf_end = buf_start + buf_len; + pd = buf_end - textlen; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte before the previous match start */ + pd = buf_start + cf->search_pos - 1; + } + for (; pd < buf_end; pd++) { + pd = (uint8_t *)ws_memrchr(buf_start, ascii_text[0], pd - buf_start + 1); + if (pd == NULL) break; + c_match = 0; + for (i = 0; pd + i < buf_end; i++) { + c_char = pd[i]; + if (c_char == ascii_text[c_match]) { + c_match++; + if (c_match == textlen) { + result = MR_MATCHED; + /* Save position and length for highlighting the field. */ + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); goto done; } i++; @@ -3872,7 +4220,12 @@ match_wide_case(capture_file *cf, frame_data *fdata, buf_len = fdata->cap_len; buf_start = ws_buffer_start_ptr(buf); buf_end = buf_start + buf_len; - for (pd = buf_start; pd < buf_end; pd++) { + pd = buf_start; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte past the previous match start */ + pd += cf->search_pos + 1; + } + for (; pd < buf_end; pd++) { pd = (guint8 *)ws_mempbrk_exec(pd, buf_end - pd, pattern, &c_char); if (pd == NULL) break; c_match = 0; @@ -3883,8 +4236,72 @@ match_wide_case(capture_file *cf, frame_data *fdata, if (c_match == textlen) { result = MR_MATCHED; /* Save position and length for highlighting the field. */ - cf->search_pos = (guint32)(pd - buf_start); - cf->search_len = (guint32)(textlen); + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); + goto done; + } + i++; + if (pd + i >= buf_end || pd[i] != '\0') break; + } else { + break; + } + } + } + +done: + return result; +} + +/* Case insensitive match */ +static match_result +match_wide_case_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *rec, Buffer *buf, void *criterion) +{ + cbs_t *info = (cbs_t *)criterion; + const guint8 *ascii_text = info->data; + size_t textlen = info->data_len; + ws_mempbrk_pattern *pattern = info->pattern; + match_result result; + guint32 buf_len; + guint8 *pd, *buf_start, *buf_end; + guint32 i; + guint8 c_char; + size_t c_match = 0; + + /* Load the frame's data. */ + if (!cf_read_record(cf, fdata, rec, buf)) { + /* Attempt to get the packet failed. */ + return MR_ERROR; + } + + ws_assert(pattern != NULL); + + result = MR_NOTMATCHED; + /* Has to be room to hold the sought data. */ + if (textlen > fdata->cap_len) { + return result; + } + buf_len = fdata->cap_len; + buf_start = ws_buffer_start_ptr(buf); + buf_end = buf_start + buf_len; + pd = buf_end - textlen; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte before the previous match start */ + pd = buf_start + cf->search_pos - 1; + } + for (; pd >= buf_start; pd--) { + pd = (guint8 *)ws_memrpbrk_exec(buf_start, pd - buf_start + 1, pattern, &c_char); + if (pd == NULL) break; + c_match = 0; + for (i = 0; pd + i < buf_end; i++) { + c_char = g_ascii_toupper(pd[i]); + if (c_char == ascii_text[c_match]) { + c_match++; + if (c_match == textlen) { + result = MR_MATCHED; + /* Save position and length for highlighting the field. */ + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)(i + 1); goto done; } i++; @@ -3906,7 +4323,7 @@ match_binary(capture_file *cf, frame_data *fdata, cbs_t *info = (cbs_t *)criterion; size_t datalen = info->data_len; match_result result; - const uint8_t *pd, *buf_start; + const uint8_t *pd = NULL, *buf_start; /* Load the frame's data. */ if (!cf_read_record(cf, fdata, rec, buf)) { @@ -3916,7 +4333,14 @@ match_binary(capture_file *cf, frame_data *fdata, result = MR_NOTMATCHED; buf_start = ws_buffer_start_ptr(buf); - pd = ws_memmem(buf_start, fdata->cap_len, info->data, datalen); + size_t offset = 0; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte past the previous match start */ + offset = cf->search_pos + 1; + } + if (offset < fdata->cap_len) { + pd = ws_memmem(buf_start + offset, fdata->cap_len - offset, info->data, datalen); + } if (pd != NULL) { result = MR_MATCHED; /* Save position and length for highlighting the field. */ @@ -3927,6 +4351,47 @@ match_binary(capture_file *cf, frame_data *fdata, return result; } +static match_result +match_binary_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *rec, Buffer *buf, void *criterion) +{ + cbs_t *info = (cbs_t *)criterion; + size_t datalen = info->data_len; + match_result result; + const uint8_t *pd = NULL, *buf_start; + + /* Load the frame's data. */ + if (!cf_read_record(cf, fdata, rec, buf)) { + /* Attempt to get the packet failed. */ + return MR_ERROR; + } + + result = MR_NOTMATCHED; + buf_start = ws_buffer_start_ptr(buf); + /* Has to be room to hold the sought data. */ + if (datalen > fdata->cap_len) { + return result; + } + pd = buf_start + fdata->cap_len - datalen; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte before the previous match start */ + pd = buf_start + cf->search_pos - 1; + } + for (; pd >= buf_start; pd--) { + pd = (uint8_t *)ws_memrchr(buf_start, info->data[0], pd - buf_start + 1); + if (pd == NULL) break; + if (memcmp(pd, info->data, datalen) == 0) { + result = MR_MATCHED; + /* Save position and length for highlighting the field. */ + cf->search_pos = (uint32_t)(pd - buf_start); + cf->search_len = (uint32_t)datalen; + break; + } + } + + return result; +} + static match_result match_regex(capture_file *cf, frame_data *fdata, wtap_rec *rec, Buffer *buf, void *criterion _U_) @@ -3940,18 +4405,62 @@ match_regex(capture_file *cf, frame_data *fdata, return MR_ERROR; } - if (ws_regex_matches_pos(cf->regex, - (const gchar *)ws_buffer_start_ptr(buf), - fdata->cap_len, 0, - result_pos)) { - //TODO: A chosen regex can match the empty string (zero length) - // which doesn't make a lot of sense for searching the packet bytes. - // Should we search with the PCRE2_NOTEMPTY option? - //TODO: Fix cast. - /* Save position and length for highlighting the field. */ - cf->search_pos = (guint32)(result_pos[0]); - cf->search_len = (guint32)(result_pos[1] - result_pos[0]); - result = MR_MATCHED; + size_t offset = 0; + if (cf->search_len || cf->search_pos) { + /* we want to start searching one byte past the previous match start */ + offset = cf->search_pos + 1; + } + if (offset < fdata->cap_len) { + if (ws_regex_matches_pos(cf->regex, + (const gchar *)ws_buffer_start_ptr(buf), + fdata->cap_len, offset, + result_pos)) { + //TODO: A chosen regex can match the empty string (zero length) + // which doesn't make a lot of sense for searching the packet bytes. + // Should we search with the PCRE2_NOTEMPTY option? + //TODO: Fix cast. + /* Save position and length for highlighting the field. */ + cf->search_pos = (guint32)(result_pos[0]); + cf->search_len = (guint32)(result_pos[1] - result_pos[0]); + result = MR_MATCHED; + } + } + return result; +} + +static match_result +match_regex_reverse(capture_file *cf, frame_data *fdata, + wtap_rec *rec, Buffer *buf, void *criterion _U_) +{ + match_result result = MR_NOTMATCHED; + size_t result_pos[2] = {0, 0}; + + /* Load the frame's data. */ + if (!cf_read_record(cf, fdata, rec, buf)) { + /* Attempt to get the packet failed. */ + return MR_ERROR; + } + + size_t offset = fdata->cap_len - 1; + if (cf->search_pos) { + /* we want to start searching one byte before the previous match */ + offset = cf->search_pos - 1; + } + for (; offset > 0; offset--) { + if (ws_regex_matches_pos(cf->regex, + (const gchar *)ws_buffer_start_ptr(buf), + fdata->cap_len, offset, + result_pos)) { + //TODO: A chosen regex can match the empty string (zero length) + // which doesn't make a lot of sense for searching the packet bytes. + // Should we search with the PCRE2_NOTEMPTY option? + //TODO: Fix cast. + /* Save position and length for highlighting the field. */ + cf->search_pos = (guint32)(result_pos[0]); + cf->search_len = (guint32)(result_pos[1] - result_pos[0]); + result = MR_MATCHED; + break; + } } return result; } @@ -4194,12 +4703,12 @@ find_packet(capture_file *cf, ws_match_function match_function, cf->search_in_progress = TRUE; found_row = packet_list_select_row_from_data(new_fd); cf->search_in_progress = FALSE; - cf->search_pos = 0; /* Reset the position */ - cf->search_len = 0; /* Reset length */ if (!found_row) { /* We didn't find a row corresponding to this frame. This means that the frame isn't being displayed currently, so we can't select it. */ + cf->search_pos = 0; /* Reset the position */ + cf->search_len = 0; /* Reset length */ simple_message_box(ESD_TYPE_INFO, NULL, "The capture file is probably not fully dissected.", "End of capture exceeded."); diff --git a/file.h b/file.h index 0c3e135a8b..bd24ca63fb 100644 --- a/file.h +++ b/file.h @@ -550,10 +550,13 @@ gboolean cf_find_packet_summary_line(capture_file *cf, const char *string, * @param string the string to find * @param string_size the size of the string to find * @param dir direction in which to search + * @param multiple whether to look for the next occurrence of the same string + * in the current packet, or to only match once per frame * @return TRUE if a packet was found, FALSE otherwise */ gboolean cf_find_packet_data(capture_file *cf, const guint8 *string, - size_t string_size, search_direction dir); + size_t string_size, search_direction dir, + bool multiple); /** * Find packet that matches a compiled display filter. diff --git a/ui/logray/logray_main_window_slots.cpp b/ui/logray/logray_main_window_slots.cpp index e592b88e27..4d08656ddb 100644 --- a/ui/logray/logray_main_window_slots.cpp +++ b/ui/logray/logray_main_window_slots.cpp @@ -1253,6 +1253,22 @@ void LograyMainWindow::setMenusForSelectedTreeRow(FieldInformation *finfo) { if (fi && fi->ds_tvb && (fi->length > 0)) { have_packet_bytes = true; } + + if (!(capture_file_.capFile()->search_in_progress && (capture_file_.capFile()->hex || (capture_file_.capFile()->string && capture_file_.capFile()->packet_data)))) { + // If we're not in the middle of a packet bytes search, then set + // search_pos and search_len so that we can start a new search + // from this point. (If we are, then we already set it.) + if (fi && capture_file_.capFile()->edt && (fi->ds_tvb == capture_file_.capFile()->edt->tvb)) { + // We can only do a Packet Bytes search in the main bytes from + // the frame, not from any secondary data sources. (XXX: This + // might be surprising to users, though.) + capture_file_.capFile()->search_pos = (uint32_t)(finfo->position().start + finfo->position().length - 1); + capture_file_.capFile()->search_len = (uint32_t)finfo->position().length; + } else { + capture_file_.capFile()->search_pos = 0; + capture_file_.capFile()->search_len = 0; + } + } } if (capture_file_.capFile() != NULL && fi != NULL) { diff --git a/ui/qt/search_frame.cpp b/ui/qt/search_frame.cpp index a7de48ed0f..e8b6cd9498 100644 --- a/ui/qt/search_frame.cpp +++ b/ui/qt/search_frame.cpp @@ -144,6 +144,9 @@ bool SearchFrame::regexCompile() if (!sf_ui_->caseCheckBox->isChecked()) { flags |= WS_REGEX_CASELESS; } + if (sf_ui_->dirCheckBox->isChecked()) { + flags |= WS_REGEX_ANCHORED; + } if (regex_) { ws_regex_free(regex_); @@ -240,7 +243,11 @@ void SearchFrame::updateWidgets() // (otherwise all strings have already been converted to UTF-8) sf_ui_->charEncodingComboBox->setEnabled(search_type == string_search_ && sf_ui_->searchInComboBox->currentIndex() == in_bytes_); - sf_ui_->multipleCheckBox->setEnabled(sf_ui_->searchInComboBox->isEnabled() && sf_ui_->searchInComboBox->currentIndex() == in_proto_tree_); + // We can search for multiple matches in the same frame if we're doing + // a Proto Tree search or a Frame Bytes search, but not a string/regex + // search in the Packet List, or a display filter search (since those + // don't highlight what fields / offsets caused the match.) + sf_ui_->multipleCheckBox->setEnabled((sf_ui_->searchInComboBox->isEnabled() && sf_ui_->searchInComboBox->currentIndex() != in_packet_list_) || search_type == hex_search_); switch (search_type) { case df_search_: @@ -303,7 +310,7 @@ void SearchFrame::on_searchInComboBox_currentIndexChanged(int idx) break; } - // We only search for multiple occurrences in packet list currently. + // We only search for multiple occurrences in packet list and bytes updateWidgets(); } @@ -474,7 +481,7 @@ void SearchFrame::on_findButton_clicked() if (cap_file_->hex) { /* Hex value in packet data */ - found_packet = cf_find_packet_data(cap_file_, bytes, nbytes, cap_file_->dir); + found_packet = cf_find_packet_data(cap_file_, bytes, nbytes, cap_file_->dir, multiple_occurrences); g_free(bytes); if (!found_packet) { /* We didn't find a packet */ @@ -504,7 +511,7 @@ void SearchFrame::on_findButton_clicked() } } else if (cap_file_->packet_data && string) { /* String in the ASCII-converted packet data */ - found_packet = cf_find_packet_data(cap_file_, (guint8 *) string, strlen(string), cap_file_->dir); + found_packet = cf_find_packet_data(cap_file_, (guint8 *) string, strlen(string), cap_file_->dir, multiple_occurrences); g_free(string); if (!found_packet) { err_string = tr("No packet contained that string in its converted data."); diff --git a/ui/qt/wireshark_main_window_slots.cpp b/ui/qt/wireshark_main_window_slots.cpp index f42a5c149c..f1a9a76342 100644 --- a/ui/qt/wireshark_main_window_slots.cpp +++ b/ui/qt/wireshark_main_window_slots.cpp @@ -1364,6 +1364,22 @@ void WiresharkMainWindow::setMenusForSelectedTreeRow(FieldInformation *finfo) { if (fi && fi->ds_tvb && (fi->length > 0)) { have_packet_bytes = true; } + + if (!(capture_file_.capFile()->search_in_progress && (capture_file_.capFile()->hex || (capture_file_.capFile()->string && capture_file_.capFile()->packet_data)))) { + // If we're not in the middle of a packet bytes search, then set + // search_pos and search_len so that we can start a new search + // from this point. (If we are, then we already set it.) + if (fi && capture_file_.capFile()->edt && (fi->ds_tvb == capture_file_.capFile()->edt->tvb)) { + // We can only do a Packet Bytes search in the main bytes from + // the frame, not from any secondary data sources. (XXX: This + // might be surprising to users, though.) + capture_file_.capFile()->search_pos = (uint32_t)(finfo->position().start + finfo->position().length - 1); + capture_file_.capFile()->search_len = (uint32_t)finfo->position().length; + } else { + capture_file_.capFile()->search_pos = 0; + capture_file_.capFile()->search_len = 0; + } + } } if (capture_file_.capFile() != NULL && fi != NULL) { diff --git a/wsutil/regex.c b/wsutil/regex.c index ee9d80effc..464a42239f 100644 --- a/wsutil/regex.c +++ b/wsutil/regex.c @@ -60,6 +60,8 @@ compile_pcre2(const char *patt, ssize_t size, char **errmsg, unsigned flags) options |= PCRE2_NEVER_UTF; if (flags & WS_REGEX_CASELESS) options |= PCRE2_CASELESS; + if (flags & WS_REGEX_ANCHORED) + options |= PCRE2_ANCHORED; /* By default UTF-8 is off. */ code = pcre2_compile_8((PCRE2_SPTR)patt, diff --git a/wsutil/regex.h b/wsutil/regex.h index 083a4ce858..199779a2af 100644 --- a/wsutil/regex.h +++ b/wsutil/regex.h @@ -26,6 +26,7 @@ ws_regex_compile(const char *patt, char **errmsg); /* By default UTF-8 is off. This option also prevents it from being * turned on using a pattern option. */ #define WS_REGEX_NEVER_UTF (1U << 1) +#define WS_REGEX_ANCHORED (1U << 2) WS_DLL_PUBLIC ws_regex_t * ws_regex_compile_ex(const char *patt, ssize_t size, char **errmsg, unsigned flags);