122705Sdist /* 268839Seric * Copyright (c) 1983, 1995 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*69748Seric static char sccsid[] = "@(#)err.c 8.34 (Berkeley) 05/28/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 46*69748Seric extern void putoutmsg __P((char *, bool, bool)); 47*69748Seric extern void puterrmsg __P((char *)); 48*69748Seric static void fmtmsg __P((char *, const char *, const char *, int, const char *, va_list)); 4946928Sbostic 5066334Seric #if NAMED_BIND && !defined(NO_DATA) 5163969Seric # define NO_DATA NO_ADDRESS 5263969Seric #endif 5363969Seric 5458824Seric void 55295Seric /*VARARGS1*/ 5657642Seric #ifdef __STDC__ 5760094Seric syserr(const char *fmt, ...) 5857642Seric #else 5957642Seric syserr(fmt, va_alist) 6060094Seric const char *fmt; 6157642Seric va_dcl 6257642Seric #endif 63295Seric { 6416901Seric register char *p; 6516901Seric int olderrno = errno; 6658690Seric bool panic; 6766006Seric #ifdef LOG 6866006Seric char *uname; 6966006Seric struct passwd *pw; 7066006Seric char ubuf[80]; 7166006Seric #endif 7256852Seric VA_LOCAL_DECL 73295Seric 7458690Seric panic = *fmt == '!'; 7558690Seric if (panic) 7658690Seric fmt++; 7758690Seric 787525Seric /* format and output the error message */ 7916901Seric if (olderrno == 0) 8058151Seric p = "554"; 817957Seric else 8258151Seric p = "451"; 8356852Seric VA_START(fmt); 8456852Seric fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); 8556852Seric VA_END; 869389Seric puterrmsg(MsgBuf); 874063Seric 8867818Seric /* save this message for mailq printing */ 8967818Seric if (!panic) 9067818Seric { 9167818Seric if (CurEnv->e_message != NULL) 9267818Seric free(CurEnv->e_message); 9367818Seric CurEnv->e_message = newstr(MsgBuf + 4); 9467818Seric } 9567818Seric 96295Seric /* determine exit status if not already set */ 97295Seric if (ExitStat == EX_OK) 98295Seric { 9916901Seric if (olderrno == 0) 100295Seric ExitStat = EX_SOFTWARE; 101295Seric else 1021598Seric ExitStat = EX_OSERR; 10366323Seric if (tTd(54, 1)) 10466323Seric printf("syserr: ExitStat = %d\n", ExitStat); 105295Seric } 106295Seric 107295Seric # ifdef LOG 10868693Seric pw = sm_getpwuid(getuid()); 10966006Seric if (pw != NULL) 11066006Seric uname = pw->pw_name; 11166006Seric else 11266006Seric { 11366006Seric uname = ubuf; 11466006Seric sprintf(ubuf, "UID%d", getuid()); 11566006Seric } 11666006Seric 1177674Seric if (LogLevel > 0) 11866006Seric syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s", 11925277Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 12066006Seric uname, &MsgBuf[4]); 12156795Seric # endif /* LOG */ 12264725Seric if (olderrno == EMFILE) 12364731Seric { 12464725Seric printopenfds(TRUE); 12564731Seric mci_dump_all(TRUE); 12664731Seric } 12758690Seric if (panic) 12859156Seric { 12959156Seric #ifdef XLA 13059156Seric xla_all_end(); 13159156Seric #endif 13258690Seric exit(EX_OSERR); 13359156Seric } 134295Seric errno = 0; 1357762Seric if (QuickAbort) 1367762Seric longjmp(TopFrame, 2); 137295Seric } 138295Seric /* 139295Seric ** USRERR -- Signal user error. 140295Seric ** 141295Seric ** This is much like syserr except it is for user errors. 142295Seric ** 143295Seric ** Parameters: 14467818Seric ** fmt -- the format string. If it does not begin with 14567818Seric ** a three-digit SMTP reply code, 501 is assumed. 14667818Seric ** (others) -- printf strings 147295Seric ** 148295Seric ** Returns: 1494084Seric ** none 1507762Seric ** Through TopFrame if QuickAbort is set. 151295Seric ** 152295Seric ** Side Effects: 1531514Seric ** increments Errors. 154295Seric */ 155295Seric 156295Seric /*VARARGS1*/ 15758824Seric void 15857642Seric #ifdef __STDC__ 15960094Seric usrerr(const char *fmt, ...) 16057642Seric #else 16157642Seric usrerr(fmt, va_alist) 16260094Seric const char *fmt; 16357642Seric va_dcl 16457642Seric #endif 165295Seric { 16656852Seric VA_LOCAL_DECL 167295Seric 168295Seric if (SuprErrs) 1694084Seric return; 170295Seric 17156852Seric VA_START(fmt); 17258524Seric fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); 17356852Seric VA_END; 1749389Seric puterrmsg(MsgBuf); 1758239Seric 17667818Seric /* save this message for mailq printing */ 17767818Seric if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4')) 17867818Seric { 17967818Seric if (CurEnv->e_message != NULL) 18067818Seric free(CurEnv->e_message); 18167818Seric CurEnv->e_message = newstr(MsgBuf + 4); 18267818Seric } 18367818Seric 18451951Seric # ifdef LOG 18558020Seric if (LogLevel > 3 && LogUsrErrs) 18651951Seric syslog(LOG_NOTICE, "%s: %s", 18751951Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 18851951Seric &MsgBuf[4]); 18956795Seric # endif /* LOG */ 19051951Seric 1917762Seric if (QuickAbort) 1927762Seric longjmp(TopFrame, 1); 1934063Seric } 1944063Seric /* 1954063Seric ** MESSAGE -- print message (not necessarily an error) 1964063Seric ** 1974063Seric ** Parameters: 19859581Seric ** msg -- the message (printf fmt) -- it can begin with 19959581Seric ** an SMTP reply code. If not, 050 is assumed. 20067818Seric ** (others) -- printf arguments 2014063Seric ** 2024063Seric ** Returns: 2034063Seric ** none 2044063Seric ** 2054063Seric ** Side Effects: 2064063Seric ** none. 2074063Seric */ 2084063Seric 2094084Seric /*VARARGS2*/ 21058826Seric void 21157642Seric #ifdef __STDC__ 21260094Seric message(const char *msg, ...) 21357642Seric #else 21458151Seric message(msg, va_alist) 21560094Seric const char *msg; 21657642Seric va_dcl 21757642Seric #endif 2184063Seric { 21956852Seric VA_LOCAL_DECL 22056852Seric 2214711Seric errno = 0; 22256852Seric VA_START(msg); 22358151Seric fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); 22456852Seric VA_END; 22568692Seric putoutmsg(MsgBuf, FALSE, FALSE); 22667818Seric 22767818Seric /* save this message for mailq printing */ 22867818Seric if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4')) 22967818Seric { 23067818Seric if (CurEnv->e_message != NULL) 23167818Seric free(CurEnv->e_message); 23267818Seric CurEnv->e_message = newstr(MsgBuf + 4); 23367818Seric } 2347613Seric } 2357613Seric /* 2368239Seric ** NMESSAGE -- print message (not necessarily an error) 2378239Seric ** 2388239Seric ** Just like "message" except it never puts the to... tag on. 2398239Seric ** 2408239Seric ** Parameters: 2418239Seric ** msg -- the message (printf fmt) -- if it begins 24267818Seric ** with a three digit SMTP reply code, that is used, 24367818Seric ** otherwise 050 is assumed. 24467818Seric ** (others) -- printf arguments 2458239Seric ** 2468239Seric ** Returns: 2478239Seric ** none 2488239Seric ** 2498239Seric ** Side Effects: 2508239Seric ** none. 2518239Seric */ 2528239Seric 2538239Seric /*VARARGS2*/ 25458826Seric void 25557642Seric #ifdef __STDC__ 25660094Seric nmessage(const char *msg, ...) 25757642Seric #else 25858151Seric nmessage(msg, va_alist) 25960094Seric const char *msg; 26057642Seric va_dcl 26157642Seric #endif 2628239Seric { 26356852Seric VA_LOCAL_DECL 26456852Seric 2658239Seric errno = 0; 26656852Seric VA_START(msg); 26758151Seric fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); 26856852Seric VA_END; 26968692Seric putoutmsg(MsgBuf, FALSE, FALSE); 2708239Seric } 2718239Seric /* 27263753Seric ** PUTOUTMSG -- output error message to transcript and channel 2737613Seric ** 2747613Seric ** Parameters: 2757613Seric ** msg -- message to output (in SMTP format). 2769108Seric ** holdmsg -- if TRUE, don't output a copy of the message to 2779108Seric ** our output channel. 27868692Seric ** heldmsg -- if TRUE, this is a previously held message; 27968692Seric ** don't log it to the transcript file. 2807613Seric ** 2817613Seric ** Returns: 2827613Seric ** none. 2837613Seric ** 2847613Seric ** Side Effects: 2857613Seric ** Outputs msg to the transcript. 2867613Seric ** If appropriate, outputs it to the channel. 2877613Seric ** Deletes SMTP reply code number as appropriate. 2887613Seric */ 2894711Seric 290*69748Seric void 29168692Seric putoutmsg(msg, holdmsg, heldmsg) 2927613Seric char *msg; 2939108Seric bool holdmsg; 29468692Seric bool heldmsg; 2957613Seric { 29664249Seric /* display for debugging */ 29764249Seric if (tTd(54, 8)) 29868692Seric printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 29968692Seric heldmsg ? " (held)" : ""); 30064249Seric 30114900Seric /* output to transcript if serious */ 30268692Seric if (!heldmsg && CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL) 30314900Seric fprintf(CurEnv->e_xfp, "%s\n", msg); 3044711Seric 3054711Seric /* output to channel if appropriate */ 30668692Seric if (!Verbose && msg[0] == '0') 30760421Seric return; 30868692Seric if (holdmsg) 30968692Seric { 31068692Seric /* save for possible future display */ 31168692Seric strcpy(HeldMessageBuf, msg); 31268692Seric return; 31368692Seric } 31460421Seric 31563848Seric /* map warnings to something SMTP can handle */ 31663848Seric if (msg[0] == '6') 31763848Seric msg[0] = '5'; 31863848Seric 31960421Seric (void) fflush(stdout); 32066017Seric 32166017Seric /* if DisConnected, OutChannel now points to the transcript */ 32266017Seric if (!DisConnected && 32366017Seric (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 32460421Seric fprintf(OutChannel, "%s\r\n", msg); 32560421Seric else 32660421Seric fprintf(OutChannel, "%s\n", &msg[4]); 32763753Seric if (TrafficLogFile != NULL) 32863753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), 32965580Seric (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]); 33060421Seric if (msg[3] == ' ') 33160421Seric (void) fflush(OutChannel); 33266017Seric if (!ferror(OutChannel) || DisConnected) 33360421Seric return; 33460421Seric 33564499Seric /* 33664499Seric ** Error on output -- if reporting lost channel, just ignore it. 33764499Seric ** Also, ignore errors from QUIT response (221 message) -- some 33864499Seric ** rude servers don't read result. 33964499Seric */ 34064499Seric 34164499Seric if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0) 34260421Seric return; 34360421Seric 34460421Seric /* can't call syserr, 'cause we are using MsgBuf */ 34560421Seric HoldErrs = TRUE; 34660283Seric #ifdef LOG 34760421Seric if (LogLevel > 0) 34860421Seric syslog(LOG_CRIT, 34966864Seric "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 35060421Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 35164123Seric CurHostName == NULL ? "NO-HOST" : CurHostName, 35266864Seric msg, errstring(errno)); 35360283Seric #endif 3549389Seric } 3559389Seric /* 35663753Seric ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 3579389Seric ** 3589389Seric ** Parameters: 3599389Seric ** msg -- the message to output. 3609389Seric ** 3619389Seric ** Returns: 3629389Seric ** none. 3639389Seric ** 3649389Seric ** Side Effects: 3659389Seric ** Sets the fatal error bit in the envelope as appropriate. 3669389Seric */ 3678239Seric 368*69748Seric void 3699389Seric puterrmsg(msg) 3709389Seric char *msg; 3719389Seric { 37263848Seric char msgcode = msg[0]; 37363848Seric 3749389Seric /* output the message as usual */ 37568692Seric putoutmsg(msg, HoldErrs, FALSE); 3769389Seric 3779389Seric /* signal the error */ 37864773Seric Errors++; 37963848Seric if (msgcode == '6') 38063848Seric { 38163848Seric /* notify the postmaster */ 38263848Seric CurEnv->e_flags |= EF_PM_NOTIFY; 38363848Seric } 38467786Seric else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 38564773Seric { 38664773Seric /* mark long-term fatal errors */ 38764773Seric CurEnv->e_flags |= EF_FATALERRS; 38864773Seric } 3894711Seric } 3904711Seric /* 3914711Seric ** FMTMSG -- format a message into buffer. 3924711Seric ** 3934711Seric ** Parameters: 3944711Seric ** eb -- error buffer to get result. 3954711Seric ** to -- the recipient tag for this message. 3964711Seric ** num -- arpanet error number. 39716901Seric ** en -- the error number to display. 3984711Seric ** fmt -- format of string. 3994711Seric ** a, b, c, d, e -- arguments. 4004711Seric ** 4014711Seric ** Returns: 4024711Seric ** none. 4034711Seric ** 4044711Seric ** Side Effects: 4054711Seric ** none. 4064711Seric */ 4074063Seric 40846928Sbostic static void 40956852Seric fmtmsg(eb, to, num, eno, fmt, ap) 4104711Seric register char *eb; 411*69748Seric const char *to; 412*69748Seric const char *num; 41316904Seric int eno; 414*69748Seric const char *fmt; 41556852Seric va_list ap; 4164711Seric { 4174711Seric char del; 41859596Seric char *meb; 4194711Seric 4204711Seric /* output the reply code */ 42124943Seric if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2])) 4224577Seric { 4234711Seric num = fmt; 4244711Seric fmt += 4; 4254711Seric } 4264711Seric if (num[3] == '-') 4274711Seric del = '-'; 4284711Seric else 4294711Seric del = ' '; 4304711Seric (void) sprintf(eb, "%3.3s%c", num, del); 4314711Seric eb += 4; 4324063Seric 4339372Seric /* output the file name and line number */ 4349372Seric if (FileName != NULL) 4359372Seric { 4369372Seric (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber); 4379372Seric eb += strlen(eb); 4389372Seric } 4399372Seric 4404711Seric /* output the "to" person */ 4414711Seric if (to != NULL && to[0] != '\0') 4424711Seric { 44366297Seric (void) sprintf(eb, "%s... ", shortenstring(to, 203)); 4445201Seric while (*eb != '\0') 4455201Seric *eb++ &= 0177; 4464711Seric } 4474711Seric 44859596Seric meb = eb; 44959596Seric 4504711Seric /* output the message */ 45156852Seric (void) vsprintf(eb, fmt, ap); 4525201Seric while (*eb != '\0') 4535201Seric *eb++ &= 0177; 4544711Seric 4554711Seric /* output the error code, if any */ 45616904Seric if (eno != 0) 4574711Seric { 45816904Seric (void) sprintf(eb, ": %s", errstring(eno)); 4594711Seric eb += strlen(eb); 4604577Seric } 461295Seric } 46215136Seric /* 46368692Seric ** BUFFER_ERRORS -- arrange to buffer future error messages 46468692Seric ** 46568692Seric ** Parameters: 46668692Seric ** none 46768692Seric ** 46868692Seric ** Returns: 46968692Seric ** none. 47068692Seric */ 47168692Seric 47268692Seric void 47368692Seric buffer_errors() 47468692Seric { 47568692Seric HeldMessageBuf[0] = '\0'; 47668692Seric HoldErrs = TRUE; 47768692Seric } 47868692Seric /* 47968692Seric ** FLUSH_ERRORS -- flush the held error message buffer 48068692Seric ** 48168692Seric ** Parameters: 48268692Seric ** print -- if set, print the message, otherwise just 48368692Seric ** delete it. 48468692Seric ** 48568692Seric ** Returns: 48668692Seric ** none. 48768692Seric */ 48868692Seric 48968692Seric void 49068692Seric flush_errors(print) 49168692Seric bool print; 49268692Seric { 49368692Seric if (print && HeldMessageBuf[0] != '\0') 49468692Seric putoutmsg(HeldMessageBuf, FALSE, TRUE); 49568692Seric HeldMessageBuf[0] = '\0'; 49668692Seric HoldErrs = FALSE; 49768692Seric } 49868692Seric /* 49915136Seric ** ERRSTRING -- return string description of error code 50015136Seric ** 50115136Seric ** Parameters: 50265751Seric ** errnum -- the error number to translate 50315136Seric ** 50415136Seric ** Returns: 50565751Seric ** A string description of errnum. 50615136Seric ** 50715136Seric ** Side Effects: 50815136Seric ** none. 50915136Seric */ 51015136Seric 51160089Seric const char * 51265751Seric errstring(errnum) 51365751Seric int errnum; 51415136Seric { 51565168Seric char *dnsmsg; 51663839Seric static char buf[MAXLINE]; 51763839Seric # ifndef ERRLIST_PREDEFINED 51863839Seric extern char *sys_errlist[]; 51915136Seric extern int sys_nerr; 52063839Seric # endif 52124943Seric # ifdef SMTP 52224943Seric extern char *SmtpPhase; 52356795Seric # endif /* SMTP */ 52415136Seric 52524943Seric /* 52624943Seric ** Handle special network error codes. 52724943Seric ** 52824943Seric ** These are 4.2/4.3bsd specific; they should be in daemon.c. 52924943Seric */ 53024943Seric 53165168Seric dnsmsg = NULL; 53265751Seric switch (errnum) 53324943Seric { 53465067Seric # if defined(DAEMON) && defined(ETIMEDOUT) 53524943Seric case ETIMEDOUT: 53624943Seric case ECONNRESET: 53765751Seric (void) strcpy(buf, sys_errlist[errnum]); 53824943Seric if (SmtpPhase != NULL) 53924943Seric { 54024943Seric (void) strcat(buf, " during "); 54124943Seric (void) strcat(buf, SmtpPhase); 54224943Seric } 54325050Seric if (CurHostName != NULL) 54424943Seric { 54524943Seric (void) strcat(buf, " with "); 54625050Seric (void) strcat(buf, CurHostName); 54724943Seric } 54824943Seric return (buf); 54924943Seric 55024943Seric case EHOSTDOWN: 55125050Seric if (CurHostName == NULL) 55224943Seric break; 55325050Seric (void) sprintf(buf, "Host %s is down", CurHostName); 55424943Seric return (buf); 55524943Seric 55624943Seric case ECONNREFUSED: 55725050Seric if (CurHostName == NULL) 55824943Seric break; 55925050Seric (void) sprintf(buf, "Connection refused by %s", CurHostName); 56024943Seric return (buf); 56165067Seric # endif 56225526Smiriam 56363993Seric case EOPENTIMEOUT: 56463993Seric return "Timeout on file open"; 56563993Seric 56666334Seric # if NAMED_BIND 56763993Seric case HOST_NOT_FOUND + E_DNSBASE: 56865168Seric dnsmsg = "host not found"; 56965168Seric break; 57058010Seric 57163993Seric case TRY_AGAIN + E_DNSBASE: 57265168Seric dnsmsg = "host name lookup failure"; 57365168Seric break; 57458010Seric 57563993Seric case NO_RECOVERY + E_DNSBASE: 57665168Seric dnsmsg = "non-recoverable error"; 57765168Seric break; 57858010Seric 57963993Seric case NO_DATA + E_DNSBASE: 58065168Seric dnsmsg = "no data known"; 58165168Seric break; 58257736Seric # endif 58365067Seric 58465067Seric case EPERM: 58565067Seric /* SunOS gives "Not owner" -- this is the POSIX message */ 58665067Seric return "Operation not permitted"; 58724943Seric } 58824943Seric 58965168Seric if (dnsmsg != NULL) 59065168Seric { 59165168Seric (void) strcpy(buf, "Name server: "); 59265168Seric if (CurHostName != NULL) 59365168Seric { 59465168Seric (void) strcat(buf, CurHostName); 59565168Seric (void) strcat(buf, ": "); 59665168Seric } 59765168Seric (void) strcat(buf, dnsmsg); 59865168Seric return buf; 59965168Seric } 60065168Seric 60165751Seric if (errnum > 0 && errnum < sys_nerr) 60265751Seric return (sys_errlist[errnum]); 60315136Seric 60465751Seric (void) sprintf(buf, "Error %d", errnum); 60515136Seric return (buf); 60615136Seric } 607