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