1 /* 2 * Copyright (c) 1998-2003, 2010, 2015 Proofpoint, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #include <sendmail.h> 15 16 SM_RCSID("@(#)$Id: err.c,v 8.206 2013-11-22 20:51:55 ca Exp $") 17 18 #if LDAPMAP 19 # include <lber.h> 20 # include <ldap.h> /* for LDAP error codes */ 21 #endif 22 23 static void putoutmsg __P((char *, bool, bool)); 24 static void puterrmsg __P((char *)); 25 static char *fmtmsg __P((char *, const char *, const char *, const char *, 26 int, const char *, va_list)); 27 28 /* 29 ** FATAL_ERROR -- handle a fatal exception 30 ** 31 ** This function is installed as the default exception handler 32 ** in the main sendmail process, and in all child processes 33 ** that we create. Its job is to handle exceptions that are not 34 ** handled at a lower level. 35 ** 36 ** The theory is that unhandled exceptions will be 'fatal' class 37 ** exceptions (with an "F:" prefix), such as the out-of-memory 38 ** exception "F:sm.heap". As such, they are handled by exiting 39 ** the process in exactly the same way that xalloc() in Sendmail 8.10 40 ** exits the process when it fails due to lack of memory: 41 ** we call syserr with a message beginning with "!". 42 ** 43 ** Parameters: 44 ** exc -- exception which is terminating this process 45 ** 46 ** Returns: 47 ** none 48 */ 49 50 void 51 fatal_error(exc) 52 SM_EXC_T *exc; 53 { 54 static char buf[256]; 55 SM_FILE_T f; 56 57 /* 58 ** This function may be called when the heap is exhausted. 59 ** The following code writes the message for 'exc' into our 60 ** static buffer without allocating memory or raising exceptions. 61 */ 62 63 sm_strio_init(&f, buf, sizeof(buf)); 64 sm_exc_write(exc, &f); 65 (void) sm_io_flush(&f, SM_TIME_DEFAULT); 66 67 /* 68 ** Terminate the process after logging an error and cleaning up. 69 ** Problems: 70 ** - syserr decides what class of error this is by looking at errno. 71 ** That's no good; we should look at the exc structure. 72 ** - The cleanup code should be moved out of syserr 73 ** and into individual exception handlers 74 ** that are part of the module they clean up after. 75 */ 76 77 errno = ENOMEM; 78 syserr("!%s", buf); 79 } 80 81 /* 82 ** SYSERR -- Print error message. 83 ** 84 ** Prints an error message via sm_io_printf to the diagnostic output. 85 ** 86 ** If the first character of the syserr message is `!' it will 87 ** log this as an ALERT message and exit immediately. This can 88 ** leave queue files in an indeterminate state, so it should not 89 ** be used lightly. 90 ** 91 ** If the first character of the syserr message is '!' or '@' 92 ** then syserr knows that the process is about to be terminated, 93 ** so the SMTP reply code defaults to 421. Otherwise, the 94 ** reply code defaults to 451 or 554, depending on errno. 95 ** 96 ** Parameters: 97 ** fmt -- the format string. An optional '!', '@', or '+', 98 ** followed by an optional three-digit SMTP 99 ** reply code, followed by message text. 100 ** (others) -- parameters 101 ** 102 ** Returns: 103 ** none 104 ** Raises E:mta.quickabort if QuickAbort is set. 105 ** 106 ** Side Effects: 107 ** increments Errors. 108 ** sets ExitStat. 109 */ 110 111 char MsgBuf[BUFSIZ*2]; /* text of most recent message */ 112 static char HeldMessageBuf[sizeof(MsgBuf)]; /* for held messages */ 113 114 #if NAMED_BIND && !defined(NO_DATA) 115 # define NO_DATA NO_ADDRESS 116 #endif 117 118 void 119 /*VARARGS1*/ 120 #ifdef __STDC__ 121 syserr(const char *fmt, ...) 122 #else /* __STDC__ */ 123 syserr(fmt, va_alist) 124 const char *fmt; 125 va_dcl 126 #endif /* __STDC__ */ 127 { 128 register char *p; 129 int save_errno = errno; 130 bool panic, exiting, keep; 131 char *user; 132 char *enhsc; 133 char *errtxt; 134 struct passwd *pw; 135 char ubuf[80]; 136 SM_VA_LOCAL_DECL 137 138 panic = exiting = keep = false; 139 switch (*fmt) 140 { 141 case '!': 142 ++fmt; 143 panic = exiting = true; 144 break; 145 case '@': 146 ++fmt; 147 exiting = true; 148 break; 149 case '+': 150 ++fmt; 151 keep = true; 152 break; 153 default: 154 break; 155 } 156 157 /* format and output the error message */ 158 if (exiting) 159 { 160 /* 161 ** Since we are terminating the process, 162 ** we are aborting the entire SMTP session, 163 ** rather than just the current transaction. 164 */ 165 166 p = "421"; 167 enhsc = "4.0.0"; 168 } 169 else if (save_errno == 0) 170 { 171 p = "554"; 172 enhsc = "5.0.0"; 173 } 174 else 175 { 176 p = "451"; 177 enhsc = "4.0.0"; 178 } 179 SM_VA_START(ap, fmt); 180 errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap); 181 SM_VA_END(ap); 182 puterrmsg(MsgBuf); 183 184 /* save this message for mailq printing */ 185 if (!panic && CurEnv != NULL && (!keep || CurEnv->e_message == NULL)) 186 { 187 char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 188 189 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 190 sm_free(CurEnv->e_message); 191 CurEnv->e_message = nmsg; 192 } 193 194 /* determine exit status if not already set */ 195 if (ExitStat == EX_OK) 196 { 197 if (save_errno == 0) 198 ExitStat = EX_SOFTWARE; 199 else 200 ExitStat = EX_OSERR; 201 if (tTd(54, 1)) 202 sm_dprintf("syserr: ExitStat = %d\n", ExitStat); 203 } 204 205 pw = sm_getpwuid(RealUid); 206 if (pw != NULL) 207 user = pw->pw_name; 208 else 209 { 210 user = ubuf; 211 (void) sm_snprintf(ubuf, sizeof(ubuf), "UID%d", (int) RealUid); 212 } 213 214 if (LogLevel > 0) 215 sm_syslog(panic ? LOG_ALERT : LOG_CRIT, 216 CurEnv == NULL ? NOQID : CurEnv->e_id, 217 "SYSERR(%s): %.900s", 218 user, errtxt); 219 switch (save_errno) 220 { 221 case EBADF: 222 case ENFILE: 223 case EMFILE: 224 case ENOTTY: 225 #ifdef EFBIG 226 case EFBIG: 227 #endif 228 #ifdef ESPIPE 229 case ESPIPE: 230 #endif 231 #ifdef EPIPE 232 case EPIPE: 233 #endif 234 #ifdef ENOBUFS 235 case ENOBUFS: 236 #endif 237 #ifdef ESTALE 238 case ESTALE: 239 #endif 240 printopenfds(true); 241 mci_dump_all(smioout, true); 242 break; 243 } 244 if (panic) 245 { 246 #if XLA 247 xla_all_end(); 248 #endif 249 sync_queue_time(); 250 if (tTd(0, 1)) 251 abort(); 252 exit(EX_OSERR); 253 } 254 errno = 0; 255 if (QuickAbort) 256 sm_exc_raisenew_x(&EtypeQuickAbort, 2); 257 } 258 259 /* 260 ** USRERR -- Signal user error. 261 ** 262 ** This is much like syserr except it is for user errors. 263 ** 264 ** Parameters: 265 ** fmt -- the format string. If it does not begin with 266 ** a three-digit SMTP reply code, 550 is assumed. 267 ** (others) -- sm_io_printf strings 268 ** 269 ** Returns: 270 ** none 271 ** Raises E:mta.quickabort if QuickAbort is set. 272 ** 273 ** Side Effects: 274 ** increments Errors. 275 */ 276 277 /*VARARGS1*/ 278 void 279 #ifdef __STDC__ 280 usrerr(const char *fmt, ...) 281 #else /* __STDC__ */ 282 usrerr(fmt, va_alist) 283 const char *fmt; 284 va_dcl 285 #endif /* __STDC__ */ 286 { 287 char *enhsc; 288 char *errtxt; 289 SM_VA_LOCAL_DECL 290 291 if (fmt[0] == '5' || fmt[0] == '6') 292 enhsc = "5.0.0"; 293 else if (fmt[0] == '4' || fmt[0] == '8') 294 enhsc = "4.0.0"; 295 else if (fmt[0] == '2') 296 enhsc = "2.0.0"; 297 else 298 enhsc = NULL; 299 SM_VA_START(ap, fmt); 300 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap); 301 SM_VA_END(ap); 302 303 if (SuprErrs) 304 return; 305 306 /* save this message for mailq printing */ 307 switch (MsgBuf[0]) 308 { 309 case '4': 310 case '8': 311 if (CurEnv->e_message != NULL) 312 break; 313 314 /* FALLTHROUGH */ 315 316 case '5': 317 case '6': 318 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 319 sm_free(CurEnv->e_message); 320 if (MsgBuf[0] == '6') 321 { 322 char buf[MAXLINE]; 323 324 (void) sm_snprintf(buf, sizeof(buf), 325 "Postmaster warning: %.*s", 326 (int) sizeof(buf) - 22, errtxt); 327 CurEnv->e_message = 328 sm_rpool_strdup_x(CurEnv->e_rpool, buf); 329 } 330 else 331 { 332 CurEnv->e_message = 333 sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 334 } 335 break; 336 } 337 338 puterrmsg(MsgBuf); 339 if (LogLevel > 3 && LogUsrErrs) 340 sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); 341 if (QuickAbort) 342 sm_exc_raisenew_x(&EtypeQuickAbort, 1); 343 } 344 345 /* 346 ** USRERRENH -- Signal user error. 347 ** 348 ** Same as usrerr but with enhanced status code. 349 ** 350 ** Parameters: 351 ** enhsc -- the enhanced status code. 352 ** fmt -- the format string. If it does not begin with 353 ** a three-digit SMTP reply code, 550 is assumed. 354 ** (others) -- sm_io_printf strings 355 ** 356 ** Returns: 357 ** none 358 ** Raises E:mta.quickabort if QuickAbort is set. 359 ** 360 ** Side Effects: 361 ** increments Errors. 362 */ 363 364 /*VARARGS2*/ 365 void 366 #ifdef __STDC__ 367 usrerrenh(char *enhsc, const char *fmt, ...) 368 #else /* __STDC__ */ 369 usrerrenh(enhsc, fmt, va_alist) 370 char *enhsc; 371 const char *fmt; 372 va_dcl 373 #endif /* __STDC__ */ 374 { 375 char *errtxt; 376 SM_VA_LOCAL_DECL 377 378 if (enhsc == NULL || *enhsc == '\0') 379 { 380 if (fmt[0] == '5' || fmt[0] == '6') 381 enhsc = "5.0.0"; 382 else if (fmt[0] == '4' || fmt[0] == '8') 383 enhsc = "4.0.0"; 384 else if (fmt[0] == '2') 385 enhsc = "2.0.0"; 386 } 387 SM_VA_START(ap, fmt); 388 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap); 389 SM_VA_END(ap); 390 391 if (SuprErrs) 392 return; 393 394 /* save this message for mailq printing */ 395 switch (MsgBuf[0]) 396 { 397 case '4': 398 case '8': 399 if (CurEnv->e_message != NULL) 400 break; 401 402 /* FALLTHROUGH */ 403 404 case '5': 405 case '6': 406 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 407 sm_free(CurEnv->e_message); 408 if (MsgBuf[0] == '6') 409 { 410 char buf[MAXLINE]; 411 412 (void) sm_snprintf(buf, sizeof(buf), 413 "Postmaster warning: %.*s", 414 (int) sizeof(buf) - 22, errtxt); 415 CurEnv->e_message = 416 sm_rpool_strdup_x(CurEnv->e_rpool, buf); 417 } 418 else 419 { 420 CurEnv->e_message = 421 sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 422 } 423 break; 424 } 425 426 puterrmsg(MsgBuf); 427 if (LogLevel > 3 && LogUsrErrs) 428 sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); 429 if (QuickAbort) 430 sm_exc_raisenew_x(&EtypeQuickAbort, 1); 431 } 432 433 /* 434 ** MESSAGE -- print message (not necessarily an error) 435 ** 436 ** Parameters: 437 ** msg -- the message (sm_io_printf fmt) -- it can begin with 438 ** an SMTP reply code. If not, 050 is assumed. 439 ** (others) -- sm_io_printf arguments 440 ** 441 ** Returns: 442 ** none 443 */ 444 445 /*VARARGS1*/ 446 void 447 #ifdef __STDC__ 448 message(const char *msg, ...) 449 #else /* __STDC__ */ 450 message(msg, va_alist) 451 const char *msg; 452 va_dcl 453 #endif /* __STDC__ */ 454 { 455 char *errtxt; 456 SM_VA_LOCAL_DECL 457 458 errno = 0; 459 SM_VA_START(ap, msg); 460 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap); 461 SM_VA_END(ap); 462 putoutmsg(MsgBuf, false, false); 463 464 /* save this message for mailq printing */ 465 switch (MsgBuf[0]) 466 { 467 case '4': 468 case '8': 469 if (CurEnv->e_message != NULL) 470 break; 471 /* FALLTHROUGH */ 472 473 case '5': 474 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 475 sm_free(CurEnv->e_message); 476 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 477 break; 478 } 479 } 480 481 #if _FFR_PROXY 482 /* 483 ** EMESSAGE -- print message (not necessarily an error) 484 ** (same as message() but requires reply code and enhanced status code) 485 ** 486 ** Parameters: 487 ** replycode -- SMTP reply code. 488 ** enhsc -- enhanced status code. 489 ** msg -- the message (sm_io_printf fmt) -- it can begin with 490 ** an SMTP reply code. If not, 050 is assumed. 491 ** (others) -- sm_io_printf arguments 492 ** 493 ** Returns: 494 ** none 495 */ 496 497 /*VARARGS3*/ 498 void 499 # ifdef __STDC__ 500 emessage(const char *replycode, const char *enhsc, const char *msg, ...) 501 # else /* __STDC__ */ 502 emessage(replycode, enhsc, msg, va_alist) 503 const char *replycode; 504 const char *enhsc; 505 const char *msg; 506 va_dcl 507 # endif /* __STDC__ */ 508 { 509 char *errtxt; 510 SM_VA_LOCAL_DECL 511 512 errno = 0; 513 SM_VA_START(ap, msg); 514 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, replycode, enhsc, 0, msg, ap); 515 SM_VA_END(ap); 516 putoutmsg(MsgBuf, false, false); 517 518 /* save this message for mailq printing */ 519 switch (MsgBuf[0]) 520 { 521 case '4': 522 case '8': 523 if (CurEnv->e_message != NULL) 524 break; 525 /* FALLTHROUGH */ 526 527 case '5': 528 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 529 sm_free(CurEnv->e_message); 530 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 531 break; 532 } 533 } 534 535 /* 536 ** EXTSC -- check and extract a status codes 537 ** 538 ** Parameters: 539 ** msg -- string with possible enhanced status code. 540 ** delim -- delim for enhanced status code. 541 ** replycode -- pointer to storage for SMTP reply code; 542 ** must be != NULL and have space for at least 543 ** 4 characters. 544 ** enhsc -- pointer to storage for enhanced status code; 545 ** must be != NULL and have space for at least 546 ** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3}) 547 ** 548 ** Returns: 549 ** -1 -- no SMTP reply code. 550 ** >=3 -- offset of error text in msg. 551 ** (<=4 -- no enhanced status code) 552 */ 553 554 int 555 extsc(msg, delim, replycode, enhsc) 556 const char *msg; 557 int delim; 558 char *replycode; 559 char *enhsc; 560 { 561 int offset; 562 563 SM_REQUIRE(replycode != NULL); 564 SM_REQUIRE(enhsc != NULL); 565 replycode[0] = '\0'; 566 enhsc[0] = '\0'; 567 if (msg == NULL) 568 return -1; 569 if (!ISSMTPREPLY(msg)) 570 return -1; 571 sm_strlcpy(replycode, msg, 4); 572 if (msg[3] == '\0') 573 return 3; 574 offset = 4; 575 if (isenhsc(msg + 4, delim)) 576 offset = extenhsc(msg + 4, delim, enhsc) + 4; 577 return offset; 578 } 579 #endif /* _FFR_PROXY */ 580 581 /* 582 ** NMESSAGE -- print message (not necessarily an error) 583 ** 584 ** Just like "message" except it never puts the to... tag on. 585 ** 586 ** Parameters: 587 ** msg -- the message (sm_io_printf fmt) -- if it begins 588 ** with a three digit SMTP reply code, that is used, 589 ** otherwise 050 is assumed. 590 ** (others) -- sm_io_printf arguments 591 ** 592 ** Returns: 593 ** none 594 */ 595 596 /*VARARGS1*/ 597 void 598 #ifdef __STDC__ 599 nmessage(const char *msg, ...) 600 #else /* __STDC__ */ 601 nmessage(msg, va_alist) 602 const char *msg; 603 va_dcl 604 #endif /* __STDC__ */ 605 { 606 char *errtxt; 607 SM_VA_LOCAL_DECL 608 609 errno = 0; 610 SM_VA_START(ap, msg); 611 errtxt = fmtmsg(MsgBuf, (char *) NULL, "050", 612 (char *) NULL, 0, msg, ap); 613 SM_VA_END(ap); 614 putoutmsg(MsgBuf, false, false); 615 616 /* save this message for mailq printing */ 617 switch (MsgBuf[0]) 618 { 619 case '4': 620 case '8': 621 if (CurEnv->e_message != NULL) 622 break; 623 /* FALLTHROUGH */ 624 625 case '5': 626 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 627 sm_free(CurEnv->e_message); 628 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 629 break; 630 } 631 } 632 633 /* 634 ** PUTOUTMSG -- output error message to transcript and channel 635 ** 636 ** Parameters: 637 ** msg -- message to output (in SMTP format). 638 ** holdmsg -- if true, don't output a copy of the message to 639 ** our output channel. 640 ** heldmsg -- if true, this is a previously held message; 641 ** don't log it to the transcript file. 642 ** 643 ** Returns: 644 ** none. 645 ** 646 ** Side Effects: 647 ** Outputs msg to the transcript. 648 ** If appropriate, outputs it to the channel. 649 ** Deletes SMTP reply code number as appropriate. 650 */ 651 652 static void 653 putoutmsg(msg, holdmsg, heldmsg) 654 char *msg; 655 bool holdmsg; 656 bool heldmsg; 657 { 658 char msgcode = msg[0]; 659 char *errtxt = msg; 660 char *id; 661 662 /* display for debugging */ 663 if (tTd(54, 8)) 664 sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 665 heldmsg ? " (held)" : ""); 666 667 /* map warnings to something SMTP can handle */ 668 if (msgcode == '6') 669 msg[0] = '5'; 670 else if (msgcode == '8') 671 msg[0] = '4'; 672 id = (CurEnv != NULL) ? CurEnv->e_id : NULL; 673 674 /* output to transcript if serious */ 675 if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && 676 strchr("45", msg[0]) != NULL) 677 (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n", 678 msg); 679 680 if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 681 sm_syslog(LOG_INFO, id, 682 "--- %s%s%s", msg, holdmsg ? " (hold)" : "", 683 heldmsg ? " (held)" : ""); 684 685 if (msgcode == '8') 686 msg[0] = '0'; 687 688 /* output to channel if appropriate */ 689 if (!Verbose && msg[0] == '0') 690 return; 691 if (holdmsg) 692 { 693 /* save for possible future display */ 694 msg[0] = msgcode; 695 if (HeldMessageBuf[0] == '5' && msgcode == '4') 696 return; 697 (void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf)); 698 return; 699 } 700 701 (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 702 703 if (OutChannel == NULL) 704 return; 705 706 /* find actual text of error (after SMTP status codes) */ 707 if (ISSMTPREPLY(errtxt)) 708 { 709 int l; 710 711 errtxt += 4; 712 l = isenhsc(errtxt, ' '); 713 if (l <= 0) 714 l = isenhsc(errtxt, '\0'); 715 if (l > 0) 716 errtxt += l + 1; 717 } 718 719 /* if DisConnected, OutChannel now points to the transcript */ 720 if (!DisConnected && 721 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 722 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n", 723 msg); 724 else 725 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n", 726 errtxt); 727 if (TrafficLogFile != NULL) 728 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 729 "%05d >>> %s\n", (int) CurrentPid, 730 (OpMode == MD_SMTP || OpMode == MD_DAEMON) 731 ? msg : errtxt); 732 #if !PIPELINING 733 /* XXX can't flush here for SMTP pipelining */ 734 if (msg[3] == ' ') 735 (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 736 if (!sm_io_error(OutChannel) || DisConnected) 737 return; 738 739 /* 740 ** Error on output -- if reporting lost channel, just ignore it. 741 ** Also, ignore errors from QUIT response (221 message) -- some 742 ** rude servers don't read result. 743 */ 744 745 if (InChannel == NULL || sm_io_eof(InChannel) || 746 sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0) 747 return; 748 749 /* can't call syserr, 'cause we are using MsgBuf */ 750 HoldErrs = true; 751 if (LogLevel > 0) 752 sm_syslog(LOG_CRIT, id, 753 "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 754 CURHOSTNAME, 755 shortenstring(msg, MAXSHORTSTR), sm_errstring(errno)); 756 #endif /* !PIPELINING */ 757 } 758 759 /* 760 ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 761 ** 762 ** Parameters: 763 ** msg -- the message to output. 764 ** 765 ** Returns: 766 ** none. 767 ** 768 ** Side Effects: 769 ** Sets the fatal error bit in the envelope as appropriate. 770 */ 771 772 static void 773 puterrmsg(msg) 774 char *msg; 775 { 776 char msgcode = msg[0]; 777 778 /* output the message as usual */ 779 putoutmsg(msg, HoldErrs, false); 780 781 /* be careful about multiple error messages */ 782 if (OnlyOneError) 783 HoldErrs = true; 784 785 /* signal the error */ 786 Errors++; 787 788 if (CurEnv == NULL) 789 return; 790 791 if (msgcode == '6') 792 { 793 /* notify the postmaster */ 794 CurEnv->e_flags |= EF_PM_NOTIFY; 795 } 796 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 797 { 798 /* mark long-term fatal errors */ 799 CurEnv->e_flags |= EF_FATALERRS; 800 } 801 } 802 803 /* 804 ** ISENHSC -- check whether a string contains an enhanced status code 805 ** 806 ** Parameters: 807 ** s -- string with possible enhanced status code. 808 ** delim -- delim for enhanced status code. 809 ** 810 ** Returns: 811 ** 0 -- no enhanced status code. 812 ** >4 -- length of enhanced status code. 813 */ 814 815 int 816 isenhsc(s, delim) 817 const char *s; 818 int delim; 819 { 820 int l, h; 821 822 if (s == NULL) 823 return 0; 824 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 825 return 0; 826 h = 0; 827 l = 2; 828 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 829 ++h; 830 if (h == 0 || s[l + h] != '.') 831 return 0; 832 l += h + 1; 833 h = 0; 834 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 835 ++h; 836 if (h == 0 || s[l + h] != delim) 837 return 0; 838 return l + h; 839 } 840 841 /* 842 ** EXTENHSC -- check and extract an enhanced status code 843 ** 844 ** Parameters: 845 ** s -- string with possible enhanced status code. 846 ** delim -- delim for enhanced status code. 847 ** e -- pointer to storage for enhanced status code. 848 ** must be != NULL and have space for at least 849 ** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3}) 850 ** 851 ** Returns: 852 ** 0 -- no enhanced status code. 853 ** >4 -- length of enhanced status code. 854 ** 855 ** Side Effects: 856 ** fills e with enhanced status code. 857 */ 858 859 int 860 extenhsc(s, delim, e) 861 const char *s; 862 int delim; 863 char *e; 864 { 865 int l, h; 866 867 if (s == NULL) 868 return 0; 869 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 870 return 0; 871 h = 0; 872 l = 2; 873 e[0] = s[0]; 874 e[1] = '.'; 875 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 876 { 877 e[l + h] = s[l + h]; 878 ++h; 879 } 880 if (h == 0 || s[l + h] != '.') 881 return 0; 882 e[l + h] = '.'; 883 l += h + 1; 884 h = 0; 885 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 886 { 887 e[l + h] = s[l + h]; 888 ++h; 889 } 890 if (h == 0 || s[l + h] != delim) 891 return 0; 892 e[l + h] = '\0'; 893 return l + h; 894 } 895 896 /* 897 ** FMTMSG -- format a message into buffer. 898 ** 899 ** Parameters: 900 ** eb -- error buffer to get result -- MUST BE MsgBuf. 901 ** to -- the recipient tag for this message. 902 ** num -- default three digit SMTP reply code. 903 ** enhsc -- enhanced status code. 904 ** en -- the error number to display. 905 ** fmt -- format of string: See NOTE below. 906 ** ap -- arguments for fmt. 907 ** 908 ** Returns: 909 ** pointer to error text beyond status codes. 910 ** 911 ** NOTE: 912 ** Do NOT use "%s" as fmt if the argument starts with an SMTP 913 ** reply code! 914 */ 915 916 static char * 917 fmtmsg(eb, to, num, enhsc, eno, fmt, ap) 918 register char *eb; 919 const char *to; 920 const char *num; 921 const char *enhsc; 922 int eno; 923 const char *fmt; 924 SM_VA_LOCAL_DECL 925 { 926 char del; 927 int l; 928 int spaceleft = sizeof(MsgBuf); 929 char *errtxt; 930 931 /* output the reply code */ 932 if (ISSMTPCODE(fmt)) 933 { 934 num = fmt; 935 fmt += 4; 936 } 937 if (num[3] == '-') 938 del = '-'; 939 else 940 del = ' '; 941 if (SoftBounce && num[0] == '5') 942 { 943 /* replace 5 by 4 */ 944 (void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del); 945 } 946 else 947 (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del); 948 eb += 4; 949 spaceleft -= 4; 950 951 if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4) 952 { 953 /* copy enh.status code including trailing blank */ 954 l++; 955 (void) sm_strlcpy(eb, fmt, l + 1); 956 eb += l; 957 spaceleft -= l; 958 fmt += l; 959 } 960 else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4) 961 { 962 /* copy enh.status code */ 963 (void) sm_strlcpy(eb, enhsc, l + 1); 964 eb[l] = ' '; 965 eb[++l] = '\0'; 966 eb += l; 967 spaceleft -= l; 968 } 969 if (SoftBounce && eb[-l] == '5') 970 { 971 /* replace 5 by 4 */ 972 eb[-l] = '4'; 973 } 974 errtxt = eb; 975 976 /* output the file name and line number */ 977 if (FileName != NULL) 978 { 979 (void) sm_snprintf(eb, spaceleft, "%s: line %d: ", 980 shortenstring(FileName, 83), LineNumber); 981 eb += (l = strlen(eb)); 982 spaceleft -= l; 983 } 984 985 /* 986 ** output the "to" address only if it is defined and one of the 987 ** following codes is used: 988 ** 050 internal notices, e.g., alias expansion 989 ** 250 Ok 990 ** 252 Cannot VRFY user, but will accept message and attempt delivery 991 ** 450 Requested mail action not taken: mailbox unavailable 992 ** 550 Requested action not taken: mailbox unavailable 993 ** 553 Requested action not taken: mailbox name not allowed 994 ** 995 ** Notice: this still isn't "the right thing", this code shouldn't 996 ** (indirectly) depend on CurEnv->e_to. 997 */ 998 999 if (to != NULL && to[0] != '\0' && 1000 (strncmp(num, "050", 3) == 0 || 1001 strncmp(num, "250", 3) == 0 || 1002 strncmp(num, "252", 3) == 0 || 1003 strncmp(num, "450", 3) == 0 || 1004 strncmp(num, "550", 3) == 0 || 1005 strncmp(num, "553", 3) == 0)) 1006 { 1007 (void) sm_strlcpyn(eb, spaceleft, 2, 1008 shortenstring(to, MAXSHORTSTR), "... "); 1009 spaceleft -= strlen(eb); 1010 #if _FFR_EAI 1011 eb += strlen(eb); 1012 #else 1013 while (*eb != '\0') 1014 *eb++ &= 0177; 1015 #endif 1016 } 1017 1018 /* output the message */ 1019 (void) sm_vsnprintf(eb, spaceleft, fmt, ap); 1020 spaceleft -= strlen(eb); 1021 #if _FFR_EAI 1022 eb += strlen(eb); 1023 #else 1024 while (*eb != '\0') 1025 *eb++ &= 0177; 1026 #endif 1027 1028 /* output the error code, if any */ 1029 if (eno != 0) 1030 (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno)); 1031 1032 return errtxt; 1033 } 1034 1035 /* 1036 ** BUFFER_ERRORS -- arrange to buffer future error messages 1037 ** 1038 ** Parameters: 1039 ** none 1040 ** 1041 ** Returns: 1042 ** none. 1043 */ 1044 1045 void 1046 buffer_errors() 1047 { 1048 HeldMessageBuf[0] = '\0'; 1049 HoldErrs = true; 1050 } 1051 1052 /* 1053 ** FLUSH_ERRORS -- flush the held error message buffer 1054 ** 1055 ** Parameters: 1056 ** print -- if set, print the message, otherwise just 1057 ** delete it. 1058 ** 1059 ** Returns: 1060 ** none. 1061 */ 1062 1063 void 1064 flush_errors(print) 1065 bool print; 1066 { 1067 if (print && HeldMessageBuf[0] != '\0') 1068 putoutmsg(HeldMessageBuf, false, true); 1069 HeldMessageBuf[0] = '\0'; 1070 HoldErrs = false; 1071 } 1072 /* 1073 ** SM_ERRSTRING -- return string description of error code 1074 ** 1075 ** Parameters: 1076 ** errnum -- the error number to translate 1077 ** 1078 ** Returns: 1079 ** A string description of errnum. 1080 */ 1081 1082 const char * 1083 sm_errstring(errnum) 1084 int errnum; 1085 { 1086 char *dnsmsg; 1087 char *bp; 1088 static char buf[MAXLINE]; 1089 #if HASSTRERROR 1090 char *err; 1091 char errbuf[30]; 1092 #endif 1093 #if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) 1094 extern char *sys_errlist[]; 1095 extern int sys_nerr; 1096 #endif 1097 1098 /* 1099 ** Handle special network error codes. 1100 ** 1101 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 1102 */ 1103 1104 dnsmsg = NULL; 1105 switch (errnum) 1106 { 1107 case ETIMEDOUT: 1108 case ECONNRESET: 1109 bp = buf; 1110 #if HASSTRERROR 1111 err = strerror(errnum); 1112 if (err == NULL) 1113 { 1114 (void) sm_snprintf(errbuf, sizeof(errbuf), 1115 "Error %d", errnum); 1116 err = errbuf; 1117 } 1118 (void) sm_strlcpy(bp, err, SPACELEFT(buf, bp)); 1119 #else /* HASSTRERROR */ 1120 if (errnum >= 0 && errnum < sys_nerr) 1121 (void) sm_strlcpy(bp, sys_errlist[errnum], 1122 SPACELEFT(buf, bp)); 1123 else 1124 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1125 "Error %d", errnum); 1126 #endif /* HASSTRERROR */ 1127 bp += strlen(bp); 1128 if (CurHostName != NULL) 1129 { 1130 if (errnum == ETIMEDOUT) 1131 { 1132 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1133 " with "); 1134 bp += strlen(bp); 1135 } 1136 else 1137 { 1138 bp = buf; 1139 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1140 "Connection reset by "); 1141 bp += strlen(bp); 1142 } 1143 (void) sm_strlcpy(bp, 1144 shortenstring(CurHostName, MAXSHORTSTR), 1145 SPACELEFT(buf, bp)); 1146 bp += strlen(buf); 1147 } 1148 if (SmtpPhase != NULL) 1149 { 1150 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1151 " during %s", SmtpPhase); 1152 } 1153 return buf; 1154 1155 case EHOSTDOWN: 1156 if (CurHostName == NULL) 1157 break; 1158 (void) sm_snprintf(buf, sizeof(buf), "Host %s is down", 1159 shortenstring(CurHostName, MAXSHORTSTR)); 1160 return buf; 1161 1162 case ECONNREFUSED: 1163 if (CurHostName == NULL) 1164 break; 1165 (void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ", 1166 shortenstring(CurHostName, MAXSHORTSTR)); 1167 return buf; 1168 1169 #if NAMED_BIND 1170 case HOST_NOT_FOUND + E_DNSBASE: 1171 dnsmsg = "host not found"; 1172 break; 1173 1174 case TRY_AGAIN + E_DNSBASE: 1175 dnsmsg = "host name lookup failure"; 1176 break; 1177 1178 case NO_RECOVERY + E_DNSBASE: 1179 dnsmsg = "non-recoverable error"; 1180 break; 1181 1182 case NO_DATA + E_DNSBASE: 1183 dnsmsg = "no data known"; 1184 break; 1185 #endif /* NAMED_BIND */ 1186 1187 case EPERM: 1188 /* SunOS gives "Not owner" -- this is the POSIX message */ 1189 return "Operation not permitted"; 1190 1191 /* 1192 ** Error messages used internally in sendmail. 1193 */ 1194 1195 case E_SM_OPENTIMEOUT: 1196 return "Timeout on file open"; 1197 1198 case E_SM_NOSLINK: 1199 return "Symbolic links not allowed"; 1200 1201 case E_SM_NOHLINK: 1202 return "Hard links not allowed"; 1203 1204 case E_SM_REGONLY: 1205 return "Regular files only"; 1206 1207 case E_SM_ISEXEC: 1208 return "Executable files not allowed"; 1209 1210 case E_SM_WWDIR: 1211 return "World writable directory"; 1212 1213 case E_SM_GWDIR: 1214 return "Group writable directory"; 1215 1216 case E_SM_FILECHANGE: 1217 return "File changed after open"; 1218 1219 case E_SM_WWFILE: 1220 return "World writable file"; 1221 1222 case E_SM_GWFILE: 1223 return "Group writable file"; 1224 1225 case E_SM_GRFILE: 1226 return "Group readable file"; 1227 1228 case E_SM_WRFILE: 1229 return "World readable file"; 1230 } 1231 1232 if (dnsmsg != NULL) 1233 { 1234 bp = buf; 1235 bp += sm_strlcpy(bp, "Name server: ", sizeof(buf)); 1236 if (CurHostName != NULL) 1237 { 1238 (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, 1239 shortenstring(CurHostName, MAXSHORTSTR), ": "); 1240 bp += strlen(bp); 1241 } 1242 (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp)); 1243 return buf; 1244 } 1245 1246 #if LDAPMAP 1247 if (errnum >= E_LDAPBASE - E_LDAP_SHIM) 1248 return ldap_err2string(errnum - E_LDAPBASE); 1249 #endif 1250 1251 #if HASSTRERROR 1252 err = strerror(errnum); 1253 if (err == NULL) 1254 { 1255 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum); 1256 return buf; 1257 } 1258 return err; 1259 #else /* HASSTRERROR */ 1260 if (errnum > 0 && errnum < sys_nerr) 1261 return sys_errlist[errnum]; 1262 1263 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum); 1264 return buf; 1265 #endif /* HASSTRERROR */ 1266 } 1267