/*
 **
 * snoop wrapper
 * 
 * Gouverneur Th - BGC ITN (2008)
 **
 */

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#define SNOOPBIN "/usr/sbin/snoop"
#define DEBUG


/* fct */
void _err_exit(const char *, const int);
void _print_help(void);
void _print_notice(void);

int main (int ac, char** av)
{
  extern char *optarg;
  extern int optopt;
  int c, tmpint, i, l, and, argc, argn;
  char **argv;
  char buf[20];
 
  /* snoop args variables */
  int 	s_verbose = 0;
  int 	s_maxcount = -1;
  char 	s_dev[10] = "";
  char 	s_src[255] = "";
  char 	s_dst[255] = "";
  char*	s_proto = "";
  short int s_spt = 0;
  short int s_dpt = 0;
  short int s_len = 34;

  if (ac == 1) {
    _print_notice();
    fprintf(stdout, "[!] Running snoop with default safe arguments...\n");
    execl(SNOOPBIN, "", "-P", "-s 54",  NULL);
  } else {
    /* must verify arguments */
    while ((c = getopt(ac, av, "c:hvd:s:t:p:P:k:")) != -1) {
      switch(c) {

	case 'h': /* help */
	  _print_help();
	  exit(0);
	break;

	case 'v': /* verbose mode */
	  s_verbose = 1;
	break;

	case 'c': /* max count */
	  tmpint = atoi(optarg);
	  if ((tmpint == 0 && errno == EINVAL)) {
	    _err_exit("Error, max count value should be integer value.", -42);
	  }
	  s_maxcount = tmpint;
	break;

	case 'd': /* device */
	  if ((l = strlen(optarg)) >= 9) {
	    _err_exit("Error, check interface name providden.", -42);
	  }
	  for (i=0; i < l && i < 9; i++) {
	    if ((optarg[i] < '0' || optarg[i] > '9') && (optarg[i] < 'a' || optarg[i] > 'z')) {
	      _err_exit("Forbidden chacacters in the interface name. feds have been called.", -42);
	    }
	  }
	  strncpy(s_dev, optarg, l);
	break;

	case 's': /* source */
          if ((l = strlen(optarg)) >= 254) {
            _err_exit("Error, check source name/IP providden.", -42);
          }
          for (i=0; i < l && i < 254; i++) {
            if ((optarg[i] < '0' || optarg[i] > '9') && (optarg[i] < 'a' || optarg[i] > 'z') && (optarg[i] != '.')) {
              _err_exit("Forbidden in the source name or IP. feds have been called.", -42);
            }
          }
          strncpy(s_src, optarg, l);
	break;

	case 't': /* target(destination) */
          if ((l = strlen(optarg)) >= 254) {
            _err_exit("Error, check destination name/IP providden.", -42);
          }
          for (i=0; i < l && i < 254; i++) {
            if ((optarg[i] < '0' || optarg[i] > '9') && (optarg[i] < 'a' || optarg[i] > 'z') && (optarg[i] != '.')) {
              _err_exit("Forbidden in the destination name or IP. feds have been called.", -42);
            }
          }
          strncpy(s_dst, optarg, l);
	break;

	case 'p': /* source port */
          tmpint = atoi(optarg);
          if ((tmpint == 0 && errno == EINVAL) || tmpint <= 0 || tmpint > 65535) {
            _err_exit("Error, max count value should be integer value between 1 and 65535.", -42);
          }
          s_spt = tmpint;

	break;

	case 'P': /* destination port */
          tmpint = atoi(optarg);
          if ((tmpint == 0 && errno == EINVAL) || tmpint <= 0 || tmpint > 65535) {
            _err_exit("Error, max count value should be integer value between 1 and 65535.", -42);
          }
          s_dpt = tmpint;

	break;

	case 'k': /* kind of packet (udp, tcp, ...) */

	  if (!strcmp(optarg, "ip"))
	  {
	    s_proto = "ip";
	    s_len = 34;
	  }
	  else if (!strcmp(optarg, "arp"))
	    s_proto = "arp";
	  else if (!strcmp(optarg, "rarp"))
	    s_proto = "rarp";
	  else if (!strcmp(optarg, "broadcast"))
	    s_proto = "broadcast";
          else if (!strcmp(optarg, "dhcp"))
	  {
            s_proto = "dhcp";
	    s_len = 200;
	  }
          else if (!strcmp(optarg, "udp"))
	  {
            s_proto = "udp";
	    s_len = 42;
	  }
          else if (!strcmp(optarg, "tcp"))
	  {
            s_proto = "tcp";
	    s_len = 54;
	  }
          else if (!strcmp(optarg, "icmp"))
	  {
            s_proto = "icmp";
	    s_len = 1500;
	  }
	  else
	    _err_exit("Unknown specified protocol.", -42);
	break;

	case '?': /* unknown option */
	  fprintf(stderr, "Unknown option: -%c\n", optopt);
	  _print_help();
	  exit(-1);
	  break;

	default:
	 _print_help();
	 exit(-1);
	 break;
      }
    }
  
    /* build command line arguments */

    argn = 0;
    argc = 5;
    argv = (char **) malloc (sizeof(char *)*argc);
    argv[argn++] = "";
    argv[argn++] = "-P";
    argv[argn++] = "-s";
    memset(buf, 0, sizeof(buf));
    argv[argn++] = (char*)strdup((char*)lltostr(s_len, buf)); 
  
    if (s_maxcount != -1) {
      argc += 2;
      argv = (char **) realloc(argv, sizeof(char *) * argc);
      argv[argn++] = "-c";
      memset(buf, 0, sizeof(buf));
      argv[argn++] = (char*)strdup((char*)lltostr(s_maxcount, buf));
    }
    if (strcmp(s_dev, "")) {
      argc += 2;
      argv = (char **) realloc(argv, sizeof(char *) * argc);
      argv[argn++] = "-d";
      argv[argn++] = s_dev;
    }

    if (strcmp(s_src, "")) {
      argc += 2;
      argv = (char **) realloc(argv, sizeof(char *) * argc);
      argv[argn++] = "src";
      argv[argn++] = s_src;
      and++;
    }
    
    if (strcmp(s_dst, "")) {
      argc += 2;
      if (and) argc++;
      argv = (char **) realloc(argv, sizeof(char *) * argc);
      if (and) argv[argn++] = "and";
      argv[argn++] = "dst";
      argv[argn++] = s_dst;
      and++;
    }

    if (strcmp(s_proto, "")) {
      argc++;
      if (and) argc++;
      argv = (char **) realloc(argv, sizeof(char *) * argc);
      if (and) argv[argn++] = "and";
      argv[argn++] = s_proto;
      and++;
    }

    if (s_spt) {
      argc += 3;
      if (and) argc++;
      argv = (char **) realloc(argv, sizeof(char *) * argc);
      if (and) argv[argn++] = "and";
      argv[argn++] = "from";
      argv[argn++] = "port";
      memset(buf, 0, sizeof(buf));
      argv[argn++] = (char*)strdup((char*)lltostr(s_spt, buf));
      and++;
    }

    if (s_dpt) {
      argc += 3;
      if (and) argc++;
      argv = (char **) realloc(argv, sizeof(char *) * argc);
      if (and) argv[argn++] = "and";
      argv[argn++] = "to";
      argv[argn++] = "port";
      memset(buf, 0, sizeof(buf));
      argv[argn++] = (char*)strdup((char*)lltostr(s_dpt, buf));
      and++;
    }

    argv[argn++] = NULL;

    _print_notice();

#ifdef DEBUG
    printf("[D] Argument list: total:%d (used: %d)\n", argc, argn);
    for (i=0; i<argc; i++) {
      if (argv[i] == NULL) {
        printf("[D] %d:\tNULL\n", i+1);
        continue;
      }
      printf("[D] %d:\t%s\n", i + 1, argv[i]);
    }
#endif

    /* executing snoop command */
    execv(SNOOPBIN, argv);
  }
  return 0;
}

/*
 * Print notice telling that snoop's starting
 */
void _print_notice(void)
{
  fprintf(stdout, "[-] lsnoop starting...\n");
  fprintf(stdout, "[!] notice that promiscuous mode is disabled.\n");
  return;
}

/*
 * Print help message with syntax of lsnoop
 */
void _print_help(void)
{
  fprintf(stdout, "lsnoop usage:\n");
  fprintf(stdout, "\t-h\t\t This help message.\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "\t-v\t\t More verbose output.\n");
  fprintf(stdout, "\t-c <n>\t\t Max count of packet\n");
  fprintf(stdout, "\t-d <dev>\t Device to snoop\n");
  fprintf(stdout, "\t-s <src>\t Source ip/host of packet to snoop\n");
  fprintf(stdout, "\t-t <dst>\t Destination ip/host of packet to snoop\n");
  fprintf(stdout, "\t-p <spt>\t Source port of packet to snoop\n");
  fprintf(stdout, "\t-P <dpt>\t Destination port of packet to snoop\n");
  fprintf(stdout, "\t-k <protocol>\t Kind of packet to snoop (udp, tcp, ...)\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "If anything else needed, please contact TEC-SUN.\n");
  return;
}

/*
 * Print error and exit program with rc code.
 */
void _err_exit(const char *err, const int rc) {

  fprintf(stderr, "[X] %s\n", err);
  exit (rc);
}
