Mercurial > notdcc
diff dblist/dblist.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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dblist/dblist.c Tue Mar 10 13:49:58 2009 +0100 @@ -0,0 +1,1521 @@ +/* Distributed Checksum Clearinghouse + * + * database lister + * + * 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.137 $Revision$ + */ + +#include "srvr_defs.h" +#include "dcc_xhdr.h" +#include "dcc_ck.h" +#include <signal.h> +#if HAVE_BOOTTIME +#include <sys/sysctl.h> +#endif + +static DCC_EMSG dcc_emsg; + +static int verbose; +#define VERBOSE_HASH 3 +static u_char no_hash; +static u_char no_data; +static u_char matching; + +static DCC_CLNT_CTXT *ctxt; +static DCC_OP_RESP aop_resp; +static DCC_SRVR_NM srvr; +static const ID_TBL *srvr_clnt_tbl; + +static struct { + DCC_CK_TYPES type; + DCC_SUM sum; + u_char type_only; +} search_cksums[16]; +static int num_search_cksums; + +static struct { + DCC_TS lo; + DCC_TS hi; +} search_ts[16]; +static int num_search_ts; + +DCC_SRVR_ID search_ids[16]; +static int num_search_ids; + +static DB_PTR page_offset; +static DB_PTR dbaddr; +static int max_pathlen; + +static DB_HOFF hash_fsize; +static char dcc_db_nm[] = DB_DCC_NAME; +static char grey_db_nm[] = DB_GREY_NAME; +static DCC_PATH hash_nm; +static char *def_argv[2]; +static const char *homedir; + +static const DB_VERSION_BUF version_buf = DB_VERSION_STR; +static const u_char hash_magic[sizeof(((HASH_CTL*)0)->s.magic) + ] = HASH_MAGIC_STR; + +static void rel_db(void); +static void sigterm(int); +static int save_cksum(DCC_EMSG, DCC_WF *, DCC_CK_TYPES, DCC_SUM, DCC_TGTS); +static void list_cleaned(const DB_PARMS *); +static void list_flod(void); +static int fd_hash = -1; +static int fd_db = -1; +static struct stat hash_sb, db_sb; +static void list_db(void); +static u_char open_db(void); +static void open_hash(void); +static void list_hash(void); + + +static void NRATTRIB +usage(void) +{ + dcc_logbad(EX_USAGE, "usage: [-vVHD] [-G on | off] [-h homedir]\n" + " [-s server-ID[,server-addr][,server-port]]\n" + " [-C '[type] [h1 h2 h3 h4]'] [-I server-Id] [-A dbptr]" + " [-L pathlen]\n" + " [-P pages] [-T timestamp] [file file2 ...]"); +} + + + +int NRATTRIB +main(int argc, char **argv) +{ + u_char print_version = 0; + char hostname[DCC_MAXDOMAINLEN]; + int file_num; + DCC_CK_TYPES type; + char tbuf[80]; + const char *cp, *cp0; + struct timeval tv1, tv2; + int us; + struct tm tm; + char *p; + u_long l; + int i; + + dcc_syslog_init(0, argv[0], 0); + + while ((i = getopt(argc, argv, "vVHDG:h:s:C:I:A:L:P:T:")) != -1) { + switch (i) { + case 'v': + ++verbose; + break; + + case 'V': + fprintf(stderr, DCC_VERSION"\n"); + print_version = 1; + break; + + case 'G': + if (!strcasecmp(optarg, "on")) { + grey_on = 1; + } else if (!strcasecmp(optarg, "off")) { + grey_on = 0; + } else { + usage(); + } + break; + + case 'h': + homedir = optarg; + break; + + case 's': + l = strtoul(optarg, &p, 10); + if ((*p != '\0' && *p != ',') + || l < DCC_SRVR_ID_MIN + || l > DCC_SRVR_ID_MAX) + dcc_logbad(EX_USAGE, "invalid DCC ID \"-s %s\"", + optarg); + srvr.clnt_id = l; + if (*p != '\0') { + ++p; + p += strspn(p, DCC_WHITESPACE); + } + hostname[0] = '\0'; + srvr.port = 0; + if (*p == '\0') + break; + cp = dcc_parse_nm_port(dcc_emsg, p, srvr.port, + hostname, sizeof(hostname), + &srvr.port, 0, 0, 0, 0); + if (!cp) + dcc_logbad(EX_USAGE, "%s", dcc_emsg); + cp += strspn(cp, DCC_WHITESPACE); + if (*cp != '\0') + dcc_logbad(EX_USAGE, + "unrecognized port number in" + "\"-s %s\"", optarg); + if (hostname[0] != '\0') + BUFCPY(srvr.hostname, hostname); + break; + + case 'H': + no_hash = 1; + break; + + case 'D': + no_data = 1; + break; + + case 'C': + if (num_search_cksums >= DIM(search_cksums)) { + dcc_error_msg("too many -C checksums"); + break; + } + matching = 1; + cp0 = optarg; + cp = dcc_parse_word(0, tbuf, sizeof(tbuf), + optarg, "checksum type", 0, 0); + if (!cp) + exit(1); + if (!strcasecmp(tbuf, "hex")) { + /* ignore "hex" */ + cp0 = cp; + cp = dcc_parse_word(0, tbuf, sizeof(tbuf), + cp, "checksum type", + 0, 0); + if (!cp) + dcc_logbad(EX_USAGE, + "unrecognized checksum" + " \"-C %s\"", optarg); + } + if (*cp == '\0') { + /* allow bare checksum type */ + type = dcc_str2type_del(tbuf, -1); + if (type == DCC_CK_INVALID) + dcc_logbad(EX_USAGE, + "unrecognized checksum type" + " \"-C %s\"", optarg); + search_cksums[num_search_cksums].type = type; + memset(search_cksums[num_search_cksums].sum, 0, + sizeof(DCC_SUM)); + search_cksums[num_search_cksums].type_only = 1; + ++num_search_cksums; + break; + } + /* allow missing checksum type */ + strtoul(tbuf, &p, 16); + if (*p == '\0') { + if (0 >= dcc_parse_hex_ck(dcc_emsg, 0, + "-", DCC_CK_FLOD_PATH, + cp0, 0, save_cksum)) + dcc_logbad(EX_USAGE, "%s", dcc_emsg); + } else { + type = dcc_str2type_del(tbuf, -1); + if (type == DCC_CK_FLOD_PATH) + dcc_logbad(EX_USAGE, + "unrecognized checksum type" + " \"-C %s\"", optarg); + if (0 >= dcc_parse_hex_ck(dcc_emsg, 0, + tbuf, type, + cp, 0, save_cksum)) + dcc_logbad(EX_USAGE, "%s", dcc_emsg); + } + break; + + case 'I': + if (num_search_ids >= DIM(search_ids)) { + dcc_error_msg("too many -I IDs"); + break; + } + search_ids[num_search_ids] = strtoul(optarg, &p, 10); + if (search_ids[num_search_ids] > DCC_SRVR_ID_MAX + || *p != '\0') + dcc_logbad(EX_USAGE, + "invalid server-ID \"-I %s\"", + optarg); + ++num_search_ids; + matching = 1; + break; + + case 'A': + dbaddr = strtoul(optarg, &p, 16); + if (*p != '\0') + dcc_logbad(EX_USAGE, + "invalid database address \"%s\"", + optarg); + matching = 1; + break; + + case 'L': + max_pathlen = strtoul(optarg, &p, 10); + if (*p != '\0') + dcc_logbad(EX_USAGE, + "invalid path length \"%s\"", + optarg); + matching = 1; + break; + + case 'P': + page_offset = strtoul(optarg, &p, 10); + if (*p != '\0') + dcc_logbad(EX_USAGE, + "invalid number of pages \"%s\"", + optarg); + matching = 1; + break; + + case 'T': + if (num_search_ts >= DIM(search_ts)) { + dcc_error_msg("too many -T timestamps"); + break; + } + memset(&tm, 0, sizeof(tm)); + i = sscanf(optarg, "%d/%d/%d %d:%d:%d.%d%c", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec, + &us, tbuf); + if (i < 6 || i > 7 + || tm.tm_mon <= 0) + dcc_logbad(EX_USAGE,"bad timestamp \"%s\"", + optarg); + --tm.tm_mon; + tm.tm_year += 100; + tv1.tv_sec = DCC_TIMEGM(&tm); + if (tv1.tv_sec < 0) + dcc_logbad(EX_USAGE, "invalid timestamp \"%s\"", + optarg); + tv2.tv_sec = tv1.tv_sec; + if (i == 7) { + if (us >= DCC_US) + dcc_logbad(EX_USAGE, + "invalid microseconds" + " in \"%s\"", + optarg); + tv1.tv_usec = us; + tv2.tv_usec = us; + } else { + tv1.tv_usec = 0; + tv2.tv_usec = DCC_US-1; + } + dcc_timeval2ts(&search_ts[num_search_ts].lo, &tv1, 0); + dcc_timeval2ts(&search_ts[num_search_ts].hi, &tv2, 0); + ++num_search_ts; + matching = 1; + break; + + default: + usage(); + } + } + argc -= optind; + argv += optind; + def_argv[0] = grey_on ? grey_db_nm : dcc_db_nm; + if (argc == 0) { + if (print_version) + exit(EX_OK); + argv = def_argv; + argc = 1; + } + + dcc_clnt_unthread_init(); + if (!dcc_cdhome(dcc_emsg, homedir, 1)) + dcc_logbad(dcc_ex_code, "%s", dcc_emsg); + + flod_mmap_path_set(); + + if (matching) { + if (no_data && no_hash) + dcc_logbad(EX_USAGE, + "patterns need data or hash table"); + if (!no_data && !no_hash) + no_hash = 1; + } + + if (dbaddr != 0 && page_offset != 0) + dcc_logbad(EX_USAGE, "-P and -A are incompatible"); + + if (srvr.clnt_id != 0) { + if (argc != 1) + dcc_logbad(EX_USAGE, "lock only one file"); + + i = load_ids(dcc_emsg, srvr.clnt_id, &srvr_clnt_tbl, 1); + if (i <= 0) + dcc_logbad(dcc_ex_code, "%s", dcc_emsg); + memcpy(srvr.passwd, srvr_clnt_tbl->cur_passwd, + sizeof(srvr.passwd)); + if (hostname[0] == '\0') + strcpy(srvr.hostname, DCC_SRVR_NM_DEF_HOST); + if (srvr.port == 0) + srvr.port = DCC_GREY2PORT(grey_on); + + i = DCC_CLNT_FG_SLOW; + if (grey_on) + i |= DCC_CLNT_FG_GREY; + ctxt = dcc_tmp_clnt_init(dcc_emsg, 0, &srvr, 0, i, 0); + if (!ctxt) + dcc_logbad(dcc_ex_code, "%s", dcc_emsg); + if (!lock_dbclean(dcc_emsg, *argv)) + dcc_logbad(dcc_ex_code, "%s: dbclean running?", + dcc_emsg); + + atexit(rel_db); + signal(SIGALRM, sigterm); + signal(SIGHUP, sigterm); + signal(SIGTERM, sigterm); + signal(SIGINT, sigterm); + if (!dcc_aop_persist(dcc_emsg, ctxt, + grey_on ? DCC_CLNT_FG_GREY : 0, + verbose != 0, + DCC_AOP_DB_UNLOAD, 0, 60*5, &aop_resp)) + dcc_logbad(dcc_ex_code, "%s", dcc_emsg); + } + + for (file_num = 1; *argv != 0; ++argv, ++file_num) { + if (fd_db >= 0) + close(fd_db); + if (fd_hash >= 0) + close(fd_hash); + + BUFCPY(db_nm, *argv); + snprintf(hash_nm, sizeof(hash_nm), "%s"DB_HASH_SUFFIX, db_nm); + + if (file_num != 1) + fputc('\n', stdout); + if (verbose || argc > 1) + printf(" %s\n", db_nm); + + /* try to open the hash table and the database + * fail only if we cannot open the database */ + open_hash(); + if (!open_db()) + continue; + + /* print the header of the database followed by its contents */ + list_db(); + list_hash(); + } + + exit(EX_OK); +} + + + +static void +rel_db(void) +{ + if (!ctxt) + return; + if (!dcc_aop_persist(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0, + 1, DCC_AOP_DB_UNLOAD, 1, 60*5, &aop_resp)) + dcc_error_msg("%s", dcc_emsg); + unlock_dbclean(); + ctxt = 0; +} + + + +static void +sigterm(int sig UATTRIB) +{ + rel_db(); +} + + + +static int +save_cksum(DCC_EMSG emsg UATTRIB, DCC_WF *wf UATTRIB, + DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts UATTRIB) +{ + search_cksums[num_search_cksums].type = type; + memcpy(search_cksums[num_search_cksums].sum, sum, sizeof(DCC_SUM)); + search_cksums[num_search_cksums].type_only = 0; + ++num_search_cksums; + return 1; +} + + + +#define RCD_PAT "%-27s %-8.8s %-10.10s %7s "L_HWPAT(8)"\n" +#define RCD_PAT1(s) RCD_PAT, s, "", "", "" + +static DB_HDR hdr_buf; + +static enum {NO_LB, /* no label */ + WHITE_LB, /* whitelist section labelled */ + DATE_LB /* normal section labelled */ +} last_lb = NO_LB; +static u_char printed_rcd; +static int rcds, white_rcds, sums, white_sums; + + +static u_char +open_db(void) +{ + int i; + + fd_db = open(db_nm, O_RDONLY, 0); + if (fd_db < 0) { + dcc_error_msg("open(%s): %s", db_nm, ERROR_STR()); + return 0; + } + + i = read_db(dcc_emsg, &hdr_buf, sizeof(hdr_buf), fd_db, 0, db_nm); + if (i != sizeof(hdr_buf)) { + if (i < 0) + dcc_error_msg("%s", dcc_emsg); + else + dcc_error_msg("found only %d bytes of magic in %s", + i, db_nm); + return 0; + } + + if (memcmp(hdr_buf.p.version, version_buf, + sizeof(hdr_buf.p.version))) { + dcc_error_msg("%s contains the wrong magic \"%.*s\"", + db_nm, ISZ(version_buf), hdr_buf.p.version); + } + if (0 > fstat(fd_db, &db_sb)) { + dcc_error_msg("stat(%s): %s", db_nm, ERROR_STR()); + return 0; + } + + if (db_sb.st_size == sizeof(hdr_buf)) { + dcc_error_msg("%s contains no checksums",db_nm); + return 0; + } + + if ((DB_PTR)db_sb.st_size < hdr_buf.p.db_csize) { + dcc_error_msg("%s says it contains "L_DPAT + " bytes instead of "OFF_DPAT, + db_nm, hdr_buf.p.db_csize, db_sb.st_size); + } + + db_pagesize = hdr_buf.p.pagesize; + db_hash_page_len = db_pagesize/sizeof(HASH_ENTRY); + + return 1; +} + + + +static void +list_db_entry(DB_PTR rcd_link, const DB_RCD *rcd) +{ + const DB_RCD_CK *rcd_ck; + DB_PTR rcd_prev; + DCC_TGTS tgts; + DCC_CK_TYPES type; + char ts_buf[40], id_buf[30]; + char tgts_buf[20]; + char ck_buf[sizeof(DCC_SUM)*3+2]; + u_char rpt_match, kept; + int i; + + /* usually skip padding */ + if (rcd->fgs_num_cks == 0) { + if (verbose > 1) { + printf(RCD_PAT1(" page padding"), rcd_link); + printed_rcd = 1; + } + return; + } + + rpt_match = 0; + + /* skip until the desired first address */ + if (dbaddr != 0) { + if (rcd_link < dbaddr) + return; + rpt_match = 1; + } + + /* if we have target server-IDs, display only their reports */ + if (num_search_ids > 0) { + for (i = 0; i < num_search_ids; ++i) { + if (search_ids[i] == DB_RCD_ID(rcd)) { + rpt_match = 1; + goto got_id; + } + } + return; +got_id:; + } + + /* if we have target checksums, display only reports containing them */ + if (num_search_cksums > 0) { + for (i = 0; i < num_search_cksums; ++i) { + for (rcd_ck = rcd->cks; + rcd_ck < &rcd->cks[DB_NUM_CKS(rcd)]; + ++rcd_ck) { + type = search_cksums[i].type; + if ((DB_CK_TYPE(rcd_ck) == type + || type == DCC_CK_FLOD_PATH) + && (search_cksums[i].type_only + || !memcmp(search_cksums[i].sum, + rcd_ck->sum, + sizeof(DCC_SUM)))) { + rpt_match = 1; + goto got_ck; + } + } + } + return; +got_ck:; + } + + if (num_search_ts > 0 + && DB_RCD_ID(rcd) != DCC_ID_WHITE) { + for (i = 0; i < num_search_ts; ++i) { + if (!dcc_ts_older_ts(&rcd->ts, + &search_ts[i].lo) + && !dcc_ts_newer_ts(&rcd->ts, + &search_ts[i].hi)) { + rpt_match = 1; + goto got_ts; + } + } + return; +got_ts:; + } + + if (max_pathlen != 0 + && DB_RCD_ID(rcd) != DCC_ID_WHITE) { + DCC_FLOD_PATH_ID *id; + DCC_SRVR_ID psrvr; + int pathlen = 0; + + for (rcd_ck = rcd->cks; + rcd_ck < &rcd->cks[DB_NUM_CKS(rcd)] + && pathlen < max_pathlen; + ++rcd_ck) { + if (DB_CK_TYPE(rcd_ck) != DCC_CK_FLOD_PATH) + break; + id = (DCC_FLOD_PATH_ID *)&rcd_ck->sum; + for (i = 0; i < DCC_NUM_FLOD_PATH; ++i, ++id) { + psrvr = ((id->hi<<8) | id->lo); + if (psrvr == DCC_ID_INVALID) + break; + ++pathlen; + } + } + if (pathlen < max_pathlen) + return; + rpt_match = 1; + } + + ++rcds; + if (DB_RCD_ID(rcd) == DCC_ID_WHITE) { + ++white_rcds; + if (last_lb != WHITE_LB) { + last_lb = WHITE_LB; + strcpy(ts_buf, "\n"DCC_XHDR_ID_WHITE); + } else { + ts_buf[0] = '\0'; + } + } else { + if (last_lb != DATE_LB) { + last_lb = DATE_LB; + if (rpt_match || verbose > 0) + putchar('\n'); + } + if (rpt_match || verbose > 0) + ts2str(ts_buf, sizeof(ts_buf), &rcd->ts); + } + + /* display separator between whitelist and ordinary entries + * along with the timestamp and the rest of the first line + * of a report */ + if (rpt_match + || verbose >= 2 + || (verbose > 0 && DB_RCD_ID(rcd) != DCC_ID_WHITE)) { + if (last_lb == DATE_LB) { + tgts = DB_TGTS_RCD_RAW(rcd); + printf(RCD_PAT, ts_buf, + (tgts == 0) + ? "deleted" + : dcc_tgts2str(tgts_buf, sizeof(tgts_buf), + tgts, grey_on), + id2str(id_buf, sizeof(id_buf), + rcd->srvr_id_auth), + DB_RCD_TRIMMED(rcd) ? "trimmed" + : DB_RCD_SUMRY(rcd) ? "summary" + : DB_RCD_DELAY(rcd) ? "delayed" + : "", + rcd_link); + } else { + printf(RCD_PAT1(ts_buf), rcd_link); + } + printed_rcd = 1; + } + + /* display a report */ + for (rcd_ck = rcd->cks; + rcd_ck < &rcd->cks[DB_NUM_CKS(rcd)]; + ++rcd_ck) { + ++sums; + /* always count whitelist entries, + * but display only as requested */ + if (DB_RCD_ID(rcd) == DCC_ID_WHITE) { + ++white_sums; + if (verbose < 2 && !rpt_match) + continue; + } else { + if (verbose < 1 && !rpt_match) + continue; + } + + /* decode the special checksum that is a path */ + if (DB_CK_TYPE(rcd_ck)== DCC_CK_FLOD_PATH) { + if (DB_RCD_ID(rcd) == DCC_ID_WHITE) { + int lno, fno; + memcpy(&lno, rcd_ck->sum, sizeof(lno)); + fno = rcd_ck->sum[sizeof(lno)]; + if (fno == 0) { + printf(" line #%d\n", lno); + } else { + printf(" line #%d" + " included file #%d\n", + lno, fno); + } + + } else { + DCC_SRVR_ID psrvr; + DCC_FLOD_PATH_ID *path_id, *path_id_lim; + const char *s; + + path_id=(DCC_FLOD_PATH_ID *)rcd_ck->sum; + path_id_lim = path_id+DCC_NUM_FLOD_PATH; + s = " path: "; + do { + psrvr = ((path_id->hi<<8) + | path_id->lo); + if (psrvr == DCC_ID_INVALID) + break; + printf("%s%d", s, psrvr); + s = "<-"; + } while (++path_id < path_id_lim); + printf("%s\n", s); + } + continue; + } + + kept = (!DB_TEST_NOKEEP(hdr_buf.p.nokeep_cks, + DB_CK_TYPE(rcd_ck)) + || DB_RCD_ID(rcd) == DCC_ID_WHITE); + + printf(" %c%-12.12s %-10.10s %-31s", + DB_CK_OBS(rcd_ck) ? '*' : ' ', + DB_TYPE2STR(DB_CK_TYPE(rcd_ck)), + !kept + ? "" : dcc_tgts2str(tgts_buf, sizeof(tgts_buf), + DB_TGTS_CK(rcd_ck), grey_on), + dcc_ck2str(ck_buf, sizeof(ck_buf), + DB_CK_TYPE(rcd_ck), rcd_ck->sum, + DB_RCD_ID(rcd))); + rcd_prev = DB_PTR_EX(rcd_ck->prev); + if (rcd_prev == DB_PTR_NULL) + printf(" %8s", ""); + else if (DB_PTR_IS_BAD(rcd_prev)) + printf(" bogus "L_HWPAT(8), rcd_prev); + else + printf(" "L_HWPAT(8), rcd_prev); + if (db_hash_len != 0 + && kept) + printf(" %x", db_hash(DB_CK_TYPE(rcd_ck), rcd_ck->sum)); + putchar('\n'); + } +} + + + +static void +list_db(void) +{ + DB_RCD rcd; + int rcd_len; + DB_PTR rcd_lim, rcd_link; + + if (fd_db < 0) + return; + + /* print the header of the database */ + if (verbose > 0) { + list_cleaned(&hdr_buf.p); + list_flod(); + } + + if (no_data) + return; + + last_lb = NO_LB; + printed_rcd = 0; + rcds = 0; + white_rcds = 0; + sums = 0; + white_sums = 0; + + /* list the records in the database */ + if (dbaddr != 0) { + if ((DB_PTR)db_sb.st_size <= dbaddr) { + page_offset = 0; + } else { + page_offset = ((db_sb.st_size - dbaddr + db_pagesize -1) + / db_pagesize); + } + } + if (page_offset == 0) { + rcd_link = DB_PTR_BASE; + } else { + rcd_link = db_sb.st_size / hdr_buf.p.pagesize; + if (rcd_link < page_offset) + rcd_link = 0; + else + rcd_link -= page_offset; + rcd_link *= hdr_buf.p.pagesize; + if (rcd_link < DB_PTR_BASE) + rcd_link = DB_PTR_BASE; + } + rcd_lim = ((verbose > 2) + ? (DB_PTR)db_sb.st_size : hdr_buf.p.db_csize); + read_rcd_invalidate(0); + while (rcd_link < rcd_lim) { + rcd_len = read_rcd(dcc_emsg, &rcd, fd_db, rcd_link, db_nm); + if (rcd_len <= 0) { + if (rcd_len == 0) + break; + /* ignore fragmentary reports at the end */ + if (rcd_link > hdr_buf.p.db_csize - DB_RCD_HDR_LEN) { + printf(RCD_PAT1(" page padding"), rcd_link); + printed_rcd = 1; + break; + } + dcc_error_msg("%s", dcc_emsg); + read_rcd_invalidate(0); + return; + } + + + list_db_entry(rcd_link, &rcd); + rcd_link += rcd_len; + } + + if (verbose || matching) { + /* print address after the last record, + * but only if we printed a record */ + if (printed_rcd) + printf(RCD_PAT1(""), rcd_link); + putchar('\n'); + } + if (!matching) { + printf("%7d records containing %d checksums\n", + rcds, sums); + if (!grey_on && rcds != white_rcds) + printf("%7d non-whitelist records containing" + " %d checksums\n", + rcds-white_rcds, sums-white_sums); + } + read_rcd_invalidate(0); +} + + + +static const char * +print_rate(char *buf, u_int buf_len, + const DB_PARMS *parms, u_char hash_or_db) +{ + double rate; + + rate = db_add_rate(parms, hash_or_db); + + if (rate <= 0.0) + return "?"; + + return size2str(buf, buf_len, rate * (24*60*60*1.0), !hash_or_db); +} + + + +static const char * +secs2str(char *buf, u_int buf_len, u_int32_t secs) +{ + int days, minutes, hours; + + days = secs / (24*60*60); + secs %= (24*60*60); + hours = secs / (60*60); + secs %= (60*60); + minutes = secs / 60; + secs %= 60; + + if (hours == 0 && minutes == 0 + && (secs == 0 || (days != 0 && secs < 15 && verbose < 3))) { + snprintf(buf, buf_len, "%d day%s", + days, (days > 1) ? "s" : " "); + return buf; + } + + if (days == 0 && minutes == 0 && secs == 0) { + snprintf(buf, buf_len, "%d hour%s", + hours, (hours > 1) ? "s" : " "); + return buf; + } + + if (days == 0 && hours == 0) { + snprintf(buf, buf_len, "%02d:%02d", + minutes, secs); + return buf; + } + + if (days == 0) { + snprintf(buf, buf_len, "%d:%02d:%02d", + hours, minutes, secs); + return buf; + } + + snprintf(buf, buf_len, "%d %d:%02d:%02d", + days, hours, minutes, secs); + return buf; +} + + + +static const char * +ex_ts2str(char *buf, u_int buf_len, const DCC_TS *ts) +{ + static DCC_TS never; + + if (!memcmp(&ts, &never, sizeof(never))) { + STRLCPY(buf, "never ", buf_len); + return buf; + } + return ts2str(buf, buf_len, ts); +} + + + +/* display the expiration information in the database header */ +static void +list_cleaned(const DB_PARMS *parms) +{ +#define CLEANED_PAT " %12s %c %17.17s %17.17s %10s %10s" + struct tm tm; + char time_buf[32]; + char db_rate[10], hash_rate[10], entries_buf[10]; + DCC_CK_TYPES type; + char spam_ts_buf[18]; + char all_ts_buf[18]; + char allsecs_buf[20]; + char spamsecs_buf[20]; + + printf(" %s%s%spage size %#-8x s/n %s\n", + (parms->flags & DB_PARM_FG_GREY) ? "greylist " : "", + (parms->flags & DB_PARM_FG_CLEARED) ? "cleared ": "", + (parms->flags & DB_PARM_EXP_SET) ? "dbclean -e/-E ": "", + parms->pagesize, ts2str_err(&parms->sn)); + + DCC_GMTIME_R(&parms->cleared, &tm); + strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S UTC", &tm); + printf(" created %s", time_buf); + if (parms->cleaned_cron == 0) { + printf("; never properly cleaned"); + } else { + DCC_GMTIME_R(&parms->cleaned_cron, &tm); + strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm); + printf("; cleaned %s", time_buf); + } + putchar('\n'); + if (parms->cleaned > parms->cleaned_cron) { + DCC_GMTIME_R(&parms->cleaned, &tm); + strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm); + printf(" failsafe cleaned %s\n", time_buf); + } + + if (verbose > 3) { + printf(" db_csize="L_DPAT" old="L_DPAT" added="L_DPAT"\n", + parms->db_csize, parms->old_db_csize, parms->db_added); + printf(" hash_used=%d old=%d added=%d old_kept_cks=%d\n", + parms->hash_used, parms->old_hash_used, + parms->hash_added, parms->old_kept_cks); + printf(" rate_secs=%d \"%.*s\"\n", + (int)parms->rate_secs, + ISZ(parms->version), parms->version); + } + + printf(" added %s database bytes/day and %s hash entries/day\n", + print_rate(db_rate, sizeof(db_rate), parms, 0), + print_rate(hash_rate, sizeof(hash_rate), parms, 1)); + + if (db_hash_len > 0 + && parms->hash_used >= DB_HADDR_BASE) + printf(" %.0f%% of %s hash entries used\n", + HADDR2LEN(parms->hash_used) * 100.0 + / HADDR2LEN(db_hash_len), + size2str(entries_buf, sizeof(entries_buf), + HADDR2LEN(db_hash_len), 0)); + + if (parms->flags & DB_PARM_FG_GREY) + printf(CLEANED_PAT, + "", ' ', "", "", + "window", "white"); + else + printf(CLEANED_PAT, + "", ' ', "non-bulk expired", "bulk expired ", + "non ", "bulk"); + for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) { + if ((type == DCC_CK_SRVR_ID + || DB_TEST_NOKEEP(parms->nokeep_cks, type)) + && verbose < 3) + continue; + if (parms->ex_secs[type].all == DB_EXPIRE_SECS_MAX) { + STRLCPY(allsecs_buf, "never", sizeof(allsecs_buf)); + STRLCPY(all_ts_buf, "- ", sizeof(all_ts_buf)); + } else { + secs2str(allsecs_buf, sizeof(allsecs_buf), + parms->ex_secs[type].all); + ex_ts2str(all_ts_buf, sizeof(all_ts_buf), + &parms->ex_all[type]); + } + if (parms->ex_secs[type].spam == DB_EXPIRE_SECS_MAX) { + STRLCPY(spamsecs_buf, "never", sizeof(spamsecs_buf)); + STRLCPY(spam_ts_buf, "- ", sizeof(spam_ts_buf)); + } else { + secs2str(spamsecs_buf, sizeof(spamsecs_buf), + parms->ex_secs[type].spam); + ex_ts2str(spam_ts_buf, sizeof(spam_ts_buf), + &parms->ex_spam[type]); + } + printf("\n"CLEANED_PAT, + DB_TYPE2STR(type), + DB_TEST_NOKEEP(parms->nokeep_cks, type) ? '*' : ' ', + all_ts_buf, spam_ts_buf, + allsecs_buf, spamsecs_buf); + } +#undef CLEANED_PAT +} + + + +static void +list_flod(void) +{ + FLOD_MMAP *mp; + DCC_PATH path; + char hostname[40], fg_buf[60]; + u_char first; + + /* display the flood map only for default database */ + if (strcmp(fnm2abs_err(path, db_nm), DB_NM2PATH_ERR(def_argv[0]))) { + putchar('\n'); + } else if (!flod_mmap(dcc_emsg, 0, 0, 0, 1)) { + dcc_error_msg("\n\n%s", dcc_emsg); + } else if (strcmp(flod_mmaps->magic, FLOD_MMAP_MAGIC)) { + dcc_error_msg("\n\n%s contains the wrong magic \"%.*s\"", + flod_mmap_path, + ISZ(flod_mmaps->magic), flod_mmaps->magic); + if (!flod_unmap(dcc_emsg, 0)) + dcc_error_msg("%s", dcc_emsg); + } else { + first = 1; + fputs("\n\n ", stdout); + fputs(flod_mmap_path, stdout); + printf(" s/n %s\n delay position "L_HWPAT(8)"\n", + ts2str_err(&flod_mmaps->sn), flod_mmaps->delay_pos); + for (mp = flod_mmaps->mmaps; + mp <= LAST(flod_mmaps->mmaps); + ++mp) { + if (mp->rem_hostname[0] == '\0') + continue; + if (first) { + first = 0; + printf("%32s %5s %9s %s\n", + "peer", "", "ID", "position"); + } + printf("%38s %9d "L_HWPAT(8)"%s\n", + dcc_host_portname(hostname, sizeof(hostname), + mp->rem_hostname, + mp->rem_portname), + mp->rem_id, + mp->confirm_pos, + flodmap_fg(fg_buf, sizeof(fg_buf), " ", mp)); + if (mp->rem_su.sa.sa_family != AF_UNSPEC + && verbose > 1) + printf("%40s\n", + dcc_su2str3(hostname, sizeof(hostname), + &mp->rem_su, + DCC_GREY2PORT(grey_on))); + } + if (!flod_unmap(dcc_emsg, 0)) + dcc_error_msg("%s", dcc_emsg); + } +} + + + +static void +open_hash(void) +{ + db_hash_len = 0; + fd_hash = open(hash_nm, O_RDONLY, 0); + if (0 > fd_hash) { + dcc_error_msg("open(%s): %s", hash_nm, ERROR_STR()); + return; + } + if (0 > fstat(fd_hash, &hash_sb)) { + dcc_error_msg("stat(%s): %s", hash_nm, ERROR_STR()); + close(fd_hash); + fd_hash = -1; + return; + } + hash_fsize = hash_sb.st_size; + db_hash_len = hash_fsize/sizeof(HASH_ENTRY); + if ((hash_fsize % sizeof(HASH_ENTRY)) != 0) { + dcc_error_msg("%s has size "L_DPAT", not a multiple of %d", + hash_nm, hash_fsize, ISZ(HASH_ENTRY)); + db_hash_len = 0; + close(fd_hash); + fd_hash = -1; + return; + } + if (db_hash_len < MIN_HASH_ENTRIES) { + dcc_error_msg("%s has too few records, "L_DPAT" bytes", + hash_nm, hash_fsize); + db_hash_len = 0; + close(fd_hash); + fd_hash = -1; + return; + } + + db_hash_divisor = get_db_hash_divisor(db_hash_len); +} + + + +#define HASH_MAP_LEN (1024*1024) +#define HASH_MAP_NUM 16 +typedef struct hash_map { + struct hash_map *fwd, *bak; + HASH_ENTRY *buf; + DB_HADDR base; + DB_HADDR lim; + DB_HOFF offset; + DB_HOFF size; +} HASH_MAP; +static HASH_MAP hash_maps[HASH_MAP_NUM]; +static HASH_MAP *hash_map_newest; + + +static u_char +hash_munmap(HASH_MAP *mp) +{ + if (!mp->buf) + return 1; + + if (0 > munmap((void *)mp->buf, mp->size)) { + dcc_error_msg("munmap(%s,"L_DPAT"): %s", + hash_nm, mp->size, ERROR_STR()); + return 0; + } + mp->buf = 0; + return 1; +} + + + +static u_char +hash_map_clear(void) +{ + HASH_MAP *mp; + int i; + + mp = hash_maps; + for (i = 0; i < DIM(hash_maps); ++i, ++mp) { + if (i == DIM(hash_maps)-1) + mp->fwd = hash_maps; + else + mp->fwd = mp+1; + if (i == 0) + mp->bak = LAST(hash_maps); + else + mp->bak = mp-1; + } + hash_map_newest = hash_maps; + + for (mp = hash_maps; mp <= LAST(hash_maps); ++mp) { + if (!hash_munmap(mp)) + return 0; + } + + return 1; +} + + + +static void +hash_map_ref(HASH_MAP *mp) +{ + if (hash_map_newest != mp) { + mp->fwd->bak = mp->bak; + mp->bak->fwd = mp->fwd; + mp->fwd = hash_map_newest; + mp->bak = hash_map_newest->bak; + mp->fwd->bak = mp; + mp->bak->fwd = mp; + hash_map_newest = mp; + } +} + + + +static const void * +haddr_mmap(DB_HADDR haddr) +{ + HASH_MAP *mp; + void *p; + int i; + + for (i = 0, mp = hash_map_newest; + i < DIM(hash_maps); + ++i, mp = mp->fwd) { + if (!mp->buf) + continue; + if (haddr >= mp->base + && haddr < mp->lim) { + hash_map_ref(mp); + return mp->buf + (haddr - mp->base); + } + } + + mp = hash_map_newest->bak; + hash_munmap(mp); + + mp->base = haddr - haddr%HASH_MAP_LEN; + mp->offset = mp->base*sizeof(HASH_ENTRY); + mp->size = hash_fsize - mp->offset; + if (mp->size > HASH_MAP_LEN*ISZ(HASH_ENTRY)) + mp->size = HASH_MAP_LEN*ISZ(HASH_ENTRY); + mp->lim = mp->base + mp->size/sizeof(HASH_ENTRY); + p = mmap(0, mp->size, PROT_READ, MAP_SHARED, fd_hash, mp->offset); + if (p != MAP_FAILED) { + mp->buf = p; + hash_map_ref(mp); + return mp->buf + (haddr - mp->base); + } + dcc_error_msg("mmap(%s,%d,%d): %s", + hash_nm, (int)mp->size, (int)mp->offset, + ERROR_STR()); + return 0; +} + + + +static void +list_hash(void) +{ +#define HEAD() (headed ? 1 : (headed = 1, printf("\n %s\n", hash_nm))) + const HASH_ENTRY *entry; + const HASH_CTL *ctl; + time_t secs; + struct tm tm; + char time_buf[30]; + DB_HADDR collisions, chains, chain_lens; + int max_chain_len, chain_len; + DB_HADDR free_fwd, free_bak; + DB_HADDR fwd, bak, haddr; + DB_HADDR db_hash_used_stored; + DB_PTR rcd_link; + DCC_CK_TYPES type; + DB_RCD rcd; + int rcd_len; + u_char headed, clean; + int i; + + if (fd_hash < 0) + return; + + headed = 0; + + if (!hash_map_clear()) + return; + + read_rcd_invalidate(DB_RCD_LEN_MAX); + + ctl = haddr_mmap(0); + if (!ctl) + return; + if (memcmp(ctl->s.magic, &hash_magic, sizeof(hash_magic))) { + HEAD(); + dcc_error_msg(" contains the wrong magic"); + return; + } + + if (verbose > VERBOSE_HASH) { + HEAD(); + printf(" magic: \"%.*s\"\n", + ISZ(ctl->s.magic), ctl->s.magic); + } + + if (srvr.clnt_id != 0) { + clean = 0; + } else { + clean = (ctl->s.flags & HASH_CTL_FG_CLEAN) != 0; + if (!clean) { + HEAD(); + printf(" not closed\n"); + } + } + secs = ctl->s.synced; + if (verbose >= VERBOSE_HASH) { + DCC_GMTIME_R(&secs, &tm); + strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm); + printf(" synced %s\n", time_buf); + if (ctl->s.flags & HASH_CTL_FG_NOSYNC) + printf(" unsafe after next system reboot\n"); + } + + free_fwd = ctl->s.free_fwd; + free_bak = ctl->s.free_bak; + if (DB_HADDR_INVALID(ctl->s.free_fwd) + && (ctl->s.free_fwd != FREE_HADDR_END + || ctl->s.free_fwd != ctl->s.free_bak)) { + HEAD(); + dcc_error_msg(" broken free list head of %#x", + ctl->s.free_fwd); + } + if (DB_HADDR_INVALID(ctl->s.free_bak) + && (ctl->s.free_bak != FREE_HADDR_END + || ctl->s.free_fwd != ctl->s.free_bak)) { + HEAD(); + dcc_error_msg(" broken free list tail of %#x", + ctl->s.free_bak); + } + if (verbose > VERBOSE_HASH) + printf(" free: %x, %x\n", free_fwd, free_bak); + + if (db_hash_len != ctl->s.len + && (ctl->s.len != 0 || verbose >= VERBOSE_HASH)) { + HEAD(); + dcc_error_msg(" has %d entries but claims %d", + HADDR2LEN(db_hash_len), HADDR2LEN(ctl->s.len)); + } + db_hash_used_stored = ctl->s.used; + if (ctl->s.used > db_hash_len) { + HEAD(); + dcc_error_msg(" contains only %d entries but %d used", + HADDR2LEN(ctl->s.len), HADDR2LEN((ctl->s.used))); + } + if (ctl->s.used == db_hash_len) { + HEAD(); + dcc_error_msg(" overflows with %d entries", + HADDR2LEN(db_hash_len)); + } + if (ctl->s.db_csize != hdr_buf.p.db_csize + && (clean || verbose >= VERBOSE_HASH)) { + HEAD(); + dcc_error_msg(" claims %s contains "L_DPAT + " bytes instead of "L_DPAT, + db_nm, ctl->s.db_csize, hdr_buf.p.db_csize); + } + if (ctl->s.divisor != get_db_hash_divisor(db_hash_len)) { + HEAD(); + dcc_error_msg(" built with hash divisor %d instead of %d", + ctl->s.divisor, get_db_hash_divisor(db_hash_len)); + } + if (verbose >= VERBOSE_HASH) { + printf(" hash length=%#x=%d used=%#x=%d\n", + ctl->s.len, ctl->s.len, + ctl->s.used, ctl->s.used); + printf(" db_csize="L_HPAT"="L_DPAT"\n", + ctl->s.db_csize, ctl->s.db_csize); + } + + if (no_hash) { + hash_map_clear(); + return; + } + + db_hash_used = DB_HADDR_BASE; + collisions = 0; + chains = 0; + chain_lens = 0; + max_chain_len = 1; + for (haddr = DB_HADDR_BASE; haddr < db_hash_len; ++haddr) { + entry = haddr_mmap(haddr); + if (!entry) + break; + + fwd = DB_HADDR_EX(entry->fwd); + bak = DB_HADDR_EX(entry->bak); + rcd_link = DB_HPTR_EX(entry->rcd); + + /* deal with a free entry */ + if (HE_IS_FREE(entry)) { + if (rcd_link != DB_PTR_NULL) + dcc_error_msg("free hash table data link at" + " %x to "L_HPAT, + haddr, rcd_link); + if (haddr == free_fwd + && bak != FREE_HADDR_END) + dcc_error_msg("bad 1st free hash bak link %x", + bak); + else if (haddr != free_fwd + && (DB_HADDR_INVALID(bak) || bak >= haddr)) + dcc_error_msg("bad hash bak link at %x", + haddr); + if (haddr == free_bak + && fwd != FREE_HADDR_END) + dcc_error_msg("bad last free hash fwd link %x", + fwd); + else if (haddr != free_bak + && (DB_HADDR_INVALID(fwd) || fwd <= haddr)) + dcc_error_msg("bad hash fwd link at %x", + haddr); + if (verbose >= VERBOSE_HASH) + printf(" %6x: %6x %6x\n", haddr, fwd, bak); + continue; + } + + if (haddr == free_fwd && clean) + dcc_error_msg("start of free list at %x not free", + haddr); + if (haddr == free_bak && clean) + dcc_error_msg("end of free list at %x not free", + haddr); + + /* deal with a used entry */ + ++db_hash_used; + if (DB_PTR_IS_BAD(rcd_link)) + dcc_error_msg("bad hash table data link at" + " %x to "L_HPAT, + haddr, rcd_link); + if (DB_HADDR_INVALID(fwd) && fwd != DB_HADDR_NULL) + dcc_error_msg("bad hash fwd link at %x to %x", + haddr, fwd); + if (DB_HADDR_INVALID(bak) && bak != DB_HADDR_NULL) + dcc_error_msg("bad hash bak link at %x to %x", + haddr, bak); + if (verbose >= VERBOSE_HASH) + printf(" %6x: %6x %6x "L_HWPAT(8)" %s\n", + haddr, fwd, bak, rcd_link, + DB_TYPE2STR(HE_TYPE(entry))); + + if (bak != DB_HADDR_NULL) { + ++collisions; + } else { + ++chains; + bak = haddr; + chain_len = 1; + while (!DB_HADDR_INVALID(fwd)) { + if (++chain_len > 500) { + dcc_error_msg("possible hash chain loop" + " starting at %x" + " continuing through %x", + haddr, fwd); + break; + } + entry = haddr_mmap(fwd); + if (!entry) + break; + if (HE_IS_FREE(entry) + || DB_HADDR_EX(entry->bak) != bak) { + dcc_error_msg("broken hash chain" + " starting at %x at %x", + haddr, fwd); + break; + } + bak = fwd; + fwd = DB_HADDR_EX(entry->fwd); + } + chain_lens += chain_len; + if (max_chain_len < chain_len) + max_chain_len = chain_len; + } + + if (matching) { + if (num_search_cksums > 0) { + for (i = 0; i < num_search_cksums; ++i) { + type = search_cksums[i].type; + if (type == HE_TYPE(entry) + || type == DCC_CK_FLOD_PATH) + break; + } + if (i >= num_search_cksums) + continue; + } + rcd_len = read_rcd(dcc_emsg, &rcd, + fd_db, rcd_link, db_nm); + if (rcd_len <= 0) { + if (rcd_len == 0) + dcc_error_msg("bogus hash table data" + " link at %x to "L_HPAT, + haddr, rcd_link); + else + dcc_error_msg("%s", dcc_emsg); + } else { + list_db_entry(rcd_link, &rcd); + } + } + } + + hash_map_clear(); + + if (db_hash_used_stored > db_hash_used) { + dcc_error_msg("%s should have %d entries but has only %d", + hash_nm, + HADDR2LEN(db_hash_used_stored), + HADDR2LEN(db_hash_used)); + } else if (db_hash_used_stored < db_hash_used + && (clean || verbose >= VERBOSE_HASH)) { + dcc_error_msg("%s should have %d filled entries but has %d", + hash_nm, + HADDR2LEN(db_hash_used_stored), + HADDR2LEN(db_hash_used)); + } + + if (verbose >= VERBOSE_HASH) + putchar('\n'); + printf("%7d hash entries total %d or %.0f%% used %d free\n" + "%7d modulus %.2f%% collisions", + HADDR2LEN(db_hash_len), + HADDR2LEN(db_hash_used), + (HADDR2LEN(db_hash_used)*100.0) / HADDR2LEN(db_hash_len), + HADDR2LEN(db_hash_len) - HADDR2LEN(db_hash_used), + db_hash_divisor, + collisions*100.0/HADDR2LEN(db_hash_len)); + if (chains != 0) + printf(" %7d hash chains\n" + " %d max length %.2f average length", + chains, max_chain_len, chain_lens*1.0/chains); + fputc('\n', stdout); +}