Mercurial > notdcc
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 } |