1/*
2 * Copyright (c) 1998-2003, 2010, 2015 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
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
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, exiting, keep;
131	char *user;
132	char *enhsc;
133	char *errtxt;
134	struct passwd *pw;
135	char ubuf[80];
136	SM_VA_LOCAL_DECL
137
138	panic = exiting = keep = false;
139	switch (*fmt)
140	{
141	  case '!':
142		++fmt;
143		panic = exiting = true;
144		break;
145	  case '@':
146		++fmt;
147		exiting = true;
148		break;
149	  case '+':
150		++fmt;
151		keep = true;
152		break;
153	  default:
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 && (!keep || CurEnv->e_message == 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
228#ifdef ESPIPE
229	  case ESPIPE:
230#endif
231#ifdef EPIPE
232	  case EPIPE:
233#endif
234#ifdef ENOBUFS
235	  case ENOBUFS:
236#endif
237#ifdef ESTALE
238	  case ESTALE:
239#endif
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
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/*
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/*
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/*VARARGS2*/
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/*
434**  MESSAGE -- print message (not necessarily an error)
435**
436**	Parameters:
437**		msg -- the message (sm_io_printf fmt) -- it can begin with
438**			an SMTP reply code.  If not, 050 is assumed.
439**		(others) -- sm_io_printf arguments
440**
441**	Returns:
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 = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
477		break;
478	}
479}
480
481#if _FFR_PROXY
482/*
483**  EMESSAGE -- print message (not necessarily an error)
484**	(same as message() but requires reply code and enhanced status code)
485**
486**	Parameters:
487**		replycode -- SMTP reply code.
488**		enhsc -- enhanced status code.
489**		msg -- the message (sm_io_printf fmt) -- it can begin with
490**			an SMTP reply code.  If not, 050 is assumed.
491**		(others) -- sm_io_printf arguments
492**
493**	Returns:
494**		none
495*/
496
497/*VARARGS3*/
498void
499# ifdef __STDC__
500emessage(const char *replycode, const char *enhsc, const char *msg, ...)
501# else /* __STDC__ */
502emessage(replycode, enhsc, msg, va_alist)
503	const char *replycode;
504	const char *enhsc;
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, CurEnv->e_to, replycode, enhsc, 0, msg, ap);
515	SM_VA_END(ap);
516	putoutmsg(MsgBuf, false, false);
517
518	/* save this message for mailq printing */
519	switch (MsgBuf[0])
520	{
521	  case '4':
522	  case '8':
523		if (CurEnv->e_message != NULL)
524			break;
525		/* FALLTHROUGH */
526
527	  case '5':
528		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
529			sm_free(CurEnv->e_message);
530		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
531		break;
532	}
533}
534
535/*
536**  EXTSC -- check and extract a status codes
537**
538**	Parameters:
539**		msg -- string with possible enhanced status code.
540**		delim -- delim for enhanced status code.
541**		replycode -- pointer to storage for SMTP reply code;
542**			must be != NULL and have space for at least
543**			4 characters.
544**		enhsc -- pointer to storage for enhanced status code;
545**			must be != NULL and have space for at least
546**			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
547**
548**	Returns:
549**		-1  -- no SMTP reply code.
550**		>=3 -- offset of error text in msg.
551**		(<=4  -- no enhanced status code)
552*/
553
554int
555extsc(msg, delim, replycode, enhsc)
556	const char *msg;
557	int delim;
558	char *replycode;
559	char *enhsc;
560{
561	int offset;
562
563	SM_REQUIRE(replycode != NULL);
564	SM_REQUIRE(enhsc != NULL);
565	replycode[0] = '\0';
566	enhsc[0] = '\0';
567	if (msg == NULL)
568		return -1;
569	if (!ISSMTPREPLY(msg))
570		return -1;
571	sm_strlcpy(replycode, msg, 4);
572	if (msg[3] == '\0')
573		return 3;
574	offset = 4;
575	if (isenhsc(msg + 4, delim))
576		offset = extenhsc(msg + 4, delim, enhsc) + 4;
577	return offset;
578}
579#endif /* _FFR_PROXY */
580
581/*
582**  NMESSAGE -- print message (not necessarily an error)
583**
584**	Just like "message" except it never puts the to... tag on.
585**
586**	Parameters:
587**		msg -- the message (sm_io_printf fmt) -- if it begins
588**			with a three digit SMTP reply code, that is used,
589**			otherwise 050 is assumed.
590**		(others) -- sm_io_printf arguments
591**
592**	Returns:
593**		none
594*/
595
596/*VARARGS1*/
597void
598#ifdef __STDC__
599nmessage(const char *msg, ...)
600#else /* __STDC__ */
601nmessage(msg, va_alist)
602	const char *msg;
603	va_dcl
604#endif /* __STDC__ */
605{
606	char *errtxt;
607	SM_VA_LOCAL_DECL
608
609	errno = 0;
610	SM_VA_START(ap, msg);
611	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
612			(char *) NULL, 0, msg, ap);
613	SM_VA_END(ap);
614	putoutmsg(MsgBuf, false, false);
615
616	/* save this message for mailq printing */
617	switch (MsgBuf[0])
618	{
619	  case '4':
620	  case '8':
621		if (CurEnv->e_message != NULL)
622			break;
623		/* FALLTHROUGH */
624
625	  case '5':
626		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
627			sm_free(CurEnv->e_message);
628		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
629		break;
630	}
631}
632
633/*
634**  PUTOUTMSG -- output error message to transcript and channel
635**
636**	Parameters:
637**		msg -- message to output (in SMTP format).
638**		holdmsg -- if true, don't output a copy of the message to
639**			our output channel.
640**		heldmsg -- if true, this is a previously held message;
641**			don't log it to the transcript file.
642**
643**	Returns:
644**		none.
645**
646**	Side Effects:
647**		Outputs msg to the transcript.
648**		If appropriate, outputs it to the channel.
649**		Deletes SMTP reply code number as appropriate.
650*/
651
652static void
653putoutmsg(msg, holdmsg, heldmsg)
654	char *msg;
655	bool holdmsg;
656	bool heldmsg;
657{
658	char msgcode = msg[0];
659	char *errtxt = msg;
660	char *id;
661
662	/* display for debugging */
663	if (tTd(54, 8))
664		sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
665			heldmsg ? " (held)" : "");
666
667	/* map warnings to something SMTP can handle */
668	if (msgcode == '6')
669		msg[0] = '5';
670	else if (msgcode == '8')
671		msg[0] = '4';
672	id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
673
674	/* output to transcript if serious */
675	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
676	    strchr("45", msg[0]) != NULL)
677		(void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
678				     msg);
679
680	if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
681		sm_syslog(LOG_INFO, id,
682			  "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
683			  heldmsg ? " (held)" : "");
684
685	if (msgcode == '8')
686		msg[0] = '0';
687
688	/* output to channel if appropriate */
689	if (!Verbose && msg[0] == '0')
690		return;
691	if (holdmsg)
692	{
693		/* save for possible future display */
694		msg[0] = msgcode;
695		if (HeldMessageBuf[0] == '5' && msgcode == '4')
696			return;
697		(void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
698		return;
699	}
700
701	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
702
703	if (OutChannel == NULL)
704		return;
705
706	/* find actual text of error (after SMTP status codes) */
707	if (ISSMTPREPLY(errtxt))
708	{
709		int l;
710
711		errtxt += 4;
712		l = isenhsc(errtxt, ' ');
713		if (l <= 0)
714			l = isenhsc(errtxt, '\0');
715		if (l > 0)
716			errtxt += l + 1;
717	}
718
719	/* if DisConnected, OutChannel now points to the transcript */
720	if (!DisConnected &&
721	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
722		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
723				     msg);
724	else
725		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
726				     errtxt);
727	if (TrafficLogFile != NULL)
728		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
729				     "%05d >>> %s\n", (int) CurrentPid,
730				     (OpMode == MD_SMTP || OpMode == MD_DAEMON)
731					? msg : errtxt);
732#if !PIPELINING
733	/* XXX can't flush here for SMTP pipelining */
734	if (msg[3] == ' ')
735		(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
736	if (!sm_io_error(OutChannel) || DisConnected)
737		return;
738
739	/*
740	**  Error on output -- if reporting lost channel, just ignore it.
741	**  Also, ignore errors from QUIT response (221 message) -- some
742	**	rude servers don't read result.
743	*/
744
745	if (InChannel == NULL || sm_io_eof(InChannel) ||
746	    sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
747		return;
748
749	/* can't call syserr, 'cause we are using MsgBuf */
750	HoldErrs = true;
751	if (LogLevel > 0)
752		sm_syslog(LOG_CRIT, id,
753			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
754			  CURHOSTNAME,
755			  shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
756#endif /* !PIPELINING */
757}
758
759/*
760**  PUTERRMSG -- like putoutmsg, but does special processing for error messages
761**
762**	Parameters:
763**		msg -- the message to output.
764**
765**	Returns:
766**		none.
767**
768**	Side Effects:
769**		Sets the fatal error bit in the envelope as appropriate.
770*/
771
772static void
773puterrmsg(msg)
774	char *msg;
775{
776	char msgcode = msg[0];
777
778	/* output the message as usual */
779	putoutmsg(msg, HoldErrs, false);
780
781	/* be careful about multiple error messages */
782	if (OnlyOneError)
783		HoldErrs = true;
784
785	/* signal the error */
786	Errors++;
787
788	if (CurEnv == NULL)
789		return;
790
791	if (msgcode == '6')
792	{
793		/* notify the postmaster */
794		CurEnv->e_flags |= EF_PM_NOTIFY;
795	}
796	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
797	{
798		/* mark long-term fatal errors */
799		CurEnv->e_flags |= EF_FATALERRS;
800	}
801}
802
803/*
804**  ISENHSC -- check whether a string contains an enhanced status code
805**
806**	Parameters:
807**		s -- string with possible enhanced status code.
808**		delim -- delim for enhanced status code.
809**
810**	Returns:
811**		0  -- no enhanced status code.
812**		>4 -- length of enhanced status code.
813*/
814
815int
816isenhsc(s, delim)
817	const char *s;
818	int delim;
819{
820	int l, h;
821
822	if (s == NULL)
823		return 0;
824	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
825		return 0;
826	h = 0;
827	l = 2;
828	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
829		++h;
830	if (h == 0 || s[l + h] != '.')
831		return 0;
832	l += h + 1;
833	h = 0;
834	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
835		++h;
836	if (h == 0 || s[l + h] != delim)
837		return 0;
838	return l + h;
839}
840
841/*
842**  EXTENHSC -- check and extract an enhanced status code
843**
844**	Parameters:
845**		s -- string with possible enhanced status code.
846**		delim -- delim for enhanced status code.
847**		e -- pointer to storage for enhanced status code.
848**			must be != NULL and have space for at least
849**			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
850**
851**	Returns:
852**		0  -- no enhanced status code.
853**		>4 -- length of enhanced status code.
854**
855**	Side Effects:
856**		fills e with enhanced status code.
857*/
858
859int
860extenhsc(s, delim, e)
861	const char *s;
862	int delim;
863	char *e;
864{
865	int l, h;
866
867	if (s == NULL)
868		return 0;
869	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
870		return 0;
871	h = 0;
872	l = 2;
873	e[0] = s[0];
874	e[1] = '.';
875	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
876	{
877		e[l + h] = s[l + h];
878		++h;
879	}
880	if (h == 0 || s[l + h] != '.')
881		return 0;
882	e[l + h] = '.';
883	l += h + 1;
884	h = 0;
885	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
886	{
887		e[l + h] = s[l + h];
888		++h;
889	}
890	if (h == 0 || s[l + h] != delim)
891		return 0;
892	e[l + h] = '\0';
893	return l + h;
894}
895
896/*
897**  FMTMSG -- format a message into buffer.
898**
899**	Parameters:
900**		eb -- error buffer to get result -- MUST BE MsgBuf.
901**		to -- the recipient tag for this message.
902**		num -- default three digit SMTP reply code.
903**		enhsc -- enhanced status code.
904**		en -- the error number to display.
905**		fmt -- format of string: See NOTE below.
906**		ap -- arguments for fmt.
907**
908**	Returns:
909**		pointer to error text beyond status codes.
910**
911**	NOTE:
912**		Do NOT use "%s" as fmt if the argument starts with an SMTP
913**		reply code!
914*/
915
916static char *
917fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
918	register char *eb;
919	const char *to;
920	const char *num;
921	const char *enhsc;
922	int eno;
923	const char *fmt;
924	SM_VA_LOCAL_DECL
925{
926	char del;
927	int l;
928	int spaceleft = sizeof(MsgBuf);
929	char *errtxt;
930
931	/* output the reply code */
932	if (ISSMTPCODE(fmt))
933	{
934		num = fmt;
935		fmt += 4;
936	}
937	if (num[3] == '-')
938		del = '-';
939	else
940		del = ' ';
941	if (SoftBounce && num[0] == '5')
942	{
943		/* replace 5 by 4 */
944		(void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
945	}
946	else
947		(void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
948	eb += 4;
949	spaceleft -= 4;
950
951	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
952	{
953		/* copy enh.status code including trailing blank */
954		l++;
955		(void) sm_strlcpy(eb, fmt, l + 1);
956		eb += l;
957		spaceleft -= l;
958		fmt += l;
959	}
960	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
961	{
962		/* copy enh.status code */
963		(void) sm_strlcpy(eb, enhsc, l + 1);
964		eb[l] = ' ';
965		eb[++l] = '\0';
966		eb += l;
967		spaceleft -= l;
968	}
969	if (SoftBounce && eb[-l] == '5')
970	{
971		/* replace 5 by 4 */
972		eb[-l] = '4';
973	}
974	errtxt = eb;
975
976	/* output the file name and line number */
977	if (FileName != NULL)
978	{
979		(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
980				   shortenstring(FileName, 83), LineNumber);
981		eb += (l = strlen(eb));
982		spaceleft -= l;
983	}
984
985	/*
986	**  output the "to" address only if it is defined and one of the
987	**  following codes is used:
988	**  050 internal notices, e.g., alias expansion
989	**  250 Ok
990	**  252 Cannot VRFY user, but will accept message and attempt delivery
991	**  450 Requested mail action not taken: mailbox unavailable
992	**  550 Requested action not taken: mailbox unavailable
993	**  553 Requested action not taken: mailbox name not allowed
994	**
995	**  Notice: this still isn't "the right thing", this code shouldn't
996	**	(indirectly) depend on CurEnv->e_to.
997	*/
998
999	if (to != NULL && to[0] != '\0' &&
1000	    (strncmp(num, "050", 3) == 0 ||
1001	     strncmp(num, "250", 3) == 0 ||
1002	     strncmp(num, "252", 3) == 0 ||
1003	     strncmp(num, "450", 3) == 0 ||
1004	     strncmp(num, "550", 3) == 0 ||
1005	     strncmp(num, "553", 3) == 0))
1006	{
1007		(void) sm_strlcpyn(eb, spaceleft, 2,
1008				   shortenstring(to, MAXSHORTSTR), "... ");
1009		spaceleft -= strlen(eb);
1010#if _FFR_EAI
1011		eb += strlen(eb);
1012#else
1013		while (*eb != '\0')
1014			*eb++ &= 0177;
1015#endif
1016	}
1017
1018	/* output the message */
1019	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
1020	spaceleft -= strlen(eb);
1021#if _FFR_EAI
1022	eb += strlen(eb);
1023#else
1024	while (*eb != '\0')
1025		*eb++ &= 0177;
1026#endif
1027
1028	/* output the error code, if any */
1029	if (eno != 0)
1030		(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
1031
1032	return errtxt;
1033}
1034
1035/*
1036**  BUFFER_ERRORS -- arrange to buffer future error messages
1037**
1038**	Parameters:
1039**		none
1040**
1041**	Returns:
1042**		none.
1043*/
1044
1045void
1046buffer_errors()
1047{
1048	HeldMessageBuf[0] = '\0';
1049	HoldErrs = true;
1050}
1051
1052/*
1053**  FLUSH_ERRORS -- flush the held error message buffer
1054**
1055**	Parameters:
1056**		print -- if set, print the message, otherwise just
1057**			delete it.
1058**
1059**	Returns:
1060**		none.
1061*/
1062
1063void
1064flush_errors(print)
1065	bool print;
1066{
1067	if (print && HeldMessageBuf[0] != '\0')
1068		putoutmsg(HeldMessageBuf, false, true);
1069	HeldMessageBuf[0] = '\0';
1070	HoldErrs = false;
1071}
1072/*
1073**  SM_ERRSTRING -- return string description of error code
1074**
1075**	Parameters:
1076**		errnum -- the error number to translate
1077**
1078**	Returns:
1079**		A string description of errnum.
1080*/
1081
1082const char *
1083sm_errstring(errnum)
1084	int errnum;
1085{
1086	char *dnsmsg;
1087	char *bp;
1088	static char buf[MAXLINE];
1089#if HASSTRERROR
1090	char *err;
1091	char errbuf[30];
1092#endif
1093#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
1094	extern char *sys_errlist[];
1095	extern int sys_nerr;
1096#endif
1097
1098	/*
1099	**  Handle special network error codes.
1100	**
1101	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
1102	*/
1103
1104	dnsmsg = NULL;
1105	switch (errnum)
1106	{
1107	  case ETIMEDOUT:
1108	  case ECONNRESET:
1109		bp = buf;
1110#if HASSTRERROR
1111		err = strerror(errnum);
1112		if (err == NULL)
1113		{
1114			(void) sm_snprintf(errbuf, sizeof(errbuf),
1115					   "Error %d", errnum);
1116			err = errbuf;
1117		}
1118		(void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
1119#else /* HASSTRERROR */
1120		if (errnum >= 0 && errnum < sys_nerr)
1121			(void) sm_strlcpy(bp, sys_errlist[errnum],
1122					  SPACELEFT(buf, bp));
1123		else
1124			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1125				"Error %d", errnum);
1126#endif /* HASSTRERROR */
1127		bp += strlen(bp);
1128		if (CurHostName != NULL)
1129		{
1130			if (errnum == ETIMEDOUT)
1131			{
1132				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1133					" with ");
1134				bp += strlen(bp);
1135			}
1136			else
1137			{
1138				bp = buf;
1139				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1140					"Connection reset by ");
1141				bp += strlen(bp);
1142			}
1143			(void) sm_strlcpy(bp,
1144					shortenstring(CurHostName, MAXSHORTSTR),
1145					SPACELEFT(buf, bp));
1146			bp += strlen(buf);
1147		}
1148		if (SmtpPhase != NULL)
1149		{
1150			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1151				" during %s", SmtpPhase);
1152		}
1153		return buf;
1154
1155	  case EHOSTDOWN:
1156		if (CurHostName == NULL)
1157			break;
1158		(void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
1159			shortenstring(CurHostName, MAXSHORTSTR));
1160		return buf;
1161
1162	  case ECONNREFUSED:
1163		if (CurHostName == NULL)
1164			break;
1165		(void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
1166			shortenstring(CurHostName, MAXSHORTSTR));
1167		return buf;
1168
1169#if NAMED_BIND
1170	  case HOST_NOT_FOUND + E_DNSBASE:
1171		dnsmsg = "host not found";
1172		break;
1173
1174	  case TRY_AGAIN + E_DNSBASE:
1175		dnsmsg = "host name lookup failure";
1176		break;
1177
1178	  case NO_RECOVERY + E_DNSBASE:
1179		dnsmsg = "non-recoverable error";
1180		break;
1181
1182	  case NO_DATA + E_DNSBASE:
1183		dnsmsg = "no data known";
1184		break;
1185#endif /* NAMED_BIND */
1186
1187	  case EPERM:
1188		/* SunOS gives "Not owner" -- this is the POSIX message */
1189		return "Operation not permitted";
1190
1191	/*
1192	**  Error messages used internally in sendmail.
1193	*/
1194
1195	  case E_SM_OPENTIMEOUT:
1196		return "Timeout on file open";
1197
1198	  case E_SM_NOSLINK:
1199		return "Symbolic links not allowed";
1200
1201	  case E_SM_NOHLINK:
1202		return "Hard links not allowed";
1203
1204	  case E_SM_REGONLY:
1205		return "Regular files only";
1206
1207	  case E_SM_ISEXEC:
1208		return "Executable files not allowed";
1209
1210	  case E_SM_WWDIR:
1211		return "World writable directory";
1212
1213	  case E_SM_GWDIR:
1214		return "Group writable directory";
1215
1216	  case E_SM_FILECHANGE:
1217		return "File changed after open";
1218
1219	  case E_SM_WWFILE:
1220		return "World writable file";
1221
1222	  case E_SM_GWFILE:
1223		return "Group writable file";
1224
1225	  case E_SM_GRFILE:
1226		return "Group readable file";
1227
1228	  case E_SM_WRFILE:
1229		return "World readable file";
1230	}
1231
1232	if (dnsmsg != NULL)
1233	{
1234		bp = buf;
1235		bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
1236		if (CurHostName != NULL)
1237		{
1238			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
1239				shortenstring(CurHostName, MAXSHORTSTR), ": ");
1240			bp += strlen(bp);
1241		}
1242		(void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
1243		return buf;
1244	}
1245
1246#if LDAPMAP
1247	if (errnum >= E_LDAPBASE - E_LDAP_SHIM)
1248		return ldap_err2string(errnum - E_LDAPBASE);
1249#endif
1250
1251#if HASSTRERROR
1252	err = strerror(errnum);
1253	if (err == NULL)
1254	{
1255		(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1256		return buf;
1257	}
1258	return err;
1259#else /* HASSTRERROR */
1260	if (errnum > 0 && errnum < sys_nerr)
1261		return sys_errlist[errnum];
1262
1263	(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1264	return buf;
1265#endif /* HASSTRERROR */
1266}
1267