err.c revision 64565
1/*
2 * Copyright (c) 1998-2000 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
15static char id[] = "@(#)$Id: err.c,v 8.120.4.1 2000/05/25 18:56:15 gshapiro Exp $";
16#endif /* ! lint */
17
18/* $FreeBSD: head/contrib/sendmail/src/err.c 64565 2000-08-12 22:19:16Z gshapiro $ */
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
27static void	putoutmsg __P((char *, bool, bool));
28static void	puterrmsg __P((char *));
29static 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
58char		MsgBuf[BUFSIZ*2];	/* text of most recent message */
59static 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
65void
66/*VARARGS1*/
67#ifdef __STDC__
68syserr(const char *fmt, ...)
69#else /* __STDC__ */
70syserr(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			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(getuid());
128	if (pw != NULL)
129		user = pw->pw_name;
130	else
131	{
132		user = ubuf;
133		snprintf(ubuf, sizeof ubuf, "UID%d", (int) getuid());
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*/
201void
202#ifdef __STDC__
203usrerr(const char *fmt, ...)
204#else /* __STDC__ */
205usrerr(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			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*/
286void
287#ifdef __STDC__
288usrerrenh(char *enhsc, const char *fmt, ...)
289#else /* __STDC__ */
290usrerrenh(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			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*/
368void
369#ifdef __STDC__
370message(const char *msg, ...)
371#else /* __STDC__ */
372message(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			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*/
421void
422#ifdef __STDC__
423nmessage(const char *msg, ...)
424#else /* __STDC__ */
425nmessage(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			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
475static void
476putoutmsg(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
584static void
585puterrmsg(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*/
628int
629isenhsc(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*/
670int
671extenhsc(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
725static char *
726fmtmsg(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
817void
818buffer_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
834void
835flush_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
856const char *
857errstring(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