/* Distributed Checksum Clearinghouse * * dump list of dccd clients * * --C-LICENSE-- * $Revision: 1.44 $ */ #include "dccd_defs.h" static const char *homedir; u_char grey_on; static u_char nonames, quiet, verbose, avg; struct total { u_int clients; u_int black_clients; DCC_SCNTR reqs; DCC_SCNTR nops; DCC_SCNTR black_reqs; DCC_SCNTR black_nops; DCC_SCNTR reqs_avg; DCC_SCNTR nops_avg; DCC_SCNTR black_reqs_avg; DCC_SCNTR black_nops_avg; }; static void DCC_NORET usage(void) { fprintf(stderr, "usage: [-anqvg] [-h homedir] [ifile1 ifile2 ...]\n"); exit(1); } static void DCC_PF(1,2) error_msg(const char *p, ...) { va_list args; fflush(stdout); va_start(args, p); vfprintf(stderr, p, args); va_end(args); putchar('\n'); fflush(stderr); } static u_char clients_read(const char *fname, FILE *f, off_t pos, void *buf, int buf_len) { int i; if (fseeko(f, pos, 0)) { error_msg("fseeko(%s,%d): %s", fname, (int)pos, ERROR_STR()); fclose(f); return 0; } i = fread(buf, buf_len, 1, f); if (i == 1) return 1; if (feof(f)) return 0; error_msg("fread(%s): %s", fname, ERROR_STR()); fclose(f); return 0; } #define PAT_TOTAL_H1 " %11s" #define PAT_TOTAL_H1_AVG " %11s average" #define PAT_TOTAL_H2_AVG "version clients requests nops requests nops" #define PAT_TOTAL_H2 "version clients requests" #define PAT_TOTAL L_DWPAT(8) #define PAT_TOTALS_AVG PAT_TOTAL" "PAT_TOTAL" "PAT_TOTAL" "PAT_TOTAL #define PAT_TOTALS PAT_TOTAL" "PAT_TOTAL #define PAT_TOTAL_LABEL "%6d " #define PAT_TOTAL_LABEL_STR "%6s " static void print_total_heading(u_char blacklist) { const char *b_str; if (blacklist) b_str = "blacklist"; else b_str = ""; if (avg) { printf(PAT_TOTAL_H1_AVG"\n"PAT_TOTAL_H2_AVG"\n", b_str); } else { printf(PAT_TOTAL_H1"\n"PAT_TOTAL_H2"\n", b_str); } } static void print_total(struct total *tp, u_char blacklist) { if (blacklist) { if (avg) printf("%8d "PAT_TOTALS_AVG"\n", tp->black_clients, tp->black_reqs, tp->black_nops, tp->black_reqs_avg, tp->black_nops_avg); else printf("%8d "PAT_TOTALS"\n", tp->black_clients, tp->black_reqs, tp->black_nops); } else { if (avg) printf("%8d "PAT_TOTALS_AVG"\n", tp->clients, tp->reqs, tp->nops, tp->reqs_avg, tp->nops_avg); else printf("%8d "PAT_TOTALS"\n", tp->clients, tp->reqs, tp->nops); } } static void dump_file(const char *fname0) { # define PAT_REQS L_DWPAT(6)" "L_DWPAT(6)" " # define PAT_REQS_B "%6s %6s " # define PAT_AVGS "%7u %7u %2d " # define PAT_AVGS_B "%15s %3s" # define PAT_DATE "%-14s" # define PAT_ID " %6u " # define PAT_ID_STR " %6s " # define PAT_VERS "%2d " # define PAT_VERS_B "%2s " # define PAT_DELAY "%-5s " DCC_PATH fname; FILE *f; struct stat sb; CLIENTS_HEADER header; RL_DATA d; struct total total_x, totals[DCC_PKT_VERS_NOP_MAX+1]; struct tm tm; char date_buf[40]; char date_buf2[40]; double delay_us; DCC_SOCKU su; char name[DCC_MAXDOMAINLEN]; char sustr[DCC_SU2STR_SIZE]; char flags[30]; off_t pos; int total_clients, active_clients, i; if (!dcc_fnm2abs(&fname, fname0, 0)) { error_msg("name \"%s\" too long", fname0); exit(1); } printf("%s\n", fname.c); f = fopen(fname.c, "r"); if (!f) { error_msg("stat(%s): %s", fname.c, ERROR_STR()); exit(1); } if (0 > fstat(fileno(f), &sb)) { error_msg("stat(%s): %s", fname.c, ERROR_STR()); exit(1); } pos = sb.st_size; if (pos < ISZ(header) || ((pos - ISZ(header)) % ISZ(RL_DATA)) != 0) { error_msg("%s has invalid size %d", fname.c, (int)pos); exit(1); } if (!clients_read(fname.c, f, 0, &header, sizeof(header))) exit(1); if (strcmp(header.magic, CLIENTS_MAGIC(0)) && strcmp(header.magic, CLIENTS_MAGIC(1))) { error_msg("unrecognized magic in %s", fname.c); exit(1); } if (header.blocks > DCC_RL_MAX_MAX) { error_msg("unrecognized blocks=%d in %s", header.blocks, fname.c); exit(1); } if ((int)(header.blocks*sizeof(RL_DATA) + sizeof(header)) != (int)pos) { error_msg("%s has wrong size of %d instead of %d", fname.c, (int)(header.blocks*sizeof(RL_DATA) + sizeof(header)), (int)pos); exit(1); } DCC_GMTIME(header.now, &tm); strftime(date_buf, sizeof(date_buf), "%m/%d %X", &tm); DCC_GMTIME(header.cleared, &tm); strftime(date_buf2, sizeof(date_buf2), "%m/%d %X", &tm); printf("recorded %s cleared %s anon delay=", date_buf, date_buf2); if (header.anon_delay_us == DCC_ANON_DELAY_FOREVER) { printf("forever"); } else { printf("%d", header.anon_delay_us/1000); if (header.anon_delay_inflate != DCC_ANON_INFLATE_OFF) printf(",%d", header.anon_delay_inflate); } putchar('\n'); if (avg) { if (verbose) printf(PAT_REQS_B PAT_AVGS_B PAT_DATE" " PAT_DATE PAT_ID_STR PAT_VERS_B PAT_DELAY "\n", "ops", "nops", " averages ", "hrs", " block used", " last seen", "ID", "V", "delay"); else printf(PAT_REQS_B PAT_AVGS_B PAT_DATE PAT_ID_STR PAT_VERS_B PAT_DELAY "\n", "ops", "nops", " averages ", "hrs", " last seen", "ID", "V", "delay"); } else { if (verbose) printf(PAT_REQS_B PAT_DATE" " PAT_DATE PAT_ID_STR PAT_VERS_B PAT_DELAY "\n", "ops", "nops", " block used", " last seen", "ID", "V", "delay"); else printf(PAT_REQS_B PAT_DATE PAT_ID_STR PAT_VERS_B PAT_DELAY "\n", "ops", "nops", " last seen", "ID", "V", "delay"); } total_clients = 0; active_clients = 0; memset(totals, 0, sizeof(totals)); /* read the file backwards because it was written that way */ while ((pos -= ISZ(RL_DATA)) >= ISZ(header)) { if (!clients_read(fname.c, f, pos, &d, sizeof(d))) break; ++total_clients; if (d.reqs != 0 || d.nops != 0) ++active_clients; if (!(d.fgs & RL_FG_BLOCK) && d.pkt_vers != DCC_PKT_VERS_INVALID && d.pkt_vers != DCC_PKT_VERS_MULTI && (d.reqs != 0 || d.nops != 0 || (avg && (d.reqs_avg != 0 || d.nops_avg != 0)))) { if (d.pkt_vers < DIM(totals)) i = d.pkt_vers; else i = 0; ++totals[i].clients; if (d.fgs & (RL_FG_BLACK_ID | RL_FG_BLACK_ADDR)) { ++totals[i].black_clients; totals[i].black_reqs += d.reqs; totals[i].black_nops += d.nops; if (avg) { totals[i].black_reqs_avg += d.reqs_avg; totals[i].black_nops_avg += d.nops_avg; } } else { ++totals[i].clients; totals[i].reqs += d.reqs; totals[i].nops += d.nops; if (avg) { totals[i].reqs_avg += d.reqs_avg; totals[i].nops_avg += d.nops_avg; } } } if (quiet) continue; if (d.reqs != 0) { printf(PAT_REQS, d.reqs, d.nops); } else { printf(PAT_REQS_B, "", ""); } if (avg) { if (d.reqs_avg != 0 || d.nops_avg != 0) { printf(PAT_AVGS, d.reqs_avg, d.nops_avg, (int)((header.now - d.avg_start) / (60*60*1.0))); } else { printf(PAT_AVGS_B, "", ""); } } if (verbose) { if (d.fgs & RL_FG_BLOCK) { DCC_GMTIME(d.u.block_used, &tm); strftime(date_buf, sizeof(date_buf), "%m/%d %X", &tm); printf(PAT_DATE" ", date_buf); } else { printf(PAT_DATE" ", ""); } } DCC_GMTIME(d.last_used, &tm); strftime(date_buf, sizeof(date_buf), "%m/%d %X", &tm); printf(PAT_DATE, date_buf); if (d.id == DCC_ID_ANON) printf(PAT_ID_STR, ""); else if (d.id == DCC_ID_CLIENTS_MULT) printf(PAT_ID_STR, "mult"); else if (d.id == DCC_ID_SRVR_ROGUE) printf(PAT_ID_STR, "server"); else printf(PAT_ID, d.id); if (d.pkt_vers == DCC_PKT_VERS_MULTI) printf(PAT_VERS_B, "m"); else if (d.pkt_vers == DCC_PKT_VERS_INVALID) printf(PAT_VERS_B, ""); else printf(PAT_VERS, d.pkt_vers); flags[0] = '\0'; if ((d.fgs & RL_FG_NO_AUTH) && !(d.fgs & RL_FG_BLACK_ADDR) && header.anon_delay_inflate != 0 && RL_REQS_AVG(&d) >= header.anon_delay_inflate) { u_int32_t inflate; inflate = 1 + (RL_REQS_AVG(&d) / header.anon_delay_inflate); if (inflate > (DCC_ANON_DELAY_MAX / header.anon_delay_us)) { delay_us = DCC_ANON_DELAY_MAX; } else { delay_us = inflate*header.anon_delay_us; } i = strlen(flags); snprintf(flags+i, sizeof(flags)-i, "%2.1f", delay_us/DCC_US); } if (d.fgs & RL_FG_BLACK_ADDR) STRLCAT(flags, " A-BL", sizeof(flags)); if (d.fgs & RL_FG_BLACK_ID) STRLCAT(flags, " ID-BL", sizeof(flags)); if (d.fgs & RL_FG_BLOCK_BLACK) STRLCAT(flags, " BB", sizeof(flags)); if (d.fgs & RL_FG_DROPPED) STRLCAT(flags, " BAD", sizeof(flags)); #if 0 if (verbose) { if (d.fgs & RL_FG_BLOCK) { if (!(d.fgs & RL_FG_BLOCK_SET)) STRLCAT(flags, " UNSET", sizeof(flags)); } else { if (!(d.fgs & RL_FG_BLACK_SET)) STRLCAT(flags, " UNSET", sizeof(flags)); } } #endif printf(PAT_DELAY, flags); dcc_mk_inet6_su(&su, &d.addr, 0, 0); dcc_ipv6su2ipv4su(&su, &su); dcc_su2str2(sustr, sizeof(sustr), &su); if (d.fgs & RL_FG_BLOCK) { printf("%s/%d\n", sustr, su.sa.sa_family == AF_INET ? DCC_ADMN_RESP_CLIENTS_IPV4_BITS : DCC_ADMN_RESP_CLIENTS_IPV6_BITS); } else if (nonames) { printf("%s\n", sustr); } else { printf("%-16s %s\n", sustr, dcc_su2name(name, sizeof(name), &su)); } } fclose(f); printf("%d of %d records active\n\n", active_clients, total_clients); print_total_heading(0); memset(&total_x, 0, sizeof(total_x)); for (i = 0; i < DIM(totals); ++i) { if (totals[i].clients == 0 && totals[i].black_clients == 0) continue; total_x.clients += totals[i].clients; total_x.reqs += totals[i].reqs; total_x.nops += totals[i].nops; total_x.reqs_avg += totals[i].reqs_avg; total_x.nops_avg += totals[i].nops_avg; printf(PAT_TOTAL_LABEL, i); print_total(&totals[i], 0); } printf(PAT_TOTAL_LABEL_STR, "total"); print_total(&total_x, 0); fputs("\n", stdout); print_total_heading(1); memset(&total_x, 0, sizeof(total_x)); for (i = 0; i < DIM(totals); ++i) { if (totals[i].clients == 0 && totals[i].black_clients == 0) continue; total_x.black_clients += totals[i].black_clients; total_x.black_reqs += totals[i].black_reqs; total_x.black_nops += totals[i].black_nops; total_x.black_reqs_avg += totals[i].black_reqs_avg; total_x.black_nops_avg += totals[i].black_nops_avg; printf(PAT_TOTAL_LABEL, i); print_total(&totals[i], 1); } printf(PAT_TOTAL_LABEL_STR, "total"); print_total(&total_x, 1); printf("\n pass bad password\n"); printf(" ID unknown ID\n"); printf(" A-BL address blacklist\n"); printf(" ID-BL ID blacklist\n"); printf(" BAD too many requests or otherwise bad\n"); } int main(int argc, char **argv) { int i; avg = 0; nonames = 0; quiet = 0; while ((i = getopt(argc, argv, "anvqgh:")) != -1) { switch (i) { case 'a': ++avg; break; case 'n': nonames = 1; break; case 'q': quiet = 1; break; case 'v': verbose = 1; break; case 'g': grey_on = 1; break; case 'h': homedir = optarg; break; default: usage(); } } argc -= optind; argv += optind; if (verbose && quiet) { fprintf(stderr, "-q and -v are incompatible\n"); exit(1); } if (!dcc_cdhome(0, homedir, 1) && homedir) dcc_cdhome(0, homedir = 0, 1); if (argc == 0) { dump_file(CLIENTS_NM()); } else { for (i = 0; i < argc; ++i) { dump_file(argv[i]); } } exit(0); }