xref: /csrg-svn/usr.sbin/sendmail/src/err.c (revision 64773)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)err.c	8.14 (Berkeley) 10/29/93";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <errno.h>
15 # include <netdb.h>
16 
17 /*
18 **  SYSERR -- Print error message.
19 **
20 **	Prints an error message via printf to the diagnostic
21 **	output.  If LOG is defined, it logs it also.
22 **
23 **	If the first character of the syserr message is `!' it will
24 **	log this as an ALERT message and exit immediately.  This can
25 **	leave queue files in an indeterminate state, so it should not
26 **	be used lightly.
27 **
28 **	Parameters:
29 **		f -- the format string
30 **		a, b, c, d, e -- parameters
31 **
32 **	Returns:
33 **		none
34 **		Through TopFrame if QuickAbort is set.
35 **
36 **	Side Effects:
37 **		increments Errors.
38 **		sets ExitStat.
39 */
40 
41 char	MsgBuf[BUFSIZ*2];	/* text of most recent message */
42 
43 static void	fmtmsg();
44 
45 #if defined(NAMED_BIND) && !defined(NO_DATA)
46 # define NO_DATA	NO_ADDRESS
47 #endif
48 
49 void
50 /*VARARGS1*/
51 #ifdef __STDC__
52 syserr(const char *fmt, ...)
53 #else
54 syserr(fmt, va_alist)
55 	const char *fmt;
56 	va_dcl
57 #endif
58 {
59 	register char *p;
60 	int olderrno = errno;
61 	bool panic;
62 	VA_LOCAL_DECL
63 
64 	panic = *fmt == '!';
65 	if (panic)
66 		fmt++;
67 
68 	/* format and output the error message */
69 	if (olderrno == 0)
70 		p = "554";
71 	else
72 		p = "451";
73 	VA_START(fmt);
74 	fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
75 	VA_END;
76 	puterrmsg(MsgBuf);
77 
78 	/* determine exit status if not already set */
79 	if (ExitStat == EX_OK)
80 	{
81 		if (olderrno == 0)
82 			ExitStat = EX_SOFTWARE;
83 		else
84 			ExitStat = EX_OSERR;
85 	}
86 
87 # ifdef LOG
88 	if (LogLevel > 0)
89 		syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR: %s",
90 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
91 			&MsgBuf[4]);
92 # endif /* LOG */
93 	if (olderrno == EMFILE)
94 	{
95 		printopenfds(TRUE);
96 		mci_dump_all(TRUE);
97 	}
98 	if (panic)
99 	{
100 #ifdef XLA
101 		xla_all_end();
102 #endif
103 		exit(EX_OSERR);
104 	}
105 	errno = 0;
106 	if (QuickAbort)
107 		longjmp(TopFrame, 2);
108 }
109 /*
110 **  USRERR -- Signal user error.
111 **
112 **	This is much like syserr except it is for user errors.
113 **
114 **	Parameters:
115 **		fmt, a, b, c, d -- printf strings
116 **
117 **	Returns:
118 **		none
119 **		Through TopFrame if QuickAbort is set.
120 **
121 **	Side Effects:
122 **		increments Errors.
123 */
124 
125 /*VARARGS1*/
126 void
127 #ifdef __STDC__
128 usrerr(const char *fmt, ...)
129 #else
130 usrerr(fmt, va_alist)
131 	const char *fmt;
132 	va_dcl
133 #endif
134 {
135 	VA_LOCAL_DECL
136 	extern char SuprErrs;
137 	extern int errno;
138 
139 	if (SuprErrs)
140 		return;
141 
142 	VA_START(fmt);
143 	fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
144 	VA_END;
145 	puterrmsg(MsgBuf);
146 
147 # ifdef LOG
148 	if (LogLevel > 3 && LogUsrErrs)
149 		syslog(LOG_NOTICE, "%s: %s",
150 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
151 			&MsgBuf[4]);
152 # endif /* LOG */
153 
154 	if (QuickAbort)
155 		longjmp(TopFrame, 1);
156 }
157 /*
158 **  MESSAGE -- print message (not necessarily an error)
159 **
160 **	Parameters:
161 **		msg -- the message (printf fmt) -- it can begin with
162 **			an SMTP reply code.  If not, 050 is assumed.
163 **		a, b, c, d, e -- printf arguments
164 **
165 **	Returns:
166 **		none
167 **
168 **	Side Effects:
169 **		none.
170 */
171 
172 /*VARARGS2*/
173 void
174 #ifdef __STDC__
175 message(const char *msg, ...)
176 #else
177 message(msg, va_alist)
178 	const char *msg;
179 	va_dcl
180 #endif
181 {
182 	VA_LOCAL_DECL
183 
184 	errno = 0;
185 	VA_START(msg);
186 	fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
187 	VA_END;
188 	putoutmsg(MsgBuf, FALSE);
189 }
190 /*
191 **  NMESSAGE -- print message (not necessarily an error)
192 **
193 **	Just like "message" except it never puts the to... tag on.
194 **
195 **	Parameters:
196 **		num -- the default ARPANET error number (in ascii)
197 **		msg -- the message (printf fmt) -- if it begins
198 **			with three digits, this number overrides num.
199 **		a, b, c, d, e -- printf arguments
200 **
201 **	Returns:
202 **		none
203 **
204 **	Side Effects:
205 **		none.
206 */
207 
208 /*VARARGS2*/
209 void
210 #ifdef __STDC__
211 nmessage(const char *msg, ...)
212 #else
213 nmessage(msg, va_alist)
214 	const char *msg;
215 	va_dcl
216 #endif
217 {
218 	VA_LOCAL_DECL
219 
220 	errno = 0;
221 	VA_START(msg);
222 	fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
223 	VA_END;
224 	putoutmsg(MsgBuf, FALSE);
225 }
226 /*
227 **  PUTOUTMSG -- output error message to transcript and channel
228 **
229 **	Parameters:
230 **		msg -- message to output (in SMTP format).
231 **		holdmsg -- if TRUE, don't output a copy of the message to
232 **			our output channel.
233 **
234 **	Returns:
235 **		none.
236 **
237 **	Side Effects:
238 **		Outputs msg to the transcript.
239 **		If appropriate, outputs it to the channel.
240 **		Deletes SMTP reply code number as appropriate.
241 */
242 
243 putoutmsg(msg, holdmsg)
244 	char *msg;
245 	bool holdmsg;
246 {
247 	/* display for debugging */
248 	if (tTd(54, 8))
249 		printf("--- %s%s\n", msg, holdmsg ? " (held)" : "");
250 
251 	/* output to transcript if serious */
252 	if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
253 		fprintf(CurEnv->e_xfp, "%s\n", msg);
254 
255 	/* output to channel if appropriate */
256 	if (holdmsg || (!Verbose && msg[0] == '0'))
257 		return;
258 
259 	/* map warnings to something SMTP can handle */
260 	if (msg[0] == '6')
261 		msg[0] = '5';
262 
263 	(void) fflush(stdout);
264 	if (OpMode == MD_SMTP)
265 		fprintf(OutChannel, "%s\r\n", msg);
266 	else
267 		fprintf(OutChannel, "%s\n", &msg[4]);
268 	if (TrafficLogFile != NULL)
269 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(),
270 			OpMode == MD_SMTP ? msg : &msg[4]);
271 	if (msg[3] == ' ')
272 		(void) fflush(OutChannel);
273 	if (!ferror(OutChannel))
274 		return;
275 
276 	/*
277 	**  Error on output -- if reporting lost channel, just ignore it.
278 	**  Also, ignore errors from QUIT response (221 message) -- some
279 	**	rude servers don't read result.
280 	*/
281 
282 	if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0)
283 		return;
284 
285 	/* can't call syserr, 'cause we are using MsgBuf */
286 	HoldErrs = TRUE;
287 #ifdef LOG
288 	if (LogLevel > 0)
289 		syslog(LOG_CRIT,
290 			"%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %m",
291 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
292 			CurHostName == NULL ? "NO-HOST" : CurHostName,
293 			msg);
294 #endif
295 }
296 /*
297 **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
298 **
299 **	Parameters:
300 **		msg -- the message to output.
301 **
302 **	Returns:
303 **		none.
304 **
305 **	Side Effects:
306 **		Sets the fatal error bit in the envelope as appropriate.
307 */
308 
309 puterrmsg(msg)
310 	char *msg;
311 {
312 	char msgcode = msg[0];
313 
314 	/* output the message as usual */
315 	putoutmsg(msg, HoldErrs);
316 
317 	/* signal the error */
318 	Errors++;
319 	if (msgcode == '6')
320 	{
321 		/* notify the postmaster */
322 		CurEnv->e_flags |= EF_PM_NOTIFY;
323 	}
324 	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
325 	{
326 		/* mark long-term fatal errors */
327 		CurEnv->e_flags |= EF_FATALERRS;
328 	}
329 }
330 /*
331 **  FMTMSG -- format a message into buffer.
332 **
333 **	Parameters:
334 **		eb -- error buffer to get result.
335 **		to -- the recipient tag for this message.
336 **		num -- arpanet error number.
337 **		en -- the error number to display.
338 **		fmt -- format of string.
339 **		a, b, c, d, e -- arguments.
340 **
341 **	Returns:
342 **		none.
343 **
344 **	Side Effects:
345 **		none.
346 */
347 
348 static void
349 fmtmsg(eb, to, num, eno, fmt, ap)
350 	register char *eb;
351 	char *to;
352 	char *num;
353 	int eno;
354 	char *fmt;
355 	va_list ap;
356 {
357 	char del;
358 	char *meb;
359 
360 	/* output the reply code */
361 	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
362 	{
363 		num = fmt;
364 		fmt += 4;
365 	}
366 	if (num[3] == '-')
367 		del = '-';
368 	else
369 		del = ' ';
370 	(void) sprintf(eb, "%3.3s%c", num, del);
371 	eb += 4;
372 
373 	/* output the file name and line number */
374 	if (FileName != NULL)
375 	{
376 		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
377 		eb += strlen(eb);
378 	}
379 
380 	/* output the "to" person */
381 	if (to != NULL && to[0] != '\0')
382 	{
383 		(void) sprintf(eb, "%s... ", to);
384 		while (*eb != '\0')
385 			*eb++ &= 0177;
386 	}
387 
388 	meb = eb;
389 
390 	/* output the message */
391 	(void) vsprintf(eb, fmt, ap);
392 	while (*eb != '\0')
393 		*eb++ &= 0177;
394 
395 	/* output the error code, if any */
396 	if (eno != 0)
397 	{
398 		(void) sprintf(eb, ": %s", errstring(eno));
399 		eb += strlen(eb);
400 	}
401 
402 	if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4'))
403 	{
404 		if (CurEnv->e_message != NULL)
405 			free(CurEnv->e_message);
406 		CurEnv->e_message = newstr(meb);
407 	}
408 }
409 /*
410 **  ERRSTRING -- return string description of error code
411 **
412 **	Parameters:
413 **		errno -- the error number to translate
414 **
415 **	Returns:
416 **		A string description of errno.
417 **
418 **	Side Effects:
419 **		none.
420 */
421 
422 const char *
423 errstring(errno)
424 	int errno;
425 {
426 	static char buf[MAXLINE];
427 # ifndef ERRLIST_PREDEFINED
428 	extern char *sys_errlist[];
429 	extern int sys_nerr;
430 # endif
431 # ifdef SMTP
432 	extern char *SmtpPhase;
433 # endif /* SMTP */
434 
435 # ifdef DAEMON
436 # ifdef ETIMEDOUT
437 	/*
438 	**  Handle special network error codes.
439 	**
440 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
441 	*/
442 
443 	switch (errno)
444 	{
445 	  case ETIMEDOUT:
446 	  case ECONNRESET:
447 		(void) strcpy(buf, sys_errlist[errno]);
448 		if (SmtpPhase != NULL)
449 		{
450 			(void) strcat(buf, " during ");
451 			(void) strcat(buf, SmtpPhase);
452 		}
453 		if (CurHostName != NULL)
454 		{
455 			(void) strcat(buf, " with ");
456 			(void) strcat(buf, CurHostName);
457 		}
458 		return (buf);
459 
460 	  case EHOSTDOWN:
461 		if (CurHostName == NULL)
462 			break;
463 		(void) sprintf(buf, "Host %s is down", CurHostName);
464 		return (buf);
465 
466 	  case ECONNREFUSED:
467 		if (CurHostName == NULL)
468 			break;
469 		(void) sprintf(buf, "Connection refused by %s", CurHostName);
470 		return (buf);
471 
472 	  case EOPENTIMEOUT:
473 		return "Timeout on file open";
474 
475 # ifdef NAMED_BIND
476 	  case HOST_NOT_FOUND + E_DNSBASE:
477 		return ("Name server: host not found");
478 
479 	  case TRY_AGAIN + E_DNSBASE:
480 		return ("Name server: host name lookup failure");
481 
482 	  case NO_RECOVERY + E_DNSBASE:
483 		return ("Name server: non-recoverable error");
484 
485 	  case NO_DATA + E_DNSBASE:
486 		return ("Name server: no data known for name");
487 # endif
488 	}
489 # endif
490 # endif
491 
492 	if (errno > 0 && errno < sys_nerr)
493 		return (sys_errlist[errno]);
494 
495 	(void) sprintf(buf, "Error %d", errno);
496 	return (buf);
497 }
498