]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/err.c
MFC: Merge sendmail 8.16.1 to HEAD: See contrib/sendmail/RELEASE_NOTES for
[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
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
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
228 #ifdef ESPIPE
229           case ESPIPE:
230 #endif
231 #ifdef EPIPE
232           case EPIPE:
233 #endif
234 #ifdef ENOBUFS
235           case ENOBUFS:
236 #endif
237 #ifdef ESTALE
238           case ESTALE:
239 #endif
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
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 /*
260 **  USRERR -- Signal user error.
261 **
262 **      This is much like syserr except it is for user errors.
263 **
264 **      Parameters:
265 **              fmt -- the format string.  If it does not begin with
266 **                      a three-digit SMTP reply code, 550 is assumed.
267 **              (others) -- sm_io_printf strings
268 **
269 **      Returns:
270 **              none
271 **              Raises E:mta.quickabort if QuickAbort is set.
272 **
273 **      Side Effects:
274 **              increments Errors.
275 */
276
277 /*VARARGS1*/
278 void
279 #ifdef __STDC__
280 usrerr(const char *fmt, ...)
281 #else /* __STDC__ */
282 usrerr(fmt, va_alist)
283         const char *fmt;
284         va_dcl
285 #endif /* __STDC__ */
286 {
287         char *enhsc;
288         char *errtxt;
289         SM_VA_LOCAL_DECL
290
291         if (fmt[0] == '5' || fmt[0] == '6')
292                 enhsc = "5.0.0";
293         else if (fmt[0] == '4' || fmt[0] == '8')
294                 enhsc = "4.0.0";
295         else if (fmt[0] == '2')
296                 enhsc = "2.0.0";
297         else
298                 enhsc = NULL;
299         SM_VA_START(ap, fmt);
300         errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
301         SM_VA_END(ap);
302
303         if (SuprErrs)
304                 return;
305
306         /* save this message for mailq printing */
307         switch (MsgBuf[0])
308         {
309           case '4':
310           case '8':
311                 if (CurEnv->e_message != NULL)
312                         break;
313
314                 /* FALLTHROUGH */
315
316           case '5':
317           case '6':
318                 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
319                         sm_free(CurEnv->e_message);
320                 if (MsgBuf[0] == '6')
321                 {
322                         char buf[MAXLINE];
323
324                         (void) sm_snprintf(buf, sizeof(buf),
325                                            "Postmaster warning: %.*s",
326                                            (int) sizeof(buf) - 22, errtxt);
327                         CurEnv->e_message =
328                                 sm_rpool_strdup_x(CurEnv->e_rpool, buf);
329                 }
330                 else
331                 {
332                         CurEnv->e_message =
333                                 sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
334                 }
335                 break;
336         }
337
338         puterrmsg(MsgBuf);
339         if (LogLevel > 3 && LogUsrErrs)
340                 sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
341         if (QuickAbort)
342                 sm_exc_raisenew_x(&EtypeQuickAbort, 1);
343 }
344
345 /*
346 **  USRERRENH -- Signal user error.
347 **
348 **      Same as usrerr but with enhanced status code.
349 **
350 **      Parameters:
351 **              enhsc -- the enhanced status code.
352 **              fmt -- the format string.  If it does not begin with
353 **                      a three-digit SMTP reply code, 550 is assumed.
354 **              (others) -- sm_io_printf strings
355 **
356 **      Returns:
357 **              none
358 **              Raises E:mta.quickabort if QuickAbort is set.
359 **
360 **      Side Effects:
361 **              increments Errors.
362 */
363
364 /*VARARGS2*/
365 void
366 #ifdef __STDC__
367 usrerrenh(char *enhsc, const char *fmt, ...)
368 #else /* __STDC__ */
369 usrerrenh(enhsc, fmt, va_alist)
370         char *enhsc;
371         const char *fmt;
372         va_dcl
373 #endif /* __STDC__ */
374 {
375         char *errtxt;
376         SM_VA_LOCAL_DECL
377
378         if (enhsc == NULL || *enhsc == '\0')
379         {
380                 if (fmt[0] == '5' || fmt[0] == '6')
381                         enhsc = "5.0.0";
382                 else if (fmt[0] == '4' || fmt[0] == '8')
383                         enhsc = "4.0.0";
384                 else if (fmt[0] == '2')
385                         enhsc = "2.0.0";
386         }
387         SM_VA_START(ap, fmt);
388         errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
389         SM_VA_END(ap);
390
391         if (SuprErrs)
392                 return;
393
394         /* save this message for mailq printing */
395         switch (MsgBuf[0])
396         {
397           case '4':
398           case '8':
399                 if (CurEnv->e_message != NULL)
400                         break;
401
402                 /* FALLTHROUGH */
403
404           case '5':
405           case '6':
406                 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
407                         sm_free(CurEnv->e_message);
408                 if (MsgBuf[0] == '6')
409                 {
410                         char buf[MAXLINE];
411
412                         (void) sm_snprintf(buf, sizeof(buf),
413                                            "Postmaster warning: %.*s",
414                                            (int) sizeof(buf) - 22, errtxt);
415                         CurEnv->e_message =
416                                 sm_rpool_strdup_x(CurEnv->e_rpool, buf);
417                 }
418                 else
419                 {
420                         CurEnv->e_message =
421                                 sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
422                 }
423                 break;
424         }
425
426         puterrmsg(MsgBuf);
427         if (LogLevel > 3 && LogUsrErrs)
428                 sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
429         if (QuickAbort)
430                 sm_exc_raisenew_x(&EtypeQuickAbort, 1);
431 }
432
433 /*
434 **  MESSAGE -- print message (not necessarily an error)
435 **
436 **      Parameters:
437 **              msg -- the message (sm_io_printf fmt) -- it can begin with
438 **                      an SMTP reply code.  If not, 050 is assumed.
439 **              (others) -- sm_io_printf arguments
440 **
441 **      Returns:
442 **              none
443 */
444
445 /*VARARGS1*/
446 void
447 #ifdef __STDC__
448 message(const char *msg, ...)
449 #else /* __STDC__ */
450 message(msg, va_alist)
451         const char *msg;
452         va_dcl
453 #endif /* __STDC__ */
454 {
455         char *errtxt;
456         SM_VA_LOCAL_DECL
457
458         errno = 0;
459         SM_VA_START(ap, msg);
460         errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
461         SM_VA_END(ap);
462         putoutmsg(MsgBuf, false, false);
463
464         /* save this message for mailq printing */
465         switch (MsgBuf[0])
466         {
467           case '4':
468           case '8':
469                 if (CurEnv->e_message != NULL)
470                         break;
471                 /* FALLTHROUGH */
472
473           case '5':
474                 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
475                         sm_free(CurEnv->e_message);
476                 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
477                 break;
478         }
479 }
480
481 #if _FFR_PROXY
482 /*
483 **  EMESSAGE -- print message (not necessarily an error)
484 **      (same as message() but requires reply code and enhanced status code)
485 **
486 **      Parameters:
487 **              replycode -- SMTP reply code.
488 **              enhsc -- enhanced status code.
489 **              msg -- the message (sm_io_printf fmt) -- it can begin with
490 **                      an SMTP reply code.  If not, 050 is assumed.
491 **              (others) -- sm_io_printf arguments
492 **
493 **      Returns:
494 **              none
495 */
496
497 /*VARARGS3*/
498 void
499 # ifdef __STDC__
500 emessage(const char *replycode, const char *enhsc, const char *msg, ...)
501 # else /* __STDC__ */
502 emessage(replycode, enhsc, msg, va_alist)
503         const char *replycode;
504         const char *enhsc;
505         const char *msg;
506         va_dcl
507 # endif /* __STDC__ */
508 {
509         char *errtxt;
510         SM_VA_LOCAL_DECL
511
512         errno = 0;
513         SM_VA_START(ap, msg);
514         errtxt = fmtmsg(MsgBuf, CurEnv->e_to, replycode, enhsc, 0, msg, ap);
515         SM_VA_END(ap);
516         putoutmsg(MsgBuf, false, false);
517
518         /* save this message for mailq printing */
519         switch (MsgBuf[0])
520         {
521           case '4':
522           case '8':
523                 if (CurEnv->e_message != NULL)
524                         break;
525                 /* FALLTHROUGH */
526
527           case '5':
528                 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
529                         sm_free(CurEnv->e_message);
530                 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
531                 break;
532         }
533 }
534
535 /*
536 **  EXTSC -- check and extract a status codes
537 **
538 **      Parameters:
539 **              msg -- string with possible enhanced status code.
540 **              delim -- delim for enhanced status code.
541 **              replycode -- pointer to storage for SMTP reply code;
542 **                      must be != NULL and have space for at least
543 **                      4 characters.
544 **              enhsc -- pointer to storage for enhanced status code;
545 **                      must be != NULL and have space for at least
546 **                      10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
547 **
548 **      Returns:
549 **              -1  -- no SMTP reply code.
550 **              >=3 -- offset of error text in msg.
551 **              (<=4  -- no enhanced status code)
552 */
553
554 int
555 extsc(msg, delim, replycode, enhsc)
556         const char *msg;
557         int delim;
558         char *replycode;
559         char *enhsc;
560 {
561         int offset;
562
563         SM_REQUIRE(replycode != NULL);
564         SM_REQUIRE(enhsc != NULL);
565         replycode[0] = '\0';
566         enhsc[0] = '\0';
567         if (msg == NULL)
568                 return -1;
569         if (!ISSMTPREPLY(msg))
570                 return -1;
571         sm_strlcpy(replycode, msg, 4);
572         if (msg[3] == '\0')
573                 return 3;
574         offset = 4;
575         if (isenhsc(msg + 4, delim))
576                 offset = extenhsc(msg + 4, delim, enhsc) + 4;
577         return offset;
578 }
579 #endif /* _FFR_PROXY */
580
581 /*
582 **  NMESSAGE -- print message (not necessarily an error)
583 **
584 **      Just like "message" except it never puts the to... tag on.
585 **
586 **      Parameters:
587 **              msg -- the message (sm_io_printf fmt) -- if it begins
588 **                      with a three digit SMTP reply code, that is used,
589 **                      otherwise 050 is assumed.
590 **              (others) -- sm_io_printf arguments
591 **
592 **      Returns:
593 **              none
594 */
595
596 /*VARARGS1*/
597 void
598 #ifdef __STDC__
599 nmessage(const char *msg, ...)
600 #else /* __STDC__ */
601 nmessage(msg, va_alist)
602         const char *msg;
603         va_dcl
604 #endif /* __STDC__ */
605 {
606         char *errtxt;
607         SM_VA_LOCAL_DECL
608
609         errno = 0;
610         SM_VA_START(ap, msg);
611         errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
612                         (char *) NULL, 0, msg, ap);
613         SM_VA_END(ap);
614         putoutmsg(MsgBuf, false, false);
615
616         /* save this message for mailq printing */
617         switch (MsgBuf[0])
618         {
619           case '4':
620           case '8':
621                 if (CurEnv->e_message != NULL)
622                         break;
623                 /* FALLTHROUGH */
624
625           case '5':
626                 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
627                         sm_free(CurEnv->e_message);
628                 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
629                 break;
630         }
631 }
632
633 /*
634 **  PUTOUTMSG -- output error message to transcript and channel
635 **
636 **      Parameters:
637 **              msg -- message to output (in SMTP format).
638 **              holdmsg -- if true, don't output a copy of the message to
639 **                      our output channel.
640 **              heldmsg -- if true, this is a previously held message;
641 **                      don't log it to the transcript file.
642 **
643 **      Returns:
644 **              none.
645 **
646 **      Side Effects:
647 **              Outputs msg to the transcript.
648 **              If appropriate, outputs it to the channel.
649 **              Deletes SMTP reply code number as appropriate.
650 */
651
652 static void
653 putoutmsg(msg, holdmsg, heldmsg)
654         char *msg;
655         bool holdmsg;
656         bool heldmsg;
657 {
658         char msgcode = msg[0];
659         char *errtxt = msg;
660         char *id;
661
662         /* display for debugging */
663         if (tTd(54, 8))
664                 sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
665                         heldmsg ? " (held)" : "");
666
667         /* map warnings to something SMTP can handle */
668         if (msgcode == '6')
669                 msg[0] = '5';
670         else if (msgcode == '8')
671                 msg[0] = '4';
672         id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
673
674         /* output to transcript if serious */
675         if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
676             strchr("45", msg[0]) != NULL)
677                 (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
678                                      msg);
679
680         if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
681                 sm_syslog(LOG_INFO, id,
682                           "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
683                           heldmsg ? " (held)" : "");
684
685         if (msgcode == '8')
686                 msg[0] = '0';
687
688         /* output to channel if appropriate */
689         if (!Verbose && msg[0] == '0')
690                 return;
691         if (holdmsg)
692         {
693                 /* save for possible future display */
694                 msg[0] = msgcode;
695                 if (HeldMessageBuf[0] == '5' && msgcode == '4')
696                         return;
697                 (void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
698                 return;
699         }
700
701         (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
702
703         if (OutChannel == NULL)
704                 return;
705
706         /* find actual text of error (after SMTP status codes) */
707         if (ISSMTPREPLY(errtxt))
708         {
709                 int l;
710
711                 errtxt += 4;
712                 l = isenhsc(errtxt, ' ');
713                 if (l <= 0)
714                         l = isenhsc(errtxt, '\0');
715                 if (l > 0)
716                         errtxt += l + 1;
717         }
718
719         /* if DisConnected, OutChannel now points to the transcript */
720         if (!DisConnected &&
721             (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
722                 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
723                                      msg);
724         else
725                 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
726                                      errtxt);
727         if (TrafficLogFile != NULL)
728                 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
729                                      "%05d >>> %s\n", (int) CurrentPid,
730                                      (OpMode == MD_SMTP || OpMode == MD_DAEMON)
731                                         ? msg : errtxt);
732 #if !PIPELINING
733         /* XXX can't flush here for SMTP pipelining */
734         if (msg[3] == ' ')
735                 (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
736         if (!sm_io_error(OutChannel) || DisConnected)
737                 return;
738
739         /*
740         **  Error on output -- if reporting lost channel, just ignore it.
741         **  Also, ignore errors from QUIT response (221 message) -- some
742         **      rude servers don't read result.
743         */
744
745         if (InChannel == NULL || sm_io_eof(InChannel) ||
746             sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
747                 return;
748
749         /* can't call syserr, 'cause we are using MsgBuf */
750         HoldErrs = true;
751         if (LogLevel > 0)
752                 sm_syslog(LOG_CRIT, id,
753                           "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
754                           CURHOSTNAME,
755                           shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
756 #endif /* !PIPELINING */
757 }
758
759 /*
760 **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
761 **
762 **      Parameters:
763 **              msg -- the message to output.
764 **
765 **      Returns:
766 **              none.
767 **
768 **      Side Effects:
769 **              Sets the fatal error bit in the envelope as appropriate.
770 */
771
772 static void
773 puterrmsg(msg)
774         char *msg;
775 {
776         char msgcode = msg[0];
777
778         /* output the message as usual */
779         putoutmsg(msg, HoldErrs, false);
780
781         /* be careful about multiple error messages */
782         if (OnlyOneError)
783                 HoldErrs = true;
784
785         /* signal the error */
786         Errors++;
787
788         if (CurEnv == NULL)
789                 return;
790
791         if (msgcode == '6')
792         {
793                 /* notify the postmaster */
794                 CurEnv->e_flags |= EF_PM_NOTIFY;
795         }
796         else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
797         {
798                 /* mark long-term fatal errors */
799                 CurEnv->e_flags |= EF_FATALERRS;
800         }
801 }
802
803 /*
804 **  ISENHSC -- check whether a string contains an enhanced status code
805 **
806 **      Parameters:
807 **              s -- string with possible enhanced status code.
808 **              delim -- delim for enhanced status code.
809 **
810 **      Returns:
811 **              0  -- no enhanced status code.
812 **              >4 -- length of enhanced status code.
813 */
814
815 int
816 isenhsc(s, delim)
817         const char *s;
818         int delim;
819 {
820         int l, h;
821
822         if (s == NULL)
823                 return 0;
824         if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
825                 return 0;
826         h = 0;
827         l = 2;
828         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
829                 ++h;
830         if (h == 0 || s[l + h] != '.')
831                 return 0;
832         l += h + 1;
833         h = 0;
834         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
835                 ++h;
836         if (h == 0 || s[l + h] != delim)
837                 return 0;
838         return l + h;
839 }
840
841 /*
842 **  EXTENHSC -- check and extract an enhanced status code
843 **
844 **      Parameters:
845 **              s -- string with possible enhanced status code.
846 **              delim -- delim for enhanced status code.
847 **              e -- pointer to storage for enhanced status code.
848 **                      must be != NULL and have space for at least
849 **                      10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
850 **
851 **      Returns:
852 **              0  -- no enhanced status code.
853 **              >4 -- length of enhanced status code.
854 **
855 **      Side Effects:
856 **              fills e with enhanced status code.
857 */
858
859 int
860 extenhsc(s, delim, e)
861         const char *s;
862         int delim;
863         char *e;
864 {
865         int l, h;
866
867         if (s == NULL)
868                 return 0;
869         if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
870                 return 0;
871         h = 0;
872         l = 2;
873         e[0] = s[0];
874         e[1] = '.';
875         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
876         {
877                 e[l + h] = s[l + h];
878                 ++h;
879         }
880         if (h == 0 || s[l + h] != '.')
881                 return 0;
882         e[l + h] = '.';
883         l += h + 1;
884         h = 0;
885         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
886         {
887                 e[l + h] = s[l + h];
888                 ++h;
889         }
890         if (h == 0 || s[l + h] != delim)
891                 return 0;
892         e[l + h] = '\0';
893         return l + h;
894 }
895
896 /*
897 **  FMTMSG -- format a message into buffer.
898 **
899 **      Parameters:
900 **              eb -- error buffer to get result -- MUST BE MsgBuf.
901 **              to -- the recipient tag for this message.
902 **              num -- default three digit SMTP reply code.
903 **              enhsc -- enhanced status code.
904 **              en -- the error number to display.
905 **              fmt -- format of string: See NOTE below.
906 **              ap -- arguments for fmt.
907 **
908 **      Returns:
909 **              pointer to error text beyond status codes.
910 **
911 **      NOTE:
912 **              Do NOT use "%s" as fmt if the argument starts with an SMTP
913 **              reply code!
914 */
915
916 static char *
917 fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
918         register char *eb;
919         const char *to;
920         const char *num;
921         const char *enhsc;
922         int eno;
923         const char *fmt;
924         SM_VA_LOCAL_DECL
925 {
926         char del;
927         int l;
928         int spaceleft = sizeof(MsgBuf);
929         char *errtxt;
930
931         /* output the reply code */
932         if (ISSMTPCODE(fmt))
933         {
934                 num = fmt;
935                 fmt += 4;
936         }
937         if (num[3] == '-')
938                 del = '-';
939         else
940                 del = ' ';
941         if (SoftBounce && num[0] == '5')
942         {
943                 /* replace 5 by 4 */
944                 (void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
945         }
946         else
947                 (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
948         eb += 4;
949         spaceleft -= 4;
950
951         if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
952         {
953                 /* copy enh.status code including trailing blank */
954                 l++;
955                 (void) sm_strlcpy(eb, fmt, l + 1);
956                 eb += l;
957                 spaceleft -= l;
958                 fmt += l;
959         }
960         else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
961         {
962                 /* copy enh.status code */
963                 (void) sm_strlcpy(eb, enhsc, l + 1);
964                 eb[l] = ' ';
965                 eb[++l] = '\0';
966                 eb += l;
967                 spaceleft -= l;
968         }
969         if (SoftBounce && eb[-l] == '5')
970         {
971                 /* replace 5 by 4 */
972                 eb[-l] = '4';
973         }
974         errtxt = eb;
975
976         /* output the file name and line number */
977         if (FileName != NULL)
978         {
979                 (void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
980                                    shortenstring(FileName, 83), LineNumber);
981                 eb += (l = strlen(eb));
982                 spaceleft -= l;
983         }
984
985         /*
986         **  output the "to" address only if it is defined and one of the
987         **  following codes is used:
988         **  050 internal notices, e.g., alias expansion
989         **  250 Ok
990         **  252 Cannot VRFY user, but will accept message and attempt delivery
991         **  450 Requested mail action not taken: mailbox unavailable
992         **  550 Requested action not taken: mailbox unavailable
993         **  553 Requested action not taken: mailbox name not allowed
994         **
995         **  Notice: this still isn't "the right thing", this code shouldn't
996         **      (indirectly) depend on CurEnv->e_to.
997         */
998
999         if (to != NULL && to[0] != '\0' &&
1000             (strncmp(num, "050", 3) == 0 ||
1001              strncmp(num, "250", 3) == 0 ||
1002              strncmp(num, "252", 3) == 0 ||
1003              strncmp(num, "450", 3) == 0 ||
1004              strncmp(num, "550", 3) == 0 ||
1005              strncmp(num, "553", 3) == 0))
1006         {
1007                 (void) sm_strlcpyn(eb, spaceleft, 2,
1008                                    shortenstring(to, MAXSHORTSTR), "... ");
1009                 spaceleft -= strlen(eb);
1010 #if _FFR_EAI
1011                 eb += strlen(eb);
1012 #else
1013                 while (*eb != '\0')
1014                         *eb++ &= 0177;
1015 #endif
1016         }
1017
1018         /* output the message */
1019         (void) sm_vsnprintf(eb, spaceleft, fmt, ap);
1020         spaceleft -= strlen(eb);
1021 #if _FFR_EAI
1022         eb += strlen(eb);
1023 #else
1024         while (*eb != '\0')
1025                 *eb++ &= 0177;
1026 #endif
1027
1028         /* output the error code, if any */
1029         if (eno != 0)
1030                 (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
1031
1032         return errtxt;
1033 }
1034
1035 /*
1036 **  BUFFER_ERRORS -- arrange to buffer future error messages
1037 **
1038 **      Parameters:
1039 **              none
1040 **
1041 **      Returns:
1042 **              none.
1043 */
1044
1045 void
1046 buffer_errors()
1047 {
1048         HeldMessageBuf[0] = '\0';
1049         HoldErrs = true;
1050 }
1051
1052 /*
1053 **  FLUSH_ERRORS -- flush the held error message buffer
1054 **
1055 **      Parameters:
1056 **              print -- if set, print the message, otherwise just
1057 **                      delete it.
1058 **
1059 **      Returns:
1060 **              none.
1061 */
1062
1063 void
1064 flush_errors(print)
1065         bool print;
1066 {
1067         if (print && HeldMessageBuf[0] != '\0')
1068                 putoutmsg(HeldMessageBuf, false, true);
1069         HeldMessageBuf[0] = '\0';
1070         HoldErrs = false;
1071 }
1072 /*
1073 **  SM_ERRSTRING -- return string description of error code
1074 **
1075 **      Parameters:
1076 **              errnum -- the error number to translate
1077 **
1078 **      Returns:
1079 **              A string description of errnum.
1080 */
1081
1082 const char *
1083 sm_errstring(errnum)
1084         int errnum;
1085 {
1086         char *dnsmsg;
1087         char *bp;
1088         static char buf[MAXLINE];
1089 #if HASSTRERROR
1090         char *err;
1091         char errbuf[30];
1092 #endif
1093 #if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
1094         extern char *sys_errlist[];
1095         extern int sys_nerr;
1096 #endif
1097
1098         /*
1099         **  Handle special network error codes.
1100         **
1101         **      These are 4.2/4.3bsd specific; they should be in daemon.c.
1102         */
1103
1104         dnsmsg = NULL;
1105         switch (errnum)
1106         {
1107           case ETIMEDOUT:
1108           case ECONNRESET:
1109                 bp = buf;
1110 #if HASSTRERROR
1111                 err = strerror(errnum);
1112                 if (err == NULL)
1113                 {
1114                         (void) sm_snprintf(errbuf, sizeof(errbuf),
1115                                            "Error %d", errnum);
1116                         err = errbuf;
1117                 }
1118                 (void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
1119 #else /* HASSTRERROR */
1120                 if (errnum >= 0 && errnum < sys_nerr)
1121                         (void) sm_strlcpy(bp, sys_errlist[errnum],
1122                                           SPACELEFT(buf, bp));
1123                 else
1124                         (void) sm_snprintf(bp, SPACELEFT(buf, bp),
1125                                 "Error %d", errnum);
1126 #endif /* HASSTRERROR */
1127                 bp += strlen(bp);
1128                 if (CurHostName != NULL)
1129                 {
1130                         if (errnum == ETIMEDOUT)
1131                         {
1132                                 (void) sm_snprintf(bp, SPACELEFT(buf, bp),
1133                                         " with ");
1134                                 bp += strlen(bp);
1135                         }
1136                         else
1137                         {
1138                                 bp = buf;
1139                                 (void) sm_snprintf(bp, SPACELEFT(buf, bp),
1140                                         "Connection reset by ");
1141                                 bp += strlen(bp);
1142                         }
1143                         (void) sm_strlcpy(bp,
1144                                         shortenstring(CurHostName, MAXSHORTSTR),
1145                                         SPACELEFT(buf, bp));
1146                         bp += strlen(buf);
1147                 }
1148                 if (SmtpPhase != NULL)
1149                 {
1150                         (void) sm_snprintf(bp, SPACELEFT(buf, bp),
1151                                 " during %s", SmtpPhase);
1152                 }
1153                 return buf;
1154
1155           case EHOSTDOWN:
1156                 if (CurHostName == NULL)
1157                         break;
1158                 (void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
1159                         shortenstring(CurHostName, MAXSHORTSTR));
1160                 return buf;
1161
1162           case ECONNREFUSED:
1163                 if (CurHostName == NULL)
1164                         break;
1165                 (void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
1166                         shortenstring(CurHostName, MAXSHORTSTR));
1167                 return buf;
1168
1169 #if NAMED_BIND
1170           case HOST_NOT_FOUND + E_DNSBASE:
1171                 dnsmsg = "host not found";
1172                 break;
1173
1174           case TRY_AGAIN + E_DNSBASE:
1175                 dnsmsg = "host name lookup failure";
1176                 break;
1177
1178           case NO_RECOVERY + E_DNSBASE:
1179                 dnsmsg = "non-recoverable error";
1180                 break;
1181
1182           case NO_DATA + E_DNSBASE:
1183                 dnsmsg = "no data known";
1184                 break;
1185 #endif /* NAMED_BIND */
1186
1187           case EPERM:
1188                 /* SunOS gives "Not owner" -- this is the POSIX message */
1189                 return "Operation not permitted";
1190
1191         /*
1192         **  Error messages used internally in sendmail.
1193         */
1194
1195           case E_SM_OPENTIMEOUT:
1196                 return "Timeout on file open";
1197
1198           case E_SM_NOSLINK:
1199                 return "Symbolic links not allowed";
1200
1201           case E_SM_NOHLINK:
1202                 return "Hard links not allowed";
1203
1204           case E_SM_REGONLY:
1205                 return "Regular files only";
1206
1207           case E_SM_ISEXEC:
1208                 return "Executable files not allowed";
1209
1210           case E_SM_WWDIR:
1211                 return "World writable directory";
1212
1213           case E_SM_GWDIR:
1214                 return "Group writable directory";
1215
1216           case E_SM_FILECHANGE:
1217                 return "File changed after open";
1218
1219           case E_SM_WWFILE:
1220                 return "World writable file";
1221
1222           case E_SM_GWFILE:
1223                 return "Group writable file";
1224
1225           case E_SM_GRFILE:
1226                 return "Group readable file";
1227
1228           case E_SM_WRFILE:
1229                 return "World readable file";
1230         }
1231
1232         if (dnsmsg != NULL)
1233         {
1234                 bp = buf;
1235                 bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
1236                 if (CurHostName != NULL)
1237                 {
1238                         (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
1239                                 shortenstring(CurHostName, MAXSHORTSTR), ": ");
1240                         bp += strlen(bp);
1241                 }
1242                 (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
1243                 return buf;
1244         }
1245
1246 #if LDAPMAP
1247         if (errnum >= E_LDAPBASE - E_LDAP_SHIM)
1248                 return ldap_err2string(errnum - E_LDAPBASE);
1249 #endif
1250
1251 #if HASSTRERROR
1252         err = strerror(errnum);
1253         if (err == NULL)
1254         {
1255                 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1256                 return buf;
1257         }
1258         return err;
1259 #else /* HASSTRERROR */
1260         if (errnum > 0 && errnum < sys_nerr)
1261                 return sys_errlist[errnum];
1262
1263         (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1264         return buf;
1265 #endif /* HASSTRERROR */
1266 }