"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. For more information about "darkstat.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.0.719_vs_3.0.721.

    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: */