err.c revision 225736
157429Smarkm/* 257429Smarkm * Copyright (c) 1998-2003, 2010 Sendmail, Inc. and its suppliers. 357429Smarkm * All rights reserved. 457429Smarkm * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 557429Smarkm * Copyright (c) 1988, 1993 660573Skris * The Regents of the University of California. All rights reserved. 765674Skris * 865674Skris * By using this file, you agree to the terms and conditions set 965674Skris * forth in the LICENSE file which can be found at the top level of 1065674Skris * the sendmail distribution. 1165674Skris * 1257429Smarkm */ 1357429Smarkm 1457429Smarkm#include <sendmail.h> 1598684Sdes 1657429SmarkmSM_RCSID("@(#)$Id: err.c,v 8.205 2010/02/03 23:22:41 ca Exp $") 1757429Smarkm 1857429Smarkm#if LDAPMAP 1976262Sgreen# include <lber.h> 2076262Sgreen# include <ldap.h> /* for LDAP error codes */ 2157429Smarkm#endif /* LDAPMAP */ 2292559Sdes 2376262Sgreenstatic void putoutmsg __P((char *, bool, bool)); 2457429Smarkmstatic void puterrmsg __P((char *)); 2557429Smarkmstatic char *fmtmsg __P((char *, const char *, const char *, const char *, 2657429Smarkm int, const char *, va_list)); 2757429Smarkm 2857429Smarkm/* 2992559Sdes** FATAL_ERROR -- handle a fatal exception 3092559Sdes** 3157429Smarkm** This function is installed as the default exception handler 3257429Smarkm** in the main sendmail process, and in all child processes 3357429Smarkm** that we create. Its job is to handle exceptions that are not 3457429Smarkm** handled at a lower level. 3557429Smarkm** 3676262Sgreen** The theory is that unhandled exceptions will be 'fatal' class 3757429Smarkm** exceptions (with an "F:" prefix), such as the out-of-memory 3857429Smarkm** exception "F:sm.heap". As such, they are handled by exiting 3957429Smarkm** the process in exactly the same way that xalloc() in Sendmail 8.10 4057429Smarkm** exits the process when it fails due to lack of memory: 4176262Sgreen** we call syserr with a message beginning with "!". 4257429Smarkm** 4357429Smarkm** Parameters: 4457429Smarkm** exc -- exception which is terminating this process 4598941Sdes** 4698941Sdes** Returns: 4798941Sdes** none 4876262Sgreen*/ 4998941Sdes 5098941Sdesvoid 5198941Sdesfatal_error(exc) 5298941Sdes SM_EXC_T *exc; 5398941Sdes{ 5498941Sdes static char buf[256]; 5598941Sdes SM_FILE_T f; 5698941Sdes 5798941Sdes /* 5898941Sdes ** This function may be called when the heap is exhausted. 5998941Sdes ** The following code writes the message for 'exc' into our 6098941Sdes ** static buffer without allocating memory or raising exceptions. 6198941Sdes */ 6298941Sdes 6398941Sdes sm_strio_init(&f, buf, sizeof(buf)); 6498941Sdes sm_exc_write(exc, &f); 6598941Sdes (void) sm_io_flush(&f, SM_TIME_DEFAULT); 6698941Sdes 6798941Sdes /* 6857429Smarkm ** Terminate the process after logging an error and cleaning up. 6992559Sdes ** Problems: 7057429Smarkm ** - syserr decides what class of error this is by looking at errno. 7157429Smarkm ** That's no good; we should look at the exc structure. 7298684Sdes ** - The cleanup code should be moved out of syserr 7398684Sdes ** and into individual exception handlers 7498684Sdes ** that are part of the module they clean up after. 7576262Sgreen */ 7657429Smarkm 7757429Smarkm errno = ENOMEM; 7892559Sdes syserr("!%s", buf); 7976262Sgreen} 8076262Sgreen 8176262Sgreen/* 8257429Smarkm** SYSERR -- Print error message. 8357429Smarkm** 8476262Sgreen** Prints an error message via sm_io_printf to the diagnostic output. 8576262Sgreen** 8676262Sgreen** If the first character of the syserr message is `!' it will 8776262Sgreen** log this as an ALERT message and exit immediately. This can 8876262Sgreen** leave queue files in an indeterminate state, so it should not 8976262Sgreen** be used lightly. 9076262Sgreen** 9176262Sgreen** If the first character of the syserr message is '!' or '@' 9276262Sgreen** then syserr knows that the process is about to be terminated, 9357429Smarkm** so the SMTP reply code defaults to 421. Otherwise, the 9492559Sdes** reply code defaults to 451 or 554, depending on errno. 9576262Sgreen** 9657429Smarkm** Parameters: 9776262Sgreen** fmt -- the format string. An optional '!' or '@', 9876262Sgreen** followed by an optional three-digit SMTP 9976262Sgreen** reply code, followed by message text. 10076262Sgreen** (others) -- parameters 10176262Sgreen** 10276262Sgreen** Returns: 10376262Sgreen** none 10457429Smarkm** Raises E:mta.quickabort if QuickAbort is set. 10576262Sgreen** 10676262Sgreen** Side Effects: 10776262Sgreen** increments Errors. 10876262Sgreen** sets ExitStat. 10976262Sgreen*/ 11076262Sgreen 11176262Sgreenchar MsgBuf[BUFSIZ*2]; /* text of most recent message */ 11257429Smarkmstatic char HeldMessageBuf[sizeof(MsgBuf)]; /* for held messages */ 11376262Sgreen 11476262Sgreen#if NAMED_BIND && !defined(NO_DATA) 11576262Sgreen# define NO_DATA NO_ADDRESS 11676262Sgreen#endif /* NAMED_BIND && !defined(NO_DATA) */ 11776262Sgreen 11876262Sgreenvoid 11976262Sgreen/*VARARGS1*/ 12076262Sgreen#ifdef __STDC__ 12176262Sgreensyserr(const char *fmt, ...) 12276262Sgreen#else /* __STDC__ */ 12376262Sgreensyserr(fmt, va_alist) 12476262Sgreen const char *fmt; 12576262Sgreen va_dcl 12676262Sgreen#endif /* __STDC__ */ 12776262Sgreen{ 12876262Sgreen register char *p; 12957429Smarkm int save_errno = errno; 13057429Smarkm bool panic; 13157429Smarkm bool exiting; 13257429Smarkm char *user; 13376262Sgreen char *enhsc; 13476262Sgreen char *errtxt; 13576262Sgreen struct passwd *pw; 13676262Sgreen char ubuf[80]; 13776262Sgreen SM_VA_LOCAL_DECL 13876262Sgreen 13976262Sgreen switch (*fmt) 14076262Sgreen { 14176262Sgreen case '!': 14276262Sgreen ++fmt; 14376262Sgreen panic = true; 14492559Sdes exiting = true; 14576262Sgreen break; 14676262Sgreen case '@': 14776262Sgreen ++fmt; 14876262Sgreen panic = false; 14976262Sgreen exiting = true; 15076262Sgreen break; 15176262Sgreen default: 15276262Sgreen panic = false; 15376262Sgreen exiting = false; 15476262Sgreen break; 15576262Sgreen } 15676262Sgreen 15776262Sgreen /* format and output the error message */ 15892559Sdes if (exiting) 15976262Sgreen { 16076262Sgreen /* 16176262Sgreen ** Since we are terminating the process, 16276262Sgreen ** we are aborting the entire SMTP session, 16376262Sgreen ** rather than just the current transaction. 16476262Sgreen */ 16576262Sgreen 16676262Sgreen p = "421"; 16776262Sgreen enhsc = "4.0.0"; 16876262Sgreen } 16976262Sgreen else if (save_errno == 0) 17076262Sgreen { 17176262Sgreen p = "554"; 17257429Smarkm enhsc = "5.0.0"; 17357429Smarkm } 17457429Smarkm else 17557429Smarkm { 17657429Smarkm p = "451"; 17757429Smarkm enhsc = "4.0.0"; 17892559Sdes } 17957429Smarkm SM_VA_START(ap, fmt); 18057429Smarkm errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap); 18192559Sdes SM_VA_END(ap); 18257429Smarkm puterrmsg(MsgBuf); 18376262Sgreen 18476262Sgreen /* save this message for mailq printing */ 18592559Sdes if (!panic && CurEnv != NULL) 18676262Sgreen { 18776262Sgreen char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 18876262Sgreen 18976262Sgreen if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 19057429Smarkm sm_free(CurEnv->e_message); 19157429Smarkm CurEnv->e_message = nmsg; 19257429Smarkm } 19376262Sgreen 19492559Sdes /* determine exit status if not already set */ 19557429Smarkm if (ExitStat == EX_OK) 19657429Smarkm { 19757429Smarkm if (save_errno == 0) 19892559Sdes ExitStat = EX_SOFTWARE; 19957429Smarkm else 20057429Smarkm ExitStat = EX_OSERR; 20157429Smarkm if (tTd(54, 1)) 20257429Smarkm sm_dprintf("syserr: ExitStat = %d\n", ExitStat); 20376262Sgreen } 20476262Sgreen 20557429Smarkm pw = sm_getpwuid(RealUid); 20692559Sdes if (pw != NULL) 20776262Sgreen user = pw->pw_name; 20857429Smarkm else 20976262Sgreen { 21076262Sgreen user = ubuf; 21157429Smarkm (void) sm_snprintf(ubuf, sizeof(ubuf), "UID%d", (int) RealUid); 21257429Smarkm } 21376262Sgreen 21476262Sgreen if (LogLevel > 0) 21576262Sgreen sm_syslog(panic ? LOG_ALERT : LOG_CRIT, 21657429Smarkm CurEnv == NULL ? NOQID : CurEnv->e_id, 21776262Sgreen "SYSERR(%s): %.900s", 21876262Sgreen user, errtxt); 21976262Sgreen switch (save_errno) 22076262Sgreen { 22176262Sgreen case EBADF: 22276262Sgreen case ENFILE: 22376262Sgreen case EMFILE: 22476262Sgreen case ENOTTY: 22576262Sgreen#ifdef EFBIG 22676262Sgreen case EFBIG: 22776262Sgreen#endif /* EFBIG */ 22876262Sgreen#ifdef ESPIPE 22976262Sgreen case ESPIPE: 23076262Sgreen#endif /* ESPIPE */ 23157429Smarkm#ifdef EPIPE 23276262Sgreen case EPIPE: 23376262Sgreen#endif /* EPIPE */ 23492559Sdes#ifdef ENOBUFS 23576262Sgreen case ENOBUFS: 23676262Sgreen#endif /* ENOBUFS */ 23757429Smarkm#ifdef ESTALE 23876262Sgreen case ESTALE: 23976262Sgreen#endif /* ESTALE */ 24057429Smarkm printopenfds(true); 24176262Sgreen mci_dump_all(smioout, true); 24276262Sgreen break; 24376262Sgreen } 24476262Sgreen if (panic) 24576262Sgreen { 24657429Smarkm#if XLA 24776262Sgreen xla_all_end(); 24876262Sgreen#endif /* XLA */ 24976262Sgreen sync_queue_time(); 25076262Sgreen if (tTd(0, 1)) 25157429Smarkm abort(); 25257429Smarkm exit(EX_OSERR); 25376262Sgreen } 25476262Sgreen errno = 0; 25576262Sgreen if (QuickAbort) 25676262Sgreen sm_exc_raisenew_x(&EtypeQuickAbort, 2); 25776262Sgreen} 25876262Sgreen/* 25962101Sgreen** USRERR -- Signal user error. 26076262Sgreen** 26176262Sgreen** This is much like syserr except it is for user errors. 26262101Sgreen** 26362101Sgreen** Parameters: 26462101Sgreen** fmt -- the format string. If it does not begin with 26592559Sdes** a three-digit SMTP reply code, 550 is assumed. 26662101Sgreen** (others) -- sm_io_printf strings 26762101Sgreen** 26862101Sgreen** Returns: 26976262Sgreen** none 27076262Sgreen** Raises E:mta.quickabort if QuickAbort is set. 27176262Sgreen** 27276262Sgreen** Side Effects: 27376262Sgreen** increments Errors. 27476262Sgreen*/ 27576262Sgreen 27676262Sgreen/*VARARGS1*/ 27776262Sgreenvoid 27876262Sgreen#ifdef __STDC__ 27976262Sgreenusrerr(const char *fmt, ...) 28062101Sgreen#else /* __STDC__ */ 28176262Sgreenusrerr(fmt, va_alist) 28276262Sgreen const char *fmt; 28362101Sgreen va_dcl 28476262Sgreen#endif /* __STDC__ */ 28592559Sdes{ 28676262Sgreen char *enhsc; 28776262Sgreen char *errtxt; 28876262Sgreen SM_VA_LOCAL_DECL 28992559Sdes 29076262Sgreen if (fmt[0] == '5' || fmt[0] == '6') 29176262Sgreen enhsc = "5.0.0"; 29276262Sgreen else if (fmt[0] == '4' || fmt[0] == '8') 29362101Sgreen enhsc = "4.0.0"; 29462101Sgreen else if (fmt[0] == '2') 29557429Smarkm enhsc = "2.0.0"; 29657429Smarkm else 29792559Sdes enhsc = NULL; 29857429Smarkm SM_VA_START(ap, fmt); 29957429Smarkm errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap); 30057429Smarkm SM_VA_END(ap); 30157429Smarkm 30257429Smarkm if (SuprErrs) 30357429Smarkm return; 30457429Smarkm 30557429Smarkm /* save this message for mailq printing */ 30657429Smarkm switch (MsgBuf[0]) 30757429Smarkm { 30857429Smarkm case '4': 30957429Smarkm case '8': 31057429Smarkm if (CurEnv->e_message != NULL) 31157429Smarkm break; 31257429Smarkm 31357429Smarkm /* FALLTHROUGH */ 31457429Smarkm 31557429Smarkm case '5': 31657429Smarkm case '6': 31757429Smarkm if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 31857429Smarkm sm_free(CurEnv->e_message); 31957429Smarkm if (MsgBuf[0] == '6') 32092559Sdes { 32157429Smarkm char buf[MAXLINE]; 32257429Smarkm 32357429Smarkm (void) sm_snprintf(buf, sizeof(buf), 32457429Smarkm "Postmaster warning: %.*s", 32557429Smarkm (int) sizeof(buf) - 22, errtxt); 32657429Smarkm CurEnv->e_message = 32792559Sdes sm_rpool_strdup_x(CurEnv->e_rpool, buf); 32857429Smarkm } 32957429Smarkm else 33057429Smarkm { 33157429Smarkm CurEnv->e_message = 33257429Smarkm sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 33357429Smarkm } 33457429Smarkm break; 33557429Smarkm } 33657429Smarkm 33757429Smarkm puterrmsg(MsgBuf); 33857429Smarkm if (LogLevel > 3 && LogUsrErrs) 33957429Smarkm sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); 34057429Smarkm if (QuickAbort) 34160573Skris sm_exc_raisenew_x(&EtypeQuickAbort, 1); 34257429Smarkm} 34357429Smarkm/* 34457429Smarkm** USRERRENH -- Signal user error. 34557429Smarkm** 34657429Smarkm** Same as usrerr but with enhanced status code. 34760573Skris** 34892559Sdes** Parameters: 34957429Smarkm** enhsc -- the enhanced status code. 35057429Smarkm** fmt -- the format string. If it does not begin with 35157429Smarkm** a three-digit SMTP reply code, 550 is assumed. 35257429Smarkm** (others) -- sm_io_printf strings 35357429Smarkm** 35492559Sdes** Returns: 35557429Smarkm** none 35657429Smarkm** Raises E:mta.quickabort if QuickAbort is set. 35757429Smarkm** 358** Side Effects: 359** increments Errors. 360*/ 361 362/*VARARGS2*/ 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/* 432** MESSAGE -- print message (not necessarily an error) 433** 434** Parameters: 435** msg -- the message (sm_io_printf fmt) -- it can begin with 436** an SMTP reply code. If not, 050 is assumed. 437** (others) -- sm_io_printf arguments 438** 439** Returns: 440** none 441** 442** Side Effects: 443** none. 444*/ 445 446/*VARARGS1*/ 447void 448#ifdef __STDC__ 449message(const char *msg, ...) 450#else /* __STDC__ */ 451message(msg, va_alist) 452 const char *msg; 453 va_dcl 454#endif /* __STDC__ */ 455{ 456 char *errtxt; 457 SM_VA_LOCAL_DECL 458 459 errno = 0; 460 SM_VA_START(ap, msg); 461 errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap); 462 SM_VA_END(ap); 463 putoutmsg(MsgBuf, false, false); 464 465 /* save this message for mailq printing */ 466 switch (MsgBuf[0]) 467 { 468 case '4': 469 case '8': 470 if (CurEnv->e_message != NULL) 471 break; 472 /* FALLTHROUGH */ 473 474 case '5': 475 if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 476 sm_free(CurEnv->e_message); 477 CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 478 break; 479 } 480} 481 482 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 = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 534 break; 535 } 536} 537/* 538** PUTOUTMSG -- output error message to transcript and channel 539** 540** Parameters: 541** msg -- message to output (in SMTP format). 542** holdmsg -- if true, don't output a copy of the message to 543** our output channel. 544** heldmsg -- if true, this is a previously held message; 545** don't log it to the transcript file. 546** 547** Returns: 548** none. 549** 550** Side Effects: 551** Outputs msg to the transcript. 552** If appropriate, outputs it to the channel. 553** Deletes SMTP reply code number as appropriate. 554*/ 555 556static void 557putoutmsg(msg, holdmsg, heldmsg) 558 char *msg; 559 bool holdmsg; 560 bool heldmsg; 561{ 562 char msgcode = msg[0]; 563 char *errtxt = msg; 564 char *id; 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 id = (CurEnv != NULL) ? CurEnv->e_id : NULL; 577 578 /* output to transcript if serious */ 579 if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && 580 strchr("45", msg[0]) != NULL) 581 (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n", 582 msg); 583 584 if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 585 sm_syslog(LOG_INFO, id, 586 "--- %s%s%s", msg, holdmsg ? " (hold)" : "", 587 heldmsg ? " (held)" : ""); 588 589 if (msgcode == '8') 590 msg[0] = '0'; 591 592 /* output to channel if appropriate */ 593 if (!Verbose && msg[0] == '0') 594 return; 595 if (holdmsg) 596 { 597 /* save for possible future display */ 598 msg[0] = msgcode; 599 if (HeldMessageBuf[0] == '5' && msgcode == '4') 600 return; 601 (void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf)); 602 return; 603 } 604 605 (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 606 607 if (OutChannel == NULL) 608 return; 609 610 /* find actual text of error (after SMTP status codes) */ 611 if (ISSMTPREPLY(errtxt)) 612 { 613 int l; 614 615 errtxt += 4; 616 l = isenhsc(errtxt, ' '); 617 if (l <= 0) 618 l = isenhsc(errtxt, '\0'); 619 if (l > 0) 620 errtxt += l + 1; 621 } 622 623 /* if DisConnected, OutChannel now points to the transcript */ 624 if (!DisConnected && 625 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 626 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n", 627 msg); 628 else 629 (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n", 630 errtxt); 631 if (TrafficLogFile != NULL) 632 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 633 "%05d >>> %s\n", (int) CurrentPid, 634 (OpMode == MD_SMTP || OpMode == MD_DAEMON) 635 ? msg : errtxt); 636#if !PIPELINING 637 /* XXX can't flush here for SMTP pipelining */ 638 if (msg[3] == ' ') 639 (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 640 if (!sm_io_error(OutChannel) || DisConnected) 641 return; 642 643 /* 644 ** Error on output -- if reporting lost channel, just ignore it. 645 ** Also, ignore errors from QUIT response (221 message) -- some 646 ** rude servers don't read result. 647 */ 648 649 if (InChannel == NULL || sm_io_eof(InChannel) || 650 sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0) 651 return; 652 653 /* can't call syserr, 'cause we are using MsgBuf */ 654 HoldErrs = true; 655 if (LogLevel > 0) 656 sm_syslog(LOG_CRIT, id, 657 "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 658 CURHOSTNAME, 659 shortenstring(msg, MAXSHORTSTR), sm_errstring(errno)); 660#endif /* !PIPELINING */ 661} 662/* 663** PUTERRMSG -- like putoutmsg, but does special processing for error messages 664** 665** Parameters: 666** msg -- the message to output. 667** 668** Returns: 669** none. 670** 671** Side Effects: 672** Sets the fatal error bit in the envelope as appropriate. 673*/ 674 675static void 676puterrmsg(msg) 677 char *msg; 678{ 679 char msgcode = msg[0]; 680 681 /* output the message as usual */ 682 putoutmsg(msg, HoldErrs, false); 683 684 /* be careful about multiple error messages */ 685 if (OnlyOneError) 686 HoldErrs = true; 687 688 /* signal the error */ 689 Errors++; 690 691 if (CurEnv == NULL) 692 return; 693 694 if (msgcode == '6') 695 { 696 /* notify the postmaster */ 697 CurEnv->e_flags |= EF_PM_NOTIFY; 698 } 699 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 700 { 701 /* mark long-term fatal errors */ 702 CurEnv->e_flags |= EF_FATALERRS; 703 } 704} 705/* 706** ISENHSC -- check whether a string contains an enhanced status code 707** 708** Parameters: 709** s -- string with possible enhanced status code. 710** delim -- delim for enhanced status code. 711** 712** Returns: 713** 0 -- no enhanced status code. 714** >4 -- length of enhanced status code. 715** 716** Side Effects: 717** none. 718*/ 719int 720isenhsc(s, delim) 721 const char *s; 722 int delim; 723{ 724 int l, h; 725 726 if (s == NULL) 727 return 0; 728 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 729 return 0; 730 h = 0; 731 l = 2; 732 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 733 ++h; 734 if (h == 0 || s[l + h] != '.') 735 return 0; 736 l += h + 1; 737 h = 0; 738 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 739 ++h; 740 if (h == 0 || s[l + h] != delim) 741 return 0; 742 return l + h; 743} 744/* 745** EXTENHSC -- check and extract an enhanced status code 746** 747** Parameters: 748** s -- string with possible enhanced status code. 749** delim -- delim for enhanced status code. 750** e -- pointer to storage for enhanced status code. 751** must be != NULL and have space for at least 752** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3}) 753** 754** Returns: 755** 0 -- no enhanced status code. 756** >4 -- length of enhanced status code. 757** 758** Side Effects: 759** fills e with enhanced status code. 760*/ 761 762int 763extenhsc(s, delim, e) 764 const char *s; 765 int delim; 766 char *e; 767{ 768 int l, h; 769 770 if (s == NULL) 771 return 0; 772 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 773 return 0; 774 h = 0; 775 l = 2; 776 e[0] = s[0]; 777 e[1] = '.'; 778 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 779 { 780 e[l + h] = s[l + h]; 781 ++h; 782 } 783 if (h == 0 || s[l + h] != '.') 784 return 0; 785 e[l + h] = '.'; 786 l += h + 1; 787 h = 0; 788 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 789 { 790 e[l + h] = s[l + h]; 791 ++h; 792 } 793 if (h == 0 || s[l + h] != delim) 794 return 0; 795 e[l + h] = '\0'; 796 return l + h; 797} 798/* 799** FMTMSG -- format a message into buffer. 800** 801** Parameters: 802** eb -- error buffer to get result -- MUST BE MsgBuf. 803** to -- the recipient tag for this message. 804** num -- default three digit SMTP reply code. 805** enhsc -- enhanced status code. 806** en -- the error number to display. 807** fmt -- format of string. 808** ap -- arguments for fmt. 809** 810** Returns: 811** pointer to error text beyond status codes. 812** 813** Side Effects: 814** none. 815*/ 816 817static char * 818fmtmsg(eb, to, num, enhsc, eno, fmt, ap) 819 register char *eb; 820 const char *to; 821 const char *num; 822 const char *enhsc; 823 int eno; 824 const char *fmt; 825 SM_VA_LOCAL_DECL 826{ 827 char del; 828 int l; 829 int spaceleft = sizeof(MsgBuf); 830 char *errtxt; 831 832 /* output the reply code */ 833 if (ISSMTPCODE(fmt)) 834 { 835 num = fmt; 836 fmt += 4; 837 } 838 if (num[3] == '-') 839 del = '-'; 840 else 841 del = ' '; 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 (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del); 849 eb += 4; 850 spaceleft -= 4; 851 852 if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4) 853 { 854 /* copy enh.status code including trailing blank */ 855 l++; 856 (void) sm_strlcpy(eb, fmt, l + 1); 857 eb += l; 858 spaceleft -= l; 859 fmt += l; 860 } 861 else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4) 862 { 863 /* copy enh.status code */ 864 (void) sm_strlcpy(eb, enhsc, l + 1); 865 eb[l] = ' '; 866 eb[++l] = '\0'; 867 eb += l; 868 spaceleft -= l; 869 } 870 if (SoftBounce && eb[-l] == '5') 871 { 872 /* replace 5 by 4 */ 873 eb[-l] = '4'; 874 } 875 errtxt = eb; 876 877 /* output the file name and line number */ 878 if (FileName != NULL) 879 { 880 (void) sm_snprintf(eb, spaceleft, "%s: line %d: ", 881 shortenstring(FileName, 83), LineNumber); 882 eb += (l = strlen(eb)); 883 spaceleft -= l; 884 } 885 886 /* 887 ** output the "to" address only if it is defined and one of the 888 ** following codes is used: 889 ** 050 internal notices, e.g., alias expansion 890 ** 250 Ok 891 ** 252 Cannot VRFY user, but will accept message and attempt delivery 892 ** 450 Requested mail action not taken: mailbox unavailable 893 ** 550 Requested action not taken: mailbox unavailable 894 ** 553 Requested action not taken: mailbox name not allowed 895 ** 896 ** Notice: this still isn't "the right thing", this code shouldn't 897 ** (indirectly) depend on CurEnv->e_to. 898 */ 899 900 if (to != NULL && to[0] != '\0' && 901 (strncmp(num, "050", 3) == 0 || 902 strncmp(num, "250", 3) == 0 || 903 strncmp(num, "252", 3) == 0 || 904 strncmp(num, "450", 3) == 0 || 905 strncmp(num, "550", 3) == 0 || 906 strncmp(num, "553", 3) == 0)) 907 { 908 (void) sm_strlcpyn(eb, spaceleft, 2, 909 shortenstring(to, MAXSHORTSTR), "... "); 910 spaceleft -= strlen(eb); 911 while (*eb != '\0') 912 *eb++ &= 0177; 913 } 914 915 /* output the message */ 916 (void) sm_vsnprintf(eb, spaceleft, fmt, ap); 917 spaceleft -= strlen(eb); 918 while (*eb != '\0') 919 *eb++ &= 0177; 920 921 /* output the error code, if any */ 922 if (eno != 0) 923 (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno)); 924 925 return errtxt; 926} 927/* 928** BUFFER_ERRORS -- arrange to buffer future error messages 929** 930** Parameters: 931** none 932** 933** Returns: 934** none. 935*/ 936 937void 938buffer_errors() 939{ 940 HeldMessageBuf[0] = '\0'; 941 HoldErrs = true; 942} 943/* 944** FLUSH_ERRORS -- flush the held error message buffer 945** 946** Parameters: 947** print -- if set, print the message, otherwise just 948** delete it. 949** 950** Returns: 951** none. 952*/ 953 954void 955flush_errors(print) 956 bool print; 957{ 958 if (print && HeldMessageBuf[0] != '\0') 959 putoutmsg(HeldMessageBuf, false, true); 960 HeldMessageBuf[0] = '\0'; 961 HoldErrs = false; 962} 963/* 964** SM_ERRSTRING -- return string description of error code 965** 966** Parameters: 967** errnum -- the error number to translate 968** 969** Returns: 970** A string description of errnum. 971** 972** Side Effects: 973** none. 974*/ 975 976const char * 977sm_errstring(errnum) 978 int errnum; 979{ 980 char *dnsmsg; 981 char *bp; 982 static char buf[MAXLINE]; 983#if HASSTRERROR 984 char *err; 985 char errbuf[30]; 986#endif /* HASSTRERROR */ 987#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) 988 extern char *sys_errlist[]; 989 extern int sys_nerr; 990#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */ 991 992 /* 993 ** Handle special network error codes. 994 ** 995 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 996 */ 997 998 dnsmsg = NULL; 999 switch (errnum) 1000 { 1001 case ETIMEDOUT: 1002 case ECONNRESET: 1003 bp = buf; 1004#if HASSTRERROR 1005 err = strerror(errnum); 1006 if (err == NULL) 1007 { 1008 (void) sm_snprintf(errbuf, sizeof(errbuf), 1009 "Error %d", errnum); 1010 err = errbuf; 1011 } 1012 (void) sm_strlcpy(bp, err, SPACELEFT(buf, bp)); 1013#else /* HASSTRERROR */ 1014 if (errnum >= 0 && errnum < sys_nerr) 1015 (void) sm_strlcpy(bp, sys_errlist[errnum], 1016 SPACELEFT(buf, bp)); 1017 else 1018 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1019 "Error %d", errnum); 1020#endif /* HASSTRERROR */ 1021 bp += strlen(bp); 1022 if (CurHostName != NULL) 1023 { 1024 if (errnum == ETIMEDOUT) 1025 { 1026 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1027 " with "); 1028 bp += strlen(bp); 1029 } 1030 else 1031 { 1032 bp = buf; 1033 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1034 "Connection reset by "); 1035 bp += strlen(bp); 1036 } 1037 (void) sm_strlcpy(bp, 1038 shortenstring(CurHostName, MAXSHORTSTR), 1039 SPACELEFT(buf, bp)); 1040 bp += strlen(buf); 1041 } 1042 if (SmtpPhase != NULL) 1043 { 1044 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1045 " during %s", SmtpPhase); 1046 } 1047 return buf; 1048 1049 case EHOSTDOWN: 1050 if (CurHostName == NULL) 1051 break; 1052 (void) sm_snprintf(buf, sizeof(buf), "Host %s is down", 1053 shortenstring(CurHostName, MAXSHORTSTR)); 1054 return buf; 1055 1056 case ECONNREFUSED: 1057 if (CurHostName == NULL) 1058 break; 1059 (void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ", 1060 shortenstring(CurHostName, MAXSHORTSTR)); 1061 return buf; 1062 1063#if NAMED_BIND 1064 case HOST_NOT_FOUND + E_DNSBASE: 1065 dnsmsg = "host not found"; 1066 break; 1067 1068 case TRY_AGAIN + E_DNSBASE: 1069 dnsmsg = "host name lookup failure"; 1070 break; 1071 1072 case NO_RECOVERY + E_DNSBASE: 1073 dnsmsg = "non-recoverable error"; 1074 break; 1075 1076 case NO_DATA + E_DNSBASE: 1077 dnsmsg = "no data known"; 1078 break; 1079#endif /* NAMED_BIND */ 1080 1081 case EPERM: 1082 /* SunOS gives "Not owner" -- this is the POSIX message */ 1083 return "Operation not permitted"; 1084 1085 /* 1086 ** Error messages used internally in sendmail. 1087 */ 1088 1089 case E_SM_OPENTIMEOUT: 1090 return "Timeout on file open"; 1091 1092 case E_SM_NOSLINK: 1093 return "Symbolic links not allowed"; 1094 1095 case E_SM_NOHLINK: 1096 return "Hard links not allowed"; 1097 1098 case E_SM_REGONLY: 1099 return "Regular files only"; 1100 1101 case E_SM_ISEXEC: 1102 return "Executable files not allowed"; 1103 1104 case E_SM_WWDIR: 1105 return "World writable directory"; 1106 1107 case E_SM_GWDIR: 1108 return "Group writable directory"; 1109 1110 case E_SM_FILECHANGE: 1111 return "File changed after open"; 1112 1113 case E_SM_WWFILE: 1114 return "World writable file"; 1115 1116 case E_SM_GWFILE: 1117 return "Group writable file"; 1118 1119 case E_SM_GRFILE: 1120 return "Group readable file"; 1121 1122 case E_SM_WRFILE: 1123 return "World readable file"; 1124 } 1125 1126 if (dnsmsg != NULL) 1127 { 1128 bp = buf; 1129 bp += sm_strlcpy(bp, "Name server: ", sizeof(buf)); 1130 if (CurHostName != NULL) 1131 { 1132 (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, 1133 shortenstring(CurHostName, MAXSHORTSTR), ": "); 1134 bp += strlen(bp); 1135 } 1136 (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp)); 1137 return buf; 1138 } 1139 1140#if LDAPMAP 1141 if (errnum >= E_LDAPBASE) 1142 return ldap_err2string(errnum - E_LDAPBASE); 1143#endif /* LDAPMAP */ 1144 1145#if HASSTRERROR 1146 err = strerror(errnum); 1147 if (err == NULL) 1148 { 1149 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum); 1150 return buf; 1151 } 1152 return err; 1153#else /* HASSTRERROR */ 1154 if (errnum > 0 && errnum < sys_nerr) 1155 return sys_errlist[errnum]; 1156 1157 (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum); 1158 return buf; 1159#endif /* HASSTRERROR */ 1160} 1161