xref: /freebsd/contrib/sendmail/src/err.c (revision c0c4794d)
1 /*
2  * Copyright (c) 1998-2001 Sendmail, 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 #ifndef lint
15 static char id[] = "@(#)$Id: err.c,v 8.120.4.2 2001/05/03 17:24:06 gshapiro Exp $";
16 #endif /* ! lint */
17 
18 /* $FreeBSD$ */
19 
20 #include <sendmail.h>
21 #ifdef LDAPMAP
22 # include <lber.h>
23 # include <ldap.h>			/* for LDAP error codes */
24 #endif /* LDAPMAP */
25 
26 
27 static void	putoutmsg __P((char *, bool, bool));
28 static void	puterrmsg __P((char *));
29 static char	*fmtmsg __P((char *, const char *, const char *, const char *,
30 			     int, const char *, va_list));
31 
32 /*
33 **  SYSERR -- Print error message.
34 **
35 **	Prints an error message via printf to the diagnostic output.
36 **
37 **	If the first character of the syserr message is `!' it will
38 **	log this as an ALERT message and exit immediately.  This can
39 **	leave queue files in an indeterminate state, so it should not
40 **	be used lightly.
41 **
42 **	Parameters:
43 **		fmt -- the format string.  If it does not begin with
44 **			a three-digit SMTP reply code, either 554 or
45 **			451 is assumed depending on whether errno
46 **			is set.
47 **		(others) -- parameters
48 **
49 **	Returns:
50 **		none
51 **		Through TopFrame if QuickAbort is set.
52 **
53 **	Side Effects:
54 **		increments Errors.
55 **		sets ExitStat.
56 */
57 
58 char		MsgBuf[BUFSIZ*2];	/* text of most recent message */
59 static char	HeldMessageBuf[sizeof MsgBuf];	/* for held messages */
60 
61 #if NAMED_BIND && !defined(NO_DATA)
62 # define NO_DATA	NO_ADDRESS
63 #endif /* NAMED_BIND && !defined(NO_DATA) */
64 
65 void
66 /*VARARGS1*/
67 #ifdef __STDC__
68 syserr(const char *fmt, ...)
69 #else /* __STDC__ */
70 syserr(fmt, va_alist)
71 	const char *fmt;
72 	va_dcl
73 #endif /* __STDC__ */
74 {
75 	register char *p;
76 	int save_errno = errno;
77 	bool panic;
78 	char *user;
79 	char *enhsc;
80 	char *errtxt;
81 	struct passwd *pw;
82 	char ubuf[80];
83 	VA_LOCAL_DECL
84 
85 	panic = *fmt == '!';
86 	if (panic)
87 	{
88 		fmt++;
89 		HoldErrs = FALSE;
90 	}
91 
92 	/* format and output the error message */
93 	if (save_errno == 0)
94 	{
95 		p = "554";
96 		enhsc = "5.0.0";
97 	}
98 	else
99 	{
100 		p = "451";
101 		enhsc = "4.0.0";
102 	}
103 	VA_START(fmt);
104 	errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
105 	VA_END;
106 	puterrmsg(MsgBuf);
107 
108 	/* save this message for mailq printing */
109 	if (!panic && CurEnv != NULL)
110 	{
111 		if (CurEnv->e_message != NULL)
112 			sm_free(CurEnv->e_message);
113 		CurEnv->e_message = newstr(errtxt);
114 	}
115 
116 	/* determine exit status if not already set */
117 	if (ExitStat == EX_OK)
118 	{
119 		if (save_errno == 0)
120 			ExitStat = EX_SOFTWARE;
121 		else
122 			ExitStat = EX_OSERR;
123 		if (tTd(54, 1))
124 			dprintf("syserr: ExitStat = %d\n", ExitStat);
125 	}
126 
127 	pw = sm_getpwuid(RealUid);
128 	if (pw != NULL)
129 		user = pw->pw_name;
130 	else
131 	{
132 		user = ubuf;
133 		snprintf(ubuf, sizeof ubuf, "UID%d", (int) RealUid);
134 	}
135 
136 	if (LogLevel > 0)
137 		sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
138 			  CurEnv == NULL ? NOQID : CurEnv->e_id,
139 			  "SYSERR(%s): %.900s",
140 			  user, errtxt);
141 	switch (save_errno)
142 	{
143 	  case EBADF:
144 	  case ENFILE:
145 	  case EMFILE:
146 	  case ENOTTY:
147 #ifdef EFBIG
148 	  case EFBIG:
149 #endif /* EFBIG */
150 #ifdef ESPIPE
151 	  case ESPIPE:
152 #endif /* ESPIPE */
153 #ifdef EPIPE
154 	  case EPIPE:
155 #endif /* EPIPE */
156 #ifdef ENOBUFS
157 	  case ENOBUFS:
158 #endif /* ENOBUFS */
159 #ifdef ESTALE
160 	  case ESTALE:
161 #endif /* ESTALE */
162 
163 
164 		printopenfds(TRUE);
165 		mci_dump_all(TRUE);
166 		break;
167 	}
168 	if (panic)
169 	{
170 #ifdef XLA
171 		xla_all_end();
172 #endif /* XLA */
173 		sync_queue_time();
174 		if (tTd(0, 1))
175 			abort();
176 		exit(EX_OSERR);
177 	}
178 	errno = 0;
179 	if (QuickAbort)
180 		longjmp(TopFrame, 2);
181 }
182 /*
183 **  USRERR -- Signal user error.
184 **
185 **	This is much like syserr except it is for user errors.
186 **
187 **	Parameters:
188 **		fmt -- the format string.  If it does not begin with
189 **			a three-digit SMTP reply code, 501 is assumed.
190 **		(others) -- printf strings
191 **
192 **	Returns:
193 **		none
194 **		Through TopFrame if QuickAbort is set.
195 **
196 **	Side Effects:
197 **		increments Errors.
198 */
199 
200 /*VARARGS1*/
201 void
202 #ifdef __STDC__
203 usrerr(const char *fmt, ...)
204 #else /* __STDC__ */
205 usrerr(fmt, va_alist)
206 	const char *fmt;
207 	va_dcl
208 #endif /* __STDC__ */
209 {
210 	char *enhsc;
211 	char *errtxt;
212 	VA_LOCAL_DECL
213 
214 	if (fmt[0] == '5' || fmt[0] == '6')
215 		enhsc = "5.0.0";
216 	else if (fmt[0] == '4' || fmt[0] == '8')
217 		enhsc = "4.0.0";
218 	else if (fmt[0] == '2')
219 		enhsc = "2.0.0";
220 	else
221 		enhsc = NULL;
222 	VA_START(fmt);
223 	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "501", enhsc, 0, fmt, ap);
224 	VA_END;
225 
226 	if (SuprErrs)
227 		return;
228 
229 	/* save this message for mailq printing */
230 	switch (MsgBuf[0])
231 	{
232 	  case '4':
233 	  case '8':
234 		if (CurEnv->e_message != NULL)
235 			break;
236 
237 		/* FALLTHROUGH */
238 
239 	  case '5':
240 	  case '6':
241 		if (CurEnv->e_message != NULL)
242 			sm_free(CurEnv->e_message);
243 		if (MsgBuf[0] == '6')
244 		{
245 			char buf[MAXLINE];
246 
247 			snprintf(buf, sizeof buf, "Postmaster warning: %.*s",
248 				(int) sizeof buf - 22, errtxt);
249 			CurEnv->e_message = newstr(buf);
250 		}
251 		else
252 		{
253 			CurEnv->e_message = newstr(errtxt);
254 		}
255 		break;
256 	}
257 
258 	puterrmsg(MsgBuf);
259 
260 	if (LogLevel > 3 && LogUsrErrs)
261 		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
262 
263 	if (QuickAbort)
264 		longjmp(TopFrame, 1);
265 }
266 /*
267 **  USRERRENH -- Signal user error.
268 **
269 **	Same as usrerr but with enhanced status code.
270 **
271 **	Parameters:
272 **		enhsc -- the enhanced status code.
273 **		fmt -- the format string.  If it does not begin with
274 **			a three-digit SMTP reply code, 501 is assumed.
275 **		(others) -- printf strings
276 **
277 **	Returns:
278 **		none
279 **		Through TopFrame if QuickAbort is set.
280 **
281 **	Side Effects:
282 **		increments Errors.
283 */
284 
285 /*VARARGS1*/
286 void
287 #ifdef __STDC__
288 usrerrenh(char *enhsc, const char *fmt, ...)
289 #else /* __STDC__ */
290 usrerrenh(enhsc, fmt, va_alist)
291 	char *enhsc;
292 	const char *fmt;
293 	va_dcl
294 #endif /* __STDC__ */
295 {
296 	char *errtxt;
297 	VA_LOCAL_DECL
298 
299 	if (enhsc == NULL || *enhsc == '\0')
300 	{
301 		if (fmt[0] == '5' || fmt[0] == '6')
302 			enhsc = "5.0.0";
303 		else if (fmt[0] == '4' || fmt[0] == '8')
304 			enhsc = "4.0.0";
305 		else if (fmt[0] == '2')
306 			enhsc = "2.0.0";
307 	}
308 	VA_START(fmt);
309 	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "501", enhsc, 0, fmt, ap);
310 	VA_END;
311 
312 	if (SuprErrs)
313 		return;
314 
315 	/* save this message for mailq printing */
316 	switch (MsgBuf[0])
317 	{
318 	  case '4':
319 	  case '8':
320 		if (CurEnv->e_message != NULL)
321 			break;
322 
323 		/* FALLTHROUGH */
324 
325 	  case '5':
326 	  case '6':
327 		if (CurEnv->e_message != NULL)
328 			sm_free(CurEnv->e_message);
329 		if (MsgBuf[0] == '6')
330 		{
331 			char buf[MAXLINE];
332 
333 			snprintf(buf, sizeof buf, "Postmaster warning: %.*s",
334 				(int) sizeof buf - 22, errtxt);
335 			CurEnv->e_message = newstr(buf);
336 		}
337 		else
338 		{
339 			CurEnv->e_message = newstr(errtxt);
340 		}
341 		break;
342 	}
343 
344 	puterrmsg(MsgBuf);
345 
346 	if (LogLevel > 3 && LogUsrErrs)
347 		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
348 
349 	if (QuickAbort)
350 		longjmp(TopFrame, 1);
351 }
352 /*
353 **  MESSAGE -- print message (not necessarily an error)
354 **
355 **	Parameters:
356 **		msg -- the message (printf fmt) -- it can begin with
357 **			an SMTP reply code.  If not, 050 is assumed.
358 **		(others) -- printf arguments
359 **
360 **	Returns:
361 **		none
362 **
363 **	Side Effects:
364 **		none.
365 */
366 
367 /*VARARGS1*/
368 void
369 #ifdef __STDC__
370 message(const char *msg, ...)
371 #else /* __STDC__ */
372 message(msg, va_alist)
373 	const char *msg;
374 	va_dcl
375 #endif /* __STDC__ */
376 {
377 	char *errtxt;
378 	VA_LOCAL_DECL
379 
380 	errno = 0;
381 	VA_START(msg);
382 	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
383 	VA_END;
384 	putoutmsg(MsgBuf, FALSE, FALSE);
385 
386 	/* save this message for mailq printing */
387 	switch (MsgBuf[0])
388 	{
389 	  case '4':
390 	  case '8':
391 		if (CurEnv->e_message != NULL)
392 			break;
393 		/* FALLTHROUGH */
394 
395 	  case '5':
396 		if (CurEnv->e_message != NULL)
397 			sm_free(CurEnv->e_message);
398 		CurEnv->e_message = newstr(errtxt);
399 		break;
400 	}
401 }
402 /*
403 **  NMESSAGE -- print message (not necessarily an error)
404 **
405 **	Just like "message" except it never puts the to... tag on.
406 **
407 **	Parameters:
408 **		msg -- the message (printf fmt) -- if it begins
409 **			with a three digit SMTP reply code, that is used,
410 **			otherwise 050 is assumed.
411 **		(others) -- printf arguments
412 **
413 **	Returns:
414 **		none
415 **
416 **	Side Effects:
417 **		none.
418 */
419 
420 /*VARARGS1*/
421 void
422 #ifdef __STDC__
423 nmessage(const char *msg, ...)
424 #else /* __STDC__ */
425 nmessage(msg, va_alist)
426 	const char *msg;
427 	va_dcl
428 #endif /* __STDC__ */
429 {
430 	char *errtxt;
431 	VA_LOCAL_DECL
432 
433 	errno = 0;
434 	VA_START(msg);
435 	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
436 			(char *) NULL, 0, msg, ap);
437 	VA_END;
438 	putoutmsg(MsgBuf, FALSE, FALSE);
439 
440 	/* save this message for mailq printing */
441 	switch (MsgBuf[0])
442 	{
443 	  case '4':
444 	  case '8':
445 		if (CurEnv->e_message != NULL)
446 			break;
447 		/* FALLTHROUGH */
448 
449 	  case '5':
450 		if (CurEnv->e_message != NULL)
451 			sm_free(CurEnv->e_message);
452 		CurEnv->e_message = newstr(errtxt);
453 		break;
454 	}
455 }
456 /*
457 **  PUTOUTMSG -- output error message to transcript and channel
458 **
459 **	Parameters:
460 **		msg -- message to output (in SMTP format).
461 **		holdmsg -- if TRUE, don't output a copy of the message to
462 **			our output channel.
463 **		heldmsg -- if TRUE, this is a previously held message;
464 **			don't log it to the transcript file.
465 **
466 **	Returns:
467 **		none.
468 **
469 **	Side Effects:
470 **		Outputs msg to the transcript.
471 **		If appropriate, outputs it to the channel.
472 **		Deletes SMTP reply code number as appropriate.
473 */
474 
475 static void
476 putoutmsg(msg, holdmsg, heldmsg)
477 	char *msg;
478 	bool holdmsg;
479 	bool heldmsg;
480 {
481 	char *errtxt = msg;
482 	char msgcode = msg[0];
483 
484 	/* display for debugging */
485 	if (tTd(54, 8))
486 		dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
487 			heldmsg ? " (held)" : "");
488 
489 	/* map warnings to something SMTP can handle */
490 	if (msgcode == '6')
491 		msg[0] = '5';
492 	else if (msgcode == '8')
493 		msg[0] = '4';
494 
495 	/* output to transcript if serious */
496 	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
497 	    strchr("45", msg[0]) != NULL)
498 		fprintf(CurEnv->e_xfp, "%s\n", msg);
499 
500 	if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
501 		sm_syslog(LOG_INFO, CurEnv->e_id,
502 			  "--> %s%s",
503 			  msg, holdmsg ? " (held)" : "");
504 
505 	if (msgcode == '8')
506 		msg[0] = '0';
507 
508 	/* output to channel if appropriate */
509 	if (!Verbose && msg[0] == '0')
510 		return;
511 	if (holdmsg)
512 	{
513 		/* save for possible future display */
514 		msg[0] = msgcode;
515 		if (HeldMessageBuf[0] == '5' && msgcode == '4')
516 			return;
517 		snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg);
518 		return;
519 	}
520 
521 	(void) fflush(stdout);
522 
523 	if (OutChannel == NULL)
524 		return;
525 
526 	/* find actual text of error (after SMTP status codes) */
527 	if (ISSMTPREPLY(errtxt))
528 	{
529 		int l;
530 
531 		errtxt += 4;
532 		l = isenhsc(errtxt, ' ');
533 		if (l <= 0)
534 			l = isenhsc(errtxt, '\0');
535 		if (l > 0)
536 			errtxt += l + 1;
537 	}
538 
539 	/* if DisConnected, OutChannel now points to the transcript */
540 	if (!DisConnected &&
541 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
542 		fprintf(OutChannel, "%s\r\n", msg);
543 	else
544 		fprintf(OutChannel, "%s\n", errtxt);
545 	if (TrafficLogFile != NULL)
546 		fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(),
547 			(OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : errtxt);
548 	if (msg[3] == ' ')
549 		(void) fflush(OutChannel);
550 	if (!ferror(OutChannel) || DisConnected)
551 		return;
552 
553 	/*
554 	**  Error on output -- if reporting lost channel, just ignore it.
555 	**  Also, ignore errors from QUIT response (221 message) -- some
556 	**	rude servers don't read result.
557 	*/
558 
559 	if (InChannel == NULL || feof(InChannel) || ferror(InChannel) ||
560 	    strncmp(msg, "221", 3) == 0)
561 		return;
562 
563 	/* can't call syserr, 'cause we are using MsgBuf */
564 	HoldErrs = TRUE;
565 	if (LogLevel > 0)
566 		sm_syslog(LOG_CRIT, CurEnv->e_id,
567 			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
568 			  CurHostName == NULL ? "NO-HOST" : CurHostName,
569 			  shortenstring(msg, MAXSHORTSTR), errstring(errno));
570 }
571 /*
572 **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
573 **
574 **	Parameters:
575 **		msg -- the message to output.
576 **
577 **	Returns:
578 **		none.
579 **
580 **	Side Effects:
581 **		Sets the fatal error bit in the envelope as appropriate.
582 */
583 
584 static void
585 puterrmsg(msg)
586 	char *msg;
587 {
588 	char msgcode = msg[0];
589 
590 	/* output the message as usual */
591 	putoutmsg(msg, HoldErrs, FALSE);
592 
593 	/* be careful about multiple error messages */
594 	if (OnlyOneError)
595 		HoldErrs = TRUE;
596 
597 	/* signal the error */
598 	Errors++;
599 
600 	if (CurEnv == NULL)
601 		return;
602 
603 	if (msgcode == '6')
604 	{
605 		/* notify the postmaster */
606 		CurEnv->e_flags |= EF_PM_NOTIFY;
607 	}
608 	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
609 	{
610 		/* mark long-term fatal errors */
611 		CurEnv->e_flags |= EF_FATALERRS;
612 	}
613 }
614 /*
615 **  ISENHSC -- check whether a string contains an enhanced status code
616 **
617 **	Parameters:
618 **		s -- string with possible enhanced status code.
619 **		delim -- delim for enhanced status code.
620 **
621 **	Returns:
622 **		0  -- no enhanced status code.
623 **		>4 -- length of enhanced status code.
624 **
625 **	Side Effects:
626 **		none.
627 */
628 int
629 isenhsc(s, delim)
630 	const char *s;
631 	int delim;
632 {
633 	int l, h;
634 
635 	if (s == NULL)
636 		return 0;
637 	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
638 		return 0;
639 	h = 0;
640 	l = 2;
641 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
642 		++h;
643 	if (h == 0 || s[l + h] != '.')
644 		return 0;
645 	l += h + 1;
646 	h = 0;
647 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
648 		++h;
649 	if (h == 0 || s[l + h] != delim)
650 		return 0;
651 	return l + h;
652 }
653 /*
654 **  EXTENHSC -- check and extract an enhanced status code
655 **
656 **	Parameters:
657 **		s -- string with possible enhanced status code.
658 **		delim -- delim for enhanced status code.
659 **		e -- pointer to storage for enhanced status code.
660 **			must be != NULL and have space for at least
661 **			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
662 **
663 **	Returns:
664 **		0  -- no enhanced status code.
665 **		>4 -- length of enhanced status code.
666 **
667 **	Side Effects:
668 **		fills e with enhanced status code.
669 */
670 int
671 extenhsc(s, delim, e)
672 	const char *s;
673 	int delim;
674 	char *e;
675 {
676 	int l, h;
677 
678 	if (s == NULL)
679 		return 0;
680 	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
681 		return 0;
682 	h = 0;
683 	l = 2;
684 	e[0] = s[0];
685 	e[1] = '.';
686 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
687 	{
688 		e[l + h] = s[l + h];
689 		++h;
690 	}
691 	if (h == 0 || s[l + h] != '.')
692 		return 0;
693 	e[l + h] = '.';
694 	l += h + 1;
695 	h = 0;
696 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
697 	{
698 		e[l + h] = s[l + h];
699 		++h;
700 	}
701 	if (h == 0 || s[l + h] != delim)
702 		return 0;
703 	e[l + h] = '\0';
704 	return l + h;
705 }
706 /*
707 **  FMTMSG -- format a message into buffer.
708 **
709 **	Parameters:
710 **		eb -- error buffer to get result -- MUST BE MsgBuf.
711 **		to -- the recipient tag for this message.
712 **		num -- default three digit SMTP reply code.
713 **		enhsc -- enhanced status code.
714 **		en -- the error number to display.
715 **		fmt -- format of string.
716 **		ap -- arguments for fmt.
717 **
718 **	Returns:
719 **		pointer to error text beyond status codes.
720 **
721 **	Side Effects:
722 **		none.
723 */
724 
725 static char *
726 fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
727 	register char *eb;
728 	const char *to;
729 	const char *num;
730 	const char *enhsc;
731 	int eno;
732 	const char *fmt;
733 	va_list ap;
734 {
735 	char del;
736 	int l;
737 	int spaceleft = sizeof MsgBuf;
738 	char *errtxt;
739 
740 	/* output the reply code */
741 	if (ISSMTPCODE(fmt))
742 	{
743 		num = fmt;
744 		fmt += 4;
745 	}
746 	if (num[3] == '-')
747 		del = '-';
748 	else
749 		del = ' ';
750 	(void) snprintf(eb, spaceleft, "%3.3s%c", num, del);
751 	eb += 4;
752 	spaceleft -= 4;
753 
754 	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
755 	{
756 		/* copy enh.status code including trailing blank */
757 		l++;
758 		(void) strlcpy(eb, fmt, l + 1);
759 		eb += l;
760 		spaceleft -= l;
761 		fmt += l;
762 	}
763 	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
764 	{
765 		/* copy enh.status code */
766 		(void) strlcpy(eb, enhsc, l + 1);
767 		eb[l] = ' ';
768 		eb[++l] = '\0';
769 		eb += l;
770 		spaceleft -= l;
771 	}
772 	errtxt = eb;
773 
774 	/* output the file name and line number */
775 	if (FileName != NULL)
776 	{
777 		(void) snprintf(eb, spaceleft, "%s: line %d: ",
778 			shortenstring(FileName, 83), LineNumber);
779 		eb += (l = strlen(eb));
780 		spaceleft -= l;
781 	}
782 
783 	/* output the "to" person */
784 	if (to != NULL && to[0] != '\0' &&
785 	    strncmp(num, "551", 3) != 0 &&
786 	    strncmp(num, "251", 3) != 0)
787 	{
788 		(void) snprintf(eb, spaceleft, "%s... ",
789 			shortenstring(to, MAXSHORTSTR));
790 		spaceleft -= strlen(eb);
791 		while (*eb != '\0')
792 			*eb++ &= 0177;
793 	}
794 
795 	/* output the message */
796 	(void) vsnprintf(eb, spaceleft, fmt, ap);
797 	spaceleft -= strlen(eb);
798 	while (*eb != '\0')
799 		*eb++ &= 0177;
800 
801 	/* output the error code, if any */
802 	if (eno != 0)
803 		(void) snprintf(eb, spaceleft, ": %s", errstring(eno));
804 
805 	return errtxt;
806 }
807 /*
808 **  BUFFER_ERRORS -- arrange to buffer future error messages
809 **
810 **	Parameters:
811 **		none
812 **
813 **	Returns:
814 **		none.
815 */
816 
817 void
818 buffer_errors()
819 {
820 	HeldMessageBuf[0] = '\0';
821 	HoldErrs = TRUE;
822 }
823 /*
824 **  FLUSH_ERRORS -- flush the held error message buffer
825 **
826 **	Parameters:
827 **		print -- if set, print the message, otherwise just
828 **			delete it.
829 **
830 **	Returns:
831 **		none.
832 */
833 
834 void
835 flush_errors(print)
836 	bool print;
837 {
838 	if (print && HeldMessageBuf[0] != '\0')
839 		putoutmsg(HeldMessageBuf, FALSE, TRUE);
840 	HeldMessageBuf[0] = '\0';
841 	HoldErrs = FALSE;
842 }
843 /*
844 **  ERRSTRING -- return string description of error code
845 **
846 **	Parameters:
847 **		errnum -- the error number to translate
848 **
849 **	Returns:
850 **		A string description of errnum.
851 **
852 **	Side Effects:
853 **		none.
854 */
855 
856 const char *
857 errstring(errnum)
858 	int errnum;
859 {
860 	char *dnsmsg;
861 	char *bp;
862 	static char buf[MAXLINE];
863 #if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
864 	extern char *sys_errlist[];
865 	extern int sys_nerr;
866 #endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
867 
868 	/*
869 	**  Handle special network error codes.
870 	**
871 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
872 	*/
873 
874 	dnsmsg = NULL;
875 	switch (errnum)
876 	{
877 #if defined(DAEMON) && defined(ETIMEDOUT)
878 	  case ETIMEDOUT:
879 	  case ECONNRESET:
880 		bp = buf;
881 # if HASSTRERROR
882 		snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum));
883 # else /* HASSTRERROR */
884 		if (errnum >= 0 && errnum < sys_nerr)
885 			snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]);
886 		else
887 			snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum);
888 # endif /* HASSTRERROR */
889 		bp += strlen(bp);
890 		if (CurHostName != NULL)
891 		{
892 			if (errnum == ETIMEDOUT)
893 			{
894 				snprintf(bp, SPACELEFT(buf, bp), " with ");
895 				bp += strlen(bp);
896 			}
897 			else
898 			{
899 				bp = buf;
900 				snprintf(bp, SPACELEFT(buf, bp),
901 					"Connection reset by ");
902 				bp += strlen(bp);
903 			}
904 			snprintf(bp, SPACELEFT(buf, bp), "%s",
905 				shortenstring(CurHostName, MAXSHORTSTR));
906 			bp += strlen(buf);
907 		}
908 		if (SmtpPhase != NULL)
909 		{
910 			snprintf(bp, SPACELEFT(buf, bp), " during %s",
911 				SmtpPhase);
912 		}
913 		return buf;
914 
915 	  case EHOSTDOWN:
916 		if (CurHostName == NULL)
917 			break;
918 		(void) snprintf(buf, sizeof buf, "Host %s is down",
919 			shortenstring(CurHostName, MAXSHORTSTR));
920 		return buf;
921 
922 	  case ECONNREFUSED:
923 		if (CurHostName == NULL)
924 			break;
925 		(void) snprintf(buf, sizeof buf, "Connection refused by %s",
926 			shortenstring(CurHostName, MAXSHORTSTR));
927 		return buf;
928 #endif /* defined(DAEMON) && defined(ETIMEDOUT) */
929 
930 #if NAMED_BIND
931 	  case HOST_NOT_FOUND + E_DNSBASE:
932 		dnsmsg = "host not found";
933 		break;
934 
935 	  case TRY_AGAIN + E_DNSBASE:
936 		dnsmsg = "host name lookup failure";
937 		break;
938 
939 	  case NO_RECOVERY + E_DNSBASE:
940 		dnsmsg = "non-recoverable error";
941 		break;
942 
943 	  case NO_DATA + E_DNSBASE:
944 		dnsmsg = "no data known";
945 		break;
946 #endif /* NAMED_BIND */
947 
948 	  case EPERM:
949 		/* SunOS gives "Not owner" -- this is the POSIX message */
950 		return "Operation not permitted";
951 
952 	/*
953 	**  Error messages used internally in sendmail.
954 	*/
955 
956 	  case E_SM_OPENTIMEOUT:
957 		return "Timeout on file open";
958 
959 	  case E_SM_NOSLINK:
960 		return "Symbolic links not allowed";
961 
962 	  case E_SM_NOHLINK:
963 		return "Hard links not allowed";
964 
965 	  case E_SM_REGONLY:
966 		return "Regular files only";
967 
968 	  case E_SM_ISEXEC:
969 		return "Executable files not allowed";
970 
971 	  case E_SM_WWDIR:
972 		return "World writable directory";
973 
974 	  case E_SM_GWDIR:
975 		return "Group writable directory";
976 
977 	  case E_SM_FILECHANGE:
978 		return "File changed after open";
979 
980 	  case E_SM_WWFILE:
981 		return "World writable file";
982 
983 	  case E_SM_GWFILE:
984 		return "Group writable file";
985 
986 	  case E_SM_GRFILE:
987 		return "Group readable file";
988 
989 	  case E_SM_WRFILE:
990 		return "World readable file";
991 	}
992 
993 	if (dnsmsg != NULL)
994 	{
995 		bp = buf;
996 		bp += strlcpy(bp, "Name server: ", sizeof buf);
997 		if (CurHostName != NULL)
998 		{
999 			snprintf(bp, SPACELEFT(buf, bp), "%s: ",
1000 				shortenstring(CurHostName, MAXSHORTSTR));
1001 			bp += strlen(bp);
1002 		}
1003 		snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg);
1004 		return buf;
1005 	}
1006 
1007 #ifdef LDAPMAP
1008 	if (errnum >= E_LDAPBASE)
1009 		return ldap_err2string(errnum - E_LDAPBASE);
1010 #endif /* LDAPMAP */
1011 
1012 #if HASSTRERROR
1013 	return strerror(errnum);
1014 #else /* HASSTRERROR */
1015 	if (errnum > 0 && errnum < sys_nerr)
1016 		return sys_errlist[errnum];
1017 
1018 	(void) snprintf(buf, sizeof buf, "Error %d", errnum);
1019 	return buf;
1020 #endif /* HASSTRERROR */
1021 }
1022