/*
 * tcpick.c -- main project file
 * Part of the tcpick project
 *
 * Author: Francesco Stablum <duskdruid @ despammed.com>
 *
 * Copyright (C) 2003, 2004  Francesco Stablum
 * Licensed under the GPL
 *
 */

/* 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at you option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111,
 * USA.
 *
 * Please read the COPYING file for the license details!
 */

/*
 * tcpick project needs somebody suggesting me bugs and new features!
 * If you want to contribute sending patches, finding bugs, compilation
 * errors, platform-specific incompatibilies you are invited to the tcpick
 * mailing-list:
 *
 * email address: 
 *   <tcpick-project@lists.sourceforge.net>
 * Archive:
 *   http://sourceforge.net/mailarchive/forum.php?forum=tcpick-project
 * Subscribe:
 *   http://lists.sourceforge.net/lists/listinfo/tcpick-project
 */

/*
 * in the code you will find many (sooo many) FIXME's: it means that
 * I (or somebody else) should improve the code.
 */

#include "tcpick.h"
#include "globals.h"

char *errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program filter_compiled;
bpf_u_int32 netp; /* ip */
bpf_u_int32 maskp; /* subnet mask */
struct in_addr addr;
char *other_args = NULL;
pcap_t *descr;
const u_char *packet;
u_char *curr_char;
struct pcap_pkthdr hdr;
u_char *datalink_str;

extern void got_packet();
extern void exit_signal(int);

void signal_setup(int sig, void (*handler)(  ))
{
#ifdef HAVE_SIGACTION
	struct sigaction sa; 
	sa.sa_handler = handler;
	sigemptyset(&(sa.sa_mask));
	sigaddset(&(sa.sa_mask), sig);
	sa.sa_flags = 0;
	if( sigaction(sig, &sa, 0) == -1)
		sorry("signal_setup", "sigaction(%d,%p,0) returned -1");
#else
#ifdef HAVE_SIGNAL
	if (signal(sig, handler) == SIG_ERR)
		sorry("signal_setup", "signal(%d,%p) returned SIG_ERR");
#endif /* HAVE_SIGNAL */
#endif /* HAVE_SIGACTION */
}

int main(int argc, char **argv) 
{
	char tbuf[128];
	struct tm *tm;
	time_t now;
		
	parse_args(argc, argv);

	/* set a timer that sends every second a SIGALRM signal */
	set_timer();
	
#ifdef HAVE_ATEXIT
	{
		extern void cleanup();
		
		if (atexit (cleanup) != 0 ) {
			sorry("main", "atexit returned a nonzero value");
		}
	}
	
#endif

#ifdef SIGINT
	signal_setup (SIGINT,  exit_signal);
#endif
#ifdef SIGTERM
	signal_setup (SIGTERM,  exit_signal);
#endif
#ifdef SIGQUIT
	signal_setup (SIGQUIT,  exit_signal);
#endif
#ifdef SIGHUP 
	/* TODO: maybe another use for this signal */
	signal_setup (SIGHUP,  exit_signal);
#endif

	/* time stuff stolen by Fyodor's nmap */
	now = time(NULL);
	if (!(tm = localtime(&now)))
		fault("main", "Unable to get current localtime()");
	if (strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M %Z", tm) <= 0)
		fault("main", "Unable to properly format time");
	
	msg( 1, c_WELCOME,
	     "Starting " PACKAGE_STRING " at %s", tbuf );

#ifdef TCPICK_DEBUG
	msg( 1, c_COMPILED,
	     "Compiled with -DTCPICK_DEBUG" );
#endif
	
	if( flags.trackonly > -1 ) {
		msg( 1, c_SETTING,
		     "Number of connections that will be tracked: %i",
		     flags.trackonly );
	}
	if( flags.timeout <= 0 ) { /* thank you Artyom Khafizov :^) */
		flags.timeout = 600;
	}
	msg( 1, c_SETTING, "Timeout for connections is %i", flags.timeout );


	if( flags.exitpackets != 0 ) {
		msg( 1, c_SETTING,
		     "when %d packets will be sniffed, "
		     "tcpick exits", flags.exitpackets);
	}
	if( flags.exitclosed ) {
		if( flags.exitclosed_first )
			msg(1, c_SETTING, 
			    "when _the first_ %i tracked "
			    "connections will be closed, tcpick exits",
			    flags.exitclosed );
		else
			msg(1, c_SETTING, 
			    "when %i tracked connections "
			    "will be closed, tcpick exits",
			    flags.exitclosed );
	}
	/* I hope this was clear enough */
			
	if( ( dev != NULL ) && ( readfile != NULL) ) {
		suicide( "main",
			 "Please specify either a file (with -r <filename>)\n"
			 "\t or an interface (with -i <int>), not both" );
	}
	
	if( ( dev == NULL ) && ( readfile == NULL ) ) {
		
		/* find first available network device */
		dev = pcap_lookupdev( (char*) errbuf );
		
		if( dev == NULL ) {
			suicide( "main", errbuf );
		}
		
	} else if( readfile != NULL ) {
		setuid(getuid());
		msg( 1, c_INTERFACE, 
		     "%s: reading from %s", 
		     TCPICK_NAME, readfile );

		descr = pcap_open_offline( readfile, (char *)errbuf );
	}
	
	if( dev != NULL) {

		debug( "Using device: %s", dev );

		msg( 1, c_INTERFACE, 
		     "%s: listening on %s", 
		     TCPICK_NAME, dev );

		/* ask pcap for the network address and mask of the device */
		ret = pcap_lookupnet( dev, &netp,
				      &maskp, (char *)errbuf);
		if( ret == -1 ) {
			netp = 0;
			maskp = 0;
			err( "%s", errbuf );
		}

		addr.s_addr = netp;
		net = (char *)strdup( inet_ntoa(addr) );
		if(! net )
			fault( "main", "inet_ntoa" );

		addr.s_addr = maskp;
		mask = (char *)strdup( inet_ntoa(addr) );
		if (! mask ) {
			fault( "main", "inet_ntoa" );
		}
		
		debug( "net: %s", net );
		debug( "mask: %s", mask ); 

		descr = pcap_open_live( dev,
					BUFSIZ, 
					flags.notpromisc ? 0 : 1 ,
					1000, 
					(char *)errbuf );
	}


	if( descr == NULL)
		suicide( "main", errbuf );

	/* compiling the filter */
	if( filter != NULL ) {
		msg(1, c_SETTING, "setting filter: \"%s\"",filter);
		
		if( ( pcap_compile( descr,
				    &filter_compiled,
				    filter, 
				    0, 
				    (int)net 
			      ) == -1) )
			err("error compiling filter \"%s\"",filter);

		pcap_setfilter( descr, &filter_compiled );
	}

	/* getting information about the datalink type of the device choosen 
	   (not all are supported) */
	datalink_id = pcap_datalink( descr );
	datalink_str = (char *)datalink2str( datalink_id );
	debug( "datalink: %s", datalink_str );
	datalink_size = datalink2off( datalink_id );
	debug ( "datalink header size: %d", datalink_size );

	/* setting entry point of linked-list of connections tracked */
	first_conn = (struct CONN *)S_calloc( sizeof(struct CONN), 1 );
	last_conn = first_conn;

	/* see timer.c for further details */
	expired_flag = CHECK_LISTENING;

	/* setting function that analyzes captured packets */
	pcap_loop( descr, -1, got_packet, NULL ); 
 
	/* this point should not be reached, 
	   unless errors or -r option */

	if( readfile ) {
		msg( 1, c_DONE,
		     "%s: done reading from %s", TCPICK_NAME, readfile );
		exit ( TCPICK_SUCCESS );
	} else {
		suicide( "main", "exited pcap loop" );
	}

	/* NOT REACHED */
	return TCPICK_FAILURE;
}


syntax highlighted by Code2HTML, v. 0.9.1