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