err.c revision 90795
138889Sjdp/*
2218822Sdim * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3130561Sobrien *	All rights reserved.
438889Sjdp * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5218822Sdim * Copyright (c) 1988, 1993
638889Sjdp *	The Regents of the University of California.  All rights reserved.
738889Sjdp *
838889Sjdp * By using this file, you agree to the terms and conditions set
938889Sjdp * forth in the LICENSE file which can be found at the top level of
1038889Sjdp * the sendmail distribution.
1138889Sjdp *
1238889Sjdp */
1338889Sjdp
1438889Sjdp#include <sendmail.h>
1538889Sjdp
1638889SjdpSM_RCSID("@(#)$Id: err.c,v 8.189 2002/01/09 18:52:30 ca Exp $")
1738889Sjdp
1838889Sjdp/* $FreeBSD: head/contrib/sendmail/src/err.c 90795 2002-02-17 21:58:34Z gshapiro $ */
1938889Sjdp
2038889Sjdp#if LDAPMAP
21218822Sdim# include <lber.h>
22218822Sdim# include <ldap.h>			/* for LDAP error codes */
2338889Sjdp#endif /* LDAPMAP */
2438889Sjdp
2538889Sjdpstatic void	putoutmsg __P((char *, bool, bool));
2638889Sjdpstatic void	puterrmsg __P((char *));
27218822Sdimstatic char	*fmtmsg __P((char *, const char *, const char *, const char *,
2838889Sjdp			     int, const char *, va_list));
2938889Sjdp
3038889Sjdp/*
3138889Sjdp**  FATAL_ERROR -- handle a fatal exception
3238889Sjdp**
3338889Sjdp**	This function is installed as the default exception handler
3438889Sjdp**	in the main sendmail process, and in all child processes
3538889Sjdp**	that we create.  Its job is to handle exceptions that are not
3638889Sjdp**	handled at a lower level.
3738889Sjdp**
3838889Sjdp**	The theory is that unhandled exceptions will be 'fatal' class
3938889Sjdp**	exceptions (with an "F:" prefix), such as the out-of-memory
4038889Sjdp**	exception "F:sm.heap".  As such, they are handled by exiting
4138889Sjdp**	the process in exactly the same way that xalloc() in Sendmail 8.10
4238889Sjdp**	exits the process when it fails due to lack of memory:
4338889Sjdp**	we call syserr with a message beginning with "!".
4438889Sjdp**
4538889Sjdp**	Parameters:
4638889Sjdp**		exc -- exception which is terminating this process
4738889Sjdp**
4838889Sjdp**	Returns:
4938889Sjdp**		none
5038889Sjdp*/
5138889Sjdp
5238889Sjdpvoid
53218822Sdimfatal_error(exc)
5438889Sjdp	SM_EXC_T *exc;
5538889Sjdp{
5638889Sjdp	static char buf[256];
5738889Sjdp	SM_FILE_T f;
58218822Sdim
5938889Sjdp	/*
6038889Sjdp	**  This function may be called when the heap is exhausted.
6138889Sjdp	**  The following code writes the message for 'exc' into our
6238889Sjdp	**  static buffer without allocating memory or raising exceptions.
6338889Sjdp	*/
6438889Sjdp
6538889Sjdp	sm_strio_init(&f, buf, sizeof(buf));
6638889Sjdp	sm_exc_write(exc, &f);
6738889Sjdp	(void) sm_io_flush(&f, SM_TIME_DEFAULT);
6838889Sjdp
6938889Sjdp	/*
7038889Sjdp	**  Terminate the process after logging an error and cleaning up.
7138889Sjdp	**  Problems:
7238889Sjdp	**  - syserr decides what class of error this is by looking at errno.
7338889Sjdp	**    That's no good; we should look at the exc structure.
7438889Sjdp	**  - The cleanup code should be moved out of syserr
7538889Sjdp	**    and into individual exception handlers
7638889Sjdp	**    that are part of the module they clean up after.
7738889Sjdp	*/
7838889Sjdp
7938889Sjdp	errno = ENOMEM;
8038889Sjdp	syserr("!%s", buf);
8138889Sjdp}
8238889Sjdp
8338889Sjdp/*
8438889Sjdp**  SYSERR -- Print error message.
8538889Sjdp**
8638889Sjdp**	Prints an error message via sm_io_printf to the diagnostic output.
8738889Sjdp**
8838889Sjdp**	If the first character of the syserr message is `!' it will
8938889Sjdp**	log this as an ALERT message and exit immediately.  This can
9038889Sjdp**	leave queue files in an indeterminate state, so it should not
9138889Sjdp**	be used lightly.
9238889Sjdp**
9338889Sjdp**	If the first character of the syserr message is '!' or '@'
9438889Sjdp**	then syserr knows that the process is about to be terminated,
9538889Sjdp**	so the SMTP reply code defaults to 421.  Otherwise, the
9638889Sjdp**	reply code defaults to 451 or 554, depending on errno.
9738889Sjdp**
9838889Sjdp**	Parameters:
9938889Sjdp**		fmt -- the format string.  An optional '!' or '@',
10038889Sjdp**			followed by an optional three-digit SMTP
101130561Sobrien**			reply code, followed by message text.
102218822Sdim**		(others) -- parameters
103218822Sdim**
104218822Sdim**	Returns:
105218822Sdim**		none
106218822Sdim**		Raises E:mta.quickabort if QuickAbort is set.
107218822Sdim**
10838889Sjdp**	Side Effects:
10938889Sjdp**		increments Errors.
11038889Sjdp**		sets ExitStat.
111218822Sdim*/
112130561Sobrien
11338889Sjdpchar		MsgBuf[BUFSIZ*2];	/* text of most recent message */
114218822Sdimstatic char	HeldMessageBuf[sizeof MsgBuf];	/* for held messages */
11538889Sjdp
116218822Sdim#if NAMED_BIND && !defined(NO_DATA)
11738889Sjdp# define NO_DATA	NO_ADDRESS
11838889Sjdp#endif /* NAMED_BIND && !defined(NO_DATA) */
11938889Sjdp
12038889Sjdpvoid
12138889Sjdp/*VARARGS1*/
12238889Sjdp#ifdef __STDC__
12338889Sjdpsyserr(const char *fmt, ...)
12460484Sobrien#else /* __STDC__ */
12538889Sjdpsyserr(fmt, va_alist)
12638889Sjdp	const char *fmt;
12738889Sjdp	va_dcl
12838889Sjdp#endif /* __STDC__ */
12938889Sjdp{
13038889Sjdp	register char *p;
13138889Sjdp	int save_errno = errno;
13238889Sjdp	bool panic;
13338889Sjdp	bool exiting;
13438889Sjdp	char *user;
13538889Sjdp	char *enhsc;
13638889Sjdp	char *errtxt;
13738889Sjdp	struct passwd *pw;
13838889Sjdp	char ubuf[80];
13938889Sjdp	SM_VA_LOCAL_DECL
14038889Sjdp
14160484Sobrien	switch (*fmt)
14238889Sjdp	{
14338889Sjdp	  case '!':
144218822Sdim		++fmt;
14538889Sjdp		panic = true;
14638889Sjdp		exiting = true;
14738889Sjdp		break;
148218822Sdim	  case '@':
14938889Sjdp		++fmt;
15038889Sjdp		panic = false;
15138889Sjdp		exiting = true;
15238889Sjdp		break;
15338889Sjdp	  default:
15438889Sjdp		panic = false;
15538889Sjdp		exiting = false;
15638889Sjdp		break;
15738889Sjdp	}
15838889Sjdp
15938889Sjdp	/* format and output the error message */
16038889Sjdp	if (exiting)
161218822Sdim	{
162218822Sdim		/*
163218822Sdim		**  Since we are terminating the process,
164218822Sdim		**  we are aborting the entire SMTP session,
165218822Sdim		**  rather than just the current transaction.
16638889Sjdp		*/
16738889Sjdp
16838889Sjdp		p = "421";
16938889Sjdp		enhsc = "4.0.0";
17038889Sjdp	}
171130561Sobrien	else if (save_errno == 0)
17238889Sjdp	{
17360484Sobrien		p = "554";
17438889Sjdp		enhsc = "5.0.0";
17538889Sjdp	}
17638889Sjdp	else
17738889Sjdp	{
178218822Sdim		p = "451";
179218822Sdim		enhsc = "4.0.0";
180218822Sdim	}
181218822Sdim	SM_VA_START(ap, fmt);
18238889Sjdp	errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
18338889Sjdp	SM_VA_END(ap);
184218822Sdim	puterrmsg(MsgBuf);
18538889Sjdp
186218822Sdim	/* save this message for mailq printing */
18738889Sjdp	if (!panic && CurEnv != NULL)
18838889Sjdp	{
18960484Sobrien		char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
19060484Sobrien
19138889Sjdp		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
19238889Sjdp			sm_free(CurEnv->e_message);
19338889Sjdp		CurEnv->e_message = nmsg;
194218822Sdim	}
195218822Sdim
196218822Sdim	/* determine exit status if not already set */
197218822Sdim	if (ExitStat == EX_OK)
198218822Sdim	{
19938889Sjdp		if (save_errno == 0)
20038889Sjdp			ExitStat = EX_SOFTWARE;
201218822Sdim		else
202218822Sdim			ExitStat = EX_OSERR;
20338889Sjdp		if (tTd(54, 1))
20438889Sjdp			sm_dprintf("syserr: ExitStat = %d\n", ExitStat);
20538889Sjdp	}
20638889Sjdp
20738889Sjdp	pw = sm_getpwuid(RealUid);
20838889Sjdp	if (pw != NULL)
20938889Sjdp		user = pw->pw_name;
21038889Sjdp	else
21138889Sjdp	{
212218822Sdim		user = ubuf;
213218822Sdim		(void) sm_snprintf(ubuf, sizeof ubuf, "UID%d", (int) RealUid);
21438889Sjdp	}
21538889Sjdp
21638889Sjdp	if (LogLevel > 0)
21738889Sjdp		sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
21860484Sobrien			  CurEnv == NULL ? NOQID : CurEnv->e_id,
21938889Sjdp			  "SYSERR(%s): %.900s",
220218822Sdim			  user, errtxt);
221218822Sdim	switch (save_errno)
22238889Sjdp	{
22338889Sjdp	  case EBADF:
22438889Sjdp	  case ENFILE:
22538889Sjdp	  case EMFILE:
226218822Sdim	  case ENOTTY:
22760484Sobrien#ifdef EFBIG
22838889Sjdp	  case EFBIG:
22938889Sjdp#endif /* EFBIG */
23038889Sjdp#ifdef ESPIPE
231218822Sdim	  case ESPIPE:
23238889Sjdp#endif /* ESPIPE */
23338889Sjdp#ifdef EPIPE
234218822Sdim	  case EPIPE:
23538889Sjdp#endif /* EPIPE */
23638889Sjdp#ifdef ENOBUFS
23738889Sjdp	  case ENOBUFS:
238218822Sdim#endif /* ENOBUFS */
23938889Sjdp#ifdef ESTALE
24038889Sjdp	  case ESTALE:
24138889Sjdp#endif /* ESTALE */
24238889Sjdp		printopenfds(true);
24338889Sjdp		mci_dump_all(true);
24438889Sjdp		break;
24538889Sjdp	}
246218822Sdim	if (panic)
24760484Sobrien	{
24838889Sjdp#if XLA
249218822Sdim		xla_all_end();
25038889Sjdp#endif /* XLA */
25138889Sjdp		sync_queue_time();
25238889Sjdp		if (tTd(0, 1))
25338889Sjdp			abort();
254218822Sdim		exit(EX_OSERR);
25560484Sobrien	}
25638889Sjdp	errno = 0;
257218822Sdim	if (QuickAbort)
25838889Sjdp		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
25938889Sjdp}
26038889Sjdp/*
26138889Sjdp**  USRERR -- Signal user error.
26238889Sjdp**
26338889Sjdp**	This is much like syserr except it is for user errors.
26438889Sjdp**
26538889Sjdp**	Parameters:
26638889Sjdp**		fmt -- the format string.  If it does not begin with
267218822Sdim**			a three-digit SMTP reply code, 550 is assumed.
26838889Sjdp**		(others) -- sm_io_printf strings
26938889Sjdp**
27060484Sobrien**	Returns:
27138889Sjdp**		none
272218822Sdim**		Raises E:mta.quickabort if QuickAbort is set.
273218822Sdim**
27438889Sjdp**	Side Effects:
275218822Sdim**		increments Errors.
27638889Sjdp*/
27738889Sjdp
27838889Sjdp/*VARARGS1*/
27938889Sjdpvoid
28038889Sjdp#ifdef __STDC__
28138889Sjdpusrerr(const char *fmt, ...)
28238889Sjdp#else /* __STDC__ */
28338889Sjdpusrerr(fmt, va_alist)
28438889Sjdp	const char *fmt;
28538889Sjdp	va_dcl
286218822Sdim#endif /* __STDC__ */
28760484Sobrien{
28838889Sjdp	char *enhsc;
289218822Sdim	char *errtxt;
29038889Sjdp	SM_VA_LOCAL_DECL
29138889Sjdp
29238889Sjdp	if (fmt[0] == '5' || fmt[0] == '6')
29338889Sjdp		enhsc = "5.0.0";
294218822Sdim	else if (fmt[0] == '4' || fmt[0] == '8')
29560484Sobrien		enhsc = "4.0.0";
29638889Sjdp	else if (fmt[0] == '2')
297218822Sdim		enhsc = "2.0.0";
29838889Sjdp	else
29938889Sjdp		enhsc = NULL;
30038889Sjdp	SM_VA_START(ap, fmt);
30138889Sjdp	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
30238889Sjdp	SM_VA_END(ap);
30338889Sjdp
30438889Sjdp	if (SuprErrs)
30538889Sjdp		return;
30638889Sjdp
30738889Sjdp	/* save this message for mailq printing */
30838889Sjdp	switch (MsgBuf[0])
309218822Sdim	{
310218822Sdim	  case '4':
311218822Sdim	  case '8':
312218822Sdim		if (CurEnv->e_message != NULL)
31338889Sjdp			break;
31438889Sjdp
315218822Sdim		/* FALLTHROUGH */
316218822Sdim
31738889Sjdp	  case '5':
31838889Sjdp	  case '6':
31938889Sjdp		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
32060484Sobrien			sm_free(CurEnv->e_message);
32138889Sjdp		if (MsgBuf[0] == '6')
32260484Sobrien		{
32360484Sobrien			char buf[MAXLINE];
32438889Sjdp
32538889Sjdp			(void) sm_snprintf(buf, sizeof buf,
32638889Sjdp					   "Postmaster warning: %.*s",
327218822Sdim					   (int) sizeof buf - 22, errtxt);
328218822Sdim			CurEnv->e_message =
32938889Sjdp				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
330218822Sdim		}
33160484Sobrien		else
33238889Sjdp		{
33338889Sjdp			CurEnv->e_message =
33438889Sjdp				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
335218822Sdim		}
33660484Sobrien		break;
33738889Sjdp	}
338218822Sdim
33938889Sjdp	puterrmsg(MsgBuf);
340218822Sdim	if (LogLevel > 3 && LogUsrErrs)
341218822Sdim		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
342218822Sdim	if (QuickAbort)
34338889Sjdp		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
34438889Sjdp}
34538889Sjdp/*
34638889Sjdp**  USRERRENH -- Signal user error.
34738889Sjdp**
34838889Sjdp**	Same as usrerr but with enhanced status code.
34938889Sjdp**
35038889Sjdp**	Parameters:
35138889Sjdp**		enhsc -- the enhanced status code.
352218822Sdim**		fmt -- the format string.  If it does not begin with
35338889Sjdp**			a three-digit SMTP reply code, 550 is assumed.
354218822Sdim**		(others) -- sm_io_printf strings
35538889Sjdp**
35638889Sjdp**	Returns:
35738889Sjdp**		none
35838889Sjdp**		Raises E:mta.quickabort if QuickAbort is set.
359218822Sdim**
360218822Sdim**	Side Effects:
361218822Sdim**		increments Errors.
362218822Sdim*/
363218822Sdim
364218822Sdim/*VARARGS1*/
365218822Sdimvoid
366218822Sdim#ifdef __STDC__
367218822Sdimusrerrenh(char *enhsc, const char *fmt, ...)
368218822Sdim#else /* __STDC__ */
36938889Sjdpusrerrenh(enhsc, fmt, va_alist)
37038889Sjdp	char *enhsc;
37138889Sjdp	const char *fmt;
37238889Sjdp	va_dcl
37338889Sjdp#endif /* __STDC__ */
37438889Sjdp{
37538889Sjdp	char *errtxt;
376218822Sdim	SM_VA_LOCAL_DECL
37738889Sjdp
37838889Sjdp	if (enhsc == NULL || *enhsc == '\0')
37938889Sjdp	{
38038889Sjdp		if (fmt[0] == '5' || fmt[0] == '6')
38138889Sjdp			enhsc = "5.0.0";
38238889Sjdp		else if (fmt[0] == '4' || fmt[0] == '8')
38338889Sjdp			enhsc = "4.0.0";
38438889Sjdp		else if (fmt[0] == '2')
38538889Sjdp			enhsc = "2.0.0";
38638889Sjdp	}
38738889Sjdp	SM_VA_START(ap, fmt);
38838889Sjdp	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
38938889Sjdp	SM_VA_END(ap);
39038889Sjdp
39138889Sjdp	if (SuprErrs)
39238889Sjdp		return;
39338889Sjdp
39438889Sjdp	/* save this message for mailq printing */
39538889Sjdp	switch (MsgBuf[0])
396218822Sdim	{
39738889Sjdp	  case '4':
39838889Sjdp	  case '8':
39938889Sjdp		if (CurEnv->e_message != NULL)
40038889Sjdp			break;
40138889Sjdp
40238889Sjdp		/* FALLTHROUGH */
403218822Sdim
404218822Sdim	  case '5':
40538889Sjdp	  case '6':
406218822Sdim		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
40738889Sjdp			sm_free(CurEnv->e_message);
408218822Sdim		if (MsgBuf[0] == '6')
40938889Sjdp		{
41038889Sjdp			char buf[MAXLINE];
41138889Sjdp
41238889Sjdp			(void) sm_snprintf(buf, sizeof buf,
41338889Sjdp					   "Postmaster warning: %.*s",
41438889Sjdp					   (int) sizeof buf - 22, errtxt);
41538889Sjdp			CurEnv->e_message =
41638889Sjdp				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
41738889Sjdp		}
41838889Sjdp		else
419130561Sobrien		{
420218822Sdim			CurEnv->e_message =
42138889Sjdp				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
42238889Sjdp		}
42338889Sjdp		break;
42438889Sjdp	}
425218822Sdim
426218822Sdim	puterrmsg(MsgBuf);
427218822Sdim	if (LogLevel > 3 && LogUsrErrs)
42838889Sjdp		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
42938889Sjdp	if (QuickAbort)
43038889Sjdp		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
43160484Sobrien}
43238889Sjdp/*
43338889Sjdp**  MESSAGE -- print message (not necessarily an error)
43438889Sjdp**
43538889Sjdp**	Parameters:
43638889Sjdp**		msg -- the message (sm_io_printf fmt) -- it can begin with
43738889Sjdp**			an SMTP reply code.  If not, 050 is assumed.
43838889Sjdp**		(others) -- sm_io_printf arguments
43938889Sjdp**
44060484Sobrien**	Returns:
44160484Sobrien**		none
44260484Sobrien**
44360484Sobrien**	Side Effects:
44460484Sobrien**		none.
44560484Sobrien*/
44660484Sobrien
44760484Sobrien/*VARARGS1*/
44860484Sobrienvoid
44960484Sobrien#ifdef __STDC__
45038889Sjdpmessage(const char *msg, ...)
45138889Sjdp#else /* __STDC__ */
45260484Sobrienmessage(msg, va_alist)
45360484Sobrien	const char *msg;
45438889Sjdp	va_dcl
45538889Sjdp#endif /* __STDC__ */
45638889Sjdp{
45738889Sjdp	char *errtxt;
45838889Sjdp	SM_VA_LOCAL_DECL
45938889Sjdp
46038889Sjdp	errno = 0;
46138889Sjdp	SM_VA_START(ap, msg);
46238889Sjdp	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
46338889Sjdp	SM_VA_END(ap);
46438889Sjdp	putoutmsg(MsgBuf, false, false);
46538889Sjdp
46638889Sjdp	/* save this message for mailq printing */
46738889Sjdp	switch (MsgBuf[0])
46838889Sjdp	{
46938889Sjdp	  case '4':
47038889Sjdp	  case '8':
47138889Sjdp		if (CurEnv->e_message != NULL)
47238889Sjdp			break;
47338889Sjdp		/* FALLTHROUGH */
47438889Sjdp
47538889Sjdp	  case '5':
47638889Sjdp		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
47738889Sjdp			sm_free(CurEnv->e_message);
47838889Sjdp		CurEnv->e_message =
47938889Sjdp			sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
48038889Sjdp		break;
481218822Sdim	}
482218822Sdim}
483218822Sdim/*
48438889Sjdp**  NMESSAGE -- print message (not necessarily an error)
48538889Sjdp**
48638889Sjdp**	Just like "message" except it never puts the to... tag on.
48738889Sjdp**
48838889Sjdp**	Parameters:
48938889Sjdp**		msg -- the message (sm_io_printf fmt) -- if it begins
49038889Sjdp**			with a three digit SMTP reply code, that is used,
49138889Sjdp**			otherwise 050 is assumed.
49238889Sjdp**		(others) -- sm_io_printf arguments
49338889Sjdp**
49438889Sjdp**	Returns:
49538889Sjdp**		none
49638889Sjdp**
49738889Sjdp**	Side Effects:
49838889Sjdp**		none.
49938889Sjdp*/
50038889Sjdp
50138889Sjdp/*VARARGS1*/
50238889Sjdpvoid
50338889Sjdp#ifdef __STDC__
50438889Sjdpnmessage(const char *msg, ...)
50538889Sjdp#else /* __STDC__ */
50638889Sjdpnmessage(msg, va_alist)
50738889Sjdp	const char *msg;
50838889Sjdp	va_dcl
50938889Sjdp#endif /* __STDC__ */
51038889Sjdp{
51138889Sjdp	char *errtxt;
51238889Sjdp	SM_VA_LOCAL_DECL
51338889Sjdp
51438889Sjdp	errno = 0;
51538889Sjdp	SM_VA_START(ap, msg);
51638889Sjdp	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
51738889Sjdp			(char *) NULL, 0, msg, ap);
518218822Sdim	SM_VA_END(ap);
51938889Sjdp	putoutmsg(MsgBuf, false, false);
52038889Sjdp
52138889Sjdp	/* save this message for mailq printing */
52238889Sjdp	switch (MsgBuf[0])
52338889Sjdp	{
52438889Sjdp	  case '4':
52538889Sjdp	  case '8':
52638889Sjdp		if (CurEnv->e_message != NULL)
52738889Sjdp			break;
52838889Sjdp		/* FALLTHROUGH */
52938889Sjdp
53038889Sjdp	  case '5':
53138889Sjdp		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
53238889Sjdp			sm_free(CurEnv->e_message);
53338889Sjdp		CurEnv->e_message =
53438889Sjdp			sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
53538889Sjdp		break;
53638889Sjdp	}
53738889Sjdp}
53838889Sjdp/*
53938889Sjdp**  PUTOUTMSG -- output error message to transcript and channel
54038889Sjdp**
54138889Sjdp**	Parameters:
54238889Sjdp**		msg -- message to output (in SMTP format).
54338889Sjdp**		holdmsg -- if true, don't output a copy of the message to
54438889Sjdp**			our output channel.
54538889Sjdp**		heldmsg -- if true, this is a previously held message;
54638889Sjdp**			don't log it to the transcript file.
54738889Sjdp**
54838889Sjdp**	Returns:
54938889Sjdp**		none.
550218822Sdim**
55138889Sjdp**	Side Effects:
55238889Sjdp**		Outputs msg to the transcript.
55338889Sjdp**		If appropriate, outputs it to the channel.
55438889Sjdp**		Deletes SMTP reply code number as appropriate.
555218822Sdim*/
55638889Sjdp
55738889Sjdpstatic void
558218822Sdimputoutmsg(msg, holdmsg, heldmsg)
55938889Sjdp	char *msg;
560218822Sdim	bool holdmsg;
561218822Sdim	bool heldmsg;
56238889Sjdp{
56338889Sjdp	char *errtxt = msg;
56438889Sjdp	char msgcode = msg[0];
56538889Sjdp
56638889Sjdp	/* display for debugging */
56738889Sjdp	if (tTd(54, 8))
56838889Sjdp		sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
56938889Sjdp			heldmsg ? " (held)" : "");
57038889Sjdp
57138889Sjdp	/* map warnings to something SMTP can handle */
57238889Sjdp	if (msgcode == '6')
57338889Sjdp		msg[0] = '5';
57438889Sjdp	else if (msgcode == '8')
57538889Sjdp		msg[0] = '4';
57638889Sjdp
577218822Sdim	/* output to transcript if serious */
578130561Sobrien	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
57938889Sjdp	    strchr("45", msg[0]) != NULL)
580218822Sdim		(void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
58138889Sjdp				     msg);
58238889Sjdp
58338889Sjdp	if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
58438889Sjdp		sm_syslog(LOG_INFO, CurEnv->e_id,
58538889Sjdp			  "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
58638889Sjdp			  heldmsg ? " (held)" : "");
58738889Sjdp
58838889Sjdp	if (msgcode == '8')
58938889Sjdp		msg[0] = '0';
59038889Sjdp
59138889Sjdp	/* output to channel if appropriate */
59238889Sjdp	if (!Verbose && msg[0] == '0')
59338889Sjdp		return;
59438889Sjdp	if (holdmsg)
59538889Sjdp	{
59638889Sjdp		/* save for possible future display */
59738889Sjdp		msg[0] = msgcode;
59838889Sjdp		if (HeldMessageBuf[0] == '5' && msgcode == '4')
59938889Sjdp			return;
600218822Sdim		(void) sm_strlcpy(HeldMessageBuf, msg, sizeof HeldMessageBuf);
601218822Sdim		return;
60238889Sjdp	}
603218822Sdim
60438889Sjdp	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
605218822Sdim
60638889Sjdp	if (OutChannel == NULL)
60738889Sjdp		return;
608218822Sdim
60938889Sjdp	/* find actual text of error (after SMTP status codes) */
61038889Sjdp	if (ISSMTPREPLY(errtxt))
61138889Sjdp	{
61238889Sjdp		int l;
61338889Sjdp
61438889Sjdp		errtxt += 4;
61538889Sjdp		l = isenhsc(errtxt, ' ');
61638889Sjdp		if (l <= 0)
61738889Sjdp			l = isenhsc(errtxt, '\0');
61838889Sjdp		if (l > 0)
61938889Sjdp			errtxt += l + 1;
62038889Sjdp	}
62138889Sjdp
62238889Sjdp	/* if DisConnected, OutChannel now points to the transcript */
62338889Sjdp	if (!DisConnected &&
624218822Sdim	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
62538889Sjdp		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
62638889Sjdp				     msg);
62738889Sjdp	else
628218822Sdim		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
62938889Sjdp				     errtxt);
63038889Sjdp	if (TrafficLogFile != NULL)
63138889Sjdp		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
63238889Sjdp				     "%05d >>> %s\n", (int) CurrentPid,
63338889Sjdp				     (OpMode == MD_SMTP || OpMode == MD_DAEMON)
63438889Sjdp					? msg : errtxt);
63538889Sjdp#if !PIPELINING
636218822Sdim	/* XXX can't flush here for SMTP pipelining */
637218822Sdim	if (msg[3] == ' ')
638218822Sdim		(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
639218822Sdim	if (!sm_io_error(OutChannel) || DisConnected)
64038889Sjdp		return;
64138889Sjdp
64238889Sjdp	/*
64338889Sjdp	**  Error on output -- if reporting lost channel, just ignore it.
64438889Sjdp	**  Also, ignore errors from QUIT response (221 message) -- some
64538889Sjdp	**	rude servers don't read result.
64638889Sjdp	*/
64738889Sjdp
64838889Sjdp	if (InChannel == NULL || sm_io_eof(InChannel) ||
64938889Sjdp	    sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
65038889Sjdp		return;
651218822Sdim
652218822Sdim	/* can't call syserr, 'cause we are using MsgBuf */
65338889Sjdp	HoldErrs = true;
65438889Sjdp	if (LogLevel > 0)
65538889Sjdp		sm_syslog(LOG_CRIT, CurEnv->e_id,
65638889Sjdp			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
65738889Sjdp			  CURHOSTNAME,
65838889Sjdp			  shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
65938889Sjdp#endif /* !PIPELINING */
66038889Sjdp}
66138889Sjdp/*
66238889Sjdp**  PUTERRMSG -- like putoutmsg, but does special processing for error messages
663218822Sdim**
66438889Sjdp**	Parameters:
66538889Sjdp**		msg -- the message to output.
666218822Sdim**
667218822Sdim**	Returns:
66838889Sjdp**		none.
66938889Sjdp**
67038889Sjdp**	Side Effects:
67138889Sjdp**		Sets the fatal error bit in the envelope as appropriate.
672218822Sdim*/
673218822Sdim
67438889Sjdpstatic void
67538889Sjdpputerrmsg(msg)
676218822Sdim	char *msg;
67738889Sjdp{
678218822Sdim	char msgcode = msg[0];
67938889Sjdp
68038889Sjdp	/* output the message as usual */
68138889Sjdp	putoutmsg(msg, HoldErrs, false);
68238889Sjdp
683218822Sdim	/* be careful about multiple error messages */
68438889Sjdp	if (OnlyOneError)
68538889Sjdp		HoldErrs = true;
68638889Sjdp
68738889Sjdp	/* signal the error */
688218822Sdim	Errors++;
689218822Sdim
69038889Sjdp	if (CurEnv == NULL)
69138889Sjdp		return;
69238889Sjdp
69338889Sjdp	if (msgcode == '6')
69438889Sjdp	{
69538889Sjdp		/* notify the postmaster */
69638889Sjdp		CurEnv->e_flags |= EF_PM_NOTIFY;
69738889Sjdp	}
69838889Sjdp	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
699218822Sdim	{
70038889Sjdp		/* mark long-term fatal errors */
70138889Sjdp		CurEnv->e_flags |= EF_FATALERRS;
70238889Sjdp	}
703218822Sdim}
70438889Sjdp/*
70538889Sjdp**  ISENHSC -- check whether a string contains an enhanced status code
70638889Sjdp**
70738889Sjdp**	Parameters:
70838889Sjdp**		s -- string with possible enhanced status code.
70938889Sjdp**		delim -- delim for enhanced status code.
71038889Sjdp**
71138889Sjdp**	Returns:
71238889Sjdp**		0  -- no enhanced status code.
71338889Sjdp**		>4 -- length of enhanced status code.
71438889Sjdp**
71538889Sjdp**	Side Effects:
716218822Sdim**		none.
71738889Sjdp*/
71860484Sobrienint
71938889Sjdpisenhsc(s, delim)
72038889Sjdp	const char *s;
72138889Sjdp	int delim;
72238889Sjdp{
72338889Sjdp	int l, h;
72438889Sjdp
72538889Sjdp	if (s == NULL)
72638889Sjdp		return 0;
72738889Sjdp	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
728218822Sdim		return 0;
72938889Sjdp	h = 0;
73038889Sjdp	l = 2;
73138889Sjdp	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
732218822Sdim		++h;
733218822Sdim	if (h == 0 || s[l + h] != '.')
734218822Sdim		return 0;
73538889Sjdp	l += h + 1;
736218822Sdim	h = 0;
737218822Sdim	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
738218822Sdim		++h;
739218822Sdim	if (h == 0 || s[l + h] != delim)
74038889Sjdp		return 0;
74138889Sjdp	return l + h;
74238889Sjdp}
74338889Sjdp/*
74438889Sjdp**  EXTENHSC -- check and extract an enhanced status code
74538889Sjdp**
74638889Sjdp**	Parameters:
747218822Sdim**		s -- string with possible enhanced status code.
74838889Sjdp**		delim -- delim for enhanced status code.
749218822Sdim**		e -- pointer to storage for enhanced status code.
75038889Sjdp**			must be != NULL and have space for at least
75138889Sjdp**			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
752218822Sdim**
75338889Sjdp**	Returns:
754**		0  -- no enhanced status code.
755**		>4 -- length of enhanced status code.
756**
757**	Side Effects:
758**		fills e with enhanced status code.
759*/
760
761int
762extenhsc(s, delim, e)
763	const char *s;
764	int delim;
765	char *e;
766{
767	int l, h;
768
769	if (s == NULL)
770		return 0;
771	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
772		return 0;
773	h = 0;
774	l = 2;
775	e[0] = s[0];
776	e[1] = '.';
777	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
778	{
779		e[l + h] = s[l + h];
780		++h;
781	}
782	if (h == 0 || s[l + h] != '.')
783		return 0;
784	e[l + h] = '.';
785	l += h + 1;
786	h = 0;
787	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
788	{
789		e[l + h] = s[l + h];
790		++h;
791	}
792	if (h == 0 || s[l + h] != delim)
793		return 0;
794	e[l + h] = '\0';
795	return l + h;
796}
797/*
798**  FMTMSG -- format a message into buffer.
799**
800**	Parameters:
801**		eb -- error buffer to get result -- MUST BE MsgBuf.
802**		to -- the recipient tag for this message.
803**		num -- default three digit SMTP reply code.
804**		enhsc -- enhanced status code.
805**		en -- the error number to display.
806**		fmt -- format of string.
807**		ap -- arguments for fmt.
808**
809**	Returns:
810**		pointer to error text beyond status codes.
811**
812**	Side Effects:
813**		none.
814*/
815
816static char *
817fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
818	register char *eb;
819	const char *to;
820	const char *num;
821	const char *enhsc;
822	int eno;
823	const char *fmt;
824	SM_VA_LOCAL_DECL
825{
826	char del;
827	int l;
828	int spaceleft = sizeof MsgBuf;
829	char *errtxt;
830
831	/* output the reply code */
832	if (ISSMTPCODE(fmt))
833	{
834		num = fmt;
835		fmt += 4;
836	}
837	if (num[3] == '-')
838		del = '-';
839	else
840		del = ' ';
841#if _FFR_SOFT_BOUNCE
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#endif /* _FFR_SOFT_BOUNCE */
849	(void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
850	eb += 4;
851	spaceleft -= 4;
852
853	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
854	{
855		/* copy enh.status code including trailing blank */
856		l++;
857		(void) sm_strlcpy(eb, fmt, l + 1);
858		eb += l;
859		spaceleft -= l;
860		fmt += l;
861	}
862	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
863	{
864		/* copy enh.status code */
865		(void) sm_strlcpy(eb, enhsc, l + 1);
866		eb[l] = ' ';
867		eb[++l] = '\0';
868		eb += l;
869		spaceleft -= l;
870	}
871#if _FFR_SOFT_BOUNCE
872	if (SoftBounce && eb[-l] == '5')
873	{
874		/* replace 5 by 4 */
875		eb[-l] = '4';
876	}
877#endif /* _FFR_SOFT_BOUNCE */
878	errtxt = eb;
879
880	/* output the file name and line number */
881	if (FileName != NULL)
882	{
883		(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
884				   shortenstring(FileName, 83), LineNumber);
885		eb += (l = strlen(eb));
886		spaceleft -= l;
887	}
888
889	/*
890	**  output the "to" address only if it is defined and one of the
891	**  following codes is used:
892	**  050 internal notices, e.g., alias expansion
893	**  250 Ok
894	**  252 Cannot VRFY user, but will accept message and attempt delivery
895	**  450 Requested mail action not taken: mailbox unavailable
896	**  550 Requested action not taken: mailbox unavailable
897	**  553 Requested action not taken: mailbox name not allowed
898	**
899	**  Notice: this still isn't "the right thing", this code shouldn't
900	**	(indirectly) depend on CurEnv->e_to.
901	*/
902
903	if (to != NULL && to[0] != '\0' &&
904	    (strncmp(num, "050", 3) == 0 ||
905	     strncmp(num, "250", 3) == 0 ||
906	     strncmp(num, "252", 3) == 0 ||
907	     strncmp(num, "450", 3) == 0 ||
908	     strncmp(num, "550", 3) == 0 ||
909	     strncmp(num, "553", 3) == 0))
910	{
911		(void) sm_strlcpyn(eb, spaceleft, 2,
912				   shortenstring(to, MAXSHORTSTR), "... ");
913		spaceleft -= strlen(eb);
914		while (*eb != '\0')
915			*eb++ &= 0177;
916	}
917
918	/* output the message */
919	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
920	spaceleft -= strlen(eb);
921	while (*eb != '\0')
922		*eb++ &= 0177;
923
924	/* output the error code, if any */
925	if (eno != 0)
926		(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
927
928	return errtxt;
929}
930/*
931**  BUFFER_ERRORS -- arrange to buffer future error messages
932**
933**	Parameters:
934**		none
935**
936**	Returns:
937**		none.
938*/
939
940void
941buffer_errors()
942{
943	HeldMessageBuf[0] = '\0';
944	HoldErrs = true;
945}
946/*
947**  FLUSH_ERRORS -- flush the held error message buffer
948**
949**	Parameters:
950**		print -- if set, print the message, otherwise just
951**			delete it.
952**
953**	Returns:
954**		none.
955*/
956
957void
958flush_errors(print)
959	bool print;
960{
961	if (print && HeldMessageBuf[0] != '\0')
962		putoutmsg(HeldMessageBuf, false, true);
963	HeldMessageBuf[0] = '\0';
964	HoldErrs = false;
965}
966/*
967**  SM_ERRSTRING -- return string description of error code
968**
969**	Parameters:
970**		errnum -- the error number to translate
971**
972**	Returns:
973**		A string description of errnum.
974**
975**	Side Effects:
976**		none.
977*/
978
979const char *
980sm_errstring(errnum)
981	int errnum;
982{
983	char *dnsmsg;
984	char *bp;
985	static char buf[MAXLINE];
986#if HASSTRERROR
987	char *err;
988	char errbuf[30];
989#endif /* HASSTRERROR */
990#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
991	extern char *sys_errlist[];
992	extern int sys_nerr;
993#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
994
995	/*
996	**  Handle special network error codes.
997	**
998	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
999	*/
1000
1001	dnsmsg = NULL;
1002	switch (errnum)
1003	{
1004	  case ETIMEDOUT:
1005	  case ECONNRESET:
1006		bp = buf;
1007#if HASSTRERROR
1008		err = strerror(errnum);
1009		if (err == NULL)
1010		{
1011			(void) sm_snprintf(errbuf, sizeof errbuf,
1012					   "Error %d", errnum);
1013			err = errbuf;
1014		}
1015		(void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
1016#else /* HASSTRERROR */
1017		if (errnum >= 0 && errnum < sys_nerr)
1018			(void) sm_strlcpy(bp, sys_errlist[errnum],
1019					  SPACELEFT(buf, bp));
1020		else
1021			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1022				"Error %d", errnum);
1023#endif /* HASSTRERROR */
1024		bp += strlen(bp);
1025		if (CurHostName != NULL)
1026		{
1027			if (errnum == ETIMEDOUT)
1028			{
1029				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1030					" with ");
1031				bp += strlen(bp);
1032			}
1033			else
1034			{
1035				bp = buf;
1036				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1037					"Connection reset by ");
1038				bp += strlen(bp);
1039			}
1040			(void) sm_strlcpy(bp,
1041					shortenstring(CurHostName, MAXSHORTSTR),
1042					SPACELEFT(buf, bp));
1043			bp += strlen(buf);
1044		}
1045		if (SmtpPhase != NULL)
1046		{
1047			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1048				" during %s", SmtpPhase);
1049		}
1050		return buf;
1051
1052	  case EHOSTDOWN:
1053		if (CurHostName == NULL)
1054			break;
1055		(void) sm_snprintf(buf, sizeof buf, "Host %s is down",
1056			shortenstring(CurHostName, MAXSHORTSTR));
1057		return buf;
1058
1059	  case ECONNREFUSED:
1060		if (CurHostName == NULL)
1061			break;
1062		(void) sm_strlcpyn(buf, sizeof buf, 2, "Connection refused by ",
1063			shortenstring(CurHostName, MAXSHORTSTR));
1064		return buf;
1065
1066#if NAMED_BIND
1067	  case HOST_NOT_FOUND + E_DNSBASE:
1068		dnsmsg = "host not found";
1069		break;
1070
1071	  case TRY_AGAIN + E_DNSBASE:
1072		dnsmsg = "host name lookup failure";
1073		break;
1074
1075	  case NO_RECOVERY + E_DNSBASE:
1076		dnsmsg = "non-recoverable error";
1077		break;
1078
1079	  case NO_DATA + E_DNSBASE:
1080		dnsmsg = "no data known";
1081		break;
1082#endif /* NAMED_BIND */
1083
1084	  case EPERM:
1085		/* SunOS gives "Not owner" -- this is the POSIX message */
1086		return "Operation not permitted";
1087
1088	/*
1089	**  Error messages used internally in sendmail.
1090	*/
1091
1092	  case E_SM_OPENTIMEOUT:
1093		return "Timeout on file open";
1094
1095	  case E_SM_NOSLINK:
1096		return "Symbolic links not allowed";
1097
1098	  case E_SM_NOHLINK:
1099		return "Hard links not allowed";
1100
1101	  case E_SM_REGONLY:
1102		return "Regular files only";
1103
1104	  case E_SM_ISEXEC:
1105		return "Executable files not allowed";
1106
1107	  case E_SM_WWDIR:
1108		return "World writable directory";
1109
1110	  case E_SM_GWDIR:
1111		return "Group writable directory";
1112
1113	  case E_SM_FILECHANGE:
1114		return "File changed after open";
1115
1116	  case E_SM_WWFILE:
1117		return "World writable file";
1118
1119	  case E_SM_GWFILE:
1120		return "Group writable file";
1121
1122	  case E_SM_GRFILE:
1123		return "Group readable file";
1124
1125	  case E_SM_WRFILE:
1126		return "World readable file";
1127	}
1128
1129	if (dnsmsg != NULL)
1130	{
1131		bp = buf;
1132		bp += sm_strlcpy(bp, "Name server: ", sizeof buf);
1133		if (CurHostName != NULL)
1134		{
1135			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
1136				shortenstring(CurHostName, MAXSHORTSTR), ": ");
1137			bp += strlen(bp);
1138		}
1139		(void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
1140		return buf;
1141	}
1142
1143#if LDAPMAP
1144	if (errnum >= E_LDAPBASE)
1145		return ldap_err2string(errnum - E_LDAPBASE);
1146#endif /* LDAPMAP */
1147
1148#if HASSTRERROR
1149	err = strerror(errnum);
1150	if (err == NULL)
1151	{
1152		(void) sm_snprintf(buf, sizeof buf, "Error %d", errnum);
1153		return buf;
1154	}
1155	return err;
1156#else /* HASSTRERROR */
1157	if (errnum > 0 && errnum < sys_nerr)
1158		return sys_errlist[errnum];
1159
1160	(void) sm_snprintf(buf, sizeof buf, "Error %d", errnum);
1161	return buf;
1162#endif /* HASSTRERROR */
1163}
1164