Logo Search packages:      
Sourcecode: dcc version File versions

dccproc.c

/* Distributed Checksum Clearinghouse server
 *
 * report a message for such as procmail
 *
 * 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.139 $Revision$
 */

#include "dcc_ck.h"
#include "dcc_xhdr.h"
#include "dcc_paths.h"
#include "dcc_heap_debug.h"
#include <signal.h>                 /* for Linux and SunOS*/
#ifndef DCC_WIN32
#include <arpa/inet.h>
#endif
#ifdef USE_XFLTR
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#else
#include <sys/pthread.h>
#endif
#endif /* USE_XFLTR */



static DCC_EMSG dcc_emsg;

static const char *homedir;
static const char *mapfile_nm = DCC_MAP_NM_DEF;

static const char *logdir;
static u_char priv_logdir;
static DCC_PATH log_nm;
static int lfd = -1;
static struct timeval ldate;
static u_char logging = 1;          /* 0=no log, 1=have file, 2=used it */
static ASK_ST ask_st;

static char id[DCC_MSG_ID_LEN+1];
static const char *tmpdir;          /* can't static initialize for WIN32 */
static DCC_PATH tmp_nm;
static int tmp_fd = -1;
static u_char tmp_nm_unlink;
static u_char tmp_rewound;
static int hdrs_len, body_len;
static u_char seen_hdr;

static int exit_code = EX_NOUSER;
static DCC_TGTS local_tgts;
static u_char local_tgts_spam, local_tgts_set;
static int total_hdrs, cr_hdrs;

static const char* white_nm;
static const char *ifile_nm = "stdin", *ofile_nm = "stdout";
static FILE *ifile, *ofile;

static DCC_CLNT_CTXT *ctxt;
static char xhdr[sizeof(DCC_XHDR_START)+sizeof(DCC_BRAND)+1];
static int xhdr_len;
static u_char add_xhdr;             /* add instead of replace header */
static u_char dcc_query_only;
static u_char cksums_only;          /* output only checksums */
static u_char x_dcc_only;           /* output only the X-DCC header */
static u_char fake_envelope;        /* fake envelope log lines */
static int std_received;            /* Received: line is standard */

static DCC_GOT_CKS cks;
static DNSBL_WORK *dnsbl_work;
static DCC_CKS_WTGTS wtgts;
static char helo[DCC_HELO_MAX];
static char sender_name[MAXHOSTNAMELEN];
static char sender_str[INET6_ADDRSTRLEN];
static struct in6_addr clnt_addr;

static char env_from_buf[DCC_HDR_CK_MAX+1];
static const char *env_from;

static char mail_host[MAXHOSTNAMELEN];

static DCC_HEADER_BUF header;
#ifdef USE_XFLTR
static char xfltr_header[DCC_HDR_CK_MAX];
static int xfltr_header_len;
#endif

static EARLY_LOG early_log;

static void start_dccifd(void);
static u_char check_mx_listing(void);
static int get_hdr(char *, int);
static void add_hdr(void *, const char *, u_int);
static void tmp_write(const void *, int);
static int tmp_read(void *, int);
static void tmp_close(void);
static void scopy(int, u_char);
static void thr_log_write(void *, const char *, u_int);
static void log_write(const void *, int);
static void log_late(void);
static void log_print(u_char, const char *, ...) PATTRIB(2,3);
#define LOG_CAPTION(s) log_write((s), STRZ(s))
#define LOG_EOL() LOG_CAPTION("\n")
static void log_fin(void);
static void log_ck(void *, const char *, u_int);
static void dccproc_error_msg(const char *, ...) PATTRIB(1,2);
static void sigterm(int);


static const char *usage_str =
"[-VdAQCHER]  [-h homedir] [-m map] [-w whiteclnt] [-T tmpdir]\n"
"   [-a IP-address] [-f env_from] [-t targets] [-x exitcode]\n"
"   [-c type,[log-thold,][spam-thold]] [-g [not-]type] [-S header]\n"
"   [-i infile] [-o outfile] [-l logdir] [-B dnsbl-option] [-X xfltr-option]\n"
"   [-L ltype,facility.level]";

static void NRATTRIB
usage(const char* barg)
{
      if (barg) {
            dcc_logbad(EX_USAGE, "unrecognized \"%s\"\n%s\n",
                     barg, usage_str);
      } else {
            dcc_logbad(EX_USAGE, "%s\n", usage_str);
      }
}



int NRATTRIB
main(int argc, char **argv)
{
      char buf[20*DCC_HDR_CK_MAX];  /* at least DCC_HDR_CK_MAX*3 */
      u_char log_tgts_set = 0;
      u_char ask_result;
      char *p;
      const char *p2;
      u_long l;
      int error, blen, i;

      /* because stderr is often mixed with stdout and effectively
       * invisible, also complain to syslog */
      tmpdir = _PATH_TMP;
      dcc_syslog_init(1, argv[0], 0);
      dcc_clear_tholds();

      /* we must be SUID to read and write the system's common connection
       * parameter memory mapped file.  We also need to read the common
       * local white list and write the mmap()'ed hash file */
      dcc_init_priv();

      ofile = stdout;
      ifile = stdin;
      opterr = 0;
      while ((i = getopt(argc, argv, "VdAQCHER"
                     "r:h:m:w:T:a:f:g:S:t:x:c:i:o:l:B:X:L:")) != EOF) {
            switch (i) {
            case 'V':
                  fprintf(stderr, DCC_VERSION"\n");
                  exit(EX_OK);
                  break;

            case 'd':
                  ++dcc_clnt_debug;
                  break;

            case 'A':
                  add_xhdr = 1;
                  break;

            case 'Q':
                  dcc_query_only = 1;
                  break;

            case 'C':
                  cksums_only = 1;
                  break;

            case 'H':
                  x_dcc_only = 1;
                  break;

            case 'E':
                  fake_envelope = 1;
                  break;

            case 'R':
                  if (!std_received)
                        std_received = 1;
                  break;

            case 'r':         /* a bad idea replacment for -R */
                  std_received = strtoul(optarg, &p, 0);
                  if (*p != '\0' || i == 0) {
                        dccproc_error_msg("invalid count"
                                      "\"-e %s\"", optarg);
                        std_received = 1;
                  }
                  break;

            case 'h':
                  homedir = optarg;
                  break;

            case 'm':
                  mapfile_nm = optarg;
                  break;

            case 'w':
                  white_nm = optarg;
                  break;

            case 'T':
                  if (optarg[0] == '\0')
                        dcc_error_msg("illegal temporary directory");
                  else
                        tmpdir = optarg;
                  break;

            case 'a':
                  /* ignore SpamAssassin noise */
                  if (!strcmp("0.0.0.0", optarg))
                        break;
                  dcc_host_lock();
                  if (!dcc_get_host(optarg, 2, &error)) {
                        dccproc_error_msg("\"-a %s\": %s",
                                      optarg, DCC_HSTRERROR(error));
                  } else {
                        if (dcc_hostaddrs[0].sa.sa_family == AF_INET)
                              dcc_ipv4toipv6(&clnt_addr,
                                           dcc_hostaddrs[0]
                                           .ipv4.sin_addr);
                        else
                              clnt_addr = (dcc_hostaddrs[0].ipv6
                                         .sin6_addr);
                        dcc_get_ipv6_ck(&cks, &clnt_addr);
                        dcc_ipv6tostr(sender_str, sizeof(sender_str),
                                    &clnt_addr);
                  }
                  dcc_host_unlock();
                  break;

            case 'f':
                  env_from = optarg;
                  dcc_get_cks(&cks, DCC_CK_ENV_FROM, env_from, 1);
                  break;

            case 'g':         /* honor not-spam "counts" */
                  dcc_parse_honor(optarg);
                  break;

            case 'S':
                  if (!dcc_add_sub_hdr(dcc_emsg, optarg))
                        dcc_logbad(EX_USAGE, "%s", dcc_emsg);
                  break;

            case 't':
                  if (!strcasecmp(optarg, "many")) {
                        local_tgts = 1;
                        local_tgts_spam = 1;
                        local_tgts_set = 1;
                  } else {
                        l = strtoul(optarg, &p, 0);
                        if (*p != '\0' || l > DCC_TGTS_RPT_MAX) {
                              dccproc_error_msg("invalid count"
                                          "\"-t %s\"", optarg);
                        } else {
                              local_tgts = l;
                              local_tgts_spam = 0;
                              local_tgts_set = 1;
                        }
                  }
                  break;

            case 'x':
                  l = strtoul(optarg, &p, 0);
                  if (*p != '\0') {
                        dccproc_error_msg("invalid exit code \"-x %s\"",
                                      optarg);
                  } else {
                        exit_code = l;
                  }
                  break;

            case 'c':
                  if (dcc_parse_tholds('c', optarg))
                        log_tgts_set = 1;
                  break;

            case 'i':
                  /* open the input file now, before changing to the
                   * home DCC directory */
                  ifile_nm = optarg;
                  ifile = fopen(ifile_nm, "r");
                  if (!ifile)
                        dcc_logbad(EX_USAGE,
                                 "bad input file \"%s\": %s",
                                 ifile_nm, ERROR_STR());
                  break;

            case 'o':
                  /* open the output file now, before changing to the
                   * home DCC directory */
                  ofile_nm = optarg;
                  ofile = fopen(ofile_nm, "w");
                  if (!ofile)
                        dcc_logbad(EX_USAGE,
                                 "bad output file \"%s\": %s",
                                 ofile_nm, ERROR_STR());
                  break;

            case 'l':
                  logdir = optarg;
                  break;

            case 'B':
                  if (!dcc_parse_dnsbl(dcc_emsg, optarg))
                        dcc_logbad(EX_USAGE, "%s", dcc_emsg);
#ifdef HAVE_HELPERS
                  /* exec() dccifd to talk to the DNS */
                  helpers_init(1, DCC_LIBEXECDIR"/dccifd");
#endif
                  break;

#ifdef HAVE_HELPERS
            case 'X':
                  if (!dcc_parse_xfltr(dcc_emsg, optarg))
                        dcc_logbad(EX_USAGE, "%s", dcc_emsg);
                  /* exec() dccifd to talk to the external filter */
                  helpers_init(1, DCC_LIBEXECDIR"/dccifd");
                  break;
#endif

#ifndef DCC_WIN32
            case 'L':
                  dcc_parse_log_opt(optarg);
                  break;
#endif

            default:
                  usage(argv[optind-1]);
            }
      }
      if (argc != optind)
            usage(argv[optind]);

#ifdef SIGHUP
      signal(SIGHUP, sigterm);
#endif
      signal(SIGTERM, sigterm);
      signal(SIGINT, sigterm);

      dcc_clnt_unthread_init();

      dcc_cdhome(0, homedir);

      if (logdir) {
            if (!dcc_log_init(dcc_emsg,logdir)) {
                  dccproc_error_msg("%s", dcc_emsg);
            } else {
#ifndef DCC_WIN32
                  /* use privileges to make log files in the built-in home
                   * directory */
                  if (!homedir
                      && 0 > access(dcc_logdir, R_OK|W_OK|X_OK)) {
                        priv_logdir = 1;
                        dcc_get_priv_home(dcc_logdir);
                  }
#endif
                  lfd = dcc_log_open(dcc_emsg, log_nm, sizeof(log_nm),
                                 id, sizeof(id));
                  if (priv_logdir)
                        dcc_rel_priv();
                  if (lfd < 0) {
                        dccproc_error_msg("%s", dcc_emsg);
                        logging = 0;
                  }
            }
      } else {
            if (log_tgts_set)
                  dccproc_error_msg("log thresholds set with -c"
                                " but no -l directory");
            logging = 0;
      }

      if (fake_envelope && lfd >= 0) {
            struct tm tm;
            char date_buf[40];

            gettimeofday(&ldate, 0);
            strftime(date_buf, sizeof(date_buf), DCC_LOG_DATE_FMT,
                   dcc_localtime(ldate.tv_sec, &tm));
            log_print(0, DCC_LOG_DATE_PAT"\n", date_buf);
      }

      if (!local_tgts_set) {
            local_tgts = dcc_query_only ? 0 : 1;
            local_tgts_spam = 0;
      } else if (local_tgts == 0) {
            dcc_query_only = 1;
            local_tgts_spam = 0;
      } else if (dcc_query_only) {
            dcc_error_msg("\"-t %s\" is incompatible with \"-Q\"",
                        local_tgts_spam
                        ? "many"
                        : dcc_tgts2str(buf, sizeof(buf), local_tgts, 0));
            local_tgts = 0;
            local_tgts_spam = 0;
      }
      if (local_tgts == DCC_TGTS_TOO_MANY) {
            local_tgts = 1;
            local_tgts_spam = 1;
      }

#ifdef DCC_WIN32
      /* Apparently WIN32 lacks /dev/null */
#else
      /* Close STDERR to keep it from being mixed with the message,
       * unless we are not going to output the message.
       * Ensure that stderr and file descriptor 2 are open to something
       * to prevent surprises from busybody libraries. */
      if (!dcc_clnt_debug && !cksums_only && !x_dcc_only) {
            close(STDERR_FILENO);
            clean_stdio();
      }
#endif

      if (logging
#ifdef USE_XFLTR
          || xfltr_parm
#endif
          || (!cksums_only && !x_dcc_only)) {
#ifdef USE_XFLTR
            /* we need to be able to use the temporary file by name
             * for the external filter */
            tmp_nm_unlink = (xfltr_parm != 0);
#endif
            tmp_fd = dcc_mkstemp(dcc_emsg, tmp_nm, sizeof(tmp_nm),
                             id, sizeof(id),
                             tmpdir, DCC_TMP_LOG_PAT,
                             !tmp_nm_unlink, 0, 0);
            if (tmp_fd < 0)
                  dcc_logbad(EX_IOERR, "%s", dcc_emsg);
            if (tmp_nm_unlink)
                  atexit(tmp_close);
      }

      /* start a connection to a DCC server */
      ctxt = dcc_clnt_start(dcc_emsg, 0, mapfile_nm, DCC_CLNT_FG_NONE);
      if (ctxt) {
            if (!homedir)
                  start_dccifd();
            ctxt = dcc_clnt_start_fin(dcc_emsg, ctxt);
      }
      if (!ctxt) {
            dccproc_error_msg("%s", dcc_emsg);
      } else {
            xhdr_len = dcc_xhdr_start(xhdr, sizeof(xhdr), dcc_clnt_info);
      }

      /* get the local whitelist ready */
      dcc_wf_init(&cmn_wf, 0);
      if (white_nm
          && !dcc_new_white_nm(dcc_emsg, &cmn_wf, white_nm)) {
            dccproc_error_msg("%s", dcc_emsg);
            white_nm = 0;
      }
      /* look past the SMTP client if it is a listed MX server */
      if (sender_str[0] != '\0' && white_nm) {
            check_mx_listing();
      }

      /* get ready for the body checksums before the headers so that
       * we can notice the MIME separator */
      dcc_ck_body_init(&cks);
      dcc_dnsbl_init(&cks, &dnsbl_work, ctxt, 0, id);

      /* get the headers */
      for (;;) {
            int hlen;

            hlen = get_hdr(buf, sizeof(buf));
            if (hlen <= 2
                && (buf[0] == '\n'
                  || (buf[0] == '\r' && buf[1] == '\n'))) {
                  /* stop at the separator between the body and headers */
                  if (!seen_hdr)
                        dcc_logbad(EX_DATAERR,
                                 "missing SMTP header lines");
                  hdrs_len -= hlen;
                  body_len = hlen;
                  break;
            }

#define GET_HDR_CK(h,t) {                                   \
                  if (!CSTRCMP(buf, h)) {                   \
                        dcc_get_cks(&cks,DCC_CK_##t, &buf[STRZ(h)], 1);\
                        seen_hdr = 1;                       \
                        continue;}}
            GET_HDR_CK(DCC_XHDR_TYPE_FROM":", FROM);
            GET_HDR_CK(DCC_XHDR_TYPE_MESSAGE_ID":", MESSAGE_ID);
#undef GET_HDR_CK

            if (!CSTRCMP(buf, "Return-Path:")) {
                  if (parse_return_path(&buf[STRZ("Return-Path:")], &cks,
                                    env_from_buf))
                        env_from = env_from_buf;
                  seen_hdr = 1;
                  continue;
            }

            /* notice UNIX From_ line */
            if (!seen_hdr
                && !strncmp(buf, "From ", STRZ("From "))) {
                  p = &buf[STRZ("From ")];
                  p += strspn(p, " ");
                  p2 = strchr(p, ' ');
                  if (p2 != 0) {
                        if (cks.sums[DCC_CK_ENV_FROM].type
                            == DCC_CK_INVALID) {
                              if (p2 > p+sizeof(env_from_buf))
                                  p2 = p+sizeof(env_from_buf);
                              memcpy(env_from_buf, p, p2-p);
                              env_from_buf[p2-p] = '\0';
                              env_from = env_from_buf;
                              dcc_get_cks(&cks, DCC_CK_ENV_FROM,
                                        env_from, 1);
                        }
                        seen_hdr = 1;
                        continue;
                  }
            }

            if (!CSTRCMP(buf, DCC_XHDR_TYPE_RECEIVED":")) {
                  seen_hdr = 1;

                  p2 = &buf[STRZ(DCC_XHDR_TYPE_RECEIVED":")];

                  /* compute checksum of the last Received: header */
                  dcc_get_cks(&cks, DCC_CK_RECEIVED, p2, 1);

                  /* pick IP address out of Nth Received: header
                   * unless we had a good -a value */
                  if (sender_str[0] != '\0')
                        std_received = 0;
                  if (!std_received)
                        continue;
                  if (--std_received > 0)
                        continue;

                  p2 = parse_received(p2, &cks, helo, sizeof(helo),
                                  sender_str, sizeof(sender_str),
                                  sender_name, sizeof(sender_name));
                  if (p2 == 0) {
                        /* to avoid being fooled by forged Received:
                         * fields, do not skip unrecognized forms */
                        std_received = 0;
                  } else if (*p2 != '\0') {
                        log_print(1, "skip %s Received: header\n", p2);
                        std_received = 1;
                  } else {
                        std_received = check_mx_listing();
                  }
                  continue;
            }

            /* Notice MIME multipart boundary definitions */
            dcc_ck_mime_hdr(&cks, buf, 0);

            if (dcc_ck_get_sub(&cks, buf, 0))
                  seen_hdr = 1;

            /* notice any sort of header */
            if (!seen_hdr) {
                  for (p = buf; ; ++p) {
                        if (*p == ':') {
                              seen_hdr = 1;
                              break;
                        }
                        if (*p <= ' ' || *p >= 0x7f)
                              break;
                  }
            }
      }
      /* Create a checksum for a null Message-ID header if there
       * was no Message-ID header.  */
      if (cks.sums[DCC_CK_MESSAGE_ID].type != DCC_CK_MESSAGE_ID)
            dcc_get_cks(&cks, DCC_CK_MESSAGE_ID, "", 0);

      /* Check DNS blacklists for STMP client and envelope sender
       * before collecting the body to avoid wasting time DNS resolving
       * URLs if the envelope answers the question.  Much of the DNS
       * work for the envelope has probably already been done. */
      if (cks.sums[DCC_CK_IP].type == DCC_CK_IP)
            dcc_sender_dnsbl(cks.dnsbl, &cks.ip_addr);

      if (env_from
          && (p = strchr(env_from, '@')) != 0) {
            BUFCPY(mail_host, p+1);
            p = strchr(mail_host, '>');
            if (p)
                  *p = '\0';
            if (strpbrk(mail_host, ";@,"))
                  mail_host[0] = '\0';
      }
      if (mail_host[0] != '\0') {
            dcc_ck_get_sub(&cks, "mail_host", mail_host);
            dcc_mail_host_dnsbl(cks.dnsbl, mail_host);
      }

      /* collect the body */
      do {
            blen = fread(buf, 1, sizeof(buf), ifile);
            if (blen != sizeof(buf)) {
                  if (ferror(ifile))
                        dcc_logbad(EX_DATAERR, "fgets(%s): %s",
                                 ifile_nm, ERROR_STR());
                  if (!blen)
                        break;
            }

            tmp_write(buf, blen);
            body_len += blen;
            dcc_ck_body(&cks, buf, blen);
      } while (!feof(ifile));
      fclose(ifile);

      dcc_ck_body_fin(&cks);

      if (!unthr_ask_white(dcc_emsg, &ask_st, white_nm, &cks, wtgts))
            dccproc_error_msg("%s", dcc_emsg);

      dcc_dnsbl_result(&ask_st, 0, cks.dnsbl);
      if ((ask_st & ASK_ST_DNSBL_ISSPAM)
          && (cmn_wf.info_flags & DCC_WHITE_FG_DNSBL_ON))
            ask_st |= (ASK_ST_CLNT_ISSPAM
                     | ASK_ST_LOGIT);

      if (!ctxt) {
            ask_result = 0;
      } else {
#ifdef USE_XFLTR
            /* notice external filter result */
            if (xfltr_parm) {
                  xfltr_header_len = sizeof(xfltr_header);
                  if (ask_xfltr(ctxt, 0,
                              xfltr_header, &xfltr_header_len,
                              id,
                              sender_name,
                              &cks.ip_addr,
                              "", env_from ? env_from : "",
                              1, tmp_nm)) {
                        ask_st |= (ASK_ST_XFLTR_ISSPAM | ASK_ST_LOGIT);
                        if (cmn_wf.info_flags & DCC_WHITE_FG_XFLTR_ON)
                              ask_st |= (ASK_ST_CLNT_ISSPAM
                                       | ASK_ST_LOGIT);
                  }
            }
#endif
            if (dcc_query_only) {
                  local_tgts_spam = 0;
                  local_tgts = 0;
            }
            if (local_tgts != 0
                && (ask_st & ASK_ST_CLNT_ISSPAM))
                  local_tgts_spam = 1;

            ask_result = unthr_ask_dcc(dcc_emsg, ctxt, &header, &ask_st,
                                 &cks, local_tgts_spam, local_tgts);
            if (!ask_result)
                  dccproc_error_msg("%s", dcc_emsg);
      }

      if (fake_envelope && lfd >= 0) {
            if (sender_str[0] != '\0') {
                  LOG_CAPTION(DCC_XHDR_TYPE_IP": ");
                  log_write(sender_name, strlen(sender_name));
                  LOG_CAPTION(" ");
                  log_write(sender_str, strlen(sender_str));
                  LOG_EOL();
            }
            if (helo[0] != '\0') {
                  LOG_CAPTION("HELO: ");
                  log_write(helo, strlen(helo));
                  LOG_EOL();
                  dcc_ck_get_sub(&cks, "helo", helo);
            }
            if (env_from) {
                  LOG_CAPTION(DCC_XHDR_TYPE_ENV_FROM": ");
                  log_write(env_from, strlen(env_from));
                  /* this would be nice, but might break compatibility */
                  log_print(0, "  mail_host=%s", mail_host);
                  LOG_EOL();
            }
            LOG_EOL();
      }

      /* copy the headers to the log file and the output */
      scopy(hdrs_len, 1);

      /* emit the X-DCC and external filter headers
       *    End them with "\r\n" if at least
       *    half of the header lines ended that way */
#ifdef USE_XFLTR
      if (xfltr_header_len)
            dcc_write_header(add_hdr, 0,
                         xfltr_header, xfltr_header_len,
                         cr_hdrs > total_hdrs/2);
#endif
      if (ask_result && header.buf[0] != '\0')
            dcc_write_header(add_hdr, 0, header.buf, header.used,
                         cr_hdrs > total_hdrs/2);

      /* emit body */
      scopy(body_len, 1);

      LOG_CAPTION(DCC_LOG_MSG_SEP);

      log_late();

      /* make the log file look like a dccm or dccifd log file */
      if (fake_envelope)
            log_ask_st(thr_log_write, 0, ask_st, FLTR_SWS_SETTINGS_ON, 0, 0,
#ifdef USE_XFLTR
                     xfltr_header, xfltr_header_len,
#endif
                     &header);

      dcc_print_cks(log_ck, 0, local_tgts_spam, local_tgts, &cks, wtgts, 0);

      if (ofile && fclose(ofile))
            dcc_logbad(EX_IOERR, "fclose(%s): %s", ofile_nm, ERROR_STR());

      log_fin();
      if ((ask_st & ASK_ST_CLNT_ISSPAM)
          || ((ask_st & ASK_ST_SRVR_ISSPAM)
            && !(cmn_wf.info_flags & DCC_WHITE_FG_DCC_OFF))
          ||  ((ask_st & ASK_ST_REP_ISSPAM)
             && (cmn_wf.info_flags & DCC_WHITE_FG_REP_ON)))
            exit(exit_code);

      exit(EX_OK);

#ifdef DCC_WIN32
      return 0;
#endif
}



static void
start_dccifd(void)
{
#ifndef DCC_WIN32
      time_t t;
      int c;
      pid_t pid;

      /* once an hour,
       * start dccifd if dccproc is run more often than
       * DCCPROC_MAX_CREDITS times at an average rate of at least
       * DCCPROC_COST times per second */

      t = (ctxt->start.tv_sec/DCCPROC_COST
           - dcc_clnt_info->dccproc_last/DCCPROC_COST);
      if (t > DCCPROC_MAX_CREDITS*2)      /* don't overflow */
            t = DCCPROC_MAX_CREDITS*2;
      else if (t < 0)
            t = 0;
      c = t + dcc_clnt_info->dccproc_c;
      if (c > DCCPROC_MAX_CREDITS)
            c = DCCPROC_MAX_CREDITS;
      --c;
      if (c < -DCCPROC_MAX_CREDITS)
            c = -DCCPROC_MAX_CREDITS;
      dcc_clnt_info->dccproc_c = c;
      dcc_clnt_info->dccproc_last = ctxt->start.tv_sec;

      if (dcc_clnt_info->dccproc_c >= 0)
            return;

      if (!DCC_IS_TIME(ctxt->start.tv_sec,
                   dcc_clnt_info->dccproc_dccifd_try,
                   DCCPROC_TRY_DCCIFD))
            return;
      dcc_clnt_info->dccproc_dccifd_try = (ctxt->start.tv_sec
                                   + DCCPROC_TRY_DCCIFD);
      pid = fork();
      if (pid) {
            if (pid < 0)
                  dccproc_error_msg("fork(): %s", ERROR_STR());
            return;
      }

      close(STDIN_FILENO);
      close(STDOUT_FILENO);
      close(STDERR_FILENO);
      clean_stdio();

      dcc_unmap_info(0);
      dcc_rel_ctxt(ctxt);
      dcc_ctxts_unlock();

      dcc_get_priv();
      setuid(dcc_effective_uid);
      setgid(dcc_effective_gid);

      dcc_trace_msg("try to start dccifd");
      execl(DCC_LIBEXECDIR"/start-dccifd",
            "start-dccifd", "-A", (const char *)0);
      dcc_trace_msg("exec("DCC_LIBEXECDIR"/start-dccifd): %s", ERROR_STR());
      exit(0);
#endif /* DCC_WIN32 */
}


static u_char                       /* 1=listed MX server */
check_mx_listing(void)
{
      DCC_TGTS tgts;

      if (!dcc_white_mx(dcc_emsg, &tgts, &cks))
            dccproc_error_msg("%s", dcc_emsg);

      if (tgts == DCC_TGTS_OK_MXDCC) {
            log_print(1, "%s is a whitelisted MX server with DCC client\n",
                    dcc_trim_ffff(sender_str));
            dcc_query_only = 1;
      } else if (tgts == DCC_TGTS_OK_MX) {
            log_print(1, "%s is a whitelisted MX server\n",
                    dcc_trim_ffff(sender_str));
      } else {
            return 0;
      }

      dcc_unget_ipv6_ck(&cks);
      sender_str[0] = '\0';

      return 1;
}



/* send a new header to the output and the log */
static void
add_hdr(void *wp0 UATTRIB, const char *buf, u_int buf_len)
{
      log_write(buf, buf_len);
      if (ofile)
            fwrite(buf, buf_len, 1, ofile);
}



/* get the next header line */
static int                    /* header length */
get_hdr(char *buf,
      int buflen)             /* >DCC_HDR_CK_MAX*3 */
{
      u_char no_copy;
      int hlen, wpos;
      const char *line;
      char c;
      int llen, i;

      no_copy = 0;
      hlen = wpos = 0;
      for (;;) {
            line = fgets(&buf[hlen], buflen-hlen, ifile);
            if (!line) {
                  if (ferror(ifile))
                        dcc_logbad(EX_DATAERR, "fgets(%s): %s",
                                 ifile_nm, ERROR_STR());
                  else
                        dcc_logbad(EX_DATAERR, "missing message body");
            }
            llen = strlen(line);

            /* delete our X-DCC header */
            if (hlen == 0
                && !add_xhdr && xhdr_len != 0
                && llen > xhdr_len
                && buf[xhdr_len] == ':'
                && !strncasecmp(buf, xhdr, xhdr_len)) {
                  seen_hdr = 1;
                  no_copy = 1;
            }

            /* do not crash on too-long headers */
            hlen += llen;
            if (hlen > DCC_HDR_CK_MAX*2) {
                  /* truncate headers too big for our buffer */
                  if (!no_copy
                      && ((i = (hlen - wpos)) > 0)) {
                        tmp_write(&buf[wpos], i);
                        hdrs_len += i;
                  }
                  c = buf[hlen-1];
                  hlen = DCC_HDR_CK_MAX;
                  buf[hlen++] = '\r';
                  buf[hlen++] = '\n';
                  wpos = hlen;
                  if (c != '\n')
                        continue;
            }

            /* get the next character after the end-of-line to see if
             * the next line is a continuation */
            if (hlen > 2) {
                  i = getc(ifile);
                  if (i != EOF)
                        ungetc(i, ifile);
                  if (i == ' ' || i == '\t')
                        continue;
            }

            /* not a continuation, so stop reading the field */
            ++total_hdrs;
            /* notice if this line ended with "\r\n" */
            if (hlen > 1 && buf[hlen-2] == '\r')
                  ++cr_hdrs;

            if (!no_copy) {
                  i = hlen - wpos;
                  if (i > 0) {
                        tmp_write(&buf[wpos], hlen-wpos);
                        hdrs_len += i;
                  }
                  return hlen;
            }

            /* at the end of our X-DCC header, look for another */
            no_copy = 0;
            hlen = wpos = 0;
      }
}



static void
tmp_write(const void *buf, int len)
{
      int i;

      if (tmp_fd < 0)
            return;

      if (tmp_rewound)
            dcc_logbad(EX_SOFTWARE, "writing to rewound temp file");

      i = write(tmp_fd, buf, len);
      if (i != len) {
            if (i < 0)
                  dcc_logbad(EX_IOERR, "write(%s,%d): %s",
                           tmp_nm, len, ERROR_STR());
            else
                  dcc_logbad(EX_IOERR, "write(%s,%d)=%d",
                           tmp_nm, len, i);
      }
}



static int
tmp_read(void *buf, int len)
{
      int i;

      i = read(tmp_fd, buf, len);
      if (i <= 0) {
            if (i < 0)
                  dcc_logbad(EX_IOERR, "read(%s,%d): %s",
                           tmp_nm, len, ERROR_STR());
            if (!tmp_nm_unlink)
                  tmp_close();
      }
      return i;
}



static void
tmp_close(void)
{
      if (tmp_fd >= 0) {
            if (0 < close(tmp_fd))
                  dcc_error_msg("close(%s): %s", tmp_nm, ERROR_STR());
            tmp_fd = -1;
      }
      if (tmp_nm_unlink) {
            if (0 < unlink(tmp_nm))
                  dcc_error_msg("unlink(%s): %s", tmp_nm, ERROR_STR());
            tmp_nm_unlink = 0;
      }
}



/* copy some of the temporary file to the output */
static void
scopy(int total_len,                /* copy this much of temporary file */
      u_char complain)              /* 1=ok to complain about problems */
{
      char buf[BUFSIZ];
      int len, i;

      if (tmp_fd < 0)
            return;

      /* if the temporary file has not been rewound,
       * then rewind it now */
      if (!tmp_rewound) {
            tmp_rewound = 1;
            if (0 > lseek(tmp_fd, 0, SEEK_SET)) {
                  if (complain)
                        dcc_logbad(EX_IOERR, "rewind(%s): %s",
                                 tmp_nm, ERROR_STR());
                  tmp_close();
            }
      }

      while (total_len > 0) {
            len = sizeof(buf);
            if (len > total_len) {
                  len = total_len;
            }
            len = tmp_read(buf, len);
            if (len == 0) {
                  if (complain)
                        dcc_logbad(EX_IOERR, "premature end of %s",
                                 tmp_nm);
                  tmp_close();
                  return;
            }

            log_write(buf, len);

            if (ofile && (!cksums_only && !x_dcc_only)) {
                  i = fwrite(buf, 1, len, ofile);
                  if (i != len) {
                        if (complain) {
                              if (feof(ofile))
                                  dcc_logbad(EX_IOERR,
                                           "premature end of %s",
                                           ofile_nm);
                              else
                                  dcc_logbad(EX_IOERR,
                                           "fwrite(%s): %s",
                                           ofile_nm, ERROR_STR());
                        }
                        tmp_close();
                        return;
                  }
            }

            total_len -= len;
      }
}



static void
log_write(const void *buf, int len)
{
      int i;

      if (lfd < 0)
            return;

      i = write(lfd, buf, len);
      if (i == len) {
            logging = 2;
      } else {
            dcc_error_msg("write(log %s): %s", log_nm, ERROR_STR());
            dcc_log_close(0, log_nm, lfd, &ldate);
            lfd = -1;
            logging = 0;
            log_nm[0] = '\0';
      }
}



static void
thr_log_write(void *cp UATTRIB, const char *buf, u_int len)
{
      log_write(buf, len);
}



static int
vlog_print(u_char error, const char *p, va_list args)
{
      char logbuf[MAXHOSTNAMELEN*2];
      int i;

      if (error
          &&  (lfd < 0 || !tmp_rewound)) {
            /* buffer the message if we cannot write to the log file */
            return dcc_vearly_log(&early_log, p, args);
      }

      if (lfd < 0)
            return 0;
      i = vsnprintf(logbuf, sizeof(logbuf), p, args);
      if (i >= ISZ(logbuf))
            i = sizeof(logbuf)-1;
      log_write(logbuf, i);
      return i;
}



static void
log_late(void)
{
      if (early_log.len) {
            log_write(early_log.buf, early_log.len);
            early_log.len = 0;
      }
}



static void PATTRIB(2,3)
log_print(u_char error, const char *p, ...)
{
      va_list args;

      va_start(args, p);
      vlog_print(error, p, args);
      va_end(args);
}



int
thr_log_print(void *cp UATTRIB, u_char error, const char *p, ...)
{
      va_list args;
      int i;

      va_start(args, p);
      i = vlog_print(error, p, args);
      va_end(args);
      return i;
}



static void
log_fin(void)
{
      if (log_nm[0] == '\0')
            return;

      /* Close before renaming to accomodate WIN32 foolishness.
       * Assuming dcc_mkstemp() works properly, there is no race */
      dcc_log_close(0, log_nm, lfd, &ldate);
      lfd = -1;
#ifndef DCC_WIN32
      if (priv_logdir)
            dcc_get_priv_home(dcc_logdir);
#endif
      if (ask_st & ASK_ST_LOGIT) {
            dcc_log_keep(0, log_nm, sizeof(log_nm));
      } else {
            unlink(log_nm);
            log_nm[0] = '\0';
      }
      if (priv_logdir)
            dcc_rel_priv();
}



static void
log_ck(void *arg UATTRIB, const char *buf, u_int buf_len)
{
      if (cksums_only && ofile)
            fputs(buf, ofile);
      log_write(buf, buf_len);
}



/* try to send error message to dccproc log file as well as sendmail */
static void
dccproc_verror_msg(const char *p, va_list args)
{
      DCC_ARGS2_COPY();
      dcc_verror_msg(p, DCC_ARGS2);
      DCC_ARGS2_END();

      vlog_print(1, p, args);
      log_print(1, "\n");

      ask_st |= ASK_ST_LOGIT;
}



/* try to send error message to dccproc log file as well as sendmail */
static void PATTRIB(1,2)
dccproc_error_msg(const char *p, ...)
{
      va_list args;

      va_start(args, p);
      dccproc_verror_msg(p, args);
      va_end(args);
}



void
thr_error_msg(void *cp UATTRIB, const char *p, ...)
{
      va_list args;

      va_start(args, p);
      dccproc_verror_msg(p, args);
      va_end(args);
}



void
thr_trace_msg(void *cp UATTRIB, const char *p, ...)
{
      va_list args;

      va_start(args, p);
      dccproc_verror_msg(p, args);
      va_end(args);
}



/* things are so sick that we must bail out */
void NRATTRIB
dcc_logbad(int ex_code UATTRIB, const char *p, ...)
{
      char buf[BUFSIZ];
      va_list args;
      size_t len;

      log_late();
      if (*p >= ' ' && !tmp_rewound) {
            va_start(args, p);
            dcc_vfatal_msg(p, args);
            va_end(args);

            ask_st |= ASK_ST_LOGIT;
            if (logging > 1)
                  log_write("\n", 1);
            va_start(args, p);
            vlog_print(0, p, args);
            va_end(args);
            log_write("\n\n", 2);
            p = 0;
      }

      /* copy first from the temporary file and then the input
       * to try to ensure that we don't lose mail */
      scopy(INT_MAX, 0);
      if (ifile && ofile && !cksums_only && !x_dcc_only) {
            do {
                  len = fread(buf, 1, sizeof(buf), ifile);
                  if (!len)
                        break;
                  log_write(buf, len);
            } while (len == fwrite(buf, 1, len, ofile));
      }

      if (p && *p >= ' ') {
            va_start(args, p);
            dcc_vfatal_msg(p, args);
            va_end(args);

            log_write("\n\n", 2);
            va_start(args, p);
            vlog_print(0,p, args);
            va_end(args);
            log_write("\n", 1);
      }
      log_fin();

      exit(0);                /* don't tell procmail to reject mail */
}



/* watch for fatal signals */
static void NRATTRIB
sigterm(int sig)
{
      log_fin();
      exit(-sig);
}

Generated by  Doxygen 1.6.0   Back to index