"Fossies" - the Fresh Open Source Software Archive

Member "darkstat-3.0.721/darkstat.c" (12 Jan 2022, 14599 Bytes) of package /linux/privat/darkstat-3.0.721.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /* darkstat 3
    2  * copyright (c) 2001-2014 Emil Mikulic.
    3  *
    4  * darkstat.c: signals, cmdline parsing, program body.
    5  *
    6  * You may use, modify and redistribute this file under the terms of the
    7  * GNU General Public License version 2. (see COPYING.GPL)
    8  */
    9 
   10 #include "acct.h"
   11 #include "cap.h"
   12 #include "cdefs.h"
   13 #include "config.h"
   14 #include "conv.h"
   15 #include "daylog.h"
   16 #include "db.h"
   17 #include "dns.h"
   18 #include "err.h"
   19 #include "hosts_db.h"
   20 #include "http.h"
   21 #include "localip.h"
   22 #include "ncache.h"
   23 #include "now.h"
   24 #include "pidfile.h"
   25 #include "str.h"
   26 
   27 #include <assert.h>
   28 #include <errno.h>
   29 #include <inttypes.h>
   30 #include <signal.h>
   31 #include <stdio.h>
   32 #include <stdlib.h>
   33 #include <string.h>
   34 #include <syslog.h>
   35 #include <unistd.h>
   36 #include <pcap.h>
   37 
   38 #ifndef INADDR_NONE
   39 # define INADDR_NONE (-1) /* Solaris */
   40 #endif
   41 
   42 /* --- Signal handling --- */
   43 static volatile int running = 1;
   44 static void sig_shutdown(int signum _unused_) { running = 0; }
   45 
   46 static volatile int reset_pending = 0, export_pending = 0;
   47 static void sig_reset(int signum _unused_) {
   48    reset_pending = 1;
   49    export_pending = 1;
   50 }
   51 
   52 static void sig_export(int signum _unused_) { export_pending = 1; }
   53 
   54 /* --- Commandline parsing --- */
   55 static unsigned long parsenum(const char *str,
   56                               unsigned long max /* 0 for no max */) {
   57    unsigned long n;
   58    char *end;
   59 
   60    errno = 0;
   61    n = strtoul(str, &end, 10);
   62    if (*end != '\0')
   63       errx(1, "\"%s\" is not a valid number", str);
   64    if (errno == ERANGE)
   65       errx(1, "\"%s\" is out of range", str);
   66    if ((max != 0) && (n > max))
   67       errx(1, "\"%s\" is out of range (max %lu)", str, max);
   68    return n;
   69 }
   70 
   71 static int opt_iface_seen = 0;
   72 static void cb_interface(const char *arg) {
   73    cap_add_ifname(arg);
   74    opt_iface_seen = 1;
   75 }
   76 
   77 static void cb_filter(const char *arg) { cap_add_filter(arg); }
   78 
   79 static const char *opt_capfile = NULL;
   80 static void cb_capfile(const char *arg) { opt_capfile = arg; }
   81 
   82 int opt_want_snaplen = -1;
   83 static void cb_snaplen(const char *arg)
   84 { opt_want_snaplen = (int)parsenum(arg, 0); }
   85 
   86 int opt_want_pppoe = 0;
   87 static void cb_pppoe(const char *arg _unused_) { opt_want_pppoe = 1; }
   88 
   89 int opt_want_syslog = 0;
   90 static void cb_syslog(const char *arg _unused_) { opt_want_syslog = 1; }
   91 
   92 int opt_want_verbose = 0;
   93 static void cb_verbose(const char *arg _unused_) { opt_want_verbose = 1; }
   94 
   95 static int opt_want_daemonize = 1;
   96 static void cb_no_daemon(const char *arg _unused_) { opt_want_daemonize = 0; }
   97 
   98 static int opt_want_promisc = 1;
   99 static void cb_no_promisc(const char *arg _unused_) { opt_want_promisc = 0; }
  100 
  101 static int opt_want_dns = 1;
  102 static void cb_no_dns(const char *arg _unused_) { opt_want_dns = 0; }
  103 
  104 int opt_want_macs = 1;
  105 static void cb_no_macs(const char *arg _unused_) { opt_want_macs = 0; }
  106 
  107 int opt_want_lastseen = 1;
  108 static void cb_no_lastseen(const char *arg _unused_) { opt_want_lastseen = 0; }
  109 
  110 static unsigned short opt_bindport = 667;
  111 static void cb_port(const char *arg)
  112 { opt_bindport = (unsigned short)parsenum(arg, 65536); }
  113 
  114 static void cb_bindaddr(const char *arg) { http_add_bindaddr(arg); }
  115 
  116 static int is_localnet_specified = 0;
  117 static void cb_local(const char *arg)
  118 {
  119    acct_init_localnet(arg);
  120    is_localnet_specified = 1;
  121 }
  122 
  123 int opt_want_local_only = 0;
  124 static void cb_local_only(const char *arg _unused_)
  125 { opt_want_local_only = 1; }
  126 
  127 static const char *opt_chroot_dir = NULL;
  128 static void cb_chroot(const char *arg) { opt_chroot_dir = arg; }
  129 
  130 static const char *opt_base = NULL;
  131 static void cb_base(const char *arg) { opt_base = arg; }
  132 
  133 static const char *opt_privdrop_user = NULL;
  134 static void cb_user(const char *arg) { opt_privdrop_user = arg; }
  135 
  136 static const char *opt_daylog_fn = NULL;
  137 static void cb_daylog(const char *arg) { opt_daylog_fn = arg; }
  138 
  139 static const char *import_fn = NULL;
  140 static void cb_import(const char *arg) { import_fn = arg; }
  141 
  142 static const char *export_fn = NULL;
  143 static void cb_export(const char *arg) { export_fn = arg; }
  144 
  145 static const char *pid_fn = NULL;
  146 static void cb_pidfile(const char *arg) { pid_fn = arg; }
  147 
  148 unsigned int opt_hosts_max = 1000;
  149 static void cb_hosts_max(const char *arg)
  150 { opt_hosts_max = parsenum(arg, 0); }
  151 
  152 unsigned int opt_hosts_keep = 500;
  153 static void cb_hosts_keep(const char *arg)
  154 { opt_hosts_keep = parsenum(arg, 0); }
  155 
  156 unsigned int opt_ports_max = 60;
  157 static void cb_ports_max(const char *arg)
  158 { opt_ports_max = parsenum(arg, 65536); }
  159 
  160 unsigned int opt_ports_keep = 30;
  161 static void cb_ports_keep(const char *arg)
  162 { opt_ports_keep = parsenum(arg, 65536); }
  163 
  164 unsigned int opt_highest_port = 65535;
  165 static void cb_highest_port(const char *arg)
  166 { opt_highest_port = parsenum(arg, 65535); }
  167 
  168 int opt_wait_secs = -1;
  169 static void cb_wait_secs(const char *arg)
  170 { opt_wait_secs = (int)parsenum(arg, 0); }
  171 
  172 int opt_want_hexdump = 0;
  173 static void cb_hexdump(const char *arg _unused_)
  174 { opt_want_hexdump = 1; }
  175 
  176 static int opt_want_help = 0;
  177 static void cb_help(const char *arg _unused_)
  178 { opt_want_help = 1; }
  179 static void cb_version(const char *arg _unused_)
  180 { opt_want_help = -1; }
  181 
  182 /* --- */
  183 
  184 struct cmdline_arg {
  185    const char *name, *arg_name; /* NULL arg_name means unary */
  186    void (*callback)(const char *arg);
  187    int num_seen;
  188 };
  189 
  190 static struct cmdline_arg cmdline_args[] = {
  191    {"-i",             "interface",       cb_interface,   -1},
  192    {"-f",             "filter",          cb_filter,      -1},
  193    {"-r",             "capfile",         cb_capfile,      0},
  194    {"-p",             "port",            cb_port,         0},
  195    {"-b",             "bindaddr",        cb_bindaddr,    -1},
  196    {"-l",             "network/netmask", cb_local,        0},
  197    {"--base",         "path",            cb_base,         0},
  198    {"--local-only",   NULL,              cb_local_only,   0},
  199    {"--snaplen",      "bytes",           cb_snaplen,      0},
  200    {"--pppoe",        NULL,              cb_pppoe,        0},
  201    {"--syslog",       NULL,              cb_syslog,       0},
  202    {"--verbose",      NULL,              cb_verbose,      0},
  203    {"--no-daemon",    NULL,              cb_no_daemon,    0},
  204    {"--no-promisc",   NULL,              cb_no_promisc,   0},
  205    {"--no-dns",       NULL,              cb_no_dns,       0},
  206    {"--no-macs",      NULL,              cb_no_macs,      0},
  207    {"--no-lastseen",  NULL,              cb_no_lastseen,  0},
  208    {"--chroot",       "dir",             cb_chroot,       0},
  209    {"--user",         "username",        cb_user,         0},
  210    {"--daylog",       "filename",        cb_daylog,       0},
  211    {"--import",       "filename",        cb_import,       0},
  212    {"--export",       "filename",        cb_export,       0},
  213    {"--pidfile",      "filename",        cb_pidfile,      0},
  214    {"--hosts-max",    "count",           cb_hosts_max,    0},
  215    {"--hosts-keep",   "count",           cb_hosts_keep,   0},
  216    {"--ports-max",    "count",           cb_ports_max,    0},
  217    {"--ports-keep",   "count",           cb_ports_keep,   0},
  218    {"--highest-port", "port",            cb_highest_port, 0},
  219    {"--wait",         "secs",            cb_wait_secs,    0},
  220    {"--hexdump",      NULL,              cb_hexdump,      0},
  221    {"--version",      NULL,              cb_version,      0},
  222    {"--help",         NULL,              cb_help,         0},
  223    {NULL,             NULL,              NULL,            0}
  224 };
  225 
  226 /* We autogenerate the usage statement from the cmdline_args data structure. */
  227 static void usage(void) {
  228    static char intro[] = "usage: darkstat ";
  229    char indent[sizeof(intro)];
  230    struct cmdline_arg *arg;
  231 
  232    printf(PACKAGE_STRING " (using %s)\n", pcap_lib_version());
  233    if (opt_want_help == -1) return;
  234 
  235    memset(indent, ' ', sizeof(indent));
  236    indent[0] = indent[sizeof(indent) - 1] = 0;
  237 
  238    printf("\n%s", intro);
  239    for (arg = cmdline_args; arg->name != NULL; arg++) {
  240       printf("%s[ %s%s%s ]\n",
  241           indent,
  242           arg->name,
  243           arg->arg_name != NULL ? " " : "",
  244           arg->arg_name != NULL ? arg->arg_name : "");
  245       indent[0] = ' ';
  246    }
  247    printf("\n"
  248 "Please refer to the darkstat(8) manual page for further\n"
  249 "documentation and usage examples.\n");
  250 }
  251 
  252 static void parse_sub_cmdline(const int argc, char * const *argv) {
  253    struct cmdline_arg *arg;
  254 
  255    if (argc == 0) return;
  256    for (arg = cmdline_args; arg->name != NULL; arg++)
  257       if (strcmp(argv[0], arg->name) == 0) {
  258          if ((arg->arg_name != NULL) && (argc == 1)) {
  259             fprintf(stderr,
  260                "error: argument \"%s\" requires parameter \"%s\"\n",
  261                arg->name, arg->arg_name);
  262             usage();
  263             exit(EXIT_FAILURE);
  264          }
  265          if (arg->num_seen > 0) {
  266             fprintf(stderr,
  267                "error: already specified argument \"%s\"\n",
  268                arg->name);
  269             usage();
  270             exit(EXIT_FAILURE);
  271          }
  272 
  273          if (arg->num_seen != -1) /* accept more than one */
  274             arg->num_seen++;
  275 
  276          if (arg->arg_name == NULL) {
  277             arg->callback(NULL);
  278             parse_sub_cmdline(argc-1, argv+1);
  279          } else {
  280             arg->callback(argv[1]);
  281             parse_sub_cmdline(argc-2, argv+2);
  282          }
  283          return;
  284       }
  285 
  286    fprintf(stderr, "error: illegal argument: \"%s\"\n", argv[0]);
  287    usage();
  288    exit(EXIT_FAILURE);
  289 }
  290 
  291 static void parse_cmdline(const int argc, char * const *argv) {
  292    if (argc < 1) {
  293       /* Not enough args. */
  294       usage();
  295       exit(EXIT_FAILURE);
  296    }
  297 
  298    parse_sub_cmdline(argc, argv);
  299 
  300    if (opt_want_help) {
  301      usage();
  302      exit(EXIT_SUCCESS);
  303    }
  304 
  305    /* start syslogging as early as possible */
  306    if (opt_want_syslog)
  307       openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON);
  308 
  309    /* default value */
  310    if (opt_privdrop_user == NULL)
  311       opt_privdrop_user = PRIVDROP_USER;
  312 
  313    /* sanity check args */
  314    if (!opt_iface_seen && opt_capfile == NULL)
  315       errx(1, "must specify either interface (-i) or capture file (-r)");
  316 
  317    if (opt_iface_seen && opt_capfile != NULL)
  318       errx(1, "can't specify both interface (-i) and capture file (-r)");
  319 
  320    if ((opt_hosts_max != 0) && (opt_hosts_keep >= opt_hosts_max)) {
  321       opt_hosts_keep = opt_hosts_max / 2;
  322       warnx("reducing --hosts-keep to %u, to be under --hosts-max (%u)",
  323          opt_hosts_keep, opt_hosts_max);
  324    }
  325    verbosef("max %u hosts, cutting down to %u when exceeded",
  326       opt_hosts_max, opt_hosts_keep);
  327 
  328    if ((opt_ports_max != 0) && (opt_ports_keep >= opt_ports_max)) {
  329       opt_ports_keep = opt_ports_max / 2;
  330       warnx("reducing --ports-keep to %u, to be under --ports-max (%u)",
  331          opt_ports_keep, opt_ports_max);
  332    }
  333    verbosef("max %u ports per host, cutting down to %u when exceeded",
  334       opt_ports_max, opt_ports_keep);
  335 
  336    if (opt_want_hexdump && !opt_want_verbose) {
  337       opt_want_verbose = 1;
  338       verbosef("--hexdump implies --verbose");
  339    }
  340 
  341    if (opt_want_hexdump && opt_want_daemonize) {
  342       opt_want_daemonize = 0;
  343       verbosef("--hexdump implies --no-daemon");
  344    }
  345 
  346    if (opt_want_local_only && !is_localnet_specified)
  347       verbosef("WARNING: --local-only without -l only matches the local host");
  348 }
  349 
  350 static void run_from_capfile(void) {
  351    now_init();
  352    graph_init();
  353    hosts_db_init();
  354    cap_from_file(opt_capfile);
  355    if (export_fn != NULL) db_export(export_fn);
  356    hosts_db_free();
  357    graph_free();
  358    verbosef("Total packets: %llu, bytes: %llu",
  359             (llu)acct_total_packets,
  360             (llu)acct_total_bytes);
  361 }
  362 
  363 /* --- Program body --- */
  364 int
  365 main(int argc, char **argv)
  366 {
  367    test_64order();
  368    parse_cmdline(argc-1, argv+1);
  369 
  370    if (opt_capfile) {
  371       run_from_capfile();
  372       return 0;
  373    }
  374 
  375    /* must verbosef() before first fork to init lock */
  376    verbosef("starting up");
  377    if (pid_fn) pidfile_create(opt_chroot_dir, pid_fn, opt_privdrop_user);
  378 
  379    if (opt_want_daemonize) {
  380       verbosef("daemonizing to run in the background!");
  381       daemonize_start();
  382       verbosef("I am the main process");
  383    }
  384    if (pid_fn) pidfile_write_close();
  385 
  386    /* do this first as it forks - minimize memory use */
  387    if (opt_want_dns) dns_init(opt_privdrop_user);
  388    cap_start(opt_want_promisc); /* needs root */
  389    http_init_base(opt_base);
  390    http_listen(opt_bindport);
  391    ncache_init(); /* must do before chroot() */
  392 
  393    privdrop(opt_chroot_dir, opt_privdrop_user);
  394 
  395    /* Don't need root privs for these: */
  396    now_init();
  397    if (opt_daylog_fn != NULL) daylog_init(opt_daylog_fn);
  398    graph_init();
  399    hosts_db_init();
  400    if (import_fn != NULL) db_import(import_fn);
  401 
  402    if (signal(SIGTERM, sig_shutdown) == SIG_ERR)
  403       errx(1, "signal(SIGTERM) failed");
  404    if (signal(SIGINT, sig_shutdown) == SIG_ERR)
  405       errx(1, "signal(SIGINT) failed");
  406    if (signal(SIGUSR1, sig_reset) == SIG_ERR)
  407       errx(1, "signal(SIGUSR1) failed");
  408    if (signal(SIGUSR2, sig_export) == SIG_ERR)
  409       errx(1, "signal(SIGUSR2) failed");
  410 
  411    verbosef("entering main loop");
  412    daemonize_finish();
  413 
  414    while (running) {
  415       int select_ret;
  416       int max_fd = -1;
  417       int use_timeout = 0;
  418       int cap_ret;
  419       struct timeval timeout;
  420       struct timespec t;
  421       fd_set rs, ws;
  422 
  423       FD_ZERO(&rs);
  424       FD_ZERO(&ws);
  425       cap_fd_set(&rs, &max_fd, &timeout, &use_timeout);
  426       http_fd_set(&rs, &ws, &max_fd, &timeout, &use_timeout);
  427 
  428       select_ret = select(max_fd+1, &rs, &ws, NULL,
  429          (use_timeout) ? &timeout : NULL);
  430       if (select_ret == 0 && !use_timeout)
  431             errx(1, "select() erroneously timed out");
  432       if (select_ret == -1) {
  433          if (errno == EINTR)
  434             continue;
  435          else
  436             err(1, "select()");
  437       }
  438 
  439       timer_start(&t);
  440       now_update();
  441 
  442       if (export_pending) {
  443          if (export_fn != NULL)
  444             db_export(export_fn);
  445          export_pending = 0;
  446       }
  447 
  448       if (reset_pending) {
  449          if (export_pending)
  450             continue; /* export before reset */
  451          hosts_db_reset();
  452          graph_reset();
  453          reset_pending = 0;
  454       }
  455 
  456       graph_rotate();
  457       cap_ret = cap_poll(&rs);
  458       dns_poll();
  459       http_poll(&rs, &ws);
  460       timer_stop(&t, 1000000000, "event processing took longer than a second");
  461 
  462       if (!cap_ret) {
  463          running = 0;
  464       }
  465    }
  466 
  467    verbosef("shutting down");
  468    verbosef("pcap stats: %u packets received, %u packets dropped",
  469       cap_pkts_recv, cap_pkts_drop);
  470    http_stop();
  471    cap_stop();
  472    dns_stop();
  473    if (export_fn != NULL) db_export(export_fn);
  474    hosts_db_free();
  475    graph_free();
  476    if (opt_daylog_fn != NULL) daylog_free();
  477    ncache_free();
  478    if (pid_fn) pidfile_unlink();
  479    verbosef("shut down");
  480    return (EXIT_SUCCESS);
  481 }
  482 
  483 /* vim:set ts=3 sw=3 tw=78 expandtab: */