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