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

ck.c

/* Distributed Checksum Clearinghouse
 *
 * compute simple 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.45 $Revision$
 */

#include "dcc_ck.h"
#include "dcc_heap_debug.h"
#include "dcc_xhdr.h"
#ifndef DCC_WIN32
#include <arpa/inet.h>
#endif


/* substitute or locally configured checksums */
typedef struct {
    u_int   nm_len;
    const char    *nm;              /* name of the checksum */
} DCC_SUB_CK;
static DCC_SUB_CK sub_cks[DCC_MAX_SUB_CKS];
static u_int num_sub_cks;


/* get the checksum of an IPv6 address */
void
dcc_ck_ipv6(DCC_SUM sum, const struct in6_addr *addr)
{
      MD5_CTX ctx;

      MD5Init(&ctx);
      MD5Update(&ctx, (void *)addr, sizeof(*addr));
      MD5Final(sum, &ctx);
}



/* add an IP address to set of checksums */
void
dcc_get_ipv6_ck(DCC_GOT_CKS *cks, const struct in6_addr *addrp)
{
      cks->sums[DCC_CK_IP].type = DCC_CK_IP;
      cks->sums[DCC_CK_IP].rpt = 1;
      cks->sums[DCC_CK_IP].tgts = DCC_TGTS_INVALID;
      cks->ip_addr = *addrp;
      dcc_ck_ipv6(cks->sums[DCC_CK_IP].sum, addrp);
      cks->flags |= DCC_CKS_HAVE_SUM;
}



/* Make DCC_CK_IP from a string containing an IPv4 or IPv6 address.
 *    Because inet_pton() is picky, the string must be unambiguous and
 *    fussy. */
u_char
dcc_get_str_ip_ck(DCC_GOT_CKS *cks, const char *str, struct in6_addr *addrp)
{
      DCC_SOCKU su;
      struct in6_addr addr;

      if (!dcc_str2ip(&su, str))
            return 0;

      if (!addrp)
            addrp = &addr;

      if (su.sa.sa_family == AF_INET) {
            /* treat IPv4 addresses as IPv6 so that everyone computes
             * the same checksum */
            dcc_ipv4toipv6(addrp, su.ipv4.sin_addr);
      } else {
            *addrp = su.ipv6.sin6_addr;
      }

      dcc_get_ipv6_ck(cks, addrp);
      return 1;
}



/* Compute a checksum from a string with matching but optional carets or
 * quotes, after stripping the quotes or carets.
 * Ignore case and white space */
void
dcc_str2ck(DCC_SUM sum,
         const char *hdr,           /* substitute header type */
         u_int hdr_len,
         const char *str)           /* string to checksum */
{
      MD5_CTX ctx;
      u_int len;
      char *p;
      char c, cbuf[DCC_HDR_CK_MAX];

      /* ignore whitespace and case */
      p = cbuf;
      while ((c = *str++) != '\0' && p <= LAST(cbuf)) {
            if (DCC_IS_WHITE(c))
                  continue;
            *p++ = DCC_TO_LOWER(c);
      }
      str = cbuf;
      len = p - str;
      /* "<>" is legal at least as a sender
       * Remove a matching pair of leading and trailing <> or " characters */
      if (len >= 2
          && ((*str == '<' && *(p-1) == '>')
            || (*str == '"' && *(p-1) == '"'))) {
            ++str;
            len -= 2;
            p -= 2;
      }
      /* strip trailing periods, mostly for mail_host */
      while (len >= 1
             && *(p-1) == '.') {
            --len;
            --p;
      }
      MD5Init(&ctx);
      if (hdr)
            MD5Update(&ctx, hdr, hdr_len);
      MD5Update(&ctx, str, len);
      MD5Final(sum, &ctx);
}



/* make checksum from a string for headers and envelope */
u_char                              /* 1=ok 0=bad string */
dcc_get_cks(DCC_GOT_CKS *cks, DCC_CK_TYPES type, const char *str, u_char rpt)
{
      DCC_GOT_SUM *g;

      g = &cks->sums[type];

      switch (type) {
      case DCC_CK_INVALID:
      case DCC_CK_IP:
      case DCC_CK_SUB:
      case DCC_CK_SRVR_ID:
      case DCC_CK_BODY:
      case DCC_CK_FUZ1:
      case DCC_CK_FUZ2:
      case DCC_CK_GREY_MSG:
      case DCC_CK_GREY_TRIPLE:
            dcc_logbad(EX_SOFTWARE, "invalid checksum %s",
                     dcc_type2str_err(type, 0, 0));
            return 0;

      case DCC_CK_ENV_FROM:
      case DCC_CK_FROM:
      case DCC_CK_ENV_TO:
      case DCC_CK_RECEIVED:
      case DCC_CK_MESSAGE_ID:
            dcc_str2ck(g->sum, 0, 0, str);
            break;
      }

      g->type = type;
      g->rpt = rpt;
      g->tgts = DCC_TGTS_INVALID;
      cks->flags |= DCC_CKS_HAVE_SUM;
      return 1;
}



/* make checksum for a locally configured header */
u_char                              /* 1=done 0=failed */
dcc_ck_get_sub(DCC_GOT_CKS *cks,
             const char *hdr, /* header name, not '\0' terminated */
             const char *str) /* header value if not after hdr */
{
      DCC_GOT_SUM *g;
      const DCC_SUB_CK *sck;
      DCC_CK_TYPE type;
      int i;

      /* look for the header name in the list of locally configured headers */
      sck = &sub_cks[0];
      for (i = num_sub_cks; ; ++sck, --i) {
            if (i <= 0)
                  return 0;   /* this header is not in the list */
            if (!strncasecmp(hdr, sck->nm, sck->nm_len)
                && (hdr[sck->nm_len] == '\0'
                  || hdr[sck->nm_len] == ':'))
                  break;
      }

      /* Get the header value if the caller did not separate it.
       * The colon is present if the header field was not separated */
      if (!str)
            str = hdr+sck->nm_len+1;

      /* find a free checksum slot
       * or a slot already assigned to the header */
      type = DCC_CK_SUB;
      g = &cks->sums[type];
      for (;;) {
            if (type >= DIM(cks->sums))
                  return 0;   /* none free */

            if (g->type == DCC_CK_INVALID
                && (type > DCC_CK_TYPE_LAST
                  || type == DCC_CK_SUB))
                  break;            /* found a free slot */

            if (g->type == DCC_CK_SUB
                && g->hdr == sck->nm)
                  break;            /* found previously assigned slot */
            ++g;
            ++type;
      }

      dcc_str2ck(g->sum, sck->nm, sck->nm_len, str);
      g->type = DCC_CK_SUB;
      g->rpt = 1;
      g->tgts = DCC_TGTS_INVALID;
      g->hdr = sck->nm;
      cks->flags |= DCC_CKS_HAVE_SUM;
      return 1;
}



/* add to the list of locally configured or substitute headers */
u_char
dcc_add_sub_hdr(DCC_EMSG emsg, const char *hdr)
{
      const char *p;
      char c, *q;
      u_int n, len;

      if (num_sub_cks >= DIM(sub_cks)) {
            dcc_pemsg(EX_USAGE, emsg,
                    "too many substitute headers with \"%s\"", hdr);
            return 0;
      }

      p = hdr;
      for (;;) {
            if (*p == '\0')
                  break;
            if (*p == ':' && p[1] == '\0') {
                  --p;
                  break;
            }
            if (*p <= ' ' || *p >= 0x7f || *p == ':') {
                  dcc_pemsg(EX_USAGE, emsg,
                          "illegal SMTP field name character in \"%s\"",
                          hdr);
                  return 0;
            }
            ++p;
      }

      len = p - hdr;
      if (len == 0) {
            dcc_pemsg(EX_USAGE, emsg, "illegal empty field name");
            return 0;
      }

      /* ignore duplicates */
      for (n = 0; n < num_sub_cks; ++n) {
            if (len == sub_cks[n].nm_len
                && !strncasecmp(hdr, sub_cks[n].nm, len))
                  return 1;
      }

      sub_cks[num_sub_cks].nm_len = len;
      q = dcc_malloc(len+1);
      sub_cks[num_sub_cks].nm = q;
      do {
            c = *hdr++;
            *q++ = DCC_TO_LOWER(c);
      } while (--len > 0);
      *q = '\0';
      ++num_sub_cks;

      return 1;
}



void
dcc_print_cks(void *arg, const DCC_GOT_CKS *cks, DCC_CKS_WTGTS wtgts,
            void (out)(void *, const void *, u_int))
{
#define CK_PAT_CK_H     "%25s  %35s"
#define CK_PAT_CK "%25s: %35s"
#define CK_PAT_SERVER   " %7s"
#define CK_PAT_WLIST    " %5s"
      char white_tgts_buf[12], dcc_tgts_buf[12];
      char type_buf[26], cbuf[DCC_CK2STR_LEN];
      char buf[80];
      const DCC_GOT_SUM *g;
      u_char have_server, have_wlist, headed;
      int inx, i;

      have_server = 0;
      have_wlist = 0;
      for (g = cks->sums, inx = 0; g <= LAST(cks->sums); ++g, ++inx) {
            if (g->type == DCC_CK_INVALID)
                  continue;
            if (g->tgts != DCC_TGTS_INVALID)
                  have_server = 1;
            if (wtgts[inx] != 0)
                  have_wlist = 1;
      }

      headed = 0;
      for (g = cks->sums, inx = 0; g <= LAST(cks->sums); ++g, ++inx) {
            if (g->type == DCC_CK_INVALID)
                  continue;

            if (!headed) {
                  headed = 1;
                  i = snprintf(buf, sizeof(buf), CK_PAT_CK_H,
                             "", "checksum");
                  if (i < ISZ(buf)
                      && (have_server || have_wlist))
                        i += snprintf(buf+i, sizeof(buf)-i,
                                    CK_PAT_SERVER,
                                    have_server ? "server" : "");
                  if (i < ISZ(buf)
                      && have_wlist)
                        i += snprintf(buf+i, sizeof(buf)-i,
                                    CK_PAT_WLIST, "wlist");
                  if (i >= ISZ(buf)-1)
                        i = sizeof(buf)-2;
                  buf[i] = '\n';
                  buf[++i] = '\0';
                  out(arg, buf, i);
            }

            i = snprintf(buf, sizeof(buf), CK_PAT_CK,
                       dcc_type2str(type_buf, sizeof(type_buf),
                                g->type, g->hdr, 0),
                       dcc_ck2str(cbuf, sizeof(cbuf),
                              g->type, g->sum));
            if (i < ISZ(buf)
                && ((g->rpt != 0 && g->tgts != DCC_TGTS_INVALID)
                  || wtgts[inx] != 0))
                  i += snprintf(buf+i, sizeof(buf)-i,
                              CK_PAT_SERVER,
                              (g->rpt == 0 || g->tgts==DCC_TGTS_INVALID)
                              ? ""
                              : dcc_cnt2str(dcc_tgts_buf,
                                        sizeof(dcc_tgts_buf),
                                        g->tgts, 0));
            if (i < ISZ(buf)
                  && wtgts[inx] != 0)
                  i += snprintf(buf+i, sizeof(buf)-i,
                              CK_PAT_WLIST,
                              dcc_cnt2str(white_tgts_buf,
                                      sizeof(white_tgts_buf),
                                      wtgts[inx], 0));
            if (i >= ISZ(buf)-1)
                  i = sizeof(buf)-2;
            buf[i] = '\n';
            buf[++i] = '\0';
            out(arg, buf, i);
      }
#undef CK_PAT_CK_H
#undef CK_PAT_CK
#undef CK_PAT_SERVER
#undef CK_PAT_WLIST
}



void
dcc_print_grey(void *arg,           /* for out() */
             DCC_ASK_GREY_RESULT result,
             u_int embargo_num,
             u_char *headed,
             const char *env_to,
             const DCC_SUM msg_sum,
             const DCC_SUM triple_sum,
             void (out)(void *, const void *, u_int))
{
#define CK_MSG_PAT "%25.*s: %35s "
#define CK_TRIPLE_PAT "                           %35s "
#define CK_HEADING "       "DCC_XHDR_GREY_RECIP"\n"
      const char *msg_pat, *triple_pat;
      char cbuf[DCC_CK2STR_LEN];
      char buf[100];
      int env_to_len, i;

      msg_pat = triple_pat = 0;
      switch (result) {
      case DCC_GREY_ASK_FAIL:
            msg_pat = CK_MSG_PAT"\n";
            triple_pat = CK_TRIPLE_PAT DCC_XHDR_EMBARGO_FAIL"\n";
            break;
      case DCC_GREY_ASK_OFF:
            return;
      case DCC_GREY_ASK_EMBARGO:
            msg_pat = CK_MSG_PAT"\n";
            if (embargo_num > 0)
                  triple_pat = CK_TRIPLE_PAT DCC_XHDR_EMBARGO_NUM"\n";
            else
                  triple_pat = CK_TRIPLE_PAT DCC_XHDR_EMBARGO"\n";
            break;
      case DCC_GREY_ASK_EMBARGO_END:
            msg_pat = CK_MSG_PAT"\n";
            triple_pat = CK_TRIPLE_PAT DCC_XHDR_EMBARGO_ENDED"\n";
            break;
      case DCC_GREY_ASK_PASS:
            msg_pat = CK_MSG_PAT"\n";
            triple_pat = CK_TRIPLE_PAT DCC_XHDR_EMBARGO_PASS"\n";
            break;
      case DCC_GREY_ASK_WHITE:
            msg_pat = CK_MSG_PAT"\n";
            triple_pat = CK_TRIPLE_PAT DCC_XHDR_EMBARGO_OK"\n";
            break;
      }

      env_to_len = strlen(env_to);
      if (env_to_len > 1 && env_to[0] == '<' && env_to[env_to_len-1] == '>') {
            ++env_to;
            env_to_len -= 2;
      }
      if (!headed || !*headed) {
            if (headed)
                  *headed = 1;
            out(arg, CK_HEADING, sizeof(CK_HEADING)-1);
      }

      i = snprintf(buf, sizeof(buf), msg_pat,
                 env_to_len, env_to,
                 dcc_ck2str(cbuf, sizeof(cbuf),
                        DCC_CK_GREY_MSG, msg_sum));
      if (i >= ISZ(buf)) {
            i = sizeof(buf)-1;
            buf[i-1] = '\n';
      }
      out(arg, buf, i);

      i = snprintf(buf, sizeof(buf), triple_pat,
                 dcc_ck2str(cbuf, sizeof(cbuf),
                        DCC_CK_GREY_TRIPLE, triple_sum),
                 embargo_num);
      if (i >= ISZ(buf)) {
            i = sizeof(buf)-1;
            buf[i-1] = '\n';
      }
      out(arg, buf, i);

      if (!headed)
            out(arg, "\n", 1);

#undef CK_MSG_PAT
#undef CK_TRIPLE_PAT
#undef CK_HEADING
}

Generated by  Doxygen 1.6.0   Back to index