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