xref: /csrg-svn/usr.sbin/sendmail/src/err.c (revision 68692)
122705Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
362525Sbostic  * Copyright (c) 1988, 1993
462525Sbostic  *	The Regents of the University of California.  All rights reserved.
533729Sbostic  *
642826Sbostic  * %sccs.include.redist.c%
733729Sbostic  */
822705Sdist 
922705Sdist #ifndef lint
10*68692Seric static char sccsid[] = "@(#)err.c	8.31 (Berkeley) 03/31/95";
1133729Sbostic #endif /* not lint */
1222705Sdist 
133311Seric # include "sendmail.h"
1424943Seric # include <errno.h>
1525526Smiriam # include <netdb.h>
1666006Seric # include <pwd.h>
17295Seric 
18295Seric /*
191514Seric **  SYSERR -- Print error message.
20295Seric **
21295Seric **	Prints an error message via printf to the diagnostic
22295Seric **	output.  If LOG is defined, it logs it also.
23295Seric **
2458690Seric **	If the first character of the syserr message is `!' it will
2558690Seric **	log this as an ALERT message and exit immediately.  This can
2658690Seric **	leave queue files in an indeterminate state, so it should not
2758690Seric **	be used lightly.
2858690Seric **
29295Seric **	Parameters:
3067818Seric **		fmt -- the format string.  If it does not begin with
3167818Seric **			a three-digit SMTP reply code, either 554 or
3267818Seric **			451 is assumed depending on whether errno
3367818Seric **			is set.
3467818Seric **		(others) -- parameters
35295Seric **
36295Seric **	Returns:
374084Seric **		none
387762Seric **		Through TopFrame if QuickAbort is set.
39295Seric **
40295Seric **	Side Effects:
411514Seric **		increments Errors.
421514Seric **		sets ExitStat.
43295Seric */
44295Seric 
45*68692Seric char	MsgBuf[BUFSIZ*2];		/* text of most recent message */
46*68692Seric char	HeldMessageBuf[sizeof MsgBuf];	/* for held messages */
474084Seric 
4863969Seric static void	fmtmsg();
4946928Sbostic 
5066334Seric #if NAMED_BIND && !defined(NO_DATA)
5163969Seric # define NO_DATA	NO_ADDRESS
5263969Seric #endif
5363969Seric 
5458824Seric void
55295Seric /*VARARGS1*/
5657642Seric #ifdef __STDC__
5760094Seric syserr(const char *fmt, ...)
5857642Seric #else
5957642Seric syserr(fmt, va_alist)
6060094Seric 	const char *fmt;
6157642Seric 	va_dcl
6257642Seric #endif
63295Seric {
6416901Seric 	register char *p;
6516901Seric 	int olderrno = errno;
6658690Seric 	bool panic;
6766006Seric #ifdef LOG
6866006Seric 	char *uname;
6966006Seric 	struct passwd *pw;
7066006Seric 	char ubuf[80];
7166006Seric #endif
7256852Seric 	VA_LOCAL_DECL
73295Seric 
7458690Seric 	panic = *fmt == '!';
7558690Seric 	if (panic)
7658690Seric 		fmt++;
7758690Seric 
787525Seric 	/* format and output the error message */
7916901Seric 	if (olderrno == 0)
8058151Seric 		p = "554";
817957Seric 	else
8258151Seric 		p = "451";
8356852Seric 	VA_START(fmt);
8456852Seric 	fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
8556852Seric 	VA_END;
869389Seric 	puterrmsg(MsgBuf);
874063Seric 
8867818Seric 	/* save this message for mailq printing */
8967818Seric 	if (!panic)
9067818Seric 	{
9167818Seric 		if (CurEnv->e_message != NULL)
9267818Seric 			free(CurEnv->e_message);
9367818Seric 		CurEnv->e_message = newstr(MsgBuf + 4);
9467818Seric 	}
9567818Seric 
96295Seric 	/* determine exit status if not already set */
97295Seric 	if (ExitStat == EX_OK)
98295Seric 	{
9916901Seric 		if (olderrno == 0)
100295Seric 			ExitStat = EX_SOFTWARE;
101295Seric 		else
1021598Seric 			ExitStat = EX_OSERR;
10366323Seric 		if (tTd(54, 1))
10466323Seric 			printf("syserr: ExitStat = %d\n", ExitStat);
105295Seric 	}
106295Seric 
107295Seric # ifdef LOG
10866006Seric 	pw = getpwuid(getuid());
10966006Seric 	if (pw != NULL)
11066006Seric 		uname = pw->pw_name;
11166006Seric 	else
11266006Seric 	{
11366006Seric 		uname = ubuf;
11466006Seric 		sprintf(ubuf, "UID%d", getuid());
11566006Seric 	}
11666006Seric 
1177674Seric 	if (LogLevel > 0)
11866006Seric 		syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s",
11925277Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
12066006Seric 			uname, &MsgBuf[4]);
12156795Seric # endif /* LOG */
12264725Seric 	if (olderrno == EMFILE)
12364731Seric 	{
12464725Seric 		printopenfds(TRUE);
12564731Seric 		mci_dump_all(TRUE);
12664731Seric 	}
12758690Seric 	if (panic)
12859156Seric 	{
12959156Seric #ifdef XLA
13059156Seric 		xla_all_end();
13159156Seric #endif
13258690Seric 		exit(EX_OSERR);
13359156Seric 	}
134295Seric 	errno = 0;
1357762Seric 	if (QuickAbort)
1367762Seric 		longjmp(TopFrame, 2);
137295Seric }
138295Seric /*
139295Seric **  USRERR -- Signal user error.
140295Seric **
141295Seric **	This is much like syserr except it is for user errors.
142295Seric **
143295Seric **	Parameters:
14467818Seric **		fmt -- the format string.  If it does not begin with
14567818Seric **			a three-digit SMTP reply code, 501 is assumed.
14667818Seric **		(others) -- printf strings
147295Seric **
148295Seric **	Returns:
1494084Seric **		none
1507762Seric **		Through TopFrame if QuickAbort is set.
151295Seric **
152295Seric **	Side Effects:
1531514Seric **		increments Errors.
154295Seric */
155295Seric 
156295Seric /*VARARGS1*/
15758824Seric void
15857642Seric #ifdef __STDC__
15960094Seric usrerr(const char *fmt, ...)
16057642Seric #else
16157642Seric usrerr(fmt, va_alist)
16260094Seric 	const char *fmt;
16357642Seric 	va_dcl
16457642Seric #endif
165295Seric {
16656852Seric 	VA_LOCAL_DECL
167295Seric 
168295Seric 	if (SuprErrs)
1694084Seric 		return;
170295Seric 
17156852Seric 	VA_START(fmt);
17258524Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
17356852Seric 	VA_END;
1749389Seric 	puterrmsg(MsgBuf);
1758239Seric 
17667818Seric 	/* save this message for mailq printing */
17767818Seric 	if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4'))
17867818Seric 	{
17967818Seric 		if (CurEnv->e_message != NULL)
18067818Seric 			free(CurEnv->e_message);
18167818Seric 		CurEnv->e_message = newstr(MsgBuf + 4);
18267818Seric 	}
18367818Seric 
18451951Seric # ifdef LOG
18558020Seric 	if (LogLevel > 3 && LogUsrErrs)
18651951Seric 		syslog(LOG_NOTICE, "%s: %s",
18751951Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
18851951Seric 			&MsgBuf[4]);
18956795Seric # endif /* LOG */
19051951Seric 
1917762Seric 	if (QuickAbort)
1927762Seric 		longjmp(TopFrame, 1);
1934063Seric }
1944063Seric /*
1954063Seric **  MESSAGE -- print message (not necessarily an error)
1964063Seric **
1974063Seric **	Parameters:
19859581Seric **		msg -- the message (printf fmt) -- it can begin with
19959581Seric **			an SMTP reply code.  If not, 050 is assumed.
20067818Seric **		(others) -- printf arguments
2014063Seric **
2024063Seric **	Returns:
2034063Seric **		none
2044063Seric **
2054063Seric **	Side Effects:
2064063Seric **		none.
2074063Seric */
2084063Seric 
2094084Seric /*VARARGS2*/
21058826Seric void
21157642Seric #ifdef __STDC__
21260094Seric message(const char *msg, ...)
21357642Seric #else
21458151Seric message(msg, va_alist)
21560094Seric 	const char *msg;
21657642Seric 	va_dcl
21757642Seric #endif
2184063Seric {
21956852Seric 	VA_LOCAL_DECL
22056852Seric 
2214711Seric 	errno = 0;
22256852Seric 	VA_START(msg);
22358151Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
22456852Seric 	VA_END;
225*68692Seric 	putoutmsg(MsgBuf, FALSE, FALSE);
22667818Seric 
22767818Seric 	/* save this message for mailq printing */
22867818Seric 	if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4'))
22967818Seric 	{
23067818Seric 		if (CurEnv->e_message != NULL)
23167818Seric 			free(CurEnv->e_message);
23267818Seric 		CurEnv->e_message = newstr(MsgBuf + 4);
23367818Seric 	}
2347613Seric }
2357613Seric /*
2368239Seric **  NMESSAGE -- print message (not necessarily an error)
2378239Seric **
2388239Seric **	Just like "message" except it never puts the to... tag on.
2398239Seric **
2408239Seric **	Parameters:
2418239Seric **		msg -- the message (printf fmt) -- if it begins
24267818Seric **			with a three digit SMTP reply code, that is used,
24367818Seric **			otherwise 050 is assumed.
24467818Seric **		(others) -- printf arguments
2458239Seric **
2468239Seric **	Returns:
2478239Seric **		none
2488239Seric **
2498239Seric **	Side Effects:
2508239Seric **		none.
2518239Seric */
2528239Seric 
2538239Seric /*VARARGS2*/
25458826Seric void
25557642Seric #ifdef __STDC__
25660094Seric nmessage(const char *msg, ...)
25757642Seric #else
25858151Seric nmessage(msg, va_alist)
25960094Seric 	const char *msg;
26057642Seric 	va_dcl
26157642Seric #endif
2628239Seric {
26356852Seric 	VA_LOCAL_DECL
26456852Seric 
2658239Seric 	errno = 0;
26656852Seric 	VA_START(msg);
26758151Seric 	fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
26856852Seric 	VA_END;
269*68692Seric 	putoutmsg(MsgBuf, FALSE, FALSE);
2708239Seric }
2718239Seric /*
27263753Seric **  PUTOUTMSG -- output error message to transcript and channel
2737613Seric **
2747613Seric **	Parameters:
2757613Seric **		msg -- message to output (in SMTP format).
2769108Seric **		holdmsg -- if TRUE, don't output a copy of the message to
2779108Seric **			our output channel.
278*68692Seric **		heldmsg -- if TRUE, this is a previously held message;
279*68692Seric **			don't log it to the transcript file.
2807613Seric **
2817613Seric **	Returns:
2827613Seric **		none.
2837613Seric **
2847613Seric **	Side Effects:
2857613Seric **		Outputs msg to the transcript.
2867613Seric **		If appropriate, outputs it to the channel.
2877613Seric **		Deletes SMTP reply code number as appropriate.
2887613Seric */
2894711Seric 
290*68692Seric putoutmsg(msg, holdmsg, heldmsg)
2917613Seric 	char *msg;
2929108Seric 	bool holdmsg;
293*68692Seric 	bool heldmsg;
2947613Seric {
29564249Seric 	/* display for debugging */
29664249Seric 	if (tTd(54, 8))
297*68692Seric 		printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
298*68692Seric 			heldmsg ? " (held)" : "");
29964249Seric 
30014900Seric 	/* output to transcript if serious */
301*68692Seric 	if (!heldmsg && CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
30214900Seric 		fprintf(CurEnv->e_xfp, "%s\n", msg);
3034711Seric 
3044711Seric 	/* output to channel if appropriate */
305*68692Seric 	if (!Verbose && msg[0] == '0')
30660421Seric 		return;
307*68692Seric 	if (holdmsg)
308*68692Seric 	{
309*68692Seric 		/* save for possible future display */
310*68692Seric 		strcpy(HeldMessageBuf, msg);
311*68692Seric 		return;
312*68692Seric 	}
31360421Seric 
31463848Seric 	/* map warnings to something SMTP can handle */
31563848Seric 	if (msg[0] == '6')
31663848Seric 		msg[0] = '5';
31763848Seric 
31860421Seric 	(void) fflush(stdout);
31966017Seric 
32066017Seric 	/* if DisConnected, OutChannel now points to the transcript */
32166017Seric 	if (!DisConnected &&
32266017Seric 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
32360421Seric 		fprintf(OutChannel, "%s\r\n", msg);
32460421Seric 	else
32560421Seric 		fprintf(OutChannel, "%s\n", &msg[4]);
32663753Seric 	if (TrafficLogFile != NULL)
32763753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(),
32865580Seric 			(OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
32960421Seric 	if (msg[3] == ' ')
33060421Seric 		(void) fflush(OutChannel);
33166017Seric 	if (!ferror(OutChannel) || DisConnected)
33260421Seric 		return;
33360421Seric 
33464499Seric 	/*
33564499Seric 	**  Error on output -- if reporting lost channel, just ignore it.
33664499Seric 	**  Also, ignore errors from QUIT response (221 message) -- some
33764499Seric 	**	rude servers don't read result.
33864499Seric 	*/
33964499Seric 
34064499Seric 	if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0)
34160421Seric 		return;
34260421Seric 
34360421Seric 	/* can't call syserr, 'cause we are using MsgBuf */
34460421Seric 	HoldErrs = TRUE;
34560283Seric #ifdef LOG
34660421Seric 	if (LogLevel > 0)
34760421Seric 		syslog(LOG_CRIT,
34866864Seric 			"%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
34960421Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
35064123Seric 			CurHostName == NULL ? "NO-HOST" : CurHostName,
35166864Seric 			msg, errstring(errno));
35260283Seric #endif
3539389Seric }
3549389Seric /*
35563753Seric **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
3569389Seric **
3579389Seric **	Parameters:
3589389Seric **		msg -- the message to output.
3599389Seric **
3609389Seric **	Returns:
3619389Seric **		none.
3629389Seric **
3639389Seric **	Side Effects:
3649389Seric **		Sets the fatal error bit in the envelope as appropriate.
3659389Seric */
3668239Seric 
3679389Seric puterrmsg(msg)
3689389Seric 	char *msg;
3699389Seric {
37063848Seric 	char msgcode = msg[0];
37163848Seric 
3729389Seric 	/* output the message as usual */
373*68692Seric 	putoutmsg(msg, HoldErrs, FALSE);
3749389Seric 
3759389Seric 	/* signal the error */
37664773Seric 	Errors++;
37763848Seric 	if (msgcode == '6')
37863848Seric 	{
37963848Seric 		/* notify the postmaster */
38063848Seric 		CurEnv->e_flags |= EF_PM_NOTIFY;
38163848Seric 	}
38267786Seric 	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
38364773Seric 	{
38464773Seric 		/* mark long-term fatal errors */
38564773Seric 		CurEnv->e_flags |= EF_FATALERRS;
38664773Seric 	}
3874711Seric }
3884711Seric /*
3894711Seric **  FMTMSG -- format a message into buffer.
3904711Seric **
3914711Seric **	Parameters:
3924711Seric **		eb -- error buffer to get result.
3934711Seric **		to -- the recipient tag for this message.
3944711Seric **		num -- arpanet error number.
39516901Seric **		en -- the error number to display.
3964711Seric **		fmt -- format of string.
3974711Seric **		a, b, c, d, e -- arguments.
3984711Seric **
3994711Seric **	Returns:
4004711Seric **		none.
4014711Seric **
4024711Seric **	Side Effects:
4034711Seric **		none.
4044711Seric */
4054063Seric 
40646928Sbostic static void
40756852Seric fmtmsg(eb, to, num, eno, fmt, ap)
4084711Seric 	register char *eb;
4094711Seric 	char *to;
4104711Seric 	char *num;
41116904Seric 	int eno;
4124711Seric 	char *fmt;
41356852Seric 	va_list ap;
4144711Seric {
4154711Seric 	char del;
41659596Seric 	char *meb;
4174711Seric 
4184711Seric 	/* output the reply code */
41924943Seric 	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
4204577Seric 	{
4214711Seric 		num = fmt;
4224711Seric 		fmt += 4;
4234711Seric 	}
4244711Seric 	if (num[3] == '-')
4254711Seric 		del = '-';
4264711Seric 	else
4274711Seric 		del = ' ';
4284711Seric 	(void) sprintf(eb, "%3.3s%c", num, del);
4294711Seric 	eb += 4;
4304063Seric 
4319372Seric 	/* output the file name and line number */
4329372Seric 	if (FileName != NULL)
4339372Seric 	{
4349372Seric 		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
4359372Seric 		eb += strlen(eb);
4369372Seric 	}
4379372Seric 
4384711Seric 	/* output the "to" person */
4394711Seric 	if (to != NULL && to[0] != '\0')
4404711Seric 	{
44166297Seric 		(void) sprintf(eb, "%s... ", shortenstring(to, 203));
4425201Seric 		while (*eb != '\0')
4435201Seric 			*eb++ &= 0177;
4444711Seric 	}
4454711Seric 
44659596Seric 	meb = eb;
44759596Seric 
4484711Seric 	/* output the message */
44956852Seric 	(void) vsprintf(eb, fmt, ap);
4505201Seric 	while (*eb != '\0')
4515201Seric 		*eb++ &= 0177;
4524711Seric 
4534711Seric 	/* output the error code, if any */
45416904Seric 	if (eno != 0)
4554711Seric 	{
45616904Seric 		(void) sprintf(eb, ": %s", errstring(eno));
4574711Seric 		eb += strlen(eb);
4584577Seric 	}
459295Seric }
46015136Seric /*
461*68692Seric **  BUFFER_ERRORS -- arrange to buffer future error messages
462*68692Seric **
463*68692Seric **	Parameters:
464*68692Seric **		none
465*68692Seric **
466*68692Seric **	Returns:
467*68692Seric **		none.
468*68692Seric */
469*68692Seric 
470*68692Seric void
471*68692Seric buffer_errors()
472*68692Seric {
473*68692Seric 	HeldMessageBuf[0] = '\0';
474*68692Seric 	HoldErrs = TRUE;
475*68692Seric }
476*68692Seric /*
477*68692Seric **  FLUSH_ERRORS -- flush the held error message buffer
478*68692Seric **
479*68692Seric **	Parameters:
480*68692Seric **		print -- if set, print the message, otherwise just
481*68692Seric **			delete it.
482*68692Seric **
483*68692Seric **	Returns:
484*68692Seric **		none.
485*68692Seric */
486*68692Seric 
487*68692Seric void
488*68692Seric flush_errors(print)
489*68692Seric 	bool print;
490*68692Seric {
491*68692Seric 	if (print && HeldMessageBuf[0] != '\0')
492*68692Seric 		putoutmsg(HeldMessageBuf, FALSE, TRUE);
493*68692Seric 	HeldMessageBuf[0] = '\0';
494*68692Seric 	HoldErrs = FALSE;
495*68692Seric }
496*68692Seric /*
49715136Seric **  ERRSTRING -- return string description of error code
49815136Seric **
49915136Seric **	Parameters:
50065751Seric **		errnum -- the error number to translate
50115136Seric **
50215136Seric **	Returns:
50365751Seric **		A string description of errnum.
50415136Seric **
50515136Seric **	Side Effects:
50615136Seric **		none.
50715136Seric */
50815136Seric 
50960089Seric const char *
51065751Seric errstring(errnum)
51165751Seric 	int errnum;
51215136Seric {
51365168Seric 	char *dnsmsg;
51463839Seric 	static char buf[MAXLINE];
51563839Seric # ifndef ERRLIST_PREDEFINED
51663839Seric 	extern char *sys_errlist[];
51715136Seric 	extern int sys_nerr;
51863839Seric # endif
51924943Seric # ifdef SMTP
52024943Seric 	extern char *SmtpPhase;
52156795Seric # endif /* SMTP */
52215136Seric 
52324943Seric 	/*
52424943Seric 	**  Handle special network error codes.
52524943Seric 	**
52624943Seric 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
52724943Seric 	*/
52824943Seric 
52965168Seric 	dnsmsg = NULL;
53065751Seric 	switch (errnum)
53124943Seric 	{
53265067Seric # if defined(DAEMON) && defined(ETIMEDOUT)
53324943Seric 	  case ETIMEDOUT:
53424943Seric 	  case ECONNRESET:
53565751Seric 		(void) strcpy(buf, sys_errlist[errnum]);
53624943Seric 		if (SmtpPhase != NULL)
53724943Seric 		{
53824943Seric 			(void) strcat(buf, " during ");
53924943Seric 			(void) strcat(buf, SmtpPhase);
54024943Seric 		}
54125050Seric 		if (CurHostName != NULL)
54224943Seric 		{
54324943Seric 			(void) strcat(buf, " with ");
54425050Seric 			(void) strcat(buf, CurHostName);
54524943Seric 		}
54624943Seric 		return (buf);
54724943Seric 
54824943Seric 	  case EHOSTDOWN:
54925050Seric 		if (CurHostName == NULL)
55024943Seric 			break;
55125050Seric 		(void) sprintf(buf, "Host %s is down", CurHostName);
55224943Seric 		return (buf);
55324943Seric 
55424943Seric 	  case ECONNREFUSED:
55525050Seric 		if (CurHostName == NULL)
55624943Seric 			break;
55725050Seric 		(void) sprintf(buf, "Connection refused by %s", CurHostName);
55824943Seric 		return (buf);
55965067Seric # endif
56025526Smiriam 
56163993Seric 	  case EOPENTIMEOUT:
56263993Seric 		return "Timeout on file open";
56363993Seric 
56466334Seric # if NAMED_BIND
56563993Seric 	  case HOST_NOT_FOUND + E_DNSBASE:
56665168Seric 		dnsmsg = "host not found";
56765168Seric 		break;
56858010Seric 
56963993Seric 	  case TRY_AGAIN + E_DNSBASE:
57065168Seric 		dnsmsg = "host name lookup failure";
57165168Seric 		break;
57258010Seric 
57363993Seric 	  case NO_RECOVERY + E_DNSBASE:
57465168Seric 		dnsmsg = "non-recoverable error";
57565168Seric 		break;
57658010Seric 
57763993Seric 	  case NO_DATA + E_DNSBASE:
57865168Seric 		dnsmsg = "no data known";
57965168Seric 		break;
58057736Seric # endif
58165067Seric 
58265067Seric 	  case EPERM:
58365067Seric 		/* SunOS gives "Not owner" -- this is the POSIX message */
58465067Seric 		return "Operation not permitted";
58524943Seric 	}
58624943Seric 
58765168Seric 	if (dnsmsg != NULL)
58865168Seric 	{
58965168Seric 		(void) strcpy(buf, "Name server: ");
59065168Seric 		if (CurHostName != NULL)
59165168Seric 		{
59265168Seric 			(void) strcat(buf, CurHostName);
59365168Seric 			(void) strcat(buf, ": ");
59465168Seric 		}
59565168Seric 		(void) strcat(buf, dnsmsg);
59665168Seric 		return buf;
59765168Seric 	}
59865168Seric 
59965751Seric 	if (errnum > 0 && errnum < sys_nerr)
60065751Seric 		return (sys_errlist[errnum]);
60115136Seric 
60265751Seric 	(void) sprintf(buf, "Error %d", errnum);
60315136Seric 	return (buf);
60415136Seric }
605