1 /* 2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 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 16 SM_RCSID("@(#)$Id: mci.c,v 8.205.2.2 2002/11/26 19:15:19 gshapiro Exp $") 17 18 #if NETINET || NETINET6 19 # include <arpa/inet.h> 20 #endif /* NETINET || NETINET6 */ 21 22 #include <dirent.h> 23 24 static int mci_generate_persistent_path __P((const char *, char *, 25 int, bool)); 26 static bool mci_load_persistent __P((MCI *)); 27 static void mci_uncache __P((MCI **, bool)); 28 static int mci_lock_host_statfile __P((MCI *)); 29 static int mci_read_persistent __P((SM_FILE_T *, MCI *)); 30 31 /* 32 ** Mail Connection Information (MCI) Caching Module. 33 ** 34 ** There are actually two separate things cached. The first is 35 ** the set of all open connections -- these are stored in a 36 ** (small) list. The second is stored in the symbol table; it 37 ** has the overall status for all hosts, whether or not there 38 ** is a connection open currently. 39 ** 40 ** There should never be too many connections open (since this 41 ** could flood the socket table), nor should a connection be 42 ** allowed to sit idly for too long. 43 ** 44 ** MaxMciCache is the maximum number of open connections that 45 ** will be supported. 46 ** 47 ** MciCacheTimeout is the time (in seconds) that a connection 48 ** is permitted to survive without activity. 49 ** 50 ** We actually try any cached connections by sending a NOOP 51 ** before we use them; if the NOOP fails we close down the 52 ** connection and reopen it. Note that this means that a 53 ** server SMTP that doesn't support NOOP will hose the 54 ** algorithm -- but that doesn't seem too likely. 55 ** 56 ** The persistent MCI code is donated by Mark Lovell and Paul 57 ** Vixie. It is based on the long term host status code in KJS 58 ** written by Paul but has been adapted by Mark to fit into the 59 ** MCI structure. 60 */ 61 62 static MCI **MciCache; /* the open connection cache */ 63 64 /* 65 ** MCI_CACHE -- enter a connection structure into the open connection cache 66 ** 67 ** This may cause something else to be flushed. 68 ** 69 ** Parameters: 70 ** mci -- the connection to cache. 71 ** 72 ** Returns: 73 ** none. 74 */ 75 76 void 77 mci_cache(mci) 78 register MCI *mci; 79 { 80 register MCI **mcislot; 81 82 /* 83 ** Find the best slot. This may cause expired connections 84 ** to be closed. 85 */ 86 87 mcislot = mci_scan(mci); 88 if (mcislot == NULL) 89 { 90 /* we don't support caching */ 91 return; 92 } 93 94 if (mci->mci_host == NULL) 95 return; 96 97 /* if this is already cached, we are done */ 98 if (bitset(MCIF_CACHED, mci->mci_flags)) 99 return; 100 101 /* otherwise we may have to clear the slot */ 102 if (*mcislot != NULL) 103 mci_uncache(mcislot, true); 104 105 if (tTd(42, 5)) 106 sm_dprintf("mci_cache: caching %p (%s) in slot %d\n", 107 mci, mci->mci_host, (int) (mcislot - MciCache)); 108 if (tTd(91, 100)) 109 sm_syslog(LOG_DEBUG, CurEnv->e_id, 110 "mci_cache: caching %lx (%.100s) in slot %d", 111 (unsigned long) mci, mci->mci_host, 112 (int) (mcislot - MciCache)); 113 114 *mcislot = mci; 115 mci->mci_flags |= MCIF_CACHED; 116 } 117 /* 118 ** MCI_SCAN -- scan the cache, flush junk, and return best slot 119 ** 120 ** Parameters: 121 ** savemci -- never flush this one. Can be null. 122 ** 123 ** Returns: 124 ** The LRU (or empty) slot. 125 */ 126 127 MCI ** 128 mci_scan(savemci) 129 MCI *savemci; 130 { 131 time_t now; 132 register MCI **bestmci; 133 register MCI *mci; 134 register int i; 135 136 if (MaxMciCache <= 0) 137 { 138 /* we don't support caching */ 139 return NULL; 140 } 141 142 if (MciCache == NULL) 143 { 144 /* first call */ 145 MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof *MciCache); 146 memset((char *) MciCache, '\0', MaxMciCache * sizeof *MciCache); 147 return &MciCache[0]; 148 } 149 150 now = curtime(); 151 bestmci = &MciCache[0]; 152 for (i = 0; i < MaxMciCache; i++) 153 { 154 mci = MciCache[i]; 155 if (mci == NULL || mci->mci_state == MCIS_CLOSED) 156 { 157 bestmci = &MciCache[i]; 158 continue; 159 } 160 if ((mci->mci_lastuse + MciCacheTimeout <= now || 161 (mci->mci_mailer != NULL && 162 mci->mci_mailer->m_maxdeliveries > 0 && 163 mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&& 164 mci != savemci) 165 { 166 /* connection idle too long or too many deliveries */ 167 bestmci = &MciCache[i]; 168 169 /* close it */ 170 mci_uncache(bestmci, true); 171 continue; 172 } 173 if (*bestmci == NULL) 174 continue; 175 if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 176 bestmci = &MciCache[i]; 177 } 178 return bestmci; 179 } 180 /* 181 ** MCI_UNCACHE -- remove a connection from a slot. 182 ** 183 ** May close a connection. 184 ** 185 ** Parameters: 186 ** mcislot -- the slot to empty. 187 ** doquit -- if true, send QUIT protocol on this connection. 188 ** if false, we are assumed to be in a forked child; 189 ** all we want to do is close the file(s). 190 ** 191 ** Returns: 192 ** none. 193 */ 194 195 static void 196 mci_uncache(mcislot, doquit) 197 register MCI **mcislot; 198 bool doquit; 199 { 200 register MCI *mci; 201 extern ENVELOPE BlankEnvelope; 202 203 mci = *mcislot; 204 if (mci == NULL) 205 return; 206 *mcislot = NULL; 207 if (mci->mci_host == NULL) 208 return; 209 210 mci_unlock_host(mci); 211 212 if (tTd(42, 5)) 213 sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n", 214 mci, mci->mci_host, (int) (mcislot - MciCache), 215 doquit); 216 if (tTd(91, 100)) 217 sm_syslog(LOG_DEBUG, CurEnv->e_id, 218 "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)", 219 (unsigned long) mci, mci->mci_host, 220 (int) (mcislot - MciCache), doquit); 221 222 mci->mci_deliveries = 0; 223 if (doquit) 224 { 225 message("Closing connection to %s", mci->mci_host); 226 227 mci->mci_flags &= ~MCIF_CACHED; 228 229 /* only uses the envelope to flush the transcript file */ 230 if (mci->mci_state != MCIS_CLOSED) 231 smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 232 #if XLA 233 xla_host_end(mci->mci_host); 234 #endif /* XLA */ 235 } 236 else 237 { 238 if (mci->mci_in != NULL) 239 (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 240 if (mci->mci_out != NULL) 241 (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 242 mci->mci_in = mci->mci_out = NULL; 243 mci->mci_state = MCIS_CLOSED; 244 mci->mci_exitstat = EX_OK; 245 mci->mci_errno = 0; 246 mci->mci_flags = 0; 247 248 mci->mci_retryrcpt = false; 249 mci->mci_tolist = NULL; 250 #if PIPELINING 251 mci->mci_okrcpts = 0; 252 #endif /* PIPELINING */ 253 } 254 255 SM_FREE_CLR(mci->mci_status); 256 SM_FREE_CLR(mci->mci_rstatus); 257 SM_FREE_CLR(mci->mci_heloname); 258 if (mci->mci_rpool != NULL) 259 { 260 sm_rpool_free(mci->mci_rpool); 261 mci->mci_macro.mac_rpool = NULL; 262 mci->mci_rpool = NULL; 263 } 264 } 265 /* 266 ** MCI_FLUSH -- flush the entire cache 267 ** 268 ** Parameters: 269 ** doquit -- if true, send QUIT protocol. 270 ** if false, just close the connection. 271 ** allbut -- but leave this one open. 272 ** 273 ** Returns: 274 ** none. 275 */ 276 277 void 278 mci_flush(doquit, allbut) 279 bool doquit; 280 MCI *allbut; 281 { 282 register int i; 283 284 if (MciCache == NULL) 285 return; 286 287 for (i = 0; i < MaxMciCache; i++) 288 { 289 if (allbut != MciCache[i]) 290 mci_uncache(&MciCache[i], doquit); 291 } 292 } 293 /* 294 ** MCI_GET -- get information about a particular host 295 ** 296 ** Parameters: 297 ** host -- host to look for. 298 ** m -- mailer. 299 ** 300 ** Returns: 301 ** mci for this host (might be new). 302 */ 303 304 MCI * 305 mci_get(host, m) 306 char *host; 307 MAILER *m; 308 { 309 register MCI *mci; 310 register STAB *s; 311 extern SOCKADDR CurHostAddr; 312 313 /* clear CurHostAddr so we don't get a bogus address with this name */ 314 memset(&CurHostAddr, '\0', sizeof CurHostAddr); 315 316 /* clear out any expired connections */ 317 (void) mci_scan(NULL); 318 319 if (m->m_mno < 0) 320 syserr("!negative mno %d (%s)", m->m_mno, m->m_name); 321 322 s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 323 mci = &s->s_mci; 324 325 /* initialize per-message data */ 326 mci->mci_retryrcpt = false; 327 mci->mci_tolist = NULL; 328 #if PIPELINING 329 mci->mci_okrcpts = 0; 330 #endif /* PIPELINING */ 331 332 if (mci->mci_rpool == NULL) 333 mci->mci_rpool = sm_rpool_new_x(NULL); 334 335 if (mci->mci_macro.mac_rpool == NULL) 336 mci->mci_macro.mac_rpool = mci->mci_rpool; 337 338 /* 339 ** We don't need to load the persistent data if we have data 340 ** already loaded in the cache. 341 */ 342 343 if (mci->mci_host == NULL && 344 (mci->mci_host = s->s_name) != NULL && 345 !mci_load_persistent(mci)) 346 { 347 if (tTd(42, 2)) 348 sm_dprintf("mci_get(%s %s): lock failed\n", 349 host, m->m_name); 350 mci->mci_exitstat = EX_TEMPFAIL; 351 mci->mci_state = MCIS_CLOSED; 352 mci->mci_statfile = NULL; 353 return mci; 354 } 355 356 if (tTd(42, 2)) 357 { 358 sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n", 359 host, m->m_name, mci->mci_state, mci->mci_flags, 360 mci->mci_exitstat, mci->mci_errno); 361 } 362 363 if (mci->mci_state == MCIS_OPEN) 364 { 365 /* poke the connection to see if it's still alive */ 366 (void) smtpprobe(mci); 367 368 /* reset the stored state in the event of a timeout */ 369 if (mci->mci_state != MCIS_OPEN) 370 { 371 mci->mci_errno = 0; 372 mci->mci_exitstat = EX_OK; 373 mci->mci_state = MCIS_CLOSED; 374 } 375 else 376 { 377 /* get peer host address */ 378 /* (this should really be in the mci struct) */ 379 SOCKADDR_LEN_T socklen = sizeof CurHostAddr; 380 381 (void) getpeername(sm_io_getinfo(mci->mci_in, 382 SM_IO_WHAT_FD, NULL), 383 (struct sockaddr *) &CurHostAddr, &socklen); 384 } 385 } 386 if (mci->mci_state == MCIS_CLOSED) 387 { 388 time_t now = curtime(); 389 390 /* if this info is stale, ignore it */ 391 if (mci->mci_lastuse + MciInfoTimeout <= now) 392 { 393 mci->mci_lastuse = now; 394 mci->mci_errno = 0; 395 mci->mci_exitstat = EX_OK; 396 } 397 } 398 399 return mci; 400 } 401 /* 402 ** MCI_NEW -- allocate new MCI structure 403 ** 404 ** Parameters: 405 ** rpool -- if non-NULL: allocate from that rpool. 406 ** 407 ** Returns: 408 ** mci (new). 409 */ 410 411 MCI * 412 mci_new(rpool) 413 SM_RPOOL_T *rpool; 414 { 415 register MCI *mci; 416 417 if (rpool == NULL) 418 mci = (MCI *) sm_malloc_x(sizeof *mci); 419 else 420 mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof *mci); 421 memset((char *) mci, '\0', sizeof *mci); 422 mci->mci_rpool = sm_rpool_new_x(NULL); 423 mci->mci_macro.mac_rpool = mci->mci_rpool; 424 return mci; 425 } 426 /* 427 ** MCI_MATCH -- check connection cache for a particular host 428 ** 429 ** Parameters: 430 ** host -- host to look for. 431 ** m -- mailer. 432 ** 433 ** Returns: 434 ** true iff open connection exists. 435 */ 436 437 bool 438 mci_match(host, m) 439 char *host; 440 MAILER *m; 441 { 442 register MCI *mci; 443 register STAB *s; 444 445 if (m->m_mno < 0 || m->m_mno > MAXMAILERS) 446 return false; 447 s = stab(host, ST_MCI + m->m_mno, ST_FIND); 448 if (s == NULL) 449 return false; 450 451 mci = &s->s_mci; 452 return mci->mci_state == MCIS_OPEN; 453 } 454 /* 455 ** MCI_SETSTAT -- set status codes in MCI structure. 456 ** 457 ** Parameters: 458 ** mci -- the MCI structure to set. 459 ** xstat -- the exit status code. 460 ** dstat -- the DSN status code. 461 ** rstat -- the SMTP status code. 462 ** 463 ** Returns: 464 ** none. 465 */ 466 467 void 468 mci_setstat(mci, xstat, dstat, rstat) 469 MCI *mci; 470 int xstat; 471 char *dstat; 472 char *rstat; 473 { 474 /* protocol errors should never be interpreted as sticky */ 475 if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL) 476 mci->mci_exitstat = xstat; 477 478 SM_FREE_CLR(mci->mci_status); 479 if (dstat != NULL) 480 mci->mci_status = sm_strdup_x(dstat); 481 482 SM_FREE_CLR(mci->mci_rstatus); 483 if (rstat != NULL) 484 mci->mci_rstatus = sm_strdup_x(rstat); 485 } 486 /* 487 ** MCI_DUMP -- dump the contents of an MCI structure. 488 ** 489 ** Parameters: 490 ** mci -- the MCI structure to dump. 491 ** 492 ** Returns: 493 ** none. 494 ** 495 ** Side Effects: 496 ** none. 497 */ 498 499 struct mcifbits 500 { 501 int mcif_bit; /* flag bit */ 502 char *mcif_name; /* flag name */ 503 }; 504 static struct mcifbits MciFlags[] = 505 { 506 { MCIF_VALID, "VALID" }, 507 { MCIF_CACHED, "CACHED" }, 508 { MCIF_ESMTP, "ESMTP" }, 509 { MCIF_EXPN, "EXPN" }, 510 { MCIF_SIZE, "SIZE" }, 511 { MCIF_8BITMIME, "8BITMIME" }, 512 { MCIF_7BIT, "7BIT" }, 513 { MCIF_INHEADER, "INHEADER" }, 514 { MCIF_CVT8TO7, "CVT8TO7" }, 515 { MCIF_DSN, "DSN" }, 516 { MCIF_8BITOK, "8BITOK" }, 517 { MCIF_CVT7TO8, "CVT7TO8" }, 518 { MCIF_INMIME, "INMIME" }, 519 { MCIF_AUTH, "AUTH" }, 520 { MCIF_AUTHACT, "AUTHACT" }, 521 { MCIF_ENHSTAT, "ENHSTAT" }, 522 { MCIF_PIPELINED, "PIPELINED" }, 523 #if STARTTLS 524 { MCIF_TLS, "TLS" }, 525 { MCIF_TLSACT, "TLSACT" }, 526 #endif /* STARTTLS */ 527 { MCIF_DLVR_BY, "DLVR_BY" }, 528 { 0, NULL } 529 }; 530 531 void 532 mci_dump(mci, logit) 533 register MCI *mci; 534 bool logit; 535 { 536 register char *p; 537 char *sep; 538 char buf[4000]; 539 540 sep = logit ? " " : "\n\t"; 541 p = buf; 542 (void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci); 543 p += strlen(p); 544 if (mci == NULL) 545 { 546 (void) sm_snprintf(p, SPACELEFT(buf, p), "NULL"); 547 goto printit; 548 } 549 (void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags); 550 p += strlen(p); 551 if (mci->mci_flags != 0) 552 { 553 struct mcifbits *f; 554 555 *p++ = '<'; 556 for (f = MciFlags; f->mcif_bit != 0; f++) 557 { 558 if (!bitset(f->mcif_bit, mci->mci_flags)) 559 continue; 560 (void) sm_strlcpyn(p, SPACELEFT(buf, p), 2, 561 f->mcif_name, ","); 562 p += strlen(p); 563 } 564 p[-1] = '>'; 565 } 566 567 /* Note: sm_snprintf() takes care of NULL arguments for %s */ 568 (void) sm_snprintf(p, SPACELEFT(buf, p), 569 ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 570 sep, mci->mci_errno, mci->mci_herrno, 571 mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep); 572 p += strlen(p); 573 (void) sm_snprintf(p, SPACELEFT(buf, p), 574 "maxsize=%ld, phase=%s, mailer=%s,%s", 575 mci->mci_maxsize, mci->mci_phase, 576 mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 577 sep); 578 p += strlen(p); 579 (void) sm_snprintf(p, SPACELEFT(buf, p), 580 "status=%s, rstatus=%s,%s", 581 mci->mci_status, mci->mci_rstatus, sep); 582 p += strlen(p); 583 (void) sm_snprintf(p, SPACELEFT(buf, p), 584 "host=%s, lastuse=%s", 585 mci->mci_host, ctime(&mci->mci_lastuse)); 586 printit: 587 if (logit) 588 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); 589 else 590 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf); 591 } 592 /* 593 ** MCI_DUMP_ALL -- print the entire MCI cache 594 ** 595 ** Parameters: 596 ** logit -- if set, log the result instead of printing 597 ** to stdout. 598 ** 599 ** Returns: 600 ** none. 601 */ 602 603 void 604 mci_dump_all(logit) 605 bool logit; 606 { 607 register int i; 608 609 if (MciCache == NULL) 610 return; 611 612 for (i = 0; i < MaxMciCache; i++) 613 mci_dump(MciCache[i], logit); 614 } 615 /* 616 ** MCI_LOCK_HOST -- Lock host while sending. 617 ** 618 ** If we are contacting a host, we'll need to 619 ** update the status information in the host status 620 ** file, and if we want to do that, we ought to have 621 ** locked it. This has the (according to some) 622 ** desirable effect of serializing connectivity with 623 ** remote hosts -- i.e.: one connection to a given 624 ** host at a time. 625 ** 626 ** Parameters: 627 ** mci -- containing the host we want to lock. 628 ** 629 ** Returns: 630 ** EX_OK -- got the lock. 631 ** EX_TEMPFAIL -- didn't get the lock. 632 */ 633 634 int 635 mci_lock_host(mci) 636 MCI *mci; 637 { 638 if (mci == NULL) 639 { 640 if (tTd(56, 1)) 641 sm_dprintf("mci_lock_host: NULL mci\n"); 642 return EX_OK; 643 } 644 645 if (!SingleThreadDelivery) 646 return EX_OK; 647 648 return mci_lock_host_statfile(mci); 649 } 650 651 static int 652 mci_lock_host_statfile(mci) 653 MCI *mci; 654 { 655 int save_errno = errno; 656 int retVal = EX_OK; 657 char fname[MAXPATHLEN]; 658 659 if (HostStatDir == NULL || mci->mci_host == NULL) 660 return EX_OK; 661 662 if (tTd(56, 2)) 663 sm_dprintf("mci_lock_host: attempting to lock %s\n", 664 mci->mci_host); 665 666 if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 667 true) < 0) 668 { 669 /* of course this should never happen */ 670 if (tTd(56, 2)) 671 sm_dprintf("mci_lock_host: Failed to generate host path for %s\n", 672 mci->mci_host); 673 674 retVal = EX_TEMPFAIL; 675 goto cleanup; 676 } 677 678 mci->mci_statfile = safefopen(fname, O_RDWR, FileMode, 679 SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT); 680 681 if (mci->mci_statfile == NULL) 682 { 683 syserr("mci_lock_host: cannot create host lock file %s", fname); 684 goto cleanup; 685 } 686 687 if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 688 fname, "", LOCK_EX|LOCK_NB)) 689 { 690 if (tTd(56, 2)) 691 sm_dprintf("mci_lock_host: couldn't get lock on %s\n", 692 fname); 693 (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 694 mci->mci_statfile = NULL; 695 retVal = EX_TEMPFAIL; 696 goto cleanup; 697 } 698 699 if (tTd(56, 12) && mci->mci_statfile != NULL) 700 sm_dprintf("mci_lock_host: Sanity check -- lock is good\n"); 701 702 cleanup: 703 errno = save_errno; 704 return retVal; 705 } 706 /* 707 ** MCI_UNLOCK_HOST -- unlock host 708 ** 709 ** Clean up the lock on a host, close the file, let 710 ** someone else use it. 711 ** 712 ** Parameters: 713 ** mci -- us. 714 ** 715 ** Returns: 716 ** nothing. 717 */ 718 719 void 720 mci_unlock_host(mci) 721 MCI *mci; 722 { 723 int save_errno = errno; 724 725 if (mci == NULL) 726 { 727 if (tTd(56, 1)) 728 sm_dprintf("mci_unlock_host: NULL mci\n"); 729 return; 730 } 731 732 if (HostStatDir == NULL || mci->mci_host == NULL) 733 return; 734 735 if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL) 736 { 737 if (tTd(56, 1)) 738 sm_dprintf("mci_unlock_host: stat file already locked\n"); 739 } 740 else 741 { 742 if (tTd(56, 2)) 743 sm_dprintf("mci_unlock_host: store prior to unlock\n"); 744 mci_store_persistent(mci); 745 } 746 747 if (mci->mci_statfile != NULL) 748 { 749 (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 750 mci->mci_statfile = NULL; 751 } 752 753 errno = save_errno; 754 } 755 /* 756 ** MCI_LOAD_PERSISTENT -- load persistent host info 757 ** 758 ** Load information about host that is kept 759 ** in common for all running sendmails. 760 ** 761 ** Parameters: 762 ** mci -- the host/connection to load persistent info for. 763 ** 764 ** Returns: 765 ** true -- lock was successful 766 ** false -- lock failed 767 */ 768 769 static bool 770 mci_load_persistent(mci) 771 MCI *mci; 772 { 773 int save_errno = errno; 774 bool locked = true; 775 SM_FILE_T *fp; 776 char fname[MAXPATHLEN]; 777 778 if (mci == NULL) 779 { 780 if (tTd(56, 1)) 781 sm_dprintf("mci_load_persistent: NULL mci\n"); 782 return true; 783 } 784 785 if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL) 786 return true; 787 788 /* Already have the persistent information in memory */ 789 if (SingleThreadDelivery && mci->mci_statfile != NULL) 790 return true; 791 792 if (tTd(56, 1)) 793 sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n", 794 mci->mci_host); 795 796 if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 797 false) < 0) 798 { 799 /* Not much we can do if the file isn't there... */ 800 if (tTd(56, 1)) 801 sm_dprintf("mci_load_persistent: Couldn't generate host path\n"); 802 goto cleanup; 803 } 804 805 fp = safefopen(fname, O_RDONLY, FileMode, 806 SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 807 if (fp == NULL) 808 { 809 /* I can't think of any reason this should ever happen */ 810 if (tTd(56, 1)) 811 sm_dprintf("mci_load_persistent: open(%s): %s\n", 812 fname, sm_errstring(errno)); 813 goto cleanup; 814 } 815 816 FileName = fname; 817 locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "", 818 LOCK_SH|LOCK_NB); 819 if (locked) 820 { 821 (void) mci_read_persistent(fp, mci); 822 (void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, 823 "", LOCK_UN); 824 } 825 FileName = NULL; 826 (void) sm_io_close(fp, SM_TIME_DEFAULT); 827 828 cleanup: 829 errno = save_errno; 830 return locked; 831 } 832 /* 833 ** MCI_READ_PERSISTENT -- read persistent host status file 834 ** 835 ** Parameters: 836 ** fp -- the file pointer to read. 837 ** mci -- the pointer to fill in. 838 ** 839 ** Returns: 840 ** -1 -- if the file was corrupt. 841 ** 0 -- otherwise. 842 ** 843 ** Warning: 844 ** This code makes the assumption that this data 845 ** will be read in an atomic fashion, and that the data 846 ** was written in an atomic fashion. Any other functioning 847 ** may lead to some form of insanity. This should be 848 ** perfectly safe due to underlying stdio buffering. 849 */ 850 851 static int 852 mci_read_persistent(fp, mci) 853 SM_FILE_T *fp; 854 register MCI *mci; 855 { 856 int ver; 857 register char *p; 858 int saveLineNumber = LineNumber; 859 char buf[MAXLINE]; 860 861 if (fp == NULL) 862 syserr("mci_read_persistent: NULL fp"); 863 if (mci == NULL) 864 syserr("mci_read_persistent: NULL mci"); 865 if (tTd(56, 93)) 866 { 867 sm_dprintf("mci_read_persistent: fp=%lx, mci=", 868 (unsigned long) fp); 869 } 870 871 SM_FREE_CLR(mci->mci_status); 872 SM_FREE_CLR(mci->mci_rstatus); 873 874 sm_io_rewind(fp, SM_TIME_DEFAULT); 875 ver = -1; 876 LineNumber = 0; 877 while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 878 { 879 LineNumber++; 880 p = strchr(buf, '\n'); 881 if (p != NULL) 882 *p = '\0'; 883 switch (buf[0]) 884 { 885 case 'V': /* version stamp */ 886 ver = atoi(&buf[1]); 887 if (ver < 0 || ver > 0) 888 syserr("Unknown host status version %d: %d max", 889 ver, 0); 890 break; 891 892 case 'E': /* UNIX error number */ 893 mci->mci_errno = atoi(&buf[1]); 894 break; 895 896 case 'H': /* DNS error number */ 897 mci->mci_herrno = atoi(&buf[1]); 898 break; 899 900 case 'S': /* UNIX exit status */ 901 mci->mci_exitstat = atoi(&buf[1]); 902 break; 903 904 case 'D': /* DSN status */ 905 mci->mci_status = newstr(&buf[1]); 906 break; 907 908 case 'R': /* SMTP status */ 909 mci->mci_rstatus = newstr(&buf[1]); 910 break; 911 912 case 'U': /* last usage time */ 913 mci->mci_lastuse = atol(&buf[1]); 914 break; 915 916 case '.': /* end of file */ 917 if (tTd(56, 93)) 918 mci_dump(mci, false); 919 return 0; 920 921 default: 922 sm_syslog(LOG_CRIT, NOQID, 923 "%s: line %d: Unknown host status line \"%s\"", 924 FileName == NULL ? mci->mci_host : FileName, 925 LineNumber, buf); 926 LineNumber = saveLineNumber; 927 return -1; 928 } 929 } 930 LineNumber = saveLineNumber; 931 if (tTd(56, 93)) 932 sm_dprintf("incomplete (missing dot for EOF)\n"); 933 if (ver < 0) 934 return -1; 935 return 0; 936 } 937 /* 938 ** MCI_STORE_PERSISTENT -- Store persistent MCI information 939 ** 940 ** Store information about host that is kept 941 ** in common for all running sendmails. 942 ** 943 ** Parameters: 944 ** mci -- the host/connection to store persistent info for. 945 ** 946 ** Returns: 947 ** none. 948 */ 949 950 void 951 mci_store_persistent(mci) 952 MCI *mci; 953 { 954 int save_errno = errno; 955 956 if (mci == NULL) 957 { 958 if (tTd(56, 1)) 959 sm_dprintf("mci_store_persistent: NULL mci\n"); 960 return; 961 } 962 963 if (HostStatDir == NULL || mci->mci_host == NULL) 964 return; 965 966 if (tTd(56, 1)) 967 sm_dprintf("mci_store_persistent: Storing information for %s\n", 968 mci->mci_host); 969 970 if (mci->mci_statfile == NULL) 971 { 972 if (tTd(56, 1)) 973 sm_dprintf("mci_store_persistent: no statfile\n"); 974 return; 975 } 976 977 sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT); 978 #if !NOFTRUNCATE 979 (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 980 (off_t) 0); 981 #endif /* !NOFTRUNCATE */ 982 983 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n"); 984 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n", 985 mci->mci_errno); 986 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n", 987 mci->mci_herrno); 988 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n", 989 mci->mci_exitstat); 990 if (mci->mci_status != NULL) 991 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 992 "D%.80s\n", 993 denlstring(mci->mci_status, true, false)); 994 if (mci->mci_rstatus != NULL) 995 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 996 "R%.80s\n", 997 denlstring(mci->mci_rstatus, true, false)); 998 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n", 999 (long)(mci->mci_lastuse)); 1000 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n"); 1001 1002 (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT); 1003 1004 errno = save_errno; 1005 return; 1006 } 1007 /* 1008 ** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree 1009 ** 1010 ** Recursively find all the mci host files in `pathname'. Default to 1011 ** main host status directory if no path is provided. 1012 ** Call (*action)(pathname, host) for each file found. 1013 ** 1014 ** Note: all information is collected in a list before it is processed. 1015 ** This may not be the best way to do it, but it seems safest, since 1016 ** the file system would be touched while we are attempting to traverse 1017 ** the directory tree otherwise (during purges). 1018 ** 1019 ** Parameters: 1020 ** action -- function to call on each node. If returns < 0, 1021 ** return immediately. 1022 ** pathname -- root of tree. If null, use main host status 1023 ** directory. 1024 ** 1025 ** Returns: 1026 ** < 0 -- if any action routine returns a negative value, that 1027 ** value is returned. 1028 ** 0 -- if we successfully went to completion. 1029 ** > 0 -- return status from action() 1030 */ 1031 1032 int 1033 mci_traverse_persistent(action, pathname) 1034 int (*action)(); 1035 char *pathname; 1036 { 1037 struct stat statbuf; 1038 DIR *d; 1039 int ret; 1040 1041 if (pathname == NULL) 1042 pathname = HostStatDir; 1043 if (pathname == NULL) 1044 return -1; 1045 1046 if (tTd(56, 1)) 1047 sm_dprintf("mci_traverse: pathname is %s\n", pathname); 1048 1049 ret = stat(pathname, &statbuf); 1050 if (ret < 0) 1051 { 1052 if (tTd(56, 2)) 1053 sm_dprintf("mci_traverse: Failed to stat %s: %s\n", 1054 pathname, sm_errstring(errno)); 1055 return ret; 1056 } 1057 if (S_ISDIR(statbuf.st_mode)) 1058 { 1059 bool leftone, removedone; 1060 size_t len; 1061 char *newptr; 1062 struct dirent *e; 1063 char newpath[MAXPATHLEN]; 1064 1065 if ((d = opendir(pathname)) == NULL) 1066 { 1067 if (tTd(56, 2)) 1068 sm_dprintf("mci_traverse: opendir %s: %s\n", 1069 pathname, sm_errstring(errno)); 1070 return -1; 1071 } 1072 len = sizeof(newpath) - MAXNAMLEN - 3; 1073 if (sm_strlcpy(newpath, pathname, len) >= len) 1074 { 1075 if (tTd(56, 2)) 1076 sm_dprintf("mci_traverse: path \"%s\" too long", 1077 pathname); 1078 return -1; 1079 } 1080 newptr = newpath + strlen(newpath); 1081 *newptr++ = '/'; 1082 1083 /* 1084 ** repeat until no file has been removed 1085 ** this may become ugly when several files "expire" 1086 ** during these loops, but it's better than doing 1087 ** a rewinddir() inside the inner loop 1088 */ 1089 1090 do 1091 { 1092 leftone = removedone = false; 1093 while ((e = readdir(d)) != NULL) 1094 { 1095 if (e->d_name[0] == '.') 1096 continue; 1097 1098 (void) sm_strlcpy(newptr, e->d_name, 1099 sizeof newpath - 1100 (newptr - newpath)); 1101 1102 if (StopRequest) 1103 stop_sendmail(); 1104 ret = mci_traverse_persistent(action, newpath); 1105 if (ret < 0) 1106 break; 1107 if (ret == 1) 1108 leftone = true; 1109 if (!removedone && ret == 0 && 1110 action == mci_purge_persistent) 1111 removedone = true; 1112 } 1113 if (ret < 0) 1114 break; 1115 1116 /* 1117 ** The following appears to be 1118 ** necessary during purges, since 1119 ** we modify the directory structure 1120 */ 1121 1122 if (removedone) 1123 rewinddir(d); 1124 if (tTd(56, 40)) 1125 sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n", 1126 pathname, ret, removedone, leftone); 1127 } while (removedone); 1128 1129 /* purge (or whatever) the directory proper */ 1130 if (!leftone) 1131 { 1132 *--newptr = '\0'; 1133 ret = (*action)(newpath, NULL); 1134 } 1135 (void) closedir(d); 1136 } 1137 else if (S_ISREG(statbuf.st_mode)) 1138 { 1139 char *end = pathname + strlen(pathname) - 1; 1140 char *start; 1141 char *scan; 1142 char host[MAXHOSTNAMELEN]; 1143 char *hostptr = host; 1144 1145 /* 1146 ** Reconstruct the host name from the path to the 1147 ** persistent information. 1148 */ 1149 1150 do 1151 { 1152 if (hostptr != host) 1153 *(hostptr++) = '.'; 1154 start = end; 1155 while (*(start - 1) != '/') 1156 start--; 1157 1158 if (*end == '.') 1159 end--; 1160 1161 for (scan = start; scan <= end; scan++) 1162 *(hostptr++) = *scan; 1163 1164 end = start - 2; 1165 } while (*end == '.'); 1166 1167 *hostptr = '\0'; 1168 1169 /* 1170 ** Do something with the file containing the persistent 1171 ** information. 1172 */ 1173 1174 ret = (*action)(pathname, host); 1175 } 1176 1177 return ret; 1178 } 1179 /* 1180 ** MCI_PRINT_PERSISTENT -- print persistent info 1181 ** 1182 ** Dump the persistent information in the file 'pathname' 1183 ** 1184 ** Parameters: 1185 ** pathname -- the pathname to the status file. 1186 ** hostname -- the corresponding host name. 1187 ** 1188 ** Returns: 1189 ** 0 1190 */ 1191 1192 int 1193 mci_print_persistent(pathname, hostname) 1194 char *pathname; 1195 char *hostname; 1196 { 1197 static bool initflag = false; 1198 SM_FILE_T *fp; 1199 int width = Verbose ? 78 : 25; 1200 bool locked; 1201 MCI mcib; 1202 1203 /* skip directories */ 1204 if (hostname == NULL) 1205 return 0; 1206 1207 if (StopRequest) 1208 stop_sendmail(); 1209 1210 if (!initflag) 1211 { 1212 initflag = true; 1213 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 1214 " -------------- Hostname --------------- How long ago ---------Results---------\n"); 1215 } 1216 1217 fp = safefopen(pathname, O_RDONLY, FileMode, 1218 SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 1219 1220 if (fp == NULL) 1221 { 1222 if (tTd(56, 1)) 1223 sm_dprintf("mci_print_persistent: cannot open %s: %s\n", 1224 pathname, sm_errstring(errno)); 1225 return 0; 1226 } 1227 1228 FileName = pathname; 1229 memset(&mcib, '\0', sizeof mcib); 1230 if (mci_read_persistent(fp, &mcib) < 0) 1231 { 1232 syserr("%s: could not read status file", pathname); 1233 (void) sm_io_close(fp, SM_TIME_DEFAULT); 1234 FileName = NULL; 1235 return 0; 1236 } 1237 1238 locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname, 1239 "", LOCK_SH|LOCK_NB); 1240 (void) sm_io_close(fp, SM_TIME_DEFAULT); 1241 FileName = NULL; 1242 1243 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ", 1244 locked ? '*' : ' ', hostname, 1245 pintvl(curtime() - mcib.mci_lastuse, true)); 1246 if (mcib.mci_rstatus != NULL) 1247 { 1248 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width, 1249 mcib.mci_rstatus); 1250 } 1251 else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0) 1252 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 1253 "Deferred: %.*s\n", width - 10, 1254 sm_errstring(mcib.mci_errno)); 1255 else if (mcib.mci_exitstat != 0) 1256 { 1257 char *exmsg = sm_sysexmsg(mcib.mci_exitstat); 1258 1259 if (exmsg == NULL) 1260 { 1261 char buf[80]; 1262 1263 (void) sm_snprintf(buf, sizeof buf, 1264 "Unknown mailer error %d", 1265 mcib.mci_exitstat); 1266 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 1267 width, buf); 1268 } 1269 else 1270 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 1271 width, &exmsg[5]); 1272 } 1273 else if (mcib.mci_errno == 0) 1274 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n"); 1275 else 1276 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n", 1277 width - 4, sm_errstring(mcib.mci_errno)); 1278 1279 return 0; 1280 } 1281 /* 1282 ** MCI_PURGE_PERSISTENT -- Remove a persistence status file. 1283 ** 1284 ** Parameters: 1285 ** pathname -- path to the status file. 1286 ** hostname -- name of host corresponding to that file. 1287 ** NULL if this is a directory (domain). 1288 ** 1289 ** Returns: 1290 ** 0 -- ok 1291 ** 1 -- file not deleted (too young, incorrect format) 1292 ** < 0 -- some error occurred 1293 */ 1294 1295 int 1296 mci_purge_persistent(pathname, hostname) 1297 char *pathname; 1298 char *hostname; 1299 { 1300 struct stat statbuf; 1301 char *end = pathname + strlen(pathname) - 1; 1302 int ret; 1303 1304 if (tTd(56, 1)) 1305 sm_dprintf("mci_purge_persistent: purging %s\n", pathname); 1306 1307 ret = stat(pathname, &statbuf); 1308 if (ret < 0) 1309 { 1310 if (tTd(56, 2)) 1311 sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n", 1312 pathname, sm_errstring(errno)); 1313 return ret; 1314 } 1315 if (curtime() - statbuf.st_mtime <= MciInfoTimeout) 1316 return 1; 1317 if (hostname != NULL) 1318 { 1319 /* remove the file */ 1320 ret = unlink(pathname); 1321 if (ret < 0) 1322 { 1323 if (LogLevel > 8) 1324 sm_syslog(LOG_ERR, NOQID, 1325 "mci_purge_persistent: failed to unlink %s: %s", 1326 pathname, sm_errstring(errno)); 1327 if (tTd(56, 2)) 1328 sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n", 1329 pathname, sm_errstring(errno)); 1330 return ret; 1331 } 1332 } 1333 else 1334 { 1335 /* remove the directory */ 1336 if (*end != '.') 1337 return 1; 1338 1339 if (tTd(56, 1)) 1340 sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname); 1341 1342 ret = rmdir(pathname); 1343 if (ret < 0) 1344 { 1345 if (tTd(56, 2)) 1346 sm_dprintf("mci_purge_persistent: rmdir %s: %s\n", 1347 pathname, sm_errstring(errno)); 1348 return ret; 1349 } 1350 } 1351 1352 return 0; 1353 } 1354 /* 1355 ** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname 1356 ** 1357 ** Given `host', convert from a.b.c to $QueueDir/.hoststat/c./b./a, 1358 ** putting the result into `path'. if `createflag' is set, intervening 1359 ** directories will be created as needed. 1360 ** 1361 ** Parameters: 1362 ** host -- host name to convert from. 1363 ** path -- place to store result. 1364 ** pathlen -- length of path buffer. 1365 ** createflag -- if set, create intervening directories as 1366 ** needed. 1367 ** 1368 ** Returns: 1369 ** 0 -- success 1370 ** -1 -- failure 1371 */ 1372 1373 static int 1374 mci_generate_persistent_path(host, path, pathlen, createflag) 1375 const char *host; 1376 char *path; 1377 int pathlen; 1378 bool createflag; 1379 { 1380 char *elem, *p, *x, ch; 1381 int ret = 0; 1382 int len; 1383 char t_host[MAXHOSTNAMELEN]; 1384 #if NETINET6 1385 struct in6_addr in6_addr; 1386 #endif /* NETINET6 */ 1387 1388 /* 1389 ** Rationality check the arguments. 1390 */ 1391 1392 if (host == NULL) 1393 { 1394 syserr("mci_generate_persistent_path: null host"); 1395 return -1; 1396 } 1397 if (path == NULL) 1398 { 1399 syserr("mci_generate_persistent_path: null path"); 1400 return -1; 1401 } 1402 1403 if (tTd(56, 80)) 1404 sm_dprintf("mci_generate_persistent_path(%s): ", host); 1405 1406 if (*host == '\0' || *host == '.') 1407 return -1; 1408 1409 /* make certain this is not a bracketed host number */ 1410 if (strlen(host) > sizeof t_host - 1) 1411 return -1; 1412 if (host[0] == '[') 1413 (void) sm_strlcpy(t_host, host + 1, sizeof t_host); 1414 else 1415 (void) sm_strlcpy(t_host, host, sizeof t_host); 1416 1417 /* 1418 ** Delete any trailing dots from the hostname. 1419 ** Leave 'elem' pointing at the \0. 1420 */ 1421 1422 elem = t_host + strlen(t_host); 1423 while (elem > t_host && 1424 (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']'))) 1425 *--elem = '\0'; 1426 1427 /* check for bogus bracketed address */ 1428 if (host[0] == '[') 1429 { 1430 bool good = false; 1431 # if NETINET6 1432 if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1) 1433 good = true; 1434 # endif /* NETINET6 */ 1435 # if NETINET 1436 if (inet_addr(t_host) != INADDR_NONE) 1437 good = true; 1438 # endif /* NETINET */ 1439 if (!good) 1440 return -1; 1441 } 1442 1443 /* check for what will be the final length of the path */ 1444 len = strlen(HostStatDir) + 2; 1445 for (p = (char *) t_host; *p != '\0'; p++) 1446 { 1447 if (*p == '.') 1448 len++; 1449 len++; 1450 if (p[0] == '.' && p[1] == '.') 1451 return -1; 1452 } 1453 if (len > pathlen || len < 1) 1454 return -1; 1455 (void) sm_strlcpy(path, HostStatDir, pathlen); 1456 p = path + strlen(path); 1457 while (elem > t_host) 1458 { 1459 if (!path_is_dir(path, createflag)) 1460 { 1461 ret = -1; 1462 break; 1463 } 1464 elem--; 1465 while (elem >= t_host && *elem != '.') 1466 elem--; 1467 *p++ = '/'; 1468 x = elem + 1; 1469 while ((ch = *x++) != '\0' && ch != '.') 1470 { 1471 if (isascii(ch) && isupper(ch)) 1472 ch = tolower(ch); 1473 if (ch == '/') 1474 ch = ':'; /* / -> : */ 1475 *p++ = ch; 1476 } 1477 if (elem >= t_host) 1478 *p++ = '.'; 1479 *p = '\0'; 1480 } 1481 if (tTd(56, 80)) 1482 { 1483 if (ret < 0) 1484 sm_dprintf("FAILURE %d\n", ret); 1485 else 1486 sm_dprintf("SUCCESS %s\n", path); 1487 } 1488 return ret; 1489 } 1490