1 /* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 */ 12 13 #ifndef lint 14 static char sccsid[] = "@(#)err.c 8.74 (Berkeley) 6/4/1998"; 15 #endif /* not lint */ 16 17 # include "sendmail.h" 18 # include <errno.h> 19 20 /* 21 ** SYSERR -- Print error message. 22 ** 23 ** Prints an error message via printf to the diagnostic output. 24 ** 25 ** If the first character of the syserr message is `!' it will 26 ** log this as an ALERT message and exit immediately. This can 27 ** leave queue files in an indeterminate state, so it should not 28 ** be used lightly. 29 ** 30 ** Parameters: 31 ** fmt -- the format string. If it does not begin with 32 ** a three-digit SMTP reply code, either 554 or 33 ** 451 is assumed depending on whether errno 34 ** is set. 35 ** (others) -- parameters 36 ** 37 ** Returns: 38 ** none 39 ** Through TopFrame if QuickAbort is set. 40 ** 41 ** Side Effects: 42 ** increments Errors. 43 ** sets ExitStat. 44 */ 45 46 char MsgBuf[BUFSIZ*2]; /* text of most recent message */ 47 char HeldMessageBuf[sizeof MsgBuf]; /* for held messages */ 48 49 extern void putoutmsg __P((char *, bool, bool)); 50 extern void puterrmsg __P((char *)); 51 static void fmtmsg __P((char *, const char *, const char *, int, const char *, va_list)); 52 53 #if NAMED_BIND && !defined(NO_DATA) 54 # define NO_DATA NO_ADDRESS 55 #endif 56 57 void 58 /*VARARGS1*/ 59 #ifdef __STDC__ 60 syserr(const char *fmt, ...) 61 #else 62 syserr(fmt, va_alist) 63 const char *fmt; 64 va_dcl 65 #endif 66 { 67 register char *p; 68 int olderrno = errno; 69 bool panic; 70 char *uname; 71 struct passwd *pw; 72 char ubuf[80]; 73 VA_LOCAL_DECL 74 75 panic = *fmt == '!'; 76 if (panic) 77 { 78 fmt++; 79 HoldErrs = FALSE; 80 } 81 82 /* format and output the error message */ 83 if (olderrno == 0) 84 p = "554"; 85 else 86 p = "451"; 87 VA_START(fmt); 88 fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); 89 VA_END; 90 puterrmsg(MsgBuf); 91 92 /* save this message for mailq printing */ 93 if (!panic && CurEnv != NULL) 94 { 95 if (CurEnv->e_message != NULL) 96 free(CurEnv->e_message); 97 CurEnv->e_message = newstr(MsgBuf + 4); 98 } 99 100 /* determine exit status if not already set */ 101 if (ExitStat == EX_OK) 102 { 103 if (olderrno == 0) 104 ExitStat = EX_SOFTWARE; 105 else 106 ExitStat = EX_OSERR; 107 if (tTd(54, 1)) 108 printf("syserr: ExitStat = %d\n", ExitStat); 109 } 110 111 pw = sm_getpwuid(getuid()); 112 if (pw != NULL) 113 uname = pw->pw_name; 114 else 115 { 116 uname = ubuf; 117 snprintf(ubuf, sizeof ubuf, "UID%d", getuid()); 118 } 119 120 if (LogLevel > 0) 121 sm_syslog(panic ? LOG_ALERT : LOG_CRIT, 122 CurEnv == NULL ? NOQID : CurEnv->e_id, 123 "SYSERR(%s): %.900s", 124 uname, &MsgBuf[4]); 125 switch (olderrno) 126 { 127 case EBADF: 128 case ENFILE: 129 case EMFILE: 130 case ENOTTY: 131 #ifdef EFBIG 132 case EFBIG: 133 #endif 134 #ifdef ESPIPE 135 case ESPIPE: 136 #endif 137 #ifdef EPIPE 138 case EPIPE: 139 #endif 140 #ifdef ENOBUFS 141 case ENOBUFS: 142 #endif 143 #ifdef ESTALE 144 case ESTALE: 145 #endif 146 printopenfds(TRUE); 147 mci_dump_all(TRUE); 148 break; 149 } 150 if (panic) 151 { 152 #ifdef XLA 153 xla_all_end(); 154 #endif 155 if (tTd(0, 1)) 156 abort(); 157 exit(EX_OSERR); 158 } 159 errno = 0; 160 if (QuickAbort) 161 longjmp(TopFrame, 2); 162 } 163 /* 164 ** USRERR -- Signal user error. 165 ** 166 ** This is much like syserr except it is for user errors. 167 ** 168 ** Parameters: 169 ** fmt -- the format string. If it does not begin with 170 ** a three-digit SMTP reply code, 501 is assumed. 171 ** (others) -- printf strings 172 ** 173 ** Returns: 174 ** none 175 ** Through TopFrame if QuickAbort is set. 176 ** 177 ** Side Effects: 178 ** increments Errors. 179 */ 180 181 /*VARARGS1*/ 182 void 183 #ifdef __STDC__ 184 usrerr(const char *fmt, ...) 185 #else 186 usrerr(fmt, va_alist) 187 const char *fmt; 188 va_dcl 189 #endif 190 { 191 VA_LOCAL_DECL 192 193 if (SuprErrs) 194 return; 195 196 VA_START(fmt); 197 fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); 198 VA_END; 199 200 /* save this message for mailq printing */ 201 switch (MsgBuf[0]) 202 { 203 case '4': 204 case '8': 205 if (CurEnv->e_message != NULL) 206 break; 207 208 /* fall through.... */ 209 210 case '5': 211 case '6': 212 if (CurEnv->e_message != NULL) 213 free(CurEnv->e_message); 214 if (MsgBuf[0] == '6') 215 { 216 char buf[MAXLINE]; 217 218 snprintf(buf, sizeof buf, "Postmaster warning: %.*s", 219 (int)sizeof buf - 22, MsgBuf + 4); 220 CurEnv->e_message = newstr(buf); 221 } 222 else 223 { 224 CurEnv->e_message = newstr(MsgBuf + 4); 225 } 226 break; 227 } 228 229 puterrmsg(MsgBuf); 230 231 if (LogLevel > 3 && LogUsrErrs) 232 sm_syslog(LOG_NOTICE, CurEnv->e_id, 233 "%.900s", 234 &MsgBuf[4]); 235 236 if (QuickAbort) 237 longjmp(TopFrame, 1); 238 } 239 /* 240 ** MESSAGE -- print message (not necessarily an error) 241 ** 242 ** Parameters: 243 ** msg -- the message (printf fmt) -- it can begin with 244 ** an SMTP reply code. If not, 050 is assumed. 245 ** (others) -- printf arguments 246 ** 247 ** Returns: 248 ** none 249 ** 250 ** Side Effects: 251 ** none. 252 */ 253 254 /*VARARGS1*/ 255 void 256 #ifdef __STDC__ 257 message(const char *msg, ...) 258 #else 259 message(msg, va_alist) 260 const char *msg; 261 va_dcl 262 #endif 263 { 264 VA_LOCAL_DECL 265 266 errno = 0; 267 VA_START(msg); 268 fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); 269 VA_END; 270 putoutmsg(MsgBuf, FALSE, FALSE); 271 272 /* save this message for mailq printing */ 273 switch (MsgBuf[0]) 274 { 275 case '4': 276 case '8': 277 if (CurEnv->e_message != NULL) 278 break; 279 /* fall through.... */ 280 281 case '5': 282 if (CurEnv->e_message != NULL) 283 free(CurEnv->e_message); 284 CurEnv->e_message = newstr(MsgBuf + 4); 285 break; 286 } 287 } 288 /* 289 ** NMESSAGE -- print message (not necessarily an error) 290 ** 291 ** Just like "message" except it never puts the to... tag on. 292 ** 293 ** Parameters: 294 ** msg -- the message (printf fmt) -- if it begins 295 ** with a three digit SMTP reply code, that is used, 296 ** otherwise 050 is assumed. 297 ** (others) -- printf arguments 298 ** 299 ** Returns: 300 ** none 301 ** 302 ** Side Effects: 303 ** none. 304 */ 305 306 /*VARARGS1*/ 307 void 308 #ifdef __STDC__ 309 nmessage(const char *msg, ...) 310 #else 311 nmessage(msg, va_alist) 312 const char *msg; 313 va_dcl 314 #endif 315 { 316 VA_LOCAL_DECL 317 318 errno = 0; 319 VA_START(msg); 320 fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); 321 VA_END; 322 putoutmsg(MsgBuf, FALSE, FALSE); 323 324 /* save this message for mailq printing */ 325 switch (MsgBuf[0]) 326 { 327 case '4': 328 case '8': 329 if (CurEnv->e_message != NULL) 330 break; 331 /* fall through.... */ 332 333 case '5': 334 if (CurEnv->e_message != NULL) 335 free(CurEnv->e_message); 336 CurEnv->e_message = newstr(MsgBuf + 4); 337 break; 338 } 339 } 340 /* 341 ** PUTOUTMSG -- output error message to transcript and channel 342 ** 343 ** Parameters: 344 ** msg -- message to output (in SMTP format). 345 ** holdmsg -- if TRUE, don't output a copy of the message to 346 ** our output channel. 347 ** heldmsg -- if TRUE, this is a previously held message; 348 ** don't log it to the transcript file. 349 ** 350 ** Returns: 351 ** none. 352 ** 353 ** Side Effects: 354 ** Outputs msg to the transcript. 355 ** If appropriate, outputs it to the channel. 356 ** Deletes SMTP reply code number as appropriate. 357 */ 358 359 void 360 putoutmsg(msg, holdmsg, heldmsg) 361 char *msg; 362 bool holdmsg; 363 bool heldmsg; 364 { 365 char msgcode = msg[0]; 366 367 /* display for debugging */ 368 if (tTd(54, 8)) 369 printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 370 heldmsg ? " (held)" : ""); 371 372 /* map warnings to something SMTP can handle */ 373 if (msgcode == '6') 374 msg[0] = '5'; 375 else if (msgcode == '8') 376 msg[0] = '4'; 377 378 /* output to transcript if serious */ 379 if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && 380 strchr("45", msg[0]) != NULL) 381 fprintf(CurEnv->e_xfp, "%s\n", msg); 382 383 if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 384 sm_syslog(LOG_INFO, CurEnv->e_id, 385 "--> %s%s", 386 msg, holdmsg ? " (held)" : ""); 387 388 if (msgcode == '8') 389 msg[0] = '0'; 390 391 /* output to channel if appropriate */ 392 if (!Verbose && msg[0] == '0') 393 return; 394 if (holdmsg) 395 { 396 /* save for possible future display */ 397 msg[0] = msgcode; 398 snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg); 399 return; 400 } 401 402 (void) fflush(stdout); 403 404 if (OutChannel == NULL) 405 return; 406 407 /* if DisConnected, OutChannel now points to the transcript */ 408 if (!DisConnected && 409 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 410 fprintf(OutChannel, "%s\r\n", msg); 411 else 412 fprintf(OutChannel, "%s\n", &msg[4]); 413 if (TrafficLogFile != NULL) 414 fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(), 415 (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]); 416 if (msg[3] == ' ') 417 (void) fflush(OutChannel); 418 if (!ferror(OutChannel) || DisConnected) 419 return; 420 421 /* 422 ** Error on output -- if reporting lost channel, just ignore it. 423 ** Also, ignore errors from QUIT response (221 message) -- some 424 ** rude servers don't read result. 425 */ 426 427 if (InChannel == NULL || feof(InChannel) || ferror(InChannel) || 428 strncmp(msg, "221", 3) == 0) 429 return; 430 431 /* can't call syserr, 'cause we are using MsgBuf */ 432 HoldErrs = TRUE; 433 if (LogLevel > 0) 434 sm_syslog(LOG_CRIT, CurEnv->e_id, 435 "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 436 CurHostName == NULL ? "NO-HOST" : CurHostName, 437 shortenstring(msg, MAXSHORTSTR), errstring(errno)); 438 } 439 /* 440 ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 441 ** 442 ** Parameters: 443 ** msg -- the message to output. 444 ** 445 ** Returns: 446 ** none. 447 ** 448 ** Side Effects: 449 ** Sets the fatal error bit in the envelope as appropriate. 450 */ 451 452 void 453 puterrmsg(msg) 454 char *msg; 455 { 456 char msgcode = msg[0]; 457 458 /* output the message as usual */ 459 putoutmsg(msg, HoldErrs, FALSE); 460 461 /* be careful about multiple error messages */ 462 if (OnlyOneError) 463 HoldErrs = TRUE; 464 465 /* signal the error */ 466 Errors++; 467 468 if (CurEnv == NULL) 469 return; 470 471 if (msgcode == '6') 472 { 473 /* notify the postmaster */ 474 CurEnv->e_flags |= EF_PM_NOTIFY; 475 } 476 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 477 { 478 /* mark long-term fatal errors */ 479 CurEnv->e_flags |= EF_FATALERRS; 480 } 481 } 482 /* 483 ** FMTMSG -- format a message into buffer. 484 ** 485 ** Parameters: 486 ** eb -- error buffer to get result. 487 ** to -- the recipient tag for this message. 488 ** num -- arpanet error number. 489 ** en -- the error number to display. 490 ** fmt -- format of string. 491 ** a, b, c, d, e -- arguments. 492 ** 493 ** Returns: 494 ** none. 495 ** 496 ** Side Effects: 497 ** none. 498 */ 499 500 static void 501 fmtmsg(eb, to, num, eno, fmt, ap) 502 register char *eb; 503 const char *to; 504 const char *num; 505 int eno; 506 const char *fmt; 507 va_list ap; 508 { 509 char del; 510 int l; 511 int spaceleft = sizeof MsgBuf; 512 513 /* output the reply code */ 514 if (isascii(fmt[0]) && isdigit(fmt[0]) && 515 isascii(fmt[1]) && isdigit(fmt[1]) && 516 isascii(fmt[2]) && isdigit(fmt[2])) 517 { 518 num = fmt; 519 fmt += 4; 520 } 521 if (num[3] == '-') 522 del = '-'; 523 else 524 del = ' '; 525 (void) snprintf(eb, spaceleft, "%3.3s%c", num, del); 526 eb += 4; 527 spaceleft -= 4; 528 529 /* output the file name and line number */ 530 if (FileName != NULL) 531 { 532 (void) snprintf(eb, spaceleft, "%s: line %d: ", 533 shortenstring(FileName, 83), LineNumber); 534 eb += (l = strlen(eb)); 535 spaceleft -= l; 536 } 537 538 /* output the "to" person */ 539 if (to != NULL && to[0] != '\0' && 540 strncmp(num, "551", 3) != 0 && 541 strncmp(num, "251", 3) != 0) 542 { 543 (void) snprintf(eb, spaceleft, "%s... ", 544 shortenstring(to, MAXSHORTSTR)); 545 spaceleft -= strlen(eb); 546 while (*eb != '\0') 547 *eb++ &= 0177; 548 } 549 550 /* output the message */ 551 (void) vsnprintf(eb, spaceleft, fmt, ap); 552 spaceleft -= strlen(eb); 553 while (*eb != '\0') 554 *eb++ &= 0177; 555 556 /* output the error code, if any */ 557 if (eno != 0) 558 (void) snprintf(eb, spaceleft, ": %s", errstring(eno)); 559 } 560 /* 561 ** BUFFER_ERRORS -- arrange to buffer future error messages 562 ** 563 ** Parameters: 564 ** none 565 ** 566 ** Returns: 567 ** none. 568 */ 569 570 void 571 buffer_errors() 572 { 573 HeldMessageBuf[0] = '\0'; 574 HoldErrs = TRUE; 575 } 576 /* 577 ** FLUSH_ERRORS -- flush the held error message buffer 578 ** 579 ** Parameters: 580 ** print -- if set, print the message, otherwise just 581 ** delete it. 582 ** 583 ** Returns: 584 ** none. 585 */ 586 587 void 588 flush_errors(print) 589 bool print; 590 { 591 if (print && HeldMessageBuf[0] != '\0') 592 putoutmsg(HeldMessageBuf, FALSE, TRUE); 593 HeldMessageBuf[0] = '\0'; 594 HoldErrs = FALSE; 595 } 596 /* 597 ** ERRSTRING -- return string description of error code 598 ** 599 ** Parameters: 600 ** errnum -- the error number to translate 601 ** 602 ** Returns: 603 ** A string description of errnum. 604 ** 605 ** Side Effects: 606 ** none. 607 */ 608 609 const char * 610 errstring(errnum) 611 int errnum; 612 { 613 char *dnsmsg; 614 char *bp; 615 static char buf[MAXLINE]; 616 # if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) 617 extern char *sys_errlist[]; 618 extern int sys_nerr; 619 # endif 620 # if SMTP 621 extern char *SmtpPhase; 622 # endif /* SMTP */ 623 624 /* 625 ** Handle special network error codes. 626 ** 627 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 628 */ 629 630 dnsmsg = NULL; 631 switch (errnum) 632 { 633 # if defined(DAEMON) && defined(ETIMEDOUT) 634 case ETIMEDOUT: 635 case ECONNRESET: 636 bp = buf; 637 #if HASSTRERROR 638 snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum)); 639 #else 640 if (errnum >= 0 && errnum < sys_nerr) 641 snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]); 642 else 643 snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum); 644 #endif 645 bp += strlen(bp); 646 if (CurHostName != NULL) 647 { 648 if (errnum == ETIMEDOUT) 649 { 650 snprintf(bp, SPACELEFT(buf, bp), " with "); 651 bp += strlen(bp); 652 } 653 else 654 { 655 bp = buf; 656 snprintf(bp, SPACELEFT(buf, bp), 657 "Connection reset by "); 658 bp += strlen(bp); 659 } 660 snprintf(bp, SPACELEFT(buf, bp), "%s", 661 shortenstring(CurHostName, MAXSHORTSTR)); 662 bp += strlen(buf); 663 } 664 if (SmtpPhase != NULL) 665 { 666 snprintf(bp, SPACELEFT(buf, bp), " during %s", 667 SmtpPhase); 668 } 669 return (buf); 670 671 case EHOSTDOWN: 672 if (CurHostName == NULL) 673 break; 674 (void) snprintf(buf, sizeof buf, "Host %s is down", 675 shortenstring(CurHostName, MAXSHORTSTR)); 676 return (buf); 677 678 case ECONNREFUSED: 679 if (CurHostName == NULL) 680 break; 681 (void) snprintf(buf, sizeof buf, "Connection refused by %s", 682 shortenstring(CurHostName, MAXSHORTSTR)); 683 return (buf); 684 # endif 685 686 # if NAMED_BIND 687 case HOST_NOT_FOUND + E_DNSBASE: 688 dnsmsg = "host not found"; 689 break; 690 691 case TRY_AGAIN + E_DNSBASE: 692 dnsmsg = "host name lookup failure"; 693 break; 694 695 case NO_RECOVERY + E_DNSBASE: 696 dnsmsg = "non-recoverable error"; 697 break; 698 699 case NO_DATA + E_DNSBASE: 700 dnsmsg = "no data known"; 701 break; 702 # endif 703 704 case EPERM: 705 /* SunOS gives "Not owner" -- this is the POSIX message */ 706 return "Operation not permitted"; 707 708 /* 709 ** Error messages used internally in sendmail. 710 */ 711 712 case E_SM_OPENTIMEOUT: 713 return "Timeout on file open"; 714 715 case E_SM_NOSLINK: 716 return "Symbolic links not allowed"; 717 718 case E_SM_NOHLINK: 719 return "Hard links not allowed"; 720 721 case E_SM_REGONLY: 722 return "Regular files only"; 723 724 case E_SM_ISEXEC: 725 return "Executable files not allowed"; 726 727 case E_SM_WWDIR: 728 return "World writable directory"; 729 730 case E_SM_GWDIR: 731 return "Group writable directory"; 732 733 case E_SM_FILECHANGE: 734 return "File changed after open"; 735 736 case E_SM_WWFILE: 737 return "World writable file"; 738 739 case E_SM_GWFILE: 740 return "Group writable file"; 741 } 742 743 if (dnsmsg != NULL) 744 { 745 bp = buf; 746 strcpy(bp, "Name server: "); 747 bp += strlen(bp); 748 if (CurHostName != NULL) 749 { 750 snprintf(bp, SPACELEFT(buf, bp), "%s: ", 751 shortenstring(CurHostName, MAXSHORTSTR)); 752 bp += strlen(bp); 753 } 754 snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg); 755 return buf; 756 } 757 758 #if HASSTRERROR 759 return strerror(errnum); 760 #else 761 if (errnum > 0 && errnum < sys_nerr) 762 return (sys_errlist[errnum]); 763 764 (void) snprintf(buf, sizeof buf, "Error %d", errnum); 765 return (buf); 766 #endif 767 } 768