Mercurial > notdcc
view 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 |
line wrap: on
line source
/* Distributed Checksum Clearinghouse * * control dcc server * * Copyright (c) 2008 by Rhyolite Software, LLC * * This agreement is not applicable to any entity which sells anti-spam * solutions to others or provides an anti-spam solution as part of a * security solution sold to other entities, or to a private network * which employs the DCC or uses data provided by operation of the DCC * but does not provide corresponding data to other users. * * Permission to use, copy, modify, and distribute this software without * changes for any purpose with or without fee is hereby granted, provided * that the above copyright notice and this permission notice appear in all * copies and any distributed versions or copies are either unchanged * or not called anything similar to "DCC" or "Distributed Checksum * Clearinghouse". * * Parties not eligible to receive a license under this agreement can * obtain a commercial license to use DCC by contacting Rhyolite Software * at sales@rhyolite.com. * * A commercial license would be for Distributed Checksum and Reputation * Clearinghouse software. That software includes additional features. This * free license for Distributed ChecksumClearinghouse Software does not in any * way grant permision to use Distributed Checksum and Reputation Clearinghouse * software * * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Rhyolite Software DCC 1.3.103-1.227 $Revision$ */ #include "dcc_ck.h" #include "dcc_xhdr.h" #include "dcc_heap_debug.h" #include "dcc_ids.h" #ifndef DCC_WIN32 #include <arpa/inet.h> #endif static DCC_EMSG dcc_emsg; static DCC_FNM_LNO_BUF fnm_buf; static DCC_CLNT_CTXT *ctxt; static DCC_PATH info_map_nm = DCC_MAP_NM_DEF; static const char *ids_nm; static time_t clock_kludge; static const char *homedir; static DCC_PASSWD passwd; static u_char passwd_set; static DCC_IP src; static DCC_SRVR_NM srvr = DCC_SRVR_NM_DEF; static u_char port_set; static DCC_CLNT_ID srvr_clnt_id = DCC_ID_ANON; static enum WHICH_MAP {MAP_TMP, MAP_INFO} which_map = MAP_INFO; static u_char map_changed = 1; static u_char info_flags; static u_char grey_set; static u_char quiet; static u_char do_cmds(char *); static void set_which_map(enum WHICH_MAP); static u_char init_map(u_char, u_char); struct cmd_tbl_entry; /* -1=display help message, 0=command failed, 1=success */ typedef int CMD (const char *, const struct cmd_tbl_entry *); typedef struct cmd_tbl_entry { const char *cmd; CMD (*fnc); u_char args; /* 0=optional, 1=required, 2=none */ u_char privileged; /* 1=must have server's password */ u_char write_map; /* 1=write map, 2=write /var/dcc/map */ const char *help_str; } CMD_TBL_ENTRY; static CMD help_cmd; static CMD exit_cmd; static CMD grey_cmd; static CMD file_cmd; static CMD new_map_cmd; static CMD delete_cmd; static CMD add_cmd; static CMD load_cmd; static CMD host_cmd; static CMD port_cmd; static CMD passwd_cmd; static CMD id_cmd; static CMD homedir_cmd; static CMD debug_cmd; static CMD quiet_cmd; static CMD no_fail_cmd; static CMD ckludge_cmd; static CMD ipv6_cmd; static CMD src_cmd; static CMD socks_cmd; static CMD info_cmd; static CMD rtt_cmd; static CMD delck_cmd; static CMD sleep_cmd; static CMD clients_cmd; static CMD anon_cmd; static CMD flod_rewind; static CMD ffwd_in; static CMD ffwd_out; static CMD flod_stats; static CMD stats_cmd; static const char *stats_help; static CMD clock_ck_cmd; static CMD trace_def; static const CMD_TBL_ENTRY cmds_tbl[] = { {"help", help_cmd, 0, 0, 0, "help [cmd]"}, {"?", help_cmd, 0, 0, 0, 0}, {"exit", exit_cmd, 2, 0, 0, "exit"}, {"quit", exit_cmd, 2, 0, 0, 0}, {"grey", grey_cmd, 0, 0, 0, "grey [on|off]"}, {"homedir", homedir_cmd, 0, 0, 0, "homedir [path]"}, {"file", file_cmd, 0, 0, 0, "file [map]"}, {"map", file_cmd, 0, 0, 0, 0}, {"new map", new_map_cmd, 0, 0, 0, "new map [map]"}, {"delete", delete_cmd, 1, 0, 2, "delete host[,port]"}, {"add", add_cmd, 1, 0, 2, "add host,[port|-] [RTT+/-#] [ID [passwd]]"}, {"load", load_cmd, 1, 0, 2, "load {info-file | -}"}, {"host", host_cmd, 0, 0, 0, "host [hostname]"}, {"server", host_cmd, 0, 0, 0, 0}, {"port", port_cmd, 0, 0, 0, "port #"}, {"password", passwd_cmd, 0, 0, 0, "password secret"}, {"passwd", passwd_cmd, 0, 0, 0, 0}, {"id", id_cmd, 0, 0, 0, "id [ID]"}, {"debug", debug_cmd, 0, 0, 0, "debug [on|off|TTL=x]"}, {"quiet", quiet_cmd, 0, 0, 0, "-quiet [on|off]"}, {"no fail", no_fail_cmd, 2, 0, 2, "-no fail"}, {"clock kludge",ckludge_cmd, 0, 0, 0, "clock kludge +/-secs"}, {"IPv6", ipv6_cmd, 0, 0, 0, "IPv6 [on|off]"}, {"src", src_cmd, 0, 0, 0, "src [-|IPaddress]"}, {"SOCKS", socks_cmd, 0, 0, 0, "SOCKS [on|off]"}, {"info", info_cmd, 0, 0, 0, "info [-N]"}, {"RTT", rtt_cmd, 0, 0, 0, "RTT [-N]"}, {"delck", delck_cmd, 1, 1, 0, "delck type hex1..4"}, {"sleep", sleep_cmd, 1, 0, 0, "sleep sec.onds"}, {"clients", clients_cmd, 0, 0, 0, "clients [-nsiaVAK] [max [thold [addr/prefix]]]"}, {"anon delay", anon_cmd, 0, 0, 0, "\nanon delay [delay[,inflate]]"}, {"flood rewind",flod_rewind, 1, 1, 0, "flood rewind ID"}, {"flod rewind", flod_rewind, 1, 1, 0, 0}, {"flood FFWD in",ffwd_in, 1, 1, 0, "flood FFWD in ID"}, {"flod FFWD in",ffwd_in, 1, 1, 0, 0}, {"flood FFWD out",ffwd_out, 1, 1, 0, "flood FFWD out ID"}, {"flod FFWD out",ffwd_out, 1, 1, 0, 0}, {"flood stats", flod_stats, 1, 1, 0, "flood stats [clear] {ID|all}"}, {"flod stats", flod_stats, 1, 1, 0, 0}, {"stats", stats_cmd, 0, 0, 0, "stats [clear|all]"}, {"status", stats_cmd, 0, 0, 0, 0}, {"clock check", clock_ck_cmd, 0, 0, 0, "clock check"}, {"trace default",trace_def, 2, 1, 0, "trace default"}, }; #define PRV_MSG ";\n" \ " use the \"id server-ID\" command\n" \ " and either \"password secret\" command or `su` to read passwords from %s" static DCC_OP_RESP aop_resp; static struct timeval op_start, op_end; static DCC_SOCKU op_result_su; static struct { const char *op; const char *help_str; DCC_AOPS aop; u_char privileged; u_int32_t val; } aops_tbl[] = { #define TMAC(s,b) \ {"trace "#s" on", "trace "#s" {on|off}", \ DCC_AOP_TRACE_ON, 1, DCC_TRACE_##b},\ {"trace "#s" off", 0, DCC_AOP_TRACE_OFF, 1, DCC_TRACE_##b} TMAC(admn,ADMN_BIT), TMAC(anon,ANON_BIT), TMAC(clnt,CLNT_BIT), TMAC(rlim,RLIM_BIT), TMAC(query,QUERY_BIT), TMAC(ridc,RIDC_BIT), TMAC(flood,FLOD_BIT), TMAC(flood2,FLOD2_BIT), TMAC(ids,IDS_BIT), TMAC(bl,BL_BIT), TMAC(db,DB_BIT), TMAC(wlist,WLIST_BIT), #undef TMAC {"stop", "", DCC_AOP_STOP, 1, 0}, {"system stop", "", DCC_AOP_STOP, 1, 1}, {"clean stop", "", DCC_AOP_STOP, 1, 2}, {"flood check", "", DCC_AOP_FLOD, 1, DCC_AOP_FLOD_CHECK}, {"flod check", 0, DCC_AOP_FLOD, 1, DCC_AOP_FLOD_CHECK}, {"flood shutdown", "", DCC_AOP_FLOD, 1, DCC_AOP_FLOD_SHUTDOWN}, {"flood shutdown", 0, DCC_AOP_FLOD, 1, DCC_AOP_FLOD_SHUTDOWN}, {"flood halt", "", DCC_AOP_FLOD, 1, DCC_AOP_FLOD_HALT}, {"flood halt", 0, DCC_AOP_FLOD, 1, DCC_AOP_FLOD_HALT}, {"flood resume", "", DCC_AOP_FLOD, 1, DCC_AOP_FLOD_RESUME}, {"flood resume", 0, DCC_AOP_FLOD, 1, DCC_AOP_FLOD_RESUME}, {"flood list", "", DCC_AOP_FLOD, 0, DCC_AOP_FLOD_LIST}, {"flood list", 0, DCC_AOP_FLOD, 0, DCC_AOP_FLOD_LIST}, {"DB clean", "", DCC_AOP_DB_CLEAN, 1, 0}, {"DB new", "", DCC_AOP_DB_NEW, 1, 0}, {"DB flush cache", "", DCC_AOP_DB_UNLOAD, 1, 0}, {"DB cache ok", "", DCC_AOP_DB_UNLOAD, 1, 1}, }; static void NRATTRIB usage(void) { dcc_logbad(EX_USAGE, "usage: [-Vdq] [-h homedir] [-c ids] [op1 [op2] ... ]\n"); } int NRATTRIB main(int argc, char **argv) { char cmd_buf[500]; int i; srvr.port = htons(DCC_SRVR_PORT); dcc_init_priv(); dcc_syslog_init(0, argv[0], 0); while ((i = getopt(argc, argv, "Vdqh:c:")) != -1) { switch (i) { case 'V': fprintf(stderr, DCC_VERSION"\n"); break; case 'd': ++dcc_clnt_debug; break; case 'q': ++quiet; break; case 'h': homedir = optarg; break; case 'c': ids_nm = optarg; break; default: usage(); } } argc -= optind; argv += optind; dcc_clnt_unthread_init(); dcc_cdhome(0, homedir, 1); set_ids_path(0, ids_nm); dcc_wf_init(&cmn_wf, 0); dcc_all_srvrs = 1; if (!init_map(!quiet, 0)) set_which_map(MAP_TMP); /* with a list of commands, act as a batch utility */ if (argc != 0) { for (;;) { /* a final arg of "-" says switch to interactive mode */ if (argc == 1 && !strcmp(*argv, "-")) break; if (!do_cmds(*argv)) { fputs(" ?\n", stderr); exit(EX_UNAVAILABLE); } assert_ctxts_unlocked(); assert_info_unlocked(); ++argv; if (!--argc) { exit(EX_OK); } } } /* Without an arg list of commands, look for commands from STDIN. * Commands end with a semicolon or newline. */ for (;;) { assert_ctxts_unlocked(); assert_info_unlocked(); printf("cdcc %s> ", which_map == MAP_INFO ? info_map_nm : "-"); fflush(stderr); fflush(stdout); if (!fgets(cmd_buf, sizeof(cmd_buf), stdin)) { fputc('\n', stdout); exit(EX_OK); } if (!do_cmds(cmd_buf)) fputs(" ?\n", stderr); } } /* see if don't need a server-ID password, have one or if we can get it */ static u_char /* 0=failed, 1=ok */ get_passwd(u_char privileged) /* 0=no privileges need, 1=need power */ { const ID_TBL *srvr_clnt_tbl; srvr.clnt_id = srvr_clnt_id; if (passwd_set) { /* set to use the manual password */ memcpy(srvr.passwd, passwd, sizeof(srvr.passwd)); /* succeed if this is not a priviledge command * or if we won't be trying to get the server to do * something priviledge for the anonymous client-ID */ return (!privileged || srvr.clnt_id != DCC_ID_ANON); } memset(srvr.passwd, 0, sizeof(srvr.passwd)); /* fail if we would be trying to get the server to do something * powerful for the anonymous client-ID */ if (srvr.clnt_id == DCC_ID_ANON) return !privileged; /* Fetch the common server passwords only if we can read them without * set-UID. This keeps random local users from attacking local * or remote servers with privileged commands, but does not slow * down privilege users who could use an editor to read and use * the cleartext passwords manually. */ dcc_rel_priv(); if (0 > access(ids_path, R_OK) && errno == EACCES) { srvr.clnt_id = DCC_ID_ANON; if (!privileged) return 1; dcc_error_msg("access(%s): %s", fnm2abs_err(0, ids_path), ERROR_STR()); return 0; } if (0 >= load_ids(dcc_emsg, srvr_clnt_id, &srvr_clnt_tbl, 1)) { if (srvr_clnt_id != DCC_ID_ANON && privileged) dcc_error_msg("%s", dcc_emsg); srvr.clnt_id = DCC_ID_ANON; return !privileged; } if (srvr_clnt_tbl) memcpy(srvr.passwd, srvr_clnt_tbl->cur_passwd, sizeof(srvr.passwd)); return 1; } static void set_which_map(enum WHICH_MAP new) { /* release things even if nothing seems to be changing * to ensure that we bind a new socket */ if (ctxt) { dcc_ctxts_lock(); if (dcc_clnt_info) dcc_unmap_close_info(0); if (ctxt) { dcc_rel_ctxt(ctxt); ctxt = 0; } dcc_ctxts_unlock(); } map_changed = 1; which_map = new; if (new == MAP_INFO) { passwd_set = 0; src.family = AF_UNSPEC; } } static u_char cdcc_unlock(u_char complain) { u_char result; result = dcc_info_unlock(dcc_emsg); dcc_ctxts_unlock(); if (!result && complain) dcc_error_msg("%s", dcc_emsg); return result; } /* start talking to the local map file */ static u_char /* 0=failed 1=mapped and locked */ init_map(u_char complain, u_char lock) /* 1=keep both locks on success */ { u_char result; info_flags = 0; dcc_emsg[0] = '\0'; dcc_ctxts_lock(); if (which_map == MAP_TMP) { result = (dcc_map_tmp_info(dcc_emsg, &srvr, &src, info_flags) && dcc_info_lock(dcc_emsg)); } else { result = dcc_map_lock_info(dcc_emsg, info_map_nm, -1); } if (result) { info_flags = dcc_clnt_info->flags; if (!lock) result = cdcc_unlock(complain); } else { dcc_ctxts_unlock(); if (complain) dcc_error_msg("%s", dcc_emsg); } return result; } /* get ready start talking to a DCC server */ static u_char /* 0=failed, 1=ok */ rdy_ctxt(DCC_CLNT_FGS fgs) { u_char rdy_done, need_unlock; info_flags = 0; if (grey_on) fgs |= DCC_CLNT_FG_GREY; else fgs &= ~DCC_CLNT_FG_GREY; fgs |= DCC_CLNT_FG_NO_FAIL; if (!dcc_clnt_info && ctxt) { dcc_rel_ctxt(ctxt); ctxt = 0; } if (ctxt) { rdy_done = 0; } else { if (which_map == MAP_TMP) { /* create a new temporary map */ ctxt = dcc_tmp_clnt_init(dcc_emsg, ctxt, &srvr, &src, fgs, 0); } else { /* open official map file */ ctxt = dcc_clnt_init(dcc_emsg, ctxt, info_map_nm, fgs); } if (!ctxt) { dcc_error_msg("%s", dcc_emsg); return 0; } rdy_done = 1; } if (!grey_set && dcc_clnt_info && !grey_on && dcc_clnt_info->dcc.nms[0].hostname[0] == '\0' && dcc_clnt_info->grey.nms[0].hostname[0] != '\0') { grey_on = 1; fgs |= DCC_CLNT_FG_GREY; rdy_done = 0; } dcc_ctxts_lock(); if (rdy_done) { need_unlock = 0; } else { dcc_emsg[0] = '\0'; need_unlock = dcc_clnt_rdy(dcc_emsg, ctxt, fgs); if (!dcc_clnt_info) { dcc_rel_ctxt(ctxt); ctxt = 0; dcc_ctxts_unlock(); dcc_error_msg("%s", dcc_emsg); return 0; } } info_flags = dcc_clnt_info->flags; if (!(fgs & DCC_CLNT_FG_NO_PICK_SRVR)) map_changed = 0; if (need_unlock && !dcc_info_unlock(0)) { dcc_rel_ctxt(ctxt); ctxt = 0; dcc_ctxts_unlock(); dcc_error_msg("%s", dcc_emsg); return 0; } /* check the other (greylist or not) server */ if (which_map != MAP_TMP) { dcc_emsg[0] = '\0'; if (!dcc_clnt_rdy(dcc_emsg, ctxt, fgs ^ DCC_CLNT_FG_GREY)) { if (dcc_clnt_debug > 1) dcc_error_msg("%s", dcc_emsg); } else { dcc_info_unlock(0); } } dcc_ctxts_unlock(); return 1; } static void fix_info(DCC_SRVR_CLASS *class) { map_changed = 1; if (class) { dcc_force_measure_rtt(class); } else { dcc_force_measure_rtt(&dcc_clnt_info->dcc); dcc_force_measure_rtt(&dcc_clnt_info->grey); } cdcc_unlock(1); /* repair addresses in the real map file now */ if (!quiet && which_map == MAP_INFO) rdy_ctxt(DCC_CLNT_FG_BAD_SRVR_OK); } /* compare ignoring case */ static const char * /* 0 or mismatch in str */ cmd_cmp(const char *str, const char *op) { char op_c, str_c; int len; len = 0; for (;;) { op_c = *op; /* avoid tolower() to avoid build hassles on odd systems */ if (op_c >= 'A' && op_c <= 'Z') op_c += 'a'-'A'; str_c = *str; if (str_c == '\t') str_c = ' '; else if (str_c >= 'A' && str_c <= 'Z') str_c += 'a'-'A'; if (op_c != str_c) { /* compress bursts of blanks */ if (str_c == ' ' && len != 0 && *(op-1) == ' ') { ++str; continue; } return str; } if (op_c == '\0') return 0; ++op; ++str; ++len; } } /* Display our name for the server and its address, * while suppressing some duplicates */ static void print_aop(SRVR_INX srvr_inx) /* -1 or server index */ { const DCC_SRVR_CLASS *class; char date_buf[40]; char sustr[DCC_SU2STR_SIZE]; const char *srvr_nm; NAM_INX nam_inx; dcc_su2str2(sustr, sizeof(sustr), &op_result_su); class = DCC_GREY2CLASS(grey_on); /* Display the preferred server if srvr_inx is NO_SRVR */ if (!GOOD_SRVR(class, srvr_inx)) srvr_inx = class->srvr_inx; if (GOOD_SRVR(class, srvr_inx) && (GOOD_NAM(nam_inx = class->addrs[srvr_inx].nam_inx))) { srvr_nm = class->nms[nam_inx].hostname; if (strcmp(srvr_nm, sustr)) { fputs(srvr_nm, stdout); putchar(' '); } printf("%s\n server-ID %d", dcc_su2str_err(&op_result_su), class->addrs[srvr_inx].srvr_id); } else { printf("%s\n ", dcc_su2str_err(&op_result_su)); } if (srvr.clnt_id != DCC_ID_ANON) printf(" client-ID %d", srvr.clnt_id); if (which_map == MAP_INFO) printf(" %s", info_map_nm); dcc_time2str(date_buf, sizeof(date_buf), " %X", op_start.tv_sec); fputs(date_buf, stdout); putchar('\n'); } static u_char /* 0=some kind of problem, 1=done */ start_aop(DCC_AOPS aop, u_int32_t val1, SRVR_INX srvr_inx) { DCC_OPS result; if (!rdy_ctxt(0)) return 0; gettimeofday(&op_start, 0); result = dcc_aop(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0, srvr_inx, clock_kludge, aop, val1, 0, 0, 0, 0, 0, &aop_resp, &op_result_su); gettimeofday(&op_end, 0); if (result == DCC_OP_INVALID || result == DCC_OP_ERROR) { dcc_error_msg("%s", dcc_emsg); return 0; } return 1; } static void fin_aop(SRVR_INX srvr_inx, /* index of server */ u_char psrvr) /* 1=print server name */ { if (quiet && !dcc_clnt_debug) return; if (psrvr) print_aop(srvr_inx); /* say what the server had to say */ if (aop_resp.resp.val.string[0] >= ' ' && aop_resp.resp.val.string[0] < 0x7f) { fputs(aop_resp.resp.val.string, stdout); putchar('\n'); } if (dcc_clnt_debug) { printf("%.2f ms\n", ((op_end.tv_sec-op_start.tv_sec)*1000.0 + (op_end.tv_usec-op_start.tv_usec)/1000.0)); } putchar('\n'); } static u_char /* 0=some kind of problem, 1=done */ do_aop(DCC_AOPS aop, u_int32_t val1, SRVR_INX srvr_inx, u_char psrvr) { if (!start_aop(aop, val1, srvr_inx)) return 0; fin_aop(srvr_inx, psrvr); return 1; } static u_char /* 0=not enough power */ ck_cmd_priv(const CMD_TBL_ENTRY *ce, u_char privileged, /* 1=need good server-ID & password */ u_char write_map) /* 1=write map, 2=write /var/dcc/map */ { /* always call get_passwd() so we have always fetched a password * fail if this command needs a good server-ID and password */ if (!get_passwd(privileged)) { dcc_error_msg("\"%s\" is a privileged server command"PRV_MSG, ce->cmd, ids_path); return 0; } if (!write_map) return 1; /* we can always write to our own throw-away map file */ if (write_map == 1 && which_map == MAP_TMP) return 1; if (0 > access(info_map_nm, R_OK) && errno != ENOENT && errno != ENOTDIR) { dcc_error_msg("\"%s\" is a privileged command changing %s", ce->cmd, fnm2abs_err(0, info_map_nm)); return 0; } return 1; } static u_char /* 1=ok 0=bad command */ cmd(const char *op) { const char *arg, *help_str; int op_num, j; const CMD_TBL_ENTRY *ce; /* look for the string as a command and execute it if we find */ ce = &cmds_tbl[0]; for (op_num = 0; op_num < DIM(cmds_tbl); ++op_num) { if (cmds_tbl[op_num].help_str) ce = &cmds_tbl[op_num]; arg = cmd_cmp(op, cmds_tbl[op_num].cmd); /* if the command table entry and the command completely * matched, then infer a null argument */ if (!arg) { if (!ck_cmd_priv(ce, ce->privileged, ce->write_map)) return 0; if (ce->args != 1) { j = ce->fnc("", ce); if (j >= 0) return j; } help_cmd(op, 0); return 0; } /* If the command table entry is an initial sustring of * the user's command, then the rest of the command must * start with white space or '='. (Allow '=' to let * homedir/fix-map not need use `eval` to quote blanks * `eval` in bash loses exit status. * Trim and use the rest of the string as the argument */ j = strspn(arg, DCC_WHITESPACE"="); if (j) { if (ce->args == 2) { help_cmd(op, 0); /* arg not allowed */ return 0; } if (!ck_cmd_priv(ce, ce->privileged, ce->write_map)) return 0; j = ce->fnc(arg+j, ce); if (j >= 0) return j; help_cmd(op, 0); return 0; } } /* otherwise try to interpret it as a DCC administrative packet */ op_num = 0; help_str = ""; for (;;) { if (op_num >= DIM(aops_tbl)) { dcc_error_msg("unrecognized command \"%s\"", op); return 0; } /* do a command */ if (aops_tbl[op_num].help_str) { help_str = aops_tbl[op_num].help_str; if (*help_str == '\0') help_str = aops_tbl[op_num].op; } if (!cmd_cmp(op, aops_tbl[op_num].op)) break; ++op_num; } /* send an administrative request to the server */ if (!get_passwd(aops_tbl[op_num].privileged)) { dcc_error_msg("\"%s\" is a privileged operation"PRV_MSG, help_str, ids_path); return 0; } /* try to send it */ return do_aop(aops_tbl[op_num].aop, aops_tbl[op_num].val, NO_SRVR, 1); } static u_char /* 0=bad command, 1=ok */ do_cmds(char *cmd_buf) { char *next_cmd, *cur_cmd, *cmd_end; char c; next_cmd = cmd_buf; for (;;) { cur_cmd = next_cmd + strspn(next_cmd, DCC_WHITESPACE";"); if (*cur_cmd == '#' || *cur_cmd == '\0') return 1; next_cmd = cur_cmd + strcspn(cur_cmd, ";\n\r"); cmd_end = next_cmd; next_cmd += strspn(next_cmd, ";\n\r"); /* null terminate and trim trailing white space from * command or arg */ do { *cmd_end-- = '\0'; c = *cmd_end; } while (cmd_end >= cur_cmd && strchr(DCC_WHITESPACE";", c)); if (*cur_cmd == '\0') /* ignore blank commands */ continue; if (!cmd(cur_cmd)) return 0; } } static int help_cmd_print(int pos, const char *str) { #define HELP_COL 24 int col, nl; if (str[0] == '\n') { nl = 100; ++str; } else { nl = 0; } col = strlen(str)+1; col += HELP_COL - (col % HELP_COL); pos += col; if (pos > 78) { putchar('\n'); pos = col; } printf("%-*s", col, str); pos += nl; return pos; #undef HELP_COL } static int help_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { int i, pos; const char *help_str; const char *p; /* say something about one command */ if (arg) { help_str = ""; for (i = 0; i < DIM(cmds_tbl); ++i) { if (cmds_tbl[i].help_str) help_str = cmds_tbl[i].help_str; p = cmd_cmp(arg, cmds_tbl[i].cmd); if (!p || *p == ' ' || *p == '\t') { while (*help_str == '\n' || *help_str == '-') ++help_str; printf("usage: %s\n", help_str); if (cmds_tbl[i].fnc == stats_cmd) printf(stats_help); return 1; } } for (i = 0; i < DIM(aops_tbl); ++i) { if (aops_tbl[i].help_str) { help_str = aops_tbl[i].help_str; if (*help_str == '\0') help_str = aops_tbl[i].op; } p = cmd_cmp(arg, aops_tbl[i].op); if (!p || *p == ' ' || *p == '\t') { while (*help_str == '\n' || *help_str == '-') ++help_str; printf("usage: %s\n", help_str); return 1; } } } /* talk about all of the commands */ printf(" version "DCC_VERSION"\n"); pos = 0; for (i = 0; i < DIM(cmds_tbl); ++i) { if (cmds_tbl[i].help_str && cmds_tbl[i].help_str[0] != '-') pos = help_cmd_print(pos, cmds_tbl[i].help_str); } for (i = 0; i < DIM(aops_tbl); ++i) { help_str = aops_tbl[i].help_str; if (!help_str) continue; if (*help_str == '\0') help_str = aops_tbl[i].op; pos = help_cmd_print(pos, help_str); } putchar('\n'); return 1; } static int NRATTRIB exit_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { exit(EX_OK); #ifndef HAVE_GCC_ATTRIBUTES return -1; #endif } static int grey_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { if (arg[0] == '\0') { printf(" Greylist mode %s%s\n", grey_on ? "on" : "off", grey_set ? "" : " by default"); return 1; } if (!strcmp(arg, "off")) { grey_on = 0; grey_set = 1; set_which_map(which_map); } else if (!strcmp(arg, "on")) { grey_on = 1; grey_set = 1; set_which_map(which_map); } else { return -1; } if (!port_set) srvr.port = DCC_GREY2PORT(grey_on); return 1; } static int homedir_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { if (arg[0] != '\0') { if (!dcc_cdhome(0, arg, 1)) return 0; if (ids_nm && !set_ids_path(dcc_emsg, ids_nm)) dcc_error_msg("%s", dcc_emsg); set_which_map(MAP_INFO); } printf(" homedir=%s\n", dcc_homedir); return 1; } /* set name of map file */ static int file_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { if (arg[0] == '\0') { if (which_map == MAP_INFO) printf(" using map file %s\n", fnm2abs_err(0, info_map_nm)); else printf(" map file %s but using temporary file\n", fnm2abs_err(0, info_map_nm)); return 1; } BUFCPY(info_map_nm, arg); set_which_map(MAP_INFO); return 1; } /* create a new client map or parameter file */ static int new_map_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { if (arg[0] == '\0') arg = DCC_MAP_NM_DEF; dcc_rel_priv(); if (!dcc_create_map(dcc_emsg, arg, 0, 0, 0, 0, 0, 0, info_flags)) { dcc_error_msg("%s", dcc_emsg); return 0; } BUFCPY(info_map_nm, arg); set_which_map(MAP_INFO); if (!quiet) printf(" created %s\n", fnm2abs_err(0, info_map_nm)); return 1; } static int info_work(const char *arg, int fgs) { DCC_CLNT_INFO info; u_char dcc, srcbad, names; if (*arg == '\0') { names = 0; } else if (!strcmp(arg, "-N")) { names = 1; } else { return -1; } if (!rdy_ctxt(fgs)) return 0; /* Snapshot the data and then release it while we print it. */ dcc_ctxts_lock(); if (!dcc_info_lock(0)) { dcc_ctxts_lock(); return 0; } memcpy(&info, dcc_clnt_info, sizeof(info)); srcbad = ctxt && (ctxt->flags & DCC_CTXT_SRCBAD); cdcc_unlock(1); dcc_rel_priv(); if (which_map == MAP_INFO) { if (info.dcc.nms[0].hostname[0] != '\0' || !grey_on) { dcc_print_info(info_map_nm, &info, quiet, 0, srcbad, names, 0 <= access(info_map_nm, R_OK)); dcc = 1; } else { dcc = 0; } if (info.grey.nms[0].hostname[0] != '\0' || grey_on) { if (dcc && !quiet) fputs("\n################\n", stdout); dcc_print_info(info_map_nm, &info, quiet, 1, srcbad, names, 0 <= access(info_map_nm, R_OK)); } } else { dcc_print_info(0, &info, quiet, grey_on, srcbad, names, 1); } if (!quiet) putchar('\n'); return 1; } /* server hostname */ static int host_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { DCC_SRVR_NM nm; int error; if (arg[0] == '\0') { if (which_map == MAP_INFO) return info_work(arg, DCC_CLNT_FG_BAD_SRVR_OK) ; printf(" %s server hostname \"%s\"\n", grey_on ? "greylist" : "DCC", srvr.hostname); return 1; } if (!strcmp(arg, "-")) { set_which_map(MAP_INFO); if (!init_map(1, 0)) { set_which_map(MAP_TMP); return 0; } return 1; } arg = dcc_parse_nm_port(0, arg, 0, nm.hostname, sizeof(nm.hostname), &nm.port, 0, 0, 0, 0); if (!arg) return 0; arg += strspn(arg, DCC_WHITESPACE); if (*arg != '\0') return 0; set_which_map(MAP_TMP); memcpy(srvr.hostname, nm.hostname, sizeof(srvr.hostname)); if (nm.port != 0) { srvr.port = nm.port; port_set = 1; } /* go with the flow for IPv6 */ dcc_host_lock(); if (!dcc_get_host(nm.hostname, (info_flags & DCC_INFO_FG_IPV6) ? 2 : 3, &error)) { dcc_host_unlock(); dcc_error_msg("%s: %s", nm.hostname, DCC_HSTRERROR(error)); } else { if (dcc_hostaddrs[0].sa.sa_family == AF_INET) info_flags &= ~DCC_INFO_FG_IPV6; else info_flags |= DCC_INFO_FG_IPV6; dcc_host_unlock(); } return 1; } /* server port # */ static int port_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { u_int port; if (arg[0] == '\0') { if (which_map == MAP_INFO) return info_work(arg, DCC_CLNT_FG_BAD_SRVR_OK) ; printf(" port=%d\n", ntohs(srvr.port)); return 1; } port = dcc_get_port(0, arg, DCC_GREY2PORT(grey_on), 0, 0); if (port == DCC_GET_PORT_INVALID) return 0; srvr.port = port; port_set = 1; set_which_map(MAP_TMP); return 1; } static int ipv6_cmd(const char *arg, const CMD_TBL_ENTRY *ce) { u_char new_use_ipv6; if (arg[0] == '\0') { if (!init_map(1, 0)) return 0; printf(" IPv6 %s\n", (info_flags & DCC_INFO_FG_IPV6) ? "on" : "off"); return 1; } if (!strcasecmp(arg, "off")) { new_use_ipv6 = 0; } else if (!strcasecmp(arg, "on")) { new_use_ipv6 = DCC_INFO_FG_IPV6; } else { return -1; } if (!ck_cmd_priv(ce, 0, 1)) return 0; if (!init_map(1, 1)) return 0; if ((dcc_clnt_info->flags & DCC_INFO_FG_IPV6) != new_use_ipv6) { dcc_clnt_info->flags ^= DCC_INFO_FG_IPV6; info_flags = dcc_clnt_info->flags; fix_info(0); } else if (!cdcc_unlock(1)) { return 0; } if (rdy_ctxt(0) && (dcc_clnt_info->flags & DCC_INFO_FG_IPV6) != new_use_ipv6) { #ifdef NO_IPV6 dcc_error_msg("IPv6 switch not changed;" " No IPv6 support in this system?"); #else dcc_error_msg("IPv6 switch not changed."); #endif return 0; } return 1; } static u_char ck_new_src(DCC_IP *new_ip, const char *arg, u_char use_ipv6) { SOCKET soc; DCC_SOCKU su; int error; memset(new_ip, 0, sizeof(*new_ip)); if (!strcmp(arg, "-")) return 1; dcc_host_lock(); if (!dcc_get_host(arg, use_ipv6 ? 1 : 0, &error)) { dcc_host_unlock(); dcc_error_msg("%s: %s", arg, DCC_HSTRERROR(error)); return 0; } if (use_ipv6) dcc_ipv4sutoipv6(&su, &dcc_hostaddrs[0]); else dcc_ipv6sutoipv4(&su, &dcc_hostaddrs[0]); dcc_su2ip(new_ip, &su); dcc_host_unlock(); soc = INVALID_SOCKET; if (0 >= dcc_udp_bind(dcc_emsg, &soc, &su, 0)) { dcc_error_msg("%s", dcc_emsg); return 0; } closesocket(soc); return 1; } static int src_cmd(const char *arg, const CMD_TBL_ENTRY *ce) { DCC_IP new_ip; char sustr[DCC_SU2STR_SIZE]; if (arg[0] == '\0') { if (!init_map(1, 0)) return 0; if (dcc_clnt_info->src.family == AF_UNSPEC) { printf(" no source address specified\n"); } else { /* display what the system actually uses */ printf(" source address=%s%s\n", dcc_su2str2(sustr, sizeof(sustr), &ctxt->bind_su), (ctxt->flags & DCC_CTXT_SRCBAD) ? " "DCC_INFO_USE_SRCBAD : ""); } return 1; } if (!ck_new_src(&new_ip, arg, DCC_INFO_IPV6())) return 0; if (!ck_cmd_priv(ce, 0, 1)) return 0; if (!init_map(1, 1)) return 0; src = new_ip; dcc_clnt_info->src = src; fix_info(0); return 1; } static int socks_cmd(const char *arg, const CMD_TBL_ENTRY *ce) { u_char new_use_socks; if (arg[0] == '\0') { if (!init_map(1, 0)) return 0; printf(" SOCKS %s\n", (info_flags & DCC_INFO_FG_SOCKS) ? "on" : "off"); return 1; } if (!strcmp(arg, "off")) { new_use_socks = 0; } else if (!strcmp(arg, "on")) { new_use_socks = DCC_INFO_FG_SOCKS; } else { return -1; } if (!ck_cmd_priv(ce, 0, 1)) return 0; if (!init_map(1, 1)) return 0; if ((dcc_clnt_info->flags & DCC_INFO_FG_SOCKS) == new_use_socks) return cdcc_unlock(1); /* nothing to do */ dcc_clnt_info->flags ^= DCC_INFO_FG_SOCKS; info_flags = dcc_clnt_info->flags; fix_info(0); return 1; } static int passwd_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { DCC_PASSWD new_passwd; if (arg[0] == '\0') { if (which_map == MAP_INFO) { printf(" using password in %s\n", fnm2abs_err(0, info_map_nm)); if (passwd_set) printf(" but the password for explicitly" " named servers is "DCC_PASSWD_PAT"\n", passwd); } else { if (passwd_set) printf(" password "DCC_PASSWD_PAT"\n", passwd); else printf(" password not set\n"); } return 1; } arg = parse_passwd(0, new_passwd, arg, "password", 0, 0); if (!arg || *arg != '\0') return -1; memcpy(passwd, new_passwd, sizeof(passwd)); passwd_set = 1; set_which_map(MAP_TMP); return 1; } static int id_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { DCC_CLNT_ID id; if (arg[0] == '\0') { printf(" ID=%d\n", srvr_clnt_id); return 1; } id = dcc_get_id(0, arg, 0, 0); if (id == DCC_ID_INVALID) return -1; srvr_clnt_id = id; set_which_map(MAP_TMP); return 1; } static int debug_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { char debug_str[24]; char ttl_str[24]; int new_ttl, new_debug; char *p; if (arg[0] == '\0') { if (!dcc_clnt_debug) snprintf(debug_str, sizeof(debug_str), "debug off"); else if (dcc_clnt_debug == 1) snprintf(debug_str, sizeof(debug_str), "debug on"); else snprintf(debug_str, sizeof(debug_str), "debug on+%d\n", dcc_clnt_debug-1); if (dcc_debug_ttl != 0) snprintf(ttl_str, sizeof(ttl_str), " TTL=%d", dcc_debug_ttl); else ttl_str[0] = '\0'; printf(" %s%s\n", debug_str, ttl_str); return 1; } new_ttl = dcc_debug_ttl; new_debug = dcc_clnt_debug; for (;;) { if (!CLITCMP(arg, "off")) { new_debug = 0; arg += LITZ("off"); } else if (!CLITCMP(arg, "on")) { ++new_debug; arg += LITZ("on"); } else if (!CLITCMP(arg, "ttl=")) { new_ttl = strtoul(arg+LITZ("ttl="), &p, 10); #if defined(IPPROTO_IP) && defined(IP_TTL) if (new_ttl < 256) arg = p; #else printf(" TTL setting not supported\n"); #endif } if (*arg == '\0') break; if (*arg == ' ' || *arg == '\t') { arg += strspn(arg, DCC_WHITESPACE); } else { return -1; } } dcc_debug_ttl = new_ttl; if (dcc_debug_ttl != 0) set_which_map(MAP_TMP); dcc_clnt_debug = new_debug; if (dcc_clnt_debug > 1) printf(" debug on+%d\n", dcc_clnt_debug-1); return 1; } static int no_fail_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { if (!init_map(1, 1)) return 0; DCC_GREY2CLASS(grey_on)->fail_time= 0; cdcc_unlock(1); return 1; } static int quiet_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { if (arg[0] == '\0') { printf(" %s\n", quiet ? "on" : "off"); return 1; } else if (!CLITCMP(arg, "on")) { quiet = 1; return 1; } else if (!CLITCMP(arg, "off")) { quiet = 0; return 1; } return -1; } static int ckludge_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { char *p; long l; if (arg[0] == '\0') { printf(" clock kludge=%d\n", (int)clock_kludge); return 1; } l = strtol(arg, &p, 10); if (*p != '\0') { dcc_error_msg("invalid clock kludge \"%s\"", arg); return -1; } clock_kludge = l; return 1; } static int delete_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { DCC_SRVR_CLASS *class; DCC_SRVR_NM nm, *nmp; DCC_SRVR_ADDR *addr; u_char del_grey; del_grey = grey_on; if (!dcc_parse_srvr_nm(dcc_emsg, &nm, &del_grey, arg, 0, 0)) { dcc_error_msg("%s", dcc_emsg); return 0; } /* map and lock */ set_which_map(MAP_INFO); if (!init_map(1, 1)) return 0; class = DCC_GREY2CLASS(del_grey); for (nmp = class->nms; nmp <= LAST(class->nms); ++nmp) { if (strcasecmp(nmp->hostname, nm.hostname) || nmp->port != nm.port) continue; /* Found it. */ /* zap its IP addresses so they won't be used * if resolving the remaining names fails */ for (addr = class->addrs; addr <= LAST(class->addrs); ++addr) { if (addr->nam_inx == nmp - class->nms) { addr->rtt = DCC_RTT_BAD; addr->nam_inx = NO_NAM; } } if (nmp != LAST(class->nms)) memmove(nmp, nmp+1, (LAST(class->nms) - nmp)*sizeof(*nmp)); memset(LAST(class->nms), 0, sizeof(*nmp)); ++class->gen; fix_info(class); return 1; } dcc_error_msg("server \"%s,%d\" not found", nm.hostname, ntohs(nm.port)); cdcc_unlock(1); return 0; } static int add_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { DCC_SRVR_CLASS *class; DCC_SRVR_NM nm, *nmp, *tgt_nmp; u_char add_grey; add_grey = grey_set && grey_on; if (0 >= dcc_parse_srvr_nm(dcc_emsg, &nm, &add_grey, arg, 0, 0)) { dcc_error_msg("%s", dcc_emsg); return 0; } if (nm.clnt_id == DCC_ID_ANON && add_grey) { dcc_error_msg("anonymous client-ID invalid" " for Greylist server %s", nm.hostname); return 0; } /* map and lock the information */ set_which_map(MAP_INFO); if (!init_map(1, 1)) return 0; /* look for the old entry or a new, free entry */ class = DCC_GREY2CLASS(add_grey); tgt_nmp = 0; for (nmp = class->nms; nmp <= LAST(class->nms); ++nmp) { if (nmp->hostname[0] == '\0') { if (!tgt_nmp) tgt_nmp = nmp; continue; } if (!strcmp(nmp->hostname, nm.hostname) && nmp->port == nm.port) { printf(" overwriting existing entry\n"); tgt_nmp = nmp; break; } } if (tgt_nmp) { memcpy(tgt_nmp, &nm, sizeof(*tgt_nmp)); fix_info(class); return 1; } cdcc_unlock(1); if (add_grey) dcc_error_msg("too many Greylist server names"); else dcc_error_msg("too many DCC server names"); return 0; } static void add_new_nms(const DCC_SRVR_NM new_nms[DCC_MAX_SRVR_NMS], DCC_SRVR_NM old_nms[DCC_MAX_SRVR_NMS]) { const DCC_SRVR_NM *new_nmp; DCC_SRVR_NM *old_nmp; for (new_nmp = new_nms; new_nmp < &new_nms[DCC_MAX_SRVR_NMS] && new_nmp->hostname[0] != '\0'; ++new_nmp) { for (old_nmp = old_nms; old_nmp <= &old_nms[DCC_MAX_SRVR_NMS]; ++old_nmp) { if (old_nmp->hostname[0] == '\0' || (!strcmp(old_nmp->hostname, new_nmp->hostname) && old_nmp->port == new_nmp->port)) { memcpy(old_nmp, new_nmp, sizeof(*old_nmp)); break; } } } } static int load_cmd(const char *lfile, const CMD_TBL_ENTRY *ce UATTRIB) { u_char new_info_flags, load_grey; int flags_set; DCC_SRVR_NM new_nm; DCC_SRVR_NM dcc_nms[DCC_MAX_SRVR_NMS]; int num_dcc_nms; DCC_SRVR_NM grey_nms[DCC_MAX_SRVR_NMS]; int num_grey_nms; char src_addr[INET6_ADDRSTRLEN+1]; char buf[sizeof(DCC_SRVR_NM)*3]; DCC_IP new_src; const char *bufp, *cp; FILE *f; int fd, lno; if (*lfile == '\0') return -1; dcc_rel_priv(); if (!strcmp(lfile,"-")) { lfile = 0; fd = dup(fileno(stdin)); if (fd < 0) { dcc_error_msg("dup(stdin): %s", ERROR_STR()); return 0; } f = fdopen(fd, "r"); if (!f) { dcc_error_msg("fdopen(): %s", ERROR_STR()); return 0; } } else { f = dcc_open_srvr_nm(dcc_emsg, lfile); if (!f) { dcc_error_msg("%s", dcc_emsg); return 0; } } /* parse the text file to create a pair of lists of server names */ flags_set = 0; new_info_flags = info_flags; num_dcc_nms = 0; memset(dcc_nms, 0, sizeof(dcc_nms)); num_grey_nms = 0; memset(grey_nms, 0, sizeof(grey_nms)); memset(&new_src, 0, sizeof(new_src)); lno = 0; for (;;) { bufp = fgets(buf, sizeof(buf), f); if (!bufp) { if (ferror(f)) { dcc_error_msg("fgets(%s): %s", !lfile ? "STDIN" : fnm2abs_err(0, lfile), ERROR_STR()); fclose(f); return 0; } break; } ++lno; /* skip blank lines and comments */ bufp += strspn(bufp, DCC_WHITESPACE); if (*bufp == '\0' || *bufp == '#') continue; /* look for flags in the first non-comment line */ if (!flags_set++) { cp = bufp; if (!CLITCMP(cp, DCC_INFO_USE_IPV4)) { cp += LITZ(DCC_INFO_USE_IPV4); new_info_flags &= ~DCC_INFO_FG_IPV6; } else if (!CLITCMP(cp, DCC_INFO_USE_IPV6)) { cp += LITZ(DCC_INFO_USE_IPV6); new_info_flags |= DCC_INFO_FG_IPV6; } else { ++flags_set; } if (flags_set == 1) { /* We found "IPv6 on" or "off". * Look for "use SOCKS" and "src=x.y.z.w" */ cp += strspn(cp, DCC_WHITESPACE); if (!CLITCMP(cp, DCC_INFO_USE_SOCKS)) { new_info_flags |= DCC_INFO_FG_SOCKS; cp += LITZ(DCC_INFO_USE_SOCKS); cp += strspn(cp, DCC_WHITESPACE); } if (!CLITCMP(cp, DCC_INFO_USE_SRC)) { cp += LITZ(DCC_INFO_USE_SRC); cp = dcc_parse_word(dcc_emsg, src_addr, sizeof(src_addr), cp, 0, 0, 0); if (!cp) { dcc_error_msg("%s", dcc_emsg); continue; } if (!CLITCMP(cp, DCC_INFO_USE_SRCBAD)) { cp += LITZ(DCC_INFO_USE_SRCBAD); cp += strspn(cp, DCC_WHITESPACE); } ck_new_src(&new_src, src_addr, (new_info_flags & DCC_INFO_FG_SOCKS)); } } if (*cp == '\0') continue; /* the first non-comment line must be a server name */ } load_grey = 0; if (0 >= dcc_parse_srvr_nm(dcc_emsg, &new_nm, &load_grey, bufp, lfile, lno)) { dcc_error_msg("%s", dcc_emsg); fclose(f); return 0; } if (load_grey) { if (new_nm.clnt_id == DCC_ID_ANON) { dcc_error_msg("anonymous client-ID invalid" " for Greylist server %s%s", new_nm.hostname, fnm_lno(&fnm_buf, lfile, lno)); fclose(f); return 0; } if (num_grey_nms >= DIM(grey_nms)) { dcc_error_msg("too many Greylist server names" "%s", fnm_lno(&fnm_buf, lfile, lno)); fclose(f); return 0; } grey_nms[num_grey_nms++] = new_nm; } else { if (num_dcc_nms >= DIM(dcc_nms)) { dcc_error_msg("too many DCC server names%s", fnm_lno(&fnm_buf, lfile, lno)); fclose(f); return 0; } dcc_nms[num_dcc_nms++] = new_nm; } } fclose(f); if (num_grey_nms == 0 && num_dcc_nms == 0) { dcc_error_msg("no DCC server names%s", fnm_lno(&fnm_buf, lfile, lno)); return 0; } /* create the map, without set-UID powers to prevent games, * and then lock, install, and unlock the information */ dcc_rel_priv(); if (which_map != MAP_INFO) set_which_map(MAP_INFO); if (!init_map(0, 1)) { /* create a new map */ if (!dcc_create_map(0, info_map_nm, 0, 0, 0, 0, 0, &new_src, new_info_flags)) return 0; printf(" created %s\n", fnm2abs_err(0, info_map_nm)); if (!init_map(1, 1)) return 0; } /* merge the old and new entries */ add_new_nms(grey_nms, dcc_clnt_info->grey.nms); add_new_nms(dcc_nms, dcc_clnt_info->dcc.nms); dcc_clnt_info->flags = info_flags = new_info_flags; if (new_src.family != AF_UNSPEC) dcc_clnt_info->src = new_src; fix_info(0); if (!quiet) { if (!lfile) printf("##################\n\n"); return info_work("", DCC_CLNT_FG_BAD_SRVR_OK) ; } return 1; } static int info_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { /* map, copy, and unlock the information * prefer to talk to the server, but don't wait * unless we have changed the file */ return info_work(arg, map_changed ? DCC_CLNT_FG_BAD_SRVR_OK : (DCC_CLNT_FG_NO_PICK_SRVR | DCC_CLNT_FG_BAD_SRVR_OK)); } static int rtt_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { if (!init_map(1, 1)) return 0; dcc_force_measure_rtt(&dcc_clnt_info->dcc); dcc_force_measure_rtt(&dcc_clnt_info->grey); cdcc_unlock(1); /* wait to talk to the server, but don't insist */ return info_work(arg, quiet ? DCC_CLNT_FG_BAD_SRVR_OK : 0); } /* delete a checksum */ static int /* 1=ok, 0=bad checksum, -1=fatal */ delck_sub(DCC_EMSG emsg, DCC_WF *wf UATTRIB, DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts UATTRIB) { struct timeval cmd_start, cmd_end; char type_buf[DCC_XHDR_MAX_TYPE_LEN]; char ck_buf[sizeof(DCC_SUM)*3+2]; DCC_DELETE del; DCC_OP_RESP resp; char ob[DCC_OPBUF]; u_char result; printf(" deleting %s %s\n", dcc_type2str(type_buf, sizeof(type_buf), type, 0, 1, grey_on), dcc_ck2str(ck_buf, sizeof(ck_buf), type, sum, 0)); memset(&del, 0, sizeof(del)); gettimeofday(&cmd_start, 0); del.date = htonl(cmd_start.tv_sec); del.ck.type = type; del.ck.len = sizeof(del.ck); memcpy(&del.ck.sum, sum, sizeof(DCC_SUM)); result = dcc_clnt_op(emsg, ctxt, DCC_CLNT_FG_NO_FAIL, 0, 0, 0, &del.hdr, sizeof(del), DCC_OP_DELETE, &resp, sizeof(resp)); gettimeofday(&cmd_end, 0); if (!result) { dcc_error_msg("%s", dcc_emsg); } else { switch (resp.hdr.op) { case DCC_OP_OK: break; case DCC_OP_ERROR: dcc_error_msg(" %.*s", (ntohs(resp.hdr.len) -(int)(sizeof(resp.error) - sizeof(resp.error.msg))), resp.error.msg); result = 0; break; default: dcc_error_msg("unexpected response: %s", dcc_hdr_op2str(ob,sizeof(ob), &resp.hdr)); result = 0; break; } } if (dcc_clnt_debug) { printf("%.2f ms\n", ((cmd_end.tv_sec-cmd_start.tv_sec)*1000.0 + (cmd_end.tv_usec-cmd_start.tv_usec)/1000.0)); } return result; } /* delete a simple checksum */ static int delck_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { char type_str[DCC_XHDR_MAX_TYPE_LEN+1]; if (*arg == '\0') return -1; arg = dcc_parse_word(0, type_str, sizeof(type_str), arg, 0, 0, 0); if (!arg) return -1; if (!rdy_ctxt(0)) return 0; return 0 < dcc_parse_hex_ck(0, &cmn_wf, type_str, dcc_str2type_del(type_str, -1), arg, 0, delck_sub); } static int sleep_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { double s; char *p; s = strtod(arg, &p); if (*p != '\0' || s < 0.001 || s > 1000) return -1; usleep((u_int)(s*1000000.0)); return 1; } static const u_char * client_unpack4(const u_char *cp, u_int *vp) { u_char c; u_int v; int shift; v = 0; shift = 0; do { c = *cp++; v |= (c & 0x7f) << shift; shift += 7; } while (c & 0x80); *vp = v; return cp; } static int client_unpack(const u_char *cp0, u_char *flagsp, u_int *clnt_idp, u_int *last_usedp, u_int *requestsp, u_int *nopsp, u_char *versp, DCC_SOCKU *su) { const u_char *cp; u_char flags; u_int v; struct in6_addr in6_addr; struct in_addr in_addr; #ifdef DCC_PKT_VERSION6 if (aop_resp.hdr.pkt_vers <= DCC_PKT_VERSION6) { #define CPY2(s) ((s[0]<<8) | s[1]) #define CPY3(s) ((s[0]<<16) | (s[1]<<8) | s[2]) #define CPY4(s) ((s[0]<<24) | (s[1]<<16) | (s[2]<<8) | s[3]) const DCC_ADMN_RESP_CLIENTSv6 *cl; cl = (DCC_ADMN_RESP_CLIENTSv6 *)cp0; flags = cl->flags; *flagsp = flags & (DCC_ADMN_RESP_CLIENTS_BL | DCC_ADMN_RESP_CLIENTS_SKIP); *clnt_idp = CPY4(cl->clnt_id); *last_usedp = CPY4(cl->last_used); if (flags & DCC_ADMN_RESP_CLIENTS_SKIP) { /* skip place keepers */ *last_usedp = CPY3(cl->requests); *requestsp = 0; } else { *requestsp = CPY3(cl->requests); } *nopsp = CPY2(cl->nops); if (flags & DCC_ADMN_RESP_CLIENTS_IPV6) { memcpy(&in6_addr, &cl->addr, sizeof(in6_addr)); dcc_mk_su(su, AF_INET6, &in6_addr, 0); return (sizeof(*cl) - sizeof(cl->addr) + sizeof(cl->addr.ipv6)); } memcpy(&in_addr, &cl->addr, sizeof(in_addr)); dcc_mk_su(su, AF_INET, &in_addr, 0); return (sizeof(*cl) - sizeof(cl->addr) + sizeof(cl->addr.ipv4)); } #undef CPY2 #undef CPY3 #undef CPY4 #endif cp = cp0; flags = *cp++; *flagsp = flags & (DCC_ADMN_RESP_CLIENTS_BL | DCC_ADMN_RESP_CLIENTS_BAD | DCC_ADMN_RESP_CLIENTS_SKIP | DCC_ADMN_RESP_CLIENTS_LAST); /* if the version is absent, * then it must be the same as the previous value */ if (flags & DCC_ADMN_RESP_CLIENTS_VERS) *versp = *cp++; v = *cp++ << 24; v |= *cp++ << 16; v |= *cp++ << 8; v |= *cp++; *last_usedp = v; if ((flags & DCC_ADMN_RESP_CLIENTS_ID1) != 0) *clnt_idp = DCC_ID_ANON; else cp = client_unpack4(cp, clnt_idp); cp = client_unpack4(cp, requestsp); cp = client_unpack4(cp, nopsp); if (flags & DCC_ADMN_RESP_CLIENTS_IPV6) { memcpy(&in6_addr, cp, sizeof(in6_addr)); dcc_mk_su(su, AF_INET6, &in6_addr, 0); cp += 16; } else { memcpy(&in_addr, cp, sizeof(in_addr)); dcc_mk_su(su, AF_INET, &in_addr, 0); cp += 4; } return cp - cp0; } /* get the server's list of recent clients */ static int clients_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { u_char nonames, sort, ids, req_flags; u_char passed_flags, passed_max_clients, passed_thold, passed_cidr; struct in6_addr addr6; DCC_AOP_CLIENTS_CIDR addr_bits; u_int max_clients, thold; u_int total, subtotal; u_int max_ops, max_nops; int ops_width, nops_width; u_int offset; /* next client wanted from server */ u_int num_clients; DCC_SOCKU su; struct ct { struct ct *lt, *gt, *up; time_t last_used; u_int requests; u_int nops; u_int rank; u_char flags; u_char vers; DCC_CLNT_ID clnt_id; DCC_SOCKU su; } *clist, **ctptr, *ctup, *ct, *ctnew; u_int versions[DCC_PKT_VERSION_MAX+1]; char date_buf[40]; struct tm last, now; char *p; const char *ac; u_char need_head; int i; passed_flags = 0; thold = 0; passed_thold = 0; max_clients = DCC_ADMIN_RESP_MAX_CLIENTS; passed_max_clients = 0; passed_cidr = 0; memset(addr_bits, 0, sizeof(addr_bits)); ac = strpbrk(arg, "/.:"); /* look for "-n", "-ns", "-n -s", etc. */ nonames = 0; sort = 0; ids = 0; req_flags = 0; while (*arg != 0) { arg += strspn(arg, " \t"); if (*arg == '-' && !passed_flags) { ++arg; do { switch (*arg) { case 'n': nonames = 1; break; case 's': sort = 1; break; case 'i': ids = 1; break; case 'a': req_flags |= DCC_AOP_CLIENTS_AVG; break; case 'V': req_flags |= DCC_AOP_CLIENTS_VERS; break; case 'A': req_flags |= DCC_AOP_CLIENTS_ANON; req_flags &= ~DCC_AOP_CLIENTS_NON_ANON; break; case 'K': req_flags |= DCC_AOP_CLIENTS_NON_ANON; req_flags &= ~DCC_AOP_CLIENTS_ANON; break; default: help_cmd("clients", 0); return -1; } } while (*++arg != ' ' && *arg != '\t' && *arg != '\0'); continue; } if (!passed_cidr && ac && !strpbrk(arg, DCC_WHITESPACE)) { int bits; bits = dcc_str2cidr(0, &addr6, 0, 0, arg, 0, 0); if (bits <= 0) return -1; memcpy(addr_bits, &addr6, sizeof(addr6)); addr_bits[sizeof(addr6)] = bits; arg = ""; passed_cidr = 1; passed_flags = 1; passed_max_clients = 1; passed_thold = 1; continue; } if (!passed_max_clients && (i = strtoul(arg, &p, 10)) != 0 && (*p == ' ' || *p == '\t' || *p == '\0')) { max_clients = i; arg = p; passed_max_clients = 1; passed_flags = 1; continue; } if (!passed_thold && (i = strtoul(arg, &p, 10)) > 0 && (*p == ' ' || *p == '\t' || *p == '\0') && i <= DCC_ADMIN_RESP_CLIENTS_MAX_THOLD) { thold = i; arg = p; passed_thold = 1; passed_max_clients = 1; passed_flags = 1; continue; } help_cmd("clients", 0); return -1; } if (ids) req_flags &= ~DCC_AOP_CLIENTS_VERS; /* Require a server password for client IP addresses * The server demands only client ID for "clients -i" */ if (!ids && !ck_cmd_priv(ce, 1, 0)) return 0; if (!rdy_ctxt(0)) return 0; /* Collect all of the information before printing it to minimize * the changes in the position of hosts and so deleted or missing * entries. */ total = 0; subtotal = 0; max_ops = 0; max_nops = 0; memset(versions, 0, sizeof(versions)); offset = 0; num_clients = 0; clist = 0; for (;;) { DCC_OPS result; int len, result_len; u_char vers, result_flags; # define BL_FLAGS (DCC_ADMN_RESP_CLIENTS_BL | DCC_ADMN_RESP_CLIENTS_BAD) u_int clnt_id, last_used, requests, nops; if (offset > DCC_AOP_CLIENTS_MAX_OFFSET) { dcc_error_msg("%d are too many clients", offset); break; } gettimeofday(&op_start, 0); result = dcc_aop(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0, NO_SRVR, clock_kludge, ids ? DCC_AOP_CLIENTS_ID : DCC_AOP_CLIENTS, (offset << 16) + min(thold, DCC_ADMIN_RESP_CLIENTS_MAX_THOLD), ISZ(aop_resp.resp.val.string ) >> DCC_ADMIN_RESP_CLIENTS_SHIFT, req_flags, offset >> 16, addr_bits, passed_cidr ? sizeof(addr_bits) : 0, &aop_resp, &op_result_su); if (result == DCC_OP_INVALID || result == DCC_OP_ERROR) { dcc_error_msg("%s", dcc_emsg); break; } /* print heading before the first chunk */ if (!offset) print_aop(-1); result_len = (ntohs(aop_resp.hdr.len) - (sizeof(aop_resp.resp) - sizeof(aop_resp.resp.val.string))); /* stop when the server has nothing to add */ if (result_len <= 1) break; len = 0; vers = 0; do { len += client_unpack(&aop_resp.resp.val.clients[len], &result_flags, &clnt_id, &last_used, &requests, &nops, &vers, &su); if (result_flags & DCC_ADMN_RESP_CLIENTS_SKIP) { offset += last_used; continue; } if (vers != 0) { if (vers < DIM(versions)) versions[vers] += requests; else versions[0] += requests; } /* quit if we are in some kind of loop */ if (++num_clients > DCC_ADMIN_RESP_MAX_CLIENTS) goto stop; ++offset; /* add the new entry to the possibly sorted list */ ctnew = dcc_malloc(sizeof(*ctnew)); memset(ctnew, 0, sizeof(*ctnew)); ctnew->flags = (result_flags & BL_FLAGS ); ctnew->vers = vers; ctnew->clnt_id = clnt_id; ctnew->last_used = last_used; ctnew->requests = requests; if (max_ops < requests) max_ops = requests; total += requests; ctnew->nops = nops; if (max_nops < nops) max_nops = nops; ctnew->su = su; ctptr = &clist; ctup = 0; for (;;) { ct = *ctptr; if (!ct) { ctnew->up = ctup; *ctptr = ctnew; break; } i = !sort; if (!i) { i = (0!= (ct->flags & BL_FLAGS)); i -= (0 != (ctnew->flags & BL_FLAGS)); } if (!i) { i = ct->requests; i -= ctnew->requests; } ctup = ct; if (i >= 0) { ctptr = &ct->lt; } else { /* update the threshold if sorting */ if (++ct->rank >= max_clients && thold < ct->requests) { thold = ct->requests; } ctptr = &ct->gt; } } } while (len < result_len); if (len != result_len) { dcc_error_msg("wrong sized clients response; %d != %d", result_len, len); break; } /* quit if the server ran out of things to say */ if (result_flags & DCC_ADMN_RESP_CLIENTS_LAST) break; /* Quit if we want only part of the list and we have it. * We must get everything the server sends if we are sorting. * The server uses our threshold to avoid sending everything * it know. */ if (!sort && offset >= max_clients) break; #undef BL_FLAGS } stop: if (!total) total = 1; dcc_localtime(time(0), &now); if (max_ops > 99*1000*1000) ops_width = 9; else if (max_ops > 9*1000*1000) ops_width = 8; else ops_width = 7; if (max_nops > 99*1000) nops_width = 6; else if (max_nops > 9*1000) nops_width = 5; else nops_width = 4; /* print the list */ num_clients = 0; for (ct = clist; ct; ct = ctnew) { ctnew = ct->gt; if (ctnew) { ct->gt = 0; continue; } if (num_clients == 0) { if (sort) { printf(" %*s %*s ", ops_width, "ops", nops_width, "nops"); if (ids) fputs(" ID ", stdout); fputs(" last ", stdout); if (req_flags & DCC_AOP_CLIENTS_VERS) fputs(" v", stdout); } else { printf("%*s %*s last ID ", ops_width, "ops", nops_width, "nops"); if (req_flags & DCC_AOP_CLIENTS_VERS) fputs(" v", stdout); } putchar('\n'); } if (++num_clients <= max_clients) { if (sort) { subtotal += ct->requests; printf("%3d%% %3d%% ", (int)(ct->requests*100.0/total), (int)(subtotal*100.0/total)); } printf("%*d %*d", ops_width, ct->requests, nops_width, ct->nops); if (sort && ids) printf(" %6d", ct->clnt_id); /* print year and no time if it was long ago */ dcc_localtime(ct->last_used, &last); printf(" %s", dcc_time2str(date_buf, sizeof(date_buf), (last.tm_year != now.tm_year && (last.tm_mon < 6 || now.tm_mon > 2)) ? "%Y/%m/%d" : "%m/%d %X", ct->last_used)); if (!sort) printf(" %6d", ct->clnt_id); if (req_flags & DCC_AOP_CLIENTS_VERS) { if (ct->vers != 0) printf(" %d", ct->vers); else fputs(" ?", stdout); } if (ct->flags & DCC_ADMN_RESP_CLIENTS_BL) fputs(" BLACKLIST", stdout); else if (ct->flags & DCC_ADMN_RESP_CLIENTS_BAD) fputs(" BAD", stdout); if (!ids) { char name[DCC_MAXDOMAINLEN]; char sustr[DCC_SU2STR_SIZE]; if (nonames) { printf(" %s", dcc_su2str2(sustr, sizeof(sustr), &ct->su)); } else { printf(" %-16s %s", dcc_su2str2(sustr, sizeof(sustr), &ct->su), dcc_su2name(name, sizeof(name), &ct->su)); } } putchar('\n'); ctnew = ct->lt; if (!ctnew) { ctnew = ct->up; } else { ctnew->up = ct->up; } } memset(ct, 0, sizeof(*ct)); dcc_free(ct); } putchar('\n'); need_head = 1; for (i = 0; i < DIM(versions); ++i) { if (versions[i] == 0) continue; if (need_head) { need_head = 0; fputs("version total\n", stdout); } printf("%6d %8d\n", i, versions[i]); } return 1; } /* get and set the server's default anonymous client delay */ static int anon_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { int new_delay, old_delay, inflate; DCC_OPS result; char *inflate_str, *p; inflate = 0; if (*arg == '\0') { new_delay = DCC_NO_ANON_DELAY; } else { if (!strcasecmp(arg, "forever")) { new_delay = DCC_ANON_DELAY_FOREVER; } else { new_delay = strtoul(arg, &inflate_str, 10); if (new_delay > DCC_ANON_DELAY_MAX || (*inflate_str != '\0' && *inflate_str != ',' && *inflate_str != '*')) { dcc_error_msg("invalid delay: \"%s\"", arg); return 0; } if (*inflate_str != '\0') { ++inflate_str; inflate_str += strspn(inflate_str, DCC_WHITESPACE); } if (*inflate_str != '\0' && strcasecmp(inflate_str, "none")) { inflate = strtoul(inflate_str, &p, 10); if (*p != '\0') { dcc_error_msg("invalid delay inflation:" " \"%s\"", inflate_str); return 0; } } } if (!ck_cmd_priv(ce, 1, 0)) return 0; } if (!rdy_ctxt(0)) return 0; gettimeofday(&op_start, 0); result = dcc_aop(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0, NO_SRVR, clock_kludge, DCC_AOP_ANON_DELAY, inflate, new_delay>>8, new_delay, 0, 0, 0, &aop_resp, &op_result_su); if (result == DCC_OP_INVALID || result == DCC_OP_ERROR) { dcc_error_msg("%s", dcc_emsg); return 0; } old_delay = ((aop_resp.resp.val.anon_delay.delay[0]<<8) + aop_resp.resp.val.anon_delay.delay[1]); if (old_delay == DCC_ANON_DELAY_FOREVER) { printf(" anon delay %s FOREVER\n", new_delay != DCC_NO_ANON_DELAY ? "was" : "is"); } else { printf(" anon delay %s %d", new_delay != DCC_NO_ANON_DELAY ? "was" : "is", old_delay); inflate = ((aop_resp.resp.val.anon_delay.inflate[0]<<24) +(aop_resp.resp.val.anon_delay.inflate[1]<<16) +(aop_resp.resp.val.anon_delay.inflate[2]<<8) +aop_resp.resp.val.anon_delay.inflate[3]); if (inflate != 0) printf(",%d", inflate); putchar('\n'); } return 1; } /* rewind the flood from a single server */ static int flod_rewind(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { DCC_CLNT_ID id; if (!arg) return -1; id = dcc_get_id(0, arg, 0, 0); if (id == DCC_ID_INVALID) return -1; return do_aop(DCC_AOP_FLOD, id*256 + DCC_AOP_FLOD_REWIND, NO_SRVR, 1); } /* fast forward the flood to a single server */ static int ffwd_out(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { DCC_CLNT_ID id; if (!arg) return -1; id = dcc_get_id(0, arg, 0, 0); if (id == DCC_ID_INVALID) return -1; return do_aop(DCC_AOP_FLOD, id*256 + DCC_AOP_FLOD_FFWD_OUT, NO_SRVR, 1); } /* fast forward the flood to a single server */ static int ffwd_in(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { DCC_CLNT_ID id; if (!arg) return -1; id = dcc_get_id(0, arg, 0, 0); if (id == DCC_ID_INVALID) return -1; return do_aop(DCC_AOP_FLOD, id*256 + DCC_AOP_FLOD_FFWD_IN, NO_SRVR, 1); } /* get the flood counts for a server */ static int flod_stats(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { u_int32_t id, next_id; DCC_AOP_FLODS op; u_char heading; int sresult; if (!arg) return -1; if (!CLITCMP(arg, "clear")) { arg += LITZ("clear"); arg += strspn(arg, DCC_WHITESPACE); op = DCC_AOP_FLOD_STATS_CLEAR; } else { op = DCC_AOP_FLOD_STATS; } heading = 1; if (!strcasecmp(arg, "all")) { id = DCC_SRVR_ID_MAX+1; for (;;) { if (!start_aop(DCC_AOP_FLOD, id*256 + op, NO_SRVR)) return 0; sresult = sscanf(aop_resp.resp.val.string, DCC_AOP_FLOD_STATS_ID, &next_id); if (1 == sresult && id == next_id) { if (id == DCC_SRVR_ID_MAX+1) { BUFCPY(aop_resp.resp.val.string, " (no flooding peers)"); fin_aop(NO_SRVR, 1); } return 1; } fin_aop(NO_SRVR, heading); heading = 0; if (1 != sresult) return 0; id = next_id+DCC_SRVR_ID_MAX+1; } } id = dcc_get_id(0, arg, 0, 0); if (id == DCC_ID_INVALID) return -1; return do_aop(DCC_AOP_FLOD, id*256 + op, NO_SRVR, heading); } static const char *stats_help = ""; /* get the statistics from all known servers */ static int stats_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB) { DCC_SRVR_CLASS *class; SRVR_INX srvr_inx; int srvrs_gen; DCC_AOPS aop; /* look for "clear" or "all" */ srvr_inx = NO_SRVR; aop = DCC_AOP_STATS; while (*arg != 0) { arg += strspn(arg, " \t"); if (srvr_inx == NO_SRVR && !CLITCMP(arg, "clear")) { arg += LITZ("clear"); aop = DCC_AOP_STATS_CLEAR; if (!get_passwd(aops_tbl[aop].privileged)) { dcc_error_msg("\"stats clear\"" " is a privileged operation" PRV_MSG, ids_path); return 0; } } else if (aop == DCC_AOP_STATS && !CLITCMP(arg, "all")) { arg += LITZ("all"); srvr_inx = 0; } if (*arg != '\0' && *arg != ' ' && *arg != '\t') return -1; } if (!rdy_ctxt(0)) return 0; class = DCC_GREY2CLASS(grey_on); srvrs_gen = class->gen; do { if (srvrs_gen != class->gen) { dcc_error_msg("list of servers changed"); return 0; } /* skip dead servers */ if (srvr_inx != NO_SRVR && class->addrs[srvr_inx].srvr_id == DCC_ID_INVALID ) continue; do_aop(aop, sizeof(aop_resp.resp.val.string), srvr_inx, 1); fflush(stderr); fflush(stdout); } while (srvr_inx != NO_SRVR && ++srvr_inx < class->num_srvrs); return 1; } static int clock_ck_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { if (!rdy_ctxt(0)) return 0; do_aop(DCC_AOP_CLOCK_CHECK, sizeof(aop_resp.resp.val.string), NO_SRVR, 1); return 1; } /* restore tracing to default */ static int trace_def(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB) { if (!rdy_ctxt(0)) return 0; return (do_aop(DCC_AOP_TRACE_ON, DCC_TRACE_ON_DEF_BITS, NO_SRVR, 1) && do_aop(DCC_AOP_TRACE_OFF, DCC_TRACE_OFF_DEF_BITS, NO_SRVR, 1)); }