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