err.c revision 40497
1/* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 */ 12 13#ifndef lint 14static char sccsid[] = "@(#)err.c 8.74 (Berkeley) 6/4/98"; 15#endif /* not lint */ 16 17# include "sendmail.h" 18# include <errno.h> 19 20/* 21** SYSERR -- Print error message. 22** 23** Prints an error message via printf to the diagnostic output. 24** 25** If the first character of the syserr message is `!' it will 26** log this as an ALERT message and exit immediately. This can 27** leave queue files in an indeterminate state, so it should not 28** be used lightly. 29** 30** Parameters: 31** fmt -- the format string. If it does not begin with 32** a three-digit SMTP reply code, either 554 or 33** 451 is assumed depending on whether errno 34** is set. 35** (others) -- parameters 36** 37** Returns: 38** none 39** Through TopFrame if QuickAbort is set. 40** 41** Side Effects: 42** increments Errors. 43** sets ExitStat. 44*/ 45 46char MsgBuf[BUFSIZ*2]; /* text of most recent message */ 47char HeldMessageBuf[sizeof MsgBuf]; /* for held messages */ 48 49extern void putoutmsg __P((char *, bool, bool)); 50extern void puterrmsg __P((char *)); 51static void fmtmsg __P((char *, const char *, const char *, int, const char *, va_list)); 52 53#if NAMED_BIND && !defined(NO_DATA) 54# define NO_DATA NO_ADDRESS 55#endif 56 57void 58/*VARARGS1*/ 59#ifdef __STDC__ 60syserr(const char *fmt, ...) 61#else 62syserr(fmt, va_alist) 63 const char *fmt; 64 va_dcl 65#endif 66{ 67 register char *p; 68 int olderrno = errno; 69 bool panic; 70 char *uname; 71 struct passwd *pw; 72 char ubuf[80]; 73 VA_LOCAL_DECL 74 75 panic = *fmt == '!'; 76 if (panic) 77 { 78 fmt++; 79 HoldErrs = FALSE; 80 } 81 82 /* format and output the error message */ 83 if (olderrno == 0) 84 p = "554"; 85 else 86 p = "451"; 87 VA_START(fmt); 88 fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); 89 VA_END; 90 puterrmsg(MsgBuf); 91 92 /* save this message for mailq printing */ 93 if (!panic && CurEnv != NULL) 94 { 95 if (CurEnv->e_message != NULL) 96 free(CurEnv->e_message); 97 CurEnv->e_message = newstr(MsgBuf + 4); 98 } 99 100 /* determine exit status if not already set */ 101 if (ExitStat == EX_OK) 102 { 103 if (olderrno == 0) 104 ExitStat = EX_SOFTWARE; 105 else 106 ExitStat = EX_OSERR; 107 if (tTd(54, 1)) 108 printf("syserr: ExitStat = %d\n", ExitStat); 109 } 110 111 pw = sm_getpwuid(getuid()); 112 if (pw != NULL) 113 uname = pw->pw_name; 114 else 115 { 116 uname = ubuf; 117 snprintf(ubuf, sizeof ubuf, "UID%d", getuid()); 118 } 119 120 if (LogLevel > 0) 121 sm_syslog(panic ? LOG_ALERT : LOG_CRIT, 122 CurEnv == NULL ? NOQID : CurEnv->e_id, 123 "SYSERR(%s): %.900s", 124 uname, &MsgBuf[4]); 125 switch (olderrno) 126 { 127 case EBADF: 128 case ENFILE: 129 case EMFILE: 130 case ENOTTY: 131#ifdef EFBIG 132 case EFBIG: 133#endif 134#ifdef ESPIPE 135 case ESPIPE: 136#endif 137#ifdef EPIPE 138 case EPIPE: 139#endif 140#ifdef ENOBUFS 141 case ENOBUFS: 142#endif 143#ifdef ESTALE 144 case ESTALE: 145#endif 146 printopenfds(TRUE); 147 mci_dump_all(TRUE); 148 break; 149 } 150 if (panic) 151 { 152#ifdef XLA 153 xla_all_end(); 154#endif 155 if (tTd(0, 1)) 156 abort(); 157 exit(EX_OSERR); 158 } 159 errno = 0; 160 if (QuickAbort) 161 longjmp(TopFrame, 2); 162} 163/* 164** USRERR -- Signal user error. 165** 166** This is much like syserr except it is for user errors. 167** 168** Parameters: 169** fmt -- the format string. If it does not begin with 170** a three-digit SMTP reply code, 501 is assumed. 171** (others) -- printf strings 172** 173** Returns: 174** none 175** Through TopFrame if QuickAbort is set. 176** 177** Side Effects: 178** increments Errors. 179*/ 180 181/*VARARGS1*/ 182void 183#ifdef __STDC__ 184usrerr(const char *fmt, ...) 185#else 186usrerr(fmt, va_alist) 187 const char *fmt; 188 va_dcl 189#endif 190{ 191 VA_LOCAL_DECL 192 193 if (SuprErrs) 194 return; 195 196 VA_START(fmt); 197 fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); 198 VA_END; 199 200 /* save this message for mailq printing */ 201 switch (MsgBuf[0]) 202 { 203 case '4': 204 case '8': 205 if (CurEnv->e_message != NULL) 206 break; 207 208 /* fall through.... */ 209 210 case '5': 211 case '6': 212 if (CurEnv->e_message != NULL) 213 free(CurEnv->e_message); 214 if (MsgBuf[0] == '6') 215 { 216 char buf[MAXLINE]; 217 218 snprintf(buf, sizeof buf, "Postmaster warning: %.*s", 219 (int)sizeof buf - 22, MsgBuf + 4); 220 CurEnv->e_message = newstr(buf); 221 } 222 else 223 { 224 CurEnv->e_message = newstr(MsgBuf + 4); 225 } 226 break; 227 } 228 229 puterrmsg(MsgBuf); 230 231 if (LogLevel > 3 && LogUsrErrs) 232 sm_syslog(LOG_NOTICE, CurEnv->e_id, 233 "%.900s", 234 &MsgBuf[4]); 235 236 if (QuickAbort) 237 longjmp(TopFrame, 1); 238} 239/* 240** MESSAGE -- print message (not necessarily an error) 241** 242** Parameters: 243** msg -- the message (printf fmt) -- it can begin with 244** an SMTP reply code. If not, 050 is assumed. 245** (others) -- printf arguments 246** 247** Returns: 248** none 249** 250** Side Effects: 251** none. 252*/ 253 254/*VARARGS1*/ 255void 256#ifdef __STDC__ 257message(const char *msg, ...) 258#else 259message(msg, va_alist) 260 const char *msg; 261 va_dcl 262#endif 263{ 264 VA_LOCAL_DECL 265 266 errno = 0; 267 VA_START(msg); 268 fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); 269 VA_END; 270 putoutmsg(MsgBuf, FALSE, FALSE); 271 272 /* save this message for mailq printing */ 273 switch (MsgBuf[0]) 274 { 275 case '4': 276 case '8': 277 if (CurEnv->e_message != NULL) 278 break; 279 /* fall through.... */ 280 281 case '5': 282 if (CurEnv->e_message != NULL) 283 free(CurEnv->e_message); 284 CurEnv->e_message = newstr(MsgBuf + 4); 285 break; 286 } 287} 288/* 289** NMESSAGE -- print message (not necessarily an error) 290** 291** Just like "message" except it never puts the to... tag on. 292** 293** Parameters: 294** msg -- the message (printf fmt) -- if it begins 295** with a three digit SMTP reply code, that is used, 296** otherwise 050 is assumed. 297** (others) -- printf arguments 298** 299** Returns: 300** none 301** 302** Side Effects: 303** none. 304*/ 305 306/*VARARGS1*/ 307void 308#ifdef __STDC__ 309nmessage(const char *msg, ...) 310#else 311nmessage(msg, va_alist) 312 const char *msg; 313 va_dcl 314#endif 315{ 316 VA_LOCAL_DECL 317 318 errno = 0; 319 VA_START(msg); 320 fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); 321 VA_END; 322 putoutmsg(MsgBuf, FALSE, FALSE); 323 324 /* save this message for mailq printing */ 325 switch (MsgBuf[0]) 326 { 327 case '4': 328 case '8': 329 if (CurEnv->e_message != NULL) 330 break; 331 /* fall through.... */ 332 333 case '5': 334 if (CurEnv->e_message != NULL) 335 free(CurEnv->e_message); 336 CurEnv->e_message = newstr(MsgBuf + 4); 337 break; 338 } 339} 340/* 341** PUTOUTMSG -- output error message to transcript and channel 342** 343** Parameters: 344** msg -- message to output (in SMTP format). 345** holdmsg -- if TRUE, don't output a copy of the message to 346** our output channel. 347** heldmsg -- if TRUE, this is a previously held message; 348** don't log it to the transcript file. 349** 350** Returns: 351** none. 352** 353** Side Effects: 354** Outputs msg to the transcript. 355** If appropriate, outputs it to the channel. 356** Deletes SMTP reply code number as appropriate. 357*/ 358 359void 360putoutmsg(msg, holdmsg, heldmsg) 361 char *msg; 362 bool holdmsg; 363 bool heldmsg; 364{ 365 char msgcode = msg[0]; 366 367 /* display for debugging */ 368 if (tTd(54, 8)) 369 printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 370 heldmsg ? " (held)" : ""); 371 372 /* map warnings to something SMTP can handle */ 373 if (msgcode == '6') 374 msg[0] = '5'; 375 else if (msgcode == '8') 376 msg[0] = '4'; 377 378 /* output to transcript if serious */ 379 if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && 380 strchr("45", msg[0]) != NULL) 381 fprintf(CurEnv->e_xfp, "%s\n", msg); 382 383 if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 384 sm_syslog(LOG_INFO, CurEnv->e_id, 385 "--> %s%s", 386 msg, holdmsg ? " (held)" : ""); 387 388 if (msgcode == '8') 389 msg[0] = '0'; 390 391 /* output to channel if appropriate */ 392 if (!Verbose && msg[0] == '0') 393 return; 394 if (holdmsg) 395 { 396 /* save for possible future display */ 397 msg[0] = msgcode; 398 snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg); 399 return; 400 } 401 402 (void) fflush(stdout); 403 404 if (OutChannel == NULL) 405 return; 406 407 /* if DisConnected, OutChannel now points to the transcript */ 408 if (!DisConnected && 409 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 410 fprintf(OutChannel, "%s\r\n", msg); 411 else 412 fprintf(OutChannel, "%s\n", &msg[4]); 413 if (TrafficLogFile != NULL) 414 fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(), 415 (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]); 416 if (msg[3] == ' ') 417 (void) fflush(OutChannel); 418 if (!ferror(OutChannel) || DisConnected) 419 return; 420 421 /* 422 ** Error on output -- if reporting lost channel, just ignore it. 423 ** Also, ignore errors from QUIT response (221 message) -- some 424 ** rude servers don't read result. 425 */ 426 427 if (InChannel == NULL || feof(InChannel) || ferror(InChannel) || 428 strncmp(msg, "221", 3) == 0) 429 return; 430 431 /* can't call syserr, 'cause we are using MsgBuf */ 432 HoldErrs = TRUE; 433 if (LogLevel > 0) 434 sm_syslog(LOG_CRIT, CurEnv->e_id, 435 "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 436 CurHostName == NULL ? "NO-HOST" : CurHostName, 437 shortenstring(msg, MAXSHORTSTR), errstring(errno)); 438} 439/* 440** PUTERRMSG -- like putoutmsg, but does special processing for error messages 441** 442** Parameters: 443** msg -- the message to output. 444** 445** Returns: 446** none. 447** 448** Side Effects: 449** Sets the fatal error bit in the envelope as appropriate. 450*/ 451 452void 453puterrmsg(msg) 454 char *msg; 455{ 456 char msgcode = msg[0]; 457 458 /* output the message as usual */ 459 putoutmsg(msg, HoldErrs, FALSE); 460 461 /* be careful about multiple error messages */ 462 if (OnlyOneError) 463 HoldErrs = TRUE; 464 465 /* signal the error */ 466 Errors++; 467 468 if (CurEnv == NULL) 469 return; 470 471 if (msgcode == '6') 472 { 473 /* notify the postmaster */ 474 CurEnv->e_flags |= EF_PM_NOTIFY; 475 } 476 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 477 { 478 /* mark long-term fatal errors */ 479 CurEnv->e_flags |= EF_FATALERRS; 480 } 481} 482/* 483** FMTMSG -- format a message into buffer. 484** 485** Parameters: 486** eb -- error buffer to get result. 487** to -- the recipient tag for this message. 488** num -- arpanet error number. 489** en -- the error number to display. 490** fmt -- format of string. 491** a, b, c, d, e -- arguments. 492** 493** Returns: 494** none. 495** 496** Side Effects: 497** none. 498*/ 499 500static void 501fmtmsg(eb, to, num, eno, fmt, ap) 502 register char *eb; 503 const char *to; 504 const char *num; 505 int eno; 506 const char *fmt; 507 va_list ap; 508{ 509 char del; 510 int l; 511 int spaceleft = sizeof MsgBuf; 512 513 /* output the reply code */ 514 if (isascii(fmt[0]) && isdigit(fmt[0]) && 515 isascii(fmt[1]) && isdigit(fmt[1]) && 516 isascii(fmt[2]) && isdigit(fmt[2])) 517 { 518 num = fmt; 519 fmt += 4; 520 } 521 if (num[3] == '-') 522 del = '-'; 523 else 524 del = ' '; 525 (void) snprintf(eb, spaceleft, "%3.3s%c", num, del); 526 eb += 4; 527 spaceleft -= 4; 528 529 /* output the file name and line number */ 530 if (FileName != NULL) 531 { 532 (void) snprintf(eb, spaceleft, "%s: line %d: ", 533 shortenstring(FileName, 83), LineNumber); 534 eb += (l = strlen(eb)); 535 spaceleft -= l; 536 } 537 538 /* output the "to" person */ 539 if (to != NULL && to[0] != '\0' && 540 strncmp(num, "551", 3) != 0 && 541 strncmp(num, "251", 3) != 0) 542 { 543 (void) snprintf(eb, spaceleft, "%s... ", 544 shortenstring(to, MAXSHORTSTR)); 545 spaceleft -= strlen(eb); 546 while (*eb != '\0') 547 *eb++ &= 0177; 548 } 549 550 /* output the message */ 551 (void) vsnprintf(eb, spaceleft, fmt, ap); 552 spaceleft -= strlen(eb); 553 while (*eb != '\0') 554 *eb++ &= 0177; 555 556 /* output the error code, if any */ 557 if (eno != 0) 558 (void) snprintf(eb, spaceleft, ": %s", errstring(eno)); 559} 560/* 561** BUFFER_ERRORS -- arrange to buffer future error messages 562** 563** Parameters: 564** none 565** 566** Returns: 567** none. 568*/ 569 570void 571buffer_errors() 572{ 573 HeldMessageBuf[0] = '\0'; 574 HoldErrs = TRUE; 575} 576/* 577** FLUSH_ERRORS -- flush the held error message buffer 578** 579** Parameters: 580** print -- if set, print the message, otherwise just 581** delete it. 582** 583** Returns: 584** none. 585*/ 586 587void 588flush_errors(print) 589 bool print; 590{ 591 if (print && HeldMessageBuf[0] != '\0') 592 putoutmsg(HeldMessageBuf, FALSE, TRUE); 593 HeldMessageBuf[0] = '\0'; 594 HoldErrs = FALSE; 595} 596/* 597** ERRSTRING -- return string description of error code 598** 599** Parameters: 600** errnum -- the error number to translate 601** 602** Returns: 603** A string description of errnum. 604** 605** Side Effects: 606** none. 607*/ 608 609const char * 610errstring(errnum) 611 int errnum; 612{ 613 char *dnsmsg; 614 char *bp; 615 static char buf[MAXLINE]; 616# if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) 617 extern char *sys_errlist[]; 618 extern int sys_nerr; 619# endif 620# if SMTP 621 extern char *SmtpPhase; 622# endif /* SMTP */ 623 624 /* 625 ** Handle special network error codes. 626 ** 627 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 628 */ 629 630 dnsmsg = NULL; 631 switch (errnum) 632 { 633# if defined(DAEMON) && defined(ETIMEDOUT) 634 case ETIMEDOUT: 635 case ECONNRESET: 636 bp = buf; 637#if HASSTRERROR 638 snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum)); 639#else 640 if (errnum >= 0 && errnum < sys_nerr) 641 snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]); 642 else 643 snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum); 644#endif 645 bp += strlen(bp); 646 if (CurHostName != NULL) 647 { 648 if (errnum == ETIMEDOUT) 649 { 650 snprintf(bp, SPACELEFT(buf, bp), " with "); 651 bp += strlen(bp); 652 } 653 else 654 { 655 bp = buf; 656 snprintf(bp, SPACELEFT(buf, bp), 657 "Connection reset by "); 658 bp += strlen(bp); 659 } 660 snprintf(bp, SPACELEFT(buf, bp), "%s", 661 shortenstring(CurHostName, MAXSHORTSTR)); 662 bp += strlen(buf); 663 } 664 if (SmtpPhase != NULL) 665 { 666 snprintf(bp, SPACELEFT(buf, bp), " during %s", 667 SmtpPhase); 668 } 669 return (buf); 670 671 case EHOSTDOWN: 672 if (CurHostName == NULL) 673 break; 674 (void) snprintf(buf, sizeof buf, "Host %s is down", 675 shortenstring(CurHostName, MAXSHORTSTR)); 676 return (buf); 677 678 case ECONNREFUSED: 679 if (CurHostName == NULL) 680 break; 681 (void) snprintf(buf, sizeof buf, "Connection refused by %s", 682 shortenstring(CurHostName, MAXSHORTSTR)); 683 return (buf); 684# endif 685 686# if NAMED_BIND 687 case HOST_NOT_FOUND + E_DNSBASE: 688 dnsmsg = "host not found"; 689 break; 690 691 case TRY_AGAIN + E_DNSBASE: 692 dnsmsg = "host name lookup failure"; 693 break; 694 695 case NO_RECOVERY + E_DNSBASE: 696 dnsmsg = "non-recoverable error"; 697 break; 698 699 case NO_DATA + E_DNSBASE: 700 dnsmsg = "no data known"; 701 break; 702# endif 703 704 case EPERM: 705 /* SunOS gives "Not owner" -- this is the POSIX message */ 706 return "Operation not permitted"; 707 708 /* 709 ** Error messages used internally in sendmail. 710 */ 711 712 case E_SM_OPENTIMEOUT: 713 return "Timeout on file open"; 714 715 case E_SM_NOSLINK: 716 return "Symbolic links not allowed"; 717 718 case E_SM_NOHLINK: 719 return "Hard links not allowed"; 720 721 case E_SM_REGONLY: 722 return "Regular files only"; 723 724 case E_SM_ISEXEC: 725 return "Executable files not allowed"; 726 727 case E_SM_WWDIR: 728 return "World writable directory"; 729 730 case E_SM_GWDIR: 731 return "Group writable directory"; 732 733 case E_SM_FILECHANGE: 734 return "File changed after open"; 735 736 case E_SM_WWFILE: 737 return "World writable file"; 738 739 case E_SM_GWFILE: 740 return "Group writable file"; 741 } 742 743 if (dnsmsg != NULL) 744 { 745 bp = buf; 746 strcpy(bp, "Name server: "); 747 bp += strlen(bp); 748 if (CurHostName != NULL) 749 { 750 snprintf(bp, SPACELEFT(buf, bp), "%s: ", 751 shortenstring(CurHostName, MAXSHORTSTR)); 752 bp += strlen(bp); 753 } 754 snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg); 755 return buf; 756 } 757 758#if HASSTRERROR 759 return strerror(errnum); 760#else 761 if (errnum > 0 && errnum < sys_nerr) 762 return (sys_errlist[errnum]); 763 764 (void) snprintf(buf, sizeof buf, "Error %d", errnum); 765 return (buf); 766#endif 767} 768