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.216 2005/07/12 22:27:44 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 syserr("mci_read_persistent: NULL fp"); 928 if (mci == NULL) 929 syserr("mci_read_persistent: NULL mci"); 930 if (tTd(56, 93)) 931 { 932 sm_dprintf("mci_read_persistent: fp=%lx, mci=", 933 (unsigned long) fp); 934 } 935 936 SM_FREE_CLR(mci->mci_status); 937 SM_FREE_CLR(mci->mci_rstatus); 938 939 sm_io_rewind(fp, SM_TIME_DEFAULT); 940 ver = -1; 941 LineNumber = 0; 942 while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 943 { 944 LineNumber++; 945 p = strchr(buf, '\n'); 946 if (p != NULL) 947 *p = '\0'; 948 switch (buf[0]) 949 { 950 case 'V': /* version stamp */ 951 ver = atoi(&buf[1]); 952 if (ver < 0 || ver > 0) 953 syserr("Unknown host status version %d: %d max", 954 ver, 0); 955 break; 956 957 case 'E': /* UNIX error number */ 958 mci->mci_errno = atoi(&buf[1]); 959 break; 960 961 case 'H': /* DNS error number */ 962 mci->mci_herrno = atoi(&buf[1]); 963 break; 964 965 case 'S': /* UNIX exit status */ 966 mci->mci_exitstat = atoi(&buf[1]); 967 break; 968 969 case 'D': /* DSN status */ 970 mci->mci_status = newstr(&buf[1]); 971 break; 972 973 case 'R': /* SMTP status */ 974 mci->mci_rstatus = newstr(&buf[1]); 975 break; 976 977 case 'U': /* last usage time */ 978 mci->mci_lastuse = atol(&buf[1]); 979 break; 980 981 case '.': /* end of file */ 982 if (tTd(56, 93)) 983 mci_dump(sm_debug_file(), mci, false); 984 return 0; 985 986 default: 987 sm_syslog(LOG_CRIT, NOQID, 988 "%s: line %d: Unknown host status line \"%s\"", 989 FileName == NULL ? mci->mci_host : FileName, 990 LineNumber, buf); 991 LineNumber = saveLineNumber; 992 return -1; 993 } 994 } 995 LineNumber = saveLineNumber; 996 if (tTd(56, 93)) 997 sm_dprintf("incomplete (missing dot for EOF)\n"); 998 if (ver < 0) 999 return -1; 1000 return 0; 1001 } 1002 /* 1003 ** MCI_STORE_PERSISTENT -- Store persistent MCI information 1004 ** 1005 ** Store information about host that is kept 1006 ** in common for all running sendmails. 1007 ** 1008 ** Parameters: 1009 ** mci -- the host/connection to store persistent info for. 1010 ** 1011 ** Returns: 1012 ** none. 1013 */ 1014 1015 void 1016 mci_store_persistent(mci) 1017 MCI *mci; 1018 { 1019 int save_errno = errno; 1020 1021 if (mci == NULL) 1022 { 1023 if (tTd(56, 1)) 1024 sm_dprintf("mci_store_persistent: NULL mci\n"); 1025 return; 1026 } 1027 1028 if (HostStatDir == NULL || mci->mci_host == NULL) 1029 return; 1030 1031 if (tTd(56, 1)) 1032 sm_dprintf("mci_store_persistent: Storing information for %s\n", 1033 mci->mci_host); 1034 1035 if (mci->mci_statfile == NULL) 1036 { 1037 if (tTd(56, 1)) 1038 sm_dprintf("mci_store_persistent: no statfile\n"); 1039 return; 1040 } 1041 1042 sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT); 1043 #if !NOFTRUNCATE 1044 (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 1045 (off_t) 0); 1046 #endif /* !NOFTRUNCATE */ 1047 1048 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n"); 1049 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n", 1050 mci->mci_errno); 1051 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n", 1052 mci->mci_herrno); 1053 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n", 1054 mci->mci_exitstat); 1055 if (mci->mci_status != NULL) 1056 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 1057 "D%.80s\n", 1058 denlstring(mci->mci_status, true, false)); 1059 if (mci->mci_rstatus != NULL) 1060 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 1061 "R%.80s\n", 1062 denlstring(mci->mci_rstatus, true, false)); 1063 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n", 1064 (long)(mci->mci_lastuse)); 1065 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n"); 1066 1067 (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT); 1068 1069 errno = save_errno; 1070 return; 1071 } 1072 /* 1073 ** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree 1074 ** 1075 ** Recursively find all the mci host files in `pathname'. Default to 1076 ** main host status directory if no path is provided. 1077 ** Call (*action)(pathname, host) for each file found. 1078 ** 1079 ** Note: all information is collected in a list before it is processed. 1080 ** This may not be the best way to do it, but it seems safest, since 1081 ** the file system would be touched while we are attempting to traverse 1082 ** the directory tree otherwise (during purges). 1083 ** 1084 ** Parameters: 1085 ** action -- function to call on each node. If returns < 0, 1086 ** return immediately. 1087 ** pathname -- root of tree. If null, use main host status 1088 ** directory. 1089 ** 1090 ** Returns: 1091 ** < 0 -- if any action routine returns a negative value, that 1092 ** value is returned. 1093 ** 0 -- if we successfully went to completion. 1094 ** > 0 -- return status from action() 1095 */ 1096 1097 int 1098 mci_traverse_persistent(action, pathname) 1099 int (*action)__P((char *, char *)); 1100 char *pathname; 1101 { 1102 struct stat statbuf; 1103 DIR *d; 1104 int ret; 1105 1106 if (pathname == NULL) 1107 pathname = HostStatDir; 1108 if (pathname == NULL) 1109 return -1; 1110 1111 if (tTd(56, 1)) 1112 sm_dprintf("mci_traverse: pathname is %s\n", pathname); 1113 1114 ret = stat(pathname, &statbuf); 1115 if (ret < 0) 1116 { 1117 if (tTd(56, 2)) 1118 sm_dprintf("mci_traverse: Failed to stat %s: %s\n", 1119 pathname, sm_errstring(errno)); 1120 return ret; 1121 } 1122 if (S_ISDIR(statbuf.st_mode)) 1123 { 1124 bool leftone, removedone; 1125 size_t len; 1126 char *newptr; 1127 struct dirent *e; 1128 char newpath[MAXPATHLEN]; 1129 #if MAXPATHLEN <= MAXNAMLEN - 3 1130 ERROR "MAXPATHLEN <= MAXNAMLEN - 3" 1131 #endif /* MAXPATHLEN <= MAXNAMLEN - 3 */ 1132 1133 if ((d = opendir(pathname)) == NULL) 1134 { 1135 if (tTd(56, 2)) 1136 sm_dprintf("mci_traverse: opendir %s: %s\n", 1137 pathname, sm_errstring(errno)); 1138 return -1; 1139 } 1140 len = sizeof(newpath) - MAXNAMLEN - 3; 1141 if (sm_strlcpy(newpath, pathname, len) >= len) 1142 { 1143 if (tTd(56, 2)) 1144 sm_dprintf("mci_traverse: path \"%s\" too long", 1145 pathname); 1146 return -1; 1147 } 1148 newptr = newpath + strlen(newpath); 1149 *newptr++ = '/'; 1150 1151 /* 1152 ** repeat until no file has been removed 1153 ** this may become ugly when several files "expire" 1154 ** during these loops, but it's better than doing 1155 ** a rewinddir() inside the inner loop 1156 */ 1157 1158 do 1159 { 1160 leftone = removedone = false; 1161 while ((e = readdir(d)) != NULL) 1162 { 1163 if (e->d_name[0] == '.') 1164 continue; 1165 1166 (void) sm_strlcpy(newptr, e->d_name, 1167 sizeof newpath - 1168 (newptr - newpath)); 1169 1170 if (StopRequest) 1171 stop_sendmail(); 1172 ret = mci_traverse_persistent(action, newpath); 1173 if (ret < 0) 1174 break; 1175 if (ret == 1) 1176 leftone = true; 1177 if (!removedone && ret == 0 && 1178 action == mci_purge_persistent) 1179 removedone = true; 1180 } 1181 if (ret < 0) 1182 break; 1183 1184 /* 1185 ** The following appears to be 1186 ** necessary during purges, since 1187 ** we modify the directory structure 1188 */ 1189 1190 if (removedone) 1191 rewinddir(d); 1192 if (tTd(56, 40)) 1193 sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n", 1194 pathname, ret, removedone, leftone); 1195 } while (removedone); 1196 1197 /* purge (or whatever) the directory proper */ 1198 if (!leftone) 1199 { 1200 *--newptr = '\0'; 1201 ret = (*action)(newpath, NULL); 1202 } 1203 (void) closedir(d); 1204 } 1205 else if (S_ISREG(statbuf.st_mode)) 1206 { 1207 char *end = pathname + strlen(pathname) - 1; 1208 char *start; 1209 char *scan; 1210 char host[MAXHOSTNAMELEN]; 1211 char *hostptr = host; 1212 1213 /* 1214 ** Reconstruct the host name from the path to the 1215 ** persistent information. 1216 */ 1217 1218 do 1219 { 1220 if (hostptr != host) 1221 *(hostptr++) = '.'; 1222 start = end; 1223 while (start > pathname && *(start - 1) != '/') 1224 start--; 1225 1226 if (*end == '.') 1227 end--; 1228 1229 for (scan = start; scan <= end; scan++) 1230 *(hostptr++) = *scan; 1231 1232 end = start - 2; 1233 } while (end > pathname && *end == '.'); 1234 1235 *hostptr = '\0'; 1236 1237 /* 1238 ** Do something with the file containing the persistent 1239 ** information. 1240 */ 1241 1242 ret = (*action)(pathname, host); 1243 } 1244 1245 return ret; 1246 } 1247 /* 1248 ** MCI_PRINT_PERSISTENT -- print persistent info 1249 ** 1250 ** Dump the persistent information in the file 'pathname' 1251 ** 1252 ** Parameters: 1253 ** pathname -- the pathname to the status file. 1254 ** hostname -- the corresponding host name. 1255 ** 1256 ** Returns: 1257 ** 0 1258 */ 1259 1260 int 1261 mci_print_persistent(pathname, hostname) 1262 char *pathname; 1263 char *hostname; 1264 { 1265 static bool initflag = false; 1266 SM_FILE_T *fp; 1267 int width = Verbose ? 78 : 25; 1268 bool locked; 1269 MCI mcib; 1270 1271 /* skip directories */ 1272 if (hostname == NULL) 1273 return 0; 1274 1275 if (StopRequest) 1276 stop_sendmail(); 1277 1278 if (!initflag) 1279 { 1280 initflag = true; 1281 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 1282 " -------------- Hostname --------------- How long ago ---------Results---------\n"); 1283 } 1284 1285 fp = safefopen(pathname, O_RDONLY, FileMode, 1286 SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 1287 1288 if (fp == NULL) 1289 { 1290 if (tTd(56, 1)) 1291 sm_dprintf("mci_print_persistent: cannot open %s: %s\n", 1292 pathname, sm_errstring(errno)); 1293 return 0; 1294 } 1295 1296 FileName = pathname; 1297 memset(&mcib, '\0', sizeof mcib); 1298 if (mci_read_persistent(fp, &mcib) < 0) 1299 { 1300 syserr("%s: could not read status file", pathname); 1301 (void) sm_io_close(fp, SM_TIME_DEFAULT); 1302 FileName = NULL; 1303 return 0; 1304 } 1305 1306 locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname, 1307 "", LOCK_SH|LOCK_NB); 1308 (void) sm_io_close(fp, SM_TIME_DEFAULT); 1309 FileName = NULL; 1310 1311 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ", 1312 locked ? '*' : ' ', hostname, 1313 pintvl(curtime() - mcib.mci_lastuse, true)); 1314 if (mcib.mci_rstatus != NULL) 1315 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width, 1316 mcib.mci_rstatus); 1317 else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0) 1318 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 1319 "Deferred: %.*s\n", width - 10, 1320 sm_errstring(mcib.mci_errno)); 1321 else if (mcib.mci_exitstat != 0) 1322 { 1323 char *exmsg = sm_sysexmsg(mcib.mci_exitstat); 1324 1325 if (exmsg == NULL) 1326 { 1327 char buf[80]; 1328 1329 (void) sm_snprintf(buf, sizeof buf, 1330 "Unknown mailer error %d", 1331 mcib.mci_exitstat); 1332 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 1333 width, buf); 1334 } 1335 else 1336 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 1337 width, &exmsg[5]); 1338 } 1339 else if (mcib.mci_errno == 0) 1340 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n"); 1341 else 1342 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n", 1343 width - 4, sm_errstring(mcib.mci_errno)); 1344 1345 return 0; 1346 } 1347 /* 1348 ** MCI_PURGE_PERSISTENT -- Remove a persistence status file. 1349 ** 1350 ** Parameters: 1351 ** pathname -- path to the status file. 1352 ** hostname -- name of host corresponding to that file. 1353 ** NULL if this is a directory (domain). 1354 ** 1355 ** Returns: 1356 ** 0 -- ok 1357 ** 1 -- file not deleted (too young, incorrect format) 1358 ** < 0 -- some error occurred 1359 */ 1360 1361 int 1362 mci_purge_persistent(pathname, hostname) 1363 char *pathname; 1364 char *hostname; 1365 { 1366 struct stat statbuf; 1367 char *end = pathname + strlen(pathname) - 1; 1368 int ret; 1369 1370 if (tTd(56, 1)) 1371 sm_dprintf("mci_purge_persistent: purging %s\n", pathname); 1372 1373 ret = stat(pathname, &statbuf); 1374 if (ret < 0) 1375 { 1376 if (tTd(56, 2)) 1377 sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n", 1378 pathname, sm_errstring(errno)); 1379 return ret; 1380 } 1381 if (curtime() - statbuf.st_mtime <= MciInfoTimeout) 1382 return 1; 1383 if (hostname != NULL) 1384 { 1385 /* remove the file */ 1386 ret = unlink(pathname); 1387 if (ret < 0) 1388 { 1389 if (LogLevel > 8) 1390 sm_syslog(LOG_ERR, NOQID, 1391 "mci_purge_persistent: failed to unlink %s: %s", 1392 pathname, sm_errstring(errno)); 1393 if (tTd(56, 2)) 1394 sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n", 1395 pathname, sm_errstring(errno)); 1396 return ret; 1397 } 1398 } 1399 else 1400 { 1401 /* remove the directory */ 1402 if (*end != '.') 1403 return 1; 1404 1405 if (tTd(56, 1)) 1406 sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname); 1407 1408 ret = rmdir(pathname); 1409 if (ret < 0) 1410 { 1411 if (tTd(56, 2)) 1412 sm_dprintf("mci_purge_persistent: rmdir %s: %s\n", 1413 pathname, sm_errstring(errno)); 1414 return ret; 1415 } 1416 } 1417 1418 return 0; 1419 } 1420 /* 1421 ** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname 1422 ** 1423 ** Given `host', convert from a.b.c to $HostStatDir/c./b./a, 1424 ** putting the result into `path'. if `createflag' is set, intervening 1425 ** directories will be created as needed. 1426 ** 1427 ** Parameters: 1428 ** host -- host name to convert from. 1429 ** path -- place to store result. 1430 ** pathlen -- length of path buffer. 1431 ** createflag -- if set, create intervening directories as 1432 ** needed. 1433 ** 1434 ** Returns: 1435 ** 0 -- success 1436 ** -1 -- failure 1437 */ 1438 1439 static int 1440 mci_generate_persistent_path(host, path, pathlen, createflag) 1441 const char *host; 1442 char *path; 1443 int pathlen; 1444 bool createflag; 1445 { 1446 char *elem, *p, *x, ch; 1447 int ret = 0; 1448 int len; 1449 char t_host[MAXHOSTNAMELEN]; 1450 #if NETINET6 1451 struct in6_addr in6_addr; 1452 #endif /* NETINET6 */ 1453 1454 /* 1455 ** Rationality check the arguments. 1456 */ 1457 1458 if (host == NULL) 1459 { 1460 syserr("mci_generate_persistent_path: null host"); 1461 return -1; 1462 } 1463 if (path == NULL) 1464 { 1465 syserr("mci_generate_persistent_path: null path"); 1466 return -1; 1467 } 1468 1469 if (tTd(56, 80)) 1470 sm_dprintf("mci_generate_persistent_path(%s): ", host); 1471 1472 if (*host == '\0' || *host == '.') 1473 return -1; 1474 1475 /* make certain this is not a bracketed host number */ 1476 if (strlen(host) > sizeof t_host - 1) 1477 return -1; 1478 if (host[0] == '[') 1479 (void) sm_strlcpy(t_host, host + 1, sizeof t_host); 1480 else 1481 (void) sm_strlcpy(t_host, host, sizeof t_host); 1482 1483 /* 1484 ** Delete any trailing dots from the hostname. 1485 ** Leave 'elem' pointing at the \0. 1486 */ 1487 1488 elem = t_host + strlen(t_host); 1489 while (elem > t_host && 1490 (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']'))) 1491 *--elem = '\0'; 1492 1493 /* check for bogus bracketed address */ 1494 if (host[0] == '[') 1495 { 1496 bool good = false; 1497 # if NETINET6 1498 if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1) 1499 good = true; 1500 # endif /* NETINET6 */ 1501 # if NETINET 1502 if (inet_addr(t_host) != INADDR_NONE) 1503 good = true; 1504 # endif /* NETINET */ 1505 if (!good) 1506 return -1; 1507 } 1508 1509 /* check for what will be the final length of the path */ 1510 len = strlen(HostStatDir) + 2; 1511 for (p = (char *) t_host; *p != '\0'; p++) 1512 { 1513 if (*p == '.') 1514 len++; 1515 len++; 1516 if (p[0] == '.' && p[1] == '.') 1517 return -1; 1518 } 1519 if (len > pathlen || len < 1) 1520 return -1; 1521 (void) sm_strlcpy(path, HostStatDir, pathlen); 1522 p = path + strlen(path); 1523 while (elem > t_host) 1524 { 1525 if (!path_is_dir(path, createflag)) 1526 { 1527 ret = -1; 1528 break; 1529 } 1530 elem--; 1531 while (elem >= t_host && *elem != '.') 1532 elem--; 1533 *p++ = '/'; 1534 x = elem + 1; 1535 while ((ch = *x++) != '\0' && ch != '.') 1536 { 1537 if (isascii(ch) && isupper(ch)) 1538 ch = tolower(ch); 1539 if (ch == '/') 1540 ch = ':'; /* / -> : */ 1541 *p++ = ch; 1542 } 1543 if (elem >= t_host) 1544 *p++ = '.'; 1545 *p = '\0'; 1546 } 1547 if (tTd(56, 80)) 1548 { 1549 if (ret < 0) 1550 sm_dprintf("FAILURE %d\n", ret); 1551 else 1552 sm_dprintf("SUCCESS %s\n", path); 1553 } 1554 return ret; 1555 } 1556