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