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
fatal_error(exc)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__
syserr(const char * fmt,...)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__
usrerr(const char * fmt,...)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__
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 /*
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__
message(const char * msg,...)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__
emessage(const char * replycode,const char * enhsc,const char * msg,...)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
extsc(msg,delim,replycode,enhsc)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__
nmessage(const char * msg,...)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
putoutmsg(msg,holdmsg,heldmsg)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
puterrmsg(msg)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
isenhsc(s,delim)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
extenhsc(s,delim,e)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 *
fmtmsg(eb,to,num,enhsc,eno,fmt,ap)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
buffer_errors()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
flush_errors(print)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 *
sm_errstring(errnum)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 }
1267