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__
syserr(const char * fmt,...)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__
usrerr(const char * fmt,...)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__
message(const char * msg,...)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__
nmessage(const char * msg,...)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
putoutmsg(msg,holdmsg,heldmsg)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
puterrmsg(msg)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
fmtmsg(eb,to,num,eno,fmt,ap)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
buffer_errors()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
flush_errors(print)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 *
errstring(errnum)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