122705Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362525Sbostic * Copyright (c) 1988, 1993 462525Sbostic * The Regents of the University of California. All rights reserved. 533729Sbostic * 642826Sbostic * %sccs.include.redist.c% 733729Sbostic */ 822705Sdist 922705Sdist #ifndef lint 10*68693Seric static char sccsid[] = "@(#)err.c 8.32 (Berkeley) 03/31/95"; 1133729Sbostic #endif /* not lint */ 1222705Sdist 133311Seric # include "sendmail.h" 1424943Seric # include <errno.h> 15295Seric 16295Seric /* 171514Seric ** SYSERR -- Print error message. 18295Seric ** 19295Seric ** Prints an error message via printf to the diagnostic 20295Seric ** output. If LOG is defined, it logs it also. 21295Seric ** 2258690Seric ** If the first character of the syserr message is `!' it will 2358690Seric ** log this as an ALERT message and exit immediately. This can 2458690Seric ** leave queue files in an indeterminate state, so it should not 2558690Seric ** be used lightly. 2658690Seric ** 27295Seric ** Parameters: 2867818Seric ** fmt -- the format string. If it does not begin with 2967818Seric ** a three-digit SMTP reply code, either 554 or 3067818Seric ** 451 is assumed depending on whether errno 3167818Seric ** is set. 3267818Seric ** (others) -- parameters 33295Seric ** 34295Seric ** Returns: 354084Seric ** none 367762Seric ** Through TopFrame if QuickAbort is set. 37295Seric ** 38295Seric ** Side Effects: 391514Seric ** increments Errors. 401514Seric ** sets ExitStat. 41295Seric */ 42295Seric 4368692Seric char MsgBuf[BUFSIZ*2]; /* text of most recent message */ 4468692Seric char HeldMessageBuf[sizeof MsgBuf]; /* for held messages */ 454084Seric 4663969Seric static void fmtmsg(); 4746928Sbostic 4866334Seric #if NAMED_BIND && !defined(NO_DATA) 4963969Seric # define NO_DATA NO_ADDRESS 5063969Seric #endif 5163969Seric 5258824Seric void 53295Seric /*VARARGS1*/ 5457642Seric #ifdef __STDC__ 5560094Seric syserr(const char *fmt, ...) 5657642Seric #else 5757642Seric syserr(fmt, va_alist) 5860094Seric const char *fmt; 5957642Seric va_dcl 6057642Seric #endif 61295Seric { 6216901Seric register char *p; 6316901Seric int olderrno = errno; 6458690Seric bool panic; 6566006Seric #ifdef LOG 6666006Seric char *uname; 6766006Seric struct passwd *pw; 6866006Seric char ubuf[80]; 6966006Seric #endif 7056852Seric VA_LOCAL_DECL 71295Seric 7258690Seric panic = *fmt == '!'; 7358690Seric if (panic) 7458690Seric fmt++; 7558690Seric 767525Seric /* format and output the error message */ 7716901Seric if (olderrno == 0) 7858151Seric p = "554"; 797957Seric else 8058151Seric p = "451"; 8156852Seric VA_START(fmt); 8256852Seric fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); 8356852Seric VA_END; 849389Seric puterrmsg(MsgBuf); 854063Seric 8667818Seric /* save this message for mailq printing */ 8767818Seric if (!panic) 8867818Seric { 8967818Seric if (CurEnv->e_message != NULL) 9067818Seric free(CurEnv->e_message); 9167818Seric CurEnv->e_message = newstr(MsgBuf + 4); 9267818Seric } 9367818Seric 94295Seric /* determine exit status if not already set */ 95295Seric if (ExitStat == EX_OK) 96295Seric { 9716901Seric if (olderrno == 0) 98295Seric ExitStat = EX_SOFTWARE; 99295Seric else 1001598Seric ExitStat = EX_OSERR; 10166323Seric if (tTd(54, 1)) 10266323Seric printf("syserr: ExitStat = %d\n", ExitStat); 103295Seric } 104295Seric 105295Seric # ifdef LOG 106*68693Seric pw = sm_getpwuid(getuid()); 10766006Seric if (pw != NULL) 10866006Seric uname = pw->pw_name; 10966006Seric else 11066006Seric { 11166006Seric uname = ubuf; 11266006Seric sprintf(ubuf, "UID%d", getuid()); 11366006Seric } 11466006Seric 1157674Seric if (LogLevel > 0) 11666006Seric syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s", 11725277Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 11866006Seric uname, &MsgBuf[4]); 11956795Seric # endif /* LOG */ 12064725Seric if (olderrno == EMFILE) 12164731Seric { 12264725Seric printopenfds(TRUE); 12364731Seric mci_dump_all(TRUE); 12464731Seric } 12558690Seric if (panic) 12659156Seric { 12759156Seric #ifdef XLA 12859156Seric xla_all_end(); 12959156Seric #endif 13058690Seric exit(EX_OSERR); 13159156Seric } 132295Seric errno = 0; 1337762Seric if (QuickAbort) 1347762Seric longjmp(TopFrame, 2); 135295Seric } 136295Seric /* 137295Seric ** USRERR -- Signal user error. 138295Seric ** 139295Seric ** This is much like syserr except it is for user errors. 140295Seric ** 141295Seric ** Parameters: 14267818Seric ** fmt -- the format string. If it does not begin with 14367818Seric ** a three-digit SMTP reply code, 501 is assumed. 14467818Seric ** (others) -- printf strings 145295Seric ** 146295Seric ** Returns: 1474084Seric ** none 1487762Seric ** Through TopFrame if QuickAbort is set. 149295Seric ** 150295Seric ** Side Effects: 1511514Seric ** increments Errors. 152295Seric */ 153295Seric 154295Seric /*VARARGS1*/ 15558824Seric void 15657642Seric #ifdef __STDC__ 15760094Seric usrerr(const char *fmt, ...) 15857642Seric #else 15957642Seric usrerr(fmt, va_alist) 16060094Seric const char *fmt; 16157642Seric va_dcl 16257642Seric #endif 163295Seric { 16456852Seric VA_LOCAL_DECL 165295Seric 166295Seric if (SuprErrs) 1674084Seric return; 168295Seric 16956852Seric VA_START(fmt); 17058524Seric fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); 17156852Seric VA_END; 1729389Seric puterrmsg(MsgBuf); 1738239Seric 17467818Seric /* save this message for mailq printing */ 17567818Seric if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4')) 17667818Seric { 17767818Seric if (CurEnv->e_message != NULL) 17867818Seric free(CurEnv->e_message); 17967818Seric CurEnv->e_message = newstr(MsgBuf + 4); 18067818Seric } 18167818Seric 18251951Seric # ifdef LOG 18358020Seric if (LogLevel > 3 && LogUsrErrs) 18451951Seric syslog(LOG_NOTICE, "%s: %s", 18551951Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 18651951Seric &MsgBuf[4]); 18756795Seric # endif /* LOG */ 18851951Seric 1897762Seric if (QuickAbort) 1907762Seric longjmp(TopFrame, 1); 1914063Seric } 1924063Seric /* 1934063Seric ** MESSAGE -- print message (not necessarily an error) 1944063Seric ** 1954063Seric ** Parameters: 19659581Seric ** msg -- the message (printf fmt) -- it can begin with 19759581Seric ** an SMTP reply code. If not, 050 is assumed. 19867818Seric ** (others) -- printf arguments 1994063Seric ** 2004063Seric ** Returns: 2014063Seric ** none 2024063Seric ** 2034063Seric ** Side Effects: 2044063Seric ** none. 2054063Seric */ 2064063Seric 2074084Seric /*VARARGS2*/ 20858826Seric void 20957642Seric #ifdef __STDC__ 21060094Seric message(const char *msg, ...) 21157642Seric #else 21258151Seric message(msg, va_alist) 21360094Seric const char *msg; 21457642Seric va_dcl 21557642Seric #endif 2164063Seric { 21756852Seric VA_LOCAL_DECL 21856852Seric 2194711Seric errno = 0; 22056852Seric VA_START(msg); 22158151Seric fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); 22256852Seric VA_END; 22368692Seric putoutmsg(MsgBuf, FALSE, FALSE); 22467818Seric 22567818Seric /* save this message for mailq printing */ 22667818Seric if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4')) 22767818Seric { 22867818Seric if (CurEnv->e_message != NULL) 22967818Seric free(CurEnv->e_message); 23067818Seric CurEnv->e_message = newstr(MsgBuf + 4); 23167818Seric } 2327613Seric } 2337613Seric /* 2348239Seric ** NMESSAGE -- print message (not necessarily an error) 2358239Seric ** 2368239Seric ** Just like "message" except it never puts the to... tag on. 2378239Seric ** 2388239Seric ** Parameters: 2398239Seric ** msg -- the message (printf fmt) -- if it begins 24067818Seric ** with a three digit SMTP reply code, that is used, 24167818Seric ** otherwise 050 is assumed. 24267818Seric ** (others) -- printf arguments 2438239Seric ** 2448239Seric ** Returns: 2458239Seric ** none 2468239Seric ** 2478239Seric ** Side Effects: 2488239Seric ** none. 2498239Seric */ 2508239Seric 2518239Seric /*VARARGS2*/ 25258826Seric void 25357642Seric #ifdef __STDC__ 25460094Seric nmessage(const char *msg, ...) 25557642Seric #else 25658151Seric nmessage(msg, va_alist) 25760094Seric const char *msg; 25857642Seric va_dcl 25957642Seric #endif 2608239Seric { 26156852Seric VA_LOCAL_DECL 26256852Seric 2638239Seric errno = 0; 26456852Seric VA_START(msg); 26558151Seric fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); 26656852Seric VA_END; 26768692Seric putoutmsg(MsgBuf, FALSE, FALSE); 2688239Seric } 2698239Seric /* 27063753Seric ** PUTOUTMSG -- output error message to transcript and channel 2717613Seric ** 2727613Seric ** Parameters: 2737613Seric ** msg -- message to output (in SMTP format). 2749108Seric ** holdmsg -- if TRUE, don't output a copy of the message to 2759108Seric ** our output channel. 27668692Seric ** heldmsg -- if TRUE, this is a previously held message; 27768692Seric ** don't log it to the transcript file. 2787613Seric ** 2797613Seric ** Returns: 2807613Seric ** none. 2817613Seric ** 2827613Seric ** Side Effects: 2837613Seric ** Outputs msg to the transcript. 2847613Seric ** If appropriate, outputs it to the channel. 2857613Seric ** Deletes SMTP reply code number as appropriate. 2867613Seric */ 2874711Seric 28868692Seric putoutmsg(msg, holdmsg, heldmsg) 2897613Seric char *msg; 2909108Seric bool holdmsg; 29168692Seric bool heldmsg; 2927613Seric { 29364249Seric /* display for debugging */ 29464249Seric if (tTd(54, 8)) 29568692Seric printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 29668692Seric heldmsg ? " (held)" : ""); 29764249Seric 29814900Seric /* output to transcript if serious */ 29968692Seric if (!heldmsg && CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL) 30014900Seric fprintf(CurEnv->e_xfp, "%s\n", msg); 3014711Seric 3024711Seric /* output to channel if appropriate */ 30368692Seric if (!Verbose && msg[0] == '0') 30460421Seric return; 30568692Seric if (holdmsg) 30668692Seric { 30768692Seric /* save for possible future display */ 30868692Seric strcpy(HeldMessageBuf, msg); 30968692Seric return; 31068692Seric } 31160421Seric 31263848Seric /* map warnings to something SMTP can handle */ 31363848Seric if (msg[0] == '6') 31463848Seric msg[0] = '5'; 31563848Seric 31660421Seric (void) fflush(stdout); 31766017Seric 31866017Seric /* if DisConnected, OutChannel now points to the transcript */ 31966017Seric if (!DisConnected && 32066017Seric (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 32160421Seric fprintf(OutChannel, "%s\r\n", msg); 32260421Seric else 32360421Seric fprintf(OutChannel, "%s\n", &msg[4]); 32463753Seric if (TrafficLogFile != NULL) 32563753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), 32665580Seric (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]); 32760421Seric if (msg[3] == ' ') 32860421Seric (void) fflush(OutChannel); 32966017Seric if (!ferror(OutChannel) || DisConnected) 33060421Seric return; 33160421Seric 33264499Seric /* 33364499Seric ** Error on output -- if reporting lost channel, just ignore it. 33464499Seric ** Also, ignore errors from QUIT response (221 message) -- some 33564499Seric ** rude servers don't read result. 33664499Seric */ 33764499Seric 33864499Seric if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0) 33960421Seric return; 34060421Seric 34160421Seric /* can't call syserr, 'cause we are using MsgBuf */ 34260421Seric HoldErrs = TRUE; 34360283Seric #ifdef LOG 34460421Seric if (LogLevel > 0) 34560421Seric syslog(LOG_CRIT, 34666864Seric "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 34760421Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 34864123Seric CurHostName == NULL ? "NO-HOST" : CurHostName, 34966864Seric msg, errstring(errno)); 35060283Seric #endif 3519389Seric } 3529389Seric /* 35363753Seric ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 3549389Seric ** 3559389Seric ** Parameters: 3569389Seric ** msg -- the message to output. 3579389Seric ** 3589389Seric ** Returns: 3599389Seric ** none. 3609389Seric ** 3619389Seric ** Side Effects: 3629389Seric ** Sets the fatal error bit in the envelope as appropriate. 3639389Seric */ 3648239Seric 3659389Seric puterrmsg(msg) 3669389Seric char *msg; 3679389Seric { 36863848Seric char msgcode = msg[0]; 36963848Seric 3709389Seric /* output the message as usual */ 37168692Seric putoutmsg(msg, HoldErrs, FALSE); 3729389Seric 3739389Seric /* signal the error */ 37464773Seric Errors++; 37563848Seric if (msgcode == '6') 37663848Seric { 37763848Seric /* notify the postmaster */ 37863848Seric CurEnv->e_flags |= EF_PM_NOTIFY; 37963848Seric } 38067786Seric else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 38164773Seric { 38264773Seric /* mark long-term fatal errors */ 38364773Seric CurEnv->e_flags |= EF_FATALERRS; 38464773Seric } 3854711Seric } 3864711Seric /* 3874711Seric ** FMTMSG -- format a message into buffer. 3884711Seric ** 3894711Seric ** Parameters: 3904711Seric ** eb -- error buffer to get result. 3914711Seric ** to -- the recipient tag for this message. 3924711Seric ** num -- arpanet error number. 39316901Seric ** en -- the error number to display. 3944711Seric ** fmt -- format of string. 3954711Seric ** a, b, c, d, e -- arguments. 3964711Seric ** 3974711Seric ** Returns: 3984711Seric ** none. 3994711Seric ** 4004711Seric ** Side Effects: 4014711Seric ** none. 4024711Seric */ 4034063Seric 40446928Sbostic static void 40556852Seric fmtmsg(eb, to, num, eno, fmt, ap) 4064711Seric register char *eb; 4074711Seric char *to; 4084711Seric char *num; 40916904Seric int eno; 4104711Seric char *fmt; 41156852Seric va_list ap; 4124711Seric { 4134711Seric char del; 41459596Seric char *meb; 4154711Seric 4164711Seric /* output the reply code */ 41724943Seric if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2])) 4184577Seric { 4194711Seric num = fmt; 4204711Seric fmt += 4; 4214711Seric } 4224711Seric if (num[3] == '-') 4234711Seric del = '-'; 4244711Seric else 4254711Seric del = ' '; 4264711Seric (void) sprintf(eb, "%3.3s%c", num, del); 4274711Seric eb += 4; 4284063Seric 4299372Seric /* output the file name and line number */ 4309372Seric if (FileName != NULL) 4319372Seric { 4329372Seric (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber); 4339372Seric eb += strlen(eb); 4349372Seric } 4359372Seric 4364711Seric /* output the "to" person */ 4374711Seric if (to != NULL && to[0] != '\0') 4384711Seric { 43966297Seric (void) sprintf(eb, "%s... ", shortenstring(to, 203)); 4405201Seric while (*eb != '\0') 4415201Seric *eb++ &= 0177; 4424711Seric } 4434711Seric 44459596Seric meb = eb; 44559596Seric 4464711Seric /* output the message */ 44756852Seric (void) vsprintf(eb, fmt, ap); 4485201Seric while (*eb != '\0') 4495201Seric *eb++ &= 0177; 4504711Seric 4514711Seric /* output the error code, if any */ 45216904Seric if (eno != 0) 4534711Seric { 45416904Seric (void) sprintf(eb, ": %s", errstring(eno)); 4554711Seric eb += strlen(eb); 4564577Seric } 457295Seric } 45815136Seric /* 45968692Seric ** BUFFER_ERRORS -- arrange to buffer future error messages 46068692Seric ** 46168692Seric ** Parameters: 46268692Seric ** none 46368692Seric ** 46468692Seric ** Returns: 46568692Seric ** none. 46668692Seric */ 46768692Seric 46868692Seric void 46968692Seric buffer_errors() 47068692Seric { 47168692Seric HeldMessageBuf[0] = '\0'; 47268692Seric HoldErrs = TRUE; 47368692Seric } 47468692Seric /* 47568692Seric ** FLUSH_ERRORS -- flush the held error message buffer 47668692Seric ** 47768692Seric ** Parameters: 47868692Seric ** print -- if set, print the message, otherwise just 47968692Seric ** delete it. 48068692Seric ** 48168692Seric ** Returns: 48268692Seric ** none. 48368692Seric */ 48468692Seric 48568692Seric void 48668692Seric flush_errors(print) 48768692Seric bool print; 48868692Seric { 48968692Seric if (print && HeldMessageBuf[0] != '\0') 49068692Seric putoutmsg(HeldMessageBuf, FALSE, TRUE); 49168692Seric HeldMessageBuf[0] = '\0'; 49268692Seric HoldErrs = FALSE; 49368692Seric } 49468692Seric /* 49515136Seric ** ERRSTRING -- return string description of error code 49615136Seric ** 49715136Seric ** Parameters: 49865751Seric ** errnum -- the error number to translate 49915136Seric ** 50015136Seric ** Returns: 50165751Seric ** A string description of errnum. 50215136Seric ** 50315136Seric ** Side Effects: 50415136Seric ** none. 50515136Seric */ 50615136Seric 50760089Seric const char * 50865751Seric errstring(errnum) 50965751Seric int errnum; 51015136Seric { 51165168Seric char *dnsmsg; 51263839Seric static char buf[MAXLINE]; 51363839Seric # ifndef ERRLIST_PREDEFINED 51463839Seric extern char *sys_errlist[]; 51515136Seric extern int sys_nerr; 51663839Seric # endif 51724943Seric # ifdef SMTP 51824943Seric extern char *SmtpPhase; 51956795Seric # endif /* SMTP */ 52015136Seric 52124943Seric /* 52224943Seric ** Handle special network error codes. 52324943Seric ** 52424943Seric ** These are 4.2/4.3bsd specific; they should be in daemon.c. 52524943Seric */ 52624943Seric 52765168Seric dnsmsg = NULL; 52865751Seric switch (errnum) 52924943Seric { 53065067Seric # if defined(DAEMON) && defined(ETIMEDOUT) 53124943Seric case ETIMEDOUT: 53224943Seric case ECONNRESET: 53365751Seric (void) strcpy(buf, sys_errlist[errnum]); 53424943Seric if (SmtpPhase != NULL) 53524943Seric { 53624943Seric (void) strcat(buf, " during "); 53724943Seric (void) strcat(buf, SmtpPhase); 53824943Seric } 53925050Seric if (CurHostName != NULL) 54024943Seric { 54124943Seric (void) strcat(buf, " with "); 54225050Seric (void) strcat(buf, CurHostName); 54324943Seric } 54424943Seric return (buf); 54524943Seric 54624943Seric case EHOSTDOWN: 54725050Seric if (CurHostName == NULL) 54824943Seric break; 54925050Seric (void) sprintf(buf, "Host %s is down", CurHostName); 55024943Seric return (buf); 55124943Seric 55224943Seric case ECONNREFUSED: 55325050Seric if (CurHostName == NULL) 55424943Seric break; 55525050Seric (void) sprintf(buf, "Connection refused by %s", CurHostName); 55624943Seric return (buf); 55765067Seric # endif 55825526Smiriam 55963993Seric case EOPENTIMEOUT: 56063993Seric return "Timeout on file open"; 56163993Seric 56266334Seric # if NAMED_BIND 56363993Seric case HOST_NOT_FOUND + E_DNSBASE: 56465168Seric dnsmsg = "host not found"; 56565168Seric break; 56658010Seric 56763993Seric case TRY_AGAIN + E_DNSBASE: 56865168Seric dnsmsg = "host name lookup failure"; 56965168Seric break; 57058010Seric 57163993Seric case NO_RECOVERY + E_DNSBASE: 57265168Seric dnsmsg = "non-recoverable error"; 57365168Seric break; 57458010Seric 57563993Seric case NO_DATA + E_DNSBASE: 57665168Seric dnsmsg = "no data known"; 57765168Seric break; 57857736Seric # endif 57965067Seric 58065067Seric case EPERM: 58165067Seric /* SunOS gives "Not owner" -- this is the POSIX message */ 58265067Seric return "Operation not permitted"; 58324943Seric } 58424943Seric 58565168Seric if (dnsmsg != NULL) 58665168Seric { 58765168Seric (void) strcpy(buf, "Name server: "); 58865168Seric if (CurHostName != NULL) 58965168Seric { 59065168Seric (void) strcat(buf, CurHostName); 59165168Seric (void) strcat(buf, ": "); 59265168Seric } 59365168Seric (void) strcat(buf, dnsmsg); 59465168Seric return buf; 59565168Seric } 59665168Seric 59765751Seric if (errnum > 0 && errnum < sys_nerr) 59865751Seric return (sys_errlist[errnum]); 59915136Seric 60065751Seric (void) sprintf(buf, "Error %d", errnum); 60115136Seric return (buf); 60215136Seric } 603