/*
* #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 <jpr5@darkridge.com>
*
* Modifications for ngrep-lib by Edwin Groothuis <edwin@mavetju.org>
*
* Please refer to the COPYRIGHT.ngrep file for more information.
*
*/
#if defined(BSD) || defined(SOLARIS) || defined(MAXOSX)
#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 <sys/tty.h>
#include <arpa/inet.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>
#endif
#if defined(LINUX)
#include <getopt.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <time.h>
#endif
#if defined(AIX)
#include <sys/machine.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <time.h>
#endif
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#define PCAP_DONT_INCLUDE_PCAP_BPF_H
#include <pcap.h>
#include <net/bpf.h>
#include <net/ppp_defs.h>
#include <net/ethernet.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/ioctl.h>
#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> <match expression> <bpf filter>\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);
}
syntax highlighted by Code2HTML, v. 0.9.1