xref: /original-bsd/usr.sbin/sendmail/src/err.c (revision 64cd2caf)
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.27 (Berkeley) 04/18/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 == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
343 	{
344 		/* mark long-term fatal errors */
345 		CurEnv->e_flags |= EF_FATALERRS;
346 	}
347 }
348 /*
349 **  FMTMSG -- format a message into buffer.
350 **
351 **	Parameters:
352 **		eb -- error buffer to get result.
353 **		to -- the recipient tag for this message.
354 **		num -- arpanet error number.
355 **		en -- the error number to display.
356 **		fmt -- format of string.
357 **		a, b, c, d, e -- arguments.
358 **
359 **	Returns:
360 **		none.
361 **
362 **	Side Effects:
363 **		none.
364 */
365 
366 static void
367 fmtmsg(eb, to, num, eno, fmt, ap)
368 	register char *eb;
369 	char *to;
370 	char *num;
371 	int eno;
372 	char *fmt;
373 	va_list ap;
374 {
375 	char del;
376 	char *meb;
377 
378 	/* output the reply code */
379 	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
380 	{
381 		num = fmt;
382 		fmt += 4;
383 	}
384 	if (num[3] == '-')
385 		del = '-';
386 	else
387 		del = ' ';
388 	(void) sprintf(eb, "%3.3s%c", num, del);
389 	eb += 4;
390 
391 	/* output the file name and line number */
392 	if (FileName != NULL)
393 	{
394 		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
395 		eb += strlen(eb);
396 	}
397 
398 	/* output the "to" person */
399 	if (to != NULL && to[0] != '\0')
400 	{
401 		(void) sprintf(eb, "%s... ", shortenstring(to, 203));
402 		while (*eb != '\0')
403 			*eb++ &= 0177;
404 	}
405 
406 	meb = eb;
407 
408 	/* output the message */
409 	(void) vsprintf(eb, fmt, ap);
410 	while (*eb != '\0')
411 		*eb++ &= 0177;
412 
413 	/* output the error code, if any */
414 	if (eno != 0)
415 	{
416 		(void) sprintf(eb, ": %s", errstring(eno));
417 		eb += strlen(eb);
418 	}
419 
420 	if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4'))
421 	{
422 		if (CurEnv->e_message != NULL)
423 			free(CurEnv->e_message);
424 		CurEnv->e_message = newstr(meb);
425 	}
426 }
427 /*
428 **  ERRSTRING -- return string description of error code
429 **
430 **	Parameters:
431 **		errnum -- the error number to translate
432 **
433 **	Returns:
434 **		A string description of errnum.
435 **
436 **	Side Effects:
437 **		none.
438 */
439 
440 const char *
441 errstring(errnum)
442 	int errnum;
443 {
444 	char *dnsmsg;
445 	static char buf[MAXLINE];
446 # ifndef ERRLIST_PREDEFINED
447 	extern char *sys_errlist[];
448 	extern int sys_nerr;
449 # endif
450 # ifdef SMTP
451 	extern char *SmtpPhase;
452 # endif /* SMTP */
453 
454 	/*
455 	**  Handle special network error codes.
456 	**
457 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
458 	*/
459 
460 	dnsmsg = NULL;
461 	switch (errnum)
462 	{
463 # if defined(DAEMON) && defined(ETIMEDOUT)
464 	  case ETIMEDOUT:
465 	  case ECONNRESET:
466 		(void) strcpy(buf, sys_errlist[errnum]);
467 		if (SmtpPhase != NULL)
468 		{
469 			(void) strcat(buf, " during ");
470 			(void) strcat(buf, SmtpPhase);
471 		}
472 		if (CurHostName != NULL)
473 		{
474 			(void) strcat(buf, " with ");
475 			(void) strcat(buf, CurHostName);
476 		}
477 		return (buf);
478 
479 	  case EHOSTDOWN:
480 		if (CurHostName == NULL)
481 			break;
482 		(void) sprintf(buf, "Host %s is down", CurHostName);
483 		return (buf);
484 
485 	  case ECONNREFUSED:
486 		if (CurHostName == NULL)
487 			break;
488 		(void) sprintf(buf, "Connection refused by %s", CurHostName);
489 		return (buf);
490 # endif
491 
492 	  case EOPENTIMEOUT:
493 		return "Timeout on file open";
494 
495 # if NAMED_BIND
496 	  case HOST_NOT_FOUND + E_DNSBASE:
497 		dnsmsg = "host not found";
498 		break;
499 
500 	  case TRY_AGAIN + E_DNSBASE:
501 		dnsmsg = "host name lookup failure";
502 		break;
503 
504 	  case NO_RECOVERY + E_DNSBASE:
505 		dnsmsg = "non-recoverable error";
506 		break;
507 
508 	  case NO_DATA + E_DNSBASE:
509 		dnsmsg = "no data known";
510 		break;
511 # endif
512 
513 	  case EPERM:
514 		/* SunOS gives "Not owner" -- this is the POSIX message */
515 		return "Operation not permitted";
516 	}
517 
518 	if (dnsmsg != NULL)
519 	{
520 		(void) strcpy(buf, "Name server: ");
521 		if (CurHostName != NULL)
522 		{
523 			(void) strcat(buf, CurHostName);
524 			(void) strcat(buf, ": ");
525 		}
526 		(void) strcat(buf, dnsmsg);
527 		return buf;
528 	}
529 
530 	if (errnum > 0 && errnum < sys_nerr)
531 		return (sys_errlist[errnum]);
532 
533 	(void) sprintf(buf, "Error %d", errnum);
534 	return (buf);
535 }
536