/*
 * #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