Logo Search packages:      
Sourcecode: dcc version File versions

ckwhite.c

/* Distributed Checksum Clearinghouse
 *
 * check checksums in the local whitelist
 *
 * Copyright (c) 2005 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 for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * Parties not eligible to receive a license under this agreement can
 * obtain a commercial license to use DCC and permission to use
 * U.S. Patent 6,330,590 by contacting Commtouch at http://www.commtouch.com/
 * or by email to nospam@commtouch.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.42-1.110 $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->info
          && 0 > MSYNC(wf->info, wf->info_size, MS_SYNC))
            dcc_error_msg("msync(%s): %s", wf->ht_nm, ERROR_STR());
}



static void
unmap_white_ht(DCC_WF *wf)
{
      if (!wf->info)
            return;

      sync_white(wf);
#ifdef DCC_WIN32
      win32_unmap(&wf->ht_map, wf->info, wf->ht_nm);
#else
      if (0 > munmap((void *)wf->info, wf->info_size))
            dcc_error_msg("munmap(%s,%d): %s",
                        wf->ht_nm, wf->info_size, ERROR_STR());
#endif
      wf->info = 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->info) {
            wf->info = win32_map(emsg, &wf->ht_map, wf->ht_nm, wf->ht_fd,
                             size);
            if (!wf->info)
                  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->info = p;
#endif
      wf->info_size = size;
      wf->info_entries = entries;
      wf->info_flags = wf->info->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;

      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->info && !(wf->wf_flags & DCC_WF_RO))
            wf->info->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 (result < 0
          && now
          && DCC_IS_TIME(now, wf->broken, DCC_WHITE_PARSE_DELAY))
            wf->broken = now + DCC_WHITE_PARSE_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) - STRZ(DCC_WHITE_SUFFIX)) {
            dcc_pemsg(EX_NOINPUT, emsg, "bad whitelist file name \"%s\"",
                    wf->ascii_nm);
            wf->broken = time(0) + DCC_WHITE_PARSE_DELAY;
            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 (!fnm2path(new_path, new_white_nm, 0)) {
            dcc_pemsg(EX_USAGE, emsg, "long whitelist name \"%s\"",
                    new_white_nm);
            return 0;
      }

      len = strlen(new_path);
      i = len - STRZ(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)
            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);
            wf->broken = time(0) + DCC_WHITE_PARSE_DELAY;
            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);
            if (wf->ht_sb.st_size < ISZ(DCC_WHITE_MAGIC)) {
                  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));
            }
            entries = MAX_WHITE_ENTRIES+1;
            /* temporarily map the file to check the magic string below */
            if (!map_white_ht(emsg, wf, 0))
                  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 (size < 0
          || memcmp(&wf->info->magic, &white_magic,
                  sizeof(white_magic))) {
            /* rebuild old format files */
            if (!memcmp(&wf->info->magic, WHITE_MAGIC_B_STR,
                      sizeof(WHITE_MAGIC_B_STR)-1))
                  dcc_trace_msg("%s is obsolete %s",
                              wf->ht_nm, wf->info->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->info->hdr.entries) {
            dcc_pemsg(EX_NOINPUT, emsg,
                    "impossible size of whitelist %s: "OFF_DPAT,
                    wf->ht_nm, wf->ht_sb.st_size);
            return unlink_white_ht(wf, time(0));
      }

      if (wf->info->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->changed = 0;
      wf->info_flags = wf->info->hdr.flags;

      /* the wlist command works on both per-user and global whitelists */
      if (wf->wf_flags & DCC_WF_WLIST_CMD) {
            if (wf->info_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 (tmp_wf->wf_flags & DCC_WF_RO) {
            dcc_pemsg(EX_IOERR, emsg,
                    "read only access; cannot create whitelist %s",
                    tmp_wf->ht_nm);
            tmp_wf->ht_nm[0] = '\0';
            return -1;
      }

      if (!new_ht_nm(emsg, tmp_wf, 1)) {
            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->info->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->info->bins);
      if (binp)
            *binp = bin;
      inx = wf->info->bins[bin];

      for (loop_cnt = wf->info->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->info_entries) {
                  if (inx >= wf->info->hdr.entries) {
                        dcc_pemsg(EX_DATAERR, emsg,
                                "bogus index %u in %s",
                                inx, wf->ht_nm);
                        if (!(wf->wf_flags & DCC_WF_RO))
                              wf->info->hdr.ascii_mtime = 0;
                        sync_white(wf);
                        return FIND_WHITE_BROKEN;
                  }
                  if (!map_white_ht(emsg, wf, wf->info->hdr.entries))
                        return 0;
            }
            e = &wf->info->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->info->hdr.entries+1,
              wf->ht_nm,
              (DCC_WHITE_INX)(accum % DIM(wf->info->bins)), inx,
              dcc_type2str_err(type, 0, 0, 0), dcc_ck2str_err(type, sum));

      if (!(wf->wf_flags & DCC_WF_RO))
            wf->info->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->info
                || (wf->wf_flags & DCC_WF_RO)) {
                  dcc_trace_msg("stat(%s): %s", nm, ERROR_STR());
                  *resultp = 0;
                  return 1;
            }

            /* only complain if an ASCII file disappears temporarily */
            now = time(0);
            if (wf->broken == 0) {
                  dcc_trace_msg("%s disappeared: %s", nm, ERROR_STR());
                  wf->broken = now + DCC_WHITE_BRIDGE_SECS;
                  wf->info->hdr.broken = wf->broken;
                  *resultp = 1;
                  return 1;
            }
            if (DCC_IS_TIME(now, wf->broken, DCC_WHITE_BRIDGE_SECS)) {
                  if (dcc_clnt_debug > 1)
                        dcc_trace_msg("pay attention to stat(%s): %s",
                                    nm, ERROR_STR());
                  *resultp = 0;
            } else {
                  if (dcc_clnt_debug > 1)
                        dcc_trace_msg("temporarily ignoring"
                                    " stat(%s): %s",
                                    nm, ERROR_STR());
                  *resultp = 1;
            }
            return 1;
      }

      if (sb.st_mtime != ascii_mtime) {
            *resultp = 0;
            return 1;
      }

      return 0;
}



/* see if the ASCII files have changed */
static int                    /* 1=unchanged 0=changed -1=broken */
stat_white_nms(DCC_EMSG emsg, DCC_WF *wf)
{
      struct stat ht_sb;
      time_t now;
      u_char fin;
      int i, result;

      if (!wf->info)
            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->info->hdr.reparse)
            wf->reparse = wf->info->hdr.reparse;
      if (wf->broken < wf->info->hdr.broken)
            wf->broken = wf->info->hdr.broken;

      /* seriously broken hash tables are unusable */
      if (wf->info->hdr.ascii_mtime == 0)
            return -1;
      /* pretend things are fine if they are recently badly broken */
      if (!DCC_IS_TIME(now, wf->broken, DCC_WHITE_PARSE_DELAY))
            return 1;

      /* force periodic reparsing of syntax errors to nag in system log */
      if (wf->reparse != 0
          && DCC_IS_TIME(now, wf->reparse, DCC_WHITE_PARSE_DELAY))
            return 0;

      /* if the main ASCII file has disappeared,
       * leave the hash file open and just complain,
       * but only for a while */
      fin = stat_1white_nm(&result, wf, wf->ascii_nm,
                       wf->info->hdr.ascii_mtime);
      /* see if any of the included ASCII files are new */
      for (i = 0; i < DIM(wf->info->hdr.white_incs); ++i) {
            if (wf->info->hdr.white_incs[i].nm[0] == '\0')
                  break;
            /* stop at the first missing or changed included file */
            fin |= stat_1white_nm(&result, wf,
                              wf->info->hdr.white_incs[i].nm,
                              wf->info->hdr.white_incs[i].mtime);
      }
      if (fin)
            return result;

      if ((wf->info_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->info_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->info->hdr.flags & DCC_WHITE_FG_HOSTNAMES)
          && DCC_IS_TIME(now, wf->ht_sb.st_mtime+DCC_RE_RESOLVE,
                     DCC_RE_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->info) {
#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->info+pos, buf, buf_len);
            return 1;
#else
            /* some UNIX systems have coherence trouble without msync() */
            if (0 > MSYNC(wf->info, wf->info_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->info->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->info->hdr.entries >= MAX_WHITE_ENTRIES) {
            dcc_pemsg(EX_DATAERR, emsg, "more than maximum %d entries%s",
                    wf->info->hdr.entries,wf_fnm_lno(fnm_buf, wf));
            return -1;
      }
      end = ENTRIES2SIZE(wf->info->hdr.entries);
      wf->info->bins[bin] = ++wf->info->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 IPv4 /25 (MAX_CIDR_BITS) and smaller */
      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->info->hdr.cidr.len;
      for (e = wf->info->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->info->hdr.cidr.len >= DIM(wf->info->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->info->hdr.cidr.len;

      return 1;
}



static void
dup_msg(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_trace_msg("%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_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;
      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)
{
      DCC_WHITE_ENTRY *e;
      DCC_WHITE_INX bin;

      /* find the hash chain for the new entry */
      e = find_white(emsg, wf, type, sum, &bin);
      if (e == FIND_WHITE_BROKEN)
            return -1;
      if (!e) {
            dcc_pemsg(EX_DATAERR, emsg, "entry disappeared in %s",
                    wf->ht_nm);
            return -1;
      }

      /* ignore perfect duplicates */
      if (e->tgts == tgts)
            return 1;

      dup_msg(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 1;
}



/* without brute force checks that would take too long,
 * it is impossible to check that a CIDR block does not collide with
 * individual entries that are already in the hash table */
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;

      i = wf->info->hdr.cidr.len;
      for (e = wf->info->hdr.cidr.e; i > 0; ++e, --i) {
            if (!DCC_IN_BLOCK(e->addr, *addrp, *maskp)
                && !DCC_IN_BLOCK(*addrp, e->addr, e->mask))
                  continue;

            if (e->tgts == tgts)
                  continue;

            dup_msg(wf, e->fno, e->lno, e->tgts, tgts);

            /* try to 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);
                  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;
}



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';
      }

      if (wf->info) {
            /* we have a usable file open
             *
             * 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->info->hdr.broken = wf->broken;
                  sync_white(wf);
            }
            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;
      int part_result, i;

      if (dcc_clnt_debug > 1)
            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)
            have_cwf_lock();
#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);
                  unlink(wf->ht_nm);
                  if (dcc_clnt_debug > 1)
                        dcc_trace_msg("delete %s after %s missing",
                                    fnm2path_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->info)
                  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->info->hdr.flags |= DCC_WHITE_FG_PER_USER;

      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->info->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,
                                          ck_dup_white_cidr);
            }
      }

      /* if the hash table is reasonable, compute a checksum of it
       * to detect differing hash tables */
      if (part_result >= 0
          && map_white_ht(emsg, tmp_wf, tmp_wf->info->hdr.entries)) {
            MD5_CTX ctx;

            MD5Init(&ctx);
            MD5Update(&ctx, (u_char *)&tmp_wf->info->hdr.cidr,
                    sizeof(tmp_wf->info->hdr.cidr));
            i = tmp_wf->info->hdr.entries;
            while (--i >= 0) {
                  MD5Update(&ctx, &tmp_wf->info->tbl[i].type,
                          sizeof(tmp_wf->info->tbl[i].type));
                  MD5Update(&ctx, tmp_wf->info->tbl[i].sum,
                          sizeof(tmp_wf->info->tbl[i].sum));
            }
            MD5Final(tmp_wf->info->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->info->hdr.reparse = time(0) + DCC_WHITE_PARSE_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->info_entries))
            return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
      memcpy(wf->info, tmp_wf->info, tmp_wf->info_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;

      now = time(0);

      if (wf->changed) {
            wf->broken = 0;
            wf->reparse = 0;
            close_white_ht(0, wf);
      }

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

            /* only check for a missing file occassionally */
            if ((wf->wf_flags & DCC_WF_NOFILE)
                && !DCC_IS_TIME(now, wf->broken, DCC_WHITE_STAT_DELAY)) {
                  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. */
            if (!DCC_IS_TIME(now, wf->broken, DCC_WHITE_PARSE_DELAY)) {
                  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 */
      if (i == 0
          && wf->info
          && !(wf->wf_flags & DCC_WF_PER_USER)
          && dcc_clnt_wake_resolve())
            return DCC_WHITE_OK;

      if (!DCC_IS_TIME(now, wf->broken, DCC_WHITE_PARSE_DELAY)) {
            dcc_pemsg(EX_DATAERR, emsg, "%s still broken", wf->ascii_nm);
            if (i == 0)
                  return (dcc_clnt_debug > 2
                        ? DCC_WHITE_CONTINUE : DCC_WHITE_OK);
            return (dcc_clnt_debug > 2
                  ? DCC_WHITE_COMPLAIN : DCC_WHITE_SILENT);
      }

      if (emsg && *emsg != '\0'
          && (i < 0 || dcc_clnt_debug > 1))
            dcc_error_msg("%s", emsg);

      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:      /* syntax error or bad hostname */
      case DCC_WHITE_SILENT:
            wf->wf_flags &= ~DCC_WF_NOFILE;
            return DCC_WHITE_CONTINUE;
      case DCC_WHITE_NOFILE:
            wf->broken = now + DCC_WHITE_STAT_DELAY;
            wf->wf_flags |= DCC_WF_NOFILE;
            return DCC_WHITE_NOFILE;
      case DCC_WHITE_COMPLAIN:
      default:
            wf->broken = now + DCC_WHITE_PARSE_DELAY;
            wf->wf_flags &= ~DCC_WF_NOFILE;
            return DCC_WHITE_COMPLAIN;
      }
}



static u_char
lookup_white(DCC_EMSG emsg, DCC_TGTS *tgtsp, 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->info->hdr.cidr.e[wf->info->hdr.cidr.len];
            while (cidrp != wf->info->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,
            DCC_CK_TYPES type, const DCC_SUM sum,
            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) {
            *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
dcc_white_cks(DCC_EMSG emsg, DCC_WF *wf,
            const DCC_GOT_CKS *cks, /* these checksums */
            DCC_CKS_WTGTS wtgts,    /* whitelist targets */
            DCC_WHITE_LISTING *listingp)
{
      const DCC_GOT_SUM *g;
      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[DCC_CK_TYPE_FIRST]; g <= LAST(cks->sums); ++g) {
            /* 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)
                  continue;

            if (wtgts)
                  wtgts[g - cks->sums] = 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;
}

Generated by  Doxygen 1.6.0   Back to index