err.c revision 182352
1/* 2 * Copyright (c) 1998-2003 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#include <sendmail.h> 15 16SM_RCSID("@(#)$Id: err.c,v 8.196 2006/11/10 23:14:08 ca Exp $") 17 18#if LDAPMAP 19# include <lber.h> 20# include <ldap.h> /* for LDAP error codes */ 21#endif /* LDAPMAP */ 22 23static void putoutmsg __P((char *, bool, bool)); 24static void puterrmsg __P((char *)); 25static 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 50void 51fatal_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 111char MsgBuf[BUFSIZ*2]; /* text of most recent message */ 112static 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 118void 119/*VARARGS1*/ 120#ifdef __STDC__ 121syserr(const char *fmt, ...) 122#else /* __STDC__ */ 123syserr(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; 131 bool exiting; 132 char *user; 133 char *enhsc; 134 char *errtxt; 135 struct passwd *pw; 136 char ubuf[80]; 137 SM_VA_LOCAL_DECL 138 139 switch (*fmt) 140 { 141 case '!': 142 ++fmt; 143 panic = true; 144 exiting = true; 145 break; 146 case '@': 147 ++fmt; 148 panic = false; 149 exiting = true; 150 break; 151 default: 152 panic = false; 153 exiting = false; 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) 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*/ 277void 278#ifdef __STDC__ 279usrerr(const char *fmt, ...) 280#else /* __STDC__ */ 281usrerr(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/*VARARGS1*/ 363void 364#ifdef __STDC__ 365usrerrenh(char *enhsc, const char *fmt, ...) 366#else /* __STDC__ */ 367usrerrenh(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** MESSAGE -- print message (not necessarily an error) 432** 433** Parameters: 434** msg -- the message (sm_io_printf fmt) -- it can begin with 435** an SMTP reply code. If not, 050 is assumed. 436** (others) -- sm_io_printf arguments 437** 438** Returns: 439** none 440** 441** Side Effects: 442** none. 443*/ 444 445/*VARARGS1*/ 446void 447#ifdef __STDC__ 448message(const char *msg, ...) 449#else /* __STDC__ */ 450message(msg, va_alist) 451 const char *msg; 452 va_dcl 453#endif /* __STDC__ */ 454{ 455 char *errtxt; 456 SM_VA_LOCAL_DECL 457 458 errno = 0; 459 SM_VA_START(ap, msg); 460 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap); 461 SM_VA_END(ap); 462 putoutmsg(MsgBuf, false, false); 463 464 /* save this message for mailq printing */ 465 switch (MsgBuf[0]) 466 { 467 case '4': 468 case '8': 469 if (CurEnv->e_message != NULL) 470 break; 471 /* FALLTHROUGH */ 472 473 case '5': 474 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 475 sm_free(CurEnv->e_message); 476 CurEnv->e_message = 477 sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 478 break; 479 } 480} 481/* 482** NMESSAGE -- print message (not necessarily an error) 483** 484** Just like "message" except it never puts the to... tag on. 485** 486** Parameters: 487** msg -- the message (sm_io_printf fmt) -- if it begins 488** with a three digit SMTP reply code, that is used, 489** otherwise 050 is assumed. 490** (others) -- sm_io_printf arguments 491** 492** Returns: 493** none 494** 495** Side Effects: 496** none. 497*/ 498 499/*VARARGS1*/ 500void 501#ifdef __STDC__ 502nmessage(const char *msg, ...) 503#else /* __STDC__ */ 504nmessage(msg, va_alist) 505 const char *msg; 506 va_dcl 507#endif /* __STDC__ */ 508{ 509 char *errtxt; 510 SM_VA_LOCAL_DECL 511 512 errno = 0; 513 SM_VA_START(ap, msg); 514 errtxt = fmtmsg(MsgBuf, (char *) NULL, "050", 515 (char *) NULL, 0, msg, ap); 516 SM_VA_END(ap); 517 putoutmsg(MsgBuf, false, false); 518 519 /* save this message for mailq printing */ 520 switch (MsgBuf[0]) 521 { 522 case '4': 523 case '8': 524 if (CurEnv->e_message != NULL) 525 break; 526 /* FALLTHROUGH */ 527 528 case '5': 529 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 530 sm_free(CurEnv->e_message); 531 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 532 break; 533 } 534} 535/* 536** PUTOUTMSG -- output error message to transcript and channel 537** 538** Parameters: 539** msg -- message to output (in SMTP format). 540** holdmsg -- if true, don't output a copy of the message to 541** our output channel. 542** heldmsg -- if true, this is a previously held message; 543** don't log it to the transcript file. 544** 545** Returns: 546** none. 547** 548** Side Effects: 549** Outputs msg to the transcript. 550** If appropriate, outputs it to the channel. 551** Deletes SMTP reply code number as appropriate. 552*/ 553 554static void 555putoutmsg(msg, holdmsg, heldmsg) 556 char *msg; 557 bool holdmsg; 558 bool heldmsg; 559{ 560 char msgcode = msg[0]; 561 char *errtxt = msg; 562 char *id; 563 564 /* display for debugging */ 565 if (tTd(54, 8)) 566 sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 567 heldmsg ? " (held)" : ""); 568 569 /* map warnings to something SMTP can handle */ 570 if (msgcode == '6') 571 msg[0] = '5'; 572 else if (msgcode == '8') 573 msg[0] = '4'; 574 id = (CurEnv != NULL) ? CurEnv->e_id : NULL; 575 576 /* output to transcript if serious */ 577 if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && 578 strchr("45", msg[0]) != NULL) 579 (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n", 580 msg); 581 582 if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 583 sm_syslog(LOG_INFO, id, 584 "--- %s%s%s", msg, holdmsg ? " (hold)" : "", 585 heldmsg ? " (held)" : ""); 586 587 if (msgcode == '8') 588 msg[0] = '0'; 589 590 /* output to channel if appropriate */ 591 if (!Verbose && msg[0] == '0') 592 return; 593 if (holdmsg) 594 { 595 /* save for possible future display */ 596 msg[0] = msgcode; 597 if (HeldMessageBuf[0] == '5' && msgcode == '4') 598 return; 599 (void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf)); 600 return; 601 } 602 603 (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 604 605 if (OutChannel == NULL) 606 return; 607 608 /* find actual text of error (after SMTP status codes) */ 609 if (ISSMTPREPLY(errtxt)) 610 { 611 int l; 612 613 errtxt += 4; 614 l = isenhsc(errtxt, ' '); 615 if (l <= 0) 616 l = isenhsc(errtxt, '\0'); 617 if (l > 0) 618 errtxt += l + 1; 619 } 620 621 /* if DisConnected, OutChannel now points to the transcript */ 622 if (!DisConnected && 623 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 624 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n", 625 msg); 626 else 627 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n", 628 errtxt); 629 if (TrafficLogFile != NULL) 630 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 631 "%05d >>> %s\n", (int) CurrentPid, 632 (OpMode == MD_SMTP || OpMode == MD_DAEMON) 633 ? msg : errtxt); 634#if !PIPELINING 635 /* XXX can't flush here for SMTP pipelining */ 636 if (msg[3] == ' ') 637 (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 638 if (!sm_io_error(OutChannel) || DisConnected) 639 return; 640 641 /* 642 ** Error on output -- if reporting lost channel, just ignore it. 643 ** Also, ignore errors from QUIT response (221 message) -- some 644 ** rude servers don't read result. 645 */ 646 647 if (InChannel == NULL || sm_io_eof(InChannel) || 648 sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0) 649 return; 650 651 /* can't call syserr, 'cause we are using MsgBuf */ 652 HoldErrs = true; 653 if (LogLevel > 0) 654 sm_syslog(LOG_CRIT, id, 655 "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 656 CURHOSTNAME, 657 shortenstring(msg, MAXSHORTSTR), sm_errstring(errno)); 658#endif /* !PIPELINING */ 659} 660/* 661** PUTERRMSG -- like putoutmsg, but does special processing for error messages 662** 663** Parameters: 664** msg -- the message to output. 665** 666** Returns: 667** none. 668** 669** Side Effects: 670** Sets the fatal error bit in the envelope as appropriate. 671*/ 672 673static void 674puterrmsg(msg) 675 char *msg; 676{ 677 char msgcode = msg[0]; 678 679 /* output the message as usual */ 680 putoutmsg(msg, HoldErrs, false); 681 682 /* be careful about multiple error messages */ 683 if (OnlyOneError) 684 HoldErrs = true; 685 686 /* signal the error */ 687 Errors++; 688 689 if (CurEnv == NULL) 690 return; 691 692 if (msgcode == '6') 693 { 694 /* notify the postmaster */ 695 CurEnv->e_flags |= EF_PM_NOTIFY; 696 } 697 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 698 { 699 /* mark long-term fatal errors */ 700 CurEnv->e_flags |= EF_FATALERRS; 701 } 702} 703/* 704** ISENHSC -- check whether a string contains an enhanced status code 705** 706** Parameters: 707** s -- string with possible enhanced status code. 708** delim -- delim for enhanced status code. 709** 710** Returns: 711** 0 -- no enhanced status code. 712** >4 -- length of enhanced status code. 713** 714** Side Effects: 715** none. 716*/ 717int 718isenhsc(s, delim) 719 const char *s; 720 int delim; 721{ 722 int l, h; 723 724 if (s == NULL) 725 return 0; 726 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 727 return 0; 728 h = 0; 729 l = 2; 730 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 731 ++h; 732 if (h == 0 || s[l + h] != '.') 733 return 0; 734 l += h + 1; 735 h = 0; 736 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 737 ++h; 738 if (h == 0 || s[l + h] != delim) 739 return 0; 740 return l + h; 741} 742/* 743** EXTENHSC -- check and extract an enhanced status code 744** 745** Parameters: 746** s -- string with possible enhanced status code. 747** delim -- delim for enhanced status code. 748** e -- pointer to storage for enhanced status code. 749** must be != NULL and have space for at least 750** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3}) 751** 752** Returns: 753** 0 -- no enhanced status code. 754** >4 -- length of enhanced status code. 755** 756** Side Effects: 757** fills e with enhanced status code. 758*/ 759 760int 761extenhsc(s, delim, e) 762 const char *s; 763 int delim; 764 char *e; 765{ 766 int l, h; 767 768 if (s == NULL) 769 return 0; 770 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 771 return 0; 772 h = 0; 773 l = 2; 774 e[0] = s[0]; 775 e[1] = '.'; 776 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 777 { 778 e[l + h] = s[l + h]; 779 ++h; 780 } 781 if (h == 0 || s[l + h] != '.') 782 return 0; 783 e[l + h] = '.'; 784 l += h + 1; 785 h = 0; 786 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 787 { 788 e[l + h] = s[l + h]; 789 ++h; 790 } 791 if (h == 0 || s[l + h] != delim) 792 return 0; 793 e[l + h] = '\0'; 794 return l + h; 795} 796/* 797** FMTMSG -- format a message into buffer. 798** 799** Parameters: 800** eb -- error buffer to get result -- MUST BE MsgBuf. 801** to -- the recipient tag for this message. 802** num -- default three digit SMTP reply code. 803** enhsc -- enhanced status code. 804** en -- the error number to display. 805** fmt -- format of string. 806** ap -- arguments for fmt. 807** 808** Returns: 809** pointer to error text beyond status codes. 810** 811** Side Effects: 812** none. 813*/ 814 815static char * 816fmtmsg(eb, to, num, enhsc, eno, fmt, ap) 817 register char *eb; 818 const char *to; 819 const char *num; 820 const char *enhsc; 821 int eno; 822 const char *fmt; 823 SM_VA_LOCAL_DECL 824{ 825 char del; 826 int l; 827 int spaceleft = sizeof(MsgBuf); 828 char *errtxt; 829 830 /* output the reply code */ 831 if (ISSMTPCODE(fmt)) 832 { 833 num = fmt; 834 fmt += 4; 835 } 836 if (num[3] == '-') 837 del = '-'; 838 else 839 del = ' '; 840 if (SoftBounce && num[0] == '5') 841 { 842 /* replace 5 by 4 */ 843 (void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del); 844 } 845 else 846 (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del); 847 eb += 4; 848 spaceleft -= 4; 849 850 if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4) 851 { 852 /* copy enh.status code including trailing blank */ 853 l++; 854 (void) sm_strlcpy(eb, fmt, l + 1); 855 eb += l; 856 spaceleft -= l; 857 fmt += l; 858 } 859 else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4) 860 { 861 /* copy enh.status code */ 862 (void) sm_strlcpy(eb, enhsc, l + 1); 863 eb[l] = ' '; 864 eb[++l] = '\0'; 865 eb += l; 866 spaceleft -= l; 867 } 868 if (SoftBounce && eb[-l] == '5') 869 { 870 /* replace 5 by 4 */ 871 eb[-l] = '4'; 872 } 873 errtxt = eb; 874 875 /* output the file name and line number */ 876 if (FileName != NULL) 877 { 878 (void) sm_snprintf(eb, spaceleft, "%s: line %d: ", 879 shortenstring(FileName, 83), LineNumber); 880 eb += (l = strlen(eb)); 881 spaceleft -= l; 882 } 883 884 /* 885 ** output the "to" address only if it is defined and one of the 886 ** following codes is used: 887 ** 050 internal notices, e.g., alias expansion 888 ** 250 Ok 889 ** 252 Cannot VRFY user, but will accept message and attempt delivery 890 ** 450 Requested mail action not taken: mailbox unavailable 891 ** 550 Requested action not taken: mailbox unavailable 892 ** 553 Requested action not taken: mailbox name not allowed 893 ** 894 ** Notice: this still isn't "the right thing", this code shouldn't 895 ** (indirectly) depend on CurEnv->e_to. 896 */ 897 898 if (to != NULL && to[0] != '\0' && 899 (strncmp(num, "050", 3) == 0 || 900 strncmp(num, "250", 3) == 0 || 901 strncmp(num, "252", 3) == 0 || 902 strncmp(num, "450", 3) == 0 || 903 strncmp(num, "550", 3) == 0 || 904 strncmp(num, "553", 3) == 0)) 905 { 906 (void) sm_strlcpyn(eb, spaceleft, 2, 907 shortenstring(to, MAXSHORTSTR), "... "); 908 spaceleft -= strlen(eb); 909 while (*eb != '\0') 910 *eb++ &= 0177; 911 } 912 913 /* output the message */ 914 (void) sm_vsnprintf(eb, spaceleft, fmt, ap); 915 spaceleft -= strlen(eb); 916 while (*eb != '\0') 917 *eb++ &= 0177; 918 919 /* output the error code, if any */ 920 if (eno != 0) 921 (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno)); 922 923 return errtxt; 924} 925/* 926** BUFFER_ERRORS -- arrange to buffer future error messages 927** 928** Parameters: 929** none 930** 931** Returns: 932** none. 933*/ 934 935void 936buffer_errors() 937{ 938 HeldMessageBuf[0] = '\0'; 939 HoldErrs = true; 940} 941/* 942** FLUSH_ERRORS -- flush the held error message buffer 943** 944** Parameters: 945** print -- if set, print the message, otherwise just 946** delete it. 947** 948** Returns: 949** none. 950*/ 951 952void 953flush_errors(print) 954 bool print; 955{ 956 if (print && HeldMessageBuf[0] != '\0') 957 putoutmsg(HeldMessageBuf, false, true); 958 HeldMessageBuf[0] = '\0'; 959 HoldErrs = false; 960} 961/* 962** SM_ERRSTRING -- return string description of error code 963** 964** Parameters: 965** errnum -- the error number to translate 966** 967** Returns: 968** A string description of errnum. 969** 970** Side Effects: 971** none. 972*/ 973 974const char * 975sm_errstring(errnum) 976 int errnum; 977{ 978 char *dnsmsg; 979 char *bp; 980 static char buf[MAXLINE]; 981#if HASSTRERROR 982 char *err; 983 char errbuf[30]; 984#endif /* HASSTRERROR */ 985#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) 986 extern char *sys_errlist[]; 987 extern int sys_nerr; 988#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */ 989 990 /* 991 ** Handle special network error codes. 992 ** 993 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 994 */ 995 996 dnsmsg = NULL; 997 switch (errnum) 998 { 999 case ETIMEDOUT: 1000 case ECONNRESET: 1001 bp = buf; 1002#if HASSTRERROR 1003 err = strerror(errnum); 1004 if (err == NULL) 1005 { 1006 (void) sm_snprintf(errbuf, sizeof(errbuf), 1007 "Error %d", errnum); 1008 err = errbuf; 1009 } 1010 (void) sm_strlcpy(bp, err, SPACELEFT(buf, bp)); 1011#else /* HASSTRERROR */ 1012 if (errnum >= 0 && errnum < sys_nerr) 1013 (void) sm_strlcpy(bp, sys_errlist[errnum], 1014 SPACELEFT(buf, bp)); 1015 else 1016 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1017 "Error %d", errnum); 1018#endif /* HASSTRERROR */ 1019 bp += strlen(bp); 1020 if (CurHostName != NULL) 1021 { 1022 if (errnum == ETIMEDOUT) 1023 { 1024 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1025 " with "); 1026 bp += strlen(bp); 1027 } 1028 else 1029 { 1030 bp = buf; 1031 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1032 "Connection reset by "); 1033 bp += strlen(bp); 1034 } 1035 (void) sm_strlcpy(bp, 1036 shortenstring(CurHostName, MAXSHORTSTR), 1037 SPACELEFT(buf, bp)); 1038 bp += strlen(buf); 1039 } 1040 if (SmtpPhase != NULL) 1041 { 1042 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1043 " during %s", SmtpPhase); 1044 } 1045 return buf; 1046 1047 case EHOSTDOWN: 1048 if (CurHostName == NULL) 1049 break; 1050 (void) sm_snprintf(buf, sizeof(buf), "Host %s is down", 1051 shortenstring(CurHostName, MAXSHORTSTR)); 1052 return buf; 1053 1054 case ECONNREFUSED: 1055 if (CurHostName == NULL) 1056 break; 1057 (void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ", 1058 shortenstring(CurHostName, MAXSHORTSTR)); 1059 return buf; 1060 1061#if NAMED_BIND 1062 case HOST_NOT_FOUND + E_DNSBASE: 1063 dnsmsg = "host not found"; 1064 break; 1065 1066 case TRY_AGAIN + E_DNSBASE: 1067 dnsmsg = "host name lookup failure"; 1068 break; 1069 1070 case NO_RECOVERY + E_DNSBASE: 1071 dnsmsg = "non-recoverable error"; 1072 break; 1073 1074 case NO_DATA + E_DNSBASE: 1075 dnsmsg = "no data known"; 1076 break; 1077#endif /* NAMED_BIND */ 1078 1079 case EPERM: 1080 /* SunOS gives "Not owner" -- this is the POSIX message */ 1081 return "Operation not permitted"; 1082 1083 /* 1084 ** Error messages used internally in sendmail. 1085 */ 1086 1087 case E_SM_OPENTIMEOUT: 1088 return "Timeout on file open"; 1089 1090 case E_SM_NOSLINK: 1091 return "Symbolic links not allowed"; 1092 1093 case E_SM_NOHLINK: 1094 return "Hard links not allowed"; 1095 1096 case E_SM_REGONLY: 1097 return "Regular files only"; 1098 1099 case E_SM_ISEXEC: 1100 return "Executable files not allowed"; 1101 1102 case E_SM_WWDIR: 1103 return "World writable directory"; 1104 1105 case E_SM_GWDIR: 1106 return "Group writable directory"; 1107 1108 case E_SM_FILECHANGE: 1109 return "File changed after open"; 1110 1111 case E_SM_WWFILE: 1112 return "World writable file"; 1113 1114 case E_SM_GWFILE: 1115 return "Group writable file"; 1116 1117 case E_SM_GRFILE: 1118 return "Group readable file"; 1119 1120 case E_SM_WRFILE: 1121 return "World readable file"; 1122 } 1123 1124 if (dnsmsg != NULL) 1125 { 1126 bp = buf; 1127 bp += sm_strlcpy(bp, "Name server: ", sizeof(buf)); 1128 if (CurHostName != NULL) 1129 { 1130 (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, 1131 shortenstring(CurHostName, MAXSHORTSTR), ": "); 1132 bp += strlen(bp); 1133 } 1134 (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp)); 1135 return buf; 1136 } 1137 1138#if LDAPMAP 1139 if (errnum >= E_LDAPBASE) 1140 return ldap_err2string(errnum - E_LDAPBASE); 1141#endif /* LDAPMAP */ 1142 1143#if HASSTRERROR 1144 err = strerror(errnum); 1145 if (err == NULL) 1146 { 1147 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum); 1148 return buf; 1149 } 1150 return err; 1151#else /* HASSTRERROR */ 1152 if (errnum > 0 && errnum < sys_nerr) 1153 return sys_errlist[errnum]; 1154 1155 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum); 1156 return buf; 1157#endif /* HASSTRERROR */ 1158} 1159