diff dccd/oflod.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/oflod.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2483 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * deal with outgoing 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.151 $Revision$
+ */
+
+#include "dccd_defs.h"
+#include "dcc_ck.h"
+
+
+int flods_off;				/* # of reasons flooding is off */
+
+time_t next_flods_ck;
+time_t flod_mtime = 1;
+enum FLODS_ST flods_st = FLODS_ST_OFF;
+OFLODS oflods;
+
+/* records after this have not been flooded
+ *	0 if invalid */
+DB_PTR oflods_max_cur_pos;
+
+int summarize_delay_secs;		/* delay summaries by this */
+
+
+static void oflod_fill(OFLOD_INFO *);
+
+
+/* the socket must already be closed */
+static void
+oflod_clear(OFLOD_INFO *ofp)
+{
+	memset(ofp, 0, sizeof(*ofp));
+	ofp->soc = -1;
+}
+
+
+
+void
+oflods_clear(void)
+{
+	OFLOD_INFO *ofp;
+
+	iflods_stop("", 1);		/* paranoia */
+
+	flod_unmap(0, &dccd_stats);
+
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp)
+		oflod_clear(ofp);
+	oflods.total = 0;
+	complained_many_iflods = 0;
+	oflods_max_cur_pos = 0;
+}
+
+
+
+/* parse ID1->tgt in a flood file entry */
+static char
+oflod_parse_map(OFLOD_OPTS *opts, const char *str0, int lno)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	const char *str;
+	OFLOD_SRVR_ID_MAP *imp;
+
+	if (opts->num_maps >= DIM(opts->srvr_map)) {
+		dcc_error_msg("too many ID mappings with\"%s\"%s",
+			      str0, fnm_lno(&fnm_buf, flod_path, lno));
+		return 0;
+	}
+	imp = &opts->srvr_map[opts->num_maps];
+
+	if (!CLITCMP(str0, "self->")) {
+		str = str0+LITZ("self->");
+		imp->from_lo = imp->from_hi = my_srvr_id;
+	} else if (!CLITCMP(str0, "all->")) {
+		str = str0+LITZ("all->");
+		imp->from_lo = DCC_SRVR_ID_MIN;
+		imp->from_hi = DCC_SRVR_ID_MAX;
+	} else {
+		/* get ID1 */
+		str = dcc_get_srvr_id(0, &imp->from_lo, str0, "-",
+				      flod_path, lno);
+		if (!str)
+			return 0;
+		if (str[0] == '-' && str[1] == '>') {
+			/* ID1 is not a range */
+			imp->from_hi = imp->from_lo;
+		} else {
+			/* ID1 is a range of IDs */
+			str = dcc_get_srvr_id(0, &imp->from_hi,
+					      str+1, "-", flod_path, lno);
+			if (!str)
+				return 0;
+			if (imp->from_hi < imp->from_lo) {
+				dcc_error_msg("invalid ID mapping range "
+					      "\"%d-%d\"%s",
+					      imp->from_lo, imp->from_hi,
+					      fnm_lno(&fnm_buf,
+						      flod_path, lno));
+				return 0;
+			}
+		}
+		if (*str++ != '-' || *str++ != '>') {
+			dcc_error_msg("invalid server-ID mapping \"%s\"%s",
+				      str0, fnm_lno(&fnm_buf, flod_path, lno));
+			return 0;
+		}
+	}
+	if (!strcasecmp(str, "self")) {
+		imp->result = ID_MAP_SELF;
+	} else if (!strcasecmp(str, "reject")) {
+		imp->result = ID_MAP_REJ;
+	} else if (!strcasecmp(str, "ok")) {
+		imp->result = ID_MAP_NO;
+	} else {
+		dcc_error_msg("invalid ID mapping result \"%s\"%s",
+			      str, fnm_lno(&fnm_buf, flod_path, lno));
+		return 0;
+	}
+
+	++opts->num_maps;
+	return 1;
+}
+
+
+
+static u_char
+ck_socks_flags(OFLOD_INFO *ofp, OPT_FLAGS new,
+	       DCC_FNM_LNO_BUF fnm_buf, int lno)
+{
+	if (0 != (ofp->o_opts.flags & (FLOD_OPT_PASSIVE | FLOD_OPT_SOCKS
+				       | FLOD_OPT_NAT)
+		  & ~new)) {
+		dcc_error_msg("only one of \"passive\", \"SOCKS\", or \"NAT\""
+			      " allowed%s",
+			      fnm_lno(&fnm_buf, flod_path, lno));
+		return 0;
+	}
+
+	if ((new & (FLOD_OPT_SOCKS | FLOD_OPT_NAT)) != 0
+	    && (ofp->loc_hostname[0] != '\0' || ofp->loc_port != 0)) {
+		dcc_error_msg("source host name or port number"
+			      " and \"SOCKS\" or \"NAT\""
+			      " cannot both be set%s",
+			      fnm_lno(&fnm_buf, flod_path, lno));
+		ofp->loc_hostname[0] = '\0';
+		ofp->loc_port = 0;
+	}
+
+	ofp->o_opts.flags |= new;
+	return 1;
+}
+
+
+
+/* parse remote or local options that can be any of
+ *  a "off", "del", "no-del", "log-del", "passive", ID->map, etc. */
+static const char *			/* rest of the line */
+parse_flod_opts(OFLOD_INFO *ofp,
+		OFLOD_OPTS *opts,
+		const char *buf, int lno)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	char opts_buf[200];
+	char opt[20];
+	const char *buf_ptr, *p;
+	char *end;
+	unsigned long l;
+	u_int olen;
+
+	/* pick out the blank delimited string of options */
+	buf = dcc_parse_word(0, opts_buf, sizeof(opts_buf),
+			     buf, "flood options", flod_path, lno);
+	if (!buf)
+		return 0;
+
+	opts->path_len = DCC_MAX_FLOD_PATH;
+	if (grey_on)
+		opts->flags |= (FLOD_OPT_DEL_OK
+				| FLOD_OPT_NO_LOG_DEL
+				| FLOD_OPT_DEL_SET);
+
+	/* parse the options */
+	buf_ptr = opts_buf;
+	while (*buf_ptr != '\0') {
+		if (*buf_ptr == ',') {
+			++buf_ptr;
+			continue;
+		}
+		olen = strcspn(buf_ptr, ",");
+		if (olen >= sizeof(opt))
+			olen = sizeof(opt)-1;
+		strncpy(opt, buf_ptr, olen);
+		opt[olen] = '\0';
+		buf_ptr += olen;
+
+		/* ignore "-" */
+		if (!strcmp(opt, "-"))
+			continue;
+
+		if (!strcasecmp(opt, "off")) {
+			opts->flags |= FLOD_OPT_OFF;
+			continue;
+		}
+
+		if (!grey_on) {
+			if (!strcasecmp(opt, "traps")) {
+				opts->flags |= FLOD_OPT_TRAPS;
+				continue;
+			}
+			if (!strcasecmp(opt, "no-del")) {
+				opts->flags &= ~FLOD_OPT_DEL_OK;
+				opts->flags |= FLOD_OPT_DEL_SET;
+				continue;
+			}
+			if (!strcasecmp(opt, "del")) {
+				opts->flags |= FLOD_OPT_DEL_OK;
+				opts->flags |= FLOD_OPT_DEL_SET;
+				continue;
+			}
+		}
+
+		/* put some options in one or the other flag word no matter
+		 * for which they are specified */
+		if (!strcasecmp(opt, "trace")) {
+			ofp->o_opts.flags |= FLOD_OPT_TRACE;
+			continue;
+		}
+		if (!strcasecmp(opt, "trace2")) {
+			ofp->o_opts.flags |= FLOD_OPT_TRACE2;
+			continue;
+		}
+
+		if (!strcasecmp(opt, "no-log-del")) {
+			ofp->i_opts.flags |= FLOD_OPT_NO_LOG_DEL;
+			continue;
+		}
+		if (!strcasecmp(opt, "log-del")) {
+			ofp->i_opts.flags &= ~FLOD_OPT_NO_LOG_DEL;
+			continue;
+		}
+		if (!strcasecmp(opt, "passive")) {
+			if (!ck_socks_flags(ofp, FLOD_OPT_PASSIVE,
+					    fnm_buf, lno))
+				return 0;
+			continue;
+		}
+		if (!strcasecmp(opt, "socks")) {
+			if (!ck_socks_flags(ofp, FLOD_OPT_SOCKS,
+					    fnm_buf, lno))
+				return 0;
+			continue;
+		}
+		if (!strcasecmp(opt, "nat")) {
+			if (!ck_socks_flags(ofp, FLOD_OPT_NAT,
+					    fnm_buf, lno))
+				return 0;
+			continue;
+		}
+		if (!strcasecmp(opt, "IPv4")) {
+			if (ofp->o_opts.flags & FLOD_OPT_IPv6) {
+				dcc_error_msg("\"IPv4\" and \"IPv6\";"
+					      " cannot both be%s",
+					      fnm_lno(&fnm_buf,
+						      flod_path, lno));
+				return 0;
+			}
+			ofp->o_opts.flags |= FLOD_OPT_IPv4;
+			continue;
+		}
+		if (!strcasecmp(opt, "IPv6")) {
+			if (ofp->o_opts.flags & FLOD_OPT_IPv4) {
+				dcc_error_msg("\"IPv4\" and \"IPv6\";"
+					      " cannot both be%s",
+					      fnm_lno(&fnm_buf,
+						      flod_path, lno));
+				return 0;
+			}
+			ofp->o_opts.flags |= FLOD_OPT_IPv6;
+			continue;
+		}
+		if (!CLITCMP(opt, "leaf=")
+		    && (l = strtoul(opt+LITZ("leaf="), &end, 10),
+			*end == '\0')) {
+			if (l > DCC_MAX_FLOD_PATH)
+				l = DCC_MAX_FLOD_PATH;
+			ofp->o_opts.path_len = l;
+			continue;
+		}
+
+#ifdef DCC_FLOD_VERSION7
+		if (!strcasecmp(opt, "version7")) {
+			ofp->oversion = DCC_FLOD_VERSION7;
+			continue;
+		}
+
+#endif
+		/* parse an ID->map */
+		p = strchr(opt, '>');
+		if (p && p > opt && *(p-1) == '-') {
+			if (!oflod_parse_map(opts, opt, lno))
+				return 0;
+			continue;
+		}
+
+		dcc_error_msg("unknown option \"%s\"%s",
+			      opt, fnm_lno(&fnm_buf, flod_path, lno));
+		return 0;
+	}
+
+	return buf;
+}
+
+
+
+static const char *			/* rest of a flod file line */
+oflod_parse_id(DCC_SRVR_ID *id,
+	       const char *buf, const char *type, int lno)
+{
+	char id_buf[20];
+
+	buf = dcc_parse_word(0, id_buf, sizeof(id_buf),
+			     buf, type, flod_path, lno);
+	if (!buf)
+		return 0;
+
+	if (!strcmp(id_buf, "-")
+	    || id_buf[0] == '\0') {
+		*id = DCC_ID_INVALID;
+		return buf;
+	}
+
+	if (!dcc_get_srvr_id(0, id, id_buf, 0, flod_path, lno))
+		return 0;
+
+	/* do not check whether we know the local ID here, because
+	 * changes in the ids file can make that check moot */
+
+	return buf;
+}
+
+
+
+/* compute the maximum position among all floods */
+static void
+get_oflods_max_cur_pos(void)
+{
+	OFLOD_INFO *ofp;
+
+	oflods_max_cur_pos = DB_PTR_BASE;
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] != '\0'
+		    && oflods_max_cur_pos < ofp->cur_pos)
+			oflods_max_cur_pos = ofp->cur_pos;
+	}
+}
+
+
+
+static void
+copy_opts2mp(OFLOD_INFO *ofp)
+{
+	FLOD_MMAP *mp;
+	FLOD_MMAP_FLAGS new_flags;
+	u_char changed;
+
+	mp = ofp->mp;
+	changed = 0;
+	new_flags = mp->flags & (FLODMAP_FG_REWINDING
+				 | FLODMAP_FG_NEED_REWIND
+				 | FLODMAP_FG_FFWD_IN
+				 | FLODMAP_FG_OUT_SRVR
+				 | FLODMAP_FG_IN_SRVR
+				 | FLODMAP_FG_NAT_AUTO
+				 | FLODMAP_FG_USE_2PASSWD);
+
+	if (db_parms.flags & DB_PARM_FG_CLEARED) {
+		new_flags |= FLODMAP_FG_NEED_REWIND;
+		new_flags &= ~FLODMAP_FG_FFWD_IN;
+		changed = 1;
+	}
+
+	if (ofp->o_opts.flags & FLOD_OPT_ROGUE)
+		new_flags |= FLODMAP_FG_ROGUE;
+
+	if ((ofp->o_opts.flags & FLOD_OPT_OFF))
+		new_flags |= FLODMAP_FG_OUT_OFF;
+
+	if (ofp->i_opts.flags & FLOD_OPT_OFF)
+		new_flags |= FLODMAP_FG_IN_OFF;
+
+	if (ofp->o_opts.flags & FLOD_OPT_IPv4) {
+		new_flags |= FLODMAP_FG_IPv4;
+		if (mp->flags & FLODMAP_FG_IPv6)
+			got_hosts = 0;
+	} else if (ofp->o_opts.flags & FLOD_OPT_IPv6) {
+		new_flags |= FLODMAP_FG_IPv6;
+		if (mp->flags & FLODMAP_FG_IPv4)
+			got_hosts = 0;
+	}
+
+	if (ofp->o_opts.flags & FLOD_OPT_PASSIVE) {
+		new_flags |= FLODMAP_FG_PASSIVE;
+	} else if (ofp->o_opts.flags & FLOD_OPT_SOCKS) {
+		new_flags |= FLODMAP_FG_SOCKS;
+	} else if (ofp->o_opts.flags & FLOD_OPT_NAT) {
+		new_flags |= FLODMAP_FG_NAT;
+	}
+
+	if (ofp->o_opts.path_len != DCC_MAX_FLOD_PATH)
+		new_flags |= FLODMAP_FG_LEAF;
+
+	if (ofp->o_opts.num_maps != 0
+	    || ofp->i_opts.num_maps != 0)
+		new_flags |= FLODMAP_FG_MAPPED;
+
+	if ((mp->flags ^ new_flags) && (FLODMAP_FG_ROGUE
+					| FLODMAP_FG_OUT_OFF
+					| FLODMAP_FG_IN_OFF
+					| FLODMAP_FG_IPv6
+					| FLODMAP_FG_IPv4
+					| FLODMAP_FG_PASSIVE
+					| FLODMAP_FG_SOCKS
+					| FLODMAP_FG_NAT
+					| FLODMAP_FG_LEAF
+					| FLODMAP_FG_MAPPED)) {
+		mp->flags = new_flags;
+		changed = 1;
+	}
+
+	/* get new hostname if it changes */
+	if (strcasecmp(mp->rem_hostname, ofp->rem_hostname)) {
+		BUFCPY(mp->rem_hostname, ofp->rem_hostname);
+		got_hosts = 0;		/* force name resolution for new name */
+		changed = 1;
+	}
+	/* always get new port name
+	 * in case the name but not the number changes */
+	BUFCPY(mp->rem_portname, ofp->rem_portname);
+	if (mp->rem_port != ofp->rem_port) {
+		mp->rem_port = ofp->rem_port;
+		changed = 1;
+	}
+
+	if (mp->ids_mtime != ids_mtime) {
+		mp->ids_mtime = ids_mtime;
+		mp->flags &= ~FLODMAP_FG_USE_2PASSWD;
+		changed = 1;
+	}
+
+	if (mp->in_passwd_id != ofp->in_passwd_id) {
+		mp->in_passwd_id = ofp->in_passwd_id;
+		changed = 1;
+	}
+	if (mp->out_passwd_id != ofp->out_passwd_id) {
+		mp->out_passwd_id = ofp->out_passwd_id;
+		changed = 1;
+	}
+
+	if (changed)
+		flod_try_again(ofp);
+}
+
+
+
+/* Load the hostnames of DCC server peers and their output flood positions.
+ *	flod_names_resolve_ck() must say ok before this function is called,
+ *	to avoid races with changing host names.
+ *
+ *	Parse lines of the form
+ *	  name,port;name,port rem-ID [passwd-id [out-opts [in-opts [versionX]]]]
+ */
+u_char					/* 1=ok to start flooding */
+load_flod(u_char complain)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	OFLOD_INFO *ofp, *ofp1;
+	FILE *f;
+	struct stat sb;
+	int lno;
+	char buf[200];
+	char hostname[60];
+	const char *bufp, *bufp1;
+	FLOD_MMAP *mp, *mp1;
+	union {
+	    OFLOD_INFO	info;
+	    FLOD_MMAP	map;
+	} swap;
+	const ID_TBL *tp;
+	char *p;
+	int i;
+
+	/* the caller should use RUSH_NEXT_FLODS_CK() or similar, but
+	 * just in case ... */
+	if (!flod_names_resolve_ck())
+		return 0;
+
+	/* forget everything about flooding */
+	oflods_clear();
+
+	/* keep the map open and locked most of the time */
+	if (!flod_mmap(dcc_emsg, &db_parms.sn, &dccd_stats, 1,
+		       (DCC_TRACE_FLOD_BIT & dccd_tracemask) != 0)) {
+		if (complain)
+			dcc_error_msg("%s", dcc_emsg);
+		flod_mtime = 0;
+		return 0;
+	}
+
+	f = fopen(flod_path, "r");
+	if (!f) {
+		if (flod_mtime != 0) {
+			dcc_error_msg("fopen(%s): %s",
+				      flod_path, ERROR_STR());
+			flod_mtime = 0;
+		}
+
+		flod_unmap(0, &dccd_stats);
+		return 0;
+	}
+	if (0 > fstat(fileno(f), &sb)) {
+		if (flod_mtime!= 0)
+			dcc_error_msg("stat(%s): %s",
+				      flod_path, ERROR_STR());
+		fclose(f);
+		flod_unmap(0, &dccd_stats);
+		flod_mtime = 0;
+		return 0;
+	}
+	flod_mtime = sb.st_mtime;
+
+	/* Parse the ASCII file of names and parameters first so that we do not
+	 * destroy the position information if there is a problem with names */
+	ofp = oflods.infos;
+	lno = 0;
+	for (;;) {
+		/* clear the entry in case we started to set it with the
+		 * preceding line from the /var/dcc/flod file */
+		if (ofp <= LAST(oflods.infos))
+			oflod_clear(ofp);
+
+		++lno;
+		bufp = fgets(buf, sizeof(buf), f);
+		if (!bufp) {
+			if (ferror(f)) {
+				dcc_error_msg("fgets(%s): %s",
+					      flod_path, ERROR_STR());
+				break;
+			}
+			if (fclose(f) == EOF) {
+				dcc_error_msg("fclose(%s): %s",
+					      flod_path, ERROR_STR());
+			}
+			f = 0;
+			break;
+		}
+		i = strlen(bufp);
+		if (i >= ISZ(buf)-1) {
+			dcc_error_msg("too many characters%s",
+				      fnm_lno(&fnm_buf, flod_path, lno));
+			do {
+				i = getc(f);
+			} while (i != '\n' && i != EOF);
+			continue;
+		}
+		/* ignore comments */
+		p = strchr(bufp, '#');
+		if (p)
+			*p = '\0';
+		else
+			p = &buf[i];
+		/* trim trailing blanks */
+		while (--p > bufp && (*p == ' ' || *p == '\t' || *p == '\n'))
+			*p = '\0';
+		/* skip blank lines */
+		bufp += strspn(bufp, DCC_WHITESPACE);
+		if (*bufp == '\0')
+			continue;
+
+		if (oflods.total >= DIM(oflods.infos)) {
+			dcc_error_msg("too many DCC peers in %s; max=%d",
+				      flod_path, DIM(oflods.infos));
+			continue;
+		}
+
+		ofp->lno = lno;
+
+		/* get IP address and port number of remote DCC server */
+		bufp1 = bufp+strcspn(bufp, DCC_WHITESPACE";");
+		if (*bufp1 != ';') {
+			bufp1 = 0;
+		} else {
+			/* Allow the local or client TCP IP address and
+			 * port number to be specified. */
+			buf[bufp1++ - buf] = '\0';
+		}
+		bufp = dcc_parse_nm_port(0, bufp, def_port,
+					 ofp->rem_hostname,
+					 sizeof(ofp->rem_hostname),
+					 &ofp->rem_port,
+					 ofp->rem_portname,
+					 sizeof(ofp->rem_portname),
+					 flod_path, lno);
+		if (!bufp)
+			continue;
+		if (bufp1) {
+			/* parse the local IP address first */
+			bufp = dcc_parse_nm_port(0, bufp1, 0,
+						 ofp->loc_hostname,
+						 sizeof(ofp->loc_hostname),
+						 &ofp->loc_port, 0, 0,
+						 flod_path, lno);
+			if (!bufp)
+				continue;
+		}
+
+		bufp = oflod_parse_id(&ofp->rem_id, bufp,
+				      "rem-id", lno);
+		if (!bufp)
+			continue;
+		if (ofp->rem_id == DCC_ID_INVALID) {
+			dcc_error_msg("missing rem-id%s",
+				      fnm_lno(&fnm_buf, flod_path, lno));
+			continue;
+		}
+
+		bufp = oflod_parse_id(&ofp->out_passwd_id, bufp,
+				      "passwd-id", lno);
+		if (!bufp)
+			continue;
+		if (ofp->out_passwd_id == DCC_ID_INVALID) {
+			ofp->out_passwd_id = my_srvr_id;
+			ofp->in_passwd_id = ofp->rem_id;
+		} else {
+			ofp->in_passwd_id = ofp->out_passwd_id;
+		}
+
+		ofp->oversion = DCC_FLOD_VERSION_DEF;
+		bufp = parse_flod_opts(ofp, &ofp->o_opts, bufp, lno);
+		if (!bufp)
+			continue;
+		bufp = parse_flod_opts(ofp, &ofp->i_opts, bufp, lno);
+		if (!bufp)
+			continue;
+		if (*bufp != '\0')
+			dcc_error_msg("trailing garbage \"%s\" ignored%s",
+				      bufp, fnm_lno(&fnm_buf, flod_path, lno));
+
+		tp = find_srvr_type(ofp->rem_id);
+		if (0 < find_srvr_rcd_type(ofp->rem_id)) {
+			switch (DB_RCD_ID(db_sts.rcd2.d.r)) {
+			case DCC_ID_SRVR_REP_OK:
+			case DCC_ID_SRVR_SIMPLE:
+				ofp->o_opts.flags |= FLOD_OPT_SIMPLE;
+				break;
+			case DCC_ID_SRVR_ROGUE:
+				ofp->o_opts.flags |= FLOD_OPT_ROGUE;
+				break;
+			default:
+				dcc_error_msg("unknown state for server-ID %d",
+					      ofp->rem_id);
+				break;
+			}
+		}
+
+		/* both servers having spam traps and assuming the other
+		 * doesn't makes no sense */
+		if (((ofp->o_opts.flags & FLOD_OPT_TRAPS)
+		     || (ofp->i_opts.flags & FLOD_OPT_TRAPS))
+		    && !(ofp->i_opts.flags & FLOD_OPT_OFF)
+		    && !(ofp->o_opts.flags & FLOD_OPT_OFF)) {
+			dcc_error_msg("symmetric trap-only link%s",
+				      fnm_lno(&fnm_buf, flod_path, lno));
+			continue;
+		}
+
+		for (ofp1 = oflods.infos; ofp1 < ofp; ++ofp1) {
+			if ((!strcmp(ofp1->rem_hostname, ofp->rem_hostname)
+			     && ofp1->rem_port == ofp->rem_port)
+			    || ofp1->rem_id == ofp->rem_id)
+				break;
+		}
+		if (ofp1 != ofp) {
+			dcc_error_msg("duplicate DCC peer%s",
+				      fnm_lno(&fnm_buf, flod_path, lno));
+			continue;
+		}
+
+		/* ignore ourself */
+		if (ofp->rem_id == my_srvr_id)
+			continue;
+
+		ofp->limit_reset = db_time.tv_sec + FLOD_LIM_CLEAR_SECS;
+
+		++ofp;
+		++oflods.total;
+	}
+	if (f)
+		fclose(f);
+
+	/* sort new list by server-ID so that `cdcc "flood list"` is sorted */
+	ofp = oflods.infos;
+	while (ofp < LAST(oflods.infos)) {
+		ofp1 = ofp+1;
+		if (ofp1->rem_hostname[0] == '\0')
+			break;
+		if (ofp->rem_id <= ofp1->rem_id) {
+			ofp = ofp1;
+			continue;
+		}
+		/* bubble sort because the list is usually already
+		 * ordered and almost always tiny */
+		memcpy(&swap.info, ofp1, sizeof(swap.info));
+		memcpy(ofp1, ofp, sizeof(*ofp1));
+		memcpy(ofp, &swap.info, sizeof(*ofp));
+		ofp = oflods.infos;
+	}
+
+	mp = flod_mmaps->mmaps;
+
+	/* Bubble sort the list in the /var/dcc/flod/map file so that is
+	 * sorted for `dblist -Hv`.  The file will usually already be sorted
+	 * and is tiny. */
+	mp1 = mp+1;
+	while (mp1 <= LAST(flod_mmaps->mmaps)) {
+		if (mp1->rem_hostname[0] == '\0') {
+			++mp1;
+			continue;
+		}
+		if (mp->rem_hostname[0] == '\0'
+		    || mp->rem_id <= mp1->rem_id) {
+			mp = mp1++;
+			continue;
+		}
+		memcpy(&swap.map, mp1, sizeof(swap.map));
+		memcpy(mp1, mp, sizeof(*mp1));
+		memcpy(mp, &swap.map, sizeof(*mp));
+		mp = flod_mmaps->mmaps;
+		mp1 = mp+1;
+	}
+
+	/* combine our list that is based on the ASCII file /var/dcc/flod
+	 * with the memory mapped /var/dcc/flod.map list of what has
+	 * been sent to each peer */
+	for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp)
+		mp->flags &= ~FLODMAP_FG_MARK;
+
+	/* make one pass matching old names with their slots in the
+	 * mapped file */
+	mp = flod_mmaps->mmaps;
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] == '\0')
+			break;
+		for (i = 0; i < DIM(flod_mmaps->mmaps); ++i) {
+			if (++mp > LAST(flod_mmaps->mmaps))
+				mp = flod_mmaps->mmaps;
+			if (mp->rem_hostname[0] == '\0')
+				continue;
+			if (!(mp->flags & FLODMAP_FG_MARK)
+			    && ofp->rem_id == mp->rem_id) {
+				/* found the old slot */
+				if (DB_PTR_IS_BAD(mp->confirm_pos)
+				    || mp->confirm_pos > db_csize) {
+					dcc_error_msg("bogus position "L_HPAT
+						    " for %s in %s",
+						    mp->confirm_pos,
+						    ofp->rem_hostname,
+						    flod_mmap_path);
+					mp->rem_hostname[0] = '\0';
+					continue;
+				}
+				ofp->cur_pos = mp->confirm_pos;
+				ofp->rewind_pos = db_csize;
+				ofp->mp = mp;
+				copy_opts2mp(ofp);
+				mp->flags |= FLODMAP_FG_MARK;
+				break;
+			}
+		}
+	}
+
+
+	/* use a free or obsolete slot in the mapped file for new entries */
+	mp = flod_mmaps->mmaps;
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		/* find the next new peer without a mapped file slot */
+		if (ofp->rem_hostname[0] == '\0')
+			break;
+		if (ofp->mp != 0)
+			continue;
+
+		/* find a free or no longer used slot */
+		while (mp->flags & FLODMAP_FG_MARK) {
+			if (++mp > LAST(flod_mmaps->mmaps)) {
+				bad_stop("too few oflod mmap slots");
+				goto out;
+			}
+		}
+		if (mp->rem_hostname[0] != '\0')
+			dcc_error_msg("forget flood to %s %d",
+				      dcc_host_portname(hostname,
+							sizeof(hostname),
+							mp->rem_hostname,
+							mp->rem_portname),
+				      mp->rem_id);
+
+		/* we have found a free slot */
+		memset(mp, 0, sizeof(*mp));
+		ofp->mp = mp;
+		mp->rem_su.sa.sa_family = AF_UNSPEC;
+		mp->rem_id = ofp->rem_id;
+		copy_opts2mp(ofp);
+
+		mp->cnts.cnts_cleared = db_time.tv_sec;
+
+		if (flod_mmaps->delay_pos < DB_PTR_BASE) {
+			/* do not rewind if repairing a broken file */
+			ofp->cur_pos = mp->confirm_pos = db_csize;
+			ofp->recv_pos = ofp->xmit_pos = db_csize;
+		} else {
+			ofp->cur_pos = mp->confirm_pos = DB_PTR_BASE;
+			ofp->recv_pos = ofp->xmit_pos = DB_PTR_BASE;
+		}
+
+		mp->flags |= FLODMAP_FG_MARK;
+
+		dcc_error_msg("initialize flood to %s %d%s",
+			      dcc_host_portname(hostname, sizeof(hostname),
+						mp->rem_hostname,
+						mp->rem_portname),
+			      mp->rem_id,
+			      fnm_lno(&fnm_buf, flod_path, ofp->lno));
+	}
+out:;
+
+	/* clear the slots that contain forgotten hosts */
+	for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp) {
+		if (!(mp->flags & FLODMAP_FG_MARK)) {
+			if (mp->rem_hostname[0] != '\0')
+				dcc_error_msg("forget flood to %s %d",
+					      dcc_host_portname(hostname,
+							sizeof(hostname),
+							mp->rem_hostname,
+							mp->rem_portname),
+					      mp->rem_id);
+			memset(mp, 0, sizeof(*mp));
+		}
+	}
+
+	flod_mmap_sync(0, 1);
+
+	db_parms.flags &= ~DB_PARM_FG_CLEARED;
+	db_flush_parms(0);
+
+	get_oflods_max_cur_pos();
+	return 1;
+}
+
+
+
+/* put the flood counters in stable storage */
+void
+save_flod_cnts(OFLOD_INFO *ofp)
+{
+	FLOD_MMAP *mp;
+	time_t delta;
+	FLOD_LIMCNT *lc;
+
+	dccd_stats.iflod_total += ofp->cnts.total;
+	dccd_stats.iflod_accepted += ofp->cnts.accepted;
+	dccd_stats.iflod_stale += ofp->lc.stale.cur;
+	dccd_stats.iflod_dup += ofp->lc.dup.cur;
+	dccd_stats.iflod_wlist += ofp->lc.wlist.cur;
+	dccd_stats.iflod_not_deleted += ofp->lc.not_deleted.cur;
+
+	mp = ofp->mp;
+	if (mp) {
+		if (ofp->xmit_pos == ofp->recv_pos)
+			ofp->mp->confirm_pos = ofp->cur_pos;
+
+		mp->cnts.total += ofp->cnts.total;
+		mp->cnts.accepted += ofp->cnts.accepted;
+		mp->cnts.stale += ofp->lc.stale.cur;
+		mp->cnts.dup += ofp->lc.dup.cur;
+		mp->cnts.wlist += ofp->lc.wlist.cur;
+		mp->cnts.not_deleted += ofp->lc.not_deleted.cur;
+
+		mp->cnts.out_reports += ofp->cnts.out_reports;
+
+		delta = db_time.tv_sec - ofp->cnts.saved;
+		if (delta < 0)
+			delta = 0;
+		if (ofp->ifp) {
+			if (mp->flags & FLODMAP_FG_IN_CONN) {
+				mp->cnts.in_total_conn += delta;
+			} else {
+				mp->flags |= FLODMAP_FG_IN_CONN;
+				mp->cnts.in_conn_changed = db_time.tv_sec;
+			}
+		} else {
+			if (mp->flags & FLODMAP_FG_IN_CONN) {
+				mp->flags &= ~FLODMAP_FG_IN_CONN;
+				mp->cnts.in_conn_changed = db_time.tv_sec;
+			}
+		}
+
+		if (ofp->flags & OFLOD_FG_CONNECTED) {
+			if (mp->flags & FLODMAP_FG_OUT_CONN) {
+				mp->cnts.out_total_conn += delta;
+			} else {
+				mp->flags |= FLODMAP_FG_OUT_CONN;
+				mp->cnts.out_conn_changed = db_time.tv_sec;
+			}
+		} else {
+			if (mp->flags & FLODMAP_FG_OUT_CONN) {
+				mp->flags &= ~FLODMAP_FG_OUT_CONN;
+				mp->cnts.out_conn_changed = db_time.tv_sec;
+			}
+		}
+	}
+
+	memset(&ofp->cnts, 0, sizeof(ofp->cnts));
+	for (lc = (FLOD_LIMCNT *)&ofp->lc;
+	     lc < (FLOD_LIMCNT *)(sizeof(ofp->lc)+(char *)&ofp->lc);
+	     ++lc) {
+		lc->lim -= lc->cur;
+		lc->cur = 0;
+	}
+
+	ofp->cnts.saved = db_time.tv_sec;
+}
+
+
+
+void
+oflod_close(OFLOD_INFO *ofp, u_char fail)
+{
+	u_char need_oflods_clear = 0;
+
+	if (ofp->rem_hostname[0] == '\0')
+		return;
+
+	if (ofp->soc >= 0) {
+		if (0 > close(ofp->soc)
+		    && !fail) {
+			if (errno == ECONNRESET)
+				TMSG2_FLOD(ofp, "close(flood to %s): %s",
+					   ofp_rem_str(ofp), ERROR_STR());
+			else
+				dcc_error_msg("close(flod to %s): %s",
+					      ofp_rem_str(ofp), ERROR_STR());
+		}
+		ofp->soc = -1;
+		ofp->flags &= ~(OFLOD_FG_CONNECTED
+				| OFLOD_FG_SHUTDOWN_REQ
+				| OFLOD_FG_SHUTDOWN);
+		ofp->obuf_len = 0;
+		ofp->cnts.out_reports = 0;
+
+		save_flod_cnts(ofp);
+
+		if (--oflods.open == 0
+		    && iflods.open == 0
+		    && flods_st != FLODS_ST_ON)
+			need_oflods_clear = 1;
+	}
+
+	if (fail) {
+		if (ofp->mp->otimers.retry_secs < FLOD_RETRY_SECS/2)
+			ofp->mp->otimers.retry_secs = FLOD_RETRY_SECS/2;
+		ofp->mp->otimers.retry = (db_time.tv_sec
+					  + ofp->mp->otimers.retry_secs);
+		if (!(ofp->mp->flags & FLODMAP_FG_PASSIVE))
+			TMSG2_FLOD(ofp,
+				   "postpone restarting flood to %s"
+				   " for %d seconds",
+				   ofp_rem_str(ofp),
+				   ofp->mp->otimers.retry_secs);
+	}
+
+	if (need_oflods_clear)
+		oflods_clear();
+}
+
+
+
+/* get ready to shut down */
+static void
+start_shutdown(OFLOD_INFO *ofp)
+{
+	if (ofp->flags & (OFLOD_FG_SHUTDOWN_REQ
+			  | OFLOD_FG_SHUTDOWN))
+		return;
+
+	/* arrange to ask the peer to ask us to stop */
+	ofp->flags |= OFLOD_FG_SHUTDOWN_REQ;
+	ofp->flags &= ~OFLOD_FG_NEW;
+	oflod_fill(ofp);
+
+	/* oflod_write() might set this again, but that will either be soon
+	 * or a good thing if delayed */
+	ofp->oflod_alive = db_time.tv_sec;
+}
+
+
+
+/* Half-close the TCP connection.
+ *	The other DCC server will notice and send our final position
+ *	to acknowledge dealing with our reports. */
+static void
+oflod_shutdown(OFLOD_INFO *ofp)
+{
+	struct linger nowait;
+
+	/* wait until the output buffer is empty */
+	if (ofp->obuf_len != 0)
+		return;
+
+	/* do it only once */
+	if ((ofp->flags & OFLOD_FG_SHUTDOWN))
+		return;
+	ofp->flags |= OFLOD_FG_SHUTDOWN;
+
+	/* on Solaris and Linux you must set SO_LINGER before shutdown() */
+	nowait.l_onoff = 1;
+	nowait.l_linger = SHUTDOWN_DELAY;
+	if (0 > setsockopt(ofp->soc, SOL_SOCKET, SO_LINGER,
+			   &nowait, sizeof(nowait)))
+		rpt_err(ofp, 0, 0,
+			"setsockopt(SO_LINGER flood to %s): %s",
+			ofp_rem_str(ofp), ERROR_STR());
+
+	if (0 > shutdown(ofp->soc, 1)) {
+		rpt_err(ofp, errno == ECONNRESET, 0,
+			"shutdown(flood to %s): %s",
+			ofp_rem_str(ofp), ERROR_STR());
+		oflod_close(ofp, 1);
+	}
+}
+
+
+
+/* see if a report should be put into the output buffer for a flood
+ *  db_sts.rcd.d.r points to the current record.
+ *  ofp->cur_pos has already been advanced */
+static u_char				/* 1=flood this report, 0=don't */
+oflod_ck_put(void)
+{
+	const DB_RCD_CK *cur_rcd_ck;
+	DCC_TGTS rcd_tgts, ck_tgts;
+	int num_cks;
+	DCC_CK_TYPES type;
+	u_char obs_lvl, result;
+
+	/* skip padding, whitelisted, compressed, trimmed
+	 * and deleted entries */
+	if (DB_RCD_ID(db_sts.rcd.d.r) == DCC_ID_WHITE
+	    || DB_RCD_ID(db_sts.rcd.d.r) == DCC_ID_COMP
+	    || (rcd_tgts = DB_TGTS_RCD_RAW(db_sts.rcd.d.r)) == 0
+	    || DB_RCD_TRIMMED(db_sts.rcd.d.r))
+		return 0;
+
+	/* Skip reports that should not be flooded yet
+	 * The flooding thresholds are used to set the delay flag.
+	 * Small reports are marked with the delay flag when they are added
+	 * to the database.  If later it seems they should be flooded,
+	 * they are summarized in a new report that is flooded. */
+	if (DB_RCD_DELAY(db_sts.rcd.d.r))
+		return 0;
+
+	result = 0;
+	obs_lvl = 0;
+	cur_rcd_ck = db_sts.rcd.d.r->cks;
+	for (num_cks = DB_NUM_CKS(db_sts.rcd.d.r);
+	     num_cks != 0;
+	     ++cur_rcd_ck, --num_cks) {
+		type = DB_CK_TYPE(cur_rcd_ck);
+
+		/* ignore junk for deciding whether we can send this report. */
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+			continue;
+
+		if (DB_CK_OBS(cur_rcd_ck)) {
+			/* an obsolete fuzzier checksum
+			 * makes less fuzzy checksums obsolete */
+			if (obs_lvl < db_ck_fuzziness[type]) {
+				obs_lvl = db_ck_fuzziness[type];
+				result = 0;
+			}
+			continue;
+		}
+
+		/* send server-ID declarations
+		 * unless they are delete requests */
+		if (type == DCC_CK_SRVR_ID) {
+			if (rcd_tgts == DCC_TGTS_DEL)
+				continue;
+			return 1;
+		}
+
+		/* do not send whitelisted reports */
+		ck_tgts = DB_TGTS_CK(cur_rcd_ck);
+		if (ck_tgts == DCC_TGTS_OK || ck_tgts == DCC_TGTS_OK2)
+			return 0;
+
+		/* send non-obsolete results */
+		if (obs_lvl <= db_ck_fuzziness[type]) {
+			obs_lvl = db_ck_fuzziness[type];
+			result = 1;
+		}
+	}
+	return result;
+}
+
+
+
+/* put report into the output buffer for a flood if appropriate
+ *  db_sts.rcd.d.r points to the current record.
+ *  ofp->cur_pos has already been advanced */
+static void
+put_rcd_obuf(OFLOD_INFO *ofp, const DB_RCD *cur_rcd)
+{
+	DCC_FLOD_RPT *rp;
+	DCC_TGTS tgts;
+	DCC_SRVR_ID srvr, psrvr;
+	const DB_RCD_CK *cur_rcd_ck;
+	DCC_CK *buf_ck;
+	DCC_FLOD_PATH_ID *new_path_idp, *new_path_id_limp, *rcd_path_id;
+	int path_id_max;
+	DCC_CK_TYPES type;
+	ID_MAP_RESULT srvr_mapped;
+	u_char reflecting;		/* 1=report is pointed at its source */
+	u_char non_path, all_spam;
+	int num_cks, j;
+
+	/* decide whether to send this report */
+	if (!oflod_ck_put())
+		return;			/* skip it */
+
+	rp = (DCC_FLOD_RPT *)&ofp->obuf.b[ofp->obuf_len];
+	db_ptr2flod_pos(rp->pos, ofp->cur_pos);
+	tgts = DB_TGTS_RCD_RAW(cur_rcd);
+	if (tgts == DCC_TGTS_DEL) {
+		/* don't send delete requests to systems that don't want them */
+		if (!(ofp->o_opts.flags & FLOD_OPT_DEL_OK))
+			return;
+	} else if (ofp->o_opts.flags & FLOD_OPT_TRAPS) {
+		tgts = DCC_TGTS_TOO_MANY;
+	}
+
+	srvr = DB_RCD_ID(cur_rcd);
+	/* translate the source server-ID */
+	srvr_mapped = id_map(srvr, &ofp->o_opts);
+	switch (srvr_mapped) {
+	case ID_MAP_NO:
+		break;
+	case ID_MAP_REJ:
+		return;
+	case ID_MAP_SELF:
+		srvr = my_srvr_id;
+		break;
+	}
+	/* this loses the DCC_SRVR_ID_AUTH bit */
+	rp->srvr_id_auth[0] = srvr>>8;
+	rp->srvr_id_auth[1] = srvr;
+
+	reflecting = (srvr == ofp->rem_id);
+	non_path = 0;
+
+	rp->ts = cur_rcd->ts;
+
+	cur_rcd_ck = cur_rcd->cks;
+
+	/* Add a path if we are not the source of the report
+	 * or if it already has a path */
+	buf_ck = rp->cks;
+	if (srvr != my_srvr_id
+	    || DB_CK_TYPE(cur_rcd_ck) == DCC_CK_FLOD_PATH) {
+		/* Add a checksum entry for a path consisting of only our
+		 * server-ID.  If the report contains a path, we will
+		 * concatenate to this entry */
+		memset(buf_ck, 0, sizeof(*buf_ck));
+		buf_ck->len = sizeof(*buf_ck);
+		buf_ck->type = DCC_CK_FLOD_PATH;
+		new_path_idp = (DCC_FLOD_PATH_ID *)buf_ck->sum;
+		new_path_idp->hi = my_srvr_id>>8;
+		new_path_idp->lo = my_srvr_id;
+		new_path_id_limp = new_path_idp + DCC_NUM_FLOD_PATH;
+		path_id_max = ofp->o_opts.path_len-1;
+		++new_path_idp;
+		++buf_ck;
+		rp->num_cks = 1;
+	} else {
+		/* do not add a path */
+		new_path_idp = new_path_id_limp = 0;
+		path_id_max = 0;
+		rp->num_cks = 0;
+	}
+
+	all_spam = 1;
+	for (num_cks = DB_NUM_CKS(cur_rcd);
+	     num_cks != 0;
+	     ++cur_rcd_ck, --num_cks) {
+		type = DB_CK_TYPE(cur_rcd_ck);
+		if (type == DCC_CK_FLOD_PATH) {
+			rcd_path_id = (DCC_FLOD_PATH_ID *)&cur_rcd_ck->sum;
+			for (j = 0; j < DCC_NUM_FLOD_PATH; ++j, ++rcd_path_id) {
+				psrvr = ((rcd_path_id->hi<<8)
+					 | rcd_path_id->lo);
+				/* stop copying the path at its end */
+				if (psrvr == DCC_ID_INVALID)
+					break;
+				/* don't send report if its path is too long */
+				if (--path_id_max < 0)
+					return;
+				/* add another "checksum" to continue path */
+				if (new_path_idp >= new_path_id_limp) {
+					memset(buf_ck, 0, sizeof(*buf_ck));
+					buf_ck->len = sizeof(*buf_ck);
+					buf_ck->type = DCC_CK_FLOD_PATH;
+					new_path_idp = (DCC_FLOD_PATH_ID *
+							)buf_ck->sum;
+					new_path_id_limp = (new_path_idp
+							+ DCC_NUM_FLOD_PATH);
+					++buf_ck;
+					++rp->num_cks;
+				}
+				/* Do not send reports from the target back
+				 * to the target unless the report is a
+				 * server-ID declaration */
+				if (psrvr == ofp->rem_id)
+					reflecting = 1;
+				switch (id_map(psrvr, &ofp->o_opts)) {
+				case ID_MAP_NO:
+					break;
+				case ID_MAP_REJ:
+					return;
+				case ID_MAP_SELF:
+					psrvr = my_srvr_id;
+					break;
+				}
+				new_path_idp->hi = psrvr>>8;
+				new_path_idp->lo = psrvr;
+				++new_path_idp;
+			}
+
+		} else {
+			/* Do not send translated server-ID declarations
+			 * or checksums in our own or in translated server-ID
+			 * reports that we wouldn't have kept if we had
+			 * received the original reports */
+			if (srvr_mapped == ID_MAP_SELF) {
+				if (type == DCC_CK_SRVR_ID)
+					return;
+				if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+					continue;
+			}
+			/* Do not send reports from the target to the target
+			 * unless the report is a Server-ID declaration */
+			if (reflecting && type != DCC_CK_SRVR_ID)
+				return;
+
+			/* send everything else */
+			buf_ck->type = type;
+			buf_ck->len = sizeof(*buf_ck);
+			memcpy(buf_ck->sum, cur_rcd_ck->sum,
+			       sizeof(buf_ck->sum));
+			++buf_ck;
+			++rp->num_cks;
+
+			non_path = 1;
+			if (all_spam
+			    && DB_TGTS_CK(cur_rcd_ck) != DCC_TGTS_TOO_MANY)
+				all_spam = 0;
+		}
+	}
+
+	/* quit if we found nothing but the path to send */
+	if (!non_path)
+		return;
+
+	if (all_spam && srvr == my_srvr_id)
+		tgts = DCC_TGTS_TOO_MANY;
+	tgts = htonl(tgts);
+	memcpy(&rp->tgts, &tgts, sizeof(rp->tgts));
+
+	ofp->obuf_len += (char *)buf_ck - (char *)rp;
+	++ofp->cnts.out_reports;
+	ofp->xmit_pos = ofp->cur_pos;
+}
+
+
+
+/* send reports from the database to a peer DCC server
+ *	This routine only fills the buffer.  The buffer is eventually
+ *	written by oflod_write(). */
+static void
+oflod_fill(OFLOD_INFO *ofp)
+{
+	int cur_rcd_len;
+
+	/* stop when things are not ready or shutting down */
+	if (!(ofp->flags & OFLOD_FG_CONNECTED)
+	    || (ofp->flags & OFLOD_FG_SHUTDOWN)
+	    || (ofp->flags & OFLOD_FG_NEW))
+		return;
+
+	/* stop when we are about to clean the database for a deletion so
+	 * that we will not be shut down cleaning along with our neighbors */
+	if (need_del_dbclean)
+		return;
+
+	if (db_failed_line)
+		return;
+
+	while (ofp->obuf_len < sizeof(ofp->obuf) - sizeof(DCC_FLOD_RPT)) {
+		/* start a new entry unless we are shutting down */
+		if (ofp->flags & OFLOD_FG_SHUTDOWN_REQ) {
+			oflod_shutdown(ofp);
+			break;
+		}
+
+		if (ofp->cur_pos >= db_csize) {
+			/* nothing to send
+			 * shut down if needed */
+			if (ofp->xmit_pos == ofp->recv_pos)
+				ofp->mp->confirm_pos = ofp->cur_pos;
+			if (ofp->mp->confirm_pos >= ofp->rewind_pos)
+				ofp->mp->flags &= ~FLODMAP_FG_REWINDING;
+			break;
+		}
+
+		/* don't try to look at reports crossing page bounardies */
+		if (ofp->cur_pos%db_pagesize >= db_page_max) {
+			ofp->cur_pos += DB_RCD_HDR_LEN;
+			continue;
+		}
+
+		if (!db_map_rcd(dcc_emsg, &db_sts.rcd, ofp->cur_pos,
+				&cur_rcd_len)) {
+			dcc_error_msg("oflod_fill() starting at "L_HPAT
+				      " for %s: %s",
+				      ofp->cur_pos, ofp_rem_str(ofp), dcc_emsg);
+			ofp->cur_pos = db_csize;
+			break;
+		}
+
+		if (DB_NUM_CKS(db_sts.rcd.d.r) > DCC_DIM_CKS) {
+			dcc_error_msg("impossible %d checksums in "L_HPAT,
+				      DB_NUM_CKS(db_sts.rcd.d.r),
+				      ofp->cur_pos);
+			ofp->cur_pos = db_csize;
+			break;
+		}
+
+		/* send the record */
+		ofp->cur_pos += cur_rcd_len;
+		put_rcd_obuf(ofp, db_sts.rcd.d.r);
+	}
+
+	if (oflods_max_cur_pos < ofp->cur_pos)
+		oflods_max_cur_pos = ofp->cur_pos;
+}
+
+
+
+/* figure out what version to tell the peer */
+const char *
+version_str(OFLOD_INFO *ofp)
+{
+	if (ofp->oversion == 0)
+		return DCC_FLOD_VERSION_CUR_STR;
+#ifdef DCC_FLOD_VERSION7
+	if (ofp->oversion == DCC_FLOD_VERSION7)
+		return DCC_FLOD_VERSION_CUR_STR;
+#endif
+	dcc_logbad(EX_SOFTWARE, "unknown ofp->oversion=%d",
+		   ofp->oversion);
+}
+
+
+
+/* reset connect() or daily complaint timers and keep backoffs steady */
+void
+flod_try_again(OFLOD_INFO *ofp)
+{
+	FLOD_MMAP *mp = ofp->mp;
+
+	mp = ofp->mp;
+	if (!mp)
+		return;
+
+	/* ordinary connect() timer should fire immediately */
+	mp->otimers.retry_secs /= 2;
+	mp->otimers.retry = 0;
+
+	/* delay complaints for passive connections */
+	if (DCC_IS_TIME(db_time.tv_sec+FLOD_IN_COMPLAIN_NOW,
+			mp->otimers.msg, mp->otimers.msg_secs)) {
+		mp->otimers.msg_secs = FLOD_IN_COMPLAIN_NOW;
+		mp->otimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN_NOW;
+	}
+
+	mp->oflod_err.msg[0] = '\0';
+	mp->oflod_err.trace_msg[0] = '\0';
+
+	/* give the peer a chance to connect to us */
+	mp->itimers.retry_secs /= 2;
+	if (mp->itimers.retry_secs < FLOD_SOCKS_SOCKS_IRETRY)
+		mp->itimers.retry_secs = FLOD_SOCKS_SOCKS_IRETRY;
+	mp->itimers.retry = db_time.tv_sec + FLOD_SOCKS_SOCKS_IRETRY;
+
+	if (DCC_IS_TIME(db_time.tv_sec+FLOD_IN_COMPLAIN_NOW,
+			mp->itimers.msg, mp->itimers.msg_secs)) {
+		mp->itimers.msg_secs = FLOD_IN_COMPLAIN_NOW;
+		mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN_NOW;
+	}
+
+	mp->iflod_err.msg[0] = '\0';
+	mp->iflod_err.trace_msg[0] = '\0';
+}
+
+
+
+/* authenticate the outgoing start of a flood */
+const char *				/* error message */
+flod_sign(OFLOD_INFO *ofp, u_char in, void *buf, int buf_len)
+{
+	const ID_TBL *tp;
+
+	tp = find_id_tbl(ofp->out_passwd_id);
+	if (!tp)
+		return DCC_FLOD_PASSWD_ID_MSG;
+	if (tp->cur_passwd[0] == '\0')
+		return "no password for passwd-ID";
+
+	if (tp->next_passwd[0] != '\0') {
+		ofp->flags |= OFLOD_FG_HAVE_2PASSWD;
+	} else {
+		ofp->flags &= ~OFLOD_FG_HAVE_2PASSWD;
+		ofp->mp->flags &= ~FLODMAP_FG_USE_2PASSWD;
+	}
+	if (ofp->mp->flags & FLODMAP_FG_USE_2PASSWD) {
+		if (in)
+			ofp->flags |= OFLOD_FG_I_USED_2PASSWD;
+		else
+			ofp->flags |= OFLOD_FG_O_USED_2PASSWD;
+		TMSG1_FLOD(ofp, "try 2nd password to %s", ofp_rem_str(ofp));
+		dcc_sign(tp->next_passwd, sizeof(tp->next_passwd),
+			 buf, buf_len);
+	} else {
+		if (in)
+			ofp->flags &= ~OFLOD_FG_I_USED_2PASSWD;
+		else
+			ofp->flags &= ~OFLOD_FG_O_USED_2PASSWD;
+		dcc_sign(tp->cur_passwd, sizeof(tp->cur_passwd),
+			 buf, buf_len);
+	}
+	return 0;
+}
+
+
+
+/* finish connecting output flood by sending our version number and signature
+ *	to authenticate ourself */
+u_char					/* 1=ok, 0=close output stream */
+oflod_connect_fin(OFLOD_INFO *ofp)
+{
+	DCC_SRVR_ID id;
+	DCC_FNM_LNO_BUF fnm_buf;
+	const char *emsg;
+
+	ofp->oflod_alive = db_time.tv_sec;
+	ofp->flags |= (OFLOD_FG_CONNECTED | OFLOD_FG_NEW);
+	save_flod_cnts(ofp);
+
+	ofp->recv_pos = ofp->xmit_pos = ofp->cur_pos = ofp->mp->confirm_pos;
+	get_oflods_max_cur_pos();
+
+	ofp->ibuf_len = 0;
+
+	/* convince the peer we're sane by sending our version string */
+	ofp->obuf_len = sizeof(ofp->obuf.s.v);
+	memset(&ofp->obuf.s.v, 0, sizeof(ofp->obuf.s.v));
+	strcpy(ofp->obuf.s.v.body.str, version_str(ofp));
+	id = htons(my_srvr_id);
+	memcpy(ofp->obuf.s.v.body.sender_srvr_id, &id,
+	       sizeof(ofp->obuf.s.v.body.sender_srvr_id));
+
+	emsg = flod_sign(ofp, 0, &ofp->obuf.s.v, ofp->obuf_len);
+	if (emsg) {
+		rpt_err(ofp, 0, 0, "%s %d%s",
+			emsg, ofp->out_passwd_id,
+			fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		return 0;
+	}
+
+	TMSG1_FLOD(ofp, "start flood to %s", ofp_rem_str(ofp));
+
+	/* all is well, so forget old complaints */
+	if (!(ofp->mp->flags & FLODMAP_FG_OUT_SRVR)) {
+		ofp->mp->oflod_err.msg[0] = '\0';
+		ofp->mp->oflod_err.trace_msg[0] = '\0';
+	}
+
+	oflod_write(ofp);		/* send our authentication */
+
+	return 1;
+}
+
+
+
+static void
+oflod_backoff(OFLOD_INFO *ofp, u_char fast)
+{
+	FLOD_MMAP *mp;
+	int max;
+
+	mp = ofp->mp;
+	mp->otimers.retry_secs *= 2;
+	max = fast ? FLOD_SUBMAX_RETRY_SECS : FLOD_MAX_RETRY_SECS;
+	if (mp->otimers.retry_secs > max)
+		mp->otimers.retry_secs = max;
+	else if (mp->otimers.retry_secs < FLOD_RETRY_SECS)
+		mp->otimers.retry_secs = FLOD_RETRY_SECS;
+}
+
+
+
+/* start to connect an out-going flood */
+static int				/* -1=failure, 0=not yet, 1=done */
+oflod_connect_start(OFLOD_INFO *ofp, const char *syscall_name)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	int i;
+
+	ofp->mp->flags &= ~FLODMAP_FG_OUT_SRVR;
+
+	if (ofp->o_opts.flags & FLOD_OPT_SOCKS) {
+		i = Rconnect(ofp->soc, &ofp->rem_su.sa,
+			     DCC_SU_LEN(&ofp->rem_su));
+	} else {
+		/* must be NAT */
+		i = connect(ofp->soc, &ofp->rem_su.sa,
+			    DCC_SU_LEN(&ofp->rem_su));
+	}
+	if (0 > i && errno != EISCONN) {
+		if (errno == EAGAIN
+		    || errno == EINPROGRESS
+		    || errno == EALREADY) {
+			rpt_err(ofp, 1, 0, "starting flood to %s",
+				ofp_rem_str(ofp));
+			return 0;
+		}
+
+		/* Several UNIX-like systems return EINVAL for the second
+		 * connect() after a Unreachable ICMP message or timeout.
+		 * It is lame to obscure the real errno, but it is worse
+		 * to worry users and deal with their concerns. */
+		rpt_err(ofp,
+			(errno == EINVAL || errno == ECONNABORTED
+			 || errno == ECONNRESET || errno == ETIMEDOUT
+			 || errno == ECONNREFUSED),
+			0, "%s(%s%s): %s",
+			syscall_name,
+			ofp_rem_str(ofp),
+			fnm_lno(&fnm_buf, flod_path, ofp->lno),
+			errno == EINVAL
+			? "likely connection refused or local firewall"
+			: ERROR_STR());
+
+		/* do not back off connection attempts for SOCKS or NAT because
+		 * the peer cannot trigger anything by connecting to us */
+		oflod_backoff(ofp, (ofp->mp->flags & FLODMAP_FG_ACT) != 0);
+		oflod_close(ofp, 1);
+		return -1;
+	}
+
+	if (!oflod_connect_fin(ofp)) {
+		oflod_close(ofp, 1);
+		return -1;
+	}
+
+	return 1;
+}
+
+
+
+void
+oflod_open(OFLOD_INFO *ofp)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	DCC_SOCKU loc_su, su2;
+	const DCC_SOCKU *sup;
+	const SRVR_SOC *sp;
+	int error;
+
+	if (ofp->soc >= 0
+	    || ofp->rem_hostname[0] == '\0'
+	    || OFLOD_OPT_OFF_ROGUE(ofp)
+	    || flods_st != FLODS_ST_ON
+	    || (ofp->mp->flags & FLODMAP_FG_PASSIVE))
+		return;
+
+	if (!DB_IS_TIME(ofp->mp->otimers.retry, ofp->mp->otimers.retry_secs))
+		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, 0, "peer name %s: '%s'%s",
+			ofp->rem_hostname,
+			DCC_HSTRERROR(ofp->mp->host_error),
+			fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		oflod_backoff(ofp, 0);
+		oflod_close(ofp, 1);
+		return;
+	}
+	if (!LITCMP(ofp->mp->oflod_err.msg, "peer name "))
+		ofp->mp->oflod_err.msg[0] = '\0';
+
+	ofp->rem_su = ofp->mp->rem_su;
+
+	ofp->soc = socket(ofp->rem_su.sa.sa_family, SOCK_STREAM, 0);
+	if (ofp->soc < 0) {
+		rpt_err(ofp, 0, 0, "flood socket(%s): %s",
+			ofp_rem_str(ofp), ERROR_STR());
+		oflod_close(ofp, 1);
+		return;
+	}
+	++oflods.open;
+
+	if (!set_flod_socket(ofp, 0, ofp->soc,
+			     ofp->rem_hostname, &ofp->rem_su)) {
+		oflod_close(ofp, 1);
+		return;
+	}
+
+	memset(&loc_su, 0, sizeof(loc_su));
+	if (ofp->loc_hostname[0] != '\0'
+	    || ofp->loc_port != 0) {
+		/* Resolve the local host name.
+		 * This should not take significant time because
+		 * the local hostnames should be locally known.  That
+		 * implies that we don't need to use a separate thread. */
+		if (ofp->loc_hostname[0] != '\0') {
+			dcc_host_lock();
+			if (!dcc_get_host(ofp->loc_hostname,
+					  ofp->rem_su.sa.sa_family == AF_INET
+					  ? 0 : 1,
+					  &error)) {
+				dcc_error_msg("flood local name %s: %s%s",
+					      ofp->loc_hostname,
+					      DCC_HSTRERROR(error),
+					      fnm_lno(&fnm_buf, flod_path,
+						      ofp->lno));
+			} else {
+				/* match local address family to remote */
+				sup = dcc_hostaddrs;
+				for (;;) {
+					if (sup->sa.sa_family
+					    == ofp->rem_su.sa.sa_family) {
+					    loc_su = *sup;
+					    break;
+					}
+					if (++sup >= dcc_hostaddrs_end) {
+					    dcc_error_msg("family matching %s"
+							" not available for %s",
+							ofp_rem_str(ofp),
+							ofp->loc_hostname);
+					    ofp->loc_hostname[0] = '\0';
+					    break;
+					}
+				}
+			}
+			dcc_host_unlock();
+		}
+	}
+
+	/* If there is a single "-a address" other than localhost
+	 * and of the right family, then default to it.
+	 * but only if we are not trying to get past a firewall */
+	if ((ofp->mp->flags & (FLODMAP_FG_SOCKS | FLODMAP_FG_NAT)) == 0
+	    && loc_su.sa.sa_family == AF_UNSPEC) {
+		for (sp = srvr_socs; sp; sp = sp->fwd) {
+			if (dcc_ipv6sutoipv4(&su2, &sp->su)
+			    && su2.ipv4.sin_addr.s_addr == ntohl(0x7f000001))
+				continue;
+			if (sp->su.sa.sa_family != ofp->rem_su.sa.sa_family)
+				continue;
+			if (loc_su.sa.sa_family != AF_UNSPEC) {
+				/* more than one, so give up */
+				memset(&loc_su, 0, sizeof(loc_su));
+				break;
+			}
+			loc_su = sp->su;
+		}
+	}
+
+	if (loc_su.sa.sa_family != AF_UNSPEC
+	    || ofp->loc_port != 0) {
+		loc_su.sa.sa_family = ofp->rem_su.sa.sa_family;
+		*DCC_SU_PORTP(&loc_su) = ofp->loc_port;
+		if (0 > bind(ofp->soc, &loc_su.sa, DCC_SU_LEN(&loc_su)))
+			dcc_error_msg("bind(flood %s%s): %s",
+				      dcc_su2str_err(&loc_su),
+				      fnm_lno(&fnm_buf, flod_path, ofp->lno),
+				      ERROR_STR());
+	}
+
+	oflod_connect_start(ofp, "connect");
+}
+
+
+
+void
+oflod_write(OFLOD_INFO *ofp)
+{
+	int i;
+
+	if (ofp->obuf_len == 0) {
+		if (!(ofp->flags & OFLOD_FG_CONNECTED)
+		    && 0 >= oflod_connect_start(ofp, "connect2"))
+			return;
+		oflod_fill(ofp);
+		if (ofp->obuf_len == 0)
+			return;
+	}
+
+	if (ofp->o_opts.flags & FLOD_OPT_SOCKS)
+		i = Rsend(ofp->soc, &ofp->obuf.b, ofp->obuf_len, 0);
+	else
+		i = send(ofp->soc, &ofp->obuf.b, ofp->obuf_len, 0);
+	if (i > 0) {
+		ofp->obuf_len -= i;
+		if (ofp->obuf_len != 0)
+			memmove(&ofp->obuf.b[0], &ofp->obuf.b[i],
+				ofp->obuf_len);
+		ofp->oflod_alive = db_time.tv_sec;
+
+		/* fill buffer so that the main loop will
+		 * ask select() when we can send again */
+		oflod_fill(ofp);
+		return;
+	}
+
+	/* we had an error or EOF */
+	if (i < 0) {
+		/* oflod_write() is called only when select() has said that
+		 * we can send() and so we should never see the non-blocking
+		 * send() fail.
+		 * However, Solaris nevertheless sometimes says EAGAIN */
+		if (DCC_BLOCK_ERROR()) {
+			ofp->flags |= OFLOD_FG_EAGAIN;
+			TMSG2_FLOD(ofp, "pause after send(flood to %s): %s",
+				   ofp_rem_str(ofp), ERROR_STR());
+			return;
+		}
+
+		rpt_err(ofp, 0, 0, "send(flood to %s): %s",
+			ofp_rem_str(ofp), ERROR_STR());
+	} else {
+		rpt_err(ofp, 0, 0, "premature end of flood to %s",
+			ofp_rem_str(ofp));
+	}
+	oflod_read(ofp);		/* get any last error message */
+	oflod_close(ofp, 1);
+}
+
+
+/* parse end of transmission message for familiar complaints
+ *	to adjust retry timer */
+int					/* 1=try again soon, 0=ok, -1=failure */
+oflod_parse_eof(OFLOD_INFO *ofp, u_char in,
+		const DCC_FLOD_END *end, int msg_len)
+{
+	if (msg_len >= LITZ(DCC_FLOD_OK_STR)
+	    && !strncmp(end->msg, DCC_FLOD_OK_STR,
+			LITZ(DCC_FLOD_OK_STR))) {
+		return 0;		/* success */
+	}
+
+	if (msg_len >= LITZ(DCC_FLOD_BAD_AUTH_MSG)
+	    && !strncmp(end->msg, DCC_FLOD_BAD_AUTH_MSG,
+			LITZ(DCC_FLOD_BAD_AUTH_MSG))) {
+		/* try the second password if available
+		 * after the peer rejects the first */
+		if (in) {
+			if ((ofp->flags & OFLOD_FG_HAVE_2PASSWD)
+			    && !(ofp->flags & OFLOD_FG_I_USED_2PASSWD)) {
+				ofp->flags |= OFLOD_FG_I_USED_2PASSWD;
+				ofp->mp->flags |= FLODMAP_FG_USE_2PASSWD;
+				return 1;   /* try again soon */
+			}
+		} else {
+			if ((ofp->flags & OFLOD_FG_HAVE_2PASSWD)
+			    && !(ofp->flags & OFLOD_FG_O_USED_2PASSWD)) {
+				ofp->flags |= OFLOD_FG_O_USED_2PASSWD;
+				ofp->mp->flags |= FLODMAP_FG_USE_2PASSWD;
+				return 1;   /* try again soon */
+			}
+		}
+		return -1;
+	}
+
+	if (msg_len > LITZ(DCC_FLOD_BAD_VER_MSG)
+	    && !strncmp(end->msg, DCC_FLOD_BAD_VER_MSG,
+			LITZ(DCC_FLOD_BAD_VER_MSG))) {
+		/* notice if this peer demands a version
+		 * other than what we have been trying */
+		if (ofp->oversion != ofp->mp->iversion) {
+			ofp->oversion = ofp->mp->iversion;
+			return 1;	/* try again soon */
+		}
+		return -1;
+	}
+
+	return -1;
+}
+
+
+
+/* see what the target has to say about the reports we have been sending */
+void
+oflod_read(OFLOD_INFO *ofp)
+{
+	int used, req_len, recv_len;
+	DB_PTR pos;
+	int fail;
+
+again:;
+	req_len = sizeof(ofp->ibuf) - ofp->ibuf_len;
+	if (ofp->o_opts.flags & FLOD_OPT_SOCKS)
+		recv_len = Rrecv(ofp->soc, &ofp->ibuf.b[ofp->ibuf_len],
+				 req_len, 0);
+	else
+		recv_len = recv(ofp->soc, &ofp->ibuf.b[ofp->ibuf_len],
+				req_len, 0);
+	if (recv_len < 0) {
+		if (!DCC_BLOCK_ERROR()) {
+			rpt_err(ofp, 1, 0, "recv(outgoing flood %s): %s",
+				ofp_rem_str(ofp), ERROR_STR());
+			oflod_close(ofp, 1);
+		}
+		return;
+	}
+	if (recv_len > 0) {
+		/* the connection is alive and working */
+		if (!(ofp->flags & (OFLOD_FG_SHUTDOWN_REQ | OFLOD_FG_SHUTDOWN)))
+			ofp->oflod_alive = db_time.tv_sec;
+		ofp->flags &= ~OFLOD_FG_NEW;
+		if (!(ofp->flags & (OFLOD_FG_SHUTDOWN_REQ
+				    | OFLOD_FG_SHUTDOWN)))
+			ofp->oflod_alive = db_time.tv_sec;
+
+		/* limit the backoff for incoming SOCKS connection attempts
+		 * while the outgoing connection is working */
+		DB_ADJ_TIMER(&ofp->mp->itimers.retry,
+			     &ofp->mp->itimers.retry_secs,
+			     FLOD_RETRY_SECS);
+	}
+
+	ofp->ibuf_len += recv_len;
+	while (ofp->ibuf_len >= ISZ(ofp->ibuf.r.pos)) {
+		used = sizeof(ofp->ibuf.r.pos);
+
+		pos = flod_pos2db_ptr(ofp->ibuf.r.pos);
+		switch ((DCC_FLOD_POS_OPS)pos) {
+		case DCC_FLOD_POS_END:
+			/* Wait for all of the final status message or
+			 * until the target closes the TCP connection.
+			 * Do not worry if the target stops without
+			 * asking nicely, since at worst we will
+			 * resend whatever was in the pipe next time. */
+			if (ofp->ibuf_len <= ISZ(ofp->ibuf.r.end)
+			    && recv_len != 0)
+				goto again;
+			/* shut down after trying to recognize
+			 * a complaint from the target */
+			fail = oflod_parse_eof(ofp, 0,
+					       &ofp->ibuf.r.end,
+					       ofp->ibuf_len - FLOD_END_OVHD);
+			rpt_err(ofp, fail>=0, 0,
+				"outgoing flood end 'status from %s \"%.*s\"'",
+				ofp_rem_str(ofp),
+				ofp->ibuf_len - FLOD_END_OVHD,
+				ofp->ibuf.r.end.msg);
+			if (fail < 0)
+				oflod_backoff(ofp, 0);
+			oflod_close(ofp, fail<0);
+			return;
+
+		case DCC_FLOD_POS_END_REQ:
+			/* try to update our pointers and shutdown() */
+			start_shutdown(ofp);
+			ofp->mp->otimers.retry_secs = FLOD_RETRY_SECS;
+			ofp->mp->otimers.retry = (db_time.tv_sec
+						  + FLOD_RETRY_SECS);
+			if (!(ofp->mp->flags & FLODMAP_FG_PASSIVE))
+				TMSG2_FLOD(ofp, "postpone restarting flood to"
+					   " %s for %d"
+					   " seconds after end request",
+					   ofp_rem_str(ofp), FLOD_RETRY_SECS);
+			break;
+
+		case DCC_FLOD_POS_NOTE:
+			/* wait until we get the length of the complaint */
+			if (ofp->ibuf_len < FLOD_NOTE_OVHD)
+				goto again;
+			used = ofp->ibuf.r.note.len;
+			if (used > ISZ(ofp->ibuf.r.note)
+			    || used <= FLOD_NOTE_OVHD) {
+				rpt_err(ofp, 0,0,
+					"bogus outgoing flood note length"
+					" %d from %s",
+					used, ofp_rem_str(ofp));
+				oflod_close(ofp, 1);
+				return;
+			}
+			if (ofp->ibuf_len < used)
+				goto again;
+			TMSG3_FLOD(ofp, "outgoing flood note from %s: \"%.*s\"",
+				   ofp_rem_str(ofp),
+				   used-FLOD_NOTE_OVHD, ofp->ibuf.r.note.str);
+			break;
+
+		case DCC_FLOD_POS_COMPLAINT:
+			/* wait until we get the length of the complaint */
+			if (ofp->ibuf_len < FLOD_NOTE_OVHD)
+				goto again;
+			used = ofp->ibuf.r.note.len;
+			if (used > ISZ(ofp->ibuf.r.note)
+			    || used <= FLOD_NOTE_OVHD) {
+				rpt_err(ofp, 0, 0,
+					"bogus outgoing flood complaint length"
+					" %d from %s",
+					used, ofp_rem_str(ofp));
+				oflod_close(ofp, 1);
+				return;
+			}
+			if (ofp->ibuf_len < used)
+				goto again;
+			if (CK_FLOD_CNTERR(&ofp->lc.complaint))
+				flod_cnterr(&ofp->lc.complaint,
+					    "outgoing flood complaint from %s:"
+					    " %.*s",
+					    ofp_rem_str(ofp),
+					    used - FLOD_NOTE_OVHD,
+					    ofp->ibuf.r.note.str);
+			break;
+
+		case DCC_FLOD_POS_REWIND:
+			dcc_trace_msg("flood rewind request from %s",
+				      ofp_rem_str(ofp));
+			ofp->mp->flags |= FLODMAP_FG_REWINDING;
+			ofp->cur_pos = ofp->mp->confirm_pos = DB_PTR_BASE;
+			ofp->recv_pos = ofp->xmit_pos = DB_PTR_BASE;
+			ofp->rewind_pos = db_csize;
+			get_oflods_max_cur_pos();
+			oflod_fill(ofp);
+			break;
+
+		case DCC_FLOD_POS_FFWD_IN:
+			dcc_trace_msg("FFWD its input from %s",
+				      ofp_rem_str(ofp));
+			ofp->cur_pos = db_csize;
+			get_oflods_max_cur_pos();
+			break;
+
+
+		default:
+			/* The position from the peer must be one we sent,
+			 * and in the window we expect unless our
+			 * window has been broken by rewinding.
+			 * Even if our window is broken, the position must
+			 * be reasonable. */
+			if ((pos < ofp->recv_pos
+			     || pos > ofp->xmit_pos)
+			    && (!(ofp->mp->flags & FLODMAP_FG_REWINDING)
+				|| pos < DCC_FLOD_POS_MIN
+				|| pos > db_csize)) {
+				rpt_err(ofp, 0, 0,
+					"bogus confirmed flood position"
+					" "L_HPAT" from %s;"
+					" recv_pos="L_HPAT"  xmit_pos="L_HPAT,
+					pos, ofp_rem_str(ofp),
+					ofp->recv_pos, ofp->xmit_pos);
+				oflod_close(ofp, 1);
+				return;
+			}
+			ofp->recv_pos = pos;
+			if (ofp->xmit_pos == ofp->recv_pos)
+				ofp->mp->confirm_pos = ofp->cur_pos;
+			else if (ofp->mp->confirm_pos < ofp->recv_pos)
+				ofp->mp->confirm_pos = ofp->recv_pos;
+
+			/* things are going ok, so reset the connect() backoff
+			 * and the no-connection complaint */
+			ofp->mp->otimers.retry_secs = 0;
+			ofp->mp->otimers.msg_secs = FLOD_IN_COMPLAIN1;
+			ofp->mp->otimers.msg = (db_time.tv_sec
+						+ FLOD_IN_COMPLAIN1);
+			break;
+		}
+
+		ofp->ibuf_len -= used;
+		if (ofp->ibuf_len == 0)
+			return;
+		if (ofp->ibuf_len < 0)
+			dcc_logbad(EX_SOFTWARE, "ofp->ibuf_len=%d",
+				   ofp->ibuf_len);
+		/* assume there will rarely be more than one position
+		 * in the buffer */
+		memmove(ofp->ibuf.b, &ofp->ibuf.b[used], ofp->ibuf_len);
+	}
+
+	if (recv_len == 0) {
+		/* before closing, the peer is supposed to send a
+		 * "position" of DCC_FLOD_POS_END followed by
+		 * an ASCII message */
+		if (ofp->ibuf_len != 0)
+			rpt_err(ofp, 0, 0,
+				"truncated outgoing flood response from %s",
+				ofp_rem_str(ofp));
+		else
+			rpt_err(ofp, 0, 0,
+				"missing outgoing flood response from %s",
+				ofp_rem_str(ofp));
+		oflod_close(ofp, 1);
+	}
+}
+
+
+
+static void
+oflods_ck(void)
+{
+	OFLOD_INFO *ofp;
+
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] == '\0')
+			break;
+
+		if (ofp->flags & OFLOD_FG_EAGAIN) {
+			TMSG1_FLOD(ofp, "resume flooding %s after EAGAIN",
+				   ofp->rem_hostname);
+			ofp->flags &= ~OFLOD_FG_EAGAIN;
+		}
+
+		if (!(ofp->flags & OFLOD_FG_CONNECTED))
+			continue;
+
+		/* close the peer has failed to respond to a shutdown request */
+		if (ofp->flags & (OFLOD_FG_SHUTDOWN_REQ | OFLOD_FG_SHUTDOWN)) {
+			if (stopint && OFP_DEAD(ofp, SHUTDOWN_DELAY)) {
+				rpt_err(ofp, 1, 0,
+					"stopping; force close flood to %s",
+					ofp_rem_str(ofp));
+				oflod_close(ofp, 0);
+
+			} else if (OFP_DEAD(ofp, KEEPALIVE_OUT_STOP)) {
+				rpt_err(ofp, 1, 0,
+					"off; force close flood to %s",
+					ofp_rem_str(ofp));
+				oflod_close(ofp, 0);
+			}
+			continue;
+		}
+
+		/* Shut down any streams that have been quiet for too long.
+		 * If the TCP connection is healthy we should at least have
+		 * received keep alive position repetitions or "are you there?"
+		 * notes from the peer. */
+		if (OFP_DEAD(ofp, KEEPALIVE_OUT)) {
+			rpt_err(ofp, 1, 0,
+				"keepalive start shutdown flood to %s",
+				ofp_rem_str(ofp));
+			start_shutdown(ofp);
+			continue;
+		}
+	}
+}
+
+
+
+static void
+oflods_stop(u_char force)
+{
+	OFLOD_INFO *ofp;
+
+	if (!flod_mmaps)
+		return;
+
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] == '\0')
+			break;
+		if (ofp->soc < 0)
+			continue;
+		if (force || !(ofp->flags & OFLOD_FG_CONNECTED)) {
+			rpt_err(ofp, 1, 0, "halting flood to %s",
+				ofp_rem_str(ofp));
+			oflod_close(ofp, 0);
+		} else if (!(ofp->flags & (OFLOD_FG_SHUTDOWN_REQ
+					   | OFLOD_FG_SHUTDOWN))) {
+			rpt_err(ofp, 1, 0, "stopping flood to %s",
+				ofp_rem_str(ofp));
+			start_shutdown(ofp);
+		}
+	}
+
+	if (oflods.open == 0 && iflods.open == 0)
+		oflods_clear();
+}
+
+
+
+void
+flods_stop(const char *iflod_msg, u_char force)
+{
+	flods_st = FLODS_ST_OFF;
+	iflods_stop(iflod_msg, force);
+	oflods_stop(force);
+}
+
+
+
+/* (re)start listening for incoming floods and sending outgoing floods */
+void
+flods_restart(const char *msg, u_char force_ck)
+{
+	if (FLODS_OK())
+		flods_st = FLODS_ST_RESTART;
+	iflods_stop(msg, 0);
+	flods_ck(force_ck);
+}
+
+
+
+/* load the ids file if it has changed */
+int					/* -1=our ID missing,  0=sick file */
+check_load_ids(u_char mode)		/* 0=if needed, 1=reboot, 2=new db */
+{
+	const ID_TBL *tp;
+	int result;
+
+	result = load_ids(dcc_emsg, my_srvr_id, &tp, mode ? 1 : 0 );
+	if (result == 2)
+		return 1;
+	if (result <= 0)
+		return result;
+
+	if (mode == 0 || ( mode == 2 && db_debug))
+		dcc_trace_msg("reloaded %s", ids_path);
+
+	if (mode == 0) {
+		if (flod_mtime > 1)
+			flod_mtime = 1;
+		flods_restart("restart flooding with new IDs", 0);
+	}
+
+	return 1;
+}
+
+
+/* called periodically and at need */
+void
+flods_ck(u_char force)
+{
+	static int map_delayed;
+	IFLOD_INFO *ifp;
+	OFLOD_INFO *ofp;
+	struct stat flod_sb;
+	struct timeval;
+	DCC_TS past, present;
+	int rcd_len;
+	int work;
+	u_char loaded;			/* mapped flod.map file just for this */
+
+	if (force)			/* force hostname resolution */
+		got_hosts = 0;
+
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+		if (ifp->soc < 0)
+			continue;
+
+		/* end incoming connections that are not completed in time */
+		ofp = ifp->ofp;
+		if (!ofp) {
+			iflod_read(ifp);
+			if (ifp->soc < 0)
+				continue;
+			ofp = ifp->ofp;
+			if (!ofp) {
+				if (IFP_DEAD(ifp, KEEPALIVE_IN_STOP))
+					iflod_close(ifp, 1, 1, 0,
+						    "no authentication from %s",
+						    ifp_rem_str(ifp));
+				continue;
+			}
+		}
+
+		/* allow more complaints */
+		if (DB_IS_TIME(ofp->limit_reset, FLOD_LIM_CLEAR_SECS)
+		    || force) {
+			FLOD_LIMCNT *lc;
+
+			complained_many_iflods = 0;
+			for (lc = (FLOD_LIMCNT *)&ofp->lc;
+			     lc < (FLOD_LIMCNT *)(sizeof(ofp->lc)
+						  +(char *)&ofp->lc);
+			     ++lc) {
+				lc->lim = lc->cur;
+			}
+			ofp->limit_reset = db_time.tv_sec+FLOD_LIM_CLEAR_SECS;
+		}
+
+		if (!(ifp->flags & IFLOD_FG_VERS_CK))
+			continue;	/* done if peer not really known */
+
+		save_flod_cnts(ofp);
+
+		if (!IFP_DEAD(ifp, KEEPALIVE_IN)) {
+			/* The link is warm.
+			 * Send a delayed position update if needed. */
+			iflod_send_pos(ifp, 0);
+
+		} else if (ifp->flags & IFLOD_FG_END_REQ) {
+			/* The link is cold. If we have asked the peer to
+			 * stop but it has not, then break the link. */
+			iflod_close(ifp, 1, 0, 0, "%s ignored close request",
+				    ifp_rem_str(ifp));
+
+		} else {
+			/* The link is cold., so repeat our position or
+			 * send a note as a keepalive.  The will be closed if
+			 * that fails. */
+			iflod_send_pos(ifp, 1);
+		}
+	}
+
+	if (FLODS_OK()) {
+		/* stop and restart the pumps if the list of peers has
+		 * changed or if our map has disappeared
+		 * and if dbclean is not running */
+		if (0 > stat(flod_path, &flod_sb)) {
+			if (errno != ENOENT
+			    && flod_mtime != 0)
+				dcc_error_msg("stat(%s): %s",
+					      flod_path, ERROR_STR());
+			flod_sb.st_mtime = 0;
+		}
+		if (flod_mtime != 0
+		    && 0 > access(flod_mmap_path, W_OK | R_OK)) {
+			if (errno != ENOENT)
+				dcc_error_msg("access(%s): %s",
+					      flod_mmap_path, ERROR_STR());
+			flod_sb.st_mtime = 0;
+		}
+		if (flods_st != FLODS_ST_RESTART
+		    && flod_sb.st_mtime != flod_mtime) {
+			if (flod_mtime > 1) {
+				dcc_trace_msg("%s has changed", flod_path);
+				flod_mtime = 0;
+			}
+			flods_st = FLODS_ST_RESTART;
+		}
+	}
+
+	if (flods_st != FLODS_ST_ON) {
+		flods_stop("", 0);
+
+		/* wait until the previous floods have stopped and dbclean
+		 * is not running to restart flooding */
+		if (FLODS_OK()) {
+			if (oflods.open != 0 || iflods.open != 0
+			    || !flod_names_resolve_ck()) {
+				flods_st = FLODS_ST_RESTART;
+				/* check again soon but not immediately */
+				RUSH_NEXT_FLODS_CK();
+			} else {
+				if (load_flod(1))
+					flods_st = FLODS_ST_ON;
+			}
+		}
+	}
+
+	/* try to reap the hostname resolving child */
+	flod_names_resolve_ck();
+
+	/* that is all we can do if flooding is off or dbclean is running */
+	if (!FLODS_OK_ON()) {
+		oflods_ck();
+		return;
+	}
+
+	iflods_listen();
+
+	/* generate summaries of some of our delayed reports */
+	dcc_timeval2ts(&past, &db_time, -summarize_delay_secs);
+	dcc_timeval2ts(&present, &db_time, 0);
+	if (flod_mmaps) {
+		if (flod_mmaps->delay_pos > db_csize
+		    || flod_mmaps->delay_pos < DB_PTR_BASE)
+			flod_mmaps->delay_pos = DB_PTR_BASE;
+		work = 0;
+		while (flod_mmaps->delay_pos < db_csize) {
+			if (!db_map_rcd(0, &db_sts.sumrcd,
+					flod_mmaps->delay_pos,
+					&rcd_len)) {
+				flod_mmaps->delay_pos = db_csize;
+				break;
+			}
+			/* only our own reports are delayed */
+			if (DB_RCD_DELAY(db_sts.sumrcd.d.r)) {
+				/* wait until it is time */
+				if (dcc_ts_newer_ts(&db_sts.sumrcd.d.r->ts,
+						    &past)
+				    && !dcc_ts_newer_ts(&db_sts.sumrcd.d.r->ts,
+							&present))
+					break;
+				if (!summarize_dly()) {
+					flod_mmaps->delay_pos = db_csize;
+					break;
+				}
+			}
+			flod_mmaps->delay_pos += rcd_len;
+
+			if (++work >= 1000) {
+				/* spend at most 0.5 second at this
+				 * and then let other processes run*/
+				gettimeofday(&db_time, 0);
+				if (tv_diff2us(&db_time, &wake_time)>DCC_US/2) {
+					next_flods_ck = 0;
+					break;
+				}
+				work = 0;
+			}
+		}
+
+		/* prime the outgoing pumps */
+		for (ofp = oflods.infos;
+		     ofp <= LAST(oflods.infos);
+		     ++ofp) {
+			if (ofp->rem_hostname[0] == '\0')
+				break;
+
+			if (ofp->soc >= 0) {
+				/* The connection is no longer new if it has
+				 * been a while since it was completed */
+				if ((ofp->flags & OFLOD_FG_NEW)
+				    && DB_IS_TIME(ofp->mp->cnts.out_conn_changed
+						  + FLODS_CK_SECS,
+						  FLODS_CK_SECS))
+					ofp->flags &= ~OFLOD_FG_NEW;
+				oflod_fill(ofp);
+			} else {
+				oflod_open(ofp);
+			}
+
+			iflod_socks_start(ofp);
+		}
+	}
+
+	/* complain once per day about incoming links that are not working
+	 * even if dbclean is continually running. */
+	loaded = 0;
+	if (flod_mmaps) {
+		map_delayed = 0;
+	} else if ((force || ++map_delayed > 10) && load_flod(0)) {
+		loaded = 1;
+		map_delayed = 0;
+	}
+	for (ofp = oflods.infos;
+	     ofp->rem_hostname[0] != '\0' && ofp <= LAST(oflods.infos)
+	     && flod_mmaps;
+	     ++ofp) {
+		FLOD_MMAP *mp;
+		LAST_ERROR *ep;
+		const char *msg;
+
+		if (force) {
+			/* Force new outgoing connection attempts.  Also force
+			 * incoming error messages soon but not now to give new
+			 * connetions a chance to be triggered by outgoing
+			 * connections.  */
+			flod_try_again(ofp);
+		}
+
+		mp = ofp->mp;
+		if (ofp->soc < 0
+		    && (ofp->mp->flags & FLODMAP_FG_PASSIVE)
+		    && !OFLOD_OPT_OFF_ROGUE(ofp)
+		    && DB_IS_TIME(mp->otimers.msg, mp->otimers.msg_secs)) {
+			ep = &mp->oflod_err;
+			msg = (ep->complained
+			       ? ""
+			       : ep->msg[0] != '\0'
+			       ? ep->msg
+			       : ep->trace_msg);
+			dcc_error_msg("no passive connection to %s%s%s%s",
+				      ofp->rem_hostname,
+				      msg[0] ? ": \"" : "",
+				      msg,
+				      msg[0] ? "\"" : "");
+			ep->complained = 1;
+			mp->otimers.msg_secs = FLOD_IN_COMPLAIN;
+			mp->otimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN;
+		}
+
+		if ((ofp->mp->flags & (FLODMAP_FG_SOCKS | FLODMAP_FG_NAT)) == 0
+		    && !ofp->ifp
+		    && !IFLOD_OPT_OFF_ROGUE(ofp)
+		    && DB_IS_TIME(mp->itimers.msg, mp->itimers.msg_secs)) {
+			ep = &mp->iflod_err;
+			msg = (ep->complained
+			       ? ""
+			       : ep->msg[0] != '\0'
+			       ? ep->msg
+			       : ep->trace_msg);
+			dcc_error_msg("no incoming connection from %s%s%s%s",
+				      ofp->rem_hostname,
+				      msg[0] ? ": \"" : "",
+				      msg,
+				      msg[0] ? "\"" : "");
+			ep->complained = 1;
+			mp->itimers.msg_secs = FLOD_IN_COMPLAIN;
+			mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN;
+		}
+	}
+	if (loaded)
+		oflods_clear();
+
+	oflods_ck();
+}
+
+
+
+void
+flods_init(void)
+{
+	IFLOD_INFO *ifp;
+
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp)
+		ifp->soc = -1;
+	oflods_clear();
+
+	flods_restart("", 1);
+}