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