0
|
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 } |