comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:c7f6b056b673
1 /* Distributed Checksum Clearinghouse
2 *
3 * SMTP reply strings
4 *
5 * Copyright (c) 2008 by Rhyolite Software, LLC
6 *
7 * This agreement is not applicable to any entity which sells anti-spam
8 * solutions to others or provides an anti-spam solution as part of a
9 * security solution sold to other entities, or to a private network
10 * which employs the DCC or uses data provided by operation of the DCC
11 * but does not provide corresponding data to other users.
12 *
13 * Permission to use, copy, modify, and distribute this software without
14 * changes for any purpose with or without fee is hereby granted, provided
15 * that the above copyright notice and this permission notice appear in all
16 * copies and any distributed versions or copies are either unchanged
17 * or not called anything similar to "DCC" or "Distributed Checksum
18 * Clearinghouse".
19 *
20 * Parties not eligible to receive a license under this agreement can
21 * obtain a commercial license to use DCC by contacting Rhyolite Software
22 * at sales@rhyolite.com.
23 *
24 * A commercial license would be for Distributed Checksum and Reputation
25 * Clearinghouse software. That software includes additional features. This
26 * free license for Distributed ChecksumClearinghouse Software does not in any
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse
28 * software
29 *
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
37 * SOFTWARE.
38 *
39 * Rhyolite Software DCC 1.3.103-1.30 $Revision$
40 */
41
42
43 #include "cmn_defs.h"
44
45 REPLY_TPLT reject_reply;
46 REPLY_TPLT reputation_reply;
47 REPLY_TPLT grey_reply;
48 REPLY_TPLT dnsbl_timeo_reply;
49
50
51 /* default SMTP message templates */
52 typedef struct {
53 REPLY_TPLT *reply;
54 const char *rcode; /* default value such as "550" */
55 const char *xcode; /* default value such as "5.7.1" */
56 const char *def_pat; /* default -r pattern */
57 const char *log_result; /* "accept ..." etc. for log */
58 } REPLY_DEF;
59
60 #define DEF_REJ_MSG "mail %ID from %CIP rejected by DCC"
61 static REPLY_DEF rej_def = {&reject_reply, DCC_RCODE, DCC_XCODE,
62 DEF_REJ_MSG,
63 "reject"};
64
65 static REPLY_DEF grey_def = {&grey_reply, "452", "4.2.1",
66 "mail %ID from %CIP temporary greylist embargoed",
67 "temporary greylist embargo"};
68
69 static REPLY_DEF rep_def = {&reputation_reply, DCC_RCODE, DCC_XCODE,
70 "%ID bad reputation; see http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%CIP",
71 "reject by DCC reputation"};
72
73 static REPLY_DEF dnsbl_def = {0, DCC_RCODE, DCC_XCODE,
74 DEF_REJ_MSG,
75 "reject"};
76
77 static REPLY_DEF dnsbl_timeo_def = {&dnsbl_timeo_reply, "452", "4.2.1",
78 "mail %ID from %CIP temporary delayed for DNSBL",
79 "DNSBL check delay"};
80
81
82 REPLY_TPLT dcc_fail_reply = {"temporary reject", {REPLY_TPLT_NULL},
83 "451", "4.4.0", 0, DCC_XHDR_RESULT_DCC_FAIL};
84
85
86 /* Parse a string into RFC 821 and RFC 2034 codes and a pattern to make a
87 * message, or generate the codes for a string that has a message
88 * without codes.
89 * The string should be something like "5.7.1 550 spammer go home"
90 * or "spammer go home". */
91 void
92 make_tplt(REPLY_TPLT *tplt, /* to here */
93 u_char mode, /* 0=sendmail 1=dccid 2=dnsbl */
94 const char *xcode, /* default value such as "5.7.1" */
95 const char *rcode, /* default value such as "550" */
96 const char *arg, /* using this pattern */
97 const char *log_result) /* "reject" etc. for log */
98 {
99 const char *p;
100 char c, *new_pat;
101 char sb[5+1+3+1];
102 int p_cnt, i;
103
104 arg += strspn(arg, DCC_WHITESPACE"\"");
105
106 /* Does the message have a valid xcode and rcode?
107 * First check for sendmail.cf ERROR:### and ERROR:D.S.N:### */
108 if (mode == 0 && !CLITCMP(arg, "ERROR:")) {
109 p = dcc_parse_word(0, sb, sizeof(sb), arg+LITZ("ERROR:"),
110 0, 0, 0);
111 if (p) {
112 i = strlen(sb);
113 if (i == 5+1+3 && sb[5] == ':') {
114 memcpy(tplt->xcode, &sb[0], 5);
115 memcpy(tplt->rcode, &sb[6], 3);
116 } else if (i == 3) {
117 BUFCPY(tplt->xcode, xcode);
118 memcpy(tplt->rcode, &sb[0], 3);
119 } else {
120 p = 0;
121 }
122 }
123 } else {
124 p = dcc_parse_word(0, tplt->xcode, sizeof(tplt->xcode),
125 arg, 0, 0, 0);
126 if (p)
127 p = dcc_parse_word(0, tplt->rcode, sizeof(tplt->rcode),
128 p, 0, 0, 0);
129 }
130 if (!p
131 || tplt->rcode[0] < '4' || tplt->rcode[0] > '5'
132 || tplt->rcode[1] < '0' || tplt->rcode[1] > '9'
133 || tplt->rcode[2] < '0' || tplt->rcode[2] > '9'
134 || tplt->rcode[0] != tplt->xcode[0]
135 || tplt->xcode[1] != '.'
136 || tplt->xcode[2] < '0' || tplt->xcode[2] > '9'
137 || tplt->xcode[3] != '.'
138 || tplt->xcode[4] < '0' || tplt->xcode[4] > '9') {
139 BUFCPY(tplt->xcode, xcode);
140 BUFCPY(tplt->rcode, rcode);
141 p = arg;
142 } else {
143 p += strspn(p, DCC_WHITESPACE);
144 }
145
146 tplt->is_pat = 0;
147 p_cnt = 0;
148 new_pat = tplt->pat;
149 do {
150 c = *p++;
151 if (c == '\0')
152 break;
153 if (c == '%') {
154 /* parse %x for x=
155 * ID message queue ID
156 * CIP SMTP client IP address
157 * EMBARGO null or #%d of greylist embargo number
158 * BTYPE type of blacklist hit
159 * BTGT IP address or name sought in DNSBL
160 * BPROBE domain name found in blacklist
161 * BRESULT value of domain name found in blacklist
162 *
163 * BT obsolete equivalent of BTYPE
164 * BIP obsolete equivalent of BTGT
165 * s obsolete; 1st same as ID; 2nd same as CIP
166 */
167 if (new_pat >= LAST(tplt->pat)-2)
168 continue; /* no room for "%s\0" */
169
170 tplt->is_pat = 1;
171 *new_pat++ = '%';
172 if (mode == 0
173 || p_cnt >= NUM_REPLY_TPLT_ARGS || *p == '%') {
174 /* translate %% and bad % to %% */
175 ++p;
176
177 } else if (!LITCMP(p,"ID")) {
178 p += LITZ("ID");
179 tplt->args[p_cnt++] = REPLY_TPLT_ID;
180 c = 's';
181 } else if (!LITCMP(p,"CIP")) {
182 p += LITZ("CIP");
183 tplt->args[p_cnt++] = REPLY_TPLT_CIP;
184 c = 's';
185 } else if (mode == 2 && !LITCMP(p,"BTYPE")) {
186 p += LITZ("BTYPE");
187 tplt->args[p_cnt++] = REPLY_TPLT_BTYPE;
188 c = 's';
189 } else if (mode == 2 && !LITCMP(p,"BTGT")) {
190 p += LITZ("BTGT");
191 tplt->args[p_cnt++] = REPLY_TPLT_BTGT;
192 c = 's';
193 } else if (mode == 2 && !LITCMP(p,"BPROBE")) {
194 p += LITZ("BPROBE");
195 tplt->args[p_cnt++] = REPLY_TPLT_BPROBE;
196 c = 's';
197 } else if (mode == 2 && !LITCMP(p,"BRESULT")) {
198 p += LITZ("BRESULT");
199 tplt->args[p_cnt++] = REPLY_TPLT_BRESULT;
200 c = 's';
201
202 } else if (*p == 's' && p_cnt < 2) {
203 /* obsolete
204 * translate 1st "%s" to REPLY_TPLT_ID
205 * 2nd to REPLY_TPLT_CIP */
206 ++p;
207 tplt->args[p_cnt] = (p_cnt == 0
208 ? REPLY_TPLT_ID
209 : REPLY_TPLT_CIP);
210 ++p_cnt;
211 c = 's';
212 } else if (mode == 2 && !LITCMP(p,"BIP")) {
213 /* obsolete */
214 p += LITZ("BIP");
215 tplt->args[p_cnt++] = REPLY_TPLT_BTGT;
216 c = 's';
217 } else if (mode == 2 && !LITCMP(p,"BT")) {
218 /* obsolete */
219 p += LITZ("BT");
220 tplt->args[p_cnt++] = REPLY_TPLT_BTYPE;
221 c = 's';
222 }
223
224 } else if (c == '"' && mode == 0) {
225 /* Strip double quotes from sendmail rejection
226 * message. Sendmail needs quotes around the message
227 * so it won't convert blanks to dots. */
228 continue;
229
230 } else if (c < ' ' || c > '~') {
231 /* sendmail does not like control characters in
232 * SMTP status messages */
233 c = '.';
234 }
235
236 *new_pat++ = c;
237 } while (new_pat < LAST(tplt->pat));
238 *new_pat = '\0';
239 while (p_cnt < NUM_REPLY_TPLT_ARGS)
240 tplt->args[p_cnt++] = REPLY_TPLT_NULL;
241
242 tplt->log_result = log_result;
243 }
244
245
246
247 static inline void
248 finish_reply(const REPLY_DEF *def, const char *pat)
249 {
250 make_tplt(def->reply, 1, def->xcode, def->rcode, pat,
251 def->log_result);
252 }
253
254
255
256 void
257 finish_replies(void)
258 {
259 /* make SMTP status strings for cases not set with -rPATTERN */
260 if (reject_reply.rcode[0] == '\0')
261 finish_reply(&rej_def, rej_def.def_pat);
262 if (reputation_reply.rcode[0] == '\0')
263 finish_reply(&rep_def, rep_def.def_pat);
264 if (grey_reply.rcode[0] == '\0')
265 finish_reply(&grey_def, grey_def.def_pat);
266 if (dnsbl_timeo_reply.rcode[0] == '\0')
267 finish_reply(&dnsbl_timeo_def, dnsbl_timeo_def.def_pat);
268 }
269
270
271
272 /* parse another "-r pattern" argument */
273 void
274 parse_reply_arg(const char *arg)
275 {
276 /* a blank string implies the default */
277 arg += strspn(arg, DCC_WHITESPACE);
278
279 /* each -r finishes the next SMTP rejection message */
280
281 if (reject_reply.rcode[0] == '\0') {
282 if (*arg == '\0')
283 finish_reply(&rej_def, rej_def.def_pat);
284 else
285 finish_reply(&rej_def, arg);
286 return;
287 }
288
289 if (grey_reply.rcode[0] == '\0') {
290 if (*arg == '\0') {
291 finish_reply(&grey_def, grey_def.def_pat);
292 return;
293 }
294 finish_reply(&grey_def, arg);
295 if (grey_reply.rcode[0] != '4'
296 || grey_reply.xcode[0] != '4') {
297 dcc_error_msg("invalid greylist message: %s", arg);
298 finish_reply(&grey_def, grey_def.def_pat);
299 }
300 return;
301 }
302
303 if (reputation_reply.rcode[0] == '\0') {
304 if (*arg == '\0')
305 finish_reply(&rep_def, rep_def.def_pat);
306 else
307 finish_reply(&rep_def, arg);
308 return;
309 }
310
311 dcc_error_msg("more than 3 -r settings");
312 }
313
314
315
316 /* set up the DNSBL SMTP error message template for all threads based on
317 * a -B arg. Allow % patterns and so forth */
318 const REPLY_TPLT *
319 dnsbl_parse_reply(const char *pat)
320 {
321 REPLY_TPLT *tplt;
322
323 tplt = malloc(sizeof(REPLY_TPLT));
324 memset(tplt, 0, sizeof(REPLY_TPLT));
325 make_tplt(tplt, 2, dnsbl_def.xcode, dnsbl_def.rcode, pat,
326 dnsbl_def.log_result);
327 return tplt;
328 }
329
330
331
332 void
333 make_reply(REPLY_STRS *strs, const REPLY_TPLT *tplt,
334 const CMN_WORK *cwp, const DNSBL_GROUP *blg)
335 {
336 const char *args[NUM_REPLY_TPLT_ARGS];
337 int i;
338
339 strs->rcode = tplt->rcode;
340 strs->xcode = tplt->xcode;
341 if (!tplt->is_pat) {
342 strs->str = tplt->pat;
343 } else {
344 for (i = 0; i < NUM_REPLY_TPLT_ARGS; ++i) {
345 switch (tplt->args[i]) {
346 case REPLY_TPLT_NULL:
347 args[i] = "";
348 break;
349 case REPLY_TPLT_ID:
350 args[i] = cwp->id;
351 break;
352 case REPLY_TPLT_CIP:
353 args[i] = dcc_trim_ffff(cwp->clnt_str);
354 break;
355 case REPLY_TPLT_BTYPE:
356 args[i] = (blg && blg->btype) ? blg->btype : "";
357 break;
358 case REPLY_TPLT_BTGT:
359 args[i] = blg ? blg->tgt.c : "";
360 break;
361 case REPLY_TPLT_BPROBE:
362 args[i] = blg ? blg->probe.c : "";
363 break;
364 case REPLY_TPLT_BRESULT:
365 args[i] = blg ? blg->result : "";
366 break;
367 }
368 }
369 snprintf(strs->str_buf, sizeof(strs->str_buf), tplt->pat,
370 args[0], args[1], args[2], args[3], args[4], args[5]);
371 strs->str = strs->str_buf;
372 }
373
374 strs->log_result = tplt->log_result;
375 }