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