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