Mercurial > notdcc
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); +}