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