0
|
1 /* Distributed Checksum Clearinghouse |
|
2 * |
|
3 * deal with incoming floods of checksums |
|
4 * |
|
5 * Copyright (c) 2008 by Rhyolite Software, LLC |
|
6 * |
|
7 * This agreement is not applicable to any entity which sells anti-spam |
|
8 * solutions to others or provides an anti-spam solution as part of a |
|
9 * security solution sold to other entities, or to a private network |
|
10 * which employs the DCC or uses data provided by operation of the DCC |
|
11 * but does not provide corresponding data to other users. |
|
12 * |
|
13 * Permission to use, copy, modify, and distribute this software without |
|
14 * changes for any purpose with or without fee is hereby granted, provided |
|
15 * that the above copyright notice and this permission notice appear in all |
|
16 * copies and any distributed versions or copies are either unchanged |
|
17 * or not called anything similar to "DCC" or "Distributed Checksum |
|
18 * Clearinghouse". |
|
19 * |
|
20 * Parties not eligible to receive a license under this agreement can |
|
21 * obtain a commercial license to use DCC by contacting Rhyolite Software |
|
22 * at sales@rhyolite.com. |
|
23 * |
|
24 * A commercial license would be for Distributed Checksum and Reputation |
|
25 * Clearinghouse software. That software includes additional features. This |
|
26 * free license for Distributed ChecksumClearinghouse Software does not in any |
|
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse |
|
28 * software |
|
29 * |
|
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL |
|
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES |
|
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC |
|
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES |
|
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
|
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
|
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
|
37 * SOFTWARE. |
|
38 * |
|
39 * Rhyolite Software DCC 1.3.103-1.249 $Revision$ |
|
40 */ |
|
41 |
|
42 #include "dccd_defs.h" |
|
43 #include <sys/wait.h> |
|
44 |
|
45 |
|
46 IFLODS iflods; |
|
47 |
|
48 u_int complained_many_iflods; |
|
49 |
|
50 time_t got_hosts; |
|
51 pid_t resolve_hosts_pid = -1; |
|
52 |
|
53 time_t iflods_ok_timer; /* incoming flooding ok since then */ |
|
54 |
|
55 int flod_trace_gen; /* unsuppress tracing */ |
|
56 |
|
57 static u_char iflod_write(IFLOD_INFO *, void *, int, const char *, u_char); |
|
58 |
|
59 |
|
60 static DCC_TS future; /* timestamp sanity */ |
|
61 |
|
62 |
|
63 |
|
64 ID_MAP_RESULT |
|
65 id_map(DCC_SRVR_ID srvr, const OFLOD_OPTS *opts) |
|
66 { |
|
67 int i; |
|
68 ID_MAP_RESULT result; |
|
69 |
|
70 /* apply the ***first*** server-ID map that matches, if any */ |
|
71 for (i = 0; i < opts->num_maps; ++i) { |
|
72 if (opts->srvr_map[i].from_lo <= srvr |
|
73 && opts->srvr_map[i].from_hi >= srvr) { |
|
74 result = opts->srvr_map[i].result; |
|
75 if (result == ID_MAP_SELF |
|
76 && srvr == my_srvr_id) |
|
77 return ID_MAP_NO; |
|
78 return result; |
|
79 } |
|
80 } |
|
81 return ID_MAP_NO; |
|
82 } |
|
83 |
|
84 |
|
85 |
|
86 static const char * |
|
87 rem_str(const char *hostname, const DCC_SOCKU *su) |
|
88 { |
|
89 static int bufno; |
|
90 static struct { |
|
91 char str[90]; |
|
92 } bufs[4]; |
|
93 char *s; |
|
94 char sustr[DCC_SU2STR_SIZE]; |
|
95 int i; |
|
96 |
|
97 s = bufs[bufno].str; |
|
98 bufno = (bufno+1) % DIM(bufs); |
|
99 |
|
100 STRLCPY(s, hostname, sizeof(bufs[0].str)); |
|
101 |
|
102 if (su->sa.sa_family == AF_UNSPEC |
|
103 && *DCC_SU_PORTP(su) == 0) |
|
104 return s; |
|
105 |
|
106 dcc_su2str2(sustr, sizeof(sustr), su); |
|
107 if (strcmp(s, sustr)) { |
|
108 STRLCAT(s, " ", sizeof(bufs[0].str)); |
|
109 STRLCAT(s, sustr, sizeof(bufs[0].str)); |
|
110 } |
|
111 if (*DCC_SU_PORTP(su) != DCC_GREY2PORT(grey_on)) { |
|
112 i = strlen(s); |
|
113 snprintf(s+i, sizeof(bufs[0].str)-i, |
|
114 ",%d", ntohs(*DCC_SU_PORTP(su))); |
|
115 } |
|
116 return s; |
|
117 } |
|
118 |
|
119 |
|
120 |
|
121 const char * |
|
122 ifp_rem_str(const IFLOD_INFO *ifp) |
|
123 { |
|
124 if (!ifp) |
|
125 return "(null ifp)"; |
|
126 return rem_str(ifp->rem_hostname, &ifp->rem_su); |
|
127 } |
|
128 |
|
129 |
|
130 |
|
131 const char * |
|
132 ofp_rem_str(const OFLOD_INFO *ofp) |
|
133 { |
|
134 if (!ofp) |
|
135 return "(null ofp)"; |
|
136 return rem_str(ofp->rem_hostname, &ofp->rem_su); |
|
137 } |
|
138 |
|
139 |
|
140 |
|
141 static const char * |
|
142 rpt_id(const char *type, const DB_RCD* rcd, |
|
143 const IFLOD_INFO *ifp) |
|
144 { |
|
145 static int bufno; |
|
146 static struct { |
|
147 char str[120]; |
|
148 } bufs[4]; |
|
149 char *s; |
|
150 char id_buf[30]; |
|
151 |
|
152 s = bufs[bufno].str; |
|
153 bufno = (bufno+1) % DIM(bufs); |
|
154 |
|
155 snprintf(s, sizeof(bufs[0].str), "%s%s %s ID=%s %s%s", |
|
156 type ? "flooded " : "", |
|
157 type ? type : "", |
|
158 ts2str_err(&rcd->ts), |
|
159 id2str(id_buf, sizeof(id_buf), rcd->srvr_id_auth), |
|
160 ifp ? "from " : "", |
|
161 ifp ? ifp_rem_str(ifp) : ""); |
|
162 return s; |
|
163 } |
|
164 |
|
165 |
|
166 |
|
167 void PATTRIB(2,3) |
|
168 flod_cnterr(const FLOD_LIMCNT *lc, const char *p, ...) |
|
169 { |
|
170 char buf[200]; |
|
171 va_list args; |
|
172 |
|
173 va_start(args, p); |
|
174 if (lc->cur < lc->lim + FLOD_LIM_COMPLAINTS) { |
|
175 dcc_verror_msg(p, args); |
|
176 } else { |
|
177 vsnprintf(buf, sizeof(FLOD_EMSG), p, args); |
|
178 dcc_error_msg("%s; stop complaints", buf); |
|
179 } |
|
180 va_end(args); |
|
181 } |
|
182 |
|
183 |
|
184 |
|
185 static void |
|
186 date_err_msg(FLOD_EMSG out, int len, const char *in) |
|
187 { |
|
188 memcpy(out, in, len); |
|
189 if (len >= ISZ(FLOD_EMSG)-LITZ(" at hh:mm:ss")-1) { |
|
190 out[len] = '\0'; |
|
191 } else { |
|
192 dcc_time2str(&out[len], ISZ(FLOD_EMSG)-len, " at %T", |
|
193 db_time.tv_sec); |
|
194 } |
|
195 } |
|
196 |
|
197 |
|
198 |
|
199 /* remove extra quotes and strings from a message to or from a peer */ |
|
200 static void |
|
201 trim_err_msg(FLOD_EMSG out, const FLOD_EMSG in) |
|
202 { |
|
203 char *q1, *q2; |
|
204 int len; |
|
205 |
|
206 q1 = strchr(in, '\''); |
|
207 if (q1) { |
|
208 q2 = strrchr(++q1, '\''); |
|
209 if (q2) { |
|
210 len = q2-q1; |
|
211 if (len > LITZ(DCC_FLOD_OK_STR) |
|
212 && !LITCMP(q1, DCC_FLOD_OK_STR)) { |
|
213 len -= LITZ(DCC_FLOD_OK_STR); |
|
214 q1 += LITZ(DCC_FLOD_OK_STR); |
|
215 } |
|
216 date_err_msg(out, len, q1); |
|
217 return; |
|
218 } |
|
219 } |
|
220 date_err_msg(out, strlen(in), in); |
|
221 } |
|
222 |
|
223 |
|
224 |
|
225 /* report a flooding error */ |
|
226 void PATTRIB(4,5) |
|
227 rpt_err(OFLOD_INFO *ofp, |
|
228 u_char trace, /* 0=error, 1=trace, 2=no dup trace */ |
|
229 u_char in, /* 0=output 1=input */ |
|
230 const char *p, ...) |
|
231 { |
|
232 FLOD_MMAP *mp; |
|
233 LAST_ERROR *ep; |
|
234 FLOD_EMSG tmp, trimmed; |
|
235 va_list args; |
|
236 |
|
237 va_start(args, p); |
|
238 vsnprintf(tmp, sizeof(FLOD_EMSG), p, args); |
|
239 va_end(args); |
|
240 |
|
241 mp = ofp ? ofp->mp : 0; |
|
242 |
|
243 if (trace != 0) { |
|
244 if (!mp) { |
|
245 TMSG_FLOD(ofp, tmp); |
|
246 return; |
|
247 } |
|
248 |
|
249 trim_err_msg(trimmed, tmp); |
|
250 ep = in ? &mp->iflod_err : &mp->oflod_err; |
|
251 if (TMSG_FB(ofp) |
|
252 && (trace < 2 |
|
253 || strcmp(trimmed, ep->trace_msg) |
|
254 || ep->trace_gen != flod_trace_gen)) { |
|
255 /* suppress some duplicate flooding messages */ |
|
256 dcc_trace_msg(tmp); |
|
257 ep->trace_gen = flod_trace_gen; |
|
258 } |
|
259 strncpy(ep->trace_msg, trimmed, sizeof(ep->trace_msg)); |
|
260 ep->complained = 0; |
|
261 |
|
262 } else { |
|
263 if (!mp) { |
|
264 dcc_error_msg(tmp); |
|
265 return; |
|
266 } |
|
267 |
|
268 dcc_error_msg(tmp); |
|
269 |
|
270 ep = in ? &mp->iflod_err : &mp->oflod_err; |
|
271 trim_err_msg(ep->msg, tmp); |
|
272 ep->trace_msg[0] = '\0'; |
|
273 ep->complained = 0; |
|
274 } |
|
275 } |
|
276 |
|
277 |
|
278 |
|
279 u_char |
|
280 set_flod_socket(OFLOD_INFO *ofp, u_char in, int s, |
|
281 const char *hostname, const DCC_SOCKU *sup) |
|
282 { |
|
283 #if IP_TOS |
|
284 static u_char tos_ok = 1; |
|
285 #endif |
|
286 int on; |
|
287 |
|
288 if (0 > fcntl(s, F_SETFD, FD_CLOEXEC)) |
|
289 rpt_err(ofp, 0, in, "fcntl(%s, F_SETFD, FD_CLOEXEC): %s", |
|
290 rem_str(hostname, sup), ERROR_STR()); |
|
291 |
|
292 if (-1 == fcntl(s, F_SETFL, |
|
293 fcntl(s, F_GETFL, 0) | O_NONBLOCK)) { |
|
294 rpt_err(ofp, 0, in, "fcntl(%s, O_NONBLOCK): %s", |
|
295 rem_str(hostname, sup), ERROR_STR()); |
|
296 return 0; |
|
297 } |
|
298 |
|
299 on = 1; |
|
300 if (0 > setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, |
|
301 &on, sizeof(on))) |
|
302 rpt_err(ofp, 0, in, "setsockopt(flod %s, SO_KEEPALIVE): %s", |
|
303 rem_str(hostname, sup), ERROR_STR()); |
|
304 |
|
305 if (in) { |
|
306 /* Ensure that we have enough socket buffer space to send |
|
307 * complaints about the input flood. Normally little or |
|
308 * nothing is sent upstream, but bad clocks or other |
|
309 * problems can cause many complaints. */ |
|
310 if (0 > setsockopt(s, SOL_SOCKET, SO_SNDBUF, |
|
311 &srvr_rcvbuf, sizeof(srvr_rcvbuf))) |
|
312 rpt_err(ofp, 0, in, "setsockopt(%s, SO_SNDBUF): %s", |
|
313 rem_str(hostname, sup), ERROR_STR()); |
|
314 } |
|
315 |
|
316 #ifdef IP_TOS |
|
317 /* It would be nice and clean to use netinet/ip.h for the definition |
|
318 * of IPTOS_THROUGHPUT. However, it is hard to use netinet/ip.h |
|
319 * portably because in_sysm.h is required for n_long on some |
|
320 * systems and not others. A bunch of messy ./configure fiddling |
|
321 * might patch that hassle, but the bit really ought to be the same |
|
322 * as the old 0x08 in the IPv4 header. */ |
|
323 if (sup->sa.sa_family == AF_INET |
|
324 && tos_ok) { |
|
325 on = 0x08; /* IPTOS_THROUGHPUT */ |
|
326 if (0 > setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(on))) { |
|
327 rpt_err(ofp, 0, in, |
|
328 "setsockopt(IP_TOS, IPTOS_THROUGHPUT, %s): %s", |
|
329 rem_str(hostname, sup), ERROR_STR()); |
|
330 tos_ok = 0; |
|
331 } |
|
332 } |
|
333 #endif |
|
334 |
|
335 return 1; |
|
336 } |
|
337 |
|
338 |
|
339 |
|
340 /* see if the host name resolution process is still running */ |
|
341 u_char /* 1=not running 0=please wait */ |
|
342 flod_names_resolve_ck(void) |
|
343 { |
|
344 int status; |
|
345 |
|
346 if (resolve_hosts_pid < 0) |
|
347 return 1; |
|
348 |
|
349 if (resolve_hosts_pid == waitpid(resolve_hosts_pid, &status, WNOHANG)) { |
|
350 resolve_hosts_pid = -1; |
|
351 return 1; |
|
352 } |
|
353 |
|
354 RUSH_NEXT_FLODS_CK(); |
|
355 return 0; |
|
356 } |
|
357 |
|
358 |
|
359 |
|
360 static void |
|
361 flod_names_resolve(void) |
|
362 { |
|
363 FLOD_MMAP *mp; |
|
364 u_char ipv6, ok; |
|
365 const DCC_SOCKU *sup; |
|
366 |
|
367 for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp) { |
|
368 if (mp->rem_hostname[0] == '\0' |
|
369 || (mp->flags & FLODMAP_FG_PASSIVE)) |
|
370 continue; |
|
371 ipv6 = ((mp->flags & FLODMAP_FG_IPv4) ? 0 |
|
372 : (mp->flags & FLODMAP_FG_IPv6) ? 1 |
|
373 : use_ipv6 ? 2 : 0); |
|
374 dcc_host_lock(); |
|
375 if (mp->flags & FLODMAP_FG_SOCKS) |
|
376 ok = dcc_get_host_SOCKS(mp->rem_hostname, ipv6, |
|
377 &mp->host_error); |
|
378 else |
|
379 ok = dcc_get_host(mp->rem_hostname, ipv6, |
|
380 &mp->host_error); |
|
381 if (!ok) { |
|
382 TMSG2(FLOD, "failed to resolve %s: %s", |
|
383 mp->rem_hostname, DCC_HSTRERROR(mp->host_error)); |
|
384 } else { |
|
385 for (sup = dcc_hostaddrs; |
|
386 sup < dcc_hostaddrs_end; |
|
387 ++sup) { |
|
388 if ((ipv6 == 0 |
|
389 && sup->sa.sa_family != AF_INET) |
|
390 || (ipv6 == 1 |
|
391 && sup->sa.sa_family != AF_INET6)) |
|
392 continue; |
|
393 mp->rem_su = *sup; |
|
394 *DCC_SU_PORTP(&mp->rem_su) = mp->rem_port; |
|
395 } |
|
396 } |
|
397 dcc_host_unlock(); |
|
398 } |
|
399 } |
|
400 |
|
401 |
|
402 |
|
403 /* start a process to wait for the domain name system or other |
|
404 * hostname system to get the IP addresses of our flooding peers */ |
|
405 u_char /* 1=finished 0=please wait */ |
|
406 flod_names_resolve_start(void) |
|
407 { |
|
408 FLOD_MMAP *mp; |
|
409 |
|
410 if (!flod_mmaps) |
|
411 return 0; |
|
412 |
|
413 /* wait for background job to finish */ |
|
414 if (!flod_names_resolve_ck()) |
|
415 return 0; |
|
416 |
|
417 /* we're finished if we have recent address for all of the names */ |
|
418 if (!DB_IS_TIME(got_hosts, FLOD_NAMES_RESOLVE_SECS)) |
|
419 return 1; |
|
420 |
|
421 got_hosts = db_time.tv_sec + FLOD_NAMES_RESOLVE_SECS; |
|
422 for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp) { |
|
423 mp->rem_su.sa.sa_family = AF_UNSPEC; |
|
424 mp->host_error = 0; |
|
425 } |
|
426 flod_mmap_sync(0, 1); |
|
427 |
|
428 if (!background) { |
|
429 TMSG(FLOD, "resolving hostnames in the foreground"); |
|
430 flod_names_resolve(); |
|
431 return 1; |
|
432 } |
|
433 |
|
434 resolve_hosts_pid = fork(); |
|
435 if (resolve_hosts_pid > 0) { |
|
436 /* check again soon */ |
|
437 RUSH_NEXT_FLODS_CK(); |
|
438 return 0; |
|
439 } |
|
440 |
|
441 if (resolve_hosts_pid == -1) { |
|
442 dcc_error_msg("fork(flood names resolve start): %s;" |
|
443 " fall back to foreground resolving", |
|
444 ERROR_STR()); |
|
445 flod_names_resolve(); |
|
446 return 1; |
|
447 } |
|
448 |
|
449 TMSG(FLOD, "resolving hostnames started"); |
|
450 /* close files and sockets to avoid interfering with parent */ |
|
451 db_close(-1); |
|
452 after_fork(); |
|
453 |
|
454 flod_names_resolve(); |
|
455 |
|
456 TMSG(FLOD, "resolving hostnames finished"); |
|
457 |
|
458 exit(0); |
|
459 } |
|
460 |
|
461 |
|
462 |
|
463 static void |
|
464 iflod_clear(IFLOD_INFO *ifp, |
|
465 u_char fail) /* 1=problems so delay restart */ |
|
466 { |
|
467 OFLOD_INFO *ofp; |
|
468 FLOD_MMAP *mp; |
|
469 |
|
470 ofp = ifp->ofp; |
|
471 if (fail && ofp != 0) { |
|
472 mp = ofp->mp; |
|
473 if (mp->itimers.retry_secs < FLOD_RETRY_SECS) |
|
474 mp->itimers.retry_secs = FLOD_RETRY_SECS; |
|
475 mp->itimers.retry = db_time.tv_sec + mp->itimers.retry_secs; |
|
476 if ((mp->flags & FLODMAP_FG_ACT) != 0) |
|
477 TMSG3_FLOD(ofp, |
|
478 "postpone restarting %s flood from" |
|
479 " %s for %d seconds", |
|
480 (mp->flags & FLODMAP_FG_SOCKS) |
|
481 ? "SOCKS" |
|
482 : (mp->flags & FLODMAP_FG_NAT) |
|
483 ? "NAT" |
|
484 : "auto-NAT", |
|
485 ofp_rem_str(ofp), mp->itimers.retry_secs); |
|
486 } |
|
487 |
|
488 /* do not close the socket here, because it may be a passive outgoing |
|
489 * stream that is being converted */ |
|
490 if (ifp->soc >= 0) |
|
491 --iflods.open; |
|
492 |
|
493 memset(ifp, 0, sizeof(*ifp)); |
|
494 ifp->soc = -1; |
|
495 |
|
496 if (iflods.open == 0 |
|
497 && oflods.open == 0 |
|
498 && flods_st != FLODS_ST_ON) |
|
499 oflods_clear(); |
|
500 } |
|
501 |
|
502 |
|
503 |
|
504 void PATTRIB(5,6) |
|
505 iflod_close(IFLOD_INFO *ifp, |
|
506 u_char fail, /* 1=already sick; more is no problem */ |
|
507 u_char complain, /* 1=error not just trace message */ |
|
508 u_char send_reason, |
|
509 const char *pat, ...) |
|
510 { |
|
511 struct { |
|
512 DCC_FLOD_POS last_pos; |
|
513 DCC_FLOD_RESP e; |
|
514 u_char null; /* to eat '\0' for e.msg */ |
|
515 } resp; |
|
516 void *wp; |
|
517 va_list args; |
|
518 OFLOD_INFO *ofp; |
|
519 struct linger nowait; |
|
520 int wlen; |
|
521 |
|
522 ofp = ifp->ofp; |
|
523 |
|
524 db_ptr2flod_pos(resp.e.end.pos, DCC_FLOD_POS_END); |
|
525 va_start(args, pat); |
|
526 /* Throw away the last byte of resp.e.end.msg because the too smart |
|
527 * by half gcc Fortify nonsense won't allow ISZ(resp.e.end.msg)+1. |
|
528 * That put the would the unwanted '\0' from vsnprintf() into |
|
529 * resp.null*/ |
|
530 wlen = vsnprintf(resp.e.end.msg, ISZ(resp.e.end.msg), pat, args); |
|
531 va_end(args); |
|
532 if (wlen > ISZ(resp.e.end.msg)) |
|
533 wlen = ISZ(resp.e.end.msg); |
|
534 wlen += FLOD_END_OVHD; |
|
535 |
|
536 /* If useful, prefix our final message with our final position |
|
537 * The peer will see our position and final operation as two |
|
538 * separate responses. */ |
|
539 if (memcmp(ifp->pos, ifp->pos_sent, ISZ(ifp->pos))) { |
|
540 memcpy(resp.last_pos, ifp->pos, ISZ(resp.last_pos)); |
|
541 wlen += ISZ(resp.last_pos); |
|
542 wp = &resp.last_pos; |
|
543 } else { |
|
544 wp = &resp.e; |
|
545 } |
|
546 |
|
547 if (send_reason) { |
|
548 rpt_err(ofp, !complain, 1, |
|
549 "stop incoming flood; %ssend '%s' to %s", |
|
550 fail ? "error, " : "", |
|
551 resp.e.end.msg, |
|
552 ifp_rem_str(ifp)); |
|
553 |
|
554 /* send the final status report to the sending flooder */ |
|
555 iflod_write(ifp, wp, wlen, "stop incoming flood", fail ? 2 : 1); |
|
556 } else { |
|
557 rpt_err(ofp, !complain, 1, |
|
558 "stop incoming flood; %s'%s'", |
|
559 fail ? "error, " : "", resp.e.end.msg); |
|
560 } |
|
561 |
|
562 if (ifp->soc >= 0) { |
|
563 if (stopint |
|
564 && !(ifp->flags & IFLOD_FG_FAST_LINGER)) { |
|
565 ifp->flags |= IFLOD_FG_FAST_LINGER; |
|
566 nowait.l_onoff = 1; |
|
567 nowait.l_linger = SHUTDOWN_DELAY; |
|
568 if (0 > setsockopt(ifp->soc, SOL_SOCKET, SO_LINGER, |
|
569 &nowait, sizeof(nowait)) |
|
570 && !fail) |
|
571 dcc_error_msg("setsockopt(SO_LINGER %s): %s", |
|
572 ifp_rem_str(ifp), ERROR_STR()); |
|
573 } |
|
574 |
|
575 if (0 > close(ifp->soc) |
|
576 && !fail) { |
|
577 if (errno == ECONNRESET) |
|
578 TMSG2_FLOD(ofp, "close(flood from %s): %s", |
|
579 ifp_rem_str(ifp), ERROR_STR()); |
|
580 else |
|
581 dcc_error_msg("close(flood from %s): %s", |
|
582 ifp_rem_str(ifp), ERROR_STR()); |
|
583 } |
|
584 } |
|
585 |
|
586 /* if this was not a new duplicate connection being discarded, |
|
587 * break the association with the outgoing stream */ |
|
588 if (ofp != 0 && ofp->ifp == ifp) { |
|
589 save_flod_cnts(ofp); |
|
590 ofp->ifp = 0; |
|
591 } |
|
592 |
|
593 iflod_clear(ifp, fail); |
|
594 } |
|
595 |
|
596 |
|
597 |
|
598 /* can close the incoming flood and so clear things */ |
|
599 static u_char /* 0=failed & should be or is closed */ |
|
600 iflod_write(IFLOD_INFO *ifp, |
|
601 void *buf, int buf_len, |
|
602 const char *type, /* string describing operation */ |
|
603 u_char close_it) /* 0=iflod_close() on error, */ |
|
604 { /* 1=complain, 2=ignore error */ |
|
605 int i; |
|
606 |
|
607 if (!(ifp->flags & IFLOD_FG_CONNECTED)) |
|
608 return 1; |
|
609 |
|
610 if (ifp->ofp |
|
611 && (ifp->ofp->o_opts.flags & FLOD_OPT_SOCKS)) { |
|
612 i = Rsend(ifp->soc, buf, buf_len, 0); |
|
613 } else { |
|
614 /* If we don't know the corresponding output stream because we |
|
615 * have not yet seen any authentication, we at least know the |
|
616 * connection did not involve SOCKS because we did not |
|
617 * originate it. */ |
|
618 i = send(ifp->soc, buf, buf_len, 0); |
|
619 } |
|
620 if (i == buf_len) { |
|
621 ifp->iflod_alive = db_time.tv_sec; |
|
622 return 1; |
|
623 } |
|
624 |
|
625 if (i < 0) { |
|
626 if (close_it == 0) { |
|
627 iflod_close(ifp, 1, 0, 0, "send(%s %s): %s", |
|
628 type, ifp_rem_str(ifp), ERROR_STR()); |
|
629 } else if (close_it == 1) { |
|
630 dcc_error_msg("send(%s %s): %s", |
|
631 type, ifp_rem_str(ifp), ERROR_STR()); |
|
632 } |
|
633 } else { |
|
634 if (close_it == 0) { |
|
635 iflod_close(ifp, 1, 0, 0, "send(%s %s)=%d not %d", |
|
636 type, ifp_rem_str(ifp), i, buf_len); |
|
637 } else if (close_it == 1) { |
|
638 dcc_error_msg("send(%s %s)=%d not %d", |
|
639 type, ifp_rem_str(ifp), i, buf_len); |
|
640 } |
|
641 } |
|
642 return 0; |
|
643 } |
|
644 |
|
645 |
|
646 |
|
647 /* send our current position to the peer |
|
648 * the peer must be well known so that ifp->ofp->mp!=0, usually |
|
649 * because (ifp->flags & IFLOD_FG_VERS_CK) |
|
650 * can close the incoming flood and so clear things */ |
|
651 int /* -1=fail 0=nothing to send, 1=sent */ |
|
652 iflod_send_pos(IFLOD_INFO *ifp, |
|
653 u_char force) /* say just anything */ |
|
654 { |
|
655 DCC_FLOD_POS req; |
|
656 OFLOD_INFO *ofp; |
|
657 FLOD_MMAP *mp; |
|
658 |
|
659 ofp = ifp->ofp; |
|
660 mp = ofp->mp; |
|
661 |
|
662 /* ask peer to start over if our database has been cleared */ |
|
663 if (mp->flags & FLODMAP_FG_FFWD_IN) { |
|
664 mp->flags &= ~(FLODMAP_FG_FFWD_IN |
|
665 | FLODMAP_FG_NEED_REWIND); |
|
666 memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent)); |
|
667 db_ptr2flod_pos(req, DCC_FLOD_POS_FFWD_IN); |
|
668 dcc_trace_msg("ask %s to FFWD flood to us", |
|
669 ifp_rem_str(ifp)); |
|
670 if (!iflod_write(ifp, req, sizeof(req), "ffwd request", 0)) |
|
671 return -1; |
|
672 return 1; |
|
673 } |
|
674 if (mp->flags & FLODMAP_FG_NEED_REWIND) { |
|
675 mp->flags &= ~FLODMAP_FG_NEED_REWIND; |
|
676 memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent)); |
|
677 db_ptr2flod_pos(req, DCC_FLOD_POS_REWIND); |
|
678 dcc_trace_msg("ask %s to rewind flood to us", |
|
679 ifp_rem_str(ifp)); |
|
680 if (!iflod_write(ifp, req, sizeof(req), "rewind request", 0)) |
|
681 return -1; |
|
682 return 1; |
|
683 } |
|
684 |
|
685 if ((force && flod_pos2db_ptr(ifp->pos) >= DCC_FLOD_POS_MIN) |
|
686 || memcmp(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent))) { |
|
687 memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent)); |
|
688 if (!iflod_write(ifp, ifp->pos_sent, sizeof(ifp->pos_sent), |
|
689 "confirmed pos", 0)) |
|
690 return -1; |
|
691 |
|
692 /* reset the no-connection-from-peer complaint delay */ |
|
693 mp->itimers.msg_secs = FLOD_IN_COMPLAIN1; |
|
694 mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN1; |
|
695 |
|
696 /* things are going well, so forget old input complaints */ |
|
697 if (!(mp->flags & FLODMAP_FG_IN_SRVR)) { |
|
698 mp->iflod_err.msg[0] = '\0'; |
|
699 mp->iflod_err.trace_msg[0] = '\0'; |
|
700 } |
|
701 |
|
702 /* limit the backoff for outgoing connection attempts |
|
703 * while the incoming connection is working */ |
|
704 DB_ADJ_TIMER(&mp->otimers.retry, &mp->otimers.retry_secs, |
|
705 FLOD_RETRY_SECS); |
|
706 |
|
707 return 1; |
|
708 } |
|
709 |
|
710 /* Say just anything if we are doing a keepalive probe before |
|
711 * any checksums have been sent by the peer and so before we |
|
712 * have a position to confirm. */ |
|
713 if (force) { |
|
714 DCC_FLOD_RESP buf; |
|
715 |
|
716 db_ptr2flod_pos(buf.note.op, DCC_FLOD_POS_NOTE); |
|
717 strcpy(buf.note.str, "are you there?"); |
|
718 buf.note.len = sizeof("are you there?") + FLOD_NOTE_OVHD; |
|
719 TMSG1_FLOD(ofp, "flood note to %s: \"are you there?\"", |
|
720 ifp_rem_str(ifp)); |
|
721 if (!iflod_write(ifp, &buf.note, buf.note.len, buf.note.str, 0)) |
|
722 return -1; |
|
723 return 1; |
|
724 } |
|
725 |
|
726 return 0; |
|
727 } |
|
728 |
|
729 |
|
730 |
|
731 void |
|
732 iflod_listen_close(SRVR_SOC *sp) |
|
733 { |
|
734 if (sp->listen < 0) |
|
735 return; |
|
736 |
|
737 TMSG1(FLOD, "stop flood listening on %s", dcc_su2str_err(&sp->su)); |
|
738 if (0 > close(sp->listen)) |
|
739 TMSG2(FLOD, "close(flood listen on %s): %s", |
|
740 dcc_su2str_err(&sp->su), ERROR_STR()); |
|
741 sp->listen = -1; |
|
742 } |
|
743 |
|
744 |
|
745 |
|
746 /* send stop requests to DCC servers flooding to us |
|
747 * can close the incoming flood and so clear things */ |
|
748 void |
|
749 iflods_stop(const char *reason, |
|
750 u_char force) /* 1=now */ |
|
751 { |
|
752 SRVR_SOC *sp; |
|
753 IFLOD_INFO *ifp; |
|
754 DCC_FLOD_POS end_req; |
|
755 |
|
756 /* stop listening for new connections */ |
|
757 for (sp = srvr_socs; sp; sp = sp->fwd) { |
|
758 iflod_listen_close(sp); |
|
759 } |
|
760 |
|
761 for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) { |
|
762 if (ifp->soc < 0) |
|
763 continue; |
|
764 |
|
765 /* start shutting down each real, still alive connection */ |
|
766 if (!(ifp->flags & IFLOD_FG_END_REQ) |
|
767 && (ifp->flags & IFLOD_FG_VERS_CK)) { |
|
768 if (!reason || !*reason) |
|
769 rpt_err(ifp->ofp, 1, 1, |
|
770 "flood from %s stopping", |
|
771 ifp_rem_str(ifp)); |
|
772 else |
|
773 rpt_err(ifp->ofp, 1, 1, |
|
774 "flood from %s stopping: '%s'", |
|
775 ifp_rem_str(ifp), reason); |
|
776 |
|
777 /* send any delay position and then a stop request */ |
|
778 if (0 <= iflod_send_pos(ifp, 0)) { |
|
779 db_ptr2flod_pos(end_req, DCC_FLOD_POS_END_REQ); |
|
780 iflod_write(ifp, end_req, sizeof(end_req), |
|
781 "flood stop req", 0); |
|
782 ifp->flags |= IFLOD_FG_END_REQ; |
|
783 } |
|
784 |
|
785 /* done if the socket died */ |
|
786 if (ifp->soc < 0) |
|
787 continue; |
|
788 } |
|
789 |
|
790 /* break the connection if forced or never authenticated */ |
|
791 if (force || !(ifp->flags & IFLOD_FG_VERS_CK)) { |
|
792 if (!reason || !*reason) |
|
793 iflod_close(ifp, 1, 0, 1, |
|
794 "flooding off at %s", |
|
795 our_hostname); |
|
796 else |
|
797 iflod_close(ifp, 1, 0, 1, |
|
798 "flooding off at %s; %s", |
|
799 our_hostname, reason); |
|
800 continue; |
|
801 } |
|
802 |
|
803 /* break the conneciton if the peer is too slow */ |
|
804 if ((ifp->flags & IFLOD_FG_END_REQ) |
|
805 && IFP_DEAD(ifp, stopint |
|
806 ? SHUTDOWN_DELAY : KEEPALIVE_IN_STOP)) { |
|
807 if (!reason || !*reason) |
|
808 iflod_close(ifp, 1, 0, 1, |
|
809 DCC_FLOD_OK_STR" force close from" |
|
810 " %s", |
|
811 ifp_rem_str(ifp)); |
|
812 else |
|
813 iflod_close(ifp, 1, 0, 1, |
|
814 DCC_FLOD_OK_STR" force close from" |
|
815 " %s; %s", |
|
816 ifp_rem_str(ifp), reason); |
|
817 continue; |
|
818 } |
|
819 } |
|
820 } |
|
821 |
|
822 |
|
823 |
|
824 /* start receiving checksums from another DCC server */ |
|
825 void |
|
826 iflod_start(SRVR_SOC *sp) |
|
827 { |
|
828 IFLOD_INFO *ifp; |
|
829 DCC_SOCKLEN_T l; |
|
830 struct in6_addr peer_addr; |
|
831 int count; |
|
832 const struct in6_addr *cap; |
|
833 RL *rl; |
|
834 |
|
835 /* accept all waiting connections to avoid starving any */ |
|
836 for (count = 0; count <= DCCD_MAX_FLOODS; ++count) { |
|
837 /* find a free input flooding slot */ |
|
838 for (ifp = iflods.infos; ifp->soc >= 0; ++ifp) { |
|
839 if (ifp > LAST(iflods.infos)) { |
|
840 if (!(complained_many_iflods++)) |
|
841 dcc_error_msg("too many floods"); |
|
842 goto again; |
|
843 } |
|
844 } |
|
845 |
|
846 l = sizeof(ifp->rem_su); |
|
847 ifp->soc = accept(sp->listen, &ifp->rem_su.sa, &l); |
|
848 if (ifp->soc < 0) { |
|
849 if (!DCC_BLOCK_ERROR() || count == 0) |
|
850 dcc_error_msg("accept(flood): %s", ERROR_STR()); |
|
851 return; |
|
852 } |
|
853 |
|
854 /* use the IP address as the host name until we know which |
|
855 * peer it is */ |
|
856 dcc_su2str2(ifp->rem_hostname, sizeof(ifp->rem_hostname), |
|
857 &ifp->rem_su); |
|
858 if (!set_flod_socket(0, 1, ifp->soc, ifp->rem_hostname, |
|
859 &ifp->rem_su)) { |
|
860 close(ifp->soc); |
|
861 ifp->soc = -1; |
|
862 continue; |
|
863 } |
|
864 |
|
865 /* quietly forget this peer if it is blacklisted */ |
|
866 if (ifp->rem_su.sa.sa_family == AF_INET6) { |
|
867 cap = &ifp->rem_su.ipv6.sin6_addr; |
|
868 } else { |
|
869 dcc_ipv4toipv6(&peer_addr, ifp->rem_su.ipv4.sin_addr); |
|
870 cap = &peer_addr; |
|
871 } |
|
872 rl = 0; |
|
873 if (ck_ip_bl(&rl, DCC_ID_SRVR_ROGUE, cap)) { |
|
874 char buf[120]; |
|
875 int len; |
|
876 |
|
877 rl_inc(rl, &rl_anon_rate); |
|
878 len = snprintf(buf, sizeof(buf)-1, |
|
879 "rejected blacklisted flood from %s", |
|
880 ifp_rem_str(ifp)); |
|
881 if (len > ISZ(buf)) |
|
882 len = sizeof(buf); |
|
883 if ((rl->d.flags & RL_FG_TRACE) |
|
884 || (dccd_tracemask & DCC_TRACE_FLOD_BIT)) |
|
885 dcc_trace_msg(buf); |
|
886 buf[len++] = '\n'; |
|
887 send(ifp->soc, buf, len, 0); |
|
888 close(ifp->soc); |
|
889 ifp->soc = -1; |
|
890 continue; |
|
891 } |
|
892 |
|
893 /* reset timer that delays responses to clients while flooding |
|
894 * is stopped */ |
|
895 if (++iflods.open == 1) |
|
896 iflods_ok_timer = db_time.tv_sec + IFLODS_OK_SECS; |
|
897 |
|
898 ifp->flags |= IFLOD_FG_CONNECTED; |
|
899 ifp->iflod_alive = db_time.tv_sec; |
|
900 |
|
901 TMSG1(FLOD, "start flood from %s", ifp_rem_str(ifp)); |
|
902 again:; |
|
903 } |
|
904 } |
|
905 |
|
906 |
|
907 |
|
908 static void |
|
909 iflod_socks_backoff(OFLOD_INFO *ofp) |
|
910 { |
|
911 FLOD_MMAP *mp; |
|
912 |
|
913 mp = ofp->mp; |
|
914 mp->itimers.retry_secs *= 2; |
|
915 if (mp->itimers.retry_secs > FLOD_MAX_RETRY_SECS) |
|
916 mp->itimers.retry_secs = FLOD_MAX_RETRY_SECS; |
|
917 else if (mp->itimers.retry_secs < FLOD_RETRY_SECS) |
|
918 mp->itimers.retry_secs = FLOD_RETRY_SECS; |
|
919 } |
|
920 |
|
921 |
|
922 |
|
923 /* Start an incoming SOCKS flood by connecting to the other system. |
|
924 * We will eventually turn the connection around and pretend that |
|
925 * the other system initiated the TCP connection. |
|
926 * This can close the flood and so clear things */ |
|
927 static int /* -1=failure, 0=not yet, 1=done */ |
|
928 iflod_socks_connect(IFLOD_INFO *ifp) |
|
929 { |
|
930 OFLOD_INFO *ofp; |
|
931 DCC_SRVR_ID id; |
|
932 DCC_FLOD_VERSION_HDR buf; |
|
933 DCC_FNM_LNO_BUF fnm_buf; |
|
934 const char *emsg; |
|
935 int i; |
|
936 |
|
937 ofp = ifp->ofp; |
|
938 |
|
939 memset(&buf, 0, sizeof(buf)); |
|
940 strcpy(buf.body.str, version_str(ofp)); |
|
941 id = htons(my_srvr_id); |
|
942 memcpy(buf.body.sender_srvr_id, &id, sizeof(buf.body.sender_srvr_id)); |
|
943 buf.body.turn = 1; |
|
944 emsg = flod_sign(ofp, 1, &buf, sizeof(buf)); |
|
945 if (emsg) { |
|
946 iflod_socks_backoff(ofp); |
|
947 iflod_close(ifp, 1, 1, 1, "%s %d%s", |
|
948 emsg, ofp->out_passwd_id, |
|
949 fnm_lno(&fnm_buf, flod_path, ofp->lno)); |
|
950 return -1; |
|
951 } |
|
952 |
|
953 if (ofp->o_opts.flags & FLOD_OPT_SOCKS) { |
|
954 i = Rconnect(ifp->soc, &ifp->rem_su.sa, |
|
955 DCC_SU_LEN(&ifp->rem_su)); |
|
956 } else { |
|
957 /* must be NAT or assumed NAT */ |
|
958 i = connect(ifp->soc, &ifp->rem_su.sa, |
|
959 DCC_SU_LEN(&ifp->rem_su)); |
|
960 } |
|
961 if (0 > i && errno != EISCONN) { |
|
962 if (errno == EAGAIN |
|
963 || errno == EINPROGRESS |
|
964 || errno == EALREADY) { |
|
965 rpt_err(ofp, 1, 0, "starting flood from %s", |
|
966 ifp_rem_str(ifp)); |
|
967 return 0; |
|
968 } |
|
969 |
|
970 /* it is lame to only trace instead of reporting EINVAL as |
|
971 * an error, but several UNIX-like systems return EINVAL for |
|
972 * the second connect() after a Unreachable ICMP message |
|
973 * or after a timeout */ |
|
974 rpt_err(ofp, |
|
975 (errno == EINVAL || errno == ECONNABORTED |
|
976 || errno == ECONNRESET || errno == ETIMEDOUT |
|
977 || errno == ECONNREFUSED), |
|
978 1, "connect(SOCKS FLOD %s): %s", |
|
979 ifp_rem_str(ifp), |
|
980 errno == EINVAL |
|
981 ? "likely connection refused or local firewall" |
|
982 : ERROR_STR()); |
|
983 close(ifp->soc); |
|
984 iflod_socks_backoff(ofp); |
|
985 iflod_clear(ifp, 1); |
|
986 |
|
987 return -1; |
|
988 } |
|
989 |
|
990 ifp->flags |= (IFLOD_FG_CONNECTED | IFLOD_FG_CLIENT); |
|
991 |
|
992 rpt_err(ofp, 1, 1, "starting SOCKS from %s", ifp_rem_str(ifp)); |
|
993 |
|
994 /* After the SOCKS incoming flood socket is connected, |
|
995 * send our authentication to convince the peer to send its |
|
996 * authentication and then its checksums. */ |
|
997 if (!iflod_write(ifp, &buf, sizeof(buf), |
|
998 "flood SOCKS authentication", 0)) |
|
999 return -1; |
|
1000 return 1; |
|
1001 } |
|
1002 |
|
1003 |
|
1004 |
|
1005 /* Request the start of an input flood via SOCKS if it is not already flowing, |
|
1006 * by connecting to the remote system. |
|
1007 * This can close the flood and so clear things */ |
|
1008 void |
|
1009 iflod_socks_start(OFLOD_INFO *ofp) |
|
1010 { |
|
1011 DCC_FNM_LNO_BUF fnm_buf; |
|
1012 IFLOD_INFO *ifp, *ifp1; |
|
1013 |
|
1014 /* do nothing if it is already running or is not using SOCKS */ |
|
1015 if (ofp->ifp |
|
1016 || (ofp->mp->flags & (FLODMAP_FG_SOCKS | FLODMAP_FG_NAT)) == 0 |
|
1017 || IFLOD_OPT_OFF_ROGUE(ofp)) |
|
1018 return; |
|
1019 if (!DB_IS_TIME(ofp->mp->itimers.retry, ofp->mp->itimers.retry_secs)) |
|
1020 return; |
|
1021 |
|
1022 /* look for a free slot or an existing slot for the incoming flood */ |
|
1023 ifp = 0; |
|
1024 for (ifp1 = iflods.infos; ifp1 <= LAST(iflods.infos); ++ifp1) { |
|
1025 if (ifp1->soc < 0) { |
|
1026 if (!ifp) |
|
1027 ifp = ifp1; |
|
1028 } |
|
1029 /* there is nothing to do if it already exists */ |
|
1030 if (ifp1->ofp == ofp) |
|
1031 return; |
|
1032 } |
|
1033 if (!ifp) { |
|
1034 rpt_err(ofp, ++complained_many_iflods == 1 ? 0 : 2, 1, |
|
1035 "too many incoming floods to start SOCKS from %s", |
|
1036 ofp->rem_hostname); |
|
1037 iflod_socks_backoff(ofp); |
|
1038 return; |
|
1039 } |
|
1040 |
|
1041 if (!flod_names_resolve_start()) |
|
1042 return; /* wait for name resolution */ |
|
1043 if (ofp->mp->rem_su.sa.sa_family == AF_UNSPEC) { |
|
1044 rpt_err(ofp, 0, 1, "SOCKS flood peer name %s: %s%s", |
|
1045 ofp->rem_hostname, DCC_HSTRERROR(ofp->mp->host_error), |
|
1046 fnm_lno(&fnm_buf, flod_path, ofp->lno)); |
|
1047 iflod_socks_backoff(ofp); |
|
1048 return; |
|
1049 } |
|
1050 if (!LITCMP(ofp->mp->oflod_err.msg, "SOCKS flood peer name ")) |
|
1051 ofp->mp->oflod_err.msg[0] = '\0'; |
|
1052 |
|
1053 ifp->ofp = ofp; |
|
1054 ifp->rem_su = ofp->rem_su = ofp->mp->rem_su; |
|
1055 STRLCPY(ifp->rem_hostname, ofp->rem_hostname, |
|
1056 sizeof(ifp->rem_hostname)); |
|
1057 |
|
1058 ifp->soc = socket(ifp->rem_su.sa.sa_family, SOCK_STREAM, 0); |
|
1059 if (ifp->soc < 0) { |
|
1060 rpt_err(ofp, 0, 1, "socket(SOCKS flood %s): %s", |
|
1061 ifp_rem_str(ifp), ERROR_STR()); |
|
1062 iflod_socks_backoff(ofp); |
|
1063 return; |
|
1064 } |
|
1065 |
|
1066 if (!set_flod_socket(ofp, 1, ifp->soc, |
|
1067 ifp->rem_hostname, &ifp->rem_su)) { |
|
1068 close(ifp->soc); |
|
1069 ifp->soc = -1; |
|
1070 iflod_socks_backoff(ofp); |
|
1071 return; |
|
1072 } |
|
1073 |
|
1074 /* reset timer that delays responses to clients while flooding is |
|
1075 * not working */ |
|
1076 if (++iflods.open == 1) |
|
1077 iflods_ok_timer = db_time.tv_sec + IFLODS_OK_SECS; |
|
1078 |
|
1079 iflod_socks_connect(ifp); |
|
1080 } |
|
1081 |
|
1082 |
|
1083 |
|
1084 /* See if the new report is a duplicate of an old record in the database |
|
1085 * db_sts.rcd.d points to the old record */ |
|
1086 static int /* -1=continue, 0=not dup, 1=dup */ |
|
1087 ck_dup_rcd(IFLOD_INFO *ifp, |
|
1088 const DB_RCD *new, DCC_TGTS new_tgts_raw) |
|
1089 { |
|
1090 DCC_TGTS old_tgts; |
|
1091 const DB_RCD_CK *new_ck, *old_ck; |
|
1092 int new_num_cks, old_num_cks; |
|
1093 int new_unique, old_unique; |
|
1094 |
|
1095 /* ignore reports for deleted checksums |
|
1096 * unless they are new reports or delete requests */ |
|
1097 old_tgts = DB_TGTS_RCD_RAW(db_sts.rcd.d.r); |
|
1098 if (old_tgts == DCC_TGTS_DEL |
|
1099 && new_tgts_raw != DCC_TGTS_DEL |
|
1100 && !dcc_ts_newer_ts(&new->ts, &db_sts.rcd.d.r->ts)) { |
|
1101 if (CK_FLOD_CNTERR(&ifp->ofp->lc.stale) |
|
1102 && TMSG_FB2(ifp->ofp)) |
|
1103 flod_cnterr(&ifp->ofp->lc.stale, |
|
1104 "ignore deleted %s after %s", |
|
1105 rpt_id("report", new, ifp), |
|
1106 rpt_id(0, db_sts.rcd.d.r, 0)); |
|
1107 return 1; |
|
1108 } |
|
1109 |
|
1110 /* not duplicate if the server-IDs or timestamps differ */ |
|
1111 if (DB_RCD_ID(new) != DB_RCD_ID(db_sts.rcd.d.r) |
|
1112 || memcmp(&new->ts, &db_sts.rcd.d.r->ts, sizeof(new->ts))) |
|
1113 return -1; |
|
1114 |
|
1115 /* We know we have a duplicate |
|
1116 * Stop looking if the old record has been deleted */ |
|
1117 if (old_tgts == 0) |
|
1118 return 0; |
|
1119 |
|
1120 /* look for the first real checksum in the new record */ |
|
1121 for (new_num_cks = DB_NUM_CKS(new), new_ck = new->cks; |
|
1122 new_num_cks != 0; |
|
1123 --new_num_cks, ++new_ck) { |
|
1124 if (DB_CK_TYPE(new_ck) != DCC_CK_FLOD_PATH) |
|
1125 break; |
|
1126 } |
|
1127 |
|
1128 /* do not even count duplicate server-ID declarations */ |
|
1129 if (DB_CK_TYPE(new_ck) == DCC_CK_SRVR_ID) |
|
1130 return 1; |
|
1131 |
|
1132 /* See if one record is a subset of the other */ |
|
1133 new_unique = 0; |
|
1134 old_unique = 0; |
|
1135 old_num_cks = DB_NUM_CKS(db_sts.rcd.d.r); |
|
1136 old_ck = db_sts.rcd.d.r->cks; |
|
1137 while (old_num_cks != 0 && new_num_cks != 0) { |
|
1138 if (DB_CK_TYPE(old_ck) == DCC_CK_FLOD_PATH) { |
|
1139 /* skip paths in the old record */ |
|
1140 ++old_ck; |
|
1141 --old_num_cks; |
|
1142 } else if (DB_CK_TYPE(old_ck) == DB_CK_TYPE(new_ck)) { |
|
1143 /* skip identical checksums */ |
|
1144 ++old_ck; |
|
1145 --old_num_cks; |
|
1146 ++new_ck; |
|
1147 --new_num_cks; |
|
1148 } else if (DB_CK_TYPE(old_ck) < DB_CK_TYPE(new_ck)) { |
|
1149 /* skip unique checksum in the old record, |
|
1150 * using the ordering of checksums in records */ |
|
1151 ++old_unique; |
|
1152 ++old_ck; |
|
1153 --old_num_cks; |
|
1154 } else { |
|
1155 /* skip unique checksum in the new record */ |
|
1156 ++new_unique; |
|
1157 ++new_ck; |
|
1158 --new_num_cks; |
|
1159 } |
|
1160 } |
|
1161 |
|
1162 /* forget the new record if it has nothing unique */ |
|
1163 if (new_unique+new_num_cks == 0) { |
|
1164 if (CK_FLOD_CNTERR(&ifp->ofp->lc.dup) |
|
1165 && TMSG_FB2(ifp->ofp)) |
|
1166 flod_cnterr(&ifp->ofp->lc.dup, "%sduplicate %s", |
|
1167 old_unique+old_num_cks == 0 |
|
1168 ? "" : "subset ", |
|
1169 rpt_id("report", new, ifp)); |
|
1170 |
|
1171 return 1; |
|
1172 } |
|
1173 |
|
1174 /* Keep both records if each has unique checksums |
|
1175 * This inflates the count for common checksums, so hope it |
|
1176 * is rare. */ |
|
1177 if (old_unique+old_num_cks != 0) { |
|
1178 if (CK_FLOD_CNTERR(&ifp->ofp->lc.dup) |
|
1179 && TMSG_FB2(ifp->ofp)) |
|
1180 flod_cnterr(&ifp->ofp->lc.dup, "partial duplicate %s", |
|
1181 rpt_id("report", new, ifp)); |
|
1182 |
|
1183 return 0; |
|
1184 } |
|
1185 |
|
1186 /* Delete the original report if it has nothing unique |
|
1187 * This results in doubling the contribution to the total for |
|
1188 * each checksum from this report in our database until the |
|
1189 * next time dbclean is run. At worst this could push a |
|
1190 * DCC client over its bulk threshold */ |
|
1191 DB_TGTS_RCD_SET(db_sts.rcd.d.r, 0); |
|
1192 SET_FLUSH_RCD_HDR(&db_sts.rcd, 1); |
|
1193 |
|
1194 if (CK_FLOD_CNTERR(&ifp->ofp->lc.dup) |
|
1195 && TMSG_FB2(ifp->ofp)) |
|
1196 flod_cnterr(&ifp->ofp->lc.dup, "superset duplicate %s", |
|
1197 rpt_id("report", new, ifp)); |
|
1198 return 0; |
|
1199 } |
|
1200 |
|
1201 |
|
1202 |
|
1203 /* see if the new record is a duplicate of any existing records containing |
|
1204 * the specified checksum |
|
1205 * use db_sts.rcd for the initial record and the chain */ |
|
1206 static int /* -1=fail, 0=not dup, 1=duplicate */ |
|
1207 ck_dup_ck_chain(IFLOD_INFO *ifp, const DB_RCD *new, DCC_TGTS new_tgts_raw, |
|
1208 DCC_CK_TYPES type, const DB_RCD_CK *found_ck) |
|
1209 { |
|
1210 DB_PTR next_rcd_pos; |
|
1211 int cnt, i; |
|
1212 |
|
1213 for (cnt = 0; ; ++cnt) { |
|
1214 i = ck_dup_rcd(ifp, new, new_tgts_raw); |
|
1215 if (i >= 0) { |
|
1216 if (cnt >= 50 && (dccd_tracemask & DCC_TRACE_DB_BIT)) |
|
1217 dcc_trace_msg("long duplicate chain of %d" |
|
1218 " from %s at %s", |
|
1219 cnt, ifp->rem_hostname, |
|
1220 rpt_id("report",new,ifp)); |
|
1221 return i; |
|
1222 } |
|
1223 |
|
1224 next_rcd_pos = DB_PTR_EX(found_ck->prev); |
|
1225 if (next_rcd_pos == DB_PTR_NULL) |
|
1226 return 0; |
|
1227 if (next_rcd_pos >= db_sts.rcd.s.rptr) { /* prevent loops */ |
|
1228 db_error_msg(__LINE__,__FILE__, |
|
1229 "bad %s link of "L_HPAT" at "L_HPAT, |
|
1230 DB_TYPE2STR(type), |
|
1231 db_sts.rcd.s.rptr, next_rcd_pos); |
|
1232 return -1; |
|
1233 } |
|
1234 |
|
1235 found_ck = db_map_rcd_ck(dcc_emsg, &db_sts.rcd, |
|
1236 next_rcd_pos, type); |
|
1237 if (!found_ck) { |
|
1238 iflod_close(ifp, 1, 1, 1, "%s", dcc_emsg); |
|
1239 DB_ERROR_MSG(dcc_emsg); |
|
1240 return -1; |
|
1241 } |
|
1242 } |
|
1243 } |
|
1244 |
|
1245 |
|
1246 |
|
1247 /* complain about a received flooded report, |
|
1248 * and possibly close and so clear things */ |
|
1249 static u_char PATTRIB(6,7) /* 0=flood closed */ |
|
1250 iflod_rpt_complain(IFLOD_INFO *ifp, |
|
1251 const DB_RCD *new, /* complain about this report */ |
|
1252 u_char serious, /* 0=send mere note, 1=send complaint */ |
|
1253 FLOD_LIMCNT *lc, /* limit complaints with this */ |
|
1254 const char *str, /* type of report */ |
|
1255 const char *pat,...) /* the complaint */ |
|
1256 { |
|
1257 DCC_FLOD_RESP buf; |
|
1258 const char *sc; |
|
1259 va_list args; |
|
1260 int i, len; |
|
1261 |
|
1262 if (!lc && ifp->ofp) |
|
1263 lc = &ifp->ofp->lc.iflod_bad; |
|
1264 if (!lc) { |
|
1265 sc = ""; |
|
1266 } else { |
|
1267 i = ++lc->cur - (lc->lim + FLOD_LIM_COMPLAINTS); |
|
1268 if (i > 0) |
|
1269 return 1; |
|
1270 sc = i < 0 ? "" : "; stop complaints"; |
|
1271 } |
|
1272 |
|
1273 va_start(args, pat); |
|
1274 len = vsnprintf(buf.note.str, sizeof(buf.note.str), pat, args); |
|
1275 if (len >= ISZ(buf.note.str)) |
|
1276 len = sizeof(buf.note.str)-1; |
|
1277 va_end(args); |
|
1278 |
|
1279 if (serious) { |
|
1280 dcc_error_msg("%s %s%s", |
|
1281 buf.note.str, rpt_id(str, new, ifp), sc); |
|
1282 db_ptr2flod_pos(buf.note.op, DCC_FLOD_POS_COMPLAINT); |
|
1283 } else { |
|
1284 TMSG3_FLOD2(ifp->ofp, "%s %s%s", |
|
1285 buf.note.str, rpt_id(str, new, ifp), sc); |
|
1286 db_ptr2flod_pos(buf.note.op, DCC_FLOD_POS_NOTE); |
|
1287 } |
|
1288 |
|
1289 len += snprintf(&buf.note.str[len], sizeof(buf.note.str)-len, " %s%s", |
|
1290 rpt_id(str, new, 0), sc); |
|
1291 if (len >= ISZ(buf.note.str)) |
|
1292 len = ISZ(buf.note.str)-1; |
|
1293 |
|
1294 buf.note.len = len+1 + FLOD_NOTE_OVHD; |
|
1295 return iflod_write(ifp, &buf, buf.note.len, buf.note.str, 0); |
|
1296 } |
|
1297 |
|
1298 |
|
1299 |
|
1300 static u_char |
|
1301 parse_srvr_id(const DB_RCD_CK *ck, const IFLOD_INFO *ifp, |
|
1302 const DB_RCD *new, DCC_SRVR_ID srvr_id) |
|
1303 { |
|
1304 DCC_SRVR_ID tgt_id, type_id; |
|
1305 char buf1[24]; |
|
1306 OPT_FLAGS opt_flags; |
|
1307 const OFLOD_INFO *ofp1; |
|
1308 ID_TBL *tp; |
|
1309 |
|
1310 /* notice server-ID type announcements */ |
|
1311 tgt_id = (ck->sum[1] << 8) + ck->sum[2]; |
|
1312 type_id = srvr_id; |
|
1313 switch (type_id) { |
|
1314 case DCC_ID_SRVR_REP_OK: |
|
1315 type_id = DCC_ID_SRVR_SIMPLE; |
|
1316 opt_flags = 0; |
|
1317 break; |
|
1318 case DCC_ID_SRVR_SIMPLE: |
|
1319 opt_flags = FLOD_OPT_SIMPLE; |
|
1320 break; |
|
1321 case DCC_ID_SRVR_IGNORE: |
|
1322 opt_flags = 0; |
|
1323 break; |
|
1324 case DCC_ID_SRVR_ROGUE: |
|
1325 opt_flags = FLOD_OPT_ROGUE; |
|
1326 break; |
|
1327 default: |
|
1328 return 0; |
|
1329 } |
|
1330 if (ck->sum[0] != DCC_CK_SRVR_ID) { |
|
1331 return 0; |
|
1332 } |
|
1333 |
|
1334 tp = find_srvr_type(tgt_id); |
|
1335 /* Restart flooding if the announced type of a peer changes. */ |
|
1336 for (ofp1 = oflods.infos; ofp1 <= LAST(oflods.infos); ++ofp1) { |
|
1337 /* Wait a bit for more announcements before restarting |
|
1338 * flooding. When we restart flooding, we will |
|
1339 * check the database. */ |
|
1340 if (ofp1->rem_id == tgt_id) { |
|
1341 if (opt_flags != (ofp1->o_opts.flags |
|
1342 & (FLOD_OPT_ROGUE |
|
1343 | FLOD_OPT_SIMPLE))) { |
|
1344 if (TMSG_FB(ifp->ofp) || TMSG_FB(ofp1)) |
|
1345 dcc_trace_msg("server-ID %d for %s" |
|
1346 " changed to \"%s\"" |
|
1347 " in %s", |
|
1348 tgt_id, |
|
1349 ofp1->rem_hostname, |
|
1350 id2str(buf1, sizeof(buf1), |
|
1351 type_id), |
|
1352 rpt_id("report",new,ifp)); |
|
1353 if (flod_mtime > 1) |
|
1354 flod_mtime = 1; |
|
1355 } |
|
1356 break; |
|
1357 } |
|
1358 } |
|
1359 |
|
1360 /* accept changes to our records */ |
|
1361 tp->srvr_type = type_id; |
|
1362 |
|
1363 return 1; |
|
1364 } |
|
1365 |
|
1366 |
|
1367 |
|
1368 /* consider an incoming flooded report */ |
|
1369 static int /* -1=failed, 0=not yet, else length */ |
|
1370 iflod_rpt(IFLOD_INFO *ifp, OFLOD_INFO *ofp, |
|
1371 const DCC_FLOD_STREAM *stream, int max_len) |
|
1372 { |
|
1373 DB_PTR pos; |
|
1374 DCC_TGTS new_tgts, found_tgts; |
|
1375 DB_RCD new; |
|
1376 DCC_SRVR_ID old_srvr, psrvr; |
|
1377 const DCC_CK *ck_lim, *ck; |
|
1378 const DB_RCD_CK *new_ck_lim, *srvr_id_ck; |
|
1379 DB_RCD_CK *found_ck, *new_ck; |
|
1380 DCC_CK_TYPES type, prev_type; |
|
1381 DCC_FLOD_PATH_ID *new_path_id, *old_path_id; |
|
1382 int num_path_blocks; |
|
1383 char tgts_buf[DCC_XHDR_MAX_TGTS_LEN]; |
|
1384 int ok2; |
|
1385 int rpt_len; |
|
1386 u_char stale; |
|
1387 ID_MAP_RESULT srvr_mapped; |
|
1388 ID_TBL *tp; |
|
1389 int i; |
|
1390 |
|
1391 pos = flod_pos2db_ptr(stream->r.pos); |
|
1392 if (pos < DCC_FLOD_POS_MIN) { |
|
1393 iflod_close(ifp, 1, 1, 1, |
|
1394 "bogus position "L_HPAT" in flooded report #%d", |
|
1395 pos, ofp->cnts.total); |
|
1396 return -1; |
|
1397 } |
|
1398 |
|
1399 /* wait for the header of the report */ |
|
1400 if (max_len < DCC_FLOD_RPT_LEN(0)) { |
|
1401 return 0; |
|
1402 } |
|
1403 |
|
1404 if (stream->r.num_cks == 0 || stream->r.num_cks > DCC_QUERY_MAX) { |
|
1405 iflod_close(ifp, 1, 1, 1, |
|
1406 "impossible %d checksums in report #%d", |
|
1407 stream->r.num_cks, ofp->cnts.total); |
|
1408 return -1; |
|
1409 } |
|
1410 rpt_len = DCC_FLOD_RPT_LEN(stream->r.num_cks); |
|
1411 if (rpt_len > max_len) |
|
1412 return 0; /* wait for more */ |
|
1413 |
|
1414 if (db_failed_line) |
|
1415 return rpt_len; /* can do nothing if database broken */ |
|
1416 |
|
1417 /* save the position to return to the sender */ |
|
1418 memcpy(ifp->pos, stream->r.pos, sizeof(ifp->pos)); |
|
1419 |
|
1420 new.ts = stream->r.ts; |
|
1421 memcpy(&new.srvr_id_auth, stream->r.srvr_id_auth, |
|
1422 sizeof(new.srvr_id_auth)); |
|
1423 old_srvr = ntohs(new.srvr_id_auth) & ~DCC_SRVR_ID_AUTH; |
|
1424 new.srvr_id_auth = old_srvr; |
|
1425 new.fgs_num_cks = 0; |
|
1426 |
|
1427 memcpy(&new_tgts, stream->r.tgts, sizeof(new_tgts)); |
|
1428 new_tgts = ntohl(new_tgts); |
|
1429 if (new_tgts == DCC_TGTS_DEL) { |
|
1430 if (!(ofp->i_opts.flags & FLOD_OPT_DEL_OK)) { |
|
1431 if (!iflod_rpt_complain(ifp, &new, 1, |
|
1432 &ofp->lc.not_deleted, |
|
1433 "delete request", "refuse")) |
|
1434 return -1; |
|
1435 return rpt_len; |
|
1436 } |
|
1437 } else if (new_tgts == 0 |
|
1438 || (new_tgts > DCC_TGTS_FLOD_RPT_MAX |
|
1439 && new_tgts != DCC_TGTS_TOO_MANY)) { |
|
1440 iflod_close(ifp, 1, 1, 1, "bogus target count %s in %s", |
|
1441 dcc_tgts2str(tgts_buf, sizeof(tgts_buf), |
|
1442 new_tgts, grey_on), |
|
1443 rpt_id("report", &new, 0)); |
|
1444 return -1; |
|
1445 } else if (ofp->i_opts.flags & FLOD_OPT_TRAPS) { |
|
1446 /* comply if the source watches only spam traps */ |
|
1447 new_tgts = DCC_TGTS_TOO_MANY; |
|
1448 } |
|
1449 |
|
1450 /* notice reports from the distant future */ |
|
1451 if (dcc_ts_newer_ts(&new.ts, &future)) { |
|
1452 if (!iflod_rpt_complain(ifp, &new, 1, &ofp->lc.stale, |
|
1453 "report", "future")) |
|
1454 return -1; |
|
1455 return rpt_len; |
|
1456 } |
|
1457 |
|
1458 DB_TGTS_RCD_SET(&new, new_tgts); |
|
1459 new.fgs_num_cks = 0; |
|
1460 srvr_id_ck = 0; |
|
1461 stale = 1; |
|
1462 ck_lim = &stream->r.cks[stream->r.num_cks]; |
|
1463 new_ck = new.cks; |
|
1464 num_path_blocks = 0; |
|
1465 |
|
1466 tp = 0; |
|
1467 srvr_mapped = id_map(old_srvr, &ofp->i_opts); |
|
1468 switch (srvr_mapped) { |
|
1469 case ID_MAP_NO: |
|
1470 tp = find_srvr_type(old_srvr); |
|
1471 break; |
|
1472 case ID_MAP_REJ: |
|
1473 if (!iflod_rpt_complain(ifp, &new, 0, 0, |
|
1474 "rejected server-ID in", "refuse")) |
|
1475 return -1; |
|
1476 return rpt_len; |
|
1477 case ID_MAP_SELF: |
|
1478 new.srvr_id_auth = my_srvr_id; |
|
1479 /* create path pointing to ourself if we translate the ID */ |
|
1480 memset(new_ck, 0, sizeof(*new_ck)); |
|
1481 new_ck->type_fgs = DCC_CK_FLOD_PATH; |
|
1482 new_path_id = (DCC_FLOD_PATH_ID *)new_ck->sum; |
|
1483 /* start the path with the ID of the previous hop because |
|
1484 * we know it is defined */ |
|
1485 new_path_id->hi = ofp->rem_id>>8; |
|
1486 new_path_id->lo = ofp->rem_id; |
|
1487 new.fgs_num_cks = 1; |
|
1488 ++new_ck; |
|
1489 break; |
|
1490 } |
|
1491 |
|
1492 for (prev_type = DCC_CK_INVALID, ck = stream->r.cks; |
|
1493 ck < ck_lim; |
|
1494 prev_type = type, ++ck) { |
|
1495 type = ck->type; |
|
1496 if (!DCC_CK_OK_FLOD(grey_on, type)) { |
|
1497 if (!iflod_rpt_complain(ifp, &new, 1, 0, |
|
1498 "report", |
|
1499 "unknown checksum type %s in", |
|
1500 DB_TYPE2STR(type))) |
|
1501 return -1; |
|
1502 continue; |
|
1503 } |
|
1504 if (ck->len != sizeof(*ck)) { |
|
1505 iflod_close(ifp, 1, 1, 1, |
|
1506 "unknown checksum length %d in %s", |
|
1507 ck->len, rpt_id("report", &new, 0)); |
|
1508 return -1; |
|
1509 } |
|
1510 if (type <= prev_type && prev_type != DCC_CK_FLOD_PATH) { |
|
1511 if (!iflod_rpt_complain(ifp, &new, 1, 0, |
|
1512 "report", |
|
1513 "out of order %s checksum in", |
|
1514 DB_TYPE2STR(type))) |
|
1515 return -1; |
|
1516 return rpt_len; |
|
1517 } |
|
1518 |
|
1519 new_ck->type_fgs = type; |
|
1520 new_ck->prev = DB_PTR_CP(DB_PTR_NULL); |
|
1521 memcpy(new_ck->sum, ck->sum, sizeof(new_ck->sum)); |
|
1522 if (type == DCC_CK_FLOD_PATH) { |
|
1523 /* discard report if path is too long */ |
|
1524 if (++num_path_blocks > DCC_MAX_FLOD_PATH_CKSUMS) { |
|
1525 TMSG2_FLOD(ofp, "%d path blocks in %s", |
|
1526 num_path_blocks, |
|
1527 rpt_id("report", &new, ifp)); |
|
1528 return rpt_len; |
|
1529 } |
|
1530 /* don't add this path if we translated the origin */ |
|
1531 if (srvr_mapped == ID_MAP_SELF) |
|
1532 continue; |
|
1533 old_path_id = (DCC_FLOD_PATH_ID *)ck->sum; |
|
1534 new_path_id = old_path_id; |
|
1535 for (i = 0; i < DCC_NUM_FLOD_PATH; ++i, ++old_path_id) { |
|
1536 psrvr = (old_path_id->hi<<8) | old_path_id->lo; |
|
1537 if (psrvr == DCC_ID_INVALID) |
|
1538 break; /* end of path */ |
|
1539 switch (id_map(psrvr, &ofp->i_opts)) { |
|
1540 case ID_MAP_NO: |
|
1541 case ID_MAP_REJ: |
|
1542 break; |
|
1543 case ID_MAP_SELF: |
|
1544 psrvr = my_srvr_id; |
|
1545 break; |
|
1546 } |
|
1547 new_path_id->hi = psrvr>>8; |
|
1548 new_path_id->lo = psrvr; |
|
1549 ++new_path_id; |
|
1550 } |
|
1551 |
|
1552 } else { |
|
1553 /* discard this checksum if we would not have kept |
|
1554 * it if we had received the original report |
|
1555 * and either its server-ID is translated |
|
1556 * or it is not kept by default */ |
|
1557 if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type) |
|
1558 && (srvr_mapped == ID_MAP_SELF |
|
1559 || DB_GLOBAL_NOKEEP(grey_on, type))) |
|
1560 continue; |
|
1561 |
|
1562 /* server-ID declarations are never stale */ |
|
1563 if (type == DCC_CK_SRVR_ID) { |
|
1564 stale = 0; |
|
1565 srvr_id_ck = new_ck; |
|
1566 } |
|
1567 |
|
1568 /* Notice if this checksum makes the report timely |
|
1569 * We cannot detect duplicates of reports that |
|
1570 * have expired, so consider stale anything older |
|
1571 * than our expiration. |
|
1572 * Ignore reports of checksums from crazy servers */ |
|
1573 if (stale |
|
1574 && dcc_ts_newer_ts(&new.ts, |
|
1575 new_tgts >= db_tholds[type] |
|
1576 ? &db_parms.ex_spam[type] |
|
1577 : &db_parms.ex_all[type]) |
|
1578 && (tp == 0 |
|
1579 || (tp->srvr_type != DCC_ID_SRVR_IGNORE |
|
1580 && tp->srvr_type != DCC_ID_SRVR_ROGUE))) |
|
1581 stale = 0; |
|
1582 } |
|
1583 |
|
1584 ++new_ck; |
|
1585 ++new.fgs_num_cks; |
|
1586 } |
|
1587 if (stale) { |
|
1588 if (CK_FLOD_CNTERR(&ofp->lc.stale) |
|
1589 && TMSG_FB2(ofp)) |
|
1590 flod_cnterr(&ofp->lc.stale, "stale %s", |
|
1591 rpt_id("report", &new, ifp)); |
|
1592 return rpt_len; |
|
1593 } |
|
1594 |
|
1595 if (!DB_NUM_CKS(&new)) { |
|
1596 iflod_close(ifp, 1, 1, 1, "no known checksum types in %s", |
|
1597 rpt_id("report", &new, 0)); |
|
1598 return -1; |
|
1599 } |
|
1600 |
|
1601 /* only now might we look at the database */ |
|
1602 if (db_lock() < 0) { |
|
1603 iflod_close(ifp, 1, 1, 1, "iflod lock failure"); |
|
1604 return -1; |
|
1605 } |
|
1606 |
|
1607 /* See if the report is a duplicate. |
|
1608 * Check all of the checksums to find one that is absent or |
|
1609 * the one with the smallest total to minimize the number |
|
1610 * of reports we must check to see if this is a duplicate */ |
|
1611 ok2 = 0; |
|
1612 new_ck_lim = &new.cks[DB_NUM_CKS(&new)]; |
|
1613 for (new_ck = new.cks; new_ck < new_ck_lim; ++new_ck) { |
|
1614 type = DB_CK_TYPE(new_ck); |
|
1615 if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)) |
|
1616 continue; |
|
1617 |
|
1618 switch (db_lookup(dcc_emsg, type, new_ck->sum, |
|
1619 0, MAX_HASH_ENTRIES, |
|
1620 &db_sts.hash, &db_sts.rcd, &found_ck)) { |
|
1621 case DB_FOUND_LATER: |
|
1622 case DB_FOUND_SYSERR: |
|
1623 iflod_close(ifp, 1, 1, 1, "%s", dcc_emsg); |
|
1624 DB_ERROR_MSG(dcc_emsg); |
|
1625 return -1; |
|
1626 |
|
1627 case DB_FOUND_IT: |
|
1628 /* At least this checksum is already in the database */ |
|
1629 i = ck_dup_ck_chain(ifp, &new, new_tgts, |
|
1630 type, found_ck); |
|
1631 if (i < 0) |
|
1632 return -1; /* broken database */ |
|
1633 if (i > 0) |
|
1634 return rpt_len; /* duplicate */ |
|
1635 |
|
1636 /* Maybe not a duplicate. |
|
1637 * Notice reports of checksums on the local server's |
|
1638 * whitelist. |
|
1639 * An ordinary checksum is whitelisted by DCC_TGTS_OK |
|
1640 * or two reports with DCC_TGTS_OK2. |
|
1641 * Greylisting uses DCC_TGTS_GREY_WHITE=DCC_TGTS_OK2 |
|
1642 * and so one report of DCC_TGTS_GREY_WHITE is enough */ |
|
1643 found_tgts = DB_TGTS_CK(found_ck); |
|
1644 if (found_tgts == DCC_TGTS_OK |
|
1645 || (found_tgts == DCC_TGTS_GREY_WHITE |
|
1646 && (++ok2 >= 2 || grey_on))) { |
|
1647 if (!iflod_rpt_complain(ifp, &new, 0, |
|
1648 &ofp->lc.wlist, |
|
1649 "report","whitelisted")) |
|
1650 return -1; |
|
1651 return rpt_len; |
|
1652 } |
|
1653 break; |
|
1654 |
|
1655 case DB_FOUND_EMPTY: |
|
1656 case DB_FOUND_CHAIN: |
|
1657 case DB_FOUND_INTRUDER: |
|
1658 /* We will fail to find this checksum in our database |
|
1659 * if the new report is not a duplicate |
|
1660 * or if it is a duplicate superset report */ |
|
1661 break; |
|
1662 } |
|
1663 } |
|
1664 |
|
1665 /* If the new report is a delete request, |
|
1666 * then we need to run dbclean to fix all of the |
|
1667 * totals affected by the deleted reports. */ |
|
1668 if (new_tgts == DCC_TGTS_DEL) { |
|
1669 if (!(ofp->i_opts.flags & FLOD_OPT_NO_LOG_DEL)) |
|
1670 dcc_trace_msg("accept %s", |
|
1671 rpt_id("delete request", &new, ifp)); |
|
1672 if (!DCC_CK_IS_REP_OP(grey_on, type) && !grey_on) |
|
1673 need_del_dbclean = "flood checksum deletion"; |
|
1674 } |
|
1675 |
|
1676 if (srvr_id_ck) { |
|
1677 /* discard translated server-ID declarations */ |
|
1678 if (srvr_mapped == ID_MAP_SELF) { |
|
1679 TMSG2_FLOD(ofp, "translated server-ID from %d in %s", |
|
1680 old_srvr, rpt_id("report", &new, ifp)); |
|
1681 return rpt_len; |
|
1682 } |
|
1683 |
|
1684 /* notice claims by other servers to our ID */ |
|
1685 if (old_srvr == my_srvr_id) { |
|
1686 if (memcmp(host_id_sum, srvr_id_ck->sum, |
|
1687 sizeof(host_id_sum))) |
|
1688 dcc_error_msg("host %s used our server-ID" |
|
1689 " %d at %s", |
|
1690 dcc_ck2str_err(DCC_CK_SRVR_ID, |
|
1691 srvr_id_ck->sum, 0), |
|
1692 my_srvr_id, |
|
1693 ts2str_err(&new.ts)); |
|
1694 return rpt_len; |
|
1695 } |
|
1696 |
|
1697 if (old_srvr < DCC_SRVR_ID_MIN |
|
1698 && !parse_srvr_id(srvr_id_ck, ifp, &new, old_srvr)) |
|
1699 return rpt_len; |
|
1700 } |
|
1701 |
|
1702 /* the report is ok and not a duplicate, so add it to our database */ |
|
1703 if (!add_dly_rcd(&new, 1)) { |
|
1704 iflod_close(ifp, 1, 1, 1, "%s", dcc_emsg); |
|
1705 return -1; |
|
1706 } |
|
1707 |
|
1708 ++ofp->cnts.accepted; |
|
1709 return rpt_len; |
|
1710 } |
|
1711 |
|
1712 |
|
1713 |
|
1714 static void |
|
1715 bad_vers(IFLOD_INFO *ifp, |
|
1716 u_char fail) /* 1=complain */ |
|
1717 { |
|
1718 iflod_close(ifp, fail, fail, 1, |
|
1719 DCC_FLOD_BAD_VER_MSG" need \"" |
|
1720 DCC_FLOD_VERSION_CUR_STR |
|
1721 "\" not \"%.*s\"", |
|
1722 LITZ(DCC_FLOD_VERSION_STR_BASE)+10, |
|
1723 ifp->ibuf.s.v.body.str); |
|
1724 } |
|
1725 |
|
1726 |
|
1727 |
|
1728 /* authenticate and otherwise check a new incoming flood */ |
|
1729 static u_char /* 0=closed or switched to output */ |
|
1730 check_iflod_vers(IFLOD_INFO *ifp) |
|
1731 { |
|
1732 DCC_FNM_LNO_BUF fnm_buf; |
|
1733 const DCC_FLOD_VERSION_HDR *vp; |
|
1734 OFLOD_INFO *ofp; |
|
1735 IFLOD_INFO *ifp1; |
|
1736 const ID_TBL *tp; |
|
1737 DCC_SRVR_ID rem_id; |
|
1738 int iversion; |
|
1739 int i; |
|
1740 |
|
1741 vp = &ifp->ibuf.s.v; |
|
1742 if (!strcmp(vp->body.str, DCC_FLOD_VERSION_CUR_STR)) { |
|
1743 iversion = DCC_FLOD_VERSION_CUR; |
|
1744 ifp->flags |= IFLOD_FG_VERS_CK; |
|
1745 |
|
1746 #ifdef DCC_FLOD_VERSION7 |
|
1747 } else if (!strcmp(vp->body.str, DCC_FLOD_VERSION7_STR)) { |
|
1748 iversion = DCC_FLOD_VERSION7; |
|
1749 ifp->flags |= IFLOD_FG_VERS_CK; |
|
1750 |
|
1751 #endif /* DCC_FLOD_VERSION7 */ |
|
1752 |
|
1753 } else if (!strncmp(vp->body.str, DCC_FLOD_VERSION_STR_BASE, |
|
1754 LITZ(DCC_FLOD_VERSION_STR_BASE))) { |
|
1755 /* it seems to be a DCC server, |
|
1756 * so complain after identifying the peer */ |
|
1757 iversion = 1; |
|
1758 |
|
1759 } else { |
|
1760 /* junk, so complain and give up */ |
|
1761 bad_vers(ifp, 1); |
|
1762 return 0; |
|
1763 } |
|
1764 |
|
1765 /* require a sane and familiar server-ID from the prospective peer */ |
|
1766 memcpy(&rem_id, vp->body.sender_srvr_id, sizeof(rem_id)); |
|
1767 rem_id = ntohs(rem_id); |
|
1768 if (rem_id < DCC_SRVR_ID_MIN |
|
1769 || rem_id > DCC_SRVR_ID_MAX) { |
|
1770 iflod_close(ifp, 1, 1, 1, DCC_FLOD_BAD_ID_MSG" %d", |
|
1771 rem_id); |
|
1772 return 0; |
|
1773 } |
|
1774 for (ofp = oflods.infos; ; ++ofp) { |
|
1775 if (ofp > LAST(oflods.infos)) { |
|
1776 iflod_close(ifp, 1, 1, 1, DCC_FLOD_BAD_ID_MSG" %d", |
|
1777 rem_id); |
|
1778 return 0; |
|
1779 } |
|
1780 if (ofp->rem_id == rem_id) { |
|
1781 ifp->ofp = ofp; |
|
1782 STRLCPY(ifp->rem_hostname, ofp->rem_hostname, |
|
1783 sizeof(ifp->rem_hostname)); |
|
1784 break; |
|
1785 } |
|
1786 } |
|
1787 |
|
1788 /* ofp and ofp->mp are not null, because we now know which peer |
|
1789 * it claims to be |
|
1790 * |
|
1791 * check that it knows the password */ |
|
1792 i = ck_sign(&tp, 0, ofp->in_passwd_id, vp, sizeof(*vp)); |
|
1793 if (!i) { |
|
1794 if (!tp) |
|
1795 iflod_close(ifp, 1, 1, 1, DCC_FLOD_PASSWD_ID_MSG" %d%s", |
|
1796 ofp->in_passwd_id, |
|
1797 fnm_lno(&fnm_buf, flod_path, ofp->lno)); |
|
1798 else |
|
1799 iflod_close(ifp, 1, 1, 1, DCC_FLOD_BAD_AUTH_MSG" %d", |
|
1800 ofp->in_passwd_id); |
|
1801 return 0; |
|
1802 } |
|
1803 if (i == 1) |
|
1804 ofp->mp->flags &= ~FLODMAP_FG_USE_2PASSWD; |
|
1805 else |
|
1806 ofp->mp->flags |= FLODMAP_FG_USE_2PASSWD; |
|
1807 |
|
1808 /* no more assumed NAT games because it has contacted us */ |
|
1809 ofp->mp->flags &= ~FLODMAP_FG_NAT_AUTO; |
|
1810 |
|
1811 /* Note the version of the protocol it is using so that we can use that |
|
1812 * version when connecting to it. */ |
|
1813 ofp->mp->iversion = iversion; |
|
1814 /* if we do not like its version, reject the connection and hope |
|
1815 * that it will retry with a version we like */ |
|
1816 if (!(ifp->flags & IFLOD_FG_VERS_CK)) { |
|
1817 bad_vers(ifp, iversion != 0); |
|
1818 return 0; |
|
1819 } |
|
1820 if (iversion != DCC_FLOD_VERSION_CUR) |
|
1821 TMSG2_FLOD(ofp, "version %d from %s", |
|
1822 iversion, ifp_rem_str(ifp)); |
|
1823 |
|
1824 /* convert to a passive output flood as requested by the peer |
|
1825 * This works even if the peer is configured to use SOCKS or NAT |
|
1826 * but we are not using PASSIVE */ |
|
1827 if (vp->body.turn) { |
|
1828 if (OFLOD_OPT_OFF_ROGUE(ofp)) { |
|
1829 iflod_close(ifp, 1, 0, 1, |
|
1830 "passive output flooding off from %s%s", |
|
1831 ifp_rem_str(ifp), |
|
1832 fnm_lno(&fnm_buf, flod_path, ofp->lno)); |
|
1833 return 0; |
|
1834 } |
|
1835 |
|
1836 /* We have a duplicate passive outgoing flood. |
|
1837 * See whether the old stream has broken. */ |
|
1838 if (ofp->soc >= 0) |
|
1839 oflod_read(ofp); |
|
1840 /* If we still have a duplicate and we sent a shutdown request, |
|
1841 * assume the response got lost */ |
|
1842 if (ofp->soc >= 0 |
|
1843 && (ofp->flags & OFLOD_FG_SHUTDOWN_REQ)) { |
|
1844 rpt_err(ofp, 1, 0, |
|
1845 " assume response to shutdown lost from %s", |
|
1846 ofp_rem_str(ofp)); |
|
1847 oflod_close(ofp, 0); |
|
1848 } |
|
1849 if (ofp->soc >= 0) { |
|
1850 /* We still have duplicates. |
|
1851 * Reject the new one if the IP addresses differ */ |
|
1852 if (!DCC_SU_EQ(&ofp->rem_su, &ifp->rem_su)) { |
|
1853 iflod_close(ifp, 1, 0, 1, |
|
1854 "reject duplicate passive output" |
|
1855 " flood from %s", |
|
1856 ifp_rem_str(ifp)); |
|
1857 return 0; |
|
1858 } |
|
1859 rpt_err(ofp, 1, 0, |
|
1860 "accept duplicate passive output flood from %s", |
|
1861 ifp_rem_str(ifp)); |
|
1862 oflod_close(ofp, 0); |
|
1863 } |
|
1864 |
|
1865 ofp->soc = ifp->soc; |
|
1866 ofp->rem_su = ifp->rem_su; |
|
1867 ++oflods.open; |
|
1868 TMSG1_FLOD(ofp, |
|
1869 "convert incoming flood to passive outgoing to %s", |
|
1870 ofp_rem_str(ofp)); |
|
1871 iflod_clear(ifp, 0); |
|
1872 |
|
1873 ofp->mp->flags |= FLODMAP_FG_OUT_SRVR; |
|
1874 |
|
1875 if (!oflod_connect_fin(ofp)) |
|
1876 oflod_close(ofp, 0); |
|
1877 return 0; |
|
1878 } |
|
1879 |
|
1880 if (IFLOD_OPT_OFF_ROGUE(ofp)) { |
|
1881 iflod_close(ifp, 1, 0, 1, "flood from %s turned off%s", |
|
1882 ifp_rem_str(ifp), |
|
1883 fnm_lno(&fnm_buf, flod_path, ofp->lno)); |
|
1884 return 0; |
|
1885 } |
|
1886 |
|
1887 /* detect duplicate incoming floods */ |
|
1888 for (ifp1 = iflods.infos; ifp1 <= LAST(iflods.infos); ++ifp1) { |
|
1889 if (ifp1->ofp != ofp || ifp1 == ifp) |
|
1890 continue; |
|
1891 |
|
1892 /* We have a duplicate. Either two servers are using the |
|
1893 * same server-ID or the peer has restarted flooding without |
|
1894 * our seeing a clean shutdown. |
|
1895 * If socket is in CLOSE_WAIT, then sending something will fail |
|
1896 * immediately. If the peer was rebooted, then sending will |
|
1897 * not fail for at least a round trip time and possibly longer |
|
1898 * if the peer has moved. |
|
1899 * |
|
1900 * Before trying to send anything, check for a FIN waiting |
|
1901 * on the other socket */ |
|
1902 for (i = 65536/FLOD_BUF_SIZE; i >= 0; --i) { |
|
1903 if (iflod_read(ifp1)) |
|
1904 break; |
|
1905 } |
|
1906 /* forget it if reading closed the other stream */ |
|
1907 if (ifp1->ofp != ofp) |
|
1908 break; |
|
1909 |
|
1910 /* assume the it is ok if we have sent an end request */ |
|
1911 if (ifp1->flags & IFLOD_FG_END_REQ) { |
|
1912 iflod_close(ifp1, 0, 0, 1, |
|
1913 "missing end response;" |
|
1914 " have replacement for flood from %s", |
|
1915 ifp_rem_str(ifp1)); |
|
1916 break; |
|
1917 } |
|
1918 |
|
1919 /* assume we missed the shutdown |
|
1920 * if the IP addresses are the same */ |
|
1921 if (DCC_SU_EQ(&ifp->rem_su, &ifp1->rem_su)) { |
|
1922 iflod_close(ifp1, 0, 0, 1, |
|
1923 "assumed dead link;" |
|
1924 " have replacement for flood from %s", |
|
1925 ifp_rem_str(ifp1)); |
|
1926 break; |
|
1927 } |
|
1928 |
|
1929 /* assume we do not have a duplicate server-ID and switch to |
|
1930 * the new connection if sending a position or note |
|
1931 * fails immediately, */ |
|
1932 if (0 >= iflod_send_pos(ifp1, 1)) { |
|
1933 iflod_close(ifp1, 0, 0, 1, |
|
1934 "have replacement for flood from %s", |
|
1935 ifp_rem_str(ifp1)); |
|
1936 break; |
|
1937 } |
|
1938 |
|
1939 /* Otherwise, kill the new flood. If it was legitimate, |
|
1940 * sending the note will eventually kill the old stream |
|
1941 * and the peer will get through with it tries later */ |
|
1942 iflod_close(ifp, 1, 1, 1, "duplicate flood from %s", |
|
1943 ifp_rem_str(ifp)); |
|
1944 return 0; |
|
1945 } |
|
1946 |
|
1947 ofp->ifp = ifp; |
|
1948 if (ifp->flags & IFLOD_FG_CLIENT) |
|
1949 ofp->mp->flags &= ~FLODMAP_FG_IN_SRVR; |
|
1950 else |
|
1951 ofp->mp->flags |= FLODMAP_FG_IN_SRVR; |
|
1952 save_flod_cnts(ofp); |
|
1953 ifp->iflod_alive = db_time.tv_sec; |
|
1954 |
|
1955 /* Try to restart the corresponding output flood because a new |
|
1956 * incoming flood might indicate that peer has awakened. |
|
1957 * Kludge the backoff so that it does not increase. */ |
|
1958 if (ofp->soc < 0 |
|
1959 && !(ofp->o_opts.flags & FLOD_OPT_PASSIVE)) { |
|
1960 ofp->mp->otimers.retry_secs /= 2; |
|
1961 ofp->mp->otimers.retry = 0; |
|
1962 oflod_open(ofp); |
|
1963 } |
|
1964 |
|
1965 /* Send a rewind or fast forward request immediately if needed. |
|
1966 * If not, send a keepalive message so that peer knows we have |
|
1967 * accepted the connection and it can stop worrying about an |
|
1968 * immediate rejection. */ |
|
1969 if (0 > iflod_send_pos(ifp, 1)) |
|
1970 return 0; |
|
1971 |
|
1972 return 1; |
|
1973 } |
|
1974 |
|
1975 |
|
1976 |
|
1977 /* A new SOCKS incoming stream that we originated has been closed by the |
|
1978 * peer without authenticating itself. It could have responded to our |
|
1979 * authentication with an error message. */ |
|
1980 static void |
|
1981 parse_socks_error(IFLOD_INFO *ifp) |
|
1982 { |
|
1983 const DCC_FLOD_STREAM *stream; |
|
1984 int i, msg_len; |
|
1985 int fail; |
|
1986 |
|
1987 stream = (DCC_FLOD_STREAM *)&ifp->ibuf.b[0]; |
|
1988 msg_len = ifp->ibuf_len - FLOD_END_OVHD; |
|
1989 |
|
1990 /* it must look like an end request with an entirely ASCII message */ |
|
1991 if (flod_pos2db_ptr(stream->r.pos) != DCC_FLOD_POS_END |
|
1992 || msg_len < 1 || msg_len > ISZ(stream->e.msg)) { |
|
1993 iflod_close(ifp, 1, 1, 1, "SOCKS rejected with \"%.*s\"", |
|
1994 msg_len, stream->e.msg); |
|
1995 return; |
|
1996 } |
|
1997 |
|
1998 for (i = 0; i < msg_len; ++i) { |
|
1999 if (stream->e.msg[i] < ' ' || stream->e.msg[i] > '~') { |
|
2000 iflod_close(ifp, 1, 1, 1, |
|
2001 "SOCKS rejected with \"%.*s\"", |
|
2002 msg_len, stream->e.msg); |
|
2003 return; |
|
2004 } |
|
2005 } |
|
2006 |
|
2007 fail = oflod_parse_eof(ifp->ofp, 1, &stream->e, msg_len); |
|
2008 if (fail <= 0) { |
|
2009 iflod_socks_backoff(ifp->ofp); |
|
2010 } else { |
|
2011 /* try again immediately |
|
2012 * with another protocol version or the 2nd password */ |
|
2013 ifp->ofp->mp->itimers.retry = 0; |
|
2014 } |
|
2015 iflod_close(ifp, fail<=0, fail<=0, 0, |
|
2016 "SOCKS rejected by %s with \"%.*s\"", |
|
2017 ifp_rem_str(ifp), msg_len, stream->e.msg); |
|
2018 } |
|
2019 |
|
2020 |
|
2021 |
|
2022 /* see what a distant flooder is telling us |
|
2023 * can close the flood and so clear things */ |
|
2024 u_char /* 1=kernel buffers empty */ |
|
2025 iflod_read(IFLOD_INFO *ifp) |
|
2026 { |
|
2027 OFLOD_INFO *ofp; |
|
2028 int off, req_len, recv_len; |
|
2029 const DCC_FLOD_STREAM *stream; |
|
2030 int len, i; |
|
2031 |
|
2032 /* if this is an incoming SOCKS or NAT stream that we originated, |
|
2033 * and if Rconnect() said "not yet" when we first tried to connect, |
|
2034 * then we must be here because select() says it is time to |
|
2035 * try Rconnect() again to finish the connection */ |
|
2036 if (!(ifp->flags & IFLOD_FG_CONNECTED) |
|
2037 && iflod_socks_connect(ifp) <= 0) |
|
2038 return 1; |
|
2039 |
|
2040 /* read only once before returning |
|
2041 * to ensure we pay attention to other work */ |
|
2042 |
|
2043 req_len = sizeof(ifp->ibuf) - ifp->ibuf_len; |
|
2044 ofp = ifp->ofp; |
|
2045 if (ofp && (ofp->o_opts.flags & FLOD_OPT_SOCKS)) |
|
2046 recv_len = Rrecv(ifp->soc, &ifp->ibuf.b[ifp->ibuf_len], |
|
2047 req_len, 0); |
|
2048 else |
|
2049 recv_len = recv(ifp->soc, &ifp->ibuf.b[ifp->ibuf_len], |
|
2050 req_len, 0); |
|
2051 if (recv_len < 0) { |
|
2052 /* If kernel ran out of data, stop for now. |
|
2053 * Give up on an I/O error */ |
|
2054 if (!DCC_BLOCK_ERROR()) { |
|
2055 iflod_close(ifp, 1, 0, 0, "incoming flood recv(%s): %s", |
|
2056 ifp_rem_str(ifp), ERROR_STR()); |
|
2057 } |
|
2058 return 1; |
|
2059 } |
|
2060 ifp->ibuf_len += recv_len; |
|
2061 |
|
2062 off = 0; |
|
2063 |
|
2064 /* deal with a new connection */ |
|
2065 if (!(ifp->flags & IFLOD_FG_VERS_CK)) { |
|
2066 if (ifp->ibuf_len >= ISZ(DCC_FLOD_VERSION_HDR)) { |
|
2067 if (!check_iflod_vers(ifp)) |
|
2068 return 1; /* stream closed or converted */ |
|
2069 ofp = ifp->ofp; |
|
2070 off = ISZ(DCC_FLOD_VERSION_HDR); |
|
2071 |
|
2072 } else if (recv_len != 0) { |
|
2073 return 1; /* wait for rest of authentication */ |
|
2074 |
|
2075 } else if (ofp && ofp->mp |
|
2076 && (ofp->mp->flags & FLODMAP_FG_ACT) != 0) { |
|
2077 parse_socks_error(ifp); |
|
2078 return 1; |
|
2079 |
|
2080 } else { |
|
2081 iflod_close(ifp, 1, 1, 0, "garbage connection from %s", |
|
2082 ifp_rem_str(ifp)); |
|
2083 return 1; |
|
2084 } |
|
2085 } |
|
2086 /* ofp != 0 because check_iflod_vers() has found the peer */ |
|
2087 |
|
2088 /* deal with the data */ |
|
2089 dcc_timeval2ts(&future, &db_time, MAX_FLOD_CLOCK_SKEW); |
|
2090 while ((len = ifp->ibuf_len - off) > 0) { |
|
2091 stream = (DCC_FLOD_STREAM *)&ifp->ibuf.b[off]; |
|
2092 if (len < ISZ(stream->r.pos)) |
|
2093 break; /* need at least the position */ |
|
2094 i = iflod_rpt(ifp, ofp, stream, len); |
|
2095 if (i < 0) |
|
2096 return 1; /* stream closed */ |
|
2097 if (i == 0) |
|
2098 break; /* wait for rest of report */ |
|
2099 off += i; |
|
2100 ++ofp->cnts.total; |
|
2101 } |
|
2102 |
|
2103 /* save unprocessed bytes for next time */ |
|
2104 if (off != 0) { |
|
2105 ifp->ibuf_len -= off; |
|
2106 if (ifp->ibuf_len < 0) |
|
2107 dcc_logbad(EX_SOFTWARE, "ifp->ibuf_len=%d", |
|
2108 ifp->ibuf_len); |
|
2109 if (ifp->ibuf_len > 0) |
|
2110 memmove(&ifp->ibuf.b[0], &ifp->ibuf.b[off], |
|
2111 ifp->ibuf_len); |
|
2112 } |
|
2113 |
|
2114 if (recv_len == 0) { |
|
2115 /* We are at EOF and have processed all input that we can. */ |
|
2116 if (ifp->ibuf_len != 0) { |
|
2117 /* Something is wrong if any input remains, */ |
|
2118 iflod_close(ifp, 1, 0, 1, "report %d truncated", |
|
2119 ofp ? ofp->cnts.total : 0); |
|
2120 } else if (flods_st != FLODS_ST_ON |
|
2121 || (ofp && IFLOD_OPT_OFF_ROGUE(ofp))) { |
|
2122 iflod_close(ifp, 0, 0, 1, DCC_FLOD_OK_STR"%s off", |
|
2123 our_hostname); |
|
2124 } else { |
|
2125 iflod_close(ifp, 0, 0, 1, DCC_FLOD_OK_STR"%s off", |
|
2126 ifp_rem_str(ifp)); |
|
2127 } |
|
2128 return 1; |
|
2129 } |
|
2130 |
|
2131 /* things are going ok, so reset the SOCKS restart backoff |
|
2132 * and the no-connection complaint */ |
|
2133 ofp->mp->itimers.retry_secs = FLOD_SOCKS_SOCKS_IRETRY; |
|
2134 ofp->mp->itimers.msg_secs = FLOD_IN_COMPLAIN1; |
|
2135 ofp->mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN1; |
|
2136 |
|
2137 return (req_len > recv_len); |
|
2138 } |
|
2139 |
|
2140 |
|
2141 |
|
2142 void |
|
2143 iflods_listen(void) |
|
2144 { |
|
2145 SRVR_SOC *sp; |
|
2146 DCC_SOCKU su; |
|
2147 const DCC_SOCKU *sup = 0; |
|
2148 int i, on; |
|
2149 |
|
2150 for (sp = srvr_socs; sp; sp = sp->fwd) { |
|
2151 if (sp->flags & SRVR_SOC_ADDR) { |
|
2152 /* need to open a TCP listen socket for incoming floods |
|
2153 * for each explicitly configured IP address */ |
|
2154 sup = &sp->su; |
|
2155 } else if (sp->flags & SRVR_SOC_LISTEN) { |
|
2156 /* need to open one TCP INADDR_ANY listen socket for |
|
2157 * the first implicitly configured interface */ |
|
2158 sup = dcc_mk_su(&su, sp->su.sa.sa_family, 0, |
|
2159 sp->su.ipv6.sin6_port); |
|
2160 } else { |
|
2161 /* otherwise close unneeded socket */ |
|
2162 iflod_listen_close(sp); |
|
2163 continue; |
|
2164 } |
|
2165 |
|
2166 if (sp->listen >= 0) |
|
2167 continue; |
|
2168 |
|
2169 /* don't need to listen if there is no flooding */ |
|
2170 if (!oflods.total) |
|
2171 continue; |
|
2172 |
|
2173 TMSG1(FLOD, "start flood listening on %s", |
|
2174 dcc_su2str_err(sup)); |
|
2175 |
|
2176 sp->listen = socket(sup->sa.sa_family, SOCK_STREAM, 0); |
|
2177 if (sp->listen < 0) { |
|
2178 dcc_error_msg("socket(flood listen %s): %s", |
|
2179 dcc_su2str_err(sup), ERROR_STR()); |
|
2180 continue; |
|
2181 } |
|
2182 |
|
2183 if (-1 == fcntl(sp->listen, F_SETFL, |
|
2184 fcntl(sp->listen, F_GETFL, 0) | O_NONBLOCK)) { |
|
2185 dcc_error_msg("fcntl(flood listen %s, O_NONBLOCK): %s", |
|
2186 dcc_su2str_err(sup), ERROR_STR()); |
|
2187 } |
|
2188 on = 1; |
|
2189 if (0 > setsockopt(sp->listen, SOL_SOCKET, SO_REUSEADDR, |
|
2190 &on, sizeof(on))) |
|
2191 dcc_error_msg("setsockopt(flood listen %s," |
|
2192 " SO_REUSADDR): %s", |
|
2193 dcc_su2str_err(sup), ERROR_STR()); |
|
2194 if (0 > fcntl(sp->listen, F_SETFD, FD_CLOEXEC)) |
|
2195 dcc_error_msg("fcntl(flood listen %s FD_CLOEXEC): %s", |
|
2196 dcc_su2str_err(sup), ERROR_STR()); |
|
2197 |
|
2198 i = bind(sp->listen, &sup->sa, DCC_SU_LEN(sup)); |
|
2199 if (0 > i) { |
|
2200 dcc_error_msg("bind(flood listen %s): %s", |
|
2201 dcc_su2str_err(sup), ERROR_STR()); |
|
2202 close(sp->listen); |
|
2203 sp->listen = -1; |
|
2204 continue; |
|
2205 } |
|
2206 |
|
2207 if (0 > listen(sp->listen, DCCD_MAX_FLOODS+1)) { |
|
2208 dcc_error_msg("flood listen(%s): %s", |
|
2209 dcc_su2str_err(sup), ERROR_STR()); |
|
2210 close(sp->listen); |
|
2211 sp->listen = -1; |
|
2212 } |
|
2213 } |
|
2214 } |
|
2215 |
|
2216 |
|
2217 |
|
2218 static const char * |
|
2219 oflod_state_str(char outstr[DCC_SU2STR_SIZE], const OFLOD_INFO *ofp, |
|
2220 u_char anon) |
|
2221 { |
|
2222 if (ofp->soc >= 0) { |
|
2223 if (ofp->flags & (OFLOD_FG_SHUTDOWN_REQ |
|
2224 | OFLOD_FG_SHUTDOWN)) |
|
2225 return " (shutting)"; |
|
2226 if (!(ofp->flags & OFLOD_FG_CONNECTED)) |
|
2227 return " (connecting)"; |
|
2228 if (anon) |
|
2229 return ""; |
|
2230 return dcc_su2str2(outstr, DCC_SU2STR_SIZE, &ofp->rem_su); |
|
2231 } |
|
2232 |
|
2233 if (OFLOD_OPT_OFF_ROGUE(ofp)) |
|
2234 return " (output off)"; |
|
2235 if (flods_st != FLODS_ST_ON) |
|
2236 return " (flood off)"; |
|
2237 return " (no output)"; |
|
2238 } |
|
2239 |
|
2240 |
|
2241 |
|
2242 static const char * |
|
2243 iflod_state_str(char instr[DCC_SU2STR_SIZE], |
|
2244 const OFLOD_INFO *ofp, const IFLOD_INFO *ifp, |
|
2245 u_char anon, u_char have_in, u_char distinct_in) |
|
2246 { |
|
2247 |
|
2248 if (have_in) { |
|
2249 if (!(ifp->flags & IFLOD_FG_VERS_CK)) |
|
2250 return " (connecting)"; |
|
2251 if (anon) |
|
2252 return ""; |
|
2253 if (distinct_in) |
|
2254 return dcc_su2str2(instr, DCC_SU2STR_SIZE, &ifp->rem_su); |
|
2255 return "\t"; |
|
2256 } |
|
2257 if (IFLOD_OPT_OFF_ROGUE(ofp)) |
|
2258 return " (input off)"; |
|
2259 if (flods_st != FLODS_ST_ON) |
|
2260 return " (flood off)"; |
|
2261 return " (no input)"; |
|
2262 } |
|
2263 |
|
2264 |
|
2265 |
|
2266 /* list the current flooders */ |
|
2267 int |
|
2268 flods_list(char *buf, int buf_len, u_char anon) |
|
2269 { |
|
2270 #define FLODS_LIST_TOO_SHORT "buffer too short\n" |
|
2271 #define FLODS_LIST_ALLOC(i) { \ |
|
2272 p += (i); \ |
|
2273 if ((buf_len -= (i)) <= 0) { \ |
|
2274 strcpy(p, FLODS_LIST_TOO_SHORT); \ |
|
2275 return (p-buf)+ISZ(FLODS_LIST_TOO_SHORT); \ |
|
2276 }} |
|
2277 IFLOD_INFO *ifp; |
|
2278 OFLOD_INFO *ofp; |
|
2279 char instr[DCC_SU2STR_SIZE], outstr[DCC_SU2STR_SIZE]; |
|
2280 char hostname[60], fg_buf[60]; |
|
2281 DCC_SOCKU in, out; |
|
2282 u_char have_in, distinct_in; |
|
2283 int i; |
|
2284 char *p; |
|
2285 |
|
2286 if (buf_len < ISZ(FLODS_LIST_TOO_SHORT) +INET6_ADDRSTRLEN+1) |
|
2287 return 0; |
|
2288 |
|
2289 buf_len -= ISZ(FLODS_LIST_TOO_SHORT); |
|
2290 p = buf; |
|
2291 for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) { |
|
2292 if (ofp->rem_hostname[0] == '\0') |
|
2293 break; |
|
2294 have_in = 0; |
|
2295 distinct_in = 0; |
|
2296 for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) { |
|
2297 if (ifp->ofp == ofp) { |
|
2298 if (ifp->soc >= 0) { |
|
2299 have_in = 1; |
|
2300 dcc_ipv6sutoipv4(&in, &ifp->rem_su); |
|
2301 dcc_ipv6sutoipv4(&out, &ofp->rem_su); |
|
2302 if (ofp->soc < 0 |
|
2303 || !DCC_SU_EQ(&in, &out)) |
|
2304 distinct_in = 1; |
|
2305 } |
|
2306 break; |
|
2307 } |
|
2308 } |
|
2309 if (anon) { |
|
2310 i = snprintf(p, buf_len, "%5d %15s\t%s\n", |
|
2311 ofp->rem_id, |
|
2312 oflod_state_str(outstr, ofp, 1), |
|
2313 iflod_state_str(instr, ofp, ifp, |
|
2314 1, have_in, 0)); |
|
2315 } else { |
|
2316 dcc_host_portname(hostname, sizeof(hostname), |
|
2317 ofp->rem_hostname, |
|
2318 ofp->rem_port == def_port |
|
2319 ? 0 : ofp->rem_portname), |
|
2320 i = strlen(hostname); |
|
2321 flodmap_fg(fg_buf, sizeof(fg_buf), |
|
2322 i < 16 ? "\t\t" : i > 24 ? " " : "\t", |
|
2323 ofp->mp); |
|
2324 i = snprintf(p, buf_len, "%5d %15s\t%s\t%s%s\n", |
|
2325 ofp->rem_id, |
|
2326 oflod_state_str(outstr, ofp, 0), |
|
2327 iflod_state_str(instr, ofp, ifp, |
|
2328 0, have_in, distinct_in), |
|
2329 hostname, |
|
2330 fg_buf); |
|
2331 } |
|
2332 FLODS_LIST_ALLOC(i); |
|
2333 } |
|
2334 |
|
2335 for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) { |
|
2336 if (ifp->soc < 0 || ifp->ofp != 0) |
|
2337 continue; /* already handled this one */ |
|
2338 |
|
2339 /* say something about an incomplete connection */ |
|
2340 i = snprintf(p, buf_len, " ? %s\n", ifp->rem_hostname); |
|
2341 FLODS_LIST_ALLOC(i); |
|
2342 } |
|
2343 if (p > buf) |
|
2344 --p; /* trim trailing '\n' */ |
|
2345 return p-buf; |
|
2346 #undef FLODS_LIST_TOO_SHORT |
|
2347 #undef FLODS_LIST_ALLOC |
|
2348 } |
|
2349 |
|
2350 |
|
2351 |
|
2352 static PATTRIB(3,4) u_char /* 0=no room */ |
|
2353 flod_stats_str(char **buf, int *buf_len, |
|
2354 const char *pat, ...) |
|
2355 { |
|
2356 int i; |
|
2357 va_list args; |
|
2358 |
|
2359 if (*buf_len <= 0) |
|
2360 return 0; |
|
2361 |
|
2362 va_start(args, pat); |
|
2363 i = vsnprintf(*buf, *buf_len, pat, args); |
|
2364 va_end(args); |
|
2365 |
|
2366 if ((*buf_len -= i) <= 0) { |
|
2367 *buf_len = 0; |
|
2368 return 0; |
|
2369 } |
|
2370 *buf += i; |
|
2371 return 1; |
|
2372 } |
|
2373 |
|
2374 |
|
2375 |
|
2376 |
|
2377 static u_char /* 0=no room */ |
|
2378 flod_stats_time(char **buf, int *buf_len, |
|
2379 const char *str, const char *timepat, time_t when) |
|
2380 { |
|
2381 char timebuf[40]; |
|
2382 |
|
2383 return flod_stats_str(buf, buf_len, "%s %s", str, |
|
2384 dcc_time2str(timebuf, sizeof(timebuf), timepat, |
|
2385 when)); |
|
2386 } |
|
2387 |
|
2388 |
|
2389 |
|
2390 static void |
|
2391 flod_stats_conn_total(char **buf, int *buf_len, |
|
2392 const char *label, int connected) |
|
2393 { |
|
2394 int i; |
|
2395 |
|
2396 if (*buf_len <= 0) |
|
2397 return; |
|
2398 |
|
2399 i = snprintf(*buf, *buf_len, |
|
2400 "\n %s connected a total of %d days %d:%02d:%02d\n", |
|
2401 label, |
|
2402 connected/(24*60*60), |
|
2403 (connected/(60*60)) % 24, |
|
2404 (connected/60) % 60, |
|
2405 connected % 60); |
|
2406 *buf += i; |
|
2407 *buf_len -= i; |
|
2408 } |
|
2409 |
|
2410 |
|
2411 |
|
2412 static void |
|
2413 flod_stats_conn_cur(char **buf, int *buf_len, const OFLOD_INFO *ofp, u_char in) |
|
2414 { |
|
2415 u_char connected; |
|
2416 time_t conn_changed; |
|
2417 time_t flod_alive; |
|
2418 const FLOD_MMAP *mp; |
|
2419 const LAST_ERROR *ep; |
|
2420 DCC_FNM_LNO_BUF fnm_buf; |
|
2421 time_t deadline; |
|
2422 u_char passive; |
|
2423 const char *msg; |
|
2424 |
|
2425 if (*buf_len <= 0) |
|
2426 return; |
|
2427 |
|
2428 mp = ofp->mp; |
|
2429 |
|
2430 if (in) { |
|
2431 connected = ofp->ifp != 0; |
|
2432 conn_changed = ofp->mp->cnts.in_conn_changed; |
|
2433 flod_alive = ofp->ifp ? ofp->ifp->iflod_alive : 0; |
|
2434 } else { |
|
2435 connected = (ofp->flags & OFLOD_FG_CONNECTED) != 0; |
|
2436 conn_changed = ofp->mp->cnts.out_conn_changed; |
|
2437 flod_alive = ofp->oflod_alive; |
|
2438 } |
|
2439 |
|
2440 if (connected) { |
|
2441 if (conn_changed >= mp->cnts.cnts_cleared |
|
2442 && !flod_stats_time(buf, buf_len, " connected since", |
|
2443 "%b %d %X", conn_changed)) |
|
2444 return; |
|
2445 flod_stats_time(buf, buf_len, " last active", "%X", |
|
2446 flod_alive); |
|
2447 return; |
|
2448 } |
|
2449 |
|
2450 if ((in && (mp->flags & FLODMAP_FG_IN_OFF)) |
|
2451 || (!in && (mp->flags & FLODMAP_FG_OUT_OFF))) { |
|
2452 flod_stats_str(buf, buf_len, " off%s", |
|
2453 fnm_lno(&fnm_buf, flod_path, ofp->lno)); |
|
2454 return; |
|
2455 } |
|
2456 |
|
2457 if (!flod_stats_str(buf, buf_len, " not connected")) |
|
2458 return; |
|
2459 if (conn_changed >= mp->cnts.cnts_cleared) { |
|
2460 if (!flod_stats_time(buf, buf_len, " since", |
|
2461 "%b %d %X", conn_changed)) |
|
2462 return; |
|
2463 } |
|
2464 ep = in ? &mp->iflod_err : &mp->oflod_err; |
|
2465 msg = ep->msg[0] != '\0' ? ep->msg : ep->trace_msg; |
|
2466 if (msg[0] != '\0') { |
|
2467 if (!flod_stats_str(buf, buf_len, "\n\t%s", msg)) |
|
2468 return; |
|
2469 } |
|
2470 |
|
2471 if (!FLODS_OK_ON()) { |
|
2472 flod_stats_str(buf, buf_len, "\n flooding off"); |
|
2473 return; |
|
2474 } |
|
2475 |
|
2476 if (in) { |
|
2477 if ((ofp->mp->flags & FLODMAP_FG_ACT) != 0) { |
|
2478 passive = 0; |
|
2479 deadline = ofp->mp->itimers.retry; |
|
2480 if (DB_IS_TIME(deadline, ofp->mp->itimers.retry_secs)) |
|
2481 deadline = 0; |
|
2482 |
|
2483 } else { |
|
2484 passive = 1; |
|
2485 deadline = ofp->mp->itimers.msg; |
|
2486 if (DB_IS_TIME(deadline, ofp->mp->itimers.msg_secs)) |
|
2487 deadline = 0; |
|
2488 } |
|
2489 } else { |
|
2490 if (ofp->mp->flags & FLODMAP_FG_PASSIVE) { |
|
2491 passive = 1; |
|
2492 deadline = ofp->mp->otimers.msg; |
|
2493 if (DB_IS_TIME(deadline, ofp->mp->otimers.msg_secs)) |
|
2494 deadline = 0; |
|
2495 } else { |
|
2496 passive = 0; |
|
2497 deadline = ofp->mp->otimers.retry; |
|
2498 if (DB_IS_TIME(deadline, ofp->mp->otimers.retry_secs)) |
|
2499 deadline = 0; |
|
2500 } |
|
2501 } |
|
2502 if (deadline == 0) { |
|
2503 flod_stats_str(buf, buf_len, |
|
2504 passive |
|
2505 ? "\n complain soon" |
|
2506 : "\n try again soon"); |
|
2507 } else { |
|
2508 flod_stats_time(buf, buf_len, |
|
2509 passive |
|
2510 ? "\n complain after" |
|
2511 : "\n try again after", |
|
2512 "%b %d %X", deadline); |
|
2513 } |
|
2514 } |
|
2515 |
|
2516 |
|
2517 |
|
2518 /* list the counts for a flood */ |
|
2519 int /* -1 or buffer length */ |
|
2520 flod_stats(char *buf, int buf_len, u_int32_t tgt, u_char clear) |
|
2521 { |
|
2522 #define FLOD_STATS_TOO_SHORT "buffer too short\n" |
|
2523 #define FLOD_STATS_ALLOC(i) (p += (i), len -= (i)) |
|
2524 OFLOD_INFO *ofp, *ofp1; |
|
2525 FLOD_MMAP *mp; |
|
2526 char now_buf[26], time_buf[26], fg_buf[60]; |
|
2527 DCC_SRVR_ID min_srvr, max_srvr; |
|
2528 u_char loaded; |
|
2529 int len, i; |
|
2530 char *p; |
|
2531 |
|
2532 if (buf_len < ISZ(FLOD_STATS_TOO_SHORT)) |
|
2533 return 0; |
|
2534 len = buf_len - ISZ(FLOD_STATS_TOO_SHORT); |
|
2535 p = buf; |
|
2536 |
|
2537 if (flod_mmaps) { |
|
2538 loaded = 0; |
|
2539 } else if (!load_flod(0)) { |
|
2540 return -1; |
|
2541 } else { |
|
2542 loaded = 1; |
|
2543 } |
|
2544 |
|
2545 if (tgt <= DCC_SRVR_ID_MAX) { |
|
2546 /* an explicit target server-ID was specified */ |
|
2547 min_srvr = max_srvr = tgt; |
|
2548 } else { |
|
2549 /* look for next server-ID after the target value */ |
|
2550 min_srvr = tgt - DCC_SRVR_ID_MAX; |
|
2551 max_srvr = DCC_SRVR_ID_MAX; |
|
2552 } |
|
2553 ofp = 0; |
|
2554 for (ofp1 = oflods.infos; ofp1 <= LAST(oflods.infos); ++ofp1) { |
|
2555 if (ofp1->rem_hostname[0] != '\0' |
|
2556 && ofp1->rem_id >= min_srvr |
|
2557 && ofp1->rem_id <= max_srvr) { |
|
2558 /* This peer fits and is the best so far. */ |
|
2559 ofp = ofp1; |
|
2560 max_srvr = ofp->rem_id-1; |
|
2561 } |
|
2562 } |
|
2563 if (!ofp) { |
|
2564 i = snprintf(p, len, |
|
2565 DCC_AOP_FLOD_STATS_ID"unknown remote server-ID", |
|
2566 tgt); |
|
2567 FLOD_STATS_ALLOC(i); |
|
2568 if (loaded) |
|
2569 oflods_clear(); |
|
2570 return p-buf; |
|
2571 } |
|
2572 mp = ofp->mp; |
|
2573 |
|
2574 save_flod_cnts(ofp); |
|
2575 i = snprintf(p, len, |
|
2576 DCC_AOP_FLOD_STATS_ID" %s%s %s\n status start %s", |
|
2577 ofp->rem_id, mp->rem_hostname, |
|
2578 flodmap_fg(fg_buf, sizeof(fg_buf), " ", mp), |
|
2579 dcc_time2str(now_buf, sizeof(now_buf), "%b %d %X %Z", |
|
2580 db_time.tv_sec), |
|
2581 dcc_time2str(time_buf, sizeof(time_buf), "%b %d %X %Z", |
|
2582 mp->cnts.cnts_cleared)); |
|
2583 FLOD_STATS_ALLOC(i); |
|
2584 |
|
2585 flod_stats_conn_total(&p, &len, "output", mp->cnts.out_total_conn); |
|
2586 i = snprintf(p, len, " "L_DPAT" reports sent\n", |
|
2587 mp->cnts.out_reports+ofp->cnts.out_reports); |
|
2588 FLOD_STATS_ALLOC(i); |
|
2589 flod_stats_conn_cur(&p, &len, ofp, 0); |
|
2590 i = snprintf(p, len, "\n position "L_HPAT, mp->confirm_pos); |
|
2591 FLOD_STATS_ALLOC(i); |
|
2592 |
|
2593 flod_stats_conn_total(&p, &len, "input", mp->cnts.in_total_conn); |
|
2594 i = snprintf(p, len, |
|
2595 " "L_DPAT" reports received "L_DPAT" accepted" |
|
2596 " "L_DPAT" duplicate "L_DPAT" stale\n" |
|
2597 " "L_DPAT" bad whitelist "L_DPAT" not deleted\n", |
|
2598 mp->cnts.total+ofp->cnts.total, |
|
2599 mp->cnts.accepted+ofp->cnts.accepted, |
|
2600 mp->cnts.dup+ofp->lc.dup.cur, |
|
2601 mp->cnts.stale+ofp->lc.stale.cur, |
|
2602 mp->cnts.wlist+ofp->lc.wlist.cur, |
|
2603 mp->cnts.not_deleted+ofp->lc.not_deleted.cur); |
|
2604 FLOD_STATS_ALLOC(i); |
|
2605 flod_stats_conn_cur(&p, &len, ofp, 1); |
|
2606 |
|
2607 if (len <= 0) { |
|
2608 strcpy(buf, FLOD_STATS_TOO_SHORT); |
|
2609 if (loaded) |
|
2610 oflods_clear(); |
|
2611 return ISZ(FLOD_STATS_TOO_SHORT); |
|
2612 } |
|
2613 |
|
2614 if (clear) { |
|
2615 flod_try_again(ofp); |
|
2616 save_flod_cnts(ofp); |
|
2617 ofp->limit_reset = 0; |
|
2618 memset(&mp->cnts, 0, sizeof(mp->cnts)); |
|
2619 mp->cnts.cnts_cleared = db_time.tv_sec; |
|
2620 } |
|
2621 |
|
2622 if (loaded) |
|
2623 oflods_clear(); |
|
2624 return p-buf; |
|
2625 #undef FLOD_STATS_TOO_SHORT |
|
2626 #undef FLOD_STATS_ALLOC |
|
2627 } |