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