ongrep

A cleaned up fork of ngrep for OpenBSD
git clone git://git.sgregoratto.me/ongrep
Log | Files | Refs | README | LICENSE

commit 47c1ebdcc73b3a53d0f683654b031709761d5e4f
parent 2b405bcea731e1f15cc05ce488bf69385b7a8000
Author: Stephen Gregoratto <dev@sgregoratto.me>
Date:   Tue, 23 Jun 2020 20:24:36 +1000

Split setup_matcher() into two, rewrite hex conversion

Moved the logic for setup_matcher into main and split it into two
functions: setup_{hex,pattern}_match().

For PCRE matching:
- Changed re_err and err_offset to be the same types required by pcre.
  This also removes some type-casting.
- Replaced (unchecked!) calls to malloc and snprintf with asprintf for
  creating the word regex pattern.

For hex matching:
- Completely rewrite how hex strings are turned into bytes. The original
  method felt sketchy with it calling sscanf, and it didn't check if
  there was any hex after the 0x prefix. I replaced it with two simpler
  functions.
- Hex expressions can now have a "0X" prefix.
- When printing the match expression, main would call strchr searching
  for 'x' to see if it had a prefix. Because of the above change, a new
  variable hex_exp_pfx will be set if the hex expression has a prefix.

The option checking for -X and -[iMw] has also been moved higher up in
main.

Diffstat:
Mngrep.c | 193++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mngrep.h | 6+++---
2 files changed, 125 insertions(+), 74 deletions(-)

diff --git a/ngrep.c b/ngrep.c @@ -59,13 +59,14 @@ char *usedev = NULL; char nonprint_char = '.'; /* PCRE and matching */ -int32_t err_offset; -char *re_err = NULL; +int err_offset; +const char *re_err = NULL; pcre *pattern = NULL; pcre_extra *pattern_extra = NULL; char *match_data = NULL, *bin_data = NULL; +int hex_exp_pfx = 0; uint16_t match_len = 0; int8_t (*match_func)() = &blank_match_func; @@ -255,6 +256,18 @@ main(int argc, char **argv) warnx("-x is incompatible with -W"); usage(); } + if (bin_match) { + if (re_ignore_case) { + warnx("-i is incompatible with -X"); + usage(); + } else if (re_multiline_match == 0) { + warnx("-M is incompatible with -X"); + usage(); + } else if (re_match_word) { + warnx("-w is incompatible with -X"); + usage(); + } + } if (argv[optind]) match_data = argv[optind++]; @@ -280,12 +293,19 @@ main(int argc, char **argv) /* Setup matcher */ if (match_data) { - if (setup_matcher()) - clean_exit(-1); + if (bin_match) { + if (setup_hex_match() == -1) + clean_exit(-1); + } else { + if (setup_pattern_match() == -1) + clean_exit(-1); + } - if (quiet < 2 && strlen(match_data)) - printf("%smatch: %s%s\n", invert_match ? "don't " : "", - (bin_data && !strchr(match_data, 'x')) ? "0x" : "", match_data); + if (quiet < 2) + printf("%smatch: %s%s\n", + invert_match ? "don't " : "", + hex_exp_pfx ? "" : "0x", + match_data); if (re_match_word) free(match_data); @@ -430,64 +450,113 @@ setup_bpf_filter(char **argv) } int -setup_matcher(void) +hextoc(char d, char *c) { - if (bin_match) { - uint32_t i = 0, n; - uint32_t len; - char *s, *d; + if (c == NULL) + return -1; - if (re_match_word || re_ignore_case) { - warn("fatal: regex switches are incompatible with binary matching"); - return 1; - } - len = (uint32_t) strlen(match_data); - if (len % 2 != 0 || !strishex(match_data)) { - warnx("fatal: invalid hex string specified"); - return 1; - } - bin_data = (char *) malloc(len / 2); - memset(bin_data, 0, len / 2); - d = bin_data; - - if ((s = strchr(match_data, 'x'))) - len -= (uint32_t) (++s - match_data - 1); - else - s = match_data; - - while (i <= len) { - sscanf(s + i, "%2x", &n); - *d++ = n; - i += 2; - } + if (d >= '0' && d <= '9') + *c = d - '0'; + else if (d >= 'A' && d <= 'F') + *c = d - 'A' + 10; + else if (d >= 'a' && d <= 'f') + *c = d - 'a' + 10; + else + return -1; - match_len = len / 2; - match_func = &bin_match_func; + return 0; +} - } else { - uint32_t pcre_options = PCRE_UNGREEDY; +int +setup_hex_match(void) +{ + size_t len; + char *bytes = NULL, *str = match_data; + char ld, rd; + + if (str == NULL || *str == '\0') { + warnx("hex expression is empty"); + goto err; + } + + len = strlen(str); + if (len % 2 != 0) { + warnx("hex expression has odd digits: %s", match_data); + goto err; + } - if (re_ignore_case) - pcre_options |= PCRE_CASELESS; + if (str[0] == '0' && (str[1] == 'X' || str[1] == 'x')) { + hex_exp_pfx = 1; + str += 2; + len -= 2; + } + if (len == 0) { + warnx("hex expression has no digits after prefix: %s", + match_data); + goto err; + } else if (len / 2 > UINT16_MAX) { + /* XXX: Check bin_match_func to see if this is needed */ + warnx("hex expression is too long: %s", match_data); + goto err; + } - if (re_multiline_match) - pcre_options |= PCRE_DOTALL; + if ((bytes = calloc(len / 2, 1)) == NULL) + err(2, "calloc"); - if (re_match_word) { - char *word_regex = (char *) malloc(strlen(match_data) * 3 + strlen(WORD_REGEX)); - sprintf(word_regex, WORD_REGEX, match_data, match_data, match_data); - match_data = word_regex; + for (size_t i = 0, j = 0; j < len; j += 2, i++) { + if (hextoc(str[j], &ld) == -1) { + warnx("hex expression has invalid digit '%c': %s", + str[j], match_data); + goto err; } - pattern = pcre_compile(match_data, pcre_options, (const char **) &re_err, &err_offset, 0); + if (hextoc(str[j + 1], &rd) == -1) { + warnx("hex expression has invalid digit '%c': %s", + str[j + 1], match_data); + goto err; + } + bytes[i] = (ld << 4) | rd; + } - if (!pattern) { - warnx("pcre_compile: %s", re_err); - return 1; + bin_data = bytes; + match_len = len / 2; + match_func = &bin_match_func; + + return 0; +err: + free(bytes); + + return -1; +} + +int +setup_pattern_match(void) +{ + int pcre_options = PCRE_UNGREEDY; + char *word_regex; + + if (re_ignore_case) + pcre_options |= PCRE_CASELESS; + if (re_multiline_match) + pcre_options |= PCRE_DOTALL; + + if (re_match_word) { + if (asprintf(&word_regex, WORD_REGEX, + match_data, match_data, match_data) < 0) { + warn("asprintf"); + return -1; } - pattern_extra = pcre_study(pattern, 0, (const char **) &re_err); - match_func = &re_match_func; + match_data = word_regex; } + pattern = pcre_compile(match_data, pcre_options, &re_err, &err_offset, + NULL); + if (!pattern) { + warnx("pcre_compile: %s", re_err); + return -1; + } + pattern_extra = pcre_study(pattern, 0, &re_err); + match_func = &re_match_func; + return 0; } @@ -1042,24 +1111,6 @@ get_filter_from_argv(char **argv) return fstr; } -uint8_t -strishex(char *str) -{ - char *s; - - if ((s = strchr(str, 'x'))) - s++; - else - s = str; - - while (*s) - if (!isxdigit(*s++)) - return 0; - - return 1; -} - - void print_time_absolute(struct pcap_pkthdr * h) { diff --git a/ngrep.h b/ngrep.h @@ -61,11 +61,11 @@ typedef enum { TCP = 'T', UDP = 'U', ICMP = 'I', ICMPv6 = 'I', IGMP = 'G', UNKNOWN = '?' } netident_t; -/* - * Prototypes function signatures. - */ int setup_pcap_source(void); int setup_bpf_filter(char **); +int hextoc(char d, char *c); +int setup_hex_match(void); +int setup_pattern_match(void); int setup_matcher(void); void process(u_char *, struct pcap_pkthdr *, u_char *);