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