Commit | Line | Data |
---|---|---|
8e669ac1 | 1 | /* $Cambridge: exim/src/src/bmi_spam.c,v 1.3 2005/02/17 11:58:25 ph10 Exp $ */ |
8523533c TK |
2 | |
3 | /************************************************* | |
4 | * Exim - an Internet mail transport agent * | |
5 | *************************************************/ | |
8e669ac1 | 6 | |
8523533c TK |
7 | /* Code for calling Brightmail AntiSpam. |
8 | Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 | |
9 | License: GPL */ | |
10 | ||
11 | #include "exim.h" | |
12 | #ifdef EXPERIMENTAL_BRIGHTMAIL | |
13 | ||
14 | #include "bmi_spam.h" | |
15 | ||
16 | uschar *bmi_current_optin = NULL; | |
17 | ||
18 | uschar *bmi_process_message(header_line *header_list, int data_fd) { | |
19 | BmiSystem *system = NULL; | |
20 | BmiMessage *message = NULL; | |
21 | BmiError err; | |
22 | BmiErrorLocation err_loc; | |
23 | BmiErrorType err_type; | |
24 | const BmiVerdict *verdict = NULL; | |
25 | FILE *data_file; | |
26 | uschar data_buffer[4096]; | |
27 | uschar localhost[] = "127.0.0.1"; | |
28 | uschar *host_address; | |
29 | uschar *verdicts = NULL; | |
30 | int i,j; | |
8e669ac1 | 31 | |
8523533c TK |
32 | err = bmiInitSystem(BMI_VERSION, (char *)bmi_config_file, &system); |
33 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
34 | err_loc = bmiErrorGetLocation(err); | |
35 | err_type = bmiErrorGetType(err); | |
36 | log_write(0, LOG_PANIC, | |
37 | "bmi error [loc %d type %d]: could not initialize Brightmail system.", (int)err_loc, (int)err_type); | |
38 | return NULL; | |
39 | } | |
40 | ||
41 | err = bmiInitMessage(system, &message); | |
42 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
43 | err_loc = bmiErrorGetLocation(err); | |
44 | err_type = bmiErrorGetType(err); | |
45 | log_write(0, LOG_PANIC, | |
46 | "bmi error [loc %d type %d]: could not initialize Brightmail message.", (int)err_loc, (int)err_type); | |
47 | bmiFreeSystem(system); | |
48 | return NULL; | |
49 | } | |
50 | ||
51 | /* Send IP address of sending host */ | |
52 | if (sender_host_address == NULL) | |
53 | host_address = localhost; | |
54 | else | |
55 | host_address = sender_host_address; | |
56 | err = bmiProcessConnection((char *)host_address, message); | |
57 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
58 | err_loc = bmiErrorGetLocation(err); | |
59 | err_type = bmiErrorGetType(err); | |
60 | log_write(0, LOG_PANIC, | |
61 | "bmi error [loc %d type %d]: bmiProcessConnection() failed (IP %s).", (int)err_loc, (int)err_type, (char *)host_address); | |
62 | bmiFreeMessage(message); | |
63 | bmiFreeSystem(system); | |
64 | return NULL; | |
65 | }; | |
66 | ||
67 | /* Send envelope sender address */ | |
68 | err = bmiProcessFROM((char *)sender_address, message); | |
69 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
70 | err_loc = bmiErrorGetLocation(err); | |
71 | err_type = bmiErrorGetType(err); | |
72 | log_write(0, LOG_PANIC, | |
73 | "bmi error [loc %d type %d]: bmiProcessFROM() failed (address %s).", (int)err_loc, (int)err_type, (char *)sender_address); | |
74 | bmiFreeMessage(message); | |
75 | bmiFreeSystem(system); | |
76 | return NULL; | |
77 | }; | |
78 | ||
79 | /* Send envelope recipients */ | |
80 | for(i=0;i<recipients_count;i++) { | |
81 | recipient_item *r = recipients_list + i; | |
82 | BmiOptin *optin = NULL; | |
8e669ac1 | 83 | |
8523533c TK |
84 | /* create optin object if optin string is given */ |
85 | if ((r->bmi_optin != NULL) && (Ustrlen(r->bmi_optin) > 1)) { | |
86 | debug_printf("passing bmiOptin string: %s\n", r->bmi_optin); | |
87 | bmiOptinInit(&optin); | |
88 | err = bmiOptinMset(optin, r->bmi_optin, ':'); | |
89 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
90 | log_write(0, LOG_PANIC|LOG_MAIN, | |
91 | "bmi warning: [loc %d type %d]: bmiOptinMSet() failed (address '%s', string '%s').", (int)err_loc, (int)err_type, (char *)r->address, (char *)r->bmi_optin); | |
92 | if (optin != NULL) | |
93 | bmiOptinFree(optin); | |
94 | optin = NULL; | |
95 | }; | |
96 | }; | |
8e669ac1 | 97 | |
8523533c | 98 | err = bmiAccumulateTO((char *)r->address, optin, message); |
8e669ac1 | 99 | |
8523533c TK |
100 | if (optin != NULL) |
101 | bmiOptinFree(optin); | |
8e669ac1 | 102 | |
8523533c TK |
103 | if (bmiErrorIsFatal(err) == BMI_TRUE) { |
104 | err_loc = bmiErrorGetLocation(err); | |
105 | err_type = bmiErrorGetType(err); | |
106 | log_write(0, LOG_PANIC, | |
107 | "bmi error [loc %d type %d]: bmiAccumulateTO() failed (address %s).", (int)err_loc, (int)err_type, (char *)r->address); | |
108 | bmiFreeMessage(message); | |
109 | bmiFreeSystem(system); | |
110 | return NULL; | |
111 | }; | |
112 | }; | |
113 | err = bmiEndTO(message); | |
114 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
115 | err_loc = bmiErrorGetLocation(err); | |
116 | err_type = bmiErrorGetType(err); | |
117 | log_write(0, LOG_PANIC, | |
118 | "bmi error [loc %d type %d]: bmiEndTO() failed.", (int)err_loc, (int)err_type); | |
119 | bmiFreeMessage(message); | |
120 | bmiFreeSystem(system); | |
121 | return NULL; | |
122 | }; | |
8e669ac1 | 123 | |
8523533c TK |
124 | /* Send message headers */ |
125 | while (header_list != NULL) { | |
126 | /* skip deleted headers */ | |
127 | if (header_list->type == '*') { | |
128 | header_list = header_list->next; | |
129 | continue; | |
130 | }; | |
131 | err = bmiAccumulateHeaders((const char *)header_list->text, header_list->slen, message); | |
132 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
133 | err_loc = bmiErrorGetLocation(err); | |
134 | err_type = bmiErrorGetType(err); | |
135 | log_write(0, LOG_PANIC, | |
136 | "bmi error [loc %d type %d]: bmiAccumulateHeaders() failed.", (int)err_loc, (int)err_type); | |
137 | bmiFreeMessage(message); | |
138 | bmiFreeSystem(system); | |
139 | return NULL; | |
140 | }; | |
141 | header_list = header_list->next; | |
142 | }; | |
143 | err = bmiEndHeaders(message); | |
144 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
145 | err_loc = bmiErrorGetLocation(err); | |
146 | err_type = bmiErrorGetType(err); | |
147 | log_write(0, LOG_PANIC, | |
148 | "bmi error [loc %d type %d]: bmiEndHeaders() failed.", (int)err_loc, (int)err_type); | |
149 | bmiFreeMessage(message); | |
150 | bmiFreeSystem(system); | |
151 | return NULL; | |
152 | }; | |
8e669ac1 | 153 | |
8523533c TK |
154 | /* Send body */ |
155 | data_file = fdopen(data_fd,"r"); | |
156 | do { | |
157 | j = fread(data_buffer, 1, sizeof(data_buffer), data_file); | |
158 | if (j > 0) { | |
159 | err = bmiAccumulateBody((const char *)data_buffer, j, message); | |
160 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
161 | err_loc = bmiErrorGetLocation(err); | |
162 | err_type = bmiErrorGetType(err); | |
163 | log_write(0, LOG_PANIC, | |
164 | "bmi error [loc %d type %d]: bmiAccumulateBody() failed.", (int)err_loc, (int)err_type); | |
165 | bmiFreeMessage(message); | |
166 | bmiFreeSystem(system); | |
167 | return NULL; | |
168 | }; | |
169 | }; | |
170 | } while (j > 0); | |
171 | err = bmiEndBody(message); | |
172 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
173 | err_loc = bmiErrorGetLocation(err); | |
174 | err_type = bmiErrorGetType(err); | |
175 | log_write(0, LOG_PANIC, | |
176 | "bmi error [loc %d type %d]: bmiEndBody() failed.", (int)err_loc, (int)err_type); | |
177 | bmiFreeMessage(message); | |
178 | bmiFreeSystem(system); | |
179 | return NULL; | |
180 | }; | |
8e669ac1 PH |
181 | |
182 | ||
8523533c TK |
183 | /* End message */ |
184 | err = bmiEndMessage(message); | |
185 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
186 | err_loc = bmiErrorGetLocation(err); | |
187 | err_type = bmiErrorGetType(err); | |
188 | log_write(0, LOG_PANIC, | |
189 | "bmi error [loc %d type %d]: bmiEndMessage() failed.", (int)err_loc, (int)err_type); | |
190 | bmiFreeMessage(message); | |
191 | bmiFreeSystem(system); | |
192 | return NULL; | |
193 | }; | |
8e669ac1 | 194 | |
8523533c TK |
195 | /* get store for the verdict string */ |
196 | verdicts = store_get(1); | |
197 | *verdicts = '\0'; | |
8e669ac1 | 198 | |
8523533c TK |
199 | for ( err = bmiAccessFirstVerdict(message, &verdict); |
200 | verdict != NULL; | |
201 | err = bmiAccessNextVerdict(message, verdict, &verdict) ) { | |
202 | char *verdict_str; | |
203 | ||
204 | err = bmiCreateStrFromVerdict(verdict,&verdict_str); | |
205 | if (!store_extend(verdicts, Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) { | |
206 | /* can't allocate more store */ | |
207 | return NULL; | |
208 | }; | |
209 | if (*verdicts != '\0') | |
210 | Ustrcat(verdicts, US ":"); | |
211 | Ustrcat(verdicts, US verdict_str); | |
212 | bmiFreeStr(verdict_str); | |
213 | }; | |
214 | ||
215 | DEBUG(D_receive) debug_printf("bmi verdicts: %s\n", verdicts); | |
216 | ||
217 | if (Ustrlen(verdicts) == 0) | |
218 | return NULL; | |
219 | else | |
220 | return verdicts; | |
221 | } | |
222 | ||
223 | ||
224 | int bmi_get_delivery_status(uschar *base64_verdict) { | |
225 | BmiError err; | |
226 | BmiErrorLocation err_loc; | |
227 | BmiErrorType err_type; | |
228 | BmiVerdict *verdict = NULL; | |
229 | int rc = 1; /* deliver by default */ | |
8e669ac1 | 230 | |
8523533c TK |
231 | /* always deliver when there is no verdict */ |
232 | if (base64_verdict == NULL) | |
233 | return 1; | |
234 | ||
235 | /* create verdict from base64 string */ | |
236 | err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict); | |
237 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
238 | err_loc = bmiErrorGetLocation(err); | |
239 | err_type = bmiErrorGetType(err); | |
240 | log_write(0, LOG_PANIC, | |
241 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
242 | return 1; | |
243 | }; | |
244 | ||
245 | err = bmiVerdictError(verdict); | |
246 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
247 | /* deliver normally due to error */ | |
248 | rc = 1; | |
249 | } | |
250 | else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) { | |
251 | /* deliver normally */ | |
8e669ac1 | 252 | rc = 1; |
8523533c TK |
253 | } |
254 | else if (bmiVerdictAccessDestination(verdict) == NULL) { | |
255 | /* do not deliver */ | |
256 | rc = 0; | |
257 | } | |
258 | else { | |
259 | /* deliver to alternate location */ | |
260 | rc = 1; | |
261 | }; | |
8e669ac1 | 262 | |
8523533c TK |
263 | bmiFreeVerdict(verdict); |
264 | return rc; | |
265 | } | |
266 | ||
267 | ||
268 | uschar *bmi_get_alt_location(uschar *base64_verdict) { | |
269 | BmiError err; | |
270 | BmiErrorLocation err_loc; | |
271 | BmiErrorType err_type; | |
272 | BmiVerdict *verdict = NULL; | |
273 | uschar *rc = NULL; | |
8e669ac1 | 274 | |
8523533c TK |
275 | /* always deliver when there is no verdict */ |
276 | if (base64_verdict == NULL) | |
277 | return NULL; | |
278 | ||
279 | /* create verdict from base64 string */ | |
280 | err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict); | |
281 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
282 | err_loc = bmiErrorGetLocation(err); | |
283 | err_type = bmiErrorGetType(err); | |
284 | log_write(0, LOG_PANIC, | |
285 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
286 | return NULL; | |
287 | }; | |
8e669ac1 | 288 | |
8523533c TK |
289 | err = bmiVerdictError(verdict); |
290 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
291 | /* deliver normally due to error */ | |
292 | rc = NULL; | |
293 | } | |
294 | else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) { | |
295 | /* deliver normally */ | |
8e669ac1 | 296 | rc = NULL; |
8523533c TK |
297 | } |
298 | else if (bmiVerdictAccessDestination(verdict) == NULL) { | |
299 | /* do not deliver */ | |
300 | rc = NULL; | |
301 | } | |
302 | else { | |
303 | /* deliver to alternate location */ | |
304 | rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1); | |
305 | Ustrcpy(rc, bmiVerdictAccessDestination(verdict)); | |
306 | rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0'; | |
307 | }; | |
8e669ac1 | 308 | |
8523533c TK |
309 | bmiFreeVerdict(verdict); |
310 | return rc; | |
311 | } | |
312 | ||
313 | uschar *bmi_get_base64_verdict(uschar *bmi_local_part, uschar *bmi_domain) { | |
314 | BmiError err; | |
315 | BmiErrorLocation err_loc; | |
316 | BmiErrorType err_type; | |
317 | BmiVerdict *verdict = NULL; | |
318 | const BmiRecipient *recipient = NULL; | |
319 | const char *verdict_str = NULL; | |
320 | uschar *verdict_ptr; | |
321 | uschar *verdict_buffer = NULL; | |
322 | int sep = 0; | |
8e669ac1 | 323 | |
8523533c TK |
324 | /* return nothing if there are no verdicts available */ |
325 | if (bmi_verdicts == NULL) | |
326 | return NULL; | |
8e669ac1 | 327 | |
8523533c TK |
328 | /* allocate room for the b64 verdict string */ |
329 | verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1); | |
8e669ac1 | 330 | |
8523533c TK |
331 | /* loop through verdicts */ |
332 | verdict_ptr = bmi_verdicts; | |
333 | while ((verdict_str = (const char *)string_nextinlist(&verdict_ptr, &sep, | |
334 | verdict_buffer, | |
335 | Ustrlen(bmi_verdicts)+1)) != NULL) { | |
8e669ac1 | 336 | |
8523533c TK |
337 | /* create verdict from base64 string */ |
338 | err = bmiCreateVerdictFromStr(verdict_str, &verdict); | |
339 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
340 | err_loc = bmiErrorGetLocation(err); | |
341 | err_type = bmiErrorGetType(err); | |
342 | log_write(0, LOG_PANIC, | |
343 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, verdict_str); | |
344 | return NULL; | |
345 | }; | |
8e669ac1 | 346 | |
8523533c TK |
347 | /* loop through rcpts for this verdict */ |
348 | for ( recipient = bmiVerdictAccessFirstRecipient(verdict); | |
349 | recipient != NULL; | |
350 | recipient = bmiVerdictAccessNextRecipient(verdict, recipient)) { | |
351 | uschar *rcpt_local_part; | |
352 | uschar *rcpt_domain; | |
8e669ac1 | 353 | |
8523533c TK |
354 | /* compare address against our subject */ |
355 | rcpt_local_part = (unsigned char *)bmiRecipientAccessAddress(recipient); | |
356 | rcpt_domain = Ustrchr(rcpt_local_part,'@'); | |
357 | if (rcpt_domain == NULL) { | |
358 | rcpt_domain = US""; | |
359 | } | |
360 | else { | |
361 | *rcpt_domain = '\0'; | |
362 | rcpt_domain++; | |
363 | }; | |
364 | ||
365 | if ( (strcmpic(rcpt_local_part, bmi_local_part) == 0) && | |
366 | (strcmpic(rcpt_domain, bmi_domain) == 0) ) { | |
367 | /* found verdict */ | |
368 | bmiFreeVerdict(verdict); | |
369 | return (uschar *)verdict_str; | |
8e669ac1 | 370 | }; |
8523533c | 371 | }; |
8e669ac1 | 372 | |
8523533c TK |
373 | bmiFreeVerdict(verdict); |
374 | }; | |
8e669ac1 | 375 | |
8523533c TK |
376 | return NULL; |
377 | } | |
378 | ||
379 | ||
380 | uschar *bmi_get_base64_tracker_verdict(uschar *base64_verdict) { | |
381 | BmiError err; | |
382 | BmiErrorLocation err_loc; | |
383 | BmiErrorType err_type; | |
384 | BmiVerdict *verdict = NULL; | |
385 | uschar *rc = NULL; | |
8e669ac1 | 386 | |
8523533c TK |
387 | /* always deliver when there is no verdict */ |
388 | if (base64_verdict == NULL) | |
389 | return NULL; | |
390 | ||
391 | /* create verdict from base64 string */ | |
392 | err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict); | |
393 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
394 | err_loc = bmiErrorGetLocation(err); | |
395 | err_type = bmiErrorGetType(err); | |
396 | log_write(0, LOG_PANIC, | |
397 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
398 | return NULL; | |
399 | }; | |
8e669ac1 | 400 | |
8523533c TK |
401 | /* create old tracker string from verdict */ |
402 | err = bmiCreateOldStrFromVerdict(verdict, &rc); | |
403 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
404 | err_loc = bmiErrorGetLocation(err); | |
405 | err_type = bmiErrorGetType(err); | |
406 | log_write(0, LOG_PANIC, | |
407 | "bmi error [loc %d type %d]: bmiCreateOldStrFromVerdict() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
408 | return NULL; | |
409 | }; | |
8e669ac1 | 410 | |
8523533c TK |
411 | bmiFreeVerdict(verdict); |
412 | return rc; | |
413 | } | |
414 | ||
415 | ||
416 | int bmi_check_rule(uschar *base64_verdict, uschar *option_list) { | |
417 | BmiError err; | |
418 | BmiErrorLocation err_loc; | |
419 | BmiErrorType err_type; | |
420 | BmiVerdict *verdict = NULL; | |
421 | int rc = 0; | |
422 | uschar *rule_num; | |
423 | uschar *rule_ptr; | |
424 | uschar rule_buffer[32]; | |
425 | int sep = 0; | |
8e669ac1 PH |
426 | |
427 | ||
8523533c TK |
428 | /* no verdict -> no rule fired */ |
429 | if (base64_verdict == NULL) | |
430 | return 0; | |
8e669ac1 | 431 | |
8523533c TK |
432 | /* create verdict from base64 string */ |
433 | err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict); | |
434 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
435 | err_loc = bmiErrorGetLocation(err); | |
436 | err_type = bmiErrorGetType(err); | |
437 | log_write(0, LOG_PANIC, | |
438 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
439 | return 0; | |
440 | }; | |
8e669ac1 | 441 | |
8523533c TK |
442 | err = bmiVerdictError(verdict); |
443 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
444 | /* error -> no rule fired */ | |
445 | bmiFreeVerdict(verdict); | |
446 | return 0; | |
447 | } | |
8e669ac1 | 448 | |
8523533c TK |
449 | /* loop through numbers */ |
450 | rule_ptr = option_list; | |
451 | while ((rule_num = string_nextinlist(&rule_ptr, &sep, | |
452 | rule_buffer, 32)) != NULL) { | |
453 | int rule_int = -1; | |
8e669ac1 | 454 | |
8523533c TK |
455 | /* try to translate to int */ |
456 | sscanf(rule_num, "%d", &rule_int); | |
457 | if (rule_int > 0) { | |
458 | debug_printf("checking rule #%d\n", rule_int); | |
459 | /* check if rule fired on the message */ | |
460 | if (bmiVerdictRuleFired(verdict, rule_int) == BMI_TRUE) { | |
461 | debug_printf("rule #%d fired\n", rule_int); | |
462 | rc = 1; | |
463 | break; | |
464 | }; | |
465 | }; | |
466 | }; | |
467 | ||
468 | bmiFreeVerdict(verdict); | |
469 | return rc; | |
470 | }; | |
471 | ||
472 | #endif |