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