view dccsight/dccsight.c @ 3:b689077d4918

Ignore old patches
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 14:31:24 +0100
parents c7f6b056b673
children
line wrap: on
line source

/* Distributed Checksum Clearinghouse server
 *
 * report the previously computed checksums of a message
 *
 * Copyright (c) 2008 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 without
 * changes for any purpose with or without fee is hereby granted, provided
 * that the above copyright notice and this permission notice appear in all
 * copies and any distributed versions or copies are either unchanged
 * or not called anything similar to "DCC" or "Distributed Checksum
 * Clearinghouse".
 *
 * Parties not eligible to receive a license under this agreement can
 * obtain a commercial license to use DCC by contacting Rhyolite Software
 * at sales@rhyolite.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.103-1.70 $Revision$
 */

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


static DCC_EMSG dcc_emsg;

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

static char *local_tgts_str;
static DCC_TGTS local_tgts = 1;
static u_char local_tgts_spam, local_tgts_set;

static const char* white_nm;
static const char *ifile_nm;
static FILE *ifile;
static const char *grey_sum;

static DCC_CLNT_CTXT *ctxt;
static u_char print_cksums;

static ASK_ST ask_st;
static FLTR_SWS rcpt_sws;

static DCC_GOT_CKS cks;
static DCC_CKS_WTGTS wtgts;
static u_char have_sum;

static DCC_HEADER_BUF header;

/* kludges to avoid linking with dnsbl.c */
DNSBL *dnsbls;
u_char have_dnsbl_groups;

static void do_grey(void);
static int add_cksum(DCC_EMSG, DCC_WF *, DCC_CK_TYPES, DCC_SUM, DCC_TGTS);
static void print_ck(void *arg, const char *, u_int);


static void NRATTRIB
usage(void)
{
	dcc_logbad(EX_USAGE,
		   "usage: [-VdCQ] [-h homedir] [-m map] [-w whiteclnt]"
		   " [-t targets]\n"
		   "   [-c type,[log-thold,][spam-thold]] [-g [not-]type]\n"
		   "   [-i infile] [-G grey-cksum] [-L ltype,facility.level]");
}



int NRATTRIB
main(int argc, char **argv)
{
	char buf[200];
	const char *bufp;
	char type_str[DCC_XHDR_MAX_TYPE_LEN+1];
	DCC_CK_TYPES type;
	u_long l;
	u_char skip_heading, result;
	char c1, c2, *p;
	int i;

	dcc_syslog_init(0, 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();

	ifile = stdin;
	while ((i = getopt(argc, argv, "VdCQh:m:w:t:c:g:i:G:L:")) != -1) {
		switch (i) {
		case 'V':
			fprintf(stderr, DCC_VERSION"\n");
			exit(EX_OK);
			break;

		case 'd':
			++dcc_clnt_debug;
			break;

		case 'Q':
			ask_st |= ASK_ST_QUERY;
			break;

		case 'C':
			print_cksums = 1;
			break;

		case 'h':
			homedir = optarg;
			break;

		case 'm':
			mapfile_nm = optarg;
			break;

		case 'w':
			white_nm = optarg;
			break;

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

		case 'c':
			dcc_parse_tholds("-c ", optarg);
			break;

		case 'g':		/* honor not-spam "counts" */
			dcc_parse_honor(optarg);
			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 'G':
			grey_sum = optarg;
			break;

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

		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;
	if (argc != 0)
		usage();

	dcc_clnt_unthread_init();
	dcc_cdhome(0, homedir, 1);

	/* open /var/dcc/map and start a connection to a DCC server */
	ctxt = dcc_clnt_init(dcc_emsg, 0, mapfile_nm, DCC_CLNT_FG_NONE);
	if (!ctxt)
		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);

	if (grey_sum) {
		if (ifile_nm)
			dcc_logbad(EX_USAGE, "-i and -G incompatible");
		do_grey();
		exit(EX_OK);
	}

	if (!ifile_nm)
		ifile_nm = "stdin";

	/* get the checksums */
	skip_heading = 0;
	for (;;) {
		bufp = fgets(buf, sizeof(buf), ifile);
		if (!bufp) {
			if (ferror(ifile))
				dcc_logbad(EX_DATAERR, "fgets(%s): %s",
					   ifile_nm, ERROR_STR());
			break;
		}

		/* ignore blank lines */
		i = strlen(buf);
		if (!i) {
			skip_heading = 0;
			continue;
		}

		/* trim leading and trailing whitespace */
		p = &buf[i-1];
		bufp += strspn(bufp, DCC_WHITESPACE);
		c2 = *p;		/* should be '\n' */
		while (p > bufp) {
			c1 = *(p-1);
			if (c1 != '\r' && c1 != ' ' && c1 != '\t')
				break;
			*--p = c2;
		}
		if (*bufp == '\n' || *bufp == '\0') {
			skip_heading = 0;
			continue;
		}

		/* ignore DCC header lines such as in the output
		 * of `dccproc -C` */
		if (skip_heading == 0
		    && !CLITCMP(bufp, DCC_XHDR_START)) {
			skip_heading = 1;
			continue;
		}
		/* skip headings for the checksums */
		if (skip_heading <= 1
		    && !CLITCMP(bufp, DCC_XHDR_REPORTED)) {
			skip_heading = 2;
			continue;
		}

		/* handle the next checksum */
		bufp = dcc_parse_word(dcc_emsg, type_str, sizeof(type_str),
				      bufp, "checksum type", 0, 0);
		if (!bufp)
			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
		type = dcc_str2type_wf(type_str, -1);
		if (type != DCC_CK_ENV_TO
		    && 0 >= dcc_parse_hex_ck(dcc_emsg, &cmn_wf,
					     type_str, type,
					     bufp, 1, add_cksum))
			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
	}
	fclose(ifile);

	if (!have_sum)
		dcc_logbad(EX_DATAERR, "no reportable checksums");

	dcc_wf_init(&cmn_wf, 0);
	if (!unthr_ask_white(dcc_emsg, &ask_st, &rcpt_sws,
			     white_nm, &cks, wtgts))
		dcc_error_msg("%s", dcc_emsg);

	if (!local_tgts_set) {
		local_tgts = (ask_st & ASK_ST_QUERY) ? 0 : 1;
		local_tgts_spam = 0;
	} else if (local_tgts == 0) {
		ask_st |= ASK_ST_QUERY;
		local_tgts_spam = 0;
	} else if (ask_st & ASK_ST_QUERY) {
		dcc_error_msg("\"-t %s\" is incompatible with \"-Q\"",
			      local_tgts_str);
		local_tgts = 0;
		local_tgts_spam = 0;
	}
	if (local_tgts == DCC_TGTS_TOO_MANY) {
		local_tgts = 1;
		local_tgts_spam = 1;
	}
	if (local_tgts != 0
	    && (ask_st & ASK_ST_CLNT_ISSPAM))
		local_tgts_spam = 1;

	result = unthr_ask_dcc(dcc_emsg, ctxt, &header, &ask_st,
			       &cks, local_tgts_spam, local_tgts);
	if (!result) {
		dcc_error_msg("%s", dcc_emsg);
	} else if (header.buf[0] != '\0') {
		printf("%s", header.buf);
	} else if (dcc_clnt_debug) {
		if (ask_st & ASK_ST_WLIST_NOTSPAM)
			printf("no header; checksums are locally whitelisted");
		else
			printf("no header");
	}

	if (print_cksums)
		dcc_print_cks(print_ck, 0, local_tgts_spam, local_tgts,
			      &cks, wtgts, 0);

	exit(EX_OK);
}



static void NRATTRIB
do_grey(void)
{
	union {
	    u_int32_t n[4];
	    DCC_SUM sum;
	} u;
	DCC_REPORT rpt;
	DCC_OP_RESP resp;

	if (4 != sscanf(grey_sum, DCC_CKSUM_HEX_PAT,
			&u.n[0], &u.n[1], &u.n[2], &u.n[3]))
		dcc_logbad(EX_USAGE,
			   "unrecognized greylist checksum");
	u.n[0] = htonl(u.n[0]);
	u.n[1] = htonl(u.n[1]);
	u.n[2] = htonl(u.n[2]);
	u.n[3] = htonl(u.n[3]);

	memset(&rpt, 0, sizeof(rpt));
	memcpy(rpt.cks[0].sum, u.sum, sizeof(rpt.cks[0].sum));
	rpt.cks[0].type = DCC_CK_GREY3;
	rpt.cks[0].len = sizeof(rpt.cks[0]);
	if (!dcc_clnt_op(dcc_emsg, ctxt, DCC_CLNT_FG_GREY, 0, 0, 0,
			 &rpt.hdr, (sizeof(rpt) - sizeof(rpt.cks)
				    + sizeof(rpt.cks[0])),
			 (ask_st & ASK_ST_QUERY)
			 ? DCC_OP_GREY_QUERY : DCC_OP_GREY_WHITE,
			 &resp, sizeof(resp)))
		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);

	if (!dcc_ck_grey_answer(dcc_emsg, &resp))
		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);

	switch (ntohl(resp.gans.triple)) {
	case DCC_TGTS_OK:		/* embargo ended just now */
		printf(DCC_XHDR_EMBARGO_ENDED"\n");
		break;
	case DCC_TGTS_TOO_MANY:		/* no current embargo */
		printf(DCC_XHDR_EMBARGO_PASS"\n");
		break;
	case DCC_TGTS_GREY_WHITE:	/* whitelisted for greylisting */
		printf(DCC_XHDR_EMBARGO_OK"\n");
		break;
	default:			/* embargoed */
		printf(DCC_XHDR_EMBARGO"\n");
		break;
	}
	exit(EX_OK);
}



static int
add_cksum(DCC_EMSG emsg, DCC_WF *wf UATTRIB,
	  DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts)
{
	static DCC_SUM zero;

	if (cks.sums[type].type != DCC_CK_INVALID
	    && type != DCC_CK_SUB) {
		dcc_pemsg(EX_DATAERR, emsg, "duplicate %s checksum",
			  dcc_type2str_err(type, 0, 0, 0));
	}

	/* envelope Rcpt_To values are never sent to the server */
	if (type == DCC_CK_ENV_TO)
		return 1;

	/* do not send FUZ2 missing checksum */
	if (type == DCC_CK_FUZ2
	    && !memcmp(sum, zero, sizeof(DCC_SUM)))
		return 1;

	memcpy(cks.sums[type].sum, sum, sizeof(cks.sums[type].sum));
	cks.sums[type].type = type;
	cks.sums[type].rpt2srvr = 1;
	cks.sums[type].tgts = DCC_TGTS_INVALID;
	if (tgts)
		have_sum = 1;
	return 1;
}



static void
print_ck(void *arg UATTRIB, const char *buf, u_int buf_len)
{
	fwrite(buf, buf_len, 1, stdout);
}