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