#define __MAIN_C__

#include "tcpflow.h"

stats_t stats;

int debug_level = DEFAULT_DEBUG_LEVEL;
int no_promisc = 0;
int bytes_per_flow = 0;
int max_flows = 0;
int max_desired_fds = 0;
int console_only = 0;
int strip_nonprint = 0;
char daemond = 0;
int time_stats = 3600;
int tcp_timeout = 7200;     /* TCP Timeout 2 hours */
int thr_seconds = 86400;    /* Threshold 1 day */
int thr_packets = 1000000;  /* Threshold 1 million packets */
int thr_bytes   = 50000;    /* Threshold 50Kb */

char error[PCAP_ERRBUF_SIZE];

void print_usage(char *progname)
{
  fprintf(stderr, "%s version "VERSION" by Fryxar (thanks Jeremy Elson!)\n\n",
		 progname);
  fprintf(stderr, "usage: %s [-hpvD] [-d debug_level] [-f tcptimeout]\n", progname);
  fprintf(stderr, "          [-i iface] [-t statistics_time] [-x bytes_threshold]\n");
  fprintf(stderr, "          [-y time_threshold] [-z packets_threshold] [expression]\n\n");
  fprintf(stderr, "        -d: debug level; default is %d\n", DEFAULT_DEBUG_LEVEL);
  fprintf(stderr, "        -D: start as daemon (log to syslog)\n");
  fprintf(stderr, "        -h: print this help message\n");
  fprintf(stderr, "        -i: network interface to listen\n");
  fprintf(stderr, "            (type \"ifconfig -a\" for a list of interfaces)\n");
  fprintf(stderr, "        -f: TCP timeout in seconds; default is %d\n", tcp_timeout );
  fprintf(stderr, "        -p: don't use promiscuous mode\n");
  fprintf(stderr, "        -v: verbose operation equivalent to -d 10\n");
  fprintf(stderr, "        -t: statistics time; default is %d, use 0 to disable\n", time_stats );
  fprintf(stderr, "        -x: cummulative bytes in both senses threshold to report an alarm; default is %d\n", thr_bytes);
  fprintf(stderr, "        -y: duration (in seconds) threshold to report an alarm; default is %d\n", thr_seconds);
  fprintf(stderr, "        -z: cummulative packets in both senses threshold to report an alarm; default is %d\n", thr_packets);
  fprintf(stderr, "expression: tcpdump-like filtering expression\n");
}


void terminate(int sig)
{
  DEBUG(1) ("terminating");
  exit(0); /* libpcap uses onexit to clean up */
}


int main(int argc, char *argv[])
{
extern int optind;
extern int opterr;
extern int optopt;
extern char *optarg;
int arg, dlt, user_expression = 0;
int need_usage = 0, pid;

char *device = NULL;
char *infile = NULL;
char *expression = NULL;
pcap_t *pd;
struct bpf_program fcode;
pcap_handler handler;

  init_debug(argv);

  opterr = 0;

  while ((arg = getopt(argc, argv, "f:d:x:y:z:t:hi:pvD")) != EOF) {
    switch (arg) {
    case 'd':
      if ((debug_level = atoi(optarg)) < 0) {
	debug_level = DEFAULT_DEBUG_LEVEL;
	DEBUG(1) ("warning: -d flag with 0 debug level '%s'", optarg);
      }
      break;
    case 'h':
      print_usage(argv[0]);
      exit(0);
      break;
    case 'i':
      device = optarg;
      break;
    case 'p':
      no_promisc = 1;
      DEBUG(10) ("NOT turning on promiscuous mode");
      break;
    case 'D':
      daemond = 1;
      break;
    case 'v':
      debug_level = 10;
      break;
    case 't':
      time_stats = atoi(optarg);
      break;
    case 'f':
      tcp_timeout = atoi(optarg);
      break;
    case 'x':
      thr_bytes = atoi(optarg);
      break;
    case 'y':
      thr_seconds = atoi(optarg);
      break;
    case 'z':
      thr_packets = atoi(optarg);
      break;
    default:
      DEBUG(1) ("error: unrecognized switch '%c'", optopt);
      need_usage = 1;
      break;
    }
  }

  /* print help and exit if there was an error in the arguments */
  if (need_usage) {
    print_usage(argv[0]);
    exit(1);
  }

  /* hello, world */
  DEBUG(10) ("%s version "VERSION" by Fryxar (Thanks Jeremy Elson!)",
	     argv[0]);

    /* if the user didn't specify a device, try to find a reasonable one */
    if (device == NULL)
      if ((device = pcap_lookupdev(error)) == NULL)
	die("%s", error);

    /* make sure we can open the device */
    if ((pd = pcap_open_live(device, SNAPLEN, !no_promisc, 1000, error)) == NULL)
      die("%s", error);

    /* drop root privileges - we don't need them any more */
    setuid(getuid());

    /* get the handler for this kind of packets */
    dlt = pcap_datalink(pd);
    handler = find_handler(dlt, device);

  /* get the user's expression out of argv */
  expression = copy_argv(&argv[optind]);

  /* add 'ip' to the user-specified filtering expression (if any) to
   * prevent non-ip packets from being delivered. */
  if (expression == NULL) {
    expression = "ip";
    user_expression = 0;
  } else {
    char *new_expression = MALLOC(char, strlen(expression) + 30);
    sprintf(new_expression, "(ip) and (%s)", expression);
    free(expression);
    expression = new_expression;
    user_expression = 1;
  }

  /* If DLT_NULL is "broken", giving *any* expression to the pcap
   * library when we are using a device of type DLT_NULL causes no
   * packets to be delivered.  In this case, we use no expression, and
   * print a warning message if there is a user-specified expression */
#ifdef DLT_NULL_BROKEN
  if (dlt == DLT_NULL && expression != NULL) {
    free(expression);
    expression = NULL;
    if (user_expression) {
      DEBUG(1)("warning: DLT_NULL (loopback device) is broken on your system;");
      DEBUG(1)("         filtering does not work.  Recording *all* packets.");
    }
  }
#endif /* DLT_NULL_BROKEN */

  DEBUG(20) ("filter expression: '%s'",
	     expression == NULL ? "<NULL>" : expression);

  /* install the filter expression in libpcap */
  if (pcap_compile(pd, &fcode, expression, 1, 0) < 0)
    die("%s", pcap_geterr(pd));

  if (pcap_setfilter(pd, &fcode) < 0)
    die("%s", pcap_geterr(pd));

  /* Start Daemon */
  if( daemond ) {
     if( ( pid = fork() ) < 0 ) {
       die( "Error when forking\n" );
       exit( -1 );
     }

     init_daemon( pid );

     syslog( LOG_INFO, "Starting %s daemon", argv[0] );
  }

  /* initialize our flow state structures */
  init_flow_state();

  bzero(&stats, sizeof(stats_t));

  if( time_stats ) {
     signal(SIGALRM, statistics);
     alarm(time_stats);
  }

  /* set up signal handlers for graceful exit (pcap uses onexit to put
     interface back into non-promiscuous mode */
  signal(SIGTERM, terminate);
  signal(SIGINT, terminate);
  signal(SIGHUP, terminate);

  /* start listening! */
  if (infile == NULL)
    DEBUG(1) ("listening on %s", device);
  if (pcap_loop(pd, -1, handler, NULL) < 0)
    die("%s", pcap_geterr(pd));

  /* NOTREACHED */
  return 0;
}
