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