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