freebsd-nq/contrib/sendmail/src/err.c
Peter Wemm 76b7bf7135 Merge sendmail 8.9.1 -> 8.9.2 changes into mainline. Some of our changes
were submitted back to sendmail.org (stage 1) and were incorporated.
1999-01-12 12:38:06 +00:00

768 lines
15 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 1998 Sendmail, Inc. 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.
*
*/
#ifndef lint
static char sccsid[] = "@(#)err.c 8.74 (Berkeley) 6/4/1998";
#endif /* not lint */
# include "sendmail.h"
# include <errno.h>
/*
** SYSERR -- Print error message.
**
** Prints an error message via 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.
**
** Parameters:
** fmt -- the format string. If it does not begin with
** a three-digit SMTP reply code, either 554 or
** 451 is assumed depending on whether errno
** is set.
** (others) -- parameters
**
** Returns:
** none
** Through TopFrame if QuickAbort is set.
**
** Side Effects:
** increments Errors.
** sets ExitStat.
*/
char MsgBuf[BUFSIZ*2]; /* text of most recent message */
char HeldMessageBuf[sizeof MsgBuf]; /* for held messages */
extern void putoutmsg __P((char *, bool, bool));
extern void puterrmsg __P((char *));
static void fmtmsg __P((char *, const char *, const char *, int, const char *, va_list));
#if NAMED_BIND && !defined(NO_DATA)
# define NO_DATA NO_ADDRESS
#endif
void
/*VARARGS1*/
#ifdef __STDC__
syserr(const char *fmt, ...)
#else
syserr(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
register char *p;
int olderrno = errno;
bool panic;
char *uname;
struct passwd *pw;
char ubuf[80];
VA_LOCAL_DECL
panic = *fmt == '!';
if (panic)
{
fmt++;
HoldErrs = FALSE;
}
/* format and output the error message */
if (olderrno == 0)
p = "554";
else
p = "451";
VA_START(fmt);
fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
VA_END;
puterrmsg(MsgBuf);
/* save this message for mailq printing */
if (!panic && CurEnv != NULL)
{
if (CurEnv->e_message != NULL)
free(CurEnv->e_message);
CurEnv->e_message = newstr(MsgBuf + 4);
}
/* determine exit status if not already set */
if (ExitStat == EX_OK)
{
if (olderrno == 0)
ExitStat = EX_SOFTWARE;
else
ExitStat = EX_OSERR;
if (tTd(54, 1))
printf("syserr: ExitStat = %d\n", ExitStat);
}
pw = sm_getpwuid(getuid());
if (pw != NULL)
uname = pw->pw_name;
else
{
uname = ubuf;
snprintf(ubuf, sizeof ubuf, "UID%d", getuid());
}
if (LogLevel > 0)
sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
CurEnv == NULL ? NOQID : CurEnv->e_id,
"SYSERR(%s): %.900s",
uname, &MsgBuf[4]);
switch (olderrno)
{
case EBADF:
case ENFILE:
case EMFILE:
case ENOTTY:
#ifdef EFBIG
case EFBIG:
#endif
#ifdef ESPIPE
case ESPIPE:
#endif
#ifdef EPIPE
case EPIPE:
#endif
#ifdef ENOBUFS
case ENOBUFS:
#endif
#ifdef ESTALE
case ESTALE:
#endif
printopenfds(TRUE);
mci_dump_all(TRUE);
break;
}
if (panic)
{
#ifdef XLA
xla_all_end();
#endif
if (tTd(0, 1))
abort();
exit(EX_OSERR);
}
errno = 0;
if (QuickAbort)
longjmp(TopFrame, 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, 501 is assumed.
** (others) -- printf strings
**
** Returns:
** none
** Through TopFrame if QuickAbort is set.
**
** Side Effects:
** increments Errors.
*/
/*VARARGS1*/
void
#ifdef __STDC__
usrerr(const char *fmt, ...)
#else
usrerr(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
VA_LOCAL_DECL
if (SuprErrs)
return;
VA_START(fmt);
fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
VA_END;
/* save this message for mailq printing */
switch (MsgBuf[0])
{
case '4':
case '8':
if (CurEnv->e_message != NULL)
break;
/* fall through.... */
case '5':
case '6':
if (CurEnv->e_message != NULL)
free(CurEnv->e_message);
if (MsgBuf[0] == '6')
{
char buf[MAXLINE];
snprintf(buf, sizeof buf, "Postmaster warning: %.*s",
(int)sizeof buf - 22, MsgBuf + 4);
CurEnv->e_message = newstr(buf);
}
else
{
CurEnv->e_message = newstr(MsgBuf + 4);
}
break;
}
puterrmsg(MsgBuf);
if (LogLevel > 3 && LogUsrErrs)
sm_syslog(LOG_NOTICE, CurEnv->e_id,
"%.900s",
&MsgBuf[4]);
if (QuickAbort)
longjmp(TopFrame, 1);
}
/*
** MESSAGE -- print message (not necessarily an error)
**
** Parameters:
** msg -- the message (printf fmt) -- it can begin with
** an SMTP reply code. If not, 050 is assumed.
** (others) -- printf arguments
**
** Returns:
** none
**
** Side Effects:
** none.
*/
/*VARARGS1*/
void
#ifdef __STDC__
message(const char *msg, ...)
#else
message(msg, va_alist)
const char *msg;
va_dcl
#endif
{
VA_LOCAL_DECL
errno = 0;
VA_START(msg);
fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
VA_END;
putoutmsg(MsgBuf, FALSE, FALSE);
/* save this message for mailq printing */
switch (MsgBuf[0])
{
case '4':
case '8':
if (CurEnv->e_message != NULL)
break;
/* fall through.... */
case '5':
if (CurEnv->e_message != NULL)
free(CurEnv->e_message);
CurEnv->e_message = newstr(MsgBuf + 4);
break;
}
}
/*
** NMESSAGE -- print message (not necessarily an error)
**
** Just like "message" except it never puts the to... tag on.
**
** Parameters:
** msg -- the message (printf fmt) -- if it begins
** with a three digit SMTP reply code, that is used,
** otherwise 050 is assumed.
** (others) -- printf arguments
**
** Returns:
** none
**
** Side Effects:
** none.
*/
/*VARARGS1*/
void
#ifdef __STDC__
nmessage(const char *msg, ...)
#else
nmessage(msg, va_alist)
const char *msg;
va_dcl
#endif
{
VA_LOCAL_DECL
errno = 0;
VA_START(msg);
fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
VA_END;
putoutmsg(MsgBuf, FALSE, FALSE);
/* save this message for mailq printing */
switch (MsgBuf[0])
{
case '4':
case '8':
if (CurEnv->e_message != NULL)
break;
/* fall through.... */
case '5':
if (CurEnv->e_message != NULL)
free(CurEnv->e_message);
CurEnv->e_message = newstr(MsgBuf + 4);
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.
*/
void
putoutmsg(msg, holdmsg, heldmsg)
char *msg;
bool holdmsg;
bool heldmsg;
{
char msgcode = msg[0];
/* display for debugging */
if (tTd(54, 8))
printf("--- %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)
fprintf(CurEnv->e_xfp, "%s\n", msg);
if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
sm_syslog(LOG_INFO, CurEnv->e_id,
"--> %s%s",
msg, holdmsg ? " (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;
snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg);
return;
}
(void) fflush(stdout);
if (OutChannel == NULL)
return;
/* if DisConnected, OutChannel now points to the transcript */
if (!DisConnected &&
(OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
fprintf(OutChannel, "%s\r\n", msg);
else
fprintf(OutChannel, "%s\n", &msg[4]);
if (TrafficLogFile != NULL)
fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(),
(OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
if (msg[3] == ' ')
(void) fflush(OutChannel);
if (!ferror(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 || feof(InChannel) || ferror(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 == NULL ? "NO-HOST" : CurHostName,
shortenstring(msg, MAXSHORTSTR), errstring(errno));
}
/*
** 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.
*/
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;
}
}
/*
** FMTMSG -- format a message into buffer.
**
** Parameters:
** eb -- error buffer to get result.
** to -- the recipient tag for this message.
** num -- arpanet error number.
** en -- the error number to display.
** fmt -- format of string.
** a, b, c, d, e -- arguments.
**
** Returns:
** none.
**
** Side Effects:
** none.
*/
static void
fmtmsg(eb, to, num, eno, fmt, ap)
register char *eb;
const char *to;
const char *num;
int eno;
const char *fmt;
va_list ap;
{
char del;
int l;
int spaceleft = sizeof MsgBuf;
/* output the reply code */
if (isascii(fmt[0]) && isdigit(fmt[0]) &&
isascii(fmt[1]) && isdigit(fmt[1]) &&
isascii(fmt[2]) && isdigit(fmt[2]))
{
num = fmt;
fmt += 4;
}
if (num[3] == '-')
del = '-';
else
del = ' ';
(void) snprintf(eb, spaceleft, "%3.3s%c", num, del);
eb += 4;
spaceleft -= 4;
/* output the file name and line number */
if (FileName != NULL)
{
(void) snprintf(eb, spaceleft, "%s: line %d: ",
shortenstring(FileName, 83), LineNumber);
eb += (l = strlen(eb));
spaceleft -= l;
}
/* output the "to" person */
if (to != NULL && to[0] != '\0' &&
strncmp(num, "551", 3) != 0 &&
strncmp(num, "251", 3) != 0)
{
(void) snprintf(eb, spaceleft, "%s... ",
shortenstring(to, MAXSHORTSTR));
spaceleft -= strlen(eb);
while (*eb != '\0')
*eb++ &= 0177;
}
/* output the message */
(void) vsnprintf(eb, spaceleft, fmt, ap);
spaceleft -= strlen(eb);
while (*eb != '\0')
*eb++ &= 0177;
/* output the error code, if any */
if (eno != 0)
(void) snprintf(eb, spaceleft, ": %s", errstring(eno));
}
/*
** 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;
}
/*
** 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 *
errstring(errnum)
int errnum;
{
char *dnsmsg;
char *bp;
static char buf[MAXLINE];
# if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
extern char *sys_errlist[];
extern int sys_nerr;
# endif
# if SMTP
extern char *SmtpPhase;
# endif /* SMTP */
/*
** Handle special network error codes.
**
** These are 4.2/4.3bsd specific; they should be in daemon.c.
*/
dnsmsg = NULL;
switch (errnum)
{
# if defined(DAEMON) && defined(ETIMEDOUT)
case ETIMEDOUT:
case ECONNRESET:
bp = buf;
#if HASSTRERROR
snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum));
#else
if (errnum >= 0 && errnum < sys_nerr)
snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]);
else
snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum);
#endif
bp += strlen(bp);
if (CurHostName != NULL)
{
if (errnum == ETIMEDOUT)
{
snprintf(bp, SPACELEFT(buf, bp), " with ");
bp += strlen(bp);
}
else
{
bp = buf;
snprintf(bp, SPACELEFT(buf, bp),
"Connection reset by ");
bp += strlen(bp);
}
snprintf(bp, SPACELEFT(buf, bp), "%s",
shortenstring(CurHostName, MAXSHORTSTR));
bp += strlen(buf);
}
if (SmtpPhase != NULL)
{
snprintf(bp, SPACELEFT(buf, bp), " during %s",
SmtpPhase);
}
return (buf);
case EHOSTDOWN:
if (CurHostName == NULL)
break;
(void) snprintf(buf, sizeof buf, "Host %s is down",
shortenstring(CurHostName, MAXSHORTSTR));
return (buf);
case ECONNREFUSED:
if (CurHostName == NULL)
break;
(void) snprintf(buf, sizeof buf, "Connection refused by %s",
shortenstring(CurHostName, MAXSHORTSTR));
return (buf);
# endif
# 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
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";
}
if (dnsmsg != NULL)
{
bp = buf;
strcpy(bp, "Name server: ");
bp += strlen(bp);
if (CurHostName != NULL)
{
snprintf(bp, SPACELEFT(buf, bp), "%s: ",
shortenstring(CurHostName, MAXSHORTSTR));
bp += strlen(bp);
}
snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg);
return buf;
}
#if HASSTRERROR
return strerror(errnum);
#else
if (errnum > 0 && errnum < sys_nerr)
return (sys_errlist[errnum]);
(void) snprintf(buf, sizeof buf, "Error %d", errnum);
return (buf);
#endif
}