]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/err.c
Merge commit '850ef5ae11d69ea3381bd310f564f025fc8caea3'
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / err.c
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
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__
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__
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__
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__
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__
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
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__
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
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
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
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
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
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 *
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
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
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 *
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 }