diff dccd/iflod.c @ 0:c7f6b056b673

First import of vendor version
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 13:49:58 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/iflod.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2627 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * deal with incoming floods of checksums
+ *
+ * Copyright (c) 2008 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 without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.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.103-1.249 $Revision$
+ */
+
+#include "dccd_defs.h"
+#include <sys/wait.h>
+
+
+IFLODS iflods;
+
+u_int complained_many_iflods;
+
+time_t got_hosts;
+pid_t resolve_hosts_pid = -1;
+
+time_t iflods_ok_timer;			/* incoming flooding ok since then */
+
+int flod_trace_gen;			/* unsuppress tracing */
+
+static u_char iflod_write(IFLOD_INFO *, void *, int, const char *, u_char);
+
+
+static DCC_TS future;			/* timestamp sanity */
+
+
+
+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 *
+rem_str(const char *hostname, const DCC_SOCKU *su)
+{
+	static int bufno;
+	static struct {
+	    char    str[90];
+	} bufs[4];
+	char *s;
+	char sustr[DCC_SU2STR_SIZE];
+	int i;
+
+	s = bufs[bufno].str;
+	bufno = (bufno+1) % DIM(bufs);
+
+	STRLCPY(s, hostname, sizeof(bufs[0].str));
+
+	if (su->sa.sa_family == AF_UNSPEC
+	    && *DCC_SU_PORTP(su) == 0)
+		return s;
+
+	dcc_su2str2(sustr, sizeof(sustr), su);
+	if (strcmp(s, sustr)) {
+		STRLCAT(s, " ", sizeof(bufs[0].str));
+		STRLCAT(s, sustr, sizeof(bufs[0].str));
+	}
+	if (*DCC_SU_PORTP(su) != DCC_GREY2PORT(grey_on)) {
+		i = strlen(s);
+		snprintf(s+i, sizeof(bufs[0].str)-i,
+			 ",%d", ntohs(*DCC_SU_PORTP(su)));
+	}
+	return s;
+}
+
+
+
+const char *
+ifp_rem_str(const IFLOD_INFO *ifp)
+{
+	if (!ifp)
+		return "(null ifp)";
+	return rem_str(ifp->rem_hostname, &ifp->rem_su);
+}
+
+
+
+const char *
+ofp_rem_str(const OFLOD_INFO *ofp)
+{
+	if (!ofp)
+		return "(null ofp)";
+	return rem_str(ofp->rem_hostname, &ofp->rem_su);
+}
+
+
+
+static const char *
+rpt_id(const char *type, const DB_RCD* rcd,
+       const IFLOD_INFO *ifp)
+{
+	static int bufno;
+	static struct {
+	    char    str[120];
+	} bufs[4];
+	char *s;
+	char id_buf[30];
+
+	s = bufs[bufno].str;
+	bufno = (bufno+1) % DIM(bufs);
+
+	snprintf(s, sizeof(bufs[0].str), "%s%s %s ID=%s %s%s",
+		 type ? "flooded " : "",
+		 type ? type : "",
+		 ts2str_err(&rcd->ts),
+		 id2str(id_buf, sizeof(id_buf), rcd->srvr_id_auth),
+		 ifp ? "from " : "",
+		 ifp ? ifp_rem_str(ifp) : "");
+	return s;
+}
+
+
+
+void PATTRIB(2,3)
+flod_cnterr(const FLOD_LIMCNT *lc, const char *p, ...)
+{
+	char buf[200];
+	va_list args;
+
+	va_start(args, p);
+	if (lc->cur < lc->lim + FLOD_LIM_COMPLAINTS) {
+		dcc_verror_msg(p, args);
+	} else {
+		vsnprintf(buf, sizeof(FLOD_EMSG), p, args);
+		dcc_error_msg("%s; stop complaints", buf);
+	}
+	va_end(args);
+}
+
+
+
+static void
+date_err_msg(FLOD_EMSG out, int len, const char *in)
+{
+	memcpy(out, in, len);
+	if (len >= ISZ(FLOD_EMSG)-LITZ(" at hh:mm:ss")-1) {
+		out[len] = '\0';
+	} else {
+		dcc_time2str(&out[len], ISZ(FLOD_EMSG)-len, " at %T",
+			     db_time.tv_sec);
+	}
+}
+
+
+
+/* remove extra quotes and strings from a message to or from a peer */
+static void
+trim_err_msg(FLOD_EMSG out, const FLOD_EMSG in)
+{
+	char *q1, *q2;
+	int len;
+
+	q1 = strchr(in, '\'');
+	if (q1) {
+		q2 = strrchr(++q1, '\'');
+		if (q2) {
+			len = q2-q1;
+			if (len > LITZ(DCC_FLOD_OK_STR)
+			    && !LITCMP(q1, DCC_FLOD_OK_STR)) {
+				len -= LITZ(DCC_FLOD_OK_STR);
+				q1 += LITZ(DCC_FLOD_OK_STR);
+			}
+			date_err_msg(out, len, q1);
+			return;
+		}
+	}
+	date_err_msg(out, strlen(in), in);
+}
+
+
+
+/* report a flooding error */
+void PATTRIB(4,5)
+rpt_err(OFLOD_INFO *ofp,
+	u_char trace,			/* 0=error, 1=trace, 2=no dup trace */
+	u_char in,			/* 0=output 1=input */
+	const char *p, ...)
+{
+	FLOD_MMAP *mp;
+	LAST_ERROR *ep;
+	FLOD_EMSG tmp, trimmed;
+	va_list args;
+
+	va_start(args, p);
+	vsnprintf(tmp, sizeof(FLOD_EMSG), p, args);
+	va_end(args);
+
+	mp = ofp ? ofp->mp : 0;
+
+	if (trace != 0) {
+		if (!mp) {
+			TMSG_FLOD(ofp, tmp);
+			return;
+		}
+
+		trim_err_msg(trimmed, tmp);
+		ep = in ? &mp->iflod_err : &mp->oflod_err;
+		if (TMSG_FB(ofp)
+		    && (trace < 2
+			|| strcmp(trimmed, ep->trace_msg)
+			|| ep->trace_gen != flod_trace_gen)) {
+			/* suppress some duplicate flooding messages */
+			dcc_trace_msg(tmp);
+			ep->trace_gen = flod_trace_gen;
+		}
+		strncpy(ep->trace_msg, trimmed, sizeof(ep->trace_msg));
+		ep->complained = 0;
+
+	} else {
+		if (!mp) {
+			dcc_error_msg(tmp);
+			return;
+		}
+
+		dcc_error_msg(tmp);
+
+		ep = in ? &mp->iflod_err : &mp->oflod_err;
+		trim_err_msg(ep->msg, tmp);
+		ep->trace_msg[0] = '\0';
+		ep->complained = 0;
+	}
+}
+
+
+
+u_char
+set_flod_socket(OFLOD_INFO *ofp, u_char in, int s,
+		const char *hostname, const DCC_SOCKU *sup)
+{
+#if IP_TOS
+	static u_char tos_ok = 1;
+#endif
+	int on;
+
+	if (0 > fcntl(s, F_SETFD, FD_CLOEXEC))
+		rpt_err(ofp, 0, in, "fcntl(%s, F_SETFD, FD_CLOEXEC): %s",
+			rem_str(hostname, sup), ERROR_STR());
+
+	if (-1 == fcntl(s, F_SETFL,
+			fcntl(s, F_GETFL, 0) | O_NONBLOCK)) {
+		rpt_err(ofp, 0, in, "fcntl(%s, O_NONBLOCK): %s",
+			rem_str(hostname, sup), ERROR_STR());
+		return 0;
+	}
+
+	on = 1;
+	if (0 > setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
+			   &on, sizeof(on)))
+		rpt_err(ofp, 0, in, "setsockopt(flod %s, SO_KEEPALIVE): %s",
+			rem_str(hostname, sup), 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)))
+			rpt_err(ofp, 0, in, "setsockopt(%s, SO_SNDBUF): %s",
+				rem_str(hostname, sup), 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))) {
+			rpt_err(ofp, 0, in,
+				"setsockopt(IP_TOS, IPTOS_THROUGHPUT, %s): %s",
+				rem_str(hostname, sup), 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)
+{
+	int status;
+
+	if (resolve_hosts_pid < 0)
+		return 1;
+
+	if (resolve_hosts_pid == waitpid(resolve_hosts_pid, &status, WNOHANG)) {
+		resolve_hosts_pid = -1;
+		return 1;
+	}
+
+	RUSH_NEXT_FLODS_CK();
+	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 & FLODMAP_FG_PASSIVE))
+			continue;
+		ipv6 = ((mp->flags & FLODMAP_FG_IPv4) ? 0
+			: (mp->flags & FLODMAP_FG_IPv6) ? 1
+			: use_ipv6 ? 2 : 0);
+		dcc_host_lock();
+		if (mp->flags & FLODMAP_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_PORTP(&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)
+{
+	FLOD_MMAP *mp;
+
+	if (!flod_mmaps)
+		return 0;
+
+	/* wait for background job to finish */
+	if (!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;
+	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);
+
+	if (!background) {
+		TMSG(FLOD, "resolving hostnames in the foreground");
+		flod_names_resolve();
+		return 1;
+	}
+
+	resolve_hosts_pid = fork();
+	if (resolve_hosts_pid > 0) {
+		/* check again soon */
+		RUSH_NEXT_FLODS_CK();
+		return 0;
+	}
+
+	if (resolve_hosts_pid == -1) {
+		dcc_error_msg("fork(flood names resolve start): %s;"
+			      " fall back to foreground resolving",
+			      ERROR_STR());
+		flod_names_resolve();
+		return 1;
+	}
+
+	TMSG(FLOD, "resolving hostnames started");
+	/* close files and sockets to avoid interfering with parent */
+	db_close(-1);
+	after_fork();
+
+	flod_names_resolve();
+
+	TMSG(FLOD, "resolving hostnames finished");
+
+	exit(0);
+}
+
+
+
+static void
+iflod_clear(IFLOD_INFO *ifp,
+	    u_char fail)		/* 1=problems so delay restart */
+{
+	OFLOD_INFO *ofp;
+	FLOD_MMAP *mp;
+
+	ofp = ifp->ofp;
+	if (fail && ofp != 0) {
+		mp = ofp->mp;
+		if (mp->itimers.retry_secs < FLOD_RETRY_SECS)
+			mp->itimers.retry_secs = FLOD_RETRY_SECS;
+		mp->itimers.retry = db_time.tv_sec + mp->itimers.retry_secs;
+		if ((mp->flags & FLODMAP_FG_ACT) != 0)
+			TMSG3_FLOD(ofp,
+				   "postpone restarting %s flood from"
+				   " %s for %d seconds",
+				   (mp->flags & FLODMAP_FG_SOCKS)
+				   ? "SOCKS"
+				   : (mp->flags & FLODMAP_FG_NAT)
+				   ? "NAT"
+				   : "auto-NAT",
+				   ofp_rem_str(ofp), mp->itimers.retry_secs);
+	}
+
+	/* do not close the socket here, because it may be a passive outgoing
+	 * stream that is being converted  */
+	if (ifp->soc >= 0)
+		--iflods.open;
+
+	memset(ifp, 0, sizeof(*ifp));
+	ifp->soc = -1;
+
+	if (iflods.open == 0
+	    && oflods.open == 0
+	    && flods_st != FLODS_ST_ON)
+		oflods_clear();
+}
+
+
+
+void PATTRIB(5,6)
+iflod_close(IFLOD_INFO *ifp,
+	    u_char fail,		/* 1=already sick; more is no problem */
+	    u_char complain,		/* 1=error not just trace message */
+	    u_char send_reason,
+	    const char *pat, ...)
+{
+	struct {
+	    DCC_FLOD_POS    last_pos;
+	    DCC_FLOD_RESP   e;
+	    u_char	    null;	/* to eat '\0' for e.msg */
+	} resp;
+	void *wp;
+	va_list args;
+	OFLOD_INFO *ofp;
+	struct linger nowait;
+	int wlen;
+
+	ofp = ifp->ofp;
+
+	db_ptr2flod_pos(resp.e.end.pos, DCC_FLOD_POS_END);
+	va_start(args, pat);
+	/* Throw away the last byte of resp.e.end.msg because the too smart
+	 * by half gcc Fortify nonsense won't allow ISZ(resp.e.end.msg)+1.
+	 * That put the would the unwanted '\0' from vsnprintf() into
+	 * resp.null*/
+	wlen = vsnprintf(resp.e.end.msg, ISZ(resp.e.end.msg), pat, args);
+	va_end(args);
+	if (wlen > ISZ(resp.e.end.msg))
+		wlen = ISZ(resp.e.end.msg);
+	wlen += FLOD_END_OVHD;
+
+	/* If useful, prefix our final message with our final position
+	 * The peer will see our position and final operation as two
+	 * separate responses. */
+	if (memcmp(ifp->pos, ifp->pos_sent, ISZ(ifp->pos))) {
+		memcpy(resp.last_pos, ifp->pos, ISZ(resp.last_pos));
+		wlen += ISZ(resp.last_pos);
+		wp = &resp.last_pos;
+	} else {
+		wp = &resp.e;
+	}
+
+	if (send_reason) {
+		rpt_err(ofp, !complain, 1,
+			"stop incoming flood; %ssend '%s' to %s",
+			fail ? "error, " : "",
+			resp.e.end.msg,
+			ifp_rem_str(ifp));
+
+		/* send the final status report to the sending flooder */
+		iflod_write(ifp, wp, wlen, "stop incoming flood", fail ? 2 : 1);
+	} else {
+		rpt_err(ofp, !complain, 1,
+			"stop incoming flood; %s'%s'",
+			fail ? "error, " : "", resp.e.end.msg);
+	}
+
+	if (ifp->soc >= 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->soc, SOL_SOCKET, SO_LINGER,
+					   &nowait, sizeof(nowait))
+			    && !fail)
+				dcc_error_msg("setsockopt(SO_LINGER %s): %s",
+					      ifp_rem_str(ifp), ERROR_STR());
+		}
+
+		if (0 > close(ifp->soc)
+		    && !fail) {
+			if (errno == ECONNRESET)
+				TMSG2_FLOD(ofp, "close(flood from %s): %s",
+					   ifp_rem_str(ifp), ERROR_STR());
+			else
+				dcc_error_msg("close(flood from %s): %s",
+					      ifp_rem_str(ifp), ERROR_STR());
+		}
+	}
+
+	/* if this was not a new duplicate connection being discarded,
+	 * break the association with the outgoing stream */
+	if (ofp != 0 && ofp->ifp == ifp) {
+		save_flod_cnts(ofp);
+		ofp->ifp = 0;
+	}
+
+	iflod_clear(ifp, fail);
+}
+
+
+
+/* can close the incoming flood and so clear things */
+static u_char				/* 0=failed & should be or is closed */
+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 (ifp->ofp
+	    && (ifp->ofp->o_opts.flags & FLOD_OPT_SOCKS)) {
+		i = Rsend(ifp->soc, buf, buf_len, 0);
+	} else {
+		/* 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. */
+		i = send(ifp->soc, buf, buf_len, 0);
+	}
+	if (i == buf_len) {
+		ifp->iflod_alive = db_time.tv_sec;
+		return 1;
+	}
+
+	if (i < 0) {
+		if (close_it == 0) {
+			iflod_close(ifp, 1, 0, 0, "send(%s %s): %s",
+				    type, ifp_rem_str(ifp), ERROR_STR());
+		} else if (close_it == 1) {
+			dcc_error_msg("send(%s %s): %s",
+				      type, ifp_rem_str(ifp), ERROR_STR());
+		}
+	} else {
+		if (close_it == 0) {
+			iflod_close(ifp, 1, 0, 0, "send(%s %s)=%d not %d",
+				    type, ifp_rem_str(ifp), i, buf_len);
+		} else if (close_it == 1) {
+			dcc_error_msg("send(%s %s)=%d not %d",
+				      type, ifp_rem_str(ifp), i, buf_len);
+		}
+	}
+	return 0;
+}
+
+
+
+/* send our current position to the peer
+ *	the peer must be well known so that ifp->ofp->mp!=0, usually
+ *	    because (ifp->flags & IFLOD_FG_VERS_CK)
+ *	can close the incoming flood and so clear things */
+int					/* -1=fail 0=nothing to send, 1=sent */
+iflod_send_pos(IFLOD_INFO *ifp,
+	       u_char force)		/* say just anything */
+{
+	DCC_FLOD_POS req;
+	OFLOD_INFO *ofp;
+	FLOD_MMAP *mp;
+
+	ofp = ifp->ofp;
+	mp = ofp->mp;
+
+	/* ask peer to start over if our database has been cleared */
+	if (mp->flags & FLODMAP_FG_FFWD_IN) {
+		mp->flags &= ~(FLODMAP_FG_FFWD_IN
+			       | FLODMAP_FG_NEED_REWIND);
+		memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent));
+		db_ptr2flod_pos(req, DCC_FLOD_POS_FFWD_IN);
+		dcc_trace_msg("ask %s to FFWD flood to us",
+			      ifp_rem_str(ifp));
+		if (!iflod_write(ifp, req, sizeof(req), "ffwd request", 0))
+			return -1;
+		return 1;
+	}
+	if (mp->flags & FLODMAP_FG_NEED_REWIND) {
+		mp->flags &= ~FLODMAP_FG_NEED_REWIND;
+		memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent));
+		db_ptr2flod_pos(req, DCC_FLOD_POS_REWIND);
+		dcc_trace_msg("ask %s to rewind flood to us",
+			      ifp_rem_str(ifp));
+		if (!iflod_write(ifp, req, sizeof(req), "rewind request", 0))
+			return -1;
+		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));
+		if (!iflod_write(ifp, ifp->pos_sent, sizeof(ifp->pos_sent),
+				 "confirmed pos", 0))
+			return -1;
+
+		/* reset the no-connection-from-peer complaint delay */
+		mp->itimers.msg_secs = FLOD_IN_COMPLAIN1;
+		mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN1;
+
+		/* things are going well, so forget old input complaints */
+		if (!(mp->flags & FLODMAP_FG_IN_SRVR)) {
+			mp->iflod_err.msg[0] = '\0';
+			mp->iflod_err.trace_msg[0] = '\0';
+		}
+
+		/* limit the backoff for outgoing connection attempts
+		 * while the incoming connection is working */
+		DB_ADJ_TIMER(&mp->otimers.retry, &mp->otimers.retry_secs,
+			     FLOD_RETRY_SECS);
+
+		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) {
+		DCC_FLOD_RESP buf;
+
+		db_ptr2flod_pos(buf.note.op, DCC_FLOD_POS_NOTE);
+		strcpy(buf.note.str, "are you there?");
+		buf.note.len = sizeof("are you there?") + FLOD_NOTE_OVHD;
+		TMSG1_FLOD(ofp, "flood note to %s: \"are you there?\"",
+			   ifp_rem_str(ifp));
+		if (!iflod_write(ifp, &buf.note, buf.note.len, buf.note.str, 0))
+			return -1;
+		return 1;
+	}
+
+	return 0;
+}
+
+
+
+void
+iflod_listen_close(SRVR_SOC *sp)
+{
+	if (sp->listen < 0)
+		return;
+
+	TMSG1(FLOD, "stop flood listening on %s", dcc_su2str_err(&sp->su));
+	if (0 > close(sp->listen))
+		TMSG2(FLOD, "close(flood listen on %s): %s",
+		      dcc_su2str_err(&sp->su), ERROR_STR());
+	sp->listen = -1;
+}
+
+
+
+/* send stop requests to DCC servers flooding to us
+ *	can close the incoming flood and so clear things */
+void
+iflods_stop(const char *reason,
+	    u_char force)		/* 1=now */
+{
+	SRVR_SOC *sp;
+	IFLOD_INFO *ifp;
+	DCC_FLOD_POS end_req;
+
+	/* stop listening for new connections */
+	for (sp = srvr_socs; sp; sp = sp->fwd) {
+		iflod_listen_close(sp);
+	}
+
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+		if (ifp->soc < 0)
+			continue;
+
+		/* start shutting down each real, still alive connection */
+		if (!(ifp->flags & IFLOD_FG_END_REQ)
+		    && (ifp->flags & IFLOD_FG_VERS_CK)) {
+			if (!reason || !*reason)
+				rpt_err(ifp->ofp, 1, 1,
+					"flood from %s stopping",
+					ifp_rem_str(ifp));
+			else
+				rpt_err(ifp->ofp, 1, 1,
+					"flood from %s stopping: '%s'",
+					ifp_rem_str(ifp), reason);
+
+			/* send any delay position and then a stop request */
+			if (0 <= iflod_send_pos(ifp, 0)) {
+				db_ptr2flod_pos(end_req, DCC_FLOD_POS_END_REQ);
+				iflod_write(ifp, end_req, sizeof(end_req),
+					    "flood stop req", 0);
+				ifp->flags |= IFLOD_FG_END_REQ;
+			}
+
+			/* done if the socket died */
+			if (ifp->soc < 0)
+				continue;
+		}
+
+		/* break the connection if forced or never authenticated */
+		if (force || !(ifp->flags & IFLOD_FG_VERS_CK)) {
+			if (!reason || !*reason)
+				iflod_close(ifp, 1, 0, 1,
+					    "flooding off at %s",
+					    our_hostname);
+			else
+				iflod_close(ifp, 1, 0, 1,
+					    "flooding off at %s; %s",
+					    our_hostname, reason);
+			continue;
+		}
+
+		/* break the conneciton if the peer is too slow */
+		if ((ifp->flags & IFLOD_FG_END_REQ)
+		    && IFP_DEAD(ifp, stopint
+				? SHUTDOWN_DELAY : KEEPALIVE_IN_STOP)) {
+			if (!reason || !*reason)
+				iflod_close(ifp, 1, 0, 1,
+					    DCC_FLOD_OK_STR" force close from"
+					    " %s",
+					    ifp_rem_str(ifp));
+			else
+				iflod_close(ifp, 1, 0, 1,
+					    DCC_FLOD_OK_STR" force close from"
+					    " %s; %s",
+					    ifp_rem_str(ifp), reason);
+			continue;
+		}
+	}
+}
+
+
+
+/* start receiving checksums from another DCC server */
+void
+iflod_start(SRVR_SOC *sp)
+{
+	IFLOD_INFO *ifp;
+	DCC_SOCKLEN_T l;
+	struct in6_addr peer_addr;
+	int count;
+	const struct in6_addr *cap;
+	RL *rl;
+
+	/* accept all waiting connections to avoid starving any	*/
+	for (count = 0; count <= DCCD_MAX_FLOODS; ++count) {
+		/* find a free input flooding slot */
+		for (ifp = iflods.infos; ifp->soc >= 0; ++ifp) {
+			if (ifp > LAST(iflods.infos)) {
+				if (!(complained_many_iflods++))
+					dcc_error_msg("too many floods");
+				goto again;
+			}
+		}
+
+		l = sizeof(ifp->rem_su);
+		ifp->soc = accept(sp->listen, &ifp->rem_su.sa, &l);
+		if (ifp->soc < 0) {
+			if (!DCC_BLOCK_ERROR() || count == 0)
+				dcc_error_msg("accept(flood): %s", ERROR_STR());
+			return;
+		}
+
+		/* use the IP address as the host name until we know which
+		 * peer it is */
+		dcc_su2str2(ifp->rem_hostname, sizeof(ifp->rem_hostname),
+			    &ifp->rem_su);
+		if (!set_flod_socket(0, 1, ifp->soc, ifp->rem_hostname,
+				     &ifp->rem_su)) {
+			close(ifp->soc);
+			ifp->soc = -1;
+			continue;
+		}
+
+		/* quietly forget this peer if it is blacklisted */
+		if (ifp->rem_su.sa.sa_family == AF_INET6) {
+			cap = &ifp->rem_su.ipv6.sin6_addr;
+		} else {
+			dcc_ipv4toipv6(&peer_addr, ifp->rem_su.ipv4.sin_addr);
+			cap = &peer_addr;
+		}
+		rl = 0;
+		if (ck_ip_bl(&rl, DCC_ID_SRVR_ROGUE, cap)) {
+			char buf[120];
+			int len;
+
+			rl_inc(rl, &rl_anon_rate);
+			len = snprintf(buf, sizeof(buf)-1,
+				       "rejected blacklisted flood from %s",
+				       ifp_rem_str(ifp));
+			if (len > ISZ(buf))
+				len = sizeof(buf);
+			if ((rl->d.flags & RL_FG_TRACE)
+			    || (dccd_tracemask & DCC_TRACE_FLOD_BIT))
+				dcc_trace_msg(buf);
+			buf[len++] = '\n';
+			send(ifp->soc, buf, len, 0);
+			close(ifp->soc);
+			ifp->soc = -1;
+			continue;
+		}
+
+		/* reset timer that delays responses to clients while flooding
+		 * is stopped */
+		if (++iflods.open == 1)
+			iflods_ok_timer = db_time.tv_sec + IFLODS_OK_SECS;
+
+		ifp->flags |= IFLOD_FG_CONNECTED;
+		ifp->iflod_alive = db_time.tv_sec;
+
+		TMSG1(FLOD, "start flood from %s", ifp_rem_str(ifp));
+again:;
+	}
+}
+
+
+
+static void
+iflod_socks_backoff(OFLOD_INFO *ofp)
+{
+	FLOD_MMAP *mp;
+
+	mp = ofp->mp;
+	mp->itimers.retry_secs *= 2;
+	if (mp->itimers.retry_secs > FLOD_MAX_RETRY_SECS)
+		mp->itimers.retry_secs = FLOD_MAX_RETRY_SECS;
+	else if (mp->itimers.retry_secs < FLOD_RETRY_SECS)
+		mp->itimers.retry_secs = FLOD_RETRY_SECS;
+}
+
+
+
+/* Start an incoming SOCKS flood by connecting to the other system.
+ *	We will eventually turn the connection around and pretend that
+ *	the other system initiated the TCP connection.
+ *	This can close the flood and so clear things */
+static int				/* -1=failure, 0=not yet, 1=done */
+iflod_socks_connect(IFLOD_INFO *ifp)
+{
+	OFLOD_INFO *ofp;
+	DCC_SRVR_ID id;
+	DCC_FLOD_VERSION_HDR buf;
+	DCC_FNM_LNO_BUF fnm_buf;
+	const char *emsg;
+	int i;
+
+	ofp = ifp->ofp;
+
+	memset(&buf, 0, sizeof(buf));
+	strcpy(buf.body.str, version_str(ofp));
+	id = htons(my_srvr_id);
+	memcpy(buf.body.sender_srvr_id, &id, sizeof(buf.body.sender_srvr_id));
+	buf.body.turn = 1;
+	emsg = flod_sign(ofp, 1, &buf, sizeof(buf));
+	if (emsg) {
+		iflod_socks_backoff(ofp);
+		iflod_close(ifp, 1, 1, 1, "%s %d%s",
+			    emsg, ofp->out_passwd_id,
+			    fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		return -1;
+	}
+
+	if (ofp->o_opts.flags & FLOD_OPT_SOCKS) {
+		i = Rconnect(ifp->soc, &ifp->rem_su.sa,
+			     DCC_SU_LEN(&ifp->rem_su));
+	} else {
+		/* must be NAT or assumed NAT */
+		i = connect(ifp->soc, &ifp->rem_su.sa,
+			    DCC_SU_LEN(&ifp->rem_su));
+	}
+	if (0 > i && errno != EISCONN) {
+		if (errno == EAGAIN
+		    || errno == EINPROGRESS
+		    || errno == EALREADY) {
+			rpt_err(ofp, 1, 0, "starting flood from %s",
+				ifp_rem_str(ifp));
+			return 0;
+		}
+
+		/* it is lame to only trace instead of reporting EINVAL as
+		 * an error, but several UNIX-like systems return EINVAL for
+		 * the second connect() after a Unreachable ICMP message
+		 * or after a timeout */
+		rpt_err(ofp,
+			(errno == EINVAL || errno == ECONNABORTED
+			 || errno == ECONNRESET || errno == ETIMEDOUT
+			 || errno == ECONNREFUSED),
+			1, "connect(SOCKS FLOD %s): %s",
+			ifp_rem_str(ifp),
+			errno == EINVAL
+			? "likely connection refused or local firewall"
+			: ERROR_STR());
+		close(ifp->soc);
+		iflod_socks_backoff(ofp);
+		iflod_clear(ifp, 1);
+
+		return -1;
+	}
+
+	ifp->flags |= (IFLOD_FG_CONNECTED | IFLOD_FG_CLIENT);
+
+	rpt_err(ofp, 1, 1, "starting SOCKS from %s", ifp_rem_str(ifp));
+
+	/* After the SOCKS incoming flood socket is connected,
+	 *	send our authentication to convince the peer to send its
+	 *	authentication and then its checksums. */
+	if (!iflod_write(ifp, &buf, sizeof(buf),
+			 "flood SOCKS authentication", 0))
+		return -1;
+	return 1;
+}
+
+
+
+/* Request the start of an input flood via SOCKS if it is not already flowing,
+ *	by connecting to the remote system.
+ *	This can close the flood and so clear things */
+void
+iflod_socks_start(OFLOD_INFO *ofp)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	IFLOD_INFO *ifp, *ifp1;
+
+	/* do nothing if it is already running or is not using SOCKS */
+	if (ofp->ifp
+	    || (ofp->mp->flags & (FLODMAP_FG_SOCKS | FLODMAP_FG_NAT)) == 0
+	    || IFLOD_OPT_OFF_ROGUE(ofp))
+		return;
+	if (!DB_IS_TIME(ofp->mp->itimers.retry, ofp->mp->itimers.retry_secs))
+		return;
+
+	/* 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->soc < 0) {
+			if (!ifp)
+				ifp = ifp1;
+		}
+		/* there is nothing to do if it already exists */
+		if (ifp1->ofp == ofp)
+			return;
+	}
+	if (!ifp) {
+		rpt_err(ofp, ++complained_many_iflods == 1 ? 0 : 2, 1,
+			"too many incoming floods to start SOCKS from %s",
+			ofp->rem_hostname);
+		iflod_socks_backoff(ofp);
+		return;
+	}
+
+	if (!flod_names_resolve_start())
+		return;			/* wait for name resolution */
+	if (ofp->mp->rem_su.sa.sa_family == AF_UNSPEC) {
+		rpt_err(ofp, 0, 1, "SOCKS flood peer name %s: %s%s",
+			ofp->rem_hostname, DCC_HSTRERROR(ofp->mp->host_error),
+			fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		iflod_socks_backoff(ofp);
+		return;
+	}
+	if (!LITCMP(ofp->mp->oflod_err.msg, "SOCKS flood peer name "))
+		ofp->mp->oflod_err.msg[0] = '\0';
+
+	ifp->ofp = ofp;
+	ifp->rem_su = ofp->rem_su = ofp->mp->rem_su;
+	STRLCPY(ifp->rem_hostname, ofp->rem_hostname,
+		sizeof(ifp->rem_hostname));
+
+	ifp->soc = socket(ifp->rem_su.sa.sa_family, SOCK_STREAM, 0);
+	if (ifp->soc < 0) {
+		rpt_err(ofp, 0, 1, "socket(SOCKS flood %s): %s",
+			ifp_rem_str(ifp), ERROR_STR());
+		iflod_socks_backoff(ofp);
+		return;
+	}
+
+	if (!set_flod_socket(ofp, 1, ifp->soc,
+			     ifp->rem_hostname, &ifp->rem_su)) {
+		close(ifp->soc);
+		ifp->soc = -1;
+		iflod_socks_backoff(ofp);
+		return;
+	}
+
+	/* reset timer that delays responses to clients while flooding is
+	 * not working */
+	if (++iflods.open == 1)
+		iflods_ok_timer = db_time.tv_sec + IFLODS_OK_SECS;
+
+	iflod_socks_connect(ifp);
+}
+
+
+
+/* See if the new report is a duplicate of an old record in the database
+ *	db_sts.rcd.d points to the old record */
+static int				/* -1=continue, 0=not dup, 1=dup */
+ck_dup_rcd(IFLOD_INFO *ifp,
+	   const DB_RCD *new, DCC_TGTS new_tgts_raw)
+{
+	DCC_TGTS old_tgts;
+	const DB_RCD_CK *new_ck, *old_ck;
+	int new_num_cks, old_num_cks;
+	int new_unique, old_unique;
+
+	/* ignore reports for deleted checksums
+	 * unless they are new reports or delete requests */
+	old_tgts = DB_TGTS_RCD_RAW(db_sts.rcd.d.r);
+	if (old_tgts == DCC_TGTS_DEL
+	    && new_tgts_raw != DCC_TGTS_DEL
+	    && !dcc_ts_newer_ts(&new->ts, &db_sts.rcd.d.r->ts)) {
+		if (CK_FLOD_CNTERR(&ifp->ofp->lc.stale)
+		    && TMSG_FB2(ifp->ofp))
+			flod_cnterr(&ifp->ofp->lc.stale,
+				    "ignore deleted %s after %s",
+				    rpt_id("report", new, ifp),
+				    rpt_id(0, db_sts.rcd.d.r, 0));
+		return 1;
+	}
+
+	/* not duplicate if the server-IDs or timestamps differ */
+	if (DB_RCD_ID(new) != DB_RCD_ID(db_sts.rcd.d.r)
+	    || memcmp(&new->ts, &db_sts.rcd.d.r->ts, sizeof(new->ts)))
+		return -1;
+
+	/* We know we have a duplicate
+	 * Stop looking if the old record has been deleted */
+	if (old_tgts == 0)
+		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;
+
+	/* See if one record is a subset of the other */
+	new_unique = 0;
+	old_unique = 0;
+	old_num_cks = DB_NUM_CKS(db_sts.rcd.d.r);
+	old_ck = db_sts.rcd.d.r->cks;
+	while (old_num_cks != 0 && new_num_cks != 0) {
+		if (DB_CK_TYPE(old_ck) == DCC_CK_FLOD_PATH) {
+			/* skip paths in the old record */
+			++old_ck;
+			--old_num_cks;
+		} else if (DB_CK_TYPE(old_ck) == DB_CK_TYPE(new_ck)) {
+			/* skip identical checksums */
+			++old_ck;
+			--old_num_cks;
+			++new_ck;
+			--new_num_cks;
+		} else if (DB_CK_TYPE(old_ck) < DB_CK_TYPE(new_ck)) {
+			/* skip unique checksum in the old record,
+			 * using the ordering of checksums in records */
+			++old_unique;
+			++old_ck;
+			--old_num_cks;
+		} else {
+			/* skip unique checksum in the new record */
+			++new_unique;
+			++new_ck;
+			--new_num_cks;
+		}
+	}
+
+	/* forget the new record if it has nothing unique */
+	if (new_unique+new_num_cks == 0) {
+		if (CK_FLOD_CNTERR(&ifp->ofp->lc.dup)
+		    && TMSG_FB2(ifp->ofp))
+			flod_cnterr(&ifp->ofp->lc.dup, "%sduplicate %s",
+				    old_unique+old_num_cks == 0
+				    ? "" : "subset ",
+				    rpt_id("report", new, ifp));
+
+		return 1;
+	}
+
+	/* Keep both records if each has unique checksums
+	 * This inflates the count for common checksums, so hope it
+	 * is rare. */
+	if (old_unique+old_num_cks != 0) {
+		if (CK_FLOD_CNTERR(&ifp->ofp->lc.dup)
+		    && TMSG_FB2(ifp->ofp))
+			flod_cnterr(&ifp->ofp->lc.dup, "partial duplicate %s",
+				    rpt_id("report", new, ifp));
+
+		return 0;
+	}
+
+	/* Delete the original report if it has nothing unique
+	 *	This results in doubling the contribution to the total for
+	 *	each checksum from this report in our database until the
+	 *	next time dbclean is run.  At worst this could push a
+	 *	DCC client over its bulk threshold */
+	DB_TGTS_RCD_SET(db_sts.rcd.d.r, 0);
+	SET_FLUSH_RCD_HDR(&db_sts.rcd, 1);
+
+	if (CK_FLOD_CNTERR(&ifp->ofp->lc.dup)
+	    && TMSG_FB2(ifp->ofp))
+		flod_cnterr(&ifp->ofp->lc.dup, "superset duplicate %s",
+			    rpt_id("report", new, ifp));
+	return 0;
+}
+
+
+
+/* see if the new record is a duplicate of any existing records containing
+ * the specified checksum
+ *	use db_sts.rcd for the initial record and the chain */
+static int				/* -1=fail,  0=not dup,  1=duplicate */
+ck_dup_ck_chain(IFLOD_INFO *ifp, const DB_RCD *new, DCC_TGTS new_tgts_raw,
+		DCC_CK_TYPES type, const DB_RCD_CK *found_ck)
+{
+	DB_PTR next_rcd_pos;
+	int cnt, i;
+
+	for (cnt = 0; ; ++cnt) {
+		i = ck_dup_rcd(ifp, new, new_tgts_raw);
+		if (i >= 0) {
+			if (cnt >= 50 && (dccd_tracemask & DCC_TRACE_DB_BIT))
+				dcc_trace_msg("long duplicate chain of %d"
+					      " from %s at %s",
+					      cnt, ifp->rem_hostname,
+					      rpt_id("report",new,ifp));
+			return i;
+		}
+
+		next_rcd_pos = DB_PTR_EX(found_ck->prev);
+		if (next_rcd_pos == DB_PTR_NULL)
+			return 0;
+		if (next_rcd_pos >= db_sts.rcd.s.rptr) {    /* prevent loops */
+			db_error_msg(__LINE__,__FILE__,
+				     "bad %s link of "L_HPAT" at "L_HPAT,
+				     DB_TYPE2STR(type),
+				     db_sts.rcd.s.rptr, next_rcd_pos);
+			return -1;
+		}
+
+		found_ck = db_map_rcd_ck(dcc_emsg, &db_sts.rcd,
+					 next_rcd_pos, type);
+		if (!found_ck) {
+			iflod_close(ifp, 1, 1, 1, "%s", dcc_emsg);
+			DB_ERROR_MSG(dcc_emsg);
+			return -1;
+		}
+	}
+}
+
+
+
+/* complain about a received flooded report,
+ *	and possibly close and so clear things */
+static u_char PATTRIB(6,7)		/* 0=flood closed */
+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 */
+{
+	DCC_FLOD_RESP buf;
+	const char *sc;
+	va_list args;
+	int i, len;
+
+	if (!lc && ifp->ofp)
+		lc = &ifp->ofp->lc.iflod_bad;
+	if (!lc) {
+		sc = "";
+	} else {
+		i = ++lc->cur - (lc->lim + FLOD_LIM_COMPLAINTS);
+		if (i > 0)
+			return 1;
+		sc = i < 0 ? "" : "; stop complaints";
+	}
+
+	va_start(args, pat);
+	len = vsnprintf(buf.note.str, sizeof(buf.note.str), pat, args);
+	if (len >= ISZ(buf.note.str))
+		len = sizeof(buf.note.str)-1;
+	va_end(args);
+
+	if (serious) {
+		dcc_error_msg("%s %s%s",
+			      buf.note.str, rpt_id(str, new, ifp), sc);
+		db_ptr2flod_pos(buf.note.op, DCC_FLOD_POS_COMPLAINT);
+	} else {
+		TMSG3_FLOD2(ifp->ofp, "%s %s%s",
+			    buf.note.str, rpt_id(str, new, ifp), sc);
+		db_ptr2flod_pos(buf.note.op, DCC_FLOD_POS_NOTE);
+	}
+
+	len += snprintf(&buf.note.str[len], sizeof(buf.note.str)-len, " %s%s",
+			rpt_id(str, new, 0), sc);
+	if (len >= ISZ(buf.note.str))
+		len = ISZ(buf.note.str)-1;
+
+	buf.note.len = len+1 + FLOD_NOTE_OVHD;
+	return iflod_write(ifp, &buf, buf.note.len, buf.note.str, 0);
+}
+
+
+
+static u_char
+parse_srvr_id(const DB_RCD_CK *ck, const IFLOD_INFO *ifp,
+	      const DB_RCD *new, DCC_SRVR_ID srvr_id)
+{
+	DCC_SRVR_ID tgt_id, type_id;
+	char buf1[24];
+	OPT_FLAGS opt_flags;
+	const OFLOD_INFO *ofp1;
+	ID_TBL *tp;
+
+	/* notice server-ID type announcements */
+	tgt_id = (ck->sum[1] << 8) + ck->sum[2];
+	type_id = srvr_id;
+	switch (type_id) {
+	case DCC_ID_SRVR_REP_OK:
+		type_id = DCC_ID_SRVR_SIMPLE;
+		opt_flags = 0;
+		break;
+	case DCC_ID_SRVR_SIMPLE:
+		opt_flags = FLOD_OPT_SIMPLE;
+		break;
+	case DCC_ID_SRVR_IGNORE:
+		opt_flags = 0;
+		break;
+	case DCC_ID_SRVR_ROGUE:
+		opt_flags = FLOD_OPT_ROGUE;
+		break;
+	default:
+		return 0;
+	}
+	if (ck->sum[0] != DCC_CK_SRVR_ID) {
+		return 0;
+	}
+
+	tp = find_srvr_type(tgt_id);
+	/* Restart flooding if the announced type of a peer changes. */
+	for (ofp1 = oflods.infos; ofp1 <= LAST(oflods.infos); ++ofp1) {
+		/* Wait a bit for more announcements before restarting
+		 * flooding.  When we restart flooding, we will
+		 * check the database. */
+		if (ofp1->rem_id == tgt_id) {
+			if (opt_flags != (ofp1->o_opts.flags
+					  & (FLOD_OPT_ROGUE
+					     | FLOD_OPT_SIMPLE))) {
+				if (TMSG_FB(ifp->ofp) || TMSG_FB(ofp1))
+					dcc_trace_msg("server-ID %d for %s"
+						      " changed to \"%s\""
+						      " in %s",
+						      tgt_id,
+						      ofp1->rem_hostname,
+						      id2str(buf1, sizeof(buf1),
+							  type_id),
+						      rpt_id("report",new,ifp));
+				if (flod_mtime > 1)
+					flod_mtime = 1;
+			}
+			break;
+		}
+	}
+
+	/* accept changes to our records */
+	tp->srvr_type = type_id;
+
+	return 1;
+}
+
+
+
+/* consider an incoming flooded report */
+static int				/* -1=failed, 0=not yet, else length */
+iflod_rpt(IFLOD_INFO *ifp, OFLOD_INFO *ofp,
+	  const DCC_FLOD_STREAM *stream, int max_len)
+{
+	DB_PTR pos;
+	DCC_TGTS new_tgts, found_tgts;
+	DB_RCD new;
+	DCC_SRVR_ID old_srvr, psrvr;
+	const DCC_CK *ck_lim, *ck;
+	const DB_RCD_CK *new_ck_lim, *srvr_id_ck;
+	DB_RCD_CK  *found_ck, *new_ck;
+	DCC_CK_TYPES type, prev_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;
+	ID_TBL *tp;
+	int i;
+
+	pos = flod_pos2db_ptr(stream->r.pos);
+	if (pos < DCC_FLOD_POS_MIN) {
+		iflod_close(ifp, 1, 1, 1,
+			    "bogus position "L_HPAT" in flooded report #%d",
+			    pos, ofp->cnts.total);
+		return -1;
+	}
+
+	/* wait for the header of the report */
+	if (max_len < DCC_FLOD_RPT_LEN(0)) {
+		return 0;
+	}
+
+	if (stream->r.num_cks == 0 || stream->r.num_cks > DCC_QUERY_MAX) {
+		iflod_close(ifp, 1, 1, 1,
+			    "impossible %d checksums in report #%d",
+			    stream->r.num_cks, ofp->cnts.total);
+		return -1;
+	}
+	rpt_len = DCC_FLOD_RPT_LEN(stream->r.num_cks);
+	if (rpt_len > max_len)
+		return 0;		/* wait for more */
+
+	if (db_failed_line)
+		return rpt_len;		/* can do nothing if database broken */
+
+	/* save the position to return to the sender */
+	memcpy(ifp->pos, stream->r.pos, sizeof(ifp->pos));
+
+	new.ts = stream->r.ts;
+	memcpy(&new.srvr_id_auth, stream->r.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(&new_tgts, stream->r.tgts, sizeof(new_tgts));
+	new_tgts = ntohl(new_tgts);
+	if (new_tgts == DCC_TGTS_DEL) {
+		if (!(ofp->i_opts.flags & FLOD_OPT_DEL_OK)) {
+			if (!iflod_rpt_complain(ifp, &new, 1,
+						&ofp->lc.not_deleted,
+						"delete request", "refuse"))
+				return -1;
+			return rpt_len;
+		}
+	} else if (new_tgts == 0
+		   || (new_tgts > DCC_TGTS_FLOD_RPT_MAX
+		       && new_tgts != DCC_TGTS_TOO_MANY)) {
+		iflod_close(ifp, 1, 1, 1, "bogus target count %s in %s",
+			    dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+					 new_tgts, grey_on),
+			    rpt_id("report", &new, 0));
+		return -1;
+	} else if (ofp->i_opts.flags & FLOD_OPT_TRAPS) {
+		/* comply if the source watches only spam traps */
+		new_tgts = DCC_TGTS_TOO_MANY;
+	}
+
+	/* notice reports from the distant future */
+	if (dcc_ts_newer_ts(&new.ts, &future)) {
+		if (!iflod_rpt_complain(ifp, &new, 1, &ofp->lc.stale,
+					"report", "future"))
+			return -1;
+		return rpt_len;
+	}
+
+	DB_TGTS_RCD_SET(&new, new_tgts);
+	new.fgs_num_cks = 0;
+	srvr_id_ck = 0;
+	stale = 1;
+	ck_lim = &stream->r.cks[stream->r.num_cks];
+	new_ck = new.cks;
+	num_path_blocks = 0;
+
+	tp = 0;
+	srvr_mapped = id_map(old_srvr, &ofp->i_opts);
+	switch (srvr_mapped) {
+	case ID_MAP_NO:
+		tp = find_srvr_type(old_srvr);
+		break;
+	case ID_MAP_REJ:
+		if (!iflod_rpt_complain(ifp, &new, 0, 0,
+					"rejected server-ID in", "refuse"))
+			return -1;
+		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 = stream->r.cks;
+	     ck < ck_lim;
+	     prev_type = type, ++ck) {
+		type = ck->type;
+		if (!DCC_CK_OK_FLOD(grey_on, type)) {
+			if (!iflod_rpt_complain(ifp, &new, 1, 0,
+						"report",
+						"unknown checksum type %s in",
+						DB_TYPE2STR(type)))
+				return -1;
+			continue;
+		}
+		if (ck->len != sizeof(*ck)) {
+			iflod_close(ifp, 1, 1, 1,
+				    "unknown checksum length %d in %s",
+				    ck->len, rpt_id("report", &new, 0));
+			return -1;
+		}
+		if (type <= prev_type && prev_type != DCC_CK_FLOD_PATH) {
+			if (!iflod_rpt_complain(ifp, &new, 1, 0,
+						"report",
+						"out of order %s checksum in",
+						DB_TYPE2STR(type)))
+				return -1;
+			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(ofp, "%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 {
+			/* 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;
+
+			/* server-ID declarations are never stale */
+			if (type == DCC_CK_SRVR_ID) {
+				stale = 0;
+				srvr_id_ck = new_ck;
+			}
+
+			/* Notice if this checksum makes the report timely
+			 * We cannot detect duplicates of reports that
+			 * have expired, so consider stale anything older
+			 * than our expiration.
+			 * Ignore reports of checksums from crazy servers */
+			if (stale
+			    && dcc_ts_newer_ts(&new.ts,
+					       new_tgts >= db_tholds[type]
+					       ? &db_parms.ex_spam[type]
+					       : &db_parms.ex_all[type])
+			    && (tp == 0
+				|| (tp->srvr_type != DCC_ID_SRVR_IGNORE
+				    && tp->srvr_type != DCC_ID_SRVR_ROGUE)))
+				stale = 0;
+		}
+
+		++new_ck;
+		++new.fgs_num_cks;
+	}
+	if (stale) {
+		if (CK_FLOD_CNTERR(&ofp->lc.stale)
+		    && TMSG_FB2(ofp))
+			flod_cnterr(&ofp->lc.stale, "stale %s",
+				    rpt_id("report", &new, ifp));
+		return rpt_len;
+	}
+
+	if (!DB_NUM_CKS(&new)) {
+		iflod_close(ifp, 1, 1, 1, "no known checksum types in %s",
+			    rpt_id("report", &new, 0));
+		return -1;
+	}
+
+	/* only now might we look at the database */
+	if (db_lock() < 0) {
+		iflod_close(ifp, 1, 1, 1, "iflod lock failure");
+		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 */
+	ok2 = 0;
+	new_ck_lim = &new.cks[DB_NUM_CKS(&new)];
+	for (new_ck = new.cks; new_ck < new_ck_lim; ++new_ck) {
+		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, 1, 1, "%s", dcc_emsg);
+			DB_ERROR_MSG(dcc_emsg);
+			return -1;
+
+		case DB_FOUND_IT:
+			/* At least this checksum is already in the database */
+			i = ck_dup_ck_chain(ifp, &new, new_tgts,
+					    type, found_ck);
+			if (i < 0)
+				return -1;  /* broken database */
+			if (i > 0)
+				return rpt_len;	/* duplicate */
+
+			/* Maybe not a duplicate.
+			 * Notice reports of checksums on the local server's
+			 * whitelist.
+			 * An ordinary checksum is whitelisted by DCC_TGTS_OK
+			 * or two reports with DCC_TGTS_OK2.
+			 * Greylisting uses DCC_TGTS_GREY_WHITE=DCC_TGTS_OK2
+			 * and so one report of DCC_TGTS_GREY_WHITE is enough */
+			found_tgts = DB_TGTS_CK(found_ck);
+			if (found_tgts == DCC_TGTS_OK
+			    || (found_tgts == DCC_TGTS_GREY_WHITE
+				&& (++ok2 >= 2 || grey_on))) {
+				if (!iflod_rpt_complain(ifp, &new, 0,
+							&ofp->lc.wlist,
+							"report","whitelisted"))
+					return -1;
+				return rpt_len;
+			}
+			break;
+
+		case DB_FOUND_EMPTY:
+		case DB_FOUND_CHAIN:
+		case DB_FOUND_INTRUDER:
+			/* We will fail to find this checksum in our database
+			 * if the new report is not a duplicate
+			 * or if it is a duplicate superset report */
+			break;
+		}
+	}
+
+	/* If the new report is a delete request,
+	 * then we need to run dbclean to fix all of the
+	 * totals affected by the deleted reports. */
+	if (new_tgts == DCC_TGTS_DEL) {
+		if (!(ofp->i_opts.flags & FLOD_OPT_NO_LOG_DEL))
+			dcc_trace_msg("accept %s",
+				      rpt_id("delete request", &new, ifp));
+		if (!DCC_CK_IS_REP_OP(grey_on, type) && !grey_on)
+			need_del_dbclean = "flood checksum deletion";
+	}
+
+	if (srvr_id_ck) {
+		/* discard translated server-ID declarations */
+		if (srvr_mapped == ID_MAP_SELF) {
+			TMSG2_FLOD(ofp, "translated server-ID from %d in %s",
+				   old_srvr, rpt_id("report", &new, ifp));
+			return rpt_len;
+		}
+
+		/* notice claims by other servers to our ID */
+		if (old_srvr == my_srvr_id) {
+			if (memcmp(host_id_sum, srvr_id_ck->sum,
+				   sizeof(host_id_sum)))
+				dcc_error_msg("host %s used our server-ID"
+					      " %d at %s",
+					      dcc_ck2str_err(DCC_CK_SRVR_ID,
+							srvr_id_ck->sum, 0),
+					      my_srvr_id,
+					      ts2str_err(&new.ts));
+			return rpt_len;
+		}
+
+		if (old_srvr < DCC_SRVR_ID_MIN
+		    && !parse_srvr_id(srvr_id_ck, ifp, &new, old_srvr))
+			return rpt_len;
+	}
+
+	/* the report is ok and not a duplicate, so add it to our database */
+	if (!add_dly_rcd(&new, 1)) {
+		iflod_close(ifp, 1, 1, 1, "%s", dcc_emsg);
+		return -1;
+	}
+
+	++ofp->cnts.accepted;
+	return rpt_len;
+}
+
+
+
+static void
+bad_vers(IFLOD_INFO *ifp,
+	 u_char fail)			/* 1=complain */
+{
+	iflod_close(ifp, fail, fail, 1,
+		    DCC_FLOD_BAD_VER_MSG" need \""
+		    DCC_FLOD_VERSION_CUR_STR
+		    "\" not \"%.*s\"",
+		    LITZ(DCC_FLOD_VERSION_STR_BASE)+10,
+		    ifp->ibuf.s.v.body.str);
+}
+
+
+
+/* authenticate and otherwise check a new incoming flood */
+static u_char				/* 0=closed or switched to output */
+check_iflod_vers(IFLOD_INFO *ifp)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	const DCC_FLOD_VERSION_HDR *vp;
+	OFLOD_INFO *ofp;
+	IFLOD_INFO *ifp1;
+	const ID_TBL *tp;
+	DCC_SRVR_ID rem_id;
+	int iversion;
+	int i;
+
+	vp = &ifp->ibuf.s.v;
+	if (!strcmp(vp->body.str, DCC_FLOD_VERSION_CUR_STR)) {
+		iversion = DCC_FLOD_VERSION_CUR;
+		ifp->flags |= IFLOD_FG_VERS_CK;
+
+#ifdef DCC_FLOD_VERSION7
+	} else if (!strcmp(vp->body.str, DCC_FLOD_VERSION7_STR)) {
+		iversion = DCC_FLOD_VERSION7;
+		ifp->flags |= IFLOD_FG_VERS_CK;
+
+#endif /* DCC_FLOD_VERSION7 */
+
+	} else if (!strncmp(vp->body.str, DCC_FLOD_VERSION_STR_BASE,
+			    LITZ(DCC_FLOD_VERSION_STR_BASE))) {
+		/* it seems to be a DCC server,
+		 * so complain after identifying the peer */
+		iversion = 1;
+
+	} else {
+		/* junk, so complain and give up */
+		bad_vers(ifp, 1);
+		return 0;
+	}
+
+	/* require a sane and familiar server-ID from the prospective peer */
+	memcpy(&rem_id, vp->body.sender_srvr_id, sizeof(rem_id));
+	rem_id = ntohs(rem_id);
+	if (rem_id < DCC_SRVR_ID_MIN
+	    || rem_id > DCC_SRVR_ID_MAX) {
+		iflod_close(ifp, 1, 1, 1, DCC_FLOD_BAD_ID_MSG" %d",
+			    rem_id);
+		return 0;
+	}
+	for (ofp = oflods.infos; ; ++ofp) {
+		if (ofp > LAST(oflods.infos)) {
+			iflod_close(ifp, 1, 1, 1, DCC_FLOD_BAD_ID_MSG" %d",
+				    rem_id);
+			return 0;
+		}
+		if (ofp->rem_id == rem_id) {
+			ifp->ofp = ofp;
+			STRLCPY(ifp->rem_hostname, ofp->rem_hostname,
+				sizeof(ifp->rem_hostname));
+			break;
+		}
+	}
+
+	/* ofp and ofp->mp are not null, because we now know which peer
+	 * it claims to be
+	 *
+	 * check that it knows the password */
+	i = ck_sign(&tp, 0, ofp->in_passwd_id, vp, sizeof(*vp));
+	if (!i) {
+		if (!tp)
+			iflod_close(ifp, 1, 1, 1, DCC_FLOD_PASSWD_ID_MSG" %d%s",
+				    ofp->in_passwd_id,
+				    fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		else
+			iflod_close(ifp, 1, 1, 1, DCC_FLOD_BAD_AUTH_MSG" %d",
+				    ofp->in_passwd_id);
+		return 0;
+	}
+	if (i == 1)
+		ofp->mp->flags &= ~FLODMAP_FG_USE_2PASSWD;
+	else
+		ofp->mp->flags |= FLODMAP_FG_USE_2PASSWD;
+
+	/* no more assumed NAT games because it has contacted us */
+	ofp->mp->flags &= ~FLODMAP_FG_NAT_AUTO;
+
+	/* Note the version of the protocol it is using so that we can use that
+	 * version when connecting to it. */
+	ofp->mp->iversion = iversion;
+	/* if we do not like its version, reject the connection and hope
+	 * that it will retry  with a version we like */
+	if (!(ifp->flags & IFLOD_FG_VERS_CK)) {
+		bad_vers(ifp, iversion != 0);
+		return 0;
+	}
+	if (iversion != DCC_FLOD_VERSION_CUR)
+		TMSG2_FLOD(ofp, "version %d from %s",
+			   iversion, ifp_rem_str(ifp));
+
+	/* convert to a passive output flood as requested by the peer
+	 *	This works even if the peer is configured to use SOCKS or NAT
+	 *	but we are not using PASSIVE */
+	if (vp->body.turn) {
+		if (OFLOD_OPT_OFF_ROGUE(ofp)) {
+			iflod_close(ifp, 1, 0, 1,
+				    "passive output flooding off from %s%s",
+				    ifp_rem_str(ifp),
+				    fnm_lno(&fnm_buf, flod_path, ofp->lno));
+			return 0;
+		}
+
+		/* We have a duplicate passive outgoing flood.
+		 * See whether the old stream has broken. */
+		if (ofp->soc >= 0)
+			oflod_read(ofp);
+		/* If we still have a duplicate and we sent a shutdown request,
+		 * assume the response got lost */
+		if (ofp->soc >= 0
+		    && (ofp->flags & OFLOD_FG_SHUTDOWN_REQ)) {
+			rpt_err(ofp, 1, 0,
+				" assume response to shutdown lost from %s",
+				ofp_rem_str(ofp));
+			oflod_close(ofp, 0);
+		}
+		if (ofp->soc >= 0) {
+			/* We still have duplicates.
+			 * Reject the new one if the IP addresses differ */
+			if (!DCC_SU_EQ(&ofp->rem_su, &ifp->rem_su)) {
+				iflod_close(ifp, 1, 0, 1,
+					    "reject duplicate passive output"
+					    " flood from %s",
+					    ifp_rem_str(ifp));
+				return 0;
+			}
+			rpt_err(ofp, 1, 0,
+				"accept duplicate passive output flood from %s",
+				ifp_rem_str(ifp));
+			oflod_close(ofp, 0);
+		}
+
+		ofp->soc = ifp->soc;
+		ofp->rem_su = ifp->rem_su;
+		++oflods.open;
+		TMSG1_FLOD(ofp,
+			   "convert incoming flood to passive outgoing to %s",
+			   ofp_rem_str(ofp));
+		iflod_clear(ifp, 0);
+
+		ofp->mp->flags |= FLODMAP_FG_OUT_SRVR;
+
+		if (!oflod_connect_fin(ofp))
+			oflod_close(ofp, 0);
+		return 0;
+	}
+
+	if (IFLOD_OPT_OFF_ROGUE(ofp)) {
+		iflod_close(ifp, 1, 0, 1, "flood from %s turned off%s",
+			    ifp_rem_str(ifp),
+			    fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		return 0;
+	}
+
+	/* detect duplicate incoming floods */
+	for (ifp1 = iflods.infos; ifp1 <= LAST(iflods.infos); ++ifp1) {
+		if (ifp1->ofp != ofp || ifp1 == ifp)
+			continue;
+
+		/* We have a duplicate.  Either two servers are using the
+		 * same server-ID or the peer has restarted flooding without
+		 * our seeing  a clean shutdown.
+		 * If socket is in CLOSE_WAIT, then sending something will fail
+		 * immediately.  If the peer was rebooted, then sending will
+		 * not fail for at least a round trip time and possibly longer
+		 * if the peer has moved.
+		 *
+		 * Before trying to send anything, check for a FIN waiting
+		 * on the other socket */
+		for (i = 65536/FLOD_BUF_SIZE; i >= 0; --i) {
+			if (iflod_read(ifp1))
+				break;
+		}
+		/* forget it if reading closed the other stream */
+		if (ifp1->ofp != ofp)
+			break;
+
+		/* assume the it is ok if we have sent an end request */
+		if (ifp1->flags & IFLOD_FG_END_REQ) {
+			iflod_close(ifp1, 0, 0, 1,
+				    "missing end response;"
+				    " have replacement for flood from %s",
+				    ifp_rem_str(ifp1));
+			break;
+		}
+
+		/* assume we missed the shutdown
+		 * if the IP addresses are the same */
+		if (DCC_SU_EQ(&ifp->rem_su, &ifp1->rem_su)) {
+			iflod_close(ifp1, 0, 0, 1,
+				    "assumed dead link;"
+				    " have replacement for flood from %s",
+				    ifp_rem_str(ifp1));
+			break;
+		}
+
+		/* assume we do not have a duplicate server-ID and switch to
+		 * the new connection if sending a position or note
+		 * fails immediately, */
+		if (0 >= iflod_send_pos(ifp1, 1)) {
+			iflod_close(ifp1, 0, 0, 1,
+				    "have replacement for flood from %s",
+				    ifp_rem_str(ifp1));
+			break;
+		}
+
+		/* Otherwise, kill the new flood.  If it was legitimate,
+		 * sending the note will eventually kill the old stream
+		 * and the peer will get through with it tries later */
+		iflod_close(ifp, 1, 1, 1, "duplicate flood from %s",
+			    ifp_rem_str(ifp));
+		return 0;
+	}
+
+	ofp->ifp = ifp;
+	if (ifp->flags & IFLOD_FG_CLIENT)
+		ofp->mp->flags &= ~FLODMAP_FG_IN_SRVR;
+	else
+		ofp->mp->flags |= FLODMAP_FG_IN_SRVR;
+	save_flod_cnts(ofp);
+	ifp->iflod_alive = db_time.tv_sec;
+
+	/* Try to restart the corresponding output flood because a new
+	 * incoming flood might indicate that peer has awakened.
+	 * Kludge the backoff so that it does not increase. */
+	if (ofp->soc < 0
+	    && !(ofp->o_opts.flags & FLOD_OPT_PASSIVE)) {
+		ofp->mp->otimers.retry_secs /= 2;
+		ofp->mp->otimers.retry = 0;
+		oflod_open(ofp);
+	}
+
+	/* Send a rewind or fast forward request immediately if needed.
+	 * If not, send a keepalive message so that peer knows we have
+	 * accepted the connection and it can stop worrying about an
+	 * immediate rejection. */
+	if (0 > iflod_send_pos(ifp, 1))
+		return 0;
+
+	return 1;
+}
+
+
+
+/* A new SOCKS incoming stream that we originated has been closed by the
+ * peer without authenticating itself.  It could have responded to our
+ * authentication with an error message. */
+static void
+parse_socks_error(IFLOD_INFO *ifp)
+{
+	const DCC_FLOD_STREAM *stream;
+	int i, msg_len;
+	int fail;
+
+	stream = (DCC_FLOD_STREAM *)&ifp->ibuf.b[0];
+	msg_len = ifp->ibuf_len - FLOD_END_OVHD;
+
+	/* it must look like an end request with an entirely ASCII message */
+	if (flod_pos2db_ptr(stream->r.pos) != DCC_FLOD_POS_END
+	    || msg_len < 1 || msg_len > ISZ(stream->e.msg)) {
+		iflod_close(ifp, 1, 1, 1, "SOCKS rejected with \"%.*s\"",
+			    msg_len, stream->e.msg);
+		return;
+	}
+
+	for (i = 0; i < msg_len; ++i) {
+		if (stream->e.msg[i] < ' ' || stream->e.msg[i] > '~') {
+			iflod_close(ifp, 1, 1, 1,
+				    "SOCKS rejected with \"%.*s\"",
+				    msg_len, stream->e.msg);
+			return;
+		}
+	}
+
+	fail = oflod_parse_eof(ifp->ofp, 1, &stream->e, msg_len);
+	if (fail <= 0) {
+		iflod_socks_backoff(ifp->ofp);
+	} else {
+		/* try again immediately
+		 * with another protocol version or the 2nd password */
+		ifp->ofp->mp->itimers.retry = 0;
+	}
+	iflod_close(ifp, fail<=0, fail<=0, 0,
+		    "SOCKS rejected by %s with \"%.*s\"",
+		    ifp_rem_str(ifp), msg_len, stream->e.msg);
+}
+
+
+
+/* see what a distant flooder is telling us
+ *	can close the flood and so clear things */
+u_char					/* 1=kernel buffers empty */
+iflod_read(IFLOD_INFO *ifp)
+{
+	OFLOD_INFO *ofp;
+	int off, req_len, recv_len;
+	const DCC_FLOD_STREAM *stream;
+	int len, i;
+
+	/* if this is an incoming SOCKS or NAT stream that we originated,
+	 * and if Rconnect() said "not yet" when we first tried to connect,
+	 * then we must be here because select() says it is time to
+	 * try Rconnect() again to finish the connection */
+	if (!(ifp->flags & IFLOD_FG_CONNECTED)
+	    && iflod_socks_connect(ifp) <= 0)
+		return 1;
+
+	/* read only once before returning
+	 * to ensure we pay attention to other work */
+
+	req_len = sizeof(ifp->ibuf) - ifp->ibuf_len;
+	ofp = ifp->ofp;
+	if (ofp && (ofp->o_opts.flags & FLOD_OPT_SOCKS))
+		recv_len = Rrecv(ifp->soc, &ifp->ibuf.b[ifp->ibuf_len],
+				 req_len, 0);
+	else
+		recv_len = recv(ifp->soc, &ifp->ibuf.b[ifp->ibuf_len],
+				req_len, 0);
+	if (recv_len < 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, 0, 0, "incoming flood recv(%s): %s",
+				    ifp_rem_str(ifp), ERROR_STR());
+		}
+		return 1;
+	}
+	ifp->ibuf_len += recv_len;
+
+	off = 0;
+
+	/* deal with a new connection */
+	if (!(ifp->flags & IFLOD_FG_VERS_CK)) {
+		if (ifp->ibuf_len >= ISZ(DCC_FLOD_VERSION_HDR)) {
+			if (!check_iflod_vers(ifp))
+				return 1;   /* stream closed or converted */
+			ofp = ifp->ofp;
+			off = ISZ(DCC_FLOD_VERSION_HDR);
+
+		} else if (recv_len != 0) {
+			return 1;	/* wait for rest of authentication */
+
+		} else if (ofp && ofp->mp
+			   && (ofp->mp->flags & FLODMAP_FG_ACT) != 0) {
+			parse_socks_error(ifp);
+			return 1;
+
+		} else {
+			iflod_close(ifp, 1, 1, 0, "garbage connection from %s",
+				    ifp_rem_str(ifp));
+			return 1;
+		}
+	}
+	/* ofp != 0 because check_iflod_vers() has found the peer */
+
+	/* deal with the data */
+	dcc_timeval2ts(&future, &db_time, MAX_FLOD_CLOCK_SKEW);
+	while ((len = ifp->ibuf_len - off) > 0) {
+		stream = (DCC_FLOD_STREAM *)&ifp->ibuf.b[off];
+		if (len < ISZ(stream->r.pos))
+			break;		/* need at least the position */
+		i = iflod_rpt(ifp, ofp, stream, len);
+		if (i < 0)
+			return 1;	/* stream closed */
+		if (i == 0)
+			break;		/* wait for rest of report */
+		off += i;
+		++ofp->cnts.total;
+	}
+
+	/* save unprocessed bytes for next time */
+	if (off != 0) {
+		ifp->ibuf_len -= off;
+		if (ifp->ibuf_len < 0)
+			dcc_logbad(EX_SOFTWARE, "ifp->ibuf_len=%d",
+				   ifp->ibuf_len);
+		if (ifp->ibuf_len > 0)
+			memmove(&ifp->ibuf.b[0], &ifp->ibuf.b[off],
+				ifp->ibuf_len);
+	}
+
+	if (recv_len == 0) {
+		/* We are at EOF and have processed all input that we can. */
+		if (ifp->ibuf_len != 0) {
+			/* Something is wrong if any input remains,  */
+			iflod_close(ifp, 1, 0, 1, "report %d truncated",
+				    ofp ? ofp->cnts.total : 0);
+		} else if (flods_st != FLODS_ST_ON
+			   || (ofp && IFLOD_OPT_OFF_ROGUE(ofp))) {
+			iflod_close(ifp, 0, 0, 1, DCC_FLOD_OK_STR"%s off",
+				    our_hostname);
+		} else {
+			iflod_close(ifp, 0, 0, 1, DCC_FLOD_OK_STR"%s off",
+				    ifp_rem_str(ifp));
+		}
+		return 1;
+	}
+
+	/* things are going ok, so reset the SOCKS restart backoff
+	 * and the no-connection complaint */
+	ofp->mp->itimers.retry_secs = FLOD_SOCKS_SOCKS_IRETRY;
+	ofp->mp->itimers.msg_secs = FLOD_IN_COMPLAIN1;
+	ofp->mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN1;
+
+	return (req_len > recv_len);
+}
+
+
+
+void
+iflods_listen(void)
+{
+	SRVR_SOC *sp;
+	DCC_SOCKU su;
+	const DCC_SOCKU *sup = 0;
+	int i, on;
+
+	for (sp = srvr_socs; sp; sp = sp->fwd) {
+		if (sp->flags & SRVR_SOC_ADDR) {
+			/* need to open a TCP listen socket for incoming floods
+			 * for each explicitly configured IP address */
+			sup = &sp->su;
+		} else if (sp->flags & SRVR_SOC_LISTEN) {
+			/* need to 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 {
+			/* otherwise close unneeded socket */
+			iflod_listen_close(sp);
+			continue;
+		}
+
+		if (sp->listen >= 0)
+			continue;
+
+		/* don't need to listen if there is no flooding */
+		if (!oflods.total)
+			continue;
+
+		TMSG1(FLOD, "start flood listening on %s",
+		      dcc_su2str_err(sup));
+
+		sp->listen = socket(sup->sa.sa_family, SOCK_STREAM, 0);
+		if (sp->listen < 0) {
+			dcc_error_msg("socket(flood listen %s): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+			continue;
+		}
+
+		if (-1 == fcntl(sp->listen, F_SETFL,
+				fcntl(sp->listen, F_GETFL, 0) | O_NONBLOCK)) {
+			dcc_error_msg("fcntl(flood listen %s, O_NONBLOCK): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+		}
+		on = 1;
+		if (0 > setsockopt(sp->listen, SOL_SOCKET, SO_REUSEADDR,
+				   &on, sizeof(on)))
+			dcc_error_msg("setsockopt(flood listen %s,"
+				      " SO_REUSADDR): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+		if (0 > fcntl(sp->listen, F_SETFD, FD_CLOEXEC))
+			dcc_error_msg("fcntl(flood 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(flood listen %s): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+			close(sp->listen);
+			sp->listen = -1;
+			continue;
+		}
+
+		if (0 > listen(sp->listen, DCCD_MAX_FLOODS+1)) {
+			dcc_error_msg("flood listen(%s): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+			close(sp->listen);
+			sp->listen = -1;
+		}
+	}
+}
+
+
+
+static const char *
+oflod_state_str(char outstr[DCC_SU2STR_SIZE], const OFLOD_INFO *ofp,
+		u_char anon)
+{
+	if (ofp->soc >= 0) {
+		if (ofp->flags & (OFLOD_FG_SHUTDOWN_REQ
+				  | OFLOD_FG_SHUTDOWN))
+			return "  (shutting)";
+		if (!(ofp->flags & OFLOD_FG_CONNECTED))
+			return "  (connecting)";
+		if (anon)
+			return "";
+		return dcc_su2str2(outstr, DCC_SU2STR_SIZE, &ofp->rem_su);
+	}
+
+	if (OFLOD_OPT_OFF_ROGUE(ofp))
+		return "  (output off)";
+	if (flods_st != FLODS_ST_ON)
+		return "  (flood off)";
+	return "  (no output)";
+}
+
+
+
+static const char *
+iflod_state_str(char instr[DCC_SU2STR_SIZE],
+		const OFLOD_INFO *ofp, const IFLOD_INFO *ifp,
+		u_char anon, u_char have_in, u_char distinct_in)
+{
+
+	if (have_in) {
+		if (!(ifp->flags & IFLOD_FG_VERS_CK))
+			return "  (connecting)";
+		if (anon)
+			return "";
+		if (distinct_in)
+			return dcc_su2str2(instr, DCC_SU2STR_SIZE, &ifp->rem_su);
+		return "\t";
+	}
+	if (IFLOD_OPT_OFF_ROGUE(ofp))
+	    return "  (input off)";
+	if (flods_st != FLODS_ST_ON)
+		return "  (flood off)";
+	return "  (no input)";
+}
+
+
+
+/* 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[60];
+	DCC_SOCKU in, out;
+	u_char have_in, distinct_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;
+		distinct_in = 0;
+		for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+			if (ifp->ofp == ofp) {
+				if (ifp->soc >= 0) {
+					have_in = 1;
+					dcc_ipv6sutoipv4(&in, &ifp->rem_su);
+					dcc_ipv6sutoipv4(&out, &ofp->rem_su);
+					if (ofp->soc < 0
+					    || !DCC_SU_EQ(&in, &out))
+					    distinct_in = 1;
+				}
+				break;
+			}
+		}
+		if (anon) {
+			i = snprintf(p, buf_len, "%5d %15s\t%s\n",
+				     ofp->rem_id,
+				     oflod_state_str(outstr, ofp, 1),
+				     iflod_state_str(instr, ofp, ifp,
+						     1, have_in, 0));
+		} else {
+			dcc_host_portname(hostname, sizeof(hostname),
+					  ofp->rem_hostname,
+					  ofp->rem_port == def_port
+					  ? 0 : ofp->rem_portname),
+			i = strlen(hostname);
+			flodmap_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,
+				     oflod_state_str(outstr, ofp, 0),
+				     iflod_state_str(instr, ofp, ifp,
+						     0, have_in, distinct_in),
+				     hostname,
+				     fg_buf);
+		}
+		FLODS_LIST_ALLOC(i);
+	}
+
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+		if (ifp->soc < 0 || ifp->ofp != 0)
+			continue;	/* already handled this one */
+
+		/* say something about an incomplete connection */
+		i = snprintf(p, buf_len, " ?    %s\n", ifp->rem_hostname);
+		FLODS_LIST_ALLOC(i);
+	}
+	if (p > buf)
+		--p;			/* trim trailing '\n' */
+	return p-buf;
+#undef FLODS_LIST_TOO_SHORT
+#undef FLODS_LIST_ALLOC
+}
+
+
+
+static PATTRIB(3,4) u_char		/* 0=no room */
+flod_stats_str(char **buf, int *buf_len,
+		const char *pat, ...)
+{
+	int i;
+	va_list args;
+
+	if (*buf_len <= 0)
+		return 0;
+
+	va_start(args, pat);
+	i = vsnprintf(*buf, *buf_len, pat, args);
+	va_end(args);
+
+	if ((*buf_len -= i) <= 0) {
+		*buf_len = 0;
+		return 0;
+	}
+	*buf += i;
+	return 1;
+}
+
+
+
+
+static u_char				/* 0=no room */
+flod_stats_time(char **buf, int *buf_len,
+		const char *str, const char *timepat, time_t when)
+{
+	char timebuf[40];
+
+	return flod_stats_str(buf, buf_len, "%s %s", str,
+			      dcc_time2str(timebuf, sizeof(timebuf), timepat,
+					   when));
+}
+
+
+
+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;
+}
+
+
+
+static void
+flod_stats_conn_cur(char **buf, int *buf_len, const OFLOD_INFO *ofp, u_char in)
+{
+	u_char connected;
+	time_t conn_changed;
+	time_t flod_alive;
+	const FLOD_MMAP *mp;
+	const LAST_ERROR *ep;
+	DCC_FNM_LNO_BUF fnm_buf;
+	time_t deadline;
+	u_char passive;
+	const char *msg;
+
+	if (*buf_len <= 0)
+		return;
+
+	mp = ofp->mp;
+
+	if (in) {
+		connected = ofp->ifp != 0;
+		conn_changed = ofp->mp->cnts.in_conn_changed;
+		flod_alive = ofp->ifp ? ofp->ifp->iflod_alive : 0;
+	} else {
+		connected = (ofp->flags & OFLOD_FG_CONNECTED) != 0;
+		conn_changed = ofp->mp->cnts.out_conn_changed;
+		flod_alive = ofp->oflod_alive;
+	}
+
+	if (connected) {
+		if (conn_changed >= mp->cnts.cnts_cleared
+		    && !flod_stats_time(buf, buf_len, "     connected since",
+					"%b %d %X", conn_changed))
+			return;
+		flod_stats_time(buf, buf_len, "     last active", "%X",
+				flod_alive);
+		return;
+	}
+
+	if ((in && (mp->flags & FLODMAP_FG_IN_OFF))
+	    || (!in && (mp->flags & FLODMAP_FG_OUT_OFF))) {
+		flod_stats_str(buf, buf_len, "     off%s",
+			       fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		return;
+	}
+
+	if (!flod_stats_str(buf, buf_len, "     not connected"))
+		return;
+	if (conn_changed >= mp->cnts.cnts_cleared) {
+		if (!flod_stats_time(buf, buf_len, " since",
+				     "%b %d %X", conn_changed))
+			return;
+	}
+	ep = in ? &mp->iflod_err : &mp->oflod_err;
+	msg = ep->msg[0] != '\0' ? ep->msg : ep->trace_msg;
+	if (msg[0] != '\0') {
+		if (!flod_stats_str(buf, buf_len, "\n\t%s", msg))
+			return;
+	}
+
+	if (!FLODS_OK_ON()) {
+		flod_stats_str(buf, buf_len, "\n     flooding off");
+		return;
+	}
+
+	if (in) {
+		if ((ofp->mp->flags & FLODMAP_FG_ACT) != 0) {
+			passive = 0;
+			deadline = ofp->mp->itimers.retry;
+			if (DB_IS_TIME(deadline, ofp->mp->itimers.retry_secs))
+				deadline = 0;
+
+		} else {
+			passive = 1;
+			deadline = ofp->mp->itimers.msg;
+			if (DB_IS_TIME(deadline, ofp->mp->itimers.msg_secs))
+				deadline = 0;
+		}
+	} else {
+		if (ofp->mp->flags & FLODMAP_FG_PASSIVE) {
+			passive = 1;
+			deadline = ofp->mp->otimers.msg;
+			if (DB_IS_TIME(deadline, ofp->mp->otimers.msg_secs))
+				deadline = 0;
+		} else {
+			passive = 0;
+			deadline = ofp->mp->otimers.retry;
+			if (DB_IS_TIME(deadline, ofp->mp->otimers.retry_secs))
+				deadline = 0;
+		}
+	}
+	if (deadline == 0) {
+		flod_stats_str(buf, buf_len,
+			       passive
+			       ? "\n     complain soon"
+			       : "\n     try again soon");
+	} else {
+		flod_stats_time(buf, buf_len,
+				passive
+				? "\n     complain after"
+				: "\n     try again after",
+				"%b %d %X", deadline);
+	}
+}
+
+
+
+/* list the counts for a flood */
+int					/* -1 or buffer length */
+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 loaded;
+	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;
+
+	if (flod_mmaps) {
+		loaded = 0;
+	} else if (!load_flod(0)) {
+		return -1;
+	} else {
+		loaded = 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_hostname[0] != '\0'
+		    && ofp1->rem_id >= min_srvr
+		    && ofp1->rem_id <= max_srvr) {
+			/* 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 (loaded)
+			oflods_clear();
+		return p-buf;
+	}
+	mp = ofp->mp;
+
+	save_flod_cnts(ofp);
+	i = snprintf(p, len,
+		     DCC_AOP_FLOD_STATS_ID" %s%s  %s\n  status start %s",
+		     ofp->rem_id, mp->rem_hostname,
+		     flodmap_fg(fg_buf, sizeof(fg_buf), "  ", mp),
+		     dcc_time2str(now_buf, sizeof(now_buf), "%b %d %X %Z",
+				  db_time.tv_sec),
+		     dcc_time2str(time_buf, sizeof(time_buf), "%b %d %X %Z",
+				  mp->cnts.cnts_cleared));
+	FLOD_STATS_ALLOC(i);
+
+	flod_stats_conn_total(&p, &len, "output", mp->cnts.out_total_conn);
+	i = snprintf(p, len, "     "L_DPAT" reports sent\n",
+		     mp->cnts.out_reports+ofp->cnts.out_reports);
+	FLOD_STATS_ALLOC(i);
+	flod_stats_conn_cur(&p, &len, ofp, 0);
+	i = snprintf(p, len, "\n     position "L_HPAT, mp->confirm_pos);
+	FLOD_STATS_ALLOC(i);
+
+	flod_stats_conn_total(&p, &len, "input", mp->cnts.in_total_conn);
+	i = snprintf(p, len,
+		     "     "L_DPAT" reports received  "L_DPAT" accepted"
+		     "  "L_DPAT" duplicate  "L_DPAT" stale\n"
+		     "     "L_DPAT" bad whitelist  "L_DPAT" not deleted\n",
+		     mp->cnts.total+ofp->cnts.total,
+		     mp->cnts.accepted+ofp->cnts.accepted,
+		     mp->cnts.dup+ofp->lc.dup.cur,
+		     mp->cnts.stale+ofp->lc.stale.cur,
+		     mp->cnts.wlist+ofp->lc.wlist.cur,
+		     mp->cnts.not_deleted+ofp->lc.not_deleted.cur);
+	FLOD_STATS_ALLOC(i);
+	flod_stats_conn_cur(&p, &len, ofp, 1);
+
+	if (len <= 0) {
+		strcpy(buf, FLOD_STATS_TOO_SHORT);
+		if (loaded)
+			oflods_clear();
+		return ISZ(FLOD_STATS_TOO_SHORT);
+	}
+
+	if (clear) {
+		flod_try_again(ofp);
+		save_flod_cnts(ofp);
+		ofp->limit_reset = 0;
+		memset(&mp->cnts, 0, sizeof(mp->cnts));
+		mp->cnts.cnts_cleared = db_time.tv_sec;
+	}
+
+	if (loaded)
+		oflods_clear();
+	return p-buf;
+#undef FLOD_STATS_TOO_SHORT
+#undef FLOD_STATS_ALLOC
+}