/* Distributed Checksum Clearinghouse * * deal with outgoing floods of checksums * * --C-LICENSE-- * $Revision: 1.195 $ */ #include "dccd_defs.h" #include "dcc_ck.h" #include 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 */ DB_PTR summarize_limit; time_t summarize_limit_secs; 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(FLOD_OPTS *opts, const char *str0, int lno) { DCC_FNM_LNO_BUF fnm_buf; const char *str; SRVR_ID_MAP *imp; if (opts->maps.len >= DIM(opts->maps.entry)) { dcc_error_msg("too many ID mappings with\"%s\"%s", str0, dcc_fnm_lno(&fnm_buf, flod_path.c, lno)); return 0; } imp = &opts->maps.entry[opts->maps.len]; if (!CLITCMP(str0, "self->")) { str = str0+LITZ("self->"); imp->lo = imp->hi = my_srvr_id; } else if (!CLITCMP(str0, "all->")) { str = str0+LITZ("all->"); imp->lo = DCC_SRVR_ID_MIN; imp->hi = DCC_SRVR_ID_MAX; } else { /* get ID1 */ str = dcc_get_srvr_id(0, &imp->lo, str0, "-", flod_path.c, lno); if (!str) return 0; if (str[0] == '-' && str[1] == '>') { /* ID1 is not a range */ imp->hi = imp->lo; } else { /* ID1 is a range of IDs */ str = dcc_get_srvr_id(0, &imp->hi, str+1, "-", flod_path.c, lno); if (!str) return 0; if (imp->hi < imp->lo) { dcc_error_msg("invalid ID mapping range " "\"%d-%d\"%s", imp->lo, imp->hi, dcc_fnm_lno(&fnm_buf, flod_path.c, lno)); return 0; } } if (*str++ != '-' || *str++ != '>') { dcc_error_msg("invalid server-ID mapping \"%s\"%s", str0, dcc_fnm_lno(&fnm_buf, flod_path.c, 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, dcc_fnm_lno(&fnm_buf, flod_path.c, lno)); return 0; } ++opts->maps.len; 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", dcc_fnm_lno(&fnm_buf, flod_path.c, lno)); return 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, FLOD_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; /* suppress compiler false alarm uninitialized warning */ fnm_buf.b[0] = '\0'; /* pick out the blank delimited string of options */ buf = dcc_parse_word(0, opts_buf, sizeof(opts_buf), buf, "flood options", flod_path.c, 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")) /* obsolete */ 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, "trace1") || !strcasecmp(opt, "trace")) { ofp->o_opts.flags |= FLOD_OPT_TRACE1; 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", dcc_fnm_lno(&fnm_buf, flod_path.c, 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", dcc_fnm_lno(&fnm_buf, flod_path.c, 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) { dcc_error_msg("invalid maximum path length in" " \"%s\"", opt); return 0; } ofp->o_opts.path_len = l; continue; } #ifdef DCC_FLOD_VERS7 if (!strcasecmp(opt, "version7")) { ofp->oversion = DCC_FLOD_VERS7; 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, dcc_fnm_lno(&fnm_buf, flod_path.c, 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.c, 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.c, 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; save_flod_cnts(ofp); } } static void copy_opts2mp(OFLOD_INFO *ofp) { FLOD_MMAP *mp; FLOD_MMAP_FLAGS mask, new_flags, delta_flags; u_char changed; mp = ofp->mp; changed = 0; /* these bits ar controlled here */ mask = (FLODMAP_FG_NEED_RWD | FLODMAP_FG_FFWD_IN | 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_NAT_AUTO | FLODMAP_FG_LEAF); new_flags = mp->flags & mask; /* these are off unless set by the flod file */ new_flags &= ~(FLODMAP_FG_ROGUE | FLODMAP_FG_OUT_OFF | FLODMAP_FG_IN_OFF | FLODMAP_FG_IPv4 | FLODMAP_FG_IPv6 | FLODMAP_FG_PASSIVE | FLODMAP_FG_SOCKS | FLODMAP_FG_NAT | FLODMAP_FG_LEAF); if (db_parms.flags & DB_PARM_FG_NEED_RWD) { new_flags |= FLODMAP_FG_NEED_RWD; 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; } else if (ofp->o_opts.flags & FLOD_OPT_IPv6) { new_flags |= FLODMAP_FG_IPv6; } if (ofp->o_opts.flags & FLOD_OPT_PASSIVE) { new_flags |= FLODMAP_FG_PASSIVE; new_flags &= ~FLODMAP_FG_NAT_AUTO; } else if (ofp->o_opts.flags & FLOD_OPT_SOCKS) { new_flags |= FLODMAP_FG_SOCKS; new_flags &= ~FLODMAP_FG_NAT_AUTO; } else if (ofp->o_opts.flags & FLOD_OPT_NAT) { new_flags |= FLODMAP_FG_NAT; new_flags &= ~FLODMAP_FG_NAT_AUTO; } if (ofp->o_opts.path_len != DCC_MAX_FLOD_PATH) new_flags |= FLODMAP_FG_LEAF; delta_flags = (mp->flags ^ new_flags) & mask; if (delta_flags != 0) { mp->flags ^= delta_flags; changed = 1; /* force name resolution for change in address family */ if (delta_flags & (FLODMAP_FG_IPv6 | FLODMAP_FG_IPv4)) got_hosts = 0; } memcpy(&mp->i_id_maps, &ofp->i_opts.maps, sizeof(mp->i_id_maps)); memcpy(&mp->o_id_maps, &ofp->o_opts.maps, sizeof(mp->o_id_maps)); /* get new host name 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; } if (strcasecmp(mp->rem_portname, ofp->rem_portname)) { BUFCPY(mp->rem_portname, ofp->rem_portname); changed = 1; } if (strcasecmp(mp->loc_hostname, ofp->loc_hostname)) { BUFCPY(mp->loc_hostname, ofp->loc_hostname); /* force name resolution for new name */ got_hosts = 0; changed = 1; } if (mp->loc_port != ofp->loc_port) { mp->loc_port = ofp->loc_port; changed = 1; } if (flod_mmaps->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, 1); } /* 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=start floods, 0=try again later */ load_flod(u_char diag, 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; DB_ST *rcd_st; char *p; int i; /* The caller should use RUSH_NEXT_FLODS_CK() or similar, * but be sure because we don't want to fight with the resolving * process. */ 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)) { if (complain) dcc_error_msg("%s", dcc_emsg.c); flod_mtime = 0; return 0; } f = fopen(flod_path.c, "r"); if (!f) { if (flod_mtime != 0) { dcc_error_msg("fopen(%s): %s", DB_NM2PATH_ERR(flod_path.c), 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", DB_NM2PATH_ERR(flod_path.c), 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 host name problem. */ 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", DB_NM2PATH_ERR(flod_path.c), ERROR_STR()); break; } if (fclose(f) == EOF) { dcc_error_msg("fclose(%s): %s", DB_NM2PATH_ERR(flod_path.c), ERROR_STR()); } f = 0; break; } i = strlen(bufp); if (i >= ISZ(buf)-1) { dcc_error_msg("too many characters%s", dcc_fnm_lno(&fnm_buf, flod_path.c, 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", DB_NM2PATH_ERR(flod_path.c), 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.c, 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.c, 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", dcc_fnm_lno(&fnm_buf, flod_path.c, 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_VERS_DEF; bufp = parse_flod_opts(ofp, &ofp->o_opts, bufp, lno); if (!bufp) { ofp->o_opts.flags |= FLOD_OPT_OFF; ofp->i_opts.flags |= FLOD_OPT_OFF; } else { bufp = parse_flod_opts(ofp, &ofp->i_opts, bufp, lno); } if (!bufp) { ofp->o_opts.flags |= FLOD_OPT_OFF; ofp->i_opts.flags |= FLOD_OPT_OFF; } else if (*bufp != '\0') { dcc_error_msg("trailing garbage \"%s\" ignored%s", bufp, dcc_fnm_lno(&fnm_buf, flod_path.c, lno)); ofp->o_opts.flags |= FLOD_OPT_OFF; ofp->i_opts.flags |= FLOD_OPT_OFF; } rcd_st = GET_DB_ST(); if (0 < find_srvr_rcd_type(ofp->rem_id, rcd_st)) { switch (DB_RCD_ID(rcd_st->d.r)) { case DCC_ID_SRVR_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; } } free_db_st(rcd_st); 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 and line %d", dcc_fnm_lno(&fnm_buf, flod_path.c, lno), ofp1->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, DB_PTR_MAX+1) || mp->confirm_pos > db_csize) { dcc_error_msg("bogus position "L_HxPAT " for %s in %s", mp->confirm_pos, ofp->rem_hostname, flod_mmap_path.c); 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_id = ofp->rem_id; copy_opts2mp(ofp); mp->cnts.cnts_cleared = db_time.tv_sec; /* normally flood from start, but avoid rewinding if not * repairing a broken mapped flod file */ if (flod_mmaps->dccd_stats.reset != 0) { mp->confirm_pos = db_parms.min_confirm_pos; } else { mp->confirm_pos = db_csize; } ofp->cur_pos = mp->confirm_pos; ofp->recv_pos = ofp->xmit_pos = mp->confirm_pos; 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, dcc_fnm_lno(&fnm_buf, flod_path.c, ofp->lno)); } out:; flod_mmaps->ids_mtime = ids_mtime; /* 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); get_oflods_max_cur_pos(); if (!diag) { db_parms.flags &= ~DB_PARM_FG_NEED_RWD; db_flush_parms(0); } 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 > 60*60) 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->cnts.in_total_conn += delta; 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, int mode) /* 1=backoff failure, 0=ok, -1=retry */ { u_char need_oflods_clear = 0; if (ofp->rem_hostname[0] == '\0') return; if (ofp->soc >= 0) { if (0 > close(ofp->soc) && !mode) { if (errno == ECONNRESET) TMSG2_FLOD1(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 (mode >= 0) ofp->o_dns.num = 0; /* start from 1st IP address next time */ if (mode > 0) { 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_FLOD1(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))) ferr(ofp, 0, FERR_STOP, 0, "setsockopt(SO_LINGER flood to %s): %s", ofp_rem_str(ofp), ERROR_STR()); if (0 > shutdown(ofp->soc, 1)) { ferr(ofp, 0, FERR_STOP, errno==ECONNRESET, "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 * ofp->cur_pos has already been advanced */ static u_char /* 1=flood this report, 0=don't */ oflod_ck_put(const DB_ST *rcd_st) { 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(rcd_st->d.r) == DCC_ID_WHITE || DB_RCD_ID(rcd_st->d.r) == DCC_ID_COMP || (rcd_tgts = DB_TGTS_RCD_RAW(rcd_st->d.r)) == 0 || ts2secs(&rcd_st->d.r->ts) < db_time.tv_sec - FLOD_STALE_SECS || rcd_tgts == DCC_TGTS_REP_ADJ || DB_RCD_TRIMMED(rcd_st->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(rcd_st->d.r)) return 0; result = 0; obs_lvl = 0; cur_rcd_ck = rcd_st->d.r->cks; for (num_cks = DB_NUM_CKS(rcd_st->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_JUNK(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 * ofp->cur_pos has already been advanced */ static void put_rcd_obuf(OFLOD_INFO *ofp, const DB_ST *rcd_st) { DCC_FLOD_RPT *rp; DCC_TGTS tgts; DCC_SRVR_ID srvr; const DB_RCD *cur_rcd; const DB_RCD_CK *cur_rcd_ck; DCC_CK *buf_ck; DCC_FLOD_PATH_ID *new_path_idp, *new_path_id_limp; const DCC_FLOD_PATH_ID *rcd_path_id; int path_ids; 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(rcd_st)) return; /* skip it */ rp = (DCC_FLOD_RPT *)&ofp->obuf.b[ofp->obuf_len]; db_ptr2flod_pos(rp->pos, ofp->cur_pos); cur_rcd = rcd_st->d.r; tgts = DB_TGTS_RCD_RAW(cur_rcd); /* don't send delete requests to systems that don't want them */ if (tgts == DCC_TGTS_DEL && !(ofp->o_opts.flags & FLOD_OPT_DEL_OK)) return; srvr = DB_RCD_ID(cur_rcd); /* translate the source server-ID */ srvr_mapped = id_map(srvr, &ofp->o_opts.maps); switch (srvr_mapped) { case ID_MAP_NO: break; case ID_MAP_REJ: return; case ID_MAP_SELF: srvr = my_srvr_id; break; } rp->srvr_id[0] = srvr>>8; rp->srvr_id[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 = buf_ck->sum.p; 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_ids = 1; ++new_path_idp; ++buf_ck; rp->num_cks = 1; } else { /* do not add a new path to our own reports */ new_path_idp = new_path_id_limp = 0; path_ids = 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); /* deal with paths */ if (type == DCC_CK_FLOD_PATH) { rcd_path_id = cur_rcd_ck->sum.p; for (j = 0; j < DCC_NUM_FLOD_PATH; ++j, ++rcd_path_id) { DCC_SRVR_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_ids > DCC_MAX_FLOD_PATH) 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 = buf_ck->sum.p; 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.maps)) { case ID_MAP_NO: case ID_MAP_REJ: break; case ID_MAP_SELF: psrvr = my_srvr_id; break; } new_path_idp->hi = psrvr>>8; new_path_idp->lo = psrvr; ++new_path_idp; } continue; } /* Deal with all other checksums. * * 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; } if (type == DCC_CK_SRVR_ID) { /* do not send types of mapped server-IDs */ if (DCC_ID_SRVR_TYPE(srvr)) { switch (id_map(DCC_ID_SRVR_TYPE_ID (&cur_rcd_ck->sum), &ofp->o_opts.maps)) { case ID_MAP_NO: case ID_MAP_REJ: break; case ID_MAP_SELF: return; } } } else { /* Do not send reports from the target to the target * or reports wtih paths that are too long * unless the report is a Server-ID declaration */ if (reflecting || path_ids >= ofp->o_opts.path_len) return; } if (DCC_CK_IS_REP(grey_on,type)) { /* do not send reputation checksumss to peers * that do not understand them */ if (!(ofp->flags & OFLOD_FG_REP_SEND)) continue; /* do not send useless bulk reputation counts */ if (DB_CK_JUNK(cur_rcd_ck) && DCC_CK_IS_REP_BULK(grey_on, type)) continue; } /* send everything else */ buf_ck->type = type; buf_ck->len = sizeof(*buf_ck); buf_ck->sum = cur_rcd_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) { DB_ST *rcd_st; 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; rcd_st = GET_DB_ST(); 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_parms.min_confirm_pos) ofp->cur_pos = db_parms.min_confirm_pos; if (ofp->cur_pos >= db_csize) { /* nothing to send, so 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; } if (!db_map_rcd(&dcc_emsg, rcd_st, ofp->cur_pos, &cur_rcd_len)) { dcc_error_msg("oflod_fill() starting at "L_HxPAT " for %s: %s", ofp->cur_pos, ofp_rem_str(ofp), dcc_emsg.c); ofp->cur_pos = db_csize; break; } if (DB_NUM_CKS(rcd_st->d.r) > DCC_DIM_CKS) { dcc_error_msg("impossible %d checksums in cur_pos=" L_HxPAT" for %s", DB_NUM_CKS(rcd_st->d.r), ofp->cur_pos, ofp_rem_str(ofp)); ofp->cur_pos = db_csize; break; } /* send the record */ ofp->cur_pos += cur_rcd_len; put_rcd_obuf(ofp, rcd_st); } free_db_st(rcd_st); 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 the peer has not refused to do reputations today * and if they are enabled on our end, * then try reputations */ if (!grey_on && (!(ofp->mp->flags & FLODMAP_FG_REP_PEER_REJ) || DB_IS_TIME(ofp->mp->reps_rejected+24*60*60, 24*60*60))) ofp->flags |= OFLOD_FG_REP_TRY; if (ofp->flags & OFLOD_FG_REP_TRY) { if (ofp->oversion == 0) return DCC_FLOD_REP_VERS_CUR_STR; #ifdef DCC_FLOD_VERS7 if (ofp->oversion == DCC_FLOD_VERS7) return DCC_FLOD_REP_VERS7_STR; #endif dcc_logbad(EX_SOFTWARE, "unknown ofp->oversion=%d", ofp->oversion); } if (ofp->oversion == 0) return DCC_FLOD_VERS_CUR_STR; #ifdef DCC_FLOD_VERS7 if (ofp->oversion == DCC_FLOD_VERS7) return DCC_FLOD_VERS7_STR; #endif dcc_logbad(EX_SOFTWARE, "unknown ofp->oversion=%d", ofp->oversion); } /* reset connect() or daily complaint timers */ void flod_try_again(OFLOD_INFO *ofp, u_char reset_backoff) { FLOD_MMAP *mp = ofp->mp; mp = ofp->mp; if (!mp) return; if (reset_backoff) { mp->otimers.retry_secs = 0; } else { /* compensate for a doubling to keep backoff steady */ mp->otimers.retry_secs /= 2; } /* ordinary connect() timer should fire immediately */ 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; } if (reset_backoff) { ferr_clear_all(ofp, 1); ferr_clear_all(ofp, 0); mp->itimers.retry_secs = 0; } else { /* compensate for a doubling to keep backoff steady */ mp->itimers.retry_secs /= 2; } 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; } } /* 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, db_debug != 0); 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_FLOD1(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) { FLOD_MMAP *mp; DCC_SRVR_ID id; DCC_FNM_LNO_BUF fnm_buf; const char *emsg; mp = ofp->mp; ofp->o_dns.num = 0; /* start from 1st IP address next time */ ofp->oflod_alive = db_time.tv_sec; ofp->flags |= (OFLOD_FG_CONNECTED | OFLOD_FG_NEW); ofp->recv_pos = ofp->xmit_pos = ofp->cur_pos = 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) { ferr(ofp, 0, FERR_START, 0, "%s %d%s", emsg, ofp->out_passwd_id, dcc_fnm_lno(&fnm_buf, flod_path.c, ofp->lno)); return 0; } if (ofp->flags & OFLOD_FG_REP_TRY) { /* send reputations because the peer accepted our * reputation version string */ mp->flags &= ~FLODMAP_FG_REP_PEER_REJ; mp->reps_rejected = 0; ofp->flags |= OFLOD_FG_REP_SEND; /* reputations imply accepting deletes by default */ if (!(ofp->o_opts.flags & FLOD_OPT_DEL_SET)) ofp->o_opts.flags |= FLOD_OPT_DEL_OK; } else { ofp->flags &= ~OFLOD_FG_REP_SEND; /* If the delete-sending option has not been set, * clear the default set with reputations */ if (!(ofp->o_opts.flags & FLOD_OPT_DEL_SET)) ofp->o_opts.flags &= ~FLOD_OPT_DEL_OK; } TMSG1_FLOD1(ofp, "flood connected to %s", ofp_rem_str(ofp)); /* All is well, so forget old complaints, * unless we have been forced passive */ if (!(mp->flags & FLODMAP_FG_OUT_SRVR) || (mp->flags & FLODMAP_FG_PASSIVE)) ferr_clear_all(ofp, 0); oflod_write(ofp); /* send our authentication */ /* if that worked, ensure that we try the auto-NAT kludge soon */ if (ofp->soc >= 0 && !ofp->ifp && !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; } return 1; } static void oflod_backoff(OFLOD_INFO *ofp, u_char fast) { FLOD_MMAP *mp; time_t 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=give up, 0=not yet, 1=done */ oflod_connect_start(OFLOD_INFO *ofp) { DCC_FNM_LNO_BUF fnm_buf; int i; /* We are starting over, so problems with previous attempts. * If this attempt fails, we will have new complaints. */ ferr_clear_all(ofp, 0); ofp->mp->flags &= ~FLODMAP_FG_OUT_SRVR; if (ofp->o_opts.flags & FLOD_OPT_SOCKS) { i = Rconnect(ofp->soc, &ofp->rem.sa, DCC_SU_LEN(&ofp->rem)); } else { i = connect(ofp->soc, &ofp->rem.sa, DCC_SU_LEN(&ofp->rem)); } if (0 > i && errno != EISCONN) { if (CONN_WAIT_ERRORS()) { ferr(ofp, 0, FERR_START, 1, "waiting to connect flood to %s", ofp_rem_str(ofp)); return 0; } /* failure */ ferr(ofp, 0, FERR_START, CONN_ERRORS(), "connect(%s%s): %s", ofp_rem_str(ofp), dcc_fnm_lno(&fnm_buf, flod_path.c, ofp->lno), CONN_EMSG()); if (++ofp->o_dns.cur < ofp->o_dns.num) { /* immediately try the next address */ oflod_close(ofp, -1); } else { /* After the last IP address, increase the delay. * Limit the back-off 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; FLOD_MMAP *mp; mp = ofp->mp; /* try the next host name */ for (;;) { 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 (ofp->o_dns.cur >= ofp->o_dns.num) { if (!flod_names_resolve_start(ofp)) return; /* wait for name resolution */ ofp->o_dns = mp->dns; if (ofp->o_dns.num == 0) { ferr(ofp, 0, FERR_DNS, 0, "flood peer name %s: '%s'%s", ofp->rem_hostname, H_ERROR_STR1(ofp->o_dns.rem_h_errno), dcc_fnm_lno(&fnm_buf, flod_path.c, ofp->lno)); oflod_backoff(ofp, 0); oflod_close(ofp, 1); return; } /* since we have addresses, * forget old complaints about bad host names */ ferr_clear(ofp, 0, FERR_DNS); } /* stop if we cannot open the socket */ ofp->rem = ofp->o_dns.su[ofp->o_dns.cur]; DCC_SU_PORT(&ofp->rem) = ofp->rem_port; ofp->soc = socket(ofp->rem.sa.sa_family, SOCK_STREAM, 0); if (ofp->soc < 0) { ferr(ofp, 0, FERR_START, 0, "socket(flood %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)) { oflod_close(ofp, 1); return; } bind_flod_socket(ofp, ofp->soc, &ofp->rem, &ofp->o_dns); if (0 <= oflod_connect_start(ofp)) return; } } void oflod_write(OFLOD_INFO *ofp) { int i; if (ofp->obuf_len == 0) { if (!(ofp->flags & OFLOD_FG_CONNECTED) && 0 >= oflod_connect_start(ofp)) 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_FLOD1(ofp, "pause after send(flood to %s): %s", ofp_rem_str(ofp), ERROR_STR()); return; } ferr(ofp, 0, FERR_IO, 0, "send(flood to %s): %s", ofp_rem_str(ofp), ERROR_STR()); } else { ferr(ofp, 0, FERR_IO, 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 */ } /* guess that this peer does not like reputations * and try an ordinary flood */ if (ofp->flags & OFLOD_FG_REP_TRY) { ofp->mp->reps_rejected = db_time.tv_sec; ofp->mp->flags |= FLODMAP_FG_REP_PEER_REJ; ofp->flags &= ~OFLOD_FG_REP_TRY; 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 (recv_len < 0) { if (!DCC_BLOCK_ERROR()) { ferr(ofp, 0, FERR_IO, 1, "recv(outgoing flood %s): %s", ofp_rem_str(ofp), ERROR_STR()); oflod_close(ofp, 1); } return; } /* the connection is active */ 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); ferr(ofp, 0, FERR_IO, fail>=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) { ferr(ofp, 1, FERR_IO, 1, "outgoing end request from %s;" " passive output", ofp_rem_str(ofp)); } else { ferr(ofp, 1, FERR_IO, 1, "outgoing end request from %s;" " postpone restarting for %d seconds", 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) { ferr(ofp, 0, FERR_IO, 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_FLOD1(ofp, "received 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) { ferr(ofp, 0, FERR_IO, 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("received FFWD output 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)) { ferr(ofp, 0, FERR_IO, 0, "bogus confirmed flood position" " "L_HxPAT" from %s;" " recv_pos="L_HxPAT" xmit_pos="L_HxPAT, 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) ferr(ofp, 0, FERR_IO, 0, "truncated outgoing flood response from %s", ofp_rem_str(ofp)); else ferr(ofp, 0, FERR_IO, 0, "missing outgoing flood response from %s", ofp_rem_str(ofp)); oflod_close(ofp, 1); /* Quit without touching anything more if flooding is * shutting down and that was the last connection. */ if (!ofp->mp) return; } } 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_FLOD1(ofp, "resume flooding %s after EAGAIN", ofp->rem_hostname); ofp->flags &= ~OFLOD_FG_EAGAIN; } if (!(ofp->flags & OFLOD_FG_CONNECTED)) continue; /* 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)) { ferr(ofp, 0, FERR_STOP, 1, "stopping; forced close flood to %s", ofp_rem_str(ofp)); oflod_close(ofp, 0); } else if (OFP_DEAD(ofp, KEEPALIVE_OUT_STOP)) { ferr(ofp, 0, FERR_STOP, 1, "off; forced 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)) { ferr(ofp, 0, FERR_STOP, 1, "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)) { ferr(ofp, 0, FERR_STOP, 1, "halting flood to %s", ofp_rem_str(ofp)); oflod_close(ofp, 0); } else if (!(ofp->flags & (OFLOD_FG_SHUTDOWN_REQ | OFLOD_FG_SHUTDOWN))) { ferr(ofp, 0, FERR_STOP, 1, "stopping flood to %s", ofp_rem_str(ofp)); start_shutdown(ofp); } } if (resolve_hosts_pid > 0) kill(resolve_hosts_pid, SIGINT); flod_names_resolve_ck(); 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 != 0, db_debug != 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.c); if (mode == 0) flods_restart("restart flooding with new IDs", 0); if (flod_mtime > 1) flod_mtime = 1; return 1; } static void flod_msg(LAST_FERR *ep, const char *str, const char *arg) { const char *msg; if (ep->error_type != FERR_NONE && ep->error_gen != flod_msg_gen) { msg = ep->error_msg; ep->error_gen = flod_msg_gen; } else if (ep->trace_type != FERR_NONE && ep->trace_gen != flod_msg_gen) { msg = ep->trace_msg; ep->trace_gen = flod_msg_gen; } else { msg = ""; } dcc_error_msg("no %s %s%s%s%s", str, arg, msg[0] ? ": \"" : "", msg, msg[0] ? "\"" : ""); } /* called periodically and at need */ void flods_ck(u_char force) { static int map_delayed; DB_ST *rcd_st; IFLOD_INFO *ifp; OFLOD_INFO *ofp; struct stat flod_sb; time_t us; DCC_TS past; 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 connection 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.c, &flod_sb)) { if (errno != ENOENT && flod_mtime != 0) dcc_error_msg("stat(%s): %s", DB_NM2PATH_ERR(flod_path.c), ERROR_STR()); flod_sb.st_mtime = 0; } if (flod_mtime != 0 && 0 > access(flod_mmap_path.c, W_OK | R_OK)) { if (errno != ENOENT) dcc_error_msg("access(%s): %s", DB_NM2PATH_ERR(flod_mmap_path.c), 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", DB_NM2PATH_ERR(flod_path.c)); 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() && DB_IS_LOCKED()) { 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(0, 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 */ if (flod_mmaps && !dbclean_running) { timeval2ts(&past, &db_time, -summarize_delay_secs); if (flod_mmaps->delay_pos > db_csize || flod_mmaps->delay_pos < DB_PTR_BASE) flod_mmaps->delay_pos = DB_PTR_BASE; work = 0; rcd_st = GET_DB_ST(); while (flod_mmaps->delay_pos < db_csize) { if (!db_map_rcd(0, rcd_st, flod_mmaps->delay_pos, &rcd_len)) { flod_mmaps->delay_pos = db_csize; break; } /* only our own reports are delayed */ if (DB_RCD_DELAY(rcd_st->d.r)) { struct timeval trace_time; /* wait until it is time */ if (ts_newer_ts(&rcd_st->d.r->ts, &past)) break; if (TMSG_BIT(DB)) gettimeofday(&trace_time, 0); if (!summarize_dly(rcd_st)) { flod_mmaps->delay_pos = db_csize; break; } if (TMSG_BIT(DB)) { gettimeofday(&db_time, 0); us = tv_diff2us(&db_time, &trace_time); if (us >= DCC_US/10) dcc_trace_msg("spent %.3f" " seconds summarizing " L_HxPAT, us/(DCC_US*1.0), flod_mmaps->delay_pos); } } flod_mmaps->delay_pos += rcd_len; /* spend at most 0.25 second at this * and then let other processes run*/ if (++work >= 50) { gettimeofday(&db_time, 0); us = tv_diff2us(&db_time, &wake_time); if (us > DCC_US/4) { next_flods_ck = 0; break; } work = 0; } } free_db_st(rcd_st); /* prime the outgoing pumps */ for (ofp = oflods.infos; ofp <= LAST(oflods.infos) && ofp->rem_hostname[0] != '\0'; ++ofp) { 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, 0)) { loaded = 1; map_delayed = 0; } for (ofp = oflods.infos; ofp <= LAST(oflods.infos) && ofp->rem_hostname[0] != '\0' && flod_mmaps; ++ofp) { FLOD_MMAP *mp; 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, 0); } mp = ofp->mp; if (ofp->soc < 0 && (mp->flags & FLODMAP_FG_PASSIVE) && !OFLOD_OPT_OFF_ROGUE(ofp) && DB_IS_TIME(mp->otimers.msg, mp->otimers.msg_secs)) { flod_msg(&mp->oflod_err, "passive connection to", ofp->rem_hostname); mp->otimers.msg_secs = FLOD_IN_COMPLAIN; mp->otimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN; } /* consider missing inputs */ if (ofp->ifp) continue; if ((mp->flags & FLODMAP_FG_ACT) == 0 && !IFLOD_OPT_OFF_ROGUE(ofp) && DB_IS_TIME(mp->itimers.msg, mp->itimers.msg_secs)) { flod_msg(&mp->iflod_err, "incoming connection from", ofp->rem_hostname); mp->itimers.msg_secs = FLOD_IN_COMPLAIN; mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN; } /* try NAT kludge around a firewall if output is working * but input is dead */ if ((ofp->flags & OFLOD_FG_CONNECTED) && DB_IS_TIME(ofp->mp->cnts.out_conn_changed + FLOD_AUTO_NAT_DELAY, FLOD_AUTO_NAT_DELAY) && DB_IS_TIME(ofp->mp->cnts.in_conn_changed + FLOD_AUTO_NAT_DELAY, FLOD_AUTO_NAT_DELAY) && !(ofp->flags & (OFLOD_FG_NEW | OFLOD_FG_SHUTDOWN_REQ | OFLOD_FG_SHUTDOWN)) && !(mp->flags & (FLODMAP_FG_NAT_AUTO | FLODMAP_FG_OUT_SRVR | FLODMAP_FG_PASSIVE | FLODMAP_FG_ACT))) { dcc_trace_msg("try auto-NAT work-around from %s", ofp->rem_hostname); mp->flags |= FLODMAP_FG_NAT_AUTO; } } 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); }