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

dccd.c

/* Distributed Checksum Clearinghouse server
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.162 $Revision$
 */

#include "dccd_defs.h"
#include <signal.h>                 /* for Linux and SunOS */
#include <sys/uio.h>
#include <sys/wait.h>
#include "dcc_ifaddrs.h"

DCC_EMSG dcc_emsg;

static const char *homedir;
static DCC_PATH dbclean_def = DCC_LIBEXECDIR"/dbclean";
static char *dbclean = dbclean_def;
static pid_t dbclean_pid = -1;
static char *addr_str;              /* an IP address for dbclean */
static char dbclean_str[] = "dbclean";
static char dbclean_mode[8];
static const char *dbclean_argv[20] = {dbclean_str, dbclean_mode};
static int dbclean_argc = 2;
static char dbclean_arg_str[200];
static int dbclean_arg_len;
time_t dbclean_limit_secs = DBCLEAN_LIMIT_SECS;

u_long dccd_tracemask = DCC_TRACE_ON_DEF_BITS;

u_char background = 1;
int stopint;                        /* !=0 if stopping or received signal */

static time_t flods_ck_secs = FLODS_CK_SECS;

const char *brand = "";

static char *my_srvr_id_str;
DCC_SRVR_ID my_srvr_id;

#if defined(NO_IPV6) || !defined(USE_IPV6)
u_char use_ipv6 = 0;
#else
u_char use_ipv6 = 2;
#endif
u_int16_t def_port;                 /* default port #, network byte order */
SRVR_SOC *srvr_socs;
static SRVR_SOC **srvr_socs_end = &srvr_socs;
int srvr_rcvbuf;

static u_char db_mode;

DB_RCD host_id_rcd;                 /* our host's globally unique ID */
time_t host_id_next, host_id_last;

u_char anon_off;              /* turn off anonymous access */
time_t anon_delay_us = DCC_ANON_DELAY_US_DEF;   /* delay anonymous clients */
u_int anon_delay_inflate = DCC_ANON_INFLATE_OFF;

static QUEUE *queue_free;
static QUEUE *queue_anon_head;

/* assume or hope we can handle 200 requests/second */
int queue_max = 200*DCC_MAX_RTT_SEC;      /* ultimate bound on queue size */
static int queue_max_cur;           /* current upper bound */
static int queue_anon_cur;          /* current queue size */

struct timeval wake_time;           /* when we awoke from select() */
DCC_TS future_ts;             /* the distant future */

const char *need_del_dbclean;
time_t del_dbclean_next;
time_t dbclean_limit;
static time_t dbclean_cron_sub;

static DB_NOKEEP_CKS new_nokeep_cks;

DB_FLOD_THOLDS oflod_thold;         /* flood at or after these thresholds */

static void parse_thold(const char *);
static const char *parse_rl_rate(RL_RATE *, const char *, const char *);
static void add_dbclean_arg(const char *);
static void ck_dbclean(int);
static void run_dbclean(u_char, const char *);
static void sigterm(int);
static void sighup(int);
static void stop_children(void);
static SRVR_SOC *add_srvr_soc(u_char, int, const void *, u_int16_t);
static void open_srvr_socs(int *);
static u_char get_ifs(u_char, u_int16_t);
static void recv_job(void) NRATTRIB;
static u_char new_job(SRVR_SOC *);
static void NRATTRIB db_quit(int, const char *, ...) PATTRIB(2,3);

static void
usage(void)
{
      static const char str[] = {
          "usage: [-64dVbfFQ] -i server-ID [-n brand] [-h homedir]\n"
          "   [-a [server-addr][,server-port]] [-I host-ID] [-q qsize]\n"
          "   [-G [on,][weak-body,][weak-IP,]embargo],[wait],[white]]\n"
          "   [-t type,threshhold] [-K [no-]type] [-T tracemode]\n"
          "   [-u anon-delay[,inflate] [-C dbclean]"
          " [-L ltype,facility.level]\n"
          "   [-R [RL_SUB],[RL_FREE],[RL_ALL_FREE],[RL_BUGS]]"
      };
      static u_char complained;

      if (!complained) {
            dcc_error_msg("%s\n... continuing", str);
            complained = 1;
      }
}


int NRATTRIB
main(int argc, char **argv)
{
      char *p;
      const char *rest;
      u_char print_version = 0;
      DCC_CK_TYPES type;
      char hostname[MAXHOSTNAMELEN];
      DCC_SOCKU *sup;
      u_int16_t port;
      int new_embargo, new_window, new_white;
      int bind_retries, error, i;
      u_long l;

      dcc_syslog_init(1, argv[0], 0);

      if (DCC_DIM_CKS != DCC_COMP_DIM_CKS)
            dcc_logbad(EX_SOFTWARE,
                     "DCC_DIM_CKS != DCC_COMP_DIM_CKS;"
                     " check uses of both");

      /* get first bytes of hostname to name our server-ID */
      memset(hostname, 0, sizeof(hostname));
      if (0 > gethostname(hostname, sizeof(hostname)-1))
            dcc_logbad(EX_NOHOST, "gethostname(): %s", ERROR_STR());
      if (hostname[0] == '\0')
            dcc_logbad(EX_NOHOST, "null hostname from gethostname()");
      memcpy(host_id_rcd.cks[0].sum, hostname,
             sizeof(host_id_rcd.cks[0].sum));

      new_nokeep_cks = def_nokeep_cks();
      for (type = 0; type < DIM(oflod_thold); type++) {
            /* flood server-ID declarations immediately,
             * body checksums by the bulk threshold,
             * and nothing else */
            oflod_thold[type] = (DCC_CK_IS_BODY(type) ? BULK_THRESHOLD
                             : type == DCC_CK_SRVR_ID ? 1
                             : DCC_TGTS_INVALID);
      }

      parse_rl_rate(&rl_sub_rate, "RL_SUB", "200");
      parse_rl_rate(&rl_anon_rate, "RL_ANON", "50");
      parse_rl_rate(&rl_all_anon_rate, "RL_ALL_ANON", "200");
      parse_rl_rate(&rl_bugs_rate, "RL_BUGS", "0.1");

      while ((i = getopt(argc, argv,
                     "64dVbfFQi:n:h:a:I:q:G:t:K:T:u:C:L:R:")) != EOF) {
            switch (i) {
            case '6':
#ifndef NO_IPV6
                  use_ipv6 = 2;
#endif
                  break;
            case '4':
                  use_ipv6 = 0;
                  break;

            case 'd':
                  ++db_debug;
                  break;

            case 'V':
                  fprintf(stderr, DCC_VERSION"\n");
                  print_version = 1;
                  break;

            case 'b':
                  background = 0;
                  break;

            case 'f':
                  db_mode &= ~DB_OPEN_NO_MMAP;
                  break;

            case 'F':
                  db_mode |= DB_OPEN_NO_MMAP;
                  break;

            case 'Q':
                  query_only = 1;
                  break;

            case 'i':
                  my_srvr_id_str = optarg;
                  if (!dcc_get_srvr_id(dcc_emsg, &my_srvr_id,
                                   my_srvr_id_str, 0, 0, 0))
                        dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
                  add_dbclean_arg("-i");
                  add_dbclean_arg(my_srvr_id_str);
                  break;

            case 'n':
                  if (*optarg == '\0'
                      || strlen(optarg) > sizeof(DCC_BRAND)
                      || strpbrk(optarg, ":"DCC_WHITESPACE)) {
                        dcc_error_msg("invalid brand name \"-n %s\"",
                                    optarg);
                  } else {
                        brand = optarg;
                  }
                  break;

            case 'h':
                  homedir = optarg;
                  /* tell dbclean "-h ." because we will already
                   * be there and that allows our -h to be relative */
                  add_dbclean_arg("-h.");
                  break;

            case 'a':
                  optarg += strspn(optarg, DCC_WHITESPACE);
                  if (!addr_str)
                        addr_str = optarg;
                  rest = dcc_parse_nm_port(dcc_emsg, optarg, 0,
                                     hostname, sizeof(hostname),
                                     &port, 0, 0, 0, 0);
                  if (!rest)
                        dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
                  rest += strspn(rest, DCC_WHITESPACE);
                  if (*rest != '\0')
                        dcc_logbad(EX_USAGE,
                                 "unrecognized port number in"
                                 "\"-a %s\"", optarg);
                  if (hostname[0] == '\0') {
                        get_ifs(1, port);
                  } else if (!strcmp(hostname, "@")) {
                        if (addr_str == optarg)
                              ++addr_str; /* "" but not a const */
                        add_srvr_soc(SRVR_SOC_ADDR, 0, 0, port);
                  } else {
                        dcc_host_lock();
                        if (!dcc_get_host(hostname, use_ipv6, &error))
                              dcc_logbad(EX_NOHOST,
                                       "dcc_get_host(%s): %s",
                                       optarg,
                                       DCC_HSTRERROR(error));
                        for (sup = dcc_hostaddrs;
                             sup < dcc_hostaddrs_end;
                             ++sup) {
                              if (sup->sa.sa_family == AF_INET)
                                  add_srvr_soc(SRVR_SOC_ADDR,
                                          AF_INET,
                                          &sup->ipv4.sin_addr,
                                          port);
                              else
                                  add_srvr_soc(SRVR_SOC_ADDR,
                                          AF_INET6,
                                          &sup->ipv6.sin6_addr,
                                          port);
                        }
                        dcc_host_unlock();
                  }
                  break;

            case 'I':
                  strncpy((char *)host_id_rcd.cks[0].sum, optarg,
                        sizeof(host_id_rcd.cks[0].sum));
                  break;

            case 'q':
                  l = strtoul(optarg, &p, 0);
                  if (*p != '\0' || l < 2 || l > 1000) {
                        dcc_error_msg("invalid queue length \"%s\"",
                                    optarg);
                  } else {
                        queue_max = l;
                  }
                  break;

            case 'G':
                  grey_on = 1;
                  dcc_syslog_init(1, argv[0], " grey");
                  add_dbclean_arg("-Gon");
                  /* handle leading "on" "weak-body", and "weak-IP" */
                  while (*optarg) {
                        if (dcc_ck_word_comma(&optarg, "weak-body")
                            || dcc_ck_word_comma(&optarg, "weak_body")
                            || dcc_ck_word_comma(&optarg, "weak")) {
                              grey_weak_body = 1;
                              continue;
                        }
                        if (dcc_ck_word_comma(&optarg, "weak-IP")
                            || dcc_ck_word_comma(&optarg, "weak_IP")) {
                              grey_weak_ip = 1;
                              continue;
                        }
                        if (dcc_ck_word_comma(&optarg, "on"))
                              continue;
                        break;
                  }
                  new_embargo = dcc_get_secs(optarg, &p,
                                       0, MAX_GREY_EMBARGO,
                                       grey_embargo);
                  if (new_embargo < 0) {
                        dcc_error_msg("invalid greylist embargo \"%s\"",
                                    optarg);
                        break;
                  }
                  new_window = dcc_get_secs(p, &p,
                                      new_embargo, 10*24*60*60,
                                      max(new_embargo,grey_window));
                  if (new_window < 0) {
                        dcc_error_msg("invalid greylist wait time"
                                    " \"%s\"",
                                    optarg);
                        break;
                  }
                  new_white = dcc_get_secs(p, 0,
                                     new_window, DB_EXPIRE_SECS_MAX,
                                     max(new_window, grey_white));
                  if (new_white < 0) {
                        dcc_error_msg("invalid greylist whitelist time"
                                    " \"%s\"",
                                    optarg);
                        break;
                  }
                  grey_embargo = new_embargo;
                  grey_window = new_window;
                  grey_white = new_white;
                  break;

            case 't':
                  parse_thold(optarg);
                  break;

            case 'K':
                  if (!strcasecmp(optarg, "all")) {
                        new_nokeep_cks = 0;
                        break;
                  }
                  if (!CSTRCMP(optarg, "no-")) {
                        optarg += STRZ("no-");
                        i = 0;
                  } else {
                        i = 1;
                  }
                  type = dcc_str2type(optarg);
                  if (type == DCC_CK_INVALID) {
                        dcc_error_msg("bad checksum type in"
                                    " \"-K %s\"", optarg);
                        break;
                  }
                  if (i)
                        DB_RESET_NOKEEP(new_nokeep_cks, type);
                  else
                        DB_SET_NOKEEP(new_nokeep_cks, type);
                  break;

            case 'T':
                  if (!strcasecmp(optarg, "ALL")) {
                        dccd_tracemask |= DCC_TRACE_ON_BITS;
                  } else if (!strcasecmp(optarg, "ADMN")) {
                        dccd_tracemask |= DCC_TRACE_ADMN_BIT;
                  } else if (!strcasecmp(optarg, "ANON")) {
                        dccd_tracemask |= DCC_TRACE_ANON_BIT;
                  } else if (!strcasecmp(optarg, "CLNT")) {
                        dccd_tracemask |= DCC_TRACE_CLNT_BIT;
                  } else if (!strcasecmp(optarg, "RLIM")) {
                        dccd_tracemask |= DCC_TRACE_RLIM_BIT;
                  } else if (!strcasecmp(optarg, "QUERY")) {
                        dccd_tracemask |= DCC_TRACE_QUERY_BIT;
                  } else if (!strcasecmp(optarg, "RIDC")) {
                        dccd_tracemask |= DCC_TRACE_RIDC_BIT;
                  } else if (!strcasecmp(optarg, "FLOOD")) {
                        dccd_tracemask |= DCC_TRACE_FLOD_BIT;
                  } else if (!strcasecmp(optarg, "FLOOD2")) {
                        dccd_tracemask |= DCC_TRACE_FLOD2_BIT;
                  } else if (!strcasecmp(optarg, "IDS")) {
                        dccd_tracemask |= DCC_TRACE_IDS_BIT;
                  } else if (!strcasecmp(optarg, "BL")) {
                        dccd_tracemask |= DCC_TRACE_BL_BIT;
                  } else {
                        dcc_error_msg("invalid trace mode \"%s\"",
                                    optarg);
                  }
                  break;

            case 'u':
                  if (!strcasecmp(optarg, "forever")) {
                        anon_off = 1;
                        break;
                  }
                  if (!parse_dccd_delay(dcc_emsg, &anon_delay_us,
                                    &anon_delay_inflate, optarg,
                                    0, 0)) {
                        dcc_error_msg("%s", dcc_emsg);
                        break;
                  }
                  anon_off = 0;
                  break;

            case 'C':
                  dbclean = optarg;
                  for (p = strpbrk(optarg, DCC_WHITESPACE);
                       p != 0;
                       p = strpbrk(p, DCC_WHITESPACE)) {
                        *p++ = '\0';
                        p += strspn(p, DCC_WHITESPACE);
                        if (*p == '\0')
                              break;
                        add_dbclean_arg(p);
                  }
                  break;

            case 'L':
                  if (dcc_parse_log_opt(optarg)) {
                        add_dbclean_arg("-L");
                        add_dbclean_arg(optarg);
                  }
                  break;

            case 'R':
                  rest = parse_rl_rate(&rl_sub_rate, "RL_SUB", optarg);
                  rest = parse_rl_rate(&rl_anon_rate, "RL_ANON", rest);
                  rest = parse_rl_rate(&rl_all_anon_rate, "RL_ALL_ANON",
                                   rest);
                  rest = parse_rl_rate(&rl_bugs_rate, "RL_BUGS", rest);
                  break;

            default:
                  usage();
            }
      }
      argc -= optind;
      argv += optind;
      if (argc != 0)
            usage();

      if (my_srvr_id == DCC_ID_INVALID) {
            if (print_version)
                  exit(EX_OK);
            dcc_logbad(EX_USAGE, "server-ID not set with -i");
      }

      if (db_mode & DB_OPEN_NO_MMAP)
            add_dbclean_arg("-F");

      if (grey_on) {
            anon_off = 1;
            dccd_tracemask |= DCC_TRACE_IDS_BIT;
            flods_ck_secs = GREY_FLODS_CK_SECS;
      }

      if (addr_str) {
            add_dbclean_arg("-a");
            add_dbclean_arg(addr_str);
      }

      dcc_clnt_unthread_init();

      if (!dcc_cdhome(dcc_emsg, homedir))
            dcc_logbad(dcc_ex_code, "%s", dcc_emsg);

      i = load_ids(dcc_emsg, 0, my_srvr_id);
      if (i < 0)
            dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
      else if (!i)
            dcc_error_msg("%s", dcc_emsg);

      if (!def_port)
            def_port = DCC_GREY2PORT(grey_on);
      if (!srvr_socs)
            get_ifs(1, def_port);
      /*  fall back to INADDR_ANY if getifaddrs() found nothing */
      if (!srvr_socs)
            add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_LISTEN, 0, 0, def_port);

      /* make initial attempt to open the server UDP sockets */
      srvr_rcvbuf = 1024*1024;
      bind_retries = 45;
      open_srvr_socs(&bind_retries);

      add_dbclean_arg(use_ipv6 == 0 ? "-4" : "-6");

      if (background && daemon(1, 0) < 0)
            dcc_logbad(EX_OSERR, "daemon(): %s", ERROR_STR());

      if (!background)
            signal(SIGHUP, sigterm);    /* SIGHUP fatal during debugging */
      else
            signal(SIGHUP, sighup); /* speed check of configuration */
      signal(SIGTERM, sigterm);
      signal(SIGINT, sigterm);
      signal(SIGPIPE, SIG_IGN);

      atexit(stop_children);

      gettimeofday(&db_time, 0);
      wake_time = db_time;

      /* open the database, and try once to fix it */
      if (!dccd_db_open(DB_OPEN_LOCK_NOWAIT)) {
            dcc_error_msg("%s", dcc_emsg);
            run_dbclean(1, "database broken");
            ck_dbclean(0);          /* stall until it ends */
            if (!dccd_db_open(DB_OPEN_LOCK_NOWAIT)) {
                  dcc_error_msg("%s", dcc_emsg);
                  dcc_logbad(EX_NOINPUT,
                           "could not start database %s", db_nm);
            }
      }

      boot_ok_time = db_time.tv_sec + LAST_ERROR_ACT_SECS;
      host_id_next = db_time.tv_sec + LAST_ERROR_ACT_SECS;
      flods_init();
      stats_clear();
      if (flod_mmaps != 0
          && flod_mmaps->dccd_stats.reset.tv_sec != 0) {
            memcpy(&dccd_stats, &flod_mmaps->dccd_stats,
                   sizeof(dccd_stats));
      }
      dcc_trace_msg(DCC_VERSION" listening to port %d with %s and %s",
                  ntohs(srvr_socs->arg_port), dcc_homedir, db_window_size);

      recv_job();
}



static SRVR_SOC *             /* 0 or new entry */
add_srvr_soc(u_char flags,
           int family,        /* 0 if addrp==0 */
           const void *addrp,       /* 0, *in_addr, or *in6_addr */
           u_int16_t port)
{
      DCC_SOCKU su;
      SRVR_SOC *sp;

      dcc_mk_su(&su, family, addrp, port);
      for (sp = srvr_socs; sp; sp = sp->fwd) {
            if (!memcmp(&su, &sp->su, sizeof(su))) {
                  if (!(sp->flags & SRVR_SOC_ADDR))
                        sp->flags = flags;
                  return 0;
            }
      }

      sp = dcc_malloc(sizeof(*sp));
      memset(sp, 0, sizeof(*sp));
      sp->flags = flags;
      sp->udp = -1;
      sp->listen = -1;
      sp->su = su;
      sp->arg_family = family;
      if (addrp)
            memcpy(&sp->arg_addr, addrp,
                   ((family == AF_INET)
                  ? sizeof(sp->arg_addr.in4)
                  : sizeof(sp->arg_addr.in6)));
      sp->arg_port = port;

      *srvr_socs_end = sp;
      srvr_socs_end = &sp->fwd;

      return sp;
}



static void
open_srvr_socs(int *retry_secs)
{
      static u_char buf_set = 0;
      int num_socs = 0;
      SRVR_SOC *sp;

      for (sp = srvr_socs; sp; sp = sp->fwd) {
            if (sp->udp >= 0) {
                  ++num_socs;
                  continue;
            }

            /* resolve default if we finally know it */
            if (!sp->arg_port)
                  sp->arg_port = def_port;

            if (!sp->arg_family && use_ipv6 == 2) {
                  /* using INADDR_ANY and do not know if IPv6 works */
                  if (!dcc_udp_bind(dcc_emsg, &sp->udp,
                                dcc_mk_su(&sp->su, AF_INET6,
                                        0, sp->arg_port),
                                retry_secs)) {
                        dcc_error_msg("%s", dcc_emsg);
                        /* still don't know about IPv6 */
                        continue;
                  }
                  if (sp->udp >= 0) {
                        use_ipv6 = 1;
                        sp->arg_family = AF_INET6;
                        continue;
                  } else {
                        use_ipv6 = 0;
                  }
            }

            /* create the UDP sockets */
            if (!sp->arg_family)
                  sp->arg_family = use_ipv6 ? AF_INET6 : AF_INET;
            else if (use_ipv6 == 2 && sp->arg_family == AF_INET6)
                  use_ipv6 = 1;
            dcc_mk_su(&sp->su, sp->arg_family, &sp->arg_addr, sp->arg_port);
            if (!dcc_udp_bind(dcc_emsg, &sp->udp, &sp->su, retry_secs)
                || sp->udp < 0) {
                  dcc_error_msg("%s", dcc_emsg);
                  continue;
            }

            for (;;) {
                  if (!setsockopt(sp->udp, SOL_SOCKET, SO_RCVBUF,
                              &srvr_rcvbuf, sizeof(srvr_rcvbuf)))
                        break;
                  if (buf_set || srvr_rcvbuf <= 4096) {
                        dcc_error_msg("setsockopt(%s,SO_RCVBUF=%d): %s",
                                    dcc_su2str(&sp->su),
                                    srvr_rcvbuf,
                                    ERROR_STR());
                        break;
                  }
                  srvr_rcvbuf -= 4096;
            }
            buf_set = 1;

            ++num_socs;
      }

      /* Finally decide the IPv6 issue if we found no sign of IPv6 */
      if (use_ipv6 == 2)
            use_ipv6 = 0;

      if (!num_socs)
            dcc_logbad(EX_OSERR, "failed to open any server sockets");
}



/* get ready to bind to all local IP addreses */
#ifndef HAVE_GETIFADDRS
static u_char                       /* 1=added an interface */
get_ifs(u_char quiet UATTRIB,
      u_int16_t port)               /* network byte order */
{
      return 0 != add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_LISTEN, 0, 0, port);
}
#else /* defined(HAVE_GETIFADDRS) */
static u_char                       /* 1=added an interface */
get_ifs(u_char quiet,
      u_int16_t port)               /* network byte order */
{
      struct ifaddrs *ifap0, *ifap;
      const SRVR_SOC *sp = 0;
      u_char changed, flags;

      changed = 0;
      flags = SRVR_SOC_IF | SRVR_SOC_LISTEN;

      if (0 > getifaddrs(&ifap0)) {
            dcc_error_msg("getifaddrs(): %s", ERROR_STR());
            return 0 != add_srvr_soc(flags, 0, 0, port);
      }

      for (ifap = ifap0; ifap; ifap = ifap->ifa_next) {
            if (!(ifap->ifa_flags & IFF_UP))
                  continue;
            if (!ifap->ifa_addr)
                  continue;
            switch (ifap->ifa_addr->sa_family) {
            case AF_INET:
                  sp = add_srvr_soc(flags, ifap->ifa_addr->sa_family,
                                &((struct sockaddr_in *)ifap->ifa_addr
                                  )->sin_addr,
                                port);
                  flags = SRVR_SOC_IF;
                  break;
            case AF_INET6:
                  if (use_ipv6 == 0)
                        continue;
                  if (use_ipv6 == 2)
                        use_ipv6 = 1;
                  sp = add_srvr_soc(flags, ifap->ifa_addr->sa_family,
                                &((struct sockaddr_in6*)ifap->ifa_addr
                                  )->sin6_addr,
                                port);
                  flags = SRVR_SOC_IF;
                  break;
            }
            if (sp) {
                  changed = 1;
                  if (!quiet)
                        dcc_trace_msg("start listening on %s",
                                    dcc_su2str(&sp->su));
                  sp = 0;
            }
      }

#ifdef HAVE_FREEIFADDRS
      /* since this is done only a few times, don't worry if we cannot release
       * the list of interfaces when HAVE_FREEIFADDRS is not defined */
      freeifaddrs(ifap0);
#endif

      return changed;
}



/* deal with changes to network interfaces */
static void
get_if_changes(void)
{
#ifdef HAVE_FREEIFADDRS
      /* only if we know how to release the result of getifaddrs() */
      SRVR_SOC *sp, **spp;
      u_char changed;

      for (sp = srvr_socs; sp; sp = sp->fwd) {
            if (sp->flags & SRVR_SOC_IF)
                  sp->flags |= SRVR_SOC_MARK;
      }

      /* reconnect to all network interfaces using the same port
       * numbers as the first time */
      changed = 0;
      for (sp = srvr_socs; sp; sp = sp->fwd) {
            if (!(sp->flags & SRVR_SOC_IF))
                  continue;
            if (!(sp->flags & SRVR_SOC_LISTEN))
                  continue;
            if (get_ifs(0, sp->arg_port))
                  changed = 1;
      }

      spp = &srvr_socs;
      while ((sp = *spp) != 0) {
            if (!(sp->flags & SRVR_SOC_MARK)) {
                  spp = &sp->fwd;
                  continue;
            }

            dcc_trace_msg("stop listening on %s", dcc_su2str(&sp->su));
            changed = 1;
            if (srvr_socs_end == &sp->fwd)
                  srvr_socs_end = spp;
            *spp = sp->fwd;
            if (sp->udp >= 0)
                  close(sp->udp);
            if (sp->listen >= 0)
                  close(sp->listen);
            memset(sp, 0x55, sizeof(*sp));
            dcc_free(sp);
      }

      if (changed) {
            /*  fall back to INADDR_ANY if getifaddrs() found nothing */
            if (!srvr_socs) {
                  add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_LISTEN,
                             0, 0, def_port);
                  dcc_trace_msg("start listening on %s",
                              dcc_su2str(&srvr_socs->su));
            }
            flods_restart("network interfaces changed");
            open_srvr_socs(0);
      }
#endif /* HAVE_FREEIFADDRS */
}
#endif /* HAVE_GETIFADDRS */



static void
parse_thold(const char *arg)
{
      DCC_TGTS tgts;
      char *duparg, *cnt;
      DCC_CK_TYPES type;

      duparg = dcc_strdup(arg);
      cnt = strchr(duparg, ',');
      if (!cnt) {
            dcc_error_msg("missing comma in \"-t %s\"",
                        arg);
            dcc_free(duparg);
            return;
      }
      *cnt++ = '\0';
      type = dcc_str2type(duparg);
      tgts = dcc_str2cnt(cnt);
      dcc_free(duparg);
      if (tgts == 0 || tgts > DCC_TGTS_TOO_MANY) {
            dcc_error_msg("unrecognized count in \"-t %s\"",
                        arg);
            return;
      }
      /* silently limit the flooding threshold */
      if (tgts < BULK_THRESHOLD/2)
            tgts = BULK_THRESHOLD/2;

      if (type != DCC_CK_INVALID) {
            oflod_thold[type] = tgts;
            return;
      }
      if (arg[0] == ',') {
            for (type = 0;
                 type < DIM(oflod_thold);
                 type++) {
                  if (DCC_CK_IS_BODY(type))
                        oflod_thold[type] = tgts;
            }
            return;
      }
      dcc_error_msg("bad checksum type in \"-t %s\"", arg);
}



static const char *
parse_rl_rate(RL_RATE *rate, const char *label, const char *arg)
{
      char *p;
      int sec, hi;

      if (*arg == '\0')
            return arg;

      if (*arg == ',')
            return ++arg;

      sec = strtod(arg, &p) * RL_SCALE;
      hi = sec*RL_AVG_SECS;
      if ((*p != '\0' && *p != ',')
          || hi < RL_SCALE || sec > RL_MAX_CREDITS) {
            dcc_error_msg("invalid %s value in \"%s\"",
                        label, arg);
            return "";
      }

      rate->sec = sec;
      rate->hi = hi;
      rate->lo = -hi;

      return (*p == ',') ? p+1 : p;
}



static void
add_dbclean_arg(const char *arg)
{
      int i;

      if (dbclean_argc >= DIM(dbclean_argv)-2)
            dcc_logbad(EX_SOFTWARE, "too many args for dbclean");
      dbclean_argv[dbclean_argc++] = arg;
      i = snprintf(dbclean_arg_str+dbclean_arg_len,
                 sizeof(dbclean_arg_str)-dbclean_arg_len,
                 " %s", arg);
      dbclean_arg_len += i;
      if (dbclean_arg_len >= ISZ(dbclean_arg_str)-2)
            dcc_logbad(EX_SOFTWARE, "too many args for dbclean");
}



/* check effort to repair database */
static void
ck_dbclean(int options)
{
      int status;
      pid_t pid;

      if (dbclean_pid < 0)
            return;

      pid = waitpid(dbclean_pid, &status, options);
      if (pid != dbclean_pid)
            return;

      dbclean_pid = -1;

      /* do not try failing dbclean too often */
#ifdef WEXITSTATUS
      status = WEXITSTATUS(status);
#else
      status &= 0xff;
#endif
      if (status == 0) {
            dbclean_limit_secs = DBCLEAN_LIMIT_SECS;
      } else {
            dbclean_limit_secs *= 2;
            if (dbclean_limit_secs > DEL_DBCLEAN_SECS)
                  dbclean_limit_secs = DEL_DBCLEAN_SECS;
      }

      /* don't restart dbclean until after it has stopped running
       * and cooled for a while */
      dbclean_limit = db_time.tv_sec + dbclean_limit_secs;
}



/* try to repair the database */
static void
run_dbclean(u_char S_mode, const char *reason)
{
      if (*dbclean == '\0')
            return;

      ck_dbclean(0);                /* wait until previous ends */

      dbclean_pid = fork();
      if (dbclean_pid < 0) {
            dcc_error_msg("dbclean fork(): %s", ERROR_STR());
      } else if (dbclean_pid == 0) {
            strcpy(dbclean_mode, "-DPq");
            if (db_failed)
                  strcat(dbclean_mode, "R");
            if (S_mode)
                  strcat(dbclean_mode, "S");
            dcc_trace_msg("%s; starting `%s %s%s`",
                        reason,
                        dbclean_argv[0], dbclean_argv[1],
                        dbclean_arg_str);
            execv(dbclean, (char **)dbclean_argv);
            dcc_error_msg("execv(%s %s): %s",
                        dbclean, dbclean_arg_str, ERROR_STR());
            exit(-1);
      }

      need_del_dbclean = 0;
      dbclean_limit = db_time.tv_sec + dbclean_limit_secs;
}



/* check for changes or other interesting events when the flood timer expires */
static time_t                       /* seconds to wait */
ck_changes(void)
{
      static time_t misc_timer;
      time_t secs;
      DB_HADDR hash_free;
      const char *reason;
      char reason_buf[100];

      /* check nothing if it is not yet time */
      secs = next_flods_ck - db_time.tv_sec;
      if (secs > 0 && secs <= flods_ck_secs)
            return secs;
      next_flods_ck = db_time.tv_sec + flods_ck_secs;

      /* do not make some checks too often */
      if (DB_IS_TIME(misc_timer, MISC_CK_SECS)) {
            if (!db_sync_some(dcc_emsg))
                  db_broken(dcc_emsg);

            switch (check_load_ids(dcc_emsg, my_srvr_id)) {
            case 0:
                  dcc_error_msg("check/reload: %s", dcc_emsg);
                  break;
            case 1:
                  dcc_trace_msg("reloaded %s", DCC_NM2PATH(ids_nm));
                  flods_restart("restart flooding with new IDs");
                  break;
            }

#ifdef HAVE_GETIFADDRS
            get_if_changes();
#endif

            cycle_q_delay();

            check_blacklist_file(0);

            /* sound a claim to our server-ID if the database is locked */
            if (DB_IS_TIME(host_id_next, DCC_SRVR_ID_SEC)
                && DB_IS_LOCKED()) {
                  host_id_last = db_time.tv_sec;
                  host_id_next = host_id_last + DCC_SRVR_ID_SEC;
                  dcc_timeval2ts(host_id_rcd.ts, &db_time, 0);

                  host_id_rcd.srvr_id_auth = my_srvr_id;
                  DB_TGTS_RCD_SET(&host_id_rcd, 1);
                  host_id_rcd.fgs_num_cks = 1;
                  host_id_rcd.cks[0].type_fgs = DCC_CK_SRVR_ID;
                  if (!db_add_rcd(dcc_emsg, &host_id_rcd))
                        db_broken("adding server-ID claim: %s",
                                dcc_emsg);
            }

            gettimeofday(&db_time, 0);
            misc_timer = db_time.tv_sec + MISC_CK_SECS;
      }

      /* note when previous hash expansion finishes and collect a zombie */
      ck_dbclean(WNOHANG);

      if (db_failed) {
            reason = "database broken";
      } else {
            if (need_del_dbclean != 0
                && DB_IS_TIME(del_dbclean_next, DEL_DBCLEAN_SECS)) {
                  reason = need_del_dbclean;
            } else if ((hash_free = db_hash_len - db_hash_used)
                     < MIN_HASH_ENTRIES
                     || hash_free < db_hash_len/20) {
                  /* try to expand the hash table when there are only
                   * a few free slots
                   * or the load factor rises above .95 */
                  if (hash_free < MIN_HASH_ENTRIES)
                        snprintf(reason_buf, sizeof(reason_buf),
                               "%d free hash entries",
                               hash_free);
                  else
                        snprintf(reason_buf, sizeof(reason_buf),
                               "%d free hash entries among %d total",
                               hash_free, db_hash_len);
                  reason = reason_buf;

            } else if (DB_IS_TIME(dbclean_cron_sub, 3*24*60*60)) {
                  /* clean the database again after being forced so a
                   * missing cron job does not let it bloat */
                  reason = "work around broken cron job";
            } else {
                  reason = 0;
            }
      }

      if (reason) {
            if (!DB_IS_TIME(dbclean_limit, dbclean_limit_secs)) {
                  if (next_flods_ck > dbclean_limit)
                        next_flods_ck = dbclean_limit;
            } else if (dbclean_pid < 0) {
                  run_dbclean(0, reason);
                  next_flods_ck = db_time.tv_sec + 1;
            } else {
                  next_flods_ck = db_time.tv_sec + 1;
            }
      } else {
            flods_ck(0);
      }

      secs = next_flods_ck - db_time.tv_sec;
      return secs >= 0 ? secs : 0;
}



static void NRATTRIB
recv_job(void)
{
      fd_set rfds, *prfds, wfds, *pwfds;
#     define PFD_SET(_fd,_fds) {p##_fds = &_fds; FD_SET(_fd,p##_fds);}
      int max_fd, nfds;
      IFLOD_INFO *ifp;
      OFLOD_INFO *ofp;
      struct timeval delay;
      time_t secs;
      u_char was_too_busy, db_has_failed;
      SRVR_SOC *sp;
      QUEUE *q;
      int fd, i;

      was_too_busy = 1;
      db_has_failed = 0;
      for (;;) {
            if (stopint != 0) {
                  if (flods_ck_secs > SHUTDOWN_DELAY)
                        flods_ck_secs = SHUTDOWN_DELAY;
                  if (flods_off < 100000) {
                        flods_off = 100000;
                        iflods_stop("server stopping", 0);
                        oflods_stop(0);
                        db_unlock(0);
                        db_unload(0);
                  }
                  if (iflods.active == 0 && oflods.active == 0) {
                        if (stopint < 0)
                              db_quit(0, "gracefully stopping");
                        db_quit(stopint | 128,
                              "exiting with signal %d", stopint);
                  }
            }
            if (db_has_failed != db_failed) {
                  db_has_failed = db_failed;
                  if (db_failed) {
                        db_unlock(0);
                        db_unload(0);
                        ++flod_db_sick;
                        ++flods_off;
                        oflods_stop(1);
                        iflods_stop("database corrupt", 1);
                  }
            }

            FD_ZERO(&rfds);
            prfds = 0;
            FD_ZERO(&wfds);
            pwfds = 0;
            max_fd = 0;
            if (was_too_busy) {
                  delay.tv_sec = 1;
                  delay.tv_usec = 0;
            } else {
                  for (sp = srvr_socs; sp; sp = sp->fwd) {
                        fd = sp->udp;
                        if (fd < 0)
                              continue;
                        PFD_SET(fd, rfds);
                        if (max_fd <= fd)
                              max_fd = fd;

                        /* accept new incoming flood connections
                         * if flooding is on
                         * and we don't already have too many */
                        fd = sp->listen;
                        if (fd >= 0
                            && iflods.active < DCCD_MAX_FLOODS) {
                              PFD_SET(fd, rfds);
                              if (max_fd < fd)
                                  max_fd = fd;
                        }
                  }
                  /* pay attention to currently flooding reports */
                  for (ifp = iflods.infos, i = 0;
                       i < iflods.active;
                       ++ifp) {
                        if (ifp->s < 0)
                              continue;
                        ++i;
                        if (ifp->flags & IFLOD_FG_CONNECTED) {
                              PFD_SET(ifp->s, rfds);
                        } else {
                              PFD_SET(ifp->s, wfds);
                        }
                        if (max_fd < ifp->s)
                              max_fd = ifp->s;
                  }
                  for (ofp = oflods.infos, i = 0;
                       i < oflods.active;
                       ++ofp) {
                        if (ofp->s < 0)
                              continue;
                        ++i;
                        if (ofp->flags & OFLOD_FG_CONNECTED) {
                              PFD_SET(ofp->s, rfds);
                              if (ofp->obuf_len != 0
                                  || (ofp->flags
                                    & OFLOD_FG_TOO_BUSY))
                                  PFD_SET(ofp->s, wfds);
                        } else {
                              PFD_SET(ofp->s, wfds);
                        }
                        if (max_fd < ofp->s)
                              max_fd = ofp->s;
                  }
            }
            /* delay only until it is time to answer the oldest
             * anonymous request */
            nfds = select(max_fd+1, prfds, pwfds, 0, &delay);
            if (nfds < 0) {
                  if (errno != EINTR)
                        dcc_logbad(EX_OSERR, "select():%s",
                                 ERROR_STR());
                  /* ignore EINTR but recompute timers */
                  FD_ZERO(&rfds);
                  FD_ZERO(&wfds);
            }
            gettimeofday(&db_time, 0);
            wake_time = db_time;

            for (sp = srvr_socs; sp; sp = sp->fwd) {
                  /* queue a new anonymous request
                   * or answer a new authenticated request */
                  fd = sp->udp;
                  if (fd >= 0 && FD_ISSET(fd, &rfds)) {
                        --nfds;
                        while (new_job(sp))
                              continue;
                  }

                  /* start a new incoming flood stream */
                  fd = sp->listen;
                  if (fd >= 0 && FD_ISSET(fd, &rfds)) {
                        --nfds;
                        iflod_start(sp);
                  }
            }

            /* accept new flood data or start new SOCKS floods */
            for (ifp = iflods.infos, i = 0;
                 nfds > 0 && i < iflods.active;
                 ++ifp) {
                  if (ifp->s < 0)
                        continue;
                  ++i;
                  if (FD_ISSET(ifp->s, &rfds)
                      || FD_ISSET(ifp->s, &wfds)) {
                        --nfds;
                        iflod_read(ifp);
                  }
            }

            /* pump output flood data and receive confirmations */
            for (ofp = oflods.infos, i = 0;
                 nfds > 0 && i < oflods.active;
                 ++ofp) {
                  if (ofp->s < 0)
                        continue;
                  ++i;
                  if (FD_ISSET(ofp->s, &rfds)) {
                        --nfds;
                        oflod_read(ofp);
                        if (ofp->s < 0)
                              continue;
                  }
                  if (FD_ISSET(ofp->s, &wfds)) {
                        --nfds;
                        oflod_write(ofp);
                  }
            }

            /* process anonymous jobs when their times arrive */
            for (;;) {
                  q = queue_anon_head;
                  if (!q) {
                        delay.tv_sec = flods_ck_secs;
                        delay.tv_usec = 0;
                        break;
                  }

                  /* decide whether this job's time has come
                   * while defending against time jumps */
                  delay.tv_sec = q->answer.tv_sec - db_time.tv_sec;
                  delay.tv_usec = q->answer.tv_usec - db_time.tv_usec;
                  if (delay.tv_usec < 0) {
                        --delay.tv_sec;
                        delay.tv_usec += DCC_USECS;
                  }
                  if ((delay.tv_sec > 0
                       && delay.tv_sec < DCC_MAX_RTT_SEC)
                      || (delay.tv_sec == 0
                        && delay.tv_usec >= 1000))
                        break;  /* not yet time for next job */

                  queue_anon_head = q->later;
                  if (queue_anon_head)
                        queue_anon_head->earlier = 0;
                  --queue_anon_cur;
                  do_work(q);
                  free_q(q);
            }

            if (!was_too_busy) {
                  /* check configuration changes etc. */
                  secs = ck_changes();
                  if (delay.tv_sec >= secs) {
                        delay.tv_sec = secs;
                        delay.tv_usec = 0;
                  }
            }

            if (too_busy) {
                  too_busy = 0;
                  was_too_busy = 1;
            } else {
                  was_too_busy = 0;
            }
      }
}



static void
add_queue(QUEUE *q)
{
      QUEUE *qnext, **qp;

      TMSG3(QUERY, "received %s from %d at %s",
            dcc_req_op2str(&q->pkt.a),
            (DCC_CLNT_ID)ntohl(q->pkt.hdr.sender), Q_CIP(q));
      if (!ck_clnt_id(q)) {
            free_q(q);
            return;
      }

      /* immediately process requests from authenticated clients */
      if (q->delay_us == 0) {
            do_work(q);
            free_q(q);
            return;
      }

      /* don't let the queue of anonymous clients get too large */
      if (queue_anon_cur >= queue_max) {
            anon_msg("drop excess queued %s request from %s",
                   dcc_req_op2str(&q->pkt.a), Q_CIP(q));
            free_q(q);
            return;
      }
      if (anon_off && q->clnt_id == DCC_ID_ANON) {
            anon_msg("drop anomymous %s request from %s",
                   dcc_req_op2str(&q->pkt.a), Q_CIP(q));
            free_q(q);
            return;
      }

      if (q->delay_us >= DCC_MAX_RTT) {
            TMSG1(BL, "drop excessively delayed request from %s", Q_CIP(q));
            free_q(q);
            return;
      }
      q->answer.tv_usec += q->delay_us;
      q->answer.tv_sec += q->answer.tv_usec / DCC_USECS;
      q->answer.tv_usec %= DCC_USECS;


      /* add the new job to the queue */
      ++queue_anon_cur;
      qp = &queue_anon_head;
      for (;;) {
            qnext = *qp;
            if (!qnext) {
                  *qp = q;
                  break;
            }
            if (qnext->answer.tv_sec > q->answer.tv_sec
                || (qnext->answer.tv_sec == q->answer.tv_sec
                  && qnext->answer.tv_usec > q->answer.tv_usec)) {
                  q->later = qnext;
                  qnext->earlier = q;
                  *qp = q;
                  break;
            }
            q->earlier = qnext;
            qp = &qnext->later;
      }
}



/* get a new job in a datagram */
static u_char                       /* 1=call again */
new_job(SRVR_SOC *sp)
{
      QUEUE *q;
      static struct iovec iov = {0, sizeof(q->pkt)};
      static struct msghdr msg;
      int i, j;

      /* Find a free queue entry for the job.
       * Because we don't check for incoming jobs unless we think the
       * queue is not full, there must always be a free entry or
       * permission to make more entries. */
      q = queue_free;
      if (q) {
            queue_free = q->later;
      } else {
            i = 16;
            q = dcc_malloc(i * sizeof(*q));
            if (!q)
                  dcc_logbad(EX_UNAVAILABLE,
                           "malloc(%d queue entries) failed", i);
            queue_max_cur += i;
            /* put all but the last new queue entry on the free list */
            while (--i > 0) {
                  q->later = queue_free;
                  queue_free = q;
                  ++q;
            }
      }

      memset(q, 0, sizeof(*q));
      q->sp = sp;
      iov.iov_base = (char *)&q->pkt;
      msg.msg_name = (void *)&q->clnt_su;
      msg.msg_namelen = sizeof(q->clnt_su);
      msg.msg_iov = &iov;
      msg.msg_iovlen = 1;
      i = recvmsg(sp->udp, &msg, 0);
      if (i < 0) {
            /* ignore some results of ICMP unreachables for UDP
             * retransmissions seen on some platforms */
            if (DCC_BLOCK_ERROR()) {
                  ;
            } else if (DCC_CONNECT_ERRORS()) {
                  TMSG2(QUERY, "recvmsg(%s): %s",
                        dcc_su2str(&sp->su), ERROR_STR());
            } else {
                  dcc_error_msg("recvmsg(%s): %s",
                              dcc_su2str(&sp->su), ERROR_STR());
            }
            free_q(q);
            return 0;
      }
      if (q->clnt_su.sa.sa_family != sp->su.sa.sa_family) {
            dcc_logbad(EX_OSERR, "unexpected recvmsg address family %d",
                     q->clnt_su.sa.sa_family);
            free_q(q);
            return 1;
      }

      q->pkt_len = i;
      if (i < ISZ(DCC_HDR)) {
            if (ck_id(q, DCC_ID_ANON)) {
                  anon_msg("short packet of %d bytes from %s",
                         i, Q_CIP(q));
                  discard_error(q, "short packet of %d bytes", i);
            }
            free_q(q);
            return 1;
      }
      j = ntohs(q->pkt.hdr.len);
      if (j != i) {
            if (ck_id(q, DCC_ID_ANON)) {
                  anon_msg("header length %d instead of %d from %s",
                         j, i, Q_CIP(q));
                  discard_error(q, "header length %d instead of %d",
                              j, i);
            }
            free_q(q);
            return 1;
      }

      if ((q->pkt.hdr.pkt_vers > DCC_PKT_VERSION_MAX
           || q->pkt.hdr.pkt_vers < DCC_PKT_VERSION_MIN)
          && q->pkt.hdr.op != DCC_OP_NOP) {
            if (ck_id(q, DCC_ID_ANON)) {
                  anon_msg("unrecognized protocol version #%d"
                         " for %s from %s",
                         q->pkt.hdr.pkt_vers,
                         dcc_req_op2str(&q->pkt.a),
                         Q_CIP(q));
                  discard_error(q, "unrecognized protocol version #%d"
                              " for %s",
                              q->pkt.hdr.pkt_vers,
                              dcc_req_op2str(&q->pkt.a));
            }
            free_q(q);
            return 1;
      }

      q->answer = wake_time;

      switch ((DCC_OPS)q->pkt.hdr.op) {
      case DCC_OP_NOP:
            do_nop(q);
            return 1;

      case DCC_OP_REPORT:
            if (db_flags & DB_MAGIC_ST_GREY)
                  break;
            ++dccd_stats.reports;
            add_queue(q);
            return 1;

      case DCC_OP_QUERY:
            ++dccd_stats.queries;
            add_queue(q);
            return 1;

      case DCC_OP_ADMN:
            do_admn(q);
            free_q(q);
            return 1;

      case DCC_OP_DELETE:
            do_delete(q);
            free_q(q);
            return 1;

      case DCC_OP_GREY_REPORT:
      case DCC_OP_GREY_QUERY:
      case DCC_OP_GREY_WHITE:
            if (!(db_flags & DB_MAGIC_ST_GREY))
                  break;
            do_grey(q);
            free_q(q);
            return 1;

      case DCC_OP_GREY_SPAM:
            if (!(db_flags & DB_MAGIC_ST_GREY))
                  break;
            do_grey_spam(q);
            free_q(q);
            return 1;

      case DCC_OP_INVALID:
      case DCC_OP_QUERY_RESP:
      case DCC_OP_OK:
      case DCC_OP_ERROR:
            break;
      }

      anon_msg("invalid op %d from %s", q->pkt.hdr.op, Q_CIP(q));
      free_q(q);
      return 1;
}



void
free_q(QUEUE *q)
{
      if (q->rl)
            --q->rl->ref_cnt;
      q->later = queue_free;
      queue_free = q;
}



u_char
dccd_db_open(u_char lock_mode)
{
      DCC_CK_TYPES type;
      struct tm tm;
      struct timeval sn;
      int cur_hour, i, j;

      if (!db_open(dcc_emsg, 0, 0, lock_mode | db_mode))
            return 0;

      if (grey_on) {
            for (type = DCC_CK_TYPE_FIRST;
                 type <= DCC_CK_TYPE_LAST;
                 ++type) {
                  /* use dbclean values for server-ID declarations */
                  if (type == DCC_CK_SRVR_ID)
                        continue;
                  db_ex_secs[type].clean_thold = 1;
                  oflod_thold[type] = 1;
                  if (type == DCC_CK_GREY_TRIPLE
                      || type == DCC_CK_IP) {
                        db_ex_secs[type].all = grey_window;
                        db_ex_secs[type].spam = grey_white;
                  } else if (type == DCC_CK_BODY
                           || type == DCC_CK_GREY_MSG) {
                        db_ex_secs[type].all = grey_window;
                        db_ex_secs[type].spam = grey_window;
                  } else {
                        db_ex_secs[type].all = 1;
                        db_ex_secs[type].spam = 1;
                  }
            }

            /* silently impose our notion of which checksums to keep */
            new_nokeep_cks = def_nokeep_cks();
            if (grey_weak_ip)
                  DB_RESET_NOKEEP(new_nokeep_cks, DCC_CK_IP);
            db_nokeep_cks = new_nokeep_cks;

            summarize_delay_secs = grey_embargo - flods_ck_secs*2;
            if (summarize_delay_secs < 1)
                  summarize_delay_secs = 1;

      } else {
            /* impose our notion of which checksums to keep */
            db_nokeep_cks = new_nokeep_cks;

            summarize_delay_secs = MAX_SUMMARIZE_DELAY_SECS;
            for (i = 0; i < DIM(db_ex_secs); ++i) {
                  j = db_ex_secs[i].all;
                  if (j != 0
                      && j < summarize_delay_secs)
                        j = summarize_delay_secs;
            }
      }

      memcpy(db_flod_tholds, oflod_thold, sizeof(db_flod_tholds));

      /* Compute the quarter hour when the database was last cleaned
       * or 3 minutes past a random quarter hour beteen midnight and 06:00 */
      dcc_ts2timeval(&sn, db_sn);
      dcc_localtime(sn.tv_sec, &tm);
      if (!(db_flags & DB_MAGIC_ST_SELF_CLEAN)) {
            i = tm.tm_hour*60 + tm.tm_min + DBCLEAN_LIMIT_SECS;
      } else if (tm.tm_hour < 6) {
            i = tm.tm_hour*60 + tm.tm_min;
      } else {
            j = (u_int)((sn.tv_sec + db_time.tv_sec) * my_srvr_id) % (6*4);
            i = (j/4)*60 + (j%4)*15 + 3;
      }
      dcc_localtime(db_time.tv_sec, &tm);
      cur_hour = tm.tm_hour;
      tm.tm_hour = i / 60;
      tm.tm_min = i % 60;
      tm.tm_sec = 0;
      dbclean_cron_sub = mktime(&tm);

      /* If we instead of cron asked for the last cleaning, make a note
       * to clean the database during the graveyard shift.
       * Otherwise the database will gradually bloat when
       * the cron job is broken. */
      if (!(db_flags & DB_MAGIC_ST_SELF_CLEAN)) {
            dbclean_cron_sub += 2*24*60*60;
      } else if (!(db_flags & DB_MAGIC_ST_SELF_CLEAN2)) {
            dbclean_cron_sub += 24*60*60;
      } else if (cur_hour >= 6) {   /* wait until tomorrow if past 06:00 */
            dbclean_cron_sub += 24*60*60;
      }
      if (dbclean_cron_sub < sn.tv_sec + 24*60*60)
            dbclean_cron_sub += 24*60*60;

      return db_flush_magic(dcc_emsg);
}



void
close_srvr_socs(void)
{
      SRVR_SOC *sp;

      for (sp = srvr_socs; sp; sp = sp->fwd) {
            if (sp->udp >= 0) {
                  close(sp->udp);
                  sp->udp = -1;
            }
            if (sp->listen >= 0) {
                  close(sp->listen);
                  sp->listen = -1;
            }
      }
}



/* clean shut down */
static void NRATTRIB
db_quit(int exitcode, const char *p, ...)
{
      va_list args;

      va_start(args, p);
      if (exitcode)
            dcc_verror_msg(p, args);
      else
            dcc_vtrace_msg(p, args);
      va_end(args);

      /* db_close() can take a long time, so close some things early.
       * Do not call close_srvr_socs() but keep the UDP sockets open
       * to prevent another server from starting until we have flushed
       * our buffers to prevent problems on some UNIX systems that lack
       * inter-process coherent mmap(),
       * except on BSD/OS where close() takes forever. */
#ifdef __bsdi
      close_srvr_socs();
#endif
      stop_children();
      db_close(0, 1);

      if (exitcode)
            dcc_error_msg("stopped");
      else
            dcc_trace_msg("stopped");
      exit(exitcode);
}



/* watch for fatal signals */
static void
sigterm(int sig)
{
      stopint = sig;
      too_busy = 0;
      next_flods_ck = db_time.tv_sec;
      (void)signal(sig, SIG_DFL);   /* catch it only once */
}



/* SIGHUP hurries checking the configuration files */
static void
sighup(int sig UATTRIB)
{
      next_flods_ck = db_time.tv_sec;
}


static void
stop_children(void)
{
      if (resolve_hosts_pid > 0) {
            kill(resolve_hosts_pid, SIGKILL);
            resolve_hosts_pid = -1;
      }

      if (dbclean_pid > 0) {
            kill(dbclean_pid, SIGKILL);
            dbclean_pid = -1;
      }
}

Generated by  Doxygen 1.6.0   Back to index