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