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