xref: /csrg-svn/usr.sbin/sendmail/src/err.c (revision 64725)
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*64725Seric static char sccsid[] = "@(#)err.c	8.11 (Berkeley) 10/17/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 */
93*64725Seric 	if (olderrno == EMFILE)
94*64725Seric 		printopenfds(TRUE);
9558690Seric 	if (panic)
9659156Seric 	{
9759156Seric #ifdef XLA
9859156Seric 		xla_all_end();
9959156Seric #endif
10058690Seric 		exit(EX_OSERR);
10159156Seric 	}
102295Seric 	errno = 0;
1037762Seric 	if (QuickAbort)
1047762Seric 		longjmp(TopFrame, 2);
105295Seric }
106295Seric /*
107295Seric **  USRERR -- Signal user error.
108295Seric **
109295Seric **	This is much like syserr except it is for user errors.
110295Seric **
111295Seric **	Parameters:
112295Seric **		fmt, a, b, c, d -- printf strings
113295Seric **
114295Seric **	Returns:
1154084Seric **		none
1167762Seric **		Through TopFrame if QuickAbort is set.
117295Seric **
118295Seric **	Side Effects:
1191514Seric **		increments Errors.
120295Seric */
121295Seric 
122295Seric /*VARARGS1*/
12358824Seric void
12457642Seric #ifdef __STDC__
12560094Seric usrerr(const char *fmt, ...)
12657642Seric #else
12757642Seric usrerr(fmt, va_alist)
12860094Seric 	const char *fmt;
12957642Seric 	va_dcl
13057642Seric #endif
131295Seric {
13256852Seric 	VA_LOCAL_DECL
133295Seric 	extern char SuprErrs;
13416901Seric 	extern int errno;
135295Seric 
136295Seric 	if (SuprErrs)
1374084Seric 		return;
138295Seric 
13956852Seric 	VA_START(fmt);
14058524Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
14156852Seric 	VA_END;
1429389Seric 	puterrmsg(MsgBuf);
1438239Seric 
14451951Seric # ifdef LOG
14558020Seric 	if (LogLevel > 3 && LogUsrErrs)
14651951Seric 		syslog(LOG_NOTICE, "%s: %s",
14751951Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
14851951Seric 			&MsgBuf[4]);
14956795Seric # endif /* LOG */
15051951Seric 
1517762Seric 	if (QuickAbort)
1527762Seric 		longjmp(TopFrame, 1);
1534063Seric }
1544063Seric /*
1554063Seric **  MESSAGE -- print message (not necessarily an error)
1564063Seric **
1574063Seric **	Parameters:
15859581Seric **		msg -- the message (printf fmt) -- it can begin with
15959581Seric **			an SMTP reply code.  If not, 050 is assumed.
1604063Seric **		a, b, c, d, e -- printf arguments
1614063Seric **
1624063Seric **	Returns:
1634063Seric **		none
1644063Seric **
1654063Seric **	Side Effects:
1664063Seric **		none.
1674063Seric */
1684063Seric 
1694084Seric /*VARARGS2*/
17058826Seric void
17157642Seric #ifdef __STDC__
17260094Seric message(const char *msg, ...)
17357642Seric #else
17458151Seric message(msg, va_alist)
17560094Seric 	const char *msg;
17657642Seric 	va_dcl
17757642Seric #endif
1784063Seric {
17956852Seric 	VA_LOCAL_DECL
18056852Seric 
1814711Seric 	errno = 0;
18256852Seric 	VA_START(msg);
18358151Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
18456852Seric 	VA_END;
18563753Seric 	putoutmsg(MsgBuf, FALSE);
1867613Seric }
1877613Seric /*
1888239Seric **  NMESSAGE -- print message (not necessarily an error)
1898239Seric **
1908239Seric **	Just like "message" except it never puts the to... tag on.
1918239Seric **
1928239Seric **	Parameters:
1938239Seric **		num -- the default ARPANET error number (in ascii)
1948239Seric **		msg -- the message (printf fmt) -- if it begins
19524943Seric **			with three digits, this number overrides num.
1968239Seric **		a, b, c, d, e -- printf arguments
1978239Seric **
1988239Seric **	Returns:
1998239Seric **		none
2008239Seric **
2018239Seric **	Side Effects:
2028239Seric **		none.
2038239Seric */
2048239Seric 
2058239Seric /*VARARGS2*/
20658826Seric void
20757642Seric #ifdef __STDC__
20860094Seric nmessage(const char *msg, ...)
20957642Seric #else
21058151Seric nmessage(msg, va_alist)
21160094Seric 	const char *msg;
21257642Seric 	va_dcl
21357642Seric #endif
2148239Seric {
21556852Seric 	VA_LOCAL_DECL
21656852Seric 
2178239Seric 	errno = 0;
21856852Seric 	VA_START(msg);
21958151Seric 	fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
22056852Seric 	VA_END;
22163753Seric 	putoutmsg(MsgBuf, FALSE);
2228239Seric }
2238239Seric /*
22463753Seric **  PUTOUTMSG -- output error message to transcript and channel
2257613Seric **
2267613Seric **	Parameters:
2277613Seric **		msg -- message to output (in SMTP format).
2289108Seric **		holdmsg -- if TRUE, don't output a copy of the message to
2299108Seric **			our output channel.
2307613Seric **
2317613Seric **	Returns:
2327613Seric **		none.
2337613Seric **
2347613Seric **	Side Effects:
2357613Seric **		Outputs msg to the transcript.
2367613Seric **		If appropriate, outputs it to the channel.
2377613Seric **		Deletes SMTP reply code number as appropriate.
2387613Seric */
2394711Seric 
24063753Seric putoutmsg(msg, holdmsg)
2417613Seric 	char *msg;
2429108Seric 	bool holdmsg;
2437613Seric {
24464249Seric 	/* display for debugging */
24564249Seric 	if (tTd(54, 8))
24664249Seric 		printf("--- %s%s\n", msg, holdmsg ? " (held)" : "");
24764249Seric 
24814900Seric 	/* output to transcript if serious */
24963848Seric 	if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
25014900Seric 		fprintf(CurEnv->e_xfp, "%s\n", msg);
2514711Seric 
2524711Seric 	/* output to channel if appropriate */
25360421Seric 	if (holdmsg || (!Verbose && msg[0] == '0'))
25460421Seric 		return;
25560421Seric 
25663848Seric 	/* map warnings to something SMTP can handle */
25763848Seric 	if (msg[0] == '6')
25863848Seric 		msg[0] = '5';
25963848Seric 
26060421Seric 	(void) fflush(stdout);
26160421Seric 	if (OpMode == MD_SMTP)
26260421Seric 		fprintf(OutChannel, "%s\r\n", msg);
26360421Seric 	else
26460421Seric 		fprintf(OutChannel, "%s\n", &msg[4]);
26563753Seric 	if (TrafficLogFile != NULL)
26663753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(),
26763753Seric 			OpMode == MD_SMTP ? msg : &msg[4]);
26860421Seric 	if (msg[3] == ' ')
26960421Seric 		(void) fflush(OutChannel);
27060421Seric 	if (!ferror(OutChannel))
27160421Seric 		return;
27260421Seric 
27364499Seric 	/*
27464499Seric 	**  Error on output -- if reporting lost channel, just ignore it.
27564499Seric 	**  Also, ignore errors from QUIT response (221 message) -- some
27664499Seric 	**	rude servers don't read result.
27764499Seric 	*/
27864499Seric 
27964499Seric 	if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0)
28060421Seric 		return;
28160421Seric 
28260421Seric 	/* can't call syserr, 'cause we are using MsgBuf */
28360421Seric 	HoldErrs = TRUE;
28460283Seric #ifdef LOG
28560421Seric 	if (LogLevel > 0)
28660421Seric 		syslog(LOG_CRIT,
28764499Seric 			"%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %m",
28860421Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
28964123Seric 			CurHostName == NULL ? "NO-HOST" : CurHostName,
29064123Seric 			msg);
29160283Seric #endif
2929389Seric }
2939389Seric /*
29463753Seric **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
2959389Seric **
2969389Seric **	Parameters:
2979389Seric **		msg -- the message to output.
2989389Seric **
2999389Seric **	Returns:
3009389Seric **		none.
3019389Seric **
3029389Seric **	Side Effects:
3039389Seric **		Sets the fatal error bit in the envelope as appropriate.
3049389Seric */
3058239Seric 
3069389Seric puterrmsg(msg)
3079389Seric 	char *msg;
3089389Seric {
30963848Seric 	char msgcode = msg[0];
31063848Seric 
3119389Seric 	/* output the message as usual */
31263753Seric 	putoutmsg(msg, HoldErrs);
3139389Seric 
3149389Seric 	/* signal the error */
31563848Seric 	if (msgcode == '6')
31663848Seric 	{
31763848Seric 		/* notify the postmaster */
31863848Seric 		CurEnv->e_flags |= EF_PM_NOTIFY;
31963848Seric 	}
32063848Seric 	else
32163848Seric 	{
32263848Seric 		Errors++;
32363848Seric 		if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
32463848Seric 			CurEnv->e_flags |= EF_FATALERRS;
32563848Seric 	}
3264711Seric }
3274711Seric /*
3284711Seric **  FMTMSG -- format a message into buffer.
3294711Seric **
3304711Seric **	Parameters:
3314711Seric **		eb -- error buffer to get result.
3324711Seric **		to -- the recipient tag for this message.
3334711Seric **		num -- arpanet error number.
33416901Seric **		en -- the error number to display.
3354711Seric **		fmt -- format of string.
3364711Seric **		a, b, c, d, e -- arguments.
3374711Seric **
3384711Seric **	Returns:
3394711Seric **		none.
3404711Seric **
3414711Seric **	Side Effects:
3424711Seric **		none.
3434711Seric */
3444063Seric 
34546928Sbostic static void
34656852Seric fmtmsg(eb, to, num, eno, fmt, ap)
3474711Seric 	register char *eb;
3484711Seric 	char *to;
3494711Seric 	char *num;
35016904Seric 	int eno;
3514711Seric 	char *fmt;
35256852Seric 	va_list ap;
3534711Seric {
3544711Seric 	char del;
35559596Seric 	char *meb;
3564711Seric 
3574711Seric 	/* output the reply code */
35824943Seric 	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
3594577Seric 	{
3604711Seric 		num = fmt;
3614711Seric 		fmt += 4;
3624711Seric 	}
3634711Seric 	if (num[3] == '-')
3644711Seric 		del = '-';
3654711Seric 	else
3664711Seric 		del = ' ';
3674711Seric 	(void) sprintf(eb, "%3.3s%c", num, del);
3684711Seric 	eb += 4;
3694063Seric 
3709372Seric 	/* output the file name and line number */
3719372Seric 	if (FileName != NULL)
3729372Seric 	{
3739372Seric 		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
3749372Seric 		eb += strlen(eb);
3759372Seric 	}
3769372Seric 
3774711Seric 	/* output the "to" person */
3784711Seric 	if (to != NULL && to[0] != '\0')
3794711Seric 	{
3804711Seric 		(void) sprintf(eb, "%s... ", to);
3815201Seric 		while (*eb != '\0')
3825201Seric 			*eb++ &= 0177;
3834711Seric 	}
3844711Seric 
38559596Seric 	meb = eb;
38659596Seric 
3874711Seric 	/* output the message */
38856852Seric 	(void) vsprintf(eb, fmt, ap);
3895201Seric 	while (*eb != '\0')
3905201Seric 		*eb++ &= 0177;
3914711Seric 
3924711Seric 	/* output the error code, if any */
39316904Seric 	if (eno != 0)
3944711Seric 	{
39516904Seric 		(void) sprintf(eb, ": %s", errstring(eno));
3964711Seric 		eb += strlen(eb);
3974577Seric 	}
39859596Seric 
39964695Seric 	if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4'))
40064695Seric 	{
40164695Seric 		if (CurEnv->e_message != NULL)
40264695Seric 			free(CurEnv->e_message);
40359596Seric 		CurEnv->e_message = newstr(meb);
40464695Seric 	}
405295Seric }
40615136Seric /*
40715136Seric **  ERRSTRING -- return string description of error code
40815136Seric **
40915136Seric **	Parameters:
41015136Seric **		errno -- the error number to translate
41115136Seric **
41215136Seric **	Returns:
41315136Seric **		A string description of errno.
41415136Seric **
41515136Seric **	Side Effects:
41615136Seric **		none.
41715136Seric */
41815136Seric 
41960089Seric const char *
42015136Seric errstring(errno)
42115136Seric 	int errno;
42215136Seric {
42363839Seric 	static char buf[MAXLINE];
42463839Seric # ifndef ERRLIST_PREDEFINED
42563839Seric 	extern char *sys_errlist[];
42615136Seric 	extern int sys_nerr;
42763839Seric # endif
42824943Seric # ifdef SMTP
42924943Seric 	extern char *SmtpPhase;
43056795Seric # endif /* SMTP */
43115136Seric 
43224943Seric # ifdef DAEMON
43352107Seric # ifdef ETIMEDOUT
43424943Seric 	/*
43524943Seric 	**  Handle special network error codes.
43624943Seric 	**
43724943Seric 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
43824943Seric 	*/
43924943Seric 
44024943Seric 	switch (errno)
44124943Seric 	{
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);
46825526Smiriam 
46963993Seric 	  case EOPENTIMEOUT:
47063993Seric 		return "Timeout on file open";
47163993Seric 
47257736Seric # ifdef NAMED_BIND
47363993Seric 	  case HOST_NOT_FOUND + E_DNSBASE:
47458010Seric 		return ("Name server: host not found");
47558010Seric 
47663993Seric 	  case TRY_AGAIN + E_DNSBASE:
47758010Seric 		return ("Name server: host name lookup failure");
47858010Seric 
47963993Seric 	  case NO_RECOVERY + E_DNSBASE:
48058010Seric 		return ("Name server: non-recoverable error");
48158010Seric 
48263993Seric 	  case NO_DATA + E_DNSBASE:
48358010Seric 		return ("Name server: no data known for name");
48457736Seric # endif
48524943Seric 	}
48652107Seric # endif
48752107Seric # endif
48824943Seric 
48915136Seric 	if (errno > 0 && errno < sys_nerr)
49015136Seric 		return (sys_errlist[errno]);
49115136Seric 
49215136Seric 	(void) sprintf(buf, "Error %d", errno);
49315136Seric 	return (buf);
49415136Seric }
495