err.c revision 98125
1/*
2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15
16SM_RCSID("@(#)$Id: err.c,v 8.189 2002/01/09 18:52:30 ca Exp $")
17
18/* $FreeBSD: head/contrib/sendmail/src/err.c 98125 2002-06-11 21:16:51Z gshapiro $ */
19
20#if LDAPMAP
21# include <lber.h>
22# include <ldap.h>			/* for LDAP error codes */
23#endif /* LDAPMAP */
24
25static void	putoutmsg __P((char *, bool, bool));
26static void	puterrmsg __P((char *));
27static char	*fmtmsg __P((char *, const char *, const char *, const char *,
28			     int, const char *, va_list));
29
30/*
31**  FATAL_ERROR -- handle a fatal exception
32**
33**	This function is installed as the default exception handler
34**	in the main sendmail process, and in all child processes
35**	that we create.  Its job is to handle exceptions that are not
36**	handled at a lower level.
37**
38**	The theory is that unhandled exceptions will be 'fatal' class
39**	exceptions (with an "F:" prefix), such as the out-of-memory
40**	exception "F:sm.heap".  As such, they are handled by exiting
41**	the process in exactly the same way that xalloc() in Sendmail 8.10
42**	exits the process when it fails due to lack of memory:
43**	we call syserr with a message beginning with "!".
44**
45**	Parameters:
46**		exc -- exception which is terminating this process
47**
48**	Returns:
49**		none
50*/
51
52void
53fatal_error(exc)
54	SM_EXC_T *exc;
55{
56	static char buf[256];
57	SM_FILE_T f;
58
59	/*
60	**  This function may be called when the heap is exhausted.
61	**  The following code writes the message for 'exc' into our
62	**  static buffer without allocating memory or raising exceptions.
63	*/
64
65	sm_strio_init(&f, buf, sizeof(buf));
66	sm_exc_write(exc, &f);
67	(void) sm_io_flush(&f, SM_TIME_DEFAULT);
68
69	/*
70	**  Terminate the process after logging an error and cleaning up.
71	**  Problems:
72	**  - syserr decides what class of error this is by looking at errno.
73	**    That's no good; we should look at the exc structure.
74	**  - The cleanup code should be moved out of syserr
75	**    and into individual exception handlers
76	**    that are part of the module they clean up after.
77	*/
78
79	errno = ENOMEM;
80	syserr("!%s", buf);
81}
82
83/*
84**  SYSERR -- Print error message.
85**
86**	Prints an error message via sm_io_printf to the diagnostic output.
87**
88**	If the first character of the syserr message is `!' it will
89**	log this as an ALERT message and exit immediately.  This can
90**	leave queue files in an indeterminate state, so it should not
91**	be used lightly.
92**
93**	If the first character of the syserr message is '!' or '@'
94**	then syserr knows that the process is about to be terminated,
95**	so the SMTP reply code defaults to 421.  Otherwise, the
96**	reply code defaults to 451 or 554, depending on errno.
97**
98**	Parameters:
99**		fmt -- the format string.  An optional '!' or '@',
100**			followed by an optional three-digit SMTP
101**			reply code, followed by message text.
102**		(others) -- parameters
103**
104**	Returns:
105**		none
106**		Raises E:mta.quickabort if QuickAbort is set.
107**
108**	Side Effects:
109**		increments Errors.
110**		sets ExitStat.
111*/
112
113char		MsgBuf[BUFSIZ*2];	/* text of most recent message */
114static char	HeldMessageBuf[sizeof MsgBuf];	/* for held messages */
115
116#if NAMED_BIND && !defined(NO_DATA)
117# define NO_DATA	NO_ADDRESS
118#endif /* NAMED_BIND && !defined(NO_DATA) */
119
120void
121/*VARARGS1*/
122#ifdef __STDC__
123syserr(const char *fmt, ...)
124#else /* __STDC__ */
125syserr(fmt, va_alist)
126	const char *fmt;
127	va_dcl
128#endif /* __STDC__ */
129{
130	register char *p;
131	int save_errno = errno;
132	bool panic;
133	bool exiting;
134	char *user;
135	char *enhsc;
136	char *errtxt;
137	struct passwd *pw;
138	char ubuf[80];
139	SM_VA_LOCAL_DECL
140
141	switch (*fmt)
142	{
143	  case '!':
144		++fmt;
145		panic = true;
146		exiting = true;
147		break;
148	  case '@':
149		++fmt;
150		panic = false;
151		exiting = true;
152		break;
153	  default:
154		panic = false;
155		exiting = false;
156		break;
157	}
158
159	/* format and output the error message */
160	if (exiting)
161	{
162		/*
163		**  Since we are terminating the process,
164		**  we are aborting the entire SMTP session,
165		**  rather than just the current transaction.
166		*/
167
168		p = "421";
169		enhsc = "4.0.0";
170	}
171	else if (save_errno == 0)
172	{
173		p = "554";
174		enhsc = "5.0.0";
175	}
176	else
177	{
178		p = "451";
179		enhsc = "4.0.0";
180	}
181	SM_VA_START(ap, fmt);
182	errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
183	SM_VA_END(ap);
184	puterrmsg(MsgBuf);
185
186	/* save this message for mailq printing */
187	if (!panic && CurEnv != NULL)
188	{
189		char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
190
191		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
192			sm_free(CurEnv->e_message);
193		CurEnv->e_message = nmsg;
194	}
195
196	/* determine exit status if not already set */
197	if (ExitStat == EX_OK)
198	{
199		if (save_errno == 0)
200			ExitStat = EX_SOFTWARE;
201		else
202			ExitStat = EX_OSERR;
203		if (tTd(54, 1))
204			sm_dprintf("syserr: ExitStat = %d\n", ExitStat);
205	}
206
207	pw = sm_getpwuid(RealUid);
208	if (pw != NULL)
209		user = pw->pw_name;
210	else
211	{
212		user = ubuf;
213		(void) sm_snprintf(ubuf, sizeof ubuf, "UID%d", (int) RealUid);
214	}
215
216	if (LogLevel > 0)
217		sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
218			  CurEnv == NULL ? NOQID : CurEnv->e_id,
219			  "SYSERR(%s): %.900s",
220			  user, errtxt);
221	switch (save_errno)
222	{
223	  case EBADF:
224	  case ENFILE:
225	  case EMFILE:
226	  case ENOTTY:
227#ifdef EFBIG
228	  case EFBIG:
229#endif /* EFBIG */
230#ifdef ESPIPE
231	  case ESPIPE:
232#endif /* ESPIPE */
233#ifdef EPIPE
234	  case EPIPE:
235#endif /* EPIPE */
236#ifdef ENOBUFS
237	  case ENOBUFS:
238#endif /* ENOBUFS */
239#ifdef ESTALE
240	  case ESTALE:
241#endif /* ESTALE */
242		printopenfds(true);
243		mci_dump_all(true);
244		break;
245	}
246	if (panic)
247	{
248#if XLA
249		xla_all_end();
250#endif /* XLA */
251		sync_queue_time();
252		if (tTd(0, 1))
253			abort();
254		exit(EX_OSERR);
255	}
256	errno = 0;
257	if (QuickAbort)
258		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
259}
260/*
261**  USRERR -- Signal user error.
262**
263**	This is much like syserr except it is for user errors.
264**
265**	Parameters:
266**		fmt -- the format string.  If it does not begin with
267**			a three-digit SMTP reply code, 550 is assumed.
268**		(others) -- sm_io_printf strings
269**
270**	Returns:
271**		none
272**		Raises E:mta.quickabort if QuickAbort is set.
273**
274**	Side Effects:
275**		increments Errors.
276*/
277
278/*VARARGS1*/
279void
280#ifdef __STDC__
281usrerr(const char *fmt, ...)
282#else /* __STDC__ */
283usrerr(fmt, va_alist)
284	const char *fmt;
285	va_dcl
286#endif /* __STDC__ */
287{
288	char *enhsc;
289	char *errtxt;
290	SM_VA_LOCAL_DECL
291
292	if (fmt[0] == '5' || fmt[0] == '6')
293		enhsc = "5.0.0";
294	else if (fmt[0] == '4' || fmt[0] == '8')
295		enhsc = "4.0.0";
296	else if (fmt[0] == '2')
297		enhsc = "2.0.0";
298	else
299		enhsc = NULL;
300	SM_VA_START(ap, fmt);
301	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
302	SM_VA_END(ap);
303
304	if (SuprErrs)
305		return;
306
307	/* save this message for mailq printing */
308	switch (MsgBuf[0])
309	{
310	  case '4':
311	  case '8':
312		if (CurEnv->e_message != NULL)
313			break;
314
315		/* FALLTHROUGH */
316
317	  case '5':
318	  case '6':
319		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
320			sm_free(CurEnv->e_message);
321		if (MsgBuf[0] == '6')
322		{
323			char buf[MAXLINE];
324
325			(void) sm_snprintf(buf, sizeof buf,
326					   "Postmaster warning: %.*s",
327					   (int) sizeof buf - 22, errtxt);
328			CurEnv->e_message =
329				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
330		}
331		else
332		{
333			CurEnv->e_message =
334				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
335		}
336		break;
337	}
338
339	puterrmsg(MsgBuf);
340	if (LogLevel > 3 && LogUsrErrs)
341		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
342	if (QuickAbort)
343		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
344}
345/*
346**  USRERRENH -- Signal user error.
347**
348**	Same as usrerr but with enhanced status code.
349**
350**	Parameters:
351**		enhsc -- the enhanced status code.
352**		fmt -- the format string.  If it does not begin with
353**			a three-digit SMTP reply code, 550 is assumed.
354**		(others) -- sm_io_printf strings
355**
356**	Returns:
357**		none
358**		Raises E:mta.quickabort if QuickAbort is set.
359**
360**	Side Effects:
361**		increments Errors.
362*/
363
364/*VARARGS1*/
365void
366#ifdef __STDC__
367usrerrenh(char *enhsc, const char *fmt, ...)
368#else /* __STDC__ */
369usrerrenh(enhsc, fmt, va_alist)
370	char *enhsc;
371	const char *fmt;
372	va_dcl
373#endif /* __STDC__ */
374{
375	char *errtxt;
376	SM_VA_LOCAL_DECL
377
378	if (enhsc == NULL || *enhsc == '\0')
379	{
380		if (fmt[0] == '5' || fmt[0] == '6')
381			enhsc = "5.0.0";
382		else if (fmt[0] == '4' || fmt[0] == '8')
383			enhsc = "4.0.0";
384		else if (fmt[0] == '2')
385			enhsc = "2.0.0";
386	}
387	SM_VA_START(ap, fmt);
388	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
389	SM_VA_END(ap);
390
391	if (SuprErrs)
392		return;
393
394	/* save this message for mailq printing */
395	switch (MsgBuf[0])
396	{
397	  case '4':
398	  case '8':
399		if (CurEnv->e_message != NULL)
400			break;
401
402		/* FALLTHROUGH */
403
404	  case '5':
405	  case '6':
406		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
407			sm_free(CurEnv->e_message);
408		if (MsgBuf[0] == '6')
409		{
410			char buf[MAXLINE];
411
412			(void) sm_snprintf(buf, sizeof buf,
413					   "Postmaster warning: %.*s",
414					   (int) sizeof buf - 22, errtxt);
415			CurEnv->e_message =
416				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
417		}
418		else
419		{
420			CurEnv->e_message =
421				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
422		}
423		break;
424	}
425
426	puterrmsg(MsgBuf);
427	if (LogLevel > 3 && LogUsrErrs)
428		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
429	if (QuickAbort)
430		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
431}
432/*
433**  MESSAGE -- print message (not necessarily an error)
434**
435**	Parameters:
436**		msg -- the message (sm_io_printf fmt) -- it can begin with
437**			an SMTP reply code.  If not, 050 is assumed.
438**		(others) -- sm_io_printf arguments
439**
440**	Returns:
441**		none
442**
443**	Side Effects:
444**		none.
445*/
446
447/*VARARGS1*/
448void
449#ifdef __STDC__
450message(const char *msg, ...)
451#else /* __STDC__ */
452message(msg, va_alist)
453	const char *msg;
454	va_dcl
455#endif /* __STDC__ */
456{
457	char *errtxt;
458	SM_VA_LOCAL_DECL
459
460	errno = 0;
461	SM_VA_START(ap, msg);
462	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
463	SM_VA_END(ap);
464	putoutmsg(MsgBuf, false, false);
465
466	/* save this message for mailq printing */
467	switch (MsgBuf[0])
468	{
469	  case '4':
470	  case '8':
471		if (CurEnv->e_message != NULL)
472			break;
473		/* FALLTHROUGH */
474
475	  case '5':
476		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
477			sm_free(CurEnv->e_message);
478		CurEnv->e_message =
479			sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
480		break;
481	}
482}
483/*
484**  NMESSAGE -- print message (not necessarily an error)
485**
486**	Just like "message" except it never puts the to... tag on.
487**
488**	Parameters:
489**		msg -- the message (sm_io_printf fmt) -- if it begins
490**			with a three digit SMTP reply code, that is used,
491**			otherwise 050 is assumed.
492**		(others) -- sm_io_printf arguments
493**
494**	Returns:
495**		none
496**
497**	Side Effects:
498**		none.
499*/
500
501/*VARARGS1*/
502void
503#ifdef __STDC__
504nmessage(const char *msg, ...)
505#else /* __STDC__ */
506nmessage(msg, va_alist)
507	const char *msg;
508	va_dcl
509#endif /* __STDC__ */
510{
511	char *errtxt;
512	SM_VA_LOCAL_DECL
513
514	errno = 0;
515	SM_VA_START(ap, msg);
516	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
517			(char *) NULL, 0, msg, ap);
518	SM_VA_END(ap);
519	putoutmsg(MsgBuf, false, false);
520
521	/* save this message for mailq printing */
522	switch (MsgBuf[0])
523	{
524	  case '4':
525	  case '8':
526		if (CurEnv->e_message != NULL)
527			break;
528		/* FALLTHROUGH */
529
530	  case '5':
531		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
532			sm_free(CurEnv->e_message);
533		CurEnv->e_message =
534			sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
535		break;
536	}
537}
538/*
539**  PUTOUTMSG -- output error message to transcript and channel
540**
541**	Parameters:
542**		msg -- message to output (in SMTP format).
543**		holdmsg -- if true, don't output a copy of the message to
544**			our output channel.
545**		heldmsg -- if true, this is a previously held message;
546**			don't log it to the transcript file.
547**
548**	Returns:
549**		none.
550**
551**	Side Effects:
552**		Outputs msg to the transcript.
553**		If appropriate, outputs it to the channel.
554**		Deletes SMTP reply code number as appropriate.
555*/
556
557static void
558putoutmsg(msg, holdmsg, heldmsg)
559	char *msg;
560	bool holdmsg;
561	bool heldmsg;
562{
563	char *errtxt = msg;
564	char msgcode = msg[0];
565
566	/* display for debugging */
567	if (tTd(54, 8))
568		sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
569			heldmsg ? " (held)" : "");
570
571	/* map warnings to something SMTP can handle */
572	if (msgcode == '6')
573		msg[0] = '5';
574	else if (msgcode == '8')
575		msg[0] = '4';
576
577	/* output to transcript if serious */
578	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
579	    strchr("45", msg[0]) != NULL)
580		(void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
581				     msg);
582
583	if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
584		sm_syslog(LOG_INFO, CurEnv->e_id,
585			  "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
586			  heldmsg ? " (held)" : "");
587
588	if (msgcode == '8')
589		msg[0] = '0';
590
591	/* output to channel if appropriate */
592	if (!Verbose && msg[0] == '0')
593		return;
594	if (holdmsg)
595	{
596		/* save for possible future display */
597		msg[0] = msgcode;
598		if (HeldMessageBuf[0] == '5' && msgcode == '4')
599			return;
600		(void) sm_strlcpy(HeldMessageBuf, msg, sizeof HeldMessageBuf);
601		return;
602	}
603
604	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
605
606	if (OutChannel == NULL)
607		return;
608
609	/* find actual text of error (after SMTP status codes) */
610	if (ISSMTPREPLY(errtxt))
611	{
612		int l;
613
614		errtxt += 4;
615		l = isenhsc(errtxt, ' ');
616		if (l <= 0)
617			l = isenhsc(errtxt, '\0');
618		if (l > 0)
619			errtxt += l + 1;
620	}
621
622	/* if DisConnected, OutChannel now points to the transcript */
623	if (!DisConnected &&
624	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
625		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
626				     msg);
627	else
628		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
629				     errtxt);
630	if (TrafficLogFile != NULL)
631		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
632				     "%05d >>> %s\n", (int) CurrentPid,
633				     (OpMode == MD_SMTP || OpMode == MD_DAEMON)
634					? msg : errtxt);
635#if !PIPELINING
636	/* XXX can't flush here for SMTP pipelining */
637	if (msg[3] == ' ')
638		(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
639	if (!sm_io_error(OutChannel) || DisConnected)
640		return;
641
642	/*
643	**  Error on output -- if reporting lost channel, just ignore it.
644	**  Also, ignore errors from QUIT response (221 message) -- some
645	**	rude servers don't read result.
646	*/
647
648	if (InChannel == NULL || sm_io_eof(InChannel) ||
649	    sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
650		return;
651
652	/* can't call syserr, 'cause we are using MsgBuf */
653	HoldErrs = true;
654	if (LogLevel > 0)
655		sm_syslog(LOG_CRIT, CurEnv->e_id,
656			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
657			  CURHOSTNAME,
658			  shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
659#endif /* !PIPELINING */
660}
661/*
662**  PUTERRMSG -- like putoutmsg, but does special processing for error messages
663**
664**	Parameters:
665**		msg -- the message to output.
666**
667**	Returns:
668**		none.
669**
670**	Side Effects:
671**		Sets the fatal error bit in the envelope as appropriate.
672*/
673
674static void
675puterrmsg(msg)
676	char *msg;
677{
678	char msgcode = msg[0];
679
680	/* output the message as usual */
681	putoutmsg(msg, HoldErrs, false);
682
683	/* be careful about multiple error messages */
684	if (OnlyOneError)
685		HoldErrs = true;
686
687	/* signal the error */
688	Errors++;
689
690	if (CurEnv == NULL)
691		return;
692
693	if (msgcode == '6')
694	{
695		/* notify the postmaster */
696		CurEnv->e_flags |= EF_PM_NOTIFY;
697	}
698	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
699	{
700		/* mark long-term fatal errors */
701		CurEnv->e_flags |= EF_FATALERRS;
702	}
703}
704/*
705**  ISENHSC -- check whether a string contains an enhanced status code
706**
707**	Parameters:
708**		s -- string with possible enhanced status code.
709**		delim -- delim for enhanced status code.
710**
711**	Returns:
712**		0  -- no enhanced status code.
713**		>4 -- length of enhanced status code.
714**
715**	Side Effects:
716**		none.
717*/
718int
719isenhsc(s, delim)
720	const char *s;
721	int delim;
722{
723	int l, h;
724
725	if (s == NULL)
726		return 0;
727	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
728		return 0;
729	h = 0;
730	l = 2;
731	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
732		++h;
733	if (h == 0 || s[l + h] != '.')
734		return 0;
735	l += h + 1;
736	h = 0;
737	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
738		++h;
739	if (h == 0 || s[l + h] != delim)
740		return 0;
741	return l + h;
742}
743/*
744**  EXTENHSC -- check and extract an enhanced status code
745**
746**	Parameters:
747**		s -- string with possible enhanced status code.
748**		delim -- delim for enhanced status code.
749**		e -- pointer to storage for enhanced status code.
750**			must be != NULL and have space for at least
751**			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
752**
753**	Returns:
754**		0  -- no enhanced status code.
755**		>4 -- length of enhanced status code.
756**
757**	Side Effects:
758**		fills e with enhanced status code.
759*/
760
761int
762extenhsc(s, delim, e)
763	const char *s;
764	int delim;
765	char *e;
766{
767	int l, h;
768
769	if (s == NULL)
770		return 0;
771	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
772		return 0;
773	h = 0;
774	l = 2;
775	e[0] = s[0];
776	e[1] = '.';
777	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
778	{
779		e[l + h] = s[l + h];
780		++h;
781	}
782	if (h == 0 || s[l + h] != '.')
783		return 0;
784	e[l + h] = '.';
785	l += h + 1;
786	h = 0;
787	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
788	{
789		e[l + h] = s[l + h];
790		++h;
791	}
792	if (h == 0 || s[l + h] != delim)
793		return 0;
794	e[l + h] = '\0';
795	return l + h;
796}
797/*
798**  FMTMSG -- format a message into buffer.
799**
800**	Parameters:
801**		eb -- error buffer to get result -- MUST BE MsgBuf.
802**		to -- the recipient tag for this message.
803**		num -- default three digit SMTP reply code.
804**		enhsc -- enhanced status code.
805**		en -- the error number to display.
806**		fmt -- format of string.
807**		ap -- arguments for fmt.
808**
809**	Returns:
810**		pointer to error text beyond status codes.
811**
812**	Side Effects:
813**		none.
814*/
815
816static char *
817fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
818	register char *eb;
819	const char *to;
820	const char *num;
821	const char *enhsc;
822	int eno;
823	const char *fmt;
824	SM_VA_LOCAL_DECL
825{
826	char del;
827	int l;
828	int spaceleft = sizeof MsgBuf;
829	char *errtxt;
830
831	/* output the reply code */
832	if (ISSMTPCODE(fmt))
833	{
834		num = fmt;
835		fmt += 4;
836	}
837	if (num[3] == '-')
838		del = '-';
839	else
840		del = ' ';
841#if _FFR_SOFT_BOUNCE
842	if (SoftBounce && num[0] == '5')
843	{
844		/* replace 5 by 4 */
845		(void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
846	}
847	else
848#endif /* _FFR_SOFT_BOUNCE */
849	(void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
850	eb += 4;
851	spaceleft -= 4;
852
853	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
854	{
855		/* copy enh.status code including trailing blank */
856		l++;
857		(void) sm_strlcpy(eb, fmt, l + 1);
858		eb += l;
859		spaceleft -= l;
860		fmt += l;
861	}
862	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
863	{
864		/* copy enh.status code */
865		(void) sm_strlcpy(eb, enhsc, l + 1);
866		eb[l] = ' ';
867		eb[++l] = '\0';
868		eb += l;
869		spaceleft -= l;
870	}
871#if _FFR_SOFT_BOUNCE
872	if (SoftBounce && eb[-l] == '5')
873	{
874		/* replace 5 by 4 */
875		eb[-l] = '4';
876	}
877#endif /* _FFR_SOFT_BOUNCE */
878	errtxt = eb;
879
880	/* output the file name and line number */
881	if (FileName != NULL)
882	{
883		(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
884				   shortenstring(FileName, 83), LineNumber);
885		eb += (l = strlen(eb));
886		spaceleft -= l;
887	}
888
889	/*
890	**  output the "to" address only if it is defined and one of the
891	**  following codes is used:
892	**  050 internal notices, e.g., alias expansion
893	**  250 Ok
894	**  252 Cannot VRFY user, but will accept message and attempt delivery
895	**  450 Requested mail action not taken: mailbox unavailable
896	**  550 Requested action not taken: mailbox unavailable
897	**  553 Requested action not taken: mailbox name not allowed
898	**
899	**  Notice: this still isn't "the right thing", this code shouldn't
900	**	(indirectly) depend on CurEnv->e_to.
901	*/
902
903	if (to != NULL && to[0] != '\0' &&
904	    (strncmp(num, "050", 3) == 0 ||
905	     strncmp(num, "250", 3) == 0 ||
906	     strncmp(num, "252", 3) == 0 ||
907	     strncmp(num, "450", 3) == 0 ||
908	     strncmp(num, "550", 3) == 0 ||
909	     strncmp(num, "553", 3) == 0))
910	{
911		(void) sm_strlcpyn(eb, spaceleft, 2,
912				   shortenstring(to, MAXSHORTSTR), "... ");
913		spaceleft -= strlen(eb);
914		while (*eb != '\0')
915			*eb++ &= 0177;
916	}
917
918	/* output the message */
919	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
920	spaceleft -= strlen(eb);
921	while (*eb != '\0')
922		*eb++ &= 0177;
923
924	/* output the error code, if any */
925	if (eno != 0)
926		(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
927
928	return errtxt;
929}
930/*
931**  BUFFER_ERRORS -- arrange to buffer future error messages
932**
933**	Parameters:
934**		none
935**
936**	Returns:
937**		none.
938*/
939
940void
941buffer_errors()
942{
943	HeldMessageBuf[0] = '\0';
944	HoldErrs = true;
945}
946/*
947**  FLUSH_ERRORS -- flush the held error message buffer
948**
949**	Parameters:
950**		print -- if set, print the message, otherwise just
951**			delete it.
952**
953**	Returns:
954**		none.
955*/
956
957void
958flush_errors(print)
959	bool print;
960{
961	if (print && HeldMessageBuf[0] != '\0')
962		putoutmsg(HeldMessageBuf, false, true);
963	HeldMessageBuf[0] = '\0';
964	HoldErrs = false;
965}
966/*
967**  SM_ERRSTRING -- return string description of error code
968**
969**	Parameters:
970**		errnum -- the error number to translate
971**
972**	Returns:
973**		A string description of errnum.
974**
975**	Side Effects:
976**		none.
977*/
978
979const char *
980sm_errstring(errnum)
981	int errnum;
982{
983	char *dnsmsg;
984	char *bp;
985	static char buf[MAXLINE];
986#if HASSTRERROR
987	char *err;
988	char errbuf[30];
989#endif /* HASSTRERROR */
990#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
991	extern char *sys_errlist[];
992	extern int sys_nerr;
993#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
994
995	/*
996	**  Handle special network error codes.
997	**
998	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
999	*/
1000
1001	dnsmsg = NULL;
1002	switch (errnum)
1003	{
1004	  case ETIMEDOUT:
1005	  case ECONNRESET:
1006		bp = buf;
1007#if HASSTRERROR
1008		err = strerror(errnum);
1009		if (err == NULL)
1010		{
1011			(void) sm_snprintf(errbuf, sizeof errbuf,
1012					   "Error %d", errnum);
1013			err = errbuf;
1014		}
1015		(void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
1016#else /* HASSTRERROR */
1017		if (errnum >= 0 && errnum < sys_nerr)
1018			(void) sm_strlcpy(bp, sys_errlist[errnum],
1019					  SPACELEFT(buf, bp));
1020		else
1021			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1022				"Error %d", errnum);
1023#endif /* HASSTRERROR */
1024		bp += strlen(bp);
1025		if (CurHostName != NULL)
1026		{
1027			if (errnum == ETIMEDOUT)
1028			{
1029				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1030					" with ");
1031				bp += strlen(bp);
1032			}
1033			else
1034			{
1035				bp = buf;
1036				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1037					"Connection reset by ");
1038				bp += strlen(bp);
1039			}
1040			(void) sm_strlcpy(bp,
1041					shortenstring(CurHostName, MAXSHORTSTR),
1042					SPACELEFT(buf, bp));
1043			bp += strlen(buf);
1044		}
1045		if (SmtpPhase != NULL)
1046		{
1047			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1048				" during %s", SmtpPhase);
1049		}
1050		return buf;
1051
1052	  case EHOSTDOWN:
1053		if (CurHostName == NULL)
1054			break;
1055		(void) sm_snprintf(buf, sizeof buf, "Host %s is down",
1056			shortenstring(CurHostName, MAXSHORTSTR));
1057		return buf;
1058
1059	  case ECONNREFUSED:
1060		if (CurHostName == NULL)
1061			break;
1062		(void) sm_strlcpyn(buf, sizeof buf, 2, "Connection refused by ",
1063			shortenstring(CurHostName, MAXSHORTSTR));
1064		return buf;
1065
1066#if NAMED_BIND
1067	  case HOST_NOT_FOUND + E_DNSBASE:
1068		dnsmsg = "host not found";
1069		break;
1070
1071	  case TRY_AGAIN + E_DNSBASE:
1072		dnsmsg = "host name lookup failure";
1073		break;
1074
1075	  case NO_RECOVERY + E_DNSBASE:
1076		dnsmsg = "non-recoverable error";
1077		break;
1078
1079	  case NO_DATA + E_DNSBASE:
1080		dnsmsg = "no data known";
1081		break;
1082#endif /* NAMED_BIND */
1083
1084	  case EPERM:
1085		/* SunOS gives "Not owner" -- this is the POSIX message */
1086		return "Operation not permitted";
1087
1088	/*
1089	**  Error messages used internally in sendmail.
1090	*/
1091
1092	  case E_SM_OPENTIMEOUT:
1093		return "Timeout on file open";
1094
1095	  case E_SM_NOSLINK:
1096		return "Symbolic links not allowed";
1097
1098	  case E_SM_NOHLINK:
1099		return "Hard links not allowed";
1100
1101	  case E_SM_REGONLY:
1102		return "Regular files only";
1103
1104	  case E_SM_ISEXEC:
1105		return "Executable files not allowed";
1106
1107	  case E_SM_WWDIR:
1108		return "World writable directory";
1109
1110	  case E_SM_GWDIR:
1111		return "Group writable directory";
1112
1113	  case E_SM_FILECHANGE:
1114		return "File changed after open";
1115
1116	  case E_SM_WWFILE:
1117		return "World writable file";
1118
1119	  case E_SM_GWFILE:
1120		return "Group writable file";
1121
1122	  case E_SM_GRFILE:
1123		return "Group readable file";
1124
1125	  case E_SM_WRFILE:
1126		return "World readable file";
1127	}
1128
1129	if (dnsmsg != NULL)
1130	{
1131		bp = buf;
1132		bp += sm_strlcpy(bp, "Name server: ", sizeof buf);
1133		if (CurHostName != NULL)
1134		{
1135			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
1136				shortenstring(CurHostName, MAXSHORTSTR), ": ");
1137			bp += strlen(bp);
1138		}
1139		(void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
1140		return buf;
1141	}
1142
1143#if LDAPMAP
1144	if (errnum >= E_LDAPBASE)
1145		return ldap_err2string(errnum - E_LDAPBASE);
1146#endif /* LDAPMAP */
1147
1148#if HASSTRERROR
1149	err = strerror(errnum);
1150	if (err == NULL)
1151	{
1152		(void) sm_snprintf(buf, sizeof buf, "Error %d", errnum);
1153		return buf;
1154	}
1155	return err;
1156#else /* HASSTRERROR */
1157	if (errnum > 0 && errnum < sys_nerr)
1158		return sys_errlist[errnum];
1159
1160	(void) sm_snprintf(buf, sizeof buf, "Error %d", errnum);
1161	return buf;
1162#endif /* HASSTRERROR */
1163}
1164