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