diff thrlib/reply.c @ 0:c7f6b056b673

First import of vendor version
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 13:49:58 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thrlib/reply.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,375 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * SMTP reply strings
+ *
+ * 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.30 $Revision$
+ */
+
+
+#include "cmn_defs.h"
+
+REPLY_TPLT reject_reply;
+REPLY_TPLT reputation_reply;
+REPLY_TPLT grey_reply;
+REPLY_TPLT dnsbl_timeo_reply;
+
+
+/* default SMTP message templates */
+typedef struct {
+    REPLY_TPLT *reply;
+    const char *rcode;			/* default value such as "550" */
+    const char *xcode;			/* default value such as "5.7.1" */
+    const char *def_pat;		/* default -r pattern */
+    const char *log_result;		/* "accept ..." etc. for log */
+} REPLY_DEF;
+
+#define	DEF_REJ_MSG "mail %ID from %CIP rejected by DCC"
+static REPLY_DEF rej_def = {&reject_reply, DCC_RCODE, DCC_XCODE,
+	DEF_REJ_MSG,
+	"reject"};
+
+static REPLY_DEF grey_def = {&grey_reply, "452", "4.2.1",
+	"mail %ID from %CIP temporary greylist embargoed",
+	"temporary greylist embargo"};
+
+static REPLY_DEF rep_def = {&reputation_reply, DCC_RCODE, DCC_XCODE,
+	"%ID bad reputation; see http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%CIP",
+	"reject by DCC reputation"};
+
+static REPLY_DEF dnsbl_def = {0, DCC_RCODE, DCC_XCODE,
+	DEF_REJ_MSG,
+	"reject"};
+
+static REPLY_DEF dnsbl_timeo_def = {&dnsbl_timeo_reply, "452", "4.2.1",
+	"mail %ID from %CIP temporary delayed for DNSBL",
+	"DNSBL check delay"};
+
+
+REPLY_TPLT dcc_fail_reply = {"temporary reject", {REPLY_TPLT_NULL},
+	"451", "4.4.0", 0, DCC_XHDR_RESULT_DCC_FAIL};
+
+
+/* Parse a string into RFC 821 and RFC 2034 codes and a pattern to make a
+ * message, or generate the codes for a string that has a message
+ * without codes.
+ * The string should be something like "5.7.1 550 spammer go home"
+ * or "spammer go home". */
+void
+make_tplt(REPLY_TPLT *tplt,		/* to here */
+	  u_char mode,			/* 0=sendmail 1=dccid 2=dnsbl */
+	  const char *xcode,		/* default value such as "5.7.1" */
+	  const char *rcode,		/* default value such as "550" */
+	  const char *arg,		/* using this pattern */
+	  const char *log_result)	/* "reject" etc. for log */
+{
+	const char *p;
+	char c, *new_pat;
+	char sb[5+1+3+1];
+	int p_cnt, i;
+
+	arg += strspn(arg, DCC_WHITESPACE"\"");
+
+	/* Does the message have a valid xcode and rcode?
+	 * First check for sendmail.cf ERROR:### and ERROR:D.S.N:### */
+	if (mode == 0 && !CLITCMP(arg, "ERROR:")) {
+		p = dcc_parse_word(0, sb, sizeof(sb), arg+LITZ("ERROR:"),
+				   0, 0, 0);
+		if (p) {
+			i = strlen(sb);
+			if (i == 5+1+3 && sb[5] == ':') {
+				memcpy(tplt->xcode, &sb[0], 5);
+				memcpy(tplt->rcode, &sb[6], 3);
+			} else if (i == 3) {
+				BUFCPY(tplt->xcode, xcode);
+				memcpy(tplt->rcode, &sb[0], 3);
+			} else {
+				p = 0;
+			}
+		}
+	} else {
+		p = dcc_parse_word(0, tplt->xcode, sizeof(tplt->xcode),
+				   arg, 0, 0, 0);
+		if (p)
+			p = dcc_parse_word(0, tplt->rcode, sizeof(tplt->rcode),
+					   p, 0, 0, 0);
+	}
+	if (!p
+	    || tplt->rcode[0] < '4' || tplt->rcode[0] > '5'
+	    || tplt->rcode[1] < '0' || tplt->rcode[1] > '9'
+	    || tplt->rcode[2] < '0' || tplt->rcode[2] > '9'
+	    || tplt->rcode[0] != tplt->xcode[0]
+	    || tplt->xcode[1] != '.'
+	    || tplt->xcode[2] < '0' || tplt->xcode[2] > '9'
+	    || tplt->xcode[3] != '.'
+	    || tplt->xcode[4] < '0' || tplt->xcode[4] > '9') {
+		BUFCPY(tplt->xcode, xcode);
+		BUFCPY(tplt->rcode, rcode);
+		p = arg;
+	} else {
+		p += strspn(p, DCC_WHITESPACE);
+	}
+
+	tplt->is_pat = 0;
+	p_cnt = 0;
+	new_pat = tplt->pat;
+	do {
+		c = *p++;
+		if (c == '\0')
+			break;
+		if (c == '%') {
+			/* parse %x for x=
+			 *  ID	    message queue ID
+			 *  CIP	    SMTP client IP address
+			 *  EMBARGO null or #%d of greylist embargo number
+			 *  BTYPE   type of blacklist hit
+			 *  BTGT    IP address or name sought in DNSBL
+			 *  BPROBE  domain name found in blacklist
+			 *  BRESULT value of domain name found in blacklist
+			 *
+			 *  BT	    obsolete equivalent of BTYPE
+			 *  BIP	    obsolete equivalent of BTGT
+			 *  s	    obsolete; 1st same as ID; 2nd same as CIP
+			 */
+			if (new_pat >= LAST(tplt->pat)-2)
+				continue;   /* no room for "%s\0" */
+
+			tplt->is_pat = 1;
+			*new_pat++ = '%';
+			if (mode == 0
+			    || p_cnt >= NUM_REPLY_TPLT_ARGS || *p == '%') {
+				/* translate %% and bad % to %% */
+				++p;
+
+			} else if (!LITCMP(p,"ID")) {
+					p += LITZ("ID");
+				tplt->args[p_cnt++] = REPLY_TPLT_ID;
+				c = 's';
+			} else if (!LITCMP(p,"CIP")) {
+				p += LITZ("CIP");
+				tplt->args[p_cnt++] = REPLY_TPLT_CIP;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BTYPE")) {
+				p += LITZ("BTYPE");
+				tplt->args[p_cnt++] = REPLY_TPLT_BTYPE;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BTGT")) {
+				p += LITZ("BTGT");
+				tplt->args[p_cnt++] = REPLY_TPLT_BTGT;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BPROBE")) {
+				p += LITZ("BPROBE");
+				tplt->args[p_cnt++] = REPLY_TPLT_BPROBE;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BRESULT")) {
+				p += LITZ("BRESULT");
+				tplt->args[p_cnt++] = REPLY_TPLT_BRESULT;
+				c = 's';
+
+			} else if (*p == 's' && p_cnt < 2) {
+				/* obsolete
+				 * translate 1st "%s" to REPLY_TPLT_ID
+				 * 2nd to REPLY_TPLT_CIP */
+				++p;
+				tplt->args[p_cnt] = (p_cnt == 0
+						     ? REPLY_TPLT_ID
+						     : REPLY_TPLT_CIP);
+				++p_cnt;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BIP")) {
+				/* obsolete */
+				p += LITZ("BIP");
+				tplt->args[p_cnt++] = REPLY_TPLT_BTGT;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BT")) {
+				/* obsolete */
+				p += LITZ("BT");
+				tplt->args[p_cnt++] = REPLY_TPLT_BTYPE;
+				c = 's';
+			}
+
+		} else if (c == '"' && mode == 0) {
+			/* Strip double quotes from sendmail rejection
+			 * message. Sendmail needs quotes around the message
+			 * so it won't convert blanks to dots. */
+			continue;
+
+		} else if (c < ' ' || c > '~') {
+			/* sendmail does not like control characters in
+			 * SMTP status messages */
+			c = '.';
+		}
+
+		*new_pat++ = c;
+	} while (new_pat < LAST(tplt->pat));
+	*new_pat = '\0';
+	while (p_cnt < NUM_REPLY_TPLT_ARGS)
+		tplt->args[p_cnt++] = REPLY_TPLT_NULL;
+
+	tplt->log_result = log_result;
+}
+
+
+
+static inline void
+finish_reply(const REPLY_DEF *def, const char *pat)
+{
+	make_tplt(def->reply, 1, def->xcode, def->rcode, pat,
+		  def->log_result);
+}
+
+
+
+void
+finish_replies(void)
+{
+	/* make SMTP status strings for cases not set with -rPATTERN */
+	if (reject_reply.rcode[0] == '\0')
+		finish_reply(&rej_def, rej_def.def_pat);
+	if (reputation_reply.rcode[0] == '\0')
+		finish_reply(&rep_def, rep_def.def_pat);
+	if (grey_reply.rcode[0] == '\0')
+		finish_reply(&grey_def, grey_def.def_pat);
+	if (dnsbl_timeo_reply.rcode[0] == '\0')
+		finish_reply(&dnsbl_timeo_def, dnsbl_timeo_def.def_pat);
+}
+
+
+
+/* parse another "-r pattern" argument */
+void
+parse_reply_arg(const char *arg)
+{
+	/* a blank string implies the default */
+	arg += strspn(arg, DCC_WHITESPACE);
+
+	/* each -r finishes the next SMTP rejection message */
+
+	if (reject_reply.rcode[0] == '\0') {
+		if (*arg == '\0')
+			finish_reply(&rej_def, rej_def.def_pat);
+		else
+			finish_reply(&rej_def, arg);
+		return;
+	}
+
+	if (grey_reply.rcode[0] == '\0') {
+		if (*arg == '\0') {
+			finish_reply(&grey_def, grey_def.def_pat);
+			return;
+		}
+		finish_reply(&grey_def, arg);
+		if (grey_reply.rcode[0] != '4'
+		    || grey_reply.xcode[0] != '4') {
+			dcc_error_msg("invalid greylist message: %s", arg);
+			finish_reply(&grey_def, grey_def.def_pat);
+		}
+		return;
+	}
+
+	if (reputation_reply.rcode[0] == '\0') {
+		if (*arg == '\0')
+			finish_reply(&rep_def, rep_def.def_pat);
+		else
+			finish_reply(&rep_def, arg);
+		return;
+	}
+
+	dcc_error_msg("more than 3 -r settings");
+}
+
+
+
+/* set up the DNSBL SMTP error message template for all threads based on
+ *	a -B arg.  Allow % patterns and so forth */
+const REPLY_TPLT *
+dnsbl_parse_reply(const char *pat)
+{
+	REPLY_TPLT *tplt;
+
+	tplt = malloc(sizeof(REPLY_TPLT));
+	memset(tplt, 0, sizeof(REPLY_TPLT));
+	make_tplt(tplt, 2, dnsbl_def.xcode, dnsbl_def.rcode, pat,
+		  dnsbl_def.log_result);
+	return tplt;
+}
+
+
+
+void
+make_reply(REPLY_STRS *strs, const REPLY_TPLT *tplt,
+	   const CMN_WORK *cwp, const DNSBL_GROUP *blg)
+{
+	const char *args[NUM_REPLY_TPLT_ARGS];
+	int i;
+
+	strs->rcode = tplt->rcode;
+	strs->xcode = tplt->xcode;
+	if (!tplt->is_pat) {
+		strs->str = tplt->pat;
+	} else {
+		for (i = 0; i < NUM_REPLY_TPLT_ARGS; ++i) {
+			switch (tplt->args[i]) {
+			case REPLY_TPLT_NULL:
+				args[i] = "";
+				break;
+			case REPLY_TPLT_ID:
+				args[i] = cwp->id;
+				break;
+			case REPLY_TPLT_CIP:
+				args[i] = dcc_trim_ffff(cwp->clnt_str);
+				break;
+			case REPLY_TPLT_BTYPE:
+				args[i] = (blg && blg->btype) ? blg->btype : "";
+				break;
+			case REPLY_TPLT_BTGT:
+				args[i] = blg ? blg->tgt.c : "";
+				break;
+			case REPLY_TPLT_BPROBE:
+				args[i] = blg ? blg->probe.c : "";
+				break;
+			case REPLY_TPLT_BRESULT:
+				args[i] = blg ? blg->result : "";
+				break;
+			}
+		}
+		snprintf(strs->str_buf, sizeof(strs->str_buf), tplt->pat,
+			 args[0], args[1], args[2], args[3], args[4], args[5]);
+		strs->str = strs->str_buf;
+	}
+
+	strs->log_result = tplt->log_result;
+}