/* * 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 SM_RCSID("@(#)$Id: err.c,v 8.191 2003/01/10 02:16:46 ca Exp $") #if LDAPMAP # include # include /* 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 */ }