/*
 *	Copyright (c) 1993-1997 JSC Rinet, Novosibirsk, Russia
 *
 * Redistribution and use in source forms, with and without modification,
 * are permitted provided that this entire comment appears intact.
 * Redistribution in binary form may occur without any restrictions.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
 */

/* trafshow.c -- main code to cooperate with pcap and curses */

#ifdef	HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef	HAVE_SLCURSES
#include <slcurses.h>
#endif
#ifdef	HAVE_NCURSES
#include <ncurses.h>
#endif
#ifdef	HAVE_CURSES
#include <curses.h>
#endif

#include <sys/types.h>
#ifdef	HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <pcap.h>

#include "trafshow.h"

#ifndef	TIOCGWINSZ
#define	TIOCGWINSZ	104
#endif

/* command line switches */
int fflag = 0;		/* don't translate foreign IP address */
int nflag = 0;		/* don't convert addresses to host names */
int Nflag = 0;		/* output only host names without domain */
int Oflag = 1;		/* don't run filter code optimizer */
int pflag = 0;		/* don't put the interface into promiscuous mode */
int kflag = 1;		/* disable keyboard input checking */
int eflag = 0;		/* show ethernet traffic rather than ip */
struct t_entry t_mask;	/* traffic mask */

/* global variables */
char *program_name;		/* myself */
char *device_name;		/* network interface name */
int alarm_flag = 0;		/* flag -- screen refresh requested */
int scr_interval = DEFAULT_SCR;	/* screen refresh interval in seconds */
int dns_timeout = DEFAULT_DNS;	/* dns query timeout in seconds */
int snaplen = DEFAULT_SNAPLEN;	/* length of saved portion of packet */

int curses_initialized = FALSE;
int use_colors = FALSE;
int resize_flag = 0;
int addr_size, proto_size, bytes_size, cps_size, count_size;

void
main(argc, argv)
	int argc;
	char **argv;
{
	int op, cnt;
	pcap_t *pd;
	bpf_u_int32 localnet, netmask;
	char *cp, *infile, *expr, ebuf[PCAP_ERRBUF_SIZE];
	struct bpf_program fcode;
	extern char *optarg;
	extern int optind, opterr;
	void usage(), onalarm(), onwinch(), vers();
	extern int abort_on_misalignment();
	extern pcap_handler lookup_if();

	t_mask.src.s_addr = 0xffffffff;	/* all bits valid */
	t_mask.dst.s_addr = 0xffffffff;	/* all bits valid */
	t_mask.sport = 0xffff;		/* all bits valid */
	t_mask.dport = 0xffff;		/* all bits valid */
	t_mask.proto = 0xffff;		/* all bits valid */

	cnt = -1;
	device_name = NULL;
	infile = NULL;
	if ((cp = (char *)strrchr(argv[0], '/')) != NULL)
		program_name = cp + 1;
	else	program_name = argv[0];

	if (abort_on_misalignment(ebuf) < 0) error(0, ebuf);

	while ((op = getopt(argc, argv, "c:CefF:i:kmnNOpr:t:vh?")) != EOF)
		switch (op) {
		case 'C':
#ifdef	HAVE_SLCURSES
			use_colors = TRUE;
#else
			error(0, "must be compiled with slang curses");
#endif
			break;
		case 'c':
			cnt = atoi(optarg);
			if (cnt < 1) usage();
			break;
		case 'e':
			++eflag;
			break;
		case 'f':
			++fflag;
			break;
		case 'F':
			infile = optarg;
			break;
		case 'i':
			device_name = optarg;
			break;
		case 'k':
			kflag = 0;
			break;
		case 'm':
			t_mask.src.s_addr = 0;
			t_mask.dst.s_addr = 0;
			t_mask.sport = 0;
			t_mask.dport = 0;
			t_mask.proto = 0;
			for (;optind + 1 <= argc;) {
			    char *s = argv[optind];
			    u_int32_t arg = 0xffffffff;
			    int save=optind;
			    
			    optind++;
			    if (optind + 1 <= argc &&
				    isdigit(*(argv[optind])) ) {
				arg = strtoul(argv[optind], NULL, 0);
				optind++;
			    }
				
			    if (!strcmp(s, "src-ip"))
				t_mask.src.s_addr = htonl(arg);
			    else if (!strcmp(s, "dst-ip"))
				t_mask.dst.s_addr = htonl(arg);
			    else if (!strcmp(s, "src-port"))
				t_mask.sport = htons((u_short)(arg));
			    else if (!strcmp(s, "dst-port"))
				t_mask.dport = htons((u_short)(arg));
			    else if (!strcmp(s, "proto"))
				t_mask.proto = arg;
			    else {
				optind = save;
				break;
			    }
			}
			break;
		case 'n':
			++nflag;
			break;
		case 'N':
			++Nflag;
			break;
		case 'O':
			Oflag = 0;
			break;
		case 'p':
			++pflag;
			break;
		case 'r':
			scr_interval = atoi(optarg);
			if (scr_interval < 1) usage();
			break;
		case 't':
			dns_timeout = atoi(optarg);
			if (dns_timeout < 1) usage();
			break;
		case 'v':
			vers();
		case 'h':
		case '?':
		default:
			usage();
		}

	/* Find network interface */
	if (device_name == NULL &&
	    (device_name = pcap_lookupdev(ebuf)) == NULL)
		error(0, ebuf);

	/* Attach pcap to the network interface */
	if ((pd = pcap_open_live(device_name, snaplen, !pflag, 1000, ebuf)) == NULL)
		error(0, ebuf);
	if ((op = pcap_snapshot(pd)) > snaplen) snaplen = op;
	op = pcap_datalink(pd);

	if (eflag && op != DLT_EN10MB
#ifdef	DLT_IEEE802
	    && op != DLT_IEEE802
#endif
	    ) error(0, "interface %s not an Ethernet", device_name);

	if (pcap_lookupnet(device_name, &localnet, &netmask, ebuf) < 0)
		error(0, ebuf);

	/* Get back to user process after socket has been opened */
	setuid(getuid());

	/* Read out the user filter expression */
	if (infile) expr = read_infile(infile);
	else expr = copy_argv(&argv[optind]);

	/* and compile it */
	if (pcap_compile(pd, &fcode, expr, Oflag, netmask) < 0 ||
	    pcap_setfilter(pd, &fcode) < 0)
		error(0, pcap_geterr(pd));

	init_addrtoname(localnet, netmask);

	init_term(FALSE);

	(void) signal(SIGHUP, cleanup);
	(void) signal(SIGINT, cleanup);
	(void) signal(SIGQUIT, cleanup);
	(void) signal(SIGTERM, cleanup);
	(void) signal(SIGALRM, onalarm);
	(void) signal(SIGWINCH, onwinch);

	init_display(FALSE);

	if (pcap_loop(pd, cnt, lookup_if(op), NULL) < 0)
		error(0, pcap_geterr(pd));
	pcap_close(pd);
	cleanup(0);
}

/*
 * SIGALRM interrupt handler, should be ran each scr_interval seconds.
 */
void
onalarm()
{
	if (alarm_flag) {
		if (resize_flag) {
			resize_flag = 0;
			init_term(TRUE);
		}
		scr_redraw(TRUE);
		scr_update();
	} else alarm(scr_interval);
	alarm_flag++;
	(void) signal(SIGALRM, onalarm);
}

void
cleanup(sig)
	int sig;
{
#ifdef	HAVE_HAS_COLORS
	if (use_colors) attrset(A_NORMAL);
#endif
	if (sig == SIGINT) {
		move(LINES-1, 0);
		clrtoeol();
		refresh();
	}
	endwin();
	exit(sig < 0 ? 1 : 0);
}

void
init_term(reinit)
	int reinit;
{
	float i;

	if (!reinit) {
		if (initscr() == (WINDOW *)ERR)
			error(0, "Can't initialize terminal -- unknown terminal type?");
		curses_initialized = TRUE;
#ifdef	HAVE_HAS_COLORS
		if (use_colors == FALSE)
			use_colors = has_colors();
#ifdef	HAVE_SLCURSES
		else	SLtt_Use_Ansi_Colors = TRUE;
#endif
		if (use_colors == TRUE) {
			start_color();
			if (init_colormask() == 0) use_colors = FALSE;
		}
#endif
		cbreak();
		noecho();
	} else {
#ifdef	HAVE_RESIZETERM
		struct winsize ws;
		if (ioctl(0, TIOCGWINSZ, &ws) == 0)
			resizeterm(ws.ws_row, ws.ws_col);
#else /* assume it will work on all curses without resizeterm() */
		endwin();
		initscr();
		cbreak();
		noecho();
#endif
	}
	if (LINES < 10) error(0, "Must more LINES on term; cur %d min %d", LINES, 10);
	if (COLS < 40) error(0, "Must more COLS on term; cur %d min %d", COLS, 40);

	page_size = LINES - SCR_OFFS - 2;
	i = (float)COLS / (float)DEFAULT_COLS;
	addr_size = i * (float)ADDR_SIZE;
	proto_size = i * (float)PROTO_SIZE;
	bytes_size = i * (float)BYTES_SIZE;
	cps_size = i * (float)CPS_SIZE;
	count_size = i * (float)COUNT_SIZE;

	clear();
	header_line();

	if (reinit) init_display(TRUE);
	else scr_update();
}

void
onwinch()
{
	alarm_flag++;
	resize_flag++;
	(void) signal(SIGWINCH, onwinch);
}

void
vers()
{
	int hc;
	extern char version[], compiled[], target[], libpcap[], copyright[];

#ifdef	HAVE_HAS_COLORS
	initscr();
	hc = has_colors();
	endwin();
#endif
	fprintf(stderr, "\n%s version %s\ncompiled for %s with\n %s\n",
		program_name, version, target, compiled);
	fprintf(stderr, "\tpcap library version %s\n", libpcap);
#ifdef	HAVE_SLCURSES
	fprintf(stderr, "\tslang curses version %d\n", SLang_Version);
#endif
#ifdef	HAVE_NCURSES
#ifdef	NCURSES_VERSION
	fprintf(stderr, "\tncurses version %s\n", NCURSES_VERSION);
#else
	fprintf(stderr, "\tncurses version unknown\n");
#endif
#endif
#ifdef	HAVE_CURSES
	fprintf(stderr, "\tunknown curses library\n");
#endif
#ifdef	HAVE_HAS_COLORS
	fprintf(stderr, "\tcolors support\n");
	if (hc) fprintf(stderr, "your current terminal has color capability\n");
	else fprintf(stderr,
"your current terminal has no color capability\n\
\ttry -C option to force ansi color mode\n");
#else
	fprintf(stderr, "\tno colors support\n");
#endif
	fprintf(stderr, "\n%s\n", copyright);
	fprintf(stderr,"For bugs report email to trafshow@rinet.nsk.su (include this page)\n");
	exit(1);
}

void
usage()
{
	fprintf(stderr,
"Usage: %s [-eCfknNOpv -c num -i name -r sec -t sec] [-F file | expr]\n\
\t-c number\tcount number of packets and exit\n\
\t-C\t\tforce color mode (when color capability is missing)\n\
\t-e\t\tshow Ethernet traffic rather than IP\n\
\t-f\t\tconvert addresses to name only for local hosts\n\
\t-F file\t\tuse file as input for the filter expression\n\
\t-i name\t\tnetwork interface name; ef0, sl0, ppp0, lo0, etc\n\
\t-k\t\tdisable keyboard input checking\n\
\t-n\t\tdon't convert addresses to host names\n\
\t-N\t\toutput only host names without domain\n\
\t-O\t\tdon't run the packet-matching code optimizer\n\
\t-p\t\tdon't put the interface into promiscuous mode\n\
\t-r seconds\tscreen refresh interval, default %d sec\n\
\t-t seconds\tmax timeout in DNS query, default %d sec\n\
\t-v\t\tprint version information\n\
\texpr\t\tfilter expression like tcpdump's\n",
		program_name, DEFAULT_SCR, DEFAULT_DNS);

	exit(1);
}


syntax highlighted by Code2HTML, v. 0.9.1