diff options
Diffstat (limited to 'sendmail/src/err.c')
-rw-r--r-- | sendmail/src/err.c | 1158 |
1 files changed, 1158 insertions, 0 deletions
diff --git a/sendmail/src/err.c b/sendmail/src/err.c new file mode 100644 index 0000000..5825666 --- /dev/null +++ b/sendmail/src/err.c @@ -0,0 +1,1158 @@ +/* + * 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.196 2006/11/10 23:14:08 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 msgcode = msg[0]; + char *errtxt = msg; + char *id; + + /* 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'; + id = (CurEnv != NULL) ? CurEnv->e_id : NULL; + + /* 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, 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, 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 (SoftBounce && num[0] == '5') + { + /* replace 5 by 4 */ + (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) + { + /* 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 (SoftBounce && eb[-l] == '5') + { + /* replace 5 by 4 */ + eb[-l] = '4'; + } + 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 */ +} |