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