1 /* 2 * Copyright (c) 1998-2001 Sendmail, 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 #ifndef lint 15 static char id[] = "@(#)$Id: err.c,v 8.120.4.5 2001/08/17 22:09:40 ca Exp $"; 16 #endif /* ! lint */ 17 18 /* $FreeBSD$ */ 19 20 #include <sendmail.h> 21 #ifdef LDAPMAP 22 # include <lber.h> 23 # include <ldap.h> /* for LDAP error codes */ 24 #endif /* LDAPMAP */ 25 26 27 static void putoutmsg __P((char *, bool, bool)); 28 static void puterrmsg __P((char *)); 29 static char *fmtmsg __P((char *, const char *, const char *, const char *, 30 int, const char *, va_list)); 31 32 /* 33 ** SYSERR -- Print error message. 34 ** 35 ** Prints an error message via printf to the diagnostic output. 36 ** 37 ** If the first character of the syserr message is `!' it will 38 ** log this as an ALERT message and exit immediately. This can 39 ** leave queue files in an indeterminate state, so it should not 40 ** be used lightly. 41 ** 42 ** Parameters: 43 ** fmt -- the format string. If it does not begin with 44 ** a three-digit SMTP reply code, either 554 or 45 ** 451 is assumed depending on whether errno 46 ** is set. 47 ** (others) -- parameters 48 ** 49 ** Returns: 50 ** none 51 ** Through TopFrame if QuickAbort is set. 52 ** 53 ** Side Effects: 54 ** increments Errors. 55 ** sets ExitStat. 56 */ 57 58 char MsgBuf[BUFSIZ*2]; /* text of most recent message */ 59 static char HeldMessageBuf[sizeof MsgBuf]; /* for held messages */ 60 61 #if NAMED_BIND && !defined(NO_DATA) 62 # define NO_DATA NO_ADDRESS 63 #endif /* NAMED_BIND && !defined(NO_DATA) */ 64 65 void 66 /*VARARGS1*/ 67 #ifdef __STDC__ 68 syserr(const char *fmt, ...) 69 #else /* __STDC__ */ 70 syserr(fmt, va_alist) 71 const char *fmt; 72 va_dcl 73 #endif /* __STDC__ */ 74 { 75 register char *p; 76 int save_errno = errno; 77 bool panic; 78 char *user; 79 char *enhsc; 80 char *errtxt; 81 struct passwd *pw; 82 char ubuf[80]; 83 VA_LOCAL_DECL 84 85 panic = *fmt == '!'; 86 if (panic) 87 { 88 fmt++; 89 HoldErrs = FALSE; 90 } 91 92 /* format and output the error message */ 93 if (save_errno == 0) 94 { 95 p = "554"; 96 enhsc = "5.0.0"; 97 } 98 else 99 { 100 p = "451"; 101 enhsc = "4.0.0"; 102 } 103 VA_START(fmt); 104 errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap); 105 VA_END; 106 puterrmsg(MsgBuf); 107 108 /* save this message for mailq printing */ 109 if (!panic && CurEnv != NULL) 110 { 111 if (CurEnv->e_message != NULL) 112 sm_free(CurEnv->e_message); 113 CurEnv->e_message = newstr(errtxt); 114 } 115 116 /* determine exit status if not already set */ 117 if (ExitStat == EX_OK) 118 { 119 if (save_errno == 0) 120 ExitStat = EX_SOFTWARE; 121 else 122 ExitStat = EX_OSERR; 123 if (tTd(54, 1)) 124 dprintf("syserr: ExitStat = %d\n", ExitStat); 125 } 126 127 pw = sm_getpwuid(RealUid); 128 if (pw != NULL) 129 user = pw->pw_name; 130 else 131 { 132 user = ubuf; 133 snprintf(ubuf, sizeof ubuf, "UID%d", (int) RealUid); 134 } 135 136 if (LogLevel > 0) 137 sm_syslog(panic ? LOG_ALERT : LOG_CRIT, 138 CurEnv == NULL ? NOQID : CurEnv->e_id, 139 "SYSERR(%s): %.900s", 140 user, errtxt); 141 switch (save_errno) 142 { 143 case EBADF: 144 case ENFILE: 145 case EMFILE: 146 case ENOTTY: 147 #ifdef EFBIG 148 case EFBIG: 149 #endif /* EFBIG */ 150 #ifdef ESPIPE 151 case ESPIPE: 152 #endif /* ESPIPE */ 153 #ifdef EPIPE 154 case EPIPE: 155 #endif /* EPIPE */ 156 #ifdef ENOBUFS 157 case ENOBUFS: 158 #endif /* ENOBUFS */ 159 #ifdef ESTALE 160 case ESTALE: 161 #endif /* ESTALE */ 162 printopenfds(TRUE); 163 mci_dump_all(TRUE); 164 break; 165 } 166 if (panic) 167 { 168 #ifdef XLA 169 xla_all_end(); 170 #endif /* XLA */ 171 sync_queue_time(); 172 if (tTd(0, 1)) 173 abort(); 174 exit(EX_OSERR); 175 } 176 errno = 0; 177 if (QuickAbort) 178 longjmp(TopFrame, 2); 179 } 180 /* 181 ** USRERR -- Signal user error. 182 ** 183 ** This is much like syserr except it is for user errors. 184 ** 185 ** Parameters: 186 ** fmt -- the format string. If it does not begin with 187 ** a three-digit SMTP reply code, 501 is assumed. 188 ** (others) -- printf strings 189 ** 190 ** Returns: 191 ** none 192 ** Through TopFrame if QuickAbort is set. 193 ** 194 ** Side Effects: 195 ** increments Errors. 196 */ 197 198 /*VARARGS1*/ 199 void 200 #ifdef __STDC__ 201 usrerr(const char *fmt, ...) 202 #else /* __STDC__ */ 203 usrerr(fmt, va_alist) 204 const char *fmt; 205 va_dcl 206 #endif /* __STDC__ */ 207 { 208 char *enhsc; 209 char *errtxt; 210 VA_LOCAL_DECL 211 212 if (fmt[0] == '5' || fmt[0] == '6') 213 enhsc = "5.0.0"; 214 else if (fmt[0] == '4' || fmt[0] == '8') 215 enhsc = "4.0.0"; 216 else if (fmt[0] == '2') 217 enhsc = "2.0.0"; 218 else 219 enhsc = NULL; 220 VA_START(fmt); 221 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "501", enhsc, 0, fmt, ap); 222 VA_END; 223 224 if (SuprErrs) 225 return; 226 227 /* save this message for mailq printing */ 228 switch (MsgBuf[0]) 229 { 230 case '4': 231 case '8': 232 if (CurEnv->e_message != NULL) 233 break; 234 235 /* FALLTHROUGH */ 236 237 case '5': 238 case '6': 239 if (CurEnv->e_message != NULL) 240 sm_free(CurEnv->e_message); 241 if (MsgBuf[0] == '6') 242 { 243 char buf[MAXLINE]; 244 245 snprintf(buf, sizeof buf, "Postmaster warning: %.*s", 246 (int) sizeof buf - 22, errtxt); 247 CurEnv->e_message = newstr(buf); 248 } 249 else 250 { 251 CurEnv->e_message = newstr(errtxt); 252 } 253 break; 254 } 255 256 puterrmsg(MsgBuf); 257 258 if (LogLevel > 3 && LogUsrErrs) 259 sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); 260 261 if (QuickAbort) 262 longjmp(TopFrame, 1); 263 } 264 /* 265 ** USRERRENH -- Signal user error. 266 ** 267 ** Same as usrerr but with enhanced status code. 268 ** 269 ** Parameters: 270 ** enhsc -- the enhanced status code. 271 ** fmt -- the format string. If it does not begin with 272 ** a three-digit SMTP reply code, 501 is assumed. 273 ** (others) -- printf strings 274 ** 275 ** Returns: 276 ** none 277 ** Through TopFrame if QuickAbort is set. 278 ** 279 ** Side Effects: 280 ** increments Errors. 281 */ 282 283 /*VARARGS1*/ 284 void 285 #ifdef __STDC__ 286 usrerrenh(char *enhsc, const char *fmt, ...) 287 #else /* __STDC__ */ 288 usrerrenh(enhsc, fmt, va_alist) 289 char *enhsc; 290 const char *fmt; 291 va_dcl 292 #endif /* __STDC__ */ 293 { 294 char *errtxt; 295 VA_LOCAL_DECL 296 297 if (enhsc == NULL || *enhsc == '\0') 298 { 299 if (fmt[0] == '5' || fmt[0] == '6') 300 enhsc = "5.0.0"; 301 else if (fmt[0] == '4' || fmt[0] == '8') 302 enhsc = "4.0.0"; 303 else if (fmt[0] == '2') 304 enhsc = "2.0.0"; 305 } 306 VA_START(fmt); 307 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "501", enhsc, 0, fmt, ap); 308 VA_END; 309 310 if (SuprErrs) 311 return; 312 313 /* save this message for mailq printing */ 314 switch (MsgBuf[0]) 315 { 316 case '4': 317 case '8': 318 if (CurEnv->e_message != NULL) 319 break; 320 321 /* FALLTHROUGH */ 322 323 case '5': 324 case '6': 325 if (CurEnv->e_message != NULL) 326 sm_free(CurEnv->e_message); 327 if (MsgBuf[0] == '6') 328 { 329 char buf[MAXLINE]; 330 331 snprintf(buf, sizeof buf, "Postmaster warning: %.*s", 332 (int) sizeof buf - 22, errtxt); 333 CurEnv->e_message = newstr(buf); 334 } 335 else 336 { 337 CurEnv->e_message = newstr(errtxt); 338 } 339 break; 340 } 341 342 puterrmsg(MsgBuf); 343 344 if (LogLevel > 3 && LogUsrErrs) 345 sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); 346 347 if (QuickAbort) 348 longjmp(TopFrame, 1); 349 } 350 /* 351 ** MESSAGE -- print message (not necessarily an error) 352 ** 353 ** Parameters: 354 ** msg -- the message (printf fmt) -- it can begin with 355 ** an SMTP reply code. If not, 050 is assumed. 356 ** (others) -- printf arguments 357 ** 358 ** Returns: 359 ** none 360 ** 361 ** Side Effects: 362 ** none. 363 */ 364 365 /*VARARGS1*/ 366 void 367 #ifdef __STDC__ 368 message(const char *msg, ...) 369 #else /* __STDC__ */ 370 message(msg, va_alist) 371 const char *msg; 372 va_dcl 373 #endif /* __STDC__ */ 374 { 375 char *errtxt; 376 VA_LOCAL_DECL 377 378 errno = 0; 379 VA_START(msg); 380 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap); 381 VA_END; 382 putoutmsg(MsgBuf, FALSE, FALSE); 383 384 /* save this message for mailq printing */ 385 switch (MsgBuf[0]) 386 { 387 case '4': 388 case '8': 389 if (CurEnv->e_message != NULL) 390 break; 391 /* FALLTHROUGH */ 392 393 case '5': 394 if (CurEnv->e_message != NULL) 395 sm_free(CurEnv->e_message); 396 CurEnv->e_message = newstr(errtxt); 397 break; 398 } 399 } 400 /* 401 ** NMESSAGE -- print message (not necessarily an error) 402 ** 403 ** Just like "message" except it never puts the to... tag on. 404 ** 405 ** Parameters: 406 ** msg -- the message (printf fmt) -- if it begins 407 ** with a three digit SMTP reply code, that is used, 408 ** otherwise 050 is assumed. 409 ** (others) -- printf arguments 410 ** 411 ** Returns: 412 ** none 413 ** 414 ** Side Effects: 415 ** none. 416 */ 417 418 /*VARARGS1*/ 419 void 420 #ifdef __STDC__ 421 nmessage(const char *msg, ...) 422 #else /* __STDC__ */ 423 nmessage(msg, va_alist) 424 const char *msg; 425 va_dcl 426 #endif /* __STDC__ */ 427 { 428 char *errtxt; 429 VA_LOCAL_DECL 430 431 errno = 0; 432 VA_START(msg); 433 errtxt = fmtmsg(MsgBuf, (char *) NULL, "050", 434 (char *) NULL, 0, msg, ap); 435 VA_END; 436 putoutmsg(MsgBuf, FALSE, FALSE); 437 438 /* save this message for mailq printing */ 439 switch (MsgBuf[0]) 440 { 441 case '4': 442 case '8': 443 if (CurEnv->e_message != NULL) 444 break; 445 /* FALLTHROUGH */ 446 447 case '5': 448 if (CurEnv->e_message != NULL) 449 sm_free(CurEnv->e_message); 450 CurEnv->e_message = newstr(errtxt); 451 break; 452 } 453 } 454 /* 455 ** PUTOUTMSG -- output error message to transcript and channel 456 ** 457 ** Parameters: 458 ** msg -- message to output (in SMTP format). 459 ** holdmsg -- if TRUE, don't output a copy of the message to 460 ** our output channel. 461 ** heldmsg -- if TRUE, this is a previously held message; 462 ** don't log it to the transcript file. 463 ** 464 ** Returns: 465 ** none. 466 ** 467 ** Side Effects: 468 ** Outputs msg to the transcript. 469 ** If appropriate, outputs it to the channel. 470 ** Deletes SMTP reply code number as appropriate. 471 */ 472 473 static void 474 putoutmsg(msg, holdmsg, heldmsg) 475 char *msg; 476 bool holdmsg; 477 bool heldmsg; 478 { 479 char *errtxt = msg; 480 char msgcode = msg[0]; 481 482 /* display for debugging */ 483 if (tTd(54, 8)) 484 dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 485 heldmsg ? " (held)" : ""); 486 487 /* map warnings to something SMTP can handle */ 488 if (msgcode == '6') 489 msg[0] = '5'; 490 else if (msgcode == '8') 491 msg[0] = '4'; 492 493 /* output to transcript if serious */ 494 if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && 495 strchr("45", msg[0]) != NULL) 496 fprintf(CurEnv->e_xfp, "%s\n", msg); 497 498 if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 499 sm_syslog(LOG_INFO, CurEnv->e_id, 500 "--> %s%s", 501 msg, holdmsg ? " (held)" : ""); 502 503 if (msgcode == '8') 504 msg[0] = '0'; 505 506 /* output to channel if appropriate */ 507 if (!Verbose && msg[0] == '0') 508 return; 509 if (holdmsg) 510 { 511 /* save for possible future display */ 512 msg[0] = msgcode; 513 if (HeldMessageBuf[0] == '5' && msgcode == '4') 514 return; 515 snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg); 516 return; 517 } 518 519 (void) fflush(stdout); 520 521 if (OutChannel == NULL) 522 return; 523 524 /* find actual text of error (after SMTP status codes) */ 525 if (ISSMTPREPLY(errtxt)) 526 { 527 int l; 528 529 errtxt += 4; 530 l = isenhsc(errtxt, ' '); 531 if (l <= 0) 532 l = isenhsc(errtxt, '\0'); 533 if (l > 0) 534 errtxt += l + 1; 535 } 536 537 /* if DisConnected, OutChannel now points to the transcript */ 538 if (!DisConnected && 539 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 540 fprintf(OutChannel, "%s\r\n", msg); 541 else 542 fprintf(OutChannel, "%s\n", errtxt); 543 if (TrafficLogFile != NULL) 544 fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(), 545 (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : errtxt); 546 if (msg[3] == ' ') 547 (void) fflush(OutChannel); 548 if (!ferror(OutChannel) || DisConnected) 549 return; 550 551 /* 552 ** Error on output -- if reporting lost channel, just ignore it. 553 ** Also, ignore errors from QUIT response (221 message) -- some 554 ** rude servers don't read result. 555 */ 556 557 if (InChannel == NULL || feof(InChannel) || ferror(InChannel) || 558 strncmp(msg, "221", 3) == 0) 559 return; 560 561 /* can't call syserr, 'cause we are using MsgBuf */ 562 HoldErrs = TRUE; 563 if (LogLevel > 0) 564 sm_syslog(LOG_CRIT, CurEnv->e_id, 565 "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 566 CurHostName == NULL ? "NO-HOST" : CurHostName, 567 shortenstring(msg, MAXSHORTSTR), errstring(errno)); 568 } 569 /* 570 ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 571 ** 572 ** Parameters: 573 ** msg -- the message to output. 574 ** 575 ** Returns: 576 ** none. 577 ** 578 ** Side Effects: 579 ** Sets the fatal error bit in the envelope as appropriate. 580 */ 581 582 static void 583 puterrmsg(msg) 584 char *msg; 585 { 586 char msgcode = msg[0]; 587 588 /* output the message as usual */ 589 putoutmsg(msg, HoldErrs, FALSE); 590 591 /* be careful about multiple error messages */ 592 if (OnlyOneError) 593 HoldErrs = TRUE; 594 595 /* signal the error */ 596 Errors++; 597 598 if (CurEnv == NULL) 599 return; 600 601 if (msgcode == '6') 602 { 603 /* notify the postmaster */ 604 CurEnv->e_flags |= EF_PM_NOTIFY; 605 } 606 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 607 { 608 /* mark long-term fatal errors */ 609 CurEnv->e_flags |= EF_FATALERRS; 610 } 611 } 612 /* 613 ** ISENHSC -- check whether a string contains an enhanced status code 614 ** 615 ** Parameters: 616 ** s -- string with possible enhanced status code. 617 ** delim -- delim for enhanced status code. 618 ** 619 ** Returns: 620 ** 0 -- no enhanced status code. 621 ** >4 -- length of enhanced status code. 622 ** 623 ** Side Effects: 624 ** none. 625 */ 626 int 627 isenhsc(s, delim) 628 const char *s; 629 int delim; 630 { 631 int l, h; 632 633 if (s == NULL) 634 return 0; 635 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 636 return 0; 637 h = 0; 638 l = 2; 639 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 640 ++h; 641 if (h == 0 || s[l + h] != '.') 642 return 0; 643 l += h + 1; 644 h = 0; 645 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 646 ++h; 647 if (h == 0 || s[l + h] != delim) 648 return 0; 649 return l + h; 650 } 651 /* 652 ** EXTENHSC -- check and extract an enhanced status code 653 ** 654 ** Parameters: 655 ** s -- string with possible enhanced status code. 656 ** delim -- delim for enhanced status code. 657 ** e -- pointer to storage for enhanced status code. 658 ** must be != NULL and have space for at least 659 ** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3}) 660 ** 661 ** Returns: 662 ** 0 -- no enhanced status code. 663 ** >4 -- length of enhanced status code. 664 ** 665 ** Side Effects: 666 ** fills e with enhanced status code. 667 */ 668 int 669 extenhsc(s, delim, e) 670 const char *s; 671 int delim; 672 char *e; 673 { 674 int l, h; 675 676 if (s == NULL) 677 return 0; 678 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 679 return 0; 680 h = 0; 681 l = 2; 682 e[0] = s[0]; 683 e[1] = '.'; 684 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 685 { 686 e[l + h] = s[l + h]; 687 ++h; 688 } 689 if (h == 0 || s[l + h] != '.') 690 return 0; 691 e[l + h] = '.'; 692 l += h + 1; 693 h = 0; 694 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 695 { 696 e[l + h] = s[l + h]; 697 ++h; 698 } 699 if (h == 0 || s[l + h] != delim) 700 return 0; 701 e[l + h] = '\0'; 702 return l + h; 703 } 704 /* 705 ** FMTMSG -- format a message into buffer. 706 ** 707 ** Parameters: 708 ** eb -- error buffer to get result -- MUST BE MsgBuf. 709 ** to -- the recipient tag for this message. 710 ** num -- default three digit SMTP reply code. 711 ** enhsc -- enhanced status code. 712 ** en -- the error number to display. 713 ** fmt -- format of string. 714 ** ap -- arguments for fmt. 715 ** 716 ** Returns: 717 ** pointer to error text beyond status codes. 718 ** 719 ** Side Effects: 720 ** none. 721 */ 722 723 static char * 724 fmtmsg(eb, to, num, enhsc, eno, fmt, ap) 725 register char *eb; 726 const char *to; 727 const char *num; 728 const char *enhsc; 729 int eno; 730 const char *fmt; 731 va_list ap; 732 { 733 char del; 734 int l; 735 int spaceleft = sizeof MsgBuf; 736 char *errtxt; 737 738 /* output the reply code */ 739 if (ISSMTPCODE(fmt)) 740 { 741 num = fmt; 742 fmt += 4; 743 } 744 if (num[3] == '-') 745 del = '-'; 746 else 747 del = ' '; 748 (void) snprintf(eb, spaceleft, "%3.3s%c", num, del); 749 eb += 4; 750 spaceleft -= 4; 751 752 if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4) 753 { 754 /* copy enh.status code including trailing blank */ 755 l++; 756 (void) strlcpy(eb, fmt, l + 1); 757 eb += l; 758 spaceleft -= l; 759 fmt += l; 760 } 761 else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4) 762 { 763 /* copy enh.status code */ 764 (void) strlcpy(eb, enhsc, l + 1); 765 eb[l] = ' '; 766 eb[++l] = '\0'; 767 eb += l; 768 spaceleft -= l; 769 } 770 errtxt = eb; 771 772 /* output the file name and line number */ 773 if (FileName != NULL) 774 { 775 (void) snprintf(eb, spaceleft, "%s: line %d: ", 776 shortenstring(FileName, 83), LineNumber); 777 eb += (l = strlen(eb)); 778 spaceleft -= l; 779 } 780 781 /* 782 ** output the "to" address only if it is defined and one of the 783 ** following codes is used: 784 ** 050 internal notices, e.g., alias expansion 785 ** 250 Ok 786 ** 252 Cannot VRFY user, but will accept message and attempt delivery 787 ** 450 Requested mail action not taken: mailbox unavailable 788 ** 550 Requested action not taken: mailbox unavailable 789 ** 553 Requested action not taken: mailbox name not allowed 790 ** 791 ** Notice: this still isn't "the right thing", this code shouldn't 792 ** (indirectly) depend on CurEnv->e_to. 793 */ 794 795 if (to != NULL && to[0] != '\0' && 796 (strncmp(num, "050", 3) == 0 || 797 strncmp(num, "250", 3) == 0 || 798 strncmp(num, "252", 3) == 0 || 799 strncmp(num, "450", 3) == 0 || 800 strncmp(num, "550", 3) == 0 || 801 strncmp(num, "553", 3) == 0)) 802 { 803 (void) snprintf(eb, spaceleft, "%s... ", 804 shortenstring(to, MAXSHORTSTR)); 805 spaceleft -= strlen(eb); 806 while (*eb != '\0') 807 *eb++ &= 0177; 808 } 809 810 /* output the message */ 811 (void) vsnprintf(eb, spaceleft, fmt, ap); 812 spaceleft -= strlen(eb); 813 while (*eb != '\0') 814 *eb++ &= 0177; 815 816 /* output the error code, if any */ 817 if (eno != 0) 818 (void) snprintf(eb, spaceleft, ": %s", errstring(eno)); 819 820 return errtxt; 821 } 822 /* 823 ** BUFFER_ERRORS -- arrange to buffer future error messages 824 ** 825 ** Parameters: 826 ** none 827 ** 828 ** Returns: 829 ** none. 830 */ 831 832 void 833 buffer_errors() 834 { 835 HeldMessageBuf[0] = '\0'; 836 HoldErrs = TRUE; 837 } 838 /* 839 ** FLUSH_ERRORS -- flush the held error message buffer 840 ** 841 ** Parameters: 842 ** print -- if set, print the message, otherwise just 843 ** delete it. 844 ** 845 ** Returns: 846 ** none. 847 */ 848 849 void 850 flush_errors(print) 851 bool print; 852 { 853 if (print && HeldMessageBuf[0] != '\0') 854 putoutmsg(HeldMessageBuf, FALSE, TRUE); 855 HeldMessageBuf[0] = '\0'; 856 HoldErrs = FALSE; 857 } 858 /* 859 ** ERRSTRING -- return string description of error code 860 ** 861 ** Parameters: 862 ** errnum -- the error number to translate 863 ** 864 ** Returns: 865 ** A string description of errnum. 866 ** 867 ** Side Effects: 868 ** none. 869 */ 870 871 const char * 872 errstring(errnum) 873 int errnum; 874 { 875 char *dnsmsg; 876 char *bp; 877 static char buf[MAXLINE]; 878 #if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) 879 extern char *sys_errlist[]; 880 extern int sys_nerr; 881 #endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */ 882 883 /* 884 ** Handle special network error codes. 885 ** 886 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 887 */ 888 889 dnsmsg = NULL; 890 switch (errnum) 891 { 892 #if defined(DAEMON) && defined(ETIMEDOUT) 893 case ETIMEDOUT: 894 case ECONNRESET: 895 bp = buf; 896 # if HASSTRERROR 897 snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum)); 898 # else /* HASSTRERROR */ 899 if (errnum >= 0 && errnum < sys_nerr) 900 snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]); 901 else 902 snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum); 903 # endif /* HASSTRERROR */ 904 bp += strlen(bp); 905 if (CurHostName != NULL) 906 { 907 if (errnum == ETIMEDOUT) 908 { 909 snprintf(bp, SPACELEFT(buf, bp), " with "); 910 bp += strlen(bp); 911 } 912 else 913 { 914 bp = buf; 915 snprintf(bp, SPACELEFT(buf, bp), 916 "Connection reset by "); 917 bp += strlen(bp); 918 } 919 snprintf(bp, SPACELEFT(buf, bp), "%s", 920 shortenstring(CurHostName, MAXSHORTSTR)); 921 bp += strlen(buf); 922 } 923 if (SmtpPhase != NULL) 924 { 925 snprintf(bp, SPACELEFT(buf, bp), " during %s", 926 SmtpPhase); 927 } 928 return buf; 929 930 case EHOSTDOWN: 931 if (CurHostName == NULL) 932 break; 933 (void) snprintf(buf, sizeof buf, "Host %s is down", 934 shortenstring(CurHostName, MAXSHORTSTR)); 935 return buf; 936 937 case ECONNREFUSED: 938 if (CurHostName == NULL) 939 break; 940 (void) snprintf(buf, sizeof buf, "Connection refused by %s", 941 shortenstring(CurHostName, MAXSHORTSTR)); 942 return buf; 943 #endif /* defined(DAEMON) && defined(ETIMEDOUT) */ 944 945 #if NAMED_BIND 946 case HOST_NOT_FOUND + E_DNSBASE: 947 dnsmsg = "host not found"; 948 break; 949 950 case TRY_AGAIN + E_DNSBASE: 951 dnsmsg = "host name lookup failure"; 952 break; 953 954 case NO_RECOVERY + E_DNSBASE: 955 dnsmsg = "non-recoverable error"; 956 break; 957 958 case NO_DATA + E_DNSBASE: 959 dnsmsg = "no data known"; 960 break; 961 #endif /* NAMED_BIND */ 962 963 case EPERM: 964 /* SunOS gives "Not owner" -- this is the POSIX message */ 965 return "Operation not permitted"; 966 967 /* 968 ** Error messages used internally in sendmail. 969 */ 970 971 case E_SM_OPENTIMEOUT: 972 return "Timeout on file open"; 973 974 case E_SM_NOSLINK: 975 return "Symbolic links not allowed"; 976 977 case E_SM_NOHLINK: 978 return "Hard links not allowed"; 979 980 case E_SM_REGONLY: 981 return "Regular files only"; 982 983 case E_SM_ISEXEC: 984 return "Executable files not allowed"; 985 986 case E_SM_WWDIR: 987 return "World writable directory"; 988 989 case E_SM_GWDIR: 990 return "Group writable directory"; 991 992 case E_SM_FILECHANGE: 993 return "File changed after open"; 994 995 case E_SM_WWFILE: 996 return "World writable file"; 997 998 case E_SM_GWFILE: 999 return "Group writable file"; 1000 1001 case E_SM_GRFILE: 1002 return "Group readable file"; 1003 1004 case E_SM_WRFILE: 1005 return "World readable file"; 1006 } 1007 1008 if (dnsmsg != NULL) 1009 { 1010 bp = buf; 1011 bp += strlcpy(bp, "Name server: ", sizeof buf); 1012 if (CurHostName != NULL) 1013 { 1014 snprintf(bp, SPACELEFT(buf, bp), "%s: ", 1015 shortenstring(CurHostName, MAXSHORTSTR)); 1016 bp += strlen(bp); 1017 } 1018 snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg); 1019 return buf; 1020 } 1021 1022 #ifdef LDAPMAP 1023 if (errnum >= E_LDAPBASE) 1024 return ldap_err2string(errnum - E_LDAPBASE); 1025 #endif /* LDAPMAP */ 1026 1027 #if HASSTRERROR 1028 return strerror(errnum); 1029 #else /* HASSTRERROR */ 1030 if (errnum > 0 && errnum < sys_nerr) 1031 return sys_errlist[errnum]; 1032 1033 (void) snprintf(buf, sizeof buf, "Error %d", errnum); 1034 return buf; 1035 #endif /* HASSTRERROR */ 1036 } 1037