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