/* * #Id: ngrep.c,v 1.23 2001/12/31 22:11:00 jpr5 Exp # # # $Id: ngrep-lib.c,v 1.9 2002/01/02 03:52:32 mavetju Exp $ * * Copyright (c) 2001 Jordan Ritter * * Modifications for ngrep-lib by Edwin Groothuis * * Please refer to the COPYRIGHT.ngrep file for more information. * */ #if defined(BSD) || defined(SOLARIS) || defined(MAXOSX) #include #include #include #include #include #include #include #include #include #endif #if defined(OSF1) #include #include #include #include #include #include #endif #if defined(LINUX) #include #include #include #include #endif #if defined(AIX) #include #include #include #include #endif #include #include #include #include #define PCAP_DONT_INCLUDE_PCAP_BPF_H #include #include #include #include #include #include #include #include #ifdef USE_PCRE #include "pcre-3.4/pcre.h" #else #include "regex-0.12/regex.h" #endif #include "ngrep-lib.h" static char rcsver[] = "$Revision: 1.9 $"; int snaplen = 65535, promisc = 1, to = 1000; int show_empty = 0, show_hex = 0; int match_after = 0, keep_matching = 0; int invert_match = 0, bin_match = 0; int matches = 0, max_matches = 0; int live_read = 1, want_delay = 0; extern FILE *yyin; char pc_err[PCAP_ERRBUF_SIZE]; #ifdef USE_PCRE int err_offset; char *re_err = NULL; #else const char *re_err = NULL; #endif int re_match_word = 0, re_ignore_case = 0; #ifdef USE_PCRE pcre *pattern = NULL; pcre_extra *pattern_extra = NULL; #else struct re_pattern_buffer pattern; #endif char *match_data = NULL, *bin_data = NULL, *filter = NULL; int (*match_func)() = &blank_match_func; int match_len = 0; struct bpf_program pcapfilter; struct in_addr net, mask; pcap_t *pd = NULL; char *dev = NULL; int link_offset; int link_type; char *read_file = NULL, *dump_file = NULL; pcap_dumper_t *pd_dump = NULL; struct timeval prev_ts = {0, 0}, prev_delay_ts = {0,0}; void (*print_time)() = NULL, (*dump_delay)() = dump_delay_proc_init; unsigned ws_row, ws_col; void (*callback_arp)()=NULL; void (*callback_ip)()=NULL; void (*callback_tcp)()=NULL; void (*callback_udp)()=NULL; void (*callback_icmp)()=NULL; void (*callback_info)()=NULL; void (*callback_stats)()=NULL; void ngrep_callback_arp(void *arp) { callback_arp=arp; } void ngrep_callback_ip(void *ip) { callback_ip=ip; } void ngrep_callback_tcp(void *tcp) { callback_tcp=tcp; } void ngrep_callback_udp(void *udp) { callback_udp=udp; } void ngrep_callback_icmp(void *icmp) { callback_icmp=icmp; } void ngrep_callback_process(void *ip,void *tcp,void *udp,void *icmp) { callback_ip=ip; callback_tcp=tcp; callback_udp=udp; callback_icmp=icmp; } void ngrep_callback_info(void *info,void *stats) { callback_info=info; callback_stats=stats; } void ngrep_run(void) { while (pcap_loop(pd, 0, (pcap_handler)process, 0)); clean_exit(-2); } void ngrep_setfilter(char *BPFFilter,char *MatchExpression,char *Options) { int c; int ac; char *av[100]; char **ap; char *options; char *strinterface=NULL,*strfilter=NULL,*strmatch=NULL,*stroutput=NULL; signal(SIGINT, clean_exit); signal(SIGQUIT, clean_exit); signal(SIGABRT, clean_exit); signal(SIGPIPE, clean_exit); signal(SIGWINCH, update_windowsize); // // When to add a space between the three strings. // // A B C A' B' // 0 0 0 0 0 // 0 0 1 0 0 // 0 1 0 0 0 // 0 1 1 0 1 // 1 0 0 0 0 // 1 0 1 0 1 // 1 1 0 1 0 // 1 1 1 1 1 // // A' = A && B // B' = C && (A || B) // asprintf(&options,"%s%s%s%s%s", Options==NULL?"":Options, Options!=NULL && MatchExpression!=NULL ?" ":"", MatchExpression==NULL?"":MatchExpression, BPFFilter!=NULL && (Options!=NULL || MatchExpression!=NULL) ?" ":"", BPFFilter==NULL?"":BPFFilter); ac=1; av[0]="ngrep-lib"; for (ap=&av[1];(*ap=strsep(&options," \t"))!=NULL;ac++) { if (**ap!=NULL) { if (++ap>=&av[100]) break; } } // // These values might have been used already, therefor re-initialised // optopt=0; opterr=1; optreset=0; optind=1; optarg=NULL; while ((c = getopt(ac,av,"hXViwpevxlDtTs:n:d:A:I:O:")) >0 ) { switch (c) { case 'I': read_file = optarg; break; case 'O': dump_file = optarg; break; case 'A': match_after = atoi(optarg) + 1; break; case 'd': dev = optarg; break; case 'n': max_matches = atoi(optarg); break; case 's': snaplen = atoi(optarg); break; case 'T': print_time = &print_time_diff_init; 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 'w': re_match_word++; break; case 'i': re_ignore_case++; break; case 'V': version(); case 'X': bin_match++; break; case 'h': ngrep_usage(0); default: ngrep_usage(-1); } } if (av[optind]) match_data = av[optind++]; if (read_file) { if (!(pd = pcap_open_offline(read_file, pc_err))) { perror(pc_err); clean_exit(-1); } live_read = 0; printf("input: %s\n", read_file); } else { if (!dev) if (!(dev = pcap_lookupdev(pc_err))) { perror(pc_err); clean_exit(-1); } if ((pd = pcap_open_live(dev, snaplen, promisc, to, pc_err)) == NULL) { perror(pc_err); clean_exit(-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 (net.s_addr && mask.s_addr) asprintf(&strinterface,"%s (%s/%s)", inet_ntoa(net),inet_ntoa(mask),dev); else asprintf(&strinterface,"%s", dev); } if (av[optind]) { filter = get_filter(&av[optind]); if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) { free(filter); filter = get_filter(&av[optind-1]); #ifdef NEED_RESTART PCAP_RESTART(yyin); #endif if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) { pcap_perror(pd, "pcap compile"); clean_exit(-1); } else match_data = NULL; } asprintf(&strfilter,"%s",filter); if (pcap_setfilter(pd, &pcapfilter)) { pcap_perror(pd, "pcap set"); clean_exit(-1); } } if (match_data) { if (bin_match) { int i = 0, n; char *s, *d; int len; if (re_match_word || re_ignore_case) { fprintf(stderr, "fatal: regex switches are incompatible with binary matching\n"); clean_exit(-1); } len = strlen(match_data); if (len % 2 != 0 || !strishex(match_data)) { fprintf(stderr, "fatal: invalid hex string specified\n"); clean_exit(-1); } bin_data = malloc(len / 2); memset(bin_data, 0, len / 2); d = bin_data; if ((s = strchr(match_data, 'x'))) len -= ++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 { #ifdef USE_PCRE int pcre_options = PCRE_UNGREEDY; if (re_ignore_case) pcre_options |= PCRE_CASELESS; re_err = malloc(512); #else re_syntax_options = RE_SYNTAX_EGREP; if (re_ignore_case) { char *s; int i; 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); } else pattern.translate = NULL; #endif if (re_match_word) { char *word_regex = malloc(strlen(match_data) * 3 + strlen(WORD_REGEX)); sprintf(word_regex, WORD_REGEX, match_data, match_data, match_data); match_data = word_regex; } #ifdef 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); clean_exit(-1); } pattern_extra = pcre_study(pattern, 0, (const char **)&re_err); free(re_err); re_err = NULL; #else re_err = re_compile_pattern(match_data, strlen(match_data), &pattern); if (re_err) { fprintf(stderr, "regex compile: %s\n", re_err); clean_exit(-1); } pattern.fastmap = (char*)malloc(256); if (re_compile_fastmap(&pattern)) { perror("fastmap compile failed"); clean_exit(-1); } #endif match_func = &re_match_func; } if (match_data && strlen(match_data)) asprintf(&strmatch,"%s%s%s", invert_match?"(inverted) ":"", (bin_data && !strchr(match_data, 'x'))?"0x":"", match_data); } if (filter) free(filter); if (re_match_word) free(match_data); link_type = pcap_datalink(pd); switch (link_type) { 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; case DLT_RAW: link_offset = RAWHDR_SIZE; break; case DLT_LOOP: case DLT_NULL: link_offset = LOOPHDR_SIZE; break; case DLT_LINUX_SLL: link_offset = ISDNHDR_SIZE; break; default: fprintf(stderr, "fatal: unsupported interface type %d\n", pcap_datalink(pd)); clean_exit(-1); } if (dump_file) { if (!(pd_dump = pcap_dump_open(pd, dump_file))) { fprintf(stderr, "fatal: %s\n", pcap_geterr(pd)); clean_exit(-1); } else asprintf(&stroutput,"%s", dump_file); } if (callback_info) (*callback_info)(strinterface,strfilter,strmatch,stroutput); if (strinterface!=NULL) free(strinterface); if (strfilter!=NULL) free(strfilter); if (strmatch!=NULL) free(strmatch); if (stroutput!=NULL) free(strmatch); free(options); } void ngrep_main(char *BPFFilter,char *MatchExpression,char *Options) { ngrep_setfilter(BPFFilter,MatchExpression,Options); ngrep_run(); } void process(u_char *data1, struct pcap_pkthdr* h, u_char *p) { struct ip *ip_packet = (struct ip *)(p + link_offset); struct ether_header *ether_packet = (struct ether_header *)p; struct token_header *tokenring_packet = (struct token_header *)p; struct fddi_header *fddi_packet = (struct fddi_header *)p; struct arp_hdr *arp_packet=(struct arp_hdr *)ip_packet; #if defined(AIX) #undef ip_hl unsigned ip_hl = ip_packet->ip_ff.ip_fhl*4; #else unsigned ip_hl = ip_packet->ip_hl*4; #endif unsigned ip_off = ntohs(ip_packet->ip_off); unsigned fragmented = ip_off & (IP_MF | IP_OFFMASK); char *data; int len; int is_ipv4=0; int is_ipv6=0; int is_arp=0; switch (link_type) { case DLT_EN10MB: // simple ethernet switch (ntohs(ether_packet->ether_type)) { case ETHERTYPE_IP: is_ipv4=1; break; case ETHERTYPE_IPV6: is_ipv6=1; break; case ETHERTYPE_ARP: is_arp=1; break; default: return; } break; case DLT_IEEE802: is_ipv4=1; // XXX couldn't find any information about it. break; case DLT_FDDI: is_ipv4=1; // XXX couldn't find any information about it. break; case DLT_SLIP: is_ipv4=1; // that's one thing that is for sure. break; case DLT_PPP: switch (PPP_PROTOCOL(p)) { case PPP_IP: is_ipv4=1; break; case PPP_IPV6: is_ipv6=1; break; default: return; } break; case DLT_RAW: is_ipv4=1; // XXX couldn't find any information about it. break; case DLT_NULL: is_ipv4=1; // XXX couldn't find any information about it. break; default: return; } // same kind of tests should be done for different kind of link-layers if (is_arp==1 && callback_arp!=NULL) (*callback_arp)(arp_packet); if (is_ipv4==1) { if (callback_ip!=NULL) (*callback_ip)(ip_packet); switch (ip_packet->ip_p) { case IPPROTO_TCP: { struct tcphdr* tcp = (struct tcphdr *)(((char *)ip_packet) + ip_hl); unsigned tcphdr_offset = fragmented?0:(tcp->th_off * 4); data = ((char*)tcp) + tcphdr_offset; if ((len = ntohs(ip_packet->ip_len)) < h->caplen) len -= ip_hl + tcphdr_offset; else len = h->caplen - link_offset - ip_hl - tcphdr_offset; // // ip : ip packet // tcp : tcp packet // data : data packet // len : lenght of data packet // if (callback_tcp!=NULL) (*callback_tcp)(ip_packet,tcp,data,len); break; } case IPPROTO_UDP: { struct udphdr* udp = (struct udphdr *)(((char *)ip_packet) + ip_hl); unsigned udphdr_offset = (fragmented)?0:sizeof(struct udphdr); data = ((char*)udp) + udphdr_offset; if ((len = ntohs(ip_packet->ip_len)) < h->caplen) len -= ip_hl + udphdr_offset; else len = h->caplen - link_offset - ip_hl - udphdr_offset; // // ip : ip packet // udp : udp packet // data : data packet // len : lenght of data packet // if (callback_udp!=NULL) (*callback_udp)(ip_packet,udp,data,len); break; } case IPPROTO_ICMP: { struct icmp* ic = (struct icmp *)(((char *)ip_packet) + ip_hl); unsigned icmphdr_offset = fragmented?0:4; data = ((char*)ic) + icmphdr_offset; if ((len = ntohs(ip_packet->ip_len)) < h->caplen) len -= ip_hl + icmphdr_offset; else len = h->caplen - link_offset - ip_hl - icmphdr_offset; // // ip : ip packet // ic : icmp packet // data : data packet // len : lenght of data packet // if (callback_icmp!=NULL) (*callback_icmp)(ip_packet,ic,data,len); break; } } } if (match_after && keep_matching) keep_matching--; } int re_match_func(char *data, int len) { #ifdef USE_PCRE switch(pcre_exec(pattern, 0, data, 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; } #else switch (re_search(&pattern, data, len, 0, len, 0)) { case -2: perror("she's dead, jim\n"); clean_exit(-2); case -1: return 0; } #endif if (max_matches && ++matches > max_matches) clean_exit(0); if (match_after && keep_matching != match_after) keep_matching = match_after; return 1; } int bin_match_func(char *data, int len) { int stop = len - match_len; int i = 0; if (stop < 0) return 0; while (i <= stop) if (!memcmp(data+(i++), bin_data, match_len)) { if (max_matches && ++matches > max_matches) clean_exit(0); if (match_after && keep_matching != match_after) keep_matching = match_after; return 1; } return 0; } int blank_match_func(char *data, int len) { if (max_matches && ++matches > max_matches) clean_exit(0); return 1; } void dump(char *data, int len) { if (len > 0) { unsigned width = show_hex?16:(ws_col-5); char *str = data; int j, i = 0; while (i < len) { printf(" "); if (show_hex) for (j = 0; j < width; j++) { if (i+j < len) printf("%02x ", (unsigned char)str[j]); else printf(" "); if ((j+1) % (width/2) == 0) printf(" "); } for (j = 0; j < width; j++) if (i+j < len) printf("%c", isprint(str[j])?str[j]:'.'); else printf(" "); str += width; i += j; printf("\n"); } } } char *get_filter(char **argv) { char **arg = argv, *theirs, *mine; char *from, *to; int len = 0; if (!*arg) return NULL; while (*arg) len += strlen(*arg++) + 1; if (!(theirs = (char*)malloc(len + 1)) || !(mine = (char*)malloc(len + sizeof(IP_ONLY)))) return NULL; memset(theirs, 0, len + 1); memset(mine, 0, len + sizeof(IP_ONLY)); arg = argv; to = theirs; while ((from = *arg++)) { while ((*to++ = *from++)); *(to-1) = ' '; } sprintf(mine, IP_ONLY, theirs); free(theirs); return mine; } int 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) { #ifdef MACOSX struct tm *t = localtime((const time_t *)&h->ts.tv_sec); #else struct tm *t = localtime(&h->ts.tv_sec); #endif printf("%02d/%02d/%02d %02d:%02d:%02d.%06ld ", t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, h->ts.tv_usec); } void print_time_diff_init(struct pcap_pkthdr *h) { print_time = &print_time_diff; prev_ts.tv_sec = h->ts.tv_sec; prev_ts.tv_usec = h->ts.tv_usec; print_time(h); } void print_time_diff(struct pcap_pkthdr *h) { unsigned 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("+%d.%06d ", secs, usecs); prev_ts.tv_sec = h->ts.tv_sec; prev_ts.tv_usec = h->ts.tv_usec; } 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) { unsigned 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); } sleep(secs); usleep(usecs); prev_delay_ts.tv_sec = h->ts.tv_sec; prev_delay_ts.tv_usec = h->ts.tv_usec; } void update_windowsize(int e) { const struct winsize ws; if (!ioctl(0, TIOCGWINSZ, &ws)) { ws_row = ws.ws_row; ws_col = ws.ws_col; } else { ws_row = 24; ws_col = 80; } } void ngrep_usage(int e) { printf("usage: ngrep <-hXViwqpevxlDtT> <-IO pcap_dump> <-n num> <-d dev>\n" " <-A num> <-s snaplen> \n"); exit(e); } void version(void) { printf("ngrep: V%s, %s\n", VERSION, rcsver); exit(0); } void clean_exit(int sig) { struct pcap_stat s; if (sig >= 0) printf("exit\n"); #ifdef USE_PCRE if (re_err) free(re_err); 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); if (callback_stats!=NULL && sig >= 0 && !read_file && pd && !pcap_stats(pd, &s)) callback_stats(s.ps_recv, s.ps_drop); if (pd) pcap_close(pd); if (pd_dump) pcap_dump_close(pd_dump); if (sig!=-2) exit(sig); }