1 /* 2 * Copyright (c) 1998-2003, 2010 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 /* LDAPMAP */ 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 /* NAMED_BIND && !defined(NO_DATA) */ 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 /* EFBIG */ 228 #ifdef ESPIPE 229 case ESPIPE: 230 #endif /* ESPIPE */ 231 #ifdef EPIPE 232 case EPIPE: 233 #endif /* EPIPE */ 234 #ifdef ENOBUFS 235 case ENOBUFS: 236 #endif /* ENOBUFS */ 237 #ifdef ESTALE 238 case ESTALE: 239 #endif /* ESTALE */ 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 /* XLA */ 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 ** 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 ** USRERRENH -- Signal user error. 345 ** 346 ** Same as usrerr but with enhanced status code. 347 ** 348 ** Parameters: 349 ** enhsc -- the enhanced status code. 350 ** fmt -- the format string. If it does not begin with 351 ** a three-digit SMTP reply code, 550 is assumed. 352 ** (others) -- sm_io_printf strings 353 ** 354 ** Returns: 355 ** none 356 ** Raises E:mta.quickabort if QuickAbort is set. 357 ** 358 ** Side Effects: 359 ** increments Errors. 360 */ 361 362 /*VARARGS2*/ 363 void 364 #ifdef __STDC__ 365 usrerrenh(char *enhsc, const char *fmt, ...) 366 #else /* __STDC__ */ 367 usrerrenh(enhsc, fmt, va_alist) 368 char *enhsc; 369 const char *fmt; 370 va_dcl 371 #endif /* __STDC__ */ 372 { 373 char *errtxt; 374 SM_VA_LOCAL_DECL 375 376 if (enhsc == NULL || *enhsc == '\0') 377 { 378 if (fmt[0] == '5' || fmt[0] == '6') 379 enhsc = "5.0.0"; 380 else if (fmt[0] == '4' || fmt[0] == '8') 381 enhsc = "4.0.0"; 382 else if (fmt[0] == '2') 383 enhsc = "2.0.0"; 384 } 385 SM_VA_START(ap, fmt); 386 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap); 387 SM_VA_END(ap); 388 389 if (SuprErrs) 390 return; 391 392 /* save this message for mailq printing */ 393 switch (MsgBuf[0]) 394 { 395 case '4': 396 case '8': 397 if (CurEnv->e_message != NULL) 398 break; 399 400 /* FALLTHROUGH */ 401 402 case '5': 403 case '6': 404 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 405 sm_free(CurEnv->e_message); 406 if (MsgBuf[0] == '6') 407 { 408 char buf[MAXLINE]; 409 410 (void) sm_snprintf(buf, sizeof(buf), 411 "Postmaster warning: %.*s", 412 (int) sizeof(buf) - 22, errtxt); 413 CurEnv->e_message = 414 sm_rpool_strdup_x(CurEnv->e_rpool, buf); 415 } 416 else 417 { 418 CurEnv->e_message = 419 sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 420 } 421 break; 422 } 423 424 puterrmsg(MsgBuf); 425 if (LogLevel > 3 && LogUsrErrs) 426 sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); 427 if (QuickAbort) 428 sm_exc_raisenew_x(&EtypeQuickAbort, 1); 429 } 430 431 /* 432 ** MESSAGE -- print message (not necessarily an error) 433 ** 434 ** Parameters: 435 ** msg -- the message (sm_io_printf fmt) -- it can begin with 436 ** an SMTP reply code. If not, 050 is assumed. 437 ** (others) -- sm_io_printf arguments 438 ** 439 ** Returns: 440 ** none 441 ** 442 ** Side Effects: 443 ** none. 444 */ 445 446 /*VARARGS1*/ 447 void 448 #ifdef __STDC__ 449 message(const char *msg, ...) 450 #else /* __STDC__ */ 451 message(msg, va_alist) 452 const char *msg; 453 va_dcl 454 #endif /* __STDC__ */ 455 { 456 char *errtxt; 457 SM_VA_LOCAL_DECL 458 459 errno = 0; 460 SM_VA_START(ap, msg); 461 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap); 462 SM_VA_END(ap); 463 putoutmsg(MsgBuf, false, false); 464 465 /* save this message for mailq printing */ 466 switch (MsgBuf[0]) 467 { 468 case '4': 469 case '8': 470 if (CurEnv->e_message != NULL) 471 break; 472 /* FALLTHROUGH */ 473 474 case '5': 475 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 476 sm_free(CurEnv->e_message); 477 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 478 break; 479 } 480 } 481 482 #if _FFR_PROXY 483 /* 484 ** EMESSAGE -- print message (not necessarily an error) 485 ** (same as message() but requires reply code and enhanced status code) 486 ** 487 ** Parameters: 488 ** replycode -- SMTP reply code. 489 ** enhsc -- enhanced status code. 490 ** msg -- the message (sm_io_printf fmt) -- it can begin with 491 ** an SMTP reply code. If not, 050 is assumed. 492 ** (others) -- sm_io_printf arguments 493 ** 494 ** Returns: 495 ** none 496 ** 497 ** Side Effects: 498 ** none. 499 */ 500 501 /*VARARGS3*/ 502 void 503 # ifdef __STDC__ 504 emessage(const char *replycode, const char *enhsc, const char *msg, ...) 505 # else /* __STDC__ */ 506 emessage(replycode, enhsc, msg, va_alist) 507 const char *replycode; 508 const char *enhsc; 509 const char *msg; 510 va_dcl 511 # endif /* __STDC__ */ 512 { 513 char *errtxt; 514 SM_VA_LOCAL_DECL 515 516 errno = 0; 517 SM_VA_START(ap, msg); 518 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, replycode, enhsc, 0, msg, ap); 519 SM_VA_END(ap); 520 putoutmsg(MsgBuf, false, false); 521 522 /* save this message for mailq printing */ 523 switch (MsgBuf[0]) 524 { 525 case '4': 526 case '8': 527 if (CurEnv->e_message != NULL) 528 break; 529 /* FALLTHROUGH */ 530 531 case '5': 532 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 533 sm_free(CurEnv->e_message); 534 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 535 break; 536 } 537 } 538 539 /* 540 ** EXTSC -- check and extract a status codes 541 ** 542 ** Parameters: 543 ** msg -- string with possible enhanced status code. 544 ** delim -- delim for enhanced status code. 545 ** replycode -- pointer to storage for SMTP reply code; 546 ** must be != NULL and have space for at least 547 ** 4 characters. 548 ** enhsc -- pointer to storage for enhanced status code; 549 ** must be != NULL and have space for at least 550 ** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3}) 551 ** 552 ** Returns: 553 ** -1 -- no SMTP reply code. 554 ** >=3 -- offset of error text in msg. 555 ** (<=4 -- no enhanced status code) 556 */ 557 558 int 559 extsc(msg, delim, replycode, enhsc) 560 const char *msg; 561 int delim; 562 char *replycode; 563 char *enhsc; 564 { 565 int offset; 566 567 SM_REQUIRE(replycode != NULL); 568 SM_REQUIRE(enhsc != NULL); 569 replycode[0] = '\0'; 570 enhsc[0] = '\0'; 571 if (msg == NULL) 572 return -1; 573 if (!ISSMTPREPLY(msg)) 574 return -1; 575 sm_strlcpy(replycode, msg, 4); 576 if (msg[3] == '\0') 577 return 3; 578 offset = 4; 579 if (isenhsc(msg + 4, delim)) 580 offset = extenhsc(msg + 4, delim, enhsc) + 4; 581 return offset; 582 } 583 #endif /* _FFR_PROXY */ 584 585 /* 586 ** NMESSAGE -- print message (not necessarily an error) 587 ** 588 ** Just like "message" except it never puts the to... tag on. 589 ** 590 ** Parameters: 591 ** msg -- the message (sm_io_printf fmt) -- if it begins 592 ** with a three digit SMTP reply code, that is used, 593 ** otherwise 050 is assumed. 594 ** (others) -- sm_io_printf arguments 595 ** 596 ** Returns: 597 ** none 598 ** 599 ** Side Effects: 600 ** none. 601 */ 602 603 /*VARARGS1*/ 604 void 605 #ifdef __STDC__ 606 nmessage(const char *msg, ...) 607 #else /* __STDC__ */ 608 nmessage(msg, va_alist) 609 const char *msg; 610 va_dcl 611 #endif /* __STDC__ */ 612 { 613 char *errtxt; 614 SM_VA_LOCAL_DECL 615 616 errno = 0; 617 SM_VA_START(ap, msg); 618 errtxt = fmtmsg(MsgBuf, (char *) NULL, "050", 619 (char *) NULL, 0, msg, ap); 620 SM_VA_END(ap); 621 putoutmsg(MsgBuf, false, false); 622 623 /* save this message for mailq printing */ 624 switch (MsgBuf[0]) 625 { 626 case '4': 627 case '8': 628 if (CurEnv->e_message != NULL) 629 break; 630 /* FALLTHROUGH */ 631 632 case '5': 633 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 634 sm_free(CurEnv->e_message); 635 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 636 break; 637 } 638 } 639 /* 640 ** PUTOUTMSG -- output error message to transcript and channel 641 ** 642 ** Parameters: 643 ** msg -- message to output (in SMTP format). 644 ** holdmsg -- if true, don't output a copy of the message to 645 ** our output channel. 646 ** heldmsg -- if true, this is a previously held message; 647 ** don't log it to the transcript file. 648 ** 649 ** Returns: 650 ** none. 651 ** 652 ** Side Effects: 653 ** Outputs msg to the transcript. 654 ** If appropriate, outputs it to the channel. 655 ** Deletes SMTP reply code number as appropriate. 656 */ 657 658 static void 659 putoutmsg(msg, holdmsg, heldmsg) 660 char *msg; 661 bool holdmsg; 662 bool heldmsg; 663 { 664 char msgcode = msg[0]; 665 char *errtxt = msg; 666 char *id; 667 668 /* display for debugging */ 669 if (tTd(54, 8)) 670 sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 671 heldmsg ? " (held)" : ""); 672 673 /* map warnings to something SMTP can handle */ 674 if (msgcode == '6') 675 msg[0] = '5'; 676 else if (msgcode == '8') 677 msg[0] = '4'; 678 id = (CurEnv != NULL) ? CurEnv->e_id : NULL; 679 680 /* output to transcript if serious */ 681 if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && 682 strchr("45", msg[0]) != NULL) 683 (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n", 684 msg); 685 686 if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 687 sm_syslog(LOG_INFO, id, 688 "--- %s%s%s", msg, holdmsg ? " (hold)" : "", 689 heldmsg ? " (held)" : ""); 690 691 if (msgcode == '8') 692 msg[0] = '0'; 693 694 /* output to channel if appropriate */ 695 if (!Verbose && msg[0] == '0') 696 return; 697 if (holdmsg) 698 { 699 /* save for possible future display */ 700 msg[0] = msgcode; 701 if (HeldMessageBuf[0] == '5' && msgcode == '4') 702 return; 703 (void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf)); 704 return; 705 } 706 707 (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 708 709 if (OutChannel == NULL) 710 return; 711 712 /* find actual text of error (after SMTP status codes) */ 713 if (ISSMTPREPLY(errtxt)) 714 { 715 int l; 716 717 errtxt += 4; 718 l = isenhsc(errtxt, ' '); 719 if (l <= 0) 720 l = isenhsc(errtxt, '\0'); 721 if (l > 0) 722 errtxt += l + 1; 723 } 724 725 /* if DisConnected, OutChannel now points to the transcript */ 726 if (!DisConnected && 727 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 728 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n", 729 msg); 730 else 731 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n", 732 errtxt); 733 if (TrafficLogFile != NULL) 734 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 735 "%05d >>> %s\n", (int) CurrentPid, 736 (OpMode == MD_SMTP || OpMode == MD_DAEMON) 737 ? msg : errtxt); 738 #if !PIPELINING 739 /* XXX can't flush here for SMTP pipelining */ 740 if (msg[3] == ' ') 741 (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 742 if (!sm_io_error(OutChannel) || DisConnected) 743 return; 744 745 /* 746 ** Error on output -- if reporting lost channel, just ignore it. 747 ** Also, ignore errors from QUIT response (221 message) -- some 748 ** rude servers don't read result. 749 */ 750 751 if (InChannel == NULL || sm_io_eof(InChannel) || 752 sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0) 753 return; 754 755 /* can't call syserr, 'cause we are using MsgBuf */ 756 HoldErrs = true; 757 if (LogLevel > 0) 758 sm_syslog(LOG_CRIT, id, 759 "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 760 CURHOSTNAME, 761 shortenstring(msg, MAXSHORTSTR), sm_errstring(errno)); 762 #endif /* !PIPELINING */ 763 } 764 /* 765 ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 766 ** 767 ** Parameters: 768 ** msg -- the message to output. 769 ** 770 ** Returns: 771 ** none. 772 ** 773 ** Side Effects: 774 ** Sets the fatal error bit in the envelope as appropriate. 775 */ 776 777 static void 778 puterrmsg(msg) 779 char *msg; 780 { 781 char msgcode = msg[0]; 782 783 /* output the message as usual */ 784 putoutmsg(msg, HoldErrs, false); 785 786 /* be careful about multiple error messages */ 787 if (OnlyOneError) 788 HoldErrs = true; 789 790 /* signal the error */ 791 Errors++; 792 793 if (CurEnv == NULL) 794 return; 795 796 if (msgcode == '6') 797 { 798 /* notify the postmaster */ 799 CurEnv->e_flags |= EF_PM_NOTIFY; 800 } 801 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 802 { 803 /* mark long-term fatal errors */ 804 CurEnv->e_flags |= EF_FATALERRS; 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 ** Side Effects: 819 ** none. 820 */ 821 int 822 isenhsc(s, delim) 823 const char *s; 824 int delim; 825 { 826 int l, h; 827 828 if (s == NULL) 829 return 0; 830 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 831 return 0; 832 h = 0; 833 l = 2; 834 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 835 ++h; 836 if (h == 0 || s[l + h] != '.') 837 return 0; 838 l += h + 1; 839 h = 0; 840 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 841 ++h; 842 if (h == 0 || s[l + h] != delim) 843 return 0; 844 return l + h; 845 } 846 /* 847 ** EXTENHSC -- check and extract an enhanced status code 848 ** 849 ** Parameters: 850 ** s -- string with possible enhanced status code. 851 ** delim -- delim for enhanced status code. 852 ** e -- pointer to storage for enhanced status code. 853 ** must be != NULL and have space for at least 854 ** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3}) 855 ** 856 ** Returns: 857 ** 0 -- no enhanced status code. 858 ** >4 -- length of enhanced status code. 859 ** 860 ** Side Effects: 861 ** fills e with enhanced status code. 862 */ 863 864 int 865 extenhsc(s, delim, e) 866 const char *s; 867 int delim; 868 char *e; 869 { 870 int l, h; 871 872 if (s == NULL) 873 return 0; 874 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 875 return 0; 876 h = 0; 877 l = 2; 878 e[0] = s[0]; 879 e[1] = '.'; 880 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 881 { 882 e[l + h] = s[l + h]; 883 ++h; 884 } 885 if (h == 0 || s[l + h] != '.') 886 return 0; 887 e[l + h] = '.'; 888 l += h + 1; 889 h = 0; 890 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 891 { 892 e[l + h] = s[l + h]; 893 ++h; 894 } 895 if (h == 0 || s[l + h] != delim) 896 return 0; 897 e[l + h] = '\0'; 898 return l + h; 899 } 900 /* 901 ** FMTMSG -- format a message into buffer. 902 ** 903 ** Parameters: 904 ** eb -- error buffer to get result -- MUST BE MsgBuf. 905 ** to -- the recipient tag for this message. 906 ** num -- default three digit SMTP reply code. 907 ** enhsc -- enhanced status code. 908 ** en -- the error number to display. 909 ** fmt -- format of string. 910 ** ap -- arguments for fmt. 911 ** 912 ** Returns: 913 ** pointer to error text beyond status codes. 914 ** 915 ** Side Effects: 916 ** none. 917 */ 918 919 static char * 920 fmtmsg(eb, to, num, enhsc, eno, fmt, ap) 921 register char *eb; 922 const char *to; 923 const char *num; 924 const char *enhsc; 925 int eno; 926 const char *fmt; 927 SM_VA_LOCAL_DECL 928 { 929 char del; 930 int l; 931 int spaceleft = sizeof(MsgBuf); 932 char *errtxt; 933 934 /* output the reply code */ 935 if (ISSMTPCODE(fmt)) 936 { 937 num = fmt; 938 fmt += 4; 939 } 940 if (num[3] == '-') 941 del = '-'; 942 else 943 del = ' '; 944 if (SoftBounce && num[0] == '5') 945 { 946 /* replace 5 by 4 */ 947 (void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del); 948 } 949 else 950 (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del); 951 eb += 4; 952 spaceleft -= 4; 953 954 if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4) 955 { 956 /* copy enh.status code including trailing blank */ 957 l++; 958 (void) sm_strlcpy(eb, fmt, l + 1); 959 eb += l; 960 spaceleft -= l; 961 fmt += l; 962 } 963 else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4) 964 { 965 /* copy enh.status code */ 966 (void) sm_strlcpy(eb, enhsc, l + 1); 967 eb[l] = ' '; 968 eb[++l] = '\0'; 969 eb += l; 970 spaceleft -= l; 971 } 972 if (SoftBounce && eb[-l] == '5') 973 { 974 /* replace 5 by 4 */ 975 eb[-l] = '4'; 976 } 977 errtxt = eb; 978 979 /* output the file name and line number */ 980 if (FileName != NULL) 981 { 982 (void) sm_snprintf(eb, spaceleft, "%s: line %d: ", 983 shortenstring(FileName, 83), LineNumber); 984 eb += (l = strlen(eb)); 985 spaceleft -= l; 986 } 987 988 /* 989 ** output the "to" address only if it is defined and one of the 990 ** following codes is used: 991 ** 050 internal notices, e.g., alias expansion 992 ** 250 Ok 993 ** 252 Cannot VRFY user, but will accept message and attempt delivery 994 ** 450 Requested mail action not taken: mailbox unavailable 995 ** 550 Requested action not taken: mailbox unavailable 996 ** 553 Requested action not taken: mailbox name not allowed 997 ** 998 ** Notice: this still isn't "the right thing", this code shouldn't 999 ** (indirectly) depend on CurEnv->e_to. 1000 */ 1001 1002 if (to != NULL && to[0] != '\0' && 1003 (strncmp(num, "050", 3) == 0 || 1004 strncmp(num, "250", 3) == 0 || 1005 strncmp(num, "252", 3) == 0 || 1006 strncmp(num, "450", 3) == 0 || 1007 strncmp(num, "550", 3) == 0 || 1008 strncmp(num, "553", 3) == 0)) 1009 { 1010 (void) sm_strlcpyn(eb, spaceleft, 2, 1011 shortenstring(to, MAXSHORTSTR), "... "); 1012 spaceleft -= strlen(eb); 1013 while (*eb != '\0') 1014 *eb++ &= 0177; 1015 } 1016 1017 /* output the message */ 1018 (void) sm_vsnprintf(eb, spaceleft, fmt, ap); 1019 spaceleft -= strlen(eb); 1020 while (*eb != '\0') 1021 *eb++ &= 0177; 1022 1023 /* output the error code, if any */ 1024 if (eno != 0) 1025 (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno)); 1026 1027 return errtxt; 1028 } 1029 /* 1030 ** BUFFER_ERRORS -- arrange to buffer future error messages 1031 ** 1032 ** Parameters: 1033 ** none 1034 ** 1035 ** Returns: 1036 ** none. 1037 */ 1038 1039 void 1040 buffer_errors() 1041 { 1042 HeldMessageBuf[0] = '\0'; 1043 HoldErrs = true; 1044 } 1045 /* 1046 ** FLUSH_ERRORS -- flush the held error message buffer 1047 ** 1048 ** Parameters: 1049 ** print -- if set, print the message, otherwise just 1050 ** delete it. 1051 ** 1052 ** Returns: 1053 ** none. 1054 */ 1055 1056 void 1057 flush_errors(print) 1058 bool print; 1059 { 1060 if (print && HeldMessageBuf[0] != '\0') 1061 putoutmsg(HeldMessageBuf, false, true); 1062 HeldMessageBuf[0] = '\0'; 1063 HoldErrs = false; 1064 } 1065 /* 1066 ** SM_ERRSTRING -- return string description of error code 1067 ** 1068 ** Parameters: 1069 ** errnum -- the error number to translate 1070 ** 1071 ** Returns: 1072 ** A string description of errnum. 1073 ** 1074 ** Side Effects: 1075 ** none. 1076 */ 1077 1078 const char * 1079 sm_errstring(errnum) 1080 int errnum; 1081 { 1082 char *dnsmsg; 1083 char *bp; 1084 static char buf[MAXLINE]; 1085 #if HASSTRERROR 1086 char *err; 1087 char errbuf[30]; 1088 #endif /* HASSTRERROR */ 1089 #if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) 1090 extern char *sys_errlist[]; 1091 extern int sys_nerr; 1092 #endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */ 1093 1094 /* 1095 ** Handle special network error codes. 1096 ** 1097 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 1098 */ 1099 1100 dnsmsg = NULL; 1101 switch (errnum) 1102 { 1103 case ETIMEDOUT: 1104 case ECONNRESET: 1105 bp = buf; 1106 #if HASSTRERROR 1107 err = strerror(errnum); 1108 if (err == NULL) 1109 { 1110 (void) sm_snprintf(errbuf, sizeof(errbuf), 1111 "Error %d", errnum); 1112 err = errbuf; 1113 } 1114 (void) sm_strlcpy(bp, err, SPACELEFT(buf, bp)); 1115 #else /* HASSTRERROR */ 1116 if (errnum >= 0 && errnum < sys_nerr) 1117 (void) sm_strlcpy(bp, sys_errlist[errnum], 1118 SPACELEFT(buf, bp)); 1119 else 1120 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1121 "Error %d", errnum); 1122 #endif /* HASSTRERROR */ 1123 bp += strlen(bp); 1124 if (CurHostName != NULL) 1125 { 1126 if (errnum == ETIMEDOUT) 1127 { 1128 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1129 " with "); 1130 bp += strlen(bp); 1131 } 1132 else 1133 { 1134 bp = buf; 1135 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1136 "Connection reset by "); 1137 bp += strlen(bp); 1138 } 1139 (void) sm_strlcpy(bp, 1140 shortenstring(CurHostName, MAXSHORTSTR), 1141 SPACELEFT(buf, bp)); 1142 bp += strlen(buf); 1143 } 1144 if (SmtpPhase != NULL) 1145 { 1146 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1147 " during %s", SmtpPhase); 1148 } 1149 return buf; 1150 1151 case EHOSTDOWN: 1152 if (CurHostName == NULL) 1153 break; 1154 (void) sm_snprintf(buf, sizeof(buf), "Host %s is down", 1155 shortenstring(CurHostName, MAXSHORTSTR)); 1156 return buf; 1157 1158 case ECONNREFUSED: 1159 if (CurHostName == NULL) 1160 break; 1161 (void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ", 1162 shortenstring(CurHostName, MAXSHORTSTR)); 1163 return buf; 1164 1165 #if NAMED_BIND 1166 case HOST_NOT_FOUND + E_DNSBASE: 1167 dnsmsg = "host not found"; 1168 break; 1169 1170 case TRY_AGAIN + E_DNSBASE: 1171 dnsmsg = "host name lookup failure"; 1172 break; 1173 1174 case NO_RECOVERY + E_DNSBASE: 1175 dnsmsg = "non-recoverable error"; 1176 break; 1177 1178 case NO_DATA + E_DNSBASE: 1179 dnsmsg = "no data known"; 1180 break; 1181 #endif /* NAMED_BIND */ 1182 1183 case EPERM: 1184 /* SunOS gives "Not owner" -- this is the POSIX message */ 1185 return "Operation not permitted"; 1186 1187 /* 1188 ** Error messages used internally in sendmail. 1189 */ 1190 1191 case E_SM_OPENTIMEOUT: 1192 return "Timeout on file open"; 1193 1194 case E_SM_NOSLINK: 1195 return "Symbolic links not allowed"; 1196 1197 case E_SM_NOHLINK: 1198 return "Hard links not allowed"; 1199 1200 case E_SM_REGONLY: 1201 return "Regular files only"; 1202 1203 case E_SM_ISEXEC: 1204 return "Executable files not allowed"; 1205 1206 case E_SM_WWDIR: 1207 return "World writable directory"; 1208 1209 case E_SM_GWDIR: 1210 return "Group writable directory"; 1211 1212 case E_SM_FILECHANGE: 1213 return "File changed after open"; 1214 1215 case E_SM_WWFILE: 1216 return "World writable file"; 1217 1218 case E_SM_GWFILE: 1219 return "Group writable file"; 1220 1221 case E_SM_GRFILE: 1222 return "Group readable file"; 1223 1224 case E_SM_WRFILE: 1225 return "World readable file"; 1226 } 1227 1228 if (dnsmsg != NULL) 1229 { 1230 bp = buf; 1231 bp += sm_strlcpy(bp, "Name server: ", sizeof(buf)); 1232 if (CurHostName != NULL) 1233 { 1234 (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, 1235 shortenstring(CurHostName, MAXSHORTSTR), ": "); 1236 bp += strlen(bp); 1237 } 1238 (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp)); 1239 return buf; 1240 } 1241 1242 #if LDAPMAP 1243 if (errnum >= E_LDAPBASE - E_LDAP_SHIM) 1244 return ldap_err2string(errnum - E_LDAPBASE); 1245 #endif /* LDAPMAP */ 1246 1247 #if HASSTRERROR 1248 err = strerror(errnum); 1249 if (err == NULL) 1250 { 1251 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum); 1252 return buf; 1253 } 1254 return err; 1255 #else /* HASSTRERROR */ 1256 if (errnum > 0 && errnum < sys_nerr) 1257 return sys_errlist[errnum]; 1258 1259 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum); 1260 return buf; 1261 #endif /* HASSTRERROR */ 1262 } 1263