0
|
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 } |