Mercurial > notdcc
view dcclib/ckwhite.c @ 5:0a7a5940ee3a
Change description per license
author | Peter Gervai <grin@grin.hu> |
---|---|
date | Tue, 10 Mar 2009 15:03:24 +0100 |
parents | c7f6b056b673 |
children |
line wrap: on
line source
/* Distributed Checksum Clearinghouse * * check checksums in the local whitelist * * 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.133 $Revision$ */ #include "dcc_ck.h" static const DCC_WHITE_MAGIC white_magic = WHITE_MAGIC_B_STR WHITE_MAGIC_V_STR; #define EMPTY_WHITE_SIZE (sizeof(DCC_WHITE_TBL) - sizeof(DCC_WHITE_ENTRY)) #define MAX_WHITE_ENTRIES (DCC_WHITE_TBL_BINS*10) #define ENTRIES2SIZE0(_l) (sizeof(DCC_WHITE_TBL) \ - sizeof(DCC_WHITE_ENTRY) \ + ((_l) * sizeof(DCC_WHITE_ENTRY))) #ifdef DCC_WIN32 /* Make the hash table files maximum size on WIN32. * You cannot change the size of a WIN32 mapping object without * getting all processes using it to release it so that it can be * recreated. This may cause problems if the size of hash table * header changes. * Since the file does not change size, there is no need to remap it */ #define ENTRIES2SIZE(_l) ENTRIES2SIZE0(MAX_WHITE_ENTRIES) #else #define ENTRIES2SIZE(_l) ENTRIES2SIZE0(_l) #endif void dcc_wf_init(DCC_WF *wf, u_int wf_flags) /* DCC_WF_* */ { memset(wf, 0, sizeof(*wf)); wf->ht_fd = -1; wf->wf_flags = wf_flags & ~DCC_WF_NOFILE; } /* this is needed only on systems without coherent mmap()/read()/write() */ static void sync_white(DCC_WF *wf) { if (wf->wtbl && 0 > MSYNC(wf->wtbl, wf->wtbl_size, MS_SYNC)) dcc_error_msg("msync(%s): %s", wf->ht_nm, ERROR_STR()); } #define STILL_BROKE(wf,now) ((wf)->broken != 0 \ && !DCC_IS_TIME(now, (wf)->broken, \ DCC_WHITE_BROKEN_DELAY)) static void is_broken(DCC_WF *wf) { wf->broken = time(0) + DCC_WHITE_BROKEN_DELAY; /* This is racy, but the worst that can happen is that * another process races to set the retry timer */ if (!(wf->wf_flags & DCC_WF_RO) && wf->wtbl) { wf->wtbl->hdr.broken = wf->broken; sync_white(wf); } } static void unmap_white_ht(DCC_WF *wf) { if (!wf->wtbl) return; sync_white(wf); #ifdef DCC_WIN32 win32_unmap(&wf->ht_map, wf->wtbl, wf->ht_nm); #else if (0 > munmap((void *)wf->wtbl, wf->wtbl_size)) dcc_error_msg("munmap(%s,%d): %s", wf->ht_nm, wf->wtbl_size, ERROR_STR()); #endif wf->wtbl = 0; } static u_char /* 1=done, 0=failed */ map_white_ht(DCC_EMSG emsg, DCC_WF *wf, DCC_WHITE_INX entries) { size_t size; #ifndef DCC_WIN32 void *p; #endif unmap_white_ht(wf); if (entries > MAX_WHITE_ENTRIES) { dcc_pemsg(EX_IOERR, emsg, "%s should not contain %d entries", wf->ht_nm, entries); return 0; } size = ENTRIES2SIZE(entries); #ifdef DCC_WIN32 if (!wf->wtbl) { wf->wtbl = win32_map(emsg, &wf->ht_map, wf->ht_nm, wf->ht_fd, size); if (!wf->wtbl) return 0; } #else p = mmap(0, size, (wf->wf_flags & DCC_WF_RO) ? PROT_READ : (PROT_READ|PROT_WRITE), MAP_SHARED, wf->ht_fd, 0); if (p == MAP_FAILED) { dcc_pemsg(EX_IOERR, emsg, "mmap(%s,%d): %s", wf->ht_nm, (int)size, ERROR_STR()); return 0; } wf->wtbl = p; #endif wf->wtbl_size = size; wf->wtbl_entries = entries; wf->wtbl_flags = wf->wtbl->hdr.flags; return 1; } static u_char close_white_ht(DCC_EMSG emsg, DCC_WF *wf) { u_char result = 1; if (wf->ht_fd < 0) return result; wf->closed = 1; unmap_white_ht(wf); #ifdef DCC_WIN32 /* unlock the file before closing it to keep Win95 happy */ if (!dcc_unlock_fd(emsg, wf->ht_fd, DCC_LOCK_ALL_FILE, "whitelist ", wf->ht_nm)) result = 0; #endif if (0 > close(wf->ht_fd)) { dcc_pemsg(EX_IOERR, emsg, "close(%s): %s", wf->ht_nm, ERROR_STR()); result = 0; } wf->ht_fd = -1; memset(&wf->ht_sb, 0, sizeof(wf->ht_sb)); #ifndef DCC_WIN32 wf->ht_sb.st_dev = -1; wf->ht_sb.st_ino = -1; #endif return result; } static int /* 0=ok -1=failed */ unlink_white_ht(DCC_WF *wf, time_t now) /* 0=our own new file */ { int result; /* mark it bad if it is a brand new hash table */ if (!now && wf->wtbl && !(wf->wf_flags & DCC_WF_RO)) wf->wtbl->hdr.ascii_mtime = 0; #ifdef DCC_WIN32 /* racy but you cannot unlink an open file on WIN32 */ close_white_ht(0, wf); #endif result = -1; if (!(wf->wf_flags & DCC_WF_RO)) { if (0 <= unlink(wf->ht_nm)) { result = 0; #ifndef DCC_WIN32 } else if ((errno == EACCES || errno == EPERM) && dcc_get_priv_home(wf->ht_nm)) { if (0 <= unlink(wf->ht_nm)) result = 0; dcc_rel_priv(); #endif } if (result < 0) dcc_error_msg("unlink(%s): %s", wf->ht_nm, ERROR_STR()); } /* If we failed to unlink the old hash table, * remember in core that things are broken but do not touch the file * in case it is a link to /etc/passwd or something else dangerous */ if (result < 0 && now) wf->broken = now + DCC_WHITE_BROKEN_DELAY; #ifndef DCC_WIN32 close_white_ht(0, wf); #endif return result; } static u_char new_ht_nm(DCC_EMSG emsg, DCC_WF *wf, u_char new) { if (wf->ascii_nm_len >= ISZ(wf->ht_nm) - LITZ(DCC_WHITE_SUFFIX)) { dcc_pemsg(EX_NOINPUT, emsg, "bad whitelist file name \"%s\"", wf->ascii_nm); is_broken(wf); return 0; } memcpy(wf->ht_nm, wf->ascii_nm, wf->ascii_nm_len); strcpy(&wf->ht_nm[wf->ascii_nm_len], new ? DCC_WHITE_NEW_SUFFIX : DCC_WHITE_SUFFIX); return 1; } u_char dcc_new_white_nm(DCC_EMSG emsg, DCC_WF *wf, const char *new_white_nm) { DCC_PATH new_path; const char *new; u_int len; int i; if (!strcmp(new_white_nm, wf->ascii_nm)) return 1; close_white_ht(0, wf); dcc_wf_init(wf, wf->wf_flags); if (!fnm2rel(new_path, new_white_nm, 0)) { dcc_pemsg(EX_USAGE, emsg, "bad whitelist name \"%s\"", new_white_nm); return 0; } len = strlen(new_path); i = len - LITZ(DCC_WHITE_SUFFIX); if (i > 0 && (!strcmp(&new_path[i], DCC_WHITE_SUFFIX) || !strcmp(&new_path[i], DCC_WHITE_NEW_SUFFIX))) { len = i; new_path[len] = '\0'; } if (len > ISZ(wf->ht_nm) - ISZ(DCC_WHITE_SUFFIX)) { dcc_pemsg(EX_USAGE, emsg, "long whitelist name \"%s\"", new_white_nm); return 0; } new = path2fnm(new_path); len = strlen(new); memcpy(wf->ascii_nm, new, len+1); wf->ascii_nm_len = len; return 1; } /* open and shared-lock a hash table file */ static int /* -1=fatal 0=rebuild, 1=ok */ open_white_ht(DCC_EMSG emsg, DCC_WF *wf) { int size; DCC_WHITE_INX entries; close_white_ht(0, wf); if (!new_ht_nm(emsg, wf, 0)) return -1; wf->ht_fd = dcc_lock_open(emsg, wf->ht_nm, (wf->wf_flags & DCC_WF_RO) ? O_RDONLY : O_RDWR, DCC_LOCK_OPEN_SHARE, DCC_LOCK_ALL_FILE, 0); if (wf->ht_fd < 0) { /* if this is a `wlist` command and neither -P nor -Q * were specified, try to open the file read-only */ if ((wf->wf_flags & DCC_WF_WLIST) && !(wf->wf_flags & (DCC_WF_WLIST_RO | DCC_WF_WLIST_RW))) { if (emsg) emsg[0] = '\0'; wf->wf_flags |= DCC_WF_RO; wf->ht_fd = dcc_lock_open(emsg, wf->ht_nm, O_RDONLY, DCC_LOCK_OPEN_SHARE, DCC_LOCK_ALL_FILE, 0); } if (wf->ht_fd < 0) return 0; } if (fstat(wf->ht_fd, &wf->ht_sb) < 0) { dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s", wf->ht_nm, ERROR_STR()); close_white_ht(0, wf); is_broken(wf); return -1; } size = wf->ht_sb.st_size - EMPTY_WHITE_SIZE; if (size < 0) { dcc_pemsg(EX_NOINPUT, emsg, "%s is too small to be a DCC whitelist hash table", wf->ht_nm); return unlink_white_ht(wf, time(0)); } else { entries = size / sizeof(DCC_WHITE_ENTRY); if (!map_white_ht(emsg, wf, entries)) return unlink_white_ht(wf, time(0)); } if (memcmp(&wf->wtbl->magic, &white_magic, sizeof(white_magic))) { /* rebuild old format files */ if (!memcmp(&wf->wtbl->magic, WHITE_MAGIC_B_STR, LITZ(WHITE_MAGIC_B_STR))) { if (dcc_clnt_debug) dcc_trace_msg("%s is obsolete %s", wf->ht_nm, wf->wtbl->magic); } else { dcc_pemsg(EX_NOINPUT, emsg, "%s is not a DCC whitelist hash file", wf->ht_nm); } return unlink_white_ht(wf, time(0)); } if ((size % sizeof(DCC_WHITE_ENTRY)) != 0 || entries > MAX_WHITE_ENTRIES || entries < wf->wtbl->hdr.entries) { dcc_pemsg(EX_NOINPUT, emsg, "invalid size of whitelist %s="OFF_DPAT, wf->ht_nm, wf->ht_sb.st_size); return unlink_white_ht(wf, time(0)); } if (wf->wtbl->hdr.ascii_mtime == 0) { close_white_ht(0, wf); return 0; /* broken hash table */ } /* we know the hash table is usable * but we might want to rebuild it */ wf->need_reopen = 0; wf->wtbl_flags = wf->wtbl->hdr.flags; /* wlist and dccproc work on both per-user and global whitelists, * so do not change the nature of the file if it is already known */ if (wf->wf_flags & DCC_WF_EITHER) { if (wf->wtbl_flags & DCC_WHITE_FG_PER_USER) wf->wf_flags |= DCC_WF_PER_USER; else wf->wf_flags &= ~DCC_WF_PER_USER; } return 1; } static void create_white_ht_sub(DCC_EMSG emsg, DCC_WF *new_wf, const DCC_WF *wf, u_char *busyp) { /* do not use O_EXCL because we want to wait for any other * process to finish * * wait if and only if we do not have a usable file open */ new_wf->ht_fd = dcc_lock_open(emsg, new_wf->ht_nm, O_RDWR | O_CREAT, wf->ht_fd >= 0 ? DCC_LOCK_OPEN_NOWAIT : 0, DCC_LOCK_ALL_FILE, busyp); if (new_wf->ht_fd < 0) return; /* a new hash table must be empty */ if (0 > fstat(new_wf->ht_fd, &new_wf->ht_sb)) { dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s", new_wf->ht_nm, ERROR_STR()); close_white_ht(emsg, new_wf); return; } if (new_wf->ht_sb.st_size != 0) { dcc_pemsg(EX_IOERR, emsg, "%s has non-zero size %d", new_wf->ht_nm, (int)new_wf->ht_sb.st_size); close_white_ht(emsg, new_wf); return; } } /* create and write-lock a new hash table file * wait for lock if we don't have existing file open */ static int /* 1=done, 0=file busy, -1=fatal */ create_white_ht(DCC_EMSG emsg, DCC_WF *tmp_wf, /* build with this */ const DCC_WF *wf) /* from this */ { u_char busy = 0; tmp_wf->ascii_nm_len = wf->ascii_nm_len; memcpy(tmp_wf->ascii_nm, wf->ascii_nm, wf->ascii_nm_len+1); if (!new_ht_nm(emsg, tmp_wf, 1)) { tmp_wf->ht_nm[0] = '\0'; return -1; } if (tmp_wf->wf_flags & DCC_WF_RO) { dcc_pemsg(EX_IOERR, emsg, "read only access; cannot create or update %s", tmp_wf->ht_nm); tmp_wf->ht_nm[0] = '\0'; return -1; } #ifndef DCC_WIN32 /* We want to create a private hash table if the ASCII file * is private, but a hash table owned by the DCC user if the * ASCII file is public */ if (0 > access(tmp_wf->ascii_nm, R_OK | W_OK) && dcc_get_priv_home(tmp_wf->ht_nm)) { /* first try to open a public hash table */ create_white_ht_sub(emsg, tmp_wf, wf, &busy); if (tmp_wf->ht_fd < 0 && !busy) { if (emsg && dcc_clnt_debug > 2) dcc_error_msg("%s", emsg); unlink(tmp_wf->ht_nm); create_white_ht_sub(emsg, tmp_wf, wf, &busy); } dcc_rel_priv(); } #endif if (tmp_wf->ht_fd < 0 && !busy) { /* try to open or create a private hash table */ create_white_ht_sub(emsg, tmp_wf, wf, &busy); if (tmp_wf->ht_fd < 0 && !busy) { if (emsg && dcc_clnt_debug > 2) dcc_error_msg("%s", emsg); unlink(tmp_wf->ht_nm); create_white_ht_sub(emsg, tmp_wf, wf, &busy); } } #ifndef DCC_WIN32 /* try one last time with privileges in case the ASCII file has * mode 666 but the directory does not */ if (tmp_wf->ht_fd < 0 && !busy) { if (dcc_get_priv_home(tmp_wf->ht_nm)) { if (emsg && dcc_clnt_debug > 2) dcc_error_msg("%s", emsg); unlink(tmp_wf->ht_nm); create_white_ht_sub(emsg, tmp_wf, wf, &busy); dcc_rel_priv(); } } #endif if (tmp_wf->ht_fd < 0) { tmp_wf->ht_nm[0] = '\0'; if (busy) return 0; return -1; } return 1; } #define FIND_WHITE_BROKEN ((DCC_WHITE_ENTRY *)-1) static DCC_WHITE_ENTRY * find_white(DCC_EMSG emsg, DCC_WF *wf, DCC_CK_TYPES type, const DCC_SUM sum, DCC_WHITE_INX *binp) { u_long accum; DCC_WHITE_INX bin, inx; DCC_WHITE_ENTRY *e; int loop_cnt, i; if (!wf->wtbl || wf->wtbl->hdr.ascii_mtime == 0) return FIND_WHITE_BROKEN; accum = type; for (i = sizeof(DCC_SUM)-1; i >= 0; --i) accum = (accum >> 28) + (accum << 4) + sum[i]; bin = accum % DIM(wf->wtbl->bins); if (binp) *binp = bin; inx = wf->wtbl->bins[bin]; for (loop_cnt = wf->wtbl->hdr.entries+1; loop_cnt >= 0; --loop_cnt) { if (!inx) return 0; --inx; /* if necessary, expand the mapped window into the file */ if (inx >= wf->wtbl_entries) { if (inx >= wf->wtbl->hdr.entries) { dcc_pemsg(EX_DATAERR, emsg, "bogus index %u in %s", inx, wf->ht_nm); if (!(wf->wf_flags & DCC_WF_RO)) wf->wtbl->hdr.ascii_mtime = 0; sync_white(wf); return FIND_WHITE_BROKEN; } if (!map_white_ht(emsg, wf, wf->wtbl->hdr.entries)) return 0; } e = &wf->wtbl->tbl[inx]; if (e->type == type && !memcmp(e->sum, sum, sizeof(DCC_SUM))) return e; inx = e->fwd; } dcc_pemsg(EX_DATAERR, emsg, "chain length %d in %s" " starting at %d near %d for %s %s", wf->wtbl->hdr.entries+1, wf->ht_nm, (DCC_WHITE_INX)(accum % DIM(wf->wtbl->bins)), inx, dcc_type2str_err(type, 0, 0, 0), dcc_ck2str_err(type, sum, 0)); if (!(wf->wf_flags & DCC_WF_RO)) wf->wtbl->hdr.ascii_mtime = 0; sync_white(wf); return FIND_WHITE_BROKEN; } static int /* 1=quit stat_white_nms() */ stat_1white_nm(int *resultp, /* set=1 if (supposedly) no change */ DCC_WF *wf, const char *nm, time_t ascii_mtime) { struct stat sb; time_t now; if (stat(nm, &sb) < 0) { if (errno != ENOENT || !wf->wtbl || (wf->wf_flags & DCC_WF_RO)) { dcc_trace_msg("stat(%s): %s", nm, ERROR_STR()); is_broken(wf); *resultp = 0; return 1; } /* only complain if an ASCII file disappears temporarily */ if (wf->broken == 0) { dcc_trace_msg("%s disappeared: %s", nm, ERROR_STR()); is_broken(wf); *resultp = 1; return 1; } now = time(0); if (STILL_BROKE(wf, now)) { if (dcc_clnt_debug > 1) dcc_trace_msg("ignoring stat(%s): %s", nm, ERROR_STR()); *resultp = 1; } else { if (dcc_clnt_debug > 1) dcc_trace_msg("pay attention to stat(%s): %s", nm, ERROR_STR()); *resultp = 0; } return 1; } if (sb.st_mtime != ascii_mtime) { *resultp = 0; return 1; } return 0; } /* see if the ASCII files have changed */ static int /* 1=same 0=changed or sick -1=broken */ stat_white_nms(DCC_EMSG emsg, DCC_WF *wf) { struct stat ht_sb; time_t now; int i, result; if (!wf->wtbl) return -1; now = time(0); wf->next_stat_time = now + DCC_WHITE_STAT_DELAY; /* Notice if the hash file has been unlinked */ if (stat(wf->ht_nm, &ht_sb) < 0) { if (emsg && dcc_clnt_debug) dcc_error_msg("stat(%s): %s", wf->ht_nm, ERROR_STR()); return -1; } #ifdef DCC_WIN32 /* open files cannot be unlinked in WIN32, which lets us not * worry about whether WIN32 files have device and i-numbers */ #else if (wf->ht_sb.st_dev != ht_sb.st_dev || wf->ht_sb.st_ino != ht_sb.st_ino) { if (emsg && dcc_clnt_debug > 2) dcc_error_msg("%s disappeared", wf->ht_nm); return -1; } #endif /* DCC_WIN32 */ wf->ht_sb = ht_sb; /* delays on re-parsing and complaining in the file override */ if (wf->reparse < wf->wtbl->hdr.reparse) wf->reparse = wf->wtbl->hdr.reparse; if (wf->broken < wf->wtbl->hdr.broken) wf->broken = wf->wtbl->hdr.broken; /* seriously broken hash tables are unusable */ if (wf->wtbl->hdr.ascii_mtime == 0) return -1; /* pretend things are fine if they are recently badly broken */ if (STILL_BROKE(wf, now)) return 1; /* if the main ASCII file has disappeared, * leave the hash file open and just complain, * but only for a while */ result = 1; if (stat_1white_nm(&result, wf, wf->ascii_nm, wf->wtbl->hdr.ascii_mtime)) return result; /* see if any of the included ASCII files are new */ for (i = 0; i < DIM(wf->wtbl->hdr.white_incs); ++i) { if (wf->wtbl->hdr.white_incs[i].nm[0] == '\0') break; /* stop at the first missing or changed included file */ if (stat_1white_nm(&result, wf, wf->wtbl->hdr.white_incs[i].nm, wf->wtbl->hdr.white_incs[i].mtime)) return result; } /* force periodic reparsing of syntax errors to nag in system log */ if (wf->reparse != 0 && DCC_IS_TIME(now, wf->reparse, DCC_WHITE_REPARSE_DELAY)) return 0; if ((wf->wtbl_flags & DCC_WHITE_FG_PER_USER) && !(wf->wf_flags & DCC_WF_PER_USER)) { dcc_error_msg("%s is a per-user whitelist" " used as a global whitelist", wf->ht_nm); return 0; } if (!(wf->wtbl_flags & DCC_WHITE_FG_PER_USER) && (wf->wf_flags & DCC_WF_PER_USER)) { dcc_error_msg("%s is a global whitelist" " used as a per-user whitelist", wf->ht_nm); return 0; } /* Checksums of SMTP client IP addresses are compared against the * checksums of the IP addresses of the hostnames in the ASCII file. * Occassionaly check for changes in DNS A RR's for entries in * the ASCII file, but only if there are host names or IP * addresses in the file */ if ((wf->wtbl->hdr.flags & DCC_WHITE_FG_HOSTNAMES) && DCC_IS_TIME(now, wf->ht_sb.st_mtime+DCC_WHITECLNT_RESOLVE, DCC_WHITECLNT_RESOLVE)) { if (dcc_clnt_debug > 2) dcc_trace_msg("time to rebuild %s", wf->ht_nm); return 0; } return 1; } static u_char write_white(DCC_EMSG emsg, DCC_WF *wf, const void *buf, int buf_len, off_t pos) { int i; if (wf->wtbl) { #ifdef DCC_WIN32 /* Windows disclaims coherence between ordinary writes * and memory mapped writing. The hash tables are * fixed size on Windows because of problems with WIN32 * mapping objects, so we do not need to worry about * extending the hash table file. */ memcpy((char *)wf->wtbl+pos, buf, buf_len); return 1; #else /* some UNIX systems have coherence trouble without msync() */ if (0 > MSYNC(wf->wtbl, wf->wtbl_size, MS_SYNC)) { dcc_pemsg(EX_IOERR, emsg, "msync(%s): %s", wf->ht_nm, ERROR_STR()); return 0; } #endif } i = lseek(wf->ht_fd, pos, SEEK_SET); if (i < 0) { dcc_pemsg(EX_IOERR, emsg, "lseek(%s,"OFF_DPAT"): %s", wf->ht_nm, pos, ERROR_STR()); return 0; } i = write(wf->ht_fd, buf, buf_len); if (i != buf_len) { if (i < 0) dcc_pemsg(EX_IOERR, emsg, "write(%s,%d): %s", wf->ht_nm, buf_len, ERROR_STR()); else dcc_pemsg(EX_IOERR, emsg, "write(%s,%d): %d", wf->ht_nm, buf_len, i); return 0; } return 1; } static int /* 1=ok, 0=bad entry, -1=fatal */ add_white(DCC_EMSG emsg, DCC_WF *wf, DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts) { DCC_WHITE_ENTRY *e, new; DCC_WHITE_INX bin; DCC_FNM_LNO_BUF fnm_buf; off_t end; /* find the hash chain for the new entry */ e = find_white(emsg, wf, type, sum, &bin); if (e == FIND_WHITE_BROKEN) return -1; /* ignore duplicates on this, the first pass */ if (e) return 1; memset(&new, 0, sizeof(new)); new.type = type; memcpy(new.sum, sum, sizeof(DCC_SUM)); new.tgts = tgts; new.fwd = wf->wtbl->bins[bin]; new.lno = wf->lno; new.fno = wf->fno; /* Use a new entry at the end of the file * It will be beyond the memory mapped window into the file */ if (wf->wtbl->hdr.entries >= MAX_WHITE_ENTRIES) { dcc_pemsg(EX_DATAERR, emsg, "more than maximum %d entries%s", wf->wtbl->hdr.entries, wf_fnm_lno(&fnm_buf, wf)); return 0; } end = ENTRIES2SIZE(wf->wtbl->hdr.entries); wf->wtbl->bins[bin] = ++wf->wtbl->hdr.entries; return write_white(emsg, wf, &new, sizeof(new), end) ? 1 : -1; } #define MAX_CIDR_BITS 7 static int /* 1=ok, 0=bad entry, -1=fatal */ add_white_cidr(DCC_EMSG emsg, DCC_WF *wf, int bits, const struct in6_addr *addrp, const struct in6_addr *maskp, DCC_TGTS tgts) { DCC_WHITE_CIDR_ENTRY *e; struct in6_addr addr; DCC_SUM sum; DCC_FNM_LNO_BUF fnm_buf; int result, i, j; /* use individual entries for MAX_CIDR_BITS and smaller blocks */ if (128-bits <= MAX_CIDR_BITS) { addr = *addrp; result = 1; for (i = 1 << (128-bits); i > 0; --i) { dcc_ck_ipv6(sum, &addr); j = add_white(emsg, wf, DCC_CK_IP, sum, tgts); if (j <= 0) { if (j < 0) return j; result = j; } addr.s6_addr32[3] = ntohl(addr.s6_addr32[3]); ++addr.s6_addr32[3]; addr.s6_addr32[3] = htonl(addr.s6_addr32[3]); } return result; } i = wf->wtbl->hdr.cidr.len; for (e = wf->wtbl->hdr.cidr.e; i > 0; ++e, --i) { /* ignore collisions on this, the first pass */ if (e->bits == bits && !memcmp(addrp, &e->addr, sizeof(*addrp))) return 1; } if (wf->wtbl->hdr.cidr.len >= DIM(wf->wtbl->hdr.cidr.e)) { dcc_pemsg(EX_DATAERR, emsg, "too many CIDR blocks%s", wf_fnm_lno(&fnm_buf, wf)); return 0; } e->bits = bits; e->addr = *addrp; e->mask = *maskp; e->tgts = tgts; e->lno = wf->lno; e->fno = wf->fno; ++wf->wtbl->hdr.cidr.len; return 1; } static void dup_msg(DCC_EMSG emsg, DCC_WF *wf, int e_fno, int e_lno, DCC_TGTS e_tgts, DCC_TGTS tgts) { char tgts_buf[30], e_tgts_buf[30]; const char *fname1, *fname2; fname1 = wf_fnm(wf, wf->fno); fname2 = wf_fnm(wf, e_fno); dcc_pemsg(EX_DATAERR, emsg, "\"%s\" in line %d%s%s conflicts with \"%s\" in line" " %d of %s", dcc_tgts2str(tgts_buf, sizeof(tgts_buf), tgts, 0), wf->lno, fname1 != fname2 ? " of " : "", fname1 != fname2 ? fname1 : "", dcc_tgts2str(e_tgts_buf, sizeof(e_tgts_buf), e_tgts, 0), e_lno, fname2); } static DCC_TGTS combine_white_tgts(DCC_TGTS new, DCC_TGTS old) { if (new < DCC_TGTS_TOO_MANY && old == DCC_TGTS_TOO_MANY) return new; if (new == DCC_TGTS_OK || old == DCC_TGTS_OK) return DCC_TGTS_OK; if (new == DCC_TGTS_OK2 || old == DCC_TGTS_OK2) return DCC_TGTS_OK2; if (new == DCC_TGTS_OK_MX || old == DCC_TGTS_OK_MX) return DCC_TGTS_OK_MX; if (new == DCC_TGTS_OK_MXDCC || old == DCC_TGTS_OK_MXDCC) return DCC_TGTS_OK_MXDCC; if (new == DCC_TGTS_TOO_MANY || old == DCC_TGTS_TOO_MANY) return DCC_TGTS_TOO_MANY; if (new == DCC_TGTS_SUBMIT_CLIENT || old == DCC_TGTS_SUBMIT_CLIENT) return DCC_TGTS_SUBMIT_CLIENT; return new; } static int /* 1=ok, 0=bad entry, -1=fatal */ ck_dup_white(DCC_EMSG emsg, DCC_WF *wf, DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts, u_char complain) { DCC_WHITE_ENTRY *e; DCC_FNM_LNO_BUF buf; const char *from; /* find the hash table entry */ e = find_white(emsg, wf, type, sum, 0); if (e == FIND_WHITE_BROKEN) return -1; if (!e) { /* We failed to find the hash table entry. Either the * hash file is corrupt, the ASCII file is changing beneath * our feet, or a host name that failed to resolve the first * time we parsed the ASCII file is now resolving during * this second parsing. */ from = wf_fnm_lno(&buf, wf); if (type == DCC_CK_IP) { if (dcc_clnt_debug > 2) dcc_trace_msg("%s entry (dis)appeared in %s%s", dcc_type2str_err(type, 0, 0, 0), wf->ht_nm, *from == '\0' ? "in ?" : from); return 0; } dcc_pemsg(EX_DATAERR, emsg, "%s entry (dis)appeared in %s%s", dcc_type2str_err(type, 0, 0, 0), wf->ht_nm, *from == '\0' ? "in ?" : from); return -1; } /* ignore perfect duplicates */ if (e->tgts == tgts) return 1; if (complain) dup_msg(emsg, wf, e->fno, e->lno, e->tgts, tgts); if (e->tgts != combine_white_tgts(tgts, e->tgts)) { e->tgts = tgts; e->lno = wf->lno; e->fno = wf->fno; } return 0; } static int /* 1=ok, 0=bad entry, -1=fatal */ ck_dup_white_complain(DCC_EMSG emsg, DCC_WF *wf, DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts) { return ck_dup_white(emsg, wf, type, sum, tgts, 1); } /* Without brute force checks that would take too long, it is impossible to * check that a new CIDR block does not collide with individual entries that * are already in the hash table. Without expanding a big CIDR block into * IP addresses and looking for each in the hash table, how can you check * whether it covers an entry for an individual address? One way is to * parse the file twice and so check each individual IP address against * the CIDR blocks. */ static int /* 1=ok, 0=bad entry, -1=fatal */ ck_dup_white_cidr(DCC_EMSG emsg UATTRIB, DCC_WF *wf, int bits, const struct in6_addr *addrp, const struct in6_addr *maskp, DCC_TGTS tgts) { DCC_WHITE_CIDR_ENTRY *e; struct in6_addr addr; DCC_SUM sum; int result, i, j; result = 1; /* Check for collisions between CIDR blocks and either other * CIDR blocks or individual addresses. Individual addresses * are sent here as /128 blocks. */ i = wf->wtbl->hdr.cidr.len; for (e = wf->wtbl->hdr.cidr.e; i > 0; ++e, --i) { if (!DCC_IN_BLOCK(e->addr, *addrp, *maskp) && !DCC_IN_BLOCK(*addrp, e->addr, e->mask)) continue; /* ignore simple duplicates */ if (e->tgts == tgts) continue; dup_msg(emsg, wf, e->fno, e->lno, e->tgts, tgts); result = 0; /* fix direct collisions */ if (e->bits == bits && !memcmp(addrp, &e->addr, sizeof(*addrp)) && e->tgts != combine_white_tgts(tgts, e->tgts)) { e->tgts = tgts; e->lno = wf->lno; e->fno = wf->fno; } } /* check and fix collisions among individual entries */ if (128-bits <= MAX_CIDR_BITS) { addr = *addrp; for (i = 1 << (128-bits); i > 0; --i) { dcc_ck_ipv6(sum, &addr); j = ck_dup_white(emsg, wf, DCC_CK_IP, sum, tgts, result > 0); if (j < 0) return j; if (j == 0) result = j; addr.s6_addr32[3] = htonl(1+ntohl(addr.s6_addr32[3])); } } return result; } /* bail out on creating a whiteclnt hash table file */ static DCC_WHITE_RESULT make_white_hash_bail(DCC_WHITE_RESULT result, DCC_WF *wf, DCC_WF *tmp_wf) { if (tmp_wf->ht_nm[0] != '\0') { unlink_white_ht(tmp_wf, 0); tmp_wf->ht_nm[0] = '\0'; } /* we have a usable file open */ if (wf->wtbl) return DCC_WHITE_CONTINUE; return result; } /* (re)create the hash table file */ static DCC_WHITE_RESULT make_white_hash(DCC_EMSG emsg, DCC_WF *wf, DCC_WF *tmp_wf) { static const u_char zero = 0; int ascii_fd; struct stat ascii_sb; DCC_PATH path; DCC_WHITE_RESULT result; DCC_CK_TYPES type; int part_result, i; if (dcc_clnt_debug > 2) dcc_trace_msg("start parsing %s", wf->ascii_nm); /* Do not wait to create a new, locked hash table file if we have * a usable file. Assume some other process is re-parsing * the ASCII file. * If we should wait to create a new file, then we don't have a * usable hash table and so there is no reason to unlock the * DCC_WF structure. */ #ifdef DCC_DEBUG_CLNT_LOCK if (tmp_wf == &cmn_tmp_wf) assert_cwf_locked(); #endif dcc_wf_init(tmp_wf, wf->wf_flags); if (0 > stat(wf->ascii_nm, &ascii_sb)) { result = ((errno == ENOENT || errno == ENOTDIR) ? DCC_WHITE_NOFILE : DCC_WHITE_COMPLAIN); dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s", wf->ascii_nm, ERROR_STR()); /* Delete the hash table if the ASCII file has disappeared. * stat_white_nms() delays forcing a re-build if the ASCII * file disappears only temporarily */ if (wf->ht_nm[0] != '\0') { close_white_ht(0, wf); i = unlink(wf->ht_nm); if (i < 0) { if (errno != ENOENT && errno != ENOTDIR) dcc_trace_msg("%s missing,:unlink(%s):" " %s", wf->ascii_nm, fnm2abs_err(path, wf->ht_nm), ERROR_STR()); } else if (dcc_clnt_debug > 1) { dcc_trace_msg("delete %s after %s missing", fnm2abs_err(path, wf->ht_nm), wf->ascii_nm); } } return make_white_hash_bail(result, wf, tmp_wf); } part_result = create_white_ht(emsg, tmp_wf, wf); if (part_result == 0) { /* The new hash table file is busy. * Do not complain if we have a usable open hash table file */ if (!dcc_clnt_debug && wf->wtbl) return DCC_WHITE_OK; /* at least ignore the need to reparse */ return DCC_WHITE_CONTINUE; } if (part_result < 0) return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf); /* clear the new hash table */ if (!write_white(emsg, tmp_wf, white_magic, sizeof(white_magic), 0) || !write_white(emsg, tmp_wf, &zero, 1, EMPTY_WHITE_SIZE-1) || !map_white_ht(emsg, tmp_wf, 0)) return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf); if (tmp_wf->wf_flags & DCC_WF_PER_USER) tmp_wf->wtbl->hdr.flags = DCC_WHITE_FG_PER_USER; tmp_wf->wtbl_flags = tmp_wf->wtbl->hdr.flags; for (type = 0; type <= DCC_CK_TYPE_LAST; ++type) tmp_wf->wtbl->hdr.tholds_rej[type] = DCC_THOLD_UNSET; ascii_fd = dcc_lock_open(emsg, tmp_wf->ascii_nm, O_RDONLY, DCC_LOCK_OPEN_NOWAIT | DCC_LOCK_OPEN_SHARE, DCC_LOCK_ALL_FILE, 0); if (ascii_fd == -1) return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf); tmp_wf->wtbl->hdr.ascii_mtime = ascii_sb.st_mtime; part_result = dcc_parse_whitefile(emsg, tmp_wf, ascii_fd, add_white, add_white_cidr); if (part_result > 0) { /* parse again to detect colliding definitions among * host names and CIDR blocks */ if (0 != lseek(ascii_fd, 0, SEEK_SET)) { dcc_pemsg(EX_IOERR, emsg, "lseek(%s, 0, SEEK_SET): %s", wf->ascii_nm, ERROR_STR()); part_result = -1; } else { part_result = dcc_parse_whitefile(emsg, tmp_wf, ascii_fd, ck_dup_white_complain, ck_dup_white_cidr); } } /* if the hash table is tolerable, compute a checksum of it * to detect differing per-user hash tables */ if (part_result >= 0 && map_white_ht(emsg, tmp_wf, tmp_wf->wtbl->hdr.entries)) { MD5_CTX ctx; MD5Init(&ctx); MD5Update(&ctx, (u_char *)&tmp_wf->wtbl->hdr.cidr, sizeof(tmp_wf->wtbl->hdr.cidr)); i = tmp_wf->wtbl->hdr.entries; while (--i >= 0) { MD5Update(&ctx, &tmp_wf->wtbl->tbl[i].type, sizeof(tmp_wf->wtbl->tbl[i].type)); MD5Update(&ctx, tmp_wf->wtbl->tbl[i].sum, sizeof(tmp_wf->wtbl->tbl[i].sum)); } MD5Final(tmp_wf->wtbl->hdr.ck_sum, &ctx); } #ifdef DCC_WIN32 /* unlock the file before closing it to keep Win95 happy */ dcc_unlock_fd(0, ascii_fd, DCC_LOCK_ALL_FILE, "whitelist ", tmp_wf->ascii_nm); #endif if (close(ascii_fd) < 0) dcc_trace_msg("close(%s): %s", wf->ascii_nm, ERROR_STR()); if (part_result < 0) return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf); result = (part_result > 0) ? DCC_WHITE_OK : DCC_WHITE_CONTINUE; /* ensure continued complaints about errors */ if (result == DCC_WHITE_CONTINUE) tmp_wf->wtbl->hdr.reparse = time(0) + DCC_WHITE_REPARSE_DELAY; sync_white(tmp_wf); #ifdef DCC_WIN32 /* WIN32 prohibits renaming open files * and there is little or no concurrency on WIN32 DCC clients * So lock the original file and copy to it. */ close_white_ht(0, wf); wf->ht_fd = dcc_lock_open(emsg, wf->ht_nm, O_RDWR | O_CREAT, 0, DCC_LOCK_ALL_FILE, 0); if (wf->ht_fd < 0) return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf); if (!map_white_ht(emsg, wf, tmp_wf->wtbl_entries)) return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf); memcpy(wf->wtbl, tmp_wf->wtbl, tmp_wf->wtbl_size); close_white_ht(emsg, tmp_wf); unlink(tmp_wf->ht_nm); #else part_result = rename(tmp_wf->ht_nm, wf->ht_nm); if (0 > part_result && dcc_get_priv_home(wf->ht_nm)) { part_result = rename(tmp_wf->ht_nm, wf->ht_nm); dcc_rel_priv(); } if (0 > part_result) { dcc_pemsg(EX_IOERR, emsg, "rename(%s, %s): %s", tmp_wf->ht_nm, wf->ht_nm, ERROR_STR()); return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf); } #endif close_white_ht(0, tmp_wf); if (dcc_clnt_debug > 1) dcc_trace_msg("finished parsing %s", wf->ascii_nm); part_result = open_white_ht(emsg, wf); if (part_result < 0) result = DCC_WHITE_COMPLAIN; else if (part_result == 0 && result == DCC_WHITE_OK) result = DCC_WHITE_CONTINUE; wf->next_stat_time = time(0) + DCC_WHITE_STAT_DELAY; return result; } /* see that a local whitelist is ready * on failure the file is not locked * The caller must lock the DCC_WF if necessary */ DCC_WHITE_RESULT dcc_rdy_white(DCC_EMSG emsg, DCC_WF *wf, DCC_WF *tmp_wf) { time_t now; int i; if (wf->need_reopen) { /* The resolver thread has change the hash table in the file * and possibly renamed it. * We need to re-open the hash table */ wf->broken = 0; wf->reparse = 0; close_white_ht(0, wf); } now = time(0); if (emsg) *emsg = '\0'; if (wf->ht_fd >= 0) { /* The hash table is open. * If we have checked recently, assume everything is good. */ if (!DCC_IS_TIME(now, wf->next_stat_time, DCC_WHITE_STAT_DELAY)) return DCC_WHITE_OK; i = stat_white_nms(emsg, wf); if (i > 0) return DCC_WHITE_OK; if (i < 0) { /* Things are broken or not open, so try to open the * hash table. * We may be racing here. Be happy if another process * fixes the hash table while we stall trying to open * it locked. */ i = open_white_ht(emsg, wf); if (i < 0) return DCC_WHITE_COMPLAIN; if (i > 0) i = stat_white_nms(emsg, wf); } } else { if (wf->ascii_nm[0] == '\0') { dcc_pemsg(EX_NOINPUT, emsg, "no whitelist"); return DCC_WHITE_NOFILE; } if (STILL_BROKE(wf, now)) { /* only check for a missing file occassionally */ if (wf->wf_flags & DCC_WF_NOFILE) { dcc_pemsg(EX_NOINPUT, emsg, "%s does not exist", wf->ascii_nm); return DCC_WHITE_NOFILE; } /* If things are broken, and it has not been a while, * then assume things are still broken. */ dcc_pemsg(EX_DATAERR, emsg, "%s still broken", wf->ascii_nm); return (dcc_clnt_debug > 2 ? DCC_WHITE_COMPLAIN : DCC_WHITE_SILENT); } i = open_white_ht(emsg, wf); if (i < 0) return DCC_WHITE_COMPLAIN; if (i > 0) i = stat_white_nms(emsg, wf); } if (i > 0) return DCC_WHITE_OK; /* Try to let the resolver thread wait for the DNS chitchat * for host names that might now be in the ASCII file. * To avoid racing with the resolver thread to delete the main * hash files, fail if there is no hash table and this is not * the resolver thread. */ if (i == 0 && !(wf->wf_flags & DCC_WF_PER_USER) && dcc_clnt_wake_resolve()) { if (wf->wtbl) return DCC_WHITE_OK; return (dcc_clnt_debug > 2 ? DCC_WHITE_COMPLAIN : DCC_WHITE_SILENT); } if (STILL_BROKE(wf, now)) { dcc_pemsg(EX_DATAERR, emsg, "%s still broken", wf->ascii_nm); if (i == 0 && wf->wtbl) return (dcc_clnt_debug > 2 ? DCC_WHITE_CONTINUE : DCC_WHITE_SILENT); return (dcc_clnt_debug > 2 ? DCC_WHITE_COMPLAIN : DCC_WHITE_SILENT); } if (emsg && *emsg != '\0') { if (i < 0) { dcc_error_msg("%s", emsg); } else if (dcc_clnt_debug > 2) { dcc_trace_msg("%s", emsg); } *emsg = '\0'; } switch (make_white_hash(emsg, wf, tmp_wf)) { case DCC_WHITE_OK: wf->wf_flags &= ~DCC_WF_NOFILE; return DCC_WHITE_OK; /* all is good */ case DCC_WHITE_CONTINUE: /* minor syntax error or bad hostname */ wf->wf_flags &= ~DCC_WF_NOFILE; return DCC_WHITE_CONTINUE; case DCC_WHITE_SILENT: /* no new message */ wf->wf_flags &= ~DCC_WF_NOFILE; return DCC_WHITE_CONTINUE; case DCC_WHITE_NOFILE: wf->wf_flags |= DCC_WF_NOFILE; return DCC_WHITE_NOFILE; case DCC_WHITE_COMPLAIN: default: is_broken(wf); wf->wf_flags &= ~DCC_WF_NOFILE; return DCC_WHITE_COMPLAIN; } } static u_char lookup_white(DCC_EMSG emsg, DCC_TGTS *tgtsp, /* value if hit else DCC_TGTS_INVALID */ DCC_WF *wf, const DCC_GOT_CKS *cks, const DCC_GOT_SUM *g) { const DCC_WHITE_ENTRY *e; const DCC_WHITE_CIDR_ENTRY *cidrp; int bits; e = find_white(emsg, wf, g->type, g->sum, 0); if (e == FIND_WHITE_BROKEN) { *tgtsp = DCC_TGTS_OK; return 0; } if (!e) { if (g->type != DCC_CK_IP) { *tgtsp = DCC_TGTS_INVALID; return 1; } /* if we had no hit and it is an IP address, * check the CIDR blocks */ bits = 0; cidrp = &wf->wtbl->hdr.cidr.e[wf->wtbl->hdr.cidr.len]; while (cidrp != wf->wtbl->hdr.cidr.e) { --cidrp; /* look for the longest match */ if (cidrp->bits <= bits) continue; if (DCC_IN_BLOCK(cks->ip_addr, cidrp->addr, cidrp->mask)) { *tgtsp = cidrp->tgts; bits = cidrp->bits; } } if (bits == 0) *tgtsp = DCC_TGTS_INVALID; return 1; } *tgtsp = e->tgts; return 1; } /* check a local whitelist for a single checksum * on exit the file is locked except after an error */ DCC_WHITE_RESULT dcc_white_sum(DCC_EMSG emsg, DCC_WF *wf, /* in this whitelist */ DCC_CK_TYPES type, const DCC_SUM sum, /* look for this checksum */ DCC_TGTS *tgtsp, /* set only if we find the checksum */ DCC_WHITE_LISTING *listingp) { DCC_WHITE_ENTRY *e; DCC_WHITE_RESULT result; result = dcc_rdy_white(emsg, wf, &cmn_tmp_wf); switch (result) { case DCC_WHITE_OK: case DCC_WHITE_CONTINUE: break; case DCC_WHITE_SILENT: case DCC_WHITE_NOFILE: case DCC_WHITE_COMPLAIN: *listingp = DCC_WHITE_RESULT_FAILURE; return result; } e = find_white(emsg, wf, type, sum, 0); if (e == FIND_WHITE_BROKEN) { *listingp = DCC_WHITE_RESULT_FAILURE; return DCC_WHITE_COMPLAIN; } if (!e) { *listingp = DCC_WHITE_UNLISTED; } else if (e->tgts == DCC_TGTS_OK2 && type == DCC_CK_ENV_TO) { /* deprecated mechanism for turn DCC checks on and off for * individual targets */ *tgtsp = e->tgts; *listingp = DCC_WHITE_USE_DCC; } else if (e->tgts == DCC_TGTS_OK) { *tgtsp = e->tgts; *listingp = DCC_WHITE_LISTED; } else if (e->tgts == DCC_TGTS_TOO_MANY) { *tgtsp = e->tgts; *listingp = DCC_WHITE_BLACK; } else { *listingp = DCC_WHITE_UNLISTED; } return result; } /* see if an IP addess is that of one of our MX servers * the caller must lock cmn_wf if necessray */ u_char /* 0=problems */ dcc_white_mx(DCC_EMSG emsg, DCC_TGTS *tgtsp, /* !=0 if listed */ const DCC_GOT_CKS *cks) /* this IP address checksum */ { u_char result; result = 1; switch (dcc_rdy_white(emsg, &cmn_wf, &cmn_tmp_wf)) { case DCC_WHITE_OK: break; case DCC_WHITE_CONTINUE: result = 0; break; case DCC_WHITE_NOFILE: *tgtsp = 0; return 1; case DCC_WHITE_SILENT: case DCC_WHITE_COMPLAIN: *tgtsp = 0; return 0; } if (cks->sums[DCC_CK_IP].type != DCC_CK_IP) { *tgtsp = 0; return result; } if (!lookup_white(emsg, tgtsp, &cmn_wf, cks, &cks->sums[DCC_CK_IP])) return 0; return result; } /* See what a local whitelist file says about the checksums for a message. * The message is whitelisted if at least one checksum is in the local * whitelist or if there are two or more OK2 values. * Otherwise it is blacklisted if at least one checksum is. * The caller must lock the DCC_WF if necessary. */ DCC_WHITE_RESULT /* whether the lookup worked */ dcc_white_cks(DCC_EMSG emsg, DCC_WF *wf, DCC_GOT_CKS *cks, /* these checksums */ DCC_CKS_WTGTS wtgts, /* whitelist targets, each cks->sums */ DCC_WHITE_LISTING *listingp) /* the answer found */ { const DCC_GOT_SUM *g; int inx; DCC_TGTS tgts, prev_tgts; DCC_WHITE_RESULT result; result = dcc_rdy_white(emsg, wf, &cmn_tmp_wf); switch (result) { case DCC_WHITE_OK: case DCC_WHITE_CONTINUE: break; case DCC_WHITE_NOFILE: case DCC_WHITE_COMPLAIN: case DCC_WHITE_SILENT: *listingp = DCC_WHITE_RESULT_FAILURE; return result; } /* look for each checksum in the hash file */ *listingp = DCC_WHITE_UNLISTED; prev_tgts = DCC_TGTS_INVALID; for (g = &cks->sums[inx = DCC_CK_TYPE_FIRST]; g <= LAST(cks->sums); ++g, ++inx) { /* ignore checksums we don't have */ if (g->type == DCC_CK_INVALID) continue; if (!lookup_white(emsg, &tgts, wf, cks, g)) { *listingp = DCC_WHITE_RESULT_FAILURE; return DCC_WHITE_COMPLAIN; } if (tgts == DCC_TGTS_INVALID) { /* report any body checksums as spam for a spam trap */ if ((wf->wtbl_flags & DCC_WHITE_FG_TRAPS) && DCC_CK_IS_BODY(g->type)) tgts = DCC_TGTS_TOO_MANY; else continue; } if (wtgts) wtgts[inx] = tgts; if (tgts == DCC_TGTS_OK) { /* found the checksum in our whitelist, * so we have the answer */ *listingp = DCC_WHITE_LISTED; } else if (tgts == DCC_TGTS_OK2) { if (prev_tgts == DCC_TGTS_OK2) { /* two half-white checksums count the same * as a single pure white checksum * and gives the answer */ *listingp = DCC_WHITE_LISTED; continue; } prev_tgts = DCC_TGTS_OK2; } else if (tgts == DCC_TGTS_TOO_MANY) { if (*listingp == DCC_WHITE_UNLISTED) *listingp = DCC_WHITE_BLACK; } } return result; }