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