Mercurial > notdcc
comparison cdcc/cdcc.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 * control dcc 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.227 $Revision$ | |
40 */ | |
41 | |
42 #include "dcc_ck.h" | |
43 #include "dcc_xhdr.h" | |
44 #include "dcc_heap_debug.h" | |
45 #include "dcc_ids.h" | |
46 #ifndef DCC_WIN32 | |
47 #include <arpa/inet.h> | |
48 #endif | |
49 | |
50 static DCC_EMSG dcc_emsg; | |
51 static DCC_FNM_LNO_BUF fnm_buf; | |
52 | |
53 static DCC_CLNT_CTXT *ctxt; | |
54 | |
55 static DCC_PATH info_map_nm = DCC_MAP_NM_DEF; | |
56 static const char *ids_nm; | |
57 static time_t clock_kludge; | |
58 static const char *homedir; | |
59 static DCC_PASSWD passwd; | |
60 static u_char passwd_set; | |
61 static DCC_IP src; | |
62 static DCC_SRVR_NM srvr = DCC_SRVR_NM_DEF; | |
63 static u_char port_set; | |
64 static DCC_CLNT_ID srvr_clnt_id = DCC_ID_ANON; | |
65 static enum WHICH_MAP {MAP_TMP, MAP_INFO} which_map = MAP_INFO; | |
66 static u_char map_changed = 1; | |
67 | |
68 static u_char info_flags; | |
69 | |
70 static u_char grey_set; | |
71 | |
72 static u_char quiet; | |
73 | |
74 | |
75 static u_char do_cmds(char *); | |
76 static void set_which_map(enum WHICH_MAP); | |
77 static u_char init_map(u_char, u_char); | |
78 | |
79 struct cmd_tbl_entry; | |
80 /* -1=display help message, 0=command failed, 1=success */ | |
81 typedef int CMD (const char *, const struct cmd_tbl_entry *); | |
82 typedef struct cmd_tbl_entry { | |
83 const char *cmd; | |
84 CMD (*fnc); | |
85 u_char args; /* 0=optional, 1=required, 2=none */ | |
86 u_char privileged; /* 1=must have server's password */ | |
87 u_char write_map; /* 1=write map, 2=write /var/dcc/map */ | |
88 const char *help_str; | |
89 } CMD_TBL_ENTRY; | |
90 | |
91 static CMD help_cmd; | |
92 static CMD exit_cmd; | |
93 static CMD grey_cmd; | |
94 static CMD file_cmd; | |
95 static CMD new_map_cmd; | |
96 static CMD delete_cmd; | |
97 static CMD add_cmd; | |
98 static CMD load_cmd; | |
99 static CMD host_cmd; | |
100 static CMD port_cmd; | |
101 static CMD passwd_cmd; | |
102 static CMD id_cmd; | |
103 static CMD homedir_cmd; | |
104 static CMD debug_cmd; | |
105 static CMD quiet_cmd; | |
106 static CMD no_fail_cmd; | |
107 static CMD ckludge_cmd; | |
108 static CMD ipv6_cmd; | |
109 static CMD src_cmd; | |
110 static CMD socks_cmd; | |
111 static CMD info_cmd; | |
112 static CMD rtt_cmd; | |
113 static CMD delck_cmd; | |
114 static CMD sleep_cmd; | |
115 static CMD clients_cmd; | |
116 static CMD anon_cmd; | |
117 static CMD flod_rewind; | |
118 static CMD ffwd_in; | |
119 static CMD ffwd_out; | |
120 static CMD flod_stats; | |
121 static CMD stats_cmd; | |
122 static const char *stats_help; | |
123 static CMD clock_ck_cmd; | |
124 static CMD trace_def; | |
125 | |
126 static const CMD_TBL_ENTRY cmds_tbl[] = { | |
127 {"help", help_cmd, 0, 0, 0, "help [cmd]"}, | |
128 {"?", help_cmd, 0, 0, 0, 0}, | |
129 {"exit", exit_cmd, 2, 0, 0, "exit"}, | |
130 {"quit", exit_cmd, 2, 0, 0, 0}, | |
131 {"grey", grey_cmd, 0, 0, 0, "grey [on|off]"}, | |
132 {"homedir", homedir_cmd, 0, 0, 0, "homedir [path]"}, | |
133 {"file", file_cmd, 0, 0, 0, "file [map]"}, | |
134 {"map", file_cmd, 0, 0, 0, 0}, | |
135 {"new map", new_map_cmd, 0, 0, 0, "new map [map]"}, | |
136 {"delete", delete_cmd, 1, 0, 2, "delete host[,port]"}, | |
137 {"add", add_cmd, 1, 0, 2, | |
138 "add host,[port|-] [RTT+/-#] [ID [passwd]]"}, | |
139 {"load", load_cmd, 1, 0, 2, "load {info-file | -}"}, | |
140 {"host", host_cmd, 0, 0, 0, "host [hostname]"}, | |
141 {"server", host_cmd, 0, 0, 0, 0}, | |
142 {"port", port_cmd, 0, 0, 0, "port #"}, | |
143 {"password", passwd_cmd, 0, 0, 0, "password secret"}, | |
144 {"passwd", passwd_cmd, 0, 0, 0, 0}, | |
145 {"id", id_cmd, 0, 0, 0, "id [ID]"}, | |
146 {"debug", debug_cmd, 0, 0, 0, "debug [on|off|TTL=x]"}, | |
147 {"quiet", quiet_cmd, 0, 0, 0, "-quiet [on|off]"}, | |
148 {"no fail", no_fail_cmd, 2, 0, 2, "-no fail"}, | |
149 {"clock kludge",ckludge_cmd, 0, 0, 0, "clock kludge +/-secs"}, | |
150 {"IPv6", ipv6_cmd, 0, 0, 0, "IPv6 [on|off]"}, | |
151 {"src", src_cmd, 0, 0, 0, "src [-|IPaddress]"}, | |
152 {"SOCKS", socks_cmd, 0, 0, 0, "SOCKS [on|off]"}, | |
153 {"info", info_cmd, 0, 0, 0, "info [-N]"}, | |
154 {"RTT", rtt_cmd, 0, 0, 0, "RTT [-N]"}, | |
155 {"delck", delck_cmd, 1, 1, 0, "delck type hex1..4"}, | |
156 {"sleep", sleep_cmd, 1, 0, 0, "sleep sec.onds"}, | |
157 {"clients", clients_cmd, 0, 0, 0, | |
158 "clients [-nsiaVAK] [max [thold [addr/prefix]]]"}, | |
159 {"anon delay", anon_cmd, 0, 0, 0, "\nanon delay [delay[,inflate]]"}, | |
160 {"flood rewind",flod_rewind, 1, 1, 0, "flood rewind ID"}, | |
161 {"flod rewind", flod_rewind, 1, 1, 0, 0}, | |
162 {"flood FFWD in",ffwd_in, 1, 1, 0, "flood FFWD in ID"}, | |
163 {"flod FFWD in",ffwd_in, 1, 1, 0, 0}, | |
164 {"flood FFWD out",ffwd_out, 1, 1, 0, "flood FFWD out ID"}, | |
165 {"flod FFWD out",ffwd_out, 1, 1, 0, 0}, | |
166 {"flood stats", flod_stats, 1, 1, 0, "flood stats [clear] {ID|all}"}, | |
167 {"flod stats", flod_stats, 1, 1, 0, 0}, | |
168 {"stats", stats_cmd, 0, 0, 0, "stats [clear|all]"}, | |
169 {"status", stats_cmd, 0, 0, 0, 0}, | |
170 {"clock check", clock_ck_cmd, 0, 0, 0, "clock check"}, | |
171 {"trace default",trace_def, 2, 1, 0, "trace default"}, | |
172 }; | |
173 | |
174 | |
175 #define PRV_MSG ";\n" \ | |
176 " use the \"id server-ID\" command\n" \ | |
177 " and either \"password secret\" command or `su` to read passwords from %s" | |
178 | |
179 | |
180 static DCC_OP_RESP aop_resp; | |
181 static struct timeval op_start, op_end; | |
182 static DCC_SOCKU op_result_su; | |
183 | |
184 static struct { | |
185 const char *op; | |
186 const char *help_str; | |
187 DCC_AOPS aop; | |
188 u_char privileged; | |
189 u_int32_t val; | |
190 } aops_tbl[] = { | |
191 #define TMAC(s,b) \ | |
192 {"trace "#s" on", "trace "#s" {on|off}", \ | |
193 DCC_AOP_TRACE_ON, 1, DCC_TRACE_##b},\ | |
194 {"trace "#s" off", 0, DCC_AOP_TRACE_OFF, 1, DCC_TRACE_##b} | |
195 TMAC(admn,ADMN_BIT), | |
196 TMAC(anon,ANON_BIT), | |
197 TMAC(clnt,CLNT_BIT), | |
198 TMAC(rlim,RLIM_BIT), | |
199 TMAC(query,QUERY_BIT), | |
200 TMAC(ridc,RIDC_BIT), | |
201 TMAC(flood,FLOD_BIT), | |
202 TMAC(flood2,FLOD2_BIT), | |
203 TMAC(ids,IDS_BIT), | |
204 TMAC(bl,BL_BIT), | |
205 TMAC(db,DB_BIT), | |
206 TMAC(wlist,WLIST_BIT), | |
207 #undef TMAC | |
208 | |
209 {"stop", "", DCC_AOP_STOP, 1, 0}, | |
210 {"system stop", "", DCC_AOP_STOP, 1, 1}, | |
211 {"clean stop", "", DCC_AOP_STOP, 1, 2}, | |
212 {"flood check", "", DCC_AOP_FLOD, 1, DCC_AOP_FLOD_CHECK}, | |
213 {"flod check", 0, DCC_AOP_FLOD, 1, DCC_AOP_FLOD_CHECK}, | |
214 {"flood shutdown", "", DCC_AOP_FLOD, 1, DCC_AOP_FLOD_SHUTDOWN}, | |
215 {"flood shutdown", 0, DCC_AOP_FLOD, 1, DCC_AOP_FLOD_SHUTDOWN}, | |
216 {"flood halt", "", DCC_AOP_FLOD, 1, DCC_AOP_FLOD_HALT}, | |
217 {"flood halt", 0, DCC_AOP_FLOD, 1, DCC_AOP_FLOD_HALT}, | |
218 {"flood resume", "", DCC_AOP_FLOD, 1, DCC_AOP_FLOD_RESUME}, | |
219 {"flood resume", 0, DCC_AOP_FLOD, 1, DCC_AOP_FLOD_RESUME}, | |
220 {"flood list", "", DCC_AOP_FLOD, 0, DCC_AOP_FLOD_LIST}, | |
221 {"flood list", 0, DCC_AOP_FLOD, 0, DCC_AOP_FLOD_LIST}, | |
222 {"DB clean", "", DCC_AOP_DB_CLEAN, 1, 0}, | |
223 {"DB new", "", DCC_AOP_DB_NEW, 1, 0}, | |
224 {"DB flush cache", "", DCC_AOP_DB_UNLOAD, 1, 0}, | |
225 {"DB cache ok", "", DCC_AOP_DB_UNLOAD, 1, 1}, | |
226 }; | |
227 | |
228 | |
229 static void NRATTRIB | |
230 usage(void) | |
231 { | |
232 dcc_logbad(EX_USAGE, | |
233 "usage: [-Vdq] [-h homedir] [-c ids] [op1 [op2] ... ]\n"); | |
234 } | |
235 | |
236 | |
237 | |
238 int NRATTRIB | |
239 main(int argc, char **argv) | |
240 { | |
241 char cmd_buf[500]; | |
242 int i; | |
243 | |
244 srvr.port = htons(DCC_SRVR_PORT); | |
245 | |
246 dcc_init_priv(); | |
247 dcc_syslog_init(0, argv[0], 0); | |
248 | |
249 while ((i = getopt(argc, argv, "Vdqh:c:")) != -1) { | |
250 switch (i) { | |
251 case 'V': | |
252 fprintf(stderr, DCC_VERSION"\n"); | |
253 break; | |
254 | |
255 case 'd': | |
256 ++dcc_clnt_debug; | |
257 break; | |
258 | |
259 case 'q': | |
260 ++quiet; | |
261 break; | |
262 | |
263 case 'h': | |
264 homedir = optarg; | |
265 break; | |
266 | |
267 case 'c': | |
268 ids_nm = optarg; | |
269 break; | |
270 | |
271 default: | |
272 usage(); | |
273 } | |
274 } | |
275 argc -= optind; | |
276 argv += optind; | |
277 | |
278 dcc_clnt_unthread_init(); | |
279 dcc_cdhome(0, homedir, 1); | |
280 set_ids_path(0, ids_nm); | |
281 dcc_wf_init(&cmn_wf, 0); | |
282 | |
283 dcc_all_srvrs = 1; | |
284 | |
285 if (!init_map(!quiet, 0)) | |
286 set_which_map(MAP_TMP); | |
287 | |
288 /* with a list of commands, act as a batch utility */ | |
289 if (argc != 0) { | |
290 for (;;) { | |
291 /* a final arg of "-" says switch to interactive mode */ | |
292 if (argc == 1 && !strcmp(*argv, "-")) | |
293 break; | |
294 | |
295 if (!do_cmds(*argv)) { | |
296 fputs(" ?\n", stderr); | |
297 exit(EX_UNAVAILABLE); | |
298 } | |
299 assert_ctxts_unlocked(); | |
300 assert_info_unlocked(); | |
301 | |
302 ++argv; | |
303 if (!--argc) { | |
304 exit(EX_OK); | |
305 } | |
306 } | |
307 } | |
308 | |
309 /* Without an arg list of commands, look for commands from STDIN. | |
310 * Commands end with a semicolon or newline. */ | |
311 for (;;) { | |
312 assert_ctxts_unlocked(); | |
313 assert_info_unlocked(); | |
314 printf("cdcc %s> ", | |
315 which_map == MAP_INFO ? info_map_nm : "-"); | |
316 fflush(stderr); | |
317 fflush(stdout); | |
318 if (!fgets(cmd_buf, sizeof(cmd_buf), stdin)) { | |
319 fputc('\n', stdout); | |
320 exit(EX_OK); | |
321 } | |
322 if (!do_cmds(cmd_buf)) | |
323 fputs(" ?\n", stderr); | |
324 } | |
325 } | |
326 | |
327 | |
328 | |
329 /* see if don't need a server-ID password, have one or if we can get it */ | |
330 static u_char /* 0=failed, 1=ok */ | |
331 get_passwd(u_char privileged) /* 0=no privileges need, 1=need power */ | |
332 { | |
333 const ID_TBL *srvr_clnt_tbl; | |
334 | |
335 srvr.clnt_id = srvr_clnt_id; | |
336 if (passwd_set) { | |
337 /* set to use the manual password */ | |
338 memcpy(srvr.passwd, passwd, sizeof(srvr.passwd)); | |
339 /* succeed if this is not a priviledge command | |
340 * or if we won't be trying to get the server to do | |
341 * something priviledge for the anonymous client-ID */ | |
342 return (!privileged || srvr.clnt_id != DCC_ID_ANON); | |
343 } | |
344 memset(srvr.passwd, 0, sizeof(srvr.passwd)); | |
345 | |
346 /* fail if we would be trying to get the server to do something | |
347 * powerful for the anonymous client-ID */ | |
348 if (srvr.clnt_id == DCC_ID_ANON) | |
349 return !privileged; | |
350 | |
351 /* Fetch the common server passwords only if we can read them without | |
352 * set-UID. This keeps random local users from attacking local | |
353 * or remote servers with privileged commands, but does not slow | |
354 * down privilege users who could use an editor to read and use | |
355 * the cleartext passwords manually. */ | |
356 dcc_rel_priv(); | |
357 if (0 > access(ids_path, R_OK) && errno == EACCES) { | |
358 srvr.clnt_id = DCC_ID_ANON; | |
359 if (!privileged) | |
360 return 1; | |
361 dcc_error_msg("access(%s): %s", | |
362 fnm2abs_err(0, ids_path), ERROR_STR()); | |
363 return 0; | |
364 } | |
365 | |
366 if (0 >= load_ids(dcc_emsg, srvr_clnt_id, &srvr_clnt_tbl, 1)) { | |
367 if (srvr_clnt_id != DCC_ID_ANON && privileged) | |
368 dcc_error_msg("%s", dcc_emsg); | |
369 srvr.clnt_id = DCC_ID_ANON; | |
370 return !privileged; | |
371 } | |
372 if (srvr_clnt_tbl) | |
373 memcpy(srvr.passwd, srvr_clnt_tbl->cur_passwd, | |
374 sizeof(srvr.passwd)); | |
375 return 1; | |
376 } | |
377 | |
378 | |
379 | |
380 static void | |
381 set_which_map(enum WHICH_MAP new) | |
382 { | |
383 /* release things even if nothing seems to be changing | |
384 * to ensure that we bind a new socket */ | |
385 if (ctxt) { | |
386 dcc_ctxts_lock(); | |
387 if (dcc_clnt_info) | |
388 dcc_unmap_close_info(0); | |
389 if (ctxt) { | |
390 dcc_rel_ctxt(ctxt); | |
391 ctxt = 0; | |
392 } | |
393 dcc_ctxts_unlock(); | |
394 } | |
395 | |
396 map_changed = 1; | |
397 which_map = new; | |
398 if (new == MAP_INFO) { | |
399 passwd_set = 0; | |
400 src.family = AF_UNSPEC; | |
401 } | |
402 } | |
403 | |
404 | |
405 | |
406 static u_char | |
407 cdcc_unlock(u_char complain) | |
408 { | |
409 u_char result; | |
410 | |
411 result = dcc_info_unlock(dcc_emsg); | |
412 dcc_ctxts_unlock(); | |
413 if (!result && complain) | |
414 dcc_error_msg("%s", dcc_emsg); | |
415 return result; | |
416 } | |
417 | |
418 | |
419 | |
420 /* start talking to the local map file */ | |
421 static u_char /* 0=failed 1=mapped and locked */ | |
422 init_map(u_char complain, | |
423 u_char lock) /* 1=keep both locks on success */ | |
424 { | |
425 u_char result; | |
426 | |
427 info_flags = 0; | |
428 | |
429 dcc_emsg[0] = '\0'; | |
430 dcc_ctxts_lock(); | |
431 if (which_map == MAP_TMP) { | |
432 result = (dcc_map_tmp_info(dcc_emsg, &srvr, &src, info_flags) | |
433 && dcc_info_lock(dcc_emsg)); | |
434 } else { | |
435 result = dcc_map_lock_info(dcc_emsg, info_map_nm, -1); | |
436 } | |
437 if (result) { | |
438 info_flags = dcc_clnt_info->flags; | |
439 if (!lock) | |
440 result = cdcc_unlock(complain); | |
441 } else { | |
442 dcc_ctxts_unlock(); | |
443 if (complain) | |
444 dcc_error_msg("%s", dcc_emsg); | |
445 } | |
446 return result; | |
447 } | |
448 | |
449 | |
450 | |
451 /* get ready start talking to a DCC server */ | |
452 static u_char /* 0=failed, 1=ok */ | |
453 rdy_ctxt(DCC_CLNT_FGS fgs) | |
454 { | |
455 u_char rdy_done, need_unlock; | |
456 | |
457 info_flags = 0; | |
458 | |
459 if (grey_on) | |
460 fgs |= DCC_CLNT_FG_GREY; | |
461 else | |
462 fgs &= ~DCC_CLNT_FG_GREY; | |
463 fgs |= DCC_CLNT_FG_NO_FAIL; | |
464 | |
465 if (!dcc_clnt_info && ctxt) { | |
466 dcc_rel_ctxt(ctxt); | |
467 ctxt = 0; | |
468 } | |
469 | |
470 if (ctxt) { | |
471 rdy_done = 0; | |
472 } else { | |
473 if (which_map == MAP_TMP) { | |
474 /* create a new temporary map */ | |
475 ctxt = dcc_tmp_clnt_init(dcc_emsg, ctxt, &srvr, &src, | |
476 fgs, 0); | |
477 } else { | |
478 /* open official map file */ | |
479 ctxt = dcc_clnt_init(dcc_emsg, ctxt, info_map_nm, fgs); | |
480 } | |
481 if (!ctxt) { | |
482 dcc_error_msg("%s", dcc_emsg); | |
483 return 0; | |
484 } | |
485 rdy_done = 1; | |
486 } | |
487 | |
488 if (!grey_set && dcc_clnt_info | |
489 && !grey_on | |
490 && dcc_clnt_info->dcc.nms[0].hostname[0] == '\0' | |
491 && dcc_clnt_info->grey.nms[0].hostname[0] != '\0') { | |
492 grey_on = 1; | |
493 fgs |= DCC_CLNT_FG_GREY; | |
494 rdy_done = 0; | |
495 } | |
496 | |
497 dcc_ctxts_lock(); | |
498 if (rdy_done) { | |
499 need_unlock = 0; | |
500 } else { | |
501 dcc_emsg[0] = '\0'; | |
502 need_unlock = dcc_clnt_rdy(dcc_emsg, ctxt, fgs); | |
503 if (!dcc_clnt_info) { | |
504 dcc_rel_ctxt(ctxt); | |
505 ctxt = 0; | |
506 dcc_ctxts_unlock(); | |
507 dcc_error_msg("%s", dcc_emsg); | |
508 return 0; | |
509 } | |
510 } | |
511 info_flags = dcc_clnt_info->flags; | |
512 if (!(fgs & DCC_CLNT_FG_NO_PICK_SRVR)) | |
513 map_changed = 0; | |
514 | |
515 if (need_unlock && !dcc_info_unlock(0)) { | |
516 dcc_rel_ctxt(ctxt); | |
517 ctxt = 0; | |
518 dcc_ctxts_unlock(); | |
519 dcc_error_msg("%s", dcc_emsg); | |
520 return 0; | |
521 } | |
522 | |
523 /* check the other (greylist or not) server */ | |
524 if (which_map != MAP_TMP) { | |
525 dcc_emsg[0] = '\0'; | |
526 if (!dcc_clnt_rdy(dcc_emsg, ctxt, fgs ^ DCC_CLNT_FG_GREY)) { | |
527 if (dcc_clnt_debug > 1) | |
528 dcc_error_msg("%s", dcc_emsg); | |
529 } else { | |
530 dcc_info_unlock(0); | |
531 } | |
532 } | |
533 | |
534 dcc_ctxts_unlock(); | |
535 return 1; | |
536 } | |
537 | |
538 | |
539 | |
540 static void | |
541 fix_info(DCC_SRVR_CLASS *class) | |
542 { | |
543 map_changed = 1; | |
544 | |
545 if (class) { | |
546 dcc_force_measure_rtt(class); | |
547 } else { | |
548 dcc_force_measure_rtt(&dcc_clnt_info->dcc); | |
549 dcc_force_measure_rtt(&dcc_clnt_info->grey); | |
550 } | |
551 cdcc_unlock(1); | |
552 | |
553 /* repair addresses in the real map file now */ | |
554 if (!quiet | |
555 && which_map == MAP_INFO) | |
556 rdy_ctxt(DCC_CLNT_FG_BAD_SRVR_OK); | |
557 } | |
558 | |
559 | |
560 | |
561 /* compare ignoring case */ | |
562 static const char * /* 0 or mismatch in str */ | |
563 cmd_cmp(const char *str, const char *op) | |
564 { | |
565 char op_c, str_c; | |
566 int len; | |
567 | |
568 len = 0; | |
569 for (;;) { | |
570 op_c = *op; | |
571 /* avoid tolower() to avoid build hassles on odd systems */ | |
572 if (op_c >= 'A' && op_c <= 'Z') | |
573 op_c += 'a'-'A'; | |
574 str_c = *str; | |
575 if (str_c == '\t') | |
576 str_c = ' '; | |
577 else if (str_c >= 'A' && str_c <= 'Z') | |
578 str_c += 'a'-'A'; | |
579 if (op_c != str_c) { | |
580 /* compress bursts of blanks */ | |
581 if (str_c == ' ' && len != 0 && *(op-1) == ' ') { | |
582 ++str; | |
583 continue; | |
584 } | |
585 return str; | |
586 } | |
587 if (op_c == '\0') | |
588 return 0; | |
589 ++op; | |
590 ++str; | |
591 ++len; | |
592 } | |
593 } | |
594 | |
595 | |
596 | |
597 /* Display our name for the server and its address, | |
598 * while suppressing some duplicates */ | |
599 static void | |
600 print_aop(SRVR_INX srvr_inx) /* -1 or server index */ | |
601 { | |
602 const DCC_SRVR_CLASS *class; | |
603 char date_buf[40]; | |
604 char sustr[DCC_SU2STR_SIZE]; | |
605 const char *srvr_nm; | |
606 NAM_INX nam_inx; | |
607 | |
608 dcc_su2str2(sustr, sizeof(sustr), &op_result_su); | |
609 class = DCC_GREY2CLASS(grey_on); | |
610 /* Display the preferred server if srvr_inx is NO_SRVR */ | |
611 if (!GOOD_SRVR(class, srvr_inx)) | |
612 srvr_inx = class->srvr_inx; | |
613 if (GOOD_SRVR(class, srvr_inx) | |
614 && (GOOD_NAM(nam_inx = class->addrs[srvr_inx].nam_inx))) { | |
615 srvr_nm = class->nms[nam_inx].hostname; | |
616 if (strcmp(srvr_nm, sustr)) { | |
617 fputs(srvr_nm, stdout); | |
618 putchar(' '); | |
619 } | |
620 printf("%s\n server-ID %d", | |
621 dcc_su2str_err(&op_result_su), | |
622 class->addrs[srvr_inx].srvr_id); | |
623 } else { | |
624 printf("%s\n ", | |
625 dcc_su2str_err(&op_result_su)); | |
626 } | |
627 if (srvr.clnt_id != DCC_ID_ANON) | |
628 printf(" client-ID %d", srvr.clnt_id); | |
629 if (which_map == MAP_INFO) | |
630 printf(" %s", info_map_nm); | |
631 dcc_time2str(date_buf, sizeof(date_buf), " %X", | |
632 op_start.tv_sec); | |
633 fputs(date_buf, stdout); | |
634 putchar('\n'); | |
635 } | |
636 | |
637 | |
638 | |
639 static u_char /* 0=some kind of problem, 1=done */ | |
640 start_aop(DCC_AOPS aop, u_int32_t val1, SRVR_INX srvr_inx) | |
641 { | |
642 DCC_OPS result; | |
643 | |
644 if (!rdy_ctxt(0)) | |
645 return 0; | |
646 | |
647 gettimeofday(&op_start, 0); | |
648 result = dcc_aop(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0, | |
649 srvr_inx, clock_kludge, | |
650 aop, val1, 0, 0, 0, 0, 0, &aop_resp, &op_result_su); | |
651 gettimeofday(&op_end, 0); | |
652 | |
653 if (result == DCC_OP_INVALID | |
654 || result == DCC_OP_ERROR) { | |
655 dcc_error_msg("%s", dcc_emsg); | |
656 return 0; | |
657 } | |
658 | |
659 return 1; | |
660 } | |
661 | |
662 | |
663 | |
664 static void | |
665 fin_aop(SRVR_INX srvr_inx, /* index of server */ | |
666 u_char psrvr) /* 1=print server name */ | |
667 { | |
668 if (quiet && !dcc_clnt_debug) | |
669 return; | |
670 | |
671 if (psrvr) | |
672 print_aop(srvr_inx); | |
673 | |
674 /* say what the server had to say */ | |
675 if (aop_resp.resp.val.string[0] >= ' ' | |
676 && aop_resp.resp.val.string[0] < 0x7f) { | |
677 fputs(aop_resp.resp.val.string, stdout); | |
678 putchar('\n'); | |
679 } | |
680 | |
681 if (dcc_clnt_debug) { | |
682 printf("%.2f ms\n", | |
683 ((op_end.tv_sec-op_start.tv_sec)*1000.0 | |
684 + (op_end.tv_usec-op_start.tv_usec)/1000.0)); | |
685 } | |
686 putchar('\n'); | |
687 } | |
688 | |
689 | |
690 | |
691 static u_char /* 0=some kind of problem, 1=done */ | |
692 do_aop(DCC_AOPS aop, u_int32_t val1, SRVR_INX srvr_inx, u_char psrvr) | |
693 { | |
694 if (!start_aop(aop, val1, srvr_inx)) | |
695 return 0; | |
696 fin_aop(srvr_inx, psrvr); | |
697 return 1; | |
698 } | |
699 | |
700 | |
701 | |
702 static u_char /* 0=not enough power */ | |
703 ck_cmd_priv(const CMD_TBL_ENTRY *ce, | |
704 u_char privileged, /* 1=need good server-ID & password */ | |
705 u_char write_map) /* 1=write map, 2=write /var/dcc/map */ | |
706 { | |
707 /* always call get_passwd() so we have always fetched a password | |
708 * fail if this command needs a good server-ID and password */ | |
709 if (!get_passwd(privileged)) { | |
710 dcc_error_msg("\"%s\" is a privileged server command"PRV_MSG, | |
711 ce->cmd, ids_path); | |
712 return 0; | |
713 } | |
714 | |
715 if (!write_map) | |
716 return 1; | |
717 | |
718 /* we can always write to our own throw-away map file */ | |
719 if (write_map == 1 && which_map == MAP_TMP) | |
720 return 1; | |
721 | |
722 if (0 > access(info_map_nm, R_OK) | |
723 && errno != ENOENT && errno != ENOTDIR) { | |
724 dcc_error_msg("\"%s\" is a privileged command changing %s", | |
725 ce->cmd, fnm2abs_err(0, info_map_nm)); | |
726 return 0; | |
727 } | |
728 return 1; | |
729 } | |
730 | |
731 | |
732 | |
733 static u_char /* 1=ok 0=bad command */ | |
734 cmd(const char *op) | |
735 { | |
736 const char *arg, *help_str; | |
737 int op_num, j; | |
738 const CMD_TBL_ENTRY *ce; | |
739 | |
740 /* look for the string as a command and execute it if we find */ | |
741 ce = &cmds_tbl[0]; | |
742 for (op_num = 0; op_num < DIM(cmds_tbl); ++op_num) { | |
743 if (cmds_tbl[op_num].help_str) | |
744 ce = &cmds_tbl[op_num]; | |
745 arg = cmd_cmp(op, cmds_tbl[op_num].cmd); | |
746 /* if the command table entry and the command completely | |
747 * matched, then infer a null argument */ | |
748 if (!arg) { | |
749 if (!ck_cmd_priv(ce, ce->privileged, ce->write_map)) | |
750 return 0; | |
751 if (ce->args != 1) { | |
752 j = ce->fnc("", ce); | |
753 if (j >= 0) | |
754 return j; | |
755 } | |
756 help_cmd(op, 0); | |
757 return 0; | |
758 } | |
759 /* If the command table entry is an initial sustring of | |
760 * the user's command, then the rest of the command must | |
761 * start with white space or '='. (Allow '=' to let | |
762 * homedir/fix-map not need use `eval` to quote blanks | |
763 * `eval` in bash loses exit status. | |
764 * Trim and use the rest of the string as the argument */ | |
765 j = strspn(arg, DCC_WHITESPACE"="); | |
766 if (j) { | |
767 if (ce->args == 2) { | |
768 help_cmd(op, 0); /* arg not allowed */ | |
769 return 0; | |
770 } | |
771 if (!ck_cmd_priv(ce, ce->privileged, ce->write_map)) | |
772 return 0; | |
773 j = ce->fnc(arg+j, ce); | |
774 if (j >= 0) | |
775 return j; | |
776 help_cmd(op, 0); | |
777 return 0; | |
778 } | |
779 } | |
780 | |
781 | |
782 /* otherwise try to interpret it as a DCC administrative packet */ | |
783 op_num = 0; | |
784 help_str = ""; | |
785 for (;;) { | |
786 if (op_num >= DIM(aops_tbl)) { | |
787 dcc_error_msg("unrecognized command \"%s\"", op); | |
788 return 0; | |
789 } | |
790 /* do a command */ | |
791 if (aops_tbl[op_num].help_str) { | |
792 help_str = aops_tbl[op_num].help_str; | |
793 if (*help_str == '\0') | |
794 help_str = aops_tbl[op_num].op; | |
795 } | |
796 if (!cmd_cmp(op, aops_tbl[op_num].op)) | |
797 break; | |
798 ++op_num; | |
799 } | |
800 | |
801 /* send an administrative request to the server */ | |
802 if (!get_passwd(aops_tbl[op_num].privileged)) { | |
803 dcc_error_msg("\"%s\" is a privileged operation"PRV_MSG, | |
804 help_str, ids_path); | |
805 return 0; | |
806 } | |
807 | |
808 /* try to send it */ | |
809 return do_aop(aops_tbl[op_num].aop, aops_tbl[op_num].val, | |
810 NO_SRVR, 1); | |
811 } | |
812 | |
813 | |
814 | |
815 static u_char /* 0=bad command, 1=ok */ | |
816 do_cmds(char *cmd_buf) | |
817 { | |
818 char *next_cmd, *cur_cmd, *cmd_end; | |
819 char c; | |
820 | |
821 next_cmd = cmd_buf; | |
822 for (;;) { | |
823 cur_cmd = next_cmd + strspn(next_cmd, DCC_WHITESPACE";"); | |
824 | |
825 if (*cur_cmd == '#' || *cur_cmd == '\0') | |
826 return 1; | |
827 | |
828 next_cmd = cur_cmd + strcspn(cur_cmd, ";\n\r"); | |
829 cmd_end = next_cmd; | |
830 next_cmd += strspn(next_cmd, ";\n\r"); | |
831 | |
832 /* null terminate and trim trailing white space from | |
833 * command or arg */ | |
834 do { | |
835 *cmd_end-- = '\0'; | |
836 c = *cmd_end; | |
837 } while (cmd_end >= cur_cmd | |
838 && strchr(DCC_WHITESPACE";", c)); | |
839 | |
840 if (*cur_cmd == '\0') /* ignore blank commands */ | |
841 continue; | |
842 | |
843 if (!cmd(cur_cmd)) | |
844 return 0; | |
845 } | |
846 } | |
847 | |
848 | |
849 | |
850 static int | |
851 help_cmd_print(int pos, const char *str) | |
852 { | |
853 #define HELP_COL 24 | |
854 int col, nl; | |
855 | |
856 if (str[0] == '\n') { | |
857 nl = 100; | |
858 ++str; | |
859 } else { | |
860 nl = 0; | |
861 } | |
862 col = strlen(str)+1; | |
863 col += HELP_COL - (col % HELP_COL); | |
864 pos += col; | |
865 if (pos > 78) { | |
866 putchar('\n'); | |
867 pos = col; | |
868 } | |
869 printf("%-*s", col, str); | |
870 pos += nl; | |
871 | |
872 return pos; | |
873 #undef HELP_COL | |
874 } | |
875 | |
876 | |
877 | |
878 static int | |
879 help_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
880 { | |
881 int i, pos; | |
882 const char *help_str; | |
883 const char *p; | |
884 | |
885 /* say something about one command */ | |
886 if (arg) { | |
887 help_str = ""; | |
888 for (i = 0; i < DIM(cmds_tbl); ++i) { | |
889 if (cmds_tbl[i].help_str) | |
890 help_str = cmds_tbl[i].help_str; | |
891 p = cmd_cmp(arg, cmds_tbl[i].cmd); | |
892 if (!p || *p == ' ' || *p == '\t') { | |
893 while (*help_str == '\n' || *help_str == '-') | |
894 ++help_str; | |
895 printf("usage: %s\n", help_str); | |
896 if (cmds_tbl[i].fnc == stats_cmd) | |
897 printf(stats_help); | |
898 return 1; | |
899 } | |
900 } | |
901 for (i = 0; i < DIM(aops_tbl); ++i) { | |
902 if (aops_tbl[i].help_str) { | |
903 help_str = aops_tbl[i].help_str; | |
904 if (*help_str == '\0') | |
905 help_str = aops_tbl[i].op; | |
906 } | |
907 p = cmd_cmp(arg, aops_tbl[i].op); | |
908 if (!p || *p == ' ' || *p == '\t') { | |
909 while (*help_str == '\n' || *help_str == '-') | |
910 ++help_str; | |
911 printf("usage: %s\n", help_str); | |
912 return 1; | |
913 } | |
914 } | |
915 } | |
916 | |
917 /* talk about all of the commands */ | |
918 printf(" version "DCC_VERSION"\n"); | |
919 pos = 0; | |
920 for (i = 0; i < DIM(cmds_tbl); ++i) { | |
921 if (cmds_tbl[i].help_str | |
922 && cmds_tbl[i].help_str[0] != '-') | |
923 pos = help_cmd_print(pos, cmds_tbl[i].help_str); | |
924 } | |
925 for (i = 0; i < DIM(aops_tbl); ++i) { | |
926 help_str = aops_tbl[i].help_str; | |
927 if (!help_str) | |
928 continue; | |
929 if (*help_str == '\0') | |
930 help_str = aops_tbl[i].op; | |
931 pos = help_cmd_print(pos, help_str); | |
932 } | |
933 putchar('\n'); | |
934 | |
935 return 1; | |
936 } | |
937 | |
938 | |
939 | |
940 static int NRATTRIB | |
941 exit_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
942 { | |
943 exit(EX_OK); | |
944 #ifndef HAVE_GCC_ATTRIBUTES | |
945 return -1; | |
946 #endif | |
947 } | |
948 | |
949 | |
950 static int | |
951 grey_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
952 { | |
953 if (arg[0] == '\0') { | |
954 printf(" Greylist mode %s%s\n", | |
955 grey_on ? "on" : "off", | |
956 grey_set ? "" : " by default"); | |
957 return 1; | |
958 } | |
959 if (!strcmp(arg, "off")) { | |
960 grey_on = 0; | |
961 grey_set = 1; | |
962 set_which_map(which_map); | |
963 } else if (!strcmp(arg, "on")) { | |
964 grey_on = 1; | |
965 grey_set = 1; | |
966 set_which_map(which_map); | |
967 } else { | |
968 return -1; | |
969 } | |
970 if (!port_set) | |
971 srvr.port = DCC_GREY2PORT(grey_on); | |
972 return 1; | |
973 } | |
974 | |
975 | |
976 | |
977 static int | |
978 homedir_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
979 { | |
980 if (arg[0] != '\0') { | |
981 if (!dcc_cdhome(0, arg, 1)) | |
982 return 0; | |
983 if (ids_nm && !set_ids_path(dcc_emsg, ids_nm)) | |
984 dcc_error_msg("%s", dcc_emsg); | |
985 set_which_map(MAP_INFO); | |
986 } | |
987 printf(" homedir=%s\n", dcc_homedir); | |
988 return 1; | |
989 } | |
990 | |
991 | |
992 | |
993 /* set name of map file */ | |
994 static int | |
995 file_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
996 { | |
997 if (arg[0] == '\0') { | |
998 if (which_map == MAP_INFO) | |
999 printf(" using map file %s\n", | |
1000 fnm2abs_err(0, info_map_nm)); | |
1001 else | |
1002 printf(" map file %s but using temporary file\n", | |
1003 fnm2abs_err(0, info_map_nm)); | |
1004 return 1; | |
1005 } | |
1006 | |
1007 BUFCPY(info_map_nm, arg); | |
1008 set_which_map(MAP_INFO); | |
1009 return 1; | |
1010 } | |
1011 | |
1012 | |
1013 | |
1014 /* create a new client map or parameter file */ | |
1015 static int | |
1016 new_map_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1017 { | |
1018 if (arg[0] == '\0') | |
1019 arg = DCC_MAP_NM_DEF; | |
1020 | |
1021 dcc_rel_priv(); | |
1022 if (!dcc_create_map(dcc_emsg, arg, 0, 0, 0, 0, 0, 0, info_flags)) { | |
1023 dcc_error_msg("%s", dcc_emsg); | |
1024 return 0; | |
1025 } | |
1026 BUFCPY(info_map_nm, arg); | |
1027 set_which_map(MAP_INFO); | |
1028 if (!quiet) | |
1029 printf(" created %s\n", fnm2abs_err(0, info_map_nm)); | |
1030 return 1; | |
1031 } | |
1032 | |
1033 | |
1034 | |
1035 static int | |
1036 info_work(const char *arg, int fgs) | |
1037 { | |
1038 DCC_CLNT_INFO info; | |
1039 u_char dcc, srcbad, names; | |
1040 | |
1041 if (*arg == '\0') { | |
1042 names = 0; | |
1043 } else if (!strcmp(arg, "-N")) { | |
1044 names = 1; | |
1045 } else { | |
1046 return -1; | |
1047 } | |
1048 | |
1049 if (!rdy_ctxt(fgs)) | |
1050 return 0; | |
1051 | |
1052 /* Snapshot the data and then release it while we print it. */ | |
1053 dcc_ctxts_lock(); | |
1054 if (!dcc_info_lock(0)) { | |
1055 dcc_ctxts_lock(); | |
1056 return 0; | |
1057 } | |
1058 memcpy(&info, dcc_clnt_info, sizeof(info)); | |
1059 srcbad = ctxt && (ctxt->flags & DCC_CTXT_SRCBAD); | |
1060 cdcc_unlock(1); | |
1061 | |
1062 dcc_rel_priv(); | |
1063 if (which_map == MAP_INFO) { | |
1064 if (info.dcc.nms[0].hostname[0] != '\0' | |
1065 || !grey_on) { | |
1066 dcc_print_info(info_map_nm, &info, | |
1067 quiet, 0, srcbad, names, | |
1068 0 <= access(info_map_nm, R_OK)); | |
1069 dcc = 1; | |
1070 } else { | |
1071 dcc = 0; | |
1072 } | |
1073 if (info.grey.nms[0].hostname[0] != '\0' | |
1074 || grey_on) { | |
1075 if (dcc && !quiet) | |
1076 fputs("\n################\n", stdout); | |
1077 dcc_print_info(info_map_nm, &info, | |
1078 quiet, 1, srcbad, names, | |
1079 0 <= access(info_map_nm, R_OK)); | |
1080 } | |
1081 } else { | |
1082 dcc_print_info(0, &info, quiet, grey_on, srcbad, names, 1); | |
1083 } | |
1084 if (!quiet) | |
1085 putchar('\n'); | |
1086 return 1; | |
1087 } | |
1088 | |
1089 | |
1090 | |
1091 /* server hostname */ | |
1092 static int | |
1093 host_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1094 { | |
1095 DCC_SRVR_NM nm; | |
1096 int error; | |
1097 | |
1098 if (arg[0] == '\0') { | |
1099 if (which_map == MAP_INFO) | |
1100 return info_work(arg, DCC_CLNT_FG_BAD_SRVR_OK) ; | |
1101 printf(" %s server hostname \"%s\"\n", | |
1102 grey_on ? "greylist" : "DCC", srvr.hostname); | |
1103 return 1; | |
1104 } | |
1105 if (!strcmp(arg, "-")) { | |
1106 set_which_map(MAP_INFO); | |
1107 if (!init_map(1, 0)) { | |
1108 set_which_map(MAP_TMP); | |
1109 return 0; | |
1110 } | |
1111 return 1; | |
1112 } | |
1113 | |
1114 arg = dcc_parse_nm_port(0, arg, 0, | |
1115 nm.hostname, sizeof(nm.hostname), | |
1116 &nm.port, 0, 0, | |
1117 0, 0); | |
1118 if (!arg) | |
1119 return 0; | |
1120 arg += strspn(arg, DCC_WHITESPACE); | |
1121 if (*arg != '\0') | |
1122 return 0; | |
1123 | |
1124 set_which_map(MAP_TMP); | |
1125 memcpy(srvr.hostname, nm.hostname, sizeof(srvr.hostname)); | |
1126 if (nm.port != 0) { | |
1127 srvr.port = nm.port; | |
1128 port_set = 1; | |
1129 } | |
1130 | |
1131 /* go with the flow for IPv6 */ | |
1132 dcc_host_lock(); | |
1133 if (!dcc_get_host(nm.hostname, | |
1134 (info_flags & DCC_INFO_FG_IPV6) ? 2 : 3, | |
1135 &error)) { | |
1136 dcc_host_unlock(); | |
1137 dcc_error_msg("%s: %s", nm.hostname, DCC_HSTRERROR(error)); | |
1138 } else { | |
1139 if (dcc_hostaddrs[0].sa.sa_family == AF_INET) | |
1140 info_flags &= ~DCC_INFO_FG_IPV6; | |
1141 else | |
1142 info_flags |= DCC_INFO_FG_IPV6; | |
1143 dcc_host_unlock(); | |
1144 } | |
1145 | |
1146 return 1; | |
1147 } | |
1148 | |
1149 | |
1150 | |
1151 /* server port # */ | |
1152 static int | |
1153 port_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1154 { | |
1155 u_int port; | |
1156 | |
1157 if (arg[0] == '\0') { | |
1158 if (which_map == MAP_INFO) | |
1159 return info_work(arg, DCC_CLNT_FG_BAD_SRVR_OK) ; | |
1160 printf(" port=%d\n", ntohs(srvr.port)); | |
1161 return 1; | |
1162 } | |
1163 | |
1164 port = dcc_get_port(0, arg, DCC_GREY2PORT(grey_on), 0, 0); | |
1165 if (port == DCC_GET_PORT_INVALID) | |
1166 return 0; | |
1167 | |
1168 srvr.port = port; | |
1169 port_set = 1; | |
1170 set_which_map(MAP_TMP); | |
1171 return 1; | |
1172 } | |
1173 | |
1174 | |
1175 | |
1176 static int | |
1177 ipv6_cmd(const char *arg, const CMD_TBL_ENTRY *ce) | |
1178 { | |
1179 u_char new_use_ipv6; | |
1180 | |
1181 if (arg[0] == '\0') { | |
1182 if (!init_map(1, 0)) | |
1183 return 0; | |
1184 printf(" IPv6 %s\n", | |
1185 (info_flags & DCC_INFO_FG_IPV6) ? "on" : "off"); | |
1186 return 1; | |
1187 } | |
1188 | |
1189 if (!strcasecmp(arg, "off")) { | |
1190 new_use_ipv6 = 0; | |
1191 } else if (!strcasecmp(arg, "on")) { | |
1192 new_use_ipv6 = DCC_INFO_FG_IPV6; | |
1193 } else { | |
1194 return -1; | |
1195 } | |
1196 | |
1197 if (!ck_cmd_priv(ce, 0, 1)) | |
1198 return 0; | |
1199 | |
1200 if (!init_map(1, 1)) | |
1201 return 0; | |
1202 if ((dcc_clnt_info->flags & DCC_INFO_FG_IPV6) != new_use_ipv6) { | |
1203 dcc_clnt_info->flags ^= DCC_INFO_FG_IPV6; | |
1204 info_flags = dcc_clnt_info->flags; | |
1205 fix_info(0); | |
1206 } else if (!cdcc_unlock(1)) { | |
1207 return 0; | |
1208 } | |
1209 | |
1210 if (rdy_ctxt(0) | |
1211 && (dcc_clnt_info->flags & DCC_INFO_FG_IPV6) != new_use_ipv6) { | |
1212 #ifdef NO_IPV6 | |
1213 dcc_error_msg("IPv6 switch not changed;" | |
1214 " No IPv6 support in this system?"); | |
1215 #else | |
1216 dcc_error_msg("IPv6 switch not changed."); | |
1217 #endif | |
1218 return 0; | |
1219 } | |
1220 | |
1221 return 1; | |
1222 } | |
1223 | |
1224 | |
1225 | |
1226 static u_char | |
1227 ck_new_src(DCC_IP *new_ip, const char *arg, u_char use_ipv6) | |
1228 { | |
1229 SOCKET soc; | |
1230 DCC_SOCKU su; | |
1231 int error; | |
1232 | |
1233 memset(new_ip, 0, sizeof(*new_ip)); | |
1234 if (!strcmp(arg, "-")) | |
1235 return 1; | |
1236 | |
1237 dcc_host_lock(); | |
1238 if (!dcc_get_host(arg, use_ipv6 ? 1 : 0, &error)) { | |
1239 dcc_host_unlock(); | |
1240 dcc_error_msg("%s: %s", arg, DCC_HSTRERROR(error)); | |
1241 return 0; | |
1242 } | |
1243 if (use_ipv6) | |
1244 dcc_ipv4sutoipv6(&su, &dcc_hostaddrs[0]); | |
1245 else | |
1246 dcc_ipv6sutoipv4(&su, &dcc_hostaddrs[0]); | |
1247 dcc_su2ip(new_ip, &su); | |
1248 dcc_host_unlock(); | |
1249 | |
1250 soc = INVALID_SOCKET; | |
1251 if (0 >= dcc_udp_bind(dcc_emsg, &soc, &su, 0)) { | |
1252 dcc_error_msg("%s", dcc_emsg); | |
1253 return 0; | |
1254 } | |
1255 closesocket(soc); | |
1256 | |
1257 return 1; | |
1258 } | |
1259 | |
1260 | |
1261 | |
1262 static int | |
1263 src_cmd(const char *arg, const CMD_TBL_ENTRY *ce) | |
1264 { | |
1265 DCC_IP new_ip; | |
1266 char sustr[DCC_SU2STR_SIZE]; | |
1267 | |
1268 if (arg[0] == '\0') { | |
1269 if (!init_map(1, 0)) | |
1270 return 0; | |
1271 | |
1272 if (dcc_clnt_info->src.family == AF_UNSPEC) { | |
1273 printf(" no source address specified\n"); | |
1274 } else { | |
1275 /* display what the system actually uses */ | |
1276 printf(" source address=%s%s\n", | |
1277 dcc_su2str2(sustr, sizeof(sustr), | |
1278 &ctxt->bind_su), | |
1279 (ctxt->flags & DCC_CTXT_SRCBAD) | |
1280 ? " "DCC_INFO_USE_SRCBAD : ""); | |
1281 } | |
1282 return 1; | |
1283 } | |
1284 | |
1285 if (!ck_new_src(&new_ip, arg, DCC_INFO_IPV6())) | |
1286 return 0; | |
1287 | |
1288 if (!ck_cmd_priv(ce, 0, 1)) | |
1289 return 0; | |
1290 | |
1291 if (!init_map(1, 1)) | |
1292 return 0; | |
1293 src = new_ip; | |
1294 dcc_clnt_info->src = src; | |
1295 | |
1296 fix_info(0); | |
1297 return 1; | |
1298 } | |
1299 | |
1300 | |
1301 | |
1302 static int | |
1303 socks_cmd(const char *arg, const CMD_TBL_ENTRY *ce) | |
1304 { | |
1305 u_char new_use_socks; | |
1306 | |
1307 if (arg[0] == '\0') { | |
1308 if (!init_map(1, 0)) | |
1309 return 0; | |
1310 printf(" SOCKS %s\n", | |
1311 (info_flags & DCC_INFO_FG_SOCKS) ? "on" : "off"); | |
1312 return 1; | |
1313 } | |
1314 | |
1315 if (!strcmp(arg, "off")) { | |
1316 new_use_socks = 0; | |
1317 } else if (!strcmp(arg, "on")) { | |
1318 new_use_socks = DCC_INFO_FG_SOCKS; | |
1319 } else { | |
1320 return -1; | |
1321 } | |
1322 | |
1323 if (!ck_cmd_priv(ce, 0, 1)) | |
1324 return 0; | |
1325 | |
1326 if (!init_map(1, 1)) | |
1327 return 0; | |
1328 if ((dcc_clnt_info->flags & DCC_INFO_FG_SOCKS) == new_use_socks) | |
1329 return cdcc_unlock(1); /* nothing to do */ | |
1330 | |
1331 dcc_clnt_info->flags ^= DCC_INFO_FG_SOCKS; | |
1332 info_flags = dcc_clnt_info->flags; | |
1333 | |
1334 fix_info(0); | |
1335 return 1; | |
1336 } | |
1337 | |
1338 | |
1339 | |
1340 static int | |
1341 passwd_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1342 { | |
1343 DCC_PASSWD new_passwd; | |
1344 | |
1345 if (arg[0] == '\0') { | |
1346 if (which_map == MAP_INFO) { | |
1347 printf(" using password in %s\n", | |
1348 fnm2abs_err(0, info_map_nm)); | |
1349 if (passwd_set) | |
1350 printf(" but the password for explicitly" | |
1351 " named servers is "DCC_PASSWD_PAT"\n", | |
1352 passwd); | |
1353 } else { | |
1354 if (passwd_set) | |
1355 printf(" password "DCC_PASSWD_PAT"\n", | |
1356 passwd); | |
1357 else | |
1358 printf(" password not set\n"); | |
1359 } | |
1360 return 1; | |
1361 } | |
1362 | |
1363 arg = parse_passwd(0, new_passwd, arg, "password", 0, 0); | |
1364 if (!arg || *arg != '\0') | |
1365 return -1; | |
1366 memcpy(passwd, new_passwd, sizeof(passwd)); | |
1367 passwd_set = 1; | |
1368 set_which_map(MAP_TMP); | |
1369 return 1; | |
1370 } | |
1371 | |
1372 | |
1373 | |
1374 static int | |
1375 id_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1376 { | |
1377 DCC_CLNT_ID id; | |
1378 | |
1379 if (arg[0] == '\0') { | |
1380 printf(" ID=%d\n", srvr_clnt_id); | |
1381 return 1; | |
1382 } | |
1383 | |
1384 id = dcc_get_id(0, arg, 0, 0); | |
1385 if (id == DCC_ID_INVALID) | |
1386 return -1; | |
1387 | |
1388 srvr_clnt_id = id; | |
1389 set_which_map(MAP_TMP); | |
1390 return 1; | |
1391 } | |
1392 | |
1393 | |
1394 | |
1395 static int | |
1396 debug_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1397 { | |
1398 char debug_str[24]; | |
1399 char ttl_str[24]; | |
1400 int new_ttl, new_debug; | |
1401 char *p; | |
1402 | |
1403 if (arg[0] == '\0') { | |
1404 if (!dcc_clnt_debug) | |
1405 snprintf(debug_str, sizeof(debug_str), | |
1406 "debug off"); | |
1407 else if (dcc_clnt_debug == 1) | |
1408 snprintf(debug_str, sizeof(debug_str), | |
1409 "debug on"); | |
1410 else | |
1411 snprintf(debug_str, sizeof(debug_str), | |
1412 "debug on+%d\n", dcc_clnt_debug-1); | |
1413 if (dcc_debug_ttl != 0) | |
1414 snprintf(ttl_str, sizeof(ttl_str), | |
1415 " TTL=%d", dcc_debug_ttl); | |
1416 else | |
1417 ttl_str[0] = '\0'; | |
1418 printf(" %s%s\n", debug_str, ttl_str); | |
1419 return 1; | |
1420 } | |
1421 | |
1422 new_ttl = dcc_debug_ttl; | |
1423 new_debug = dcc_clnt_debug; | |
1424 for (;;) { | |
1425 if (!CLITCMP(arg, "off")) { | |
1426 new_debug = 0; | |
1427 arg += LITZ("off"); | |
1428 } else if (!CLITCMP(arg, "on")) { | |
1429 ++new_debug; | |
1430 arg += LITZ("on"); | |
1431 } else if (!CLITCMP(arg, "ttl=")) { | |
1432 new_ttl = strtoul(arg+LITZ("ttl="), &p, 10); | |
1433 #if defined(IPPROTO_IP) && defined(IP_TTL) | |
1434 if (new_ttl < 256) | |
1435 arg = p; | |
1436 #else | |
1437 printf(" TTL setting not supported\n"); | |
1438 #endif | |
1439 } | |
1440 | |
1441 if (*arg == '\0') | |
1442 break; | |
1443 if (*arg == ' ' || *arg == '\t') { | |
1444 arg += strspn(arg, DCC_WHITESPACE); | |
1445 } else { | |
1446 return -1; | |
1447 } | |
1448 } | |
1449 dcc_debug_ttl = new_ttl; | |
1450 if (dcc_debug_ttl != 0) | |
1451 set_which_map(MAP_TMP); | |
1452 dcc_clnt_debug = new_debug; | |
1453 if (dcc_clnt_debug > 1) | |
1454 printf(" debug on+%d\n", dcc_clnt_debug-1); | |
1455 return 1; | |
1456 } | |
1457 | |
1458 | |
1459 | |
1460 static int | |
1461 no_fail_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
1462 { | |
1463 if (!init_map(1, 1)) | |
1464 return 0; | |
1465 DCC_GREY2CLASS(grey_on)->fail_time= 0; | |
1466 cdcc_unlock(1); | |
1467 return 1; | |
1468 } | |
1469 | |
1470 | |
1471 | |
1472 static int | |
1473 quiet_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
1474 { | |
1475 if (arg[0] == '\0') { | |
1476 printf(" %s\n", quiet ? "on" : "off"); | |
1477 return 1; | |
1478 } else if (!CLITCMP(arg, "on")) { | |
1479 quiet = 1; | |
1480 return 1; | |
1481 } else if (!CLITCMP(arg, "off")) { | |
1482 quiet = 0; | |
1483 return 1; | |
1484 } | |
1485 return -1; | |
1486 } | |
1487 | |
1488 | |
1489 | |
1490 static int | |
1491 ckludge_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
1492 { | |
1493 char *p; | |
1494 long l; | |
1495 | |
1496 if (arg[0] == '\0') { | |
1497 printf(" clock kludge=%d\n", (int)clock_kludge); | |
1498 return 1; | |
1499 } | |
1500 | |
1501 l = strtol(arg, &p, 10); | |
1502 if (*p != '\0') { | |
1503 dcc_error_msg("invalid clock kludge \"%s\"", arg); | |
1504 return -1; | |
1505 } | |
1506 clock_kludge = l; | |
1507 return 1; | |
1508 } | |
1509 | |
1510 | |
1511 | |
1512 static int | |
1513 delete_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1514 { | |
1515 DCC_SRVR_CLASS *class; | |
1516 DCC_SRVR_NM nm, *nmp; | |
1517 DCC_SRVR_ADDR *addr; | |
1518 u_char del_grey; | |
1519 | |
1520 del_grey = grey_on; | |
1521 if (!dcc_parse_srvr_nm(dcc_emsg, &nm, &del_grey, arg, 0, 0)) { | |
1522 dcc_error_msg("%s", dcc_emsg); | |
1523 return 0; | |
1524 } | |
1525 | |
1526 /* map and lock */ | |
1527 set_which_map(MAP_INFO); | |
1528 if (!init_map(1, 1)) | |
1529 return 0; | |
1530 | |
1531 class = DCC_GREY2CLASS(del_grey); | |
1532 for (nmp = class->nms; nmp <= LAST(class->nms); ++nmp) { | |
1533 if (strcasecmp(nmp->hostname, nm.hostname) | |
1534 || nmp->port != nm.port) | |
1535 continue; | |
1536 | |
1537 /* Found it. */ | |
1538 | |
1539 /* zap its IP addresses so they won't be used | |
1540 * if resolving the remaining names fails */ | |
1541 for (addr = class->addrs; addr <= LAST(class->addrs); ++addr) { | |
1542 if (addr->nam_inx == nmp - class->nms) { | |
1543 addr->rtt = DCC_RTT_BAD; | |
1544 addr->nam_inx = NO_NAM; | |
1545 } | |
1546 } | |
1547 if (nmp != LAST(class->nms)) | |
1548 memmove(nmp, nmp+1, | |
1549 (LAST(class->nms) - nmp)*sizeof(*nmp)); | |
1550 memset(LAST(class->nms), 0, sizeof(*nmp)); | |
1551 ++class->gen; | |
1552 fix_info(class); | |
1553 return 1; | |
1554 } | |
1555 | |
1556 dcc_error_msg("server \"%s,%d\" not found", | |
1557 nm.hostname, ntohs(nm.port)); | |
1558 cdcc_unlock(1); | |
1559 return 0; | |
1560 } | |
1561 | |
1562 | |
1563 | |
1564 static int | |
1565 add_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1566 { | |
1567 DCC_SRVR_CLASS *class; | |
1568 DCC_SRVR_NM nm, *nmp, *tgt_nmp; | |
1569 u_char add_grey; | |
1570 | |
1571 add_grey = grey_set && grey_on; | |
1572 | |
1573 if (0 >= dcc_parse_srvr_nm(dcc_emsg, &nm, &add_grey, arg, 0, 0)) { | |
1574 dcc_error_msg("%s", dcc_emsg); | |
1575 return 0; | |
1576 } | |
1577 if (nm.clnt_id == DCC_ID_ANON && add_grey) { | |
1578 dcc_error_msg("anonymous client-ID invalid" | |
1579 " for Greylist server %s", | |
1580 nm.hostname); | |
1581 return 0; | |
1582 } | |
1583 | |
1584 /* map and lock the information */ | |
1585 set_which_map(MAP_INFO); | |
1586 if (!init_map(1, 1)) | |
1587 return 0; | |
1588 | |
1589 /* look for the old entry or a new, free entry */ | |
1590 class = DCC_GREY2CLASS(add_grey); | |
1591 tgt_nmp = 0; | |
1592 for (nmp = class->nms; nmp <= LAST(class->nms); ++nmp) { | |
1593 if (nmp->hostname[0] == '\0') { | |
1594 if (!tgt_nmp) | |
1595 tgt_nmp = nmp; | |
1596 continue; | |
1597 } | |
1598 if (!strcmp(nmp->hostname, nm.hostname) | |
1599 && nmp->port == nm.port) { | |
1600 printf(" overwriting existing entry\n"); | |
1601 tgt_nmp = nmp; | |
1602 break; | |
1603 } | |
1604 } | |
1605 | |
1606 if (tgt_nmp) { | |
1607 memcpy(tgt_nmp, &nm, sizeof(*tgt_nmp)); | |
1608 fix_info(class); | |
1609 return 1; | |
1610 } | |
1611 | |
1612 cdcc_unlock(1); | |
1613 if (add_grey) | |
1614 dcc_error_msg("too many Greylist server names"); | |
1615 else | |
1616 dcc_error_msg("too many DCC server names"); | |
1617 return 0; | |
1618 } | |
1619 | |
1620 | |
1621 | |
1622 static void | |
1623 add_new_nms(const DCC_SRVR_NM new_nms[DCC_MAX_SRVR_NMS], | |
1624 DCC_SRVR_NM old_nms[DCC_MAX_SRVR_NMS]) | |
1625 { | |
1626 const DCC_SRVR_NM *new_nmp; | |
1627 DCC_SRVR_NM *old_nmp; | |
1628 | |
1629 for (new_nmp = new_nms; | |
1630 new_nmp < &new_nms[DCC_MAX_SRVR_NMS] | |
1631 && new_nmp->hostname[0] != '\0'; | |
1632 ++new_nmp) { | |
1633 for (old_nmp = old_nms; | |
1634 old_nmp <= &old_nms[DCC_MAX_SRVR_NMS]; | |
1635 ++old_nmp) { | |
1636 if (old_nmp->hostname[0] == '\0' | |
1637 || (!strcmp(old_nmp->hostname, new_nmp->hostname) | |
1638 && old_nmp->port == new_nmp->port)) { | |
1639 memcpy(old_nmp, new_nmp, sizeof(*old_nmp)); | |
1640 break; | |
1641 } | |
1642 } | |
1643 } | |
1644 } | |
1645 | |
1646 | |
1647 | |
1648 static int | |
1649 load_cmd(const char *lfile, const CMD_TBL_ENTRY *ce UATTRIB) | |
1650 { | |
1651 u_char new_info_flags, load_grey; | |
1652 int flags_set; | |
1653 DCC_SRVR_NM new_nm; | |
1654 DCC_SRVR_NM dcc_nms[DCC_MAX_SRVR_NMS]; | |
1655 int num_dcc_nms; | |
1656 DCC_SRVR_NM grey_nms[DCC_MAX_SRVR_NMS]; | |
1657 int num_grey_nms; | |
1658 char src_addr[INET6_ADDRSTRLEN+1]; | |
1659 char buf[sizeof(DCC_SRVR_NM)*3]; | |
1660 DCC_IP new_src; | |
1661 const char *bufp, *cp; | |
1662 FILE *f; | |
1663 int fd, lno; | |
1664 | |
1665 if (*lfile == '\0') | |
1666 return -1; | |
1667 | |
1668 dcc_rel_priv(); | |
1669 if (!strcmp(lfile,"-")) { | |
1670 lfile = 0; | |
1671 fd = dup(fileno(stdin)); | |
1672 if (fd < 0) { | |
1673 dcc_error_msg("dup(stdin): %s", ERROR_STR()); | |
1674 return 0; | |
1675 } | |
1676 f = fdopen(fd, "r"); | |
1677 if (!f) { | |
1678 dcc_error_msg("fdopen(): %s", ERROR_STR()); | |
1679 return 0; | |
1680 } | |
1681 } else { | |
1682 f = dcc_open_srvr_nm(dcc_emsg, lfile); | |
1683 if (!f) { | |
1684 dcc_error_msg("%s", dcc_emsg); | |
1685 return 0; | |
1686 } | |
1687 } | |
1688 | |
1689 /* parse the text file to create a pair of lists of server names */ | |
1690 flags_set = 0; | |
1691 new_info_flags = info_flags; | |
1692 num_dcc_nms = 0; | |
1693 memset(dcc_nms, 0, sizeof(dcc_nms)); | |
1694 num_grey_nms = 0; | |
1695 memset(grey_nms, 0, sizeof(grey_nms)); | |
1696 memset(&new_src, 0, sizeof(new_src)); | |
1697 lno = 0; | |
1698 for (;;) { | |
1699 bufp = fgets(buf, sizeof(buf), f); | |
1700 if (!bufp) { | |
1701 if (ferror(f)) { | |
1702 dcc_error_msg("fgets(%s): %s", | |
1703 !lfile | |
1704 ? "STDIN" | |
1705 : fnm2abs_err(0, lfile), | |
1706 ERROR_STR()); | |
1707 fclose(f); | |
1708 return 0; | |
1709 } | |
1710 break; | |
1711 } | |
1712 | |
1713 ++lno; | |
1714 | |
1715 /* skip blank lines and comments */ | |
1716 bufp += strspn(bufp, DCC_WHITESPACE); | |
1717 if (*bufp == '\0' || *bufp == '#') | |
1718 continue; | |
1719 | |
1720 /* look for flags in the first non-comment line */ | |
1721 if (!flags_set++) { | |
1722 cp = bufp; | |
1723 if (!CLITCMP(cp, DCC_INFO_USE_IPV4)) { | |
1724 cp += LITZ(DCC_INFO_USE_IPV4); | |
1725 new_info_flags &= ~DCC_INFO_FG_IPV6; | |
1726 } else if (!CLITCMP(cp, DCC_INFO_USE_IPV6)) { | |
1727 cp += LITZ(DCC_INFO_USE_IPV6); | |
1728 new_info_flags |= DCC_INFO_FG_IPV6; | |
1729 } else { | |
1730 ++flags_set; | |
1731 } | |
1732 if (flags_set == 1) { | |
1733 /* We found "IPv6 on" or "off". | |
1734 * Look for "use SOCKS" and "src=x.y.z.w" */ | |
1735 cp += strspn(cp, DCC_WHITESPACE); | |
1736 if (!CLITCMP(cp, DCC_INFO_USE_SOCKS)) { | |
1737 new_info_flags |= DCC_INFO_FG_SOCKS; | |
1738 cp += LITZ(DCC_INFO_USE_SOCKS); | |
1739 cp += strspn(cp, DCC_WHITESPACE); | |
1740 } | |
1741 if (!CLITCMP(cp, DCC_INFO_USE_SRC)) { | |
1742 cp += LITZ(DCC_INFO_USE_SRC); | |
1743 cp = dcc_parse_word(dcc_emsg, | |
1744 src_addr, | |
1745 sizeof(src_addr), | |
1746 cp, 0, 0, 0); | |
1747 if (!cp) { | |
1748 dcc_error_msg("%s", dcc_emsg); | |
1749 continue; | |
1750 } | |
1751 if (!CLITCMP(cp, DCC_INFO_USE_SRCBAD)) { | |
1752 cp += LITZ(DCC_INFO_USE_SRCBAD); | |
1753 cp += strspn(cp, DCC_WHITESPACE); | |
1754 } | |
1755 ck_new_src(&new_src, src_addr, | |
1756 (new_info_flags | |
1757 & DCC_INFO_FG_SOCKS)); | |
1758 } | |
1759 } | |
1760 if (*cp == '\0') | |
1761 continue; | |
1762 /* the first non-comment line must be a server name */ | |
1763 } | |
1764 | |
1765 load_grey = 0; | |
1766 if (0 >= dcc_parse_srvr_nm(dcc_emsg, &new_nm, &load_grey, | |
1767 bufp, lfile, lno)) { | |
1768 dcc_error_msg("%s", dcc_emsg); | |
1769 fclose(f); | |
1770 return 0; | |
1771 } | |
1772 if (load_grey) { | |
1773 if (new_nm.clnt_id == DCC_ID_ANON) { | |
1774 dcc_error_msg("anonymous client-ID invalid" | |
1775 " for Greylist server %s%s", | |
1776 new_nm.hostname, | |
1777 fnm_lno(&fnm_buf, lfile, lno)); | |
1778 fclose(f); | |
1779 return 0; | |
1780 } | |
1781 if (num_grey_nms >= DIM(grey_nms)) { | |
1782 dcc_error_msg("too many Greylist server names" | |
1783 "%s", | |
1784 fnm_lno(&fnm_buf, lfile, lno)); | |
1785 fclose(f); | |
1786 return 0; | |
1787 } | |
1788 grey_nms[num_grey_nms++] = new_nm; | |
1789 } else { | |
1790 if (num_dcc_nms >= DIM(dcc_nms)) { | |
1791 dcc_error_msg("too many DCC server names%s", | |
1792 fnm_lno(&fnm_buf, lfile, lno)); | |
1793 fclose(f); | |
1794 return 0; | |
1795 } | |
1796 dcc_nms[num_dcc_nms++] = new_nm; | |
1797 } | |
1798 } | |
1799 fclose(f); | |
1800 if (num_grey_nms == 0 && num_dcc_nms == 0) { | |
1801 dcc_error_msg("no DCC server names%s", | |
1802 fnm_lno(&fnm_buf, lfile, lno)); | |
1803 return 0; | |
1804 } | |
1805 | |
1806 /* create the map, without set-UID powers to prevent games, | |
1807 * and then lock, install, and unlock the information */ | |
1808 dcc_rel_priv(); | |
1809 | |
1810 if (which_map != MAP_INFO) | |
1811 set_which_map(MAP_INFO); | |
1812 if (!init_map(0, 1)) { | |
1813 /* create a new map */ | |
1814 if (!dcc_create_map(0, info_map_nm, 0, | |
1815 0, 0, 0, 0, &new_src, new_info_flags)) | |
1816 return 0; | |
1817 printf(" created %s\n", fnm2abs_err(0, info_map_nm)); | |
1818 if (!init_map(1, 1)) | |
1819 return 0; | |
1820 } | |
1821 | |
1822 /* merge the old and new entries */ | |
1823 add_new_nms(grey_nms, dcc_clnt_info->grey.nms); | |
1824 add_new_nms(dcc_nms, dcc_clnt_info->dcc.nms); | |
1825 dcc_clnt_info->flags = info_flags = new_info_flags; | |
1826 if (new_src.family != AF_UNSPEC) | |
1827 dcc_clnt_info->src = new_src; | |
1828 | |
1829 fix_info(0); | |
1830 | |
1831 if (!quiet) { | |
1832 if (!lfile) | |
1833 printf("##################\n\n"); | |
1834 return info_work("", DCC_CLNT_FG_BAD_SRVR_OK) ; | |
1835 } | |
1836 return 1; | |
1837 } | |
1838 | |
1839 | |
1840 | |
1841 static int | |
1842 info_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1843 { | |
1844 /* map, copy, and unlock the information | |
1845 * prefer to talk to the server, but don't wait | |
1846 * unless we have changed the file */ | |
1847 return info_work(arg, | |
1848 map_changed | |
1849 ? DCC_CLNT_FG_BAD_SRVR_OK | |
1850 : (DCC_CLNT_FG_NO_PICK_SRVR | |
1851 | DCC_CLNT_FG_BAD_SRVR_OK)); | |
1852 } | |
1853 | |
1854 | |
1855 | |
1856 static int | |
1857 rtt_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1858 { | |
1859 if (!init_map(1, 1)) | |
1860 return 0; | |
1861 dcc_force_measure_rtt(&dcc_clnt_info->dcc); | |
1862 dcc_force_measure_rtt(&dcc_clnt_info->grey); | |
1863 cdcc_unlock(1); | |
1864 | |
1865 /* wait to talk to the server, but don't insist */ | |
1866 return info_work(arg, quiet ? DCC_CLNT_FG_BAD_SRVR_OK : 0); | |
1867 } | |
1868 | |
1869 | |
1870 /* delete a checksum */ | |
1871 static int /* 1=ok, 0=bad checksum, -1=fatal */ | |
1872 delck_sub(DCC_EMSG emsg, DCC_WF *wf UATTRIB, | |
1873 DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts UATTRIB) | |
1874 { | |
1875 struct timeval cmd_start, cmd_end; | |
1876 char type_buf[DCC_XHDR_MAX_TYPE_LEN]; | |
1877 char ck_buf[sizeof(DCC_SUM)*3+2]; | |
1878 DCC_DELETE del; | |
1879 DCC_OP_RESP resp; | |
1880 char ob[DCC_OPBUF]; | |
1881 u_char result; | |
1882 | |
1883 printf(" deleting %s %s\n", | |
1884 dcc_type2str(type_buf, sizeof(type_buf), type, 0, 1, grey_on), | |
1885 dcc_ck2str(ck_buf, sizeof(ck_buf), type, sum, 0)); | |
1886 | |
1887 memset(&del, 0, sizeof(del)); | |
1888 gettimeofday(&cmd_start, 0); | |
1889 del.date = htonl(cmd_start.tv_sec); | |
1890 del.ck.type = type; | |
1891 del.ck.len = sizeof(del.ck); | |
1892 memcpy(&del.ck.sum, sum, sizeof(DCC_SUM)); | |
1893 result = dcc_clnt_op(emsg, ctxt, DCC_CLNT_FG_NO_FAIL, | |
1894 0, 0, 0, &del.hdr, sizeof(del), | |
1895 DCC_OP_DELETE, &resp, sizeof(resp)); | |
1896 gettimeofday(&cmd_end, 0); | |
1897 if (!result) { | |
1898 dcc_error_msg("%s", dcc_emsg); | |
1899 } else { | |
1900 switch (resp.hdr.op) { | |
1901 case DCC_OP_OK: | |
1902 break; | |
1903 | |
1904 case DCC_OP_ERROR: | |
1905 dcc_error_msg(" %.*s", | |
1906 (ntohs(resp.hdr.len) | |
1907 -(int)(sizeof(resp.error) | |
1908 - sizeof(resp.error.msg))), | |
1909 resp.error.msg); | |
1910 result = 0; | |
1911 break; | |
1912 | |
1913 default: | |
1914 dcc_error_msg("unexpected response: %s", | |
1915 dcc_hdr_op2str(ob,sizeof(ob), &resp.hdr)); | |
1916 result = 0; | |
1917 break; | |
1918 } | |
1919 } | |
1920 | |
1921 if (dcc_clnt_debug) { | |
1922 printf("%.2f ms\n", | |
1923 ((cmd_end.tv_sec-cmd_start.tv_sec)*1000.0 | |
1924 + (cmd_end.tv_usec-cmd_start.tv_usec)/1000.0)); | |
1925 } | |
1926 return result; | |
1927 } | |
1928 | |
1929 | |
1930 | |
1931 /* delete a simple checksum */ | |
1932 static int | |
1933 delck_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1934 { | |
1935 char type_str[DCC_XHDR_MAX_TYPE_LEN+1]; | |
1936 | |
1937 if (*arg == '\0') | |
1938 return -1; | |
1939 arg = dcc_parse_word(0, type_str, sizeof(type_str), | |
1940 arg, 0, 0, 0); | |
1941 if (!arg) | |
1942 return -1; | |
1943 | |
1944 if (!rdy_ctxt(0)) | |
1945 return 0; | |
1946 return 0 < dcc_parse_hex_ck(0, &cmn_wf, | |
1947 type_str, dcc_str2type_del(type_str, -1), | |
1948 arg, 0, delck_sub); | |
1949 } | |
1950 | |
1951 | |
1952 | |
1953 static int | |
1954 sleep_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
1955 { | |
1956 double s; | |
1957 char *p; | |
1958 | |
1959 s = strtod(arg, &p); | |
1960 if (*p != '\0' || s < 0.001 || s > 1000) | |
1961 return -1; | |
1962 usleep((u_int)(s*1000000.0)); | |
1963 return 1; | |
1964 } | |
1965 | |
1966 | |
1967 | |
1968 | |
1969 static const u_char * | |
1970 client_unpack4(const u_char *cp, | |
1971 u_int *vp) | |
1972 { | |
1973 u_char c; | |
1974 u_int v; | |
1975 int shift; | |
1976 | |
1977 v = 0; | |
1978 shift = 0; | |
1979 do { | |
1980 c = *cp++; | |
1981 v |= (c & 0x7f) << shift; | |
1982 shift += 7; | |
1983 } while (c & 0x80); | |
1984 | |
1985 *vp = v; | |
1986 return cp; | |
1987 } | |
1988 | |
1989 | |
1990 | |
1991 static int | |
1992 client_unpack(const u_char *cp0, | |
1993 u_char *flagsp, | |
1994 u_int *clnt_idp, | |
1995 u_int *last_usedp, | |
1996 u_int *requestsp, | |
1997 u_int *nopsp, | |
1998 u_char *versp, | |
1999 DCC_SOCKU *su) | |
2000 { | |
2001 const u_char *cp; | |
2002 u_char flags; | |
2003 u_int v; | |
2004 struct in6_addr in6_addr; | |
2005 struct in_addr in_addr; | |
2006 | |
2007 #ifdef DCC_PKT_VERSION6 | |
2008 if (aop_resp.hdr.pkt_vers <= DCC_PKT_VERSION6) { | |
2009 #define CPY2(s) ((s[0]<<8) | s[1]) | |
2010 #define CPY3(s) ((s[0]<<16) | (s[1]<<8) | s[2]) | |
2011 #define CPY4(s) ((s[0]<<24) | (s[1]<<16) | (s[2]<<8) | s[3]) | |
2012 const DCC_ADMN_RESP_CLIENTSv6 *cl; | |
2013 | |
2014 cl = (DCC_ADMN_RESP_CLIENTSv6 *)cp0; | |
2015 flags = cl->flags; | |
2016 *flagsp = flags & (DCC_ADMN_RESP_CLIENTS_BL | |
2017 | DCC_ADMN_RESP_CLIENTS_SKIP); | |
2018 *clnt_idp = CPY4(cl->clnt_id); | |
2019 *last_usedp = CPY4(cl->last_used); | |
2020 if (flags & DCC_ADMN_RESP_CLIENTS_SKIP) { | |
2021 /* skip place keepers */ | |
2022 *last_usedp = CPY3(cl->requests); | |
2023 *requestsp = 0; | |
2024 } else { | |
2025 *requestsp = CPY3(cl->requests); | |
2026 } | |
2027 *nopsp = CPY2(cl->nops); | |
2028 if (flags & DCC_ADMN_RESP_CLIENTS_IPV6) { | |
2029 memcpy(&in6_addr, &cl->addr, sizeof(in6_addr)); | |
2030 dcc_mk_su(su, AF_INET6, &in6_addr, 0); | |
2031 return (sizeof(*cl) - sizeof(cl->addr) | |
2032 + sizeof(cl->addr.ipv6)); | |
2033 } | |
2034 memcpy(&in_addr, &cl->addr, sizeof(in_addr)); | |
2035 dcc_mk_su(su, AF_INET, &in_addr, 0); | |
2036 return (sizeof(*cl) - sizeof(cl->addr) | |
2037 + sizeof(cl->addr.ipv4)); | |
2038 } | |
2039 #undef CPY2 | |
2040 #undef CPY3 | |
2041 #undef CPY4 | |
2042 #endif | |
2043 | |
2044 cp = cp0; | |
2045 flags = *cp++; | |
2046 *flagsp = flags & (DCC_ADMN_RESP_CLIENTS_BL | |
2047 | DCC_ADMN_RESP_CLIENTS_BAD | |
2048 | DCC_ADMN_RESP_CLIENTS_SKIP | |
2049 | DCC_ADMN_RESP_CLIENTS_LAST); | |
2050 /* if the version is absent, | |
2051 * then it must be the same as the previous value */ | |
2052 if (flags & DCC_ADMN_RESP_CLIENTS_VERS) | |
2053 *versp = *cp++; | |
2054 v = *cp++ << 24; | |
2055 v |= *cp++ << 16; | |
2056 v |= *cp++ << 8; | |
2057 v |= *cp++; | |
2058 *last_usedp = v; | |
2059 if ((flags & DCC_ADMN_RESP_CLIENTS_ID1) != 0) | |
2060 *clnt_idp = DCC_ID_ANON; | |
2061 else | |
2062 cp = client_unpack4(cp, clnt_idp); | |
2063 cp = client_unpack4(cp, requestsp); | |
2064 cp = client_unpack4(cp, nopsp); | |
2065 if (flags & DCC_ADMN_RESP_CLIENTS_IPV6) { | |
2066 memcpy(&in6_addr, cp, sizeof(in6_addr)); | |
2067 dcc_mk_su(su, AF_INET6, &in6_addr, 0); | |
2068 cp += 16; | |
2069 } else { | |
2070 memcpy(&in_addr, cp, sizeof(in_addr)); | |
2071 dcc_mk_su(su, AF_INET, &in_addr, 0); | |
2072 cp += 4; | |
2073 } | |
2074 return cp - cp0; | |
2075 } | |
2076 | |
2077 | |
2078 | |
2079 /* get the server's list of recent clients */ | |
2080 static int | |
2081 clients_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
2082 { | |
2083 u_char nonames, sort, ids, req_flags; | |
2084 u_char passed_flags, passed_max_clients, passed_thold, passed_cidr; | |
2085 struct in6_addr addr6; | |
2086 DCC_AOP_CLIENTS_CIDR addr_bits; | |
2087 u_int max_clients, thold; | |
2088 u_int total, subtotal; | |
2089 u_int max_ops, max_nops; | |
2090 int ops_width, nops_width; | |
2091 u_int offset; /* next client wanted from server */ | |
2092 u_int num_clients; | |
2093 DCC_SOCKU su; | |
2094 struct ct { | |
2095 struct ct *lt, *gt, *up; | |
2096 time_t last_used; | |
2097 u_int requests; | |
2098 u_int nops; | |
2099 u_int rank; | |
2100 u_char flags; | |
2101 u_char vers; | |
2102 DCC_CLNT_ID clnt_id; | |
2103 DCC_SOCKU su; | |
2104 } *clist, **ctptr, *ctup, *ct, *ctnew; | |
2105 u_int versions[DCC_PKT_VERSION_MAX+1]; | |
2106 | |
2107 char date_buf[40]; | |
2108 struct tm last, now; | |
2109 char *p; | |
2110 const char *ac; | |
2111 u_char need_head; | |
2112 int i; | |
2113 | |
2114 passed_flags = 0; | |
2115 thold = 0; | |
2116 passed_thold = 0; | |
2117 max_clients = DCC_ADMIN_RESP_MAX_CLIENTS; | |
2118 passed_max_clients = 0; | |
2119 passed_cidr = 0; | |
2120 memset(addr_bits, 0, sizeof(addr_bits)); | |
2121 | |
2122 ac = strpbrk(arg, "/.:"); | |
2123 | |
2124 /* look for "-n", "-ns", "-n -s", etc. */ | |
2125 nonames = 0; | |
2126 sort = 0; | |
2127 ids = 0; | |
2128 req_flags = 0; | |
2129 while (*arg != 0) { | |
2130 arg += strspn(arg, " \t"); | |
2131 if (*arg == '-' && !passed_flags) { | |
2132 ++arg; | |
2133 do { | |
2134 switch (*arg) { | |
2135 case 'n': | |
2136 nonames = 1; | |
2137 break; | |
2138 case 's': | |
2139 sort = 1; | |
2140 break; | |
2141 case 'i': | |
2142 ids = 1; | |
2143 break; | |
2144 case 'a': | |
2145 req_flags |= DCC_AOP_CLIENTS_AVG; | |
2146 break; | |
2147 case 'V': | |
2148 req_flags |= DCC_AOP_CLIENTS_VERS; | |
2149 break; | |
2150 case 'A': | |
2151 req_flags |= DCC_AOP_CLIENTS_ANON; | |
2152 req_flags &= ~DCC_AOP_CLIENTS_NON_ANON; | |
2153 break; | |
2154 case 'K': | |
2155 req_flags |= DCC_AOP_CLIENTS_NON_ANON; | |
2156 req_flags &= ~DCC_AOP_CLIENTS_ANON; | |
2157 break; | |
2158 default: | |
2159 help_cmd("clients", 0); | |
2160 return -1; | |
2161 } | |
2162 } while (*++arg != ' ' && *arg != '\t' && *arg != '\0'); | |
2163 continue; | |
2164 } | |
2165 if (!passed_cidr && ac && !strpbrk(arg, DCC_WHITESPACE)) { | |
2166 int bits; | |
2167 | |
2168 bits = dcc_str2cidr(0, &addr6, 0, 0, arg, 0, 0); | |
2169 if (bits <= 0) | |
2170 return -1; | |
2171 memcpy(addr_bits, &addr6, sizeof(addr6)); | |
2172 addr_bits[sizeof(addr6)] = bits; | |
2173 arg = ""; | |
2174 passed_cidr = 1; | |
2175 passed_flags = 1; | |
2176 passed_max_clients = 1; | |
2177 passed_thold = 1; | |
2178 continue; | |
2179 } | |
2180 if (!passed_max_clients | |
2181 && (i = strtoul(arg, &p, 10)) != 0 | |
2182 && (*p == ' ' || *p == '\t' || *p == '\0')) { | |
2183 max_clients = i; | |
2184 arg = p; | |
2185 passed_max_clients = 1; | |
2186 passed_flags = 1; | |
2187 continue; | |
2188 } | |
2189 if (!passed_thold | |
2190 && (i = strtoul(arg, &p, 10)) > 0 | |
2191 && (*p == ' ' || *p == '\t' || *p == '\0') | |
2192 && i <= DCC_ADMIN_RESP_CLIENTS_MAX_THOLD) { | |
2193 thold = i; | |
2194 arg = p; | |
2195 passed_thold = 1; | |
2196 passed_max_clients = 1; | |
2197 passed_flags = 1; | |
2198 continue; | |
2199 } | |
2200 help_cmd("clients", 0); | |
2201 return -1; | |
2202 } | |
2203 | |
2204 if (ids) | |
2205 req_flags &= ~DCC_AOP_CLIENTS_VERS; | |
2206 | |
2207 /* Require a server password for client IP addresses | |
2208 * The server demands only client ID for "clients -i" */ | |
2209 if (!ids | |
2210 && !ck_cmd_priv(ce, 1, 0)) | |
2211 return 0; | |
2212 | |
2213 if (!rdy_ctxt(0)) | |
2214 return 0; | |
2215 | |
2216 /* Collect all of the information before printing it to minimize | |
2217 * the changes in the position of hosts and so deleted or missing | |
2218 * entries. */ | |
2219 total = 0; | |
2220 subtotal = 0; | |
2221 max_ops = 0; | |
2222 max_nops = 0; | |
2223 memset(versions, 0, sizeof(versions)); | |
2224 offset = 0; | |
2225 num_clients = 0; | |
2226 clist = 0; | |
2227 for (;;) { | |
2228 DCC_OPS result; | |
2229 int len, result_len; | |
2230 u_char vers, result_flags; | |
2231 # define BL_FLAGS (DCC_ADMN_RESP_CLIENTS_BL | DCC_ADMN_RESP_CLIENTS_BAD) | |
2232 u_int clnt_id, last_used, requests, nops; | |
2233 | |
2234 if (offset > DCC_AOP_CLIENTS_MAX_OFFSET) { | |
2235 dcc_error_msg("%d are too many clients", offset); | |
2236 break; | |
2237 } | |
2238 | |
2239 gettimeofday(&op_start, 0); | |
2240 result = dcc_aop(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0, | |
2241 NO_SRVR, clock_kludge, | |
2242 ids ? DCC_AOP_CLIENTS_ID : DCC_AOP_CLIENTS, | |
2243 (offset << 16) | |
2244 + min(thold, | |
2245 DCC_ADMIN_RESP_CLIENTS_MAX_THOLD), | |
2246 ISZ(aop_resp.resp.val.string | |
2247 ) >> DCC_ADMIN_RESP_CLIENTS_SHIFT, | |
2248 req_flags, | |
2249 offset >> 16, | |
2250 addr_bits, passed_cidr ? sizeof(addr_bits) : 0, | |
2251 &aop_resp, &op_result_su); | |
2252 if (result == DCC_OP_INVALID | |
2253 || result == DCC_OP_ERROR) { | |
2254 dcc_error_msg("%s", dcc_emsg); | |
2255 break; | |
2256 } | |
2257 | |
2258 /* print heading before the first chunk */ | |
2259 if (!offset) | |
2260 print_aop(-1); | |
2261 | |
2262 result_len = (ntohs(aop_resp.hdr.len) | |
2263 - (sizeof(aop_resp.resp) | |
2264 - sizeof(aop_resp.resp.val.string))); | |
2265 /* stop when the server has nothing to add */ | |
2266 if (result_len <= 1) | |
2267 break; | |
2268 | |
2269 len = 0; | |
2270 vers = 0; | |
2271 do { | |
2272 len += client_unpack(&aop_resp.resp.val.clients[len], | |
2273 &result_flags, &clnt_id, | |
2274 &last_used, | |
2275 &requests, &nops, &vers, | |
2276 &su); | |
2277 if (result_flags & DCC_ADMN_RESP_CLIENTS_SKIP) { | |
2278 offset += last_used; | |
2279 continue; | |
2280 } | |
2281 | |
2282 if (vers != 0) { | |
2283 if (vers < DIM(versions)) | |
2284 versions[vers] += requests; | |
2285 else | |
2286 versions[0] += requests; | |
2287 } | |
2288 | |
2289 | |
2290 /* quit if we are in some kind of loop */ | |
2291 if (++num_clients > DCC_ADMIN_RESP_MAX_CLIENTS) | |
2292 goto stop; | |
2293 ++offset; | |
2294 | |
2295 /* add the new entry to the possibly sorted list */ | |
2296 | |
2297 ctnew = dcc_malloc(sizeof(*ctnew)); | |
2298 memset(ctnew, 0, sizeof(*ctnew)); | |
2299 ctnew->flags = (result_flags & BL_FLAGS ); | |
2300 ctnew->vers = vers; | |
2301 ctnew->clnt_id = clnt_id; | |
2302 ctnew->last_used = last_used; | |
2303 ctnew->requests = requests; | |
2304 if (max_ops < requests) | |
2305 max_ops = requests; | |
2306 total += requests; | |
2307 ctnew->nops = nops; | |
2308 if (max_nops < nops) | |
2309 max_nops = nops; | |
2310 ctnew->su = su; | |
2311 | |
2312 ctptr = &clist; | |
2313 ctup = 0; | |
2314 for (;;) { | |
2315 ct = *ctptr; | |
2316 if (!ct) { | |
2317 ctnew->up = ctup; | |
2318 *ctptr = ctnew; | |
2319 break; | |
2320 } | |
2321 i = !sort; | |
2322 if (!i) { | |
2323 i = (0!= (ct->flags & BL_FLAGS)); | |
2324 i -= (0 != (ctnew->flags & BL_FLAGS)); | |
2325 } | |
2326 if (!i) { | |
2327 i = ct->requests; | |
2328 i -= ctnew->requests; | |
2329 } | |
2330 ctup = ct; | |
2331 if (i >= 0) { | |
2332 ctptr = &ct->lt; | |
2333 } else { | |
2334 /* update the threshold if sorting */ | |
2335 if (++ct->rank >= max_clients | |
2336 && thold < ct->requests) { | |
2337 thold = ct->requests; | |
2338 } | |
2339 ctptr = &ct->gt; | |
2340 } | |
2341 } | |
2342 } while (len < result_len); | |
2343 if (len != result_len) { | |
2344 dcc_error_msg("wrong sized clients response; %d != %d", | |
2345 result_len, len); | |
2346 break; | |
2347 } | |
2348 | |
2349 /* quit if the server ran out of things to say */ | |
2350 if (result_flags & DCC_ADMN_RESP_CLIENTS_LAST) | |
2351 break; | |
2352 | |
2353 /* Quit if we want only part of the list and we have it. | |
2354 * We must get everything the server sends if we are sorting. | |
2355 * The server uses our threshold to avoid sending everything | |
2356 * it know. */ | |
2357 if (!sort && offset >= max_clients) | |
2358 break; | |
2359 #undef BL_FLAGS | |
2360 } | |
2361 stop: | |
2362 if (!total) | |
2363 total = 1; | |
2364 | |
2365 dcc_localtime(time(0), &now); | |
2366 | |
2367 if (max_ops > 99*1000*1000) | |
2368 ops_width = 9; | |
2369 else if (max_ops > 9*1000*1000) | |
2370 ops_width = 8; | |
2371 else | |
2372 ops_width = 7; | |
2373 if (max_nops > 99*1000) | |
2374 nops_width = 6; | |
2375 else if (max_nops > 9*1000) | |
2376 nops_width = 5; | |
2377 else | |
2378 nops_width = 4; | |
2379 | |
2380 /* print the list */ | |
2381 num_clients = 0; | |
2382 for (ct = clist; ct; ct = ctnew) { | |
2383 ctnew = ct->gt; | |
2384 if (ctnew) { | |
2385 ct->gt = 0; | |
2386 continue; | |
2387 } | |
2388 | |
2389 if (num_clients == 0) { | |
2390 if (sort) { | |
2391 printf(" %*s %*s ", | |
2392 ops_width, "ops", | |
2393 nops_width, "nops"); | |
2394 if (ids) | |
2395 fputs(" ID ", stdout); | |
2396 fputs(" last ", stdout); | |
2397 if (req_flags & DCC_AOP_CLIENTS_VERS) | |
2398 fputs(" v", stdout); | |
2399 } else { | |
2400 printf("%*s %*s last ID ", | |
2401 ops_width, "ops", | |
2402 nops_width, "nops"); | |
2403 if (req_flags & DCC_AOP_CLIENTS_VERS) | |
2404 fputs(" v", stdout); | |
2405 } | |
2406 putchar('\n'); | |
2407 } | |
2408 if (++num_clients <= max_clients) { | |
2409 if (sort) { | |
2410 subtotal += ct->requests; | |
2411 printf("%3d%% %3d%% ", | |
2412 (int)(ct->requests*100.0/total), | |
2413 (int)(subtotal*100.0/total)); | |
2414 } | |
2415 printf("%*d %*d", | |
2416 ops_width, ct->requests, | |
2417 nops_width, ct->nops); | |
2418 if (sort && ids) | |
2419 printf(" %6d", ct->clnt_id); | |
2420 /* print year and no time if it was long ago */ | |
2421 dcc_localtime(ct->last_used, &last); | |
2422 printf(" %s", dcc_time2str(date_buf, sizeof(date_buf), | |
2423 (last.tm_year != now.tm_year | |
2424 && (last.tm_mon < 6 | |
2425 || now.tm_mon > 2)) | |
2426 ? "%Y/%m/%d" : "%m/%d %X", | |
2427 ct->last_used)); | |
2428 if (!sort) | |
2429 printf(" %6d", ct->clnt_id); | |
2430 if (req_flags & DCC_AOP_CLIENTS_VERS) { | |
2431 if (ct->vers != 0) | |
2432 printf(" %d", ct->vers); | |
2433 else | |
2434 fputs(" ?", stdout); | |
2435 } | |
2436 if (ct->flags & DCC_ADMN_RESP_CLIENTS_BL) | |
2437 fputs(" BLACKLIST", stdout); | |
2438 else if (ct->flags & DCC_ADMN_RESP_CLIENTS_BAD) | |
2439 fputs(" BAD", stdout); | |
2440 if (!ids) { | |
2441 char name[DCC_MAXDOMAINLEN]; | |
2442 char sustr[DCC_SU2STR_SIZE]; | |
2443 | |
2444 if (nonames) { | |
2445 printf(" %s", | |
2446 dcc_su2str2(sustr, sizeof(sustr), | |
2447 &ct->su)); | |
2448 } else { | |
2449 printf(" %-16s %s", | |
2450 dcc_su2str2(sustr, sizeof(sustr), | |
2451 &ct->su), | |
2452 dcc_su2name(name, sizeof(name), | |
2453 &ct->su)); | |
2454 } | |
2455 } | |
2456 putchar('\n'); | |
2457 | |
2458 ctnew = ct->lt; | |
2459 if (!ctnew) { | |
2460 ctnew = ct->up; | |
2461 } else { | |
2462 ctnew->up = ct->up; | |
2463 } | |
2464 } | |
2465 | |
2466 memset(ct, 0, sizeof(*ct)); | |
2467 dcc_free(ct); | |
2468 } | |
2469 putchar('\n'); | |
2470 | |
2471 | |
2472 need_head = 1; | |
2473 for (i = 0; i < DIM(versions); ++i) { | |
2474 if (versions[i] == 0) | |
2475 continue; | |
2476 if (need_head) { | |
2477 need_head = 0; | |
2478 fputs("version total\n", stdout); | |
2479 } | |
2480 printf("%6d %8d\n", i, versions[i]); | |
2481 } | |
2482 | |
2483 return 1; | |
2484 } | |
2485 | |
2486 | |
2487 | |
2488 /* get and set the server's default anonymous client delay */ | |
2489 static int | |
2490 anon_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
2491 { | |
2492 int new_delay, old_delay, inflate; | |
2493 DCC_OPS result; | |
2494 char *inflate_str, *p; | |
2495 | |
2496 inflate = 0; | |
2497 if (*arg == '\0') { | |
2498 new_delay = DCC_NO_ANON_DELAY; | |
2499 } else { | |
2500 if (!strcasecmp(arg, "forever")) { | |
2501 new_delay = DCC_ANON_DELAY_FOREVER; | |
2502 } else { | |
2503 new_delay = strtoul(arg, &inflate_str, 10); | |
2504 if (new_delay > DCC_ANON_DELAY_MAX | |
2505 || (*inflate_str != '\0' && *inflate_str != ',' | |
2506 && *inflate_str != '*')) { | |
2507 dcc_error_msg("invalid delay: \"%s\"", arg); | |
2508 return 0; | |
2509 } | |
2510 if (*inflate_str != '\0') { | |
2511 ++inflate_str; | |
2512 inflate_str += strspn(inflate_str, | |
2513 DCC_WHITESPACE); | |
2514 } | |
2515 if (*inflate_str != '\0' | |
2516 && strcasecmp(inflate_str, "none")) { | |
2517 inflate = strtoul(inflate_str, &p, 10); | |
2518 if (*p != '\0') { | |
2519 dcc_error_msg("invalid delay inflation:" | |
2520 " \"%s\"", inflate_str); | |
2521 return 0; | |
2522 } | |
2523 } | |
2524 } | |
2525 if (!ck_cmd_priv(ce, 1, 0)) | |
2526 return 0; | |
2527 } | |
2528 | |
2529 if (!rdy_ctxt(0)) | |
2530 return 0; | |
2531 | |
2532 gettimeofday(&op_start, 0); | |
2533 result = dcc_aop(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0, | |
2534 NO_SRVR, clock_kludge, DCC_AOP_ANON_DELAY, | |
2535 inflate, new_delay>>8, new_delay, 0, 0, 0, | |
2536 &aop_resp, &op_result_su); | |
2537 if (result == DCC_OP_INVALID | |
2538 || result == DCC_OP_ERROR) { | |
2539 dcc_error_msg("%s", dcc_emsg); | |
2540 return 0; | |
2541 } | |
2542 | |
2543 old_delay = ((aop_resp.resp.val.anon_delay.delay[0]<<8) | |
2544 + aop_resp.resp.val.anon_delay.delay[1]); | |
2545 if (old_delay == DCC_ANON_DELAY_FOREVER) { | |
2546 printf(" anon delay %s FOREVER\n", | |
2547 new_delay != DCC_NO_ANON_DELAY ? "was" : "is"); | |
2548 } else { | |
2549 printf(" anon delay %s %d", | |
2550 new_delay != DCC_NO_ANON_DELAY ? "was" : "is", | |
2551 old_delay); | |
2552 inflate = ((aop_resp.resp.val.anon_delay.inflate[0]<<24) | |
2553 +(aop_resp.resp.val.anon_delay.inflate[1]<<16) | |
2554 +(aop_resp.resp.val.anon_delay.inflate[2]<<8) | |
2555 +aop_resp.resp.val.anon_delay.inflate[3]); | |
2556 if (inflate != 0) | |
2557 printf(",%d", inflate); | |
2558 putchar('\n'); | |
2559 } | |
2560 return 1; | |
2561 } | |
2562 | |
2563 | |
2564 | |
2565 /* rewind the flood from a single server */ | |
2566 static int | |
2567 flod_rewind(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
2568 { | |
2569 DCC_CLNT_ID id; | |
2570 | |
2571 if (!arg) | |
2572 return -1; | |
2573 id = dcc_get_id(0, arg, 0, 0); | |
2574 if (id == DCC_ID_INVALID) | |
2575 return -1; | |
2576 | |
2577 return do_aop(DCC_AOP_FLOD, id*256 + DCC_AOP_FLOD_REWIND, NO_SRVR, 1); | |
2578 } | |
2579 | |
2580 | |
2581 | |
2582 /* fast forward the flood to a single server */ | |
2583 static int | |
2584 ffwd_out(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
2585 { | |
2586 DCC_CLNT_ID id; | |
2587 | |
2588 if (!arg) | |
2589 return -1; | |
2590 id = dcc_get_id(0, arg, 0, 0); | |
2591 if (id == DCC_ID_INVALID) | |
2592 return -1; | |
2593 | |
2594 return do_aop(DCC_AOP_FLOD, id*256 + DCC_AOP_FLOD_FFWD_OUT, NO_SRVR, 1); | |
2595 } | |
2596 | |
2597 | |
2598 | |
2599 /* fast forward the flood to a single server */ | |
2600 static int | |
2601 ffwd_in(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
2602 { | |
2603 DCC_CLNT_ID id; | |
2604 | |
2605 if (!arg) | |
2606 return -1; | |
2607 id = dcc_get_id(0, arg, 0, 0); | |
2608 if (id == DCC_ID_INVALID) | |
2609 return -1; | |
2610 | |
2611 return do_aop(DCC_AOP_FLOD, id*256 + DCC_AOP_FLOD_FFWD_IN, NO_SRVR, 1); | |
2612 } | |
2613 | |
2614 | |
2615 | |
2616 /* get the flood counts for a server */ | |
2617 static int | |
2618 flod_stats(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
2619 { | |
2620 u_int32_t id, next_id; | |
2621 DCC_AOP_FLODS op; | |
2622 u_char heading; | |
2623 int sresult; | |
2624 | |
2625 if (!arg) | |
2626 return -1; | |
2627 if (!CLITCMP(arg, "clear")) { | |
2628 arg += LITZ("clear"); | |
2629 arg += strspn(arg, DCC_WHITESPACE); | |
2630 op = DCC_AOP_FLOD_STATS_CLEAR; | |
2631 } else { | |
2632 op = DCC_AOP_FLOD_STATS; | |
2633 } | |
2634 | |
2635 heading = 1; | |
2636 if (!strcasecmp(arg, "all")) { | |
2637 id = DCC_SRVR_ID_MAX+1; | |
2638 for (;;) { | |
2639 if (!start_aop(DCC_AOP_FLOD, id*256 + op, NO_SRVR)) | |
2640 return 0; | |
2641 sresult = sscanf(aop_resp.resp.val.string, | |
2642 DCC_AOP_FLOD_STATS_ID, &next_id); | |
2643 if (1 == sresult | |
2644 && id == next_id) { | |
2645 if (id == DCC_SRVR_ID_MAX+1) { | |
2646 BUFCPY(aop_resp.resp.val.string, | |
2647 " (no flooding peers)"); | |
2648 fin_aop(NO_SRVR, 1); | |
2649 } | |
2650 return 1; | |
2651 } | |
2652 fin_aop(NO_SRVR, heading); | |
2653 heading = 0; | |
2654 if (1 != sresult) | |
2655 return 0; | |
2656 id = next_id+DCC_SRVR_ID_MAX+1; | |
2657 } | |
2658 } | |
2659 | |
2660 id = dcc_get_id(0, arg, 0, 0); | |
2661 if (id == DCC_ID_INVALID) | |
2662 return -1; | |
2663 return do_aop(DCC_AOP_FLOD, id*256 + op, NO_SRVR, heading); | |
2664 } | |
2665 | |
2666 | |
2667 | |
2668 static const char *stats_help = ""; | |
2669 | |
2670 /* get the statistics from all known servers */ | |
2671 static int | |
2672 stats_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) | |
2673 { | |
2674 DCC_SRVR_CLASS *class; | |
2675 SRVR_INX srvr_inx; | |
2676 int srvrs_gen; | |
2677 DCC_AOPS aop; | |
2678 | |
2679 /* look for "clear" or "all" */ | |
2680 srvr_inx = NO_SRVR; | |
2681 aop = DCC_AOP_STATS; | |
2682 while (*arg != 0) { | |
2683 arg += strspn(arg, " \t"); | |
2684 if (srvr_inx == NO_SRVR | |
2685 && !CLITCMP(arg, "clear")) { | |
2686 arg += LITZ("clear"); | |
2687 aop = DCC_AOP_STATS_CLEAR; | |
2688 if (!get_passwd(aops_tbl[aop].privileged)) { | |
2689 dcc_error_msg("\"stats clear\"" | |
2690 " is a privileged operation" | |
2691 PRV_MSG, ids_path); | |
2692 return 0; | |
2693 } | |
2694 } else if (aop == DCC_AOP_STATS | |
2695 && !CLITCMP(arg, "all")) { | |
2696 arg += LITZ("all"); | |
2697 srvr_inx = 0; | |
2698 } | |
2699 if (*arg != '\0' && *arg != ' ' && *arg != '\t') | |
2700 return -1; | |
2701 } | |
2702 | |
2703 if (!rdy_ctxt(0)) | |
2704 return 0; | |
2705 class = DCC_GREY2CLASS(grey_on); | |
2706 srvrs_gen = class->gen; | |
2707 do { | |
2708 if (srvrs_gen != class->gen) { | |
2709 dcc_error_msg("list of servers changed"); | |
2710 return 0; | |
2711 } | |
2712 /* skip dead servers */ | |
2713 if (srvr_inx != NO_SRVR | |
2714 && class->addrs[srvr_inx].srvr_id == DCC_ID_INVALID ) | |
2715 continue; | |
2716 | |
2717 do_aop(aop, sizeof(aop_resp.resp.val.string), srvr_inx, 1); | |
2718 fflush(stderr); | |
2719 fflush(stdout); | |
2720 } while (srvr_inx != NO_SRVR && ++srvr_inx < class->num_srvrs); | |
2721 | |
2722 return 1; | |
2723 } | |
2724 | |
2725 | |
2726 | |
2727 static int | |
2728 clock_ck_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
2729 { | |
2730 if (!rdy_ctxt(0)) | |
2731 return 0; | |
2732 do_aop(DCC_AOP_CLOCK_CHECK, sizeof(aop_resp.resp.val.string), | |
2733 NO_SRVR, 1); | |
2734 | |
2735 return 1; | |
2736 } | |
2737 | |
2738 | |
2739 | |
2740 /* restore tracing to default */ | |
2741 static int | |
2742 trace_def(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) | |
2743 { | |
2744 if (!rdy_ctxt(0)) | |
2745 return 0; | |
2746 | |
2747 return (do_aop(DCC_AOP_TRACE_ON, DCC_TRACE_ON_DEF_BITS, | |
2748 NO_SRVR, 1) | |
2749 && do_aop(DCC_AOP_TRACE_OFF, DCC_TRACE_OFF_DEF_BITS, | |
2750 NO_SRVR, 1)); | |
2751 | |
2752 } |