err.c revision 80789
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
15static char id[] = "@(#)$Id: err.c,v 8.120.4.3 2001/05/30 00:22:26 gshapiro Exp $";
16#endif /* ! lint */
17
18/* $FreeBSD: head/contrib/sendmail/src/err.c 80789 2001-08-01 01:34:52Z 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			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*/
199void
200#ifdef __STDC__
201usrerr(const char *fmt, ...)
202#else /* __STDC__ */
203usrerr(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*/
284void
285#ifdef __STDC__
286usrerrenh(char *enhsc, const char *fmt, ...)
287#else /* __STDC__ */
288usrerrenh(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*/
366void
367#ifdef __STDC__
368message(const char *msg, ...)
369#else /* __STDC__ */
370message(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*/
419void
420#ifdef __STDC__
421nmessage(const char *msg, ...)
422#else /* __STDC__ */
423nmessage(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
473static void
474putoutmsg(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
582static void
583puterrmsg(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*/
626int
627isenhsc(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*/
668int
669extenhsc(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
723static char *
724fmtmsg(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
815void
816buffer_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
832void
833flush_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
854const char *
855errstring(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