1/*
2 * Copyright (c) 1998-2003, 2010 Proofpoint, 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.206 2013-11-22 20:51:55 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/*VARARGS2*/
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/*
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 = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
478		break;
479	}
480}
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 = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
534		break;
535	}
536}
537/*
538**  PUTOUTMSG -- output error message to transcript and channel
539**
540**	Parameters:
541**		msg -- message to output (in SMTP format).
542**		holdmsg -- if true, don't output a copy of the message to
543**			our output channel.
544**		heldmsg -- if true, this is a previously held message;
545**			don't log it to the transcript file.
546**
547**	Returns:
548**		none.
549**
550**	Side Effects:
551**		Outputs msg to the transcript.
552**		If appropriate, outputs it to the channel.
553**		Deletes SMTP reply code number as appropriate.
554*/
555
556static void
557putoutmsg(msg, holdmsg, heldmsg)
558	char *msg;
559	bool holdmsg;
560	bool heldmsg;
561{
562	char msgcode = msg[0];
563	char *errtxt = msg;
564	char *id;
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	id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
577
578	/* output to transcript if serious */
579	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
580	    strchr("45", msg[0]) != NULL)
581		(void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
582				     msg);
583
584	if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
585		sm_syslog(LOG_INFO, id,
586			  "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
587			  heldmsg ? " (held)" : "");
588
589	if (msgcode == '8')
590		msg[0] = '0';
591
592	/* output to channel if appropriate */
593	if (!Verbose && msg[0] == '0')
594		return;
595	if (holdmsg)
596	{
597		/* save for possible future display */
598		msg[0] = msgcode;
599		if (HeldMessageBuf[0] == '5' && msgcode == '4')
600			return;
601		(void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
602		return;
603	}
604
605	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
606
607	if (OutChannel == NULL)
608		return;
609
610	/* find actual text of error (after SMTP status codes) */
611	if (ISSMTPREPLY(errtxt))
612	{
613		int l;
614
615		errtxt += 4;
616		l = isenhsc(errtxt, ' ');
617		if (l <= 0)
618			l = isenhsc(errtxt, '\0');
619		if (l > 0)
620			errtxt += l + 1;
621	}
622
623	/* if DisConnected, OutChannel now points to the transcript */
624	if (!DisConnected &&
625	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
626		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
627				     msg);
628	else
629		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
630				     errtxt);
631	if (TrafficLogFile != NULL)
632		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
633				     "%05d >>> %s\n", (int) CurrentPid,
634				     (OpMode == MD_SMTP || OpMode == MD_DAEMON)
635					? msg : errtxt);
636#if !PIPELINING
637	/* XXX can't flush here for SMTP pipelining */
638	if (msg[3] == ' ')
639		(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
640	if (!sm_io_error(OutChannel) || DisConnected)
641		return;
642
643	/*
644	**  Error on output -- if reporting lost channel, just ignore it.
645	**  Also, ignore errors from QUIT response (221 message) -- some
646	**	rude servers don't read result.
647	*/
648
649	if (InChannel == NULL || sm_io_eof(InChannel) ||
650	    sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
651		return;
652
653	/* can't call syserr, 'cause we are using MsgBuf */
654	HoldErrs = true;
655	if (LogLevel > 0)
656		sm_syslog(LOG_CRIT, id,
657			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
658			  CURHOSTNAME,
659			  shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
660#endif /* !PIPELINING */
661}
662/*
663**  PUTERRMSG -- like putoutmsg, but does special processing for error messages
664**
665**	Parameters:
666**		msg -- the message to output.
667**
668**	Returns:
669**		none.
670**
671**	Side Effects:
672**		Sets the fatal error bit in the envelope as appropriate.
673*/
674
675static void
676puterrmsg(msg)
677	char *msg;
678{
679	char msgcode = msg[0];
680
681	/* output the message as usual */
682	putoutmsg(msg, HoldErrs, false);
683
684	/* be careful about multiple error messages */
685	if (OnlyOneError)
686		HoldErrs = true;
687
688	/* signal the error */
689	Errors++;
690
691	if (CurEnv == NULL)
692		return;
693
694	if (msgcode == '6')
695	{
696		/* notify the postmaster */
697		CurEnv->e_flags |= EF_PM_NOTIFY;
698	}
699	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
700	{
701		/* mark long-term fatal errors */
702		CurEnv->e_flags |= EF_FATALERRS;
703	}
704}
705/*
706**  ISENHSC -- check whether a string contains an enhanced status code
707**
708**	Parameters:
709**		s -- string with possible enhanced status code.
710**		delim -- delim for enhanced status code.
711**
712**	Returns:
713**		0  -- no enhanced status code.
714**		>4 -- length of enhanced status code.
715**
716**	Side Effects:
717**		none.
718*/
719int
720isenhsc(s, delim)
721	const char *s;
722	int delim;
723{
724	int l, h;
725
726	if (s == NULL)
727		return 0;
728	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
729		return 0;
730	h = 0;
731	l = 2;
732	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
733		++h;
734	if (h == 0 || s[l + h] != '.')
735		return 0;
736	l += h + 1;
737	h = 0;
738	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
739		++h;
740	if (h == 0 || s[l + h] != delim)
741		return 0;
742	return l + h;
743}
744/*
745**  EXTENHSC -- check and extract an enhanced status code
746**
747**	Parameters:
748**		s -- string with possible enhanced status code.
749**		delim -- delim for enhanced status code.
750**		e -- pointer to storage for enhanced status code.
751**			must be != NULL and have space for at least
752**			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
753**
754**	Returns:
755**		0  -- no enhanced status code.
756**		>4 -- length of enhanced status code.
757**
758**	Side Effects:
759**		fills e with enhanced status code.
760*/
761
762int
763extenhsc(s, delim, e)
764	const char *s;
765	int delim;
766	char *e;
767{
768	int l, h;
769
770	if (s == NULL)
771		return 0;
772	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
773		return 0;
774	h = 0;
775	l = 2;
776	e[0] = s[0];
777	e[1] = '.';
778	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
779	{
780		e[l + h] = s[l + h];
781		++h;
782	}
783	if (h == 0 || s[l + h] != '.')
784		return 0;
785	e[l + h] = '.';
786	l += h + 1;
787	h = 0;
788	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
789	{
790		e[l + h] = s[l + h];
791		++h;
792	}
793	if (h == 0 || s[l + h] != delim)
794		return 0;
795	e[l + h] = '\0';
796	return l + h;
797}
798/*
799**  FMTMSG -- format a message into buffer.
800**
801**	Parameters:
802**		eb -- error buffer to get result -- MUST BE MsgBuf.
803**		to -- the recipient tag for this message.
804**		num -- default three digit SMTP reply code.
805**		enhsc -- enhanced status code.
806**		en -- the error number to display.
807**		fmt -- format of string.
808**		ap -- arguments for fmt.
809**
810**	Returns:
811**		pointer to error text beyond status codes.
812**
813**	Side Effects:
814**		none.
815*/
816
817static char *
818fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
819	register char *eb;
820	const char *to;
821	const char *num;
822	const char *enhsc;
823	int eno;
824	const char *fmt;
825	SM_VA_LOCAL_DECL
826{
827	char del;
828	int l;
829	int spaceleft = sizeof(MsgBuf);
830	char *errtxt;
831
832	/* output the reply code */
833	if (ISSMTPCODE(fmt))
834	{
835		num = fmt;
836		fmt += 4;
837	}
838	if (num[3] == '-')
839		del = '-';
840	else
841		del = ' ';
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		(void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
849	eb += 4;
850	spaceleft -= 4;
851
852	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
853	{
854		/* copy enh.status code including trailing blank */
855		l++;
856		(void) sm_strlcpy(eb, fmt, l + 1);
857		eb += l;
858		spaceleft -= l;
859		fmt += l;
860	}
861	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
862	{
863		/* copy enh.status code */
864		(void) sm_strlcpy(eb, enhsc, l + 1);
865		eb[l] = ' ';
866		eb[++l] = '\0';
867		eb += l;
868		spaceleft -= l;
869	}
870	if (SoftBounce && eb[-l] == '5')
871	{
872		/* replace 5 by 4 */
873		eb[-l] = '4';
874	}
875	errtxt = eb;
876
877	/* output the file name and line number */
878	if (FileName != NULL)
879	{
880		(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
881				   shortenstring(FileName, 83), LineNumber);
882		eb += (l = strlen(eb));
883		spaceleft -= l;
884	}
885
886	/*
887	**  output the "to" address only if it is defined and one of the
888	**  following codes is used:
889	**  050 internal notices, e.g., alias expansion
890	**  250 Ok
891	**  252 Cannot VRFY user, but will accept message and attempt delivery
892	**  450 Requested mail action not taken: mailbox unavailable
893	**  550 Requested action not taken: mailbox unavailable
894	**  553 Requested action not taken: mailbox name not allowed
895	**
896	**  Notice: this still isn't "the right thing", this code shouldn't
897	**	(indirectly) depend on CurEnv->e_to.
898	*/
899
900	if (to != NULL && to[0] != '\0' &&
901	    (strncmp(num, "050", 3) == 0 ||
902	     strncmp(num, "250", 3) == 0 ||
903	     strncmp(num, "252", 3) == 0 ||
904	     strncmp(num, "450", 3) == 0 ||
905	     strncmp(num, "550", 3) == 0 ||
906	     strncmp(num, "553", 3) == 0))
907	{
908		(void) sm_strlcpyn(eb, spaceleft, 2,
909				   shortenstring(to, MAXSHORTSTR), "... ");
910		spaceleft -= strlen(eb);
911		while (*eb != '\0')
912			*eb++ &= 0177;
913	}
914
915	/* output the message */
916	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
917	spaceleft -= strlen(eb);
918	while (*eb != '\0')
919		*eb++ &= 0177;
920
921	/* output the error code, if any */
922	if (eno != 0)
923		(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
924
925	return errtxt;
926}
927/*
928**  BUFFER_ERRORS -- arrange to buffer future error messages
929**
930**	Parameters:
931**		none
932**
933**	Returns:
934**		none.
935*/
936
937void
938buffer_errors()
939{
940	HeldMessageBuf[0] = '\0';
941	HoldErrs = true;
942}
943/*
944**  FLUSH_ERRORS -- flush the held error message buffer
945**
946**	Parameters:
947**		print -- if set, print the message, otherwise just
948**			delete it.
949**
950**	Returns:
951**		none.
952*/
953
954void
955flush_errors(print)
956	bool print;
957{
958	if (print && HeldMessageBuf[0] != '\0')
959		putoutmsg(HeldMessageBuf, false, true);
960	HeldMessageBuf[0] = '\0';
961	HoldErrs = false;
962}
963/*
964**  SM_ERRSTRING -- return string description of error code
965**
966**	Parameters:
967**		errnum -- the error number to translate
968**
969**	Returns:
970**		A string description of errnum.
971**
972**	Side Effects:
973**		none.
974*/
975
976const char *
977sm_errstring(errnum)
978	int errnum;
979{
980	char *dnsmsg;
981	char *bp;
982	static char buf[MAXLINE];
983#if HASSTRERROR
984	char *err;
985	char errbuf[30];
986#endif /* HASSTRERROR */
987#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
988	extern char *sys_errlist[];
989	extern int sys_nerr;
990#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
991
992	/*
993	**  Handle special network error codes.
994	**
995	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
996	*/
997
998	dnsmsg = NULL;
999	switch (errnum)
1000	{
1001	  case ETIMEDOUT:
1002	  case ECONNRESET:
1003		bp = buf;
1004#if HASSTRERROR
1005		err = strerror(errnum);
1006		if (err == NULL)
1007		{
1008			(void) sm_snprintf(errbuf, sizeof(errbuf),
1009					   "Error %d", errnum);
1010			err = errbuf;
1011		}
1012		(void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
1013#else /* HASSTRERROR */
1014		if (errnum >= 0 && errnum < sys_nerr)
1015			(void) sm_strlcpy(bp, sys_errlist[errnum],
1016					  SPACELEFT(buf, bp));
1017		else
1018			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1019				"Error %d", errnum);
1020#endif /* HASSTRERROR */
1021		bp += strlen(bp);
1022		if (CurHostName != NULL)
1023		{
1024			if (errnum == ETIMEDOUT)
1025			{
1026				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1027					" with ");
1028				bp += strlen(bp);
1029			}
1030			else
1031			{
1032				bp = buf;
1033				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1034					"Connection reset by ");
1035				bp += strlen(bp);
1036			}
1037			(void) sm_strlcpy(bp,
1038					shortenstring(CurHostName, MAXSHORTSTR),
1039					SPACELEFT(buf, bp));
1040			bp += strlen(buf);
1041		}
1042		if (SmtpPhase != NULL)
1043		{
1044			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1045				" during %s", SmtpPhase);
1046		}
1047		return buf;
1048
1049	  case EHOSTDOWN:
1050		if (CurHostName == NULL)
1051			break;
1052		(void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
1053			shortenstring(CurHostName, MAXSHORTSTR));
1054		return buf;
1055
1056	  case ECONNREFUSED:
1057		if (CurHostName == NULL)
1058			break;
1059		(void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
1060			shortenstring(CurHostName, MAXSHORTSTR));
1061		return buf;
1062
1063#if NAMED_BIND
1064	  case HOST_NOT_FOUND + E_DNSBASE:
1065		dnsmsg = "host not found";
1066		break;
1067
1068	  case TRY_AGAIN + E_DNSBASE:
1069		dnsmsg = "host name lookup failure";
1070		break;
1071
1072	  case NO_RECOVERY + E_DNSBASE:
1073		dnsmsg = "non-recoverable error";
1074		break;
1075
1076	  case NO_DATA + E_DNSBASE:
1077		dnsmsg = "no data known";
1078		break;
1079#endif /* NAMED_BIND */
1080
1081	  case EPERM:
1082		/* SunOS gives "Not owner" -- this is the POSIX message */
1083		return "Operation not permitted";
1084
1085	/*
1086	**  Error messages used internally in sendmail.
1087	*/
1088
1089	  case E_SM_OPENTIMEOUT:
1090		return "Timeout on file open";
1091
1092	  case E_SM_NOSLINK:
1093		return "Symbolic links not allowed";
1094
1095	  case E_SM_NOHLINK:
1096		return "Hard links not allowed";
1097
1098	  case E_SM_REGONLY:
1099		return "Regular files only";
1100
1101	  case E_SM_ISEXEC:
1102		return "Executable files not allowed";
1103
1104	  case E_SM_WWDIR:
1105		return "World writable directory";
1106
1107	  case E_SM_GWDIR:
1108		return "Group writable directory";
1109
1110	  case E_SM_FILECHANGE:
1111		return "File changed after open";
1112
1113	  case E_SM_WWFILE:
1114		return "World writable file";
1115
1116	  case E_SM_GWFILE:
1117		return "Group writable file";
1118
1119	  case E_SM_GRFILE:
1120		return "Group readable file";
1121
1122	  case E_SM_WRFILE:
1123		return "World readable file";
1124	}
1125
1126	if (dnsmsg != NULL)
1127	{
1128		bp = buf;
1129		bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
1130		if (CurHostName != NULL)
1131		{
1132			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
1133				shortenstring(CurHostName, MAXSHORTSTR), ": ");
1134			bp += strlen(bp);
1135		}
1136		(void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
1137		return buf;
1138	}
1139
1140#if LDAPMAP
1141	if (errnum >= E_LDAPBASE)
1142		return ldap_err2string(errnum - E_LDAPBASE);
1143#endif /* LDAPMAP */
1144
1145#if HASSTRERROR
1146	err = strerror(errnum);
1147	if (err == NULL)
1148	{
1149		(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1150		return buf;
1151	}
1152	return err;
1153#else /* HASSTRERROR */
1154	if (errnum > 0 && errnum < sys_nerr)
1155		return sys_errlist[errnum];
1156
1157	(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1158	return buf;
1159#endif /* HASSTRERROR */
1160}
1161