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