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