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*67818Seric static char sccsid[] = "@(#)err.c 8.30 (Berkeley) 10/16/94"; 1133729Sbostic #endif /* not lint */ 1222705Sdist 133311Seric # include "sendmail.h" 1424943Seric # include <errno.h> 1525526Smiriam # include <netdb.h> 1666006Seric # include <pwd.h> 17295Seric 18295Seric /* 191514Seric ** SYSERR -- Print error message. 20295Seric ** 21295Seric ** Prints an error message via printf to the diagnostic 22295Seric ** output. If LOG is defined, it logs it also. 23295Seric ** 2458690Seric ** If the first character of the syserr message is `!' it will 2558690Seric ** log this as an ALERT message and exit immediately. This can 2658690Seric ** leave queue files in an indeterminate state, so it should not 2758690Seric ** be used lightly. 2858690Seric ** 29295Seric ** Parameters: 30*67818Seric ** fmt -- the format string. If it does not begin with 31*67818Seric ** a three-digit SMTP reply code, either 554 or 32*67818Seric ** 451 is assumed depending on whether errno 33*67818Seric ** is set. 34*67818Seric ** (others) -- parameters 35295Seric ** 36295Seric ** Returns: 374084Seric ** none 387762Seric ** Through TopFrame if QuickAbort is set. 39295Seric ** 40295Seric ** Side Effects: 411514Seric ** increments Errors. 421514Seric ** sets ExitStat. 43295Seric */ 44295Seric 4510147Seric char MsgBuf[BUFSIZ*2]; /* text of most recent message */ 464084Seric 4763969Seric static void fmtmsg(); 4846928Sbostic 4966334Seric #if NAMED_BIND && !defined(NO_DATA) 5063969Seric # define NO_DATA NO_ADDRESS 5163969Seric #endif 5263969Seric 5358824Seric void 54295Seric /*VARARGS1*/ 5557642Seric #ifdef __STDC__ 5660094Seric syserr(const char *fmt, ...) 5757642Seric #else 5857642Seric syserr(fmt, va_alist) 5960094Seric const char *fmt; 6057642Seric va_dcl 6157642Seric #endif 62295Seric { 6316901Seric register char *p; 6416901Seric int olderrno = errno; 6558690Seric bool panic; 6666006Seric #ifdef LOG 6766006Seric char *uname; 6866006Seric struct passwd *pw; 6966006Seric char ubuf[80]; 7066006Seric #endif 7156852Seric VA_LOCAL_DECL 72295Seric 7358690Seric panic = *fmt == '!'; 7458690Seric if (panic) 7558690Seric fmt++; 7658690Seric 777525Seric /* format and output the error message */ 7816901Seric if (olderrno == 0) 7958151Seric p = "554"; 807957Seric else 8158151Seric p = "451"; 8256852Seric VA_START(fmt); 8356852Seric fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); 8456852Seric VA_END; 859389Seric puterrmsg(MsgBuf); 864063Seric 87*67818Seric /* save this message for mailq printing */ 88*67818Seric if (!panic) 89*67818Seric { 90*67818Seric if (CurEnv->e_message != NULL) 91*67818Seric free(CurEnv->e_message); 92*67818Seric CurEnv->e_message = newstr(MsgBuf + 4); 93*67818Seric } 94*67818Seric 95295Seric /* determine exit status if not already set */ 96295Seric if (ExitStat == EX_OK) 97295Seric { 9816901Seric if (olderrno == 0) 99295Seric ExitStat = EX_SOFTWARE; 100295Seric else 1011598Seric ExitStat = EX_OSERR; 10266323Seric if (tTd(54, 1)) 10366323Seric printf("syserr: ExitStat = %d\n", ExitStat); 104295Seric } 105295Seric 106295Seric # ifdef LOG 10766006Seric pw = getpwuid(getuid()); 10866006Seric if (pw != NULL) 10966006Seric uname = pw->pw_name; 11066006Seric else 11166006Seric { 11266006Seric uname = ubuf; 11366006Seric sprintf(ubuf, "UID%d", getuid()); 11466006Seric } 11566006Seric 1167674Seric if (LogLevel > 0) 11766006Seric syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s", 11825277Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 11966006Seric uname, &MsgBuf[4]); 12056795Seric # endif /* LOG */ 12164725Seric if (olderrno == EMFILE) 12264731Seric { 12364725Seric printopenfds(TRUE); 12464731Seric mci_dump_all(TRUE); 12564731Seric } 12658690Seric if (panic) 12759156Seric { 12859156Seric #ifdef XLA 12959156Seric xla_all_end(); 13059156Seric #endif 13158690Seric exit(EX_OSERR); 13259156Seric } 133295Seric errno = 0; 1347762Seric if (QuickAbort) 1357762Seric longjmp(TopFrame, 2); 136295Seric } 137295Seric /* 138295Seric ** USRERR -- Signal user error. 139295Seric ** 140295Seric ** This is much like syserr except it is for user errors. 141295Seric ** 142295Seric ** Parameters: 143*67818Seric ** fmt -- the format string. If it does not begin with 144*67818Seric ** a three-digit SMTP reply code, 501 is assumed. 145*67818Seric ** (others) -- printf strings 146295Seric ** 147295Seric ** Returns: 1484084Seric ** none 1497762Seric ** Through TopFrame if QuickAbort is set. 150295Seric ** 151295Seric ** Side Effects: 1521514Seric ** increments Errors. 153295Seric */ 154295Seric 155295Seric /*VARARGS1*/ 15658824Seric void 15757642Seric #ifdef __STDC__ 15860094Seric usrerr(const char *fmt, ...) 15957642Seric #else 16057642Seric usrerr(fmt, va_alist) 16160094Seric const char *fmt; 16257642Seric va_dcl 16357642Seric #endif 164295Seric { 16556852Seric VA_LOCAL_DECL 166295Seric 167295Seric if (SuprErrs) 1684084Seric return; 169295Seric 17056852Seric VA_START(fmt); 17158524Seric fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); 17256852Seric VA_END; 1739389Seric puterrmsg(MsgBuf); 1748239Seric 175*67818Seric /* save this message for mailq printing */ 176*67818Seric if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4')) 177*67818Seric { 178*67818Seric if (CurEnv->e_message != NULL) 179*67818Seric free(CurEnv->e_message); 180*67818Seric CurEnv->e_message = newstr(MsgBuf + 4); 181*67818Seric } 182*67818Seric 18351951Seric # ifdef LOG 18458020Seric if (LogLevel > 3 && LogUsrErrs) 18551951Seric syslog(LOG_NOTICE, "%s: %s", 18651951Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 18751951Seric &MsgBuf[4]); 18856795Seric # endif /* LOG */ 18951951Seric 1907762Seric if (QuickAbort) 1917762Seric longjmp(TopFrame, 1); 1924063Seric } 1934063Seric /* 1944063Seric ** MESSAGE -- print message (not necessarily an error) 1954063Seric ** 1964063Seric ** Parameters: 19759581Seric ** msg -- the message (printf fmt) -- it can begin with 19859581Seric ** an SMTP reply code. If not, 050 is assumed. 199*67818Seric ** (others) -- printf arguments 2004063Seric ** 2014063Seric ** Returns: 2024063Seric ** none 2034063Seric ** 2044063Seric ** Side Effects: 2054063Seric ** none. 2064063Seric */ 2074063Seric 2084084Seric /*VARARGS2*/ 20958826Seric void 21057642Seric #ifdef __STDC__ 21160094Seric message(const char *msg, ...) 21257642Seric #else 21358151Seric message(msg, va_alist) 21460094Seric const char *msg; 21557642Seric va_dcl 21657642Seric #endif 2174063Seric { 21856852Seric VA_LOCAL_DECL 21956852Seric 2204711Seric errno = 0; 22156852Seric VA_START(msg); 22258151Seric fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); 22356852Seric VA_END; 22463753Seric putoutmsg(MsgBuf, FALSE); 225*67818Seric 226*67818Seric /* save this message for mailq printing */ 227*67818Seric if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4')) 228*67818Seric { 229*67818Seric if (CurEnv->e_message != NULL) 230*67818Seric free(CurEnv->e_message); 231*67818Seric CurEnv->e_message = newstr(MsgBuf + 4); 232*67818Seric } 2337613Seric } 2347613Seric /* 2358239Seric ** NMESSAGE -- print message (not necessarily an error) 2368239Seric ** 2378239Seric ** Just like "message" except it never puts the to... tag on. 2388239Seric ** 2398239Seric ** Parameters: 2408239Seric ** msg -- the message (printf fmt) -- if it begins 241*67818Seric ** with a three digit SMTP reply code, that is used, 242*67818Seric ** otherwise 050 is assumed. 243*67818Seric ** (others) -- printf arguments 2448239Seric ** 2458239Seric ** Returns: 2468239Seric ** none 2478239Seric ** 2488239Seric ** Side Effects: 2498239Seric ** none. 2508239Seric */ 2518239Seric 2528239Seric /*VARARGS2*/ 25358826Seric void 25457642Seric #ifdef __STDC__ 25560094Seric nmessage(const char *msg, ...) 25657642Seric #else 25758151Seric nmessage(msg, va_alist) 25860094Seric const char *msg; 25957642Seric va_dcl 26057642Seric #endif 2618239Seric { 26256852Seric VA_LOCAL_DECL 26356852Seric 2648239Seric errno = 0; 26556852Seric VA_START(msg); 26658151Seric fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); 26756852Seric VA_END; 26863753Seric putoutmsg(MsgBuf, FALSE); 2698239Seric } 2708239Seric /* 27163753Seric ** PUTOUTMSG -- output error message to transcript and channel 2727613Seric ** 2737613Seric ** Parameters: 2747613Seric ** msg -- message to output (in SMTP format). 2759108Seric ** holdmsg -- if TRUE, don't output a copy of the message to 2769108Seric ** our output channel. 2777613Seric ** 2787613Seric ** Returns: 2797613Seric ** none. 2807613Seric ** 2817613Seric ** Side Effects: 2827613Seric ** Outputs msg to the transcript. 2837613Seric ** If appropriate, outputs it to the channel. 2847613Seric ** Deletes SMTP reply code number as appropriate. 2857613Seric */ 2864711Seric 28763753Seric putoutmsg(msg, holdmsg) 2887613Seric char *msg; 2899108Seric bool holdmsg; 2907613Seric { 29164249Seric /* display for debugging */ 29264249Seric if (tTd(54, 8)) 29364249Seric printf("--- %s%s\n", msg, holdmsg ? " (held)" : ""); 29464249Seric 29514900Seric /* output to transcript if serious */ 29663848Seric if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL) 29714900Seric fprintf(CurEnv->e_xfp, "%s\n", msg); 2984711Seric 2994711Seric /* output to channel if appropriate */ 30060421Seric if (holdmsg || (!Verbose && msg[0] == '0')) 30160421Seric return; 30260421Seric 30363848Seric /* map warnings to something SMTP can handle */ 30463848Seric if (msg[0] == '6') 30563848Seric msg[0] = '5'; 30663848Seric 30760421Seric (void) fflush(stdout); 30866017Seric 30966017Seric /* if DisConnected, OutChannel now points to the transcript */ 31066017Seric if (!DisConnected && 31166017Seric (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 31260421Seric fprintf(OutChannel, "%s\r\n", msg); 31360421Seric else 31460421Seric fprintf(OutChannel, "%s\n", &msg[4]); 31563753Seric if (TrafficLogFile != NULL) 31663753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), 31765580Seric (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]); 31860421Seric if (msg[3] == ' ') 31960421Seric (void) fflush(OutChannel); 32066017Seric if (!ferror(OutChannel) || DisConnected) 32160421Seric return; 32260421Seric 32364499Seric /* 32464499Seric ** Error on output -- if reporting lost channel, just ignore it. 32564499Seric ** Also, ignore errors from QUIT response (221 message) -- some 32664499Seric ** rude servers don't read result. 32764499Seric */ 32864499Seric 32964499Seric if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0) 33060421Seric return; 33160421Seric 33260421Seric /* can't call syserr, 'cause we are using MsgBuf */ 33360421Seric HoldErrs = TRUE; 33460283Seric #ifdef LOG 33560421Seric if (LogLevel > 0) 33660421Seric syslog(LOG_CRIT, 33766864Seric "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 33860421Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 33964123Seric CurHostName == NULL ? "NO-HOST" : CurHostName, 34066864Seric msg, errstring(errno)); 34160283Seric #endif 3429389Seric } 3439389Seric /* 34463753Seric ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 3459389Seric ** 3469389Seric ** Parameters: 3479389Seric ** msg -- the message to output. 3489389Seric ** 3499389Seric ** Returns: 3509389Seric ** none. 3519389Seric ** 3529389Seric ** Side Effects: 3539389Seric ** Sets the fatal error bit in the envelope as appropriate. 3549389Seric */ 3558239Seric 3569389Seric puterrmsg(msg) 3579389Seric char *msg; 3589389Seric { 35963848Seric char msgcode = msg[0]; 36063848Seric 3619389Seric /* output the message as usual */ 36263753Seric putoutmsg(msg, HoldErrs); 3639389Seric 3649389Seric /* signal the error */ 36564773Seric Errors++; 36663848Seric if (msgcode == '6') 36763848Seric { 36863848Seric /* notify the postmaster */ 36963848Seric CurEnv->e_flags |= EF_PM_NOTIFY; 37063848Seric } 37167786Seric else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 37264773Seric { 37364773Seric /* mark long-term fatal errors */ 37464773Seric CurEnv->e_flags |= EF_FATALERRS; 37564773Seric } 3764711Seric } 3774711Seric /* 3784711Seric ** FMTMSG -- format a message into buffer. 3794711Seric ** 3804711Seric ** Parameters: 3814711Seric ** eb -- error buffer to get result. 3824711Seric ** to -- the recipient tag for this message. 3834711Seric ** num -- arpanet error number. 38416901Seric ** en -- the error number to display. 3854711Seric ** fmt -- format of string. 3864711Seric ** a, b, c, d, e -- arguments. 3874711Seric ** 3884711Seric ** Returns: 3894711Seric ** none. 3904711Seric ** 3914711Seric ** Side Effects: 3924711Seric ** none. 3934711Seric */ 3944063Seric 39546928Sbostic static void 39656852Seric fmtmsg(eb, to, num, eno, fmt, ap) 3974711Seric register char *eb; 3984711Seric char *to; 3994711Seric char *num; 40016904Seric int eno; 4014711Seric char *fmt; 40256852Seric va_list ap; 4034711Seric { 4044711Seric char del; 40559596Seric char *meb; 4064711Seric 4074711Seric /* output the reply code */ 40824943Seric if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2])) 4094577Seric { 4104711Seric num = fmt; 4114711Seric fmt += 4; 4124711Seric } 4134711Seric if (num[3] == '-') 4144711Seric del = '-'; 4154711Seric else 4164711Seric del = ' '; 4174711Seric (void) sprintf(eb, "%3.3s%c", num, del); 4184711Seric eb += 4; 4194063Seric 4209372Seric /* output the file name and line number */ 4219372Seric if (FileName != NULL) 4229372Seric { 4239372Seric (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber); 4249372Seric eb += strlen(eb); 4259372Seric } 4269372Seric 4274711Seric /* output the "to" person */ 4284711Seric if (to != NULL && to[0] != '\0') 4294711Seric { 43066297Seric (void) sprintf(eb, "%s... ", shortenstring(to, 203)); 4315201Seric while (*eb != '\0') 4325201Seric *eb++ &= 0177; 4334711Seric } 4344711Seric 43559596Seric meb = eb; 43659596Seric 4374711Seric /* output the message */ 43856852Seric (void) vsprintf(eb, fmt, ap); 4395201Seric while (*eb != '\0') 4405201Seric *eb++ &= 0177; 4414711Seric 4424711Seric /* output the error code, if any */ 44316904Seric if (eno != 0) 4444711Seric { 44516904Seric (void) sprintf(eb, ": %s", errstring(eno)); 4464711Seric eb += strlen(eb); 4474577Seric } 448295Seric } 44915136Seric /* 45015136Seric ** ERRSTRING -- return string description of error code 45115136Seric ** 45215136Seric ** Parameters: 45365751Seric ** errnum -- the error number to translate 45415136Seric ** 45515136Seric ** Returns: 45665751Seric ** A string description of errnum. 45715136Seric ** 45815136Seric ** Side Effects: 45915136Seric ** none. 46015136Seric */ 46115136Seric 46260089Seric const char * 46365751Seric errstring(errnum) 46465751Seric int errnum; 46515136Seric { 46665168Seric char *dnsmsg; 46763839Seric static char buf[MAXLINE]; 46863839Seric # ifndef ERRLIST_PREDEFINED 46963839Seric extern char *sys_errlist[]; 47015136Seric extern int sys_nerr; 47163839Seric # endif 47224943Seric # ifdef SMTP 47324943Seric extern char *SmtpPhase; 47456795Seric # endif /* SMTP */ 47515136Seric 47624943Seric /* 47724943Seric ** Handle special network error codes. 47824943Seric ** 47924943Seric ** These are 4.2/4.3bsd specific; they should be in daemon.c. 48024943Seric */ 48124943Seric 48265168Seric dnsmsg = NULL; 48365751Seric switch (errnum) 48424943Seric { 48565067Seric # if defined(DAEMON) && defined(ETIMEDOUT) 48624943Seric case ETIMEDOUT: 48724943Seric case ECONNRESET: 48865751Seric (void) strcpy(buf, sys_errlist[errnum]); 48924943Seric if (SmtpPhase != NULL) 49024943Seric { 49124943Seric (void) strcat(buf, " during "); 49224943Seric (void) strcat(buf, SmtpPhase); 49324943Seric } 49425050Seric if (CurHostName != NULL) 49524943Seric { 49624943Seric (void) strcat(buf, " with "); 49725050Seric (void) strcat(buf, CurHostName); 49824943Seric } 49924943Seric return (buf); 50024943Seric 50124943Seric case EHOSTDOWN: 50225050Seric if (CurHostName == NULL) 50324943Seric break; 50425050Seric (void) sprintf(buf, "Host %s is down", CurHostName); 50524943Seric return (buf); 50624943Seric 50724943Seric case ECONNREFUSED: 50825050Seric if (CurHostName == NULL) 50924943Seric break; 51025050Seric (void) sprintf(buf, "Connection refused by %s", CurHostName); 51124943Seric return (buf); 51265067Seric # endif 51325526Smiriam 51463993Seric case EOPENTIMEOUT: 51563993Seric return "Timeout on file open"; 51663993Seric 51766334Seric # if NAMED_BIND 51863993Seric case HOST_NOT_FOUND + E_DNSBASE: 51965168Seric dnsmsg = "host not found"; 52065168Seric break; 52158010Seric 52263993Seric case TRY_AGAIN + E_DNSBASE: 52365168Seric dnsmsg = "host name lookup failure"; 52465168Seric break; 52558010Seric 52663993Seric case NO_RECOVERY + E_DNSBASE: 52765168Seric dnsmsg = "non-recoverable error"; 52865168Seric break; 52958010Seric 53063993Seric case NO_DATA + E_DNSBASE: 53165168Seric dnsmsg = "no data known"; 53265168Seric break; 53357736Seric # endif 53465067Seric 53565067Seric case EPERM: 53665067Seric /* SunOS gives "Not owner" -- this is the POSIX message */ 53765067Seric return "Operation not permitted"; 53824943Seric } 53924943Seric 54065168Seric if (dnsmsg != NULL) 54165168Seric { 54265168Seric (void) strcpy(buf, "Name server: "); 54365168Seric if (CurHostName != NULL) 54465168Seric { 54565168Seric (void) strcat(buf, CurHostName); 54665168Seric (void) strcat(buf, ": "); 54765168Seric } 54865168Seric (void) strcat(buf, dnsmsg); 54965168Seric return buf; 55065168Seric } 55165168Seric 55265751Seric if (errnum > 0 && errnum < sys_nerr) 55365751Seric return (sys_errlist[errnum]); 55415136Seric 55565751Seric (void) sprintf(buf, "Error %d", errnum); 55615136Seric return (buf); 55715136Seric } 558