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