Logo Search packages:      
Sourcecode: dcc version File versions

iflod.c

/* Distributed Checksum Clearinghouse
 *
 * deal with incoming floods of checksums
 *
 * Copyright (c) 2005 by Rhyolite Software, LLC
 *
 * This agreement is not applicable to any entity which sells anti-spam
 * solutions to others or provides an anti-spam solution as part of a
 * security solution sold to other entities, or to a private network
 * which employs the DCC or uses data provided by operation of the DCC
 * but does not provide corresponding data to other users.
 *
 * 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.
 *
 * Parties not eligible to receive a license under this agreement can
 * obtain a commercial license to use DCC and permission to use
 * U.S. Patent 6,330,590 by contacting Commtouch at http://www.commtouch.com/
 * or by email to nospam@commtouch.com.
 *
 * A commercial license would be for Distributed Checksum and Reputation
 * Clearinghouse software.  That software includes additional features.  This
 * free license for Distributed ChecksumClearinghouse Software does not in any
 * way grant permision to use Distributed Checksum and Reputation Clearinghouse
 * software
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
 * 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.3.42-1.178 $Revision$
 */

#include "dccd_defs.h"
#include <signal.h>
#include <sys/wait.h>


IFLODS iflods;

u_int complained_many_iflods;

time_t got_hosts;
pid_t resolve_hosts_pid = -1;

time_t boot_ok_time;                /* suppress flooding errors at first */

static u_char iflod_write(IFLOD_INFO *, void *, int, const char *, u_char);

static DCC_TS future_ts;
#define DCC_FUTURE_SECS     (4*60*60)     /* refuse reports this far in advance */



ID_MAP_RESULT
id_map(DCC_SRVR_ID srvr, const OFLOD_OPTS *opts)
{
      int i;
      ID_MAP_RESULT result;

      /* apply the ***first*** server-ID map that matches, if any */
      for (i = 0; i < opts->num_maps; ++i) {
            if (opts->srvr_map[i].from_lo <= srvr
                && opts->srvr_map[i].from_hi >= srvr) {
                  result = opts->srvr_map[i].result;
                  if (result == ID_MAP_SELF
                      && srvr == my_srvr_id)
                        return ID_MAP_NO;
                  return result;
            }
      }
      return ID_MAP_NO;
}



static const char *
rpt_id(const char *type, const DB_RCD* rcd,
       const IFLOD_INFO *ifp)
{
      static int bufno;
      static struct {
          char    str[90];
      } bufs[4];
      char *s;

      s = bufs[bufno].str;
      bufno = (bufno+1) % DIM(bufs);

      snprintf(s, sizeof(bufs[0].str),
             "%s%s %s ID=%d %s%s",
             type ? "flooded " : "",
             type ? type : "",
             dcc_ts2str_err(rcd->ts),
             DB_RCD_ID(rcd),
             ifp ? "from " : "",
             ifp ? ifp->hostname : "");
      return s;
}



void PATTRIB(5,6)
rpt_err(LAST_ERROR *ep, u_char bad, enum FLOD_ERR_OP new_op, int new_errno,
      const char *p, ...)
{
      va_list args;

      if (new_op == FLOD_ERR_SAME) {
          new_op = ep->op;
          new_errno = ep->old_errno;
      }

      if (!bad && (!DB_IS_TIME(ep->ok, LAST_ERROR_OK_SECS)
                 || !DB_IS_TIME(boot_ok_time, LAST_ERROR_ACT_SECS)
                 || (ep->op == new_op
                   && ep->old_errno == new_errno
                   && !DB_IS_TIME(ep->rep_report, LAST_ERROR_SECS))
                 || new_op == FLOD_ERR_LOCAL_OFF)) {
            if ((DCC_TRACE_FLOD_BIT & dccd_tracemask)
                && (ep->op != new_op
                  || ep->old_errno != new_errno)) {
                  va_start(args, p);
                  dcc_vtrace_msg(p, args);
                  va_end(args);
            }
            ep->op = new_op;
            ep->old_errno = new_errno;
            return;
      }

      va_start(args, p);
      dcc_verror_msg(p, args);
      va_end(args);

      ep->rep_report = db_time.tv_sec + LAST_ERROR_SECS;
      ep->op = new_op;
      ep->old_errno = new_errno;
}



u_char
set_flod_socket(int s, const char *hostname, const DCC_SOCKU *sup, u_char in)
{
#if IP_TOS
      static u_char tos_ok = 1;
#endif
      int on;

      if (0 > fcntl(s, F_SETFD, FD_CLOEXEC))
            dcc_error_msg("fcntl(flod %s, F_SETFD, FD_CLOEXEC): %s",
                        hostname, ERROR_STR());

      if (-1 == fcntl(s, F_SETFL,
                  fcntl(s, F_GETFL, 0) | O_NONBLOCK)) {
            dcc_error_msg("fcntl(flod %s, O_NONBLOCK): %s",
                        hostname, ERROR_STR());
            return 0;
      }

      on = 1;
      if (0 > setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
                     &on, sizeof(on)))
            dcc_error_msg("setsockopt(flod %s, SO_KEEPALIVE): %s",
                        hostname, ERROR_STR());

      if (in) {
            /* Ensure that we have enough socket buffer space to send
             * complaints about the input flood.  Normally little or
             * nothing is sent upstream, but bad clocks or other
             * problems can cause many complaints. */
            if (0 > setsockopt(s, SOL_SOCKET, SO_SNDBUF,
                           &srvr_rcvbuf, sizeof(srvr_rcvbuf)))
                  dcc_error_msg("setsockopt(%s, SO_SNDBUF): %s",
                              hostname, ERROR_STR());

            /* Use a small window for flooding to reduce stalling of
             * flood shutdowns waiting for the database.
             * A 6 KByte window with 1500 byte MSS is enough to move
             * a GByte/day over an RTT of 0.5 seconds.
             * Application layer metering could be done, but
             * because flooded reports are all about the same size,
             * it would act about the same and require much of the
             * machinery that is already in TCP. */
            on = FLOD_BUF_SIZE*3;
            if (0 > setsockopt(s, SOL_SOCKET, SO_RCVBUF,
                           &on, sizeof(on)))
                  dcc_error_msg("setsockopt(%s, SO_RCVBUF): %s",
                              hostname, ERROR_STR());

      } else {
            on = FLOD_BUF_SIZE*3;
            if (0 > setsockopt(s, SOL_SOCKET, SO_SNDBUF,
                           &on, sizeof(on)))
                  dcc_error_msg("setsockopt(%s, SO_SNDBUF): %s",
                              hostname, ERROR_STR());
      }

#ifdef IP_TOS
      /* It would be nice and clean to use netinet/ip.h for the definition
       * of IPTOS_THROUGHPUT.  However, it is hard to use netinet/ip.h
       * portably because in_sysm.h is required for n_long on some
       * systems and not others.  A bunch of messy ./configure fiddling
       * might patch that hassle, but the bit really ought to be the same
       * as the old 0x08 in the IPv4 header. */
      if (sup->sa.sa_family == AF_INET
          && tos_ok) {
            on = 0x08;        /* IPTOS_THROUGHPUT */
            if (0 > setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(on))) {
                  dcc_error_msg("setsockopt(IP_TOS,"
                              " IPTOS_THROUGHPUT, %s): %s",
                              hostname, ERROR_STR());
                  tos_ok = 0;
            }
      }
#endif

      return 1;
}



/* see if the host name resolution process is still running */
u_char                              /* 1=not running 0=please wait */
flod_names_resolve_ck(void)
{
      pid_t pid;
      int status;

      if (resolve_hosts_pid < 0)
            return 1;

      pid = waitpid(resolve_hosts_pid, &status, WNOHANG);
      if (pid == resolve_hosts_pid) {
            resolve_hosts_pid = -1;
            return 1;
      }

      /* check again soon */
      if (next_flods_ck > db_time.tv_sec + 1)
            next_flods_ck = db_time.tv_sec + 1;
      return 0;
}



static void
flod_names_resolve(void)
{
      FLOD_MMAP *mp;
      u_char ipv6, ok;
      const DCC_SOCKU *sup;

      for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp) {
            if (mp->rem_hostname[0] == '\0'
                || (mp->flags & FLOD_MMAP_FG_PASSIVE))
                  continue;
            ipv6 = ((mp->flags & FLOD_MMAP_FG_IPv4) ? 0
                  : (mp->flags & FLOD_MMAP_FG_IPv6) ? 1
                  : use_ipv6 ? 2 : 0);
            dcc_host_lock();
            if (mp->flags & FLOD_MMAP_FG_SOCKS)
                  ok = dcc_get_host_SOCKS(mp->rem_hostname, ipv6,
                                    &mp->host_error);
            else
                  ok = dcc_get_host(mp->rem_hostname, ipv6,
                                &mp->host_error);
            if (!ok) {
                  TMSG2(FLOD, "failed to resolve %s: %s",
                        mp->rem_hostname, DCC_HSTRERROR(mp->host_error));
            } else {
                  for (sup = dcc_hostaddrs;
                       sup < dcc_hostaddrs_end;
                       ++sup) {
                        if ((ipv6 == 0
                             && sup->sa.sa_family != AF_INET)
                            || (ipv6 == 1
                              && sup->sa.sa_family != AF_INET6))
                              continue;
                        mp->rem_su = *sup;
                        *DCC_SU_PORT(&mp->rem_su) = mp->rem_port;
                  }
            }
            dcc_host_unlock();
      }
}



/* start a process to wait for the domain name system or other
 * hostname system to get the IP addresses of our flooding peers */
u_char                              /* 1=finished 0=please wait */
flod_names_resolve_start(void)
{
      IFLOD_INFO *ifp;
      OFLOD_INFO *ofp;
      FLOD_MMAP *mp;

      if (!flod_mmaps
          || !flod_names_resolve_ck())
            return 0;

      /* we're finished if we have recent address for all of the names */
      if (!DB_IS_TIME(got_hosts, FLOD_NAMES_RESOLVE_SECS))
            return 1;
      got_hosts = db_time.tv_sec + FLOD_NAMES_RESOLVE_SECS;

      if (!background) {
            TMSG(FLOD, "resolving hostnames in the foreground");
            flod_names_resolve();
            return 1;
      }

      for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp) {
            mp->rem_su.sa.sa_family = AF_UNSPEC;
            mp->host_error = 0;
      }
      flod_mmap_sync(0, 1);

      resolve_hosts_pid = fork();
      if (resolve_hosts_pid > 0) {
            /* check again soon */
            if (next_flods_ck > db_time.tv_sec + 1)
                  next_flods_ck = db_time.tv_sec + 1;
            return 0;
      }

      if (resolve_hosts_pid == -1) {
            dcc_error_msg("fork(flod_names_resolve_start): %s",
                        ERROR_STR());
      } else {
            TMSG(FLOD, "resolving hostnames started");

            /* close files and sockets to avoid interfering with parent */
            db_close(-1);
            close_srvr_socs();
            for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
                  if (ifp->s >= 0)
                        close(ifp->s);
            }
            for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
                  if (ofp->s >= 0)
                        close(ofp->s);
            }

            flod_names_resolve();

            TMSG(FLOD, "resolving hostnames finished");

            exit(0);
      }
      return 1;
}



static void
iflod_clear(IFLOD_INFO *ifp, u_char fail)
{
      OFLOD_INFO *ofp;

      if (fail && (ofp = ifp->ofp) != 0
          && (ofp->o_opts.flags & FLOD_OPT_SOCKS)) {
            if (ofp->in_try_secs == 0)
                  ofp->in_try_secs = FLOD_RETRY_SECS;
            ofp->in_try_again = db_time.tv_sec + ofp->in_try_secs;
            TMSG2(FLOD, "postpone restarting flood from %s for %d seconds",
                  ofp->rem_hostname, ofp->in_try_secs);
      }

      if (ifp->s >= 0)
            --iflods.active;
      memset(ifp, 0, sizeof(*ifp));
      ifp->s = -1;

      if (iflods.active == 0
          && oflods.active == 0
          && flods_st != FLODS_ST_ON)
            oflods_unmap();
}



void PATTRIB(5,6)
iflod_close(IFLOD_INFO *ifp, u_char fail,
          enum FLOD_ERR_OP err_op, int new_errno,
          const char *pat, ...)
{
      struct {
          DCC_FLOD_POS    last_pos;
          DCC_FLOD_POS    end;
          char        str[DCC_FLOD_MAX_RESP];
          char        null;
      } resp;
      char opstr_buf[80];
      char sustr[DCC_SU2STR_SIZE+1];
      va_list args;
      OFLOD_INFO *ofp;
      struct linger nowait;
      void *wp;
      int wlen;
      const char *opstr;
      u_char is_ok;

      ofp = ifp->ofp;

      memset(&resp, 0, sizeof(resp));
      db_ptr2flod_pos(resp.end, DCC_FLOD_POS_END);
      if (!pat)
            pat = "";
      va_start(args, pat);
      wlen = vsnprintf(resp.str, sizeof(resp.str), pat, args);
      va_end(args);

      /* notice if we think it went ok */
      is_ok = !memcmp(resp.str, DCC_FLOD_OK_STR, STRZ(DCC_FLOD_OK_STR));

      /* add our counts to the string we will send the peer
       * if there is room in our buffer
       * and if it is a recognized peer for which we have counts
       * and if some progress was made */
      if (ISZ(resp.str) - wlen > 30
          && ofp != 0
          && (is_ok || ofp->cnts.total != 0)) {
            while (wlen > 0
                   && (resp.str[wlen-1] == ':'
                     || resp.str[wlen-1] == ' '))
                  --wlen;
            wlen += snprintf(&resp.str[wlen], ISZ(resp.str)-wlen+1,
                         ": %d received  %d accepted"
                         "  %d dup  %d stale"
                         "  %d bad whitelist  %d not deleted",
                         ofp->cnts.total, ofp->cnts.accepted,
                         ofp->cnts.dup.val, ofp->cnts.stale.val,
                         ofp->cnts.ok2.val, ofp->cnts.not_deleted.val);
      }
      if (wlen > ISZ(resp.str))
            wlen = ISZ(resp.str);

      /* if useful, prefix our final message with our final position */
      if (memcmp(ifp->pos, ifp->pos_sent, ISZ(ifp->pos))) {
            memcpy(resp.last_pos, ifp->pos, ISZ(resp.last_pos));
            wp = &resp.last_pos;
            wlen +=  ISZ(resp.end) + ISZ(resp.last_pos);
      } else {
            wp = &resp.end;
            wlen += ISZ(resp.end);
      }

      if (!is_ok && err_op == FLOD_ERR_NO_LINK)
            err_op = FLOD_ERR_PEER_FAIL;
      opstr = flod_stats_str(0, new_errno, err_op);
      dcc_su2str(sustr+1, sizeof(sustr)-1, &ifp->su);
      if (strcmp(ifp->hostname, sustr+1))
            sustr[0] = ' ';
      else
            sustr[0] = '\0';
      snprintf(opstr_buf, sizeof(opstr_buf), "iflod close status%s%s%s",
             fail ? " failed" : "",
             (fail && *opstr) ? ", " : *opstr ? " " : "",
             opstr);
      if (ofp && ofp->mp) {
            rpt_err(&ofp->mp->i_err, fail, err_op, new_errno,
                  "%s to %s%s: %s",
                  opstr_buf, ifp->hostname, sustr, resp.str);
      } else if (fail) {
            dcc_error_msg("%s to %s%s: %s",
                        opstr_buf, ifp->hostname, sustr, resp.str);
      } else {
            TMSG4(FLOD, "%s to %s%s: %s",
                  opstr_buf, ifp->hostname, sustr, resp.str);
      }

      /* send the final status report to the sending flooder */
      if (!iflod_write(ifp, wp, wlen, "iflod_close()",
                   fail ? 2 : 1))
            fail = 1;

      if (ifp->s >= 0) {
            if (stopint
                && !(ifp->flags & IFLOD_FG_FAST_LINGER)) {
                  ifp->flags |= IFLOD_FG_FAST_LINGER;
                  nowait.l_onoff = 1;
                  nowait.l_linger = SHUTDOWN_DELAY;
                  if (0 > setsockopt(ifp->s, SOL_SOCKET, SO_LINGER,
                                 &nowait, sizeof(nowait))
                      && !fail)
                        dcc_error_msg("setsockopt(SO_LINGER %s): %s",
                                    ofp->rem_hostname, ERROR_STR());
            }

            if (0 > close(ifp->s)
                && !fail)
                  dcc_error_msg("close(iflod %s): %s",
                              ifp->hostname, ERROR_STR());
      }

      if (ofp != 0 && ofp->ifp == ifp) {
            save_flod_cnts(ofp);
            ofp->ifp = 0;
      }

      iflod_clear(ifp, fail);
}



static u_char
iflod_write(IFLOD_INFO *ifp,
          void *buf, int buf_len,
          const char *type,         /* string describing operation */
          u_char close_it)          /* 0=iflod_close() on error, */
{                             /* 1=complain, 2=ignore error */
      int i;

      if (!(ifp->flags & IFLOD_FG_CONNECTED))
            return 1;

      /* if we don't know the corresponding output stream because we have
       * not yet seen any authentication, we at least know the connection
       * did not involve SOCKS because we did not originate it. */
      if (ifp->ofp
          && (ifp->ofp->o_opts.flags & FLOD_OPT_SOCKS))
            i = Rsend(ifp->s, buf, buf_len, 0);
      else
            i = send(ifp->s, buf, buf_len, 0);
      if (i == buf_len)
            return 1;

      if (i < 0) {
            if (close_it == 0) {
                  iflod_close(ifp, 1, FLOD_ERR_IO, errno,
                            "send(%s %s): %s",
                            type, ifp->hostname, ERROR_STR());
            } else if (close_it == 1) {
                  dcc_error_msg("send(%s %s): %s",
                              type, ifp->hostname, ERROR_STR());
            }
      } else {
            if (close_it == 0) {
                  iflod_close(ifp, 1, FLOD_ERR_IO, 0,
                            "send(%s %s)=%d not %d",
                            type, ifp->hostname, i, buf_len);
            } else if (close_it == 1) {
                  dcc_error_msg("send(%s %s)=%d not %d",
                              type, ifp->hostname, i, buf_len);
            }
      }
      return 0;
}



u_char                              /* 0=nothing to send, 1=sent */
iflod_send_pos(IFLOD_INFO *ifp, u_char force)
{
      DCC_FLOD_POS req;
      OFLOD_INFO *ofp;

      if (!(ifp->flags & IFLOD_FG_CONNECTED))
            return 0;

      /* ask peer to start over if our database has been cleared */
      ofp = ifp->ofp;
      if ((ifp->flags & IFLOD_FG_CONNECTED)
           && ofp && ofp->mp) {
            if (ofp->mp->flags & FLOD_MMAP_FG_FFWD_IN) {
                  ofp->mp->flags &= ~(FLOD_MMAP_FG_FFWD_IN
                                  | FLOD_MMAP_FG_NEED_REWIND);
                  memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent));
                  db_ptr2flod_pos(req, DCC_FLOD_POS_FFWD_IN);
                  iflod_write(ifp, req, sizeof(req),
                            "ffwd request", 0);
                  dcc_trace_msg("ask %s %s to FFWD flood to us",
                              ifp->hostname, dcc_su2str_err(&ifp->su));
                  return 1;
            }
            if (ofp->mp->flags & FLOD_MMAP_FG_NEED_REWIND) {
                  ofp->mp->flags &= ~FLOD_MMAP_FG_NEED_REWIND;
                  memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent));
                  db_ptr2flod_pos(req, DCC_FLOD_POS_REWIND);
                  iflod_write(ifp, req, sizeof(req),
                            "rewind request", 0);
                  dcc_trace_msg("ask %s %s to rewind flood to us",
                              ifp->hostname, dcc_su2str_err(&ifp->su));
                  return 1;
            }
      }

      if ((force && flod_pos2db_ptr(ifp->pos) >= DCC_FLOD_POS_MIN)
          || memcmp(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent))) {
            memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent));
            iflod_write(ifp, ifp->pos_sent, sizeof(ifp->pos_sent),
                      "confirmed pos", 0);
            if (ofp)
                  ofp->keep_in_time = db_time.tv_sec + KEEPALIVE_IN;
            return 1;
      }

      /* Say just anything if we are doing a keepalive probe before
       * any checksums have been sent by the peer and so before we
       * have a position to confirm. */
      if (force) {
            FLOD_NOTE buf;

            db_ptr2flod_pos(buf.op, DCC_FLOD_POS_NOTE);
            strcpy(buf.str, "are you still there?");
            buf.len = sizeof("are you still there?") + FLOD_NOTE_OVHD;
            iflod_write(ifp, &buf, buf.len, buf.str, 0);
            if (ofp)
                  ofp->keep_in_time = db_time.tv_sec + KEEPALIVE_IN;
            return 1;
      }

      return 0;
}



void
iflod_stop(IFLOD_INFO *ifp, const char *reason)
{
      DCC_FLOD_POS end_req;

      if (reason)
            TMSG2(FLOD, "iflod stop %s: %s", ifp->hostname, reason);
      else
            TMSG1(FLOD, "iflod stop %s", ifp->hostname);
      iflod_send_pos(ifp, 0);
      db_ptr2flod_pos(end_req, DCC_FLOD_POS_END_REQ);
      iflod_write(ifp, end_req, sizeof(end_req), "iflod stop req", 0);
      ifp->flags |= IFLOD_FG_END_REQ;
      if (ifp->ofp)
            ifp->ofp->keep_in_time = db_time.tv_sec + KEEPALIVE_IN_STOP;
}



/* send stop requests to DCC servers flooding to us */
void
iflods_stop(const char *reason,
          u_char force)       /* now! */
{
      SRVR_SOC *sp;
      IFLOD_INFO *ifp;

      /* stop listening for new connections */
      for (sp = srvr_socs; sp; sp = sp->fwd) {
            if (sp->listen >= 0) {
                  close(sp->listen);
                  sp->listen = -1;
            }
      }

      for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
            if (ifp->s < 0)
                  continue;
            if (!(ifp->flags & IFLOD_FG_END_REQ)
                && (ifp->flags & IFLOD_FG_CONNECTED))
                  iflod_stop(ifp, reason);

            if (force || !(ifp->flags & IFLOD_FG_CONNECTED)) {
                  iflod_close(ifp, 1,
                            (flods_st == FLODS_ST_OFF
                             || (ifp->ofp
                               && (ifp->ofp->i_opts.flags
                                   & FLOD_OPT_OFF)))
                            ? FLOD_ERR_LOCAL_OFF
                            : FLOD_ERR_NO_LINK,
                            0, "%s", reason);
                  continue;
            }
      }
}



/* start receiving checksums from another DCC server */
void
iflod_start(SRVR_SOC *sp)
{
      IFLOD_INFO *ifp;
      DCC_SOCKLEN_T l;

      /* find a free input flooding slot */
      for (ifp = iflods.infos; ifp->s >= 0; ++ifp) {
            if (ifp > LAST(iflods.infos)) {
                  if (!complained_many_iflods++)
                        dcc_error_msg("too many incoming floods");
                  return;
            }
      }

      l = sizeof(ifp->su);
      ifp->s = accept(sp->listen, &ifp->su.sa, &l);
      if (ifp->s < 0) {
            dcc_error_msg("accept(flod): %s", ERROR_STR());
            return;
      }
      /* use the IP address until we know which peer it is */
      strcpy(ifp->hostname, dcc_su2str_err(&ifp->su));
      if (!set_flod_socket(ifp->s, ifp->hostname, &ifp->su, 1)) {
            close(ifp->s);
            ifp->s = -1;
            return;
      }

      ++iflods.active;
      ifp->flags |= IFLOD_FG_CONNECTED;
      ifp->quit_connect = db_time.tv_sec + IFLOD_CONNECT_SECS;

      TMSG1(FLOD, "start flood from %s", ifp->hostname);
}



/* Start an incoming SOCKS flood by connecting to the other system.
 *    We will eventually turn the connection around and pretend
 *    the other system initiated the TCP connection. */
static int                    /* -1=failure, 0=not yet, 1=done */
iflod_socks_connect(IFLOD_INFO *ifp)
{
      OFLOD_INFO *ofp;
      ID_TBL *tp;
      DCC_FLOD_VERSION_HDR buf;
      DCC_FNM_LNO_BUF fnm_buf;
      int i;

      ofp = ifp->ofp;

      tp = find_id_tbl(ofp->out_passwd_id);
      if (!tp) {
            iflod_close(ifp, 1, FLOD_ERR_SIGN, 0,
                      DCC_FLOD_PASSWD_ID_MSG" %d%s",
                      ofp->out_passwd_id,
                      fnm_lno(fnm_buf, flod_path, ofp->lno));
            return -1;
      }
      if (tp->cur_passwd[0] == '\0') {
            iflod_close(ifp, 1, FLOD_ERR_SIGN, 0,
                      DCC_FLOD_NO_PASSWD_MSG" %d%s",
                      ofp->out_passwd_id,
                      fnm_lno(fnm_buf, flod_path, ofp->lno));
            return -1;
      }

      i = Rconnect(ifp->s, &ifp->su.sa, DCC_SU_LEN(&ifp->su));
      if (0 > i && errno != EISCONN) {
            if (errno == EAGAIN
                || errno == EINPROGRESS
                || errno == EALREADY)
                  return 0;

            /* it is lame to ignore EINVAL, but several UNIX-like
             * systems return EINVAL for the second connect() after
             * a Unreachable ICMP message or timeout */
            if (errno != EINVAL)
                  rpt_err(&ofp->mp->i_err, 0,
                        FLOD_ERR_CONNECT, errno,
                        "connect(iflod SOCKS %s %s): %s",
                        ofp->rem_hostname,
                        dcc_su2str_err(&ifp->su),
                        ERROR_STR());
            close(ifp->s);
            iflod_clear(ifp, 1);

            return -1;
      }

      ifp->flags |= IFLOD_FG_CONNECTED;
      ofp->mp->i_err.ok = db_time.tv_sec + LAST_ERROR_OK_SECS;

      /* after the SOCKS incoming flood socket is connected,
       * send authentication to convince the peer to send its
       * authentication and then its checksums. */
      memset(&buf, 0, sizeof(buf));
      strcpy(buf.body.str, version_str(ofp));
      buf.body.sender_srvr_id = htons(my_srvr_id);
      buf.body.turn = 1;
      dcc_sign(tp->cur_passwd, sizeof(tp->cur_passwd), &buf, sizeof(buf));
      if (!iflod_write(ifp, &buf, sizeof(buf),
                   "iflod SOCKS authentication", 0))
            return -1;

      TMSG2(FLOD, "start SOCKS flood from %s %s",
            ifp->hostname, dcc_su2str_err(&ifp->su));
      return 1;
}



/* request the start of an input flood via SOCKS if it is not already flowing */
void
iflod_socks_start(OFLOD_INFO *ofp)
{
      DCC_FNM_LNO_BUF fnm_buf;
      IFLOD_INFO *ifp, *ifp1;

      if (!(ofp->o_opts.flags & FLOD_OPT_SOCKS)
          || (ofp->i_opts.flags & FLOD_OPT_OFF))
            return;
      if (!DB_IS_TIME(ofp->in_try_again, ofp->in_try_secs))
            return;
      ofp->in_try_secs = 0;

      /* look for a free slot or an existing slot for the incoming flood */
      ifp = 0;
      for (ifp1 = iflods.infos; ifp1 <= LAST(iflods.infos); ++ifp1) {
            if (ifp1->s < 0) {
                  if (!ifp)
                        ifp = ifp1;
            }
            /* there is nothing to do if it already exists */
            if (ifp1->ofp == ofp)
                  return;
      }
      if (!ifp) {
            if (!complained_many_iflods++)
                  dcc_error_msg("too many incoming floods"
                              " to start SOCKS flood from %s",
                              ofp->rem_hostname);
            return;
      }

      if (!flod_names_resolve_start())
            return;                 /* wait for name resolution */
      if (ofp->mp->rem_su.sa.sa_family == AF_UNSPEC) {
            rpt_err(&ofp->mp->i_err, 0,
                  FLOD_ERR_GET_HOST, ofp->mp->host_error,
                  "flood SOCKS peer name %s: %s%s",
                  ofp->rem_hostname, DCC_HSTRERROR(ofp->mp->host_error),
                  fnm_lno(fnm_buf, flod_path, ofp->lno));
            return;
      }
      ifp->ofp = ofp;
      ifp->su = ofp->mp->rem_su;
      strcpy(ifp->hostname, ofp->rem_hostname);

      ifp->s = socket(ifp->su.sa.sa_family, SOCK_STREAM, 0);
      if (ifp->s < 0) {
            dcc_error_msg("socket(SOCKS iflod): %s", ERROR_STR());
            return;
      }

      if (!set_flod_socket(ifp->s, ifp->hostname, &ifp->su, 1)) {
            close(ifp->s);
            ifp->s = -1;
            return;
      }

      ++iflods.active;
      iflod_socks_connect(ifp);
}



/* db_sts.rcd.d points to the old record */
static u_char                       /* 1=duplicate or deleted */
iflod_ck_dup(IFLOD_INFO *ifp,
           DB_RCD *new, DCC_TGTS new_tgts_raw)
{
#define     OLD db_sts.rcd.d        /* original is there */
      DB_RCD_CK *new_ck, *old_ck;
      int new_num_cks, old_num_cks;
      int i;

      /* ignore reports for deleted checksums
       * unless they are new reports or delete requests */
      if (DB_TGTS_RCD_RAW(OLD.r) == DCC_TGTS_DEL
          && new_tgts_raw != DCC_TGTS_DEL
          && !DCC_TS_NEWER_TS(new->ts, OLD.r->ts)) {
            i = FLOD_CNTERR(&ifp->ofp->cnts.stale);
            if (i <= 0)
                  TMSG3(FLOD, "ignore deleted %s after %s%s",
                        rpt_id("report", new, ifp),
                        rpt_id(0, OLD.r, 0),
                        (i < 0 ? "" : "; stop complaints"));
            return 1;
      }

      /* not duplicate if the server-IDs or timestamps differ */
      if (DB_RCD_ID(new) != DB_RCD_ID(OLD.r)
          || memcmp(new->ts, OLD.r->ts, sizeof(new->ts)))
            return 0;

      /* look for the first real checksum in the new record */
      for (new_num_cks = DB_NUM_CKS(new), new_ck = new->cks;
           new_num_cks != 0;
           --new_num_cks, ++new_ck) {
            if (DB_CK_TYPE(new_ck) != DCC_CK_FLOD_PATH)
                  break;
      }

      /* do not even count duplicate server-ID declarations */
      if (DB_CK_TYPE(new_ck) == DCC_CK_SRVR_ID)
            return 1;

      /* count the duplication */
      i = FLOD_CNTERR(&ifp->ofp->cnts.dup);
      if (i <= 0)
            TMSG2(FLOD2, "duplicate %s%s",
                  rpt_id("report", new, ifp),
                  (i < 0 ? "" : "; stop complaints"));

      /* count the checksums other than paths in the old checksum
       * we have simple duplicate if there are as many or more checksums
       * in the old record */
      for (old_num_cks = DB_NUM_CKS(OLD.r), old_ck = OLD.r->cks;
           old_num_cks != 0;
           --old_num_cks, ++old_ck) {
            if (DB_CK_TYPE(old_ck) != DCC_CK_FLOD_PATH)
                  break;
      }
      if (old_num_cks >= new_num_cks)
            return 1;

      /* The new record has more checksums.  Assume or hope
       * that we are dealing with a simple case like reputations
       * and that intermediate servers always filter the same checksums.
       *
       * Mark totals for checksums in the new that were present in
       * the original to not be increased when the new record is linked
       * in the database. */
      while (old_num_cks < new_num_cks
             && old_num_cks != 0
             && new_num_cks != 0) {
            if (DB_CK_TYPE(old_ck) == DB_CK_TYPE(new_ck)) {
                  ++old_ck;
                  --old_num_cks;
            }
            ++new_ck;
            --new_num_cks;
      }
      /* if the old record has checksums not in the new record,
       * give up and call the new record a duplicate */
      if (old_num_cks != 0)
            return 1;

      /* delete the original report */
      DB_TGTS_RCD_SET(OLD.r, 0);
      db_sts.rcd.b->flags |= (DB_BUF_FG_MSYNC | DB_BUF_FG_DIRTY);

      return 0;
}



/* complain about a received flooded report */
static void PATTRIB(6,7)
iflod_rpt_complain(IFLOD_INFO *ifp,
               const DB_RCD *new,   /* complain about this report */
               u_char serious,      /* 0=send mere note, 1=send complaint */
               FLOD_LIMCNT *lc,     /* limit complaints with this */
               const char *str,     /* type of report */
               const char *pat,...) /* the complaint */
{
      FLOD_NOTE buf;
      const char *sc;
      va_list args;
      int i, len;

      va_start(args, pat);
      len = vsnprintf(buf.str, sizeof(buf.str), pat, args);
      if (len >= ISZ(buf.str))
            len = sizeof(buf.str)-1;
      va_end(args);

      if (!lc && ifp->ofp)
            lc = &ifp->ofp->cnts.iflod_bad;
      if (!lc) {
            sc = "";
      } else {
            i = FLOD_CNTERR(lc);
            if (i > 0)
                  return;
            sc = i < 0 ? "" : "; stop complaints";
      }

      if (serious) {
            dcc_error_msg("%s %s%s", buf.str, rpt_id(str, new, ifp), sc);
            db_ptr2flod_pos(buf.op, DCC_FLOD_POS_COMPLAINT);
      } else {
            TMSG3(FLOD2, "%s %s%s", buf.str, rpt_id(str, new, ifp), sc);
            db_ptr2flod_pos(buf.op, DCC_FLOD_POS_NOTE);
      }

      len += snprintf(&buf.str[len], sizeof(buf.str)-len, " %s%s",
                  rpt_id(str, new, 0), sc);
      if (len >= ISZ(buf.str))
            len = ISZ(buf.str)-1;

      buf.len = len+1 + FLOD_NOTE_OVHD;
      iflod_write(ifp, &buf, buf.len, buf.str, 0);
}



/* consider an incoming flooded report */
static int                    /* -1=failed, 0=not yet, else length */
iflod_rpt(IFLOD_INFO *ifp, int total_len)
{
      DCC_FLOD *fp;
      OFLOD_INFO *ofp;
      DB_PTR pos;
      DCC_TGTS rpt_tgts, found_tgts, min_tgts;
      DB_RCD new;
      DCC_SRVR_ID old_srvr, psrvr;
      const DCC_CK *ck_lim, *ck;
      DB_RCD_CK *new_ck, *found_ck;
      DB_PTR rcd_pos, min_rcd_pos;
      DCC_CK_TYPES type, prev_type, min_type;
      DCC_FLOD_PATH_ID *new_path_id, *old_path_id;
      int num_path_blocks;
      char tgts_buf[DCC_XHDR_MAX_TGTS_LEN];
      int ok2;
      int rpt_len;
      u_char stale;
      ID_MAP_RESULT srvr_mapped;
      int i;

      ofp = ifp->ofp;               /* must not be null here */

      fp = (DCC_FLOD *)&ifp->ibuf.c[ifp->ibuf_off];
      if (fp->num_cks == 0 || fp->num_cks > DCC_QUERY_MAX) {
            iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                      "impossible %d checksums in report #%d from %s",
                      fp->num_cks, ofp->cnts.total, ifp->hostname);
            return -1;
      }
      rpt_len = (sizeof(*fp) - sizeof(fp->cks)
               + fp->num_cks * sizeof(fp->cks[0]));
      if (rpt_len > total_len)
            return 0;         /* wait for more */

      pos = flod_pos2db_ptr(fp->pos);
      if (pos < DCC_FLOD_POS_MIN) {
            iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                      "bogus position "L_HPAT" in flooded report #%d"
                      " from %s",
                      pos, ofp->cnts.total, ifp->hostname);
            return -1;
      }

      /* save the position to return to the sender */
      memcpy(ifp->pos, fp->pos, sizeof(ifp->pos));

      memcpy(new.ts, fp->ts, sizeof(new.ts));
      memcpy(&new.srvr_id_auth, fp->srvr_id_auth, sizeof(new.srvr_id_auth));
      old_srvr = ntohs(new.srvr_id_auth) & ~DCC_SRVR_ID_AUTH;
      new.srvr_id_auth = old_srvr;
      new.fgs_num_cks = 0;

      memcpy(&rpt_tgts, fp->tgts, sizeof(rpt_tgts));
      rpt_tgts = ntohl(rpt_tgts);
      if (rpt_tgts == DCC_TGTS_DEL) {
            if (!(ofp->i_opts.flags & FLOD_OPT_DEL_OK)) {
                  iflod_rpt_complain(ifp, &new, 1, &ofp->cnts.not_deleted,
                                 "delete request", "refuse");
                  return rpt_len;
            }
            if (!(ofp->i_opts.flags & FLOD_OPT_NO_LOG_DEL))
                  dcc_error_msg("accept %s",
                              rpt_id("delete request", &new, ifp));
      } else if (rpt_tgts == 0
               || (rpt_tgts > DCC_TGTS_FLOD_RPT_MAX
                   && rpt_tgts != DCC_TGTS_TOO_MANY)) {
            iflod_close(ifp, 1,  FLOD_ERR_BAD_DATA, 0,
                      "bogus target count %s in %s",
                      dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
                               rpt_tgts, grey_on),
                      rpt_id("report", &new, ifp));
            return -1;
      } else if (ofp->i_opts.flags & FLOD_OPT_TRAPS) {
            /* comply if the source watches only spam traps */
            rpt_tgts = DCC_TGTS_TOO_MANY;
      }

      /* notice reports from the distant future */
      if (DCC_TS_NEWER_TS(new.ts, future_ts)) {
            iflod_rpt_complain(ifp, &new, 1, &ofp->cnts.stale,
                           "report", "future");
            return rpt_len;
      }

      DB_TGTS_RCD_SET(&new, rpt_tgts);
      new.fgs_num_cks = 0;
      stale = 1;
      ck_lim = &fp->cks[fp->num_cks];
      new_ck = new.cks;
      num_path_blocks = 0;

      srvr_mapped = id_map(old_srvr, &ofp->i_opts);
      switch (srvr_mapped) {
      case ID_MAP_NO:
            if (!find_id_tbl(old_srvr)
                && (DCC_TRACE_IDS_BIT & dccd_tracemask)
                && (i = FLOD_CNTERR(&ofp->cnts.bad_id)) <= 0) {
                  dcc_error_msg("%s%s",
                              rpt_id("unknown server-ID in", &new, ifp),
                              (i < 0 ? "" : "; stop complaints"));
            }
            break;
      case ID_MAP_REJ:
            iflod_rpt_complain(ifp, &new, 0, 0,
                           "rejected server-ID in", "refuse");
            return rpt_len;
      case ID_MAP_SELF:
            new.srvr_id_auth = my_srvr_id;
            /* create path pointing to ourself if we translate the ID */
            memset(new_ck, 0, sizeof(*new_ck));
            new_ck->type_fgs = DCC_CK_FLOD_PATH;
            new_path_id = (DCC_FLOD_PATH_ID *)new_ck->sum;
            /* start the path with the ID of the previous hop because
             * we know it is defined */
            new_path_id->hi = ofp->rem_id>>8;
            new_path_id->lo = ofp->rem_id;
            new.fgs_num_cks = 1;
            ++new_ck;
            break;
      }

      for (prev_type = DCC_CK_INVALID, ck = fp->cks;
           ck < ck_lim;
           prev_type = type, ++ck) {
            type = ck->type;
            if (!DCC_CK_OK_FLOD(grey_on, type)) {
                  iflod_rpt_complain(ifp, &new, 1, 0,
                                 "report",
                                 "unknown checksum type %s in",
                                 DB_TYPE2STR(type));
                  continue;
            }
            if (ck->len != sizeof(*ck)) {
                  /* relax this someday if necessary */
                  iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                            "unknown checksum length %d in %s",
                            ck->len, rpt_id("report", &new, ifp));
                  return -1;
            }
            if (type <= prev_type && prev_type != DCC_CK_FLOD_PATH) {
                  iflod_rpt_complain(ifp, &new, 1, 0,
                                 "report",
                                 "out of order %s checksum in",
                                 DB_TYPE2STR(type));
                  return rpt_len;
            }

            new_ck->type_fgs = type;
            new_ck->prev = DB_PTR_CP(DB_PTR_NULL);
            memcpy(new_ck->sum, ck->sum, sizeof(new_ck->sum));
            if (type == DCC_CK_FLOD_PATH) {
                  /* discard report if path is too long */
                  if (++num_path_blocks > DCC_MAX_FLOD_PATH_CKSUMS) {
                        TMSG2(FLOD, "%d path blocks in %s",
                              num_path_blocks,
                              rpt_id("report", &new, ifp));
                        return rpt_len;
                  }
                  /* don't add this path if we translated the origin */
                  if (srvr_mapped == ID_MAP_SELF)
                        continue;
                  old_path_id = (DCC_FLOD_PATH_ID *)ck->sum;
                  new_path_id = old_path_id;
                  for (i = 0; i < DCC_NUM_FLOD_PATH; ++i, ++old_path_id) {
                        psrvr = (old_path_id->hi<<8) | old_path_id->lo;
                        if (psrvr == DCC_ID_INVALID)
                              break;      /* end of path */
                        switch (id_map(psrvr, &ofp->i_opts)) {
                        case ID_MAP_NO:
                        case ID_MAP_REJ:
                              break;
                        case ID_MAP_SELF:
                              psrvr = my_srvr_id;
                              break;
                        }
                        new_path_id->hi = psrvr>>8;
                        new_path_id->lo = psrvr;
                        ++new_path_id;
                  }

            } else {
                  if (type == DCC_CK_SRVR_ID
                      && srvr_mapped == ID_MAP_SELF) {
                        /* discard translated server-ID declarations */
                        TMSG2(FLOD,
                              "translated server-ID from %d in %s",
                              old_srvr, rpt_id("report", &new, ifp));
                        return rpt_len;
                  }

                  /* discard this checksum if we would not have kept
                   * it if we had received the original report
                   * and either its server-ID is translated
                   * or it is not kept by default */
                  if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)
                      && (srvr_mapped == ID_MAP_SELF
                        || DB_GLOBAL_NOKEEP(grey_on, type)))
                        continue;

                  /* notice if this checksum makes the report timely */
                  if (stale
                      && !DCC_TS_OLDER_TS(new.ts,
                                    db_parms.ex_ts[DB_CK_TYPE(new_ck
                                          )].all))
                        stale = 0;
            }

            ++new_ck;
            ++new.fgs_num_cks;
      }
      if (stale) {
            i = FLOD_CNTERR(&ofp->cnts.stale);
            if (i <= 0)
                  TMSG2(FLOD2, "stale %s%s",
                        rpt_id("report", &new, ifp),
                        (i < 0 ? "" : "; stop complaints"));
            return rpt_len;
      }

      if (!DB_NUM_CKS(&new)) {      /* no known checksums */
            iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                      "no known checksum types in %s",
                      rpt_id("report", &new, ifp));
            return -1;
      }

      /* See if the report is a duplicate.
       * Check all of the checksums to find one that is absent or
       * the one with the smallest total to minimize the number
       * of reports we must check to see if this is a duplicate */
      min_tgts = DCC_TGTS_TOO_MANY;
      min_type = DCC_CK_INVALID;
      min_rcd_pos = DB_PTR_NULL;
      ok2 = 0;
      while (new_ck-- > new.cks) {
            type = DB_CK_TYPE(new_ck);
            if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
                  continue;

            switch (db_lookup(dcc_emsg, type, new_ck->sum,
                          0, MAX_HASH_ENTRIES,
                          &db_sts.hash, &db_sts.rcd, &found_ck)) {
            case DB_FOUND_LATER:
            case DB_FOUND_SYSERR:
                  iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                            "%s", dcc_emsg);
                  db_broken(__LINE__,__FILE__, "%s", dcc_emsg);
                  return -1;

            case DB_FOUND_IT:
                  /* notice claims by other servers to our ID */
                  if (DB_RCD_ID(&new) == my_srvr_id
                      && type == DCC_CK_SRVR_ID
                      && memcmp(host_id_rcd.cks[0].sum, new_ck->sum,
                              sizeof(host_id_rcd.cks[0].sum))) {
                        dcc_error_msg("host %s used our server-ID "
                                    "%d at %s",
                                    dcc_ck2str_err(type, new_ck->sum),
                                    my_srvr_id,
                                    dcc_ts2str_err(new.ts));
                  }

                  /* see if this report is the duplicate--we
                   * might get lucky */
                  if (iflod_ck_dup(ifp, &new, rpt_tgts))
                        return rpt_len;

                  /* notice the local server's white list
                   * greylisting uses DCC_TGTS_GREY_WHITE=DCC_TGTS_OK2 */
                  found_tgts = DB_TGTS_CK(found_ck);
                  if (found_tgts == DCC_TGTS_OK
                      || (found_tgts == DCC_TGTS_GREY_WHITE
                        && (++ok2 >= 2 || grey_on))) {
                        iflod_rpt_complain(ifp, &new, 0, &ofp->cnts.ok2,
                                       "report", "whitelisted");
                        return rpt_len;
                  }

                  /* At least one checksum is already known to us.
                   * If the report is a delete request,
                   * then we need to run dbclean to fix all of the
                   * totals affected by the deleted reports. */
                  if (rpt_tgts == DCC_TGTS_DEL
                      && !grey_on
                      && !DCC_CK_IS_REP(grey_on, type))
                        need_del_dbclean = "flood checksum deletion";

                  /* We can use this checksum to see if this report is
                   * a duplicate if it would not have been discarded
                   * by an upstream peer */
                  if (!DB_GLOBAL_NOKEEP(grey_on, type)) {
                        rcd_pos = DB_PTR_EX(found_ck->prev);
                        if (rcd_pos == DB_PTR_NULL)
                              goto not_dup;     /* not a duplicate */

                        /* notice the checksum with smallest count */
                        if (min_type == DCC_CK_INVALID
                            || min_tgts > found_tgts) {
                              min_tgts = found_tgts;
                              min_type = type;
                              min_rcd_pos = rcd_pos;
                        }
                  }
                  break;

            case DB_FOUND_EMPTY:
            case DB_FOUND_CHAIN:
            case DB_FOUND_INTRUDER:
                  /* We will fail to find this checksum in our database
                   * only if the report is not a duplicate or if it is
                   * a duplicate report but either an upstream peer
                   * did not pass it along or we have expired this
                   * checksum.
                   * Unless we've been using dbclean with
                   * inconsistent expiration durations, the second
                   * case won't happen, so assume it is not a duplicate */
                  if (!DB_GLOBAL_NOKEEP(grey_on, type))
                        goto not_dup;
                  break;
            }
      }
      /* At least one checksum in the report is known to us, and
       * we've chosen the checksum with the smallest total.
       * Chase the chosen chain to check all occurances of
       * the checksum to see if the flooded report is a duplicate */
      if (min_type != DCC_CK_INVALID) {
            for (;;) {
                  found_ck = db_map_rcd_ck(dcc_emsg, &db_sts.rcd,
                                     min_rcd_pos, min_type);
                  if (!found_ck) {
                        iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                                  "%s", dcc_emsg);
                        db_broken(__LINE__,__FILE__, "%s", dcc_emsg);
                        return -1;
                  }
                  if (iflod_ck_dup(ifp, &new, rpt_tgts))
                        return rpt_len;

                  rcd_pos = DB_PTR_EX(found_ck->prev);
                  if (rcd_pos == DB_PTR_NULL)
                        break;
                  if (rcd_pos >= min_rcd_pos) {
                        db_broken(__LINE__,__FILE__,
                                "bad %s link of "L_HPAT" at "L_HPAT,
                                DB_TYPE2STR(min_type),
                                rcd_pos, min_rcd_pos);
                        return -1;
                  }
                  min_rcd_pos = rcd_pos;
            }
      }
not_dup:;

      /* the report is not a duplicate, so add it to our database */
      if (!add_dly_rcd(&new, 1)) {
            iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                      "%s", dcc_emsg);
            return -1;
      }

      ++ofp->cnts.accepted;
      return rpt_len;
}



static void
bad_vers(IFLOD_INFO *ifp, u_char fail)
{
      iflod_close(ifp, fail, FLOD_ERR_BAD_VERS, 0,
                DCC_FLOD_BAD_VER_MSG DCC_FLOD_VERSION_CUR_STR
                "\" not \"%.*s\"",
                STRZ(DCC_FLOD_VERSION_STR_BASE)+10,
                ifp->ibuf.vers.body.str);
}



static u_char
check_iflod_vers(IFLOD_INFO *ifp)
{
      DCC_FNM_LNO_BUF fnm_buf;
      DCC_FLOD_VERSION_HDR *vp;
      OFLOD_INFO *ofp;
      IFLOD_INFO *ifp1;
      ID_TBL *tp;
      DCC_SRVR_ID rem_id;
      int iversion;

      vp = &ifp->ibuf.vers;
      if (!strcmp(vp->body.str, DCC_FLOD_VERSION_CUR_STR)) {
            iversion = DCC_FLOD_VERSION_CUR;
            ifp->flags |= IFLOD_FG_VERS_CK;
            ifp->flags |= IFLOD_FG_PEER_REP_OFF;

#ifdef DCC_FLOD_VERSION7
      } else if (!strcmp(vp->body.str, DCC_FLOD_VERSION7_STR)) {
            iversion = DCC_FLOD_VERSION7;
            ifp->flags |= IFLOD_FG_VERS_CK;
            ifp->flags |= IFLOD_FG_PEER_REP_OFF;
#endif /* DCC_FLOD_VERSION7 */

      } else if (!strncmp(vp->body.str, DCC_FLOD_REP_VERSION_STR_BASE,
                      STRZ(DCC_FLOD_REP_VERSION_STR_BASE))) {
            /* It seems to be a peer selling reputations.
             * Complain quietly after identifying it and hope it will
             * try again without reputations */
            iversion = 0;

      } else if (!strncmp(vp->body.str, DCC_FLOD_VERSION_STR_BASE,
                      STRZ(DCC_FLOD_VERSION_STR_BASE))) {
            /* it seems to be a DCC server,
             * so complain after identifying the peer */
            iversion = 1;

      } else {
            /* junk complain and give up now */
            bad_vers(ifp, 1);
            return 0;
      }

      rem_id = ntohs(vp->body.sender_srvr_id);
      if (rem_id < DCC_SRVR_ID_MIN
          || rem_id > DCC_SRVR_ID_MAX) {
            iflod_close(ifp, 1, FLOD_ERR_ID, 0,
                      DCC_FLOD_BAD_ID_MSG" %d", rem_id);
            return 0;
      }
      for (ofp = oflods.infos; ; ++ofp) {
            if (ofp > LAST(oflods.infos)) {
                  iflod_close(ifp, 1, FLOD_ERR_ID, 0,
                            DCC_FLOD_BAD_ID_MSG" %d", rem_id);
                  return 0;
            }
            if (ofp->rem_id == rem_id) {
                  ifp->ofp = ofp;
                  strcpy(ifp->hostname, ofp->rem_hostname);
                  if (ofp->i_opts.flags & FLOD_OPT_OFF) {
                        iflod_close(ifp, 1, FLOD_ERR_LOCAL_OFF, 0,
                                  "incoming flood turnd off%s",
                                  fnm_lno(fnm_buf, flod_path,
                                        ofp->lno));
                        return 0;
                  }
                  break;
            }
      }

      /* we now know which peer it is */

      if (!ck_sign(&tp, 0, ofp->in_passwd_id, vp, sizeof(*vp))) {
            if (!tp)
                  iflod_close(ifp, 1, FLOD_ERR_ID, 0,
                            DCC_FLOD_PASSWD_ID_MSG" %d%s",
                            ofp->in_passwd_id,
                            fnm_lno(fnm_buf, flod_path, ofp->lno));
            else
                  iflod_close(ifp, 1, FLOD_ERR_SIGN, 0,
                            DCC_FLOD_BAD_AUTH_MSG" %d",
                            ofp->in_passwd_id);
            return 0;
      }
      if (!(ifp->flags & IFLOD_FG_VERS_CK)) {
            bad_vers(ifp, iversion);
            /* hope that the peer will try with a better version someday */
            return 0;
      }
      ofp->iversion = iversion;

      ifp->ibuf_off += sizeof(*vp);
      if (iversion != DCC_FLOD_VERSION_CUR)
            TMSG2(FLOD, "iflod version %d from %s",
                  iversion, ifp->hostname);

      /* switch a passive output flood */
      if (vp->body.turn) {
            if ((ofp->o_opts.flags & FLOD_OPT_OFF)
                || flods_st != FLODS_ST_ON) {
                  iflod_close(ifp, 1, FLOD_ERR_LOCAL_OFF, 0,
                            "passive output flooding off"
                            " for ID %d%s", rem_id,
                            fnm_lno(fnm_buf, flod_path, ofp->lno));
                  return 0;
            }
            if (ofp->s >= 0) {
                  /* We have a duplicate passive output flood.
                   * Keep the new one if the existing stream
                   * has been quiet for a long time. */
                  if (!ofp->mp
                      || DB_IS_TIME(ofp->mp->o_err.ok,
                                 LAST_ERROR_OK_SECS)) {
                        TMSG2(FLOD,"new duplicate passive output flood"
                              " for ID %d%s", rem_id,
                              fnm_lno(fnm_buf, flod_path, ofp->lno));
                        oflod_close(ofp, 0, FLOD_ERR_DUP, 0);
                  } else {
                        iflod_close(ifp, 1, FLOD_ERR_DUP, 0,
                                  "duplicate passive output flood"
                                  " for ID %d%s", rem_id,
                                  fnm_lno(fnm_buf, flod_path,
                                        ofp->lno));
                        return 0;
                  }
            }

            ofp->s = ifp->s;
            ofp->rem_su = ifp->su;
            ++oflods.active;
            iflod_clear(ifp, 0);

            if (!oflod_connect_fin(ofp))
                  oflod_close(ofp, 0, FLOD_ERR_SAME, 0);
            return 0;
      }

      /* detect duplicate incoming floods */
      for (ifp1 = iflods.infos; ifp1 <= LAST(iflods.infos); ++ifp1) {
            if (ifp1 == ifp || ifp1->ofp != ofp)
                  continue;

            /* We have a duplicate.  Keep the new flood and close the
             * old flood if the old flood has no position to confirm,
             * perhaps because it was never really used. */
            if (!ofp->mp
                || !iflod_send_pos(ifp1, 1)) {
                  iflod_close(ifp1, 0, FLOD_ERR_DUP, 0,
                            "new duplicate incoming flood for ID %d%s",
                            rem_id, fnm_lno(fnm_buf, flod_path,
                                        ofp->lno));
                  break;
            }

            /* Otherwise, kill the new flood */
            iflod_close(ifp, 1,  FLOD_ERR_DUP, 0,
                      "duplicate incoming flood for ID %d%s",
                      rem_id, fnm_lno(fnm_buf, flod_path, ofp->lno));
            return 0;
      }

      ofp->ifp = ifp;
      save_flod_cnts(ofp);
      ofp->keep_in_time = db_time.tv_sec + KEEPALIVE_IN;

      /* send DCC_FLOD_POS_REWIND immediately if needed */
      iflod_send_pos(ifp, 0);

      /* try to restart corresponding output flood */
      if (ofp->s < 0) {
            ofp->out_try_again = 0;
            ofp->ids_mtime = 0;     /* try both passwords */
            oflod_open(ofp);
      }

      return 1;
}



/* see what a distant flooder is telling us */
void
iflod_read(IFLOD_INFO *ifp)
{
      int was_locked;
      DCC_FLOD_VERSION_HDR *vp;
      DCC_FLOD *fp;
      OFLOD_INFO *ofp;
      const char *reason;
      int len, i;

      if (!(ifp->flags & IFLOD_FG_CONNECTED)
          && iflod_socks_connect(ifp) <= 0)
            return;

      /* read only once before returning
       * to ensure we pay attention to other work */
      len = (((ifp->flags & IFLOD_FG_VERS_CK)
            ? sizeof(ifp->ibuf) : sizeof(*vp))
             - ifp->ibuf_len);
      ofp = ifp->ofp;
      if (ofp && ofp->o_opts.flags & FLOD_OPT_SOCKS)
            i = Rrecv(ifp->s, &ifp->ibuf.c[ifp->ibuf_len], len, 0);
      else
            i = recv(ifp->s, &ifp->ibuf.c[ifp->ibuf_len], len, 0);
      if (i < 0) {
            /* If kernel ran out of data, stop for now.
             * Give up on an I/O error */
            if (!DCC_BLOCK_ERROR()) {
                  iflod_close(ifp, 1, FLOD_ERR_IO, errno,
                            "iflod recv(%s): %s",
                            ifp->hostname, ERROR_STR());
            }
            return;
      }
      if (i == 0) {
            /* EOF or remote shutdown() on the socket */
            if (ifp->ibuf_len != 0) {
                  /* we have some final data. */
                  if (ifp->ibuf_len < sizeof(DCC_FLOD_POS)) {
                        /* complain if it is too little or noise */
                        iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                                  "report %d from %s truncated",
                                  ofp ? ofp->cnts.total : 0,
                                  ifp->hostname);

                  } else if (flod_pos2db_ptr((u_char *)&ifp->ibuf.c[
                                          ifp->ibuf_off])
                           < DCC_FLOD_POS_MIN) {
                        /* Take it as an error message if enough.
                         * Recognize bad password error message and
                         * slow down retries */
                        reason = &ifp->ibuf.c[ifp->ibuf_off
                                          +sizeof(DCC_FLOD_POS)];
                        if (ofp
                            && ofp->cnts.total == 0
                            && (ofp->o_opts.flags & FLOD_OPT_SOCKS)
                            && (!strncmp(reason,
                                     DCC_FLOD_BAD_ID_MSG,
                                     STRZ(DCC_FLOD_BAD_ID_MSG))
                              || !strncmp(reason,
                                        DCC_FLOD_BAD_AUTH_MSG,
                                      STRZ(DCC_FLOD_BAD_AUTH_MSG))))
                              ofp->in_try_secs = FLOD_SLOW_RETRY_SECS;
                        iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                                  "report %d from %s truncated"
                                  " with \"%s\"",
                                  ofp ? ofp->cnts.total : 0,
                                  ifp->hostname, reason);

                  } else {
                        /* complain if it is noise */
                        iflod_close(ifp, 1, FLOD_ERR_BAD_DATA, 0,
                                  "garbage report %d from %s",
                                  ofp ? ofp->cnts.total : 0,
                                  ifp->hostname);
                  }
            } else {
                  iflod_close(ifp, 0,
                            (flods_st == FLODS_ST_OFF
                             || (ofp && (ofp->i_opts.flags
                                   & FLOD_OPT_OFF)))
                            ? FLOD_ERR_LOCAL_OFF
                            : FLOD_ERR_REMOTE_OFF,
                            0, DCC_FLOD_OK_STR);
            }
            return;
      }
      ifp->ibuf_len += i;
      if (ofp && ofp->mp)
            ofp->mp->i_err.ok = db_time.tv_sec + LAST_ERROR_OK_SECS;

      was_locked = db_lock();
      if (was_locked < 0) {
            dcc_error_msg("flod %s", dcc_emsg);
            return;
      }

      dcc_timeval2ts(future_ts, &db_time, DCC_FUTURE_SECS);
      for (;;) {
            len = ifp->ibuf_len - ifp->ibuf_off;
            if (!(ifp->flags & IFLOD_FG_VERS_CK)) {
                  if (len < ISZ(*vp))
                        break;
                  if (!check_iflod_vers(ifp))
                        break;
                  ofp = ifp->ofp;

            } else {
                  /* when we have enough of the report, see if we have
                   * the whole thing by trying swallow it */
                  if (len < (int)(sizeof(*fp) - sizeof(fp->cks)
                              + sizeof(fp->cks[0])))
                        break;
                  i = iflod_rpt(ifp, len);
                  if (i < 0)
                        break;      /* stream closed */
                  if (i == 0)
                        break;      /* wait for more of it */
                  if (ofp != 0)
                        ++ofp->cnts.total;
                  ifp->ibuf_off += i;
            }

            if (ifp->ibuf_off >= ifp->ibuf_len) {
                  ifp->ibuf_len = 0;
                  ifp->ibuf_off = 0;
                  break;
            }
      }

      if (ifp->ibuf_off != 0) {
            ifp->ibuf_len -= ifp->ibuf_off;
            memmove(&ifp->ibuf.c[0], &ifp->ibuf.c[ifp->ibuf_off],
                  ifp->ibuf_len);
            ifp->ibuf_off = 0;
      }

      if (!was_locked)
            db_unlock();
}



void
iflods_start(void)
{
      SRVR_SOC *sp;
      DCC_SOCKU su;
      const DCC_SOCKU *sup = 0;
      int i, on;

      /* (re)start only when things are quiet */
      if (iflods.active != 0)
            return;

      for (sp = srvr_socs; sp; sp = sp->fwd) {
            if (sp->flags & SRVR_SOC_ADDR) {
                  /* open a TCP listen socket for incoming floods
                   * for each explicitly configured IP address */
                  sup = &sp->su;
            } else if (sp->flags & SRVR_SOC_LISTEN) {
                  /* open one TCP INADDR_ANY listen socket for
                   * the first implicitly configured interface */
                  sup = dcc_mk_su(&su, sp->su.sa.sa_family, 0,
                              sp->su.ipv6.sin6_port);
            } else {
                  if (sp->listen >= 0) {
                        close(sp->listen);
                        sp->listen = -1;
                  }
                  continue;
            }

            if (sp->listen >= 0)
                  continue;

            /* don't need to listen if there is no flooding */
            if (!oflods.total)
                  continue;

            sp->listen = socket(sup->sa.sa_family, SOCK_STREAM, 0);
            if (sp->listen < 0) {
                  dcc_error_msg("socket(listen %s): %s",
                              dcc_su2str_err(sup), ERROR_STR());
                  continue;
            }
            on = 1;
            if (0 > setsockopt(sp->listen, SOL_SOCKET, SO_REUSEADDR,
                           &on, sizeof(on)))
                  dcc_error_msg("setsockopt(listen %s, SO_REUSADDR): %s",
                              dcc_su2str_err(sup), ERROR_STR());
            if (0 > fcntl(sp->listen, F_SETFD, FD_CLOEXEC))
                  dcc_error_msg("fcntl(listen %s FD_CLOEXEC): %s",
                              dcc_su2str_err(sup), ERROR_STR());

            i = bind(sp->listen, &sup->sa, DCC_SU_LEN(sup));
            if (0 > i) {
                  dcc_error_msg("bind(listen %s): %s",
                              dcc_su2str_err(sup), ERROR_STR());
                  close(sp->listen);
                  sp->listen = -1;
                  continue;
            }

            if (0 > listen(sp->listen, 1)) {
                  dcc_error_msg("listen(%s): %s",
                              dcc_su2str_err(sup), ERROR_STR());
                  close(sp->listen);
                  sp->listen = -1;
            }
      }
}



/* list the current flooders */
int
flods_list(char *buf, int buf_len, u_char anon)
{
#define FLODS_LIST_TOO_SHORT "buffer too short\n"
#define FLODS_LIST_ALLOC(i) {                         \
      p += (i);                                 \
      if ((buf_len -= (i)) <= 0) {                    \
            strcpy(p, FLODS_LIST_TOO_SHORT);          \
            return (p-buf)+ISZ(FLODS_LIST_TOO_SHORT); \
      }}
      IFLOD_INFO *ifp;
      OFLOD_INFO *ofp;
      char instr[DCC_SU2STR_SIZE], outstr[DCC_SU2STR_SIZE];
      char hostname[60], fg_buf[30];
      u_char have_in, sep_in;
      int i;
      char *p;

      if (buf_len < ISZ(FLODS_LIST_TOO_SHORT) +INET6_ADDRSTRLEN+1)
            return 0;

      buf_len -= ISZ(FLODS_LIST_TOO_SHORT);
      p = buf;
      for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
            if (ofp->rem_hostname[0] == '\0')
                  break;
            have_in = 0;
            sep_in = 0;
            for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
                  if (ifp->ofp == ofp) {
                        if (ifp->s >= 0) {
                              DCC_SOCKU in, out;

                              have_in = 1;
                              dcc_ipv6sutoipv4(&in, &ifp->su);
                              dcc_ipv6sutoipv4(&out, &ofp->rem_su);
                              if (ofp->s < 0
                                  || !DCC_SU_EQ(&in, &out))
                                  sep_in = 1;
                        }
                        break;
                  }
            }
            if (anon) {
                  i = snprintf(p, buf_len, "%5d %15s\t%s\n",
                             ofp->rem_id,
                             ofp->s >= 0
                             ? (!(ofp->flags & OFLOD_FG_CONNECTED)
                              ? "  (connecting)"
                              : "")
                             : (ofp->o_opts.flags & FLOD_OPT_OFF)
                             ? "  (output off)"
                             : flods_st != FLODS_ST_ON
                             ? "  (flood off)"
                             : "  (no output)",
                             have_in
                             ? (!(ifp->flags & IFLOD_FG_VERS_CK)
                              ? "  (connecting)"
                              : "")
                             : (ofp->i_opts.flags & FLOD_OPT_OFF)
                             ? "  (input off)"
                             : flods_st != FLODS_ST_ON
                             ? "  (flood off)"
                             : "  (no input)");
            } else {
                  dcc_host_portname(hostname, sizeof(hostname),
                                ofp->rem_hostname,
                                ofp->rem_port == def_port
                                ? 0 : ofp->rem_portname),
                  i = strlen(hostname);
                  flod_mmap_fg(fg_buf, sizeof(fg_buf),
                             i < 16 ? "\t\t" : i > 24 ? "  " : "\t",
                             ofp->mp);
                  i = snprintf(p, buf_len, "%5d %15s\t%s\t%s%s\n",
                             ofp->rem_id,
                             ofp->s >= 0
                             ? ((ofp->flags & OFLOD_FG_CONNECTED)
                              ? dcc_su2str2(outstr, sizeof(outstr),
                                          &ofp->rem_su)
                              : "  (connecting)")
                             : (ofp->o_opts.flags & FLOD_OPT_OFF)
                             ? "  (output off)"
                             : flods_st != FLODS_ST_ON
                             ? "  (flood off)"
                             : "  (no output)",
                             have_in
                             ? (!(ifp->flags & IFLOD_FG_VERS_CK)
                              ? "  (connecting)"
                              : sep_in
                              ? dcc_su2str2(instr, sizeof(instr),
                                          &ifp->su)
                              : "\t")
                             : (ofp->i_opts.flags & FLOD_OPT_OFF)
                             ? "  (input off)"
                             : flods_st != FLODS_ST_ON
                             ? "  (flood off)"
                             : "  (no input)",
                             hostname,
                             fg_buf);
            }
            FLODS_LIST_ALLOC(i);
      }

      for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
            if (ifp->s < 0 || ifp->ofp != 0)
                  continue;   /* already handled this one */

            /* say something about an incomplete connection */
            i = snprintf(p, buf_len, " ?    %s\n",
                       dcc_su2str2(instr, sizeof(instr), &ifp->su));
            FLODS_LIST_ALLOC(i);
      }
      if (p > buf)
            --p;              /* trim trailing '\n' */
      return p-buf;
#undef FLODS_LIST_TOO_SHORT
#undef FLODS_LIST_ALLOC
}



static u_char                       /* 0=no room */
flod_stats_time(char **buf, int *buf_len,
            const char *pat, time_t when)
{
      struct tm tm;
      int i;

      if (*buf_len <= 0)
            return 0;
      i = strftime(*buf, *buf_len, pat, dcc_localtime(when, &tm));
      if (i <= 0) {
            *buf_len = 0;
            return 0;
      }
      *buf += i;
      *buf_len -= i;
      return 1;
}



static void
flod_stats_conn_total(char **buf, int *buf_len,
                  const char *label, int connected)
{
      int i;

      if (*buf_len <= 0)
            return;

      i = snprintf(*buf, *buf_len,
                 "\n  %s connected a total of %d days %d:%02d:%02d\n",
                 label,
                 connected/(24*60*60),
                 (connected/(60*60)) % 24,
                 (connected/60) % 60,
                 connected % 60);
      *buf += i;
      *buf_len -= i;
}



const char *
flod_stats_str(int *pe, int e, enum FLOD_ERR_OP op)
{
      int ignore;

      if (!pe)
            pe = &ignore;

      switch (op) {
      case FLOD_ERR_OK:     *pe = 0; return "";
      case FLOD_ERR_SAME:         *pe = 0; return "";
      case FLOD_ERR_NO_LINK:      *pe = 0; return "no link";
      case FLOD_ERR_GET_HOST:     *pe = 0; return "bad host name";
      case FLOD_ERR_CONNECT:      *pe = e; return "initial connect";
      case FLOD_ERR_CONNECT2:     *pe = e; return "second connect";
      case FLOD_ERR_IO:     *pe = e; return "I/O error";
      case FLOD_ERR_DUP:          *pe = 0; return "duplicate connection";
      case FLOD_ERR_LOCAL_OFF:    *pe = 0; return "local flood off";
      case FLOD_ERR_REMOTE_OFF:   *pe = 0; return "remote flood off";
      case FLOD_ERR_ID:     *pe = 0; return "bad ID";
      case FLOD_ERR_SIGN:         *pe = 0; return "bad password";
      case FLOD_ERR_BAD_VERS:     *pe = 0; return "bad version";
      case FLOD_ERR_REP:          *pe = 0; return DCC_FLOD_REPS_OFF_MSG;
      case FLOD_ERR_BAD_DATA:     *pe = 0; return "bad data";
      case FLOD_ERR_PEER_FAIL:    *pe = 0; return "peer disconnect";
      case FLOD_ERR_KEEPALIVE:    *pe = 0; return "keepalive";
      }
      *pe = 0; return "??";
}



static void
flod_stats_conn_cur(char **buf, int *buf_len, u_char connected,
                time_t conn_changed, time_t cleared,
                LAST_ERROR *ep, time_t keep_time, time_t try_again)
{
      const char *opstr;
      int old_errno;
      time_t rep_report;
      int i;

      if (*buf_len <= 0)
            return;

      if (connected) {
            if (conn_changed >= cleared
                && !flod_stats_time(buf, buf_len,
                              "     connected since %b %d %X",
                              conn_changed))
                  return;
            flod_stats_time(buf, buf_len,
                        (db_time.tv_sec >= keep_time
                         ? "     keepalive expired %X"
                         : "     keepalive expires %X"),
                        keep_time);
            return;
      }

      opstr = flod_stats_str(&old_errno, ep->old_errno, ep->op);
      i = snprintf(*buf, *buf_len, "     not connected");
      if ((*buf_len -= i) <= 0) {
            *buf_len = 0;
            return;
      }
      *buf += i;
      if (conn_changed >= cleared) {
            if (!flod_stats_time(buf, buf_len, " since %b %d %X",
                             conn_changed))
                  return;
      }
      if (old_errno == 0) {
            i = snprintf(*buf, *buf_len, "; %s",
                       opstr);
      } else {
            i = snprintf(*buf, *buf_len, "; %s: %s",
                       opstr, ERROR_STR1(old_errno));
      }
      if ((*buf_len -= i) <= 0) {
            *buf_len = 0;
            return;
      }
      *buf += i;

      if (try_again > db_time.tv_sec
          && !flod_stats_time(buf, buf_len,
                        "%n     do not check until %b %d %X",
                        try_again))
            return;

      rep_report = ep->rep_report;
      if (rep_report > ep->ok && rep_report > db_time.tv_sec
          && !flod_stats_time(buf, buf_len,
                        "%n     no repeated complaints until %b %d %X",
                        rep_report))
            return;
}



void
new_peer(OFLOD_INFO *ofp)
{
      ofp->in_try_again = 0;
      ofp->in_try_secs = 0;
      ofp->out_try_again = 0;
      ofp->out_try_secs = 0;
      if (ofp->mp) {
            ofp->mp->i_err.op = ((flods_st == FLODS_ST_OFF
                              || (ofp->i_opts.flags & FLOD_OPT_OFF))
                             ? FLOD_ERR_LOCAL_OFF
                             : FLOD_ERR_NO_LINK);
            ofp->mp->i_err.ok = 0;
            ofp->mp->i_err.rep_report = db_time.tv_sec + FLOD_RETRY_SECS*2;

            ofp->mp->o_err.op = ((flods_st == FLODS_ST_OFF
                              || (ofp->o_opts.flags & FLOD_OPT_OFF))
                             ? FLOD_ERR_LOCAL_OFF
                             : FLOD_ERR_NO_LINK);
            ofp->mp->o_err.ok = 0;
            ofp->mp->o_err.rep_report = db_time.tv_sec + FLOD_RETRY_SECS*2;
      }
}



/* list the counts for a flood */
int
flod_stats(char *buf, int buf_len, u_int32_t tgt, u_char clear)
{
#define FLOD_STATS_TOO_SHORT "buffer too short\n"
#define FLOD_STATS_ALLOC(i) (p += (i), len -= (i))
      OFLOD_INFO *ofp, *ofp1;
      FLOD_MMAP *mp;
      char now_buf[26], time_buf[26], fg_buf[60];
      DCC_SRVR_ID min_srvr, max_srvr;
      u_char mapped;
      struct tm tm;
      int len, i;
      char *p;

      if (buf_len < ISZ(FLOD_STATS_TOO_SHORT))
            return 0;
      len = buf_len - ISZ(FLOD_STATS_TOO_SHORT);
      p = buf;

      strftime(now_buf, sizeof(now_buf), "%b %d %X %Z",
             dcc_localtime(db_time.tv_sec, &tm));

      if (flod_mmaps) {
            mapped = 0;
      } else {
            oflods_load();
            mapped = 1;
      }

      if (tgt <= DCC_SRVR_ID_MAX) {
            /* an explicit target server-ID was specified */
            min_srvr = max_srvr = tgt;
      } else {
            /* look for next server-ID after the target value */
            min_srvr = tgt - DCC_SRVR_ID_MAX;
            max_srvr = DCC_SRVR_ID_MAX;
      }
      ofp = 0;
      for (ofp1 = oflods.infos; ofp1 <= LAST(oflods.infos); ++ofp1) {
            if (ofp1->rem_id != DCC_ID_INVALID
                && ofp1->rem_id >= min_srvr
                && ofp1->rem_id <= max_srvr
                && ofp1->mp != 0) {
                  /* This peer fits and is the best so far. */
                  ofp = ofp1;
                  max_srvr = ofp->rem_id-1;
            }
      }
      if (!ofp) {
            i = snprintf(p, len,
                       DCC_AOP_FLOD_STATS_ID"unknown remote server-ID",
                       tgt);
            FLOD_STATS_ALLOC(i);
            if (mapped)
                  oflods_unmap();
            return p-buf;
      }
      mp = ofp->mp;

      save_flod_cnts(ofp);
      strftime(time_buf, sizeof(time_buf), "%b %d %X %Z",
             dcc_localtime(mp->cnts.cnts_cleared, &tm));
      i = snprintf(p, len,
                 DCC_AOP_FLOD_STATS_ID" %s%s  %s\n  status start %s",
                 ofp->rem_id, mp->rem_hostname,
                 flod_mmap_fg(fg_buf, sizeof(fg_buf), "  ", mp),
                 now_buf, time_buf);
      FLOD_STATS_ALLOC(i);
      flod_stats_conn_total(&p, &len, "output", mp->cnts.out_total_conn);
      i = snprintf(p, len, "     %d reports sent%s\n",
                 mp->cnts.out_reports+ofp->cnts.out_reports,
                 mp->flags & FLOD_MMAP_FG_PASSIVE ? "   PASSIVE" : "");
      FLOD_STATS_ALLOC(i);
      flod_stats_conn_cur(&p, &len, ofp->flags & OFLOD_FG_CONNECTED,
                      ofp->mp->cnts.out_conn_changed,
                      mp->cnts.cnts_cleared,
                      &ofp->mp->o_err,
                      ofp->keep_out_time,
                      ofp->out_try_again);
      flod_stats_conn_total(&p, &len, "input", mp->cnts.in_total_conn);
      i = snprintf(p, len,
                 "     %d reports received  %d accepted"
                 "  %d duplicate  %d stale\n"
                 "     %d bad whitelist  %d not deleted%s\n",
                 mp->cnts.total+ofp->cnts.total,
                 mp->cnts.accepted+ofp->cnts.accepted,
                 mp->cnts.dup+ofp->cnts.dup.val,
                 mp->cnts.stale+ofp->cnts.stale.val,
                 mp->cnts.ok2+ofp->cnts.ok2.val,
                 mp->cnts.not_deleted+ofp->cnts.not_deleted.val,
                 mp->flags & FLOD_MMAP_FG_SOCKS ? "   SOCKS" : "");
      FLOD_STATS_ALLOC(i);
      flod_stats_conn_cur(&p, &len, ofp->ifp != 0,
                      ofp->mp->cnts.in_conn_changed,
                      mp->cnts.cnts_cleared,
                      &ofp->mp->i_err,
                      ofp->keep_in_time,
                      ofp->in_try_again);

      if (len <= 0) {
            strcpy(buf, FLOD_STATS_TOO_SHORT);
            if (mapped)
                  oflods_unmap();
            return ISZ(FLOD_STATS_TOO_SHORT);
      }

      if (clear) {
            ofp->limit_reset = db_time.tv_sec + FLOD_LIM_CLEAR_SECS;
            memset(&mp->cnts, 0, sizeof(mp->cnts));
            mp->cnts.cnts_cleared = db_time.tv_sec;
            new_peer(ofp);
            save_flod_cnts(ofp);
      }

      if (mapped)
            oflods_unmap();
      return p-buf;
#undef FLOD_STATS_TOO_SHORT
#undef FLOD_STATS_ALLOC
}

Generated by  Doxygen 1.6.0   Back to index