Mercurial > notdcc
comparison dccd/iflod.c @ 0:c7f6b056b673
First import of vendor version
author | Peter Gervai <grin@grin.hu> |
---|---|
date | Tue, 10 Mar 2009 13:49:58 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c7f6b056b673 |
---|---|
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 } |