xref: /csrg-svn/usr.sbin/sendmail/src/err.c (revision 66864)
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*66864Seric static char sccsid[] = "@(#)err.c	8.27 (Berkeley) 04/18/94";
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:
30295Seric **		f -- the format string
31295Seric **		a, b, c, d, e -- parameters
32295Seric **
33295Seric **	Returns:
344084Seric **		none
357762Seric **		Through TopFrame if QuickAbort is set.
36295Seric **
37295Seric **	Side Effects:
381514Seric **		increments Errors.
391514Seric **		sets ExitStat.
40295Seric */
41295Seric 
4210147Seric char	MsgBuf[BUFSIZ*2];	/* text of most recent message */
434084Seric 
4463969Seric static void	fmtmsg();
4546928Sbostic 
4666334Seric #if NAMED_BIND && !defined(NO_DATA)
4763969Seric # define NO_DATA	NO_ADDRESS
4863969Seric #endif
4963969Seric 
5058824Seric void
51295Seric /*VARARGS1*/
5257642Seric #ifdef __STDC__
5360094Seric syserr(const char *fmt, ...)
5457642Seric #else
5557642Seric syserr(fmt, va_alist)
5660094Seric 	const char *fmt;
5757642Seric 	va_dcl
5857642Seric #endif
59295Seric {
6016901Seric 	register char *p;
6116901Seric 	int olderrno = errno;
6258690Seric 	bool panic;
6366006Seric #ifdef LOG
6466006Seric 	char *uname;
6566006Seric 	struct passwd *pw;
6666006Seric 	char ubuf[80];
6766006Seric #endif
6856852Seric 	VA_LOCAL_DECL
69295Seric 
7058690Seric 	panic = *fmt == '!';
7158690Seric 	if (panic)
7258690Seric 		fmt++;
7358690Seric 
747525Seric 	/* format and output the error message */
7516901Seric 	if (olderrno == 0)
7658151Seric 		p = "554";
777957Seric 	else
7858151Seric 		p = "451";
7956852Seric 	VA_START(fmt);
8056852Seric 	fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
8156852Seric 	VA_END;
829389Seric 	puterrmsg(MsgBuf);
834063Seric 
84295Seric 	/* determine exit status if not already set */
85295Seric 	if (ExitStat == EX_OK)
86295Seric 	{
8716901Seric 		if (olderrno == 0)
88295Seric 			ExitStat = EX_SOFTWARE;
89295Seric 		else
901598Seric 			ExitStat = EX_OSERR;
9166323Seric 		if (tTd(54, 1))
9266323Seric 			printf("syserr: ExitStat = %d\n", ExitStat);
93295Seric 	}
94295Seric 
95295Seric # ifdef LOG
9666006Seric 	pw = getpwuid(getuid());
9766006Seric 	if (pw != NULL)
9866006Seric 		uname = pw->pw_name;
9966006Seric 	else
10066006Seric 	{
10166006Seric 		uname = ubuf;
10266006Seric 		sprintf(ubuf, "UID%d", getuid());
10366006Seric 	}
10466006Seric 
1057674Seric 	if (LogLevel > 0)
10666006Seric 		syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s",
10725277Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
10866006Seric 			uname, &MsgBuf[4]);
10956795Seric # endif /* LOG */
11064725Seric 	if (olderrno == EMFILE)
11164731Seric 	{
11264725Seric 		printopenfds(TRUE);
11364731Seric 		mci_dump_all(TRUE);
11464731Seric 	}
11558690Seric 	if (panic)
11659156Seric 	{
11759156Seric #ifdef XLA
11859156Seric 		xla_all_end();
11959156Seric #endif
12058690Seric 		exit(EX_OSERR);
12159156Seric 	}
122295Seric 	errno = 0;
1237762Seric 	if (QuickAbort)
1247762Seric 		longjmp(TopFrame, 2);
125295Seric }
126295Seric /*
127295Seric **  USRERR -- Signal user error.
128295Seric **
129295Seric **	This is much like syserr except it is for user errors.
130295Seric **
131295Seric **	Parameters:
132295Seric **		fmt, a, b, c, d -- printf strings
133295Seric **
134295Seric **	Returns:
1354084Seric **		none
1367762Seric **		Through TopFrame if QuickAbort is set.
137295Seric **
138295Seric **	Side Effects:
1391514Seric **		increments Errors.
140295Seric */
141295Seric 
142295Seric /*VARARGS1*/
14358824Seric void
14457642Seric #ifdef __STDC__
14560094Seric usrerr(const char *fmt, ...)
14657642Seric #else
14757642Seric usrerr(fmt, va_alist)
14860094Seric 	const char *fmt;
14957642Seric 	va_dcl
15057642Seric #endif
151295Seric {
15256852Seric 	VA_LOCAL_DECL
153295Seric 
154295Seric 	if (SuprErrs)
1554084Seric 		return;
156295Seric 
15756852Seric 	VA_START(fmt);
15858524Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
15956852Seric 	VA_END;
1609389Seric 	puterrmsg(MsgBuf);
1618239Seric 
16251951Seric # ifdef LOG
16358020Seric 	if (LogLevel > 3 && LogUsrErrs)
16451951Seric 		syslog(LOG_NOTICE, "%s: %s",
16551951Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
16651951Seric 			&MsgBuf[4]);
16756795Seric # endif /* LOG */
16851951Seric 
1697762Seric 	if (QuickAbort)
1707762Seric 		longjmp(TopFrame, 1);
1714063Seric }
1724063Seric /*
1734063Seric **  MESSAGE -- print message (not necessarily an error)
1744063Seric **
1754063Seric **	Parameters:
17659581Seric **		msg -- the message (printf fmt) -- it can begin with
17759581Seric **			an SMTP reply code.  If not, 050 is assumed.
1784063Seric **		a, b, c, d, e -- printf arguments
1794063Seric **
1804063Seric **	Returns:
1814063Seric **		none
1824063Seric **
1834063Seric **	Side Effects:
1844063Seric **		none.
1854063Seric */
1864063Seric 
1874084Seric /*VARARGS2*/
18858826Seric void
18957642Seric #ifdef __STDC__
19060094Seric message(const char *msg, ...)
19157642Seric #else
19258151Seric message(msg, va_alist)
19360094Seric 	const char *msg;
19457642Seric 	va_dcl
19557642Seric #endif
1964063Seric {
19756852Seric 	VA_LOCAL_DECL
19856852Seric 
1994711Seric 	errno = 0;
20056852Seric 	VA_START(msg);
20158151Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
20256852Seric 	VA_END;
20363753Seric 	putoutmsg(MsgBuf, FALSE);
2047613Seric }
2057613Seric /*
2068239Seric **  NMESSAGE -- print message (not necessarily an error)
2078239Seric **
2088239Seric **	Just like "message" except it never puts the to... tag on.
2098239Seric **
2108239Seric **	Parameters:
2118239Seric **		num -- the default ARPANET error number (in ascii)
2128239Seric **		msg -- the message (printf fmt) -- if it begins
21324943Seric **			with three digits, this number overrides num.
2148239Seric **		a, b, c, d, e -- printf arguments
2158239Seric **
2168239Seric **	Returns:
2178239Seric **		none
2188239Seric **
2198239Seric **	Side Effects:
2208239Seric **		none.
2218239Seric */
2228239Seric 
2238239Seric /*VARARGS2*/
22458826Seric void
22557642Seric #ifdef __STDC__
22660094Seric nmessage(const char *msg, ...)
22757642Seric #else
22858151Seric nmessage(msg, va_alist)
22960094Seric 	const char *msg;
23057642Seric 	va_dcl
23157642Seric #endif
2328239Seric {
23356852Seric 	VA_LOCAL_DECL
23456852Seric 
2358239Seric 	errno = 0;
23656852Seric 	VA_START(msg);
23758151Seric 	fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
23856852Seric 	VA_END;
23963753Seric 	putoutmsg(MsgBuf, FALSE);
2408239Seric }
2418239Seric /*
24263753Seric **  PUTOUTMSG -- output error message to transcript and channel
2437613Seric **
2447613Seric **	Parameters:
2457613Seric **		msg -- message to output (in SMTP format).
2469108Seric **		holdmsg -- if TRUE, don't output a copy of the message to
2479108Seric **			our output channel.
2487613Seric **
2497613Seric **	Returns:
2507613Seric **		none.
2517613Seric **
2527613Seric **	Side Effects:
2537613Seric **		Outputs msg to the transcript.
2547613Seric **		If appropriate, outputs it to the channel.
2557613Seric **		Deletes SMTP reply code number as appropriate.
2567613Seric */
2574711Seric 
25863753Seric putoutmsg(msg, holdmsg)
2597613Seric 	char *msg;
2609108Seric 	bool holdmsg;
2617613Seric {
26264249Seric 	/* display for debugging */
26364249Seric 	if (tTd(54, 8))
26464249Seric 		printf("--- %s%s\n", msg, holdmsg ? " (held)" : "");
26564249Seric 
26614900Seric 	/* output to transcript if serious */
26763848Seric 	if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
26814900Seric 		fprintf(CurEnv->e_xfp, "%s\n", msg);
2694711Seric 
2704711Seric 	/* output to channel if appropriate */
27160421Seric 	if (holdmsg || (!Verbose && msg[0] == '0'))
27260421Seric 		return;
27360421Seric 
27463848Seric 	/* map warnings to something SMTP can handle */
27563848Seric 	if (msg[0] == '6')
27663848Seric 		msg[0] = '5';
27763848Seric 
27860421Seric 	(void) fflush(stdout);
27966017Seric 
28066017Seric 	/* if DisConnected, OutChannel now points to the transcript */
28166017Seric 	if (!DisConnected &&
28266017Seric 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
28360421Seric 		fprintf(OutChannel, "%s\r\n", msg);
28460421Seric 	else
28560421Seric 		fprintf(OutChannel, "%s\n", &msg[4]);
28663753Seric 	if (TrafficLogFile != NULL)
28763753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(),
28865580Seric 			(OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
28960421Seric 	if (msg[3] == ' ')
29060421Seric 		(void) fflush(OutChannel);
29166017Seric 	if (!ferror(OutChannel) || DisConnected)
29260421Seric 		return;
29360421Seric 
29464499Seric 	/*
29564499Seric 	**  Error on output -- if reporting lost channel, just ignore it.
29664499Seric 	**  Also, ignore errors from QUIT response (221 message) -- some
29764499Seric 	**	rude servers don't read result.
29864499Seric 	*/
29964499Seric 
30064499Seric 	if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0)
30160421Seric 		return;
30260421Seric 
30360421Seric 	/* can't call syserr, 'cause we are using MsgBuf */
30460421Seric 	HoldErrs = TRUE;
30560283Seric #ifdef LOG
30660421Seric 	if (LogLevel > 0)
30760421Seric 		syslog(LOG_CRIT,
308*66864Seric 			"%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
30960421Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
31064123Seric 			CurHostName == NULL ? "NO-HOST" : CurHostName,
311*66864Seric 			msg, errstring(errno));
31260283Seric #endif
3139389Seric }
3149389Seric /*
31563753Seric **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
3169389Seric **
3179389Seric **	Parameters:
3189389Seric **		msg -- the message to output.
3199389Seric **
3209389Seric **	Returns:
3219389Seric **		none.
3229389Seric **
3239389Seric **	Side Effects:
3249389Seric **		Sets the fatal error bit in the envelope as appropriate.
3259389Seric */
3268239Seric 
3279389Seric puterrmsg(msg)
3289389Seric 	char *msg;
3299389Seric {
33063848Seric 	char msgcode = msg[0];
33163848Seric 
3329389Seric 	/* output the message as usual */
33363753Seric 	putoutmsg(msg, HoldErrs);
3349389Seric 
3359389Seric 	/* signal the error */
33664773Seric 	Errors++;
33763848Seric 	if (msgcode == '6')
33863848Seric 	{
33963848Seric 		/* notify the postmaster */
34063848Seric 		CurEnv->e_flags |= EF_PM_NOTIFY;
34163848Seric 	}
34264773Seric 	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
34364773Seric 	{
34464773Seric 		/* mark long-term fatal errors */
34564773Seric 		CurEnv->e_flags |= EF_FATALERRS;
34664773Seric 	}
3474711Seric }
3484711Seric /*
3494711Seric **  FMTMSG -- format a message into buffer.
3504711Seric **
3514711Seric **	Parameters:
3524711Seric **		eb -- error buffer to get result.
3534711Seric **		to -- the recipient tag for this message.
3544711Seric **		num -- arpanet error number.
35516901Seric **		en -- the error number to display.
3564711Seric **		fmt -- format of string.
3574711Seric **		a, b, c, d, e -- arguments.
3584711Seric **
3594711Seric **	Returns:
3604711Seric **		none.
3614711Seric **
3624711Seric **	Side Effects:
3634711Seric **		none.
3644711Seric */
3654063Seric 
36646928Sbostic static void
36756852Seric fmtmsg(eb, to, num, eno, fmt, ap)
3684711Seric 	register char *eb;
3694711Seric 	char *to;
3704711Seric 	char *num;
37116904Seric 	int eno;
3724711Seric 	char *fmt;
37356852Seric 	va_list ap;
3744711Seric {
3754711Seric 	char del;
37659596Seric 	char *meb;
3774711Seric 
3784711Seric 	/* output the reply code */
37924943Seric 	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
3804577Seric 	{
3814711Seric 		num = fmt;
3824711Seric 		fmt += 4;
3834711Seric 	}
3844711Seric 	if (num[3] == '-')
3854711Seric 		del = '-';
3864711Seric 	else
3874711Seric 		del = ' ';
3884711Seric 	(void) sprintf(eb, "%3.3s%c", num, del);
3894711Seric 	eb += 4;
3904063Seric 
3919372Seric 	/* output the file name and line number */
3929372Seric 	if (FileName != NULL)
3939372Seric 	{
3949372Seric 		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
3959372Seric 		eb += strlen(eb);
3969372Seric 	}
3979372Seric 
3984711Seric 	/* output the "to" person */
3994711Seric 	if (to != NULL && to[0] != '\0')
4004711Seric 	{
40166297Seric 		(void) sprintf(eb, "%s... ", shortenstring(to, 203));
4025201Seric 		while (*eb != '\0')
4035201Seric 			*eb++ &= 0177;
4044711Seric 	}
4054711Seric 
40659596Seric 	meb = eb;
40759596Seric 
4084711Seric 	/* output the message */
40956852Seric 	(void) vsprintf(eb, fmt, ap);
4105201Seric 	while (*eb != '\0')
4115201Seric 		*eb++ &= 0177;
4124711Seric 
4134711Seric 	/* output the error code, if any */
41416904Seric 	if (eno != 0)
4154711Seric 	{
41616904Seric 		(void) sprintf(eb, ": %s", errstring(eno));
4174711Seric 		eb += strlen(eb);
4184577Seric 	}
41959596Seric 
42064695Seric 	if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4'))
42164695Seric 	{
42264695Seric 		if (CurEnv->e_message != NULL)
42364695Seric 			free(CurEnv->e_message);
42459596Seric 		CurEnv->e_message = newstr(meb);
42564695Seric 	}
426295Seric }
42715136Seric /*
42815136Seric **  ERRSTRING -- return string description of error code
42915136Seric **
43015136Seric **	Parameters:
43165751Seric **		errnum -- the error number to translate
43215136Seric **
43315136Seric **	Returns:
43465751Seric **		A string description of errnum.
43515136Seric **
43615136Seric **	Side Effects:
43715136Seric **		none.
43815136Seric */
43915136Seric 
44060089Seric const char *
44165751Seric errstring(errnum)
44265751Seric 	int errnum;
44315136Seric {
44465168Seric 	char *dnsmsg;
44563839Seric 	static char buf[MAXLINE];
44663839Seric # ifndef ERRLIST_PREDEFINED
44763839Seric 	extern char *sys_errlist[];
44815136Seric 	extern int sys_nerr;
44963839Seric # endif
45024943Seric # ifdef SMTP
45124943Seric 	extern char *SmtpPhase;
45256795Seric # endif /* SMTP */
45315136Seric 
45424943Seric 	/*
45524943Seric 	**  Handle special network error codes.
45624943Seric 	**
45724943Seric 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
45824943Seric 	*/
45924943Seric 
46065168Seric 	dnsmsg = NULL;
46165751Seric 	switch (errnum)
46224943Seric 	{
46365067Seric # if defined(DAEMON) && defined(ETIMEDOUT)
46424943Seric 	  case ETIMEDOUT:
46524943Seric 	  case ECONNRESET:
46665751Seric 		(void) strcpy(buf, sys_errlist[errnum]);
46724943Seric 		if (SmtpPhase != NULL)
46824943Seric 		{
46924943Seric 			(void) strcat(buf, " during ");
47024943Seric 			(void) strcat(buf, SmtpPhase);
47124943Seric 		}
47225050Seric 		if (CurHostName != NULL)
47324943Seric 		{
47424943Seric 			(void) strcat(buf, " with ");
47525050Seric 			(void) strcat(buf, CurHostName);
47624943Seric 		}
47724943Seric 		return (buf);
47824943Seric 
47924943Seric 	  case EHOSTDOWN:
48025050Seric 		if (CurHostName == NULL)
48124943Seric 			break;
48225050Seric 		(void) sprintf(buf, "Host %s is down", CurHostName);
48324943Seric 		return (buf);
48424943Seric 
48524943Seric 	  case ECONNREFUSED:
48625050Seric 		if (CurHostName == NULL)
48724943Seric 			break;
48825050Seric 		(void) sprintf(buf, "Connection refused by %s", CurHostName);
48924943Seric 		return (buf);
49065067Seric # endif
49125526Smiriam 
49263993Seric 	  case EOPENTIMEOUT:
49363993Seric 		return "Timeout on file open";
49463993Seric 
49566334Seric # if NAMED_BIND
49663993Seric 	  case HOST_NOT_FOUND + E_DNSBASE:
49765168Seric 		dnsmsg = "host not found";
49865168Seric 		break;
49958010Seric 
50063993Seric 	  case TRY_AGAIN + E_DNSBASE:
50165168Seric 		dnsmsg = "host name lookup failure";
50265168Seric 		break;
50358010Seric 
50463993Seric 	  case NO_RECOVERY + E_DNSBASE:
50565168Seric 		dnsmsg = "non-recoverable error";
50665168Seric 		break;
50758010Seric 
50863993Seric 	  case NO_DATA + E_DNSBASE:
50965168Seric 		dnsmsg = "no data known";
51065168Seric 		break;
51157736Seric # endif
51265067Seric 
51365067Seric 	  case EPERM:
51465067Seric 		/* SunOS gives "Not owner" -- this is the POSIX message */
51565067Seric 		return "Operation not permitted";
51624943Seric 	}
51724943Seric 
51865168Seric 	if (dnsmsg != NULL)
51965168Seric 	{
52065168Seric 		(void) strcpy(buf, "Name server: ");
52165168Seric 		if (CurHostName != NULL)
52265168Seric 		{
52365168Seric 			(void) strcat(buf, CurHostName);
52465168Seric 			(void) strcat(buf, ": ");
52565168Seric 		}
52665168Seric 		(void) strcat(buf, dnsmsg);
52765168Seric 		return buf;
52865168Seric 	}
52965168Seric 
53065751Seric 	if (errnum > 0 && errnum < sys_nerr)
53165751Seric 		return (sys_errlist[errnum]);
53215136Seric 
53365751Seric 	(void) sprintf(buf, "Error %d", errnum);
53415136Seric 	return (buf);
53515136Seric }
536