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*69800Seric static char sccsid[] = "@(#)err.c 8.35 (Berkeley) 06/05/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
4669748Seric extern void putoutmsg __P((char *, bool, bool));
4769748Seric extern void puterrmsg __P((char *));
4869748Seric 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__
syserr(const char * fmt,...)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
132*69800Seric if (tTd(0, 1))
133*69800Seric abort();
13458690Seric exit(EX_OSERR);
13559156Seric }
136295Seric errno = 0;
1377762Seric if (QuickAbort)
1387762Seric longjmp(TopFrame, 2);
139295Seric }
140295Seric /*
141295Seric ** USRERR -- Signal user error.
142295Seric **
143295Seric ** This is much like syserr except it is for user errors.
144295Seric **
145295Seric ** Parameters:
14667818Seric ** fmt -- the format string. If it does not begin with
14767818Seric ** a three-digit SMTP reply code, 501 is assumed.
14867818Seric ** (others) -- printf strings
149295Seric **
150295Seric ** Returns:
1514084Seric ** none
1527762Seric ** Through TopFrame if QuickAbort is set.
153295Seric **
154295Seric ** Side Effects:
1551514Seric ** increments Errors.
156295Seric */
157295Seric
158295Seric /*VARARGS1*/
15958824Seric void
16057642Seric #ifdef __STDC__
usrerr(const char * fmt,...)16160094Seric usrerr(const char *fmt, ...)
16257642Seric #else
16357642Seric usrerr(fmt, va_alist)
16460094Seric const char *fmt;
16557642Seric va_dcl
16657642Seric #endif
167295Seric {
16856852Seric VA_LOCAL_DECL
169295Seric
170295Seric if (SuprErrs)
1714084Seric return;
172295Seric
17356852Seric VA_START(fmt);
17458524Seric fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
17556852Seric VA_END;
1769389Seric puterrmsg(MsgBuf);
1778239Seric
17867818Seric /* save this message for mailq printing */
17967818Seric if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4'))
18067818Seric {
18167818Seric if (CurEnv->e_message != NULL)
18267818Seric free(CurEnv->e_message);
18367818Seric CurEnv->e_message = newstr(MsgBuf + 4);
18467818Seric }
18567818Seric
18651951Seric # ifdef LOG
18758020Seric if (LogLevel > 3 && LogUsrErrs)
18851951Seric syslog(LOG_NOTICE, "%s: %s",
18951951Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
19051951Seric &MsgBuf[4]);
19156795Seric # endif /* LOG */
19251951Seric
1937762Seric if (QuickAbort)
1947762Seric longjmp(TopFrame, 1);
1954063Seric }
1964063Seric /*
1974063Seric ** MESSAGE -- print message (not necessarily an error)
1984063Seric **
1994063Seric ** Parameters:
20059581Seric ** msg -- the message (printf fmt) -- it can begin with
20159581Seric ** an SMTP reply code. If not, 050 is assumed.
20267818Seric ** (others) -- printf arguments
2034063Seric **
2044063Seric ** Returns:
2054063Seric ** none
2064063Seric **
2074063Seric ** Side Effects:
2084063Seric ** none.
2094063Seric */
2104063Seric
2114084Seric /*VARARGS2*/
21258826Seric void
21357642Seric #ifdef __STDC__
message(const char * msg,...)21460094Seric message(const char *msg, ...)
21557642Seric #else
21658151Seric message(msg, va_alist)
21760094Seric const char *msg;
21857642Seric va_dcl
21957642Seric #endif
2204063Seric {
22156852Seric VA_LOCAL_DECL
22256852Seric
2234711Seric errno = 0;
22456852Seric VA_START(msg);
22558151Seric fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
22656852Seric VA_END;
22768692Seric putoutmsg(MsgBuf, FALSE, FALSE);
22867818Seric
22967818Seric /* save this message for mailq printing */
23067818Seric if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4'))
23167818Seric {
23267818Seric if (CurEnv->e_message != NULL)
23367818Seric free(CurEnv->e_message);
23467818Seric CurEnv->e_message = newstr(MsgBuf + 4);
23567818Seric }
2367613Seric }
2377613Seric /*
2388239Seric ** NMESSAGE -- print message (not necessarily an error)
2398239Seric **
2408239Seric ** Just like "message" except it never puts the to... tag on.
2418239Seric **
2428239Seric ** Parameters:
2438239Seric ** msg -- the message (printf fmt) -- if it begins
24467818Seric ** with a three digit SMTP reply code, that is used,
24567818Seric ** otherwise 050 is assumed.
24667818Seric ** (others) -- printf arguments
2478239Seric **
2488239Seric ** Returns:
2498239Seric ** none
2508239Seric **
2518239Seric ** Side Effects:
2528239Seric ** none.
2538239Seric */
2548239Seric
2558239Seric /*VARARGS2*/
25658826Seric void
25757642Seric #ifdef __STDC__
nmessage(const char * msg,...)25860094Seric nmessage(const char *msg, ...)
25957642Seric #else
26058151Seric nmessage(msg, va_alist)
26160094Seric const char *msg;
26257642Seric va_dcl
26357642Seric #endif
2648239Seric {
26556852Seric VA_LOCAL_DECL
26656852Seric
2678239Seric errno = 0;
26856852Seric VA_START(msg);
26958151Seric fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
27056852Seric VA_END;
27168692Seric putoutmsg(MsgBuf, FALSE, FALSE);
2728239Seric }
2738239Seric /*
27463753Seric ** PUTOUTMSG -- output error message to transcript and channel
2757613Seric **
2767613Seric ** Parameters:
2777613Seric ** msg -- message to output (in SMTP format).
2789108Seric ** holdmsg -- if TRUE, don't output a copy of the message to
2799108Seric ** our output channel.
28068692Seric ** heldmsg -- if TRUE, this is a previously held message;
28168692Seric ** don't log it to the transcript file.
2827613Seric **
2837613Seric ** Returns:
2847613Seric ** none.
2857613Seric **
2867613Seric ** Side Effects:
2877613Seric ** Outputs msg to the transcript.
2887613Seric ** If appropriate, outputs it to the channel.
2897613Seric ** Deletes SMTP reply code number as appropriate.
2907613Seric */
2914711Seric
29269748Seric void
putoutmsg(msg,holdmsg,heldmsg)29368692Seric putoutmsg(msg, holdmsg, heldmsg)
2947613Seric char *msg;
2959108Seric bool holdmsg;
29668692Seric bool heldmsg;
2977613Seric {
29864249Seric /* display for debugging */
29964249Seric if (tTd(54, 8))
30068692Seric printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
30168692Seric heldmsg ? " (held)" : "");
30264249Seric
30314900Seric /* output to transcript if serious */
30468692Seric if (!heldmsg && CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
30514900Seric fprintf(CurEnv->e_xfp, "%s\n", msg);
3064711Seric
3074711Seric /* output to channel if appropriate */
30868692Seric if (!Verbose && msg[0] == '0')
30960421Seric return;
31068692Seric if (holdmsg)
31168692Seric {
31268692Seric /* save for possible future display */
31368692Seric strcpy(HeldMessageBuf, msg);
31468692Seric return;
31568692Seric }
31660421Seric
31763848Seric /* map warnings to something SMTP can handle */
31863848Seric if (msg[0] == '6')
31963848Seric msg[0] = '5';
32063848Seric
32160421Seric (void) fflush(stdout);
32266017Seric
32366017Seric /* if DisConnected, OutChannel now points to the transcript */
32466017Seric if (!DisConnected &&
32566017Seric (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
32660421Seric fprintf(OutChannel, "%s\r\n", msg);
32760421Seric else
32860421Seric fprintf(OutChannel, "%s\n", &msg[4]);
32963753Seric if (TrafficLogFile != NULL)
33063753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(),
33165580Seric (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
33260421Seric if (msg[3] == ' ')
33360421Seric (void) fflush(OutChannel);
33466017Seric if (!ferror(OutChannel) || DisConnected)
33560421Seric return;
33660421Seric
33764499Seric /*
33864499Seric ** Error on output -- if reporting lost channel, just ignore it.
33964499Seric ** Also, ignore errors from QUIT response (221 message) -- some
34064499Seric ** rude servers don't read result.
34164499Seric */
34264499Seric
34364499Seric if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0)
34460421Seric return;
34560421Seric
34660421Seric /* can't call syserr, 'cause we are using MsgBuf */
34760421Seric HoldErrs = TRUE;
34860283Seric #ifdef LOG
34960421Seric if (LogLevel > 0)
35060421Seric syslog(LOG_CRIT,
35166864Seric "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
35260421Seric CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
35364123Seric CurHostName == NULL ? "NO-HOST" : CurHostName,
35466864Seric msg, errstring(errno));
35560283Seric #endif
3569389Seric }
3579389Seric /*
35863753Seric ** PUTERRMSG -- like putoutmsg, but does special processing for error messages
3599389Seric **
3609389Seric ** Parameters:
3619389Seric ** msg -- the message to output.
3629389Seric **
3639389Seric ** Returns:
3649389Seric ** none.
3659389Seric **
3669389Seric ** Side Effects:
3679389Seric ** Sets the fatal error bit in the envelope as appropriate.
3689389Seric */
3698239Seric
37069748Seric void
puterrmsg(msg)3719389Seric puterrmsg(msg)
3729389Seric char *msg;
3739389Seric {
37463848Seric char msgcode = msg[0];
37563848Seric
3769389Seric /* output the message as usual */
37768692Seric putoutmsg(msg, HoldErrs, FALSE);
3789389Seric
3799389Seric /* signal the error */
38064773Seric Errors++;
38163848Seric if (msgcode == '6')
38263848Seric {
38363848Seric /* notify the postmaster */
38463848Seric CurEnv->e_flags |= EF_PM_NOTIFY;
38563848Seric }
38667786Seric else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
38764773Seric {
38864773Seric /* mark long-term fatal errors */
38964773Seric CurEnv->e_flags |= EF_FATALERRS;
39064773Seric }
3914711Seric }
3924711Seric /*
3934711Seric ** FMTMSG -- format a message into buffer.
3944711Seric **
3954711Seric ** Parameters:
3964711Seric ** eb -- error buffer to get result.
3974711Seric ** to -- the recipient tag for this message.
3984711Seric ** num -- arpanet error number.
39916901Seric ** en -- the error number to display.
4004711Seric ** fmt -- format of string.
4014711Seric ** a, b, c, d, e -- arguments.
4024711Seric **
4034711Seric ** Returns:
4044711Seric ** none.
4054711Seric **
4064711Seric ** Side Effects:
4074711Seric ** none.
4084711Seric */
4094063Seric
41046928Sbostic static void
fmtmsg(eb,to,num,eno,fmt,ap)41156852Seric fmtmsg(eb, to, num, eno, fmt, ap)
4124711Seric register char *eb;
41369748Seric const char *to;
41469748Seric const char *num;
41516904Seric int eno;
41669748Seric const char *fmt;
41756852Seric va_list ap;
4184711Seric {
4194711Seric char del;
42059596Seric char *meb;
4214711Seric
4224711Seric /* output the reply code */
42324943Seric if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
4244577Seric {
4254711Seric num = fmt;
4264711Seric fmt += 4;
4274711Seric }
4284711Seric if (num[3] == '-')
4294711Seric del = '-';
4304711Seric else
4314711Seric del = ' ';
4324711Seric (void) sprintf(eb, "%3.3s%c", num, del);
4334711Seric eb += 4;
4344063Seric
4359372Seric /* output the file name and line number */
4369372Seric if (FileName != NULL)
4379372Seric {
4389372Seric (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
4399372Seric eb += strlen(eb);
4409372Seric }
4419372Seric
4424711Seric /* output the "to" person */
4434711Seric if (to != NULL && to[0] != '\0')
4444711Seric {
44566297Seric (void) sprintf(eb, "%s... ", shortenstring(to, 203));
4465201Seric while (*eb != '\0')
4475201Seric *eb++ &= 0177;
4484711Seric }
4494711Seric
45059596Seric meb = eb;
45159596Seric
4524711Seric /* output the message */
45356852Seric (void) vsprintf(eb, fmt, ap);
4545201Seric while (*eb != '\0')
4555201Seric *eb++ &= 0177;
4564711Seric
4574711Seric /* output the error code, if any */
45816904Seric if (eno != 0)
4594711Seric {
46016904Seric (void) sprintf(eb, ": %s", errstring(eno));
4614711Seric eb += strlen(eb);
4624577Seric }
463295Seric }
46415136Seric /*
46568692Seric ** BUFFER_ERRORS -- arrange to buffer future error messages
46668692Seric **
46768692Seric ** Parameters:
46868692Seric ** none
46968692Seric **
47068692Seric ** Returns:
47168692Seric ** none.
47268692Seric */
47368692Seric
47468692Seric void
buffer_errors()47568692Seric buffer_errors()
47668692Seric {
47768692Seric HeldMessageBuf[0] = '\0';
47868692Seric HoldErrs = TRUE;
47968692Seric }
48068692Seric /*
48168692Seric ** FLUSH_ERRORS -- flush the held error message buffer
48268692Seric **
48368692Seric ** Parameters:
48468692Seric ** print -- if set, print the message, otherwise just
48568692Seric ** delete it.
48668692Seric **
48768692Seric ** Returns:
48868692Seric ** none.
48968692Seric */
49068692Seric
49168692Seric void
flush_errors(print)49268692Seric flush_errors(print)
49368692Seric bool print;
49468692Seric {
49568692Seric if (print && HeldMessageBuf[0] != '\0')
49668692Seric putoutmsg(HeldMessageBuf, FALSE, TRUE);
49768692Seric HeldMessageBuf[0] = '\0';
49868692Seric HoldErrs = FALSE;
49968692Seric }
50068692Seric /*
50115136Seric ** ERRSTRING -- return string description of error code
50215136Seric **
50315136Seric ** Parameters:
50465751Seric ** errnum -- the error number to translate
50515136Seric **
50615136Seric ** Returns:
50765751Seric ** A string description of errnum.
50815136Seric **
50915136Seric ** Side Effects:
51015136Seric ** none.
51115136Seric */
51215136Seric
51360089Seric const char *
errstring(errnum)51465751Seric errstring(errnum)
51565751Seric int errnum;
51615136Seric {
51765168Seric char *dnsmsg;
51863839Seric static char buf[MAXLINE];
51963839Seric # ifndef ERRLIST_PREDEFINED
52063839Seric extern char *sys_errlist[];
52115136Seric extern int sys_nerr;
52263839Seric # endif
52324943Seric # ifdef SMTP
52424943Seric extern char *SmtpPhase;
52556795Seric # endif /* SMTP */
52615136Seric
52724943Seric /*
52824943Seric ** Handle special network error codes.
52924943Seric **
53024943Seric ** These are 4.2/4.3bsd specific; they should be in daemon.c.
53124943Seric */
53224943Seric
53365168Seric dnsmsg = NULL;
53465751Seric switch (errnum)
53524943Seric {
53665067Seric # if defined(DAEMON) && defined(ETIMEDOUT)
53724943Seric case ETIMEDOUT:
53824943Seric case ECONNRESET:
53965751Seric (void) strcpy(buf, sys_errlist[errnum]);
54024943Seric if (SmtpPhase != NULL)
54124943Seric {
54224943Seric (void) strcat(buf, " during ");
54324943Seric (void) strcat(buf, SmtpPhase);
54424943Seric }
54525050Seric if (CurHostName != NULL)
54624943Seric {
54724943Seric (void) strcat(buf, " with ");
54825050Seric (void) strcat(buf, CurHostName);
54924943Seric }
55024943Seric return (buf);
55124943Seric
55224943Seric case EHOSTDOWN:
55325050Seric if (CurHostName == NULL)
55424943Seric break;
55525050Seric (void) sprintf(buf, "Host %s is down", CurHostName);
55624943Seric return (buf);
55724943Seric
55824943Seric case ECONNREFUSED:
55925050Seric if (CurHostName == NULL)
56024943Seric break;
56125050Seric (void) sprintf(buf, "Connection refused by %s", CurHostName);
56224943Seric return (buf);
56365067Seric # endif
56425526Smiriam
56563993Seric case EOPENTIMEOUT:
56663993Seric return "Timeout on file open";
56763993Seric
56866334Seric # if NAMED_BIND
56963993Seric case HOST_NOT_FOUND + E_DNSBASE:
57065168Seric dnsmsg = "host not found";
57165168Seric break;
57258010Seric
57363993Seric case TRY_AGAIN + E_DNSBASE:
57465168Seric dnsmsg = "host name lookup failure";
57565168Seric break;
57658010Seric
57763993Seric case NO_RECOVERY + E_DNSBASE:
57865168Seric dnsmsg = "non-recoverable error";
57965168Seric break;
58058010Seric
58163993Seric case NO_DATA + E_DNSBASE:
58265168Seric dnsmsg = "no data known";
58365168Seric break;
58457736Seric # endif
58565067Seric
58665067Seric case EPERM:
58765067Seric /* SunOS gives "Not owner" -- this is the POSIX message */
58865067Seric return "Operation not permitted";
58924943Seric }
59024943Seric
59165168Seric if (dnsmsg != NULL)
59265168Seric {
59365168Seric (void) strcpy(buf, "Name server: ");
59465168Seric if (CurHostName != NULL)
59565168Seric {
59665168Seric (void) strcat(buf, CurHostName);
59765168Seric (void) strcat(buf, ": ");
59865168Seric }
59965168Seric (void) strcat(buf, dnsmsg);
60065168Seric return buf;
60165168Seric }
60265168Seric
60365751Seric if (errnum > 0 && errnum < sys_nerr)
60465751Seric return (sys_errlist[errnum]);
60515136Seric
60665751Seric (void) sprintf(buf, "Error %d", errnum);
60715136Seric return (buf);
60815136Seric }
609