xref: /csrg-svn/usr.sbin/sendmail/src/err.c (revision 66006)
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*66006Seric static char sccsid[] = "@(#)err.c	8.22 (Berkeley) 02/06/94";
1133729Sbostic #endif /* not lint */
1222705Sdist 
133311Seric # include "sendmail.h"
1424943Seric # include <errno.h>
1525526Smiriam # include <netdb.h>
16*66006Seric # 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 
4663969Seric #if defined(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;
63*66006Seric #ifdef LOG
64*66006Seric 	char *uname;
65*66006Seric 	struct passwd *pw;
66*66006Seric 	char ubuf[80];
67*66006Seric #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;
91295Seric 	}
92295Seric 
93295Seric # ifdef LOG
94*66006Seric 	pw = getpwuid(getuid());
95*66006Seric 	if (pw != NULL)
96*66006Seric 		uname = pw->pw_name;
97*66006Seric 	else
98*66006Seric 	{
99*66006Seric 		uname = ubuf;
100*66006Seric 		sprintf(ubuf, "UID%d", getuid());
101*66006Seric 	}
102*66006Seric 
1037674Seric 	if (LogLevel > 0)
104*66006Seric 		syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s",
10525277Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
106*66006Seric 			uname, &MsgBuf[4]);
10756795Seric # endif /* LOG */
10864725Seric 	if (olderrno == EMFILE)
10964731Seric 	{
11064725Seric 		printopenfds(TRUE);
11164731Seric 		mci_dump_all(TRUE);
11264731Seric 	}
11358690Seric 	if (panic)
11459156Seric 	{
11559156Seric #ifdef XLA
11659156Seric 		xla_all_end();
11759156Seric #endif
11858690Seric 		exit(EX_OSERR);
11959156Seric 	}
120295Seric 	errno = 0;
1217762Seric 	if (QuickAbort)
1227762Seric 		longjmp(TopFrame, 2);
123295Seric }
124295Seric /*
125295Seric **  USRERR -- Signal user error.
126295Seric **
127295Seric **	This is much like syserr except it is for user errors.
128295Seric **
129295Seric **	Parameters:
130295Seric **		fmt, a, b, c, d -- printf strings
131295Seric **
132295Seric **	Returns:
1334084Seric **		none
1347762Seric **		Through TopFrame if QuickAbort is set.
135295Seric **
136295Seric **	Side Effects:
1371514Seric **		increments Errors.
138295Seric */
139295Seric 
140295Seric /*VARARGS1*/
14158824Seric void
14257642Seric #ifdef __STDC__
14360094Seric usrerr(const char *fmt, ...)
14457642Seric #else
14557642Seric usrerr(fmt, va_alist)
14660094Seric 	const char *fmt;
14757642Seric 	va_dcl
14857642Seric #endif
149295Seric {
15056852Seric 	VA_LOCAL_DECL
151295Seric 
152295Seric 	if (SuprErrs)
1534084Seric 		return;
154295Seric 
15556852Seric 	VA_START(fmt);
15658524Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
15756852Seric 	VA_END;
1589389Seric 	puterrmsg(MsgBuf);
1598239Seric 
16051951Seric # ifdef LOG
16158020Seric 	if (LogLevel > 3 && LogUsrErrs)
16251951Seric 		syslog(LOG_NOTICE, "%s: %s",
16351951Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
16451951Seric 			&MsgBuf[4]);
16556795Seric # endif /* LOG */
16651951Seric 
1677762Seric 	if (QuickAbort)
1687762Seric 		longjmp(TopFrame, 1);
1694063Seric }
1704063Seric /*
1714063Seric **  MESSAGE -- print message (not necessarily an error)
1724063Seric **
1734063Seric **	Parameters:
17459581Seric **		msg -- the message (printf fmt) -- it can begin with
17559581Seric **			an SMTP reply code.  If not, 050 is assumed.
1764063Seric **		a, b, c, d, e -- printf arguments
1774063Seric **
1784063Seric **	Returns:
1794063Seric **		none
1804063Seric **
1814063Seric **	Side Effects:
1824063Seric **		none.
1834063Seric */
1844063Seric 
1854084Seric /*VARARGS2*/
18658826Seric void
18757642Seric #ifdef __STDC__
18860094Seric message(const char *msg, ...)
18957642Seric #else
19058151Seric message(msg, va_alist)
19160094Seric 	const char *msg;
19257642Seric 	va_dcl
19357642Seric #endif
1944063Seric {
19556852Seric 	VA_LOCAL_DECL
19656852Seric 
1974711Seric 	errno = 0;
19856852Seric 	VA_START(msg);
19958151Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
20056852Seric 	VA_END;
20163753Seric 	putoutmsg(MsgBuf, FALSE);
2027613Seric }
2037613Seric /*
2048239Seric **  NMESSAGE -- print message (not necessarily an error)
2058239Seric **
2068239Seric **	Just like "message" except it never puts the to... tag on.
2078239Seric **
2088239Seric **	Parameters:
2098239Seric **		num -- the default ARPANET error number (in ascii)
2108239Seric **		msg -- the message (printf fmt) -- if it begins
21124943Seric **			with three digits, this number overrides num.
2128239Seric **		a, b, c, d, e -- printf arguments
2138239Seric **
2148239Seric **	Returns:
2158239Seric **		none
2168239Seric **
2178239Seric **	Side Effects:
2188239Seric **		none.
2198239Seric */
2208239Seric 
2218239Seric /*VARARGS2*/
22258826Seric void
22357642Seric #ifdef __STDC__
22460094Seric nmessage(const char *msg, ...)
22557642Seric #else
22658151Seric nmessage(msg, va_alist)
22760094Seric 	const char *msg;
22857642Seric 	va_dcl
22957642Seric #endif
2308239Seric {
23156852Seric 	VA_LOCAL_DECL
23256852Seric 
2338239Seric 	errno = 0;
23456852Seric 	VA_START(msg);
23558151Seric 	fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
23656852Seric 	VA_END;
23763753Seric 	putoutmsg(MsgBuf, FALSE);
2388239Seric }
2398239Seric /*
24063753Seric **  PUTOUTMSG -- output error message to transcript and channel
2417613Seric **
2427613Seric **	Parameters:
2437613Seric **		msg -- message to output (in SMTP format).
2449108Seric **		holdmsg -- if TRUE, don't output a copy of the message to
2459108Seric **			our output channel.
2467613Seric **
2477613Seric **	Returns:
2487613Seric **		none.
2497613Seric **
2507613Seric **	Side Effects:
2517613Seric **		Outputs msg to the transcript.
2527613Seric **		If appropriate, outputs it to the channel.
2537613Seric **		Deletes SMTP reply code number as appropriate.
2547613Seric */
2554711Seric 
25663753Seric putoutmsg(msg, holdmsg)
2577613Seric 	char *msg;
2589108Seric 	bool holdmsg;
2597613Seric {
26064249Seric 	/* display for debugging */
26164249Seric 	if (tTd(54, 8))
26264249Seric 		printf("--- %s%s\n", msg, holdmsg ? " (held)" : "");
26364249Seric 
26414900Seric 	/* output to transcript if serious */
26563848Seric 	if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
26614900Seric 		fprintf(CurEnv->e_xfp, "%s\n", msg);
2674711Seric 
2684711Seric 	/* output to channel if appropriate */
26960421Seric 	if (holdmsg || (!Verbose && msg[0] == '0'))
27060421Seric 		return;
27160421Seric 
27263848Seric 	/* map warnings to something SMTP can handle */
27363848Seric 	if (msg[0] == '6')
27463848Seric 		msg[0] = '5';
27563848Seric 
27660421Seric 	(void) fflush(stdout);
27765983Seric 	if (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)
27860421Seric 		fprintf(OutChannel, "%s\r\n", msg);
27960421Seric 	else
28060421Seric 		fprintf(OutChannel, "%s\n", &msg[4]);
28163753Seric 	if (TrafficLogFile != NULL)
28263753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(),
28365580Seric 			(OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
28460421Seric 	if (msg[3] == ' ')
28560421Seric 		(void) fflush(OutChannel);
28660421Seric 	if (!ferror(OutChannel))
28760421Seric 		return;
28860421Seric 
28964499Seric 	/*
29064499Seric 	**  Error on output -- if reporting lost channel, just ignore it.
29164499Seric 	**  Also, ignore errors from QUIT response (221 message) -- some
29264499Seric 	**	rude servers don't read result.
29364499Seric 	*/
29464499Seric 
29564499Seric 	if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0)
29660421Seric 		return;
29760421Seric 
29860421Seric 	/* can't call syserr, 'cause we are using MsgBuf */
29960421Seric 	HoldErrs = TRUE;
30060283Seric #ifdef LOG
30160421Seric 	if (LogLevel > 0)
30260421Seric 		syslog(LOG_CRIT,
30364499Seric 			"%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %m",
30460421Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
30564123Seric 			CurHostName == NULL ? "NO-HOST" : CurHostName,
30664123Seric 			msg);
30760283Seric #endif
3089389Seric }
3099389Seric /*
31063753Seric **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
3119389Seric **
3129389Seric **	Parameters:
3139389Seric **		msg -- the message to output.
3149389Seric **
3159389Seric **	Returns:
3169389Seric **		none.
3179389Seric **
3189389Seric **	Side Effects:
3199389Seric **		Sets the fatal error bit in the envelope as appropriate.
3209389Seric */
3218239Seric 
3229389Seric puterrmsg(msg)
3239389Seric 	char *msg;
3249389Seric {
32563848Seric 	char msgcode = msg[0];
32663848Seric 
3279389Seric 	/* output the message as usual */
32863753Seric 	putoutmsg(msg, HoldErrs);
3299389Seric 
3309389Seric 	/* signal the error */
33164773Seric 	Errors++;
33263848Seric 	if (msgcode == '6')
33363848Seric 	{
33463848Seric 		/* notify the postmaster */
33563848Seric 		CurEnv->e_flags |= EF_PM_NOTIFY;
33663848Seric 	}
33764773Seric 	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
33864773Seric 	{
33964773Seric 		/* mark long-term fatal errors */
34064773Seric 		CurEnv->e_flags |= EF_FATALERRS;
34164773Seric 	}
3424711Seric }
3434711Seric /*
3444711Seric **  FMTMSG -- format a message into buffer.
3454711Seric **
3464711Seric **	Parameters:
3474711Seric **		eb -- error buffer to get result.
3484711Seric **		to -- the recipient tag for this message.
3494711Seric **		num -- arpanet error number.
35016901Seric **		en -- the error number to display.
3514711Seric **		fmt -- format of string.
3524711Seric **		a, b, c, d, e -- arguments.
3534711Seric **
3544711Seric **	Returns:
3554711Seric **		none.
3564711Seric **
3574711Seric **	Side Effects:
3584711Seric **		none.
3594711Seric */
3604063Seric 
36146928Sbostic static void
36256852Seric fmtmsg(eb, to, num, eno, fmt, ap)
3634711Seric 	register char *eb;
3644711Seric 	char *to;
3654711Seric 	char *num;
36616904Seric 	int eno;
3674711Seric 	char *fmt;
36856852Seric 	va_list ap;
3694711Seric {
3704711Seric 	char del;
37159596Seric 	char *meb;
3724711Seric 
3734711Seric 	/* output the reply code */
37424943Seric 	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
3754577Seric 	{
3764711Seric 		num = fmt;
3774711Seric 		fmt += 4;
3784711Seric 	}
3794711Seric 	if (num[3] == '-')
3804711Seric 		del = '-';
3814711Seric 	else
3824711Seric 		del = ' ';
3834711Seric 	(void) sprintf(eb, "%3.3s%c", num, del);
3844711Seric 	eb += 4;
3854063Seric 
3869372Seric 	/* output the file name and line number */
3879372Seric 	if (FileName != NULL)
3889372Seric 	{
3899372Seric 		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
3909372Seric 		eb += strlen(eb);
3919372Seric 	}
3929372Seric 
3934711Seric 	/* output the "to" person */
3944711Seric 	if (to != NULL && to[0] != '\0')
3954711Seric 	{
3964711Seric 		(void) sprintf(eb, "%s... ", to);
3975201Seric 		while (*eb != '\0')
3985201Seric 			*eb++ &= 0177;
3994711Seric 	}
4004711Seric 
40159596Seric 	meb = eb;
40259596Seric 
4034711Seric 	/* output the message */
40456852Seric 	(void) vsprintf(eb, fmt, ap);
4055201Seric 	while (*eb != '\0')
4065201Seric 		*eb++ &= 0177;
4074711Seric 
4084711Seric 	/* output the error code, if any */
40916904Seric 	if (eno != 0)
4104711Seric 	{
41116904Seric 		(void) sprintf(eb, ": %s", errstring(eno));
4124711Seric 		eb += strlen(eb);
4134577Seric 	}
41459596Seric 
41564695Seric 	if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4'))
41664695Seric 	{
41764695Seric 		if (CurEnv->e_message != NULL)
41864695Seric 			free(CurEnv->e_message);
41959596Seric 		CurEnv->e_message = newstr(meb);
42064695Seric 	}
421295Seric }
42215136Seric /*
42315136Seric **  ERRSTRING -- return string description of error code
42415136Seric **
42515136Seric **	Parameters:
42665751Seric **		errnum -- the error number to translate
42715136Seric **
42815136Seric **	Returns:
42965751Seric **		A string description of errnum.
43015136Seric **
43115136Seric **	Side Effects:
43215136Seric **		none.
43315136Seric */
43415136Seric 
43560089Seric const char *
43665751Seric errstring(errnum)
43765751Seric 	int errnum;
43815136Seric {
43965168Seric 	char *dnsmsg;
44063839Seric 	static char buf[MAXLINE];
44163839Seric # ifndef ERRLIST_PREDEFINED
44263839Seric 	extern char *sys_errlist[];
44315136Seric 	extern int sys_nerr;
44463839Seric # endif
44524943Seric # ifdef SMTP
44624943Seric 	extern char *SmtpPhase;
44756795Seric # endif /* SMTP */
44815136Seric 
44924943Seric 	/*
45024943Seric 	**  Handle special network error codes.
45124943Seric 	**
45224943Seric 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
45324943Seric 	*/
45424943Seric 
45565168Seric 	dnsmsg = NULL;
45665751Seric 	switch (errnum)
45724943Seric 	{
45865067Seric # if defined(DAEMON) && defined(ETIMEDOUT)
45924943Seric 	  case ETIMEDOUT:
46024943Seric 	  case ECONNRESET:
46165751Seric 		(void) strcpy(buf, sys_errlist[errnum]);
46224943Seric 		if (SmtpPhase != NULL)
46324943Seric 		{
46424943Seric 			(void) strcat(buf, " during ");
46524943Seric 			(void) strcat(buf, SmtpPhase);
46624943Seric 		}
46725050Seric 		if (CurHostName != NULL)
46824943Seric 		{
46924943Seric 			(void) strcat(buf, " with ");
47025050Seric 			(void) strcat(buf, CurHostName);
47124943Seric 		}
47224943Seric 		return (buf);
47324943Seric 
47424943Seric 	  case EHOSTDOWN:
47525050Seric 		if (CurHostName == NULL)
47624943Seric 			break;
47725050Seric 		(void) sprintf(buf, "Host %s is down", CurHostName);
47824943Seric 		return (buf);
47924943Seric 
48024943Seric 	  case ECONNREFUSED:
48125050Seric 		if (CurHostName == NULL)
48224943Seric 			break;
48325050Seric 		(void) sprintf(buf, "Connection refused by %s", CurHostName);
48424943Seric 		return (buf);
48565067Seric # endif
48625526Smiriam 
48763993Seric 	  case EOPENTIMEOUT:
48863993Seric 		return "Timeout on file open";
48963993Seric 
49057736Seric # ifdef NAMED_BIND
49163993Seric 	  case HOST_NOT_FOUND + E_DNSBASE:
49265168Seric 		dnsmsg = "host not found";
49365168Seric 		break;
49458010Seric 
49563993Seric 	  case TRY_AGAIN + E_DNSBASE:
49665168Seric 		dnsmsg = "host name lookup failure";
49765168Seric 		break;
49858010Seric 
49963993Seric 	  case NO_RECOVERY + E_DNSBASE:
50065168Seric 		dnsmsg = "non-recoverable error";
50165168Seric 		break;
50258010Seric 
50363993Seric 	  case NO_DATA + E_DNSBASE:
50465168Seric 		dnsmsg = "no data known";
50565168Seric 		break;
50657736Seric # endif
50765067Seric 
50865067Seric 	  case EPERM:
50965067Seric 		/* SunOS gives "Not owner" -- this is the POSIX message */
51065067Seric 		return "Operation not permitted";
51124943Seric 	}
51224943Seric 
51365168Seric 	if (dnsmsg != NULL)
51465168Seric 	{
51565168Seric 		(void) strcpy(buf, "Name server: ");
51665168Seric 		if (CurHostName != NULL)
51765168Seric 		{
51865168Seric 			(void) strcat(buf, CurHostName);
51965168Seric 			(void) strcat(buf, ": ");
52065168Seric 		}
52165168Seric 		(void) strcat(buf, dnsmsg);
52265168Seric 		return buf;
52365168Seric 	}
52465168Seric 
52565751Seric 	if (errnum > 0 && errnum < sys_nerr)
52665751Seric 		return (sys_errlist[errnum]);
52715136Seric 
52865751Seric 	(void) sprintf(buf, "Error %d", errnum);
52915136Seric 	return (buf);
53015136Seric }
531