Mercurial > notdcc
comparison dccd/rl.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 * server rate limiting | |
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.142 $Revision$ | |
40 */ | |
41 | |
42 #include "dccd_defs.h" | |
43 | |
44 | |
45 RL_RATE rl_sub_rate; | |
46 RL_RATE rl_anon_rate; | |
47 RL_RATE rl_all_anon_rate; | |
48 RL_RATE rl_bugs_rate; | |
49 #ifdef RL_MIN_MAX | |
50 static int rl_min_max = RL_MIN_MAX; | |
51 #else | |
52 static int rl_min_max = 0; | |
53 #endif | |
54 | |
55 static RL rl_all_anon; /* limit all or anonymous clients */ | |
56 | |
57 static RL *rl_newest, *rl_oldest; | |
58 static RL **rl_hash; | |
59 static int rl_hash_len; | |
60 static u_char rl_too_many; | |
61 | |
62 time_t clients_cleared; | |
63 | |
64 time_t need_clients_save; | |
65 | |
66 typedef struct ip_bl { | |
67 struct ip_bl *fwd; | |
68 struct in6_addr addr; | |
69 struct in6_addr mask; | |
70 RL_DATA_FG flags; /* subset of RL_FG_* */ | |
71 # define IP_BL_ADDR RL_FG_BL_ADDR | |
72 # define IP_BL_TRACE RL_FG_TRACE | |
73 } IP_BL; | |
74 static IP_BL *ip_bl; | |
75 | |
76 static u_char rl_get(RL **, DCC_CLNT_ID, const struct in6_addr *); | |
77 | |
78 | |
79 | |
80 /* See if an IP address is evil | |
81 * The blacklist is a simple linear list. Entries from the file | |
82 * are added to the front of the file, so that the last matching | |
83 * entry determines the result. | |
84 * This should be sped up if there are ever more than a very few entries */ | |
85 u_char /* 1=blacklisted */ | |
86 ck_ip_bl(RL **rlp, DCC_CLNT_ID clnt_id, const struct in6_addr *pap) | |
87 { | |
88 const IP_BL *bl; | |
89 RL *rl; | |
90 | |
91 for (bl = ip_bl; bl; bl = bl->fwd) { | |
92 if (DCC_IN_BLOCK(*pap, bl->addr, bl->mask)) { | |
93 /* all blacklisted addresses including flooding peers | |
94 * should have rate limit blocks so that they are | |
95 * noticed */ | |
96 rl = *rlp; | |
97 if (!rl) { | |
98 rl_get(rlp, clnt_id, pap); | |
99 rl = *rlp; | |
100 } | |
101 rl->d.flags &= ~(RL_FG_BL_ADDR | RL_FG_TRACE); | |
102 rl->d.flags |= bl->flags; | |
103 rl->d.flags |= RL_FG_CK_BL; | |
104 return (rl->d.flags & RL_FG_BL_ADDR) != 0; | |
105 } | |
106 } | |
107 | |
108 rl = *rlp; | |
109 if (rl) { | |
110 rl->d.flags &= ~(RL_FG_BL_ADDR | RL_FG_TRACE); | |
111 rl->d.flags |= RL_FG_CK_BL; | |
112 } | |
113 return 0; | |
114 } | |
115 | |
116 | |
117 | |
118 static void | |
119 clear_bl(void) | |
120 { | |
121 IP_BL *bl; | |
122 RL *rl; | |
123 | |
124 while ((bl = ip_bl) != 0) { | |
125 ip_bl = ip_bl->fwd; | |
126 dcc_free(bl); | |
127 } | |
128 | |
129 for (rl = rl_newest; rl != 0; rl = rl->older) { | |
130 rl->d.flags &= ~RL_FG_CK_BL; | |
131 } | |
132 } | |
133 | |
134 | |
135 | |
136 void | |
137 check_blacklist_file(void) | |
138 { | |
139 #define BL_NM "blacklist" | |
140 DCC_FNM_LNO_BUF fnm_buf; | |
141 static time_t prev_mtime, next_msg; | |
142 #define BL_RECHECK (2*60*60) | |
143 static int serrno; | |
144 struct stat sb; | |
145 FILE *f; | |
146 char buf[120]; | |
147 IP_BL *bl; | |
148 struct in6_addr addr, mask; | |
149 u_char flags; | |
150 char *p; | |
151 int lno, entries, i; | |
152 | |
153 /* see if the file has changed */ | |
154 if (0 > stat(BL_NM, &sb)) { | |
155 if (errno != ENOENT) { | |
156 prev_mtime = 1; | |
157 if (serrno != errno | |
158 || DB_IS_TIME(next_msg, BL_RECHECK)) { | |
159 serrno = errno; | |
160 next_msg = db_time.tv_sec + BL_RECHECK; | |
161 dcc_error_msg("stat(%s): %s", | |
162 DB_NM2PATH_ERR(BL_NM), | |
163 ERROR_STR()); | |
164 } | |
165 | |
166 } else if (prev_mtime != 0) { | |
167 prev_mtime = 0; | |
168 next_msg = 0; | |
169 dcc_error_msg("%s disappeared", DB_NM2PATH_ERR(BL_NM)); | |
170 } | |
171 clear_bl(); | |
172 return; | |
173 } | |
174 if (prev_mtime == sb.st_mtime | |
175 && (next_msg == 0 || !DB_IS_TIME(next_msg, BL_RECHECK))) | |
176 return; | |
177 | |
178 /* the file has changed, so parse it */ | |
179 clear_bl(); | |
180 prev_mtime = 1; | |
181 next_msg = 0; | |
182 f = fopen(BL_NM, "r"); | |
183 if (!f) { | |
184 if (serrno != errno) { | |
185 serrno = errno; | |
186 next_msg = db_time.tv_sec + BL_RECHECK; | |
187 dcc_error_msg("fopen(%s): %s", | |
188 DB_NM2PATH_ERR(BL_NM), ERROR_STR()); | |
189 } | |
190 return; | |
191 } | |
192 if (0 > fstat(fileno(f), &sb)) { | |
193 if (serrno != errno) { | |
194 serrno = errno; | |
195 next_msg = db_time.tv_sec + BL_RECHECK; | |
196 dcc_error_msg("fstat(%s): %s", | |
197 DB_NM2PATH_ERR(BL_NM), ERROR_STR()); | |
198 } | |
199 return; | |
200 } | |
201 prev_mtime = sb.st_mtime; | |
202 | |
203 entries = 0; | |
204 for (lno = 1; ; ++lno) { | |
205 if (!fgets(buf, sizeof(buf), f)) { | |
206 if (ferror(f) && serrno != errno) { | |
207 serrno = errno; | |
208 next_msg = db_time.tv_sec + BL_RECHECK; | |
209 dcc_error_msg("fgets(%s): %s", | |
210 DB_NM2PATH_ERR(BL_NM), | |
211 ERROR_STR()); | |
212 } | |
213 break; | |
214 } | |
215 /* reject lines that are too long */ | |
216 i = strlen(buf); | |
217 if (buf[i-1] != '\n') { | |
218 next_msg = db_time.tv_sec + BL_RECHECK; | |
219 dcc_error_msg("syntax error%s", | |
220 fnm_lno(&fnm_buf, | |
221 DB_NM2PATH_ERR(BL_NM), lno)); | |
222 break; | |
223 } | |
224 | |
225 /* ignore leading blanks, comments, and blank lines */ | |
226 p = strchr(buf, '#'); | |
227 if (p) | |
228 *p = '\0'; | |
229 p = buf; | |
230 p += strspn(p, DCC_WHITESPACE); | |
231 if (*p == '\0') | |
232 continue; | |
233 | |
234 flags = IP_BL_ADDR; | |
235 for (;;) { | |
236 i = strspn(p, " \t"); | |
237 if (i != 0) { | |
238 p += i; | |
239 continue; | |
240 } | |
241 if (!CLITCMP(p, "trace")) { | |
242 p += LITZ("trace"); | |
243 if (*p == ',') | |
244 ++p; | |
245 flags |= IP_BL_TRACE; | |
246 continue; | |
247 } | |
248 if (!CLITCMP(p, "ok")) { | |
249 p += LITZ("ok"); | |
250 if (*p == ',') | |
251 ++p; | |
252 flags &= ~IP_BL_ADDR; | |
253 continue; | |
254 } | |
255 if (!CLITCMP(p, "bad")) { | |
256 p += LITZ("bad"); | |
257 if (*p == ',') | |
258 ++p; | |
259 flags |= IP_BL_ADDR; | |
260 continue; | |
261 } | |
262 break; | |
263 } | |
264 | |
265 i = dcc_str2cidr(dcc_emsg, &addr, &mask, 0, p, BL_NM, lno); | |
266 if (i <= 0) { | |
267 if (i < 0) | |
268 dcc_error_msg("%s", dcc_emsg); | |
269 else | |
270 dcc_error_msg("syntax error%s", | |
271 fnm_lno(&fnm_buf, | |
272 DB_NM2PATH_ERR(BL_NM), | |
273 lno)); | |
274 next_msg = db_time.tv_sec + BL_RECHECK; | |
275 continue; | |
276 } | |
277 | |
278 bl = dcc_malloc(sizeof(*bl)); | |
279 bl->addr = addr; | |
280 bl->mask = mask; | |
281 bl->flags = flags; | |
282 bl->fwd = ip_bl; | |
283 ip_bl = bl; | |
284 if (++entries > 100) { | |
285 dcc_error_msg("too many entries in %s", | |
286 DB_NM2PATH_ERR(BL_NM)); | |
287 next_msg = db_time.tv_sec + BL_RECHECK; | |
288 break; | |
289 } | |
290 } | |
291 fclose(f); | |
292 | |
293 if (entries) | |
294 dcc_trace_msg("read %d entries from %s", | |
295 entries, DB_NM2PATH_ERR(BL_NM)); | |
296 | |
297 #undef BL_NM | |
298 #undef BL_RECHECK | |
299 } | |
300 | |
301 | |
302 | |
303 static inline RL ** | |
304 rl_hash_fnc(DCC_CLNT_ID clnt_id, const struct in6_addr *addr) | |
305 { | |
306 u_int32_t sum; | |
307 | |
308 sum = clnt_id; | |
309 sum += addr->s6_addr32[0]; | |
310 sum += addr->s6_addr32[1]; | |
311 sum += addr->s6_addr32[2]; | |
312 sum += addr->s6_addr32[3]; | |
313 return &rl_hash[mhash(sum, rl_hash_len)]; | |
314 } | |
315 | |
316 | |
317 | |
318 /* link a rate limit block into its hash bin */ | |
319 static inline void | |
320 rl_hash_link(RL *rl, /* new block */ | |
321 RL **bin) /* its bin */ | |
322 { | |
323 RL *rl2; | |
324 | |
325 rl->bin = bin; | |
326 rl2 = *bin; | |
327 rl->hfwd = rl2; | |
328 if (rl2) | |
329 rl2->hbak = rl; | |
330 *bin = rl; | |
331 } | |
332 | |
333 | |
334 | |
335 static int | |
336 set_rl_min_max(void) | |
337 { | |
338 int i; | |
339 | |
340 if (rl_min_max == 0) { | |
341 /* allow RL_MIN_MAX_DEF for 512 MByte | |
342 * RL_MIN_MAX_DEF*2 for 1 GByte | |
343 * ... | |
344 * RL_MIN_MAX_DEF*16 for 8 GByte | |
345 */ | |
346 i = (db_max_rss + 512*1024*1024-1) / (512*1024*1024); | |
347 if (i <= 0) | |
348 i = 1; | |
349 rl_min_max = RL_MIN_MAX_DEF * i; | |
350 } | |
351 if (rl_min_max > RL_MIN_MAX_MAX) | |
352 rl_min_max = RL_MIN_MAX_MAX; | |
353 | |
354 return rl_min_max; | |
355 } | |
356 | |
357 | |
358 | |
359 static void | |
360 rl_expand(int new_len) | |
361 { | |
362 RL *rl; | |
363 int j; | |
364 | |
365 /* create a bunch of rate limit blocks */ | |
366 if (new_len < 64) | |
367 new_len = 64; | |
368 | |
369 if (rl_hash_len+new_len > set_rl_min_max()) { | |
370 if (rl_hash_len > rl_min_max) | |
371 dcc_logbad(EX_SOFTWARE, "impossible # of RL blocks"); | |
372 new_len = rl_min_max - rl_hash_len; | |
373 } | |
374 | |
375 if (rl_hash_len != 0) | |
376 dcc_trace_msg("increase from %d to %d RL blocks", | |
377 rl_hash_len, rl_hash_len+new_len); | |
378 | |
379 rl = dcc_malloc(new_len*sizeof(*rl)); | |
380 if (!rl) | |
381 dcc_logbad(EX_OSERR, "malloc(%d RL's) failed", new_len); | |
382 memset(rl, 0, new_len*sizeof(*rl)); | |
383 j = 0; | |
384 if (!rl_oldest) { | |
385 rl_oldest = rl; | |
386 rl_newest = rl; | |
387 ++rl; | |
388 ++j; | |
389 } | |
390 while (j < new_len) { /* make the new blocks oldest */ | |
391 rl_oldest->older = rl; | |
392 rl->newer = rl_oldest; | |
393 rl_oldest = rl; | |
394 ++rl; | |
395 ++j; | |
396 } | |
397 | |
398 /* Rebuild and expand the hash table. | |
399 * The hash table is an array of pointers to rate limit blocks. */ | |
400 if (rl_hash) | |
401 dcc_free(rl_hash); | |
402 rl_hash_len += new_len; | |
403 rl_hash = dcc_malloc(rl_hash_len*sizeof(*rl_hash)); | |
404 memset(rl_hash, 0, rl_hash_len*sizeof(RL *)); | |
405 | |
406 /* copy the active blocks to the new hash table */ | |
407 for (rl = rl_newest; rl; rl = rl->older) { | |
408 if (!rl->bin) | |
409 continue; | |
410 rl->hbak = 0; | |
411 rl_hash_link(rl, | |
412 rl_hash_fnc(rl->d.clnt_id, &rl->d.clnt_addr)); | |
413 } | |
414 } | |
415 | |
416 | |
417 | |
418 /* age a rate limit */ | |
419 static void | |
420 rl_age(RL *rl, const RL_RATE *credits) | |
421 { | |
422 time_t secs; | |
423 | |
424 secs = (db_time.tv_sec - rl->d.last_used); | |
425 | |
426 /* only prevent overflow if no time has passed */ | |
427 if (secs <= 0) { | |
428 if (rl->d.request_credits < credits->lo) | |
429 rl->d.request_credits = credits->lo; | |
430 if (rl->d.bug_credits < rl_bugs_rate.lo) | |
431 rl->d.bug_credits = rl_bugs_rate.lo; | |
432 } | |
433 | |
434 rl->d.last_used = db_time.tv_sec; | |
435 | |
436 /* reset idle counters */ | |
437 if (secs >= RL_AVG_SECS || secs < 0) { | |
438 rl->d.bug_credits = rl_bugs_rate.hi; | |
439 rl->d.request_credits = credits->hi; | |
440 return; | |
441 } | |
442 | |
443 rl->d.request_credits += secs * credits->per_sec; | |
444 if (rl->d.request_credits > credits->hi) | |
445 rl->d.request_credits = credits->hi; | |
446 | |
447 rl->d.bug_credits += secs * rl_bugs_rate.per_sec; | |
448 if (rl->d.bug_credits > rl_bugs_rate.hi) | |
449 rl->d.bug_credits = rl_bugs_rate.hi; | |
450 } | |
451 | |
452 | |
453 | |
454 /* age and increment a rate limit */ | |
455 void | |
456 rl_inc(RL *rl, const RL_RATE *credits) | |
457 { | |
458 rl_age(rl, credits); | |
459 ++rl->d.requests; /* increase total count of requests */ | |
460 rl->d.request_credits -= RL_SCALE; /* charge for this request */ | |
461 } | |
462 | |
463 | |
464 | |
465 /* update the request rate */ | |
466 static void | |
467 rl_avg_requests_age(RL *rl, u_char force) | |
468 { | |
469 time_t secs; | |
470 double trim; | |
471 | |
472 if (rl->d.requests_avg_aged > db_time.tv_sec) { | |
473 /* clear things if time has jumped backwards */ | |
474 rl->d.requests_avg_start = 0; | |
475 | |
476 } else if (rl->d.requests_avg_aged + RL_AVG_UPDATE > db_time.tv_sec | |
477 && !force) { | |
478 /* do nothing if it is not yet time and we are not forced */ | |
479 return; | |
480 } | |
481 | |
482 secs = db_time.tv_sec - rl->d.requests_avg_start; | |
483 if (secs > RL_AVG_TERM*2) { | |
484 /* clear if too long since the average was updated */ | |
485 rl->d.requests_old = rl->d.requests; | |
486 rl->d.requests_avg_total = 0; | |
487 rl->d.nops_old = rl->d.nops; | |
488 rl->d.nops_avg_total = 0; | |
489 rl->d.requests_avg_start = db_time.tv_sec; | |
490 trim = 1.0; | |
491 | |
492 } else if (secs < 24*60*60) { | |
493 /* we have no average for the first day */ | |
494 trim = 1.0; | |
495 | |
496 } else if (secs <= RL_AVG_TERM) { | |
497 trim = (60*60*24 * 1.0) / secs; | |
498 | |
499 } else { | |
500 /* trim old counts if we have been averaging long enough */ | |
501 trim = secs - RL_AVG_TERM; | |
502 trim /= (RL_AVG_TERM*2); | |
503 rl->d.requests_avg_total -= (rl->d.requests_avg_total * trim); | |
504 rl->d.nops_avg_total -= (rl->d.nops_avg_total * trim); | |
505 rl->d.requests_avg_start = db_time.tv_sec - RL_AVG_TERM; | |
506 trim = (60*60*24 * 1.0) / RL_AVG_TERM; | |
507 } | |
508 | |
509 rl->d.requests_avg_total += rl->d.requests - rl->d.requests_old; | |
510 rl->d.requests_old = rl->d.requests; | |
511 rl->d.nops_avg_total += rl->d.nops - rl->d.nops_old; | |
512 rl->d.nops_old = rl->d.nops; | |
513 | |
514 rl->d.requests_avg = rl->d.requests_avg_total * trim; | |
515 rl->d.nops_avg = rl->d.nops_avg_total * trim; | |
516 | |
517 rl->d.requests_avg_aged = db_time.tv_sec; | |
518 } | |
519 | |
520 | |
521 | |
522 static void | |
523 rl_unref(RL *rl) | |
524 { | |
525 if (rl->newer) { | |
526 rl->newer->older = rl->older; | |
527 } else if (rl_newest == rl) { | |
528 rl_newest = rl->older; | |
529 } | |
530 | |
531 if (rl->older) { | |
532 rl->older->newer = rl->newer; | |
533 } else if (rl_oldest == rl) { | |
534 rl_oldest = rl->newer; | |
535 } | |
536 | |
537 rl->newer = 0; | |
538 rl->older = 0; | |
539 } | |
540 | |
541 | |
542 | |
543 static void | |
544 rl_ref(RL *rl) | |
545 { | |
546 RL **bin, *bin_head; | |
547 | |
548 if (rl_newest == rl) | |
549 return; | |
550 | |
551 /* make it the most recently used block */ | |
552 rl_unref(rl); | |
553 rl->older = rl_newest; | |
554 rl_newest->newer = rl; | |
555 rl_newest = rl; | |
556 | |
557 /* move it to the head of its hash bin if it is not already there */ | |
558 bin = rl->bin; | |
559 bin_head = *bin; | |
560 if (bin_head == rl) | |
561 return; | |
562 *bin = rl; | |
563 if (rl->hfwd) | |
564 rl->hfwd->hbak = rl->hbak; | |
565 rl->hbak->hfwd = rl->hfwd; /* we know there is a predecessor */ | |
566 rl->hbak = 0; | |
567 rl->hfwd = bin_head; | |
568 bin_head->hbak = rl; | |
569 } | |
570 | |
571 | |
572 | |
573 /* get a free rate limit block, recycling the oldest block if necessary */ | |
574 static RL * /* block to use or 0 to make more */ | |
575 rl_get_free(void) | |
576 { | |
577 RL *rl, *rl2; | |
578 time_t stale; | |
579 | |
580 for (rl = rl_oldest; rl != 0; rl = rl->newer) { | |
581 if (rl->ref_cnt) | |
582 continue; | |
583 | |
584 /* Found oldest free block */ | |
585 if (rl->d.last_used != 0) { | |
586 /* oldest free block has been used and so we | |
587 * must recycle something */ | |
588 stale = db_time.tv_sec; | |
589 /* keep many blocks until we get enough to | |
590 * worry about a denial of service attack */ | |
591 if (rl_hash_len < set_rl_min_max()) | |
592 stale -= CLIENTS_SAVE_AGE; | |
593 else | |
594 stale -= RL_LIFE_SECS; | |
595 /* make more if the oldest is new | |
596 * and we don't have too many */ | |
597 if (rl->d.last_used >= stale | |
598 && rl_hash_len < rl_min_max) | |
599 return 0; | |
600 | |
601 /* We cannot make more because we have too many blocks. | |
602 * | |
603 * Notice if we are about to recycle a block that is | |
604 * not obsolete. | |
605 * Try to find an old, little used block, so that we | |
606 * do not recycle a block used by a busy client that | |
607 * has only paused */ | |
608 if (rl->d.last_used > clients_cleared) { | |
609 rl_too_many = 1; | |
610 stale = db_time.tv_sec - CLIENTS_AGE/2; | |
611 for (rl2 = rl; | |
612 rl2 && rl2->d.last_used <= stale; | |
613 rl2 = rl2->newer) { | |
614 /* avoid forgeting bad guys */ | |
615 if (rl->d.flags & RL_FG_BLS) | |
616 continue; | |
617 /* take the first tiny client found */ | |
618 if (RL_REQUESTS_AVG(&rl2->d) < 100) { | |
619 rl = rl2; | |
620 break; | |
621 } | |
622 } | |
623 } | |
624 } | |
625 | |
626 /* Recycle a block by first removing it from its hash chain */ | |
627 if (rl->hfwd) | |
628 rl->hfwd->hbak = rl->hbak; | |
629 if (rl->hbak) | |
630 rl->hbak->hfwd = rl->hfwd; | |
631 else if (rl->bin) /* It will not be on a hash chain */ | |
632 *rl->bin = rl->hfwd; /* if it has never been used */ | |
633 rl_unref(rl); | |
634 memset(rl, 0, sizeof(*rl)); | |
635 rl_avg_requests_age(rl, 1); | |
636 return rl; | |
637 } | |
638 | |
639 /* There are no free blocks that are old enough to recycle, | |
640 * so tell the caller to make more */ | |
641 return 0; | |
642 } | |
643 | |
644 | |
645 | |
646 /* get a rate limit block based on the IP address of the sender */ | |
647 static u_char /* 1=existing block */ | |
648 rl_get(RL **rlp, DCC_CLNT_ID clnt_id, const struct in6_addr *cap) | |
649 { | |
650 RL *rl, **bin; | |
651 | |
652 if (!rl_hash_len) | |
653 rl_expand(queue_max); | |
654 | |
655 bin = rl_hash_fnc(clnt_id, cap); | |
656 for (rl = *bin; rl; rl = rl->hfwd) { | |
657 if (rl->d.clnt_id != clnt_id | |
658 || memcmp(&rl->d.clnt_addr, cap, sizeof(rl->d.clnt_addr))) | |
659 continue; | |
660 rl_ref(rl); /* found it, so make it newest */ | |
661 rl_avg_requests_age(rl, 0); | |
662 *rlp = rl; | |
663 return 1; | |
664 } | |
665 | |
666 rl = rl_get_free(); | |
667 if (!rl) { | |
668 /* when we are out of rate limiting blocks, make more */ | |
669 rl_expand(queue_max); | |
670 /* which makes a new rate limit hash table which makes our | |
671 * pointer into the old hash table bogus */ | |
672 rl = rl_get_free(); | |
673 if (!rl) | |
674 dcc_logbad(EX_SOFTWARE, "no available RL blocks"); | |
675 bin = rl_hash_fnc(clnt_id, cap); | |
676 } | |
677 rl_hash_link(rl, bin); | |
678 rl_ref(rl); | |
679 rl->d.clnt_addr = *cap; | |
680 rl->d.clnt_id = clnt_id; | |
681 *rlp = rl; | |
682 return 0; | |
683 } | |
684 | |
685 | |
686 | |
687 /* get a rate limit block for a job */ | |
688 static RL * | |
689 rl_get_q(QUEUE *q, DCC_CLNT_ID id) | |
690 { | |
691 struct in6_addr clnt_addr; | |
692 const struct in6_addr *cap; | |
693 RL *rl; | |
694 | |
695 if (q->clnt_su.sa.sa_family == AF_INET6) { | |
696 cap = &q->clnt_su.ipv6.sin6_addr; | |
697 } else { | |
698 dcc_ipv4toipv6(&clnt_addr, q->clnt_su.ipv4.sin_addr); | |
699 cap = &clnt_addr; | |
700 } | |
701 rl_get(&q->rl, id, cap); | |
702 rl = q->rl; | |
703 ++rl->ref_cnt; | |
704 | |
705 return rl; | |
706 } | |
707 | |
708 | |
709 | |
710 static u_char | |
711 clients_write(FILE *f, const void *buf, int buf_len) | |
712 { | |
713 if (1 == fwrite(buf, buf_len, 1, f)) | |
714 return 1; | |
715 dcc_error_msg("fwrite(%s): %s", | |
716 DB_NM2PATH_ERR(CLIENTS_NM()), ERROR_STR()); | |
717 fclose(f); | |
718 return 0; | |
719 } | |
720 | |
721 | |
722 | |
723 /* dump the rate limit blocks into a file */ | |
724 void | |
725 clients_save(void) | |
726 { | |
727 int fd; | |
728 FILE *f; | |
729 CLIENTS_HEADER header; | |
730 RL *rl; | |
731 | |
732 need_clients_save = db_time.tv_sec + CLIENTS_SAVE_SECS; | |
733 | |
734 /* prevent evil games with symbolic links */ | |
735 unlink(CLIENTS_NM()); | |
736 fd = open(CLIENTS_NM(), O_WRONLY|O_CREAT|O_EXCL, 0644); | |
737 if (fd < 0) { | |
738 dcc_error_msg("open(%s): %s", | |
739 DB_NM2PATH_ERR(CLIENTS_NM()), ERROR_STR()); | |
740 return; | |
741 } | |
742 f = fdopen(fd, "w"); | |
743 | |
744 memset(&header, 0, sizeof(header)); | |
745 BUFCPY(header.magic, CLIENTS_MAGIC(grey_on)); | |
746 header.now = db_time.tv_sec; | |
747 header.cleared = clients_cleared; | |
748 if (anon_off) { | |
749 header.anon_delay_us = DCC_ANON_DELAY_FOREVER; | |
750 } else { | |
751 header.anon_delay_us = anon_delay_us; | |
752 header.anon_delay_inflate = anon_delay_inflate; | |
753 } | |
754 if (clients_cleared > db_time.tv_sec) /* fix time jump */ | |
755 clients_cleared = db_time.tv_sec; | |
756 | |
757 for (rl = rl_oldest; rl != 0; rl = rl->newer) { | |
758 if (rl->d.last_used == 0) | |
759 continue; | |
760 if (rl->d.last_used > db_time.tv_sec) /* fix time jump */ | |
761 rl->d.last_used = db_time.tv_sec; | |
762 ++header.hash_len; | |
763 } | |
764 if (!clients_write(f, &header, sizeof(header))) | |
765 return; | |
766 | |
767 for (rl = rl_oldest; rl != 0; rl = rl->newer) { | |
768 if (rl->d.last_used == 0) | |
769 continue; | |
770 if (!clients_write(f, &rl->d, sizeof(rl->d))) | |
771 return; | |
772 } | |
773 fclose(f); | |
774 } | |
775 | |
776 | |
777 | |
778 static u_char | |
779 clients_read(FILE *f, void *buf, int buf_len) | |
780 { | |
781 int i; | |
782 | |
783 i = fread(buf, buf_len, 1, f); | |
784 if (i == 1) | |
785 return 1; | |
786 | |
787 if (feof(f)) | |
788 return 0; | |
789 | |
790 dcc_error_msg("fread(%s): %s", | |
791 DB_NM2PATH_ERR(CLIENTS_NM()), ERROR_STR()); | |
792 fclose(f); | |
793 return 0; | |
794 } | |
795 | |
796 | |
797 | |
798 /* load the rate limit blocks from a previous instance */ | |
799 void | |
800 clients_load(void) | |
801 { | |
802 struct stat sb; | |
803 int fd; | |
804 FILE *f; | |
805 CLIENTS_HEADER header; | |
806 RL_DATA data; | |
807 RL *rl; | |
808 int rl_size, total, used, anon; | |
809 # define BAD_FILE() ((0 > rename(CLIENTS_NM(), BAD_CLIENTS_NM())) \ | |
810 ? unlink(CLIENTS_NM()) : 0) | |
811 | |
812 clients_cleared = db_time.tv_sec; | |
813 | |
814 fd = open(CLIENTS_NM(), O_RDONLY, 0); | |
815 if (fd < 0) { | |
816 if (errno != ENOENT) | |
817 dcc_error_msg("open(%s): %s", | |
818 DB_NM2PATH_ERR(CLIENTS_NM()), | |
819 ERROR_STR()); | |
820 else if (db_debug) | |
821 dcc_trace_msg("open(%s): %s", | |
822 DB_NM2PATH_ERR(CLIENTS_NM()), | |
823 ERROR_STR()); | |
824 BAD_FILE(); | |
825 return; | |
826 } | |
827 f = fdopen(fd, "r"); | |
828 if (0 > fstat(fd, &sb)) { | |
829 dcc_error_msg("stat(%s): %s", | |
830 DB_NM2PATH_ERR(CLIENTS_NM()), ERROR_STR()); | |
831 BAD_FILE(); | |
832 fclose(f); | |
833 return; | |
834 } | |
835 if ((int)sb.st_size < ISZ(header)) { | |
836 dcc_error_msg("%s has invalid size %d", | |
837 DB_NM2PATH_ERR(CLIENTS_NM()), (int)sb.st_size); | |
838 BAD_FILE(); | |
839 fclose(f); | |
840 return; | |
841 } | |
842 | |
843 if (!clients_read(f, &header, sizeof(header))) | |
844 return; | |
845 | |
846 /* try to save strange files but quietly ignore old files */ | |
847 if (strcmp(header.magic, CLIENTS_MAGIC(grey_on))) { | |
848 if (strncmp(header.magic, CLIENTS_MAGIC_BASE(grey_on), | |
849 strlen(CLIENTS_MAGIC_BASE(grey_on)))) { | |
850 dcc_error_msg("unrecognized magic in %s", | |
851 DB_NM2PATH_ERR(CLIENTS_NM())); | |
852 BAD_FILE(); | |
853 } | |
854 fclose(f); | |
855 return; | |
856 } | |
857 | |
858 if (((sb.st_size - ISZ(header)) % ISZ(RL_DATA)) != 0) { | |
859 dcc_error_msg("%s has invalid size %d", | |
860 DB_NM2PATH_ERR(CLIENTS_NM()), (int)sb.st_size); | |
861 BAD_FILE(); | |
862 fclose(f); | |
863 return; | |
864 } | |
865 | |
866 if (header.hash_len > RL_MIN_MAX_MAX) { | |
867 dcc_trace_msg("unrecognized hash_len=%d in %s", | |
868 header.hash_len, DB_NM2PATH_ERR(CLIENTS_NM())); | |
869 fclose(f); | |
870 return; | |
871 } | |
872 if (header.cleared > db_time.tv_sec) { | |
873 dcc_trace_msg("bad time %d in %s", | |
874 (int)header.cleared, | |
875 DB_NM2PATH_ERR(CLIENTS_NM())); | |
876 BAD_FILE(); | |
877 fclose(f); | |
878 return; | |
879 } | |
880 clients_cleared = header.cleared; | |
881 rl_size = header.hash_len; | |
882 rl_size += queue_max - (rl_size % queue_max); | |
883 rl_size = min(rl_size, set_rl_min_max()); | |
884 rl_expand(rl_size); | |
885 | |
886 for (total = 0, used = 0, anon = 0; ; ++total) { | |
887 if (!clients_read(f, &data, sizeof(data))) | |
888 break; | |
889 if (data.last_used > db_time.tv_sec) { | |
890 dcc_error_msg("badly timestamped entry in %s", | |
891 DB_NM2PATH_ERR(CLIENTS_NM())); | |
892 break; | |
893 } | |
894 | |
895 if (rl_get(&rl, data.clnt_id, &data.clnt_addr)) { | |
896 dcc_error_msg("duplicate entry in %s", | |
897 DB_NM2PATH_ERR(CLIENTS_NM())); | |
898 break; | |
899 } | |
900 | |
901 rl->d = data; | |
902 rl->d.flags &= ~RL_FG_CK_BL; /* check current blacklist */ | |
903 ++used; | |
904 if ((rl->d.flags & RL_FG_ANON) | |
905 && rl->d.last_used >= db_time.tv_sec-2*24*60*60) | |
906 ++anon; | |
907 } | |
908 fclose(f); | |
909 | |
910 if (used != total | |
911 || total > 500 | |
912 || anon != 0 | |
913 || db_debug) { | |
914 if (anon != 0) | |
915 dcc_trace_msg("used %d of %d RL blocks," | |
916 " %d for recent anonymous clients" | |
917 " in %s", | |
918 used, total, anon, | |
919 DB_NM2PATH_ERR(CLIENTS_NM())); | |
920 else | |
921 dcc_trace_msg("used %d of %d RL blocks in %s", | |
922 used, total, | |
923 DB_NM2PATH_ERR(CLIENTS_NM())); | |
924 } | |
925 } | |
926 | |
927 | |
928 | |
929 /* pack up a 32 bit int as part of an answer to a `cdcc clients` request */ | |
930 static u_char * | |
931 client_pack4(u_char *cp, | |
932 u_int32_t v) | |
933 { | |
934 while (v > 0x7f) { | |
935 *cp++ = v | 0x80; | |
936 v >>= 7; | |
937 } | |
938 *cp++ = v; | |
939 return cp; | |
940 } | |
941 | |
942 | |
943 | |
944 static int | |
945 client_pack(u_char *cp0, /* pack into this byte first */ | |
946 u_char **flagsp, /* pointer to packed flags */ | |
947 u_char flags, /* DCC_ADMN_RESP_CLIENTS_* */ | |
948 DCC_CLNT_ID clnt_id, | |
949 time_t last_used, /* skip count if ..._CLIENTS_SKIP */ | |
950 int requests, | |
951 int nops, | |
952 u_char vers, | |
953 const struct in6_addr *clnt_addr) | |
954 { | |
955 u_char *cp; | |
956 | |
957 cp = cp0; | |
958 *flagsp = cp0; /* announce location of these flags */ | |
959 if (vers != 0) | |
960 flags |= DCC_ADMN_RESP_CLIENTS_VERS; | |
961 *cp++ = flags; | |
962 if (flags & DCC_ADMN_RESP_CLIENTS_VERS) | |
963 *cp++ = vers; | |
964 *cp++ = last_used >> 24; | |
965 *cp++ = last_used >> 16; | |
966 *cp++ = last_used >> 8; | |
967 *cp++ = last_used; | |
968 if (clnt_id == DCC_ID_ANON) { | |
969 *cp0 |= DCC_ADMN_RESP_CLIENTS_ID1; | |
970 } else { | |
971 cp = client_pack4(cp, clnt_id); | |
972 } | |
973 cp = client_pack4(cp, requests); | |
974 cp = client_pack4(cp, nops); | |
975 if (!clnt_addr) { | |
976 memset(cp, 0, 4); | |
977 cp += 4; | |
978 } else if (DCC_IN6_ADDR_V4MAPPED(clnt_addr)) { | |
979 memcpy(cp, &clnt_addr->s6_addr32[3], 4); | |
980 cp += 4; | |
981 } else { | |
982 *cp0 |= DCC_ADMN_RESP_CLIENTS_IPV6; | |
983 memcpy(cp, clnt_addr->s6_addr32, 16); | |
984 cp += 16; | |
985 } | |
986 return cp-cp0; | |
987 } | |
988 | |
989 | |
990 | |
991 static int | |
992 client_pack_skip(u_char *cp, u_char **prev_flags, u_int skipped) | |
993 { | |
994 return client_pack(cp, prev_flags, DCC_ADMN_RESP_CLIENTS_SKIP, | |
995 DCC_ID_ANON, skipped, 0, 0, 0, 0); | |
996 } | |
997 | |
998 | |
999 | |
1000 /* get list of clients, | |
1001 * treating clients with the same ID as if they were all the same system */ | |
1002 void | |
1003 clients_get_id(DCC_ADMN_RESP_VAL *val, | |
1004 int *lenp, /* buffer length */ | |
1005 u_int offset, /* skip this many newer entries */ | |
1006 int thold, /* skip clients with fewer requests */ | |
1007 u_char req_flags, | |
1008 const struct in6_addr *addr6, | |
1009 const struct in6_addr *mask6) | |
1010 { | |
1011 RL *rl, *rl2; | |
1012 u_char *skip; | |
1013 int requests, nops; | |
1014 u_char *prev_flags; /* flags in previous record */ | |
1015 u_int skipped; | |
1016 int len, len_lim; | |
1017 | |
1018 if (offset == 0) | |
1019 need_clients_save = 0; | |
1020 | |
1021 prev_flags = 0; | |
1022 len_lim = *lenp; | |
1023 len_lim <<= DCC_ADMIN_RESP_CLIENTS_SHIFT; | |
1024 if (len_lim > ISZ(*val)) | |
1025 len_lim = ISZ(*val); | |
1026 skip = 0; | |
1027 skipped = 0; | |
1028 len = 0; | |
1029 if (!thold) | |
1030 thold = 1; | |
1031 for (rl = rl_newest; rl != 0; rl = rl->older) | |
1032 rl->d.flags &= ~RL_FG_MARKED; | |
1033 | |
1034 for (rl = rl_newest; ; rl = rl->older) { | |
1035 if (!rl) { | |
1036 /* there are no more, so | |
1037 * mark the previous record as the last */ | |
1038 if (prev_flags) | |
1039 *prev_flags |= DCC_ADMN_RESP_CLIENTS_LAST; | |
1040 break; | |
1041 } | |
1042 | |
1043 if (rl->d.flags & RL_FG_MARKED) | |
1044 continue; | |
1045 | |
1046 if (addr6 && !DCC_IN_BLOCK(rl->d.clnt_addr, *addr6, *mask6)) { | |
1047 rl->d.flags |= RL_FG_MARKED; | |
1048 continue; | |
1049 } | |
1050 | |
1051 if (rl->d.last_used == 0) { | |
1052 if (!(req_flags & DCC_AOP_CLIENTS_AVG)) | |
1053 continue; | |
1054 /* report clients that have been quiet today if | |
1055 * they were active yesterday */ | |
1056 rl_avg_requests_age(rl, 0); | |
1057 if (RL_REQUESTS_AVG(&rl->d) == 0 | |
1058 && RL_NOPS_AVG(&rl->d) == 0) | |
1059 continue; | |
1060 } | |
1061 | |
1062 requests = 0; | |
1063 nops = 0; | |
1064 for (rl2 = rl; rl2 != 0; rl2 = rl2->older) { | |
1065 if (rl2->d.clnt_id != rl->d.clnt_id) | |
1066 continue; | |
1067 rl2->d.flags |= RL_FG_MARKED; | |
1068 if (addr6 | |
1069 && !DCC_IN_BLOCK(rl->d.clnt_addr, *addr6, *mask6)) | |
1070 continue; | |
1071 if (req_flags & DCC_AOP_CLIENTS_AVG) { | |
1072 rl_avg_requests_age(rl2, 0); | |
1073 requests += RL_REQUESTS_AVG(&rl2->d); | |
1074 nops += RL_NOPS_AVG(&rl2->d); | |
1075 } else { | |
1076 requests += rl2->d.requests; | |
1077 nops += rl2->d.nops; | |
1078 } | |
1079 } | |
1080 | |
1081 /* get the part of the list that cdcc wants */ | |
1082 if (offset != 0) { | |
1083 --offset; | |
1084 continue; | |
1085 } | |
1086 | |
1087 if (requests < thold) { | |
1088 /* The threshold might be larger on the next request | |
1089 * from cdcc, so tell cdcc the number skipped for | |
1090 * the threshold this time. | |
1091 * Tell cdcc by insertint a fake entry. */ | |
1092 if (!skip) { | |
1093 skip = &val->clients[len]; | |
1094 len += client_pack_skip(skip, &prev_flags, 0); | |
1095 } | |
1096 ++skipped; | |
1097 continue; | |
1098 } | |
1099 | |
1100 /* stop at end of buffer | |
1101 * check only after skipping boring records for the common | |
1102 * case of the last 10,000 records missing the threshold */ | |
1103 if (len+DCC_ADMN_RESP_CLIENTS_MAX_SIZE > len_lim) | |
1104 break; | |
1105 | |
1106 len += client_pack(&val->clients[len], &prev_flags, 0, | |
1107 rl->d.clnt_id, rl->d.last_used, | |
1108 requests, nops, 0, 0); | |
1109 } | |
1110 if (skipped) | |
1111 client_pack_skip(skip, &prev_flags, skipped); | |
1112 *lenp = len; | |
1113 } | |
1114 | |
1115 | |
1116 | |
1117 /* get a list of the most recent clients */ | |
1118 int /* +/- number of clients */ | |
1119 clients_get(DCC_ADMN_RESP_VAL *val, | |
1120 int *lenp, /* buffer length */ | |
1121 u_int offset, /* skip this many newer entries */ | |
1122 int thold, /* skip clients with fewer requests */ | |
1123 u_char req_flags, | |
1124 const struct in6_addr *addr6, | |
1125 const struct in6_addr *mask6) | |
1126 { | |
1127 RL *rl; | |
1128 u_char *skip; | |
1129 int requests, nops; | |
1130 u_char prev_vers, vers; | |
1131 u_char *prev_flags; /* flags in previous record */ | |
1132 int skipped, total, len, len_lim; | |
1133 | |
1134 if (offset == 0) | |
1135 need_clients_save = 0; | |
1136 | |
1137 prev_flags = 0; | |
1138 if (!val || !lenp) { | |
1139 len_lim = 0; | |
1140 } else { | |
1141 len_lim = *lenp; | |
1142 len_lim <<= DCC_ADMIN_RESP_CLIENTS_SHIFT; | |
1143 if (len_lim > ISZ(*val)) | |
1144 len_lim = ISZ(*val); | |
1145 } | |
1146 if (!thold) | |
1147 thold = 1; | |
1148 prev_vers = 0; | |
1149 skip = 0; | |
1150 skipped = 0; | |
1151 total = 0; | |
1152 len = 0; | |
1153 for (rl = rl_newest; ; rl = rl->older) { | |
1154 if (!rl) { | |
1155 /* there are no more, so | |
1156 * mark the previous record as the last */ | |
1157 if (prev_flags) | |
1158 *prev_flags |= DCC_ADMN_RESP_CLIENTS_LAST; | |
1159 break; | |
1160 } | |
1161 | |
1162 if (rl->d.last_used == 0) { | |
1163 if (!(req_flags & DCC_AOP_CLIENTS_AVG)) | |
1164 continue; | |
1165 /* report clients that have been quiet today if | |
1166 * they were active yesterday | |
1167 * and we are reporting averages */ | |
1168 rl_avg_requests_age(rl, 0); | |
1169 if (RL_REQUESTS_AVG(&rl->d) == 0 | |
1170 && RL_NOPS_AVG(&rl->d) == 0) | |
1171 continue; | |
1172 } | |
1173 | |
1174 if (rl->d.requests != 0) | |
1175 ++total; | |
1176 | |
1177 /* compute only the total for `cdcc stats` if buffer is null */ | |
1178 if (len_lim == 0) | |
1179 continue; | |
1180 | |
1181 /* respect client IP address limitations */ | |
1182 if (addr6 && !DCC_IN_BLOCK(rl->d.clnt_addr, *addr6, *mask6)) | |
1183 continue; | |
1184 | |
1185 /* always report blacklisted clients | |
1186 * report only (non-)anonymous clients if asked, error | |
1187 * toward reporting clients that were treated as | |
1188 * anonymous but used a real ID */ | |
1189 if (!(rl->d.flags & RL_FG_BLS)) { | |
1190 if ((req_flags & DCC_AOP_CLIENTS_ANON) | |
1191 && !(rl->d.flags & RL_FG_ANON)) | |
1192 continue; | |
1193 if ((req_flags & DCC_AOP_CLIENTS_NON_ANON) | |
1194 && rl->d.clnt_id == DCC_ID_ANON) | |
1195 continue; | |
1196 } | |
1197 | |
1198 if (offset != 0) { | |
1199 --offset; | |
1200 continue; | |
1201 } | |
1202 | |
1203 if (req_flags & DCC_AOP_CLIENTS_AVG) { | |
1204 rl_avg_requests_age(rl, 0); | |
1205 requests = RL_REQUESTS_AVG(&rl->d); | |
1206 nops = RL_NOPS_AVG(&rl->d); | |
1207 } else { | |
1208 requests = rl->d.requests; | |
1209 nops = rl->d.nops; | |
1210 } | |
1211 | |
1212 /* skip uninteresting records and insert a fake | |
1213 * entry in the output to the client with the total | |
1214 * skipped in the entire response to the client */ | |
1215 if (requests < thold | |
1216 && (!(rl->d.flags & RL_FG_BLS) | |
1217 || rl->d.requests == 0)) { | |
1218 if (!skip) { | |
1219 /* start a new fake record */ | |
1220 skip = &val->clients[len]; | |
1221 len += client_pack_skip(skip, &prev_flags, 0); | |
1222 prev_vers = 0; | |
1223 } | |
1224 ++skipped; | |
1225 continue; | |
1226 } | |
1227 | |
1228 /* stop at end of buffer | |
1229 * check only after skipping boring records for the common | |
1230 * case of ignoring the last 10,000 records */ | |
1231 if (len + DCC_ADMN_RESP_CLIENTS_MAX_SIZE*2 > len_lim) | |
1232 break; | |
1233 | |
1234 /* send the version number if it is wanted and differs | |
1235 * from the previous value */ | |
1236 if ((req_flags & DCC_AOP_CLIENTS_VERS) | |
1237 && rl->d.pkt_vers != prev_vers) { | |
1238 vers = rl->d.pkt_vers; | |
1239 prev_vers = vers; | |
1240 } else { | |
1241 vers = 0; | |
1242 } | |
1243 | |
1244 len += client_pack(&val->clients[len], &prev_flags, | |
1245 (rl->d.flags & (RL_FG_BL_ADDR | RL_FG_BL_ID)) | |
1246 ? DCC_ADMN_RESP_CLIENTS_BL | |
1247 : (rl->d.flags & RL_FG_BL_BAD) | |
1248 ? DCC_ADMN_RESP_CLIENTS_BAD | |
1249 : 0, | |
1250 rl->d.clnt_id, rl->d.last_used, | |
1251 requests, nops, vers, &rl->d.clnt_addr); | |
1252 } | |
1253 /* put final total number of skipped records in the output */ | |
1254 if (skipped) | |
1255 client_pack_skip(skip, &prev_flags, skipped); | |
1256 | |
1257 if (lenp) | |
1258 *lenp = len; | |
1259 | |
1260 /* return negative total if the oldest block is not very old and | |
1261 * we have had to recycle a recent block */ | |
1262 if (rl_too_many) | |
1263 return -total; | |
1264 return total; | |
1265 } | |
1266 | |
1267 | |
1268 | |
1269 /* forget old clients */ | |
1270 void | |
1271 clients_clear() | |
1272 { | |
1273 RL *rl; | |
1274 | |
1275 for (rl = rl_oldest; rl != 0; rl = rl->newer) { | |
1276 rl_avg_requests_age(rl, 1); | |
1277 rl->d.requests = 0; | |
1278 rl->d.requests_old = 0; | |
1279 rl->d.nops = 0; | |
1280 rl->d.nops_old = 0; | |
1281 } | |
1282 | |
1283 clients_cleared = db_time.tv_sec; | |
1284 rl_too_many = 0; | |
1285 } | |
1286 | |
1287 | |
1288 | |
1289 u_char /* 0=bad passwd, 1=1st passwd, 2=2nd */ | |
1290 ck_sign(const ID_TBL **tpp, /* return ID table entry here */ | |
1291 DCC_PASSWD passwd, /* return matching password here */ | |
1292 DCC_CLNT_ID id, | |
1293 const void *buf, u_int buf_len) | |
1294 { | |
1295 ID_TBL *tp; | |
1296 | |
1297 tp = find_id_tbl(id); | |
1298 if (tpp) | |
1299 *tpp = tp; | |
1300 if (!tp) | |
1301 return 0; | |
1302 | |
1303 if (tp->cur_passwd[0] != '\0' | |
1304 && dcc_ck_signature(tp->cur_passwd, sizeof(tp->cur_passwd), | |
1305 buf, buf_len)) { | |
1306 if (passwd) | |
1307 memcpy(passwd, tp->cur_passwd, sizeof(DCC_PASSWD)); | |
1308 return 1; | |
1309 } | |
1310 if (tp->next_passwd[0] != '\0' | |
1311 && dcc_ck_signature(tp->next_passwd, sizeof(tp->next_passwd), | |
1312 buf, buf_len)) { | |
1313 if (passwd) | |
1314 memcpy(passwd, tp->next_passwd, sizeof(DCC_PASSWD)); | |
1315 return 2; | |
1316 } | |
1317 return 0; | |
1318 } | |
1319 | |
1320 | |
1321 | |
1322 /* decide how long a request should be delayed */ | |
1323 static u_char /* 0=delay >= maximum */ | |
1324 tp2delay(QUEUE *q, time_t delay_us, u_int delay_inflate) | |
1325 { | |
1326 int inflate; | |
1327 | |
1328 if (delay_us != 0) { | |
1329 inflate = 1 + RL_REQUESTS_AVG(&q->rl->d)/delay_inflate; | |
1330 if (inflate > DCC_ANON_DELAY_MAX / delay_us /* no overflow */ | |
1331 || (delay_us *= inflate) >= DCC_ANON_DELAY_MAX) { | |
1332 q->rl->d.flags |= RL_FG_BL_BAD; | |
1333 q->delay_us = DCC_ANON_DELAY_MAX; | |
1334 return 0; | |
1335 } | |
1336 } | |
1337 | |
1338 /* do not answer anonymous clients while we are cleaning | |
1339 * the database */ | |
1340 if ((db_minimum_map || anon_off) && (q->rl->d.flags & RL_FG_ANON)) { | |
1341 q->delay_us = DCC_ANON_DELAY_MAX; | |
1342 return 0; | |
1343 } | |
1344 | |
1345 if (flods_off > 0 | |
1346 || flods_st != FLODS_ST_ON | |
1347 || (iflods.open == 0 && (oflods.total != 0 || !grey_on)) | |
1348 || !DB_IS_TIME(iflods_ok_timer, IFLODS_OK_SECS)) { | |
1349 /* Increase the delay when flooding is off, broken, | |
1350 * or just starting after being off | |
1351 * Greylist servers without any flooding peers can be isolated | |
1352 * but not DCC servers */ | |
1353 delay_us += 400*1000; | |
1354 | |
1355 } else if (dbclean_wfix_state == WFIX_QUIET | |
1356 || dbclean_wfix_state == WFIX_CHECK) { | |
1357 /* increase the delay if we are testing to see if clients | |
1358 * have a alternative so we can do a quick window fix. */ | |
1359 delay_us += 1000*1000; | |
1360 } | |
1361 | |
1362 if (delay_us >= DCC_ANON_DELAY_MAX) { | |
1363 q->delay_us = DCC_ANON_DELAY_MAX; | |
1364 return 0; | |
1365 } | |
1366 q->delay_us = delay_us; | |
1367 return 1; | |
1368 } | |
1369 | |
1370 | |
1371 | |
1372 /* check the message authentication code and rate limit requests */ | |
1373 static u_char /* 0=forget request, 1=go ahead */ | |
1374 ck_id(QUEUE *q, | |
1375 DCC_CLNT_ID id, /* what the client claimed */ | |
1376 DCC_CLNT_ID min_id) /* allowed */ | |
1377 { | |
1378 #define RL_CNT2AVG(cur,lims) ((lims.hi - cur) / (RL_SCALE*RL_AVG_SECS*1.0)) | |
1379 RL_DATA_FG id_fgs = 0; | |
1380 const ID_TBL *tp; | |
1381 RL *rl; | |
1382 int result; /* 1=ok & no msg; 0=msg; -1=no msg */ | |
1383 | |
1384 q->clnt_id = id; | |
1385 if (id == DCC_ID_ANON) { | |
1386 tp = 0; | |
1387 q->flags |= Q_FG_UNTRUSTED; | |
1388 id_fgs = RL_FG_ANON; | |
1389 result = 1; | |
1390 | |
1391 } else if (id < min_id) { | |
1392 tp = 0; | |
1393 q->flags |= (Q_FG_UNTRUSTED | Q_FG_UKN_ID); | |
1394 id_fgs = (RL_FG_UKN_ID | RL_FG_ANON); | |
1395 result = 0; | |
1396 | |
1397 } else if (!ck_sign(&tp, q->passwd, id, &q->pkt, q->pkt_len)) { | |
1398 /* failed to authenticate the ID */ | |
1399 if (!tp) { | |
1400 q->flags |= Q_FG_UKN_ID; | |
1401 id_fgs = (RL_FG_UKN_ID | RL_FG_ANON); | |
1402 } else { | |
1403 if (tp->delay_us >= DCC_ANON_DELAY_US_BLACKLIST) | |
1404 id_fgs |= RL_FG_BL_ID; | |
1405 tp = 0; | |
1406 q->flags |= Q_FG_BAD_PASSWD; | |
1407 id_fgs = (RL_FG_PASSWD | RL_FG_ANON); | |
1408 } | |
1409 q->flags |= Q_FG_UNTRUSTED; | |
1410 result = 0; | |
1411 | |
1412 } else if (tp->delay_us >= DCC_ANON_DELAY_US_BLACKLIST) { | |
1413 q->flags |= Q_FG_UNTRUSTED; | |
1414 id_fgs = RL_FG_BL_ID; | |
1415 result = -1; | |
1416 | |
1417 } else { | |
1418 id_fgs = 0; | |
1419 result = 1; | |
1420 } | |
1421 | |
1422 | |
1423 rl = rl_get_q(q, id); | |
1424 | |
1425 rl->d.pkt_vers = q->pkt.hdr.pkt_vers; | |
1426 | |
1427 /* See if this client IP address is blacklisted, | |
1428 * if we have not checked it since (re)loading the blacklist */ | |
1429 rl->d.flags &= ~(RL_FG_BL_ID | RL_FG_BL_BAD | |
1430 | RL_FG_PASSWD | RL_FG_UKN_ID | RL_FG_ANON); | |
1431 rl->d.flags |= id_fgs; | |
1432 if (!(rl->d.flags & RL_FG_CK_BL)) | |
1433 ck_ip_bl(&rl, q->clnt_id, &rl->d.clnt_addr); | |
1434 if (rl->d.flags & (RL_FG_BL_ADDR | RL_FG_BL_ID)) | |
1435 result = -1; | |
1436 | |
1437 if (tp) { | |
1438 if (!query_only || (tp->flags & ID_FLG_RPT_OK)) | |
1439 q->flags |= Q_FG_RPT_OK; | |
1440 | |
1441 rl_inc(rl, &rl_sub_rate); | |
1442 | |
1443 if (!tp2delay(q, tp->delay_us, tp->delay_inflate)) | |
1444 result = -1; | |
1445 | |
1446 if (rl->d.request_credits <= 0 && result >= 0) { | |
1447 clnt_msg(q, "%.1f requests/sec are too many%s", | |
1448 RL_CNT2AVG(rl->d.request_credits, rl_sub_rate), | |
1449 from_id_ip(q, 0)); | |
1450 ++dccd_stats.rl; | |
1451 rl->d.flags |= RL_FG_BL_BAD; | |
1452 result = -1; | |
1453 } | |
1454 | |
1455 } else { | |
1456 if (!query_only) | |
1457 q->flags |= Q_FG_RPT_OK; | |
1458 | |
1459 rl_inc(rl, &rl_anon_rate); | |
1460 rl_inc(&rl_all_anon, &rl_all_anon_rate); | |
1461 | |
1462 if (!tp2delay(q, anon_delay_us, anon_delay_inflate)) | |
1463 result = -1; | |
1464 | |
1465 if (rl->d.request_credits <= 0 && result >= 0) { | |
1466 anon_msg("%.1f requests/sec are too many%s", | |
1467 RL_CNT2AVG(rl->d.request_credits, | |
1468 rl_anon_rate), | |
1469 from_id_ip(q, 0)); | |
1470 rl->d.flags |= RL_FG_BL_BAD; | |
1471 ++dccd_stats.anon_rl; | |
1472 result = -1; | |
1473 } else if (rl_all_anon.d.request_credits <= 0 && result >= 0) { | |
1474 anon_msg("%s contributed to %.1f" | |
1475 " anonymous requests/sec", | |
1476 Q_CIP(q), | |
1477 RL_CNT2AVG(rl_all_anon.d.request_credits, | |
1478 rl_all_anon_rate)); | |
1479 ++dccd_stats.anon_rl; | |
1480 result = -1; | |
1481 } | |
1482 } | |
1483 | |
1484 if (q->flags & (Q_FG_BAD_PASSWD | Q_FG_UKN_ID)) | |
1485 ++dccd_stats.bad_passwd; | |
1486 else if (rl->d.flags & RL_FG_BL_BAD) | |
1487 ++dccd_stats.bad_op; | |
1488 else if (rl->d.flags & RL_FG_BLS) | |
1489 ++dccd_stats.blist; | |
1490 | |
1491 /* complain if tracing all blacklisted clients | |
1492 * or if tracing this client | |
1493 * or if this client is not blacklisted | |
1494 * but uses a bad password or ID */ | |
1495 if (((rl->d.flags & RL_FG_BLS) && (DCC_TRACE_BL_BIT & dccd_tracemask)) | |
1496 || (rl->d.flags & RL_FG_TRACE) | |
1497 || result == 0) { | |
1498 char result_buf[30]; | |
1499 const char *result_str, *bl_str; | |
1500 | |
1501 if (result >= 0) { | |
1502 if (q->delay_us == 0) { | |
1503 result_str = ""; | |
1504 } else { | |
1505 snprintf(result_buf, sizeof(result_buf), | |
1506 "delay %.3f ", | |
1507 q->delay_us/(DCC_US*1.0)); | |
1508 result_str = result_buf; | |
1509 } | |
1510 } else { | |
1511 result_str = "drop "; | |
1512 } | |
1513 bl_str = ((rl->d.flags & RL_FG_BL_ADDR) ? "blacklisted " | |
1514 : (rl->d.flags & RL_FG_BL_ID) ? "ID blacklisted " | |
1515 : ""); | |
1516 dcc_trace_msg("%s%s%s", | |
1517 result_str, bl_str, from_id_ip(q, 1)); | |
1518 } | |
1519 | |
1520 return (result >= 0); | |
1521 } | |
1522 | |
1523 | |
1524 | |
1525 /* check the message authentication code for a client of our server-ID | |
1526 * and rate limit its messages */ | |
1527 u_char /* 0=forget it, 1=go ahead */ | |
1528 ck_clnt_srvr_id(QUEUE *q) | |
1529 { | |
1530 /* require a client-ID, our server-ID, or the anonymous client-ID | |
1531 * to consider allowing an administrative request */ | |
1532 return ck_id(q, ntohl(q->pkt.hdr.sender), DCC_SRVR_ID_MIN); | |
1533 } | |
1534 | |
1535 | |
1536 | |
1537 /* check the message authentication code of a request, | |
1538 * and rate limit the source */ | |
1539 u_char /* 0=forget it, 1=go ahead */ | |
1540 ck_clnt_id(QUEUE *q) | |
1541 { | |
1542 /* require a client-ID and not a server-ID to discourage server | |
1543 * operators from leaking server-ID's */ | |
1544 return ck_id(q, ntohl(q->pkt.hdr.sender), DCC_CLNT_ID_MIN); | |
1545 } | |
1546 | |
1547 | |
1548 | |
1549 const char * | |
1550 qop2str(const QUEUE *q) | |
1551 { | |
1552 static int bufno; | |
1553 static struct { | |
1554 char str[DCC_OPBUF]; | |
1555 } bufs[4]; | |
1556 char *s; | |
1557 | |
1558 s = bufs[bufno].str; | |
1559 bufno = (bufno+1) % DIM(bufs); | |
1560 | |
1561 return dcc_hdr_op2str(s, DCC_OPBUF, &q->pkt.hdr); | |
1562 } | |
1563 | |
1564 | |
1565 | |
1566 const char * | |
1567 from_id_ip(const QUEUE *q, | |
1568 u_char print_op) /* 1=include operation */ | |
1569 { | |
1570 static char buf[DCC_OPBUF | |
1571 +ISZ(" from ")+40+ISZ(" at ")+DCC_SU2STR_SIZE]; | |
1572 char ob[DCC_OPBUF]; | |
1573 const char *op; | |
1574 | |
1575 if (print_op) { | |
1576 op = dcc_hdr_op2str(ob, sizeof(ob), &q->pkt.hdr); | |
1577 } else { | |
1578 op = ""; | |
1579 } | |
1580 | |
1581 if (q->clnt_id == DCC_ID_ANON) | |
1582 snprintf(buf, sizeof(buf), | |
1583 "%s from anonymous at %s", | |
1584 op, dcc_su2str_err(&q->clnt_su)); | |
1585 else if (q->flags & Q_FG_UKN_ID) | |
1586 snprintf(buf, sizeof(buf), | |
1587 "%s from unknown ID %d at %s", | |
1588 op, q->clnt_id, dcc_su2str_err(&q->clnt_su)); | |
1589 else if (q->flags & Q_FG_BAD_PASSWD) | |
1590 snprintf(buf, sizeof(buf), | |
1591 "%s from ID %d at %s with bad password ", | |
1592 op, q->clnt_id, dcc_su2str_err(&q->clnt_su)); | |
1593 else if (q->flags & Q_FG_UNTRUSTED) | |
1594 snprintf(buf, sizeof(buf), | |
1595 "%s from bad ID %d at %s", | |
1596 op, q->clnt_id, dcc_su2str_err(&q->clnt_su)); | |
1597 else | |
1598 snprintf(buf, sizeof(buf), | |
1599 "%s from ID %d at %s", | |
1600 op, q->clnt_id, dcc_su2str_err(&q->clnt_su)); | |
1601 | |
1602 return buf; | |
1603 } | |
1604 | |
1605 | |
1606 | |
1607 const char * | |
1608 op_id_ip(const QUEUE *q) | |
1609 { | |
1610 static char buf[3+14+4+DCC_SU2STR_SIZE]; | |
1611 char ob[DCC_OPBUF]; | |
1612 | |
1613 snprintf(buf, sizeof(buf), "%s from ID %d at %s", | |
1614 dcc_hdr_op2str(ob, sizeof(ob), &q->pkt.hdr), | |
1615 (DCC_CLNT_ID)ntohl(q->pkt.hdr.sender), Q_CIP(q)); | |
1616 return buf; | |
1617 } | |
1618 | |
1619 | |
1620 | |
1621 /* complain about an anonymous, non-paying client */ | |
1622 void | |
1623 vanon_msg(const char *p, va_list args) | |
1624 { | |
1625 rl_age(&rl_all_anon, &rl_all_anon_rate); | |
1626 if ((DCC_TRACE_ANON_BIT & dccd_tracemask) | |
1627 && (rl_all_anon.d.bug_credits > 0 | |
1628 || (DCC_TRACE_RLIM_BIT & dccd_tracemask))) { | |
1629 rl_all_anon.d.bug_credits -= RL_SCALE; | |
1630 dcc_vtrace_msg(p, args); | |
1631 } | |
1632 } | |
1633 | |
1634 | |
1635 | |
1636 void | |
1637 anon_msg(const char *p, ...) | |
1638 { | |
1639 va_list args; | |
1640 | |
1641 va_start(args, p); | |
1642 vanon_msg(p, args); | |
1643 va_end(args); | |
1644 } | |
1645 | |
1646 | |
1647 | |
1648 /* complain about a client */ | |
1649 void | |
1650 vclnt_msg(const QUEUE *q, const char *p, va_list args) | |
1651 { | |
1652 if ((q->flags & Q_FG_UNTRUSTED) || !q->rl) { | |
1653 vanon_msg(p, args); | |
1654 return; | |
1655 } | |
1656 | |
1657 if (DCC_TRACE_CLNT_BIT & dccd_tracemask) { | |
1658 rl_age(q->rl, &rl_sub_rate); | |
1659 if (q->rl->d.bug_credits > 0 | |
1660 || (DCC_TRACE_RLIM_BIT & dccd_tracemask)) { | |
1661 q->rl->d.bug_credits -= RL_SCALE; | |
1662 dcc_vtrace_msg(p, args); | |
1663 } | |
1664 } | |
1665 } | |
1666 | |
1667 | |
1668 | |
1669 /* complain about a client */ | |
1670 void | |
1671 clnt_msg(const QUEUE *q, const char *p, ...) | |
1672 { | |
1673 va_list args; | |
1674 | |
1675 va_start(args, p); | |
1676 vclnt_msg(q, p, args); | |
1677 va_end(args); | |
1678 } | |
1679 | |
1680 | |
1681 | |
1682 void | |
1683 drop_msg(QUEUE *q, const char *p, ...) | |
1684 { | |
1685 RL *rl; | |
1686 char buf[80]; | |
1687 va_list args; | |
1688 | |
1689 rl = rl_get_q(q, DCC_ID_ANON); | |
1690 rl->d.flags |= RL_FG_BL_BAD; | |
1691 | |
1692 rl_inc(rl, &rl_anon_rate); | |
1693 rl_inc(&rl_all_anon, &rl_all_anon_rate); | |
1694 if (!(rl->d.flags & RL_FG_BL_ADDR)) { | |
1695 va_start(args, p); | |
1696 vsnprintf(buf, sizeof(buf), p, args); | |
1697 va_end(args); | |
1698 anon_msg("drop %s from %s", buf, Q_CIP(q)); | |
1699 } | |
1700 } |