summaryrefslogtreecommitdiffstats
path: root/sendmail/src/err.c
diff options
context:
space:
mode:
Diffstat (limited to 'sendmail/src/err.c')
-rw-r--r--sendmail/src/err.c1158
1 files changed, 1158 insertions, 0 deletions
diff --git a/sendmail/src/err.c b/sendmail/src/err.c
new file mode 100644
index 0000000..5825666
--- /dev/null
+++ b/sendmail/src/err.c
@@ -0,0 +1,1158 @@
+/*
+ * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sendmail.h>
+
+SM_RCSID("@(#)$Id: err.c,v 8.196 2006/11/10 23:14:08 ca Exp $")
+
+#if LDAPMAP
+# include <lber.h>
+# include <ldap.h> /* for LDAP error codes */
+#endif /* LDAPMAP */
+
+static void putoutmsg __P((char *, bool, bool));
+static void puterrmsg __P((char *));
+static char *fmtmsg __P((char *, const char *, const char *, const char *,
+ int, const char *, va_list));
+
+/*
+** FATAL_ERROR -- handle a fatal exception
+**
+** This function is installed as the default exception handler
+** in the main sendmail process, and in all child processes
+** that we create. Its job is to handle exceptions that are not
+** handled at a lower level.
+**
+** The theory is that unhandled exceptions will be 'fatal' class
+** exceptions (with an "F:" prefix), such as the out-of-memory
+** exception "F:sm.heap". As such, they are handled by exiting
+** the process in exactly the same way that xalloc() in Sendmail 8.10
+** exits the process when it fails due to lack of memory:
+** we call syserr with a message beginning with "!".
+**
+** Parameters:
+** exc -- exception which is terminating this process
+**
+** Returns:
+** none
+*/
+
+void
+fatal_error(exc)
+ SM_EXC_T *exc;
+{
+ static char buf[256];
+ SM_FILE_T f;
+
+ /*
+ ** This function may be called when the heap is exhausted.
+ ** The following code writes the message for 'exc' into our
+ ** static buffer without allocating memory or raising exceptions.
+ */
+
+ sm_strio_init(&f, buf, sizeof(buf));
+ sm_exc_write(exc, &f);
+ (void) sm_io_flush(&f, SM_TIME_DEFAULT);
+
+ /*
+ ** Terminate the process after logging an error and cleaning up.
+ ** Problems:
+ ** - syserr decides what class of error this is by looking at errno.
+ ** That's no good; we should look at the exc structure.
+ ** - The cleanup code should be moved out of syserr
+ ** and into individual exception handlers
+ ** that are part of the module they clean up after.
+ */
+
+ errno = ENOMEM;
+ syserr("!%s", buf);
+}
+
+/*
+** SYSERR -- Print error message.
+**
+** Prints an error message via sm_io_printf to the diagnostic output.
+**
+** If the first character of the syserr message is `!' it will
+** log this as an ALERT message and exit immediately. This can
+** leave queue files in an indeterminate state, so it should not
+** be used lightly.
+**
+** If the first character of the syserr message is '!' or '@'
+** then syserr knows that the process is about to be terminated,
+** so the SMTP reply code defaults to 421. Otherwise, the
+** reply code defaults to 451 or 554, depending on errno.
+**
+** Parameters:
+** fmt -- the format string. An optional '!' or '@',
+** followed by an optional three-digit SMTP
+** reply code, followed by message text.
+** (others) -- parameters
+**
+** Returns:
+** none
+** Raises E:mta.quickabort if QuickAbort is set.
+**
+** Side Effects:
+** increments Errors.
+** sets ExitStat.
+*/
+
+char MsgBuf[BUFSIZ*2]; /* text of most recent message */
+static char HeldMessageBuf[sizeof(MsgBuf)]; /* for held messages */
+
+#if NAMED_BIND && !defined(NO_DATA)
+# define NO_DATA NO_ADDRESS
+#endif /* NAMED_BIND && !defined(NO_DATA) */
+
+void
+/*VARARGS1*/
+#ifdef __STDC__
+syserr(const char *fmt, ...)
+#else /* __STDC__ */
+syserr(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif /* __STDC__ */
+{
+ register char *p;
+ int save_errno = errno;
+ bool panic;
+ bool exiting;
+ char *user;
+ char *enhsc;
+ char *errtxt;
+ struct passwd *pw;
+ char ubuf[80];
+ SM_VA_LOCAL_DECL
+
+ switch (*fmt)
+ {
+ case '!':
+ ++fmt;
+ panic = true;
+ exiting = true;
+ break;
+ case '@':
+ ++fmt;
+ panic = false;
+ exiting = true;
+ break;
+ default:
+ panic = false;
+ exiting = false;
+ break;
+ }
+
+ /* format and output the error message */
+ if (exiting)
+ {
+ /*
+ ** Since we are terminating the process,
+ ** we are aborting the entire SMTP session,
+ ** rather than just the current transaction.
+ */
+
+ p = "421";
+ enhsc = "4.0.0";
+ }
+ else if (save_errno == 0)
+ {
+ p = "554";
+ enhsc = "5.0.0";
+ }
+ else
+ {
+ p = "451";
+ enhsc = "4.0.0";
+ }
+ SM_VA_START(ap, fmt);
+ errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
+ SM_VA_END(ap);
+ puterrmsg(MsgBuf);
+
+ /* save this message for mailq printing */
+ if (!panic && CurEnv != NULL)
+ {
+ char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
+
+ if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
+ sm_free(CurEnv->e_message);
+ CurEnv->e_message = nmsg;
+ }
+
+ /* determine exit status if not already set */
+ if (ExitStat == EX_OK)
+ {
+ if (save_errno == 0)
+ ExitStat = EX_SOFTWARE;
+ else
+ ExitStat = EX_OSERR;
+ if (tTd(54, 1))
+ sm_dprintf("syserr: ExitStat = %d\n", ExitStat);
+ }
+
+ pw = sm_getpwuid(RealUid);
+ if (pw != NULL)
+ user = pw->pw_name;
+ else
+ {
+ user = ubuf;
+ (void) sm_snprintf(ubuf, sizeof(ubuf), "UID%d", (int) RealUid);
+ }
+
+ if (LogLevel > 0)
+ sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
+ CurEnv == NULL ? NOQID : CurEnv->e_id,
+ "SYSERR(%s): %.900s",
+ user, errtxt);
+ switch (save_errno)
+ {
+ case EBADF:
+ case ENFILE:
+ case EMFILE:
+ case ENOTTY:
+#ifdef EFBIG
+ case EFBIG:
+#endif /* EFBIG */
+#ifdef ESPIPE
+ case ESPIPE:
+#endif /* ESPIPE */
+#ifdef EPIPE
+ case EPIPE:
+#endif /* EPIPE */
+#ifdef ENOBUFS
+ case ENOBUFS:
+#endif /* ENOBUFS */
+#ifdef ESTALE
+ case ESTALE:
+#endif /* ESTALE */
+ printopenfds(true);
+ mci_dump_all(smioout, true);
+ break;
+ }
+ if (panic)
+ {
+#if XLA
+ xla_all_end();
+#endif /* XLA */
+ sync_queue_time();
+ if (tTd(0, 1))
+ abort();
+ exit(EX_OSERR);
+ }
+ errno = 0;
+ if (QuickAbort)
+ sm_exc_raisenew_x(&EtypeQuickAbort, 2);
+}
+/*
+** USRERR -- Signal user error.
+**
+** This is much like syserr except it is for user errors.
+**
+** Parameters:
+** fmt -- the format string. If it does not begin with
+** a three-digit SMTP reply code, 550 is assumed.
+** (others) -- sm_io_printf strings
+**
+** Returns:
+** none
+** Raises E:mta.quickabort if QuickAbort is set.
+**
+** Side Effects:
+** increments Errors.
+*/
+
+/*VARARGS1*/
+void
+#ifdef __STDC__
+usrerr(const char *fmt, ...)
+#else /* __STDC__ */
+usrerr(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif /* __STDC__ */
+{
+ char *enhsc;
+ char *errtxt;
+ SM_VA_LOCAL_DECL
+
+ if (fmt[0] == '5' || fmt[0] == '6')
+ enhsc = "5.0.0";
+ else if (fmt[0] == '4' || fmt[0] == '8')
+ enhsc = "4.0.0";
+ else if (fmt[0] == '2')
+ enhsc = "2.0.0";
+ else
+ enhsc = NULL;
+ SM_VA_START(ap, fmt);
+ errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
+ SM_VA_END(ap);
+
+ if (SuprErrs)
+ return;
+
+ /* save this message for mailq printing */
+ switch (MsgBuf[0])
+ {
+ case '4':
+ case '8':
+ if (CurEnv->e_message != NULL)
+ break;
+
+ /* FALLTHROUGH */
+
+ case '5':
+ case '6':
+ if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
+ sm_free(CurEnv->e_message);
+ if (MsgBuf[0] == '6')
+ {
+ char buf[MAXLINE];
+
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Postmaster warning: %.*s",
+ (int) sizeof(buf) - 22, errtxt);
+ CurEnv->e_message =
+ sm_rpool_strdup_x(CurEnv->e_rpool, buf);
+ }
+ else
+ {
+ CurEnv->e_message =
+ sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
+ }
+ break;
+ }
+
+ puterrmsg(MsgBuf);
+ if (LogLevel > 3 && LogUsrErrs)
+ sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
+ if (QuickAbort)
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
+}
+/*
+** USRERRENH -- Signal user error.
+**
+** Same as usrerr but with enhanced status code.
+**
+** Parameters:
+** enhsc -- the enhanced status code.
+** fmt -- the format string. If it does not begin with
+** a three-digit SMTP reply code, 550 is assumed.
+** (others) -- sm_io_printf strings
+**
+** Returns:
+** none
+** Raises E:mta.quickabort if QuickAbort is set.
+**
+** Side Effects:
+** increments Errors.
+*/
+
+/*VARARGS1*/
+void
+#ifdef __STDC__
+usrerrenh(char *enhsc, const char *fmt, ...)
+#else /* __STDC__ */
+usrerrenh(enhsc, fmt, va_alist)
+ char *enhsc;
+ const char *fmt;
+ va_dcl
+#endif /* __STDC__ */
+{
+ char *errtxt;
+ SM_VA_LOCAL_DECL
+
+ if (enhsc == NULL || *enhsc == '\0')
+ {
+ if (fmt[0] == '5' || fmt[0] == '6')
+ enhsc = "5.0.0";
+ else if (fmt[0] == '4' || fmt[0] == '8')
+ enhsc = "4.0.0";
+ else if (fmt[0] == '2')
+ enhsc = "2.0.0";
+ }
+ SM_VA_START(ap, fmt);
+ errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
+ SM_VA_END(ap);
+
+ if (SuprErrs)
+ return;
+
+ /* save this message for mailq printing */
+ switch (MsgBuf[0])
+ {
+ case '4':
+ case '8':
+ if (CurEnv->e_message != NULL)
+ break;
+
+ /* FALLTHROUGH */
+
+ case '5':
+ case '6':
+ if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
+ sm_free(CurEnv->e_message);
+ if (MsgBuf[0] == '6')
+ {
+ char buf[MAXLINE];
+
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Postmaster warning: %.*s",
+ (int) sizeof(buf) - 22, errtxt);
+ CurEnv->e_message =
+ sm_rpool_strdup_x(CurEnv->e_rpool, buf);
+ }
+ else
+ {
+ CurEnv->e_message =
+ sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
+ }
+ break;
+ }
+
+ puterrmsg(MsgBuf);
+ if (LogLevel > 3 && LogUsrErrs)
+ sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
+ if (QuickAbort)
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
+}
+/*
+** MESSAGE -- print message (not necessarily an error)
+**
+** Parameters:
+** msg -- the message (sm_io_printf fmt) -- it can begin with
+** an SMTP reply code. If not, 050 is assumed.
+** (others) -- sm_io_printf arguments
+**
+** Returns:
+** none
+**
+** Side Effects:
+** none.
+*/
+
+/*VARARGS1*/
+void
+#ifdef __STDC__
+message(const char *msg, ...)
+#else /* __STDC__ */
+message(msg, va_alist)
+ const char *msg;
+ va_dcl
+#endif /* __STDC__ */
+{
+ char *errtxt;
+ SM_VA_LOCAL_DECL
+
+ errno = 0;
+ SM_VA_START(ap, msg);
+ errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
+ SM_VA_END(ap);
+ putoutmsg(MsgBuf, false, false);
+
+ /* save this message for mailq printing */
+ switch (MsgBuf[0])
+ {
+ case '4':
+ case '8':
+ if (CurEnv->e_message != NULL)
+ break;
+ /* FALLTHROUGH */
+
+ case '5':
+ if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
+ sm_free(CurEnv->e_message);
+ CurEnv->e_message =
+ sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
+ break;
+ }
+}
+/*
+** NMESSAGE -- print message (not necessarily an error)
+**
+** Just like "message" except it never puts the to... tag on.
+**
+** Parameters:
+** msg -- the message (sm_io_printf fmt) -- if it begins
+** with a three digit SMTP reply code, that is used,
+** otherwise 050 is assumed.
+** (others) -- sm_io_printf arguments
+**
+** Returns:
+** none
+**
+** Side Effects:
+** none.
+*/
+
+/*VARARGS1*/
+void
+#ifdef __STDC__
+nmessage(const char *msg, ...)
+#else /* __STDC__ */
+nmessage(msg, va_alist)
+ const char *msg;
+ va_dcl
+#endif /* __STDC__ */
+{
+ char *errtxt;
+ SM_VA_LOCAL_DECL
+
+ errno = 0;
+ SM_VA_START(ap, msg);
+ errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
+ (char *) NULL, 0, msg, ap);
+ SM_VA_END(ap);
+ putoutmsg(MsgBuf, false, false);
+
+ /* save this message for mailq printing */
+ switch (MsgBuf[0])
+ {
+ case '4':
+ case '8':
+ if (CurEnv->e_message != NULL)
+ break;
+ /* FALLTHROUGH */
+
+ case '5':
+ if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
+ sm_free(CurEnv->e_message);
+ CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
+ break;
+ }
+}
+/*
+** PUTOUTMSG -- output error message to transcript and channel
+**
+** Parameters:
+** msg -- message to output (in SMTP format).
+** holdmsg -- if true, don't output a copy of the message to
+** our output channel.
+** heldmsg -- if true, this is a previously held message;
+** don't log it to the transcript file.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Outputs msg to the transcript.
+** If appropriate, outputs it to the channel.
+** Deletes SMTP reply code number as appropriate.
+*/
+
+static void
+putoutmsg(msg, holdmsg, heldmsg)
+ char *msg;
+ bool holdmsg;
+ bool heldmsg;
+{
+ char msgcode = msg[0];
+ char *errtxt = msg;
+ char *id;
+
+ /* display for debugging */
+ if (tTd(54, 8))
+ sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
+ heldmsg ? " (held)" : "");
+
+ /* map warnings to something SMTP can handle */
+ if (msgcode == '6')
+ msg[0] = '5';
+ else if (msgcode == '8')
+ msg[0] = '4';
+ id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
+
+ /* output to transcript if serious */
+ if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
+ strchr("45", msg[0]) != NULL)
+ (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
+ msg);
+
+ if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
+ sm_syslog(LOG_INFO, id,
+ "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
+ heldmsg ? " (held)" : "");
+
+ if (msgcode == '8')
+ msg[0] = '0';
+
+ /* output to channel if appropriate */
+ if (!Verbose && msg[0] == '0')
+ return;
+ if (holdmsg)
+ {
+ /* save for possible future display */
+ msg[0] = msgcode;
+ if (HeldMessageBuf[0] == '5' && msgcode == '4')
+ return;
+ (void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
+ return;
+ }
+
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
+
+ if (OutChannel == NULL)
+ return;
+
+ /* find actual text of error (after SMTP status codes) */
+ if (ISSMTPREPLY(errtxt))
+ {
+ int l;
+
+ errtxt += 4;
+ l = isenhsc(errtxt, ' ');
+ if (l <= 0)
+ l = isenhsc(errtxt, '\0');
+ if (l > 0)
+ errtxt += l + 1;
+ }
+
+ /* if DisConnected, OutChannel now points to the transcript */
+ if (!DisConnected &&
+ (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
+ (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
+ msg);
+ else
+ (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
+ errtxt);
+ if (TrafficLogFile != NULL)
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d >>> %s\n", (int) CurrentPid,
+ (OpMode == MD_SMTP || OpMode == MD_DAEMON)
+ ? msg : errtxt);
+#if !PIPELINING
+ /* XXX can't flush here for SMTP pipelining */
+ if (msg[3] == ' ')
+ (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
+ if (!sm_io_error(OutChannel) || DisConnected)
+ return;
+
+ /*
+ ** Error on output -- if reporting lost channel, just ignore it.
+ ** Also, ignore errors from QUIT response (221 message) -- some
+ ** rude servers don't read result.
+ */
+
+ if (InChannel == NULL || sm_io_eof(InChannel) ||
+ sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
+ return;
+
+ /* can't call syserr, 'cause we are using MsgBuf */
+ HoldErrs = true;
+ if (LogLevel > 0)
+ sm_syslog(LOG_CRIT, id,
+ "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
+ CURHOSTNAME,
+ shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
+#endif /* !PIPELINING */
+}
+/*
+** PUTERRMSG -- like putoutmsg, but does special processing for error messages
+**
+** Parameters:
+** msg -- the message to output.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets the fatal error bit in the envelope as appropriate.
+*/
+
+static void
+puterrmsg(msg)
+ char *msg;
+{
+ char msgcode = msg[0];
+
+ /* output the message as usual */
+ putoutmsg(msg, HoldErrs, false);
+
+ /* be careful about multiple error messages */
+ if (OnlyOneError)
+ HoldErrs = true;
+
+ /* signal the error */
+ Errors++;
+
+ if (CurEnv == NULL)
+ return;
+
+ if (msgcode == '6')
+ {
+ /* notify the postmaster */
+ CurEnv->e_flags |= EF_PM_NOTIFY;
+ }
+ else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
+ {
+ /* mark long-term fatal errors */
+ CurEnv->e_flags |= EF_FATALERRS;
+ }
+}
+/*
+** ISENHSC -- check whether a string contains an enhanced status code
+**
+** Parameters:
+** s -- string with possible enhanced status code.
+** delim -- delim for enhanced status code.
+**
+** Returns:
+** 0 -- no enhanced status code.
+** >4 -- length of enhanced status code.
+**
+** Side Effects:
+** none.
+*/
+int
+isenhsc(s, delim)
+ const char *s;
+ int delim;
+{
+ int l, h;
+
+ if (s == NULL)
+ return 0;
+ if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
+ return 0;
+ h = 0;
+ l = 2;
+ while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
+ ++h;
+ if (h == 0 || s[l + h] != '.')
+ return 0;
+ l += h + 1;
+ h = 0;
+ while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
+ ++h;
+ if (h == 0 || s[l + h] != delim)
+ return 0;
+ return l + h;
+}
+/*
+** EXTENHSC -- check and extract an enhanced status code
+**
+** Parameters:
+** s -- string with possible enhanced status code.
+** delim -- delim for enhanced status code.
+** e -- pointer to storage for enhanced status code.
+** must be != NULL and have space for at least
+** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
+**
+** Returns:
+** 0 -- no enhanced status code.
+** >4 -- length of enhanced status code.
+**
+** Side Effects:
+** fills e with enhanced status code.
+*/
+
+int
+extenhsc(s, delim, e)
+ const char *s;
+ int delim;
+ char *e;
+{
+ int l, h;
+
+ if (s == NULL)
+ return 0;
+ if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
+ return 0;
+ h = 0;
+ l = 2;
+ e[0] = s[0];
+ e[1] = '.';
+ while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
+ {
+ e[l + h] = s[l + h];
+ ++h;
+ }
+ if (h == 0 || s[l + h] != '.')
+ return 0;
+ e[l + h] = '.';
+ l += h + 1;
+ h = 0;
+ while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
+ {
+ e[l + h] = s[l + h];
+ ++h;
+ }
+ if (h == 0 || s[l + h] != delim)
+ return 0;
+ e[l + h] = '\0';
+ return l + h;
+}
+/*
+** FMTMSG -- format a message into buffer.
+**
+** Parameters:
+** eb -- error buffer to get result -- MUST BE MsgBuf.
+** to -- the recipient tag for this message.
+** num -- default three digit SMTP reply code.
+** enhsc -- enhanced status code.
+** en -- the error number to display.
+** fmt -- format of string.
+** ap -- arguments for fmt.
+**
+** Returns:
+** pointer to error text beyond status codes.
+**
+** Side Effects:
+** none.
+*/
+
+static char *
+fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
+ register char *eb;
+ const char *to;
+ const char *num;
+ const char *enhsc;
+ int eno;
+ const char *fmt;
+ SM_VA_LOCAL_DECL
+{
+ char del;
+ int l;
+ int spaceleft = sizeof(MsgBuf);
+ char *errtxt;
+
+ /* output the reply code */
+ if (ISSMTPCODE(fmt))
+ {
+ num = fmt;
+ fmt += 4;
+ }
+ if (num[3] == '-')
+ del = '-';
+ else
+ del = ' ';
+ if (SoftBounce && num[0] == '5')
+ {
+ /* replace 5 by 4 */
+ (void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
+ }
+ else
+ (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
+ eb += 4;
+ spaceleft -= 4;
+
+ if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
+ {
+ /* copy enh.status code including trailing blank */
+ l++;
+ (void) sm_strlcpy(eb, fmt, l + 1);
+ eb += l;
+ spaceleft -= l;
+ fmt += l;
+ }
+ else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
+ {
+ /* copy enh.status code */
+ (void) sm_strlcpy(eb, enhsc, l + 1);
+ eb[l] = ' ';
+ eb[++l] = '\0';
+ eb += l;
+ spaceleft -= l;
+ }
+ if (SoftBounce && eb[-l] == '5')
+ {
+ /* replace 5 by 4 */
+ eb[-l] = '4';
+ }
+ errtxt = eb;
+
+ /* output the file name and line number */
+ if (FileName != NULL)
+ {
+ (void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
+ shortenstring(FileName, 83), LineNumber);
+ eb += (l = strlen(eb));
+ spaceleft -= l;
+ }
+
+ /*
+ ** output the "to" address only if it is defined and one of the
+ ** following codes is used:
+ ** 050 internal notices, e.g., alias expansion
+ ** 250 Ok
+ ** 252 Cannot VRFY user, but will accept message and attempt delivery
+ ** 450 Requested mail action not taken: mailbox unavailable
+ ** 550 Requested action not taken: mailbox unavailable
+ ** 553 Requested action not taken: mailbox name not allowed
+ **
+ ** Notice: this still isn't "the right thing", this code shouldn't
+ ** (indirectly) depend on CurEnv->e_to.
+ */
+
+ if (to != NULL && to[0] != '\0' &&
+ (strncmp(num, "050", 3) == 0 ||
+ strncmp(num, "250", 3) == 0 ||
+ strncmp(num, "252", 3) == 0 ||
+ strncmp(num, "450", 3) == 0 ||
+ strncmp(num, "550", 3) == 0 ||
+ strncmp(num, "553", 3) == 0))
+ {
+ (void) sm_strlcpyn(eb, spaceleft, 2,
+ shortenstring(to, MAXSHORTSTR), "... ");
+ spaceleft -= strlen(eb);
+ while (*eb != '\0')
+ *eb++ &= 0177;
+ }
+
+ /* output the message */
+ (void) sm_vsnprintf(eb, spaceleft, fmt, ap);
+ spaceleft -= strlen(eb);
+ while (*eb != '\0')
+ *eb++ &= 0177;
+
+ /* output the error code, if any */
+ if (eno != 0)
+ (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
+
+ return errtxt;
+}
+/*
+** BUFFER_ERRORS -- arrange to buffer future error messages
+**
+** Parameters:
+** none
+**
+** Returns:
+** none.
+*/
+
+void
+buffer_errors()
+{
+ HeldMessageBuf[0] = '\0';
+ HoldErrs = true;
+}
+/*
+** FLUSH_ERRORS -- flush the held error message buffer
+**
+** Parameters:
+** print -- if set, print the message, otherwise just
+** delete it.
+**
+** Returns:
+** none.
+*/
+
+void
+flush_errors(print)
+ bool print;
+{
+ if (print && HeldMessageBuf[0] != '\0')
+ putoutmsg(HeldMessageBuf, false, true);
+ HeldMessageBuf[0] = '\0';
+ HoldErrs = false;
+}
+/*
+** SM_ERRSTRING -- return string description of error code
+**
+** Parameters:
+** errnum -- the error number to translate
+**
+** Returns:
+** A string description of errnum.
+**
+** Side Effects:
+** none.
+*/
+
+const char *
+sm_errstring(errnum)
+ int errnum;
+{
+ char *dnsmsg;
+ char *bp;
+ static char buf[MAXLINE];
+#if HASSTRERROR
+ char *err;
+ char errbuf[30];
+#endif /* HASSTRERROR */
+#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
+
+ /*
+ ** Handle special network error codes.
+ **
+ ** These are 4.2/4.3bsd specific; they should be in daemon.c.
+ */
+
+ dnsmsg = NULL;
+ switch (errnum)
+ {
+ case ETIMEDOUT:
+ case ECONNRESET:
+ bp = buf;
+#if HASSTRERROR
+ err = strerror(errnum);
+ if (err == NULL)
+ {
+ (void) sm_snprintf(errbuf, sizeof(errbuf),
+ "Error %d", errnum);
+ err = errbuf;
+ }
+ (void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
+#else /* HASSTRERROR */
+ if (errnum >= 0 && errnum < sys_nerr)
+ (void) sm_strlcpy(bp, sys_errlist[errnum],
+ SPACELEFT(buf, bp));
+ else
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp),
+ "Error %d", errnum);
+#endif /* HASSTRERROR */
+ bp += strlen(bp);
+ if (CurHostName != NULL)
+ {
+ if (errnum == ETIMEDOUT)
+ {
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp),
+ " with ");
+ bp += strlen(bp);
+ }
+ else
+ {
+ bp = buf;
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp),
+ "Connection reset by ");
+ bp += strlen(bp);
+ }
+ (void) sm_strlcpy(bp,
+ shortenstring(CurHostName, MAXSHORTSTR),
+ SPACELEFT(buf, bp));
+ bp += strlen(buf);
+ }
+ if (SmtpPhase != NULL)
+ {
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp),
+ " during %s", SmtpPhase);
+ }
+ return buf;
+
+ case EHOSTDOWN:
+ if (CurHostName == NULL)
+ break;
+ (void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
+ shortenstring(CurHostName, MAXSHORTSTR));
+ return buf;
+
+ case ECONNREFUSED:
+ if (CurHostName == NULL)
+ break;
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
+ shortenstring(CurHostName, MAXSHORTSTR));
+ return buf;
+
+#if NAMED_BIND
+ case HOST_NOT_FOUND + E_DNSBASE:
+ dnsmsg = "host not found";
+ break;
+
+ case TRY_AGAIN + E_DNSBASE:
+ dnsmsg = "host name lookup failure";
+ break;
+
+ case NO_RECOVERY + E_DNSBASE:
+ dnsmsg = "non-recoverable error";
+ break;
+
+ case NO_DATA + E_DNSBASE:
+ dnsmsg = "no data known";
+ break;
+#endif /* NAMED_BIND */
+
+ case EPERM:
+ /* SunOS gives "Not owner" -- this is the POSIX message */
+ return "Operation not permitted";
+
+ /*
+ ** Error messages used internally in sendmail.
+ */
+
+ case E_SM_OPENTIMEOUT:
+ return "Timeout on file open";
+
+ case E_SM_NOSLINK:
+ return "Symbolic links not allowed";
+
+ case E_SM_NOHLINK:
+ return "Hard links not allowed";
+
+ case E_SM_REGONLY:
+ return "Regular files only";
+
+ case E_SM_ISEXEC:
+ return "Executable files not allowed";
+
+ case E_SM_WWDIR:
+ return "World writable directory";
+
+ case E_SM_GWDIR:
+ return "Group writable directory";
+
+ case E_SM_FILECHANGE:
+ return "File changed after open";
+
+ case E_SM_WWFILE:
+ return "World writable file";
+
+ case E_SM_GWFILE:
+ return "Group writable file";
+
+ case E_SM_GRFILE:
+ return "Group readable file";
+
+ case E_SM_WRFILE:
+ return "World readable file";
+ }
+
+ if (dnsmsg != NULL)
+ {
+ bp = buf;
+ bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
+ if (CurHostName != NULL)
+ {
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
+ shortenstring(CurHostName, MAXSHORTSTR), ": ");
+ bp += strlen(bp);
+ }
+ (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
+ return buf;
+ }
+
+#if LDAPMAP
+ if (errnum >= E_LDAPBASE)
+ return ldap_err2string(errnum - E_LDAPBASE);
+#endif /* LDAPMAP */
+
+#if HASSTRERROR
+ err = strerror(errnum);
+ if (err == NULL)
+ {
+ (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
+ return buf;
+ }
+ return err;
+#else /* HASSTRERROR */
+ if (errnum > 0 && errnum < sys_nerr)
+ return sys_errlist[errnum];
+
+ (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
+ return buf;
+#endif /* HASSTRERROR */
+}
OpenPOWER on IntegriCloud