err.c revision 40497
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char sccsid[] = "@(#)err.c	8.74 (Berkeley) 6/4/98";
15#endif /* not lint */
16
17# include "sendmail.h"
18# include <errno.h>
19
20/*
21**  SYSERR -- Print error message.
22**
23**	Prints an error message via printf to the diagnostic output.
24**
25**	If the first character of the syserr message is `!' it will
26**	log this as an ALERT message and exit immediately.  This can
27**	leave queue files in an indeterminate state, so it should not
28**	be used lightly.
29**
30**	Parameters:
31**		fmt -- the format string.  If it does not begin with
32**			a three-digit SMTP reply code, either 554 or
33**			451 is assumed depending on whether errno
34**			is set.
35**		(others) -- parameters
36**
37**	Returns:
38**		none
39**		Through TopFrame if QuickAbort is set.
40**
41**	Side Effects:
42**		increments Errors.
43**		sets ExitStat.
44*/
45
46char	MsgBuf[BUFSIZ*2];		/* text of most recent message */
47char	HeldMessageBuf[sizeof MsgBuf];	/* for held messages */
48
49extern void	putoutmsg __P((char *, bool, bool));
50extern void	puterrmsg __P((char *));
51static void	fmtmsg __P((char *, const char *, const char *, int, const char *, va_list));
52
53#if NAMED_BIND && !defined(NO_DATA)
54# define NO_DATA	NO_ADDRESS
55#endif
56
57void
58/*VARARGS1*/
59#ifdef __STDC__
60syserr(const char *fmt, ...)
61#else
62syserr(fmt, va_alist)
63	const char *fmt;
64	va_dcl
65#endif
66{
67	register char *p;
68	int olderrno = errno;
69	bool panic;
70	char *uname;
71	struct passwd *pw;
72	char ubuf[80];
73	VA_LOCAL_DECL
74
75	panic = *fmt == '!';
76	if (panic)
77	{
78		fmt++;
79		HoldErrs = FALSE;
80	}
81
82	/* format and output the error message */
83	if (olderrno == 0)
84		p = "554";
85	else
86		p = "451";
87	VA_START(fmt);
88	fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
89	VA_END;
90	puterrmsg(MsgBuf);
91
92	/* save this message for mailq printing */
93	if (!panic && CurEnv != NULL)
94	{
95		if (CurEnv->e_message != NULL)
96			free(CurEnv->e_message);
97		CurEnv->e_message = newstr(MsgBuf + 4);
98	}
99
100	/* determine exit status if not already set */
101	if (ExitStat == EX_OK)
102	{
103		if (olderrno == 0)
104			ExitStat = EX_SOFTWARE;
105		else
106			ExitStat = EX_OSERR;
107		if (tTd(54, 1))
108			printf("syserr: ExitStat = %d\n", ExitStat);
109	}
110
111	pw = sm_getpwuid(getuid());
112	if (pw != NULL)
113		uname = pw->pw_name;
114	else
115	{
116		uname = ubuf;
117		snprintf(ubuf, sizeof ubuf, "UID%d", getuid());
118	}
119
120	if (LogLevel > 0)
121		sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
122			  CurEnv == NULL ? NOQID : CurEnv->e_id,
123			  "SYSERR(%s): %.900s",
124			  uname, &MsgBuf[4]);
125	switch (olderrno)
126	{
127	  case EBADF:
128	  case ENFILE:
129	  case EMFILE:
130	  case ENOTTY:
131#ifdef EFBIG
132	  case EFBIG:
133#endif
134#ifdef ESPIPE
135	  case ESPIPE:
136#endif
137#ifdef EPIPE
138	  case EPIPE:
139#endif
140#ifdef ENOBUFS
141	  case ENOBUFS:
142#endif
143#ifdef ESTALE
144	  case ESTALE:
145#endif
146		printopenfds(TRUE);
147		mci_dump_all(TRUE);
148		break;
149	}
150	if (panic)
151	{
152#ifdef XLA
153		xla_all_end();
154#endif
155		if (tTd(0, 1))
156			abort();
157		exit(EX_OSERR);
158	}
159	errno = 0;
160	if (QuickAbort)
161		longjmp(TopFrame, 2);
162}
163/*
164**  USRERR -- Signal user error.
165**
166**	This is much like syserr except it is for user errors.
167**
168**	Parameters:
169**		fmt -- the format string.  If it does not begin with
170**			a three-digit SMTP reply code, 501 is assumed.
171**		(others) -- printf strings
172**
173**	Returns:
174**		none
175**		Through TopFrame if QuickAbort is set.
176**
177**	Side Effects:
178**		increments Errors.
179*/
180
181/*VARARGS1*/
182void
183#ifdef __STDC__
184usrerr(const char *fmt, ...)
185#else
186usrerr(fmt, va_alist)
187	const char *fmt;
188	va_dcl
189#endif
190{
191	VA_LOCAL_DECL
192
193	if (SuprErrs)
194		return;
195
196	VA_START(fmt);
197	fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
198	VA_END;
199
200	/* save this message for mailq printing */
201	switch (MsgBuf[0])
202	{
203	  case '4':
204	  case '8':
205		if (CurEnv->e_message != NULL)
206			break;
207
208		/* fall through.... */
209
210	  case '5':
211	  case '6':
212		if (CurEnv->e_message != NULL)
213			free(CurEnv->e_message);
214		if (MsgBuf[0] == '6')
215		{
216			char buf[MAXLINE];
217
218			snprintf(buf, sizeof buf, "Postmaster warning: %.*s",
219				(int)sizeof buf - 22, MsgBuf + 4);
220			CurEnv->e_message = newstr(buf);
221		}
222		else
223		{
224			CurEnv->e_message = newstr(MsgBuf + 4);
225		}
226		break;
227	}
228
229	puterrmsg(MsgBuf);
230
231	if (LogLevel > 3 && LogUsrErrs)
232		sm_syslog(LOG_NOTICE, CurEnv->e_id,
233			"%.900s",
234			&MsgBuf[4]);
235
236	if (QuickAbort)
237		longjmp(TopFrame, 1);
238}
239/*
240**  MESSAGE -- print message (not necessarily an error)
241**
242**	Parameters:
243**		msg -- the message (printf fmt) -- it can begin with
244**			an SMTP reply code.  If not, 050 is assumed.
245**		(others) -- printf arguments
246**
247**	Returns:
248**		none
249**
250**	Side Effects:
251**		none.
252*/
253
254/*VARARGS1*/
255void
256#ifdef __STDC__
257message(const char *msg, ...)
258#else
259message(msg, va_alist)
260	const char *msg;
261	va_dcl
262#endif
263{
264	VA_LOCAL_DECL
265
266	errno = 0;
267	VA_START(msg);
268	fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
269	VA_END;
270	putoutmsg(MsgBuf, FALSE, FALSE);
271
272	/* save this message for mailq printing */
273	switch (MsgBuf[0])
274	{
275	  case '4':
276	  case '8':
277		if (CurEnv->e_message != NULL)
278			break;
279		/* fall through.... */
280
281	  case '5':
282		if (CurEnv->e_message != NULL)
283			free(CurEnv->e_message);
284		CurEnv->e_message = newstr(MsgBuf + 4);
285		break;
286	}
287}
288/*
289**  NMESSAGE -- print message (not necessarily an error)
290**
291**	Just like "message" except it never puts the to... tag on.
292**
293**	Parameters:
294**		msg -- the message (printf fmt) -- if it begins
295**			with a three digit SMTP reply code, that is used,
296**			otherwise 050 is assumed.
297**		(others) -- printf arguments
298**
299**	Returns:
300**		none
301**
302**	Side Effects:
303**		none.
304*/
305
306/*VARARGS1*/
307void
308#ifdef __STDC__
309nmessage(const char *msg, ...)
310#else
311nmessage(msg, va_alist)
312	const char *msg;
313	va_dcl
314#endif
315{
316	VA_LOCAL_DECL
317
318	errno = 0;
319	VA_START(msg);
320	fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
321	VA_END;
322	putoutmsg(MsgBuf, FALSE, FALSE);
323
324	/* save this message for mailq printing */
325	switch (MsgBuf[0])
326	{
327	  case '4':
328	  case '8':
329		if (CurEnv->e_message != NULL)
330			break;
331		/* fall through.... */
332
333	  case '5':
334		if (CurEnv->e_message != NULL)
335			free(CurEnv->e_message);
336		CurEnv->e_message = newstr(MsgBuf + 4);
337		break;
338	}
339}
340/*
341**  PUTOUTMSG -- output error message to transcript and channel
342**
343**	Parameters:
344**		msg -- message to output (in SMTP format).
345**		holdmsg -- if TRUE, don't output a copy of the message to
346**			our output channel.
347**		heldmsg -- if TRUE, this is a previously held message;
348**			don't log it to the transcript file.
349**
350**	Returns:
351**		none.
352**
353**	Side Effects:
354**		Outputs msg to the transcript.
355**		If appropriate, outputs it to the channel.
356**		Deletes SMTP reply code number as appropriate.
357*/
358
359void
360putoutmsg(msg, holdmsg, heldmsg)
361	char *msg;
362	bool holdmsg;
363	bool heldmsg;
364{
365	char msgcode = msg[0];
366
367	/* display for debugging */
368	if (tTd(54, 8))
369		printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
370			heldmsg ? " (held)" : "");
371
372	/* map warnings to something SMTP can handle */
373	if (msgcode == '6')
374		msg[0] = '5';
375	else if (msgcode == '8')
376		msg[0] = '4';
377
378	/* output to transcript if serious */
379	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
380	    strchr("45", msg[0]) != NULL)
381		fprintf(CurEnv->e_xfp, "%s\n", msg);
382
383	if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
384		sm_syslog(LOG_INFO, CurEnv->e_id,
385			"--> %s%s",
386			msg, holdmsg ? " (held)" : "");
387
388	if (msgcode == '8')
389		msg[0] = '0';
390
391	/* output to channel if appropriate */
392	if (!Verbose && msg[0] == '0')
393		return;
394	if (holdmsg)
395	{
396		/* save for possible future display */
397		msg[0] = msgcode;
398		snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg);
399		return;
400	}
401
402	(void) fflush(stdout);
403
404	if (OutChannel == NULL)
405		return;
406
407	/* if DisConnected, OutChannel now points to the transcript */
408	if (!DisConnected &&
409	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
410		fprintf(OutChannel, "%s\r\n", msg);
411	else
412		fprintf(OutChannel, "%s\n", &msg[4]);
413	if (TrafficLogFile != NULL)
414		fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(),
415			(OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
416	if (msg[3] == ' ')
417		(void) fflush(OutChannel);
418	if (!ferror(OutChannel) || DisConnected)
419		return;
420
421	/*
422	**  Error on output -- if reporting lost channel, just ignore it.
423	**  Also, ignore errors from QUIT response (221 message) -- some
424	**	rude servers don't read result.
425	*/
426
427	if (InChannel == NULL || feof(InChannel) || ferror(InChannel) ||
428	    strncmp(msg, "221", 3) == 0)
429		return;
430
431	/* can't call syserr, 'cause we are using MsgBuf */
432	HoldErrs = TRUE;
433	if (LogLevel > 0)
434		sm_syslog(LOG_CRIT, CurEnv->e_id,
435			"SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
436			CurHostName == NULL ? "NO-HOST" : CurHostName,
437			shortenstring(msg, MAXSHORTSTR), errstring(errno));
438}
439/*
440**  PUTERRMSG -- like putoutmsg, but does special processing for error messages
441**
442**	Parameters:
443**		msg -- the message to output.
444**
445**	Returns:
446**		none.
447**
448**	Side Effects:
449**		Sets the fatal error bit in the envelope as appropriate.
450*/
451
452void
453puterrmsg(msg)
454	char *msg;
455{
456	char msgcode = msg[0];
457
458	/* output the message as usual */
459	putoutmsg(msg, HoldErrs, FALSE);
460
461	/* be careful about multiple error messages */
462	if (OnlyOneError)
463		HoldErrs = TRUE;
464
465	/* signal the error */
466	Errors++;
467
468	if (CurEnv == NULL)
469		return;
470
471	if (msgcode == '6')
472	{
473		/* notify the postmaster */
474		CurEnv->e_flags |= EF_PM_NOTIFY;
475	}
476	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
477	{
478		/* mark long-term fatal errors */
479		CurEnv->e_flags |= EF_FATALERRS;
480	}
481}
482/*
483**  FMTMSG -- format a message into buffer.
484**
485**	Parameters:
486**		eb -- error buffer to get result.
487**		to -- the recipient tag for this message.
488**		num -- arpanet error number.
489**		en -- the error number to display.
490**		fmt -- format of string.
491**		a, b, c, d, e -- arguments.
492**
493**	Returns:
494**		none.
495**
496**	Side Effects:
497**		none.
498*/
499
500static void
501fmtmsg(eb, to, num, eno, fmt, ap)
502	register char *eb;
503	const char *to;
504	const char *num;
505	int eno;
506	const char *fmt;
507	va_list ap;
508{
509	char del;
510	int l;
511	int spaceleft = sizeof MsgBuf;
512
513	/* output the reply code */
514	if (isascii(fmt[0]) && isdigit(fmt[0]) &&
515	    isascii(fmt[1]) && isdigit(fmt[1]) &&
516	    isascii(fmt[2]) && isdigit(fmt[2]))
517	{
518		num = fmt;
519		fmt += 4;
520	}
521	if (num[3] == '-')
522		del = '-';
523	else
524		del = ' ';
525	(void) snprintf(eb, spaceleft, "%3.3s%c", num, del);
526	eb += 4;
527	spaceleft -= 4;
528
529	/* output the file name and line number */
530	if (FileName != NULL)
531	{
532		(void) snprintf(eb, spaceleft, "%s: line %d: ",
533			shortenstring(FileName, 83), LineNumber);
534		eb += (l = strlen(eb));
535		spaceleft -= l;
536	}
537
538	/* output the "to" person */
539	if (to != NULL && to[0] != '\0' &&
540	    strncmp(num, "551", 3) != 0 &&
541	    strncmp(num, "251", 3) != 0)
542	{
543		(void) snprintf(eb, spaceleft, "%s... ",
544			shortenstring(to, MAXSHORTSTR));
545		spaceleft -= strlen(eb);
546		while (*eb != '\0')
547			*eb++ &= 0177;
548	}
549
550	/* output the message */
551	(void) vsnprintf(eb, spaceleft, fmt, ap);
552	spaceleft -= strlen(eb);
553	while (*eb != '\0')
554		*eb++ &= 0177;
555
556	/* output the error code, if any */
557	if (eno != 0)
558		(void) snprintf(eb, spaceleft, ": %s", errstring(eno));
559}
560/*
561**  BUFFER_ERRORS -- arrange to buffer future error messages
562**
563**	Parameters:
564**		none
565**
566**	Returns:
567**		none.
568*/
569
570void
571buffer_errors()
572{
573	HeldMessageBuf[0] = '\0';
574	HoldErrs = TRUE;
575}
576/*
577**  FLUSH_ERRORS -- flush the held error message buffer
578**
579**	Parameters:
580**		print -- if set, print the message, otherwise just
581**			delete it.
582**
583**	Returns:
584**		none.
585*/
586
587void
588flush_errors(print)
589	bool print;
590{
591	if (print && HeldMessageBuf[0] != '\0')
592		putoutmsg(HeldMessageBuf, FALSE, TRUE);
593	HeldMessageBuf[0] = '\0';
594	HoldErrs = FALSE;
595}
596/*
597**  ERRSTRING -- return string description of error code
598**
599**	Parameters:
600**		errnum -- the error number to translate
601**
602**	Returns:
603**		A string description of errnum.
604**
605**	Side Effects:
606**		none.
607*/
608
609const char *
610errstring(errnum)
611	int errnum;
612{
613	char *dnsmsg;
614	char *bp;
615	static char buf[MAXLINE];
616# if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
617	extern char *sys_errlist[];
618	extern int sys_nerr;
619# endif
620# if SMTP
621	extern char *SmtpPhase;
622# endif /* SMTP */
623
624	/*
625	**  Handle special network error codes.
626	**
627	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
628	*/
629
630	dnsmsg = NULL;
631	switch (errnum)
632	{
633# if defined(DAEMON) && defined(ETIMEDOUT)
634	  case ETIMEDOUT:
635	  case ECONNRESET:
636		bp = buf;
637#if HASSTRERROR
638		snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum));
639#else
640		if (errnum >= 0 && errnum < sys_nerr)
641			snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]);
642		else
643			snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum);
644#endif
645		bp += strlen(bp);
646		if (CurHostName != NULL)
647		{
648			if (errnum == ETIMEDOUT)
649			{
650				snprintf(bp, SPACELEFT(buf, bp), " with ");
651				bp += strlen(bp);
652			}
653			else
654			{
655				bp = buf;
656				snprintf(bp, SPACELEFT(buf, bp),
657					"Connection reset by ");
658				bp += strlen(bp);
659			}
660			snprintf(bp, SPACELEFT(buf, bp), "%s",
661				shortenstring(CurHostName, MAXSHORTSTR));
662			bp += strlen(buf);
663		}
664		if (SmtpPhase != NULL)
665		{
666			snprintf(bp, SPACELEFT(buf, bp), " during %s",
667				SmtpPhase);
668		}
669		return (buf);
670
671	  case EHOSTDOWN:
672		if (CurHostName == NULL)
673			break;
674		(void) snprintf(buf, sizeof buf, "Host %s is down",
675			shortenstring(CurHostName, MAXSHORTSTR));
676		return (buf);
677
678	  case ECONNREFUSED:
679		if (CurHostName == NULL)
680			break;
681		(void) snprintf(buf, sizeof buf, "Connection refused by %s",
682			shortenstring(CurHostName, MAXSHORTSTR));
683		return (buf);
684# endif
685
686# if NAMED_BIND
687	  case HOST_NOT_FOUND + E_DNSBASE:
688		dnsmsg = "host not found";
689		break;
690
691	  case TRY_AGAIN + E_DNSBASE:
692		dnsmsg = "host name lookup failure";
693		break;
694
695	  case NO_RECOVERY + E_DNSBASE:
696		dnsmsg = "non-recoverable error";
697		break;
698
699	  case NO_DATA + E_DNSBASE:
700		dnsmsg = "no data known";
701		break;
702# endif
703
704	  case EPERM:
705		/* SunOS gives "Not owner" -- this is the POSIX message */
706		return "Operation not permitted";
707
708	/*
709	**  Error messages used internally in sendmail.
710	*/
711
712	  case E_SM_OPENTIMEOUT:
713		return "Timeout on file open";
714
715	  case E_SM_NOSLINK:
716		return "Symbolic links not allowed";
717
718	  case E_SM_NOHLINK:
719		return "Hard links not allowed";
720
721	  case E_SM_REGONLY:
722		return "Regular files only";
723
724	  case E_SM_ISEXEC:
725		return "Executable files not allowed";
726
727	  case E_SM_WWDIR:
728		return "World writable directory";
729
730	  case E_SM_GWDIR:
731		return "Group writable directory";
732
733	  case E_SM_FILECHANGE:
734		return "File changed after open";
735
736	  case E_SM_WWFILE:
737		return "World writable file";
738
739	  case E_SM_GWFILE:
740		return "Group writable file";
741	}
742
743	if (dnsmsg != NULL)
744	{
745		bp = buf;
746		strcpy(bp, "Name server: ");
747		bp += strlen(bp);
748		if (CurHostName != NULL)
749		{
750			snprintf(bp, SPACELEFT(buf, bp), "%s: ",
751				shortenstring(CurHostName, MAXSHORTSTR));
752			bp += strlen(bp);
753		}
754		snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg);
755		return buf;
756	}
757
758#if HASSTRERROR
759	return strerror(errnum);
760#else
761	if (errnum > 0 && errnum < sys_nerr)
762		return (sys_errlist[errnum]);
763
764	(void) snprintf(buf, sizeof buf, "Error %d", errnum);
765	return (buf);
766#endif
767}
768