#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>
#endif
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));
void
fatal_error(exc)
SM_EXC_T *exc;
{
static char buf[256];
SM_FILE_T f;
sm_strio_init(&f, buf, sizeof(buf));
sm_exc_write(exc, &f);
(void) sm_io_flush(&f, SM_TIME_DEFAULT);
errno = ENOMEM;
syserr("!%s", buf);
}
char MsgBuf[BUFSIZ*2];
static char HeldMessageBuf[sizeof(MsgBuf)];
#if NAMED_BIND && !defined(NO_DATA)
# define NO_DATA NO_ADDRESS
#endif
void
#ifdef __STDC__
syserr(const char *fmt, ...)
#else
syserr(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
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;
}
if (exiting)
{
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);
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;
}
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
#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(smioout, true);
break;
}
if (panic)
{
#if XLA
xla_all_end();
#endif
sync_queue_time();
if (tTd(0, 1))
abort();
exit(EX_OSERR);
}
errno = 0;
if (QuickAbort)
sm_exc_raisenew_x(&EtypeQuickAbort, 2);
}
void
#ifdef __STDC__
usrerr(const char *fmt, ...)
#else
usrerr(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
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;
switch (MsgBuf[0])
{
case '4':
case '8':
if (CurEnv->e_message != NULL)
break;
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);
}
void
#ifdef __STDC__
usrerrenh(char *enhsc, const char *fmt, ...)
#else
usrerrenh(enhsc, fmt, va_alist)
char *enhsc;
const char *fmt;
va_dcl
#endif
{
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;
switch (MsgBuf[0])
{
case '4':
case '8':
if (CurEnv->e_message != NULL)
break;
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);
}
void
#ifdef __STDC__
message(const char *msg, ...)
#else
message(msg, va_alist)
const char *msg;
va_dcl
#endif
{
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);
switch (MsgBuf[0])
{
case '4':
case '8':
if (CurEnv->e_message != NULL)
break;
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;
}
}
void
#ifdef __STDC__
nmessage(const char *msg, ...)
#else
nmessage(msg, va_alist)
const char *msg;
va_dcl
#endif
{
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);
switch (MsgBuf[0])
{
case '4':
case '8':
if (CurEnv->e_message != NULL)
break;
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;
}
}
static void
putoutmsg(msg, holdmsg, heldmsg)
char *msg;
bool holdmsg;
bool heldmsg;
{
char msgcode = msg[0];
char *errtxt = msg;
char *id;
if (tTd(54, 8))
sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
heldmsg ? " (held)" : "");
if (msgcode == '6')
msg[0] = '5';
else if (msgcode == '8')
msg[0] = '4';
id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
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';
if (!Verbose && msg[0] == '0')
return;
if (holdmsg)
{
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;
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 &&
(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
if (msg[3] == ' ')
(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
if (!sm_io_error(OutChannel) || DisConnected)
return;
if (InChannel == NULL || sm_io_eof(InChannel) ||
sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
return;
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
}
static void
puterrmsg(msg)
char *msg;
{
char msgcode = msg[0];
putoutmsg(msg, HoldErrs, false);
if (OnlyOneError)
HoldErrs = true;
Errors++;
if (CurEnv == NULL)
return;
if (msgcode == '6')
{
CurEnv->e_flags |= EF_PM_NOTIFY;
}
else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
{
CurEnv->e_flags |= EF_FATALERRS;
}
}
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;
}
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;
}
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;
if (ISSMTPCODE(fmt))
{
num = fmt;
fmt += 4;
}
if (num[3] == '-')
del = '-';
else
del = ' ';
if (SoftBounce && num[0] == '5')
{
(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)
{
l++;
(void) sm_strlcpy(eb, fmt, l + 1);
eb += l;
spaceleft -= l;
fmt += l;
}
else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
{
(void) sm_strlcpy(eb, enhsc, l + 1);
eb[l] = ' ';
eb[++l] = '\0';
eb += l;
spaceleft -= l;
}
if (SoftBounce && eb[-l] == '5')
{
eb[-l] = '4';
}
errtxt = eb;
if (FileName != NULL)
{
(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
shortenstring(FileName, 83), LineNumber);
eb += (l = strlen(eb));
spaceleft -= l;
}
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;
}
(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
spaceleft -= strlen(eb);
while (*eb != '\0')
*eb++ &= 0177;
if (eno != 0)
(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
return errtxt;
}
void
buffer_errors()
{
HeldMessageBuf[0] = '\0';
HoldErrs = true;
}
void
flush_errors(print)
bool print;
{
if (print && HeldMessageBuf[0] != '\0')
putoutmsg(HeldMessageBuf, false, true);
HeldMessageBuf[0] = '\0';
HoldErrs = false;
}
const char *
sm_errstring(errnum)
int errnum;
{
char *dnsmsg;
char *bp;
static char buf[MAXLINE];
#if HASSTRERROR
char *err;
char errbuf[30];
#endif
#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
extern char *sys_errlist[];
extern int sys_nerr;
#endif
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
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
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
case EPERM:
return "Operation not permitted";
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
#if HASSTRERROR
err = strerror(errnum);
if (err == NULL)
{
(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
return buf;
}
return err;
#else
if (errnum > 0 && errnum < sys_nerr)
return sys_errlist[errnum];
(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
return buf;
#endif
}