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