xref: /csrg-svn/usr.sbin/sendmail/src/err.c (revision 65067)
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*65067Seric static char sccsid[] = "@(#)err.c	8.16 (Berkeley) 12/10/93";
1133729Sbostic #endif /* not lint */
1222705Sdist 
133311Seric # include "sendmail.h"
1424943Seric # include <errno.h>
1525526Smiriam # include <netdb.h>
16295Seric 
17295Seric /*
181514Seric **  SYSERR -- Print error message.
19295Seric **
20295Seric **	Prints an error message via printf to the diagnostic
21295Seric **	output.  If LOG is defined, it logs it also.
22295Seric **
2358690Seric **	If the first character of the syserr message is `!' it will
2458690Seric **	log this as an ALERT message and exit immediately.  This can
2558690Seric **	leave queue files in an indeterminate state, so it should not
2658690Seric **	be used lightly.
2758690Seric **
28295Seric **	Parameters:
29295Seric **		f -- the format string
30295Seric **		a, b, c, d, e -- parameters
31295Seric **
32295Seric **	Returns:
334084Seric **		none
347762Seric **		Through TopFrame if QuickAbort is set.
35295Seric **
36295Seric **	Side Effects:
371514Seric **		increments Errors.
381514Seric **		sets ExitStat.
39295Seric */
40295Seric 
4110147Seric char	MsgBuf[BUFSIZ*2];	/* text of most recent message */
424084Seric 
4363969Seric static void	fmtmsg();
4446928Sbostic 
4563969Seric #if defined(NAMED_BIND) && !defined(NO_DATA)
4663969Seric # define NO_DATA	NO_ADDRESS
4763969Seric #endif
4863969Seric 
4958824Seric void
50295Seric /*VARARGS1*/
5157642Seric #ifdef __STDC__
5260094Seric syserr(const char *fmt, ...)
5357642Seric #else
5457642Seric syserr(fmt, va_alist)
5560094Seric 	const char *fmt;
5657642Seric 	va_dcl
5757642Seric #endif
58295Seric {
5916901Seric 	register char *p;
6016901Seric 	int olderrno = errno;
6158690Seric 	bool panic;
6256852Seric 	VA_LOCAL_DECL
63295Seric 
6458690Seric 	panic = *fmt == '!';
6558690Seric 	if (panic)
6658690Seric 		fmt++;
6758690Seric 
687525Seric 	/* format and output the error message */
6916901Seric 	if (olderrno == 0)
7058151Seric 		p = "554";
717957Seric 	else
7258151Seric 		p = "451";
7356852Seric 	VA_START(fmt);
7456852Seric 	fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
7556852Seric 	VA_END;
769389Seric 	puterrmsg(MsgBuf);
774063Seric 
78295Seric 	/* determine exit status if not already set */
79295Seric 	if (ExitStat == EX_OK)
80295Seric 	{
8116901Seric 		if (olderrno == 0)
82295Seric 			ExitStat = EX_SOFTWARE;
83295Seric 		else
841598Seric 			ExitStat = EX_OSERR;
85295Seric 	}
86295Seric 
87295Seric # ifdef LOG
887674Seric 	if (LogLevel > 0)
8958690Seric 		syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR: %s",
9025277Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
9125277Seric 			&MsgBuf[4]);
9256795Seric # endif /* LOG */
9364725Seric 	if (olderrno == EMFILE)
9464731Seric 	{
9564725Seric 		printopenfds(TRUE);
9664731Seric 		mci_dump_all(TRUE);
9764731Seric 	}
9858690Seric 	if (panic)
9959156Seric 	{
10059156Seric #ifdef XLA
10159156Seric 		xla_all_end();
10259156Seric #endif
10358690Seric 		exit(EX_OSERR);
10459156Seric 	}
105295Seric 	errno = 0;
1067762Seric 	if (QuickAbort)
1077762Seric 		longjmp(TopFrame, 2);
108295Seric }
109295Seric /*
110295Seric **  USRERR -- Signal user error.
111295Seric **
112295Seric **	This is much like syserr except it is for user errors.
113295Seric **
114295Seric **	Parameters:
115295Seric **		fmt, a, b, c, d -- printf strings
116295Seric **
117295Seric **	Returns:
1184084Seric **		none
1197762Seric **		Through TopFrame if QuickAbort is set.
120295Seric **
121295Seric **	Side Effects:
1221514Seric **		increments Errors.
123295Seric */
124295Seric 
125295Seric /*VARARGS1*/
12658824Seric void
12757642Seric #ifdef __STDC__
12860094Seric usrerr(const char *fmt, ...)
12957642Seric #else
13057642Seric usrerr(fmt, va_alist)
13160094Seric 	const char *fmt;
13257642Seric 	va_dcl
13357642Seric #endif
134295Seric {
13556852Seric 	VA_LOCAL_DECL
136295Seric 
137295Seric 	if (SuprErrs)
1384084Seric 		return;
139295Seric 
14056852Seric 	VA_START(fmt);
14158524Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
14256852Seric 	VA_END;
1439389Seric 	puterrmsg(MsgBuf);
1448239Seric 
14551951Seric # ifdef LOG
14658020Seric 	if (LogLevel > 3 && LogUsrErrs)
14751951Seric 		syslog(LOG_NOTICE, "%s: %s",
14851951Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
14951951Seric 			&MsgBuf[4]);
15056795Seric # endif /* LOG */
15151951Seric 
1527762Seric 	if (QuickAbort)
1537762Seric 		longjmp(TopFrame, 1);
1544063Seric }
1554063Seric /*
1564063Seric **  MESSAGE -- print message (not necessarily an error)
1574063Seric **
1584063Seric **	Parameters:
15959581Seric **		msg -- the message (printf fmt) -- it can begin with
16059581Seric **			an SMTP reply code.  If not, 050 is assumed.
1614063Seric **		a, b, c, d, e -- printf arguments
1624063Seric **
1634063Seric **	Returns:
1644063Seric **		none
1654063Seric **
1664063Seric **	Side Effects:
1674063Seric **		none.
1684063Seric */
1694063Seric 
1704084Seric /*VARARGS2*/
17158826Seric void
17257642Seric #ifdef __STDC__
17360094Seric message(const char *msg, ...)
17457642Seric #else
17558151Seric message(msg, va_alist)
17660094Seric 	const char *msg;
17757642Seric 	va_dcl
17857642Seric #endif
1794063Seric {
18056852Seric 	VA_LOCAL_DECL
18156852Seric 
1824711Seric 	errno = 0;
18356852Seric 	VA_START(msg);
18458151Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
18556852Seric 	VA_END;
18663753Seric 	putoutmsg(MsgBuf, FALSE);
1877613Seric }
1887613Seric /*
1898239Seric **  NMESSAGE -- print message (not necessarily an error)
1908239Seric **
1918239Seric **	Just like "message" except it never puts the to... tag on.
1928239Seric **
1938239Seric **	Parameters:
1948239Seric **		num -- the default ARPANET error number (in ascii)
1958239Seric **		msg -- the message (printf fmt) -- if it begins
19624943Seric **			with three digits, this number overrides num.
1978239Seric **		a, b, c, d, e -- printf arguments
1988239Seric **
1998239Seric **	Returns:
2008239Seric **		none
2018239Seric **
2028239Seric **	Side Effects:
2038239Seric **		none.
2048239Seric */
2058239Seric 
2068239Seric /*VARARGS2*/
20758826Seric void
20857642Seric #ifdef __STDC__
20960094Seric nmessage(const char *msg, ...)
21057642Seric #else
21158151Seric nmessage(msg, va_alist)
21260094Seric 	const char *msg;
21357642Seric 	va_dcl
21457642Seric #endif
2158239Seric {
21656852Seric 	VA_LOCAL_DECL
21756852Seric 
2188239Seric 	errno = 0;
21956852Seric 	VA_START(msg);
22058151Seric 	fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
22156852Seric 	VA_END;
22263753Seric 	putoutmsg(MsgBuf, FALSE);
2238239Seric }
2248239Seric /*
22563753Seric **  PUTOUTMSG -- output error message to transcript and channel
2267613Seric **
2277613Seric **	Parameters:
2287613Seric **		msg -- message to output (in SMTP format).
2299108Seric **		holdmsg -- if TRUE, don't output a copy of the message to
2309108Seric **			our output channel.
2317613Seric **
2327613Seric **	Returns:
2337613Seric **		none.
2347613Seric **
2357613Seric **	Side Effects:
2367613Seric **		Outputs msg to the transcript.
2377613Seric **		If appropriate, outputs it to the channel.
2387613Seric **		Deletes SMTP reply code number as appropriate.
2397613Seric */
2404711Seric 
24163753Seric putoutmsg(msg, holdmsg)
2427613Seric 	char *msg;
2439108Seric 	bool holdmsg;
2447613Seric {
24564249Seric 	/* display for debugging */
24664249Seric 	if (tTd(54, 8))
24764249Seric 		printf("--- %s%s\n", msg, holdmsg ? " (held)" : "");
24864249Seric 
24914900Seric 	/* output to transcript if serious */
25063848Seric 	if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
25114900Seric 		fprintf(CurEnv->e_xfp, "%s\n", msg);
2524711Seric 
2534711Seric 	/* output to channel if appropriate */
25460421Seric 	if (holdmsg || (!Verbose && msg[0] == '0'))
25560421Seric 		return;
25660421Seric 
25763848Seric 	/* map warnings to something SMTP can handle */
25863848Seric 	if (msg[0] == '6')
25963848Seric 		msg[0] = '5';
26063848Seric 
26160421Seric 	(void) fflush(stdout);
26260421Seric 	if (OpMode == MD_SMTP)
26360421Seric 		fprintf(OutChannel, "%s\r\n", msg);
26460421Seric 	else
26560421Seric 		fprintf(OutChannel, "%s\n", &msg[4]);
26663753Seric 	if (TrafficLogFile != NULL)
26763753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(),
26863753Seric 			OpMode == MD_SMTP ? msg : &msg[4]);
26960421Seric 	if (msg[3] == ' ')
27060421Seric 		(void) fflush(OutChannel);
27160421Seric 	if (!ferror(OutChannel))
27260421Seric 		return;
27360421Seric 
27464499Seric 	/*
27564499Seric 	**  Error on output -- if reporting lost channel, just ignore it.
27664499Seric 	**  Also, ignore errors from QUIT response (221 message) -- some
27764499Seric 	**	rude servers don't read result.
27864499Seric 	*/
27964499Seric 
28064499Seric 	if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0)
28160421Seric 		return;
28260421Seric 
28360421Seric 	/* can't call syserr, 'cause we are using MsgBuf */
28460421Seric 	HoldErrs = TRUE;
28560283Seric #ifdef LOG
28660421Seric 	if (LogLevel > 0)
28760421Seric 		syslog(LOG_CRIT,
28864499Seric 			"%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %m",
28960421Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
29064123Seric 			CurHostName == NULL ? "NO-HOST" : CurHostName,
29164123Seric 			msg);
29260283Seric #endif
2939389Seric }
2949389Seric /*
29563753Seric **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
2969389Seric **
2979389Seric **	Parameters:
2989389Seric **		msg -- the message to output.
2999389Seric **
3009389Seric **	Returns:
3019389Seric **		none.
3029389Seric **
3039389Seric **	Side Effects:
3049389Seric **		Sets the fatal error bit in the envelope as appropriate.
3059389Seric */
3068239Seric 
3079389Seric puterrmsg(msg)
3089389Seric 	char *msg;
3099389Seric {
31063848Seric 	char msgcode = msg[0];
31163848Seric 
3129389Seric 	/* output the message as usual */
31363753Seric 	putoutmsg(msg, HoldErrs);
3149389Seric 
3159389Seric 	/* signal the error */
31664773Seric 	Errors++;
31763848Seric 	if (msgcode == '6')
31863848Seric 	{
31963848Seric 		/* notify the postmaster */
32063848Seric 		CurEnv->e_flags |= EF_PM_NOTIFY;
32163848Seric 	}
32264773Seric 	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
32364773Seric 	{
32464773Seric 		/* mark long-term fatal errors */
32564773Seric 		CurEnv->e_flags |= EF_FATALERRS;
32664773Seric 	}
3274711Seric }
3284711Seric /*
3294711Seric **  FMTMSG -- format a message into buffer.
3304711Seric **
3314711Seric **	Parameters:
3324711Seric **		eb -- error buffer to get result.
3334711Seric **		to -- the recipient tag for this message.
3344711Seric **		num -- arpanet error number.
33516901Seric **		en -- the error number to display.
3364711Seric **		fmt -- format of string.
3374711Seric **		a, b, c, d, e -- arguments.
3384711Seric **
3394711Seric **	Returns:
3404711Seric **		none.
3414711Seric **
3424711Seric **	Side Effects:
3434711Seric **		none.
3444711Seric */
3454063Seric 
34646928Sbostic static void
34756852Seric fmtmsg(eb, to, num, eno, fmt, ap)
3484711Seric 	register char *eb;
3494711Seric 	char *to;
3504711Seric 	char *num;
35116904Seric 	int eno;
3524711Seric 	char *fmt;
35356852Seric 	va_list ap;
3544711Seric {
3554711Seric 	char del;
35659596Seric 	char *meb;
3574711Seric 
3584711Seric 	/* output the reply code */
35924943Seric 	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
3604577Seric 	{
3614711Seric 		num = fmt;
3624711Seric 		fmt += 4;
3634711Seric 	}
3644711Seric 	if (num[3] == '-')
3654711Seric 		del = '-';
3664711Seric 	else
3674711Seric 		del = ' ';
3684711Seric 	(void) sprintf(eb, "%3.3s%c", num, del);
3694711Seric 	eb += 4;
3704063Seric 
3719372Seric 	/* output the file name and line number */
3729372Seric 	if (FileName != NULL)
3739372Seric 	{
3749372Seric 		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
3759372Seric 		eb += strlen(eb);
3769372Seric 	}
3779372Seric 
3784711Seric 	/* output the "to" person */
3794711Seric 	if (to != NULL && to[0] != '\0')
3804711Seric 	{
3814711Seric 		(void) sprintf(eb, "%s... ", to);
3825201Seric 		while (*eb != '\0')
3835201Seric 			*eb++ &= 0177;
3844711Seric 	}
3854711Seric 
38659596Seric 	meb = eb;
38759596Seric 
3884711Seric 	/* output the message */
38956852Seric 	(void) vsprintf(eb, fmt, ap);
3905201Seric 	while (*eb != '\0')
3915201Seric 		*eb++ &= 0177;
3924711Seric 
3934711Seric 	/* output the error code, if any */
39416904Seric 	if (eno != 0)
3954711Seric 	{
39616904Seric 		(void) sprintf(eb, ": %s", errstring(eno));
3974711Seric 		eb += strlen(eb);
3984577Seric 	}
39959596Seric 
40064695Seric 	if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4'))
40164695Seric 	{
40264695Seric 		if (CurEnv->e_message != NULL)
40364695Seric 			free(CurEnv->e_message);
40459596Seric 		CurEnv->e_message = newstr(meb);
40564695Seric 	}
406295Seric }
40715136Seric /*
40815136Seric **  ERRSTRING -- return string description of error code
40915136Seric **
41015136Seric **	Parameters:
41115136Seric **		errno -- the error number to translate
41215136Seric **
41315136Seric **	Returns:
41415136Seric **		A string description of errno.
41515136Seric **
41615136Seric **	Side Effects:
41715136Seric **		none.
41815136Seric */
41915136Seric 
42060089Seric const char *
42115136Seric errstring(errno)
42215136Seric 	int errno;
42315136Seric {
42463839Seric 	static char buf[MAXLINE];
42563839Seric # ifndef ERRLIST_PREDEFINED
42663839Seric 	extern char *sys_errlist[];
42715136Seric 	extern int sys_nerr;
42863839Seric # endif
42924943Seric # ifdef SMTP
43024943Seric 	extern char *SmtpPhase;
43156795Seric # endif /* SMTP */
43215136Seric 
43324943Seric 	/*
43424943Seric 	**  Handle special network error codes.
43524943Seric 	**
43624943Seric 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
43724943Seric 	*/
43824943Seric 
43924943Seric 	switch (errno)
44024943Seric 	{
441*65067Seric # if defined(DAEMON) && defined(ETIMEDOUT)
44224943Seric 	  case ETIMEDOUT:
44324943Seric 	  case ECONNRESET:
44424943Seric 		(void) strcpy(buf, sys_errlist[errno]);
44524943Seric 		if (SmtpPhase != NULL)
44624943Seric 		{
44724943Seric 			(void) strcat(buf, " during ");
44824943Seric 			(void) strcat(buf, SmtpPhase);
44924943Seric 		}
45025050Seric 		if (CurHostName != NULL)
45124943Seric 		{
45224943Seric 			(void) strcat(buf, " with ");
45325050Seric 			(void) strcat(buf, CurHostName);
45424943Seric 		}
45524943Seric 		return (buf);
45624943Seric 
45724943Seric 	  case EHOSTDOWN:
45825050Seric 		if (CurHostName == NULL)
45924943Seric 			break;
46025050Seric 		(void) sprintf(buf, "Host %s is down", CurHostName);
46124943Seric 		return (buf);
46224943Seric 
46324943Seric 	  case ECONNREFUSED:
46425050Seric 		if (CurHostName == NULL)
46524943Seric 			break;
46625050Seric 		(void) sprintf(buf, "Connection refused by %s", CurHostName);
46724943Seric 		return (buf);
468*65067Seric # endif
46925526Smiriam 
47063993Seric 	  case EOPENTIMEOUT:
47163993Seric 		return "Timeout on file open";
47263993Seric 
47357736Seric # ifdef NAMED_BIND
47463993Seric 	  case HOST_NOT_FOUND + E_DNSBASE:
47558010Seric 		return ("Name server: host not found");
47658010Seric 
47763993Seric 	  case TRY_AGAIN + E_DNSBASE:
47858010Seric 		return ("Name server: host name lookup failure");
47958010Seric 
48063993Seric 	  case NO_RECOVERY + E_DNSBASE:
48158010Seric 		return ("Name server: non-recoverable error");
48258010Seric 
48363993Seric 	  case NO_DATA + E_DNSBASE:
48458010Seric 		return ("Name server: no data known for name");
48557736Seric # endif
486*65067Seric 
487*65067Seric 	  case EPERM:
488*65067Seric 		/* SunOS gives "Not owner" -- this is the POSIX message */
489*65067Seric 		return "Operation not permitted";
49024943Seric 	}
49124943Seric 
49215136Seric 	if (errno > 0 && errno < sys_nerr)
49315136Seric 		return (sys_errlist[errno]);
49415136Seric 
49515136Seric 	(void) sprintf(buf, "Error %d", errno);
49615136Seric 	return (buf);
49715136Seric }
498