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 }