/* Distributed Checksum Clearinghouse * * SMTP reply strings * * Copyright (c) 2006 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.50-1.21 $Revision$ */ #include "cmn_defs.h" REPLY reject_reply; REPLY reputation_reply; REPLY grey_reply; /* default SMTP messages */ typedef struct { REPLY *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 %s from %s rejected by DCC" static REPLY_DEF rej_def = {&reject_reply, DCC_RCODE, DCC_XCODE, DEF_REJ_MSG, "reject"}; static REPLY_DEF rep_def = {&reputation_reply, DCC_RCODE, DCC_XCODE, "%s bad reputation; see http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%s", "reject by DCC reputation"}; static REPLY_DEF grey_def = {&grey_reply, "452", "4.2.1", "mail %s from %s temporary greylist embargoed", "temporary greylist embargo"}; static REPLY_DEF dnsbl_def = {0, DCC_RCODE, DCC_XCODE, DEF_REJ_MSG, "reject"}; REPLY dcc_fail_reply = {"remporary reject", "451", "4.4.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 should be something like "5.7.1 550 spammer go home" * or "spammer go home". * The pattern can have as many as two "%s" references to the sendmail * queue-ID and SMTP client IP address. */ void parse_reply(REPLY *reply, /* to here */ u_char pat_ok, /* 1=allow %s in pattern */ 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 sb[5+1+3+1]; int p_cnt, i; /* Does the message have a valid xcode and rcode? * First check for sendmail.cf ERROR:### and ERROR:D.S.N:### */ if (!pat_ok && !CSTRCMP(arg, "ERROR:")) { p = dcc_parse_word(0, sb, sizeof(sb), arg+STRZ("ERROR:"), 0, 0, 0); if (p) { i = strlen(sb); if (i == 5+1+3 && sb[5] == ':') { memcpy(reply->xcode, &sb[0], 5); memcpy(reply->rcode, &sb[6], 3); } else if (i == 3) { BUFCPY(reply->xcode, xcode); memcpy(reply->rcode, &sb[0], 3); } else { p = 0; } } } else { p = dcc_parse_word(0, reply->xcode, sizeof(reply->xcode), arg, 0, 0, 0); p = dcc_parse_word(0, reply->rcode, sizeof(reply->rcode), p, 0, 0, 0); } if (!p || reply->rcode[0] < '4' || reply->rcode[0] > '5' || reply->rcode[1] < '0' || reply->rcode[1] > '9' || reply->rcode[2] < '0' || reply->rcode[2] > '9' || reply->rcode[0] != reply->xcode[0] || reply->xcode[1] != '.' || reply->xcode[2] < '0' || reply->xcode[2] > '9' || reply->xcode[3] != '.' || reply->xcode[4] < '0' || reply->xcode[4] > '9') { BUFCPY(reply->xcode, xcode); BUFCPY(reply->rcode, rcode); p = arg; } p += strspn(p, DCC_WHITESPACE); if (!pat_ok || !strchr(p, '%')) { BUFCPY(reply->buf, p); } else { u_char c; char *new_pat; p_cnt = 0; new_pat = reply->buf; do { c = *p++; if (c == '\0') break; if (c == '%') { if (*p != 's' || ++p_cnt > 2) *new_pat++ = '%'; } else if (c < ' ' || c > '~') { /* sendmail does not like control characters in * SMTP status messages */ c = '.'; } *new_pat++ = c; } while (new_pat < LAST(reply->buf)-2); *new_pat = '\0'; } reply->log_result = log_result; } static inline void finish_reply(const REPLY_DEF *def, const char *pat) { parse_reply(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); } /* 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 for all threads based on a -B arg * allow % patterns and so forth */ const REPLY * dnsbl_parse_reply(const char *pat) { REPLY *reply; reply = malloc(sizeof(REPLY)); memset(reply, 0, sizeof(*reply)); parse_reply(reply, 1, dnsbl_def.xcode, dnsbl_def.rcode, pat, dnsbl_def.log_result); return reply; } void make_reply(REPLY *reply, const REPLY *template, const CMN_WORK *cwp) { memcpy(reply->rcode, template->rcode, sizeof(reply->rcode)); memcpy(reply->xcode, template->xcode, sizeof(reply->xcode)); snprintf(reply->buf, sizeof(reply->buf), template->buf, cwp->id, dcc_trim_ffff(cwp->clnt_str)); reply->log_result = template->log_result; }