Courier and DCC

Brandon Long blong@fiction.net
Tue Apr 9 22:27:42 UTC 2002


On 04/09/02 Earl Killian uttered the following other thing:
> Has anyone out there gotten Courier (yet another MTA) to use DCC
> filtering?
> 
> (I'm using DCC with smtpd, but I'm asking for a friend who is bringing
> up Courier, a fairly nice MTA/imap/pop3/webmail package.)

Courier is based on Maildir folders, right?  I'm using the following
hacked version of dccproc that I called dcc-qmail to run the dcc stuff
over a mail message and deliver it to a Maildir folder.  I used it with
qmail first, calling it from .qmail files, and now I'm using it from
postfix from a .forward file...

Granted, this is no different than using procmail and dccproc, and then
just having procmail do the Maildir delivery.  For qmail, you would
probably actually have to add the dcc stuff to the local delivery
program... since there is no way to actually configure anything filter
wise (though, if you have the qmail-filter patch...)

anyways, this might help, it might not.

Brandon
-- 
   "Cui merda tollenda erit?"               
                                           http://www.fiction.net/blong/
-------------- next part --------------
/* Distributed Checksum Clearinghouse server
 *
 * report a message for such as procmail
 *
 * Copyright (c) 2001 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.0.27-1.24 $Revision$
 */

#include "dcc_ck.h"
#include "dcc_xhdr.h"
#include "dcc_paths.h"

#include <limits.h>
#include <sys/utsname.h>


static DCC_EMSG dcc_emsg;

static uid_t real_uid, effective_uid;

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

static char tmpdir_def[] = _PATH_TMP;
static const char *tmpdir = tmpdir_def;
static PATH tfile_nm;
static int tfd = INVALID_HANDLE_VALUE;
static int hdr_len, body_len;

static DCC_TGTS targets = 1;
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 *dcc_ctxt;
static char dcc_xhdr[sizeof(DCC_XHDR)-2+sizeof(DCC_BRAND)+1];
static int dcc_xhdr_len;
static u_char add_xhdr;			/* add instead of replace header */
static u_char query_only;
static u_char cksums_only;		/* output only checksums */
static u_char std_received;		/* first Received: line is standard */

static u_char body_started;
static DCC_GOT_CKS cks;

static DCC_HEADER_BUF header;


static void get_priv(void);
static void rel_priv(void);
static int get_hdr(char *, int);
static void get_cks(const char *, const char *, DCC_CK_TYPES);
static void twrite(const void *, int);
static int tread(void *, int);
static void scopy(int, u_char);


static void NRATTRIB
usage(void)
{
	dcc_logbad(EX_USAGE,
		   "usage: [-VdAQCR]  [-h homedir] [-m map] [-w whiteclnt]"
		   " [-T tmpdir]\n"
		   "   [-a IP-address] [-f env_from] [-t targets]"
		   " [-i infile] [-o outfile]\n"
		   "   [-L ltype,facility.level] <dest>");
}

static char *Hostname = NULL;
static int Counter = 0;

void 
new_mdir_name (char *filename, size_t len)
{
  if (Hostname == NULL)
  {
    struct utsname uts;
    char *p;

    uname (&uts);
    if ((p = strchr (uts.nodename, '.')))
    { 
      *p = '\0';
      Hostname = strdup (uts.nodename);
      *p = '.';
    }
    else
    { 
      Hostname = strdup (uts.nodename);
    }
  }

  snprintf (filename, len, "%ld.%d_%d.%s",
      time (NULL), getpid (), Counter++, Hostname);

}




int NRATTRIB
main(int argc, char **argv)
{
	char buf[2000];
	int blen, i;
	u_long l;
	u_char result;
	DCC_GOT_SUM *cp;
	char tbuf[30], cbuf[sizeof(DCC_SUM)*3];
	char *p;
	char out_mdir[_POSIX_PATH_MAX];
	char o_file[_POSIX_PATH_MAX];
	char o_path[_POSIX_PATH_MAX];
	char n_path[_POSIX_PATH_MAX];

	/* because stderr is often mixed with stdout and effectively
	 * invisible, also complain to syslog */
	dcc_get__progname(argv[0]);
	dcc_log_init(0);

	/* 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 */
	real_uid = getuid();
	if (real_uid == 0)
		effective_uid = 0;
	else
		effective_uid = geteuid();
	/* give up privilege for now */
	if (real_uid != effective_uid
	    && 0 > seteuid(real_uid))
		dcc_error_msg("seteuid(%d): %s",
			      (int)real_uid, strerror(errno));

	ofile = stdout;
	ifile = stdin;
	while ((i = getopt(argc, argv, "VdAQCRh:m:w:T:a:f:t:i:o:L:")) != EOF) {
		switch (i) {
		case 'V':
			fprintf(stderr, DCC_VERSION"\n");
			exit(111);
			break;

		case 'd':
			++dcc_clnt_debug;
			break;

		case 'A':
			add_xhdr = 1;
			break;

		case 'Q':
			query_only = 1;
			break;

		case 'C':
			cksums_only = 1;
			break;

		case 'R':
			std_received = 1;
			break;

		case 'h':
			homedir = optarg;
			break;

		case 'm':
			mapfile_nm = optarg;
			break;

		case 'w':
			white_nm = optarg;
			break;

		case 'T':
			tmpdir = optarg;
			break;

		case 'a':
			if (!dcc_str2ck_ip(cks.sums[DCC_CK_IP].sum, optarg))
				dcc_error_msg("unrecognized IP address \"%s\"",
					      optarg);
			else
				cks.sums[DCC_CK_IP].type = DCC_CK_IP;
			break;

		case 'f':
			if (!dcc_get_cks(&cks, DCC_CK_ENV_FROM, optarg))
				dcc_error_msg("unrecognized %s \"%s\"",
					      dcc_type2str_err(DCC_CK_ENV_FROM),
					      optarg);
			break;

		case 't':
			if (!strcasecmp(optarg, "many")) {
				targets = DCC_TGTS_TOO_MANY;
			} else {
				l = strtoul(optarg, &p, 0);
				if (*p != '\0' || l > 1000)
					dcc_error_msg("invalid count \"%s\"",
						      optarg);
				else
					targets = l;
			}
			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, strerror(errno));
			break;

#if 0
		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, strerror(errno));
			break;
#endif

		case 'L':
			dcc_parse_log_opt(optarg);
			break;

		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;
	if (argc != 1)
		usage();
	strncpy (out_mdir, argv[0], sizeof(out_mdir));
	out_mdir[_POSIX_PATH_MAX-1] = '\0';
	new_mdir_name(o_file, sizeof(o_file));
	snprintf (o_path, sizeof(o_path), "%s/tmp/%s", out_mdir, o_file);
	snprintf (n_path, sizeof(n_path), "%s/new/%s", out_mdir, o_file);
	ofile_nm = o_path;
	ofile = fopen(ofile_nm, "w");
	if (!ofile) 
	{
	  dcc_logbad(111, "bad output file \"%s\": %s",
	      ofile_nm, strerror(errno));
	}

	dcc_clnt_unthread_init();

	if (!dcc_cdhome(dcc_emsg, homedir))
		dcc_error_msg("%s", dcc_emsg);

	/* open the parameter file and start a connection to a DCC server */
	get_priv();
	dcc_ctxt = dcc_clnt_init(dcc_emsg, 0, mapfile_nm, 0);
	rel_priv();
	if (!dcc_ctxt)
		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);

	snprintf(dcc_xhdr, sizeof(dcc_xhdr), DCC_XHDR":",
		 dcc_ctxt->srvr_brand);
	dcc_xhdr_len = strlen(dcc_xhdr);

	if (!cksums_only) {
		snprintf(tfile_nm, sizeof(tfile_nm), "%shdr.XXXXXX", tmpdir);
		tfd = mkstemp(tfile_nm);
		if (tfd < 0)
			dcc_logbad(EX_IOERR, "open(%s): %s",
				   tfile_nm, strerror(errno));
		if (0 > unlink(tfile_nm))
			dcc_logbad(EX_IOERR, "unlink(%s): %s",
				   tfile_nm, strerror(errno));
	}

	/* get the headers */
	hdr_len = 0;
	for (;;) {
		/* get the next header line */
		blen = get_hdr(buf, sizeof(buf));

		/* delete our header */
		if (!add_xhdr
		    && !strncasecmp(buf, dcc_xhdr, dcc_xhdr_len))
			continue;

		twrite(buf, blen);

		/* stop at the separator between the body and headers */
		if (buf[0] == '\r' || buf[0] == '\n') {
			body_len = blen;
			break;
		}
		hdr_len += blen;

#define GET_HDR_CK(h,t) {						    \
			if (!strncasecmp(buf, h, sizeof(h)-1)) {	    \
				get_cks(&buf[sizeof(h)-1], h, DCC_CK_##t);  \
				continue;}}
		GET_HDR_CK("From:", FROM);
		GET_HDR_CK("Subject:", SUBJECT);
		GET_HDR_CK("Message-ID:", MESSAGE_ID);
		if (cks.sums[DCC_CK_ENV_FROM].type == DCC_CK_INVALID)
			GET_HDR_CK("Return-Path:", ENV_FROM);
		if (!strncasecmp(buf, "Received:", sizeof("Received:")-1)) {
			p = &buf[sizeof("Received:")-1];
			/* not last Received: line */
			get_cks(p, "Received:", DCC_CK_RECEIVED);
			/* pick IP address out of first Received: line */
			if (std_received
			    && cks.sums[DCC_CK_IP].type == DCC_CK_INVALID
			    && 0 != (p = strchr(p, '('))
			    && 0 != (p = strchr(p+1, '['))
			    && (i = strspn(p+1, ".0123456789")) > 1
			    && i < INET6_ADDRSTRLEN
			    && p[i+1] == ']') {
				char ip_buf[INET6_ADDRSTRLEN];
				memcpy(ip_buf, p+1, i);
				ip_buf[i] = '\0';
				if (dcc_str2ck_ip(cks.sums[DCC_CK_IP].sum,
						  ip_buf))
					cks.sums[DCC_CK_IP].type = DCC_CK_IP;
			}
			std_received = 0;
			continue;
		}
#undef GET_HDR_CK
	}

	/* 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, strerror(errno));
			if (!blen)
				break;
		}

		twrite(buf, blen);
		body_len += blen;

		if (!body_started) {
			body_started = 1;
			dcc_ck_body_init(&cks, buf, blen);
		} else {
			dcc_ck_body(&cks, buf, blen);
		}
	} while (!feof(ifile));
	fclose(ifile);

	dcc_ck_body_fin(&cks);

	if (!cksums_only) {
		i = 0;			/* emit headers */
		if (0 > lseek(tfd, 0, SEEK_SET))
			dcc_logbad(EX_IOERR, "rewind(%s): %s",
				   tfile_nm, strerror(errno));
		scopy(hdr_len, 1);
	}

	get_priv();
	result = dcc_white_ask(dcc_emsg, dcc_ctxt, &header, white_nm, &cks,
			       query_only ? 0 : targets);
	rel_priv();
	if (!result)
		dcc_error_msg("%s", dcc_emsg);
	else if (header.buf[0] != '\0')
	{
	  if (ofile)
	  {
	    fprintf(ofile, "%s%s\n", header.buf,
		(cr_hdrs > total_hdrs/2) ? "\r" : "");
	  }
	  else
	  {
	    printf("%s%s\n", header.buf,
		(cr_hdrs > total_hdrs/2) ? "\r" : "");
	  }
	}

	/* emit body or checksums */
	if (cksums_only) {
		for (cp = &cks.sums[DCC_CK_TYPE_FIRST];
		     cp <= &cks.sums[DCC_CK_TYPE_LAST];
		     ++cp) {
			if (cp->type == DCC_CK_INVALID)
				continue;
			fprintf(ofile, "%10s: %s\n",
				dcc_type2str(tbuf, sizeof(tbuf),
					     cp->type),
				dcc_ck2str(cbuf, sizeof(cbuf),
					   cp->type, cp->sum));
		}
	} else {
		scopy(body_len, 1);
	}

	if (fclose(ofile)) 
	{
	  dcc_logbad(EX_IOERR, "fclose(%s): %s",
	      ofile_nm, strerror(errno));
	  exit(111);
	}
	if (rename(o_path, n_path))
	{
	  dcc_logbad(EX_IOERR, "rename(%s,%s): %s",
	      o_path, n_path, strerror(errno));
	  exit(111);
	}

	exit(EX_OK);
}



static void
get_priv(void)
{
	if (real_uid != effective_uid
	    && 0 > seteuid(effective_uid))
		dcc_error_msg("seteuid(%d): %s",
			      (int)effective_uid, strerror(errno));
}


static void
rel_priv(void)
{
	if (real_uid != effective_uid
	    && 0 > seteuid(real_uid))
		dcc_error_msg("seteuid(%d): %s",
			      (int)real_uid, strerror(errno));
}



static int				/* length of header */
get_hdr(char *buf, int buflen)
{
	int hlen, i, c;
	const char *p;

	hlen = 0;
	for (;;) {
		p = fgets(&buf[hlen], buflen-hlen, ifile);
		if (!p) {
			if (ferror(ifile))
				dcc_logbad(EX_DATAERR, "fgets(%s): %s",
					   ifile_nm, strerror(errno));
			else
				dcc_logbad(EX_DATAERR, "missing body");
		}
		i = strlen(p);
		hlen += i;

		/* stop on blank line */
		if (hlen == 1
		    || (hlen == 2 && buf[0] == '\r'))
			return hlen;

		/* do not crash on too-long headers */
		if (hlen+1 >= buflen
		    || buf[hlen-1] != '\n')
			return hlen;

		/* get the next character after the end-of-line to see if
		 * the next line is a continuation */
		c = getc(ifile);
		if (c != ' ' && c != '\t') {
			/* not a continuation, so stop */
			if (c != EOF)
				ungetc(c, ifile);
			++total_hdrs;
			if (hlen > 1 && buf[hlen-2] == '\r')
				++cr_hdrs;
			return hlen;
		}

		buf[hlen++] = c;
		if (hlen+1 >= buflen)
			return hlen;
	}
}



static void
get_cks(const char *hdr, const char *hdr_name, DCC_CK_TYPES type)
{
	if (!dcc_get_cks(&cks, type, hdr)
	    && dcc_clnt_debug)
		dcc_error_msg("unrecognized \"%s: %s\"", hdr_name, hdr);
}



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

	if (cksums_only)
		return;

	i = write(tfd, buf, len);
	if (i != len) {
		if (i < 0)
			dcc_logbad(EX_IOERR, "write(%s,%d): %s",
				   tfile_nm, len, strerror(errno));
		else
			dcc_logbad(EX_IOERR, "write(%s,%d)=%d",
				   tfile_nm, len, i);
	}
}



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

	i = read(tfd, buf, len);
	if (i <= 0) {
		serrno = errno;
		close(tfd);
		if (i < 0) {
			tfd = INVALID_HANDLE_VALUE;
			dcc_logbad(EX_IOERR, "read(%s,%d): %s",
				   tfile_nm, len, strerror(serrno));
		}
	}
	return i;
}



/* copy the rest of the temporary file to the output */
static void
scopy(int total_len, u_char complain)
{
	char buf[BUFSIZ];
	int len, i;

	for (;;) {
		len = sizeof(buf);
		if (len > total_len) {
			len = total_len;
			if (!len)
				break;
		}
		len = tread(buf, len);
		if (len == 0) {
			if (complain)
				dcc_logbad(EX_IOERR, "premature end of %s",
					   tfile_nm);
			return;
		}

		i = fwrite(buf, 1, len, ofile);
		if (i != len) {
			if (complain) {
				if (feof(ofile))
					dcc_logbad(EX_IOERR,
						   "premature end of output %s",
						   ofile_nm);
				else
					dcc_logbad(EX_IOERR,
						   "fwrite(%s): %s",
						   ofile_nm,
						   strerror(errno));
			}
			return;
		}

		total_len -= len;
	}
}



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

	va_start(args, p);
	if (*p >= ' ')
		dcc_verror_msg(p, args);

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

	va_end(args);
	/* exit(ex_code); */
	/* always exit with a temporary error here */
	exit(111);
}


More information about the DCC mailing list

Contact vjs@rhyolite.com by mail or use the form.