Logo Search packages:      
Sourcecode: dcc version File versions  Download package

ask.c

/* Distributed Checksum Clearinghouse
 *
 * ask about a batch of checksums
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * 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.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * 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.2.74-1.63 $Revision$
 */

#include "dcc_ck.h"
#include "dcc_heap_debug.h"
#include "dcc_xhdr.h"

#define SET_ALL_THOLDS  DCC_CK_INVALID
#define ALL_CKSUMS_STR  "ALL"
#define SET_CMN_THOLDS  (DCC_CK_TYPE_LAST+1)
#define CMN_CKSUMS_STR  "CMN"
#define IS_CMN_CKSUM(t) ((t) == DCC_CK_BODY     \
                   || (t) == DCC_CK_FUZ1  \
                   || (t) == DCC_CK_FUZ2)

static DCC_TGTS dcc_tholds_spam[DCC_DIM_CKS];
static DCC_TGTS dcc_tholds_log[DCC_DIM_CKS];
static u_char dcc_honor_nospam[DCC_DIM_CKS];

static u_char trim_grey_ip_addr;    /* remove IP address from grey triple */
static struct in6_addr grey_ip_mask;

static void dcc_honor_cnt(u_int *, DCC_CK_TYPES, DCC_TGTS);


int                           /* 1=ok, 0=no answer, -1=failed */
dcc_ask(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt,
      u_char flags,                 /* DCC_CLNT_FG_* */
      DCC_HEADER_BUF *header,       /* put results here */
      DCC_GOT_CKS *cks,       /*    and here */
      u_int *honorp,                /*    and here */
      DCC_TGTS local_tgts)          /* report these targets to DCC server */
{
      /* report and response buffers */
      DCC_QUERY_REPORT rpt;
      union {
          DCC_HDR hdr;
          DCC_QUERY_RESP r;
          DCC_ERROR     error;
      } resp;
      DCC_OPS op;
      DCC_CK *ck;
      DCC_GOT_SUM *g;
      DCC_TGTS srvr_tgts;
      DCC_CKS_WTGTS hdr_tgts;
      u_int num_cks, recv_len, exp_len;
      DCC_CK_TYPES type;
      DCC_SRVR_ID srvr_id;
      int resp_num, i;

      /* prepare a report for the nearest DCC server */
      memset(&rpt, 0, sizeof(rpt));
      rpt.tgts = htonl(local_tgts);
      ck = rpt.cks;
      num_cks = 0;
      for (g = cks->sums; g <= &cks->sums[DCC_CK_TYPE_LAST]; ++g) {
            /* never tell the DCC server about some headers */
            if (!g->rpt)
                  continue;
            ck->type = g->type;
            ck->len = sizeof(*ck);
            memcpy(ck->sum, g->sum, sizeof(ck->sum));
            ++ck;
            ++num_cks;
      }

      /* send the report and see what the DCC has to say */
      op = (local_tgts == 0) ? DCC_OP_QUERY : DCC_OP_REPORT;
      if (num_cks != 0) {
            i = dcc_clnt_op(emsg, ctxt, flags, 0, &srvr_id,
                        &rpt.hdr, (sizeof(rpt) - sizeof(rpt.cks)
                                 + num_cks*sizeof(rpt.cks[0])),
                        op, &resp.hdr, sizeof(resp), 0);
            /* try a query to different server if the first failed
             * but a second was found */
            if (!i && srvr_id != DCC_ID_INVALID) {
                  if (dcc_clnt_debug) {
                        if (emsg && *emsg != '\0') {
                              dcc_trace_msg("retry after: %s", emsg);
                              *emsg = '\0';
                        }
                        dcc_trace_msg("retry with different server");
                  }
                  if (local_tgts != DCC_TGTS_TOO_MANY)
                        op = DCC_OP_QUERY;
                  i = dcc_clnt_op(emsg, ctxt, flags, 0, &srvr_id,
                              &rpt.hdr,
                              (sizeof(rpt) - sizeof(rpt.cks)
                               + num_cks*sizeof(rpt.cks[0])),
                              op, &resp.hdr, sizeof(resp), 0);
            }
            if (!i) {
                  *honorp |= DCC_HONOR_LOGIT;
                  header->buf[0] = '\0';
                  return 0;
            }

            /* forget about it if the DCC server responded strangely */
            recv_len = htons(resp.r.hdr.len);
            if (resp.hdr.op != DCC_OP_QUERY_RESP) {
                  dcc_pemsg(EX_UNAVAILABLE, emsg, "DCC %s: %s %*s",
                          dcc_srvr_nm(),
                          dcc_hdr_op2str(&resp.hdr),
                          (resp.hdr.op == DCC_OP_ERROR
                           ? (recv_len - (ISZ(resp.error)
                                      - ISZ(resp.error.msg)))
                           : 0),
                          resp.error.msg);
                  *honorp |= DCC_HONOR_LOGIT;
                  header->buf[0] = '\0';
                  return -1;
            }
            exp_len = (sizeof(resp.r) - sizeof(resp.r.body.tgts)
                     + num_cks*sizeof(resp.r.body.tgts[0]));
            if (recv_len != exp_len) {
                  dcc_pemsg(EX_UNAVAILABLE, emsg,
                          "DCC %s: answered with %d"
                          " instead of %d bytes",
                          dcc_srvr_nm(), recv_len, exp_len);
                  *honorp |= DCC_HONOR_LOGIT;
                  header->buf[0] = '\0';
                  return -1;
            }
      }

      /* check the server's response to see if we have spam */
      resp_num = 0;
      for (g = cks->sums; g <= &cks->sums[DCC_CK_TYPE_LAST]; ++g) {
            if (!g->rpt) {
                  /* pretend we always have a basic body checksum */
                  if (g == &cks->sums[DCC_CK_BODY])
                        dcc_honor_cnt(honorp, DCC_CK_BODY, 0);
                  continue;
            }
            type = g->type;
            srvr_tgts = ntohl(resp.r.body.tgts[resp_num++]);

            /* if we switched servers and converted a report
             * to a query, then guess the total that the
             * server would have produced for a report.
             * Assume the server is not running with -K and so
             * its value would have changed from zero only
             * for a body checksum */
            if (op == DCC_OP_QUERY
                && local_tgts != 0
                && (srvr_tgts != 0 || DCC_CK_IS_BODY(type))
                && srvr_tgts < DCC_TGTS_TOO_MANY) {
                  srvr_tgts += local_tgts;
                  if (srvr_tgts > DCC_TGTS_TOO_MANY)
                        srvr_tgts = DCC_TGTS_TOO_MANY;
            }
            hdr_tgts[type] = srvr_tgts;
            if (srvr_tgts >= local_tgts
                && local_tgts != DCC_TGTS_TOO_MANY) {
                  /* infer server's value before our report */
                  if (srvr_tgts >= DCC_TGTS_TOO_MANY)
                        g->tgts = srvr_tgts;
                  else
                        g->tgts = srvr_tgts - local_tgts;
                  /* honor the total */
                  dcc_honor_cnt(honorp, type, srvr_tgts);
            }
      }

      /* generate the header line now that we have checked all of
       * the counts against their thresholds and so know if we
       * must add "bulk". */
      dcc_header_init(header, srvr_id);
      if (*honorp & (DCC_HONOR_SRVR_ISSPAM
                   | DCC_HONOR_LOCAL_ISSPAM
                   | DCC_HONOR_MTA_ISSPAM))
            dcc_add_header(header, DCC_XHDR_BULK);

      for (g = cks->sums; g <= &cks->sums[DCC_CK_TYPE_LAST]; ++g) {
            if (!g->rpt) {
                  /* pretend we always have a body checksum */
                  if (g == &cks->sums[DCC_CK_BODY])
                        dcc_add_header_ck(header, DCC_CK_BODY, 0);
                  continue;
            }
            type = g->type;
            /* Add interesing counts to the header.
             * Body checksums are always interestig if we have them..
             * Pretend we always have a basic body checksum. */
            if (hdr_tgts[type] != 0 || DCC_CK_IS_BODY(type))
                  dcc_add_header_ck(header, type, hdr_tgts[type]);
      }

      return 1;
}



/* Check the local whitelist for dccproc or dccsight but not dccifd or dccm.
 * If the whitelist does not give an unambiguous answer and allows the
 *    the use of the DCC,,
 * ask the DCC server about the checksums of a message */
u_char                              /* 1=ok 0=something to complain about */
dcc_white_ask(DCC_EMSG emsg,
            DCC_WF *wf,
            DCC_CLNT_CTXT *ctxt,
            DCC_HEADER_BUF *header, /* put header here */
            u_int *honorp,          /* put state bites here */
            const char *white_nm,   /* must name whitelist every time */
            DCC_GOT_CKS *cks,       /* these checksums */
            DCC_CKS_WTGTS wtgts,    /* whitelist targets */
            DCC_TGTS local_tgts)    /* number of addressees */
{
      header->buf[0] = '\0';
      *honorp &= ~(DCC_HONOR_SRVR_OK2
                 | DCC_HONOR_SRVR_NOTSPAM
                 | DCC_HONOR_SRVR_ISSPAM
                 | DCC_HONOR_LOCAL_ISSPAM
                 | DCC_HONOR_LOCAL_NOTSPAM);

      if (wtgts)
            memset(wtgts, 0, sizeof(*wtgts));

      /* check whitelist
       * don't filter if something is wrong with it */
      if (white_nm) {
            switch (dcc_white_cks(emsg, wf, cks, wtgts, white_nm, 0, 0)) {
            case DCC_WHITE_ERROR:
                  *honorp |= DCC_HONOR_LOGIT;
                  return 0;
            case DCC_WHITE_LISTED:
                  *honorp |= DCC_HONOR_LOCAL_NOTSPAM;
                  cks->flags &= ~DCC_CKS_HAVE_SUM;
                  break;
            case DCC_WHITE_HALF_LISTED:
            case DCC_WHITE_UNLISTED:
                  break;
            case DCC_WHITE_BLACK:
                  *honorp |= (DCC_HONOR_LOGIT | DCC_HONOR_LOCAL_ISSPAM);
                  local_tgts = DCC_TGTS_TOO_MANY;
                  break;
            }
            if (wf->info_flags & DCC_WHITE_FG_LOG_ALL)
                  *honorp |= DCC_HONOR_LOGIT;
            dcc_wf_unlock(wf);
      }

      /* pay attention to DNS blacklist if we do not already have an answer */
      if (cks->dnsbl
          && cks->dnsbl->hit != DNSBL_HIT_NONE
          && !(*honorp & (DCC_HONOR_SRVR_NOTSPAM
                      | DCC_HONOR_SRVR_ISSPAM
                      | DCC_HONOR_LOCAL_ISSPAM
                      | DCC_HONOR_LOCAL_NOTSPAM))) {
            *honorp |= (DCC_HONOR_LOGIT | DCC_HONOR_LOCAL_ISSPAM);
            local_tgts = DCC_TGTS_TOO_MANY;
      }


      /* if allowed by whitelisting, report our checksums to the DCC
       * and return with that result including setting logging */
      if ((cks->flags & DCC_CKS_HAVE_SUM)
          && !(wf->flags & DCC_WHITE_FG_DCC_OFF))
            return (0 < dcc_ask(emsg, ctxt, DCC_CLNT_FG_NONE,
                            header, cks, honorp, local_tgts));

      /* else honor log threshold for local counts and white-/blacklists */
      dcc_honor_log_cnts(honorp, cks, local_tgts);
      return 1;
}



void
dcc_init_tholds(void)
{
      DCC_TGTS *tgtsp;

      dcc_honor_nospam[DCC_CK_IP] = 1;
      dcc_honor_nospam[DCC_CK_ENV_FROM] = 1;
      dcc_honor_nospam[DCC_CK_FROM] = 1;
      for (tgtsp = dcc_tholds_spam; tgtsp <= LAST(dcc_tholds_spam); ++tgtsp)
            *tgtsp = DCC_TGTS_INVALID;
      for (tgtsp = dcc_tholds_log; tgtsp <= LAST(dcc_tholds_log); ++tgtsp)
            *tgtsp = DCC_TGTS_INVALID;
}



/* parse -g for dccm and dccproc */
void
dcc_parse_honor(const char *arg0)
{
      const char *arg;
      DCC_CK_TYPES type, t2;
      int i;

      arg = arg0;
      if (!CSTRCMP(arg, "not_") || !CSTRCMP(arg, "not-")) {
            arg += STRZ("not_");
            i = 0;
      } else if (!CSTRCMP(arg, "no_") || !CSTRCMP(arg, "no-")) {
            arg += STRZ("no_");
            i = 0;
      } else {
            i = 1;
      }

      type = dcc_str2type(arg);
      if (type == DCC_CK_INVALID
          || type >= DIM(dcc_honor_nospam)) {
            if (!strcasecmp(arg, CMN_CKSUMS_STR)) {
                  type = SET_CMN_THOLDS;
            } else if (!strcasecmp(arg, ALL_CKSUMS_STR)) {
                  type = SET_ALL_THOLDS;
            } else {
                  dcc_error_msg("unrecognized checksum type in \"-g %s\"",
                              arg0);
                  return;
            }
      }
      for (t2 = DCC_CK_TYPE_FIRST; t2 <= DCC_CK_TYPE_LAST; ++t2) {
            if (t2 == type
                || type == SET_ALL_THOLDS
                || (type == SET_CMN_THOLDS
                  && IS_CMN_CKSUM(t2)))
                  dcc_honor_nospam[t2] = i;
      }
}



static void
set_tholds(DCC_TGTS tholds[DCC_DIM_CKS],
         DCC_CK_TYPES type,         /* specified type, DCC_CK_INVALID=all,
                               * SET_CMN_THOLDS=default set */
         DCC_TGTS tgts)
{
      DCC_CK_TYPES t2;

      for (t2 = DCC_CK_TYPE_FIRST; t2 <= DCC_CK_TYPE_LAST; ++t2) {
            if (t2 == type
                || type == SET_ALL_THOLDS
                || (type == SET_CMN_THOLDS
                  && IS_CMN_CKSUM(t2)))
                  tholds[t2] = tgts;
      }
}



/* parse type,[log-thold,]rej-thold */
u_char                              /* 1=need a log directory */
dcc_parse_tholds(char a,
             const char *arg)   /* optarg */
{
      DCC_CK_TYPES type;
      DCC_TGTS log_tgts, spam_tgts;
      char *duparg, *thold_spam, *thold_log;
      u_char log_tgts_set, spam_tgts_set;

      duparg = dcc_strdup(arg);
      thold_log = strchr(duparg, ',');
      if (!thold_log) {
            dcc_error_msg("missing comma in \"-%c %s\"", a, arg);
            dcc_free(duparg);
            return 0;
      }
      *thold_log++ = '\0';

      /* if there is only one threshold, take it as the spam threshold */
      thold_spam = strchr(thold_log, ',');
      if (!thold_spam) {
            thold_spam = thold_log;
            thold_log = 0;
      } else {
            *thold_spam++ = '\0';
      }

      if (!strcasecmp(duparg, ALL_CKSUMS_STR)) {
            type = SET_ALL_THOLDS;
      } else if (!strcasecmp(duparg, CMN_CKSUMS_STR)) {
            type = SET_CMN_THOLDS;
      } else {
            type = dcc_str2type(duparg);
            if (type == DCC_CK_INVALID) {
                  dcc_error_msg("unrecognized checksum type"
                              " \"%s\" in \"-%c %s\"",
                              duparg, a, arg);
                  dcc_free(duparg);
                  return 0;
            }
      }

      log_tgts = DCC_TGTS_INVALID;
      log_tgts_set = 0;
      if (thold_log && *thold_log != '\0') {
            log_tgts = dcc_str2cnt(thold_log);
            if (log_tgts == DCC_TGTS_INVALID
                && strcasecmp(thold_log, "never"))
                  dcc_error_msg("unrecognized count \"%s\" in \"-%c %s\"",
                              thold_log, a, arg);
            else
                  log_tgts_set = 1;
      }


      spam_tgts = DCC_TGTS_INVALID;
      spam_tgts_set = 0;
      if (!thold_spam || *thold_spam == '\0') {
            if (!thold_log || *thold_log == '\0')
                  dcc_error_msg("no thresholds in \"-%c %s\"", a, arg);
      } else {
            spam_tgts = dcc_str2cnt(thold_spam);
            if (spam_tgts == DCC_TGTS_INVALID
                && strcasecmp(thold_spam, "never"))
                  dcc_error_msg("unrecognized count \"%s\" in \"-%c %s\"",
                              thold_spam, a, arg);
            else
                  spam_tgts_set = 1;
      }

      if (log_tgts_set)
            set_tholds(dcc_tholds_log, type, log_tgts);
      if (spam_tgts_set)
            set_tholds(dcc_tholds_spam, type, spam_tgts);

      dcc_free(duparg);
      return log_tgts_set;
}



static void
dcc_honor_cnt(u_int *honorp,        /* previous flag bits */
            DCC_CK_TYPES type,      /* which kind of checksum */
            DCC_TGTS srvr_tgts)     /* total count for the checksum */
{
      if (type >= DIM(dcc_honor_nospam))
            return;

      /* decide whether to notice DCC server's whitelist */
      if (dcc_honor_nospam[type]) {
            if (srvr_tgts == DCC_TGTS_OK) {
                  /* if server says it is not spam then it's not */
                  *honorp |= DCC_HONOR_SRVR_NOTSPAM;
                  *honorp &= ~DCC_HONOR_SRVR_ISSPAM;

            } else if (srvr_tgts == DCC_TGTS_OK2) {
                  /* if server says it is half ok, look for two halves */
                  if (*honorp & DCC_HONOR_SRVR_OK2) {
                        *honorp |= DCC_HONOR_SRVR_NOTSPAM;
                        *honorp &= ~DCC_HONOR_SRVR_ISSPAM;
                  } else {
                        *honorp |= DCC_HONOR_SRVR_OK2;
                  }
            }
      }

      /* reject and log spam */
      if (dcc_tholds_spam[type] != DCC_TGTS_INVALID
          && dcc_tholds_spam[type] <= srvr_tgts
          && srvr_tgts <= DCC_TGTS_TOO_MANY
          && !(*honorp & DCC_HONOR_SRVR_NOTSPAM))
            *honorp |= DCC_HONOR_SRVR_ISSPAM;

      /* log bulky messages */
      if ((*honorp & DCC_HONOR_SRVR_ISSPAM)
          || (dcc_tholds_log[type] != DCC_TGTS_INVALID
            && dcc_tholds_log[type] <= srvr_tgts))
            *honorp |= DCC_HONOR_LOGIT;
}



/* honor log threshold for local counts and white-/blacklists */
void
dcc_honor_log_cnts(u_int *honorp,   /* previous flag bits */
               const DCC_GOT_CKS *cks,  /* these server counts */
               DCC_TGTS tgts)
{
      const DCC_GOT_SUM *g;
      DCC_CK_TYPES type;

      if (tgts == DCC_TGTS_TOO_MANY) {
            if (!(*honorp & DCC_HONOR_LOCAL_NOTSPAM))
                  *honorp |= DCC_HONOR_LOCAL_ISSPAM;
            *honorp |= DCC_HONOR_LOGIT;
            return;
      }

      if (*honorp & DCC_HONOR_LOGIT)
            return;

      /* pretend we always have a body checksum */
      if (dcc_tholds_log[DCC_CK_BODY] != DCC_TGTS_INVALID
          && dcc_tholds_log[DCC_CK_BODY] <= tgts) {
            *honorp |= DCC_HONOR_LOGIT;
            return;
      }

      for (g = cks->sums; g <= LAST(cks->sums); ++g) {
            type = g->type;
            if (type == DCC_CK_INVALID
                || type == DCC_CK_ENV_TO)
                  continue;
            if (dcc_tholds_log[type] == DCC_TGTS_INVALID)
                  continue;
            if (dcc_tholds_log[type] <= tgts) {
                  *honorp |= DCC_HONOR_LOGIT;
                  return;
            }
      }
}



/* parse -G options for DCC clients */
u_char                              /* 0=bad */
dcc_parse_client_grey(char *arg)
{
      int bits;
      char *p;

      while (*arg != '\0') {
            if (dcc_ck_word_comma(&arg, "on")) {
                  grey_on = 1;
                  continue;
            }
            if (dcc_ck_word_comma(&arg, "off")) {
                  grey_on = 0;
                  continue;
            }
            if (dcc_ck_word_comma(&arg, "query")) {
                  grey_query_only = 1;
                  continue;
            }
            if (dcc_ck_word_comma(&arg, "noIP")) {
                  grey_on = 1;
                  trim_grey_ip_addr = 1;
                  memset(&grey_ip_mask, 0, sizeof(grey_ip_mask));
                  continue;
            }
            if (!CSTRCMP(arg, "IPmask/")) {
                  bits = 0;
                  for (p = arg+STRZ("IPmask/");
                       *p >= '0' && *p <= '9';
                       ++p)
                        bits = bits*10 + *p - '0';
                  if (bits > 0 && bits < 128
                      && (*p == '\0' || *p == ',')) {
                        arg = p;
                        if (*p == ',')
                              ++arg;
                        grey_on = 1;
                        trim_grey_ip_addr = 1;
                        /* assume giant blocks are really IPv4 */
                        if (bits <= 32)
                              bits += 128-32;
                        dcc_bits2mask(&grey_ip_mask, bits);
                        continue;
                  }
            }
            return 0;
      }
      return 1;
}



DCC_ASK_GREY_RESULT
dcc_ask_grey(DCC_EMSG emsg,
           DCC_CLNT_CTXT *ctxt,
           DCC_OPS op,        /* DCC_OP_GREY_{REPORT,QUERY,WHITE} */
           DCC_SUM msg_sum,         /* put msg+sender+target cksum here */
           DCC_SUM triple_sum,      /* put greylist triple checksum here */
           const DCC_GOT_CKS *cks,
           const DCC_SUM env_to_sum,
           DCC_TGTS *pembargo_num,
           DCC_TGTS *pearly_tgts,   /* ++ report to DCC even if embargoed */
           DCC_TGTS *plate_tgts)    /* ++ don't report to DCC */
{
      MD5_CTX ctx;
      DCC_QUERY_REPORT rpt;
      union {
          DCC_HDR hdr;
          DCC_QUERY_RESP r;
          DCC_ERROR     error;
      } resp;
      DCC_CK *ck;
      DCC_CK_TYPES type;
      const DCC_GOT_SUM *g;
      DCC_TGTS result_tgts;
      int num_cks, recv_len;

      if (cks->sums[DCC_CK_IP].type != DCC_CK_IP) {
            dcc_pemsg(EX_UNAVAILABLE, emsg,
                    "IP address not available for greylisting");
            memset(triple_sum, 0, sizeof(*triple_sum));
            memset(msg_sum, 0, sizeof(*msg_sum));
            return DCC_GREY_ASK_FAIL;
      }
      if (cks->sums[DCC_CK_ENV_FROM].type != DCC_CK_ENV_FROM) {
            dcc_pemsg(EX_UNAVAILABLE, emsg,
                    "env_From not available for greylisting");
            memset(triple_sum, 0, sizeof(*triple_sum));
            memset(msg_sum, 0, sizeof(*msg_sum));
            return DCC_GREY_ASK_FAIL;
      }

      /* Check the common checksums for whitelisting at the greylist server.
       * This assumes DCC_CK_GREY_TRIPLE > DCC_CK_GREY_MSG > other types */
      memset(&rpt, 0, sizeof(rpt));
      ck = rpt.cks;
      num_cks = 0;
      for (type = 0, g = cks->sums;
           type <= DCC_CK_TYPE_LAST;
           ++type, ++g) {
            /* greylisting needs a body checksum, even if
             * it is the fake checksum for a missing body */
            if (!g->rpt && type != DCC_CK_BODY)
                  continue;
            ck->type = type;
            ck->len = sizeof(*ck);
            memcpy(ck->sum, g->sum, sizeof(ck->sum));
            ++ck;
            ++num_cks;
      }

      /* include the grey message checksum of
       * the body, the sender, and target checksums */
      MD5Init(&ctx);
      MD5Update(&ctx, cks->sums[DCC_CK_BODY].sum, sizeof(DCC_SUM));
      MD5Update(&ctx, cks->sums[DCC_CK_ENV_FROM].sum, sizeof(DCC_SUM));
      MD5Update(&ctx, env_to_sum, sizeof(DCC_SUM));
      MD5Final(msg_sum, &ctx);
      ck->type = DCC_CK_GREY_MSG;
      ck->len = sizeof(*ck);
      memcpy(ck->sum, msg_sum, sizeof(ck->sum));
      ++ck;
      ++num_cks;

      /* include the triple checksum of the sender, the sender's IP
       * address, and the target */
      MD5Init(&ctx);
      if (trim_grey_ip_addr) {
            struct in6_addr addr;
            DCC_SUM sum;
            int wno;

            for (wno = 0; wno < 4; ++wno) {
                  addr.s6_addr32[wno] = (cks->ip_addr.s6_addr32[wno]
                                     & grey_ip_mask.s6_addr32[wno]);
            }
            dcc_ck_ipv6(sum, &addr);
            MD5Update(&ctx, sum, sizeof(DCC_SUM));
      } else {
            MD5Update(&ctx, cks->sums[DCC_CK_IP].sum, sizeof(DCC_SUM));
      }
      MD5Update(&ctx, cks->sums[DCC_CK_ENV_FROM].sum, sizeof(DCC_SUM));
      MD5Update(&ctx, env_to_sum, sizeof(DCC_SUM));
      MD5Final(triple_sum, &ctx);
      ck->type = DCC_CK_GREY_TRIPLE;
      ck->len = sizeof(*ck);
      memcpy(ck->sum, triple_sum, sizeof(ck->sum));
      ++num_cks;

      if (!dcc_clnt_op(emsg, ctxt, DCC_CLNT_FG_GREY, 0, 0,
                   &rpt.hdr, (sizeof(rpt) - sizeof(rpt.cks)
                            + num_cks*sizeof(rpt.cks[0])),
                   op, &resp.hdr, sizeof(resp), 0)) {
            return DCC_GREY_ASK_FAIL;
      }

      recv_len = htons(resp.r.hdr.len);
      if (resp.hdr.op != DCC_OP_QUERY_RESP) {
            dcc_pemsg(EX_UNAVAILABLE, emsg, "DCC %s: %s %*s",
                    dcc_srvr_nm(),
                    dcc_hdr_op2str(&resp.hdr),
                    (resp.hdr.op == DCC_OP_ERROR
                     ? (recv_len - (ISZ(resp.error)
                                - ISZ(resp.error.msg)))
                     : 0),
                    resp.error.msg);
            return DCC_GREY_ASK_FAIL;
      }
      if (recv_len != (sizeof(resp.r) - sizeof(resp.r.body.tgts)
                   + 2*sizeof(resp.r.body.tgts[0]))) {
            dcc_pemsg(EX_UNAVAILABLE, emsg,
                    "DCC %s: answered with %d bytes",
                    dcc_srvr_nm(), recv_len);
            return DCC_GREY_ASK_FAIL;
      }

      /* see what the greylist server had to say */
      result_tgts = ntohl(resp.r.body.tgts[1]);
      switch (result_tgts) {
      case DCC_TGTS_OK:       /* embargo ended just now */
            if (resp.r.body.tgts[0] != 0)
                  ++*plate_tgts;
            *pembargo_num = 0;
            return DCC_GREY_ASK_EMBARGO_END;

      case DCC_TGTS_TOO_MANY:       /* no current embargo */
            *pembargo_num = 0;
            return ((resp.r.body.tgts[0] != 0)
                  ? DCC_GREY_ASK_EMBARGO_END
                  : DCC_GREY_ASK_PASS);

      case DCC_TGTS_OK2:            /* whitelisted for greylisting */
            *pembargo_num = 0;
            return DCC_GREY_ASK_WHITE;

      default:                /* embargoed */
            if (resp.r.body.tgts[0] == 0)
                  ++*pearly_tgts;   /* count this target in DCC report */
            *pembargo_num = result_tgts+1;
            return DCC_GREY_ASK_EMBARGO;
      }
}



/* tell the grey list to restore the embargo on a triple */
u_char
dcc_grey_spam(DCC_EMSG emsg,
            DCC_CLNT_CTXT *ctxt,
            const DCC_GOT_CKS *cks,
            const DCC_SUM triple_sum)     /* delete this cksum from database */
{
      DCC_GREY_SPAM gs;
      union {
          DCC_HDR hdr;
          DCC_ERROR     error;
      } resp;

      memset(&gs, 0, sizeof(gs));

      if (cks->sums[DCC_CK_IP].type != DCC_CK_IP) {
            dcc_pemsg(EX_SOFTWARE, emsg,
                    "missing IP checksum for dcc_grey_spam()");
            return 0;
      }

      gs.ip.type = DCC_CK_IP;
      gs.ip.len = sizeof(gs.ip);
      memcpy(&gs.ip.sum, &cks->sums[DCC_CK_IP].sum, sizeof(DCC_SUM));

      gs.triple.type = DCC_CK_GREY_TRIPLE;
      gs.triple.len = sizeof(gs.triple);
      memcpy(&gs.triple.sum, triple_sum, sizeof(DCC_SUM));

      return dcc_clnt_op(emsg, ctxt, DCC_CLNT_FG_GREY,
                     0, 0, &gs.hdr, sizeof(gs),
                     DCC_OP_GREY_SPAM, &resp.hdr, sizeof(resp), 0);
}

Generated by  Doxygen 1.6.0   Back to index