Logo Search packages:      
Sourcecode: darkstat version File versions  Download package

darkstat.c

/* darkstat: a network traffic analyzer
 * (c) 2001-2003, Emil Mikulic.
 */

/* Everything here is covered by the GPL (see the 'COPYING' file) */

#include "darkstat.h"
#include "host_db.h"
#include "port_db.h"
#include "proto.h"
#include "bignum.h"
#include "acct.h"
#include "dns.h"
#include "www.h"
#include "graph.h"
#include "spylog.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <pcap.h>

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#if defined(sun) && (defined(__svr4__) || defined(__SVR4)) 
#include <sys/sockio.h>
#endif
#include <locale.h>

int   verbose = 0, webport = DEFAULT_WEBPORT, want_spy = 0,
      webip = -1, shutting_down = 0, promisc = 1,
      up_acct=0, up_www=0, up_dns=0, up_spy=0;
char  *errdb_file = NULL, *db_file = NULL, *daylog_file = NULL,
      *acctdev = NULL, *acctexpr = NULL, *pid_file = NULL;
const char *fprefix = "";
pthread_t www_thread = (pthread_t)0, dns_thread = (pthread_t)0,
        spy_thread = (pthread_t)0, acct_thread = (pthread_t)0;



char *build_str(const char *format, ...)
{
      va_list va;
      char buf[1024], *s;
      int count;

      va_start(va, format);
      count = vsnprintf(buf, 1024, format, va);

      if (count == -1) freakout("buffer overflow in build_str()");
      va_end(va);

      s = strdup(buf);
      if (!s) freakout("ran out of memory in build_str()");
      return s;
}



void freakout(const char *err)
{
      printf("\nFatal error: %s\n\n", err);

      save_db(errdb_file);
      printf("Dying now...\n");
      exit(EXIT_FAIL);
}



void print_addr(const dword ip)
{
      printf("%d.%d.%d.%d",
            (ip >> 24) & 255,
            (ip >> 16) & 255,
            (ip >> 8) & 255,
            (ip & 255) );
}



static RETSIGTYPE term_signal(int signum unused)
{
      if (shutting_down)
      {
            printf("Caught a redundant SIGTERM. Still waiting for: ");
            if (up_acct) printf("acct ");
            if (up_dns) printf("dns ");
            if (up_www) printf("www ");
            if (up_spy) printf("spy ");
            printf("\n");
            return;
      }

      printf("Caught SIGTERM, syncing threads...\n");
      shutting_down = 1;
      putchar(10); /* break out of do_nothing */
}



void ensure_or(const int condition, const char *err)
{
      if (condition) return;
      printf("Error: %s\n", err);
      exit(EXIT_FAIL);
}



void check_sanity(void)
{
      /* check bignum functionality */
      int64 a, b, c;
      SET64(a, 2716, 18942);
      SET64(b, 6633, 457);
      SET64(c, 205, 4982111);
      i64add64(a, b);
      i64add64(a, c);
      ensure_or(HI(a) == 9554 && LO(a) == 5001510,
            "bignum is broken");

      ensure_or(GRAPH_SECS == 60, "GRAPH_SECS should be 60");
      ensure_or(GRAPH_MINS == 60, "GRAPH_MINS should be 60");
      ensure_or(GRAPH_HRS == 24, "GRAPH_HRS should be 24");
      ensure_or(GRAPH_DAYS == 31, "GRAPH_DAYS should be 31");
}



void detach(void)
{
      printf("Detaching...\n");

      switch(fork())
      {
            case 0: break;
            case -1: perror("Can't fork");
                   exit(EXIT_FAILURE);
            default: exit(EXIT_SUCCESS);  /* parent dies */
      }

      if (setsid() < 0)
      {
            printf("setsid() failed!\n");
            exit(EXIT_FAILURE);
      }

      switch(fork())
      {
            case 0: break;
            case -1: perror("Can't fork");
                   exit(EXIT_FAILURE);
            default: exit(EXIT_SUCCESS);  /* parent dies */
      }
}



int main(int argc, char **argv)
{
      char *spydev = NULL, err[PCAP_ERRBUF_SIZE];
      int i;

      check_sanity();

      setlocale(LC_ALL, "");
      setlocale(LC_NUMERIC, "en_US");
#if ENABLE_NLS
      bindtextdomain(PACKAGE, LOCALEDIR);
      textdomain(PACKAGE);
      bind_textdomain_codeset(PACKAGE, "UTF-8");
#endif

      printf(PACKAGE " v" VERSION " using libpcap v%d.%d "
            "(" HOST ")\n",
            PCAP_VERSION_MAJOR, PCAP_VERSION_MINOR);

      local_ip = 0;

      /* handle commandline arguments */
      for (i=1; i<argc; i++)
      {
            if ((strcmp(argv[i], "-h") == 0) ||
                  (strcmp(argv[i], "--help") == 0))
            {
                  printf(
"usage: %s [-i <if>] [-p <number>] [-b <ip>] [-d <path>]\n"
"\t[-l <ip>/<mask>] [-f <ip>] [-v] [-n] [-h] [-V] [-P] [-e expr]\n"
"\t[--spy <if>] [--detach]\n\n"
"  -i\tspecify which interface to listen on\n"
"  -p\tweb interface port (default: %d)\n"
"  -b\twhich local IP to bind the web interface to (default: all)\n"
"  -d\tdatabase storage directory (default: working directory)\n"
"  -l\tlocal network range for correct SNAT accounting (Linux 2.4.x)\n"
"  -f\tforce local ip to <ip> (great for multihomed servers)\n"
"  -v\tverbose mode - packet dumps and more\n"
"  -n\tdon't resolve IPs to hostnames\n"
"  -h\thelp (what you're looking at now)\n"
"  -V\tversion information\n"
"  -P\tturn off promiscuous mode\n"
"  -e\tspecify expression to filter packets\n"
"  --spy\t\tlog HTTP requests seen on specified interface\n"
"  --detach\tdetach from controlling TTY (like a daemon)\n"
"\n",
                        argv[0], webport);

                  return EXIT_SUCCESS;
            }
            else if (strcmp(argv[i], "-v") == 0) verbose = 1;
            else if (strcmp(argv[i], "-n") == 0) want_resolve = OFF;
            else if (strcmp(argv[i], "-P") == 0) promisc = 0;
            else if (strcmp(argv[i], "--spy") == 0)
            {
                  want_spy = 1;
                  i++;
                  if ((argc < i+1) || (argv[i][0] == '-'))
                  {
                        printf("Interface not specified!\n\n");
                        return EXIT_FAIL;
                  }
                  spydev = argv[i];
            }
            else if (strcmp(argv[i], "-p") == 0)
            {
                  i++;
                  if ((argc < i+1) || (argv[i][0] == '-'))
                  {
                        printf("Port not specified!\n\n");
                        return EXIT_FAIL;
                  }
                  webport = atoi(argv[i]);
            }
            else if (strcmp(argv[i], "-b") == 0)
            {
                  int i1,i2,i3,i4;

                  i++;
                  if ((argc < i+1) || (argv[i][0] == '-'))
                  {
                        printf("Bind IP not specified!\n\n");
                        return EXIT_FAIL;
                  }
                  sscanf(argv[i], "%d.%d.%d.%d",
                        &i1, &i2, &i3, &i4);

                  webip = ip_from_quad(i1,i2,i3,i4);

                  printf("Binding webserver to %d.%d.%d.%d\n",
                        i1,i2,i3,i4);
            }
            else if (strcmp(argv[i], "-i") == 0)
            {
                  i++;
                  if ((argc < i+1) || (argv[i][0] == '-'))
                  {
                        printf("Interface not specified!\n\n");
                        return EXIT_FAIL;
                  }
                  acctdev = argv[i];
            }
            else if (strcmp(argv[i], "-d") == 0)
            {
                  i++;
                  if ((argc < i+1) || (argv[i][0] == '-'))
                  {
                        printf("Directory not specified!\n\n");
                        return EXIT_FAIL;
                  }
                  fprefix = build_str("%s%s", argv[i],
                        argv[i][strlen(argv[i])-1] == '/'?"":"/");
            }
            else if ((strcmp(argv[i], "-V") == 0) ||
                  (strcmp(argv[i], "--version") == 0))
            {
                  printf(COPYRIGHT "\n" HOMEPAGE "\n\n"
"This program is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
"(at your option) any later version.\n\n"

"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
"GNU General Public License for more details.\n\n"

"You should have received a copy of the GNU General Public License\n"
"along with this program. If not, write to the Free Software\n"
"Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n\n"
                  );
                  return EXIT_SUCCESS;
            }
            else if (strcmp(argv[i], "-l") == 0)
            {
                  int i1,i2,i3,i4, m1,m2,m3,m4;
                  
                  i++;
                  if ((argc < i+1) || (argv[i][0] == '-'))
                  {
                        printf("LAN space not specified!\n\n");
                        return EXIT_FAIL;
                  }
                  sscanf(argv[i], "%d.%d.%d.%d/%d.%d.%d.%d",
                        &i1, &i2, &i3, &i4,
                        &m1, &m2, &m3, &m4);

                  lan_ip = ip_from_quad(i1,i2,i3,i4);
                  lan_mask = ip_from_quad(m1,m2,m3,m4);

                  printf("Local LAN is %d.%d.%d.%d/%d.%d.%d.%d\n",
                        i1,i2,i3,i4, m1,m2,m3,m4);
            }
            else if (strcmp(argv[i], "-f") == 0)
            {
                  int i1,i2,i3,i4;
                  
                  i++;
                  if ((argc < i+1) || (argv[i][0] == '-'))
                  {
                        printf("Forced IP not specified!\n\n");
                        return EXIT_FAIL;
                  }
                  sscanf(argv[i], "%d.%d.%d.%d",
                        &i1, &i2, &i3, &i4);

                  local_ip = ip_from_quad(i1,i2,i3,i4);

                  printf("Forced IP to %d.%d.%d.%d\n",
                        i1,i2,i3,i4);
            }
            else if (strcmp(argv[i], "-e") == 0)
            {
                  i++;
                  if ((argc < i+1) || (argv[i][0] == '-'))
                  {
                        printf("Expression not specified!\n\n");
                        return EXIT_FAIL;
                  }
                  acctexpr = argv[i];
            }
            else if (strcmp(argv[i], "--detach") == 0)
            {
                  detach();
            }
            else {
                  printf("Invalid argument: %s\n", argv[i]);
                  return EXIT_FAIL;
            }
      }

      if (!acctdev)
      {
            acctdev = pcap_lookupdev(err);
            if (!acctdev)
            {
                  printf("Error: pcap_lookupdev(): %s\n", err);
                  return EXIT_FAIL;
            }
      }

      /* database files, accounting for the specified storage directory */
      db_file = build_str("%s" DB_FILE_NAME, fprefix);
      errdb_file = build_str("%s" ERRDB_FILE_NAME, fprefix);
      daylog_file = build_str("%s" DAYLOG_FILE_NAME, fprefix);

      /* the only way out */
      signal(SIGTERM, term_signal);
      signal(SIGINT, term_signal);

      /* fire up threads */
      printf("Firing up threads...\n"); fflush(stdout);

      if (pthread_mutex_init(&graph_mutex, NULL) == -1)
      {
            printf("Error: could not create graph mutex.\n");
            return EXIT_FAIL;
      }
      if (pthread_mutex_init(&db_mutex, NULL) == -1)
      {
            printf("Error: could not create db mutex.\n");
            return EXIT_FAIL;
      }
      if (pthread_create(&acct_thread, NULL, (void*)&acct_main, NULL) == -1)
      {
            printf("Error: could not create ACCT thread.\n");
            return EXIT_FAIL;
      }
      if (pthread_create(&dns_thread, NULL, (void*)&dns_main, NULL) == -1)
      {
            printf("Error: could not create DNS thread.\n");
            return EXIT_FAIL;
      }
      if (pthread_create(&www_thread, NULL, (void*)&www_main, NULL) == -1)
      {
            printf("Error: could not create WWW thread.\n");
            return EXIT_FAIL;
      }

      if (want_spy)
      if (pthread_create(&spy_thread, NULL,
            (void*)&spylog_main, spydev) == -1)
      {
            printf("Error: could not create spy thread.\n");
            return EXIT_FAIL;
      }

      pid_file = build_str("%s" PID_FILE_NAME, fprefix);
      {
            FILE *fp = fopen(pid_file, "w");
            if (!fp) perror("Warning: can't write pidfile");
            else
            {
                  fprintf(fp, "%d\n", getpid());
                  fclose(fp);
            }
      }

      while (!shutting_down) pause();



      while ((i = up_acct+up_dns+up_www+up_spy))
      {
            printf("Syncing... %d %s", i, (i==1)?"thread":"threads");
            if (up_acct) printf(" (acct)");
            if (up_dns) printf(" (dns)");
            if (up_www) printf(" (www)");
            if (up_spy) printf(" (spy)");
            printf("\n");
            usleep(1000 * MSEC);
      }

      if (acct_thread) pthread_join(acct_thread, NULL);
      if (www_thread) pthread_join(www_thread, NULL);
      if (dns_thread) pthread_join(dns_thread, NULL);
      if (spy_thread) pthread_join(spy_thread, NULL);

      pthread_mutex_destroy(&db_mutex);
      pthread_mutex_destroy(&graph_mutex);

      printf("Synced!  Dumping DB..."); fflush(stdout);
      save_db(db_file);
      printf("done!\n");
      
      free(db_file);
      free(errdb_file);
      free(daylog_file);
      host_db_free();
      port_db_free();

      if (unlink(pid_file) == -1) perror("Can't unlink pidfile");
      free(pid_file);

      return EXIT_SUCCESS;
}

Generated by  Doxygen 1.6.0   Back to index