err.c revision 168520
1/*
2 * Copyright (c) 1998-2003 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 * $FreeBSD: head/contrib/sendmail/src/err.c 168520 2007-04-09 01:44:16Z gshapiro $
13 */
14
15#include <sendmail.h>
16
17SM_RCSID("@(#)$Id: err.c,v 8.196 2006/11/10 23:14:08 ca Exp $")
18
19#if LDAPMAP
20# include <lber.h>
21# include <ldap.h>			/* for LDAP error codes */
22#endif /* LDAPMAP */
23
24static void	putoutmsg __P((char *, bool, bool));
25static void	puterrmsg __P((char *));
26static char	*fmtmsg __P((char *, const char *, const char *, const char *,
27			     int, const char *, va_list));
28
29/*
30**  FATAL_ERROR -- handle a fatal exception
31**
32**	This function is installed as the default exception handler
33**	in the main sendmail process, and in all child processes
34**	that we create.  Its job is to handle exceptions that are not
35**	handled at a lower level.
36**
37**	The theory is that unhandled exceptions will be 'fatal' class
38**	exceptions (with an "F:" prefix), such as the out-of-memory
39**	exception "F:sm.heap".  As such, they are handled by exiting
40**	the process in exactly the same way that xalloc() in Sendmail 8.10
41**	exits the process when it fails due to lack of memory:
42**	we call syserr with a message beginning with "!".
43**
44**	Parameters:
45**		exc -- exception which is terminating this process
46**
47**	Returns:
48**		none
49*/
50
51void
52fatal_error(exc)
53	SM_EXC_T *exc;
54{
55	static char buf[256];
56	SM_FILE_T f;
57
58	/*
59	**  This function may be called when the heap is exhausted.
60	**  The following code writes the message for 'exc' into our
61	**  static buffer without allocating memory or raising exceptions.
62	*/
63
64	sm_strio_init(&f, buf, sizeof(buf));
65	sm_exc_write(exc, &f);
66	(void) sm_io_flush(&f, SM_TIME_DEFAULT);
67
68	/*
69	**  Terminate the process after logging an error and cleaning up.
70	**  Problems:
71	**  - syserr decides what class of error this is by looking at errno.
72	**    That's no good; we should look at the exc structure.
73	**  - The cleanup code should be moved out of syserr
74	**    and into individual exception handlers
75	**    that are part of the module they clean up after.
76	*/
77
78	errno = ENOMEM;
79	syserr("!%s", buf);
80}
81
82/*
83**  SYSERR -- Print error message.
84**
85**	Prints an error message via sm_io_printf to the diagnostic output.
86**
87**	If the first character of the syserr message is `!' it will
88**	log this as an ALERT message and exit immediately.  This can
89**	leave queue files in an indeterminate state, so it should not
90**	be used lightly.
91**
92**	If the first character of the syserr message is '!' or '@'
93**	then syserr knows that the process is about to be terminated,
94**	so the SMTP reply code defaults to 421.  Otherwise, the
95**	reply code defaults to 451 or 554, depending on errno.
96**
97**	Parameters:
98**		fmt -- the format string.  An optional '!' or '@',
99**			followed by an optional three-digit SMTP
100**			reply code, followed by message text.
101**		(others) -- parameters
102**
103**	Returns:
104**		none
105**		Raises E:mta.quickabort if QuickAbort is set.
106**
107**	Side Effects:
108**		increments Errors.
109**		sets ExitStat.
110*/
111
112char		MsgBuf[BUFSIZ*2];	/* text of most recent message */
113static char	HeldMessageBuf[sizeof(MsgBuf)];	/* for held messages */
114
115#if NAMED_BIND && !defined(NO_DATA)
116# define NO_DATA	NO_ADDRESS
117#endif /* NAMED_BIND && !defined(NO_DATA) */
118
119void
120/*VARARGS1*/
121#ifdef __STDC__
122syserr(const char *fmt, ...)
123#else /* __STDC__ */
124syserr(fmt, va_alist)
125	const char *fmt;
126	va_dcl
127#endif /* __STDC__ */
128{
129	register char *p;
130	int save_errno = errno;
131	bool panic;
132	bool exiting;
133	char *user;
134	char *enhsc;
135	char *errtxt;
136	struct passwd *pw;
137	char ubuf[80];
138	SM_VA_LOCAL_DECL
139
140	switch (*fmt)
141	{
142	  case '!':
143		++fmt;
144		panic = true;
145		exiting = true;
146		break;
147	  case '@':
148		++fmt;
149		panic = false;
150		exiting = true;
151		break;
152	  default:
153		panic = false;
154		exiting = false;
155		break;
156	}
157
158	/* format and output the error message */
159	if (exiting)
160	{
161		/*
162		**  Since we are terminating the process,
163		**  we are aborting the entire SMTP session,
164		**  rather than just the current transaction.
165		*/
166
167		p = "421";
168		enhsc = "4.0.0";
169	}
170	else if (save_errno == 0)
171	{
172		p = "554";
173		enhsc = "5.0.0";
174	}
175	else
176	{
177		p = "451";
178		enhsc = "4.0.0";
179	}
180	SM_VA_START(ap, fmt);
181	errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
182	SM_VA_END(ap);
183	puterrmsg(MsgBuf);
184
185	/* save this message for mailq printing */
186	if (!panic && CurEnv != NULL)
187	{
188		char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
189
190		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
191			sm_free(CurEnv->e_message);
192		CurEnv->e_message = nmsg;
193	}
194
195	/* determine exit status if not already set */
196	if (ExitStat == EX_OK)
197	{
198		if (save_errno == 0)
199			ExitStat = EX_SOFTWARE;
200		else
201			ExitStat = EX_OSERR;
202		if (tTd(54, 1))
203			sm_dprintf("syserr: ExitStat = %d\n", ExitStat);
204	}
205
206	pw = sm_getpwuid(RealUid);
207	if (pw != NULL)
208		user = pw->pw_name;
209	else
210	{
211		user = ubuf;
212		(void) sm_snprintf(ubuf, sizeof(ubuf), "UID%d", (int) RealUid);
213	}
214
215	if (LogLevel > 0)
216		sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
217			  CurEnv == NULL ? NOQID : CurEnv->e_id,
218			  "SYSERR(%s): %.900s",
219			  user, errtxt);
220	switch (save_errno)
221	{
222	  case EBADF:
223	  case ENFILE:
224	  case EMFILE:
225	  case ENOTTY:
226#ifdef EFBIG
227	  case EFBIG:
228#endif /* EFBIG */
229#ifdef ESPIPE
230	  case ESPIPE:
231#endif /* ESPIPE */
232#ifdef EPIPE
233	  case EPIPE:
234#endif /* EPIPE */
235#ifdef ENOBUFS
236	  case ENOBUFS:
237#endif /* ENOBUFS */
238#ifdef ESTALE
239	  case ESTALE:
240#endif /* ESTALE */
241		printopenfds(true);
242		mci_dump_all(smioout, true);
243		break;
244	}
245	if (panic)
246	{
247#if XLA
248		xla_all_end();
249#endif /* XLA */
250		sync_queue_time();
251		if (tTd(0, 1))
252			abort();
253		exit(EX_OSERR);
254	}
255	errno = 0;
256	if (QuickAbort)
257		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
258}
259/*
260**  USRERR -- Signal user error.
261**
262**	This is much like syserr except it is for user errors.
263**
264**	Parameters:
265**		fmt -- the format string.  If it does not begin with
266**			a three-digit SMTP reply code, 550 is assumed.
267**		(others) -- sm_io_printf strings
268**
269**	Returns:
270**		none
271**		Raises E:mta.quickabort if QuickAbort is set.
272**
273**	Side Effects:
274**		increments Errors.
275*/
276
277/*VARARGS1*/
278void
279#ifdef __STDC__
280usrerr(const char *fmt, ...)
281#else /* __STDC__ */
282usrerr(fmt, va_alist)
283	const char *fmt;
284	va_dcl
285#endif /* __STDC__ */
286{
287	char *enhsc;
288	char *errtxt;
289	SM_VA_LOCAL_DECL
290
291	if (fmt[0] == '5' || fmt[0] == '6')
292		enhsc = "5.0.0";
293	else if (fmt[0] == '4' || fmt[0] == '8')
294		enhsc = "4.0.0";
295	else if (fmt[0] == '2')
296		enhsc = "2.0.0";
297	else
298		enhsc = NULL;
299	SM_VA_START(ap, fmt);
300	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
301	SM_VA_END(ap);
302
303	if (SuprErrs)
304		return;
305
306	/* save this message for mailq printing */
307	switch (MsgBuf[0])
308	{
309	  case '4':
310	  case '8':
311		if (CurEnv->e_message != NULL)
312			break;
313
314		/* FALLTHROUGH */
315
316	  case '5':
317	  case '6':
318		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
319			sm_free(CurEnv->e_message);
320		if (MsgBuf[0] == '6')
321		{
322			char buf[MAXLINE];
323
324			(void) sm_snprintf(buf, sizeof(buf),
325					   "Postmaster warning: %.*s",
326					   (int) sizeof(buf) - 22, errtxt);
327			CurEnv->e_message =
328				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
329		}
330		else
331		{
332			CurEnv->e_message =
333				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
334		}
335		break;
336	}
337
338	puterrmsg(MsgBuf);
339	if (LogLevel > 3 && LogUsrErrs)
340		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
341	if (QuickAbort)
342		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
343}
344/*
345**  USRERRENH -- Signal user error.
346**
347**	Same as usrerr but with enhanced status code.
348**
349**	Parameters:
350**		enhsc -- the enhanced status code.
351**		fmt -- the format string.  If it does not begin with
352**			a three-digit SMTP reply code, 550 is assumed.
353**		(others) -- sm_io_printf strings
354**
355**	Returns:
356**		none
357**		Raises E:mta.quickabort if QuickAbort is set.
358**
359**	Side Effects:
360**		increments Errors.
361*/
362
363/*VARARGS1*/
364void
365#ifdef __STDC__
366usrerrenh(char *enhsc, const char *fmt, ...)
367#else /* __STDC__ */
368usrerrenh(enhsc, fmt, va_alist)
369	char *enhsc;
370	const char *fmt;
371	va_dcl
372#endif /* __STDC__ */
373{
374	char *errtxt;
375	SM_VA_LOCAL_DECL
376
377	if (enhsc == NULL || *enhsc == '\0')
378	{
379		if (fmt[0] == '5' || fmt[0] == '6')
380			enhsc = "5.0.0";
381		else if (fmt[0] == '4' || fmt[0] == '8')
382			enhsc = "4.0.0";
383		else if (fmt[0] == '2')
384			enhsc = "2.0.0";
385	}
386	SM_VA_START(ap, fmt);
387	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
388	SM_VA_END(ap);
389
390	if (SuprErrs)
391		return;
392
393	/* save this message for mailq printing */
394	switch (MsgBuf[0])
395	{
396	  case '4':
397	  case '8':
398		if (CurEnv->e_message != NULL)
399			break;
400
401		/* FALLTHROUGH */
402
403	  case '5':
404	  case '6':
405		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
406			sm_free(CurEnv->e_message);
407		if (MsgBuf[0] == '6')
408		{
409			char buf[MAXLINE];
410
411			(void) sm_snprintf(buf, sizeof(buf),
412					   "Postmaster warning: %.*s",
413					   (int) sizeof(buf) - 22, errtxt);
414			CurEnv->e_message =
415				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
416		}
417		else
418		{
419			CurEnv->e_message =
420				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
421		}
422		break;
423	}
424
425	puterrmsg(MsgBuf);
426	if (LogLevel > 3 && LogUsrErrs)
427		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
428	if (QuickAbort)
429		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
430}
431/*
432**  MESSAGE -- print message (not necessarily an error)
433**
434**	Parameters:
435**		msg -- the message (sm_io_printf fmt) -- it can begin with
436**			an SMTP reply code.  If not, 050 is assumed.
437**		(others) -- sm_io_printf arguments
438**
439**	Returns:
440**		none
441**
442**	Side Effects:
443**		none.
444*/
445
446/*VARARGS1*/
447void
448#ifdef __STDC__
449message(const char *msg, ...)
450#else /* __STDC__ */
451message(msg, va_alist)
452	const char *msg;
453	va_dcl
454#endif /* __STDC__ */
455{
456	char *errtxt;
457	SM_VA_LOCAL_DECL
458
459	errno = 0;
460	SM_VA_START(ap, msg);
461	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
462	SM_VA_END(ap);
463	putoutmsg(MsgBuf, false, false);
464
465	/* save this message for mailq printing */
466	switch (MsgBuf[0])
467	{
468	  case '4':
469	  case '8':
470		if (CurEnv->e_message != NULL)
471			break;
472		/* FALLTHROUGH */
473
474	  case '5':
475		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
476			sm_free(CurEnv->e_message);
477		CurEnv->e_message =
478			sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
479		break;
480	}
481}
482/*
483**  NMESSAGE -- print message (not necessarily an error)
484**
485**	Just like "message" except it never puts the to... tag on.
486**
487**	Parameters:
488**		msg -- the message (sm_io_printf fmt) -- if it begins
489**			with a three digit SMTP reply code, that is used,
490**			otherwise 050 is assumed.
491**		(others) -- sm_io_printf arguments
492**
493**	Returns:
494**		none
495**
496**	Side Effects:
497**		none.
498*/
499
500/*VARARGS1*/
501void
502#ifdef __STDC__
503nmessage(const char *msg, ...)
504#else /* __STDC__ */
505nmessage(msg, va_alist)
506	const char *msg;
507	va_dcl
508#endif /* __STDC__ */
509{
510	char *errtxt;
511	SM_VA_LOCAL_DECL
512
513	errno = 0;
514	SM_VA_START(ap, msg);
515	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
516			(char *) NULL, 0, msg, ap);
517	SM_VA_END(ap);
518	putoutmsg(MsgBuf, false, false);
519
520	/* save this message for mailq printing */
521	switch (MsgBuf[0])
522	{
523	  case '4':
524	  case '8':
525		if (CurEnv->e_message != NULL)
526			break;
527		/* FALLTHROUGH */
528
529	  case '5':
530		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
531			sm_free(CurEnv->e_message);
532		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
533		break;
534	}
535}
536/*
537**  PUTOUTMSG -- output error message to transcript and channel
538**
539**	Parameters:
540**		msg -- message to output (in SMTP format).
541**		holdmsg -- if true, don't output a copy of the message to
542**			our output channel.
543**		heldmsg -- if true, this is a previously held message;
544**			don't log it to the transcript file.
545**
546**	Returns:
547**		none.
548**
549**	Side Effects:
550**		Outputs msg to the transcript.
551**		If appropriate, outputs it to the channel.
552**		Deletes SMTP reply code number as appropriate.
553*/
554
555static void
556putoutmsg(msg, holdmsg, heldmsg)
557	char *msg;
558	bool holdmsg;
559	bool heldmsg;
560{
561	char msgcode = msg[0];
562	char *errtxt = msg;
563	char *id;
564
565	/* display for debugging */
566	if (tTd(54, 8))
567		sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
568			heldmsg ? " (held)" : "");
569
570	/* map warnings to something SMTP can handle */
571	if (msgcode == '6')
572		msg[0] = '5';
573	else if (msgcode == '8')
574		msg[0] = '4';
575	id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
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, 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, 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 (SoftBounce && num[0] == '5')
842	{
843		/* replace 5 by 4 */
844		(void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
845	}
846	else
847		(void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
848	eb += 4;
849	spaceleft -= 4;
850
851	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
852	{
853		/* copy enh.status code including trailing blank */
854		l++;
855		(void) sm_strlcpy(eb, fmt, l + 1);
856		eb += l;
857		spaceleft -= l;
858		fmt += l;
859	}
860	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
861	{
862		/* copy enh.status code */
863		(void) sm_strlcpy(eb, enhsc, l + 1);
864		eb[l] = ' ';
865		eb[++l] = '\0';
866		eb += l;
867		spaceleft -= l;
868	}
869	if (SoftBounce && eb[-l] == '5')
870	{
871		/* replace 5 by 4 */
872		eb[-l] = '4';
873	}
874	errtxt = eb;
875
876	/* output the file name and line number */
877	if (FileName != NULL)
878	{
879		(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
880				   shortenstring(FileName, 83), LineNumber);
881		eb += (l = strlen(eb));
882		spaceleft -= l;
883	}
884
885	/*
886	**  output the "to" address only if it is defined and one of the
887	**  following codes is used:
888	**  050 internal notices, e.g., alias expansion
889	**  250 Ok
890	**  252 Cannot VRFY user, but will accept message and attempt delivery
891	**  450 Requested mail action not taken: mailbox unavailable
892	**  550 Requested action not taken: mailbox unavailable
893	**  553 Requested action not taken: mailbox name not allowed
894	**
895	**  Notice: this still isn't "the right thing", this code shouldn't
896	**	(indirectly) depend on CurEnv->e_to.
897	*/
898
899	if (to != NULL && to[0] != '\0' &&
900	    (strncmp(num, "050", 3) == 0 ||
901	     strncmp(num, "250", 3) == 0 ||
902	     strncmp(num, "252", 3) == 0 ||
903	     strncmp(num, "450", 3) == 0 ||
904	     strncmp(num, "550", 3) == 0 ||
905	     strncmp(num, "553", 3) == 0))
906	{
907		(void) sm_strlcpyn(eb, spaceleft, 2,
908				   shortenstring(to, MAXSHORTSTR), "... ");
909		spaceleft -= strlen(eb);
910		while (*eb != '\0')
911			*eb++ &= 0177;
912	}
913
914	/* output the message */
915	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
916	spaceleft -= strlen(eb);
917	while (*eb != '\0')
918		*eb++ &= 0177;
919
920	/* output the error code, if any */
921	if (eno != 0)
922		(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
923
924	return errtxt;
925}
926/*
927**  BUFFER_ERRORS -- arrange to buffer future error messages
928**
929**	Parameters:
930**		none
931**
932**	Returns:
933**		none.
934*/
935
936void
937buffer_errors()
938{
939	HeldMessageBuf[0] = '\0';
940	HoldErrs = true;
941}
942/*
943**  FLUSH_ERRORS -- flush the held error message buffer
944**
945**	Parameters:
946**		print -- if set, print the message, otherwise just
947**			delete it.
948**
949**	Returns:
950**		none.
951*/
952
953void
954flush_errors(print)
955	bool print;
956{
957	if (print && HeldMessageBuf[0] != '\0')
958		putoutmsg(HeldMessageBuf, false, true);
959	HeldMessageBuf[0] = '\0';
960	HoldErrs = false;
961}
962/*
963**  SM_ERRSTRING -- return string description of error code
964**
965**	Parameters:
966**		errnum -- the error number to translate
967**
968**	Returns:
969**		A string description of errnum.
970**
971**	Side Effects:
972**		none.
973*/
974
975const char *
976sm_errstring(errnum)
977	int errnum;
978{
979	char *dnsmsg;
980	char *bp;
981	static char buf[MAXLINE];
982#if HASSTRERROR
983	char *err;
984	char errbuf[30];
985#endif /* HASSTRERROR */
986#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
987	extern char *sys_errlist[];
988	extern int sys_nerr;
989#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
990
991	/*
992	**  Handle special network error codes.
993	**
994	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
995	*/
996
997	dnsmsg = NULL;
998	switch (errnum)
999	{
1000	  case ETIMEDOUT:
1001	  case ECONNRESET:
1002		bp = buf;
1003#if HASSTRERROR
1004		err = strerror(errnum);
1005		if (err == NULL)
1006		{
1007			(void) sm_snprintf(errbuf, sizeof(errbuf),
1008					   "Error %d", errnum);
1009			err = errbuf;
1010		}
1011		(void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
1012#else /* HASSTRERROR */
1013		if (errnum >= 0 && errnum < sys_nerr)
1014			(void) sm_strlcpy(bp, sys_errlist[errnum],
1015					  SPACELEFT(buf, bp));
1016		else
1017			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1018				"Error %d", errnum);
1019#endif /* HASSTRERROR */
1020		bp += strlen(bp);
1021		if (CurHostName != NULL)
1022		{
1023			if (errnum == ETIMEDOUT)
1024			{
1025				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1026					" with ");
1027				bp += strlen(bp);
1028			}
1029			else
1030			{
1031				bp = buf;
1032				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1033					"Connection reset by ");
1034				bp += strlen(bp);
1035			}
1036			(void) sm_strlcpy(bp,
1037					shortenstring(CurHostName, MAXSHORTSTR),
1038					SPACELEFT(buf, bp));
1039			bp += strlen(buf);
1040		}
1041		if (SmtpPhase != NULL)
1042		{
1043			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1044				" during %s", SmtpPhase);
1045		}
1046		return buf;
1047
1048	  case EHOSTDOWN:
1049		if (CurHostName == NULL)
1050			break;
1051		(void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
1052			shortenstring(CurHostName, MAXSHORTSTR));
1053		return buf;
1054
1055	  case ECONNREFUSED:
1056		if (CurHostName == NULL)
1057			break;
1058		(void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
1059			shortenstring(CurHostName, MAXSHORTSTR));
1060		return buf;
1061
1062#if NAMED_BIND
1063	  case HOST_NOT_FOUND + E_DNSBASE:
1064		dnsmsg = "host not found";
1065		break;
1066
1067	  case TRY_AGAIN + E_DNSBASE:
1068		dnsmsg = "host name lookup failure";
1069		break;
1070
1071	  case NO_RECOVERY + E_DNSBASE:
1072		dnsmsg = "non-recoverable error";
1073		break;
1074
1075	  case NO_DATA + E_DNSBASE:
1076		dnsmsg = "no data known";
1077		break;
1078#endif /* NAMED_BIND */
1079
1080	  case EPERM:
1081		/* SunOS gives "Not owner" -- this is the POSIX message */
1082		return "Operation not permitted";
1083
1084	/*
1085	**  Error messages used internally in sendmail.
1086	*/
1087
1088	  case E_SM_OPENTIMEOUT:
1089		return "Timeout on file open";
1090
1091	  case E_SM_NOSLINK:
1092		return "Symbolic links not allowed";
1093
1094	  case E_SM_NOHLINK:
1095		return "Hard links not allowed";
1096
1097	  case E_SM_REGONLY:
1098		return "Regular files only";
1099
1100	  case E_SM_ISEXEC:
1101		return "Executable files not allowed";
1102
1103	  case E_SM_WWDIR:
1104		return "World writable directory";
1105
1106	  case E_SM_GWDIR:
1107		return "Group writable directory";
1108
1109	  case E_SM_FILECHANGE:
1110		return "File changed after open";
1111
1112	  case E_SM_WWFILE:
1113		return "World writable file";
1114
1115	  case E_SM_GWFILE:
1116		return "Group writable file";
1117
1118	  case E_SM_GRFILE:
1119		return "Group readable file";
1120
1121	  case E_SM_WRFILE:
1122		return "World readable file";
1123	}
1124
1125	if (dnsmsg != NULL)
1126	{
1127		bp = buf;
1128		bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
1129		if (CurHostName != NULL)
1130		{
1131			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
1132				shortenstring(CurHostName, MAXSHORTSTR), ": ");
1133			bp += strlen(bp);
1134		}
1135		(void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
1136		return buf;
1137	}
1138
1139#if LDAPMAP
1140	if (errnum >= E_LDAPBASE)
1141		return ldap_err2string(errnum - E_LDAPBASE);
1142#endif /* LDAPMAP */
1143
1144#if HASSTRERROR
1145	err = strerror(errnum);
1146	if (err == NULL)
1147	{
1148		(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1149		return buf;
1150	}
1151	return err;
1152#else /* HASSTRERROR */
1153	if (errnum > 0 && errnum < sys_nerr)
1154		return sys_errlist[errnum];
1155
1156	(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1157	return buf;
1158#endif /* HASSTRERROR */
1159}
1160