Mercurial > notdcc
comparison dccd/dccd.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 | |
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.297 $Revision$ | |
40 */ | |
41 | |
42 #include "dccd_defs.h" | |
43 #include <signal.h> /* for Linux and SunOS */ | |
44 #include <sys/uio.h> | |
45 #include <sys/wait.h> | |
46 #include "dcc_ifaddrs.h" | |
47 | |
48 DCC_EMSG dcc_emsg; | |
49 | |
50 static const char *homedir; | |
51 static char *aargs[10]; | |
52 static char **aarg = &aargs[0]; | |
53 static DCC_PATH dbclean_def = DCC_LIBEXECDIR"/dbclean"; | |
54 static char *dbclean = dbclean_def; | |
55 static pid_t dbclean_pid = -1; | |
56 static char *addr_str; /* an IP address for dbclean */ | |
57 static char dbclean_str[] = "dbclean"; | |
58 #define MAX_DBCLEAN_FLAGS 50 | |
59 static char dbclean_flags[MAX_DBCLEAN_FLAGS+1] = "-Pq"; | |
60 static int dbclean_flags_len = LITZ("-Pq"); | |
61 static const char *dbclean_argv[20] = {dbclean_str, dbclean_flags}; | |
62 static int dbclean_argc = 2; | |
63 static char dbclean_args_str[200]; | |
64 static int dbclean_args_str_len; | |
65 | |
66 /* do not try to run dbclean too often */ | |
67 time_t dbclean_limit_secs = DBCLEAN_LIMIT_SECS; | |
68 static time_t dbclean_failed; | |
69 | |
70 static double wfix_quiet_rate = 1.0; /* max load that allows window fixing */ | |
71 static double wfix_busy_rate; /* measured active rate */ | |
72 static double wfix_rate_change = 0.4; /* rate reduction to this allows fix */ | |
73 static DB_PTR wfix_size; | |
74 static u_char wfix_size_set = 0; | |
75 DBCLEAN_WFIX_STATE dbclean_wfix_state = WFIX_DELAY; | |
76 static double wfix_ops; | |
77 static time_t wfix_check_start; | |
78 static time_t dbclean_wfix_secs; /* window fixing timer */ | |
79 #define WFIX_POST_CLEAN_SECS (6*60*60) /* do not fix it soon after cleaning */ | |
80 #define WFIX_PRE_CLEAN_SECS (2*60*60) /* or just before cleaning */ | |
81 #define WFIX_RECHECK_SECS (15*60) /* between checks for window overflow */ | |
82 #define WFIX_QUIET_SECS (5*60) /* waiting for clients to flee */ | |
83 #define WFIX_MEASURE_SECS (5*60) /* counting clients */ | |
84 #define WFIX_MAX_SECS (WFIX_PRE_CLEAN_SECS+WFIX_POST_CLEAN_SECS) | |
85 | |
86 const char *need_del_dbclean; | |
87 time_t del_dbclean_next; | |
88 time_t dbclean_limit; | |
89 static time_t clean_fake_secs; /* timer for missing cron job */ | |
90 static time_t clean_last_secs; | |
91 | |
92 u_long dccd_tracemask = DCC_TRACE_ON_DEF_BITS; | |
93 | |
94 u_char background = 1; | |
95 int stopint; /* !=0 if stopping or received signal */ | |
96 | |
97 static time_t flods_ck_secs = FLODS_CK_SECS; | |
98 | |
99 const char *brand = ""; | |
100 | |
101 static char *my_srvr_id_str; | |
102 DCC_SRVR_ID my_srvr_id; | |
103 | |
104 u_char use_ipv6 = 0; | |
105 u_int16_t def_port; /* default port #, network byte order */ | |
106 typedef struct port_list { | |
107 struct port_list *fwd; | |
108 u_int16_t port; /* network byte order */ | |
109 } PORT_LIST; | |
110 static PORT_LIST *ports; | |
111 SRVR_SOC *srvr_socs; | |
112 static SRVR_SOC **srvr_socs_end = &srvr_socs; | |
113 int srvr_rcvbuf = 1024*1024; | |
114 | |
115 #ifdef USE_DBCLEAN_F | |
116 static u_char db_mode = DB_OPEN_MMAP_WRITE; | |
117 #else | |
118 static u_char db_mode = 0; | |
119 #endif | |
120 | |
121 int grey_embargo = DEF_GREY_EMBARGO; /* seconds to delay new traffic */ | |
122 int grey_window = DEF_GREY_WINDOW; /* wait as long as this */ | |
123 int grey_white = DEF_GREY_WHITE; /* remember this long */ | |
124 | |
125 char our_hostname[MAXHOSTNAMELEN]; | |
126 DCC_SUM host_id_sum; /* advertised with our server-ID */ | |
127 time_t host_id_next, host_id_last; /* when to advertise */ | |
128 | |
129 u_char anon_off; /* turn off anonymous access */ | |
130 time_t anon_delay_us = DCC_ANON_DELAY_US_DEF; /* delay anonymous clients */ | |
131 u_int anon_delay_inflate = DCC_ANON_INFLATE_OFF; | |
132 | |
133 u_char stop_mode; /* 0=normal 1=reboot 2=with/DB clean */ | |
134 | |
135 static int total_ops; | |
136 static QUEUE *queue_free; | |
137 static QUEUE *queue_head; | |
138 | |
139 /* assume or hope we can handle 500 requests/second */ | |
140 int queue_max = 500*DCC_MAX_RTT_SECS; /* ultimate bound on queue size */ | |
141 static int queue_max_cur; /* current upper bound */ | |
142 static int queue_cur; /* current queue size */ | |
143 | |
144 struct timeval wake_time; /* when we awoke from select() */ | |
145 struct timeval req_recv_time; /* when request arrived */ | |
146 | |
147 DCC_TS future; /* timestamp sanity */ | |
148 | |
149 | |
150 static DB_NOKEEP_CKS set_new_nokeep_cks, reset_new_nokeep_cks; | |
151 | |
152 DCC_TGTS flod_tholds[DCC_DIM_CKS]; /* flood at or after these thresholds */ | |
153 | |
154 static const char *parse_rl_rate(RL_RATE *, float, const char *, const char *); | |
155 static void add_dbclean_flag(char); | |
156 static void add_dbclean_arg(const char *); | |
157 static void check_dbclean(int); | |
158 static void run_dbclean(const char *, const char *); | |
159 static void sigterm(int); | |
160 static void sighup(int); | |
161 static void stop_children(void); | |
162 static u_char get_if_changes(u_char); | |
163 static SRVR_SOC *add_srvr_soc(u_char, int, const void *, u_int16_t); | |
164 static int open_srvr_socs(int); | |
165 static void wfix_later(time_t); | |
166 static void recv_job(void) NRATTRIB; | |
167 static u_char new_job(SRVR_SOC *); | |
168 static void NRATTRIB dccd_quit(int, const char *, ...) PATTRIB(2,3); | |
169 | |
170 static void | |
171 usage(const char* barg) | |
172 { | |
173 static const char str[] = { | |
174 "usage: [-64dVbfFQ] -i server-ID [-n brand] [-h homedir]\n" | |
175 " [-I [host-ID][,user]] [-a [server-addr][,server-port]]" | |
176 " [-q qsize]\n" | |
177 " [-G [on,][weak-body,][weak-IP,]embargo],[window],[white]]\n" | |
178 " [-W [rate][,chg][,dbsize]] [-K [no-]type] [-T tracemode]\n" | |
179 " [-u anon-delay[,inflate] [-C dbclean]" | |
180 " [-L ltype,facility.level]\n" | |
181 " [-R [RL_SUB],[RL_FREE],[RL_ALL_FREE],[RL_BUGS]]" | |
182 }; | |
183 static u_char complained; | |
184 | |
185 if (!complained) { | |
186 if (barg) | |
187 dcc_error_msg("unrecognized \"%s\"\nusage: %s\n..." | |
188 " continuing", | |
189 barg, str); | |
190 else | |
191 dcc_error_msg("%s\n... continuing", str); | |
192 complained = 1; | |
193 } | |
194 } | |
195 | |
196 | |
197 int NRATTRIB | |
198 main(int argc, char **argv) | |
199 { | |
200 char *p; | |
201 const char *rest; | |
202 u_char print_version = 0; | |
203 DCC_CK_TYPES type; | |
204 DCC_SOCKU *sup; | |
205 u_int16_t port; | |
206 int new_embargo, new_window, new_white; | |
207 int error, i; | |
208 const char *cp; | |
209 double d1, d2; | |
210 u_long l; | |
211 | |
212 dcc_syslog_init(1, argv[0], 0); | |
213 | |
214 if (DCC_DIM_CKS != DCC_COMP_DIM_CKS) | |
215 dcc_logbad(EX_SOFTWARE, | |
216 "DCC_DIM_CKS != DCC_COMP_DIM_CKS;" | |
217 " check uses of both"); | |
218 | |
219 /* get first bytes of our hostname to name our server-ID */ | |
220 memset(our_hostname, 0, sizeof(our_hostname)); | |
221 if (0 > gethostname(our_hostname, sizeof(our_hostname)-1)) | |
222 dcc_logbad(EX_NOHOST, "gethostname(): %s", ERROR_STR()); | |
223 our_hostname[sizeof(our_hostname)-1] = '\0'; | |
224 if (our_hostname[0] == '\0') | |
225 dcc_logbad(EX_NOHOST, "null hostname from gethostname()"); | |
226 strncpy((char *)host_id_sum, our_hostname, sizeof(host_id_sum)); | |
227 | |
228 parse_rl_rate(&rl_sub_rate, 0.5, "RL_SUB", "400"); | |
229 parse_rl_rate(&rl_anon_rate, RL_AVG_SECS, "RL_ANON", "50"); | |
230 parse_rl_rate(&rl_all_anon_rate, 0.5, "RL_ALL_ANON", "600"); | |
231 parse_rl_rate(&rl_bugs_rate, RL_AVG_SECS, "RL_BUGS", "0.1"); | |
232 | |
233 /* this must match DCCD_GETOPTS in cron-dccd.in */ | |
234 while ((i = getopt(argc, argv, | |
235 "64dVbfFQi:n:h:a:I:q:G:t:W:K:T:u:C:L:R:")) != -1) { | |
236 switch (i) { | |
237 case '6': | |
238 #ifndef NO_IPV6 | |
239 use_ipv6 = 2; | |
240 #endif | |
241 break; | |
242 case '4': | |
243 use_ipv6 = 0; | |
244 break; | |
245 | |
246 case 'd': | |
247 add_dbclean_flag('d'); | |
248 ++db_debug; | |
249 break; | |
250 | |
251 case 'V': | |
252 fprintf(stderr, DCC_VERSION"\n"); | |
253 print_version = 1; | |
254 break; | |
255 | |
256 case 'b': | |
257 background = 0; | |
258 break; | |
259 | |
260 case 'f': | |
261 db_mode &= ~DB_OPEN_MMAP_WRITE; | |
262 add_dbclean_flag('f'); | |
263 break; | |
264 | |
265 case 'F': | |
266 db_mode |= DB_OPEN_MMAP_WRITE; | |
267 add_dbclean_flag('F'); | |
268 break; | |
269 | |
270 case 'Q': | |
271 query_only = 1; | |
272 break; | |
273 | |
274 case 'i': | |
275 my_srvr_id_str = optarg; | |
276 if (!dcc_get_srvr_id(dcc_emsg, &my_srvr_id, | |
277 my_srvr_id_str, 0, 0, 0)) | |
278 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); | |
279 add_dbclean_arg("-i"); | |
280 add_dbclean_arg(my_srvr_id_str); | |
281 break; | |
282 | |
283 case 'n': | |
284 /* RFC 2822 says "values between 33 and 126" */ | |
285 cp = optarg; | |
286 while (*cp >= 33 && *cp <= 126 && *cp != ':') | |
287 ++cp; | |
288 if (cp == optarg | |
289 || (cp - optarg) > ISZ(DCC_BRAND) | |
290 || *cp != '\0') { | |
291 dcc_error_msg("invalid brand name \"-n %s\"", | |
292 optarg); | |
293 } else { | |
294 brand = optarg; | |
295 } | |
296 break; | |
297 | |
298 case 'h': | |
299 homedir = optarg; | |
300 /* tell dbclean "-h ." because we will already | |
301 * be there and that allows our -h to be relative */ | |
302 add_dbclean_arg("-h."); | |
303 break; | |
304 | |
305 case 'I': | |
306 p = strchr(optarg, ','); | |
307 if (p) { | |
308 *p++ = '\0'; | |
309 dcc_daemon_su(p); | |
310 if (*optarg == '\0') | |
311 break; | |
312 } | |
313 if (*optarg != '\0') | |
314 strncpy((char *)host_id_sum, optarg, | |
315 sizeof(host_id_sum)); | |
316 break; | |
317 | |
318 case 'a': | |
319 /* postpone checking host names until we know -6 */ | |
320 if (aarg > LAST(aargs)) { | |
321 dcc_error_msg("too many -a args"); | |
322 break; | |
323 } | |
324 optarg += strspn(optarg, DCC_WHITESPACE); | |
325 *aarg++ = optarg; | |
326 break; | |
327 | |
328 case 'q': | |
329 l = strtoul(optarg, &p, 10); | |
330 if (*p != '\0' || l < 2 || l > 1000) { | |
331 dcc_error_msg("invalid queue length \"%s\"", | |
332 optarg); | |
333 } else { | |
334 queue_max = l; | |
335 } | |
336 break; | |
337 | |
338 case 'G': | |
339 grey_on = 1; | |
340 dcc_syslog_init(1, argv[0], " grey"); | |
341 add_dbclean_arg("-Gon"); | |
342 /* handle leading "on" "weak-body", and "weak-IP" */ | |
343 rest = optarg; | |
344 while (*rest) { | |
345 if (dcc_ck_word_comma(&rest, "weak-body") | |
346 || dcc_ck_word_comma(&rest, "weak_body") | |
347 || dcc_ck_word_comma(&rest, "weak")) { | |
348 grey_weak_body = 1; | |
349 continue; | |
350 } | |
351 if (dcc_ck_word_comma(&rest, "weak-IP") | |
352 || dcc_ck_word_comma(&rest, "weak_IP")) { | |
353 grey_weak_ip = 1; | |
354 continue; | |
355 } | |
356 if (!dcc_ck_word_comma(&rest, "on")) | |
357 break; | |
358 } | |
359 new_embargo = dcc_get_secs(rest, &rest, | |
360 0, MAX_GREY_EMBARGO, | |
361 grey_embargo); | |
362 if (new_embargo < 0) { | |
363 dcc_error_msg("invalid greylist embargo" | |
364 " \"-G %s\"", optarg); | |
365 break; | |
366 } | |
367 new_window = dcc_get_secs(rest, &rest, | |
368 new_embargo, MAX_GREY_WINDOW, | |
369 max(new_embargo,grey_window)); | |
370 if (new_window < 0) { | |
371 dcc_error_msg("invalid greylist wait time" | |
372 " \"-G %s\"", optarg); | |
373 break; | |
374 } | |
375 new_white = dcc_get_secs(rest, &rest, | |
376 new_window, MAX_GREY_WHITE, | |
377 max(new_window, grey_white)); | |
378 if (new_white < 0 || *rest != '\0') { | |
379 dcc_error_msg("invalid greylist whitelist time" | |
380 " \"-G %s\"", optarg); | |
381 break; | |
382 } | |
383 grey_embargo = new_embargo; | |
384 grey_window = new_window; | |
385 grey_white = new_white; | |
386 break; | |
387 | |
388 case 't': /* obsolete */ | |
389 break; | |
390 | |
391 case 'W': | |
392 p = optarg; | |
393 if (*p == '\0') { | |
394 dcc_error_msg("unrecognized" | |
395 " \"-W %s\"", optarg); | |
396 break; | |
397 } | |
398 d1 = wfix_quiet_rate; | |
399 if (*p != '\0' && *p != ',') { | |
400 d1 = strtod(p, &p); | |
401 if ((*p != '\0' && *p != ',') | |
402 || d1 < 0.0 || d1 > 1000*1000.0) { | |
403 dcc_error_msg("bad quiet rate in" | |
404 " \"-W %s\"", optarg); | |
405 break; | |
406 } | |
407 } | |
408 if (*p == ',') | |
409 ++p; | |
410 d2 = wfix_rate_change; | |
411 if (*p != '\0' && *p != ',') { | |
412 d1 = strtod(p, &p); | |
413 if ((*p != '\0' && *p != ',') | |
414 || d1 < 0.0 || d1 > 1000*1000.0) { | |
415 dcc_error_msg("bad rate change in" | |
416 " \"-W %s\"", optarg); | |
417 break; | |
418 } | |
419 } | |
420 if (*p == ',') | |
421 ++p; | |
422 l = wfix_size/(1024*1024); | |
423 if (*p != '\0') { | |
424 l = strtoul(p, &p, 10); | |
425 if ((*p != '\0' || l < DB_MIN_MIN_MBYTE | |
426 || l > MAX_MAX_DB_MBYTE) | |
427 && l != wfix_size) { | |
428 dcc_error_msg("bad database size in" | |
429 " \"-W %s\"", optarg); | |
430 break; | |
431 } | |
432 } | |
433 wfix_quiet_rate = d1; | |
434 wfix_rate_change = d2; | |
435 if (wfix_size/(1024*1024) != l) { | |
436 wfix_size = ((DB_PTR)l)*(1024*1024); | |
437 wfix_size_set = 1; | |
438 } | |
439 break; | |
440 | |
441 case 'K': | |
442 if (!strcasecmp(optarg, "all")) { | |
443 reset_new_nokeep_cks = -1; | |
444 break; | |
445 } | |
446 if (!CLITCMP(optarg, "no-")) { | |
447 optarg += LITZ("no-"); | |
448 i = 0; | |
449 } else { | |
450 i = 1; | |
451 } | |
452 type = dcc_str2type_db(optarg, -1); | |
453 if (type == DCC_CK_INVALID) { | |
454 dcc_error_msg("bad checksum type in" | |
455 " \"-K %s\"", optarg); | |
456 break; | |
457 } | |
458 if (i) | |
459 DB_SET_NOKEEP(reset_new_nokeep_cks, type); | |
460 else | |
461 DB_SET_NOKEEP(set_new_nokeep_cks, type); | |
462 break; | |
463 | |
464 case 'T': | |
465 if (!strcasecmp(optarg, "ADMN")) { | |
466 dccd_tracemask |= DCC_TRACE_ADMN_BIT; | |
467 } else if (!strcasecmp(optarg, "ANON")) { | |
468 dccd_tracemask |= DCC_TRACE_ANON_BIT; | |
469 } else if (!strcasecmp(optarg, "CLNT")) { | |
470 dccd_tracemask |= DCC_TRACE_CLNT_BIT; | |
471 } else if (!strcasecmp(optarg, "RLIM")) { | |
472 dccd_tracemask |= DCC_TRACE_RLIM_BIT; | |
473 } else if (!strcasecmp(optarg, "QUERY")) { | |
474 dccd_tracemask |= DCC_TRACE_QUERY_BIT; | |
475 } else if (!strcasecmp(optarg, "RIDC")) { | |
476 dccd_tracemask |= DCC_TRACE_RIDC_BIT; | |
477 } else if (!strcasecmp(optarg, "FLOOD")) { | |
478 dccd_tracemask |= DCC_TRACE_FLOD_BIT; | |
479 } else if (!strcasecmp(optarg, "FLOOD2")) { | |
480 dccd_tracemask |= DCC_TRACE_FLOD2_BIT; | |
481 } else if (!strcasecmp(optarg, "IDS")) { | |
482 dccd_tracemask |= DCC_TRACE_IDS_BIT; | |
483 } else if (!strcasecmp(optarg, "BL")) { | |
484 dccd_tracemask |= DCC_TRACE_BL_BIT; | |
485 } else if (!strcasecmp(optarg, "DB")) { | |
486 dccd_tracemask |= DCC_TRACE_DB_BIT; | |
487 } else if (!strcasecmp(optarg, "WLIST")) { | |
488 dccd_tracemask |= DCC_TRACE_WLIST_BIT; | |
489 } else { | |
490 dcc_error_msg("invalid trace mode \"%s\"", | |
491 optarg); | |
492 } | |
493 break; | |
494 | |
495 case 'u': | |
496 i = parse_dccd_delay(dcc_emsg, &anon_delay_us, | |
497 &anon_delay_inflate, optarg, | |
498 0, 0); | |
499 if (!i) { | |
500 dcc_error_msg("%s", dcc_emsg); | |
501 } else if (i == 2) { | |
502 anon_off = 1; | |
503 } else { | |
504 anon_off = 0; | |
505 } | |
506 break; | |
507 | |
508 case 'C': | |
509 if (*optarg == '\0') { | |
510 dcc_error_msg("no path to dbclean \"-C %s\"", | |
511 optarg); | |
512 break; | |
513 } | |
514 /* capture the path to the dbclean program */ | |
515 dbclean = optarg; | |
516 /* capture any args following the program */ | |
517 for (p = strpbrk(optarg, DCC_WHITESPACE); | |
518 p != 0; | |
519 p = strpbrk(p, DCC_WHITESPACE)) { | |
520 *p++ = '\0'; | |
521 p += strspn(p, DCC_WHITESPACE); | |
522 if (*p == '\0') | |
523 break; | |
524 add_dbclean_arg(p); | |
525 } | |
526 break; | |
527 | |
528 case 'L': | |
529 if (dcc_parse_log_opt(optarg)) { | |
530 add_dbclean_arg("-L"); | |
531 add_dbclean_arg(optarg); | |
532 } | |
533 break; | |
534 | |
535 case 'R': | |
536 rest = parse_rl_rate(&rl_sub_rate, -1.0, | |
537 "RL_SUB", optarg); | |
538 rest = parse_rl_rate(&rl_anon_rate, -1.0, | |
539 "RL_ANON", rest); | |
540 rest = parse_rl_rate(&rl_all_anon_rate, -1.0, | |
541 "RL_ALL_ANON", rest); | |
542 rest = parse_rl_rate(&rl_bugs_rate, -1.0, | |
543 "RL_BUGS", rest); | |
544 break; | |
545 | |
546 default: | |
547 usage(optopt2str(optopt)); | |
548 } | |
549 } | |
550 argc -= optind; | |
551 argv += optind; | |
552 if (argc != 0) | |
553 usage(argv[0]); | |
554 | |
555 if (my_srvr_id == DCC_ID_INVALID) { | |
556 if (print_version) | |
557 exit(EX_OK); | |
558 dcc_logbad(EX_USAGE, "server-ID not set with -i"); | |
559 } | |
560 | |
561 if (grey_on) { | |
562 anon_off = 1; | |
563 dccd_tracemask |= DCC_TRACE_IDS_BIT; | |
564 } | |
565 | |
566 /* parse addresses after we know whether -6 was among the args */ | |
567 for (aarg = &aargs[0]; aarg <= LAST(aargs) && *aarg; ++aarg) { | |
568 char hostname[DCC_MAXDOMAINLEN]; | |
569 | |
570 addr_str = *aarg; | |
571 rest = dcc_parse_nm_port(dcc_emsg, *aarg, 0, | |
572 hostname, sizeof(hostname), | |
573 &port, 0, 0, 0, 0); | |
574 if (!rest) { | |
575 dcc_error_msg("%s", dcc_emsg); | |
576 continue; | |
577 } | |
578 rest += strspn(rest, DCC_WHITESPACE); | |
579 if (*rest != '\0') | |
580 dcc_error_msg("unrecognized port number in" | |
581 "\"-a %s\"", *aarg); | |
582 if (hostname[0] == '\0') { | |
583 PORT_LIST *pport = dcc_malloc(sizeof(*pport)); | |
584 pport->port = port; | |
585 pport->fwd = ports; | |
586 ports = pport; | |
587 continue; | |
588 } | |
589 if (!strcmp(hostname, "@")) { | |
590 ++addr_str; /* "" but not a const */ | |
591 add_srvr_soc(SRVR_SOC_ADDR, AF_UNSPEC, 0, port); | |
592 continue; | |
593 } | |
594 dcc_host_lock(); | |
595 if (!dcc_get_host(hostname, use_ipv6 ? 2 : 0, &error)) { | |
596 dcc_host_unlock(); | |
597 dcc_error_msg("%s: %s", | |
598 *aarg, DCC_HSTRERROR(error)); | |
599 continue; | |
600 } | |
601 for (sup = dcc_hostaddrs; sup < dcc_hostaddrs_end; ++sup) { | |
602 if (sup->sa.sa_family == AF_INET) | |
603 add_srvr_soc(SRVR_SOC_ADDR, AF_INET, | |
604 &sup->ipv4.sin_addr, port); | |
605 else | |
606 add_srvr_soc(SRVR_SOC_ADDR, AF_INET6, | |
607 &sup->ipv6.sin6_addr, port); | |
608 } | |
609 dcc_host_unlock(); | |
610 } | |
611 if (addr_str) { | |
612 /* tell dbclean about one "-a addr" */ | |
613 add_dbclean_arg("-a"); | |
614 add_dbclean_arg(addr_str); | |
615 } | |
616 | |
617 dcc_clnt_unthread_init(); | |
618 if (!dcc_cdhome(dcc_emsg, homedir, 0)) | |
619 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); | |
620 | |
621 i = check_load_ids(1); | |
622 if (i < 0) | |
623 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); | |
624 else if (!i) | |
625 dcc_error_msg("%s", dcc_emsg); | |
626 | |
627 if (!def_port) | |
628 def_port = DCC_GREY2PORT(grey_on); | |
629 if (!srvr_socs && !ports) { | |
630 ports = dcc_malloc(sizeof(*ports)); | |
631 ports->fwd = 0; | |
632 ports->port = def_port; | |
633 } | |
634 get_if_changes(db_debug != 0); | |
635 | |
636 /* make initial attempt to open the server UDP sockets | |
637 * This also sets use_ipv6 to 0 or 1 if it is still 2 */ | |
638 if (open_srvr_socs(45) <= 0) | |
639 dcc_logbad(EX_OSERR, "failed to open any server sockets"); | |
640 | |
641 add_dbclean_flag(use_ipv6 == 0 ? '4' : '6'); | |
642 | |
643 if (background) { | |
644 if (0 > daemon(1, 0)) | |
645 dcc_logbad(EX_OSERR, "daemon(): %s", ERROR_STR()); | |
646 } | |
647 | |
648 if (!background) | |
649 signal(SIGHUP, sigterm); /* SIGHUP fatal during debugging */ | |
650 else | |
651 signal(SIGHUP, sighup); /* speed configuration check */ | |
652 signal(SIGTERM, sigterm); | |
653 signal(SIGINT, sigterm); | |
654 signal(SIGPIPE, SIG_IGN); | |
655 #ifdef SIGXFSZ | |
656 signal(SIGXFSZ, SIG_IGN); | |
657 #endif | |
658 | |
659 atexit(stop_children); | |
660 | |
661 gettimeofday(&db_time, 0); | |
662 wake_time = db_time; | |
663 | |
664 flod_mmap_path_set(); | |
665 | |
666 /* open the database, and try once to fix it */ | |
667 if (!dccd_db_open(DB_OPEN_LOCK_NOWAIT)) { | |
668 dcc_error_msg("%s", dcc_emsg); | |
669 run_dbclean("SRbad", "database initially broken"); | |
670 check_dbclean(0); /* stall until dbclean finishes */ | |
671 if (!dccd_db_open(DB_OPEN_LOCK_NOWAIT)) { | |
672 dcc_error_msg("%s", dcc_emsg); | |
673 dcc_logbad(EX_NOINPUT, | |
674 "could not start database %s", db_nm); | |
675 } | |
676 } | |
677 /* do not start if dbclean is running */ | |
678 if (!lock_dbclean(dcc_emsg, db_nm)) | |
679 dcc_logbad(dcc_ex_code, "%s: dbclean already running?", | |
680 dcc_emsg); | |
681 unlock_dbclean(); | |
682 | |
683 flod_trace_gen = db_time.tv_sec; | |
684 host_id_next = db_time.tv_sec + DCC_SRVR_ID_SECS_ST; | |
685 check_blacklist_file(); | |
686 flods_init(); | |
687 clients_load(); | |
688 stats_clear(); | |
689 if (flod_mmaps != 0 | |
690 && flod_mmaps->dccd_stats.reset.tv_sec != 0) { | |
691 memcpy(&dccd_stats, &flod_mmaps->dccd_stats, | |
692 sizeof(dccd_stats)); | |
693 } | |
694 dcc_trace_msg(DCC_VERSION" listening to port %d %s %s", | |
695 ntohs(srvr_socs->arg_port), | |
696 dcc_homedir, db_window_size_str); | |
697 | |
698 recv_job(); | |
699 } | |
700 | |
701 | |
702 | |
703 static SRVR_SOC * /* 0 or new entry */ | |
704 add_srvr_soc(u_char flags, | |
705 int family, /* AF_UNSPEC or 0 if addrp==0 */ | |
706 const void *addrp, /* 0, *in_addr, or *in6_addr */ | |
707 u_int16_t port) | |
708 { | |
709 DCC_SOCKU su; | |
710 SRVR_SOC *sp; | |
711 | |
712 dcc_mk_su(&su, family, addrp, port); | |
713 for (sp = srvr_socs; sp; sp = sp->fwd) { | |
714 if (sp->arg_family == family | |
715 && sp->arg_port == port | |
716 && !memcmp(&sp->arg_addr, addrp, | |
717 ((family == AF_INET) | |
718 ? sizeof(sp->arg_addr.in4) | |
719 : sizeof(sp->arg_addr.in6)))) | |
720 return sp; | |
721 } | |
722 | |
723 sp = dcc_malloc(sizeof(*sp)); | |
724 memset(sp, 0, sizeof(*sp)); | |
725 sp->flags = flags; | |
726 sp->udp = -1; | |
727 sp->listen = -1; | |
728 sp->su = su; | |
729 sp->arg_family = family; | |
730 if (addrp) | |
731 memcpy(&sp->arg_addr, addrp, | |
732 ((family == AF_INET) | |
733 ? sizeof(sp->arg_addr.in4) | |
734 : sizeof(sp->arg_addr.in6))); | |
735 sp->arg_port = port; | |
736 | |
737 *srvr_socs_end = sp; | |
738 srvr_socs_end = &sp->fwd; | |
739 | |
740 return sp; | |
741 } | |
742 | |
743 | |
744 | |
745 static int /* # of sockets opened */ | |
746 open_srvr_socs(int retry_secs) | |
747 { | |
748 static u_char srvr_rcvbuf_set = 0; | |
749 int *retry_secsp; | |
750 u_char family; | |
751 SRVR_SOC *sp; | |
752 int i; | |
753 int num_socs = 0; | |
754 | |
755 if (stopint) | |
756 return -1; | |
757 | |
758 retry_secsp = retry_secs ? &retry_secs : 0; | |
759 | |
760 for (sp = srvr_socs; sp; sp = sp->fwd) { | |
761 if (sp->udp >= 0) { | |
762 ++num_socs; | |
763 continue; | |
764 } | |
765 | |
766 /* resolve default port number if we finally know it */ | |
767 if (!sp->arg_port) | |
768 sp->arg_port = def_port; | |
769 | |
770 family = sp->arg_family; | |
771 | |
772 /* create the UDP socket | |
773 * If we are using INADDR_ANY | |
774 * and do not yet know if IPv6 works, just try it */ | |
775 if (family == AF_UNSPEC | |
776 && use_ipv6 == 2) { | |
777 dcc_mk_su(&sp->su, AF_INET6, | |
778 &sp->arg_addr, sp->arg_port); | |
779 i = dcc_udp_bind(dcc_emsg, &sp->udp, | |
780 &sp->su, retry_secsp); | |
781 if (i == 0) { | |
782 dcc_error_msg("%s", dcc_emsg); | |
783 /* still don't know about IPv6 */ | |
784 continue; | |
785 } | |
786 if (i > 0) { | |
787 /* we finished an INADDR_ANY socket | |
788 * and learned that IPv6 works */ | |
789 use_ipv6 = 1; | |
790 continue; | |
791 } | |
792 /* we know or guess that IPv6 does not work */ | |
793 use_ipv6 = 0; | |
794 } | |
795 | |
796 if (family == AF_UNSPEC) { | |
797 /* using INADDR_ANY but now know whether IPv6 works */ | |
798 family = use_ipv6 ? AF_INET6 : AF_INET; | |
799 } else if (family == AF_INET6 && use_ipv6 == 2) { | |
800 /* don't know if IPv6 works but have an IPv6 address */ | |
801 use_ipv6 = 1; | |
802 } | |
803 | |
804 dcc_mk_su(&sp->su, family, &sp->arg_addr, sp->arg_port); | |
805 if (0 >= dcc_udp_bind(dcc_emsg, &sp->udp, | |
806 &sp->su, retry_secsp)) { | |
807 dcc_error_msg("%s", dcc_emsg); | |
808 continue; | |
809 } | |
810 | |
811 /* set socket receive buffer size as large as possible */ | |
812 for (;;) { | |
813 if (!setsockopt(sp->udp, SOL_SOCKET, SO_RCVBUF, | |
814 &srvr_rcvbuf, sizeof(srvr_rcvbuf))) | |
815 break; | |
816 if (srvr_rcvbuf_set || srvr_rcvbuf <= 4096) { | |
817 dcc_error_msg("setsockopt(%s,SO_RCVBUF=%d): %s", | |
818 dcc_su2str_err(&sp->su), | |
819 srvr_rcvbuf, | |
820 ERROR_STR()); | |
821 break; | |
822 } | |
823 srvr_rcvbuf -= 4096; | |
824 } | |
825 srvr_rcvbuf_set = 1; | |
826 | |
827 ++num_socs; | |
828 } | |
829 | |
830 /* Finally decide the IPv6 issue if we found no sign of IPv6 */ | |
831 if (use_ipv6 == 2) | |
832 use_ipv6 = 0; | |
833 | |
834 return num_socs; | |
835 } | |
836 | |
837 | |
838 | |
839 /* get ready to bind to all local IP addreses */ | |
840 static u_char /* 1=added an interface */ | |
841 add_ifs(u_char not_quiet) | |
842 { | |
843 u_char added; | |
844 SRVR_SOC *sp; | |
845 PORT_LIST *pport; | |
846 #ifdef HAVE_GETIFADDRS | |
847 struct ifaddrs *ifap0, *ifap; | |
848 int num_ifs; | |
849 #endif | |
850 | |
851 if (!ports) | |
852 return 0; | |
853 | |
854 added = 0; | |
855 | |
856 #ifdef HAVE_GETIFADDRS | |
857 if (0 > getifaddrs(&ifap0)) { | |
858 dcc_error_msg("getifaddrs(): %s", ERROR_STR()); | |
859 ifap0 = 0; | |
860 } | |
861 | |
862 num_ifs = 0; | |
863 for (pport = ports; pport; pport = pport->fwd) { | |
864 const SRVR_SOC *listener = 0; | |
865 | |
866 for (ifap = ifap0; ifap; ifap = ifap->ifa_next) { | |
867 if (!(ifap->ifa_flags & IFF_UP)) | |
868 continue; | |
869 if (!ifap->ifa_addr) | |
870 continue; | |
871 switch (ifap->ifa_addr->sa_family) { | |
872 case AF_INET: | |
873 ++num_ifs; | |
874 sp = add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_NEW, | |
875 ifap->ifa_addr->sa_family, | |
876 &((struct sockaddr_in *)ifap | |
877 ->ifa_addr)->sin_addr, | |
878 pport->port); | |
879 break; | |
880 case AF_INET6: | |
881 if (use_ipv6 == 0) | |
882 continue; | |
883 if (use_ipv6 == 2) | |
884 use_ipv6 = 1; | |
885 ++num_ifs; | |
886 sp = add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_NEW, | |
887 ifap->ifa_addr->sa_family, | |
888 &((struct sockaddr_in6*)ifap | |
889 ->ifa_addr)->sin6_addr, | |
890 pport->port); | |
891 break; | |
892 default: | |
893 continue; | |
894 } | |
895 if (sp->flags & SRVR_SOC_NEW) { | |
896 added = 1; | |
897 if (not_quiet) | |
898 dcc_trace_msg("start listening on %s", | |
899 dcc_su2str_err(&sp->su)); | |
900 } | |
901 sp->flags &= ~(SRVR_SOC_MARK | SRVR_SOC_NEW); | |
902 | |
903 /* interfaces can have duplicate addresses */ | |
904 if (listener == sp) | |
905 continue; | |
906 if (!listener) { | |
907 listener = sp; | |
908 if (!(sp->flags & SRVR_SOC_LISTEN)) { | |
909 sp->flags |= SRVR_SOC_LISTEN; | |
910 added = 1; | |
911 } | |
912 } else { | |
913 if (sp->flags & SRVR_SOC_LISTEN) { | |
914 sp->flags &= ~SRVR_SOC_LISTEN; | |
915 added = 1; | |
916 } | |
917 } | |
918 } | |
919 } | |
920 #ifdef HAVE_FREEIFADDRS | |
921 /* since this is done only a few times when HAVE_FREEIFADDRS is not | |
922 * defined, don't worry if we cannot release the list of interfaces */ | |
923 freeifaddrs(ifap0); | |
924 #endif | |
925 | |
926 if (num_ifs > 0) | |
927 return added; | |
928 #endif /* HAVE_GETIFADDRS */ | |
929 | |
930 /* if we got no joy from getifaddrs(), use INADDR_ANY */ | |
931 for (pport = ports; pport; pport = pport->fwd) { | |
932 sp = add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_LISTEN | SRVR_SOC_NEW, | |
933 AF_UNSPEC, 0, pport->port); | |
934 if (sp->flags & SRVR_SOC_NEW) { | |
935 added = 1; | |
936 if (not_quiet) | |
937 dcc_trace_msg("fallback listen on %s", | |
938 dcc_su2str_err(&sp->su)); | |
939 } | |
940 sp->flags &= ~(SRVR_SOC_MARK | SRVR_SOC_NEW); | |
941 } | |
942 | |
943 return added; | |
944 } | |
945 | |
946 | |
947 | |
948 /* deal with changes to network interfaces */ | |
949 static u_char /* 1=something changed */ | |
950 get_if_changes(u_char not_quiet) | |
951 { | |
952 SRVR_SOC *sp, **spp; | |
953 u_char changed; | |
954 | |
955 for (sp = srvr_socs; sp; sp = sp->fwd) { | |
956 if (sp->flags & SRVR_SOC_IF) | |
957 sp->flags |= SRVR_SOC_MARK; | |
958 } | |
959 | |
960 changed = add_ifs(not_quiet); | |
961 | |
962 spp = &srvr_socs; | |
963 while ((sp = *spp) != 0) { | |
964 /* an interface recognized by add_srvr_soc() will have | |
965 * its SRVR_SOC_MARK cleared */ | |
966 if (!(sp->flags & SRVR_SOC_MARK)) { | |
967 spp = &sp->fwd; | |
968 continue; | |
969 } | |
970 | |
971 /* forget interfaces that have disappeared */ | |
972 dcc_trace_msg("stop listening on %s", dcc_su2str_err(&sp->su)); | |
973 changed = 1; | |
974 if (srvr_socs_end == &sp->fwd) | |
975 srvr_socs_end = spp; | |
976 *spp = sp->fwd; | |
977 if (sp->udp >= 0) | |
978 close(sp->udp); | |
979 if (sp->listen >= 0) | |
980 close(sp->listen); | |
981 dcc_free(sp); | |
982 } | |
983 | |
984 return changed; | |
985 } | |
986 | |
987 | |
988 | |
989 static const char * | |
990 parse_rl_rate(RL_RATE *rate, float penalty_secs, | |
991 const char *label, const char *arg) | |
992 { | |
993 char *p; | |
994 int per_sec, hi; | |
995 | |
996 if (penalty_secs >= 0.0) | |
997 rate->penalty_secs = penalty_secs; | |
998 | |
999 if (*arg == '\0') | |
1000 return arg; | |
1001 | |
1002 if (*arg == ',') | |
1003 return ++arg; | |
1004 | |
1005 per_sec = strtod(arg, &p) * RL_SCALE; | |
1006 hi = per_sec*RL_AVG_SECS; | |
1007 if ((*p != '\0' && *p != ',') | |
1008 || hi < RL_SCALE || per_sec > RL_MAX_CREDITS) { | |
1009 dcc_error_msg("invalid %s value in \"%s\"", | |
1010 label, arg); | |
1011 return ""; | |
1012 } | |
1013 | |
1014 /* maximum events/second * RL_SCALE */ | |
1015 rate->per_sec = per_sec; | |
1016 | |
1017 /* maximum allowed accumulated credits */ | |
1018 rate->hi = hi; | |
1019 | |
1020 /* minimum credit account balance */ | |
1021 rate->lo = -per_sec * rate->penalty_secs; | |
1022 | |
1023 return (*p == ',') ? p+1 : p; | |
1024 } | |
1025 | |
1026 | |
1027 | |
1028 static void | |
1029 add_dbclean_flag(char flag) | |
1030 { | |
1031 if (dbclean_flags_len >= MAX_DBCLEAN_FLAGS) | |
1032 dcc_logbad(EX_SOFTWARE, "too many flags for dbclean"); | |
1033 dbclean_flags[dbclean_flags_len++] = flag; | |
1034 } | |
1035 | |
1036 | |
1037 | |
1038 static void | |
1039 add_dbclean_arg(const char *arg) | |
1040 { | |
1041 int i; | |
1042 | |
1043 if (dbclean_argc >= DIM(dbclean_argv)-2) | |
1044 dcc_logbad(EX_SOFTWARE, "too many args for dbclean"); | |
1045 dbclean_argv[dbclean_argc++] = arg; | |
1046 i = snprintf(dbclean_args_str+dbclean_args_str_len, | |
1047 sizeof(dbclean_args_str)-dbclean_args_str_len, | |
1048 " %s", arg); | |
1049 dbclean_args_str_len += i; | |
1050 if (dbclean_args_str_len >= ISZ(dbclean_args_str)-2) | |
1051 dcc_logbad(EX_SOFTWARE, "too many args for dbclean"); | |
1052 } | |
1053 | |
1054 | |
1055 | |
1056 /* check effort to repair database */ | |
1057 static void | |
1058 check_dbclean(int options) | |
1059 { | |
1060 int status; | |
1061 pid_t pid; | |
1062 u_char ok; | |
1063 | |
1064 if (dbclean_pid < 0) | |
1065 return; | |
1066 | |
1067 pid = waitpid(dbclean_pid, &status, options); | |
1068 if (pid != dbclean_pid) | |
1069 return; | |
1070 | |
1071 dbclean_pid = -1; | |
1072 | |
1073 /* do not try failing dbclean too often */ | |
1074 #if defined(WIFEXITED) && defined(WEXITSTATUS) && defined(WTERMSIG) && defined(WIFSIGNALED) | |
1075 ok = 1; | |
1076 if (WIFSIGNALED(status)) { | |
1077 dcc_error_msg("dbclean exited with signal %d", | |
1078 WTERMSIG(status)); | |
1079 ok = 0; | |
1080 } else if (WIFEXITED(status)) { | |
1081 status = WEXITSTATUS(status); | |
1082 if (status != EX_OK) { | |
1083 if (status > 100 && status < 130) | |
1084 dcc_error_msg("dbclean stopped after signal %d", | |
1085 status-100); | |
1086 else | |
1087 dcc_error_msg("dbclean exited with status %d", | |
1088 status); | |
1089 ok = 0; | |
1090 } | |
1091 } | |
1092 #else | |
1093 ok = (status == EX_OK); | |
1094 #endif | |
1095 if (ok) { | |
1096 dbclean_failed = 0; | |
1097 dbclean_limit_secs = DBCLEAN_LIMIT_SECS; | |
1098 } else { | |
1099 dbclean_failed = db_time.tv_sec; | |
1100 dbclean_limit_secs *= 2; | |
1101 if (dbclean_limit_secs > DEL_DBCLEAN_SECS) | |
1102 dbclean_limit_secs = DEL_DBCLEAN_SECS; | |
1103 } | |
1104 | |
1105 /* don't restart dbclean until after it has stopped running | |
1106 * and cooled for a while */ | |
1107 dbclean_limit = db_time.tv_sec + dbclean_limit_secs; | |
1108 } | |
1109 | |
1110 | |
1111 | |
1112 /* try to repair the database */ | |
1113 static void | |
1114 run_dbclean(const char *mode, /* combination of '', R, S, and W */ | |
1115 const char *reason) | |
1116 { | |
1117 int i; | |
1118 | |
1119 check_dbclean(0); /* wait until previous ends */ | |
1120 | |
1121 wfix_later(WFIX_RECHECK_SECS); | |
1122 | |
1123 i = snprintf(&dbclean_flags[dbclean_flags_len], | |
1124 ISZ(dbclean_flags)-dbclean_flags_len, "%s", | |
1125 mode); | |
1126 if (i+dbclean_flags_len >= ISZ(dbclean_flags)) | |
1127 dcc_logbad(EX_SOFTWARE, "too many flags for dbclean"); | |
1128 | |
1129 dbclean_pid = fork(); | |
1130 if (dbclean_pid < 0) { | |
1131 dcc_error_msg("dbclean fork(): %s", ERROR_STR()); | |
1132 } else if (dbclean_pid == 0) { | |
1133 dcc_trace_msg("%s; starting `%s %s%s`", | |
1134 reason, dbclean, dbclean_flags, dbclean_args_str); | |
1135 execv(dbclean, (char **)dbclean_argv); | |
1136 dcc_error_msg("execv(%s %s%s): %s", | |
1137 dbclean, dbclean_flags, dbclean_args_str, | |
1138 ERROR_STR()); | |
1139 exit(-1); | |
1140 } | |
1141 | |
1142 need_del_dbclean = 0; | |
1143 dbclean_limit = db_time.tv_sec + dbclean_limit_secs; | |
1144 } | |
1145 | |
1146 | |
1147 | |
1148 static void | |
1149 close_srvr_socs(void) | |
1150 { | |
1151 SRVR_SOC *sp; | |
1152 | |
1153 for (sp = srvr_socs; sp; sp = sp->fwd) { | |
1154 if (sp->udp >= 0) { | |
1155 close(sp->udp); | |
1156 sp->udp = -1; | |
1157 } | |
1158 iflod_listen_close(sp); | |
1159 } | |
1160 } | |
1161 | |
1162 | |
1163 | |
1164 /* close files and otherwise clean up after being forked as a helper */ | |
1165 void | |
1166 after_fork(void) | |
1167 { | |
1168 IFLOD_INFO *ifp; | |
1169 OFLOD_INFO *ofp; | |
1170 | |
1171 resolve_hosts_pid = -1; | |
1172 dbclean_pid = -1; | |
1173 | |
1174 close_srvr_socs(); | |
1175 for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) { | |
1176 if (ifp->soc >= 0) | |
1177 close(ifp->soc); | |
1178 } | |
1179 for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) { | |
1180 if (ofp->soc >= 0) | |
1181 close(ofp->soc); | |
1182 } | |
1183 | |
1184 signal(SIGHUP, SIG_DFL); | |
1185 signal(SIGTERM, SIG_DFL); | |
1186 signal(SIGINT, SIG_DFL); | |
1187 signal(SIGPIPE, SIG_DFL); | |
1188 #ifdef SIGXFSZ | |
1189 signal(SIGXFSZ, SIG_DFL); | |
1190 #endif | |
1191 } | |
1192 | |
1193 | |
1194 | |
1195 /* do not worry about cleaning to fix a window overflow for a while */ | |
1196 static void | |
1197 wfix_later(time_t delay) | |
1198 { | |
1199 dbclean_wfix_state = WFIX_DELAY; | |
1200 dbclean_wfix_secs = db_time.tv_sec + delay; | |
1201 } | |
1202 | |
1203 | |
1204 | |
1205 static double | |
1206 wfix_measure(u_char force) | |
1207 { | |
1208 double secs; | |
1209 | |
1210 secs = db_time.tv_sec - wfix_check_start; | |
1211 if (force || secs <= 0.0 || secs > WFIX_MEASURE_SECS*2 | |
1212 || total_ops < wfix_ops) { | |
1213 wfix_check_start = db_time.tv_sec; | |
1214 dbclean_wfix_secs = db_time.tv_sec + WFIX_MEASURE_SECS; | |
1215 wfix_ops = total_ops; | |
1216 return -1.0; | |
1217 } | |
1218 return (total_ops - wfix_ops) / secs; | |
1219 } | |
1220 | |
1221 | |
1222 | |
1223 static u_char /* 1=need dbclean now */ | |
1224 wfix(char *reason, u_int reason_len) | |
1225 { | |
1226 double rate; | |
1227 struct timeval sn; | |
1228 time_t next_clean; | |
1229 | |
1230 /* stop everything if dbclean is running */ | |
1231 if (db_minimum_map || wfix_quiet_rate <= 0.0) { | |
1232 wfix_later(WFIX_RECHECK_SECS); | |
1233 return 0; | |
1234 } | |
1235 | |
1236 switch (dbclean_wfix_state) { | |
1237 case WFIX_DELAY: /* just checking */ | |
1238 if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS)) | |
1239 return 0; | |
1240 | |
1241 /* no quick cleaning soon after the database was created, | |
1242 * cleaned or repaired */ | |
1243 dcc_ts2timeval(&sn, &db_parms.sn); | |
1244 if (sn.tv_sec > db_time.tv_sec) | |
1245 sn.tv_sec = 0; | |
1246 if (sn.tv_sec < dbclean_failed | |
1247 && dbclean_failed <= db_time.tv_sec) | |
1248 sn.tv_sec = dbclean_failed; | |
1249 if (sn.tv_sec <= db_time.tv_sec) { | |
1250 dbclean_wfix_secs = sn.tv_sec + WFIX_POST_CLEAN_SECS; | |
1251 if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS)) | |
1252 return 0; | |
1253 } | |
1254 | |
1255 /* check later if dbclean might run soon */ | |
1256 next_clean = clean_fake_secs; | |
1257 if (db_time.tv_sec >= next_clean - WFIX_PRE_CLEAN_SECS) { | |
1258 dbclean_wfix_secs = next_clean + WFIX_POST_CLEAN_SECS; | |
1259 if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS)) | |
1260 return 0; | |
1261 } | |
1262 next_clean = clean_last_secs + 24*60*60; | |
1263 if (db_time.tv_sec >= next_clean - WFIX_PRE_CLEAN_SECS) { | |
1264 dbclean_wfix_secs = next_clean + WFIX_POST_CLEAN_SECS; | |
1265 if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS)) | |
1266 return 0; | |
1267 } | |
1268 | |
1269 /* check later if the database is not too large now */ | |
1270 if (db_fsize + db_hash_fsize | |
1271 < (wfix_size_set ? wfix_size : db_max_rss)) { | |
1272 wfix_later(WFIX_RECHECK_SECS); | |
1273 break; | |
1274 } | |
1275 /* the database is too big, so try to chase the clients | |
1276 * to the other DCC server (if any) */ | |
1277 dbclean_wfix_state = WFIX_BUSY; | |
1278 wfix_measure(1); | |
1279 break; | |
1280 | |
1281 case WFIX_BUSY: | |
1282 rate = wfix_measure(0); | |
1283 if (rate >= 0.0) { | |
1284 wfix_busy_rate = rate; | |
1285 dbclean_wfix_state = WFIX_QUIET; | |
1286 dbclean_wfix_secs = db_time.tv_sec + WFIX_QUIET_SECS; | |
1287 } | |
1288 break; | |
1289 | |
1290 case WFIX_QUIET: /* waiting for clients to flee */ | |
1291 dbclean_wfix_state = WFIX_CHECK; | |
1292 wfix_measure(1); | |
1293 break; | |
1294 | |
1295 case WFIX_CHECK: /* counting clients */ | |
1296 rate = wfix_measure(0); | |
1297 if (rate < 0.0) | |
1298 break; | |
1299 if (rate <= wfix_quiet_rate | |
1300 || rate <= wfix_busy_rate * wfix_rate_change) { | |
1301 snprintf(reason, reason_len, | |
1302 "database size "L_DPAT">"L_DPAT | |
1303 "; load changed from %0.1f to %0.1f", | |
1304 db_fsize + db_hash_fsize, | |
1305 wfix_size_set ? wfix_size : db_max_rss, | |
1306 wfix_busy_rate, rate); | |
1307 return 1; | |
1308 } | |
1309 /* Other DCC servers did not take over the load. Maybe later */ | |
1310 if (db_debug) | |
1311 dcc_trace_msg("database size "L_DPAT" > "L_DPAT | |
1312 ", but load changed from %0.1f to %0.1f", | |
1313 db_fsize + db_hash_fsize, | |
1314 wfix_size_set ? wfix_size : db_max_rss, | |
1315 wfix_busy_rate, rate); | |
1316 wfix_later(WFIX_RECHECK_SECS); | |
1317 break; | |
1318 } | |
1319 | |
1320 return 0; | |
1321 } | |
1322 | |
1323 | |
1324 | |
1325 /* check for changes or other interesting events when the flood timer expires */ | |
1326 static time_t /* microseconds to wait */ | |
1327 check_changes(void) | |
1328 { | |
1329 static time_t misc_timer, files_timer; | |
1330 time_t secs; | |
1331 DB_HADDR hash_free; | |
1332 const char *mode; | |
1333 const char *reason; | |
1334 char reason_buf[100]; | |
1335 | |
1336 reason = 0; | |
1337 mode = 0; | |
1338 | |
1339 if (db_failed_line) { | |
1340 snprintf(reason_buf, sizeof(reason_buf), | |
1341 "database broken at line %d in %s "DCC_VERSION, | |
1342 db_failed_line, db_failed_file); | |
1343 reason = reason_buf; | |
1344 mode = "Rbad"; | |
1345 | |
1346 } else if ((hash_free | |
1347 = db_hash_len - db_hash_used) < MIN_CLEAN_HASH_ENTRIES | |
1348 || hash_free < db_hash_len/20) { | |
1349 /* try to expand the hash table when there are only | |
1350 * a few free slots or the load factor rises above .95 */ | |
1351 if (hash_free < MIN_HASH_ENTRIES) | |
1352 snprintf(reason_buf, sizeof(reason_buf), | |
1353 "%d free hash entries", | |
1354 hash_free); | |
1355 else | |
1356 snprintf(reason_buf, sizeof(reason_buf), | |
1357 "%d free hash entries among %d total", | |
1358 hash_free, db_hash_len); | |
1359 reason = reason_buf; | |
1360 mode = "Rhash"; | |
1361 | |
1362 } else { | |
1363 /* check nothing else if it is not yet time */ | |
1364 secs = next_flods_ck - db_time.tv_sec; | |
1365 if (secs > 0 && secs <= flods_ck_secs) | |
1366 return secs * DCC_US; | |
1367 } | |
1368 | |
1369 next_flods_ck = db_time.tv_sec + flods_ck_secs; | |
1370 | |
1371 /* do not make some checks too often even when flood checking is | |
1372 * being rushed */ | |
1373 if (DB_IS_TIME(misc_timer, MISC_CK_SECS)) { | |
1374 misc_timer = db_time.tv_sec + MISC_CK_SECS; | |
1375 | |
1376 /* shrink our memory footprint | |
1377 * if we are so idle that we don't have anything to flush */ | |
1378 if (db_need_flush_secs == 0) { | |
1379 if (db_unload(0, 1) == 1) { | |
1380 rel_db_states(); | |
1381 db_unload(0, 1); | |
1382 } | |
1383 } | |
1384 | |
1385 if (DB_IS_TIME(files_timer, 30)) { | |
1386 files_timer = db_time.tv_sec + 30; | |
1387 | |
1388 if (0 >= check_load_ids(0)) | |
1389 dcc_error_msg("check/reload: %s", dcc_emsg); | |
1390 | |
1391 check_blacklist_file(); | |
1392 | |
1393 #ifdef HAVE_GETIFADDRS | |
1394 /* check for network interface changes, but only | |
1395 * if we can release the result of getifaddrs() */ | |
1396 if (get_if_changes(1)) { | |
1397 int socs = open_srvr_socs(0); | |
1398 if (!socs) | |
1399 bad_stop("no server sockets"); | |
1400 else if (socs > 0) | |
1401 flods_restart("new network interfaces", | |
1402 1); | |
1403 } | |
1404 #endif | |
1405 } | |
1406 | |
1407 if (DB_IS_TIME(need_clients_save, CLIENTS_SAVE_SECS)) | |
1408 clients_save(); | |
1409 | |
1410 /* sound a claim to our server-ID if the database is locked */ | |
1411 if (DB_IS_TIME(host_id_next, DCC_SRVR_ID_SECS) | |
1412 && DB_IS_LOCKED() | |
1413 &&!db_failed_line ) { | |
1414 refresh_srvr_rcd(host_id_sum, my_srvr_id, | |
1415 "adding server-ID claim"); | |
1416 | |
1417 if (!db_failed_line ) { | |
1418 host_id_last = db_time.tv_sec; | |
1419 host_id_next = host_id_last + DCC_SRVR_ID_SECS; | |
1420 } | |
1421 } | |
1422 } | |
1423 | |
1424 /* note when hash expansion finishes and collect a zombie */ | |
1425 check_dbclean(WNOHANG); | |
1426 | |
1427 if (reason != 0) { | |
1428 ; | |
1429 | |
1430 } else if (DB_IS_TIME(clean_fake_secs, 3*24*60*60) | |
1431 && (!db_minimum_map | |
1432 || DB_IS_TIME(clean_last_secs+2*24*60*60, 4*24*60*60))) { | |
1433 reason = "work around broken cron job"; | |
1434 mode = "Rcron"; | |
1435 | |
1436 } else if (need_del_dbclean != 0 | |
1437 && DB_IS_TIME(del_dbclean_next, DEL_DBCLEAN_SECS)) { | |
1438 /* the deletion of a report needs to be cleaned up */ | |
1439 reason = need_del_dbclean; | |
1440 mode = "Rdel"; | |
1441 | |
1442 } else if (DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS) | |
1443 && wfix(reason_buf, sizeof(reason_buf))) { | |
1444 reason = reason_buf; | |
1445 mode = "Rquick"; | |
1446 } | |
1447 | |
1448 if (reason) { | |
1449 if (!DB_IS_TIME(dbclean_limit, dbclean_limit_secs)) { | |
1450 if (next_flods_ck > dbclean_limit) | |
1451 next_flods_ck = dbclean_limit; | |
1452 } else if (dbclean_pid < 0) { | |
1453 run_dbclean(mode, reason); | |
1454 } else { | |
1455 RUSH_NEXT_FLODS_CK(); | |
1456 } | |
1457 } else { | |
1458 flods_ck(0); | |
1459 } | |
1460 | |
1461 /* we probably delayed */ | |
1462 gettimeofday(&db_time, 0); | |
1463 | |
1464 secs = next_flods_ck - db_time.tv_sec; | |
1465 return secs >= 0 ? secs*DCC_US : 0; | |
1466 } | |
1467 | |
1468 | |
1469 | |
1470 static void NRATTRIB | |
1471 recv_job(void) | |
1472 { | |
1473 fd_set rfds, *prfds, wfds, *pwfds; | |
1474 # define PFD_SET(_fd0,_fds) {int _fd = _fd0; \ | |
1475 p##_fds = &_fds; FD_SET(_fd,p##_fds); \ | |
1476 if (max_fd < _fd) max_fd = _fd;} | |
1477 int max_fd, nfds; | |
1478 IFLOD_INFO *ifp; | |
1479 OFLOD_INFO *ofp; | |
1480 struct timeval delay; | |
1481 time_t delay_us, slept_us, was_too_busy, us; | |
1482 struct timeval iflods_read, busy_time, extra_time; | |
1483 u_char db_has_failed; | |
1484 # define QUANTUM (DCC_US/10) /* try to use only ~50% of the system */ | |
1485 SRVR_SOC *sp; | |
1486 QUEUE *q; | |
1487 int bad_select; | |
1488 u_char worked; | |
1489 int fd, i; | |
1490 | |
1491 bad_select = 3; | |
1492 was_too_busy = 0; | |
1493 gettimeofday(&db_time, 0); | |
1494 iflods_read = db_time; | |
1495 busy_time = db_time; | |
1496 extra_time = db_time; | |
1497 delay_us = flods_ck_secs*DCC_US; | |
1498 db_has_failed = 0; | |
1499 for (;;) { | |
1500 dcc_emsg[0] = '\0'; | |
1501 | |
1502 if (stopint) { | |
1503 flods_ck_secs = SHUTDOWN_DELAY; | |
1504 if (flods_off < 100000) { | |
1505 flods_off = 100000; | |
1506 flods_stop("server stopping", 0); | |
1507 check_dbclean(WNOHANG); | |
1508 if (dbclean_pid > 0) { | |
1509 kill(dbclean_pid, SIGINT); | |
1510 usleep(100*1000); | |
1511 } | |
1512 /* get started flushing the database while | |
1513 * we wait for flooding to stop */ | |
1514 rel_db_states(); | |
1515 db_minimum_map = 1; | |
1516 db_unload(0, 2); | |
1517 } | |
1518 /* get serious when the floods have stopped */ | |
1519 if (oflods.total == 0) { | |
1520 if (stopint < 0) | |
1521 dccd_quit(0, "gracefully stopping%s", | |
1522 stop_mode == 1 | |
1523 ? " for reboot" | |
1524 : stop_mode == 2 | |
1525 ? " cleanly" | |
1526 : ""); | |
1527 dccd_quit(stopint | 128, | |
1528 "exiting with signal %d", stopint); | |
1529 } | |
1530 } | |
1531 | |
1532 if (db_has_failed != db_failed_line) { | |
1533 db_has_failed = db_failed_line; | |
1534 if (db_failed_line) { | |
1535 ++flods_off; | |
1536 flods_stop("database corrupt", 1); | |
1537 rel_db_states(); | |
1538 db_unload(0, 2); | |
1539 db_unlock(); | |
1540 } | |
1541 } | |
1542 | |
1543 FD_ZERO(&rfds); | |
1544 prfds = 0; | |
1545 FD_ZERO(&wfds); | |
1546 pwfds = 0; | |
1547 max_fd = -1; | |
1548 | |
1549 /* look for client requests */ | |
1550 for (sp = srvr_socs; sp; sp = sp->fwd) { | |
1551 if (sp->udp >= 0) | |
1552 PFD_SET(sp->udp, rfds); | |
1553 } | |
1554 | |
1555 if (was_too_busy > 0) | |
1556 was_too_busy = QUANTUM - tv_diff2us(&db_time, | |
1557 &busy_time); | |
1558 if (was_too_busy > 0 | |
1559 && (tv_diff2us(&db_time, &extra_time) | |
1560 < min(30, min(KEEPALIVE_IN, KEEPALIVE_OUT)/2)) | |
1561 && FLODS_OK()) { | |
1562 /* if we have been too busy, | |
1563 * then do nothing extra for a while */ | |
1564 if (delay_us > was_too_busy) | |
1565 delay_us = was_too_busy; | |
1566 } else { | |
1567 extra_time = db_time; | |
1568 | |
1569 /* Accept new incoming flood connections | |
1570 * if flooding is on | |
1571 * and we don't already have too many floods. */ | |
1572 if (iflods.open < DCCD_MAX_FLOODS) { | |
1573 for (sp = srvr_socs; sp; sp = sp->fwd) { | |
1574 if (sp->listen >= 0) | |
1575 PFD_SET(sp->listen, rfds); | |
1576 } | |
1577 } | |
1578 | |
1579 /* pump floods out */ | |
1580 for (ofp = oflods.infos, i = 0; | |
1581 i < oflods.open; | |
1582 ++ofp) { | |
1583 if (ofp->soc < 0) | |
1584 continue; | |
1585 ++i; | |
1586 if (ofp->flags & OFLOD_FG_CONNECTED) { | |
1587 PFD_SET(ofp->soc, rfds); | |
1588 if (!(ofp->flags & OFLOD_FG_EAGAIN) | |
1589 && ofp->obuf_len != 0) | |
1590 PFD_SET(ofp->soc, wfds); | |
1591 } else { | |
1592 PFD_SET(ofp->soc, wfds); | |
1593 } | |
1594 } | |
1595 | |
1596 /* pump floods in */ | |
1597 for (ifp = iflods.infos, i = 0; | |
1598 i < iflods.open; | |
1599 ++ifp) { | |
1600 if (ifp->soc < 0) | |
1601 continue; | |
1602 ++i; | |
1603 if (ifp->flags & IFLOD_FG_CONNECTED) { | |
1604 PFD_SET(ifp->soc, rfds); | |
1605 } else { | |
1606 PFD_SET(ifp->soc, wfds); | |
1607 } | |
1608 } | |
1609 } | |
1610 | |
1611 /* push data to the disk */ | |
1612 if (db_need_flush_secs != 0) { | |
1613 if (DB_IS_TIME(db_need_flush_secs, | |
1614 max(DB_URGENT_NEED_FLUSH_SECS, | |
1615 DB_NEED_FLUSH_SECS))) { | |
1616 db_flush_needed(); | |
1617 gettimeofday(&db_time, 0); | |
1618 } | |
1619 us = db_need_flush_secs - db_time.tv_sec; | |
1620 if (us >= 0) { | |
1621 us *= DCC_US; | |
1622 if (delay_us > us) | |
1623 delay_us = us; | |
1624 } | |
1625 } | |
1626 | |
1627 /* let dbclean run if we have run out of work | |
1628 * or if we have been holding the lock for 0.1 seconds*/ | |
1629 if (db_minimum_map | |
1630 && DB_IS_LOCKED() | |
1631 && (delay_us != 0 | |
1632 || tv_diff2us(&db_time, &db_locked) >= DCC_US/10)) { | |
1633 db_unlock(); | |
1634 } | |
1635 | |
1636 /* delay until it is time to answer the oldest anonymous | |
1637 * request or something else that needs doing */ | |
1638 delay.tv_sec = delay_us/DCC_US; | |
1639 delay.tv_usec = delay_us%DCC_US; | |
1640 nfds = select(max_fd+1, prfds, pwfds, 0, &delay); | |
1641 if (nfds < 0) { | |
1642 if (errno != EINTR) { | |
1643 if (--bad_select < 0) | |
1644 bad_stop("give up after select(): %s", | |
1645 ERROR_STR()); | |
1646 else | |
1647 dcc_error_msg("select(): %s", | |
1648 ERROR_STR()); | |
1649 } | |
1650 /* ignore EINTR but recompute timers */ | |
1651 FD_ZERO(&rfds); | |
1652 FD_ZERO(&wfds); | |
1653 } else { | |
1654 bad_select = 3; | |
1655 } | |
1656 | |
1657 gettimeofday(&wake_time, 0); | |
1658 slept_us = tv_diff2us(&wake_time, &db_time); | |
1659 if (slept_us >= 500) { | |
1660 /* If select() paused for at least 0.5 millisecond, | |
1661 * then the waiting request has just now arrived. */ | |
1662 req_recv_time = wake_time; | |
1663 } else { | |
1664 /* If select() did not pause, then assume the waiting | |
1665 * requests arrived when we were half finished working | |
1666 * on flooding and other work besides ordinary requests | |
1667 * before calling select(). */ | |
1668 tv_add_us(&req_recv_time, | |
1669 tv_diff2us(&wake_time, &req_recv_time) / 2); | |
1670 } | |
1671 db_time = wake_time; | |
1672 | |
1673 worked = 0; | |
1674 for (sp = srvr_socs; sp; sp = sp->fwd) { | |
1675 /* queue a new anonymous request | |
1676 * or answer a new authenticated request */ | |
1677 fd = sp->udp; | |
1678 if (fd >= 0 && FD_ISSET(fd, &rfds)) { | |
1679 --nfds; | |
1680 worked = 1; | |
1681 while (new_job(sp)) | |
1682 continue; | |
1683 } | |
1684 | |
1685 /* start a new incoming flood */ | |
1686 fd = sp->listen; | |
1687 if (fd >= 0 && FD_ISSET(fd, &rfds)) { | |
1688 --nfds; | |
1689 worked = 1; | |
1690 iflod_start(sp); | |
1691 } | |
1692 } | |
1693 if (worked) | |
1694 gettimeofday(&db_time, 0); | |
1695 /* reset request receipt clock for next time */ | |
1696 req_recv_time = db_time; | |
1697 | |
1698 /* Accept new flood data or start new SOCKS floods. | |
1699 * Listen to all peers to prevent starvation */ | |
1700 worked = 0; | |
1701 for (ifp = iflods.infos, i = 0; | |
1702 nfds > 0 && i < iflods.open; | |
1703 ++ifp) { | |
1704 if (ifp->soc < 0) | |
1705 continue; | |
1706 ++i; | |
1707 if (FD_ISSET(ifp->soc, &rfds) | |
1708 || FD_ISSET(ifp->soc, &wfds)) { | |
1709 --nfds; | |
1710 iflod_read(ifp); | |
1711 worked = 1; | |
1712 } | |
1713 } | |
1714 if (worked) { | |
1715 gettimeofday(&db_time, 0); | |
1716 iflods_read = db_time; | |
1717 } else if (was_too_busy <= 0) { | |
1718 /* if incoming floods have been quiet for | |
1719 * awhile, then assume flooding has caught up | |
1720 * after having been turned off */ | |
1721 if (tv_diff2us(&wake_time, &iflods_read) > 2*DCC_US) | |
1722 iflods_ok_timer = db_time.tv_sec; | |
1723 } | |
1724 | |
1725 /* pump output flood data and receive confirmations | |
1726 * talk to all peers to prevent starvation */ | |
1727 worked = 0; | |
1728 for (ofp = oflods.infos, i = 0; | |
1729 nfds > 0 && i < oflods.open; | |
1730 ++ofp) { | |
1731 if (ofp->soc < 0) | |
1732 continue; | |
1733 ++i; | |
1734 if (FD_ISSET(ofp->soc, &rfds)) { | |
1735 --nfds; | |
1736 oflod_read(ofp); | |
1737 if (ofp->soc < 0) | |
1738 continue; | |
1739 } | |
1740 if (FD_ISSET(ofp->soc, &wfds)) { | |
1741 --nfds; | |
1742 oflod_write(ofp); | |
1743 worked = 1; | |
1744 } | |
1745 } | |
1746 if (worked) | |
1747 gettimeofday(&db_time, 0); | |
1748 | |
1749 /* process delayed jobs when their times arrive */ | |
1750 worked = 0; | |
1751 for (;;) { | |
1752 q = queue_head; | |
1753 if (!q) { | |
1754 delay_us = flods_ck_secs*DCC_US; | |
1755 break; | |
1756 } | |
1757 | |
1758 /* decide whether this job's time has come | |
1759 * while defending against time jumps */ | |
1760 delay_us = tv_diff2us(&q->answer, &db_time); | |
1761 if (delay_us >= 1000 | |
1762 && delay_us <= DCC_MAX_RTT | |
1763 && !stopint) | |
1764 break; /* not yet time for next job */ | |
1765 | |
1766 queue_head = q->later; | |
1767 if (queue_head) | |
1768 queue_head->earlier = 0; | |
1769 --queue_cur; | |
1770 do_work(q); | |
1771 worked = 1; | |
1772 free_q(q); | |
1773 } | |
1774 if (worked) | |
1775 gettimeofday(&db_time, 0); | |
1776 | |
1777 /* check configuration changes etc. */ | |
1778 us = check_changes(); | |
1779 if (delay_us >= us) | |
1780 delay_us = us; | |
1781 | |
1782 us = tv_diff2us(&db_time, &wake_time); | |
1783 if (us >= QUANTUM && !stopint) { | |
1784 gettimeofday(&db_time, 0); | |
1785 busy_time = db_time; | |
1786 was_too_busy = QUANTUM; | |
1787 } | |
1788 } | |
1789 } | |
1790 | |
1791 | |
1792 | |
1793 static void | |
1794 add_queue(QUEUE *q) | |
1795 { | |
1796 QUEUE *qnext, **qp; | |
1797 | |
1798 TMSG1(QUERY, "received %s", op_id_ip(q)); | |
1799 if (!ck_clnt_id(q)) { | |
1800 free_q(q); | |
1801 return; | |
1802 } | |
1803 | |
1804 ++total_ops; | |
1805 | |
1806 /* immediately process requests from authenticated clients | |
1807 * if flooding is working */ | |
1808 if (q->delay_us == 0) { | |
1809 do_work(q); | |
1810 free_q(q); | |
1811 return; | |
1812 } | |
1813 | |
1814 /* don't let the queue of delayed requests get too large */ | |
1815 if (queue_cur >= queue_max) { | |
1816 clnt_msg(q, "drop excess queued %s", op_id_ip(q)); | |
1817 free_q(q); | |
1818 return; | |
1819 } | |
1820 | |
1821 tv_add_us(&q->answer, q->delay_us); | |
1822 | |
1823 /* add the new job to the queue */ | |
1824 ++queue_cur; | |
1825 qp = &queue_head; | |
1826 for (;;) { | |
1827 qnext = *qp; | |
1828 if (!qnext) { | |
1829 *qp = q; | |
1830 break; | |
1831 } | |
1832 if (qnext->answer.tv_sec > q->answer.tv_sec | |
1833 || (qnext->answer.tv_sec == q->answer.tv_sec | |
1834 && qnext->answer.tv_usec > q->answer.tv_usec)) { | |
1835 q->later = qnext; | |
1836 qnext->earlier = q; | |
1837 *qp = q; | |
1838 break; | |
1839 } | |
1840 q->earlier = qnext; | |
1841 qp = &qnext->later; | |
1842 } | |
1843 } | |
1844 | |
1845 | |
1846 | |
1847 /* get a new job in a datagram */ | |
1848 static u_char /* 1=call again */ | |
1849 new_job(SRVR_SOC *sp) | |
1850 { | |
1851 QUEUE *q; | |
1852 static struct iovec iov = {0, sizeof(q->pkt)}; | |
1853 static struct msghdr msg; | |
1854 int i, j; | |
1855 | |
1856 /* Find a free queue entry for the job. | |
1857 * Because we don't check for incoming jobs unless we think the | |
1858 * queue is not full, there must always be a free entry or | |
1859 * permission to make more entries. */ | |
1860 q = queue_free; | |
1861 if (q) { | |
1862 queue_free = q->later; | |
1863 } else { | |
1864 i = 16; | |
1865 q = dcc_malloc(i * sizeof(*q)); | |
1866 if (!q) | |
1867 dcc_logbad(EX_UNAVAILABLE, | |
1868 "malloc(%d queue entries) failed", i); | |
1869 queue_max_cur += i; | |
1870 /* put all but the last new queue entry on the free list */ | |
1871 while (--i > 0) { | |
1872 q->later = queue_free; | |
1873 queue_free = q; | |
1874 ++q; | |
1875 } | |
1876 } | |
1877 | |
1878 memset(q, 0, sizeof(*q)); | |
1879 q->sp = sp; | |
1880 iov.iov_base = (char *)&q->pkt; | |
1881 msg.msg_name = (void *)&q->clnt_su; | |
1882 msg.msg_namelen = sizeof(q->clnt_su); | |
1883 msg.msg_iov = &iov; | |
1884 msg.msg_iovlen = 1; | |
1885 i = recvmsg(sp->udp, &msg, 0); | |
1886 if (i < 0) { | |
1887 /* ignore some results of ICMP unreachables for UDP | |
1888 * retransmissions seen on some platforms */ | |
1889 if (DCC_BLOCK_ERROR()) { | |
1890 ; | |
1891 } else if (UNREACHABLE_ERRORS()) { | |
1892 TMSG2(QUERY, "recvmsg(%s): %s", | |
1893 dcc_su2str_err(&sp->su), ERROR_STR()); | |
1894 } else { | |
1895 dcc_error_msg("recvmsg(%s): %s", | |
1896 dcc_su2str_err(&sp->su), ERROR_STR()); | |
1897 } | |
1898 free_q(q); | |
1899 return 0; | |
1900 } | |
1901 if (q->clnt_su.sa.sa_family != sp->su.sa.sa_family | |
1902 && !dcc_ipv4sutoipv6(&q->clnt_su, &q->clnt_su)) { | |
1903 dcc_error_msg("recvmsg address family %d instead of %d", | |
1904 q->clnt_su.sa.sa_family, | |
1905 sp->su.sa.sa_family); | |
1906 free_q(q); | |
1907 return 1; | |
1908 } | |
1909 | |
1910 if (DCC_SU_PORT(&q->clnt_su) == 0) { | |
1911 drop_msg(q, "source port 0"); | |
1912 free_q(q); | |
1913 return 1; | |
1914 } | |
1915 | |
1916 q->pkt_len = i; | |
1917 if (i < ISZ(DCC_HDR)) { | |
1918 drop_msg(q, "short request of %d bytes", i); | |
1919 free_q(q); | |
1920 return 1; | |
1921 } | |
1922 j = ntohs(q->pkt.hdr.len); | |
1923 if (j != i) { | |
1924 drop_msg(q, "request with header length %d instead of %d", | |
1925 j, i); | |
1926 free_q(q); | |
1927 return 1; | |
1928 } | |
1929 | |
1930 if (q->pkt.hdr.pkt_vers > DCC_PKT_VERSION_MAX_VALID | |
1931 || q->pkt.hdr.pkt_vers < DCC_PKT_VERSION_MIN_VALID | |
1932 || ((q->pkt.hdr.pkt_vers > DCC_PKT_VERSION_MAX | |
1933 || q->pkt.hdr.pkt_vers < DCC_PKT_VERSION_MIN) | |
1934 && q->pkt.hdr.op != DCC_OP_NOP)) { | |
1935 drop_msg(q, "%s in unrecognized protocol version #%d", | |
1936 qop2str(q), q->pkt.hdr.pkt_vers); | |
1937 free_q(q); | |
1938 return 1; | |
1939 } | |
1940 | |
1941 q->answer = req_recv_time; | |
1942 | |
1943 switch ((DCC_OPS)q->pkt.hdr.op) { | |
1944 case DCC_OP_NOP: | |
1945 do_nop(q); | |
1946 free_q(q); | |
1947 return 1; | |
1948 | |
1949 case DCC_OP_REPORT: | |
1950 if (db_parms.flags & DB_PARM_FG_GREY) | |
1951 break; /* not valid for greylist servers */ | |
1952 add_queue(q); | |
1953 return 1; | |
1954 | |
1955 case DCC_OP_QUERY: | |
1956 add_queue(q); | |
1957 return 1; | |
1958 | |
1959 case DCC_OP_ADMN: | |
1960 do_admn(q); | |
1961 free_q(q); | |
1962 return 1; | |
1963 | |
1964 case DCC_OP_DELETE: | |
1965 do_delete(q); | |
1966 free_q(q); | |
1967 return 1; | |
1968 | |
1969 case DCC_OP_GREY_REPORT: | |
1970 case DCC_OP_GREY_QUERY: | |
1971 case DCC_OP_GREY_WHITE: | |
1972 if (!(db_parms.flags & DB_PARM_FG_GREY)) | |
1973 break; /* valid only for greylist servers */ | |
1974 do_grey(q); | |
1975 free_q(q); | |
1976 return 1; | |
1977 | |
1978 case DCC_OP_GREY_SPAM: | |
1979 if (!(db_parms.flags & DB_PARM_FG_GREY)) | |
1980 break; /* valid only for greylist servers */ | |
1981 do_grey_spam(q); | |
1982 free_q(q); | |
1983 return 1; | |
1984 | |
1985 case DCC_OP_INVALID: | |
1986 case DCC_OP_ANSWER: | |
1987 case DCC_OP_OK: | |
1988 case DCC_OP_ERROR: | |
1989 break; | |
1990 } | |
1991 | |
1992 drop_msg(q, "invalid %s", op_id_ip(q)); | |
1993 free_q(q); | |
1994 return 1; | |
1995 } | |
1996 | |
1997 | |
1998 | |
1999 void | |
2000 free_q(QUEUE *q) | |
2001 { | |
2002 if (q->rl) | |
2003 --q->rl->ref_cnt; | |
2004 q->later = queue_free; | |
2005 queue_free = q; | |
2006 } | |
2007 | |
2008 | |
2009 | |
2010 u_char | |
2011 dccd_db_open(u_char lock_mode) | |
2012 { | |
2013 DCC_CK_TYPES type; | |
2014 time_t clean_secs; | |
2015 DCC_TGTS tgts; | |
2016 int i; | |
2017 | |
2018 if (!db_open(dcc_emsg, -1, 0, 0, lock_mode | db_mode)) | |
2019 return 0; | |
2020 | |
2021 if (grey_on) { | |
2022 /* for greylisting, ignore the args an silently impose our | |
2023 * notion of which checksums to keep and flooding thresholds */ | |
2024 db_parms.nokeep_cks = def_nokeep_cks(); | |
2025 if (grey_weak_ip) | |
2026 DB_RESET_NOKEEP(db_parms.nokeep_cks, DCC_CK_IP); | |
2027 | |
2028 for (type = DCC_CK_TYPE_FIRST; | |
2029 type <= DCC_CK_TYPE_LAST; | |
2030 ++type) { | |
2031 if (type == DCC_CK_SRVR_ID) { | |
2032 flod_tholds[type] = 1; | |
2033 continue; | |
2034 } | |
2035 | |
2036 if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)) | |
2037 flod_tholds[type] = DCC_TGTS_INVALID; | |
2038 else | |
2039 flod_tholds[type] = 1; | |
2040 | |
2041 if (DCC_CK_IS_GREY_TRIPLE(grey_on,type) | |
2042 || type == DCC_CK_IP) { | |
2043 db_parms.ex_secs[type].all = grey_window; | |
2044 db_parms.ex_secs[type].spam = grey_white; | |
2045 } else if (type == DCC_CK_BODY | |
2046 || DCC_CK_IS_GREY_MSG(grey_on,type)) { | |
2047 db_parms.ex_secs[type].all = grey_window; | |
2048 db_parms.ex_secs[type].spam = grey_window; | |
2049 } else { | |
2050 db_parms.ex_secs[type].all = 1; | |
2051 db_parms.ex_secs[type].spam = 1; | |
2052 } | |
2053 } | |
2054 | |
2055 summarize_delay_secs = grey_embargo - FLODS_CK_SECS*2; | |
2056 | |
2057 } else { | |
2058 /* impose our notion of which normal checksums to keep */ | |
2059 DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_FLOD_PATH); | |
2060 DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_INVALID); | |
2061 DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_REP_TOTAL); | |
2062 DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_REP_BULK); | |
2063 db_parms.nokeep_cks = ((def_nokeep_cks() | |
2064 & ~reset_new_nokeep_cks) | |
2065 | set_new_nokeep_cks); | |
2066 | |
2067 for (type = DCC_CK_TYPE_FIRST; | |
2068 type <= DCC_CK_TYPE_LAST; | |
2069 ++type) { | |
2070 if (type == DCC_CK_SRVR_ID) { | |
2071 flod_tholds[type] = 1; | |
2072 continue; | |
2073 } | |
2074 | |
2075 if (type == DCC_CK_REP_TOTAL) | |
2076 tgts = DCC_TGTS_INVALID; | |
2077 if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)) | |
2078 tgts = DCC_TGTS_INVALID; | |
2079 else if (DCC_CK_IS_REP_CMN(0, type)) | |
2080 tgts = DCC_TGTS_INVALID; | |
2081 else | |
2082 tgts = BULK_THRESHOLD; | |
2083 flod_tholds[type] = tgts; | |
2084 } | |
2085 | |
2086 /* We should not delay reports or summaries so much that | |
2087 * dbclean might expire them before we can summarize them. */ | |
2088 summarize_delay_secs = DCC_OLD_SPAM_SECS; | |
2089 for (type = DCC_CK_TYPE_FIRST; | |
2090 type <= DCC_CK_TYPE_LAST; | |
2091 ++type) { | |
2092 if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)) | |
2093 continue; | |
2094 i = db_parms.ex_secs[type].spam; | |
2095 if (i != 0 && summarize_delay_secs > i) | |
2096 summarize_delay_secs = i; | |
2097 } | |
2098 } | |
2099 if (summarize_delay_secs < 1) | |
2100 summarize_delay_secs = 1; | |
2101 | |
2102 /* adjust the thresholds after possible changes to kept checksums */ | |
2103 set_db_tholds(db_parms.nokeep_cks); | |
2104 | |
2105 /* If we instead of cron asked for the last cleaning, make a note | |
2106 * to clean the database during the graveyard shift. | |
2107 * Otherwise the database will bloat while the cron job is broken. | |
2108 * | |
2109 * Compute 1 day + 45 minutes after cron should have last cleaned the | |
2110 * database, if it has been cleaned by cron within the last 3 days | |
2111 * or the quarter hour when it was last cleaned by this mechanism | |
2112 * provided that was between local midnight and 05:00 | |
2113 * or 3 minutes past a random quarter hour beteen midnight and 05:00 */ | |
2114 if (db_parms.cleaned_cron >= db_time.tv_sec - 3*24*60*60 | |
2115 && db_parms.cleaned_cron <= db_time.tv_sec) { | |
2116 clean_last_secs = db_parms.cleaned_cron; | |
2117 /* failsafe cleaning for the greylist database starts 15 minutes | |
2118 * before the main database */ | |
2119 clean_secs = clean_last_secs + 45*60 - grey_on*15*60; | |
2120 } else { | |
2121 struct tm tm; | |
2122 | |
2123 clean_last_secs = db_parms.cleaned; | |
2124 if (clean_last_secs >= db_time.tv_sec) | |
2125 clean_last_secs = 0; | |
2126 clean_secs = clean_last_secs; | |
2127 | |
2128 /* if the previous time for this mechanism is not good, | |
2129 * pick a new random time */ | |
2130 dcc_localtime(clean_secs, &tm); | |
2131 if (clean_secs == 0 || tm.tm_hour < 1 || tm.tm_hour >= 5) { | |
2132 int rnum = (u_int)(db_time.tv_sec + db_time.tv_usec | |
2133 + my_srvr_id - grey_on) % 23; | |
2134 tm.tm_hour = ((rnum / 4) % 4) + 1; | |
2135 tm.tm_min = (rnum % 4) * 15; | |
2136 clean_secs = mktime(&tm); | |
2137 } | |
2138 } | |
2139 /* round down to a quarter hour to prevent creep due to inevitiable | |
2140 * delays in cron or this mechanism starting dbclean */ | |
2141 clean_secs -= clean_secs % (15*60); | |
2142 clean_secs %= (24*60*60); /* failsafe cleaning target minute */ | |
2143 | |
2144 /* compute the next scheduled failsafe cleaning. */ | |
2145 clean_fake_secs = db_time.tv_sec - (db_time.tv_sec % (24*60*60)); | |
2146 clean_fake_secs += clean_secs; | |
2147 if (clean_fake_secs <= db_time.tv_sec) | |
2148 clean_fake_secs += 24*60*60; | |
2149 | |
2150 /* The next failsafe cleaning should happen a day after the | |
2151 * most recent cron or failsafe cleaning, modulo the delay before | |
2152 * dbclean starts */ | |
2153 while (clean_fake_secs <= clean_last_secs + 24*60*60 - 60*60) | |
2154 clean_fake_secs += 24*60*60; | |
2155 | |
2156 /* Do not failsafe clean during the first 48 hours after the | |
2157 * database was created to give the cron job a chance. | |
2158 * We do not want failsafe cleaning to ever be running when the | |
2159 * cron job tries to start. */ | |
2160 while (clean_fake_secs <= db_parms.cleared + 2*24*60*60 | |
2161 && db_parms.cleared <= db_time.tv_sec) | |
2162 clean_fake_secs += 24*60*60; | |
2163 | |
2164 total_ops = 0; | |
2165 | |
2166 /* push our thresholds and flags to the file */ | |
2167 return db_flush_parms(0); | |
2168 } | |
2169 | |
2170 | |
2171 | |
2172 /* clean shut down */ | |
2173 static void NRATTRIB | |
2174 dccd_quit(int exitcode, const char *p, ...) | |
2175 { | |
2176 va_list args; | |
2177 | |
2178 if (stop_mode == 1) | |
2179 db_stop(); | |
2180 else if (stop_mode == 2) | |
2181 make_clean(2); | |
2182 db_unlock(); | |
2183 | |
2184 va_start(args, p); | |
2185 if (exitcode) | |
2186 dcc_verror_msg(p, args); | |
2187 else | |
2188 dcc_vtrace_msg(p, args); | |
2189 va_end(args); | |
2190 | |
2191 /* db_close() can take a long time, so close some things early. */ | |
2192 stop_children(); | |
2193 check_dbclean(WNOHANG); | |
2194 clients_save(); | |
2195 | |
2196 #ifdef HAVE_COHERENT_MMAP | |
2197 /* If mmap() is not coherent, do not call close_srvr_socs() but | |
2198 * keep the UDP sockets open to prevent another server from starting | |
2199 * until we have flushed our buffers to prevent problems on systems | |
2200 * that lack inter-process coherent mmap() */ | |
2201 if (!(db_mode & DB_OPEN_MMAP_WRITE)) | |
2202 close_srvr_socs(); | |
2203 #endif | |
2204 db_close(1); | |
2205 | |
2206 if (exitcode) | |
2207 dcc_error_msg("stopped"); | |
2208 else | |
2209 dcc_trace_msg("stopped"); | |
2210 exit(exitcode); | |
2211 } | |
2212 | |
2213 | |
2214 | |
2215 /* watch for fatal signals */ | |
2216 static void | |
2217 sigterm(int sig) | |
2218 { | |
2219 stopint = sig; | |
2220 stop_mode = 1; | |
2221 next_flods_ck = 0; | |
2222 (void)signal(sig, SIG_DFL); /* catch it only once */ | |
2223 } | |
2224 | |
2225 | |
2226 | |
2227 /* SIGHUP hurries checking the configuration files */ | |
2228 static void | |
2229 sighup(int sig UATTRIB) | |
2230 { | |
2231 next_flods_ck = 0; | |
2232 } | |
2233 | |
2234 | |
2235 | |
2236 /* emergency shutdown but close the database cleanly */ | |
2237 void | |
2238 bad_stop(const char *pat, ...) | |
2239 { | |
2240 va_list args; | |
2241 | |
2242 if (stopint) | |
2243 return; | |
2244 | |
2245 va_start(args, pat); | |
2246 dcc_verror_msg(pat, args); | |
2247 va_end(args); | |
2248 | |
2249 stopint = -1; | |
2250 next_flods_ck = 0; | |
2251 } | |
2252 | |
2253 | |
2254 | |
2255 static void | |
2256 stop_children(void) | |
2257 { | |
2258 if (resolve_hosts_pid > 0) | |
2259 kill(resolve_hosts_pid, SIGKILL); | |
2260 | |
2261 if (dbclean_pid > 0) | |
2262 kill(dbclean_pid, SIGKILL); | |
2263 } |