Mercurial > notdcc
comparison dccproc/dccproc.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 server | |
2 * | |
3 * report a message for such as procmail | |
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.189 $Revision$ | |
40 */ | |
41 | |
42 #include "dcc_ck.h" | |
43 #include "dcc_xhdr.h" | |
44 #include "dcc_heap_debug.h" | |
45 #include <signal.h> /* for Linux and SunOS*/ | |
46 #ifndef DCC_WIN32 | |
47 #include <arpa/inet.h> | |
48 #endif | |
49 | |
50 | |
51 static DCC_EMSG dcc_emsg; | |
52 | |
53 static const char *mapfile_nm = DCC_MAP_NM_DEF; | |
54 | |
55 static u_char priv_logdir; | |
56 static DCC_PATH log_path; | |
57 static int lfd = -1; | |
58 static struct timeval ldate; | |
59 | |
60 static u_char logging = 1; /* 0=no log, 1=have file, 2=used it */ | |
61 static size_t log_size; | |
62 | |
63 static char id[DCC_MSG_ID_LEN+1]; | |
64 static DCC_PATH tmp_nm; | |
65 static int tmp_fd = -1; | |
66 static u_char tmp_rewound; | |
67 static int hdrs_len, body_len; | |
68 static u_char seen_hdr; | |
69 | |
70 static int exit_code = EX_NOUSER; | |
71 static DCC_TGTS local_tgts; | |
72 static u_char local_tgts_spam, local_tgts_set; | |
73 static int total_hdrs, cr_hdrs; | |
74 | |
75 static const char* white_nm; | |
76 static const char *ifile_nm = "stdin", *ofile_nm = "stdout"; | |
77 static FILE *ifile, *ofile; | |
78 | |
79 static DCC_CLNT_CTXT *ctxt; | |
80 static char xhdr_fname[sizeof(DCC_XHDR_START)+sizeof(DCC_BRAND)+1]; | |
81 static int xhdr_fname_len; | |
82 static u_char add_xhdr; /* add instead of replace header */ | |
83 static u_char cksums_only; /* output only checksums */ | |
84 static u_char x_dcc_only; /* output only the X-DCC header */ | |
85 static u_char fake_envelope; /* fake envelope log lines */ | |
86 static int std_received; /* Received: line is standard */ | |
87 | |
88 static ASK_ST ask_st; | |
89 static FLTR_SWS rcpt_sws; | |
90 static DCC_GOT_CKS cks; | |
91 static DCC_CKS_WTGTS wtgts; | |
92 static char helo[DCC_HELO_MAX]; | |
93 static char sender_name[DCC_MAXDOMAINLEN]; | |
94 static char sender_str[INET6_ADDRSTRLEN]; | |
95 static struct in6_addr clnt_addr; | |
96 | |
97 static char env_from_buf[DCC_HDR_CK_MAX+1]; | |
98 static const char *env_from = 0; | |
99 | |
100 static char mail_host[DCC_MAXDOMAINLEN]; | |
101 | |
102 static DCC_HEADER_BUF header; | |
103 | |
104 static EARLY_LOG early_log; | |
105 | |
106 static void start_dccifd(void); | |
107 static u_char check_mx_listing(void); | |
108 static int get_hdr(char *, int); | |
109 static void add_hdr(void *, const char *, u_int); | |
110 static void tmp_write(const void *, int); | |
111 static void tmp_close(void); | |
112 static void scopy(int, u_char); | |
113 static void log_write(const void *, int); | |
114 static void log_body_write(const char *, u_int); | |
115 static void thr_log_write(void *, const char *, u_int); | |
116 static void log_late(void); | |
117 static int log_print(u_char, const char *, ...) PATTRIB(2,3); | |
118 #define LOG_CAPTION(s) log_write((s), LITZ(s)) | |
119 #define LOG_EOL() LOG_CAPTION("\n") | |
120 static void log_fin(void); | |
121 static void log_ck(void *, const char *, u_int); | |
122 static void dccproc_error_msg(const char *, ...) PATTRIB(1,2); | |
123 static void sigterm(int); | |
124 | |
125 | |
126 static const char *usage_str = | |
127 "[-VdAQCHER] [-h homedir] [-m map] [-w whiteclnt] [-T tmpdir]\n" | |
128 " [-a IP-address] [-f env_from] [-t targets] [-x exitcode]\n" | |
129 " [-c type,[log-thold,][spam-thold]] [-g [not-]type] [-S header]\n" | |
130 " [-i infile] [-o outfile] [-l logdir] [-B dnsbl-option]\n" | |
131 " [-L ltype,facility.level]"; | |
132 | |
133 static void NRATTRIB | |
134 usage(const char* barg) | |
135 { | |
136 if (barg) { | |
137 dcc_logbad(EX_USAGE, "unrecognized \"%s\"\nusage: %s\n", | |
138 barg, usage_str); | |
139 } else { | |
140 dcc_logbad(EX_USAGE, "%s\n", usage_str); | |
141 } | |
142 } | |
143 | |
144 | |
145 | |
146 int NRATTRIB | |
147 main(int argc, char **argv) | |
148 { | |
149 char buf[20*DCC_HDR_CK_MAX]; /* at least DCC_HDR_CK_MAX*3 */ | |
150 u_char log_tgts_set = 0; | |
151 const char *homedir = 0; | |
152 const char *logdir = 0; | |
153 const char *tmpdir = 0; | |
154 u_char ask_result; | |
155 char *p; | |
156 const char *p2; | |
157 u_long l; | |
158 int error, blen, i; | |
159 | |
160 /* because stderr is often mixed with stdout and effectively | |
161 * invisible, also complain to syslog */ | |
162 dcc_syslog_init(1, argv[0], 0); | |
163 dcc_clear_tholds(); | |
164 | |
165 /* get ready for the IP and From header checksums */ | |
166 dcc_cks_init(&cks); | |
167 | |
168 /* we must be SUID to read and write the system's common connection | |
169 * parameter memory mapped file. We also need to read the common | |
170 * local white list and write the mmap()'ed hash file */ | |
171 dcc_init_priv(); | |
172 | |
173 ofile = stdout; | |
174 ifile = stdin; | |
175 opterr = 0; | |
176 while ((i = getopt(argc, argv, "VdAQCHER" | |
177 "r:h:m:w:T:a:f:g:S:t:x:c:i:o:l:B:L:")) != -1) { | |
178 switch (i) { | |
179 case 'V': | |
180 fprintf(stderr, DCC_VERSION"\n"); | |
181 exit(EX_OK); | |
182 break; | |
183 | |
184 case 'd': | |
185 ++dcc_clnt_debug; | |
186 break; | |
187 | |
188 case 'A': | |
189 add_xhdr = 1; | |
190 break; | |
191 | |
192 case 'Q': | |
193 ask_st |= ASK_ST_QUERY; | |
194 break; | |
195 | |
196 case 'C': | |
197 cksums_only = 1; | |
198 break; | |
199 | |
200 case 'H': | |
201 x_dcc_only = 1; | |
202 break; | |
203 | |
204 case 'E': | |
205 fake_envelope = 1; | |
206 break; | |
207 | |
208 case 'R': | |
209 if (!std_received) | |
210 std_received = 1; | |
211 break; | |
212 | |
213 case 'r': /* a bad idea replacment for -R */ | |
214 std_received = strtoul(optarg, &p, 10); | |
215 if (*p != '\0' || i == 0) { | |
216 dccproc_error_msg("invalid count" | |
217 " \"-e %s\"", optarg); | |
218 std_received = 1; | |
219 } | |
220 break; | |
221 | |
222 case 'h': | |
223 homedir = optarg; | |
224 break; | |
225 | |
226 case 'm': | |
227 mapfile_nm = optarg; | |
228 break; | |
229 | |
230 case 'w': | |
231 white_nm = optarg; | |
232 break; | |
233 | |
234 case 'T': | |
235 tmpdir = optarg; | |
236 break; | |
237 | |
238 case 'a': | |
239 /* ignore SpamAssassin noise */ | |
240 if (!strcmp("0.0.0.0", optarg)) | |
241 break; | |
242 dcc_host_lock(); | |
243 if (!dcc_get_host(optarg, 2, &error)) { | |
244 dccproc_error_msg("\"-a %s\": %s", | |
245 optarg, DCC_HSTRERROR(error)); | |
246 } else { | |
247 if (dcc_hostaddrs[0].sa.sa_family == AF_INET) | |
248 dcc_ipv4toipv6(&clnt_addr, | |
249 dcc_hostaddrs[0] | |
250 .ipv4.sin_addr); | |
251 else | |
252 clnt_addr = (dcc_hostaddrs[0].ipv6 | |
253 .sin6_addr); | |
254 dcc_get_ipv6_ck(&cks, &clnt_addr); | |
255 dcc_ipv6tostr(sender_str, sizeof(sender_str), | |
256 &clnt_addr); | |
257 } | |
258 dcc_host_unlock(); | |
259 break; | |
260 | |
261 case 'f': | |
262 env_from = optarg; | |
263 break; | |
264 | |
265 case 'g': /* honor not-spam "counts" */ | |
266 dcc_parse_honor(optarg); | |
267 break; | |
268 | |
269 case 'S': | |
270 if (!dcc_add_sub_hdr(dcc_emsg, optarg)) | |
271 dcc_logbad(EX_USAGE, "%s", dcc_emsg); | |
272 break; | |
273 | |
274 case 't': | |
275 if (!strcasecmp(optarg, "many")) { | |
276 local_tgts = 1; | |
277 local_tgts_spam = 1; | |
278 local_tgts_set = 1; | |
279 } else { | |
280 l = strtoul(optarg, &p, 10); | |
281 if (*p != '\0' || l > DCC_TGTS_RPT_MAX) { | |
282 dccproc_error_msg("invalid count" | |
283 " \"-t %s\"", optarg); | |
284 } else { | |
285 local_tgts = l; | |
286 local_tgts_spam = 0; | |
287 local_tgts_set = 1; | |
288 } | |
289 } | |
290 break; | |
291 | |
292 case 'x': | |
293 l = strtoul(optarg, &p, 10); | |
294 if (*p != '\0') { | |
295 dccproc_error_msg("invalid exit code \"-x %s\"", | |
296 optarg); | |
297 } else { | |
298 exit_code = l; | |
299 } | |
300 break; | |
301 | |
302 case 'c': | |
303 if (dcc_parse_tholds("-c ", optarg)) | |
304 log_tgts_set = 1; | |
305 break; | |
306 | |
307 case 'i': | |
308 /* open the input file now, before changing to the | |
309 * home DCC directory */ | |
310 ifile_nm = optarg; | |
311 ifile = fopen(ifile_nm, "r"); | |
312 if (!ifile) | |
313 dcc_logbad(EX_USAGE, | |
314 "bad input file \"%s\": %s", | |
315 ifile_nm, ERROR_STR()); | |
316 break; | |
317 | |
318 case 'o': | |
319 /* open the output file now, before changing to the | |
320 * home DCC directory */ | |
321 ofile_nm = optarg; | |
322 ofile = fopen(ofile_nm, "w"); | |
323 if (!ofile) | |
324 dcc_logbad(EX_USAGE, | |
325 "bad output file \"%s\": %s", | |
326 ofile_nm, ERROR_STR()); | |
327 break; | |
328 | |
329 case 'l': | |
330 logdir = optarg; | |
331 break; | |
332 | |
333 case 'B': | |
334 if (!dcc_parse_dnsbl(dcc_emsg, optarg, 0, 1)) | |
335 dccproc_error_msg("%s", dcc_emsg); | |
336 break; | |
337 | |
338 #ifndef DCC_WIN32 | |
339 case 'L': | |
340 dcc_parse_log_opt(optarg); | |
341 break; | |
342 #endif | |
343 | |
344 default: | |
345 usage(optopt2str(optopt)); | |
346 } | |
347 } | |
348 if (argc != optind) | |
349 usage(argv[optind]); | |
350 | |
351 #ifdef SIGPIPE | |
352 signal(SIGPIPE, SIG_IGN); | |
353 #endif | |
354 #ifdef SIGHUP | |
355 signal(SIGHUP, sigterm); | |
356 #endif | |
357 signal(SIGTERM, sigterm); | |
358 signal(SIGINT, sigterm); | |
359 #ifdef SIGXFSZ | |
360 signal(SIGXFSZ, SIG_IGN); | |
361 #endif | |
362 | |
363 /* Close STDERR to keep it from being mixed with the message, | |
364 * unless we are not going to output the message to STDOUT. | |
365 * Ensure that stderr and file descriptor 2 are open to something | |
366 * to prevent surprises from busybody libraries. */ | |
367 if (!dcc_clnt_debug && !cksums_only && !x_dcc_only && !ofile_nm) { | |
368 close(STDERR_FILENO); | |
369 clean_stdio(); | |
370 } | |
371 | |
372 dcc_clnt_unthread_init(); | |
373 dcc_cdhome(0, homedir, 0); | |
374 if (!dcc_main_logdir_init(dcc_emsg, logdir)) { | |
375 dcc_error_msg("%s", dcc_emsg); | |
376 /* dccproc will not be around as a daemon | |
377 * when and if the log directory is created | |
378 * so forget about a directory that might | |
379 * someday be ok */ | |
380 dcc_main_logdir[0] = '\0'; | |
381 } | |
382 tmp_path_init(tmpdir, logdir); | |
383 | |
384 if (dcc_main_logdir[0] == '\0') { | |
385 if (log_tgts_set) | |
386 dccproc_error_msg("log thresholds set with -c" | |
387 " but no -l directory"); | |
388 logging = 0; | |
389 } else { | |
390 #ifndef DCC_WIN32 | |
391 /* use privileges to make log files in the built-in home | |
392 * directory */ | |
393 if (!homedir | |
394 && 0 > access(dcc_main_logdir, R_OK|W_OK|X_OK)) { | |
395 priv_logdir = 1; | |
396 dcc_get_priv_home(dcc_main_logdir); | |
397 } | |
398 #endif | |
399 lfd = dcc_main_log_open(dcc_emsg, log_path, id, sizeof(id)); | |
400 if (priv_logdir) | |
401 dcc_rel_priv(); | |
402 if (lfd < 0) { | |
403 dccproc_error_msg("%s", dcc_emsg); | |
404 logging = 0; | |
405 } | |
406 } | |
407 | |
408 if (fake_envelope && lfd >= 0) { | |
409 char date_buf[40]; | |
410 | |
411 gettimeofday(&ldate, 0); | |
412 log_print(0, DCC_LOG_DATE_PAT"\n", | |
413 dcc_time2str(date_buf, sizeof(date_buf), | |
414 DCC_LOG_DATE_FMT, | |
415 ldate.tv_sec)); | |
416 } | |
417 | |
418 if (!local_tgts_set) { | |
419 local_tgts = (ask_st & ASK_ST_QUERY) ? 0 : 1; | |
420 local_tgts_spam = 0; | |
421 } else if (local_tgts == 0) { | |
422 ask_st |= ASK_ST_QUERY; | |
423 local_tgts_spam = 0; | |
424 } else if (ask_st & ASK_ST_QUERY) { | |
425 dcc_error_msg("\"-t %s\" is incompatible with \"-Q\"", | |
426 local_tgts_spam | |
427 ? "many" | |
428 : dcc_tgts2str(buf, sizeof(buf), local_tgts, 0)); | |
429 local_tgts = 0; | |
430 local_tgts_spam = 0; | |
431 } | |
432 if (local_tgts == DCC_TGTS_TOO_MANY) { | |
433 local_tgts = 1; | |
434 local_tgts_spam = 1; | |
435 } | |
436 | |
437 if (logging | |
438 || (!cksums_only && !x_dcc_only)) { | |
439 tmp_fd = dcc_mkstemp(dcc_emsg, tmp_nm, sizeof(tmp_nm), | |
440 id, sizeof(id), 0, | |
441 0, DCC_TMP_LOG_PREFIX, 1, 0); | |
442 if (tmp_fd < 0) | |
443 dcc_logbad(EX_IOERR, "%s", dcc_emsg); | |
444 } | |
445 | |
446 /* start a connection to a DCC server | |
447 * we need the server's name for the X-DCC header. */ | |
448 ctxt = dcc_clnt_start(dcc_emsg, 0, mapfile_nm, | |
449 DCC_CLNT_FG_BAD_SRVR_OK | |
450 | DCC_CLNT_FG_NO_PICK_SRVR | |
451 | DCC_CLNT_FG_NO_FAIL); | |
452 if (ctxt) { | |
453 if (!homedir) | |
454 start_dccifd(); | |
455 dcc_emsg[0] = '\0'; | |
456 ctxt = dcc_clnt_start_fin(dcc_emsg, ctxt); | |
457 } | |
458 if (!ctxt) { | |
459 dccproc_error_msg("%s", dcc_emsg); | |
460 } else { | |
461 xhdr_fname_len = get_xhdr_fname(xhdr_fname, sizeof(xhdr_fname), | |
462 dcc_clnt_info); | |
463 } | |
464 | |
465 /* get the local whitelist ready */ | |
466 dcc_wf_init(&cmn_wf, DCC_WF_EITHER); | |
467 if (white_nm | |
468 && !dcc_new_white_nm(dcc_emsg, &cmn_wf, white_nm)) { | |
469 dccproc_error_msg("%s", dcc_emsg); | |
470 white_nm = 0; | |
471 } | |
472 /* look past the SMTP client if it is a listed MX server */ | |
473 if (sender_str[0] != '\0' && white_nm) | |
474 check_mx_listing(); | |
475 | |
476 dcc_dnsbl_init(&cks, ctxt, 0, id); | |
477 | |
478 /* get the headers */ | |
479 for (;;) { | |
480 int hlen; | |
481 | |
482 hlen = get_hdr(buf, sizeof(buf)); | |
483 if (hlen <= 2 | |
484 && (buf[0] == '\n' | |
485 || (buf[0] == '\r' && buf[1] == '\n'))) { | |
486 /* stop at the separator between the body and headers */ | |
487 if (!seen_hdr) | |
488 dcc_logbad(EX_DATAERR, | |
489 "missing SMTP header lines"); | |
490 hdrs_len -= hlen; | |
491 body_len = hlen; | |
492 break; | |
493 } | |
494 | |
495 #define GET_HDR_CK(h,t) { \ | |
496 if (!CLITCMP(buf, h)) { \ | |
497 dcc_get_cks(&cks,DCC_CK_##t, &buf[LITZ(h)], 1);\ | |
498 seen_hdr = 1; \ | |
499 continue;}} | |
500 GET_HDR_CK(DCC_XHDR_TYPE_FROM":", FROM); | |
501 GET_HDR_CK(DCC_XHDR_TYPE_MESSAGE_ID":", MESSAGE_ID); | |
502 #undef GET_HDR_CK | |
503 | |
504 /* notice UNIX From_ line */ | |
505 if (!seen_hdr | |
506 && !env_from | |
507 && parse_unix_from(buf, env_from_buf, | |
508 sizeof(env_from_buf))) { | |
509 env_from = env_from_buf; | |
510 seen_hdr = 1; | |
511 continue; | |
512 } | |
513 | |
514 if (!env_from && parse_return_path(buf, env_from_buf, | |
515 sizeof(env_from_buf))) { | |
516 env_from = env_from_buf; | |
517 seen_hdr = 1; | |
518 continue; | |
519 } | |
520 | |
521 if (!CLITCMP(buf, DCC_XHDR_TYPE_RECEIVED":")) { | |
522 seen_hdr = 1; | |
523 | |
524 p2 = &buf[LITZ(DCC_XHDR_TYPE_RECEIVED":")]; | |
525 | |
526 /* compute checksum of the last Received: header */ | |
527 dcc_get_cks(&cks, DCC_CK_RECEIVED, p2, 1); | |
528 | |
529 /* pick IP address out of Nth Received: header | |
530 * unless we had a good -a value */ | |
531 if (sender_str[0] != '\0') | |
532 std_received = 0; | |
533 if (!std_received) | |
534 continue; | |
535 if (--std_received > 0) | |
536 continue; | |
537 | |
538 p2 = parse_received(p2, &cks, helo, sizeof(helo), | |
539 sender_str, sizeof(sender_str), | |
540 sender_name, sizeof(sender_name)); | |
541 if (p2 == 0) { | |
542 /* to avoid being fooled by forged Received: | |
543 * fields, do not skip unrecognized forms */ | |
544 std_received = 0; | |
545 } else if (*p2 != '\0') { | |
546 log_print(1, "skip %s Received: header\n", p2); | |
547 std_received = 1; | |
548 } else { | |
549 std_received = check_mx_listing(); | |
550 } | |
551 continue; | |
552 } | |
553 | |
554 /* Notice MIME multipart boundary definitions */ | |
555 dcc_ck_mime_hdr(&cks, buf, 0); | |
556 | |
557 if (dcc_ck_get_sub(&cks, buf, 0)) | |
558 seen_hdr = 1; | |
559 | |
560 /* notice any sort of header */ | |
561 if (!seen_hdr) { | |
562 for (p = buf; ; ++p) { | |
563 if (*p == ':') { | |
564 seen_hdr = 1; | |
565 break; | |
566 } | |
567 if (*p <= ' ' || *p >= 0x7f) | |
568 break; | |
569 } | |
570 } | |
571 } | |
572 /* Create a checksum for a null Message-ID header if there | |
573 * was no Message-ID header. */ | |
574 if (cks.sums[DCC_CK_MESSAGE_ID].type != DCC_CK_MESSAGE_ID) | |
575 dcc_get_cks(&cks, DCC_CK_MESSAGE_ID, "", 0); | |
576 | |
577 /* Check DNS blacklists for STMP client and envelope sender | |
578 * before collecting the body to avoid wasting time DNS resolving | |
579 * URLs if the envelope answers the question. Much of the DNS | |
580 * work for the envelope has probably already been done. */ | |
581 if (cks.sums[DCC_CK_IP].type == DCC_CK_IP) | |
582 dcc_client_dnsbl(cks.dnsbl, &cks.ip_addr, sender_name); | |
583 | |
584 if (env_from) { | |
585 dcc_get_cks(&cks, DCC_CK_ENV_FROM, env_from, 1); | |
586 if (parse_mail_host(env_from, mail_host, sizeof(mail_host))) { | |
587 dcc_ck_get_sub(&cks, "mail_host", mail_host); | |
588 dcc_mail_host_dnsbl(cks.dnsbl, mail_host); | |
589 } | |
590 } | |
591 | |
592 /* collect the body */ | |
593 do { | |
594 blen = fread(buf, 1, sizeof(buf), ifile); | |
595 if (blen != sizeof(buf)) { | |
596 if (ferror(ifile)) | |
597 dcc_logbad(EX_DATAERR, "fgets(%s): %s", | |
598 ifile_nm, ERROR_STR()); | |
599 if (!blen) | |
600 break; | |
601 } | |
602 | |
603 tmp_write(buf, blen); | |
604 body_len += blen; | |
605 dcc_ck_body(&cks, buf, blen); | |
606 } while (!feof(ifile)); | |
607 fclose(ifile); | |
608 | |
609 dcc_cks_fin(&cks); | |
610 | |
611 if (!unthr_ask_white(dcc_emsg, &ask_st, &rcpt_sws, | |
612 white_nm, &cks, wtgts)) | |
613 dccproc_error_msg("%s", dcc_emsg); | |
614 | |
615 dcc_dnsbl_result(&ask_st, cks.dnsbl); | |
616 | |
617 /* Unlike dccm and dccifd, no "option DNSBL-on" line is required in | |
618 * the whiteclnt file. A -B argument is sufficient to show that | |
619 * DNSBL filtering is wanted. */ | |
620 if (ask_st & ASK_ST_DNSBL_HIT_M) | |
621 ask_st |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT); | |
622 | |
623 if (ctxt) { | |
624 if (ask_st & ASK_ST_QUERY) { | |
625 local_tgts_spam = 0; | |
626 local_tgts = 0; | |
627 } | |
628 if (local_tgts != 0 | |
629 && (ask_st & ASK_ST_CLNT_ISSPAM)) | |
630 local_tgts_spam = 1; | |
631 | |
632 ask_result = unthr_ask_dcc(dcc_emsg, ctxt, &header, &ask_st, | |
633 &cks, local_tgts_spam, local_tgts); | |
634 if (!ask_result) | |
635 dccproc_error_msg("%s", dcc_emsg); | |
636 } | |
637 | |
638 if (fake_envelope && lfd >= 0) { | |
639 if (sender_str[0] != '\0') { | |
640 LOG_CAPTION(DCC_XHDR_TYPE_IP": "); | |
641 log_write(sender_name, strlen(sender_name)); | |
642 LOG_CAPTION(" "); | |
643 log_write(sender_str, strlen(sender_str)); | |
644 LOG_EOL(); | |
645 } | |
646 if (helo[0] != '\0') { | |
647 LOG_CAPTION("HELO: "); | |
648 log_write(helo, strlen(helo)); | |
649 LOG_EOL(); | |
650 dcc_ck_get_sub(&cks, "helo", helo); | |
651 } | |
652 if (env_from) { | |
653 LOG_CAPTION(DCC_XHDR_TYPE_ENV_FROM": "); | |
654 log_write(env_from, strlen(env_from)); | |
655 log_print(0, " mail_host=%s", mail_host); | |
656 LOG_EOL(); | |
657 } | |
658 LOG_EOL(); | |
659 } | |
660 | |
661 /* copy the headers to the log file and the output */ | |
662 scopy(hdrs_len, 1); | |
663 | |
664 /* emit the X-DCC and external filter headers | |
665 * End them with "\r\n" if at least half of the header lines | |
666 * ended that way. Otherwise use "\n" */ | |
667 if (header.buf[0] != '\0') | |
668 xhdr_write(add_hdr, 0, header.buf, header.used, | |
669 cr_hdrs > total_hdrs/2); | |
670 | |
671 /* emit body */ | |
672 scopy(body_len, 1); | |
673 | |
674 LOG_CAPTION(DCC_LOG_MSG_SEP); | |
675 | |
676 log_late(); | |
677 | |
678 /* make the log file look like a dccm or dccifd log file */ | |
679 if (fake_envelope) { | |
680 DCC_PATH abs_nm; | |
681 | |
682 if (ask_st & ASK_ST_WLIST_NOTSPAM) | |
683 log_print(0, "%s"DCC_XHDR_ISOK"\n", | |
684 fnm2abs_err(abs_nm, white_nm)); | |
685 else if (ask_st & ASK_ST_WLIST_ISSPAM) | |
686 log_print(0, "%s%s\n", | |
687 fnm2abs_err(abs_nm, white_nm), | |
688 (rcpt_sws & FLTR_SW_TRAP_ACC) | |
689 ? "-->"DCC_XHDR_TRAP_ACC | |
690 : (rcpt_sws & FLTR_SW_TRAP_REJ) | |
691 ? "-->"DCC_XHDR_TRAP_REJ | |
692 : DCC_XHDR_ISSPAM); | |
693 log_ask_st(thr_log_write, 0, ask_st, rcpt_sws, 0, &header); | |
694 } | |
695 | |
696 /* log error message for most prematurely closed output pipes before | |
697 * logging the checksums or sending them to the pipe for -C */ | |
698 if (EOF == fflush(ofile)) { | |
699 dcc_error_msg("fflush(%s): %s", ofile_nm, ERROR_STR()); | |
700 fclose(ofile); | |
701 ofile = 0; | |
702 } | |
703 | |
704 dcc_print_cks(log_ck, 0, local_tgts_spam, local_tgts, &cks, wtgts, 0); | |
705 | |
706 if (ofile) { | |
707 if (fclose(ofile)) | |
708 dcc_logbad(EX_IOERR, "fclose(%s): %s", | |
709 ofile_nm, ERROR_STR()); | |
710 ofile = 0; | |
711 } | |
712 | |
713 /* Exit saying it was spam unless this is an accepting spam trap. | |
714 * Spam traps report all mail as spam, but expect to receive it for | |
715 * logging or analysis. */ | |
716 if (((ask_st & ASK_ST_CLNT_ISSPAM) | |
717 || ((ask_st & ASK_ST_SRVR_ISSPAM) | |
718 && !(rcpt_sws & FLTR_SW_DCC_OFF)) | |
719 || ((ask_st & ASK_ST_REP_ISSPAM) | |
720 && (rcpt_sws & FLTR_SW_REP_ON))) | |
721 && !(rcpt_sws & FLTR_SW_TRAP_ACC)) { | |
722 p2 = "\n"DCC_XHDR_RESULT DCC_XHDR_RESULT_REJECT"\n"; | |
723 | |
724 } else { | |
725 p2 = "\n"DCC_XHDR_RESULT DCC_XHDR_RESULT_ACCEPT"\n"; | |
726 exit_code = EX_OK; | |
727 } | |
728 if (fake_envelope) | |
729 log_write(p2, strlen(p2)); | |
730 | |
731 log_fin(); | |
732 exit(exit_code); | |
733 | |
734 #ifdef DCC_WIN32 | |
735 return 0; | |
736 #endif | |
737 } | |
738 | |
739 | |
740 | |
741 static void | |
742 start_dccifd(void) | |
743 { | |
744 #ifndef DCC_WIN32 | |
745 time_t t; | |
746 int c; | |
747 pid_t pid; | |
748 | |
749 assert_info_locked(); | |
750 | |
751 /* once an hour, | |
752 * start dccifd if dccproc is run more often than | |
753 * DCCPROC_MAX_CREDITS times at an average rate of at least | |
754 * DCCPROC_COST times per second */ | |
755 | |
756 t = (ctxt->start.tv_sec/DCCPROC_COST | |
757 - dcc_clnt_info->dccproc_last/DCCPROC_COST); | |
758 if (t > DCCPROC_MAX_CREDITS*2) /* don't overflow */ | |
759 t = DCCPROC_MAX_CREDITS*2; | |
760 else if (t < 0) | |
761 t = 0; | |
762 c = t + dcc_clnt_info->dccproc_c; | |
763 if (c > DCCPROC_MAX_CREDITS) | |
764 c = DCCPROC_MAX_CREDITS; | |
765 --c; | |
766 if (c < -DCCPROC_MAX_CREDITS) | |
767 c = -DCCPROC_MAX_CREDITS; | |
768 dcc_clnt_info->dccproc_c = c; | |
769 dcc_clnt_info->dccproc_last = ctxt->start.tv_sec; | |
770 | |
771 if (dcc_clnt_info->dccproc_c >= 0) | |
772 return; | |
773 | |
774 if (!DCC_IS_TIME(ctxt->start.tv_sec, | |
775 dcc_clnt_info->dccproc_dccifd_try, | |
776 DCCPROC_TRY_DCCIFD)) | |
777 return; | |
778 dcc_clnt_info->dccproc_dccifd_try = (ctxt->start.tv_sec | |
779 + DCCPROC_TRY_DCCIFD); | |
780 pid = fork(); | |
781 if (pid) { | |
782 if (pid < 0) | |
783 dccproc_error_msg("fork(): %s", ERROR_STR()); | |
784 return; | |
785 } | |
786 | |
787 close(STDIN_FILENO); | |
788 close(STDOUT_FILENO); | |
789 close(STDERR_FILENO); | |
790 clean_stdio(); | |
791 | |
792 dcc_get_priv(); | |
793 setuid(dcc_effective_uid); | |
794 setgid(dcc_effective_gid); | |
795 | |
796 dcc_trace_msg("try to start dccifd"); | |
797 execl(DCC_LIBEXECDIR"/start-dccifd", | |
798 "start-dccifd", "-A", (const char *)0); | |
799 dcc_trace_msg("exec("DCC_LIBEXECDIR"/start-dccifd): %s", ERROR_STR()); | |
800 exit(0); | |
801 #endif /* DCC_WIN32 */ | |
802 } | |
803 | |
804 | |
805 | |
806 /* If the immediate SMTP client because it is a listed MX server, | |
807 * then we must ignore its IP address and keep looking for the | |
808 * real SMTP client. */ | |
809 static u_char /* 1=listed MX server */ | |
810 check_mx_listing(void) | |
811 { | |
812 DCC_TGTS tgts; | |
813 | |
814 if (!dcc_white_mx(dcc_emsg, &tgts, &cks)) | |
815 dccproc_error_msg("%s", dcc_emsg); | |
816 | |
817 if (tgts == DCC_TGTS_OK) { | |
818 return 0; | |
819 } | |
820 | |
821 if (tgts == DCC_TGTS_SUBMIT_CLIENT) { | |
822 log_print(1, "%s is a listed 'submit' client\n", | |
823 dcc_trim_ffff(sender_str)); | |
824 return 0; | |
825 } | |
826 | |
827 if (tgts == DCC_TGTS_OK_MXDCC) { | |
828 log_print(1, "%s is a whitelisted MX server with DCC client\n", | |
829 dcc_trim_ffff(sender_str)); | |
830 ask_st |= ASK_ST_QUERY; | |
831 } else if (tgts == DCC_TGTS_OK_MX) { | |
832 log_print(1, "%s is a whitelisted MX server\n", | |
833 dcc_trim_ffff(sender_str)); | |
834 } else { | |
835 return 0; | |
836 } | |
837 | |
838 sender_str[0] = '\0'; | |
839 dcc_unget_ip_ck(&cks); | |
840 | |
841 /* tell caller to look at the next Received: header */ | |
842 return 1; | |
843 } | |
844 | |
845 | |
846 | |
847 /* send a new header to the output and the log */ | |
848 static void | |
849 add_hdr(void *wp0 UATTRIB, const char *buf, u_int buf_len) | |
850 { | |
851 u_int i; | |
852 const char *estr; | |
853 | |
854 log_body_write(buf, buf_len); | |
855 if (ofile) { | |
856 i = fwrite(buf, 1, buf_len, ofile); | |
857 if (i != buf_len) { | |
858 estr = ERROR_STR(); | |
859 fclose(ofile); | |
860 ofile = 0; | |
861 dcc_logbad(EX_IOERR, "fwrite(add_hdr %s,%d)=%d: %s", | |
862 ofile_nm, buf_len, i, estr); | |
863 } | |
864 } | |
865 } | |
866 | |
867 | |
868 | |
869 /* get the next header line */ | |
870 static int /* header length */ | |
871 get_hdr(char *buf, | |
872 int buflen) /* >DCC_HDR_CK_MAX*3 */ | |
873 { | |
874 u_char no_copy; | |
875 int hlen, wpos; | |
876 const char *line; | |
877 char c; | |
878 int llen, i; | |
879 | |
880 no_copy = 0; | |
881 hlen = wpos = 0; | |
882 for (;;) { | |
883 line = fgets(&buf[hlen], buflen-hlen, ifile); | |
884 if (!line) { | |
885 if (ferror(ifile)) | |
886 dcc_logbad(EX_DATAERR, "fgets(%s): %s", | |
887 ifile_nm, ERROR_STR()); | |
888 else | |
889 dcc_logbad(EX_DATAERR, "missing message body"); | |
890 } | |
891 llen = strlen(line); | |
892 | |
893 /* delete our X-DCC header at the start of a field */ | |
894 if (hlen == 0 && !add_xhdr | |
895 && is_xhdr(buf, llen)) { | |
896 seen_hdr = 1; | |
897 no_copy = 1; | |
898 } | |
899 | |
900 /* do not crash on too-long headers */ | |
901 hlen += llen; | |
902 if (hlen > DCC_HDR_CK_MAX*2) { | |
903 /* truncate headers too big for our buffer */ | |
904 if (!no_copy | |
905 && ((i = (hlen - wpos)) > 0)) { | |
906 tmp_write(&buf[wpos], i); | |
907 hdrs_len += i; | |
908 } | |
909 c = buf[hlen-1]; | |
910 hlen = DCC_HDR_CK_MAX; | |
911 buf[hlen++] = '\r'; | |
912 buf[hlen++] = '\n'; | |
913 wpos = hlen; | |
914 if (c != '\n') | |
915 continue; | |
916 } | |
917 | |
918 /* get the next character after the end-of-line to see if | |
919 * the next line is a continuation */ | |
920 if (hlen > 2) { | |
921 i = getc(ifile); | |
922 if (i != EOF) | |
923 ungetc(i, ifile); | |
924 if (i == ' ' || i == '\t') | |
925 continue; | |
926 } | |
927 | |
928 /* not a continuation, so stop reading the field */ | |
929 ++total_hdrs; | |
930 /* notice if this line ended with "\r\n" */ | |
931 if (hlen > 1 && buf[hlen-2] == '\r') | |
932 ++cr_hdrs; | |
933 | |
934 if (!no_copy) { | |
935 i = hlen - wpos; | |
936 if (i > 0) { | |
937 tmp_write(&buf[wpos], hlen-wpos); | |
938 hdrs_len += i; | |
939 } | |
940 return hlen; | |
941 } | |
942 | |
943 /* at the end of our X-DCC header, look for another */ | |
944 no_copy = 0; | |
945 hlen = wpos = 0; | |
946 } | |
947 } | |
948 | |
949 | |
950 | |
951 static void | |
952 tmp_write(const void *buf, int len) | |
953 { | |
954 int i; | |
955 | |
956 if (tmp_fd < 0) | |
957 return; | |
958 | |
959 if (tmp_rewound) | |
960 dcc_logbad(EX_SOFTWARE, "writing to rewound temp file"); | |
961 | |
962 i = write(tmp_fd, buf, len); | |
963 if (i != len) { | |
964 if (i < 0) | |
965 dcc_logbad(EX_IOERR, "write(%s,%d): %s", | |
966 tmp_nm, len, ERROR_STR()); | |
967 else | |
968 dcc_logbad(EX_IOERR, "write(%s,%d)=%d", | |
969 tmp_nm, len, i); | |
970 } | |
971 } | |
972 | |
973 | |
974 | |
975 static void | |
976 tmp_close(void) | |
977 { | |
978 if (tmp_fd >= 0) { | |
979 if (0 < close(tmp_fd)) | |
980 dcc_error_msg("close(%s): %s", tmp_nm, ERROR_STR()); | |
981 tmp_fd = -1; | |
982 } | |
983 } | |
984 | |
985 | |
986 | |
987 /* copy some of the temporary file to the output */ | |
988 static void | |
989 scopy(int total_len, /* copy this much of temporary file */ | |
990 u_char complain) /* 1=ok to complain about problems */ | |
991 { | |
992 char buf[BUFSIZ]; | |
993 const char *estr; | |
994 int buf_len, data_len, i; | |
995 | |
996 if (tmp_fd < 0) | |
997 return; | |
998 | |
999 /* if the temporary file has not been rewound, | |
1000 * then rewind it now */ | |
1001 if (!tmp_rewound) { | |
1002 tmp_rewound = 1; | |
1003 if (0 > lseek(tmp_fd, 0, SEEK_SET)) { | |
1004 estr = ERROR_STR(); | |
1005 tmp_close(); | |
1006 if (complain) | |
1007 dcc_logbad(EX_IOERR, "rewind(%s): %s", | |
1008 tmp_nm, estr); | |
1009 } | |
1010 } | |
1011 | |
1012 while (total_len > 0) { | |
1013 buf_len = sizeof(buf); | |
1014 if (buf_len > total_len) { | |
1015 buf_len = total_len; | |
1016 } | |
1017 | |
1018 data_len = read(tmp_fd, buf, buf_len); | |
1019 if (data_len <= 0) { | |
1020 estr = ERROR_STR(); | |
1021 tmp_close(); | |
1022 if (data_len < 0) | |
1023 dcc_logbad(EX_IOERR, "read(%s, %d): %s", | |
1024 tmp_nm, data_len, estr); | |
1025 if (complain) | |
1026 dcc_logbad(EX_IOERR, "premature end of %s", | |
1027 tmp_nm); | |
1028 return; | |
1029 } | |
1030 | |
1031 log_body_write(buf, data_len); | |
1032 | |
1033 if (ofile && (!cksums_only && !x_dcc_only)) { | |
1034 i = fwrite(buf, 1, data_len, ofile); | |
1035 if (i != data_len) { | |
1036 estr = ERROR_STR(); | |
1037 fclose(ofile); | |
1038 ofile = 0; | |
1039 if (complain) | |
1040 dcc_logbad(EX_IOERR, | |
1041 "fwrite(scopy %s, %d)=%d:" | |
1042 " %s", | |
1043 ofile_nm, data_len, i, estr); | |
1044 tmp_close(); | |
1045 return; | |
1046 } | |
1047 } | |
1048 | |
1049 total_len -= data_len; | |
1050 } | |
1051 } | |
1052 | |
1053 | |
1054 | |
1055 static void | |
1056 log_write(const void *buf, int len) | |
1057 { | |
1058 int i; | |
1059 | |
1060 if (lfd < 0) | |
1061 return; | |
1062 | |
1063 i = write(lfd, buf, len); | |
1064 if (i == len) { | |
1065 logging = 2; | |
1066 log_size += len; | |
1067 } else { | |
1068 dcc_error_msg("write(log %s): %s", log_path, ERROR_STR()); | |
1069 dcc_log_close(0, log_path, lfd, &ldate); | |
1070 lfd = -1; | |
1071 logging = 0; | |
1072 log_path[0] = '\0'; | |
1073 } | |
1074 } | |
1075 | |
1076 | |
1077 | |
1078 static void | |
1079 log_body_write(const char *buf, u_int buflen) | |
1080 { | |
1081 int trimlen; | |
1082 const char *p, *lim; | |
1083 | |
1084 if (lfd < 0) | |
1085 return; | |
1086 | |
1087 /* just write if there is room */ | |
1088 trimlen = MAX_LOG_KBYTE*1024 - log_size; | |
1089 if (trimlen >= (int)buflen) { | |
1090 log_write(buf, buflen); | |
1091 return; | |
1092 } | |
1093 | |
1094 /* do nothing if too much already written */ | |
1095 if (trimlen < 0) | |
1096 return; | |
1097 | |
1098 /* look for and end-of-line near the end of the buffer | |
1099 * so that we can make the truncation pretty */ | |
1100 lim = buf; | |
1101 p = lim+trimlen; | |
1102 if (trimlen > 90) | |
1103 lim += trimlen-90; | |
1104 while (--p > lim) { | |
1105 if (*p == '\n') { | |
1106 trimlen = p-buf+1; | |
1107 break; | |
1108 } | |
1109 } | |
1110 log_write(buf, trimlen); | |
1111 if (buf[trimlen-1] != '\n') | |
1112 LOG_EOL(); | |
1113 LOG_CAPTION(DCC_LOG_TRN_MSG_CR); | |
1114 log_size = MAX_LOG_KBYTE*1024+1; | |
1115 } | |
1116 | |
1117 | |
1118 | |
1119 static void | |
1120 thr_log_write(void *context UATTRIB, const char *buf, u_int len) | |
1121 { | |
1122 log_write(buf, len); | |
1123 } | |
1124 | |
1125 | |
1126 | |
1127 /* does not append '\n' */ | |
1128 static int | |
1129 vlog_print(u_char error, const char *p, va_list args) | |
1130 { | |
1131 char logbuf[LOGBUF_SIZE]; | |
1132 int i; | |
1133 | |
1134 /* buffer the message if we cannot write to the log file */ | |
1135 if (error && (lfd < 0 || !tmp_rewound)) | |
1136 return dcc_vearly_log(&early_log, p, args); | |
1137 | |
1138 if (lfd < 0) | |
1139 return 0; | |
1140 i = vsnprintf(logbuf, sizeof(logbuf), p, args); | |
1141 if (i >= ISZ(logbuf)) | |
1142 i = sizeof(logbuf)-1; | |
1143 log_write(logbuf, i); | |
1144 return i; | |
1145 } | |
1146 | |
1147 | |
1148 | |
1149 static void | |
1150 log_late(void) | |
1151 { | |
1152 if (early_log.len) { | |
1153 log_write(early_log.buf, early_log.len); | |
1154 early_log.len = 0; | |
1155 } | |
1156 } | |
1157 | |
1158 | |
1159 | |
1160 /* does not append '\n' */ | |
1161 static int PATTRIB(2,3) | |
1162 log_print(u_char error, const char *p, ...) | |
1163 { | |
1164 va_list args; | |
1165 int i; | |
1166 | |
1167 va_start(args, p); | |
1168 i = vlog_print(error, p, args); | |
1169 va_end(args); | |
1170 return i; | |
1171 } | |
1172 | |
1173 | |
1174 | |
1175 /* does not append '\n' */ | |
1176 int | |
1177 thr_log_print(void *cp UATTRIB, u_char error, const char *p, ...) | |
1178 { | |
1179 va_list args; | |
1180 int i; | |
1181 | |
1182 va_start(args, p); | |
1183 i = vlog_print(error, p, args); | |
1184 va_end(args); | |
1185 return i; | |
1186 } | |
1187 | |
1188 | |
1189 | |
1190 static void | |
1191 log_fin(void) | |
1192 { | |
1193 if (log_path[0] == '\0') | |
1194 return; | |
1195 | |
1196 /* Close before renaming to accomodate WIN32 foolishness. | |
1197 * Assuming dcc_mkstemp() works properly, there is no race */ | |
1198 dcc_log_close(0, log_path, lfd, &ldate); | |
1199 lfd = -1; | |
1200 #ifndef DCC_WIN32 | |
1201 if (priv_logdir) | |
1202 dcc_get_priv_home(dcc_main_logdir); | |
1203 #endif | |
1204 if (!(ask_st & ASK_ST_LOGIT) | |
1205 || !dcc_log_keep(0, log_path)) { | |
1206 if (0 > unlink(log_path)) | |
1207 dccproc_error_msg("unlink(%s): %s", | |
1208 log_path, ERROR_STR()); | |
1209 log_path[0] = '\0'; | |
1210 } | |
1211 if (priv_logdir) | |
1212 dcc_rel_priv(); | |
1213 } | |
1214 | |
1215 | |
1216 | |
1217 static void | |
1218 log_ck(void *arg UATTRIB, const char *buf, u_int buf_len) | |
1219 { | |
1220 if (cksums_only && ofile) | |
1221 fputs(buf, ofile); | |
1222 log_write(buf, buf_len); | |
1223 } | |
1224 | |
1225 | |
1226 | |
1227 /* try to send error message to dccproc log file as well as sendmail */ | |
1228 static int | |
1229 dccproc_verror_msg(const char *p, va_list args) | |
1230 { | |
1231 char logbuf[LOGBUF_SIZE]; | |
1232 | |
1233 /* Some systems including Linux with gcc 3.4.2 on AMD 64 processors | |
1234 * do not allow two uses of a va_list but requires va_copy() | |
1235 * Other systems do not have any notion of va_copy(). */ | |
1236 if (vsnprintf(logbuf, sizeof(logbuf), p, args) >= ISZ(logbuf)) | |
1237 strcpy(&logbuf[ISZ(logbuf)-sizeof("...")], "..."); | |
1238 | |
1239 dcc_error_msg(logbuf); | |
1240 | |
1241 ask_st |= ASK_ST_LOGIT; | |
1242 return log_print(1, "%s\n", logbuf); | |
1243 } | |
1244 | |
1245 | |
1246 | |
1247 /* try to send error message to dccproc log file as well as sendmail */ | |
1248 static void PATTRIB(1,2) | |
1249 dccproc_error_msg(const char *p, ...) | |
1250 { | |
1251 va_list args; | |
1252 | |
1253 va_start(args, p); | |
1254 dccproc_verror_msg(p, args); | |
1255 va_end(args); | |
1256 } | |
1257 | |
1258 | |
1259 | |
1260 int | |
1261 thr_error_msg(void *cp UATTRIB, const char *p, ...) | |
1262 { | |
1263 va_list args; | |
1264 int i; | |
1265 | |
1266 va_start(args, p); | |
1267 i = dccproc_verror_msg(p, args); | |
1268 va_end(args); | |
1269 | |
1270 return i; | |
1271 } | |
1272 | |
1273 | |
1274 | |
1275 void | |
1276 thr_trace_msg(void *cp UATTRIB, const char *p, ...) | |
1277 { | |
1278 va_list args; | |
1279 | |
1280 va_start(args, p); | |
1281 dccproc_verror_msg(p, args); | |
1282 va_end(args); | |
1283 } | |
1284 | |
1285 | |
1286 | |
1287 /* things are so sick that we must bail out */ | |
1288 void NRATTRIB | |
1289 dcc_logbad(int ex_code, const char *p, ...) | |
1290 { | |
1291 char buf[BUFSIZ]; | |
1292 va_list args; | |
1293 size_t len; | |
1294 | |
1295 log_late(); | |
1296 if (*p >= ' ' && !tmp_rewound) { | |
1297 va_start(args, p); | |
1298 dcc_vfatal_msg(p, args); | |
1299 va_end(args); | |
1300 | |
1301 ask_st |= ASK_ST_LOGIT; | |
1302 if (logging > 1) | |
1303 log_write("\n", 1); | |
1304 va_start(args, p); | |
1305 vlog_print(0, p, args); | |
1306 va_end(args); | |
1307 log_write("\n\n", 2); | |
1308 p = 0; | |
1309 } | |
1310 | |
1311 /* copy first from the temporary file and then the input | |
1312 * to try to ensure that we don't lose mail */ | |
1313 scopy(INT_MAX, 0); | |
1314 if (ifile && ofile && !cksums_only && !x_dcc_only) { | |
1315 do { | |
1316 len = fread(buf, 1, sizeof(buf), ifile); | |
1317 if (!len) | |
1318 break; | |
1319 log_write(buf, len); | |
1320 } while (len == fwrite(buf, 1, len, ofile)); | |
1321 } | |
1322 | |
1323 if (p && *p >= ' ') { | |
1324 va_start(args, p); | |
1325 dcc_vfatal_msg(p, args); | |
1326 va_end(args); | |
1327 | |
1328 log_write("\n\n", 2); | |
1329 va_start(args, p); | |
1330 vlog_print(0,p, args); | |
1331 va_end(args); | |
1332 log_write("\n", 1); | |
1333 } | |
1334 log_fin(); | |
1335 | |
1336 if (ex_code == EX_SOFTWARE) | |
1337 abort(); | |
1338 exit(EX_OK); /* don't tell procmail to reject mail */ | |
1339 } | |
1340 | |
1341 | |
1342 | |
1343 /* watch for fatal signals */ | |
1344 static void NRATTRIB | |
1345 sigterm(int sig) | |
1346 { | |
1347 log_fin(); | |
1348 exit(-sig); | |
1349 } |