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 }