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*65168Seric static char sccsid[] = "@(#)err.c 8.18 (Berkeley) 12/17/93"; 1133729Sbostic #endif /* not lint */ 1222705Sdist 133311Seric # include "sendmail.h" 1424943Seric # include <errno.h> 1525526Smiriam # include <netdb.h> 16295Seric 17295Seric /* 181514Seric ** SYSERR -- Print error message. 19295Seric ** 20295Seric ** Prints an error message via printf to the diagnostic 21295Seric ** output. If LOG is defined, it logs it also. 22295Seric ** 2358690Seric ** If the first character of the syserr message is `!' it will 2458690Seric ** log this as an ALERT message and exit immediately. This can 2558690Seric ** leave queue files in an indeterminate state, so it should not 2658690Seric ** be used lightly. 2758690Seric ** 28295Seric ** Parameters: 29295Seric ** f -- the format string 30295Seric ** a, b, c, d, e -- parameters 31295Seric ** 32295Seric ** Returns: 334084Seric ** none 347762Seric ** Through TopFrame if QuickAbort is set. 35295Seric ** 36295Seric ** Side Effects: 371514Seric ** increments Errors. 381514Seric ** sets ExitStat. 39295Seric */ 40295Seric 4110147Seric char MsgBuf[BUFSIZ*2]; /* text of most recent message */ 424084Seric 4363969Seric static void fmtmsg(); 4446928Sbostic 4563969Seric #if defined(NAMED_BIND) && !defined(NO_DATA) 4663969Seric # define NO_DATA NO_ADDRESS 4763969Seric #endif 4863969Seric 4958824Seric void 50295Seric /*VARARGS1*/ 5157642Seric #ifdef __STDC__ 5260094Seric syserr(const char *fmt, ...) 5357642Seric #else 5457642Seric syserr(fmt, va_alist) 5560094Seric const char *fmt; 5657642Seric va_dcl 5757642Seric #endif 58295Seric { 5916901Seric register char *p; 6016901Seric int olderrno = errno; 6158690Seric bool panic; 6256852Seric VA_LOCAL_DECL 63295Seric 6458690Seric panic = *fmt == '!'; 6558690Seric if (panic) 6658690Seric fmt++; 6758690Seric 687525Seric /* format and output the error message */ 6916901Seric if (olderrno == 0) 7058151Seric p = "554"; 717957Seric else 7258151Seric p = "451"; 7356852Seric VA_START(fmt); 7456852Seric fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); 7556852Seric VA_END; 769389Seric puterrmsg(MsgBuf); 774063Seric 78295Seric /* determine exit status if not already set */ 79295Seric if (ExitStat == EX_OK) 80295Seric { 8116901Seric if (olderrno == 0) 82295Seric ExitStat = EX_SOFTWARE; 83295Seric else 841598Seric ExitStat = EX_OSERR; 85295Seric } 86295Seric 87295Seric # ifdef LOG 887674Seric if (LogLevel > 0) 8958690Seric syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR: %s", 9025277Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 9125277Seric &MsgBuf[4]); 9256795Seric # endif /* LOG */ 9364725Seric if (olderrno == EMFILE) 9464731Seric { 9564725Seric printopenfds(TRUE); 9664731Seric mci_dump_all(TRUE); 9764731Seric } 9858690Seric if (panic) 9959156Seric { 10059156Seric #ifdef XLA 10159156Seric xla_all_end(); 10259156Seric #endif 10358690Seric exit(EX_OSERR); 10459156Seric } 105295Seric errno = 0; 1067762Seric if (QuickAbort) 1077762Seric longjmp(TopFrame, 2); 108295Seric } 109295Seric /* 110295Seric ** USRERR -- Signal user error. 111295Seric ** 112295Seric ** This is much like syserr except it is for user errors. 113295Seric ** 114295Seric ** Parameters: 115295Seric ** fmt, a, b, c, d -- printf strings 116295Seric ** 117295Seric ** Returns: 1184084Seric ** none 1197762Seric ** Through TopFrame if QuickAbort is set. 120295Seric ** 121295Seric ** Side Effects: 1221514Seric ** increments Errors. 123295Seric */ 124295Seric 125295Seric /*VARARGS1*/ 12658824Seric void 12757642Seric #ifdef __STDC__ 12860094Seric usrerr(const char *fmt, ...) 12957642Seric #else 13057642Seric usrerr(fmt, va_alist) 13160094Seric const char *fmt; 13257642Seric va_dcl 13357642Seric #endif 134295Seric { 13556852Seric VA_LOCAL_DECL 136295Seric 137295Seric if (SuprErrs) 1384084Seric return; 139295Seric 14056852Seric VA_START(fmt); 14158524Seric fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); 14256852Seric VA_END; 1439389Seric puterrmsg(MsgBuf); 1448239Seric 14551951Seric # ifdef LOG 14658020Seric if (LogLevel > 3 && LogUsrErrs) 14751951Seric syslog(LOG_NOTICE, "%s: %s", 14851951Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 14951951Seric &MsgBuf[4]); 15056795Seric # endif /* LOG */ 15151951Seric 1527762Seric if (QuickAbort) 1537762Seric longjmp(TopFrame, 1); 1544063Seric } 1554063Seric /* 1564063Seric ** MESSAGE -- print message (not necessarily an error) 1574063Seric ** 1584063Seric ** Parameters: 15959581Seric ** msg -- the message (printf fmt) -- it can begin with 16059581Seric ** an SMTP reply code. If not, 050 is assumed. 1614063Seric ** a, b, c, d, e -- printf arguments 1624063Seric ** 1634063Seric ** Returns: 1644063Seric ** none 1654063Seric ** 1664063Seric ** Side Effects: 1674063Seric ** none. 1684063Seric */ 1694063Seric 1704084Seric /*VARARGS2*/ 17158826Seric void 17257642Seric #ifdef __STDC__ 17360094Seric message(const char *msg, ...) 17457642Seric #else 17558151Seric message(msg, va_alist) 17660094Seric const char *msg; 17757642Seric va_dcl 17857642Seric #endif 1794063Seric { 18056852Seric VA_LOCAL_DECL 18156852Seric 1824711Seric errno = 0; 18356852Seric VA_START(msg); 18458151Seric fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); 18556852Seric VA_END; 18663753Seric putoutmsg(MsgBuf, FALSE); 1877613Seric } 1887613Seric /* 1898239Seric ** NMESSAGE -- print message (not necessarily an error) 1908239Seric ** 1918239Seric ** Just like "message" except it never puts the to... tag on. 1928239Seric ** 1938239Seric ** Parameters: 1948239Seric ** num -- the default ARPANET error number (in ascii) 1958239Seric ** msg -- the message (printf fmt) -- if it begins 19624943Seric ** with three digits, this number overrides num. 1978239Seric ** a, b, c, d, e -- printf arguments 1988239Seric ** 1998239Seric ** Returns: 2008239Seric ** none 2018239Seric ** 2028239Seric ** Side Effects: 2038239Seric ** none. 2048239Seric */ 2058239Seric 2068239Seric /*VARARGS2*/ 20758826Seric void 20857642Seric #ifdef __STDC__ 20960094Seric nmessage(const char *msg, ...) 21057642Seric #else 21158151Seric nmessage(msg, va_alist) 21260094Seric const char *msg; 21357642Seric va_dcl 21457642Seric #endif 2158239Seric { 21656852Seric VA_LOCAL_DECL 21756852Seric 2188239Seric errno = 0; 21956852Seric VA_START(msg); 22058151Seric fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); 22156852Seric VA_END; 22263753Seric putoutmsg(MsgBuf, FALSE); 2238239Seric } 2248239Seric /* 22563753Seric ** PUTOUTMSG -- output error message to transcript and channel 2267613Seric ** 2277613Seric ** Parameters: 2287613Seric ** msg -- message to output (in SMTP format). 2299108Seric ** holdmsg -- if TRUE, don't output a copy of the message to 2309108Seric ** our output channel. 2317613Seric ** 2327613Seric ** Returns: 2337613Seric ** none. 2347613Seric ** 2357613Seric ** Side Effects: 2367613Seric ** Outputs msg to the transcript. 2377613Seric ** If appropriate, outputs it to the channel. 2387613Seric ** Deletes SMTP reply code number as appropriate. 2397613Seric */ 2404711Seric 24163753Seric putoutmsg(msg, holdmsg) 2427613Seric char *msg; 2439108Seric bool holdmsg; 2447613Seric { 24564249Seric /* display for debugging */ 24664249Seric if (tTd(54, 8)) 24764249Seric printf("--- %s%s\n", msg, holdmsg ? " (held)" : ""); 24864249Seric 24914900Seric /* output to transcript if serious */ 25063848Seric if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL) 25114900Seric fprintf(CurEnv->e_xfp, "%s\n", msg); 2524711Seric 2534711Seric /* output to channel if appropriate */ 25460421Seric if (holdmsg || (!Verbose && msg[0] == '0')) 25560421Seric return; 25660421Seric 25763848Seric /* map warnings to something SMTP can handle */ 25863848Seric if (msg[0] == '6') 25963848Seric msg[0] = '5'; 26063848Seric 26160421Seric (void) fflush(stdout); 26260421Seric if (OpMode == MD_SMTP) 26360421Seric fprintf(OutChannel, "%s\r\n", msg); 26460421Seric else 26560421Seric fprintf(OutChannel, "%s\n", &msg[4]); 26663753Seric if (TrafficLogFile != NULL) 26763753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), 26863753Seric OpMode == MD_SMTP ? msg : &msg[4]); 26960421Seric if (msg[3] == ' ') 27060421Seric (void) fflush(OutChannel); 27160421Seric if (!ferror(OutChannel)) 27260421Seric return; 27360421Seric 27464499Seric /* 27564499Seric ** Error on output -- if reporting lost channel, just ignore it. 27664499Seric ** Also, ignore errors from QUIT response (221 message) -- some 27764499Seric ** rude servers don't read result. 27864499Seric */ 27964499Seric 28064499Seric if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0) 28160421Seric return; 28260421Seric 28360421Seric /* can't call syserr, 'cause we are using MsgBuf */ 28460421Seric HoldErrs = TRUE; 28560283Seric #ifdef LOG 28660421Seric if (LogLevel > 0) 28760421Seric syslog(LOG_CRIT, 28864499Seric "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %m", 28960421Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 29064123Seric CurHostName == NULL ? "NO-HOST" : CurHostName, 29164123Seric msg); 29260283Seric #endif 2939389Seric } 2949389Seric /* 29563753Seric ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 2969389Seric ** 2979389Seric ** Parameters: 2989389Seric ** msg -- the message to output. 2999389Seric ** 3009389Seric ** Returns: 3019389Seric ** none. 3029389Seric ** 3039389Seric ** Side Effects: 3049389Seric ** Sets the fatal error bit in the envelope as appropriate. 3059389Seric */ 3068239Seric 3079389Seric puterrmsg(msg) 3089389Seric char *msg; 3099389Seric { 31063848Seric char msgcode = msg[0]; 31163848Seric 3129389Seric /* output the message as usual */ 31363753Seric putoutmsg(msg, HoldErrs); 3149389Seric 3159389Seric /* signal the error */ 31664773Seric Errors++; 31763848Seric if (msgcode == '6') 31863848Seric { 31963848Seric /* notify the postmaster */ 32063848Seric CurEnv->e_flags |= EF_PM_NOTIFY; 32163848Seric } 32264773Seric else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 32364773Seric { 32464773Seric /* mark long-term fatal errors */ 32564773Seric CurEnv->e_flags |= EF_FATALERRS; 32664773Seric } 3274711Seric } 3284711Seric /* 3294711Seric ** FMTMSG -- format a message into buffer. 3304711Seric ** 3314711Seric ** Parameters: 3324711Seric ** eb -- error buffer to get result. 3334711Seric ** to -- the recipient tag for this message. 3344711Seric ** num -- arpanet error number. 33516901Seric ** en -- the error number to display. 3364711Seric ** fmt -- format of string. 3374711Seric ** a, b, c, d, e -- arguments. 3384711Seric ** 3394711Seric ** Returns: 3404711Seric ** none. 3414711Seric ** 3424711Seric ** Side Effects: 3434711Seric ** none. 3444711Seric */ 3454063Seric 34646928Sbostic static void 34756852Seric fmtmsg(eb, to, num, eno, fmt, ap) 3484711Seric register char *eb; 3494711Seric char *to; 3504711Seric char *num; 35116904Seric int eno; 3524711Seric char *fmt; 35356852Seric va_list ap; 3544711Seric { 3554711Seric char del; 35659596Seric char *meb; 3574711Seric 3584711Seric /* output the reply code */ 35924943Seric if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2])) 3604577Seric { 3614711Seric num = fmt; 3624711Seric fmt += 4; 3634711Seric } 3644711Seric if (num[3] == '-') 3654711Seric del = '-'; 3664711Seric else 3674711Seric del = ' '; 3684711Seric (void) sprintf(eb, "%3.3s%c", num, del); 3694711Seric eb += 4; 3704063Seric 3719372Seric /* output the file name and line number */ 3729372Seric if (FileName != NULL) 3739372Seric { 3749372Seric (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber); 3759372Seric eb += strlen(eb); 3769372Seric } 3779372Seric 3784711Seric /* output the "to" person */ 3794711Seric if (to != NULL && to[0] != '\0') 3804711Seric { 3814711Seric (void) sprintf(eb, "%s... ", to); 3825201Seric while (*eb != '\0') 3835201Seric *eb++ &= 0177; 3844711Seric } 3854711Seric 38659596Seric meb = eb; 38759596Seric 3884711Seric /* output the message */ 38956852Seric (void) vsprintf(eb, fmt, ap); 3905201Seric while (*eb != '\0') 3915201Seric *eb++ &= 0177; 3924711Seric 3934711Seric /* output the error code, if any */ 39416904Seric if (eno != 0) 3954711Seric { 39616904Seric (void) sprintf(eb, ": %s", errstring(eno)); 3974711Seric eb += strlen(eb); 3984577Seric } 39959596Seric 40064695Seric if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4')) 40164695Seric { 40264695Seric if (CurEnv->e_message != NULL) 40364695Seric free(CurEnv->e_message); 40459596Seric CurEnv->e_message = newstr(meb); 40564695Seric } 406295Seric } 40715136Seric /* 40815136Seric ** ERRSTRING -- return string description of error code 40915136Seric ** 41015136Seric ** Parameters: 41115136Seric ** errno -- the error number to translate 41215136Seric ** 41315136Seric ** Returns: 41415136Seric ** A string description of errno. 41515136Seric ** 41615136Seric ** Side Effects: 41715136Seric ** none. 41815136Seric */ 41915136Seric 42060089Seric const char * 42115136Seric errstring(errno) 42215136Seric int errno; 42315136Seric { 424*65168Seric char *dnsmsg; 42563839Seric static char buf[MAXLINE]; 42663839Seric # ifndef ERRLIST_PREDEFINED 42763839Seric extern char *sys_errlist[]; 42815136Seric extern int sys_nerr; 42963839Seric # endif 43024943Seric # ifdef SMTP 43124943Seric extern char *SmtpPhase; 43256795Seric # endif /* SMTP */ 43315136Seric 43424943Seric /* 43524943Seric ** Handle special network error codes. 43624943Seric ** 43724943Seric ** These are 4.2/4.3bsd specific; they should be in daemon.c. 43824943Seric */ 43924943Seric 440*65168Seric dnsmsg = NULL; 44124943Seric switch (errno) 44224943Seric { 44365067Seric # if defined(DAEMON) && defined(ETIMEDOUT) 44424943Seric case ETIMEDOUT: 44524943Seric case ECONNRESET: 44624943Seric (void) strcpy(buf, sys_errlist[errno]); 44724943Seric if (SmtpPhase != NULL) 44824943Seric { 44924943Seric (void) strcat(buf, " during "); 45024943Seric (void) strcat(buf, SmtpPhase); 45124943Seric } 45225050Seric if (CurHostName != NULL) 45324943Seric { 45424943Seric (void) strcat(buf, " with "); 45525050Seric (void) strcat(buf, CurHostName); 45624943Seric } 45724943Seric return (buf); 45824943Seric 45924943Seric case EHOSTDOWN: 46025050Seric if (CurHostName == NULL) 46124943Seric break; 46225050Seric (void) sprintf(buf, "Host %s is down", CurHostName); 46324943Seric return (buf); 46424943Seric 46524943Seric case ECONNREFUSED: 46625050Seric if (CurHostName == NULL) 46724943Seric break; 46825050Seric (void) sprintf(buf, "Connection refused by %s", CurHostName); 46924943Seric return (buf); 47065067Seric # endif 47125526Smiriam 47263993Seric case EOPENTIMEOUT: 47363993Seric return "Timeout on file open"; 47463993Seric 47557736Seric # ifdef NAMED_BIND 47663993Seric case HOST_NOT_FOUND + E_DNSBASE: 477*65168Seric dnsmsg = "host not found"; 478*65168Seric break; 47958010Seric 48063993Seric case TRY_AGAIN + E_DNSBASE: 481*65168Seric dnsmsg = "host name lookup failure"; 482*65168Seric break; 48358010Seric 48463993Seric case NO_RECOVERY + E_DNSBASE: 485*65168Seric dnsmsg = "non-recoverable error"; 486*65168Seric break; 48758010Seric 48863993Seric case NO_DATA + E_DNSBASE: 489*65168Seric dnsmsg = "no data known"; 490*65168Seric break; 49157736Seric # endif 49265067Seric 49365067Seric case EPERM: 49465067Seric /* SunOS gives "Not owner" -- this is the POSIX message */ 49565067Seric return "Operation not permitted"; 49624943Seric } 49724943Seric 498*65168Seric if (dnsmsg != NULL) 499*65168Seric { 500*65168Seric (void) strcpy(buf, "Name server: "); 501*65168Seric if (CurHostName != NULL) 502*65168Seric { 503*65168Seric (void) strcat(buf, CurHostName); 504*65168Seric (void) strcat(buf, ": "); 505*65168Seric } 506*65168Seric (void) strcat(buf, dnsmsg); 507*65168Seric return buf; 508*65168Seric } 509*65168Seric 51015136Seric if (errno > 0 && errno < sys_nerr) 51115136Seric return (sys_errlist[errno]); 51215136Seric 51315136Seric (void) sprintf(buf, "Error %d", errno); 51415136Seric return (buf); 51515136Seric } 516