Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stream following capability #9

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion ngrep.8
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ ngrep \- network grep

.SH SYNOPSIS

.B ngrep <-hNXViwqpevxlDtTRM> <-IO
.B ngrep <-hNXViwqpevxlDtTRMQ> <-IO
.I pcap_dump
.B > < -n
.I num
.B > < -d
.I dev
.B > < -A
.I num
.B > < -J
.I num
.B > < -s
.I snaplen
.B > < -S
Expand Down Expand Up @@ -184,6 +186,14 @@ this option to force ngrep to listen on interface \fIdev\fP.
.IP "-A num"
Dump \fInum\fP packets of trailing context after matching a packet.

.IP "-J num"
Like -A, but print only those packets in the same TCP stream as the
matched packet.

.IP -Q
When a new matching packet comes in but the number of packets given to
-J hasn't been reached yet, start with this new match and follow it instead.

.IP "\fI match expression\fP"
A match expression is either an extended regular expression, or if the
\fI-X\fP option is specified, a string signifying a hexadecimal value.
Expand Down
52 changes: 47 additions & 5 deletions ngrep.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@

uint32_t snaplen = 65535, limitlen = 65535, promisc = 1, to = 100;
uint32_t match_after = 0, keep_matching = 0, matches = 0, max_matches = 0;
uint32_t seen_frames = 0;
uint32_t seen_frames = 0, follow_stream = 0, reset_count = 0;

#if USE_TCPKILL
uint32_t tcpkill_active = 0;
Expand Down Expand Up @@ -186,6 +186,12 @@ void (*print_time)() = NULL, (*dump_delay)() = dump_delay_proc_init;

uint32_t ws_row, ws_col = 80, ws_col_forced = 0;

/*
* Stream following functionality
*/
const char *follow_ip_src = NULL, *follow_ip_dst = NULL;
uint16_t follow_sport, follow_dport;


int main(int argc, char **argv) {
int32_t c;
Expand All @@ -210,8 +216,7 @@ int main(int argc, char **argv) {
setlocale(LC_CTYPE, locale);
}
#endif

while ((c = getopt(argc, argv, "LNhXViwqpevxlDtTRMK:Cs:n:c:d:A:I:O:S:P:F:W:")) != EOF) {
while ((c = getopt(argc, argv, "LNhXViwqpevxlDtTRMQK:Cs:n:c:d:A:I:J:O:S:P:F:W:")) != EOF) {
switch (c) {
case 'W': {
if (!strcasecmp(optarg, "normal"))
Expand Down Expand Up @@ -250,6 +255,15 @@ int main(int argc, char **argv) {
if (match_after < UINT32_MAX)
match_after++;
break;
case 'J':
match_after = _atoui32(optarg);
if (match_after < UINT32_MAX)
match_after++;
follow_stream = 1;
break;
case 'Q':
reset_count = 1;
break;
#if defined(_WIN32)
case 'L':
win32_listdevices();
Expand Down Expand Up @@ -913,9 +927,35 @@ void dump_packet(struct pcap_pkthdr *h, u_char *p, uint8_t proto, unsigned char
if (len > limitlen)
len = limitlen;

if ((len > 0 && match_func(data, len, &match_index, &match_size) == invert_match) && !keep_matching)
int notmatched = len > 0 && match_func(data, len, &match_index, &match_size) == invert_match;
if (notmatched && !keep_matching)
return;

if (!notmatched && reset_count)
follow_ip_src = NULL;

if (keep_matching && follow_stream) {
if (follow_ip_src == NULL) {
follow_ip_src = ip_src;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much have you tested this functionality?

You introduce the global pointer follow_ip_src and set it to ip_src, which itself is a stack-based variable in the caller process() - thus ip_src deallocates with every call. This would make for a dangling global pointer on the next call, possibly creating a crash or a secvuln. I can see it maybe working anyway, either due to things just lining up with the same stack address for ip_src, or subsequent matches still happening due to a port match.

Do I misunderstand?

follow_ip_dst = ip_dst;
follow_sport = sport;
follow_dport = dport;
}
if (!(
(strcmp(ip_src, follow_ip_src) == 0 && // Forward stream
strcmp(ip_dst, follow_ip_dst) == 0 &&
sport == follow_sport &&
dport == follow_dport) ||
(!strcmp(ip_src, follow_ip_dst) == 0 && // Reverse stream
!strcmp(ip_dst, follow_ip_src) == 0 &&
sport == follow_dport &&
dport == follow_sport)
)) {
keep_matching++;
return;
}
}

if (!live_read && want_delay)
dump_delay(h);

Expand Down Expand Up @@ -1408,7 +1448,7 @@ void usage(void) {
#if defined(_WIN32)
"L"
#endif
"hNXViwqpevxlDtTRM> <-IO pcap_dump> <-n num> <-d dev> <-A num>\n"
"hNXViwqpevxlDtTRM> <-IO pcap_dump> <-n num> <-d dev> <-A num> <-J num>\n"
" <-s snaplen> <-S limitlen> <-W normal|byline|single|none> <-c cols>\n"
" <-P char> <-F file>"
#if USE_TCPKILL
Expand Down Expand Up @@ -1437,6 +1477,8 @@ void usage(void) {
" -O is dump matched packets in pcap format to pcap_dump\n"
" -n is look at only num packets\n"
" -A is dump num packets after a match\n"
" -J is dump num packets in TCP stream after a match\n"
" -Q is reset -J follow count after another match\n"
" -s is set the bpf caplen\n"
" -S is set the limitlen on matched packets\n"
" -W is set the dump format (normal, byline, single, none)\n"
Expand Down