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