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