xref: /csrg-svn/usr.sbin/sendmail/src/err.c (revision 67818)
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*67818Seric static char sccsid[] = "@(#)err.c	8.30 (Berkeley) 10/16/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:
30*67818Seric **		fmt -- the format string.  If it does not begin with
31*67818Seric **			a three-digit SMTP reply code, either 554 or
32*67818Seric **			451 is assumed depending on whether errno
33*67818Seric **			is set.
34*67818Seric **		(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 
4510147Seric char	MsgBuf[BUFSIZ*2];	/* text of most recent message */
464084Seric 
4763969Seric static void	fmtmsg();
4846928Sbostic 
4966334Seric #if NAMED_BIND && !defined(NO_DATA)
5063969Seric # define NO_DATA	NO_ADDRESS
5163969Seric #endif
5263969Seric 
5358824Seric void
54295Seric /*VARARGS1*/
5557642Seric #ifdef __STDC__
5660094Seric syserr(const char *fmt, ...)
5757642Seric #else
5857642Seric syserr(fmt, va_alist)
5960094Seric 	const char *fmt;
6057642Seric 	va_dcl
6157642Seric #endif
62295Seric {
6316901Seric 	register char *p;
6416901Seric 	int olderrno = errno;
6558690Seric 	bool panic;
6666006Seric #ifdef LOG
6766006Seric 	char *uname;
6866006Seric 	struct passwd *pw;
6966006Seric 	char ubuf[80];
7066006Seric #endif
7156852Seric 	VA_LOCAL_DECL
72295Seric 
7358690Seric 	panic = *fmt == '!';
7458690Seric 	if (panic)
7558690Seric 		fmt++;
7658690Seric 
777525Seric 	/* format and output the error message */
7816901Seric 	if (olderrno == 0)
7958151Seric 		p = "554";
807957Seric 	else
8158151Seric 		p = "451";
8256852Seric 	VA_START(fmt);
8356852Seric 	fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
8456852Seric 	VA_END;
859389Seric 	puterrmsg(MsgBuf);
864063Seric 
87*67818Seric 	/* save this message for mailq printing */
88*67818Seric 	if (!panic)
89*67818Seric 	{
90*67818Seric 		if (CurEnv->e_message != NULL)
91*67818Seric 			free(CurEnv->e_message);
92*67818Seric 		CurEnv->e_message = newstr(MsgBuf + 4);
93*67818Seric 	}
94*67818Seric 
95295Seric 	/* determine exit status if not already set */
96295Seric 	if (ExitStat == EX_OK)
97295Seric 	{
9816901Seric 		if (olderrno == 0)
99295Seric 			ExitStat = EX_SOFTWARE;
100295Seric 		else
1011598Seric 			ExitStat = EX_OSERR;
10266323Seric 		if (tTd(54, 1))
10366323Seric 			printf("syserr: ExitStat = %d\n", ExitStat);
104295Seric 	}
105295Seric 
106295Seric # ifdef LOG
10766006Seric 	pw = getpwuid(getuid());
10866006Seric 	if (pw != NULL)
10966006Seric 		uname = pw->pw_name;
11066006Seric 	else
11166006Seric 	{
11266006Seric 		uname = ubuf;
11366006Seric 		sprintf(ubuf, "UID%d", getuid());
11466006Seric 	}
11566006Seric 
1167674Seric 	if (LogLevel > 0)
11766006Seric 		syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s",
11825277Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
11966006Seric 			uname, &MsgBuf[4]);
12056795Seric # endif /* LOG */
12164725Seric 	if (olderrno == EMFILE)
12264731Seric 	{
12364725Seric 		printopenfds(TRUE);
12464731Seric 		mci_dump_all(TRUE);
12564731Seric 	}
12658690Seric 	if (panic)
12759156Seric 	{
12859156Seric #ifdef XLA
12959156Seric 		xla_all_end();
13059156Seric #endif
13158690Seric 		exit(EX_OSERR);
13259156Seric 	}
133295Seric 	errno = 0;
1347762Seric 	if (QuickAbort)
1357762Seric 		longjmp(TopFrame, 2);
136295Seric }
137295Seric /*
138295Seric **  USRERR -- Signal user error.
139295Seric **
140295Seric **	This is much like syserr except it is for user errors.
141295Seric **
142295Seric **	Parameters:
143*67818Seric **		fmt -- the format string.  If it does not begin with
144*67818Seric **			a three-digit SMTP reply code, 501 is assumed.
145*67818Seric **		(others) -- printf strings
146295Seric **
147295Seric **	Returns:
1484084Seric **		none
1497762Seric **		Through TopFrame if QuickAbort is set.
150295Seric **
151295Seric **	Side Effects:
1521514Seric **		increments Errors.
153295Seric */
154295Seric 
155295Seric /*VARARGS1*/
15658824Seric void
15757642Seric #ifdef __STDC__
15860094Seric usrerr(const char *fmt, ...)
15957642Seric #else
16057642Seric usrerr(fmt, va_alist)
16160094Seric 	const char *fmt;
16257642Seric 	va_dcl
16357642Seric #endif
164295Seric {
16556852Seric 	VA_LOCAL_DECL
166295Seric 
167295Seric 	if (SuprErrs)
1684084Seric 		return;
169295Seric 
17056852Seric 	VA_START(fmt);
17158524Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
17256852Seric 	VA_END;
1739389Seric 	puterrmsg(MsgBuf);
1748239Seric 
175*67818Seric 	/* save this message for mailq printing */
176*67818Seric 	if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4'))
177*67818Seric 	{
178*67818Seric 		if (CurEnv->e_message != NULL)
179*67818Seric 			free(CurEnv->e_message);
180*67818Seric 		CurEnv->e_message = newstr(MsgBuf + 4);
181*67818Seric 	}
182*67818Seric 
18351951Seric # ifdef LOG
18458020Seric 	if (LogLevel > 3 && LogUsrErrs)
18551951Seric 		syslog(LOG_NOTICE, "%s: %s",
18651951Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
18751951Seric 			&MsgBuf[4]);
18856795Seric # endif /* LOG */
18951951Seric 
1907762Seric 	if (QuickAbort)
1917762Seric 		longjmp(TopFrame, 1);
1924063Seric }
1934063Seric /*
1944063Seric **  MESSAGE -- print message (not necessarily an error)
1954063Seric **
1964063Seric **	Parameters:
19759581Seric **		msg -- the message (printf fmt) -- it can begin with
19859581Seric **			an SMTP reply code.  If not, 050 is assumed.
199*67818Seric **		(others) -- printf arguments
2004063Seric **
2014063Seric **	Returns:
2024063Seric **		none
2034063Seric **
2044063Seric **	Side Effects:
2054063Seric **		none.
2064063Seric */
2074063Seric 
2084084Seric /*VARARGS2*/
20958826Seric void
21057642Seric #ifdef __STDC__
21160094Seric message(const char *msg, ...)
21257642Seric #else
21358151Seric message(msg, va_alist)
21460094Seric 	const char *msg;
21557642Seric 	va_dcl
21657642Seric #endif
2174063Seric {
21856852Seric 	VA_LOCAL_DECL
21956852Seric 
2204711Seric 	errno = 0;
22156852Seric 	VA_START(msg);
22258151Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
22356852Seric 	VA_END;
22463753Seric 	putoutmsg(MsgBuf, FALSE);
225*67818Seric 
226*67818Seric 	/* save this message for mailq printing */
227*67818Seric 	if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4'))
228*67818Seric 	{
229*67818Seric 		if (CurEnv->e_message != NULL)
230*67818Seric 			free(CurEnv->e_message);
231*67818Seric 		CurEnv->e_message = newstr(MsgBuf + 4);
232*67818Seric 	}
2337613Seric }
2347613Seric /*
2358239Seric **  NMESSAGE -- print message (not necessarily an error)
2368239Seric **
2378239Seric **	Just like "message" except it never puts the to... tag on.
2388239Seric **
2398239Seric **	Parameters:
2408239Seric **		msg -- the message (printf fmt) -- if it begins
241*67818Seric **			with a three digit SMTP reply code, that is used,
242*67818Seric **			otherwise 050 is assumed.
243*67818Seric **		(others) -- printf arguments
2448239Seric **
2458239Seric **	Returns:
2468239Seric **		none
2478239Seric **
2488239Seric **	Side Effects:
2498239Seric **		none.
2508239Seric */
2518239Seric 
2528239Seric /*VARARGS2*/
25358826Seric void
25457642Seric #ifdef __STDC__
25560094Seric nmessage(const char *msg, ...)
25657642Seric #else
25758151Seric nmessage(msg, va_alist)
25860094Seric 	const char *msg;
25957642Seric 	va_dcl
26057642Seric #endif
2618239Seric {
26256852Seric 	VA_LOCAL_DECL
26356852Seric 
2648239Seric 	errno = 0;
26556852Seric 	VA_START(msg);
26658151Seric 	fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
26756852Seric 	VA_END;
26863753Seric 	putoutmsg(MsgBuf, FALSE);
2698239Seric }
2708239Seric /*
27163753Seric **  PUTOUTMSG -- output error message to transcript and channel
2727613Seric **
2737613Seric **	Parameters:
2747613Seric **		msg -- message to output (in SMTP format).
2759108Seric **		holdmsg -- if TRUE, don't output a copy of the message to
2769108Seric **			our output channel.
2777613Seric **
2787613Seric **	Returns:
2797613Seric **		none.
2807613Seric **
2817613Seric **	Side Effects:
2827613Seric **		Outputs msg to the transcript.
2837613Seric **		If appropriate, outputs it to the channel.
2847613Seric **		Deletes SMTP reply code number as appropriate.
2857613Seric */
2864711Seric 
28763753Seric putoutmsg(msg, holdmsg)
2887613Seric 	char *msg;
2899108Seric 	bool holdmsg;
2907613Seric {
29164249Seric 	/* display for debugging */
29264249Seric 	if (tTd(54, 8))
29364249Seric 		printf("--- %s%s\n", msg, holdmsg ? " (held)" : "");
29464249Seric 
29514900Seric 	/* output to transcript if serious */
29663848Seric 	if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
29714900Seric 		fprintf(CurEnv->e_xfp, "%s\n", msg);
2984711Seric 
2994711Seric 	/* output to channel if appropriate */
30060421Seric 	if (holdmsg || (!Verbose && msg[0] == '0'))
30160421Seric 		return;
30260421Seric 
30363848Seric 	/* map warnings to something SMTP can handle */
30463848Seric 	if (msg[0] == '6')
30563848Seric 		msg[0] = '5';
30663848Seric 
30760421Seric 	(void) fflush(stdout);
30866017Seric 
30966017Seric 	/* if DisConnected, OutChannel now points to the transcript */
31066017Seric 	if (!DisConnected &&
31166017Seric 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
31260421Seric 		fprintf(OutChannel, "%s\r\n", msg);
31360421Seric 	else
31460421Seric 		fprintf(OutChannel, "%s\n", &msg[4]);
31563753Seric 	if (TrafficLogFile != NULL)
31663753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(),
31765580Seric 			(OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
31860421Seric 	if (msg[3] == ' ')
31960421Seric 		(void) fflush(OutChannel);
32066017Seric 	if (!ferror(OutChannel) || DisConnected)
32160421Seric 		return;
32260421Seric 
32364499Seric 	/*
32464499Seric 	**  Error on output -- if reporting lost channel, just ignore it.
32564499Seric 	**  Also, ignore errors from QUIT response (221 message) -- some
32664499Seric 	**	rude servers don't read result.
32764499Seric 	*/
32864499Seric 
32964499Seric 	if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0)
33060421Seric 		return;
33160421Seric 
33260421Seric 	/* can't call syserr, 'cause we are using MsgBuf */
33360421Seric 	HoldErrs = TRUE;
33460283Seric #ifdef LOG
33560421Seric 	if (LogLevel > 0)
33660421Seric 		syslog(LOG_CRIT,
33766864Seric 			"%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
33860421Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
33964123Seric 			CurHostName == NULL ? "NO-HOST" : CurHostName,
34066864Seric 			msg, errstring(errno));
34160283Seric #endif
3429389Seric }
3439389Seric /*
34463753Seric **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
3459389Seric **
3469389Seric **	Parameters:
3479389Seric **		msg -- the message to output.
3489389Seric **
3499389Seric **	Returns:
3509389Seric **		none.
3519389Seric **
3529389Seric **	Side Effects:
3539389Seric **		Sets the fatal error bit in the envelope as appropriate.
3549389Seric */
3558239Seric 
3569389Seric puterrmsg(msg)
3579389Seric 	char *msg;
3589389Seric {
35963848Seric 	char msgcode = msg[0];
36063848Seric 
3619389Seric 	/* output the message as usual */
36263753Seric 	putoutmsg(msg, HoldErrs);
3639389Seric 
3649389Seric 	/* signal the error */
36564773Seric 	Errors++;
36663848Seric 	if (msgcode == '6')
36763848Seric 	{
36863848Seric 		/* notify the postmaster */
36963848Seric 		CurEnv->e_flags |= EF_PM_NOTIFY;
37063848Seric 	}
37167786Seric 	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
37264773Seric 	{
37364773Seric 		/* mark long-term fatal errors */
37464773Seric 		CurEnv->e_flags |= EF_FATALERRS;
37564773Seric 	}
3764711Seric }
3774711Seric /*
3784711Seric **  FMTMSG -- format a message into buffer.
3794711Seric **
3804711Seric **	Parameters:
3814711Seric **		eb -- error buffer to get result.
3824711Seric **		to -- the recipient tag for this message.
3834711Seric **		num -- arpanet error number.
38416901Seric **		en -- the error number to display.
3854711Seric **		fmt -- format of string.
3864711Seric **		a, b, c, d, e -- arguments.
3874711Seric **
3884711Seric **	Returns:
3894711Seric **		none.
3904711Seric **
3914711Seric **	Side Effects:
3924711Seric **		none.
3934711Seric */
3944063Seric 
39546928Sbostic static void
39656852Seric fmtmsg(eb, to, num, eno, fmt, ap)
3974711Seric 	register char *eb;
3984711Seric 	char *to;
3994711Seric 	char *num;
40016904Seric 	int eno;
4014711Seric 	char *fmt;
40256852Seric 	va_list ap;
4034711Seric {
4044711Seric 	char del;
40559596Seric 	char *meb;
4064711Seric 
4074711Seric 	/* output the reply code */
40824943Seric 	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
4094577Seric 	{
4104711Seric 		num = fmt;
4114711Seric 		fmt += 4;
4124711Seric 	}
4134711Seric 	if (num[3] == '-')
4144711Seric 		del = '-';
4154711Seric 	else
4164711Seric 		del = ' ';
4174711Seric 	(void) sprintf(eb, "%3.3s%c", num, del);
4184711Seric 	eb += 4;
4194063Seric 
4209372Seric 	/* output the file name and line number */
4219372Seric 	if (FileName != NULL)
4229372Seric 	{
4239372Seric 		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
4249372Seric 		eb += strlen(eb);
4259372Seric 	}
4269372Seric 
4274711Seric 	/* output the "to" person */
4284711Seric 	if (to != NULL && to[0] != '\0')
4294711Seric 	{
43066297Seric 		(void) sprintf(eb, "%s... ", shortenstring(to, 203));
4315201Seric 		while (*eb != '\0')
4325201Seric 			*eb++ &= 0177;
4334711Seric 	}
4344711Seric 
43559596Seric 	meb = eb;
43659596Seric 
4374711Seric 	/* output the message */
43856852Seric 	(void) vsprintf(eb, fmt, ap);
4395201Seric 	while (*eb != '\0')
4405201Seric 		*eb++ &= 0177;
4414711Seric 
4424711Seric 	/* output the error code, if any */
44316904Seric 	if (eno != 0)
4444711Seric 	{
44516904Seric 		(void) sprintf(eb, ": %s", errstring(eno));
4464711Seric 		eb += strlen(eb);
4474577Seric 	}
448295Seric }
44915136Seric /*
45015136Seric **  ERRSTRING -- return string description of error code
45115136Seric **
45215136Seric **	Parameters:
45365751Seric **		errnum -- the error number to translate
45415136Seric **
45515136Seric **	Returns:
45665751Seric **		A string description of errnum.
45715136Seric **
45815136Seric **	Side Effects:
45915136Seric **		none.
46015136Seric */
46115136Seric 
46260089Seric const char *
46365751Seric errstring(errnum)
46465751Seric 	int errnum;
46515136Seric {
46665168Seric 	char *dnsmsg;
46763839Seric 	static char buf[MAXLINE];
46863839Seric # ifndef ERRLIST_PREDEFINED
46963839Seric 	extern char *sys_errlist[];
47015136Seric 	extern int sys_nerr;
47163839Seric # endif
47224943Seric # ifdef SMTP
47324943Seric 	extern char *SmtpPhase;
47456795Seric # endif /* SMTP */
47515136Seric 
47624943Seric 	/*
47724943Seric 	**  Handle special network error codes.
47824943Seric 	**
47924943Seric 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
48024943Seric 	*/
48124943Seric 
48265168Seric 	dnsmsg = NULL;
48365751Seric 	switch (errnum)
48424943Seric 	{
48565067Seric # if defined(DAEMON) && defined(ETIMEDOUT)
48624943Seric 	  case ETIMEDOUT:
48724943Seric 	  case ECONNRESET:
48865751Seric 		(void) strcpy(buf, sys_errlist[errnum]);
48924943Seric 		if (SmtpPhase != NULL)
49024943Seric 		{
49124943Seric 			(void) strcat(buf, " during ");
49224943Seric 			(void) strcat(buf, SmtpPhase);
49324943Seric 		}
49425050Seric 		if (CurHostName != NULL)
49524943Seric 		{
49624943Seric 			(void) strcat(buf, " with ");
49725050Seric 			(void) strcat(buf, CurHostName);
49824943Seric 		}
49924943Seric 		return (buf);
50024943Seric 
50124943Seric 	  case EHOSTDOWN:
50225050Seric 		if (CurHostName == NULL)
50324943Seric 			break;
50425050Seric 		(void) sprintf(buf, "Host %s is down", CurHostName);
50524943Seric 		return (buf);
50624943Seric 
50724943Seric 	  case ECONNREFUSED:
50825050Seric 		if (CurHostName == NULL)
50924943Seric 			break;
51025050Seric 		(void) sprintf(buf, "Connection refused by %s", CurHostName);
51124943Seric 		return (buf);
51265067Seric # endif
51325526Smiriam 
51463993Seric 	  case EOPENTIMEOUT:
51563993Seric 		return "Timeout on file open";
51663993Seric 
51766334Seric # if NAMED_BIND
51863993Seric 	  case HOST_NOT_FOUND + E_DNSBASE:
51965168Seric 		dnsmsg = "host not found";
52065168Seric 		break;
52158010Seric 
52263993Seric 	  case TRY_AGAIN + E_DNSBASE:
52365168Seric 		dnsmsg = "host name lookup failure";
52465168Seric 		break;
52558010Seric 
52663993Seric 	  case NO_RECOVERY + E_DNSBASE:
52765168Seric 		dnsmsg = "non-recoverable error";
52865168Seric 		break;
52958010Seric 
53063993Seric 	  case NO_DATA + E_DNSBASE:
53165168Seric 		dnsmsg = "no data known";
53265168Seric 		break;
53357736Seric # endif
53465067Seric 
53565067Seric 	  case EPERM:
53665067Seric 		/* SunOS gives "Not owner" -- this is the POSIX message */
53765067Seric 		return "Operation not permitted";
53824943Seric 	}
53924943Seric 
54065168Seric 	if (dnsmsg != NULL)
54165168Seric 	{
54265168Seric 		(void) strcpy(buf, "Name server: ");
54365168Seric 		if (CurHostName != NULL)
54465168Seric 		{
54565168Seric 			(void) strcat(buf, CurHostName);
54665168Seric 			(void) strcat(buf, ": ");
54765168Seric 		}
54865168Seric 		(void) strcat(buf, dnsmsg);
54965168Seric 		return buf;
55065168Seric 	}
55165168Seric 
55265751Seric 	if (errnum > 0 && errnum < sys_nerr)
55365751Seric 		return (sys_errlist[errnum]);
55415136Seric 
55565751Seric 	(void) sprintf(buf, "Error %d", errnum);
55615136Seric 	return (buf);
55715136Seric }
558