/*
* 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.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sendmail.h>
#if LDAPMAP
# include <lber.h>
# include <ldap.h> /* for LDAP error codes */
#endif /* LDAPMAP */
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
{
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_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.
*/
}
/*
** 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.
*/
#if NAMED_BIND && !defined(NO_DATA)
#endif /* NAMED_BIND && !defined(NO_DATA) */
void
/*VARARGS1*/
#ifdef __STDC__
#else /* __STDC__ */
const char *fmt;
#endif /* __STDC__ */
{
register char *p;
bool panic;
bool exiting;
char *user;
char *enhsc;
char *errtxt;
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";
}
/* save this message for mailq printing */
{
}
/* determine exit status if not already set */
{
if (save_errno == 0)
else
}
else
{
}
if (LogLevel > 0)
"SYSERR(%s): %.900s",
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 */
if (tTd(0, 1))
abort();
}
errno = 0;
if (QuickAbort)
}
/*
** 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__
#else /* __STDC__ */
const char *fmt;
#endif /* __STDC__ */
{
char *enhsc;
char *errtxt;
enhsc = "4.0.0";
else if (fmt[0] == '2')
enhsc = "2.0.0";
else
if (SuprErrs)
return;
/* save this message for mailq printing */
switch (MsgBuf[0])
{
case '4':
case '8':
break;
/* FALLTHROUGH */
case '5':
case '6':
if (MsgBuf[0] == '6')
{
"Postmaster warning: %.*s",
}
else
{
}
break;
}
if (QuickAbort)
}
/*
** 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__
#else /* __STDC__ */
char *enhsc;
const char *fmt;
#endif /* __STDC__ */
{
char *errtxt;
{
enhsc = "5.0.0";
enhsc = "4.0.0";
else if (fmt[0] == '2')
enhsc = "2.0.0";
}
if (SuprErrs)
return;
/* save this message for mailq printing */
switch (MsgBuf[0])
{
case '4':
case '8':
break;
/* FALLTHROUGH */
case '5':
case '6':
if (MsgBuf[0] == '6')
{
"Postmaster warning: %.*s",
}
else
{
}
break;
}
if (QuickAbort)
}
/*
** 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__
#else /* __STDC__ */
const char *msg;
#endif /* __STDC__ */
{
char *errtxt;
errno = 0;
/* save this message for mailq printing */
switch (MsgBuf[0])
{
case '4':
case '8':
break;
/* FALLTHROUGH */
case '5':
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__
#else /* __STDC__ */
const char *msg;
#endif /* __STDC__ */
{
char *errtxt;
errno = 0;
/* save this message for mailq printing */
switch (MsgBuf[0])
{
case '4':
case '8':
break;
/* FALLTHROUGH */
case '5':
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
char *msg;
bool holdmsg;
bool heldmsg;
{
char *id;
/* display for debugging */
/* 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 */
msg);
if (msgcode == '8')
msg[0] = '0';
/* output to channel if appropriate */
return;
if (holdmsg)
{
/* save for possible future display */
return;
return;
}
if (OutChannel == NULL)
return;
/* find actual text of error (after SMTP status codes) */
if (ISSMTPREPLY(errtxt))
{
int l;
errtxt += 4;
if (l <= 0)
if (l > 0)
errtxt += l + 1;
}
/* if DisConnected, OutChannel now points to the transcript */
if (!DisConnected &&
msg);
else
errtxt);
if (TrafficLogFile != NULL)
"%05d >>> %s\n", (int) CurrentPid,
#if !PIPELINING
/* XXX can't flush here for SMTP pipelining */
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.
*/
return;
/* can't call syserr, 'cause we are using MsgBuf */
HoldErrs = true;
if (LogLevel > 0)
"SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
#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
char *msg;
{
/* output the message as usual */
/* be careful about multiple error messages */
if (OnlyOneError)
HoldErrs = true;
/* signal the error */
Errors++;
return;
if (msgcode == '6')
{
/* notify the postmaster */
}
{
/* mark long-term fatal errors */
}
}
/*
** 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
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;
++h;
if (h == 0 || s[l + h] != '.')
return 0;
l += h + 1;
h = 0;
++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
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] = '.';
{
e[l + h] = s[l + h];
++h;
}
if (h == 0 || s[l + h] != '.')
return 0;
e[l + h] = '.';
l += h + 1;
h = 0;
{
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 *
register char *eb;
const char *to;
const char *num;
const char *enhsc;
int eno;
const char *fmt;
{
char del;
int l;
char *errtxt;
/* output the reply code */
if (ISSMTPCODE(fmt))
{
fmt += 4;
}
del = '-';
else
del = ' ';
{
/* replace 5 by 4 */
}
else
eb += 4;
spaceleft -= 4;
{
/* copy enh.status code including trailing blank */
l++;
eb += l;
spaceleft -= l;
fmt += l;
}
{
/* copy enh.status code */
eb[l] = ' ';
eb[++l] = '\0';
eb += l;
spaceleft -= l;
}
{
/* replace 5 by 4 */
eb[-l] = '4';
}
/* output the file name and line number */
{
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.
*/
{
while (*eb != '\0')
*eb++ &= 0177;
}
/* output the message */
while (*eb != '\0')
*eb++ &= 0177;
/* output the error code, if any */
if (eno != 0)
return errtxt;
}
/*
** BUFFER_ERRORS -- arrange to buffer future error messages
**
** Parameters:
** none
**
** Returns:
** none.
*/
void
{
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
bool print;
{
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 *
int errnum;
{
char *dnsmsg;
char *bp;
#if HASSTRERROR
char *err;
#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.
*/
switch (errnum)
{
case ETIMEDOUT:
case ECONNRESET:
#if HASSTRERROR
{
"Error %d", errnum);
}
#else /* HASSTRERROR */
else
"Error %d", errnum);
#endif /* HASSTRERROR */
if (CurHostName != NULL)
{
{
" with ");
}
else
{
"Connection reset by ");
}
(void) sm_strlcpy(bp,
}
{
" during %s", SmtpPhase);
}
return buf;
case EHOSTDOWN:
if (CurHostName == NULL)
break;
return buf;
case ECONNREFUSED:
if (CurHostName == NULL)
break;
return buf;
#if NAMED_BIND
case HOST_NOT_FOUND + E_DNSBASE:
dnsmsg = "host not found";
break;
dnsmsg = "host name lookup failure";
break;
case NO_RECOVERY + E_DNSBASE:
dnsmsg = "non-recoverable error";
break;
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 (CurHostName != NULL)
{
}
return buf;
}
#if LDAPMAP
if (errnum >= E_LDAPBASE)
#endif /* LDAPMAP */
#if HASSTRERROR
{
return buf;
}
return err;
#else /* HASSTRERROR */
return sys_errlist[errnum];
return buf;
#endif /* HASSTRERROR */
}