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