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