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

#include "dccd_defs.h"
#include "dcc_ck.h"


int flods_off;				/* # of reasons flooding is off */
int flod_db_sick;			/* # of flooding DB sicknesses */


u_char too_busy;			/* too busy to flood */
time_t next_flods_ck;
enum FLODS_ST flods_st = FLODS_ST_OFF;
OFLODS oflods;

/* records after this have not been flooded
 *	0 if invalid */
DB_PTR oflods_max_pos;

static time_t flod_mtime;

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->s = -1;
	ofp->out_try_secs = FLOD_RETRY_SECS/2;
}



void
oflods_unmap(void)
{
	OFLOD_INFO *ofp;

	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp)
		ofp->mp = 0;
	flod_unmap(0, &dccd_stats);
}



static void
oflods_clear(void)
{
	OFLOD_INFO *ofp;

	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp)
		oflod_clear(ofp);
	oflods.total = 0;
	oflods.active = 0;
	complained_many_iflods = 0;
	oflods_max_pos = 0;
}



/* parse ID1->tgt in a flood file entry */
static char
oflod_parse_map(OFLOD_OPTS *opts, const char *str0, int lineno)
{
	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_lineno(FLOD_NM(grey_on), lineno));
		return 0;
	}
	imp = &opts->srvr_map[opts->num_maps];

	if (!CSTRCMP(str0, "self->")) {
		str = str0+STRZ("self->");
		imp->from_lo = imp->from_hi = my_srvr_id;
	} else if (!CSTRCMP(str0, "all->")) {
		str = str0+STRZ("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_NM(grey_on), lineno);
		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_NM(grey_on),
					      lineno);
			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_lineno(FLOD_NM(grey_on),
							lineno));
				return 0;
			}
		}
		if (*str++ != '-' || *str++ != '>') {
			dcc_error_msg("invalid server-ID mapping \"%s\"%s",
				      str0, fnm_lineno(FLOD_NM(grey_on),
						       lineno));
			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_lineno(FLOD_NM(grey_on), lineno));
		return 0;
	}

	++opts->num_maps;
	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 */
oflod_parse_opts(OFLOD_INFO *ofp,
		 OFLOD_OPTS *opts,
		 const char *buf, int lineno)
{
	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_NM(grey_on), lineno);
	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);

	/* parse the options */
	buf_ptr = opts_buf;
	while (*buf_ptr != '\0') {
		while (*buf_ptr == ',')
			++buf_ptr;
		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;
				continue;
			}
			if (!strcasecmp(opt, "del")) {
				opts->flags |= FLOD_OPT_DEL_OK;
				continue;
			}
		}

		/* put some options in one or or the other no matter
		 * for which they are specified */
		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 (ofp->o_opts.flags & FLOD_OPT_SOCKS) {
				dcc_error_msg("\"passive\" and \"SOCKS\""
					      " cannot both be%s",
					      fnm_lineno(FLOD_NM(grey_on),
							lineno));
				return 0;
			}
			ofp->o_opts.flags |= FLOD_OPT_PASSIVE;
			continue;
		}
		if (!strcasecmp(opt, "socks")) {
			if (ofp->o_opts.flags & FLOD_OPT_PASSIVE) {
				dcc_error_msg("\"passive\" and \"SOCKS\""
					      " cannot both be%s",
					      fnm_lineno(FLOD_NM(grey_on),
							lineno));
				return 0;
			}
			ofp->o_opts.flags |= FLOD_OPT_SOCKS;
			continue;
		}
		if (!strcasecmp(opt, "IPv4")) {
			if (ofp->o_opts.flags & FLOD_OPT_IPv6) {
				dcc_error_msg("\"IPv4\" and \"IPv6\""
					      " cannot both be%s",
					      fnm_lineno(FLOD_NM(grey_on),
							lineno));
				return 0;
			}
			ofp->o_opts.flags |= FLOD_OPT_IPv4;
			continue;
		}
		if (!strcasecmp(opt, "IPv6")) {
			if (ofp->o_opts.flags & FLOD_OPT_IPv6) {
				dcc_error_msg("\"IPv4\" and \"IPv6\""
					      " cannot both be%s",
					      fnm_lineno(FLOD_NM(grey_on),
							lineno));
				return 0;
			}
			ofp->o_opts.flags |= FLOD_OPT_IPv6;
			continue;
		}
		if (!CSTRCMP(opt, "leaf=")
		    && (l = strtoul(opt+STRZ("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
#ifdef DCC_FLOD_VERSION6
		if (!strcasecmp(opt, "version6")) {
			ofp->oversion = DCC_FLOD_VERSION6;
			continue;
		}
#endif

		/* parse an ID->map */
		p = strchr(opt, '>');
		if (p && p > opt && *(p-1) == '-') {
			if (!oflod_parse_map(opts, opt, lineno))
				return 0;
			continue;
		}

		dcc_error_msg("unknown option \"%s\"%s",
			      opt, fnm_lineno(FLOD_NM(grey_on), lineno));
		return 0;
	}

	return buf;
}



static const char *			/* rest of a flod file line */
oflod_parse_id(DCC_EMSG emsg, DCC_SRVR_ID *id,
	       const char *buf, const char *type, int lineno)
{
	char id_buf[20];

	buf = dcc_parse_word(emsg, id_buf, sizeof(id_buf),
			     buf, type, FLOD_NM(grey_on), lineno);
	if (!buf)
		return 0;

	if (!strcmp(id_buf, "-")
	    || id_buf[0] == '\0') {
		*id = DCC_ID_INVALID;
		return buf;
	}

	if (!dcc_get_srvr_id(emsg, id, id_buf, 0, FLOD_NM(grey_on), lineno))
		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 */
static void
get_oflods_max_pos(void)
{
	OFLOD_INFO *ofp;

	oflods_max_pos = DB_PTR_BASE;
	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
		if (ofp->hostname[0] != '\0'
		    && oflods_max_pos < ofp->cur_pos)
			oflods_max_pos = ofp->cur_pos;
	}
}



/* 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.
 *
 *	oflods.active and iflods.active must both be 0 to ensure that
 *	there are no old pointers to the ofp structures.
 *
 *	Parse lines of the form
 *	  hostname port rem-ID [passwd-id [out-opts [in-opts [versionX]]]]
 */
u_char					/* 1=ok to start flooding */
oflods_load(void)
{
	OFLOD_INFO *ofp, *ofp1;
	FILE *f;
	struct stat sb;
	int lineno;
	char buf[200];
	const char *bufp, *bufp1;
	char loc_hostname[MAXHOSTNAMELEN];
	u_int16_t loc_port;
	struct timeval have_data;
	int error;
	FLOD_MMAP *mp, *mp1;
	union {
	    OFLOD_INFO	info;
	    FLOD_MMAP	map;
	} swap;
	char *p;
	int i;

	/* forget everything about output flooding */
	oflods_clear();

	/* keep the map open and locked most of the time */
	if (!flod_mmap(dcc_emsg, db_sn, &dccd_stats, 1,
		       (DCC_TRACE_FLOD_BIT & dccd_tracemask) != 0)) {
		dcc_error_msg("%s", dcc_emsg);
		flod_mtime = 0;
		return 0;
	}

	f = fopen(FLOD_NM(grey_on), "r");
	if (!f) {
		/* do not clear the flod map file if the flod list file
		 * is missing in case it has been temporarily deleted */
		if (errno != ENOENT) {
			if (flod_mtime != 0)
				dcc_error_msg("fopen(%s): %s",
					      DCC_NM2PATH(FLOD_NM(grey_on)),
					      ERROR_STR());
			flod_unmap(0, &dccd_stats);
		}
		flod_mtime = 0;
		return 0;
	}
	if (0 > fstat(fileno(f), &sb)) {
		if (flod_mtime!= 0)
			dcc_error_msg("stat(%s): %s",
				      DCC_NM2PATH(FLOD_NM(grey_on)),
				      ERROR_STR());
		fclose(f);
		flod_mtime = 0;
		return 0;
	}
	flod_mtime = sb.st_mtime;

	/* Parse the file of names and numbers first so that we do not
	 * destroy the position information if there is a problem with names */
	ofp = oflods.infos;
	lineno = 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);

		++lineno;
		bufp = fgets(buf, sizeof(buf), f);
		if (!bufp) {
			if (ferror(f)) {
				dcc_error_msg("fgets(%s): %s",
					      DCC_NM2PATH(FLOD_NM(grey_on)),
					      ERROR_STR());
				break;
			}
			if (fclose(f) == EOF) {
				dcc_error_msg("fclose(%s): %s",
					      DCC_NM2PATH(FLOD_NM(grey_on)),
					      ERROR_STR());
			}
			f = 0;
			break;
		}
		i = strlen(bufp);
		if (i >= ISZ(buf)-1) {
			dcc_error_msg("too many characters%s",
				      fnm_lineno(FLOD_NM(grey_on),lineno));
			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",
				      DCC_NM2PATH(FLOD_NM(grey_on)),
				      DIM(oflods.infos));
			continue;
		}

		ofp->lineno = lineno;

		/* 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->hostname, sizeof(ofp->hostname),
					 &ofp->port,
					 ofp->portname, sizeof(ofp->portname),
					 FLOD_NM(grey_on), lineno);
		if (!bufp)
			continue;
		if (bufp1) {
			/* parse the local IP address first */
			ofp->o_opts.flags |= FLOD_OPT_LOC_SU;
			bufp = dcc_parse_nm_port(0, bufp1, 0,
						 loc_hostname,
						 sizeof(loc_hostname),
						 &loc_port, 0, 0,
						 FLOD_NM(grey_on), lineno);
			if (!bufp)
				continue;
		}

		bufp = oflod_parse_id(0, &ofp->rem_id, bufp,
				      "rem-id", lineno);
		if (!bufp)
			continue;
		if (ofp->rem_id == DCC_ID_INVALID) {
			dcc_error_msg("missing rem-id%s",
				      fnm_lineno(FLOD_NM(grey_on), lineno));
			continue;
		}

		bufp = oflod_parse_id(0, &ofp->out_passwd_id, bufp,
				      "passwd-id", lineno);
		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 = oflod_parse_opts(ofp, &ofp->o_opts, bufp, lineno);
		if (!bufp)
			continue;
		bufp = oflod_parse_opts(ofp, &ofp->i_opts, bufp, lineno);
		if (!bufp)
			continue;
		if (*bufp != '\0')
			dcc_error_msg("trailing garbage \"%s\" ignored%s",
				      bufp, fnm_lineno(FLOD_NM(grey_on),
						       lineno));

		/* 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_lineno(FLOD_NM(grey_on), lineno));
			continue;
		}

		/* Resolve the local host name.
		 * This should not take significant time because
		 * the hostnames should be locally known.  That
		 * means we don't need to use a separate thread. */
		if (ofp->o_opts.flags & FLOD_OPT_LOC_SU) {
			dcc_host_lock();
			if (!dcc_get_host(loc_hostname,
					  ((ofp->o_opts.flags & FLOD_OPT_IPv4)
					   ? 0
					   : (ofp->o_opts.flags & FLOD_OPT_IPv6)
					   ? 1
					   : use_ipv6),
					  &error)) {
				dcc_error_msg("flood local name %s: %s%s",
					      loc_hostname,
					      DCC_HSTRERROR(error),
					      fnm_lineno(FLOD_NM(grey_on),
							lineno));
				ofp->o_opts.flags &= ~FLOD_OPT_LOC_SU;
			} else {
				ofp->loc_su = dcc_hostaddrs[0];
				*DCC_SU_PORT(&ofp->loc_su) = loc_port;
			}
		}
		dcc_host_unlock();
		/* if there is a single "-a address", default to it */
		/* A trouble with this is that it breaks flooding
		 * with "-a localhost" */
		if (!(ofp->o_opts.flags & FLOD_OPT_LOC_SU)
		    && !srvr_socs->fwd) {
			ofp->loc_su = srvr_socs->su;
			ofp->loc_su.ipv6.sin6_port = 0;
			ofp->o_opts.flags |= FLOD_OPT_LOC_SU;
		}

		for (ofp1 = oflods.infos; ofp1 < ofp; ++ofp1) {
			if ((!strcmp(ofp1->hostname, ofp->hostname)
			     && ofp1->port == ofp->port)
			    || ofp1->rem_id == ofp->rem_id)
				break;
		}
		if (ofp1 != ofp) {
			dcc_error_msg("duplicate DCC peer%s",
				      fnm_lineno(FLOD_NM(grey_on), lineno));
			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 the list by server-ID so that `cdcc "flood list"` is sorted */
	ofp = oflods.infos;
	while (ofp < LAST(oflods.infos)) {
		ofp1 = ofp+1;
		if (ofp1->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;
	}

	/* ensure that the mtime changes at least occassionally */
	dcc_mmap_utime(FLOD_MMAP_PATH(grey_on), 1);

	dcc_ts2timeval(&have_data, db_sn);

	/* 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 almost always very short. */
	mp = flod_mmaps->mmaps;
	mp1 = mp+1;
	while (mp1 <= LAST(flod_mmaps->mmaps)) {
		if (mp1->hostname[0] == '\0') {
			++mp1;
			continue;
		}
		if (mp->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 /var/dcc/flod file
	 * with the memory mapped /var/dcc/flod/map file list of what has
	 * been sent to each peer */
	for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp)
		mp->oflod_index = -1;

	/* make one pass matching old names with their slots in the
	 * mapped file */
	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
		if (ofp->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->hostname[0] == '\0')
				continue;
			if (mp->oflod_index < 0
			    && ofp->rem_id == mp->rem_id) {
				/* found the old slot */
				if (DB_PTR_IS_BAD_FULL(mp->confirm_pos)
				    || mp->confirm_pos > db_csize) {
					dcc_error_msg("bogus position "L_HPAT
						    " for %s in %s",
						    mp->confirm_pos,
						    ofp->hostname,
						    FLOD_MMAP_PATH(grey_on));
					mp->hostname[0] = '\0';
					continue;
				}
				ofp->cur_pos = mp->confirm_pos;
				ofp->rewind_pos = db_csize;
				ofp->mp = mp;
				mp->oflod_index = ofp - oflods.infos;
				mp->flags &= (OFLOD_MMAP_FG_REWINDING
					      | OFLOD_MMAP_FG_NEED_REWIND
					      | OFLOD_MMAP_FG_FFWD_IN);
				if (have_data.tv_sec == 0) {
					mp->flags |= OFLOD_MMAP_FG_NEED_REWIND;
					mp->flags &= ~OFLOD_MMAP_FG_FFWD_IN;
				}
				if (ofp->o_opts.flags & FLOD_OPT_PASSIVE)
					mp->flags |= OFLOD_MMAP_FG_PASSIVE;
				else if (ofp->o_opts.flags & FLOD_OPT_SOCKS)
					mp->flags |= OFLOD_MMAP_FG_SOCKS;
				if (ofp->o_opts.flags & FLOD_OPT_IPv4)
					mp->flags |= OFLOD_MMAP_FG_IPv4;
				else if (ofp->o_opts.flags & FLOD_OPT_IPv6)
					mp->flags |= OFLOD_MMAP_FG_IPv6;
				/* get new hostname if it changes */
				if (strcasecmp(mp->hostname,ofp->hostname)) {
					BUFCPY(mp->hostname, ofp->hostname);
					/* force name resolution for new name */
					got_hosts = 0;
					new_peer(ofp);
				}
				/* always get new port name in case name
				 * but not the number changes */
				BUFCPY(mp->portname, ofp->portname);
				if (mp->port != ofp->port) {
					mp->port = ofp->port;
					new_peer(ofp);
				}
				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) {
		if (ofp->hostname[0] == '\0')
			break;
		if (ofp->mp != 0)
			continue;

		/* find a free or no longer used entry */
		while (mp->oflod_index >= 0) {
			if (++mp > LAST(flod_mmaps->mmaps))
				dcc_logbad(EX_SOFTWARE,
					   "too few oflod mmap slots");
		}
		if (mp->hostname[0] != '\0') {
			if (strcmp(mp->portname,"-")) {
				dcc_error_msg("forget flood stream to %s,%s",
					      mp->hostname, mp->portname);
			} else {
				dcc_error_msg("forget flood stream to %s",
					      mp->hostname);
			}
		}
		memset(mp, 0, sizeof(*mp));

		ofp->mp = mp;
		if (strcmp(ofp->portname,"-")) {
			dcc_error_msg("initialize flood stream to %s,%s%s",
				      ofp->hostname, ofp->portname,
				      fnm_lineno(FLOD_NM(grey_on),
						 ofp->lineno));
		} else {
			dcc_error_msg("initialize flood stream to %s%s",
				      ofp->hostname,
				      fnm_lineno(FLOD_NM(grey_on),
						 ofp->lineno));
		}
		mp->su.sa.sa_family = AF_UNSPEC;
		mp->rem_id = ofp->rem_id;
		BUFCPY(mp->hostname, ofp->hostname);
		BUFCPY(mp->portname, ofp->portname);
		if (ofp->o_opts.flags & FLOD_OPT_PASSIVE)
			mp->flags |= OFLOD_MMAP_FG_PASSIVE;
		else if (ofp->o_opts.flags & FLOD_OPT_SOCKS)
			mp->flags |= OFLOD_MMAP_FG_SOCKS;
		if (have_data.tv_sec == 0)
			mp->flags |= OFLOD_MMAP_FG_NEED_REWIND;
		mp->port = ofp->port;
		mp->cnts.counts_cleared = db_time.tv_sec;
		mp->cnts.out_last_connected = db_time.tv_sec;
		mp->cnts.in_last_connected = db_time.tv_sec;
		/* force host name resolution for a new name */
		got_hosts = 0;
		mp->confirm_pos = DB_PTR_BASE;
		new_peer(ofp);
		mp->oflod_index = ofp - oflods.infos;
	}

	/* clear the slots that contain forgotten hosts */
	for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp) {
		if (mp->oflod_index == -1) {
			if (mp->hostname[0] != '\0') {
				if (strcmp(mp->portname,"-")) {
					dcc_error_msg("forget flood stream to"
						      " %s,%s",
						      mp->hostname,
						      mp->portname);
				} else {
					dcc_error_msg("forget flood stream to"
						      " %s",
						      mp->hostname);
				}
			}
			memset(mp, 0, sizeof(*mp));
		}
	}

	/* finish copying OFLOD_MMAP_FG_NEED_REWIND from database */
	if (have_data.tv_sec == 0) {
		dcc_timeval2ts(flod_mmaps->sn, &have_data, 1);
		dcc_timeval2ts(db_sn, &have_data, 1);
		db_flush_magic(0);
	}

	get_oflods_max_pos();
	return 1;
}



void
oflod_close(OFLOD_INFO *ofp, u_char fail,
	    enum FLOD_ERR_OP err_op, int new_errno)
{
	const char *opstr;
	int perrno;

	if (ofp->hostname[0] == '\0')
		return;

	if (ofp->s >= 0) {
		if (0 > close(ofp->s)
		    && !fail)
			dcc_error_msg("close(oflod %s %s): %s",
				      ofp->hostname,
				      dcc_su2str(&ofp->su),
				      ERROR_STR());
		if (!ofp->mp) {
			TMSG2(FLOD, "close(oflod %s %s) (stranger)",
			      ofp->hostname, dcc_su2str(&ofp->su));
		} else if (err_op == FLOD_ERR_SAME) {
			opstr = flod_stats_str(&perrno,
					       ofp->mp->o_err.old_errno,
					       ofp->mp->o_err.op);
			if (perrno)
				TMSG4(FLOD, "close(oflod %s %s) same %s: %s",
				      ofp->hostname, dcc_su2str(&ofp->su),
				      opstr, ERROR_STR1(perrno));
			else
				TMSG3(FLOD, "close(oflod %s %s) same %s",
				      ofp->hostname, dcc_su2str(&ofp->su),
				      opstr);
		} else {
			opstr = flod_stats_str(&perrno, new_errno, err_op);
			if (perrno == 0) {
				rpt_err(&ofp->mp->o_err, 0, err_op, new_errno,
					"close(oflod %s %s): %s",
					ofp->hostname, dcc_su2str(&ofp->su),
					opstr);
			} else {
				rpt_err(&ofp->mp->o_err, 0, err_op, new_errno,
					"close(oflod %s %s): %s %s",
					ofp->hostname, dcc_su2str(&ofp->su),
					opstr, ERROR_STR1(perrno));
			}
		}
		ofp->s = -1;
		ofp->flags &= ~OFLOD_FG_CONNECTED;
		ofp->obuf_len = 0;

		save_flod_cnt(ofp, ofp->cnts.in_start != 0);

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

	if (fail) {
		ofp->out_try_again = db_time.tv_sec + ofp->out_try_secs;
		TMSG2(FLOD, "postpone restarting flood to %s for %d seconds",
		      ofp->hostname, ofp->out_try_secs);
	}
}



/* get ready to shut down */
static void
start_shutdown(OFLOD_INFO *ofp)
{
	if (ofp->flags & OFLOD_FG_SHUTDOWN_REQ)
		return;

	/* arrange to ask the peer to ask us to stop */
	ofp->flags  |= OFLOD_FG_SHUTDOWN_REQ;
	oflod_fill(ofp);

	if (stopint)
		ofp->keep_out_time = db_time.tv_sec +	SHUTDOWN_DELAY;
	else
		ofp->keep_out_time = db_time.tv_sec + KEEPALIVE_OUT_STOP;
}



/* 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->s, SOL_SOCKET, SO_LINGER,
			   &nowait, sizeof(nowait)))
		rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_IO, 0,
			"setsockopt(SO_LINGER flood to %s %s): %s",
			ofp->hostname, dcc_su2str(&ofp->su), ERROR_STR());

	if (0 > shutdown(ofp->s, 1)) {
		rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_IO, 0,
			"shutdown(flood to %s %s): %s",
			ofp->hostname, dcc_su2str(&ofp->su), ERROR_STR());
		oflod_close(ofp, 0, FLOD_ERR_SAME, 0);
	}
}



/* 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(const OFLOD_INFO *ofp)
{
	const DB_RCD_CK *cur_rcd_ck;
	DCC_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
	    || 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. */
	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_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 */
		if (type == DCC_CK_SRVR_ID) {
#ifdef DCC_FLOD_VERSION6
			/* unless talking to an old peer that does not
			 * know the new value */
			if (ofp->oversion <= DCC_FLOD_VERSION6
			    && ofp->oversion != DCC_FLOD_VERSION_DEF)
				return 0;
#endif
			return 1;
		}

		/* do not send what we think are stale reports */
		if (DCC_TS_OLDER_TS(db_sts.rcd.d.r->ts, db_ex_ts[type].all))
			continue;

		/* 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;
}



static void
put_rcd_obuf(OFLOD_INFO *ofp, const DB_RCD *cur_rcd)
{
	DCC_FLOD *bp;
	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_TYPE 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, i, j;

	if (!oflod_ck_put(ofp))
		return;			/* skip this report */

	bp = (DCC_FLOD *)&ofp->obuf.c[ofp->obuf_len];
	db_ptr2flod_pos(bp->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;
	}
	bp->srvr_id_auth[0] = srvr>>8;
	bp->srvr_id_auth[1] = srvr;

	reflecting = (srvr == ofp->rem_id);
	non_path = 0;

	memcpy(bp->ts, cur_rcd->ts, sizeof(bp->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 = bp->cks;
	if ((srvr != my_srvr_id
	     || DB_CK_TYPE(cur_rcd_ck) == DCC_CK_FLOD_PATH)
	    && (ofp->oversion > DCC_FLOD_VERSION6
		|| ofp->oversion == DCC_FLOD_VERSION_DEF)) {
		/* 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;
		bp->num_cks = 1;
	} else {
		/* do not add a path */
		new_path_idp = new_path_id_limp = 0;
		path_id_max = 0;
		bp->num_cks = 0;
	}

	all_spam = 1;
	num_cks = DB_NUM_CKS(cur_rcd);
	while (num_cks != 0) {
		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;
					++bp->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 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_nokeep_cks, type))
					continue;
			}
			/* Do not send reports from the target back
			 * to the target unless the report is a
			 * Server-ID declaration */
			if (reflecting && type != DCC_CK_SRVR_ID)
				return;

			if (all_spam
			    && DB_TGTS_CK(cur_rcd_ck) != DCC_TGTS_TOO_MANY)
				all_spam = 0;
			/* 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;
			++bp->num_cks;
			non_path = 1;
		}
		++cur_rcd_ck;
		--num_cks;
	}

	/* 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(&bp->tgts, &tgts, sizeof(bp->tgts));

	i = (char *)buf_ck - (char *)bp;
	ofp->obuf_len += i;
	++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)
{
	u_int cur_rcd_len;
	int work;

	ofp->flags &= ~OFLOD_FG_TOO_BUSY;

	/* stop when things are not ready or shutting down */
	if (!(ofp->flags & OFLOD_FG_CONNECTED)
	    || (ofp->flags & OFLOD_FG_SHUTDOWN))
		return;
	/* after having started sending the buffer,
	 * wait for it to drain before adding more */
	if (ofp->obuf_off != 0)
		return;
	/* don't overflow the buffer */
	if (ofp->obuf_len >= sizeof(ofp->obuf) - sizeof(DCC_FLOD))
		return;

	work = 0;
	do {
		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 &= ~OFLOD_MMAP_FG_REWINDING;
			if (ofp->flags & OFLOD_FG_SHUTDOWN_REQ)
				oflod_shutdown(ofp);
			return;
		}

		/* don't try to look at reports crossing page bounardies */
		if (ofp->cur_pos%db_page_size >= db_page_max) {
			ofp->cur_pos += DB_RCD_PAD;
			if (oflods_max_pos < ofp->cur_pos)
				oflods_max_pos = ofp->cur_pos;
			continue;
		}

		if (!db_map_rcd(dcc_emsg, &db_sts.rcd, ofp->cur_pos,
				&cur_rcd_len)) {
			dcc_error_msg("starting at "L_HPAT" for %s: %s",
				      ofp->cur_pos, ofp->hostname, dcc_emsg);
			ofp->cur_pos = db_csize;
			if (oflods_max_pos < ofp->cur_pos)
				oflods_max_pos = ofp->cur_pos;
			return;
		}

		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;
			if (oflods_max_pos < ofp->cur_pos)
				oflods_max_pos = ofp->cur_pos;
			return;
		}

		/* start a new entry unless we are shutting down */
		if (ofp->flags & OFLOD_FG_SHUTDOWN_REQ) {
			oflod_shutdown(ofp);
			return;
		}

		/* stop if we've spent enough time here so that
		 * incoming requests aren't lost */
		if (++work >= 20) {
			gettimeofday(&db_time, 0);
			if (db_time.tv_sec != wake_time.tv_sec) {
				too_busy = 1;
				ofp->flags |= OFLOD_FG_TOO_BUSY;
				return;
			}
			work = 0;
		}

		ofp->cur_pos += cur_rcd_len;
		if (oflods_max_pos < ofp->cur_pos)
			oflods_max_pos = ofp->cur_pos;

		/* send the record */
		put_rcd_obuf(ofp, db_sts.rcd.d.r);
	} while (ofp->obuf_len < sizeof(ofp->obuf)-sizeof(DCC_FLOD));
}



/* finish connecting output flood by sending our version number and signature */
u_char
oflod_connect_fin(OFLOD_INFO *ofp)
{
	ID_TBL *tp;

	tp = find_id_tbl(ofp->out_passwd_id);
	if (!tp || tp->cur_passwd[0] == '\0') {
		if (ofp->mp != 0)
			rpt_err(&ofp->mp->o_err, 0, FLOD_ERR_SIGN, 0,
				"oflod unknown passwd-ID %d%s",
				ofp->out_passwd_id,
				fnm_lineno(FLOD_NM(grey_on), ofp->lineno));
		return 0;
	}

	ofp->keep_out_time = db_time.tv_sec + KEEPALIVE_OUT;
	ofp->mp->o_err.ok = db_time.tv_sec + LAST_ERROR_OK_SECS;
	ofp->flags |= OFLOD_FG_CONNECTED;
	save_flod_cnt(ofp, ofp->cnts.in_start != 0);

	ofp->flags &= ~OFLOD_FG_SHUTDOWN;
	ofp->flags &= ~OFLOD_FG_SHUTDOWN_REQ;
	ofp->cur_pos = ofp->mp->confirm_pos;
	get_oflods_max_pos();
	ofp->recv_pos = DB_PTR_NULL;
	ofp->xmit_pos = DB_PTR_NULL;
	ofp->ibuf_len = 0;
	ofp->obuf_off = 0;

	/* convince the peer we're sane */
	ofp->obuf_len = sizeof(ofp->obuf.vers);
	memset(&ofp->obuf.vers, 0, sizeof(ofp->obuf.vers));
#ifdef DCC_FLOD_VERSION6
	if (ofp->oversion == DCC_FLOD_VERSION6)
		strcpy(ofp->obuf.vers.body.str, DCC_FLOD_VERSION6_STR);
	else
#endif
		strcpy(ofp->obuf.vers.body.str, DCC_FLOD_VERSION_CUR_STR);
	ofp->obuf.vers.body.sender_srvr_id = htons(my_srvr_id);

	if (tp->next_passwd[0] != '\0') {
		ofp->flags |= OFLOD_FG_PASSWD_NEXT;
	} else {
		ofp->flags &= ~OFLOD_FG_PASSWD_NEXT;
		ofp->mp->flags &= ~OFLOD_MMAP_FG_PASSWD_NEXT;
	}
	if (ofp->ids_mtime != ids_mtime) {
		ofp->ids_mtime = ids_mtime;
		ofp->mp->flags &= ~OFLOD_MMAP_FG_PASSWD_NEXT;
	}
	if (ofp->mp->flags & OFLOD_MMAP_FG_PASSWD_NEXT)
		dcc_sign(tp->next_passwd, sizeof(tp->next_passwd),
			 &ofp->obuf.vers, ofp->obuf_len);
	else
		dcc_sign(tp->cur_passwd, sizeof(tp->cur_passwd),
			 &ofp->obuf.vers, ofp->obuf_len);
	TMSG2(FLOD, "start flood to %s %s",
	      ofp->hostname, dcc_su2str(&ofp->su));

	return 1;
}



/* start to connect an out-going flood stream */
static int				/* -1=failure, 0=not yet, 1=done */
oflod_connect_start(OFLOD_INFO *ofp,
		    enum FLOD_ERR_OP op,
		    const char *msg)
{
	int i;

	if (ofp->o_opts.flags & FLOD_OPT_SOCKS)
		i = Rconnect(ofp->s, &ofp->su.sa, DCC_SU_LEN(&ofp->su));
	else
		i = connect(ofp->s, &ofp->su.sa, DCC_SU_LEN(&ofp->su));
	if (0 > i && errno != EISCONN) {
		if (errno == EAGAIN
		    || errno == EINPROGRESS
		    || errno == EALREADY)
			return 0;

		/* several UNIX-like systems return EINVAL for the second
		 * connect() after a Unreachable ICMP message or timeout */
		if (errno == EINVAL) {
			rpt_err(&ofp->mp->o_err, 0, op, errno,
				"%s(oflod %s %s%s): %s",
				msg, ofp->hostname,
				dcc_su2str(&ofp->su),
				fnm_lineno(FLOD_NM(grey_on), ofp->lineno),
				"connection rejected or refused");
		} else {
			rpt_err(&ofp->mp->o_err, 0, op, errno,
				"%s(oflod %s %s%s): %s",
				msg, ofp->hostname,
				dcc_su2str(&ofp->su),
				fnm_lineno(FLOD_NM(grey_on), ofp->lineno),
				ERROR_STR());
		}
		oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
		return -1;
	}

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

	return 1;
}



void
oflod_open(OFLOD_INFO *ofp)
{
	if (ofp->s >= 0
	    || ofp->hostname[0] == '\0'
	    || (ofp->o_opts.flags & FLOD_OPT_OFF)
	    || flods_st != FLODS_ST_ON
	    || (ofp->o_opts.flags & FLOD_OPT_PASSIVE))
		return;

	if (!DB_IS_TIME(ofp->out_try_again, ofp->out_try_secs))
		return;

	if (!flod_names_resolve_start())
		return;			/* wait for name resolution */

	if (ofp->mp->su.sa.sa_family == AF_UNSPEC) {
		if (ofp->mp->host_error != 0)
			rpt_err(&ofp->mp->o_err, 0,
				FLOD_ERR_GET_HOST, ofp->mp->host_error,
				"flood peer name %s: %s%s",
				ofp->hostname,
				DCC_HSTRERROR(ofp->mp->host_error),
				fnm_lineno(FLOD_NM(grey_on), ofp->lineno));
		oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
		return;
	}
	ofp->su = ofp->mp->su;

	ofp->s = socket(ofp->su.sa.sa_family, SOCK_STREAM, 0);
	if (ofp->s < 0) {
		rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_IO, errno,
			"oflod%s socket(): %s",
			fnm_lineno(FLOD_NM(grey_on), ofp->lineno),
			ERROR_STR());
		oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
		return;
	}
	++oflods.active;

	if (!set_flod_socket(ofp->s, ofp->hostname, &ofp->su)) {
		oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
		return;
	}

	if ((ofp->o_opts.flags & FLOD_OPT_LOC_SU)
	    && !(ofp->o_opts.flags & FLOD_OPT_SOCKS)
	    && 0 > bind(ofp->s, &ofp->loc_su.sa, DCC_SU_LEN(&ofp->loc_su)))
		dcc_error_msg("bind(oflod %s%s): %s",
			      dcc_su2str(&ofp->loc_su),
			      fnm_lineno(FLOD_NM(grey_on), ofp->lineno),
			      ERROR_STR());

	oflod_connect_start(ofp, FLOD_ERR_CONNECT, "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, FLOD_ERR_CONNECT2,
						"connect2"))
			return;
		oflod_fill(ofp);
		if (ofp->obuf_len == 0)
			return;
	}

	if (ofp->o_opts.flags & FLOD_OPT_SOCKS)
		i = Rsend(ofp->s, &ofp->obuf.c[ofp->obuf_off],
			  ofp->obuf_len - ofp->obuf_off, 0);
	else
		i = send(ofp->s, &ofp->obuf.c[ofp->obuf_off],
			 ofp->obuf_len - ofp->obuf_off, 0);
	if (i > 0) {
		ofp->obuf_off += i;
		if (ofp->obuf_off >= ofp->obuf_len) {
			ofp->obuf_len = 0;
			ofp->obuf_off = 0;
			oflod_fill(ofp);
		}
		return;
	}

	/* we had an error or EOF */
	if (i < 0) {
		rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_IO, errno,
			"send(oflod to %s %s): %s",
			ofp->hostname, dcc_su2str(&ofp->su), ERROR_STR());
	} else {
		rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_IO, 0,
			"premature end of oflod to %s %s",
			ofp->hostname, dcc_su2str(&ofp->su));
	}
	oflod_read(ofp);		/* get any last error message */
	oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
}



static inline void
oflod_retry_backoff(OFLOD_INFO *ofp)
{
	ofp->out_try_secs *= 2;
	if (ofp->out_try_secs > FLOD_SLOW_RETRY_SECS)
		ofp->out_try_secs = FLOD_SLOW_RETRY_SECS;
}



static void
oflod_read_emsg(OFLOD_INFO *ofp)
{
	enum FLOD_ERR_OP op;

	if (!strncmp(ofp->ibuf.end.msg, BAD_VER_MSG,
		     ISZ(BAD_VER_MSG)-1)) {
		if (ofp->iversion < DCC_FLOD_VERSION_CUR
		    && ofp->oversion == DCC_FLOD_VERSION_DEF) {
			/* notice if this peer demands an old version */
			ofp->oversion = ofp->iversion;
		} else {
			oflod_retry_backoff(ofp);
		}
		op = FLOD_ERR_BAD_VERS;

	} else if (!strncmp(ofp->ibuf.end.msg, BAD_ID_MSG, ISZ(BAD_ID_MSG)-1)) {
		oflod_retry_backoff(ofp);
		op = FLOD_ERR_ID;

	} else if (!strncmp(ofp->ibuf.end.msg, BAD_AUTH_MSG,
			    ISZ(BAD_AUTH_MSG)-1)) {
		/* try the second password if possible
		 * after the peer rejects the first */
		if ((ofp->flags & OFLOD_FG_PASSWD_NEXT)
		    && !(ofp->mp->flags & OFLOD_MMAP_FG_PASSWD_NEXT)) {
			ofp->mp->flags |= OFLOD_MMAP_FG_PASSWD_NEXT;
			ofp->out_try_again = 0;
		} else {
			oflod_retry_backoff(ofp);
		}
		op = FLOD_ERR_SIGN;

	} else {
		ofp->out_try_secs = FLOD_RETRY_SECS/2;
		op = FLOD_ERR_REMOTE_OFF;
	}

	rpt_err(&ofp->mp->o_err, 0, op, 0,
		"oflod end status from %s %s: %s",
		ofp->hostname, dcc_su2str(&ofp->su),
		ofp->ibuf.end.msg);
	oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
}



/* see what the target has to say about the reports we have been sending */
void
oflod_read(OFLOD_INFO *ofp)
{
	int used, i;
	DB_PTR pos;

	if (ofp->o_opts.flags & FLOD_OPT_SOCKS)
		i = Rrecv(ofp->s, &ofp->ibuf.b[ofp->ibuf_len],
			  sizeof(ofp->ibuf) - ofp->ibuf_len, 0);
	else
		i = recv(ofp->s, &ofp->ibuf.b[ofp->ibuf_len],
			 sizeof(ofp->ibuf) - ofp->ibuf_len, 0);
	if (i <= 0) {
		if (i < 0 && errno != ECONNRESET) {
			if (!DCC_BLOCK_ERROR()) {
				rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_IO, errno,
					"recv(oflod %s %s): %s",
					ofp->hostname, dcc_su2str(&ofp->su),
					ERROR_STR());
				oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
			}
			return;
		}

		/* before closing, the target is supposed to send a
		 * "position" of DCC_FLOD_POS_END followed by
		 * an ASCII message */
		if (ofp->ibuf_len <= sizeof(DCC_FLOD_POS)) {
			if (ofp->obuf_len != 0)
				rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_BAD_DATA,0,
					"truncated oflod end status from %s %s",
					ofp->hostname, dcc_su2str(&ofp->su));
			oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
			return;
		}

		pos = flod_pos2db_ptr(ofp->ibuf.end.z);
		if (pos != DCC_FLOD_POS_END) {
			dcc_error_msg("oflod end status from %s %s flag="L_HPAT,
				      ofp->hostname, dcc_su2str(&ofp->su),
				      pos);
		}
		ofp->ibuf.b[ofp->ibuf_len] = '\0';
		if (!strncmp(ofp->ibuf.end.msg, DCC_FLOD_OK_STR,
			     sizeof(DCC_FLOD_OK_STR)-1)) {
			TMSG3(FLOD, "oflod end status from %s %s: %s",
			      ofp->hostname, dcc_su2str(&ofp->su),
			      ofp->ibuf.end.msg+sizeof(DCC_FLOD_OK_STR)-1);
			oflod_close(ofp, 0, FLOD_ERR_REMOTE_OFF, 0);
		} else {
			oflod_read_emsg(ofp);
		}
		return;
	}
	ofp->ibuf_len += i;
	if (ofp->mp != 0)
		ofp->mp->o_err.ok = db_time.tv_sec + LAST_ERROR_OK_SECS;
	if (!(ofp->flags & OFLOD_FG_SHUTDOWN_REQ))
		ofp->keep_out_time = db_time.tv_sec + KEEPALIVE_OUT;

	used = 0;
	while (ofp->ibuf_len >= sizeof(ofp->ibuf.pos)) {
		if (used > 0) {
			ofp->ibuf_len -= used;
			if (ofp->ibuf_len == 0)
				return;

			memmove(ofp->ibuf.b, &ofp->ibuf.b[used],
				ofp->ibuf_len);
			used = 0;
		}

		pos = flod_pos2db_ptr(ofp->ibuf.end.z);
		if (pos < DCC_FLOD_POS_MIN) switch ((DCC_FLOD_POS_OPS)pos) {
		case DCC_FLOD_POS_END:
			/* Wait for all of final status message
			 * until the target closes the TCP connection.
			 * Do not worry if the target stops without
			 * asking nicely, since at worst we will
			 * resend whatever is in the pipe next time. */
			if (ofp->ibuf_len >= sizeof(ofp->ibuf.end)) {
				rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_BAD_DATA,0,
					"babbling flod receiver %s %s",
					ofp->hostname, dcc_su2str(&ofp->su));
				oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
				return;
			}
			return;		/* wait for the status */

		case DCC_FLOD_POS_END_REQ:
			/* since the peer asked us to stop,
			 * do not start again for a while */
			ofp->out_try_secs = FLOD_RETRY_SECS;
			ofp->out_try_again = (db_time.tv_sec
					      + ofp->out_try_secs);
			TMSG2(FLOD, "postpone restarting flood to "
			      "%s for %d seconds",
			      ofp->hostname, ofp->out_try_secs);
			start_shutdown(ofp);
			used = sizeof(ofp->ibuf.pos);
			continue;

		case DCC_FLOD_POS_NOTE:
			/* wait until we get the length of the complaint */
			if (ofp->ibuf_len < FLOD_NOTE_OVHD)
				return;
			used = ofp->ibuf.note.len;
			if (used > ISZ(ofp->ibuf.note)
			    || used <= FLOD_NOTE_OVHD) {
				rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_BAD_DATA,0,
					"bogus oflod note length %d from %s %s",
					used, ofp->hostname,
					dcc_su2str(&ofp->su));
				oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
				return;
			}
			if ((int)ofp->ibuf_len < used)
				return;
			TMSG4(FLOD, "oflod note from %s %s: %.*s",
			      ofp->hostname, dcc_su2str(&ofp->su),
			      used-FLOD_NOTE_OVHD, ofp->ibuf.note.str);
			continue;

		case DCC_FLOD_POS_COMPLAINT:
			/* wait until we get the length of the complaint */
			if (ofp->ibuf_len < FLOD_NOTE_OVHD)
				return;
			used = ofp->ibuf.note.len;
			if (used > ISZ(ofp->ibuf.note)
			    || used <= FLOD_NOTE_OVHD) {
				rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_BAD_DATA,0,
					"bogus oflod complaint length %d"
					" from %s %s",
					used, ofp->hostname,
					dcc_su2str(&ofp->su));
				oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
				return;
			}
			if ((int)ofp->ibuf_len < used)
				return;
			i = FLOD_CNTERR(&ofp->cnts.complaint);
			if (i <= 0)
				dcc_error_msg("oflod complaint from %s %s:"
					      " %.*s%s",
					      ofp->hostname,
					      dcc_su2str(&ofp->su),
					      used-FLOD_NOTE_OVHD,
					      ofp->ibuf.note.str,
					      (i<0 ? "" : "; stop complaints"));
			continue;

		case DCC_FLOD_POS_REWIND:
			used = sizeof(ofp->ibuf.pos);
			dcc_trace_msg("oflod rewind from %s %s",
				      ofp->hostname, dcc_su2str(&ofp->su));
			ofp->mp->flags |= OFLOD_MMAP_FG_REWINDING;
			ofp->cur_pos = ofp->mp->confirm_pos = DB_PTR_BASE;
			ofp->rewind_pos = db_csize;
			get_oflods_max_pos();
			oflod_fill(ofp);
			continue;

		case DCC_FLOD_POS_FFWD_IN:
			used = sizeof(ofp->ibuf.pos);
			dcc_trace_msg("FFWD its input from %s %s",
				      ofp->hostname, dcc_su2str(&ofp->su));
			ofp->cur_pos = db_csize;
			get_oflods_max_pos();
			continue;
		}


		/* 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 & OFLOD_MMAP_FG_REWINDING)
			|| pos < DCC_FLOD_POS_MIN
			|| pos > db_csize)) {
			rpt_err(&ofp->mp->o_err, 1, FLOD_ERR_BAD_DATA,0,
				"bogus confirmed flood position"
				" "L_HPAT" from %s;"
				" recv_pos="L_HPAT"  xmit_pos="L_HPAT,
				pos, ofp->hostname,
				ofp->recv_pos, ofp->xmit_pos);
			oflod_close(ofp, 1, FLOD_ERR_SAME, 0);
			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;
		used = sizeof(ofp->ibuf.pos);

		/* things are going ok, so reset restart backoff */
		ofp->out_try_secs = FLOD_RETRY_SECS/2;
	}
}



void
oflods_stop(u_char force)
{
	OFLOD_INFO *ofp;

	flods_st = FLODS_ST_OFF;

	flod_sync(0);

	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
		if (ofp->hostname[0] == '\0')
			break;
		if (ofp->s < 0 || !(ofp->flags & OFLOD_FG_CONNECTED)) {
			oflod_close(ofp, 0, FLOD_ERR_LOCAL_OFF, 0);
		} else if (force) {
			oflod_close(ofp, 1, FLOD_ERR_LOCAL_OFF, 0);
		} else {
			start_shutdown(ofp);
		}
	}

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



void PATTRIB(1,2)
db_broken(const char *p, ...)
{
	va_list args;

	db_failed = 1;
	va_start(args, p);
	dcc_verror_msg(p, args);
	va_end(args);
}



/* (re)start listening for incoming floods and sending outgoing floods */
void
flods_restart(const char *msg)
{
	/* unlocked database keeps flooding off */
	if (flods_off < 0)
		flods_off  = 0;
	if (flods_off > 0 || !DB_IS_LOCKED())
		return;

	if (msg)
		iflods_stop(msg, 0);

	flods_st = FLODS_ST_RESTART;

	flods_ck(0);
}



/* 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;
	u_int rcd_len;
	int work;
	u_char mapped;
	int i, j;

	if (force)			/* force hostname resolution */
		got_hosts = 0;

	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
		if (ifp->s < 0)
			continue;

		/* end connections that failed to be completed */
		ofp = ifp->ofp;
		if (!ofp) {
			if (DB_IS_TIME(ifp->quit_connect, IFLOD_CONNECT_SECS))
				iflod_close(ifp, 1, FLOD_ERR_SIGN, 0,
					    "%s failed to authenticate itself",
					    ifp->hostname);
			continue;
		}

		/* allow more complaints */
		if (DB_IS_TIME(ofp->limit_reset, FLOD_LIM_CLEAR_SECS)
		    || force) {
			complained_many_iflods = 0;
			ofp->cnts.stale.limit = ofp->cnts.stale.val;
			ofp->cnts.dup.limit = ofp->cnts.dup.val;
			ofp->cnts.ok2.limit = ofp->cnts.ok2.val;
			ofp->cnts.not_deleted.limit = ofp->cnts.not_deleted.val;
			ofp->cnts.bad_id.limit = ofp->cnts.bad_id.val;
			ofp->cnts.complaint.limit = ofp->cnts.complaint.val;
			ofp->limit_reset = db_time.tv_sec+FLOD_LIM_CLEAR_SECS;
		}

		if (!(ifp->flags & IFLOD_FG_VERS_CK))
			continue;
		save_flod_cnt(ofp, ofp->cnts.in_start != 0);
		if (!DB_IS_TIME(ifp->ofp->keep_in_time, KEEPALIVE_IN)) {
			/* Tell the peer how much we've processed. */
			iflod_send_pos(ifp, 0);
		} else {
			/* If we have not heard from the peer for a long time,
			 * then repeat our position or close the connection
			 * as a "keepalive".
			 * If we have asked the peer to stop and it has not
			 * done so, then slam the link shut. */
			if (ifp->flags & IFLOD_FG_END_REQ) {
				iflod_close(ifp, 1, FLOD_ERR_KEEPALIVE, 0,
					    "%s ignored close request",
					    ifp->hostname);
			} else if (!iflod_send_pos(ifp, 1)) {
				iflod_stop(ifp, "keepalive");
			}
		}
	}

	if (!flods_off && DB_IS_LOCKED()) {
		/* stop and restart the pumps if the list of peers has
		 * changed or if our map has disappeared */
		if (0 > stat(FLOD_NM(grey_on), &flod_sb)) {
			if (errno != ENOENT
			    && flod_mtime != 0)
				dcc_error_msg("stat(%s): %s",
					      DCC_NM2PATH(FLOD_NM(grey_on)),
					      ERROR_STR());
			flod_sb.st_mtime = 0;
		}
		if (flod_mtime != 0
		    && 0 > access(FLOD_MMAP_PATH(grey_on), W_OK | R_OK)) {
			dcc_error_msg("access(%s): %s",
				      FLOD_MMAP_PATH(grey_on), ERROR_STR());
			flod_sb.st_mtime = 0;
		}
		if (flods_st != FLODS_ST_RESTART
		    && flod_sb.st_mtime != flod_mtime) {
			if (flod_mtime != 0) {
				dcc_trace_msg("oflod %s has changed",
					      DCC_NM2PATH(FLOD_NM(grey_on)));
				flod_mtime = 0;
			}
			flods_st = FLODS_ST_RESTART;
		}
	}

	if (flods_st != FLODS_ST_ON) {
		oflods_stop(0);
		iflods_stop(0, 0);

		/* wait until the previous floods have stopped to restart */
		if (!flods_off && DB_IS_LOCKED()) {
			if (oflods.active != 0 || iflods.active != 0
			    || !flod_names_resolve_ck()) {
				flods_st = FLODS_ST_RESTART;
				/* check again soon but not immediately */
				if (next_flods_ck > db_time.tv_sec + 1)
					next_flods_ck = db_time.tv_sec + 1;
			} else {
				if (oflods_load())
					flods_st = FLODS_ST_ON;
			}
		}
	}

	/* that is all we can do if flooding is off */
	if (flods_st != FLODS_ST_ON || flods_off || !DB_IS_LOCKED())
		return;

	iflods_start();

	/* generate delayed summaries */
	if (summarize_delay_secs == 0) {
		/* must summarize before reports are compressed */
		summarize_delay_secs = MAX_SUMMARIZE_DELAY_SECS;
		for (i = 0; i < DIM(db_ex_secs); ++i) {
			j = db_ex_secs[i].all;
			if (j != 0
			    && j < summarize_delay_secs)
				j = summarize_delay_secs;
		}
	}
	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;
			}
			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 >= 20) {
				/* spend at most 1 second at this
				 * and then let other processes run*/
				gettimeofday(&db_time, 0);
				if (db_time.tv_sec!=wake_time.tv_sec) {
					too_busy = 1;
					break;
				}
				work = 0;
			}
		}

		/* prime the outgoing pumps */
		for (ofp = oflods.infos;
		     ofp <= LAST(oflods.infos);
		     ++ofp) {
			if (ofp->hostname[0] == '\0')
				break;

			/* force error messages soon but not now to give
			 * new connetions a chance to be made */
			if (force)
				new_peer(ofp);

			if (ofp->s >= 0) {
				oflod_fill(ofp);
			} else {
				oflod_open(ofp);
			}

			if ((ofp->o_opts.flags & FLOD_OPT_SOCKS)
			    && !(ofp->i_opts.flags & FLOD_OPT_OFF)) {
				iflod_socks_start(ofp);
			}
		}
	}

	mapped = 0;
	if (flod_mmaps) {
		map_delayed = 0;
	} else if (!force && ++map_delayed > 10) {
		oflods_load();
		mapped = 1;
		map_delayed = 0;
	}
	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
		if (ofp->hostname[0] == '\0')
			break;
		if (!ofp->mp)
			continue;

		if (ofp->s < 0
		    && !(ofp->o_opts.flags & FLOD_OPT_OFF)) {
			rpt_err(&ofp->mp->o_err, 0, FLOD_ERR_SAME, 0,
				"no outgoing flood connection to"
				" %s, server-ID %d",
				ofp->hostname, ofp->rem_id);
		}
		if (ofp->cnts.in_start == 0
		    && !(ofp->i_opts.flags & FLOD_OPT_OFF)) {
			rpt_err(&ofp->mp->i_err, 0, FLOD_ERR_SAME, 0,
				"no incoming flood connection from"
				" %s, server-ID %d",
				ofp->hostname, ofp->rem_id);
		}
	}
	if (mapped)
		oflods_unmap();

	/* shut down any streams that have been quiet for too long */
	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
		if (ofp->hostname[0] == '\0')
			break;
		if ((ofp->flags & OFLOD_FG_CONNECTED)
		    && DB_IS_TIME(ofp->keep_out_time, KEEPALIVE_OUT))
			oflod_close(ofp, 0, FLOD_ERR_KEEPALIVE, 0);
	}

	/* send the flood positions to the disk so that dblist can see them */
	flod_sync(0);

	/* try to reap the hostname resolving child */
	flod_names_resolve_ck();
}



void
flods_init(void)
{
	IFLOD_INFO *ifp;

	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp)
		ifp->s = -1;

	oflods_clear();

	flods_restart(0);
}
