Mercurial > notdcc
comparison thrlib/cmn.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 * threaded version of client library | |
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.148 $Revision$ | |
40 */ | |
41 | |
42 | |
43 #include "cmn_defs.h" | |
44 #include "dcc_paths.h" | |
45 | |
46 CMN_ACTION action = CMN_REJECT; | |
47 | |
48 CHGHDR chghdr = SETHDR; | |
49 | |
50 const char *userdirs; | |
51 static DCC_PATH userdirs_path; | |
52 static int userdirs_len; | |
53 | |
54 u_char dcc_query_only; | |
55 u_char try_extra_hard; /* 0 or DCC_CLNT_FG_NO_FAIL */ | |
56 u_char to_white_only; | |
57 | |
58 u_int dcc_ctxt_sn = 1; /* change X-DCC header server name */ | |
59 | |
60 const char *max_max_work_src = "FD_SETSIZE limit"; | |
61 int max_work; | |
62 int init_work; | |
63 int total_work; | |
64 | |
65 static int total_rcpt_sts; | |
66 RCPT_ST *rcpt_st_free; | |
67 | |
68 /* cwf_mutex protects all CWF structures as well as the cmn_wf structure */ | |
69 typedef struct cwf { /* private whitelist state */ | |
70 struct cwf *older, *newer; | |
71 DCC_WF wf; | |
72 } CWF; | |
73 static CWF *cur_cwf, cwfs[NUM_CWFS]; | |
74 | |
75 /* protected by user_log_mutex */ | |
76 static int user_log_fd = -1; | |
77 static int log_fd2 = -1; | |
78 | |
79 | |
80 /* the work lock must be held or not yet exist */ | |
81 static void | |
82 add_rcpt_sts(int i) | |
83 { | |
84 RCPT_ST *rcpt_st; | |
85 | |
86 total_rcpt_sts += i; | |
87 | |
88 rcpt_st = dcc_malloc(sizeof(*rcpt_st)*i); | |
89 memset(rcpt_st, 0, sizeof(*rcpt_st)*i); | |
90 | |
91 while (i-- != 0) { | |
92 rcpt_st->fwd = rcpt_st_free; | |
93 rcpt_st_free = rcpt_st; | |
94 ++rcpt_st; | |
95 } | |
96 } | |
97 | |
98 | |
99 | |
100 void | |
101 cmn_init(void) | |
102 { | |
103 init_work = 50; | |
104 if (init_work > max_max_work) | |
105 init_work = max_max_work; | |
106 add_rcpt_sts(init_work); | |
107 | |
108 finish_replies(); | |
109 | |
110 /* start the client library threads and locks */ | |
111 dcc_clnt_thread_init(); | |
112 for (cur_cwf = cwfs; cur_cwf <= LAST(cwfs); ++cur_cwf) { | |
113 cur_cwf->newer = cur_cwf-1; | |
114 cur_cwf->older = cur_cwf+1; | |
115 dcc_wf_init(&cur_cwf->wf, DCC_WF_PER_USER); | |
116 } | |
117 cur_cwf = cwfs; | |
118 LAST(cwfs)->older = cur_cwf; | |
119 cur_cwf->newer = LAST(cwfs); | |
120 | |
121 totals_init(); | |
122 } | |
123 | |
124 | |
125 | |
126 void | |
127 cmn_create(CMN_WORK *cwp) | |
128 { | |
129 cwp->tmp_fd = -1; | |
130 cwp->log_fd = -1; | |
131 } | |
132 | |
133 | |
134 | |
135 u_char | |
136 cmn_open_tmp(CMN_WORK *cwp) | |
137 { | |
138 cwp->tmp_fd = dcc_mkstemp(cwp->emsg, | |
139 cwp->tmp_nm, sizeof(cwp->tmp_nm), | |
140 cwp->id, sizeof(cwp->id), | |
141 0, 0, DCC_TMP_LOG_PREFIX, 1, 0); | |
142 | |
143 return cwp->tmp_fd >= 0; | |
144 } | |
145 | |
146 | |
147 | |
148 void | |
149 cmn_close_tmp(CMN_WORK *cwp) | |
150 { | |
151 if (cwp->tmp_fd >= 0) { | |
152 if (0 > close(cwp->tmp_fd)) | |
153 thr_error_msg(cwp, "close(%s): %s", | |
154 cwp->tmp_nm, ERROR_STR()); | |
155 cwp->tmp_fd = -1; | |
156 } | |
157 cwp->tmp_nm[0] = '\0'; | |
158 } | |
159 | |
160 | |
161 | |
162 u_char | |
163 cmn_write_tmp(CMN_WORK *cwp, const void *buf, int len) | |
164 { | |
165 int i; | |
166 | |
167 if (cwp->tmp_fd < 0) | |
168 return 1; | |
169 | |
170 i = write(cwp->tmp_fd, buf, len); | |
171 if (i == len) | |
172 return 1; | |
173 | |
174 if (i < 0) | |
175 thr_error_msg(cwp, "write(%s,%d): %s", | |
176 cwp->tmp_nm, len, ERROR_STR()); | |
177 else | |
178 thr_error_msg(cwp, "write(%s,%d)=%d", | |
179 cwp->tmp_nm, len, i); | |
180 cmn_close_tmp(cwp); | |
181 return 0; | |
182 } | |
183 | |
184 | |
185 | |
186 /* If the immediate SMTP client because it is a listed MX server, | |
187 * then we must ignore its IP address and keep looking for the | |
188 * real SMTP client. */ | |
189 u_char /* 1=listed MX server */ | |
190 check_mx_listing(CMN_WORK *cwp) | |
191 { | |
192 DCC_TGTS tgts; | |
193 u_char result; | |
194 | |
195 lock_wf(); | |
196 result = dcc_white_mx(cwp->emsg, &tgts, &cwp->cks); | |
197 unlock_wf(); | |
198 if (!result) { | |
199 thr_error_msg(cwp, "%s", cwp->emsg); | |
200 return 0; | |
201 } | |
202 | |
203 if (tgts == DCC_TGTS_OK) { | |
204 return 0; | |
205 } | |
206 | |
207 if (tgts == DCC_TGTS_SUBMIT_CLIENT) { | |
208 /* Common SMTP submission clients are too dumb to do the | |
209 * right thing with 4yz rejections of individual Rcpt_To | |
210 * commands. So reject the message for all or no recipients. */ | |
211 cwp->cmn_fgs |= CMN_FG_FROM_SUBMIT; | |
212 thr_log_print(cwp, 1, | |
213 "%s is a listed 'submit' client\n", | |
214 dcc_trim_ffff(cwp->sender_str)); | |
215 return 0; | |
216 } | |
217 | |
218 if (tgts == DCC_TGTS_OK_MXDCC) { | |
219 thr_log_print(cwp, 1, | |
220 "%s is a whitelisted MX server with DCC client\n", | |
221 dcc_trim_ffff(cwp->sender_str)); | |
222 cwp->ask_st |= ASK_ST_QUERY; | |
223 } else if (tgts == DCC_TGTS_OK_MX) { | |
224 thr_log_print(cwp, 1, "%s is a whitelisted MX server\n", | |
225 dcc_trim_ffff(cwp->sender_str)); | |
226 } else { | |
227 return 0; | |
228 } | |
229 | |
230 /* we cannot greylist or reject through our MX servers */ | |
231 cwp->cmn_fgs |= CMN_FG_FROM_MX; | |
232 if (cwp->action == CMN_REJECT) | |
233 cwp->action = CMN_DISCARD; | |
234 | |
235 cwp->sender_name[0] = '\0'; | |
236 cwp->sender_str[0] = '\0'; | |
237 dcc_unget_ip_ck(&cwp->cks); | |
238 | |
239 /* tell caller to look at the next Received: header */ | |
240 return 1; | |
241 } | |
242 | |
243 | |
244 | |
245 /* clear a common work area for a message, possibly not the first | |
246 * in the session */ | |
247 void | |
248 cmn_clear(CMN_WORK *cwp, struct work *wp, | |
249 u_char new_conn) /* 1=first message for the connection */ | |
250 { | |
251 log_stop(cwp); | |
252 | |
253 cmn_close_tmp(cwp); | |
254 | |
255 if (cwp->num_rcpts) | |
256 free_rcpt_sts(cwp, 1); | |
257 | |
258 memset(&cwp->CMN_WORK_ZERO, 0, | |
259 sizeof(*cwp) - ((char*)&cwp->CMN_WORK_ZERO - (char*)cwp)); | |
260 cwp->cmn_fgs = 0; | |
261 cwp->mail_host[0] = '\0'; | |
262 cwp->env_from[0] = '\0'; | |
263 cwp->early_log.len = 0; | |
264 cwp->emsg[0] = '\0'; | |
265 cwp->id[0] = '\0'; | |
266 cwp->header.used = 0; | |
267 cwp->cmn_fgs = CMN_FG_LOG_EARLY; | |
268 if (dcc_query_only) | |
269 cwp->ask_st |= ASK_ST_QUERY_GREY; | |
270 cwp->action = action; | |
271 | |
272 if (new_conn) { | |
273 cwp->wp = wp; | |
274 cwp->helo[0] = '\0'; | |
275 cwp->clnt_name[0] = '\0'; | |
276 cwp->clnt_str[0] = '\0'; | |
277 } else { | |
278 /* assume for now that the sender is the current SMTP client */ | |
279 strcpy(cwp->sender_name, cwp->clnt_name); | |
280 strcpy(cwp->sender_str, cwp->clnt_str); | |
281 } | |
282 } | |
283 | |
284 | |
285 | |
286 /* free all of the per-recipient state for a message */ | |
287 void | |
288 free_rcpt_sts(CMN_WORK *cwp, u_char need_lock) | |
289 { | |
290 RCPT_ST *rcpt_st, *next_rcpt_st; | |
291 | |
292 rcpt_st = cwp->rcpt_st_first; | |
293 if (!rcpt_st) | |
294 return; | |
295 | |
296 if (need_lock) | |
297 lock_work(); | |
298 cwp->rcpt_st_first = 0; | |
299 do { | |
300 next_rcpt_st = rcpt_st->fwd; | |
301 rcpt_st->fwd = rcpt_st_free; | |
302 rcpt_st_free = rcpt_st; | |
303 } while ((rcpt_st = next_rcpt_st) != 0); | |
304 cwp->num_rcpts = 0; | |
305 | |
306 if (need_lock) | |
307 unlock_work(); | |
308 } | |
309 | |
310 | |
311 | |
312 RCPT_ST * | |
313 alloc_rcpt_st(CMN_WORK *cwp, | |
314 u_char unlocked) /* 1=unlocked on entry & exit */ | |
315 { | |
316 RCPT_ST *rcpt_st; | |
317 | |
318 if (cwp->num_rcpts >= MAX_RCPTS) { | |
319 thr_error_msg(cwp, "too many recipients"); | |
320 return 0; | |
321 } | |
322 | |
323 if (unlocked) | |
324 lock_work(); | |
325 rcpt_st = rcpt_st_free; | |
326 if (!rcpt_st) { | |
327 if (dcc_clnt_debug > 1) | |
328 thr_trace_msg(cwp, | |
329 "add %d recipient blocks to %d", | |
330 init_work, total_rcpt_sts); | |
331 add_rcpt_sts(init_work); | |
332 rcpt_st = rcpt_st_free; | |
333 } | |
334 rcpt_st_free = rcpt_st->fwd; | |
335 if (unlocked) | |
336 unlock_work(); | |
337 | |
338 rcpt_st->fwd = 0; | |
339 rcpt_st->log_pos_white = -1; | |
340 rcpt_st->log_pos_to = -1; | |
341 memset(rcpt_st->wtgts, 0, sizeof(rcpt_st->wtgts)); | |
342 rcpt_st->env_to_tgts = 0; | |
343 rcpt_st->user_tgts = 0; | |
344 rcpt_st->grey_result = ASK_GREY_OFF; | |
345 rcpt_st->embargo_num = 0; | |
346 rcpt_st->fgs = 0; | |
347 rcpt_st->sws = 0; | |
348 rcpt_st->user[0] = '\0'; | |
349 rcpt_st->rej_msg[0] = '\0'; | |
350 rcpt_st->dir[0] = '\0'; | |
351 rcpt_st->user_log_nm[0] = '\0'; | |
352 | |
353 rcpt_st->cwp = cwp; | |
354 if (!cwp->rcpt_st_first) { | |
355 cwp->rcpt_st_first = rcpt_st; | |
356 } else { | |
357 cwp->rcpt_st_last->fwd = rcpt_st; | |
358 } | |
359 cwp->rcpt_st_last = rcpt_st; | |
360 ++cwp->num_rcpts; | |
361 | |
362 return rcpt_st; | |
363 } | |
364 | |
365 | |
366 | |
367 void | |
368 parse_userdirs(const char *arg) | |
369 { | |
370 /* add '/' to end of the path without converting it to "/" */ | |
371 if (*arg == '\0') { | |
372 userdirs_path[0] = '\0'; | |
373 userdirs_len = 0; | |
374 } else { | |
375 strncpy(userdirs_path, arg, | |
376 sizeof(userdirs_path)); | |
377 userdirs_len = strlen(userdirs_path)-1; | |
378 while (userdirs_len > 1 && userdirs_path[userdirs_len] == '/') | |
379 --userdirs_len; | |
380 userdirs_path[++userdirs_len] = '/'; | |
381 userdirs_path[++userdirs_len] = '\0'; | |
382 | |
383 } | |
384 userdirs = userdirs_path; | |
385 } | |
386 | |
387 | |
388 | |
389 /* sanitize recipient mailbox and per-user log and whitelist directory */ | |
390 u_char /* 0=complain about something */ | |
391 get_user_dir(RCPT_ST *rcpt_st, | |
392 const char *str1, int str1_len, const char *str2, int str2_len) | |
393 { | |
394 char *p; | |
395 char c; | |
396 u_char seen_slash; | |
397 int dots; | |
398 int i; | |
399 | |
400 if (!userdirs) { | |
401 rcpt_st->dir[0] = '\0'; | |
402 return 1; | |
403 } | |
404 | |
405 memcpy(rcpt_st->dir, userdirs, userdirs_len); | |
406 i = userdirs_len; | |
407 if (i+str1_len < ISZ(rcpt_st->dir)) | |
408 memcpy(&rcpt_st->dir[i], str1, str1_len); | |
409 i += str1_len; | |
410 if (str2) { | |
411 if (i+str2_len+1 < ISZ(rcpt_st->dir)) { | |
412 rcpt_st->dir[i++] = '/'; | |
413 memcpy(&rcpt_st->dir[i], str2, str2_len); | |
414 } | |
415 i += str2_len; | |
416 } | |
417 if (i >= ISZ(rcpt_st->dir)) { | |
418 dcc_pemsg(EX_DATAERR, rcpt_st->cwp->emsg, | |
419 "recipient \"%s\" is too long", rcpt_st->dir); | |
420 rcpt_st->dir[0] = '\0'; | |
421 return 0; | |
422 } | |
423 rcpt_st->dir[i] = '\0'; | |
424 | |
425 /* To get a consistent directory name, | |
426 * convert ASCII upper to lower case. | |
427 * Be simplistic about international character sets and | |
428 * avoid locale and portability complications. | |
429 * Refuse insecure paths. */ | |
430 seen_slash = 1; /* userdirs ends with '/' */ | |
431 dots = 0; | |
432 p = &rcpt_st->dir[userdirs_len]; | |
433 for (;;) { | |
434 c = *p; | |
435 if (c == '/' || c == '\\' || c == '\0') { | |
436 if (dots == 2) { | |
437 dcc_pemsg(EX_DATAERR, rcpt_st->cwp->emsg, | |
438 "path \"%s\" is insecure", | |
439 rcpt_st->dir); | |
440 rcpt_st->dir[0] = '\0'; | |
441 return 0; | |
442 } | |
443 if (c == '\0') | |
444 break; | |
445 seen_slash = 1; | |
446 dots = 0; | |
447 } else if (c == '.' && seen_slash && dots <= 1) { | |
448 ++dots; | |
449 } else { | |
450 *p = DCC_TO_LOWER(c); | |
451 seen_slash = 0; | |
452 dots = 0; | |
453 } | |
454 ++p; | |
455 } | |
456 | |
457 return 1; | |
458 } | |
459 | |
460 | |
461 | |
462 /* start main log file */ | |
463 void | |
464 log_start(CMN_WORK *cwp) | |
465 { | |
466 char date_buf[40]; | |
467 | |
468 /* don't even whine if there is no log directory */ | |
469 if (dcc_main_logdir[0] == '\0') | |
470 return; | |
471 | |
472 /* nothing to do if we already have a log file */ | |
473 if (cwp->log_fd >= 0) | |
474 return; | |
475 | |
476 cwp->log_size = 0; | |
477 cwp->log_fd = dcc_main_log_open(cwp->emsg, cwp->log_nm, | |
478 cwp->id, sizeof(cwp->id)); | |
479 if (cwp->log_fd < 0) { | |
480 static time_t whined; | |
481 time_t now; | |
482 | |
483 /* complain about not being able to open log files | |
484 * only occassionally */ | |
485 now = time(0); | |
486 if (now < whined || now > whined+5*60 || dcc_clnt_debug) | |
487 dcc_error_msg("%s", cwp->emsg); | |
488 whined = now; | |
489 cwp->emsg[0] = '\0'; | |
490 return; | |
491 } | |
492 | |
493 gettimeofday(&cwp->ldate, 0); | |
494 | |
495 thr_log_print(cwp, 0, DCC_LOG_DATE_PAT"\n", | |
496 dcc_time2str(date_buf, sizeof(date_buf), DCC_LOG_DATE_FMT, | |
497 cwp->ldate.tv_sec)); | |
498 } | |
499 | |
500 | |
501 | |
502 /* get an independent FD for the main log file that can be | |
503 * repositioned without affecting additional output to the main log. */ | |
504 static u_char | |
505 log2_start(CMN_WORK *cwp) | |
506 { | |
507 DCC_PATH abs_nm; | |
508 | |
509 #ifdef DCC_DEBUG_CLNT_LOCK | |
510 if (user_log_owner != pthread_self()) | |
511 dcc_logbad(EX_SOFTWARE, "don't have user_log lock"); | |
512 #endif | |
513 | |
514 if (log_fd2 >= 0) | |
515 return 1; | |
516 | |
517 /* give up if things are already broken */ | |
518 if (log_fd2 != -1 | |
519 || cwp->log_fd < 0) | |
520 return 0; | |
521 | |
522 /* Some systems don't synchronize the meta data among FDs for | |
523 * a file, causing the second FD to appear to be truncated. */ | |
524 if (fsync(cwp->log_fd) < 0) | |
525 thr_error_msg(cwp, "log_fd fsync(%s): %s", | |
526 fnm2abs_err(abs_nm, cwp->log_nm), | |
527 ERROR_STR()); | |
528 | |
529 log_fd2 = open(cwp->log_nm, O_RDWR, 0); | |
530 if (log_fd2 < 0) { | |
531 thr_error_msg(cwp, "log_fd2 open(%s): %s", | |
532 fnm2abs_err(abs_nm, cwp->log_nm), | |
533 ERROR_STR()); | |
534 log_fd2 = -2; | |
535 return 0; | |
536 } | |
537 | |
538 return 1; | |
539 } | |
540 | |
541 | |
542 | |
543 static void | |
544 log_fd2_close(int flag) | |
545 { | |
546 if (user_log_owner == pthread_self()) { | |
547 if (log_fd2 >= 0) | |
548 close(log_fd2); | |
549 log_fd2 = flag; | |
550 } | |
551 } | |
552 | |
553 | |
554 | |
555 void | |
556 log_stop(CMN_WORK *cwp) | |
557 { | |
558 thr_log_late(cwp); | |
559 log_fd2_close(-1); | |
560 | |
561 if (cwp->log_fd < 0) | |
562 return; | |
563 | |
564 /* Close before renaming to accomodate WIN32 foolishness. | |
565 * Assuming dcc_mkstemp() works properly, there is no race */ | |
566 dcc_log_close(0, cwp->log_nm, cwp->log_fd, &cwp->ldate); | |
567 if (!(cwp->ask_st & ASK_ST_LOGIT)) { | |
568 /* Delete the log file if it is not interesting */ | |
569 unlink(cwp->log_nm); | |
570 } else { | |
571 /* give it a permanent name if it is interesting */ | |
572 dcc_log_keep(0, cwp->log_nm); | |
573 } | |
574 cwp->log_nm[0] = '\0'; | |
575 cwp->log_fd = -1; | |
576 } | |
577 | |
578 | |
579 | |
580 void | |
581 log_write(CMN_WORK *cwp, const void *buf, u_int buflen) | |
582 { | |
583 int result; | |
584 | |
585 if (cwp->log_fd < 0) | |
586 return; | |
587 | |
588 if (!buflen) | |
589 buflen = strlen(buf); | |
590 cwp->log_size += buflen; | |
591 | |
592 result = write(cwp->log_fd, buf, buflen); | |
593 if (buflen == (u_int)result) | |
594 return; | |
595 | |
596 if (result < 0) | |
597 dcc_error_msg("write(%s): %s", cwp->log_nm, ERROR_STR()); | |
598 else | |
599 dcc_error_msg("write(%s,%d)=%d", cwp->log_nm, buflen, result); | |
600 dcc_log_close(0, cwp->log_nm, cwp->log_fd, &cwp->ldate); | |
601 cwp->log_fd = -1; | |
602 } | |
603 | |
604 | |
605 | |
606 void | |
607 log_body_write(CMN_WORK *cwp, const char *buf, u_int buflen) | |
608 { | |
609 int trimlen; | |
610 const char *p, *lim; | |
611 | |
612 if (cwp->log_fd < 0) | |
613 return; | |
614 | |
615 /* just write if there is room */ | |
616 trimlen = MAX_LOG_KBYTE*1024 - cwp->log_size; | |
617 if (trimlen >= (int)buflen) { | |
618 log_write(cwp, buf, buflen); | |
619 return; | |
620 } | |
621 | |
622 /* do nothing if too much already written */ | |
623 if (trimlen < 0) | |
624 return; | |
625 | |
626 /* look for and end-of-line near the end of the buffer | |
627 * so that we can make the truncation pretty */ | |
628 lim = buf; | |
629 p = lim+trimlen; | |
630 if (trimlen > 90) | |
631 lim += trimlen-90; | |
632 while (--p > lim) { | |
633 if (*p == '\n') { | |
634 trimlen = p-buf+1; | |
635 break; | |
636 } | |
637 } | |
638 log_write(cwp, buf, trimlen); | |
639 if (buf[trimlen-1] != '\n') | |
640 LOG_CMN_EOL(cwp); | |
641 LOG_CMN_CAPTION(cwp, DCC_LOG_TRN_MSG_CR); | |
642 cwp->log_size = MAX_LOG_KBYTE*1024+1; | |
643 } | |
644 | |
645 | |
646 | |
647 off_t | |
648 log_lseek_get(CMN_WORK *cwp) | |
649 { | |
650 off_t result; | |
651 | |
652 if (cwp->log_fd < 0) | |
653 return 0; | |
654 result = lseek(cwp->log_fd, 0, SEEK_END); | |
655 if (result == -1) { | |
656 thr_error_msg(cwp, "lseek(%s, 0, SEEK_END): %s", | |
657 cwp->log_nm, ERROR_STR()); | |
658 dcc_log_close(0, cwp->log_nm, cwp->log_fd, &cwp->ldate); | |
659 cwp->log_fd = -1; | |
660 return 0; | |
661 } | |
662 return result; | |
663 } | |
664 | |
665 | |
666 | |
667 static u_char | |
668 log_lseek_set(CMN_WORK *cwp, off_t pos) | |
669 { | |
670 #ifdef DCC_DEBUG_CLNT_LOCK | |
671 if (user_log_owner != pthread_self()) | |
672 dcc_logbad(EX_SOFTWARE, "don't have user_log lock"); | |
673 #endif | |
674 | |
675 if (log_fd2 < 0) | |
676 return 0; | |
677 | |
678 if (-1 == lseek(log_fd2, pos, SEEK_SET)) { | |
679 thr_error_msg(cwp, "lseek(%s,%d,SEEK_SET): %s", | |
680 cwp->log_nm, (int)pos, ERROR_STR()); | |
681 log_fd2_close(-2); | |
682 return 0; | |
683 } | |
684 | |
685 return 1; | |
686 } | |
687 | |
688 | |
689 | |
690 /* put something into a log file | |
691 * does not append '\n' */ | |
692 static int /* bytes written */ | |
693 vthr_log_print(CMN_WORK *cwp, | |
694 u_char error, /* 1=important enough to buffer */ | |
695 const char *p, va_list args) | |
696 { | |
697 char logbuf[LOGBUF_SIZE*2]; | |
698 int i; | |
699 | |
700 if (cwp->log_fd < 0 | |
701 || (error && (cwp->cmn_fgs & CMN_FG_LOG_EARLY))) { | |
702 return dcc_vearly_log(&cwp->early_log, p, args); | |
703 } | |
704 | |
705 i = vsnprintf(logbuf, sizeof(logbuf), p, args); | |
706 if (i < ISZ(logbuf)) { | |
707 log_write(cwp, logbuf, i); | |
708 return i; | |
709 } | |
710 log_write(cwp, logbuf, sizeof(logbuf)); | |
711 log_write(cwp, "...", 3); | |
712 return i+3; | |
713 } | |
714 | |
715 | |
716 | |
717 /* does not append '\n' */ | |
718 int PATTRIB(3,4) /* bytes written */ | |
719 thr_log_print(void *cwp, u_char error, const char *pat, ...) | |
720 { | |
721 va_list args; | |
722 int i; | |
723 | |
724 va_start(args, pat); | |
725 i = vthr_log_print(cwp, error, pat, args); | |
726 va_end(args); | |
727 return i; | |
728 } | |
729 | |
730 | |
731 | |
732 int /* bytes written */ | |
733 thr_error_msg(void *cwp0, const char *pat, ...) | |
734 { | |
735 CMN_WORK *cwp = cwp0; | |
736 va_list args; | |
737 int i; | |
738 | |
739 va_start(args, pat); | |
740 dcc_verror_msg(pat, args); | |
741 va_end(args); | |
742 | |
743 va_start(args, pat); | |
744 i = vthr_log_print(cwp, 1, pat, args); | |
745 va_end(args); | |
746 thr_log_print(cwp, 1, "\n"); | |
747 | |
748 cwp->ask_st |= ASK_ST_LOGIT; | |
749 | |
750 return i+1; | |
751 } | |
752 | |
753 | |
754 | |
755 void | |
756 thr_trace_msg(void *cwp0, const char *p, ...) | |
757 { | |
758 va_list args; | |
759 | |
760 va_start(args, p); | |
761 dcc_vtrace_msg(p, args); | |
762 va_end(args); | |
763 | |
764 if (cwp0) { | |
765 CMN_WORK *cwp = cwp0; | |
766 va_start(args, p); | |
767 vthr_log_print(cwp, 1, p, args); | |
768 va_end(args); | |
769 thr_log_print(cwp, 1, "\n"); | |
770 | |
771 cwp->ask_st |= ASK_ST_LOGIT; | |
772 } | |
773 } | |
774 | |
775 | |
776 | |
777 void | |
778 thr_log_late(CMN_WORK *cwp) | |
779 { | |
780 cwp->cmn_fgs &= ~CMN_FG_LOG_EARLY; | |
781 if (cwp->early_log.len) { | |
782 log_write(cwp, cwp->early_log.buf, cwp->early_log.len); | |
783 cwp->early_log.len = 0; | |
784 } | |
785 } | |
786 | |
787 | |
788 | |
789 void | |
790 thr_log_envelope(CMN_WORK *cwp, u_char ip_placekeeper) | |
791 { | |
792 RCPT_ST *rcpt_st; | |
793 off_t cur_pos; | |
794 int i; | |
795 | |
796 cwp->cmn_fgs |= CMN_FG_ENV_LOGGED; | |
797 | |
798 /* install the sender in blank area in the log file that we skipped */ | |
799 cwp->log_ip_pos = log_lseek_get(cwp) + LITZ(DCC_XHDR_TYPE_IP": "); | |
800 if (cwp->sender_str[0] != '\0') { | |
801 /* Dccm will not have computed the checksum | |
802 * if there were no envelope Mail_From commands | |
803 * On a second message in a session, dccm will not have | |
804 * looked at cwp->clnt_addr */ | |
805 if (cwp->cks.sums[DCC_CK_IP].type == DCC_CK_INVALID) | |
806 dcc_get_ipv6_ck(&cwp->cks, &cwp->clnt_addr); | |
807 i = thr_log_print(cwp, 0, | |
808 DCC_XHDR_TYPE_IP": %s %s\n", | |
809 cwp->sender_name, cwp->sender_str); | |
810 | |
811 } else if (ip_placekeeper) { | |
812 /* log a blank, place keeping string for the IP address | |
813 * so that it can be inserted later */ | |
814 i = thr_log_print(cwp, 0, | |
815 DCC_XHDR_TYPE_IP": %*s\n", | |
816 1+1+1+INET6_ADDRSTRLEN, ""); | |
817 } else { | |
818 i = 0; | |
819 } | |
820 i -= LITZ(DCC_XHDR_TYPE_IP": \n"); | |
821 cwp->log_ip_len = i > 0 ? i : 0; | |
822 | |
823 /* log HELO value if we have it | |
824 * make checksum of it or of null string if we don't */ | |
825 if (cwp->helo[0] != '\0') | |
826 thr_log_print(cwp, 0, "HELO: %s\n", cwp->helo); | |
827 dcc_ck_get_sub(&cwp->cks, "helo", cwp->helo); | |
828 | |
829 if (cwp->env_from[0] != '\0') { | |
830 LOG_CMN_CAPTION(cwp, DCC_XHDR_TYPE_ENV_FROM": "); | |
831 log_write(cwp, cwp->env_from, 0); | |
832 dcc_get_cks(&cwp->cks, DCC_CK_ENV_FROM, cwp->env_from, 1); | |
833 | |
834 if (cwp->mail_host[0] == '\0') | |
835 parse_mail_host(cwp->env_from, cwp->mail_host, | |
836 sizeof(cwp->mail_host)); | |
837 | |
838 LOG_CMN_CAPTION(cwp, " mail_host="); | |
839 log_write(cwp, cwp->mail_host, 0); | |
840 if (cwp->mail_host[0] != '\0') | |
841 dcc_ck_get_sub(&cwp->cks, "mail_host", cwp->mail_host); | |
842 LOG_CMN_EOL(cwp); | |
843 } | |
844 | |
845 cwp->log_pos_to_first = cur_pos = log_lseek_get(cwp); | |
846 for (rcpt_st = cwp->rcpt_st_first; rcpt_st; rcpt_st = rcpt_st->fwd) { | |
847 rcpt_st->log_pos_to = cur_pos; | |
848 LOG_CMN_CAPTION(cwp, DCC_XHDR_TYPE_ENV_TO": "); | |
849 log_write(cwp, rcpt_st->env_to, 0); | |
850 if (rcpt_st->fgs & RCPT_FG_BAD_USERNAME) { | |
851 LOG_CMN_CAPTION(cwp, " "DCC_XHDR_MTA_REJECTION"\n"); | |
852 } else { | |
853 LOG_CMN_CAPTION(cwp, " addr="); | |
854 log_write(cwp, rcpt_st->user, 0); | |
855 LOG_CMN_CAPTION(cwp, " dir="); | |
856 log_write(cwp, rcpt_st->dir, 0); | |
857 LOG_CMN_EOL(cwp); | |
858 } | |
859 cur_pos = log_lseek_get(cwp); | |
860 } | |
861 cwp->log_pos_to_end = cur_pos; | |
862 | |
863 /* log the blank line between the log file header and mail message */ | |
864 LOG_CMN_EOL(cwp); | |
865 } | |
866 | |
867 | |
868 | |
869 /* open the connection to the nearest DCC server */ | |
870 u_char | |
871 ck_dcc_ctxt(CMN_WORK *cwp) | |
872 { | |
873 if (cwp->dcc_ctxt_sn != dcc_ctxt_sn) { | |
874 cwp->dcc_ctxt_sn = dcc_ctxt_sn; | |
875 cwp->dcc_ctxt = dcc_clnt_init(cwp->emsg, cwp->dcc_ctxt, 0, | |
876 DCC_CLNT_FG_BAD_SRVR_OK | |
877 | DCC_CLNT_FG_NO_PICK_SRVR | |
878 | DCC_CLNT_FG_NO_FAIL); | |
879 if (!cwp->dcc_ctxt) { | |
880 /* failed to create context */ | |
881 thr_error_msg(cwp, "%s", cwp->emsg); | |
882 cwp->dcc_ctxt_sn = 0; | |
883 return 0; | |
884 } | |
885 cwp->xhdr_fname_len = get_xhdr_fname(cwp->xhdr_fname, | |
886 sizeof(cwp->xhdr_fname), | |
887 dcc_clnt_info); | |
888 } | |
889 return 1; | |
890 } | |
891 | |
892 | |
893 | |
894 /* find and lock a per-user DCC_WF | |
895 * it is locked by grabbing the mutex for the main whiteclnt file */ | |
896 static CWF * | |
897 find_cwf(RCPT_ST *rcpt_st) | |
898 { | |
899 CWF *cwf; | |
900 DCC_PATH white_nm_buf; | |
901 const char *white_nm_ptr; | |
902 | |
903 if (rcpt_st->dir[0] == '\0') { | |
904 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; | |
905 return 0; | |
906 } | |
907 | |
908 /* canonicalize the key */ | |
909 if (!fnm2rel(white_nm_buf, rcpt_st->dir, "/whiteclnt")) { | |
910 thr_error_msg(rcpt_st->cwp, | |
911 "long user whiteclnt name \"%s/whiteclnt\"", | |
912 rcpt_st->dir); | |
913 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; | |
914 return 0; | |
915 } | |
916 white_nm_ptr = path2fnm(white_nm_buf); | |
917 | |
918 lock_wf(); | |
919 cwf = cur_cwf; | |
920 for (;;) { | |
921 if (!strcmp(white_nm_ptr, cwf->wf.ascii_nm)) | |
922 break; /* found old DCC_WF for target file */ | |
923 | |
924 if (cwf->older == cur_cwf) { | |
925 /* We do not know this file. | |
926 * Recycle the oldest DCC_WF */ | |
927 if (!dcc_new_white_nm(rcpt_st->cwp->emsg, &cwf->wf, | |
928 white_nm_ptr)) { | |
929 thr_error_msg(rcpt_st->cwp, "%s", | |
930 rcpt_st->cwp->emsg); | |
931 unlock_wf(); | |
932 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; | |
933 return 0; | |
934 } | |
935 break; | |
936 } | |
937 | |
938 cwf = cwf->older; | |
939 } | |
940 | |
941 /* move to front */ | |
942 if (cwf != cur_cwf) { | |
943 cwf->older->newer = cwf->newer; | |
944 cwf->newer->older = cwf->older; | |
945 cwf->older = cur_cwf; | |
946 cwf->newer = cur_cwf->newer; | |
947 cwf->newer->older = cwf; | |
948 cwf->older->newer = cwf; | |
949 cur_cwf = cwf; | |
950 } | |
951 | |
952 switch (dcc_rdy_white(rcpt_st->cwp->emsg, &cwf->wf, &cmn_tmp_wf)) { | |
953 case DCC_WHITE_CONTINUE: | |
954 thr_error_msg(rcpt_st->cwp, "%s", rcpt_st->cwp->emsg); | |
955 /* fall through */ | |
956 case DCC_WHITE_OK: | |
957 /* notice if the file contains no checksums or CIDR blocks | |
958 * or flag bits that make the file differ from an empty | |
959 * or non-existent file */ | |
960 if (cwf->wf.wtbl->hdr.entries == 0 | |
961 && cwf->wf.wtbl->hdr.cidr.len == 0 | |
962 && !(cwf->wf.wtbl_flags & DCC_WHITE_FG_TRAPS)) { | |
963 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; | |
964 } else { | |
965 rcpt_st->fgs &= ~RCPT_FG_NULL_WHITECLNT; | |
966 memcpy(rcpt_st->wf_sum, cwf->wf.wtbl->hdr.ck_sum, | |
967 sizeof(rcpt_st->wf_sum)); | |
968 } | |
969 return cwf; | |
970 case DCC_WHITE_NOFILE: | |
971 break; | |
972 case DCC_WHITE_SILENT: | |
973 if (dcc_clnt_debug) | |
974 thr_error_msg(rcpt_st->cwp, "%s", rcpt_st->cwp->emsg); | |
975 break; | |
976 case DCC_WHITE_COMPLAIN: | |
977 thr_error_msg(rcpt_st->cwp, "%s", rcpt_st->cwp->emsg); | |
978 break; | |
979 } | |
980 | |
981 unlock_wf(); | |
982 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; | |
983 return 0; | |
984 } | |
985 | |
986 | |
987 | |
988 /* digest the results of one recipient's whitelist */ | |
989 static void | |
990 white_results(RCPT_ST *rcpt_st, | |
991 CMN_WORK *cwp, | |
992 RCPT_FGS *fgsp, | |
993 DCC_WHITE_RESULT result, | |
994 const DCC_WHITE_LISTING *listingp) | |
995 { | |
996 | |
997 DCC_WHITE_LISTING listing; | |
998 | |
999 /* call-by-reference parameter to resolve order of evaluation | |
1000 * in callers */ | |
1001 listing = *listingp; | |
1002 | |
1003 /* override if the result of the whitelist lookup was bad */ | |
1004 switch (result) { | |
1005 case DCC_WHITE_OK: | |
1006 break; | |
1007 case DCC_WHITE_SILENT: | |
1008 case DCC_WHITE_NOFILE: | |
1009 listing = DCC_WHITE_UNLISTED; | |
1010 break; | |
1011 case DCC_WHITE_COMPLAIN: | |
1012 case DCC_WHITE_CONTINUE: | |
1013 thr_error_msg(cwp, "%s", cwp->emsg); | |
1014 return; | |
1015 } | |
1016 | |
1017 switch (listing) { | |
1018 case DCC_WHITE_USE_DCC: | |
1019 /* "OK2" for the env_to checksum in the local whitelist | |
1020 * does not mean the address is half whitelisted, | |
1021 * but that it is ok to reject or discard spam for it based | |
1022 * on DCC results. | |
1023 * It is the original, deprecated mechanism for turn DCC checks | |
1024 * on and off for individual targets | |
1025 * We get this value only from dcc_white_sum() */ | |
1026 if (rcpt_st) | |
1027 rcpt_st->sws &= ~FLTR_SW_DCC_OFF; | |
1028 /* fall through */ | |
1029 case DCC_WHITE_UNLISTED: | |
1030 /* a spam trap rejects everything | |
1031 * or accepts but marks everything as spam */ | |
1032 if (rcpt_st && (rcpt_st->sws & FLTR_SW_TRAPS)) { | |
1033 if (!(*fgsp & RCPT_FG_WHITE)) | |
1034 *fgsp |= RCPT_FG_BLACK; | |
1035 /* remember this hit for the log */ | |
1036 *fgsp |= RCPT_FG_WLIST_ISSPAM; | |
1037 cwp->rcpt_fgs |= RCPT_FG_WLIST_ISSPAM; | |
1038 } | |
1039 break; | |
1040 | |
1041 case DCC_WHITE_LISTED: | |
1042 *fgsp |= RCPT_FG_WHITE; | |
1043 *fgsp &= ~RCPT_FG_BLACK; | |
1044 /* remember this hit for the log */ | |
1045 *fgsp |= RCPT_FG_WLIST_NOTSPAM; | |
1046 cwp->rcpt_fgs |= RCPT_FG_WLIST_NOTSPAM; | |
1047 break; | |
1048 | |
1049 case DCC_WHITE_BLACK: | |
1050 if (!(*fgsp & RCPT_FG_WHITE)) | |
1051 *fgsp |= RCPT_FG_BLACK; | |
1052 /* remember this hit for the log */ | |
1053 *fgsp |= RCPT_FG_WLIST_ISSPAM; | |
1054 cwp->rcpt_fgs |= RCPT_FG_WLIST_ISSPAM; | |
1055 break; | |
1056 } | |
1057 } | |
1058 | |
1059 | |
1060 | |
1061 static void | |
1062 rcpt_fgs2ask_st(CMN_WORK *cwp, FLTR_SWS sws, RCPT_FGS fgs) | |
1063 { | |
1064 /* We need to know if all targets are whitelisted for the DCC | |
1065 * before we ask the DCC server. Mail sent only to whitelisted | |
1066 * targets should not be reported to the DCC server. | |
1067 * For that we need a count of whitelisted targets */ | |
1068 if (fgs & RCPT_FG_WHITE) { | |
1069 ++cwp->white_tgts; | |
1070 return; | |
1071 } | |
1072 | |
1073 /* it is spam if it is blacklisted by any target */ | |
1074 if (fgs & RCPT_FG_BLACK) { | |
1075 cwp->ask_st |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT); | |
1076 return; | |
1077 } | |
1078 | |
1079 /* If we had a DNS blacklist hit | |
1080 * and if this recipient believes the DNS blacklist, | |
1081 * then it is spam to report to DCC server. | |
1082 * We need to know if it is spam for at least one target before | |
1083 * deciding what to do for each target. */ | |
1084 if (0 != (FLTR_SW_DNSBL_BITS(sws) & ASK_ST_DNSBL_HIT_BITS(cwp->ask_st))) | |
1085 cwp->ask_st |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT); | |
1086 } | |
1087 | |
1088 | |
1089 | |
1090 /* set the global defaults for the switches or options */ | |
1091 static void | |
1092 rcpt_sws_def(CMN_WORK *cwp, u_char locked) | |
1093 { | |
1094 if (!locked) | |
1095 lock_wf(); | |
1096 | |
1097 if (to_white_only) | |
1098 cwp->init_sws |= FLTR_SW_DCC_OFF; | |
1099 cwp->init_sws |= FLTR_SW_NO_DISCARD; | |
1100 cwp->init_sws = wf2sws(cwp->init_sws, &cmn_wf); | |
1101 if (cannot_discard) /* enforce the default if necessary */ | |
1102 cwp->init_sws |= FLTR_SW_NO_DISCARD; | |
1103 cwp->rcpts_sws = cwp->init_sws; | |
1104 | |
1105 if (!locked) | |
1106 unlock_wf(); | |
1107 } | |
1108 | |
1109 | |
1110 | |
1111 /* merge global and per-user thresholds */ | |
1112 static void | |
1113 make_tholds(DCC_CKSUM_THOLDS out, CWF *cwf) | |
1114 { | |
1115 dcc_merge_tholds(out, dcc_tholds_rej, cmn_wf.wtbl); | |
1116 | |
1117 if (cwf && cwf->wf.wtbl) | |
1118 dcc_merge_tholds(out, out, cwf->wf.wtbl); | |
1119 } | |
1120 | |
1121 | |
1122 | |
1123 /* set per-user switches and compute env_to whitelisting | |
1124 * If we return a pointer, then we grabbed and have kept the lock */ | |
1125 static CWF * | |
1126 rcpt_sws_env_to(CMN_WORK *cwp, RCPT_ST *rcpt_st) | |
1127 { | |
1128 CWF *cwf; | |
1129 DCC_WHITE_LISTING listing; | |
1130 | |
1131 /* We are finished after finding the recipient's whitelist if we | |
1132 * have already checked its settings. | |
1133 * | |
1134 * In this case, we have been here before for this recipient | |
1135 * and fetched the global as well as this recipient's switch settings | |
1136 * and action. | |
1137 * | |
1138 * If we found that the file was empty of checksums or non-existent | |
1139 * before, then assume it is still is and so repeat the previous 0 | |
1140 * answer without wasting time looking. This is an extremely | |
1141 * common case that deserves optimizing. */ | |
1142 if (rcpt_st->fgs & RCPT_FG_NULL_WHITECLNT) | |
1143 return 0; | |
1144 | |
1145 /* after mapping the per-recipient whiteclnt file, | |
1146 * do not repeat the work of examining it if we have already done it */ | |
1147 cwf = find_cwf(rcpt_st); | |
1148 if (rcpt_st->sws & FLTR_SW_SET) | |
1149 return cwf; | |
1150 | |
1151 /* The first time for the first recipient, | |
1152 * we must get the global switch settings. | |
1153 * If we have a per-user whitelist, then we have locked cmn_wf | |
1154 * to protect the per-user whitelist data structures */ | |
1155 if (!(cwp->init_sws & FLTR_SW_SET)) | |
1156 rcpt_sws_def(cwp, cwf != 0); | |
1157 | |
1158 /* get flags and filter settings for recipient, | |
1159 * including setting FLTR_SW_SET so that we won't do this again */ | |
1160 if (cwf) | |
1161 rcpt_st->sws = wf2sws(cwp->init_sws, &cwf->wf); | |
1162 else | |
1163 rcpt_st->sws = cwp->init_sws; | |
1164 if (cannot_discard) /* dccifd cannot discard */ | |
1165 rcpt_st->sws |= FLTR_SW_NO_DISCARD; | |
1166 | |
1167 /* if we have a per-user whitelist, then we have already locked cmn_wf | |
1168 * to protect the per-user whitelist data structures */ | |
1169 if (!cwf) | |
1170 lock_wf(); | |
1171 | |
1172 /* check the env_to address in the global whitelist */ | |
1173 dcc_str2ck(rcpt_st->env_to_sum, 0, 0, rcpt_st->env_to); | |
1174 rcpt_st->global_env_to_fgs = 0; | |
1175 white_results(rcpt_st, cwp, &rcpt_st->global_env_to_fgs, | |
1176 dcc_white_sum(cwp->emsg, &cmn_wf, | |
1177 DCC_CK_ENV_TO, rcpt_st->env_to_sum, | |
1178 &rcpt_st->env_to_tgts, &listing), | |
1179 &listing); | |
1180 if (listing != DCC_WHITE_UNLISTED) | |
1181 cwp->cmn_fgs |= CMN_FG_LOG_ENV_TO; | |
1182 | |
1183 /* check the mailbox name (after aliases etc.) in the global whitelist | |
1184 * if we did not just check it as the envelope Rcpt_To value */ | |
1185 if (rcpt_st->user[0] != '\0' | |
1186 && strcmp(rcpt_st->env_to, rcpt_st->user)) { | |
1187 dcc_str2ck(rcpt_st->user_sum, 0, 0, rcpt_st->user); | |
1188 white_results(rcpt_st, cwp, &rcpt_st->global_env_to_fgs, | |
1189 dcc_white_sum(cwp->emsg, &cmn_wf, | |
1190 DCC_CK_ENV_TO, rcpt_st->user_sum, | |
1191 &rcpt_st->user_tgts, &listing), | |
1192 &listing); | |
1193 if (listing != DCC_WHITE_UNLISTED) | |
1194 cwp->cmn_fgs |= CMN_FG_LOG_ENV_TO; | |
1195 } | |
1196 if (!cwf) { | |
1197 unlock_wf(); | |
1198 | |
1199 } else { | |
1200 /* save per-user envelope Rcpt_To value and mailbox name | |
1201 * (after aliases etc.) white- or blacklisting | |
1202 * and the DCC_WHITE_USE_DCC setting */ | |
1203 rcpt_st->env_to_fgs = 0; | |
1204 white_results(rcpt_st, cwp, &rcpt_st->env_to_fgs, | |
1205 dcc_white_sum(cwp->emsg, &cwf->wf, | |
1206 DCC_CK_ENV_TO, rcpt_st->env_to_sum, | |
1207 &rcpt_st->env_to_tgts, &listing), | |
1208 &listing); | |
1209 if (rcpt_st->user[0] != '\0' | |
1210 && strcmp(rcpt_st->env_to, rcpt_st->user)) { | |
1211 white_results(rcpt_st, cwp, &rcpt_st->env_to_fgs, | |
1212 dcc_white_sum(cwp->emsg, &cwf->wf, | |
1213 DCC_CK_ENV_TO, | |
1214 rcpt_st->user_sum, | |
1215 &rcpt_st->user_tgts, | |
1216 &listing), | |
1217 &listing); | |
1218 } | |
1219 } | |
1220 | |
1221 /* remove any reset _ON bits from the consensus */ | |
1222 cwp->rcpts_sws &= (rcpt_st->sws | ~FLTR_SWS_SETTINGS_ON); | |
1223 /* add any set _OFF bits to the consensus */ | |
1224 cwp->rcpts_sws |= (rcpt_st->sws & FLTR_SWS_SETTINGS_OFF); | |
1225 | |
1226 return cwf; | |
1227 } | |
1228 | |
1229 | |
1230 | |
1231 /* see if a recipient's whitelist decision is certain to be the same | |
1232 * as all preceding recipients */ | |
1233 u_char /* 0=must reject this recipient */ | |
1234 cmn_compat_whitelist(CMN_WORK *cwp, | |
1235 RCPT_ST *rcpt_st_new) | |
1236 { | |
1237 RCPT_ST *rcpt_st2; | |
1238 CWF *cwf; | |
1239 FLTR_SWS save_rcpts_sws; | |
1240 DCC_CKSUM_THOLDS tholds_rej; | |
1241 | |
1242 /* everything is compatible if we won't reject | |
1243 * or if we are forced to reject for all if we reject for any */ | |
1244 if ((cwp->action != CMN_REJECT) | |
1245 || cannot_reject | |
1246 || (cwp->cmn_fgs & CMN_FG_FROM_SUBMIT)) | |
1247 return 1; | |
1248 | |
1249 /* postpone poking at whitelists on the first not-rejected recipient */ | |
1250 rcpt_st2 = cwp->rcpt_st_first; | |
1251 for (;;) { | |
1252 /* wait until later if this is the first recipient */ | |
1253 if (rcpt_st2 == rcpt_st_new) | |
1254 return 1; | |
1255 | |
1256 if (!(rcpt_st2->fgs & (RCPT_FG_REJ_FILTER | |
1257 | RCPT_FG_BAD_USERNAME))) | |
1258 break; | |
1259 rcpt_st2 = rcpt_st2->fwd; | |
1260 if (!rcpt_st2) | |
1261 return 1; | |
1262 } | |
1263 | |
1264 /* we are dealing with a second recipient | |
1265 * get the settings that we postponed when we saw the first recipient */ | |
1266 if (!(rcpt_st2->sws & FLTR_SW_SET)) { | |
1267 cwf = rcpt_sws_env_to(cwp, rcpt_st2); | |
1268 make_tholds(cwp->cks.tholds_rej, cwf); | |
1269 cwp->cmn_fgs |= CMN_FG_THOLDS_SET; | |
1270 if (cwf) | |
1271 unlock_wf(); | |
1272 } | |
1273 | |
1274 /* See if this message might be accepted for this recipient | |
1275 * but rejected for some other recipient that does not want | |
1276 * forced discarding or if this recipient does not want forced | |
1277 * discarding and has weaker filtering than other recipients. | |
1278 * If so, reject this recipient. */ | |
1279 | |
1280 save_rcpts_sws = cwp->rcpts_sws; | |
1281 cwf = rcpt_sws_env_to(cwp, rcpt_st_new); | |
1282 make_tholds(tholds_rej, cwf); | |
1283 if (cwf) | |
1284 unlock_wf(); | |
1285 | |
1286 /* Differing DCC thresholds conflict */ | |
1287 if (memcmp(tholds_rej, cwp->cks.tholds_rej, sizeof(tholds_rej))) { | |
1288 cwp->rcpts_sws = save_rcpts_sws; | |
1289 cwp->ask_st |= ASK_ST_LOGIT; | |
1290 rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ; | |
1291 return 0; | |
1292 } | |
1293 | |
1294 /* Accept-traps prefer to avoid all rejections including Mail_From | |
1295 * commands and implicitly discard but tolerate rejections. | |
1296 * Reject-traps require rejections. */ | |
1297 if (rcpt_st_new->sws & FLTR_SW_TRAP_ACC) | |
1298 return 1; | |
1299 | |
1300 /* everything other than differing thesholds is compatible | |
1301 * if discarding is ok for all recipients so far */ | |
1302 if (!(cwp->rcpts_sws & FLTR_SW_NO_DISCARD)) | |
1303 return 1; | |
1304 | |
1305 do { | |
1306 /* ignore already rejected recipients */ | |
1307 if (rcpt_st2->fgs & (RCPT_FG_REJ_FILTER | RCPT_FG_BAD_USERNAME)) | |
1308 continue; | |
1309 | |
1310 /* Traps are fexible. */ | |
1311 if (rcpt_st2->sws & FLTR_SW_TRAP_ACC) | |
1312 continue; | |
1313 | |
1314 /* Differing whitelists make it possible that the message | |
1315 * could need to be rejected for one recipient but accepted | |
1316 * for the other */ | |
1317 if (((rcpt_st2->sws & FLTR_SW_NO_DISCARD) | |
1318 || (rcpt_st_new->sws & FLTR_SW_NO_DISCARD)) | |
1319 && (((rcpt_st2->fgs ^ rcpt_st_new->fgs) | |
1320 & RCPT_FG_NULL_WHITECLNT) != 0 | |
1321 || (!(rcpt_st2->fgs & RCPT_FG_NULL_WHITECLNT) | |
1322 && memcmp(rcpt_st2->wf_sum, rcpt_st_new->wf_sum, | |
1323 sizeof(rcpt_st2->wf_sum))))) { | |
1324 cwp->rcpts_sws = save_rcpts_sws; | |
1325 cwp->ask_st |= ASK_ST_LOGIT; | |
1326 rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ; | |
1327 return 0; | |
1328 } | |
1329 | |
1330 /* Stronger filter choices for a preceding recipient are | |
1331 * potential reasons to reject the message not shared by the | |
1332 * new recipient and that could force the message to be | |
1333 * discarded for the preceding recipient */ | |
1334 if ((rcpt_st2->sws & FLTR_SW_NO_DISCARD) | |
1335 && ((FLTR_SWS_ON(rcpt_st2->sws) | |
1336 & ~FLTR_SWS_ON(rcpt_st_new->sws)) != 0)) { | |
1337 cwp->rcpts_sws = save_rcpts_sws; | |
1338 cwp->ask_st |= ASK_ST_LOGIT; | |
1339 rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ; | |
1340 return 0; | |
1341 } | |
1342 | |
1343 /* weaker filter choices for a preceding recipient imply | |
1344 * potential reasons to reject the message not shared by the | |
1345 * preceding recipient and that could force the message to | |
1346 * be discarded for the new recipient */ | |
1347 if ((rcpt_st_new->sws & FLTR_SW_NO_DISCARD) | |
1348 && ((~FLTR_SWS_ON(rcpt_st2->sws) | |
1349 & FLTR_SWS_ON(rcpt_st_new->sws)) != 0)) { | |
1350 cwp->rcpts_sws = save_rcpts_sws; | |
1351 cwp->ask_st |= ASK_ST_LOGIT; | |
1352 rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ; | |
1353 return 0; | |
1354 } | |
1355 } while ((rcpt_st2 = rcpt_st2->fwd) != rcpt_st_new); | |
1356 | |
1357 return 1; | |
1358 } | |
1359 | |
1360 | |
1361 | |
1362 /* check the whitelists for a single user or target */ | |
1363 static void | |
1364 ask_white_rcpt(CMN_WORK *cwp, | |
1365 RCPT_ST *rcpt_st, | |
1366 RCPT_FGS global_fgs) | |
1367 { | |
1368 DCC_WHITE_LISTING listing; | |
1369 CWF *cwf; | |
1370 DCC_PATH abs_nm; | |
1371 | |
1372 rcpt_st->log_pos_white = log_lseek_get(cwp); | |
1373 | |
1374 /* Quit after capturing the log position if the recipient has | |
1375 * been rejected. | |
1376 * We cannot do more because dccm does not have the mailer and so | |
1377 * cannot find the likely userdirs/local/user/whiteclnt file */ | |
1378 if (rcpt_st->fgs & (RCPT_FG_REJ_FILTER | RCPT_FG_BAD_USERNAME)) | |
1379 return; | |
1380 | |
1381 /* Get the switch settings and the env_to whitelist results. */ | |
1382 cwf = rcpt_sws_env_to(cwp, rcpt_st); | |
1383 if (!(cwp->cmn_fgs & CMN_FG_THOLDS_SET)) { | |
1384 cwp->cmn_fgs |= CMN_FG_THOLDS_SET; | |
1385 make_tholds(cwp->cks.tholds_rej, cwf); | |
1386 } | |
1387 | |
1388 /* Compute white- or blacklisting. | |
1389 * The per-user whiteclnt file overrides the global file. | |
1390 * The whiteclnt files override the MTA with "option MTA-first". | |
1391 * Without, the MTA controls. | |
1392 * Within each category, whitelisting overrides blacklisting. | |
1393 * | |
1394 * The global whitelist's answer for message's checksums other | |
1395 * than the env_to checksums have already been computed in | |
1396 * global_fgs. */ | |
1397 | |
1398 if (global_fgs != 0) { | |
1399 rcpt_st->fgs &= ~(RCPT_FG_WHITE | RCPT_FG_BLACK); | |
1400 rcpt_st->fgs |= global_fgs; | |
1401 } | |
1402 | |
1403 if (rcpt_st->sws & FLTR_SW_MTA_FIRST) { | |
1404 if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) { | |
1405 rcpt_st->fgs |= RCPT_FG_WHITE; | |
1406 rcpt_st->fgs &= ~RCPT_FG_BLACK; | |
1407 } else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) { | |
1408 rcpt_st->fgs |= RCPT_FG_BLACK; | |
1409 rcpt_st->fgs &= ~RCPT_FG_WHITE; | |
1410 } | |
1411 } | |
1412 | |
1413 /* apply the previously computed env_to global whitelist results | |
1414 * as well as the other global whitelist results */ | |
1415 if ((rcpt_st->global_env_to_fgs | global_fgs) & RCPT_FG_WHITE) { | |
1416 rcpt_st->fgs &= ~RCPT_FG_BLACK; | |
1417 rcpt_st->fgs |= RCPT_FG_WHITE; | |
1418 } else if ((rcpt_st->global_env_to_fgs | global_fgs) & RCPT_FG_BLACK) { | |
1419 rcpt_st->fgs &= ~RCPT_FG_WHITE; | |
1420 rcpt_st->fgs |= RCPT_FG_BLACK; | |
1421 } | |
1422 | |
1423 if (!cwf) { | |
1424 /* Without a per-user whitelist or without any entries in | |
1425 * the per-user whitelist, we will be using the | |
1426 * global whitelist for the other messages checksums. | |
1427 * Arrange to include those results in the per-user log file */ | |
1428 memcpy(rcpt_st->wtgts, cwp->wtgts, sizeof(rcpt_st->wtgts)); | |
1429 | |
1430 } else { | |
1431 /* Check other message checksums in per-user whitelist */ | |
1432 white_results(rcpt_st, cwp, &rcpt_st->env_to_fgs, | |
1433 dcc_white_cks(cwp->emsg, &cwf->wf, &cwp->cks, | |
1434 rcpt_st->wtgts, &listing), | |
1435 &listing); | |
1436 | |
1437 if (rcpt_st->env_to_fgs == 0) { | |
1438 /* Without an answer from the per-user whitelist, | |
1439 * we will be using the global whitelist. | |
1440 * So arrange to include those results in the per-user | |
1441 * log file */ | |
1442 memcpy(rcpt_st->wtgts, cwp->wtgts, | |
1443 sizeof(rcpt_st->wtgts)); | |
1444 | |
1445 } else { | |
1446 thr_log_print(cwp, 0, "%s%s\n", | |
1447 fnm2abs_err(abs_nm, cwf->wf.ascii_nm), | |
1448 (rcpt_st->env_to_fgs & RCPT_FG_WHITE) | |
1449 ? DCC_XHDR_ISOK | |
1450 : (rcpt_st->sws & FLTR_SW_TRAP_ACC) | |
1451 ? "-->"DCC_XHDR_TRAP_ACC | |
1452 : (rcpt_st->sws & FLTR_SW_TRAP_REJ) | |
1453 ? "-->"DCC_XHDR_TRAP_REJ | |
1454 : DCC_XHDR_ISSPAM); | |
1455 rcpt_st->fgs &= ~(RCPT_FG_WHITE | RCPT_FG_BLACK); | |
1456 rcpt_st->fgs |= rcpt_st->env_to_fgs; | |
1457 } | |
1458 | |
1459 /* release common lock that protected the per-user whitelist | |
1460 * because we are finished with the per-user whitelist */ | |
1461 unlock_wf(); | |
1462 } | |
1463 | |
1464 if (rcpt_st->env_to_tgts != 0 | |
1465 || rcpt_st->user_tgts != 0) | |
1466 cwp->cmn_fgs |= CMN_FG_LOG_ENV_TO; | |
1467 | |
1468 if (!(rcpt_st->sws & FLTR_SW_MTA_FIRST)) { | |
1469 if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) { | |
1470 rcpt_st->fgs |= RCPT_FG_WHITE; | |
1471 rcpt_st->fgs &= ~RCPT_FG_BLACK; | |
1472 } else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) { | |
1473 rcpt_st->fgs |= RCPT_FG_BLACK; | |
1474 rcpt_st->fgs &= ~RCPT_FG_WHITE; | |
1475 } | |
1476 } | |
1477 } | |
1478 | |
1479 | |
1480 | |
1481 /* check the whitelists for all targets */ | |
1482 void | |
1483 cmn_ask_white(CMN_WORK *cwp) | |
1484 { | |
1485 RCPT_ST *rcpt_st; | |
1486 RCPT_FGS global_fgs; | |
1487 DCC_OPS grey_op; | |
1488 DCC_WHITE_LISTING listing; | |
1489 | |
1490 /* log sendmail access_db spam */ | |
1491 if (cwp->ask_st & ASK_ST_MTA_ISSPAM) | |
1492 cwp->ask_st |= ASK_ST_LOGIT; | |
1493 | |
1494 dcc_dnsbl_result(&cwp->ask_st, cwp->cks.dnsbl); | |
1495 | |
1496 cwp->log_pos_white_first = log_lseek_get(cwp); | |
1497 | |
1498 /* Use the main whitelist only for recipients whose individual | |
1499 * whitelists don't give a black or white answer. | |
1500 * Check the main whitelist first (and so even if not necessary) | |
1501 * so that problems with it are in all of the logs and to simplify | |
1502 * merging the global and per-user whitelist results. */ | |
1503 lock_wf(); | |
1504 global_fgs = 0; | |
1505 white_results(0, cwp, &global_fgs, | |
1506 dcc_white_cks(cwp->emsg, &cmn_wf, &cwp->cks, | |
1507 cwp->wtgts, &listing), | |
1508 &listing); | |
1509 | |
1510 /* get the defaults for the options */ | |
1511 if (!(cwp->init_sws & FLTR_SW_SET)) | |
1512 rcpt_sws_def(cwp, 1); | |
1513 unlock_wf(); | |
1514 | |
1515 /* kludge similar to ask_white_rcpt() for no recipients for dccifd with | |
1516 * the ASCII protocol */ | |
1517 if ((rcpt_st = cwp->rcpt_st_first) == 0) { | |
1518 if (cwp->init_sws & FLTR_SW_MTA_FIRST) { | |
1519 if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) { | |
1520 cwp->rcpt_fgs |= RCPT_FG_WHITE; | |
1521 cwp->rcpt_fgs &= ~RCPT_FG_BLACK; | |
1522 } else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) { | |
1523 cwp->rcpt_fgs |= RCPT_FG_BLACK; | |
1524 cwp->rcpt_fgs &= ~RCPT_FG_WHITE; | |
1525 } | |
1526 } | |
1527 if (global_fgs != 0) { | |
1528 cwp->rcpt_fgs &= ~(RCPT_FG_WHITE | RCPT_FG_BLACK); | |
1529 cwp->rcpt_fgs |= global_fgs; | |
1530 } | |
1531 if (!(cwp->init_sws & FLTR_SW_MTA_FIRST)) { | |
1532 if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) { | |
1533 cwp->rcpt_fgs |= RCPT_FG_WHITE; | |
1534 cwp->rcpt_fgs &= ~RCPT_FG_BLACK; | |
1535 } else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) { | |
1536 cwp->rcpt_fgs |= RCPT_FG_BLACK; | |
1537 cwp->rcpt_fgs &= ~RCPT_FG_WHITE; | |
1538 } | |
1539 } | |
1540 | |
1541 rcpt_fgs2ask_st(cwp, cwp->init_sws, cwp->rcpt_fgs); | |
1542 } | |
1543 | |
1544 for (; rcpt_st; rcpt_st = rcpt_st->fwd) { | |
1545 /* maybe this recipient is whitelisted or a spam trap | |
1546 * or has per-user option settings */ | |
1547 ask_white_rcpt(cwp, rcpt_st, global_fgs); | |
1548 | |
1549 rcpt_fgs2ask_st(cwp, rcpt_st->sws, rcpt_st->fgs); | |
1550 | |
1551 /* no greylist check if it is off or should not be done */ | |
1552 if ((rcpt_st->sws & (FLTR_SW_GREY_OFF | FLTR_SW_TRAPS)) | |
1553 || (cwp->cmn_fgs & (CMN_FG_FROM_MX | CMN_FG_FROM_SUBMIT)) | |
1554 || (rcpt_st->fgs & (RCPT_FG_REJ_FILTER | |
1555 | RCPT_FG_BAD_USERNAME)) | |
1556 || (cwp->ask_st & ASK_ST_INVALID_MSG)) | |
1557 continue; | |
1558 | |
1559 if (rcpt_st->fgs & RCPT_FG_BLACK) { | |
1560 if (cwp->cks.sums[DCC_CK_IP].type != DCC_CK_IP | |
1561 || (cwp->cks.sums[DCC_CK_ENV_FROM].type | |
1562 != DCC_CK_ENV_FROM)) | |
1563 continue; | |
1564 grey_op = DCC_OP_GREY_QUERY; | |
1565 } else if (cwp->ask_st & ASK_ST_QUERY_GREY) { | |
1566 grey_op = DCC_OP_GREY_QUERY; | |
1567 } else if (rcpt_st->fgs & RCPT_FG_WHITE) { | |
1568 grey_op = DCC_OP_GREY_WHITE; | |
1569 } else { | |
1570 grey_op = DCC_OP_GREY_REPORT; | |
1571 } | |
1572 rcpt_st->grey_result = ask_grey(cwp->emsg, cwp->dcc_ctxt, | |
1573 grey_op, | |
1574 rcpt_st->msg_sum, | |
1575 rcpt_st->triple_sum, | |
1576 &cwp->cks, | |
1577 rcpt_st->env_to_sum, | |
1578 &rcpt_st->embargo_num, | |
1579 &cwp->early_grey_tgts, | |
1580 &cwp->late_grey_tgts); | |
1581 | |
1582 switch (rcpt_st->grey_result) { | |
1583 case ASK_GREY_OFF: | |
1584 case ASK_GREY_SPAM: | |
1585 dcc_logbad(EX_SOFTWARE, | |
1586 "cmn_ask_white grey_result=%d", | |
1587 rcpt_st->grey_result); | |
1588 break; | |
1589 case ASK_GREY_FAIL: | |
1590 thr_error_msg(cwp, "%s", cwp->emsg); | |
1591 /* If we are trying hard, assume the | |
1592 * message would have been embargoed */ | |
1593 if (try_extra_hard) | |
1594 cwp->ask_st |= (ASK_ST_GREY_EMBARGO | |
1595 | ASK_ST_GREY_LOGIT | |
1596 | ASK_ST_LOGIT); | |
1597 break; | |
1598 case ASK_GREY_EMBARGO: | |
1599 if (rcpt_st->embargo_num == 1 | |
1600 && (rcpt_st->fgs & RCPT_FG_BLACK)) { | |
1601 /* don't bother revoking non-existent entry */ | |
1602 rcpt_st->grey_result = ASK_GREY_OFF; | |
1603 } else { | |
1604 cwp->ask_st |= (ASK_ST_GREY_EMBARGO | |
1605 | ASK_ST_GREY_LOGIT | |
1606 | ASK_ST_LOGIT); | |
1607 if (cwp->max_embargo_num < rcpt_st->embargo_num) | |
1608 cwp->max_embargo_num = rcpt_st->embargo_num; | |
1609 } | |
1610 break; | |
1611 case ASK_GREY_EMBARGO_END: | |
1612 cwp->ask_st |= (ASK_ST_GREY_LOGIT | ASK_ST_LOGIT); | |
1613 cwp->rcpt_fgs |= RCPT_FG_GREY_END; | |
1614 break; | |
1615 case ASK_GREY_PASS: | |
1616 break; | |
1617 case ASK_GREY_WHITE: | |
1618 rcpt_st->fgs |= RCPT_FG_GREY_WHITE; | |
1619 break; | |
1620 } | |
1621 } | |
1622 | |
1623 cwp->log_pos_white_last = log_lseek_get(cwp); | |
1624 cwp->log_pos_ask_error = cwp->log_pos_white_last; | |
1625 } | |
1626 | |
1627 | |
1628 | |
1629 /* ask a DCC server */ | |
1630 int /* <0=big problem, 0=retryable, 1=ok */ | |
1631 cmn_ask_dcc(CMN_WORK *cwp) | |
1632 { | |
1633 int i; | |
1634 | |
1635 /* Talk to the DCC server and make the X-DCC header. | |
1636 * If we have blacklist entries for it, then we'll tell the DCC | |
1637 * server it is spam and say so in the X-DCC header. | |
1638 * Note that a target count of 0 is a query. */ | |
1639 if (cwp->ask_st & ASK_ST_QUERY) { | |
1640 cwp->cmn_fgs &= ~CMN_FG_LOCAL_SPAM; | |
1641 cwp->local_tgts = 0; | |
1642 } else if (cwp->ask_st & ASK_ST_CLNT_ISSPAM) { | |
1643 cwp->cmn_fgs |= CMN_FG_LOCAL_SPAM; | |
1644 cwp->local_tgts = cwp->tgts + cwp->mta_rej_tgts; | |
1645 } else if (cwp->ask_st & ASK_ST_GREY_EMBARGO) { | |
1646 /* if the message is under a greylist embargo, | |
1647 * then report to the DCC only the targets for | |
1648 * which it is an initial transmission or embargo #1 */ | |
1649 cwp->cmn_fgs &= ~CMN_FG_LOCAL_SPAM; | |
1650 cwp->local_tgts = cwp->early_grey_tgts + cwp->mta_rej_tgts; | |
1651 } else { | |
1652 /* if this is the end of a greylist embargo | |
1653 * then do not tell the DCC about targets that were | |
1654 * counted with previous transmissions. Those targets | |
1655 * are counted in late_grey_tgts this time, but were | |
1656 * counted in early_grey_tgts for previous transmissions */ | |
1657 cwp->cmn_fgs &= ~CMN_FG_LOCAL_SPAM; | |
1658 cwp->local_tgts = (cwp->tgts - cwp->late_grey_tgts | |
1659 + cwp->mta_rej_tgts); | |
1660 } | |
1661 | |
1662 /* talk to the DCC server */ | |
1663 i = ask_dcc(cwp->emsg, cwp->dcc_ctxt, try_extra_hard, | |
1664 &cwp->header, &cwp->cks, &cwp->ask_st, | |
1665 (cwp->cmn_fgs & CMN_FG_LOCAL_SPAM) != 0, | |
1666 cwp->local_tgts); | |
1667 if (i <= 0) { | |
1668 cwp->log_pos_ask_error += thr_error_msg(cwp, "%s", cwp->emsg); | |
1669 return i; | |
1670 } | |
1671 | |
1672 /* if we are talking to a new server, | |
1673 * remember to fix the X-DCC headers of the other contexts */ | |
1674 if (cwp->xhdr_fname_len != cwp->header.start_len | |
1675 || strncmp(cwp->header.buf, cwp->xhdr_fname, cwp->xhdr_fname_len)) { | |
1676 if (dcc_clnt_debug > 1) | |
1677 thr_trace_msg(cwp, DCC_XHDR_START | |
1678 "header changed from %s to %.*s", | |
1679 cwp->xhdr_fname, | |
1680 (int)cwp->header.start_len, | |
1681 cwp->header.buf); | |
1682 cwp->xhdr_fname_len = get_xhdr_fname(cwp->xhdr_fname, | |
1683 sizeof(cwp->xhdr_fname), | |
1684 dcc_clnt_info); | |
1685 if (++dcc_ctxt_sn == 0) | |
1686 dcc_ctxt_sn = 1; | |
1687 cwp->dcc_ctxt_sn = dcc_ctxt_sn; | |
1688 } | |
1689 | |
1690 return 1; | |
1691 } | |
1692 | |
1693 | |
1694 #define USER_LOG_CAPTION(rcpt_st, s) user_log_write((rcpt_st), (s), LITZ(s)) | |
1695 #define USER_LOG_EOL(rcpt_st) USER_LOG_CAPTION((rcpt_st), "\n") | |
1696 | |
1697 static u_char | |
1698 user_log_write(RCPT_ST *rcpt_st, const void *buf, u_int len) | |
1699 { | |
1700 DCC_PATH abs_nm; | |
1701 int result; | |
1702 | |
1703 if (user_log_fd < 0) | |
1704 return 0; | |
1705 | |
1706 if (!len) | |
1707 len = strlen(buf); | |
1708 result = write(user_log_fd, buf, len); | |
1709 if (result == (int)len) | |
1710 return 1; | |
1711 | |
1712 if (result < 0) | |
1713 thr_error_msg(rcpt_st->cwp, "write(%s): %s", | |
1714 fnm2abs_err(abs_nm, rcpt_st->user_log_nm), | |
1715 ERROR_STR()); | |
1716 else | |
1717 thr_error_msg(rcpt_st->cwp, | |
1718 "write(%s)=%d instead of %d", | |
1719 fnm2abs_err(abs_nm, rcpt_st->user_log_nm), | |
1720 result, (int)len); | |
1721 dcc_log_close(0, rcpt_st->user_log_nm, user_log_fd, | |
1722 &rcpt_st->cwp->ldate); | |
1723 user_log_fd = -1; | |
1724 return 0; | |
1725 } | |
1726 | |
1727 | |
1728 | |
1729 static void PATTRIB(2,3) | |
1730 user_log_print(RCPT_ST *rcpt_st, const char *p, ...) | |
1731 { | |
1732 char logbuf[LOGBUF_SIZE*2]; | |
1733 int i; | |
1734 va_list args; | |
1735 | |
1736 if (user_log_fd < 0) | |
1737 return; | |
1738 | |
1739 va_start(args, p); | |
1740 i = vsnprintf(logbuf, sizeof(logbuf), p, args); | |
1741 va_end(args); | |
1742 if (i < ISZ(logbuf)) { | |
1743 user_log_write(rcpt_st, logbuf, i); | |
1744 return; | |
1745 } | |
1746 user_log_write(rcpt_st, logbuf, sizeof(logbuf)); | |
1747 user_log_write(rcpt_st, "...", 3); | |
1748 } | |
1749 | |
1750 | |
1751 | |
1752 static void | |
1753 user_log_block(RCPT_ST *rcpt_st, /* copy from main log file to this */ | |
1754 off_t start, /* starting here */ | |
1755 off_t stop) /* and ending here */ | |
1756 { | |
1757 CMN_WORK *cwp; | |
1758 char buf[4096]; | |
1759 int len; | |
1760 int result; | |
1761 | |
1762 if (user_log_fd < 0) | |
1763 return; | |
1764 | |
1765 if (start == stop) | |
1766 return; | |
1767 | |
1768 cwp = rcpt_st->cwp; | |
1769 | |
1770 if (start == -1 || stop == -1 || start > stop) { | |
1771 thr_error_msg(cwp, "bogus user_log_block position %d to %d", | |
1772 (int)start, (int)stop); | |
1773 return; | |
1774 } | |
1775 | |
1776 if (!log_lseek_set(cwp, start)) | |
1777 return; | |
1778 | |
1779 while ((len = stop - start) != 0) { | |
1780 if (len > ISZ(buf)) | |
1781 len = ISZ(buf); | |
1782 result = read(log_fd2, buf, len); | |
1783 if (result != len) { | |
1784 if (result < 0) | |
1785 thr_error_msg(cwp, | |
1786 "user_log_block read(%s,%d): %s", | |
1787 cwp->log_nm, len, ERROR_STR()); | |
1788 else | |
1789 thr_error_msg(cwp, "user_log_block" | |
1790 " read(%s,%d)=%d", | |
1791 cwp->log_nm, len, result); | |
1792 log_fd2_close(-2); | |
1793 return; | |
1794 } | |
1795 if (!user_log_write(rcpt_st, buf, len)) | |
1796 return; | |
1797 start += len; | |
1798 } | |
1799 } | |
1800 | |
1801 | |
1802 /* print | |
1803 * env_To: user@example.com ok | |
1804 * user@example.com: f7a48ff4 70d29d39 4ed1e36f 104e4fa0 | |
1805 * 86e0517b b455b130 4cfccc8c f1a1ff37 Pass | |
1806 */ | |
1807 static void | |
1808 print_addr_sum(LOG_WRITE_FNC out, void *arg, | |
1809 const char *addr, | |
1810 int addr_len, /* trim trailing '>' from grey addr */ | |
1811 const char *sum, | |
1812 int sum_len, /* trim trailing '>' from env_To */ | |
1813 const char *tgts, | |
1814 int tgts_width) /* to right-justify env_To tgts */ | |
1815 { | |
1816 char buf[100]; | |
1817 int i; | |
1818 | |
1819 i = snprintf(buf, sizeof(buf), PRINT_CK_PAT_LIM_CK" %*s\n", | |
1820 addr_len, addr, | |
1821 addr_len > 0 ? ':' : ' ', | |
1822 sum_len, sum, | |
1823 tgts_width, tgts); | |
1824 if (i >= ISZ(buf)) { | |
1825 i = sizeof(buf); | |
1826 buf[i-1] = '\n'; | |
1827 } | |
1828 out(arg, buf, i); | |
1829 } | |
1830 | |
1831 | |
1832 | |
1833 static void | |
1834 print_env_to(LOG_WRITE_FNC out, void *arg, const RCPT_ST *rcpt_st) | |
1835 { | |
1836 const char *addr; | |
1837 int addr_len; | |
1838 char tgts_buf[16]; | |
1839 | |
1840 if (rcpt_st->env_to_tgts != 0) { | |
1841 addr = rcpt_st->env_to; | |
1842 addr_len = strlen(addr); | |
1843 if (addr_len > 1 && addr[0] == '<' && addr[addr_len-1] == '>') { | |
1844 ++addr; | |
1845 addr_len -= 2; | |
1846 } | |
1847 print_addr_sum(out, arg, | |
1848 DCC_XHDR_TYPE_ENV_TO, LITZ(DCC_XHDR_TYPE_ENV_TO), | |
1849 addr, addr_len, | |
1850 dcc_tgts2str(tgts_buf, sizeof(tgts_buf), | |
1851 rcpt_st->env_to_tgts, 0), | |
1852 PRINT_CK_PAT_SRVR_LEN+PRINT_CK_PAT_WLIST_LEN); | |
1853 } | |
1854 | |
1855 if (rcpt_st->user_tgts != 0) { | |
1856 addr = rcpt_st->user; | |
1857 addr_len = strlen(addr); | |
1858 if (addr_len > 1 && addr[0] == '<' && addr[addr_len-1] == '>') { | |
1859 ++addr; | |
1860 addr_len -= 2; | |
1861 } | |
1862 print_addr_sum(out, arg, | |
1863 DCC_XHDR_TYPE_ENV_TO, LITZ(DCC_XHDR_TYPE_ENV_TO), | |
1864 addr, addr_len, | |
1865 dcc_tgts2str(tgts_buf, sizeof(tgts_buf), | |
1866 rcpt_st->user_tgts, 0), | |
1867 PRINT_CK_PAT_SRVR_LEN+PRINT_CK_PAT_WLIST_LEN); | |
1868 } | |
1869 } | |
1870 | |
1871 | |
1872 | |
1873 static void | |
1874 print_grey(LOG_WRITE_FNC out, void *arg, | |
1875 const RCPT_ST *rcpt_st, u_char *headed) | |
1876 { | |
1877 #define CK_HEADING " "DCC_XHDR_GREY_RECIP"\n" | |
1878 #define CK_HEADING_QUERY " "DCC_XHDR_GREY_RECIP" query\n" | |
1879 char cbuf[DCC_CK2STR_LEN]; | |
1880 char embargo_buf[20]; | |
1881 const char *addr; | |
1882 int addr_len; | |
1883 const char *embargo; | |
1884 | |
1885 embargo = 0; | |
1886 switch (rcpt_st->grey_result) { | |
1887 case ASK_GREY_FAIL: | |
1888 embargo = DCC_XHDR_EMBARGO_FAIL; | |
1889 break; | |
1890 case ASK_GREY_OFF: | |
1891 return; | |
1892 case ASK_GREY_EMBARGO: | |
1893 if (rcpt_st->embargo_num > 0) { | |
1894 snprintf(embargo_buf, sizeof(embargo_buf), | |
1895 DCC_XHDR_EMBARGO_NUM, rcpt_st->embargo_num); | |
1896 embargo = embargo_buf; | |
1897 } else { | |
1898 embargo = DCC_XHDR_EMBARGO; | |
1899 } | |
1900 break; | |
1901 case ASK_GREY_EMBARGO_END: | |
1902 embargo = DCC_XHDR_EMBARGO_ENDED; | |
1903 break; | |
1904 case ASK_GREY_PASS: | |
1905 embargo = DCC_XHDR_EMBARGO_PASS; | |
1906 break; | |
1907 case ASK_GREY_WHITE: | |
1908 embargo = DCC_XHDR_EMBARGO_OK; | |
1909 break; | |
1910 case ASK_GREY_SPAM: | |
1911 snprintf(embargo_buf, sizeof(embargo_buf), | |
1912 DCC_XHDR_EMBARGO_RESET, rcpt_st->embargo_num); | |
1913 embargo = embargo_buf; | |
1914 break; | |
1915 } | |
1916 | |
1917 if (!headed || !*headed) { | |
1918 if (headed) | |
1919 *headed = 1; | |
1920 if (rcpt_st->cwp->ask_st & ASK_ST_QUERY) | |
1921 out(arg, CK_HEADING_QUERY, LITZ(CK_HEADING_QUERY)); | |
1922 else | |
1923 out(arg, CK_HEADING, LITZ(CK_HEADING)); | |
1924 } | |
1925 | |
1926 addr = rcpt_st->env_to; | |
1927 addr_len = strlen(addr); | |
1928 if (addr_len > 1 && addr[0] == '<' && addr[addr_len-1] == '>') { | |
1929 ++addr; | |
1930 addr_len -= 2; | |
1931 } | |
1932 print_addr_sum(out, arg, addr, addr_len, | |
1933 dcc_ck2str(cbuf, sizeof(cbuf), | |
1934 DCC_CK_GREY_MSG, rcpt_st->msg_sum, 0), | |
1935 PRINT_CK_SUM_LEN, | |
1936 "", 0); | |
1937 print_addr_sum(out, arg, "", 0, | |
1938 dcc_ck2str(cbuf, sizeof(cbuf), | |
1939 DCC_CK_GREY3, rcpt_st->triple_sum, 0), | |
1940 PRINT_CK_SUM_LEN, | |
1941 embargo, 0); | |
1942 | |
1943 if (!headed) | |
1944 out(arg, "\n", 1); | |
1945 | |
1946 #undef CK_HEADING | |
1947 } | |
1948 | |
1949 | |
1950 | |
1951 /* log external header, X-DCC header, and DCC results */ | |
1952 static void | |
1953 log_isspam(LOG_WRITE_FNC fnc, void *cp, CMN_WORK *cwp, | |
1954 u_char log_type, /* 0="" 1="per-user" 2="global" */ | |
1955 FLTR_SWS sws, RCPT_FGS rcpt_fgs) | |
1956 { | |
1957 ASK_ST ask_st; | |
1958 | |
1959 ask_st = cwp->ask_st; | |
1960 if (rcpt_fgs & RCPT_FG_WLIST_NOTSPAM) | |
1961 ask_st |= ASK_ST_WLIST_NOTSPAM; | |
1962 if (rcpt_fgs & RCPT_FG_WLIST_ISSPAM) | |
1963 ask_st |= ASK_ST_WLIST_ISSPAM; | |
1964 log_ask_st(fnc, cp, ask_st, sws, log_type, &cwp->header); | |
1965 } | |
1966 | |
1967 | |
1968 | |
1969 static void | |
1970 user_log_msg(CMN_WORK *cwp, RCPT_ST *rcpt_st) | |
1971 { | |
1972 DCC_PATH rcpt_logdir; | |
1973 const char *old_log; | |
1974 | |
1975 /* since the user wants a log file, make one for the system */ | |
1976 cwp->ask_st |= ASK_ST_LOGIT; | |
1977 | |
1978 /* we need the main log file to create per-user log files */ | |
1979 if (rcpt_st->dir[0] == '\0' | |
1980 || dcc_main_logdir[0] == '\0') | |
1981 return; | |
1982 | |
1983 if (!log2_start(cwp)) | |
1984 return; | |
1985 | |
1986 /* create the user's log file */ | |
1987 snprintf(rcpt_logdir, sizeof(rcpt_logdir), "%s/log", rcpt_st->dir); | |
1988 /* try to use the same name as the main log file */ | |
1989 old_log = rindex(cwp->log_nm, '.'); | |
1990 if (old_log) { | |
1991 if (strlen(++old_log) != DCC_MKSTEMP_LEN) | |
1992 old_log = 0; | |
1993 } | |
1994 user_log_fd = dcc_log_open(cwp->emsg, rcpt_st->user_log_nm, | |
1995 cwp->id, sizeof(cwp->id), old_log, | |
1996 rcpt_logdir, DCC_FIN_LOG_PREFIX, | |
1997 (rcpt_st->sws & FLTR_SW_LOG_M) | |
1998 ? LOG_MODE_MINUTE | |
1999 : (rcpt_st->sws & FLTR_SW_LOG_H) | |
2000 ? LOG_MODE_HOUR | |
2001 : (rcpt_st->sws & FLTR_SW_LOG_D) | |
2002 ? LOG_MODE_DAY | |
2003 : LOG_MODE_FLAT); | |
2004 if (user_log_fd < 0) { | |
2005 if (user_log_fd == -1) | |
2006 thr_error_msg(cwp, "%s", cwp->emsg); | |
2007 return; | |
2008 } | |
2009 | |
2010 user_log_block(rcpt_st, /* copy envelope before env_To line */ | |
2011 0, cwp->log_pos_to_first); | |
2012 user_log_block(rcpt_st, /* copy this env_To line */ | |
2013 rcpt_st->log_pos_to, | |
2014 rcpt_st->fwd | |
2015 ? rcpt_st->fwd->log_pos_to | |
2016 : cwp->log_pos_to_end); | |
2017 user_log_block(rcpt_st, /* copy the body of the message */ | |
2018 cwp->log_pos_to_end, | |
2019 cwp->log_pos_white_first); | |
2020 user_log_block(rcpt_st, /* copy whitelist error messages */ | |
2021 rcpt_st->log_pos_white, | |
2022 rcpt_st->fwd | |
2023 ? rcpt_st->fwd->log_pos_white | |
2024 : cwp->log_pos_white_last); | |
2025 user_log_block(rcpt_st, /* copy DCC error messages */ | |
2026 cwp->log_pos_white_last, | |
2027 cwp->log_pos_ask_error); | |
2028 | |
2029 /* log external header, X-DCC header, and DCC results */ | |
2030 log_isspam((LOG_WRITE_FNC)user_log_write, rcpt_st, cwp, 1, | |
2031 rcpt_st->sws, rcpt_st->fgs); | |
2032 | |
2033 /* log the checksums and their counts */ | |
2034 dcc_print_cks((LOG_WRITE_FNC)user_log_write, rcpt_st, | |
2035 cwp->cmn_fgs & CMN_FG_LOCAL_SPAM, cwp->local_tgts, | |
2036 &cwp->cks, rcpt_st->wtgts, | |
2037 (cwp->cmn_fgs & CMN_FG_LOG_ENV_TO) != 0); | |
2038 print_env_to((LOG_WRITE_FNC)user_log_write, rcpt_st, rcpt_st); | |
2039 USER_LOG_EOL(rcpt_st); | |
2040 print_grey((LOG_WRITE_FNC)user_log_write, rcpt_st, rcpt_st, 0); | |
2041 } | |
2042 | |
2043 | |
2044 | |
2045 void | |
2046 log_smtp_reply(CMN_WORK *cwp) | |
2047 { | |
2048 thr_log_print(cwp, 1, DCC_XHDR_REJ_DATA_MSG); | |
2049 log_write(cwp, cwp->reply.rcode, 0); | |
2050 LOG_CMN_CAPTION(cwp, " "); | |
2051 log_write(cwp, cwp->reply.xcode, 0); | |
2052 LOG_CMN_CAPTION(cwp, " "); | |
2053 log_write(cwp, cwp->reply.str, 0); | |
2054 LOG_CMN_EOL(cwp); | |
2055 } | |
2056 | |
2057 | |
2058 | |
2059 static void | |
2060 user_log_smtp_reply(CMN_WORK *cwp, RCPT_ST *rcpt_st) | |
2061 { | |
2062 user_log_print(rcpt_st, DCC_XHDR_REJ_DATA_MSG"%s %s %s\n", | |
2063 cwp->reply.rcode, cwp->reply.xcode, cwp->reply.str); | |
2064 } | |
2065 | |
2066 | |
2067 | |
2068 /* tell the grey list to restore the embargo on a triple */ | |
2069 static void | |
2070 grey_spam(CMN_WORK *cwp, RCPT_ST *rcpt_st) | |
2071 { | |
2072 DCC_GREY_SPAM gs; | |
2073 DCC_OP_RESP resp; | |
2074 | |
2075 switch (rcpt_st->grey_result) { | |
2076 case ASK_GREY_FAIL: | |
2077 case ASK_GREY_OFF: | |
2078 case ASK_GREY_WHITE: | |
2079 return; | |
2080 case ASK_GREY_EMBARGO: | |
2081 if (rcpt_st->embargo_num == 0) { | |
2082 rcpt_st->grey_result = ASK_GREY_OFF; | |
2083 return; | |
2084 } | |
2085 break; | |
2086 case ASK_GREY_EMBARGO_END: | |
2087 case ASK_GREY_PASS: | |
2088 break; | |
2089 case ASK_GREY_SPAM: | |
2090 dcc_logbad(EX_SOFTWARE, "cmn_ask_white grey_result=%d", | |
2091 rcpt_st->grey_result); | |
2092 return; | |
2093 } | |
2094 | |
2095 memset(&gs, 0, sizeof(gs)); | |
2096 | |
2097 if (cwp->cks.sums[DCC_CK_IP].type != DCC_CK_IP) { | |
2098 thr_error_msg(cwp, "missing IP checksum for dcc_grey_spam()"); | |
2099 return; | |
2100 } | |
2101 | |
2102 gs.ip.type = DCC_CK_IP; | |
2103 gs.ip.len = sizeof(gs.ip); | |
2104 memcpy(&gs.ip.sum, &cwp->cks.sums[DCC_CK_IP].sum, sizeof(DCC_SUM)); | |
2105 | |
2106 gs.triple.type = DCC_CK_GREY3; | |
2107 gs.triple.len = sizeof(gs.triple); | |
2108 memcpy(&gs.triple.sum, rcpt_st->triple_sum, sizeof(DCC_SUM)); | |
2109 | |
2110 gs.msg.type = DCC_CK_GREY_MSG; | |
2111 gs.msg.len = sizeof(gs.msg); | |
2112 memcpy(&gs.msg.sum, rcpt_st->msg_sum, sizeof(DCC_SUM)); | |
2113 | |
2114 if (!dcc_clnt_op(cwp->emsg, cwp->dcc_ctxt, DCC_CLNT_FG_GREY, | |
2115 0, 0, 0, &gs.hdr, sizeof(gs), | |
2116 DCC_OP_GREY_SPAM, &resp, sizeof(resp))) { | |
2117 thr_error_msg(cwp, "%s", cwp->emsg); | |
2118 } | |
2119 | |
2120 rcpt_st->grey_result = ASK_GREY_SPAM; | |
2121 } | |
2122 | |
2123 | |
2124 | |
2125 /* process the message for each user to decide what to do with it */ | |
2126 void | |
2127 users_process(CMN_WORK *cwp) | |
2128 { | |
2129 RCPT_ST *rcpt_st; | |
2130 u_char need_eol; | |
2131 int trap_acc_tgts; | |
2132 DNSBL_GBITS dnsbl_sws, dnsbl_hits, common_dnsbl_hits; | |
2133 int dnsbl_delay_tgts; | |
2134 u_char dnsbl_timeo; | |
2135 const DNSBL_GROUP *blg; | |
2136 const REPLY_TPLT *reply; | |
2137 int i; | |
2138 | |
2139 /* log the DCC results and headers in the common log file */ | |
2140 log_isspam((LOG_WRITE_FNC)log_write, cwp, cwp, 2, | |
2141 cwp->rcpts_sws, cwp->rcpt_fgs); | |
2142 | |
2143 /* log the checksums, DCC server counts and global whitelist values */ | |
2144 dcc_print_cks((LOG_WRITE_FNC)log_write, cwp, | |
2145 cwp->cmn_fgs & CMN_FG_LOCAL_SPAM, cwp->local_tgts, | |
2146 &cwp->cks, cwp->wtgts, | |
2147 (cwp->cmn_fgs & CMN_FG_LOG_ENV_TO) != 0); | |
2148 if (cwp->cmn_fgs & CMN_FG_LOG_ENV_TO) { | |
2149 for (rcpt_st = cwp->rcpt_st_first; | |
2150 rcpt_st; | |
2151 rcpt_st = rcpt_st->fwd) { | |
2152 print_env_to((LOG_WRITE_FNC)log_write, cwp, rcpt_st); | |
2153 } | |
2154 } | |
2155 LOG_CMN_EOL(cwp); | |
2156 | |
2157 /* mark recipients who won't receive it */ | |
2158 need_eol = 0; | |
2159 trap_acc_tgts = 0; | |
2160 common_dnsbl_hits = -1; | |
2161 dnsbl_delay_tgts = 0; | |
2162 for (rcpt_st = cwp->rcpt_st_first; | |
2163 rcpt_st; | |
2164 rcpt_st = rcpt_st->fwd) { | |
2165 /* Ignore recipients whose RCPT_TO commands were rejected */ | |
2166 if (rcpt_st->fgs & RCPT_FG_REJ_FILTER) | |
2167 continue; | |
2168 | |
2169 /* We cannot decide whether the message might be spam | |
2170 * for targets rejected by the MTA because dccm does not | |
2171 * have the mailer and so cannot find the likely | |
2172 * userdirs/local/user/whiteclnt file */ | |
2173 if (rcpt_st->fgs & RCPT_FG_BAD_USERNAME) | |
2174 continue; | |
2175 | |
2176 if (rcpt_st->fgs & RCPT_FG_WHITE) { | |
2177 common_dnsbl_hits = 0; | |
2178 dnsbl_timeo = 0; | |
2179 } else { | |
2180 /* decide whether it is spam for this target */ | |
2181 if ((rcpt_st->fgs & RCPT_FG_BLACK) | |
2182 || ((cwp->ask_st & ASK_ST_SRVR_ISSPAM) | |
2183 && !(rcpt_st->sws & FLTR_SW_DCC_OFF))) | |
2184 rcpt_st->fgs |= RCPT_FG_ISSPAM; | |
2185 | |
2186 dnsbl_sws = FLTR_SW_DNSBL_BITS(rcpt_st->sws); | |
2187 dnsbl_hits = (dnsbl_sws | |
2188 & ASK_ST_DNSBL_HIT_BITS(cwp->ask_st)); | |
2189 common_dnsbl_hits &= dnsbl_hits; | |
2190 if (dnsbl_hits != 0) { | |
2191 dnsbl_timeo = 0; | |
2192 rcpt_st->fgs |= RCPT_FG_ISSPAM; | |
2193 } else if (0 != (ASK_ST_DNSBL_TFAIL_BITS(cwp->ask_st) | |
2194 & dnsbl_sws)) { | |
2195 dnsbl_timeo = 1; | |
2196 } else { | |
2197 dnsbl_timeo = 0; | |
2198 } | |
2199 } | |
2200 | |
2201 if (rcpt_st->fgs & RCPT_FG_ISSPAM) { | |
2202 if (rcpt_st->sws & FLTR_SW_TRAP_ACC) | |
2203 ++trap_acc_tgts; | |
2204 else | |
2205 ++cwp->reject_tgts; | |
2206 } else if (dnsbl_timeo) { | |
2207 ++dnsbl_delay_tgts; | |
2208 } else { | |
2209 ++cwp->deliver_tgts; | |
2210 } | |
2211 | |
2212 /* Tell greylist to restore the embargo for targets that believe | |
2213 * the message was spam and did not white- or blacklist it */ | |
2214 if ((rcpt_st->fgs & RCPT_FG_ISSPAM) | |
2215 && !(rcpt_st->sws & FLTR_SW_TRAPS)) | |
2216 grey_spam(cwp, rcpt_st); | |
2217 | |
2218 print_grey((LOG_WRITE_FNC)log_write, cwp, rcpt_st, &need_eol); | |
2219 } | |
2220 if (need_eol) | |
2221 LOG_CMN_EOL(cwp); | |
2222 | |
2223 /* If real targets or rejection-traps need to reject the message, | |
2224 * then treat any accept-traps as reject-traps to avoid telling the | |
2225 * SMTP client that the message was accepted. */ | |
2226 if (cwp->reject_tgts != 0 && cwp->deliver_tgts == 0) { | |
2227 cwp->reject_tgts += trap_acc_tgts; | |
2228 } else { | |
2229 cwp->deliver_tgts += trap_acc_tgts; | |
2230 } | |
2231 | |
2232 /* temp-fail ambiguous mail if DNSBL checks timed out */ | |
2233 if (dnsbl_delay_tgts != 0) { | |
2234 if (cwp->deliver_tgts != 0) { | |
2235 /* It is not ambiguous if it must be delivered to | |
2236 * at least one recipient. */ | |
2237 cwp->deliver_tgts += dnsbl_delay_tgts; | |
2238 dnsbl_delay_tgts = 0; | |
2239 } else { | |
2240 cwp->reject_tgts += dnsbl_delay_tgts; | |
2241 } | |
2242 } | |
2243 | |
2244 if (cwp->reject_tgts != 0 && cwp->deliver_tgts != 0) { | |
2245 /* It is spam for some targets and not for others. | |
2246 * If we cannot discard, then reject it for all targets */ | |
2247 if ((cwp->cmn_fgs & CMN_FG_FROM_SUBMIT) | |
2248 || cannot_discard) { | |
2249 thr_log_print(cwp, 0, | |
2250 "rejection forced for %d targets\n", | |
2251 cwp->deliver_tgts); | |
2252 cwp->reject_tgts += cwp->deliver_tgts; | |
2253 cwp->deliver_tgts = 0; | |
2254 } else { | |
2255 thr_log_print(cwp, 0, | |
2256 "discard forced for %d targets\n", | |
2257 cwp->reject_tgts); | |
2258 } | |
2259 } | |
2260 | |
2261 /* do not embargo the message if no target wants it */ | |
2262 if (cwp->deliver_tgts == 0) | |
2263 cwp->ask_st &= ~ASK_ST_GREY_EMBARGO; | |
2264 | |
2265 if (cwp->ask_st & ASK_ST_GREY_EMBARGO) { | |
2266 make_reply(&cwp->reply, &grey_reply, cwp, 0); | |
2267 return; | |
2268 } | |
2269 | |
2270 /* finished if it is not spam or we won't do anything about it */ | |
2271 if (cwp->reject_tgts == 0 || cwp->action == CMN_IGNORE) | |
2272 return; | |
2273 | |
2274 /* make an SMTP rejection message unless we have already have one */ | |
2275 if (cwp->reply.log_result) | |
2276 return; | |
2277 | |
2278 /* if possible, use a DNSBL rejection message that applies | |
2279 * to all recipients */ | |
2280 if (common_dnsbl_hits != 0) { | |
2281 for (i = 0; i < MAX_DNSBL_GROUPS; ++i) { | |
2282 if ((common_dnsbl_hits & DNSBL_G2B(i)) != 0 | |
2283 && 0 != (blg = &cwp->cks.dnsbl->groups[i]) | |
2284 && 0 != (reply = blg->dnsbl->reply)) { | |
2285 make_reply(&cwp->reply, reply, cwp, blg); | |
2286 return; | |
2287 } | |
2288 } | |
2289 } | |
2290 | |
2291 if (dnsbl_delay_tgts != 0) { | |
2292 make_reply(&cwp->reply, &dnsbl_timeo_reply, cwp, 0); | |
2293 return; | |
2294 } | |
2295 | |
2296 /* use the generic DCC rejection message */ | |
2297 make_reply(&cwp->reply, &reject_reply, cwp, 0); | |
2298 } | |
2299 | |
2300 | |
2301 | |
2302 /* after having checked each user or recipient, | |
2303 * dispose of the message for each */ | |
2304 static void | |
2305 user_log_result(CMN_WORK *cwp, RCPT_ST *rcpt_st, const char *result) | |
2306 { | |
2307 /* create the per-user log file */ | |
2308 if ((rcpt_st->fgs & RCPT_FG_ISSPAM) | |
2309 || ((cwp->ask_st & ASK_ST_GREY_LOGIT) | |
2310 && !(rcpt_st->sws & FLTR_SW_GREY_LOG_OFF)) | |
2311 || (rcpt_st->sws & FLTR_SW_LOG_ALL)) | |
2312 user_log_msg(cwp, rcpt_st); | |
2313 | |
2314 if (cwp->ask_st & ASK_ST_INVALID_MSG) { | |
2315 user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n", result); | |
2316 return; | |
2317 } | |
2318 | |
2319 if (rcpt_st->rej_msg[0] != '\0') { | |
2320 /* rejection result for this recipient in the main log */ | |
2321 thr_log_print(cwp, 0, | |
2322 DCC_XHDR_RESULT_REJECT" %s: %s\n", | |
2323 rcpt_st->env_to, rcpt_st->rej_result); | |
2324 /* per-user log file */ | |
2325 if (!(rcpt_st->fgs & RCPT_FG_INCOMPAT_REJ) | |
2326 || (rcpt_st->sws & FLTR_SW_LOG_ALL)) { | |
2327 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_REJ_RCPT_MSG); | |
2328 user_log_write(rcpt_st, rcpt_st->rej_msg, 0); | |
2329 if (rcpt_st->rej_msg[0] == '4') | |
2330 USER_LOG_CAPTION(rcpt_st, | |
2331 "\n"DCC_XHDR_RESULT | |
2332 DCC_XHDR_RESULT_REJECT | |
2333 " temporarily\n"); | |
2334 else | |
2335 USER_LOG_CAPTION(rcpt_st, | |
2336 "\n"DCC_XHDR_RESULT | |
2337 DCC_XHDR_RESULT_REJECT); | |
2338 } | |
2339 return; | |
2340 } | |
2341 | |
2342 if (cwp->ask_st & ASK_ST_GREY_EMBARGO) { | |
2343 user_log_smtp_reply(cwp, rcpt_st); | |
2344 if (rcpt_st->embargo_num != 0) { | |
2345 user_log_print(rcpt_st, | |
2346 DCC_XHDR_RESULT"%s #%d\n", | |
2347 cwp->reply.log_result, | |
2348 rcpt_st->embargo_num); | |
2349 } else { | |
2350 user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n", | |
2351 cwp->reply.log_result); | |
2352 } | |
2353 return; | |
2354 } | |
2355 | |
2356 if (!(rcpt_st->fgs & RCPT_FG_ISSPAM)) { | |
2357 /* It is not spam for this target. | |
2358 * | |
2359 * If it was rejected late, e.g. by the SMTP server for dccifd | |
2360 * in proxy, mode, then log the rejection message */ | |
2361 if (result) { | |
2362 if (cwp->reply.str && cwp->reply.str[0] != '\0') | |
2363 user_log_print(rcpt_st, | |
2364 DCC_XHDR_REJ_DATA_MSG"%s\n", | |
2365 cwp->reply.str); | |
2366 user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n", | |
2367 result); | |
2368 return; | |
2369 } | |
2370 | |
2371 /* If it was spam for some other target and we cannot | |
2372 * discard for that target, then log the rejection */ | |
2373 if (cwp->reject_tgts != 0 | |
2374 && ((cwp->cmn_fgs & CMN_FG_FROM_SUBMIT) | |
2375 || cannot_discard)) { | |
2376 user_log_smtp_reply(cwp, rcpt_st); | |
2377 USER_LOG_CAPTION(rcpt_st, | |
2378 DCC_XHDR_RESULT | |
2379 DCC_XHDR_RESULT_REJECT | |
2380 DCC_XHDR_RESULT_FORCED"\n"); | |
2381 return; | |
2382 } | |
2383 | |
2384 if (cwp->ask_st & (ASK_ST_CLNT_ISSPAM | |
2385 | ASK_ST_SRVR_ISSPAM | |
2386 | ASK_ST_REP_ISSPAM)) { | |
2387 if (cwp->rcpt_fgs & RCPT_FG_GREY_END) | |
2388 USER_LOG_CAPTION(rcpt_st, | |
2389 DCC_XHDR_RESULT | |
2390 DCC_XHDR_RESULT_I_A | |
2391 " "DCC_XHDR_RESULT_A_GREY"\n"); | |
2392 else | |
2393 USER_LOG_CAPTION(rcpt_st, | |
2394 DCC_XHDR_RESULT | |
2395 DCC_XHDR_RESULT_I_A"\n"); | |
2396 return; | |
2397 } | |
2398 if (cwp->rcpt_fgs & RCPT_FG_GREY_END) { | |
2399 USER_LOG_CAPTION(rcpt_st, | |
2400 DCC_XHDR_RESULT | |
2401 DCC_XHDR_RESULT_ACCEPT | |
2402 " "DCC_XHDR_RESULT_A_GREY"\n"); | |
2403 return; | |
2404 } | |
2405 if (rcpt_st->fgs & RCPT_FG_GREY_WHITE) { | |
2406 USER_LOG_CAPTION(rcpt_st, | |
2407 DCC_XHDR_RESULT | |
2408 DCC_XHDR_RESULT_ACCEPT | |
2409 "; greylist whitelist\n"); | |
2410 return; | |
2411 } | |
2412 USER_LOG_CAPTION(rcpt_st, | |
2413 DCC_XHDR_RESULT DCC_XHDR_RESULT_ACCEPT"\n"); | |
2414 return; | |
2415 } | |
2416 | |
2417 /* It was spam for this target | |
2418 * | |
2419 * If some other target wanted the message, then we should discard it | |
2420 * for this target. Dccifd in proxy mode cannot discard for an | |
2421 * individual target, and so must avoid the possibility by | |
2422 * recipients that might have differing choices with cannot_discard. */ | |
2423 if (cwp->deliver_tgts != 0) { | |
2424 /* Prefer to discard for spam traps, but just accept | |
2425 * spam when we cannot discard it for dccifd */ | |
2426 if (rcpt_st->sws & FLTR_SW_TRAPS) { | |
2427 if (!cannot_discard) { | |
2428 user_reject_discard(cwp, rcpt_st); | |
2429 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT | |
2430 DCC_XHDR_RESULT_DISCARD"\n"); | |
2431 } else { | |
2432 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT | |
2433 DCC_XHDR_RESULT_ACCEPT"\n"); | |
2434 } | |
2435 | |
2436 } else { | |
2437 --cwp->reject_tgts; | |
2438 ++totals.tgts_discarded; | |
2439 user_reject_discard(cwp, rcpt_st); | |
2440 if (cwp->action == CMN_DISCARD) { | |
2441 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT | |
2442 DCC_XHDR_RESULT_DISCARD"\n"); | |
2443 } else { | |
2444 thr_log_print(cwp, 0, | |
2445 DCC_XHDR_RESULT | |
2446 DCC_XHDR_RESULT_DISCARD | |
2447 " forced for %s\n", | |
2448 rcpt_st->env_to); | |
2449 USER_LOG_CAPTION(rcpt_st, | |
2450 DCC_XHDR_RESULT | |
2451 DCC_XHDR_RESULT_DISCARD | |
2452 DCC_XHDR_RESULT_FORCED"\n"); | |
2453 } | |
2454 } | |
2455 return; | |
2456 } | |
2457 | |
2458 /* spam for all targets including this one */ | |
2459 | |
2460 if (cwp->action == CMN_DISCARD) { | |
2461 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT | |
2462 DCC_XHDR_RESULT_DISCARD"\n"); | |
2463 return; | |
2464 } | |
2465 | |
2466 if (cwp->action == CMN_IGNORE) { | |
2467 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT | |
2468 DCC_XHDR_RESULT_I_A"\n"); | |
2469 return; | |
2470 } | |
2471 | |
2472 user_log_smtp_reply(cwp, rcpt_st); | |
2473 if (rcpt_st->sws & FLTR_SW_TRAP_ACC) { | |
2474 user_log_print(rcpt_st, DCC_XHDR_RESULT | |
2475 "%s"DCC_XHDR_RESULT_FORCED"\n", | |
2476 cwp->reply.log_result); | |
2477 } else { | |
2478 user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n", | |
2479 cwp->reply.log_result); | |
2480 } | |
2481 } | |
2482 | |
2483 | |
2484 | |
2485 /* log the consensus in each target's log file */ | |
2486 void | |
2487 users_log_result(CMN_WORK *cwp, | |
2488 const char *result) /* 0 or reject by dccifd's server */ | |
2489 { | |
2490 RCPT_ST *rcpt_st; | |
2491 int error; | |
2492 | |
2493 error = pthread_mutex_lock(&user_log_mutex); | |
2494 if (error) | |
2495 dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(user_log): %s", | |
2496 ERROR_STR1(error)); | |
2497 user_log_owner = pthread_self(); | |
2498 | |
2499 /* create individual log files and trim target list */ | |
2500 for (rcpt_st = cwp->rcpt_st_first; rcpt_st; rcpt_st = rcpt_st->fwd) { | |
2501 if (rcpt_st->fgs & RCPT_FG_BAD_USERNAME) | |
2502 continue; | |
2503 if ((cwp->ask_st & ASK_ST_INVALID_MSG) | |
2504 && !(rcpt_st->sws & FLTR_SW_LOG_ALL)) | |
2505 continue; | |
2506 | |
2507 user_log_result(cwp, rcpt_st, result); | |
2508 | |
2509 if (user_log_fd >= 0) { | |
2510 dcc_log_close(0, rcpt_st->user_log_nm, | |
2511 user_log_fd, &cwp->ldate); | |
2512 user_log_fd = -1; | |
2513 } | |
2514 } | |
2515 | |
2516 log_fd2_close(-1); | |
2517 | |
2518 user_log_owner = 0; | |
2519 error = pthread_mutex_unlock(&user_log_mutex); | |
2520 if (error) | |
2521 dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(user_log): %s", | |
2522 ERROR_STR1(error)); | |
2523 } |