err.c revision 225736
157429Smarkm/*
257429Smarkm * Copyright (c) 1998-2003, 2010 Sendmail, Inc. and its suppliers.
357429Smarkm *	All rights reserved.
457429Smarkm * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
557429Smarkm * Copyright (c) 1988, 1993
660573Skris *	The Regents of the University of California.  All rights reserved.
765674Skris *
865674Skris * By using this file, you agree to the terms and conditions set
965674Skris * forth in the LICENSE file which can be found at the top level of
1065674Skris * the sendmail distribution.
1165674Skris *
1257429Smarkm */
1357429Smarkm
1457429Smarkm#include <sendmail.h>
1598684Sdes
1657429SmarkmSM_RCSID("@(#)$Id: err.c,v 8.205 2010/02/03 23:22:41 ca Exp $")
1757429Smarkm
1857429Smarkm#if LDAPMAP
1976262Sgreen# include <lber.h>
2076262Sgreen# include <ldap.h>			/* for LDAP error codes */
2157429Smarkm#endif /* LDAPMAP */
2292559Sdes
2376262Sgreenstatic void	putoutmsg __P((char *, bool, bool));
2457429Smarkmstatic void	puterrmsg __P((char *));
2557429Smarkmstatic char	*fmtmsg __P((char *, const char *, const char *, const char *,
2657429Smarkm			     int, const char *, va_list));
2757429Smarkm
2857429Smarkm/*
2992559Sdes**  FATAL_ERROR -- handle a fatal exception
3092559Sdes**
3157429Smarkm**	This function is installed as the default exception handler
3257429Smarkm**	in the main sendmail process, and in all child processes
3357429Smarkm**	that we create.  Its job is to handle exceptions that are not
3457429Smarkm**	handled at a lower level.
3557429Smarkm**
3676262Sgreen**	The theory is that unhandled exceptions will be 'fatal' class
3757429Smarkm**	exceptions (with an "F:" prefix), such as the out-of-memory
3857429Smarkm**	exception "F:sm.heap".  As such, they are handled by exiting
3957429Smarkm**	the process in exactly the same way that xalloc() in Sendmail 8.10
4057429Smarkm**	exits the process when it fails due to lack of memory:
4176262Sgreen**	we call syserr with a message beginning with "!".
4257429Smarkm**
4357429Smarkm**	Parameters:
4457429Smarkm**		exc -- exception which is terminating this process
4598941Sdes**
4698941Sdes**	Returns:
4798941Sdes**		none
4876262Sgreen*/
4998941Sdes
5098941Sdesvoid
5198941Sdesfatal_error(exc)
5298941Sdes	SM_EXC_T *exc;
5398941Sdes{
5498941Sdes	static char buf[256];
5598941Sdes	SM_FILE_T f;
5698941Sdes
5798941Sdes	/*
5898941Sdes	**  This function may be called when the heap is exhausted.
5998941Sdes	**  The following code writes the message for 'exc' into our
6098941Sdes	**  static buffer without allocating memory or raising exceptions.
6198941Sdes	*/
6298941Sdes
6398941Sdes	sm_strio_init(&f, buf, sizeof(buf));
6498941Sdes	sm_exc_write(exc, &f);
6598941Sdes	(void) sm_io_flush(&f, SM_TIME_DEFAULT);
6698941Sdes
6798941Sdes	/*
6857429Smarkm	**  Terminate the process after logging an error and cleaning up.
6992559Sdes	**  Problems:
7057429Smarkm	**  - syserr decides what class of error this is by looking at errno.
7157429Smarkm	**    That's no good; we should look at the exc structure.
7298684Sdes	**  - The cleanup code should be moved out of syserr
7398684Sdes	**    and into individual exception handlers
7498684Sdes	**    that are part of the module they clean up after.
7576262Sgreen	*/
7657429Smarkm
7757429Smarkm	errno = ENOMEM;
7892559Sdes	syserr("!%s", buf);
7976262Sgreen}
8076262Sgreen
8176262Sgreen/*
8257429Smarkm**  SYSERR -- Print error message.
8357429Smarkm**
8476262Sgreen**	Prints an error message via sm_io_printf to the diagnostic output.
8576262Sgreen**
8676262Sgreen**	If the first character of the syserr message is `!' it will
8776262Sgreen**	log this as an ALERT message and exit immediately.  This can
8876262Sgreen**	leave queue files in an indeterminate state, so it should not
8976262Sgreen**	be used lightly.
9076262Sgreen**
9176262Sgreen**	If the first character of the syserr message is '!' or '@'
9276262Sgreen**	then syserr knows that the process is about to be terminated,
9357429Smarkm**	so the SMTP reply code defaults to 421.  Otherwise, the
9492559Sdes**	reply code defaults to 451 or 554, depending on errno.
9576262Sgreen**
9657429Smarkm**	Parameters:
9776262Sgreen**		fmt -- the format string.  An optional '!' or '@',
9876262Sgreen**			followed by an optional three-digit SMTP
9976262Sgreen**			reply code, followed by message text.
10076262Sgreen**		(others) -- parameters
10176262Sgreen**
10276262Sgreen**	Returns:
10376262Sgreen**		none
10457429Smarkm**		Raises E:mta.quickabort if QuickAbort is set.
10576262Sgreen**
10676262Sgreen**	Side Effects:
10776262Sgreen**		increments Errors.
10876262Sgreen**		sets ExitStat.
10976262Sgreen*/
11076262Sgreen
11176262Sgreenchar		MsgBuf[BUFSIZ*2];	/* text of most recent message */
11257429Smarkmstatic char	HeldMessageBuf[sizeof(MsgBuf)];	/* for held messages */
11376262Sgreen
11476262Sgreen#if NAMED_BIND && !defined(NO_DATA)
11576262Sgreen# define NO_DATA	NO_ADDRESS
11676262Sgreen#endif /* NAMED_BIND && !defined(NO_DATA) */
11776262Sgreen
11876262Sgreenvoid
11976262Sgreen/*VARARGS1*/
12076262Sgreen#ifdef __STDC__
12176262Sgreensyserr(const char *fmt, ...)
12276262Sgreen#else /* __STDC__ */
12376262Sgreensyserr(fmt, va_alist)
12476262Sgreen	const char *fmt;
12576262Sgreen	va_dcl
12676262Sgreen#endif /* __STDC__ */
12776262Sgreen{
12876262Sgreen	register char *p;
12957429Smarkm	int save_errno = errno;
13057429Smarkm	bool panic;
13157429Smarkm	bool exiting;
13257429Smarkm	char *user;
13376262Sgreen	char *enhsc;
13476262Sgreen	char *errtxt;
13576262Sgreen	struct passwd *pw;
13676262Sgreen	char ubuf[80];
13776262Sgreen	SM_VA_LOCAL_DECL
13876262Sgreen
13976262Sgreen	switch (*fmt)
14076262Sgreen	{
14176262Sgreen	  case '!':
14276262Sgreen		++fmt;
14376262Sgreen		panic = true;
14492559Sdes		exiting = true;
14576262Sgreen		break;
14676262Sgreen	  case '@':
14776262Sgreen		++fmt;
14876262Sgreen		panic = false;
14976262Sgreen		exiting = true;
15076262Sgreen		break;
15176262Sgreen	  default:
15276262Sgreen		panic = false;
15376262Sgreen		exiting = false;
15476262Sgreen		break;
15576262Sgreen	}
15676262Sgreen
15776262Sgreen	/* format and output the error message */
15892559Sdes	if (exiting)
15976262Sgreen	{
16076262Sgreen		/*
16176262Sgreen		**  Since we are terminating the process,
16276262Sgreen		**  we are aborting the entire SMTP session,
16376262Sgreen		**  rather than just the current transaction.
16476262Sgreen		*/
16576262Sgreen
16676262Sgreen		p = "421";
16776262Sgreen		enhsc = "4.0.0";
16876262Sgreen	}
16976262Sgreen	else if (save_errno == 0)
17076262Sgreen	{
17176262Sgreen		p = "554";
17257429Smarkm		enhsc = "5.0.0";
17357429Smarkm	}
17457429Smarkm	else
17557429Smarkm	{
17657429Smarkm		p = "451";
17757429Smarkm		enhsc = "4.0.0";
17892559Sdes	}
17957429Smarkm	SM_VA_START(ap, fmt);
18057429Smarkm	errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
18192559Sdes	SM_VA_END(ap);
18257429Smarkm	puterrmsg(MsgBuf);
18376262Sgreen
18476262Sgreen	/* save this message for mailq printing */
18592559Sdes	if (!panic && CurEnv != NULL)
18676262Sgreen	{
18776262Sgreen		char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
18876262Sgreen
18976262Sgreen		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
19057429Smarkm			sm_free(CurEnv->e_message);
19157429Smarkm		CurEnv->e_message = nmsg;
19257429Smarkm	}
19376262Sgreen
19492559Sdes	/* determine exit status if not already set */
19557429Smarkm	if (ExitStat == EX_OK)
19657429Smarkm	{
19757429Smarkm		if (save_errno == 0)
19892559Sdes			ExitStat = EX_SOFTWARE;
19957429Smarkm		else
20057429Smarkm			ExitStat = EX_OSERR;
20157429Smarkm		if (tTd(54, 1))
20257429Smarkm			sm_dprintf("syserr: ExitStat = %d\n", ExitStat);
20376262Sgreen	}
20476262Sgreen
20557429Smarkm	pw = sm_getpwuid(RealUid);
20692559Sdes	if (pw != NULL)
20776262Sgreen		user = pw->pw_name;
20857429Smarkm	else
20976262Sgreen	{
21076262Sgreen		user = ubuf;
21157429Smarkm		(void) sm_snprintf(ubuf, sizeof(ubuf), "UID%d", (int) RealUid);
21257429Smarkm	}
21376262Sgreen
21476262Sgreen	if (LogLevel > 0)
21576262Sgreen		sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
21657429Smarkm			  CurEnv == NULL ? NOQID : CurEnv->e_id,
21776262Sgreen			  "SYSERR(%s): %.900s",
21876262Sgreen			  user, errtxt);
21976262Sgreen	switch (save_errno)
22076262Sgreen	{
22176262Sgreen	  case EBADF:
22276262Sgreen	  case ENFILE:
22376262Sgreen	  case EMFILE:
22476262Sgreen	  case ENOTTY:
22576262Sgreen#ifdef EFBIG
22676262Sgreen	  case EFBIG:
22776262Sgreen#endif /* EFBIG */
22876262Sgreen#ifdef ESPIPE
22976262Sgreen	  case ESPIPE:
23076262Sgreen#endif /* ESPIPE */
23157429Smarkm#ifdef EPIPE
23276262Sgreen	  case EPIPE:
23376262Sgreen#endif /* EPIPE */
23492559Sdes#ifdef ENOBUFS
23576262Sgreen	  case ENOBUFS:
23676262Sgreen#endif /* ENOBUFS */
23757429Smarkm#ifdef ESTALE
23876262Sgreen	  case ESTALE:
23976262Sgreen#endif /* ESTALE */
24057429Smarkm		printopenfds(true);
24176262Sgreen		mci_dump_all(smioout, true);
24276262Sgreen		break;
24376262Sgreen	}
24476262Sgreen	if (panic)
24576262Sgreen	{
24657429Smarkm#if XLA
24776262Sgreen		xla_all_end();
24876262Sgreen#endif /* XLA */
24976262Sgreen		sync_queue_time();
25076262Sgreen		if (tTd(0, 1))
25157429Smarkm			abort();
25257429Smarkm		exit(EX_OSERR);
25376262Sgreen	}
25476262Sgreen	errno = 0;
25576262Sgreen	if (QuickAbort)
25676262Sgreen		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
25776262Sgreen}
25876262Sgreen/*
25962101Sgreen**  USRERR -- Signal user error.
26076262Sgreen**
26176262Sgreen**	This is much like syserr except it is for user errors.
26262101Sgreen**
26362101Sgreen**	Parameters:
26462101Sgreen**		fmt -- the format string.  If it does not begin with
26592559Sdes**			a three-digit SMTP reply code, 550 is assumed.
26662101Sgreen**		(others) -- sm_io_printf strings
26762101Sgreen**
26862101Sgreen**	Returns:
26976262Sgreen**		none
27076262Sgreen**		Raises E:mta.quickabort if QuickAbort is set.
27176262Sgreen**
27276262Sgreen**	Side Effects:
27376262Sgreen**		increments Errors.
27476262Sgreen*/
27576262Sgreen
27676262Sgreen/*VARARGS1*/
27776262Sgreenvoid
27876262Sgreen#ifdef __STDC__
27976262Sgreenusrerr(const char *fmt, ...)
28062101Sgreen#else /* __STDC__ */
28176262Sgreenusrerr(fmt, va_alist)
28276262Sgreen	const char *fmt;
28362101Sgreen	va_dcl
28476262Sgreen#endif /* __STDC__ */
28592559Sdes{
28676262Sgreen	char *enhsc;
28776262Sgreen	char *errtxt;
28876262Sgreen	SM_VA_LOCAL_DECL
28992559Sdes
29076262Sgreen	if (fmt[0] == '5' || fmt[0] == '6')
29176262Sgreen		enhsc = "5.0.0";
29276262Sgreen	else if (fmt[0] == '4' || fmt[0] == '8')
29362101Sgreen		enhsc = "4.0.0";
29462101Sgreen	else if (fmt[0] == '2')
29557429Smarkm		enhsc = "2.0.0";
29657429Smarkm	else
29792559Sdes		enhsc = NULL;
29857429Smarkm	SM_VA_START(ap, fmt);
29957429Smarkm	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
30057429Smarkm	SM_VA_END(ap);
30157429Smarkm
30257429Smarkm	if (SuprErrs)
30357429Smarkm		return;
30457429Smarkm
30557429Smarkm	/* save this message for mailq printing */
30657429Smarkm	switch (MsgBuf[0])
30757429Smarkm	{
30857429Smarkm	  case '4':
30957429Smarkm	  case '8':
31057429Smarkm		if (CurEnv->e_message != NULL)
31157429Smarkm			break;
31257429Smarkm
31357429Smarkm		/* FALLTHROUGH */
31457429Smarkm
31557429Smarkm	  case '5':
31657429Smarkm	  case '6':
31757429Smarkm		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
31857429Smarkm			sm_free(CurEnv->e_message);
31957429Smarkm		if (MsgBuf[0] == '6')
32092559Sdes		{
32157429Smarkm			char buf[MAXLINE];
32257429Smarkm
32357429Smarkm			(void) sm_snprintf(buf, sizeof(buf),
32457429Smarkm					   "Postmaster warning: %.*s",
32557429Smarkm					   (int) sizeof(buf) - 22, errtxt);
32657429Smarkm			CurEnv->e_message =
32792559Sdes				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
32857429Smarkm		}
32957429Smarkm		else
33057429Smarkm		{
33157429Smarkm			CurEnv->e_message =
33257429Smarkm				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
33357429Smarkm		}
33457429Smarkm		break;
33557429Smarkm	}
33657429Smarkm
33757429Smarkm	puterrmsg(MsgBuf);
33857429Smarkm	if (LogLevel > 3 && LogUsrErrs)
33957429Smarkm		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
34057429Smarkm	if (QuickAbort)
34160573Skris		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
34257429Smarkm}
34357429Smarkm/*
34457429Smarkm**  USRERRENH -- Signal user error.
34557429Smarkm**
34657429Smarkm**	Same as usrerr but with enhanced status code.
34760573Skris**
34892559Sdes**	Parameters:
34957429Smarkm**		enhsc -- the enhanced status code.
35057429Smarkm**		fmt -- the format string.  If it does not begin with
35157429Smarkm**			a three-digit SMTP reply code, 550 is assumed.
35257429Smarkm**		(others) -- sm_io_printf strings
35357429Smarkm**
35492559Sdes**	Returns:
35557429Smarkm**		none
35657429Smarkm**		Raises E:mta.quickabort if QuickAbort is set.
35757429Smarkm**
358**	Side Effects:
359**		increments Errors.
360*/
361
362/*VARARGS2*/
363void
364#ifdef __STDC__
365usrerrenh(char *enhsc, const char *fmt, ...)
366#else /* __STDC__ */
367usrerrenh(enhsc, fmt, va_alist)
368	char *enhsc;
369	const char *fmt;
370	va_dcl
371#endif /* __STDC__ */
372{
373	char *errtxt;
374	SM_VA_LOCAL_DECL
375
376	if (enhsc == NULL || *enhsc == '\0')
377	{
378		if (fmt[0] == '5' || fmt[0] == '6')
379			enhsc = "5.0.0";
380		else if (fmt[0] == '4' || fmt[0] == '8')
381			enhsc = "4.0.0";
382		else if (fmt[0] == '2')
383			enhsc = "2.0.0";
384	}
385	SM_VA_START(ap, fmt);
386	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
387	SM_VA_END(ap);
388
389	if (SuprErrs)
390		return;
391
392	/* save this message for mailq printing */
393	switch (MsgBuf[0])
394	{
395	  case '4':
396	  case '8':
397		if (CurEnv->e_message != NULL)
398			break;
399
400		/* FALLTHROUGH */
401
402	  case '5':
403	  case '6':
404		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
405			sm_free(CurEnv->e_message);
406		if (MsgBuf[0] == '6')
407		{
408			char buf[MAXLINE];
409
410			(void) sm_snprintf(buf, sizeof(buf),
411					   "Postmaster warning: %.*s",
412					   (int) sizeof(buf) - 22, errtxt);
413			CurEnv->e_message =
414				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
415		}
416		else
417		{
418			CurEnv->e_message =
419				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
420		}
421		break;
422	}
423
424	puterrmsg(MsgBuf);
425	if (LogLevel > 3 && LogUsrErrs)
426		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
427	if (QuickAbort)
428		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
429}
430
431/*
432**  MESSAGE -- print message (not necessarily an error)
433**
434**	Parameters:
435**		msg -- the message (sm_io_printf fmt) -- it can begin with
436**			an SMTP reply code.  If not, 050 is assumed.
437**		(others) -- sm_io_printf arguments
438**
439**	Returns:
440**		none
441**
442**	Side Effects:
443**		none.
444*/
445
446/*VARARGS1*/
447void
448#ifdef __STDC__
449message(const char *msg, ...)
450#else /* __STDC__ */
451message(msg, va_alist)
452	const char *msg;
453	va_dcl
454#endif /* __STDC__ */
455{
456	char *errtxt;
457	SM_VA_LOCAL_DECL
458
459	errno = 0;
460	SM_VA_START(ap, msg);
461	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
462	SM_VA_END(ap);
463	putoutmsg(MsgBuf, false, false);
464
465	/* save this message for mailq printing */
466	switch (MsgBuf[0])
467	{
468	  case '4':
469	  case '8':
470		if (CurEnv->e_message != NULL)
471			break;
472		/* FALLTHROUGH */
473
474	  case '5':
475		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
476			sm_free(CurEnv->e_message);
477		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
478		break;
479	}
480}
481
482
483/*
484**  NMESSAGE -- print message (not necessarily an error)
485**
486**	Just like "message" except it never puts the to... tag on.
487**
488**	Parameters:
489**		msg -- the message (sm_io_printf fmt) -- if it begins
490**			with a three digit SMTP reply code, that is used,
491**			otherwise 050 is assumed.
492**		(others) -- sm_io_printf arguments
493**
494**	Returns:
495**		none
496**
497**	Side Effects:
498**		none.
499*/
500
501/*VARARGS1*/
502void
503#ifdef __STDC__
504nmessage(const char *msg, ...)
505#else /* __STDC__ */
506nmessage(msg, va_alist)
507	const char *msg;
508	va_dcl
509#endif /* __STDC__ */
510{
511	char *errtxt;
512	SM_VA_LOCAL_DECL
513
514	errno = 0;
515	SM_VA_START(ap, msg);
516	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
517			(char *) NULL, 0, msg, ap);
518	SM_VA_END(ap);
519	putoutmsg(MsgBuf, false, false);
520
521	/* save this message for mailq printing */
522	switch (MsgBuf[0])
523	{
524	  case '4':
525	  case '8':
526		if (CurEnv->e_message != NULL)
527			break;
528		/* FALLTHROUGH */
529
530	  case '5':
531		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
532			sm_free(CurEnv->e_message);
533		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
534		break;
535	}
536}
537/*
538**  PUTOUTMSG -- output error message to transcript and channel
539**
540**	Parameters:
541**		msg -- message to output (in SMTP format).
542**		holdmsg -- if true, don't output a copy of the message to
543**			our output channel.
544**		heldmsg -- if true, this is a previously held message;
545**			don't log it to the transcript file.
546**
547**	Returns:
548**		none.
549**
550**	Side Effects:
551**		Outputs msg to the transcript.
552**		If appropriate, outputs it to the channel.
553**		Deletes SMTP reply code number as appropriate.
554*/
555
556static void
557putoutmsg(msg, holdmsg, heldmsg)
558	char *msg;
559	bool holdmsg;
560	bool heldmsg;
561{
562	char msgcode = msg[0];
563	char *errtxt = msg;
564	char *id;
565
566	/* display for debugging */
567	if (tTd(54, 8))
568		sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
569			heldmsg ? " (held)" : "");
570
571	/* map warnings to something SMTP can handle */
572	if (msgcode == '6')
573		msg[0] = '5';
574	else if (msgcode == '8')
575		msg[0] = '4';
576	id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
577
578	/* output to transcript if serious */
579	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
580	    strchr("45", msg[0]) != NULL)
581		(void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
582				     msg);
583
584	if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
585		sm_syslog(LOG_INFO, id,
586			  "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
587			  heldmsg ? " (held)" : "");
588
589	if (msgcode == '8')
590		msg[0] = '0';
591
592	/* output to channel if appropriate */
593	if (!Verbose && msg[0] == '0')
594		return;
595	if (holdmsg)
596	{
597		/* save for possible future display */
598		msg[0] = msgcode;
599		if (HeldMessageBuf[0] == '5' && msgcode == '4')
600			return;
601		(void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
602		return;
603	}
604
605	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
606
607	if (OutChannel == NULL)
608		return;
609
610	/* find actual text of error (after SMTP status codes) */
611	if (ISSMTPREPLY(errtxt))
612	{
613		int l;
614
615		errtxt += 4;
616		l = isenhsc(errtxt, ' ');
617		if (l <= 0)
618			l = isenhsc(errtxt, '\0');
619		if (l > 0)
620			errtxt += l + 1;
621	}
622
623	/* if DisConnected, OutChannel now points to the transcript */
624	if (!DisConnected &&
625	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
626		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
627				     msg);
628	else
629		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
630				     errtxt);
631	if (TrafficLogFile != NULL)
632		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
633				     "%05d >>> %s\n", (int) CurrentPid,
634				     (OpMode == MD_SMTP || OpMode == MD_DAEMON)
635					? msg : errtxt);
636#if !PIPELINING
637	/* XXX can't flush here for SMTP pipelining */
638	if (msg[3] == ' ')
639		(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
640	if (!sm_io_error(OutChannel) || DisConnected)
641		return;
642
643	/*
644	**  Error on output -- if reporting lost channel, just ignore it.
645	**  Also, ignore errors from QUIT response (221 message) -- some
646	**	rude servers don't read result.
647	*/
648
649	if (InChannel == NULL || sm_io_eof(InChannel) ||
650	    sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
651		return;
652
653	/* can't call syserr, 'cause we are using MsgBuf */
654	HoldErrs = true;
655	if (LogLevel > 0)
656		sm_syslog(LOG_CRIT, id,
657			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
658			  CURHOSTNAME,
659			  shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
660#endif /* !PIPELINING */
661}
662/*
663**  PUTERRMSG -- like putoutmsg, but does special processing for error messages
664**
665**	Parameters:
666**		msg -- the message to output.
667**
668**	Returns:
669**		none.
670**
671**	Side Effects:
672**		Sets the fatal error bit in the envelope as appropriate.
673*/
674
675static void
676puterrmsg(msg)
677	char *msg;
678{
679	char msgcode = msg[0];
680
681	/* output the message as usual */
682	putoutmsg(msg, HoldErrs, false);
683
684	/* be careful about multiple error messages */
685	if (OnlyOneError)
686		HoldErrs = true;
687
688	/* signal the error */
689	Errors++;
690
691	if (CurEnv == NULL)
692		return;
693
694	if (msgcode == '6')
695	{
696		/* notify the postmaster */
697		CurEnv->e_flags |= EF_PM_NOTIFY;
698	}
699	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
700	{
701		/* mark long-term fatal errors */
702		CurEnv->e_flags |= EF_FATALERRS;
703	}
704}
705/*
706**  ISENHSC -- check whether a string contains an enhanced status code
707**
708**	Parameters:
709**		s -- string with possible enhanced status code.
710**		delim -- delim for enhanced status code.
711**
712**	Returns:
713**		0  -- no enhanced status code.
714**		>4 -- length of enhanced status code.
715**
716**	Side Effects:
717**		none.
718*/
719int
720isenhsc(s, delim)
721	const char *s;
722	int delim;
723{
724	int l, h;
725
726	if (s == NULL)
727		return 0;
728	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
729		return 0;
730	h = 0;
731	l = 2;
732	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
733		++h;
734	if (h == 0 || s[l + h] != '.')
735		return 0;
736	l += h + 1;
737	h = 0;
738	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
739		++h;
740	if (h == 0 || s[l + h] != delim)
741		return 0;
742	return l + h;
743}
744/*
745**  EXTENHSC -- check and extract an enhanced status code
746**
747**	Parameters:
748**		s -- string with possible enhanced status code.
749**		delim -- delim for enhanced status code.
750**		e -- pointer to storage for enhanced status code.
751**			must be != NULL and have space for at least
752**			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
753**
754**	Returns:
755**		0  -- no enhanced status code.
756**		>4 -- length of enhanced status code.
757**
758**	Side Effects:
759**		fills e with enhanced status code.
760*/
761
762int
763extenhsc(s, delim, e)
764	const char *s;
765	int delim;
766	char *e;
767{
768	int l, h;
769
770	if (s == NULL)
771		return 0;
772	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
773		return 0;
774	h = 0;
775	l = 2;
776	e[0] = s[0];
777	e[1] = '.';
778	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
779	{
780		e[l + h] = s[l + h];
781		++h;
782	}
783	if (h == 0 || s[l + h] != '.')
784		return 0;
785	e[l + h] = '.';
786	l += h + 1;
787	h = 0;
788	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
789	{
790		e[l + h] = s[l + h];
791		++h;
792	}
793	if (h == 0 || s[l + h] != delim)
794		return 0;
795	e[l + h] = '\0';
796	return l + h;
797}
798/*
799**  FMTMSG -- format a message into buffer.
800**
801**	Parameters:
802**		eb -- error buffer to get result -- MUST BE MsgBuf.
803**		to -- the recipient tag for this message.
804**		num -- default three digit SMTP reply code.
805**		enhsc -- enhanced status code.
806**		en -- the error number to display.
807**		fmt -- format of string.
808**		ap -- arguments for fmt.
809**
810**	Returns:
811**		pointer to error text beyond status codes.
812**
813**	Side Effects:
814**		none.
815*/
816
817static char *
818fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
819	register char *eb;
820	const char *to;
821	const char *num;
822	const char *enhsc;
823	int eno;
824	const char *fmt;
825	SM_VA_LOCAL_DECL
826{
827	char del;
828	int l;
829	int spaceleft = sizeof(MsgBuf);
830	char *errtxt;
831
832	/* output the reply code */
833	if (ISSMTPCODE(fmt))
834	{
835		num = fmt;
836		fmt += 4;
837	}
838	if (num[3] == '-')
839		del = '-';
840	else
841		del = ' ';
842	if (SoftBounce && num[0] == '5')
843	{
844		/* replace 5 by 4 */
845		(void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
846	}
847	else
848		(void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
849	eb += 4;
850	spaceleft -= 4;
851
852	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
853	{
854		/* copy enh.status code including trailing blank */
855		l++;
856		(void) sm_strlcpy(eb, fmt, l + 1);
857		eb += l;
858		spaceleft -= l;
859		fmt += l;
860	}
861	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
862	{
863		/* copy enh.status code */
864		(void) sm_strlcpy(eb, enhsc, l + 1);
865		eb[l] = ' ';
866		eb[++l] = '\0';
867		eb += l;
868		spaceleft -= l;
869	}
870	if (SoftBounce && eb[-l] == '5')
871	{
872		/* replace 5 by 4 */
873		eb[-l] = '4';
874	}
875	errtxt = eb;
876
877	/* output the file name and line number */
878	if (FileName != NULL)
879	{
880		(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
881				   shortenstring(FileName, 83), LineNumber);
882		eb += (l = strlen(eb));
883		spaceleft -= l;
884	}
885
886	/*
887	**  output the "to" address only if it is defined and one of the
888	**  following codes is used:
889	**  050 internal notices, e.g., alias expansion
890	**  250 Ok
891	**  252 Cannot VRFY user, but will accept message and attempt delivery
892	**  450 Requested mail action not taken: mailbox unavailable
893	**  550 Requested action not taken: mailbox unavailable
894	**  553 Requested action not taken: mailbox name not allowed
895	**
896	**  Notice: this still isn't "the right thing", this code shouldn't
897	**	(indirectly) depend on CurEnv->e_to.
898	*/
899
900	if (to != NULL && to[0] != '\0' &&
901	    (strncmp(num, "050", 3) == 0 ||
902	     strncmp(num, "250", 3) == 0 ||
903	     strncmp(num, "252", 3) == 0 ||
904	     strncmp(num, "450", 3) == 0 ||
905	     strncmp(num, "550", 3) == 0 ||
906	     strncmp(num, "553", 3) == 0))
907	{
908		(void) sm_strlcpyn(eb, spaceleft, 2,
909				   shortenstring(to, MAXSHORTSTR), "... ");
910		spaceleft -= strlen(eb);
911		while (*eb != '\0')
912			*eb++ &= 0177;
913	}
914
915	/* output the message */
916	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
917	spaceleft -= strlen(eb);
918	while (*eb != '\0')
919		*eb++ &= 0177;
920
921	/* output the error code, if any */
922	if (eno != 0)
923		(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
924
925	return errtxt;
926}
927/*
928**  BUFFER_ERRORS -- arrange to buffer future error messages
929**
930**	Parameters:
931**		none
932**
933**	Returns:
934**		none.
935*/
936
937void
938buffer_errors()
939{
940	HeldMessageBuf[0] = '\0';
941	HoldErrs = true;
942}
943/*
944**  FLUSH_ERRORS -- flush the held error message buffer
945**
946**	Parameters:
947**		print -- if set, print the message, otherwise just
948**			delete it.
949**
950**	Returns:
951**		none.
952*/
953
954void
955flush_errors(print)
956	bool print;
957{
958	if (print && HeldMessageBuf[0] != '\0')
959		putoutmsg(HeldMessageBuf, false, true);
960	HeldMessageBuf[0] = '\0';
961	HoldErrs = false;
962}
963/*
964**  SM_ERRSTRING -- return string description of error code
965**
966**	Parameters:
967**		errnum -- the error number to translate
968**
969**	Returns:
970**		A string description of errnum.
971**
972**	Side Effects:
973**		none.
974*/
975
976const char *
977sm_errstring(errnum)
978	int errnum;
979{
980	char *dnsmsg;
981	char *bp;
982	static char buf[MAXLINE];
983#if HASSTRERROR
984	char *err;
985	char errbuf[30];
986#endif /* HASSTRERROR */
987#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
988	extern char *sys_errlist[];
989	extern int sys_nerr;
990#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
991
992	/*
993	**  Handle special network error codes.
994	**
995	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
996	*/
997
998	dnsmsg = NULL;
999	switch (errnum)
1000	{
1001	  case ETIMEDOUT:
1002	  case ECONNRESET:
1003		bp = buf;
1004#if HASSTRERROR
1005		err = strerror(errnum);
1006		if (err == NULL)
1007		{
1008			(void) sm_snprintf(errbuf, sizeof(errbuf),
1009					   "Error %d", errnum);
1010			err = errbuf;
1011		}
1012		(void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
1013#else /* HASSTRERROR */
1014		if (errnum >= 0 && errnum < sys_nerr)
1015			(void) sm_strlcpy(bp, sys_errlist[errnum],
1016					  SPACELEFT(buf, bp));
1017		else
1018			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1019				"Error %d", errnum);
1020#endif /* HASSTRERROR */
1021		bp += strlen(bp);
1022		if (CurHostName != NULL)
1023		{
1024			if (errnum == ETIMEDOUT)
1025			{
1026				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1027					" with ");
1028				bp += strlen(bp);
1029			}
1030			else
1031			{
1032				bp = buf;
1033				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1034					"Connection reset by ");
1035				bp += strlen(bp);
1036			}
1037			(void) sm_strlcpy(bp,
1038					shortenstring(CurHostName, MAXSHORTSTR),
1039					SPACELEFT(buf, bp));
1040			bp += strlen(buf);
1041		}
1042		if (SmtpPhase != NULL)
1043		{
1044			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1045				" during %s", SmtpPhase);
1046		}
1047		return buf;
1048
1049	  case EHOSTDOWN:
1050		if (CurHostName == NULL)
1051			break;
1052		(void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
1053			shortenstring(CurHostName, MAXSHORTSTR));
1054		return buf;
1055
1056	  case ECONNREFUSED:
1057		if (CurHostName == NULL)
1058			break;
1059		(void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
1060			shortenstring(CurHostName, MAXSHORTSTR));
1061		return buf;
1062
1063#if NAMED_BIND
1064	  case HOST_NOT_FOUND + E_DNSBASE:
1065		dnsmsg = "host not found";
1066		break;
1067
1068	  case TRY_AGAIN + E_DNSBASE:
1069		dnsmsg = "host name lookup failure";
1070		break;
1071
1072	  case NO_RECOVERY + E_DNSBASE:
1073		dnsmsg = "non-recoverable error";
1074		break;
1075
1076	  case NO_DATA + E_DNSBASE:
1077		dnsmsg = "no data known";
1078		break;
1079#endif /* NAMED_BIND */
1080
1081	  case EPERM:
1082		/* SunOS gives "Not owner" -- this is the POSIX message */
1083		return "Operation not permitted";
1084
1085	/*
1086	**  Error messages used internally in sendmail.
1087	*/
1088
1089	  case E_SM_OPENTIMEOUT:
1090		return "Timeout on file open";
1091
1092	  case E_SM_NOSLINK:
1093		return "Symbolic links not allowed";
1094
1095	  case E_SM_NOHLINK:
1096		return "Hard links not allowed";
1097
1098	  case E_SM_REGONLY:
1099		return "Regular files only";
1100
1101	  case E_SM_ISEXEC:
1102		return "Executable files not allowed";
1103
1104	  case E_SM_WWDIR:
1105		return "World writable directory";
1106
1107	  case E_SM_GWDIR:
1108		return "Group writable directory";
1109
1110	  case E_SM_FILECHANGE:
1111		return "File changed after open";
1112
1113	  case E_SM_WWFILE:
1114		return "World writable file";
1115
1116	  case E_SM_GWFILE:
1117		return "Group writable file";
1118
1119	  case E_SM_GRFILE:
1120		return "Group readable file";
1121
1122	  case E_SM_WRFILE:
1123		return "World readable file";
1124	}
1125
1126	if (dnsmsg != NULL)
1127	{
1128		bp = buf;
1129		bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
1130		if (CurHostName != NULL)
1131		{
1132			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
1133				shortenstring(CurHostName, MAXSHORTSTR), ": ");
1134			bp += strlen(bp);
1135		}
1136		(void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
1137		return buf;
1138	}
1139
1140#if LDAPMAP
1141	if (errnum >= E_LDAPBASE)
1142		return ldap_err2string(errnum - E_LDAPBASE);
1143#endif /* LDAPMAP */
1144
1145#if HASSTRERROR
1146	err = strerror(errnum);
1147	if (err == NULL)
1148	{
1149		(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1150		return buf;
1151	}
1152	return err;
1153#else /* HASSTRERROR */
1154	if (errnum > 0 && errnum < sys_nerr)
1155		return sys_errlist[errnum];
1156
1157	(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1158	return buf;
1159#endif /* HASSTRERROR */
1160}
1161