Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1612 lines (1272 sloc) 40.9 KB
/*
* Copyright (c) 2017 Jordan Ritter <jpr5@darkridge.com>
*
* Please refer to the LICENSE file for more information.
*
*/
#if defined(BSD) || defined(SOLARIS) || defined(MACOSX)
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/tty.h>
#include <pwd.h>
#endif
#if defined(OSF1)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <net/route.h>
#include <sys/mbuf.h>
#include <arpa/inet.h>
#include <unistd>
#include <pwd.h>
#endif
#if defined(LINUX) || defined(__GLIBC__) || defined(__GNU__)
#include <getopt.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#endif
#if defined(AIX)
#include <sys/machine.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <time.h>
#include <unistd.h>
#include <pwd.h>
#endif
#if defined(_WIN32)
#include <io.h>
#include <getopt.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <types.h>
#include <config.h>
#define strcasecmp stricmp
#define strncasecmp strnicmp
#else
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <netinet/igmp.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <locale.h>
#if !defined(_WIN32)
#include <errno.h>
#include <sys/ioctl.h>
#endif
#include <pcap.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if USE_IPv6 && !defined(_WIN32)
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#endif
#if USE_PCRE
#include <pcre.h>
#else
#include <regex.h>
#endif
#include "ngrep.h"
/*
* Configuration Options
*/
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;
#if USE_TCPKILL
uint32_t tcpkill_active = 0;
#endif
uint8_t re_match_word = 0, re_ignore_case = 0, re_multiline_match = 1;
uint8_t show_empty = 0, show_hex = 0, show_proto = 0, quiet = 0;
uint8_t invert_match = 0, bin_match = 0;
uint8_t live_read = 1, want_delay = 0;
uint8_t dont_dropprivs = 0;
uint8_t enable_hilite = 0;
char *read_file = NULL, *dump_file = NULL;
char *usedev = NULL;
char nonprint_char = '.';
/*
* GNU Regex/PCRE
*/
#if USE_PCRE
int32_t err_offset;
char *re_err = NULL;
pcre *pattern = NULL;
pcre_extra *pattern_extra = NULL;
#else
const char *re_err = NULL;
struct re_pattern_buffer pattern;
#endif
/*
* Matching
*/
char *match_data = NULL, *bin_data = NULL;
uint16_t match_len = 0;
int8_t (*match_func)() = &blank_match_func;
int8_t dump_single = 0;
void (*dump_func)(unsigned char *, uint32_t, uint16_t, uint16_t) = &dump_formatted;
/*
* BPF/Network
*/
char *filter = NULL, *filter_file = NULL;
char pc_err[PCAP_ERRBUF_SIZE];
uint8_t link_offset;
uint8_t radiotap_present = 0;
uint8_t include_vlan = USE_VLAN_HACK;
pcap_t *pd = NULL, *pd_dumppcap = NULL;
pcap_dumper_t *pd_dump = NULL;
struct bpf_program pcapfilter;
struct in_addr net, mask;
/*
* Timestamp/delay functionality
*/
struct timeval prev_ts = {0, 0}, prev_delay_ts = {0,0};
#if defined(_WIN32)
struct timeval delay_tv;
FD_SET delay_fds;
SOCKET delay_socket = 0;
#endif
void (*print_time)() = NULL, (*dump_delay)() = dump_delay_proc_init;
/*
* Window-size functionality (adjust output based on width of console display)
*/
uint32_t ws_row, ws_col = 80, ws_col_forced = 0;
int main(int argc, char **argv) {
int32_t c;
signal(SIGINT, clean_exit);
signal(SIGABRT, clean_exit);
#if !defined(_WIN32)
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, clean_exit);
signal(SIGWINCH, update_windowsize);
#endif
setlocale(LC_ALL, "");
#if !defined(_WIN32)
{
char const *locale = getenv("LANG");
if (locale == NULL)
locale = "en_US";
setlocale(LC_CTYPE, locale);
}
#endif
while ((c = getopt(argc, argv, "LNhXViwqpevxlDtTRMK:Cs:n:c:d:A:I:O:S:P:F:W:")) != EOF) {
switch (c) {
case 'W': {
if (!strcasecmp(optarg, "normal"))
dump_func = &dump_formatted;
else if (!strcasecmp(optarg, "byline"))
dump_func = &dump_byline;
else if (!strcasecmp(optarg, "none"))
dump_func = &dump_unwrapped;
else if (!strcasecmp(optarg, "single")) {
dump_func = &dump_unwrapped;
dump_single = 1;
} else {
printf("fatal: unknown wrap method '%s'\n", optarg);
usage();
}
} break;
case 'F':
filter_file = optarg;
break;
case 'P':
nonprint_char = *optarg;
break;
case 'S': {
limitlen = _atoui32(optarg);
break;
}
case 'O':
dump_file = optarg;
break;
case 'I':
read_file = optarg;
break;
case 'A':
match_after = _atoui32(optarg);
if (match_after < UINT32_MAX)
match_after++;
break;
#if defined(_WIN32)
case 'L':
win32_listdevices();
clean_exit(2);
case 'd':
usedev = win32_usedevice(optarg);
break;
#else
case 'L':
perror("-L is a Win32-only option");
clean_exit(2);
case 'd':
usedev = optarg;
/* Linux: any = DLT_LINUX_SLL, pcap says incompatible with VLAN */
if (!strncasecmp(usedev, "any", 3))
include_vlan = 0;
break;
#endif
case 'c':
ws_col_forced = atoi(optarg);
break;
case 'n':
max_matches = _atoui32(optarg);
break;
case 's': {
uint16_t value = _atoui32(optarg);
if (value > 0)
snaplen = value;
} break;
case 'C':
enable_hilite = 1;
break;
case 'M':
re_multiline_match = 0;
break;
case 'R':
dont_dropprivs = 1;
break;
case 'T':
if (print_time == &print_time_diff) {
print_time = print_time_offset;
memset(&prev_ts, 0, sizeof(prev_ts));
} else {
print_time = &print_time_diff;
#if defined(_WIN32)
prev_ts.tv_sec = (uint32_t)time(NULL);
prev_ts.tv_usec = 0;
#else
gettimeofday(&prev_ts, NULL);
#endif
}
break;
case 't':
print_time = &print_time_absolute;
break;
case 'D':
want_delay = 1;
break;
case 'l':
setvbuf(stdout, NULL, _IOLBF, 0);
break;
case 'x':
show_hex++;
break;
case 'v':
invert_match++;
break;
case 'e':
show_empty++;
break;
case 'p':
promisc = 0;
break;
case 'q':
quiet++;
break;
case 'w':
re_match_word++;
break;
case 'i':
re_ignore_case++;
break;
case 'V':
version();
case 'X':
bin_match++;
break;
case 'N':
show_proto++;
break;
#if USE_TCPKILL
case 'K':
tcpkill_active = _atoui32(optarg);
break;
#endif
case 'h':
usage();
default:
usage();
}
}
if (show_hex && dump_func != &dump_formatted) {
printf("fatal: -x (hex dump) is incompatible with -W (alternate format)\n");
usage();
}
if (argv[optind])
match_data = argv[optind++];
#if USE_TCPKILL
if (tcpkill_active)
tcpkill_init();
#endif
/* Setup PCAP input */
if (setup_pcap_source())
clean_exit(2);
/* Setup BPF filter */
if (setup_bpf_filter(argv)) {
include_vlan = 0;
if (filter) { free(filter); filter = NULL; }
if (setup_bpf_filter(argv)) {
pcap_perror(pd, "pcap");
clean_exit(2);
}
}
if (filter) {
if (quiet < 2)
printf("filter: %s\n", filter);
free(filter);
}
/* Setup matcher */
if (match_data) {
if (setup_matcher())
clean_exit(2);
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 (re_match_word) free(match_data);
}
/* Misc */
if (dump_file) {
pd_dump = pcap_dump_open(pd, dump_file);
if (!pd_dump) {
fprintf(stderr, "fatal: %s\n", pcap_geterr(pd));
clean_exit(2);
} else printf("output: %s\n", dump_file);
}
update_windowsize(0);
#if defined(_WIN32)
win32_initwinsock();
#endif
#if !defined(_WIN32) && USE_DROPPRIVS
drop_privs();
#endif
while (pcap_loop(pd, -1, (pcap_handler)process, 0));
clean_exit(0);
/* NOT REACHED */
return 0;
}
int setup_pcap_source(void) {
if (read_file) {
if (!(pd = pcap_open_offline(read_file, pc_err))) {
perror(pc_err);
return 1;
}
live_read = 0;
printf("input: %s\n", read_file);
} else {
char *dev = usedev ? usedev :
#if defined(_WIN32)
win32_choosedevice();
#else
pcap_lookupdev(pc_err);
#endif
if (!dev) {
perror(pc_err);
return 1;
}
if ((pd = pcap_open_live(dev, snaplen, promisc, to, pc_err)) == NULL) {
perror(pc_err);
return 1;
}
if (pcap_lookupnet(dev, &net.s_addr, &mask.s_addr, pc_err) == -1) {
perror(pc_err);
memset(&net, 0, sizeof(net));
memset(&mask, 0, sizeof(mask));
}
if (quiet < 2) {
printf("interface: %s", dev);
if (net.s_addr && mask.s_addr) {
printf(" (%s/", inet_ntoa(net));
printf("%s)", inet_ntoa(mask));
}
printf("\n");
}
}
switch(pcap_datalink(pd)) {
case DLT_EN10MB:
link_offset = ETHHDR_SIZE;
break;
case DLT_IEEE802:
link_offset = TOKENRING_SIZE;
break;
case DLT_FDDI:
link_offset = FDDIHDR_SIZE;
break;
case DLT_SLIP:
link_offset = SLIPHDR_SIZE;
break;
case DLT_PPP:
link_offset = PPPHDR_SIZE;
break;
#if HAVE_DLT_LOOP
case DLT_LOOP:
#endif
case DLT_NULL:
link_offset = LOOPHDR_SIZE;
break;
#if HAVE_DLT_RAW
case DLT_RAW:
link_offset = RAWHDR_SIZE;
break;
#endif
#if HAVE_DLT_LINUX_SLL
case DLT_LINUX_SLL:
link_offset = ISDNHDR_SIZE;
include_vlan = 0;
break;
#endif
#if HAVE_DLT_IEEE802_11_RADIO
case DLT_IEEE802_11_RADIO:
radiotap_present = 1;
#endif
#if HAVE_DLT_IEEE802_11
case DLT_IEEE802_11:
link_offset = IEEE80211HDR_SIZE;
break;
#endif
#if HAVE_DLT_PFLOG
case DLT_PFLOG:
link_offset = PFLOGHDR_SIZE;
break;
#endif
#if HAVE_DLT_IPNET
case DLT_IPNET:
link_offset = IPNETHDR_SIZE;
include_vlan = 0;
break;
#endif
default:
fprintf(stderr, "fatal: unsupported interface type %u\n", pcap_datalink(pd));
return 1;
}
return 0;
}
int setup_bpf_filter(char **argv) {
if (filter_file) {
char buf[1024] = {0};
FILE *f = fopen(filter_file, "r");
if (!f || !fgets(buf, sizeof(buf)-1, f)) {
fprintf(stderr, "fatal: unable to get filter from %s: %s\n", filter_file, strerror(errno));
usage();
}
fclose(f);
filter = get_filter_from_string(buf);
if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr))
return 1;
} else if (argv[optind]) {
filter = get_filter_from_argv(&argv[optind]);
if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) {
free(filter);
filter = get_filter_from_argv(&argv[optind-1]);
#if USE_PCAP_RESTART
PCAP_RESTART_FUNC();
#endif
if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr))
return 1;
match_data = NULL;
}
} else {
filter = include_vlan ? strdup(BPF_TEMPLATE_IP_VLAN) : strdup(BPF_TEMPLATE_IP);
if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr))
return 1;
}
if (pcap_setfilter(pd, &pcapfilter))
return 1;
return 0;
}
int setup_matcher(void) {
if (bin_match) {
uint32_t i = 0, n;
uint32_t len;
char *s, *d;
if (re_match_word || re_ignore_case) {
fprintf(stderr, "fatal: regex switches are incompatible with binary matching\n");
return 1;
}
len = (uint32_t)strlen(match_data);
if (len % 2 != 0 || !strishex(match_data)) {
fprintf(stderr, "fatal: invalid hex string specified\n");
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;
}
match_len = len / 2;
match_func = &bin_match_func;
} else {
#if USE_PCRE
uint32_t pcre_options = PCRE_UNGREEDY;
if (re_ignore_case)
pcre_options |= PCRE_CASELESS;
if (re_multiline_match)
pcre_options |= PCRE_DOTALL;
#else
re_syntax_options = RE_CHAR_CLASSES | RE_NO_BK_PARENS | RE_NO_BK_VBAR |
RE_CONTEXT_INDEP_ANCHORS | RE_CONTEXT_INDEP_OPS;
if (re_multiline_match)
re_syntax_options |= RE_DOT_NEWLINE;
if (re_ignore_case) {
uint32_t i;
char *s;
pattern.translate = (char*)malloc(256);
s = pattern.translate;
for (i = 0; i < 256; i++)
s[i] = i;
for (i = 'A'; i <= 'Z'; i++)
s[i] = i + 32;
s = match_data;
while (*s) {
*s = tolower(*s);
s++;
}
} else pattern.translate = NULL;
#endif
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;
}
#if USE_PCRE
pattern = pcre_compile(match_data, pcre_options, (const char **)&re_err, &err_offset, 0);
if (!pattern) {
fprintf(stderr, "compile failed: %s\n", re_err);
return 1;
}
pattern_extra = pcre_study(pattern, 0, (const char **)&re_err);
#else
re_err = re_compile_pattern(match_data, strlen(match_data), &pattern);
if (re_err) {
fprintf(stderr, "regex compile: %s\n", re_err);
return 1;
}
pattern.fastmap = (char*)malloc(256);
if (re_compile_fastmap(&pattern)) {
perror("fastmap compile failed");
return 1;
}
#endif
match_func = &re_match_func;
}
return 0;
}
static inline uint8_t vlan_frame_count(u_char *p, uint16_t limit) {
uint8_t *et = (uint8_t*)(p + 12);
uint16_t ether_type = EXTRACT_16BITS(et);
uint8_t count = 0;
while ((void*)et < (void*)(p + limit) &&
ether_type != ETHERTYPE_IP &&
ether_type != ETHERTYPE_IPV6) {
count++;
et += VLANHDR_SIZE;
ether_type = EXTRACT_16BITS(et);
}
return count;
}
void process(u_char *d, struct pcap_pkthdr *h, u_char *p) {
uint8_t vlan_offset = include_vlan ? vlan_frame_count(p, h->caplen) * VLANHDR_SIZE : 0;
struct ip *ip4_pkt = (struct ip *) (p + link_offset + vlan_offset);
#if USE_IPv6
struct ip6_hdr *ip6_pkt = (struct ip6_hdr*)(p + link_offset + vlan_offset);
#endif
uint32_t ip_ver;
uint8_t ip_proto = 0;
uint32_t ip_hl = 0;
uint32_t ip_off = 0;
uint8_t fragmented = 0;
uint16_t frag_offset = 0;
uint32_t frag_id = 0;
char ip_src[INET6_ADDRSTRLEN + 1],
ip_dst[INET6_ADDRSTRLEN + 1];
unsigned char *data;
uint32_t len = h->caplen - vlan_offset;
seen_frames++;
#if HAVE_DLT_IEEE802_11_RADIO
if (radiotap_present) {
uint16_t radio_len = ((struct NGREP_rtaphdr_t *)(p))->it_len;
ip4_pkt = (struct ip *)(p + link_offset + radio_len);
len -= radio_len;
}
#endif
ip_ver = ip4_pkt->ip_v;
switch (ip_ver) {
case 4: {
#if defined(AIX)
#undef ip_hl
ip_hl = ip4_pkt->ip_ff.ip_fhl * 4;
#else
ip_hl = ip4_pkt->ip_hl * 4;
#endif
ip_proto = ip4_pkt->ip_p;
ip_off = ntohs(ip4_pkt->ip_off);
fragmented = ip_off & (IP_MF | IP_OFFMASK);
frag_offset = (fragmented) ? (ip_off & IP_OFFMASK) * 8 : 0;
frag_id = ntohs(ip4_pkt->ip_id);
inet_ntop(AF_INET, (const void *)&ip4_pkt->ip_src, ip_src, sizeof(ip_src));
inet_ntop(AF_INET, (const void *)&ip4_pkt->ip_dst, ip_dst, sizeof(ip_dst));
} break;
#if USE_IPv6
case 6: {
ip_hl = sizeof(struct ip6_hdr);
ip_proto = ip6_pkt->ip6_nxt;
if (ip_proto == IPPROTO_FRAGMENT) {
struct ip6_frag *ip6_fraghdr;
ip6_fraghdr = (struct ip6_frag *)((unsigned char *)(ip6_pkt) + ip_hl);
ip_hl += sizeof(struct ip6_frag);
ip_proto = ip6_fraghdr->ip6f_nxt;
fragmented = 1;
frag_offset = ntohs(ip6_fraghdr->ip6f_offlg & IP6F_OFF_MASK);
frag_id = ntohl(ip6_fraghdr->ip6f_ident);
}
inet_ntop(AF_INET6, (const void *)&ip6_pkt->ip6_src, ip_src, sizeof(ip_src));
inet_ntop(AF_INET6, (const void *)&ip6_pkt->ip6_dst, ip_dst, sizeof(ip_dst));
} break;
#endif
}
if (quiet < 1) {
printf("#");
fflush(stdout);
}
switch (ip_proto) {
case IPPROTO_TCP: {
struct tcphdr *tcp_pkt = (struct tcphdr *)((unsigned char *)(ip4_pkt) + ip_hl);
uint16_t tcphdr_offset = (frag_offset) ? 0 : (tcp_pkt->th_off * 4);
data = (unsigned char *)(tcp_pkt) + tcphdr_offset;
len -= link_offset + ip_hl + tcphdr_offset;
if ((int32_t)len < 0)
len = 0;
dump_packet(h, p, ip_proto, data, len,
ip_src, ip_dst, ntohs(tcp_pkt->th_sport), ntohs(tcp_pkt->th_dport), tcp_pkt->th_flags,
tcphdr_offset, fragmented, frag_offset, frag_id);
} break;
case IPPROTO_UDP: {
struct udphdr *udp_pkt = (struct udphdr *)((unsigned char *)(ip4_pkt) + ip_hl);
uint16_t udphdr_offset = (frag_offset) ? 0 : sizeof(*udp_pkt);
data = (unsigned char *)(udp_pkt) + udphdr_offset;
len -= link_offset + ip_hl + udphdr_offset;
if ((int32_t)len < 0)
len = 0;
dump_packet(h, p, ip_proto, data, len, ip_src, ip_dst,
ntohs(udp_pkt->uh_sport), ntohs(udp_pkt->uh_dport), 0,
udphdr_offset, fragmented, frag_offset, frag_id);
} break;
case IPPROTO_ICMP: {
struct icmp *icmp4_pkt = (struct icmp *)((unsigned char *)(ip4_pkt) + ip_hl);
uint16_t icmp4hdr_offset = (frag_offset) ? 0 : 4;
data = (unsigned char *)(icmp4_pkt) + icmp4hdr_offset;
len -= link_offset + ip_hl + icmp4hdr_offset;
if ((int32_t)len < 0)
len = 0;
dump_packet(h, p, ip_proto, data, len,
ip_src, ip_dst, icmp4_pkt->icmp_type, icmp4_pkt->icmp_code, 0,
icmp4hdr_offset, fragmented, frag_offset, frag_id);
} break;
#if USE_IPv6
case IPPROTO_ICMPV6: {
struct icmp6_hdr *icmp6_pkt = (struct icmp6_hdr *)((unsigned char *)(ip6_pkt) + ip_hl);
uint16_t icmp6hdr_offset = (frag_offset) ? 0 : 4;
data = (unsigned char *)(icmp6_pkt) + icmp6hdr_offset;
len -= link_offset + ip_hl + icmp6hdr_offset;
if ((int32_t)len < 0)
len = 0;
dump_packet(h, p, ip_proto, data, len,
ip_src, ip_dst, icmp6_pkt->icmp6_type, icmp6_pkt->icmp6_code, 0,
icmp6hdr_offset, fragmented, frag_offset, frag_id);
} break;
#endif
case IPPROTO_IGMP: {
struct igmp *igmp_pkt = (struct igmp *)((unsigned char *)(ip4_pkt) + ip_hl);
uint16_t igmphdr_offset = (frag_offset) ? 0 : 4;
data = (unsigned char *)(igmp_pkt) + igmphdr_offset;
len -= link_offset + ip_hl + igmphdr_offset;
if ((int32_t)len < 0)
len = 0;
dump_packet(h, p, ip_proto, data, len,
ip_src, ip_dst, igmp_pkt->igmp_type, igmp_pkt->igmp_code, 0,
igmphdr_offset, fragmented, frag_offset, frag_id);
} break;
default: {
data = (unsigned char *)(ip4_pkt) + ip_hl;
len -= link_offset + ip_hl;
if ((int32_t)len < 0)
len = 0;
dump_packet(h, p, ip_proto, data, len,
ip_src, ip_dst, 0, 0, 0,
0, fragmented, frag_offset, frag_id);
} break;
}
if (max_matches && matches >= max_matches)
clean_exit(0);
if (match_after && keep_matching)
keep_matching--;
}
void dump_packet(struct pcap_pkthdr *h, u_char *p, uint8_t proto, unsigned char *data, uint32_t len,
const char *ip_src, const char *ip_dst, uint16_t sport, uint16_t dport, uint8_t flags,
uint16_t hdr_offset, uint8_t frag, uint16_t frag_offset, uint32_t frag_id) {
uint16_t match_size, match_index;
if (!show_empty && len == 0)
return;
if (len > limitlen)
len = limitlen;
if ((len > 0 && match_func(data, len, &match_index, &match_size) == invert_match) && !keep_matching)
return;
if (!live_read && want_delay)
dump_delay(h);
{
char ident;
switch (proto) {
case IPPROTO_TCP: ident = TCP; break;
case IPPROTO_UDP: ident = UDP; break;
case IPPROTO_ICMP: ident = ICMP; break;
case IPPROTO_ICMPV6: ident = ICMPv6; break;
case IPPROTO_IGMP: ident = IGMP; break;
default: ident = UNKNOWN; break;
}
printf("\n%c", ident);
}
if (show_proto)
printf("(%u)", proto);
printf(" ");
if (print_time)
print_time(h);
if ((proto == IPPROTO_TCP || proto == IPPROTO_UDP) && (sport || dport) && (hdr_offset || frag_offset == 0))
printf("%s:%u -> %s:%u", ip_src, sport, ip_dst, dport);
else
printf("%s -> %s", ip_src, ip_dst);
if (proto == IPPROTO_TCP && flags)
printf(" [%s%s%s%s%s%s%s%s]",
(flags & TH_ACK) ? "A" : "",
(flags & TH_SYN) ? "S" : "",
(flags & TH_RST) ? "R" : "",
(flags & TH_FIN) ? "F" : "",
(flags & TH_URG) ? "U" : "",
(flags & TH_PUSH)? "P" : "",
(flags & TH_ECE) ? "E" : "",
(flags & TH_CWR) ? "C" : "");
switch (proto) {
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
case IPPROTO_IGMP:
printf(" %u:%u", sport, dport);
}
if (frag)
printf(" %s%u@%u:%u",
frag_offset?"+":"", frag_id, frag_offset, len);
if (dump_single)
printf(" ");
else
printf(" #%u\n", seen_frames);
if (quiet < 3)
dump_func(data, len, match_index, match_size);
if (pd_dump)
pcap_dump((u_char*)pd_dump, h, p);
#if USE_TCPKILL
if (tcpkill_active)
tcpkill_kill(h, p, link_offset, tcpkill_active);
#endif
}
int8_t re_match_func(unsigned char *data, uint32_t len, uint16_t *mindex, uint16_t *msize) {
#if USE_PCRE
static int sub[2];
switch(pcre_exec(pattern, 0, (char const *)data, (int32_t)len, 0, 0, 0, 0)) {
case PCRE_ERROR_NULL:
case PCRE_ERROR_BADOPTION:
case PCRE_ERROR_BADMAGIC:
case PCRE_ERROR_UNKNOWN_NODE:
case PCRE_ERROR_NOMEMORY:
perror("she's dead, jim\n");
clean_exit(2);
case PCRE_ERROR_NOMATCH:
return 0;
default:
*mindex = sub[0];
*msize = sub[1] - sub[0];
}
#else
static struct re_registers regs;
switch (re_search(&pattern, (char const *)data, (int32_t)len, 0, len, &regs)) {
case -2:
perror("she's dead, jim\n");
clean_exit(2);
case -1:
return 0;
default:
*mindex = regs.start[0];
*msize = regs.end[0] - regs.start[0];
}
#endif
matches++;
if (match_after && keep_matching != match_after)
keep_matching = match_after;
return 1;
}
int8_t bin_match_func(unsigned char *data, uint32_t len, uint16_t *mindex, uint16_t *msize) {
int32_t stop = len - match_len;
int32_t i = 0;
if (stop < 0)
return 0;
while (i <= stop)
if (!memcmp(data+(i++), bin_data, match_len)) {
matches++;
if (match_after && keep_matching != match_after)
keep_matching = match_after;
*mindex = i - 1;
*msize = match_len;
return 1;
}
return 0;
}
int8_t blank_match_func(unsigned char *data, uint32_t len, uint16_t *mindex, uint16_t *msize) {
matches++;
*mindex = 0;
*msize = 0;
return 1;
}
void dump_byline(unsigned char *data, uint32_t len, uint16_t mindex, uint16_t msize) {
if (len > 0) {
const unsigned char *s = data;
uint8_t should_hilite = (msize && enable_hilite);
unsigned char *hilite_start = data + mindex;
unsigned char *hilite_end = hilite_start + msize;
while (s < data + len) {
if (should_hilite && s == hilite_start)
printf("%s", ANSI_hilite);
printf("%c", (*s == '\n' || isprint(*s)) ? *s : nonprint_char);
s++;
if (should_hilite && s == hilite_end)
printf("%s", ANSI_off);
}
printf("\n");
}
}
void dump_unwrapped(unsigned char *data, uint32_t len, uint16_t mindex, uint16_t msize) {
if (len > 0) {
const unsigned char *s = data;
uint8_t should_hilite = (msize && enable_hilite);
unsigned char *hilite_start = data + mindex;
unsigned char *hilite_end = hilite_start + msize;
while (s < data + len) {
if (should_hilite && s == hilite_start)
printf("%s", ANSI_hilite);
printf("%c", isprint(*s) ? *s : nonprint_char);
s++;
if (should_hilite && s == hilite_end)
printf("%s", ANSI_off);
}
printf("\n");
}
}
void dump_formatted(unsigned char *data, uint32_t len, uint16_t mindex, uint16_t msize) {
if (len > 0) {
uint8_t should_hilite = (msize && enable_hilite);
unsigned char *str = data;
uint8_t hiliting = 0;
uint8_t width = show_hex ? 16 : (ws_col-5);
uint32_t i = 0,
j = 0;
while (i < len) {
printf(" ");
if (show_hex) {
for (j = 0; j < width; j++) {
if (should_hilite && (mindex <= (i+j) && (i+j) < mindex + msize)) {
hiliting = 1;
printf("%s", ANSI_hilite);
}
if (i + j < len)
printf("%02x ", str[j]);
else printf(" ");
if ((j+1) % (width/2) == 0)
printf(" ");
if (hiliting) {
hiliting = 0;
printf("%s", ANSI_off);
}
}
}
for (j = 0; j < width; j++) {
if (should_hilite && mindex <= (i+j) && (i+j) < mindex + msize) {
hiliting = 1;
printf("%s", ANSI_hilite);
}
if (i + j < len)
printf("%c", isprint(str[j]) ? str[j] : nonprint_char);
else printf(" ");
if (hiliting) {
hiliting = 0;
printf("%s", ANSI_off);
}
}
str += width;
i += j;
printf("\n");
}
}
}
char *get_filter_from_string(char *str) {
const char *template = include_vlan ? BPF_TEMPLATE_USERSPEC_IP_VLAN : BPF_TEMPLATE_USERSPEC_IP;
char *mine, *s;
uint32_t len;
if (!str || !*str)
return NULL;
len = (uint32_t)strlen(str);
for (s = str; *s; s++)
if (*s == '\r' || *s == '\n')
*s = ' ';
if (!(mine = (char*)malloc(len + strlen(template) + 1)))
return NULL;
memset(mine, 0, len + strlen(template) + 1);
sprintf(mine, template, str);
return mine;
}
char *get_filter_from_argv(char **argv) {
const char *template = include_vlan ? BPF_TEMPLATE_USERSPEC_IP_VLAN : BPF_TEMPLATE_USERSPEC_IP;
char **arg = argv, *theirs, *mine;
char *from, *to;
uint32_t len = 0;
if (!*arg)
return NULL;
while (*arg)
len += (uint32_t)strlen(*arg++) + 1;
if (!(theirs = (char*)malloc(len + 1)) ||
!(mine = (char*)malloc(len + strlen(template) + 1)))
return NULL;
memset(theirs, 0, len + 1);
memset(mine, 0, len + strlen(template) + 1);
arg = argv;
to = theirs;
while ((from = *arg++)) {
while ((*to++ = *from++));
*(to-1) = ' ';
}
sprintf(mine, template, theirs);
free(theirs);
return mine;
}
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) {
struct tm *t = localtime((const time_t *)&h->ts.tv_sec);
printf("%02u/%02u/%02u %02u:%02u:%02u.%06u ",
t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour,
t->tm_min, t->tm_sec, (uint32_t)h->ts.tv_usec);
}
void print_time_diff(struct pcap_pkthdr *h) {
uint32_t secs, usecs;
secs = h->ts.tv_sec - prev_ts.tv_sec;
if (h->ts.tv_usec >= prev_ts.tv_usec)
usecs = h->ts.tv_usec - prev_ts.tv_usec;
else {
secs--;
usecs = 1000000 - (prev_ts.tv_usec - h->ts.tv_usec);
}
printf("+%u.%06u ", secs, usecs);
prev_ts.tv_sec = h->ts.tv_sec;
prev_ts.tv_usec = h->ts.tv_usec;
}
void print_time_offset(struct pcap_pkthdr *h) {
uint32_t secs, usecs;
secs = h->ts.tv_sec - prev_ts.tv_sec;
if (h->ts.tv_usec >= prev_ts.tv_usec)
usecs = h->ts.tv_usec - prev_ts.tv_usec;
else {
secs--;
usecs = 1000000 - (prev_ts.tv_usec - h->ts.tv_usec);
}
if (prev_ts.tv_sec == 0 && prev_ts.tv_usec == 0) {
prev_ts.tv_sec = h->ts.tv_sec;
prev_ts.tv_usec = h->ts.tv_usec;
secs = 0;
usecs = 0;
}
printf("+%u.%06u ", secs, usecs);
}
void dump_delay_proc_init(struct pcap_pkthdr *h) {
dump_delay = &dump_delay_proc;
prev_delay_ts.tv_sec = h->ts.tv_sec;
prev_delay_ts.tv_usec = h->ts.tv_usec;
dump_delay(h);
}
void dump_delay_proc(struct pcap_pkthdr *h) {
uint32_t secs, usecs;
secs = h->ts.tv_sec - prev_delay_ts.tv_sec;
if (h->ts.tv_usec >= prev_delay_ts.tv_usec)
usecs = h->ts.tv_usec - prev_delay_ts.tv_usec;
else {
secs--;
usecs = 1000000 - (prev_delay_ts.tv_usec - h->ts.tv_usec);
}
#ifdef _WIN32
{
// grevious hack, yes, but windows sucks. sorry. :( --jordan
if ((delay_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
perror("delay socket creation failed, disabling -D");
Sleep(3000); // give them time to read the message
want_delay = 0;
return;
}
FD_ZERO(&delay_fds);
FD_SET(delay_socket, &delay_fds);
delay_tv.tv_sec = secs;
delay_tv.tv_usec = usecs;
if (select(0, &delay_fds, 0, 0, &delay_tv) == -1)
fprintf(stderr, "WSAGetLastError = %u\n", WSAGetLastError());
closesocket(delay_socket);
delay_socket = 0; // in case someone ^C's out of me
}
#else
sleep(secs);
usleep(usecs);
#endif
prev_delay_ts.tv_sec = h->ts.tv_sec;
prev_delay_ts.tv_usec = h->ts.tv_usec;
}
void update_windowsize(int32_t e) {
if (e == 0 && ws_col_forced)
ws_col = ws_col_forced;
else if (!ws_col_forced) {
#if !defined(_WIN32)
const struct winsize ws;
if (!ioctl(0, TIOCGWINSZ, &ws)) {
ws_row = ws.ws_row;
ws_col = ws.ws_col;
}
#else
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
ws_row = csbi.dwSize.Y;
ws_col = csbi.dwSize.X;
}
#endif
else {
ws_row = 24;
ws_col = 80;
}
}
}
#if !defined(_WIN32) && USE_DROPPRIVS
void drop_privs(void) {
struct passwd *pw;
uid_t newuid;
gid_t newgid;
if ((getuid() || geteuid()) || dont_dropprivs)
return;
pw = getpwnam(DROPPRIVS_USER);
if (!pw) {
perror("attempt to drop privileges failed: getpwnam failed");
clean_exit(2);
}
newgid = pw->pw_gid;
newuid = pw->pw_uid;
if (getgroups(0, NULL) > 0)
if (setgroups(1, &newgid) == -1) {
perror("attempt to drop privileges failed");
clean_exit(2);
}
if (((getgid() != newgid) && (setgid(newgid) == -1)) ||
((getegid() != newgid) && (setegid(newgid) == -1)) ||
((getuid() != newuid) && (setuid(newuid) == -1)) ||
((geteuid() != newuid) && (seteuid(newuid) == -1))) {
perror("attempt to drop privileges failed");
clean_exit(2);
}
}
#endif
void usage(void) {
printf("usage: ngrep <-"
#if defined(_WIN32)
"L"
#endif
"hNXViwqpevxlDtTRM> <-IO pcap_dump> <-n num> <-d dev> <-A num>\n"
" <-s snaplen> <-S limitlen> <-W normal|byline|single|none> <-c cols>\n"
" <-P char> <-F file>"
#if USE_TCPKILL
" <-K count>"
#endif
"\n"
" <match expression> <bpf filter>\n"
" -h is help/usage\n"
" -V is version information\n"
" -q is be quiet (don't print packet reception hash marks)\n"
" -e is show empty packets\n"
" -i is ignore case\n"
" -v is invert match\n"
" -R is don't do privilege revocation logic\n"
" -x is print in alternate hexdump format\n"
" -X is interpret match expression as hexadecimal\n"
" -w is word-regex (expression must match as a word)\n"
" -p is don't go into promiscuous mode\n"
" -l is make stdout line buffered\n"
" -D is replay pcap_dumps with their recorded time intervals\n"
" -t is print timestamp every time a packet is matched\n"
" -T is print delta timestamp every time a packet is matched\n"
" specify twice for delta from first match\n"
" -M is don't do multi-line match (do single-line match instead)\n"
" -I is read packet stream from pcap format file pcap_dump\n"
" -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"
" -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"
" -c is force the column width to the specified size\n"
" -P is set the non-printable display char to what is specified\n"
" -F is read the bpf filter from the specified file\n"
" -N is show sub protocol number\n"
#if defined(_WIN32)
" -d is use specified device (index) instead of the pcap default\n"
" -L is show the winpcap device list index\n"
#else
" -d is use specified device instead of the pcap default\n"
#endif
#if USE_TCPKILL
" -K is send N packets to kill observed connections\n"
#endif
"");
exit(2);
}
void version(void) {
printf("ngrep: V%s, %s\n", VERSION, pcap_lib_version());
exit(0);
}
void clean_exit(int32_t sig) {
struct pcap_stat s;
signal(SIGINT, SIG_IGN);
signal(SIGABRT, SIG_IGN);
#if !defined(_WIN32)
signal(SIGQUIT, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
signal(SIGWINCH, SIG_IGN);
#endif
if (quiet < 1 && sig >= 0)
printf("exit\n");
#if USE_PCRE
if (pattern) pcre_free(pattern);
if (pattern_extra) pcre_free(pattern_extra);
#else
if (pattern.translate) free(pattern.translate);
if (pattern.fastmap) free(pattern.fastmap);
#endif
if (bin_data) free(bin_data);
/* We used to report pcap_stats; but PCAP manpage says pcap_stats "may or
may not" be accurate. So useless. :-( And confusing for a user to see
counts not match what ngrep thinks. */
if (quiet < 1 && sig >= 0 && !read_file)
printf("%u received, %u matched\n", seen_frames, matches);
if (pd) pcap_close(pd);
if (pd_dumppcap) pcap_close(pd_dumppcap);
if (pd_dump) pcap_dump_close(pd_dump);
#if defined(_WIN32)
if (delay_socket) closesocket(delay_socket);
if (want_delay) WSACleanup();
if (usedev) free(usedev);
#endif
exit(matches ? 0 : 1);
}
#if defined(_WIN32)
int8_t win32_initwinsock(void) {
WORD wVersionRequested = MAKEWORD(2, 0);
WSADATA wsaData;
if (WSAStartup(wVersionRequested, &wsaData)) {
perror("unable to initialize winsock");
return 0;
}
// we want at least major version 2
if (LOBYTE(wsaData.wVersion) < 2) {
fprintf(stderr, "unable to find winsock 2.0 or greater (found %u.%u)\n",
LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
WSACleanup();
return 0;
}
return 1;
}
void win32_listdevices(void) {
uint32_t i = 0;
pcap_if_t *alldevs, *d;
char errbuf[PCAP_ERRBUF_SIZE];
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
perror("unable to enumerate device list");
clean_exit(2);
}
printf("idx\tdev\n");
printf("---\t---\n");
for (d = alldevs; d != NULL; d = d->next) {
printf("%2u:\t%s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
}
pcap_freealldevs(alldevs);
}
char *win32_usedevice(const char *index) {
int32_t idx = atoi(index), i = 0;
pcap_if_t *alldevs, *d;
char errbuf[PCAP_ERRBUF_SIZE];
char *dev = NULL;
if (idx <= 0) {
perror("invalid device index");
clean_exit(2);
}
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
perror("unable to enumerate devices");
clean_exit(2);
}
for (d = alldevs; d != NULL && i != idx; d = d->next)
if (++i == idx)
dev = _strdup(d->name);
if (i <= 0) {
perror("no known devices");
clean_exit(2);
}
if (i != idx) {
perror("unknown device specified");
clean_exit(2);
}
pcap_freealldevs(alldevs);
return dev;
}
char *win32_choosedevice(void) {
pcap_if_t *alldevs, *d;
char errbuf[PCAP_ERRBUF_SIZE];
char *dev = NULL;
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
perror("unable to enumerate devices");
clean_exit(2);
}
for (d = alldevs; d != NULL; d = d->next)
if ((d->addresses) && (d->addresses->addr))
dev = _strdup(d->name);
pcap_freealldevs(alldevs);
if (!dev)
dev = pcap_lookupdev(errbuf);
return dev;
}
#endif