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