
#include "usniff.h"

typedef struct ether_header eth_hdr;  /* Ethernet header */
typedef struct ip ip_hdr;	      /* IP header       */
typedef struct tcphdr tcp_hdr;	      /* TCP header      */

char *pcap_dev;            /* Pcap device file descriptor */

char    *copy_argv (char **); 
void     pwcall    (u_char *, const struct pcap_pkthdr *, const u_char *);
void     usage     (void);

int main(int argc, char *argv[])
{
	struct bpf_program program;    /* BPF filter program   */
	register int c;                /* Temporary variable   */
	char errbuf[PCAP_ERRBUF_SIZE]; /* pcap error buffer    */
	char *filter = NULL;           /* pcap filter          */
	pcap_t *handle;                /* pcap handle          */
	bpf_u_int32 mask;              /* our netmask          */
	bpf_u_int32 net;               /* our IP adx           */
	uint32_t npolls = -1;          /* Number of pcap polls */

	while ((c = getopt(argc, argv, "i:p:u")) != -1) {
		switch (c) {
		case 'i':
			pcap_dev = optarg;
			break;
		case 'p': 
			if (optarg != NULL && isdigit(*optarg)) {
				npolls = atol(optarg);
				if (npolls < 0) {
					fprintf(stderr,
						"Packets must be > than 0\n");
					exit(1);
				}
			} else {
				fprintf(stderr, "Invalid packet number\n");
				exit(1);
			}
			break;
		case 'u':
			usage();
			exit(0);
			break;
		default:
			usage();
			exit(1);
			break;
		}
	}

	/* Got root? */
	if (getuid()) {
		fprintf(stderr, "Must be root user.\n");
		exit(1);
	}

	/* Strip off any none getopt arguments for pcap filter */
	if (!filter)
		filter = copy_argv(&argv[optind]);

	/* Initialize the interface to listen on */
	if ((!pcap_dev)
	    && ((pcap_dev = pcap_lookupdev(errbuf)) == NULL)) {
		fprintf(stderr, "%s\n", errbuf);
		exit(-1);
	}

	if ((handle =
	     pcap_open_live(pcap_dev, 68, 0, 0, errbuf)) == NULL) {
		fprintf(stderr, "%s\n", errbuf);
		exit(-1);
	}

	pcap_lookupnet(pcap_dev, &net, &mask, errbuf); /* Get netinfo */
	if (filter) {
		if (pcap_compile(handle, &program, filter, 0, net) == -1) {
			fprintf(stderr, "Error - `pcap_compile()'\n");
			return 1;
		}

		if (pcap_setfilter(handle, &program) == -1) {
			fprintf(stderr, "Error - `pcap_setfilter()'\n");
			return 1;
		}

		pcap_freecode(&program);
	}

	/* Main loop */
	printf("Starting capturing engine on %s...\n", pcap_dev);
	pcap_loop(handle, npolls, pwcall, NULL);

	/* Exit program */
	printf("Closing capturing engine...\n");
	pcap_close(handle);
}

/*
 * copy_argv: copy out any unprocessed arguments off of argv and return
 * requires: argument vector
 */
char *copy_argv(char **argv)
{
	char **p;
	u_int len = 0;
	char *buf;
	char *src, *dst;

	p = argv;

	if (*p == 0)
		return 0;

	while (*p)
		len += strlen(*p++) + 1;

	buf = (char *) malloc(len);
	if (buf == NULL) {
		fprintf(stdout, "copy_argv: malloc");
		exit(1);
	}

	p = argv;
	dst = buf;
	while ((src = *p++) != NULL) {
		while ((*dst++ = *src++) != '\0');
		dst[-1] = ' ';
	}
	dst[-1] = '\0';

	return buf;
}

/*
 * pwcall: This is the pcap looper. It is like most pcap callbacks
 *         except it does a lot of printing.
 * requires: all of the standard pcap_loop data
 */
void pwcall(u_char * args, const struct pcap_pkthdr *header,
					const u_char * packet)
{
	eth_hdr *ethernet;        /* The ethernet header    */
	ip_hdr *ip;               /* The IP header          */
	u_int id;                 /* Host id                */
	u_int i;                  /* Counter                */
	const struct tcphdr *tcp; /* TCP Header             */
	int len;                  /* real length            */
	u_int off, version;       /* offset, version        */
	u_int length=header->len; /* True header len        */
	char *t;                  /* Timestamp intermediary */
	time_t result;            /* Time result (as read)  */

	t = ""; /* empty */

	/* Extract ethernet, ip and tcp headers */
	ethernet = (eth_hdr *) (packet); /* Pointer to ethernet header */
	ip = (ip_hdr *) (packet + sizeof(eth_hdr));
	tcp = (struct tcphdr *) (packet +
		       sizeof(struct ether_header) +
		       sizeof(struct ip));

	len = ntohs(ip->ip_len);
	off = ntohs(ip->ip_off);

	/* Setup the timestamp */
	result = time(NULL);
	t = asctime(localtime(&result));
	t[strlen(t) - 1] = ' ';
	t[strlen(t)] = 0;

	/* this is just real fun - inet_ntoa mixed with fprintf screws up */
	/* so the WAR is to mix up the print types.                       */
	printf("%s: %s:", t, inet_ntoa(ip->ip_src));
	fprintf(stdout,"%u ", tcp->th_sport);
	printf("-> %s:", inet_ntoa(ip->ip_dst));
	fprintf(stdout,"%u ", tcp->th_dport);

	fprintf(stdout,
		"tos %u len %u off %u ttl %u prot %u cksum %u seq %u ack %u win %u\n",
		ip->ip_tos, len, off, ip->ip_ttl, ip->ip_p,
		ip->ip_sum, tcp->th_seq, tcp->th_ack, tcp->th_win);
}

/*
 * usage - Simple usage message 
 */
void usage()
{
	printf(PACKAGE " [option][arguments]\n"
	       PACKAGE
	       " "
		"[-i <interface>][-p <number>][-u]\n" 
	       "Options:\n"
	       " -i <dev>   Specify the interface to watch\n"
	       " -p <int>   Exit after analyzing int polls\n"
	       " -u         Display help\n");
}


