Mercurial > notdcc
view dccd/oflod.c @ 2:f6716cb00029
Replace buggy stuff in deb dir, never make phone calls while working
author | Peter Gervai <grin@grin.hu> |
---|---|
date | Tue, 10 Mar 2009 14:29:12 +0100 |
parents | c7f6b056b673 |
children |
line wrap: on
line source
/* 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); }