mirror of
https://github.com/opnsense/src.git
synced 2026-03-18 00:25:50 -04:00
1161 lines
24 KiB
C
1161 lines
24 KiB
C
/*
|
|
* 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.191 2003/01/10 02:16:46 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 *errtxt = msg;
|
|
char msgcode = msg[0];
|
|
|
|
/* 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';
|
|
|
|
/* 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, CurEnv->e_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, CurEnv->e_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 _FFR_SOFT_BOUNCE
|
|
if (SoftBounce && num[0] == '5')
|
|
{
|
|
/* replace 5 by 4 */
|
|
(void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
|
|
}
|
|
else
|
|
#endif /* _FFR_SOFT_BOUNCE */
|
|
(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 _FFR_SOFT_BOUNCE
|
|
if (SoftBounce && eb[-l] == '5')
|
|
{
|
|
/* replace 5 by 4 */
|
|
eb[-l] = '4';
|
|
}
|
|
#endif /* _FFR_SOFT_BOUNCE */
|
|
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 */
|
|
}
|