1 /*
2 * Copyright (c) 1998-2003, 2010, 2015 Proofpoint, Inc. and its suppliers.
3 * All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14 #include <sendmail.h>
15
16 SM_RCSID("@(#)$Id: err.c,v 8.206 2013-11-22 20:51:55 ca Exp $")
17
18 #if LDAPMAP
19 # include <lber.h>
20 # include <ldap.h> /* for LDAP error codes */
21 #endif
22 #if _FFR_8BITENVADDR
23 # include <sm/sendmail.h>
24 #endif
25
26 static void putoutmsg __P((char *, bool, bool));
27 static void puterrmsg __P((char *));
28 static char *fmtmsg __P((char *, const char *, const char *, const char *,
29 int, const char *, va_list));
30
31 /*
32 ** FATAL_ERROR -- handle a fatal exception
33 **
34 ** This function is installed as the default exception handler
35 ** in the main sendmail process, and in all child processes
36 ** that we create. Its job is to handle exceptions that are not
37 ** handled at a lower level.
38 **
39 ** The theory is that unhandled exceptions will be 'fatal' class
40 ** exceptions (with an "F:" prefix), such as the out-of-memory
41 ** exception "F:sm.heap". As such, they are handled by exiting
42 ** the process in exactly the same way that xalloc() in Sendmail 8.10
43 ** exits the process when it fails due to lack of memory:
44 ** we call syserr with a message beginning with "!".
45 **
46 ** Parameters:
47 ** exc -- exception which is terminating this process
48 **
49 ** Returns:
50 ** none
51 */
52
53 void
fatal_error(exc)54 fatal_error(exc)
55 SM_EXC_T *exc;
56 {
57 static char buf[256];
58 SM_FILE_T f;
59
60 /*
61 ** This function may be called when the heap is exhausted.
62 ** The following code writes the message for 'exc' into our
63 ** static buffer without allocating memory or raising exceptions.
64 */
65
66 sm_strio_init(&f, buf, sizeof(buf));
67 sm_exc_write(exc, &f);
68 (void) sm_io_flush(&f, SM_TIME_DEFAULT);
69
70 /*
71 ** Terminate the process after logging an error and cleaning up.
72 ** Problems:
73 ** - syserr decides what class of error this is by looking at errno.
74 ** That's no good; we should look at the exc structure.
75 ** - The cleanup code should be moved out of syserr
76 ** and into individual exception handlers
77 ** that are part of the module they clean up after.
78 */
79
80 errno = ENOMEM;
81 syserr("!%s", buf);
82 }
83
84 /*
85 ** SYSERR -- Print error message.
86 **
87 ** Prints an error message via sm_io_printf to the diagnostic output.
88 **
89 ** If the first character of the syserr message is `!' it will
90 ** log this as an ALERT message and exit immediately. This can
91 ** leave queue files in an indeterminate state, so it should not
92 ** be used lightly.
93 **
94 ** If the first character of the syserr message is '!' or '@'
95 ** then syserr knows that the process is about to be terminated,
96 ** so the SMTP reply code defaults to 421. Otherwise, the
97 ** reply code defaults to 451 or 554, depending on errno.
98 **
99 ** Parameters:
100 ** fmt -- the format string. An optional '!', '@', or '+',
101 ** followed by an optional three-digit SMTP
102 ** reply code, followed by message text.
103 ** (others) -- parameters
104 **
105 ** Returns:
106 ** none
107 ** Raises E:mta.quickabort if QuickAbort is set.
108 **
109 ** Side Effects:
110 ** increments Errors.
111 ** sets ExitStat.
112 */
113
114 char MsgBuf[BUFSIZ*2]; /* text of most recent message */
115 static char HeldMessageBuf[sizeof(MsgBuf)]; /* for held messages */
116
117 void
118 /*VARARGS1*/
119 #ifdef __STDC__
syserr(const char * fmt,...)120 syserr(const char *fmt, ...)
121 #else /* __STDC__ */
122 syserr(fmt, va_alist)
123 const char *fmt;
124 va_dcl
125 #endif /* __STDC__ */
126 {
127 register char *p;
128 int save_errno = errno;
129 bool panic, exiting, keep;
130 char *user;
131 char *enhsc;
132 char *errtxt;
133 struct passwd *pw;
134 char ubuf[80];
135 SM_VA_LOCAL_DECL
136
137 panic = exiting = keep = false;
138 switch (*fmt)
139 {
140 case '!':
141 ++fmt;
142 panic = exiting = true;
143 break;
144 case '@':
145 ++fmt;
146 exiting = true;
147 break;
148 case '+':
149 ++fmt;
150 keep = true;
151 break;
152 default:
153 break;
154 }
155
156 /* format and output the error message */
157 if (exiting)
158 {
159 /*
160 ** Since we are terminating the process,
161 ** we are aborting the entire SMTP session,
162 ** rather than just the current transaction.
163 */
164
165 p = "421";
166 enhsc = "4.0.0";
167 }
168 else if (save_errno == 0)
169 {
170 p = "554";
171 enhsc = "5.0.0";
172 }
173 else
174 {
175 p = "451";
176 enhsc = "4.0.0";
177 }
178 SM_VA_START(ap, fmt);
179 errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
180 SM_VA_END(ap);
181 puterrmsg(MsgBuf);
182
183 /* save this message for mailq printing */
184 if (!panic && CurEnv != NULL && (!keep || CurEnv->e_message == NULL))
185 {
186 char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
187
188 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
189 sm_free(CurEnv->e_message);
190 CurEnv->e_message = nmsg;
191 }
192
193 /* determine exit status if not already set */
194 if (ExitStat == EX_OK)
195 {
196 if (save_errno == 0)
197 ExitStat = EX_SOFTWARE;
198 else
199 ExitStat = EX_OSERR;
200 if (tTd(54, 1))
201 sm_dprintf("syserr: ExitStat = %d\n", ExitStat);
202 }
203
204 pw = sm_getpwuid(RealUid);
205 if (pw != NULL)
206 user = pw->pw_name;
207 else
208 {
209 user = ubuf;
210 (void) sm_snprintf(ubuf, sizeof(ubuf), "UID%d", (int) RealUid);
211 }
212
213 if (LogLevel > 0)
214 sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
215 CurEnv == NULL ? NOQID : CurEnv->e_id,
216 "SYSERR(%s): %.900s",
217 user, errtxt);
218 switch (save_errno)
219 {
220 case EBADF:
221 case ENFILE:
222 case EMFILE:
223 case ENOTTY:
224 #ifdef EFBIG
225 case EFBIG:
226 #endif
227 #ifdef ESPIPE
228 case ESPIPE:
229 #endif
230 #ifdef EPIPE
231 case EPIPE:
232 #endif
233 #ifdef ENOBUFS
234 case ENOBUFS:
235 #endif
236 #ifdef ESTALE
237 case ESTALE:
238 #endif
239 printopenfds(true);
240 mci_dump_all(smioout, true);
241 break;
242 }
243 if (panic)
244 {
245 #if XLA
246 xla_all_end();
247 #endif
248 sync_queue_time();
249 if (tTd(0, 1))
250 abort();
251 exit(EX_OSERR);
252 }
253 errno = 0;
254 if (QuickAbort)
255 sm_exc_raisenew_x(&EtypeQuickAbort, 2);
256 }
257
258 /*
259 ** USRERR -- Signal user error.
260 **
261 ** This is much like syserr except it is for user errors.
262 **
263 ** Parameters:
264 ** fmt -- the format string. If it does not begin with
265 ** a three-digit SMTP reply code, 550 is assumed.
266 ** (others) -- sm_io_printf strings
267 **
268 ** Returns:
269 ** none
270 ** Raises E:mta.quickabort if QuickAbort is set.
271 **
272 ** Side Effects:
273 ** increments Errors.
274 */
275
276 /*VARARGS1*/
277 void
278 #ifdef __STDC__
usrerr(const char * fmt,...)279 usrerr(const char *fmt, ...)
280 #else /* __STDC__ */
281 usrerr(fmt, va_alist)
282 const char *fmt;
283 va_dcl
284 #endif /* __STDC__ */
285 {
286 char *enhsc;
287 char *errtxt;
288 SM_VA_LOCAL_DECL
289
290 if (fmt[0] == '5' || fmt[0] == '6')
291 enhsc = "5.0.0";
292 else if (fmt[0] == '4' || fmt[0] == '8')
293 enhsc = "4.0.0";
294 else if (fmt[0] == '2')
295 enhsc = "2.0.0";
296 else
297 enhsc = NULL;
298 SM_VA_START(ap, fmt);
299 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
300 SM_VA_END(ap);
301
302 if (SuprErrs)
303 return;
304
305 /* save this message for mailq printing */
306 switch (MsgBuf[0])
307 {
308 case '4':
309 case '8':
310 if (CurEnv->e_message != NULL)
311 break;
312
313 /* FALLTHROUGH */
314
315 case '5':
316 case '6':
317 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
318 sm_free(CurEnv->e_message);
319 if (MsgBuf[0] == '6')
320 {
321 char buf[MAXLINE];
322
323 (void) sm_snprintf(buf, sizeof(buf),
324 "Postmaster warning: %.*s",
325 (int) sizeof(buf) - 22, errtxt);
326 CurEnv->e_message =
327 sm_rpool_strdup_x(CurEnv->e_rpool, buf);
328 }
329 else
330 {
331 CurEnv->e_message =
332 sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
333 }
334 break;
335 }
336
337 puterrmsg(MsgBuf);
338 if (LogLevel > 3 && LogUsrErrs)
339 sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
340 if (QuickAbort)
341 sm_exc_raisenew_x(&EtypeQuickAbort, 1);
342 }
343
344 /*
345 ** USRERRENH -- Signal user error.
346 **
347 ** Same as usrerr but with enhanced status code.
348 **
349 ** Parameters:
350 ** enhsc -- the enhanced status code.
351 ** fmt -- the format string. If it does not begin with
352 ** a three-digit SMTP reply code, 550 is assumed.
353 ** (others) -- sm_io_printf strings
354 **
355 ** Returns:
356 ** none
357 ** Raises E:mta.quickabort if QuickAbort is set.
358 **
359 ** Side Effects:
360 ** increments Errors.
361 */
362
363 /*VARARGS2*/
364 void
365 #ifdef __STDC__
usrerrenh(char * enhsc,const char * fmt,...)366 usrerrenh(char *enhsc, const char *fmt, ...)
367 #else /* __STDC__ */
368 usrerrenh(enhsc, fmt, va_alist)
369 char *enhsc;
370 const char *fmt;
371 va_dcl
372 #endif /* __STDC__ */
373 {
374 char *errtxt;
375 SM_VA_LOCAL_DECL
376
377 if (SM_IS_EMPTY(enhsc))
378 {
379 if (fmt[0] == '5' || fmt[0] == '6')
380 enhsc = "5.0.0";
381 else if (fmt[0] == '4' || fmt[0] == '8')
382 enhsc = "4.0.0";
383 else if (fmt[0] == '2')
384 enhsc = "2.0.0";
385 }
386 SM_VA_START(ap, fmt);
387 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
388 SM_VA_END(ap);
389
390 if (SuprErrs)
391 return;
392
393 /* save this message for mailq printing */
394 switch (MsgBuf[0])
395 {
396 case '4':
397 case '8':
398 if (CurEnv->e_message != NULL)
399 break;
400
401 /* FALLTHROUGH */
402
403 case '5':
404 case '6':
405 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
406 sm_free(CurEnv->e_message);
407 if (MsgBuf[0] == '6')
408 {
409 char buf[MAXLINE];
410
411 (void) sm_snprintf(buf, sizeof(buf),
412 "Postmaster warning: %.*s",
413 (int) sizeof(buf) - 22, errtxt);
414 CurEnv->e_message =
415 sm_rpool_strdup_x(CurEnv->e_rpool, buf);
416 }
417 else
418 {
419 CurEnv->e_message =
420 sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
421 }
422 break;
423 }
424
425 puterrmsg(MsgBuf);
426 if (LogLevel > 3 && LogUsrErrs)
427 sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
428 if (QuickAbort)
429 sm_exc_raisenew_x(&EtypeQuickAbort, 1);
430 }
431
432 /*
433 ** MESSAGE -- print message (not necessarily an error)
434 **
435 ** Parameters:
436 ** msg -- the message (sm_io_printf fmt) -- it can begin with
437 ** an SMTP reply code. If not, 050 is assumed.
438 ** (others) -- sm_io_printf arguments
439 **
440 ** Returns:
441 ** none
442 */
443
444 /*VARARGS1*/
445 void
446 #ifdef __STDC__
message(const char * msg,...)447 message(const char *msg, ...)
448 #else /* __STDC__ */
449 message(msg, va_alist)
450 const char *msg;
451 va_dcl
452 #endif /* __STDC__ */
453 {
454 char *errtxt;
455 SM_VA_LOCAL_DECL
456
457 errno = 0;
458 SM_VA_START(ap, msg);
459 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
460 SM_VA_END(ap);
461 putoutmsg(MsgBuf, false, false);
462
463 /* save this message for mailq printing */
464 switch (MsgBuf[0])
465 {
466 case '4':
467 case '8':
468 if (CurEnv->e_message != NULL)
469 break;
470 /* FALLTHROUGH */
471
472 case '5':
473 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
474 sm_free(CurEnv->e_message);
475 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
476 break;
477 }
478 }
479
480 #if _FFR_PROXY
481 /*
482 ** EMESSAGE -- print message (not necessarily an error)
483 ** (same as message() but requires reply code and enhanced status code)
484 **
485 ** Parameters:
486 ** replycode -- SMTP reply code.
487 ** enhsc -- enhanced status code.
488 ** msg -- the message (sm_io_printf fmt) -- it can begin with
489 ** an SMTP reply code. If not, 050 is assumed.
490 ** (others) -- sm_io_printf arguments
491 **
492 ** Returns:
493 ** none
494 */
495
496 /*VARARGS3*/
497 void
498 # ifdef __STDC__
emessage(const char * replycode,const char * enhsc,const char * msg,...)499 emessage(const char *replycode, const char *enhsc, const char *msg, ...)
500 # else /* __STDC__ */
501 emessage(replycode, enhsc, msg, va_alist)
502 const char *replycode;
503 const char *enhsc;
504 const char *msg;
505 va_dcl
506 # endif /* __STDC__ */
507 {
508 char *errtxt;
509 SM_VA_LOCAL_DECL
510
511 errno = 0;
512 SM_VA_START(ap, msg);
513 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, replycode, enhsc, 0, msg, ap);
514 SM_VA_END(ap);
515 putoutmsg(MsgBuf, false, false);
516
517 /* save this message for mailq printing */
518 switch (MsgBuf[0])
519 {
520 case '4':
521 case '8':
522 if (CurEnv->e_message != NULL)
523 break;
524 /* FALLTHROUGH */
525
526 case '5':
527 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
528 sm_free(CurEnv->e_message);
529 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
530 break;
531 }
532 }
533
534 /*
535 ** EXTSC -- check and extract a status codes
536 **
537 ** Parameters:
538 ** msg -- string with possible enhanced status code.
539 ** delim -- delim for enhanced status code.
540 ** replycode -- pointer to storage for SMTP reply code;
541 ** must be != NULL and have space for at least
542 ** 4 characters.
543 ** enhsc -- pointer to storage for enhanced status code;
544 ** must be != NULL and have space for at least
545 ** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
546 **
547 ** Returns:
548 ** -1 -- no SMTP reply code.
549 ** >=3 -- offset of error text in msg.
550 ** (<=4 -- no enhanced status code)
551 */
552
553 int
extsc(msg,delim,replycode,enhsc)554 extsc(msg, delim, replycode, enhsc)
555 const char *msg;
556 int delim;
557 char *replycode;
558 char *enhsc;
559 {
560 int offset;
561
562 SM_REQUIRE(replycode != NULL);
563 SM_REQUIRE(enhsc != NULL);
564 replycode[0] = '\0';
565 enhsc[0] = '\0';
566 if (msg == NULL)
567 return -1;
568 if (!ISSMTPREPLY(msg))
569 return -1;
570 sm_strlcpy(replycode, msg, 4);
571 if (msg[3] == '\0')
572 return 3;
573 offset = 4;
574 if (isenhsc(msg + 4, delim))
575 offset = extenhsc(msg + 4, delim, enhsc) + 4;
576 return offset;
577 }
578 #endif /* _FFR_PROXY */
579
580 /*
581 ** NMESSAGE -- print message (not necessarily an error)
582 **
583 ** Just like "message" except it never puts the to... tag on.
584 **
585 ** Parameters:
586 ** msg -- the message (sm_io_printf fmt) -- if it begins
587 ** with a three digit SMTP reply code, that is used,
588 ** otherwise 050 is assumed.
589 ** (others) -- sm_io_printf arguments
590 **
591 ** Returns:
592 ** none
593 */
594
595 /*VARARGS1*/
596 void
597 #ifdef __STDC__
nmessage(const char * msg,...)598 nmessage(const char *msg, ...)
599 #else /* __STDC__ */
600 nmessage(msg, va_alist)
601 const char *msg;
602 va_dcl
603 #endif /* __STDC__ */
604 {
605 char *errtxt;
606 SM_VA_LOCAL_DECL
607
608 errno = 0;
609 SM_VA_START(ap, msg);
610 errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
611 (char *) NULL, 0, msg, ap);
612 SM_VA_END(ap);
613 putoutmsg(MsgBuf, false, false);
614
615 /* save this message for mailq printing */
616 switch (MsgBuf[0])
617 {
618 case '4':
619 case '8':
620 if (CurEnv->e_message != NULL)
621 break;
622 /* FALLTHROUGH */
623
624 case '5':
625 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
626 sm_free(CurEnv->e_message);
627 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
628 break;
629 }
630 }
631
632 /*
633 ** PUTOUTMSG -- output error message to transcript and channel
634 **
635 ** Parameters:
636 ** msg -- message to output (in SMTP format).
637 ** holdmsg -- if true, don't output a copy of the message to
638 ** our output channel.
639 ** heldmsg -- if true, this is a previously held message;
640 ** don't log it to the transcript file.
641 **
642 ** Returns:
643 ** none.
644 **
645 ** Side Effects:
646 ** Outputs msg to the transcript.
647 ** If appropriate, outputs it to the channel.
648 ** Deletes SMTP reply code number as appropriate.
649 */
650
651 static void
putoutmsg(msg,holdmsg,heldmsg)652 putoutmsg(msg, holdmsg, heldmsg)
653 char *msg;
654 bool holdmsg;
655 bool heldmsg;
656 {
657 char msgcode = msg[0];
658 char *errtxt = msg;
659 char *id;
660
661 /* display for debugging */
662 if (tTd(54, 8))
663 sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
664 heldmsg ? " (held)" : "");
665
666 /* map warnings to something SMTP can handle */
667 if (msgcode == '6')
668 msg[0] = '5';
669 else if (msgcode == '8')
670 msg[0] = '4';
671 id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
672
673 /* output to transcript if serious */
674 if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
675 strchr("45", msg[0]) != NULL)
676 (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
677 msg);
678
679 if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
680 sm_syslog(LOG_INFO, id,
681 "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
682 heldmsg ? " (held)" : "");
683
684 if (msgcode == '8')
685 msg[0] = '0';
686
687 /* output to channel if appropriate */
688 if (!Verbose && msg[0] == '0')
689 return;
690 if (holdmsg)
691 {
692 /* save for possible future display */
693 msg[0] = msgcode;
694 if (HeldMessageBuf[0] == '5' && msgcode == '4')
695 return;
696 (void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
697 return;
698 }
699
700 (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
701
702 if (OutChannel == NULL)
703 return;
704
705 /* find actual text of error (after SMTP status codes) */
706 if (ISSMTPREPLY(errtxt))
707 {
708 int l;
709
710 errtxt += 4;
711 l = isenhsc(errtxt, ' ');
712 if (l <= 0)
713 l = isenhsc(errtxt, '\0');
714 if (l > 0)
715 errtxt += l + 1;
716 }
717
718 /* if DisConnected, OutChannel now points to the transcript */
719 if (!DisConnected &&
720 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
721 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
722 msg);
723 else
724 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
725 errtxt);
726 if (TrafficLogFile != NULL)
727 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
728 "%05d >>> %s\n", (int) CurrentPid,
729 (OpMode == MD_SMTP || OpMode == MD_DAEMON)
730 ? msg : errtxt);
731 #if !PIPELINING
732 /*
733 ** Note: in case of an SMTP reply this should check
734 ** that the last line of msg is not a continuation line
735 ** but that's probably not worth the effort.
736 */
737
738 if (ISSMTPREPLY(msg))
739 (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
740 if (!sm_io_error(OutChannel) || DisConnected)
741 return;
742
743 /*
744 ** Error on output -- if reporting lost channel, just ignore it.
745 ** Also, ignore errors from QUIT response (221 message) -- some
746 ** rude servers don't read result.
747 */
748
749 if (InChannel == NULL || sm_io_eof(InChannel) ||
750 sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
751 return;
752
753 /* can't call syserr, 'cause we are using MsgBuf */
754 HoldErrs = true;
755 if (LogLevel > 0)
756 sm_syslog(LOG_CRIT, id,
757 "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
758 CURHOSTNAME,
759 shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
760 #endif /* !PIPELINING */
761 }
762
763 /*
764 ** PUTERRMSG -- like putoutmsg, but does special processing for error messages
765 **
766 ** Parameters:
767 ** msg -- the message to output.
768 **
769 ** Returns:
770 ** none.
771 **
772 ** Side Effects:
773 ** Sets the fatal error bit in the envelope as appropriate.
774 */
775
776 static void
puterrmsg(msg)777 puterrmsg(msg)
778 char *msg;
779 {
780 char msgcode = msg[0];
781
782 /* output the message as usual */
783 putoutmsg(msg, HoldErrs, false);
784
785 /* be careful about multiple error messages */
786 if (OnlyOneError)
787 HoldErrs = true;
788
789 /* signal the error */
790 Errors++;
791
792 if (CurEnv == NULL)
793 return;
794
795 if (msgcode == '6')
796 {
797 /* notify the postmaster */
798 CurEnv->e_flags |= EF_PM_NOTIFY;
799 }
800 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
801 {
802 /* mark long-term fatal errors */
803 CurEnv->e_flags |= EF_FATALERRS;
804 }
805 }
806
807 /*
808 ** ISENHSC -- check whether a string contains an enhanced status code
809 **
810 ** Parameters:
811 ** s -- string with possible enhanced status code.
812 ** delim -- delim for enhanced status code.
813 **
814 ** Returns:
815 ** 0 -- no enhanced status code.
816 ** >4 -- length of enhanced status code.
817 */
818
819 int
isenhsc(s,delim)820 isenhsc(s, delim)
821 const char *s;
822 int delim;
823 {
824 int l, h;
825
826 if (s == NULL)
827 return 0;
828 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
829 return 0;
830 h = 0;
831 l = 2;
832 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
833 ++h;
834 if (h == 0 || s[l + h] != '.')
835 return 0;
836 l += h + 1;
837 h = 0;
838 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
839 ++h;
840 if (h == 0 || s[l + h] != delim)
841 return 0;
842 return l + h;
843 }
844
845 /*
846 ** EXTENHSC -- check and extract an enhanced status code
847 **
848 ** Parameters:
849 ** s -- string with possible enhanced status code.
850 ** delim -- delim for enhanced status code.
851 ** e -- pointer to storage for enhanced status code.
852 ** must be != NULL and have space for at least
853 ** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
854 **
855 ** Returns:
856 ** 0 -- no enhanced status code.
857 ** >4 -- length of enhanced status code.
858 **
859 ** Side Effects:
860 ** fills e with enhanced status code.
861 */
862
863 int
extenhsc(s,delim,e)864 extenhsc(s, delim, e)
865 const char *s;
866 int delim;
867 char *e;
868 {
869 int l, h;
870
871 if (s == NULL)
872 return 0;
873 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
874 return 0;
875 h = 0;
876 l = 2;
877 e[0] = s[0];
878 e[1] = '.';
879 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
880 {
881 e[l + h] = s[l + h];
882 ++h;
883 }
884 if (h == 0 || s[l + h] != '.')
885 return 0;
886 e[l + h] = '.';
887 l += h + 1;
888 h = 0;
889 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
890 {
891 e[l + h] = s[l + h];
892 ++h;
893 }
894 if (h == 0 || s[l + h] != delim)
895 return 0;
896 e[l + h] = '\0';
897 return l + h;
898 }
899
900 #if USE_EAI
901 /*
902 ** SKIPADDRHOST -- skip address and host in a message
903 **
904 ** Parameters:
905 ** s -- string with possible address and host
906 ** skiphost -- skip also host?
907 **
908 ** Returns:
909 ** 0 -- no address and host
910 ** >0 -- position after address (and host)
911 */
912
913 int
skipaddrhost(s,skiphost)914 skipaddrhost(s, skiphost)
915 const char *s;
916 bool skiphost;
917 {
918 char *str;
919 size_t len;
920
921 #define SM_ADDR_DELIM "... "
922 if (s == NULL)
923 return 0;
924 str = strstr(s, SM_ADDR_DELIM);
925 if (str == NULL)
926 return 0;
927 str += sizeof(SM_ADDR_DELIM) + 1;
928 len = strlen(s);
929 if (str >= s + len)
930 return 0;
931 if (!skiphost)
932 return str - s + 1;
933
934 str = strchr(str, ' ');
935 if (str >= s + len)
936 return 0;
937 return str - s + 1;
938 }
939 #endif /* USE_EAI */
940
941 /*
942 ** FMTMSG -- format a message into buffer.
943 **
944 ** Parameters:
945 ** eb -- error buffer to get result -- MUST BE MsgBuf.
946 ** to -- the recipient tag for this message.
947 ** num -- default three digit SMTP reply code.
948 ** enhsc -- enhanced status code.
949 ** en -- the error number to display.
950 ** fmt -- format of string: See NOTE below.
951 ** ap -- arguments for fmt.
952 **
953 ** Returns:
954 ** pointer to error text beyond status codes.
955 **
956 ** NOTE:
957 ** Do NOT use "%s" as fmt if the argument starts with an SMTP
958 ** reply code!
959 */
960
961 static char *
fmtmsg(eb,to,num,enhsc,eno,fmt,ap)962 fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
963 register char *eb;
964 const char *to;
965 const char *num;
966 const char *enhsc;
967 int eno;
968 const char *fmt;
969 va_list ap;
970 {
971 char del;
972 int l;
973 int spaceleft = sizeof(MsgBuf);
974 char *errtxt;
975
976 /* output the reply code */
977 if (ISSMTPCODE(fmt))
978 {
979 num = fmt;
980 fmt += 4;
981 }
982 if (num[3] == '-')
983 del = '-';
984 else
985 del = ' ';
986 if (SoftBounce && num[0] == '5')
987 {
988 /* replace 5 by 4 */
989 (void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
990 }
991 else
992 (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
993 eb += 4;
994 spaceleft -= 4;
995
996 if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
997 {
998 /* copy enh.status code including trailing blank */
999 l++;
1000 (void) sm_strlcpy(eb, fmt, l + 1);
1001 eb += l;
1002 spaceleft -= l;
1003 fmt += l;
1004 }
1005 else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
1006 {
1007 /* copy enh.status code */
1008 (void) sm_strlcpy(eb, enhsc, l + 1);
1009 eb[l] = ' ';
1010 eb[++l] = '\0';
1011 eb += l;
1012 spaceleft -= l;
1013 }
1014 if (SoftBounce && eb[-l] == '5')
1015 {
1016 /* replace 5 by 4 */
1017 eb[-l] = '4';
1018 }
1019 errtxt = eb;
1020
1021 /* output the file name and line number */
1022 if (FileName != NULL)
1023 {
1024 (void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
1025 shortenstring(FileName, 83), LineNumber);
1026 eb += (l = strlen(eb));
1027 spaceleft -= l;
1028 }
1029
1030 /*
1031 ** output the "to" address only if it is defined and one of the
1032 ** following codes is used:
1033 ** 050 internal notices, e.g., alias expansion
1034 ** 250 Ok
1035 ** 252 Cannot VRFY user, but will accept message and attempt delivery
1036 ** 450 Requested mail action not taken: mailbox unavailable
1037 ** 550 Requested action not taken: mailbox unavailable
1038 ** 553 Requested action not taken: mailbox name not allowed
1039 **
1040 ** Notice: this still isn't "the right thing", this code shouldn't
1041 ** (indirectly) depend on CurEnv->e_to.
1042 */
1043
1044 if (to != NULL && to[0] != '\0' &&
1045 (strncmp(num, "050", 3) == 0 ||
1046 strncmp(num, "250", 3) == 0 ||
1047 strncmp(num, "252", 3) == 0 ||
1048 strncmp(num, "450", 3) == 0 ||
1049 strncmp(num, "550", 3) == 0 ||
1050 strncmp(num, "553", 3) == 0))
1051 {
1052 #if _FFR_8BITENVADDR
1053 char xbuf[MAXNAME + 1]; /* EAI:ok */
1054 int len;
1055
1056 len = sizeof(xbuf);
1057 (void) sm_strlcpy(xbuf, to, len);
1058 (void) dequote_internal_chars(xbuf, xbuf, len);
1059 (void) sm_strlcpyn(eb, spaceleft, 2,
1060 shortenstring(xbuf, MAXSHORTSTR), "... ");
1061 eb += strlen(eb);
1062 #else /* _FFR_8BITENVADDR */
1063 (void) sm_strlcpyn(eb, spaceleft, 2,
1064 shortenstring(to, MAXSHORTSTR), "... ");
1065 while (*eb != '\0')
1066 *eb++ &= 0177;
1067 #endif /* _FFR_8BITENVADDR */
1068 spaceleft -= strlen(eb);
1069 }
1070
1071 /* output the message */
1072 (void) sm_vsnprintf(eb, spaceleft, fmt, ap);
1073 spaceleft -= strlen(eb);
1074 #if USE_EAI
1075 eb += strlen(eb);
1076 #else
1077 while (*eb != '\0')
1078 *eb++ &= 0177;
1079 #endif
1080
1081 /* output the error code, if any */
1082 if (eno != 0)
1083 (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
1084
1085 return errtxt;
1086 }
1087
1088 /*
1089 ** BUFFER_ERRORS -- arrange to buffer future error messages
1090 **
1091 ** Parameters:
1092 ** none
1093 **
1094 ** Returns:
1095 ** none.
1096 */
1097
1098 void
buffer_errors()1099 buffer_errors()
1100 {
1101 HeldMessageBuf[0] = '\0';
1102 HoldErrs = true;
1103 }
1104
1105 /*
1106 ** FLUSH_ERRORS -- flush the held error message buffer
1107 **
1108 ** Parameters:
1109 ** print -- if set, print the message, otherwise just
1110 ** delete it.
1111 **
1112 ** Returns:
1113 ** none.
1114 */
1115
1116 void
flush_errors(print)1117 flush_errors(print)
1118 bool print;
1119 {
1120 if (print && HeldMessageBuf[0] != '\0')
1121 putoutmsg(HeldMessageBuf, false, true);
1122 HeldMessageBuf[0] = '\0';
1123 HoldErrs = false;
1124 }
1125 /*
1126 ** SM_ERRSTRING -- return string description of error code
1127 **
1128 ** Parameters:
1129 ** errnum -- the error number to translate
1130 **
1131 ** Returns:
1132 ** A string description of errnum.
1133 */
1134
1135 const char *
sm_errstring(errnum)1136 sm_errstring(errnum)
1137 int errnum;
1138 {
1139 char *dnsmsg;
1140 char *bp;
1141 static char buf[MAXLINE];
1142 #if HASSTRERROR
1143 char *err;
1144 char errbuf[30];
1145 #endif
1146 #if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
1147 extern char *sys_errlist[];
1148 extern int sys_nerr;
1149 #endif
1150
1151 /*
1152 ** Handle special network error codes.
1153 **
1154 ** These are 4.2/4.3bsd specific; they should be in daemon.c.
1155 */
1156
1157 dnsmsg = NULL;
1158 switch (errnum)
1159 {
1160 case ETIMEDOUT:
1161 case ECONNRESET:
1162 bp = buf;
1163 #if HASSTRERROR
1164 err = strerror(errnum);
1165 if (err == NULL)
1166 {
1167 (void) sm_snprintf(errbuf, sizeof(errbuf),
1168 "Error %d", errnum);
1169 err = errbuf;
1170 }
1171 (void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
1172 #else /* HASSTRERROR */
1173 if (errnum >= 0 && errnum < sys_nerr)
1174 (void) sm_strlcpy(bp, sys_errlist[errnum],
1175 SPACELEFT(buf, bp));
1176 else
1177 (void) sm_snprintf(bp, SPACELEFT(buf, bp),
1178 "Error %d", errnum);
1179 #endif /* HASSTRERROR */
1180 bp += strlen(bp);
1181 if (CurHostName != NULL)
1182 {
1183 if (errnum == ETIMEDOUT)
1184 {
1185 (void) sm_snprintf(bp, SPACELEFT(buf, bp),
1186 " with ");
1187 bp += strlen(bp);
1188 }
1189 else
1190 {
1191 bp = buf;
1192 (void) sm_snprintf(bp, SPACELEFT(buf, bp),
1193 "Connection reset by ");
1194 bp += strlen(bp);
1195 }
1196 (void) sm_strlcpy(bp,
1197 shortenstring(CurHostName, MAXSHORTSTR),
1198 SPACELEFT(buf, bp));
1199 bp += strlen(buf);
1200 }
1201 if (SmtpPhase != NULL)
1202 {
1203 (void) sm_snprintf(bp, SPACELEFT(buf, bp),
1204 " during %s", SmtpPhase);
1205 }
1206 return buf;
1207
1208 case EHOSTDOWN:
1209 if (CurHostName == NULL)
1210 break;
1211 (void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
1212 shortenstring(CurHostName, MAXSHORTSTR));
1213 return buf;
1214
1215 case ECONNREFUSED:
1216 if (CurHostName == NULL)
1217 break;
1218 (void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
1219 shortenstring(CurHostName, MAXSHORTSTR));
1220 return buf;
1221
1222 #if NAMED_BIND
1223 case HOST_NOT_FOUND + E_DNSBASE:
1224 dnsmsg = "host not found";
1225 break;
1226
1227 case TRY_AGAIN + E_DNSBASE:
1228 dnsmsg = "host name lookup failure";
1229 break;
1230
1231 case NO_RECOVERY + E_DNSBASE:
1232 dnsmsg = "non-recoverable error";
1233 break;
1234
1235 case NO_DATA + E_DNSBASE:
1236 dnsmsg = "no data known";
1237 break;
1238 #endif /* NAMED_BIND */
1239
1240 case EPERM:
1241 /* SunOS gives "Not owner" -- this is the POSIX message */
1242 return "Operation not permitted";
1243
1244 /*
1245 ** Error messages used internally in sendmail.
1246 */
1247
1248 case E_SM_OPENTIMEOUT:
1249 return "Timeout on file open";
1250
1251 case E_SM_NOSLINK:
1252 return "Symbolic links not allowed";
1253
1254 case E_SM_NOHLINK:
1255 return "Hard links not allowed";
1256
1257 case E_SM_REGONLY:
1258 return "Regular files only";
1259
1260 case E_SM_ISEXEC:
1261 return "Executable files not allowed";
1262
1263 case E_SM_WWDIR:
1264 return "World writable directory";
1265
1266 case E_SM_GWDIR:
1267 return "Group writable directory";
1268
1269 case E_SM_FILECHANGE:
1270 return "File changed after open";
1271
1272 case E_SM_WWFILE:
1273 return "World writable file";
1274
1275 case E_SM_GWFILE:
1276 return "Group writable file";
1277
1278 case E_SM_GRFILE:
1279 return "Group readable file";
1280
1281 case E_SM_WRFILE:
1282 return "World readable file";
1283 }
1284
1285 if (dnsmsg != NULL)
1286 {
1287 bp = buf;
1288 bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
1289 if (CurHostName != NULL)
1290 {
1291 (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
1292 shortenstring(CurHostName, MAXSHORTSTR), ": ");
1293 bp += strlen(bp);
1294 }
1295 (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
1296 return buf;
1297 }
1298
1299 #if LDAPMAP
1300 if (errnum >= E_LDAPBASE - E_LDAP_SHIM)
1301 return ldap_err2string(errnum - E_LDAPBASE);
1302 #endif
1303
1304 #if HASSTRERROR
1305 err = strerror(errnum);
1306 if (err == NULL)
1307 {
1308 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1309 return buf;
1310 }
1311 return err;
1312 #else /* HASSTRERROR */
1313 if (errnum > 0 && errnum < sys_nerr)
1314 return sys_errlist[errnum];
1315
1316 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1317 return buf;
1318 #endif /* HASSTRERROR */
1319 }
1320