1 /* 2 * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 * $FreeBSD$ 13 */ 14 15 #include <sendmail.h> 16 17 SM_RCSID("@(#)$Id: mci.c,v 8.216 2005/07/12 22:27:44 ca Exp $") 18 19 #if NETINET || NETINET6 20 # include <arpa/inet.h> 21 #endif /* NETINET || NETINET6 */ 22 23 #include <dirent.h> 24 25 static int mci_generate_persistent_path __P((const char *, char *, 26 int, bool)); 27 static bool mci_load_persistent __P((MCI *)); 28 static void mci_uncache __P((MCI **, bool)); 29 static int mci_lock_host_statfile __P((MCI *)); 30 static int mci_read_persistent __P((SM_FILE_T *, MCI *)); 31 32 /* 33 ** Mail Connection Information (MCI) Caching Module. 34 ** 35 ** There are actually two separate things cached. The first is 36 ** the set of all open connections -- these are stored in a 37 ** (small) list. The second is stored in the symbol table; it 38 ** has the overall status for all hosts, whether or not there 39 ** is a connection open currently. 40 ** 41 ** There should never be too many connections open (since this 42 ** could flood the socket table), nor should a connection be 43 ** allowed to sit idly for too long. 44 ** 45 ** MaxMciCache is the maximum number of open connections that 46 ** will be supported. 47 ** 48 ** MciCacheTimeout is the time (in seconds) that a connection 49 ** is permitted to survive without activity. 50 ** 51 ** We actually try any cached connections by sending a RSET 52 ** before we use them; if the RSET fails we close down the 53 ** connection and reopen it (see smtpprobe()). 54 ** 55 ** The persistent MCI code is donated by Mark Lovell and Paul 56 ** Vixie. It is based on the long term host status code in KJS 57 ** written by Paul but has been adapted by Mark to fit into the 58 ** MCI structure. 59 */ 60 61 static MCI **MciCache; /* the open connection cache */ 62 63 /* 64 ** MCI_CACHE -- enter a connection structure into the open connection cache 65 ** 66 ** This may cause something else to be flushed. 67 ** 68 ** Parameters: 69 ** mci -- the connection to cache. 70 ** 71 ** Returns: 72 ** none. 73 */ 74 75 void 76 mci_cache(mci) 77 register MCI *mci; 78 { 79 register MCI **mcislot; 80 81 /* 82 ** Find the best slot. This may cause expired connections 83 ** to be closed. 84 */ 85 86 mcislot = mci_scan(mci); 87 if (mcislot == NULL) 88 { 89 /* we don't support caching */ 90 return; 91 } 92 93 if (mci->mci_host == NULL) 94 return; 95 96 /* if this is already cached, we are done */ 97 if (bitset(MCIF_CACHED, mci->mci_flags)) 98 return; 99 100 /* otherwise we may have to clear the slot */ 101 if (*mcislot != NULL) 102 mci_uncache(mcislot, true); 103 104 if (tTd(42, 5)) 105 sm_dprintf("mci_cache: caching %p (%s) in slot %d\n", 106 mci, mci->mci_host, (int) (mcislot - MciCache)); 107 if (tTd(91, 100)) 108 sm_syslog(LOG_DEBUG, CurEnv->e_id, 109 "mci_cache: caching %lx (%.100s) in slot %d", 110 (unsigned long) mci, mci->mci_host, 111 (int) (mcislot - MciCache)); 112 113 *mcislot = mci; 114 mci->mci_flags |= MCIF_CACHED; 115 } 116 /* 117 ** MCI_SCAN -- scan the cache, flush junk, and return best slot 118 ** 119 ** Parameters: 120 ** savemci -- never flush this one. Can be null. 121 ** 122 ** Returns: 123 ** The LRU (or empty) slot. 124 */ 125 126 MCI ** 127 mci_scan(savemci) 128 MCI *savemci; 129 { 130 time_t now; 131 register MCI **bestmci; 132 register MCI *mci; 133 register int i; 134 135 if (MaxMciCache <= 0) 136 { 137 /* we don't support caching */ 138 return NULL; 139 } 140 141 if (MciCache == NULL) 142 { 143 /* first call */ 144 MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof *MciCache); 145 memset((char *) MciCache, '\0', MaxMciCache * sizeof *MciCache); 146 return &MciCache[0]; 147 } 148 149 now = curtime(); 150 bestmci = &MciCache[0]; 151 for (i = 0; i < MaxMciCache; i++) 152 { 153 mci = MciCache[i]; 154 if (mci == NULL || mci->mci_state == MCIS_CLOSED) 155 { 156 bestmci = &MciCache[i]; 157 continue; 158 } 159 if ((mci->mci_lastuse + MciCacheTimeout <= now || 160 (mci->mci_mailer != NULL && 161 mci->mci_mailer->m_maxdeliveries > 0 && 162 mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&& 163 mci != savemci) 164 { 165 /* connection idle too long or too many deliveries */ 166 bestmci = &MciCache[i]; 167 168 /* close it */ 169 mci_uncache(bestmci, true); 170 continue; 171 } 172 if (*bestmci == NULL) 173 continue; 174 if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 175 bestmci = &MciCache[i]; 176 } 177 return bestmci; 178 } 179 /* 180 ** MCI_UNCACHE -- remove a connection from a slot. 181 ** 182 ** May close a connection. 183 ** 184 ** Parameters: 185 ** mcislot -- the slot to empty. 186 ** doquit -- if true, send QUIT protocol on this connection. 187 ** if false, we are assumed to be in a forked child; 188 ** all we want to do is close the file(s). 189 ** 190 ** Returns: 191 ** none. 192 */ 193 194 static void 195 mci_uncache(mcislot, doquit) 196 register MCI **mcislot; 197 bool doquit; 198 { 199 register MCI *mci; 200 extern ENVELOPE BlankEnvelope; 201 202 mci = *mcislot; 203 if (mci == NULL) 204 return; 205 *mcislot = NULL; 206 if (mci->mci_host == NULL) 207 return; 208 209 mci_unlock_host(mci); 210 211 if (tTd(42, 5)) 212 sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n", 213 mci, mci->mci_host, (int) (mcislot - MciCache), 214 doquit); 215 if (tTd(91, 100)) 216 sm_syslog(LOG_DEBUG, CurEnv->e_id, 217 "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)", 218 (unsigned long) mci, mci->mci_host, 219 (int) (mcislot - MciCache), doquit); 220 221 mci->mci_deliveries = 0; 222 if (doquit) 223 { 224 message("Closing connection to %s", mci->mci_host); 225 226 mci->mci_flags &= ~MCIF_CACHED; 227 228 /* only uses the envelope to flush the transcript file */ 229 if (mci->mci_state != MCIS_CLOSED) 230 smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 231 #if XLA 232 xla_host_end(mci->mci_host); 233 #endif /* XLA */ 234 } 235 else 236 { 237 if (mci->mci_in != NULL) 238 (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 239 if (mci->mci_out != NULL) 240 (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 241 mci->mci_in = mci->mci_out = NULL; 242 mci->mci_state = MCIS_CLOSED; 243 mci->mci_exitstat = EX_OK; 244 mci->mci_errno = 0; 245 mci->mci_flags = 0; 246 247 mci->mci_retryrcpt = false; 248 mci->mci_tolist = NULL; 249 #if PIPELINING 250 mci->mci_okrcpts = 0; 251 #endif /* PIPELINING */ 252 } 253 254 SM_FREE_CLR(mci->mci_status); 255 SM_FREE_CLR(mci->mci_rstatus); 256 SM_FREE_CLR(mci->mci_heloname); 257 if (mci->mci_rpool != NULL) 258 { 259 sm_rpool_free(mci->mci_rpool); 260 mci->mci_macro.mac_rpool = NULL; 261 mci->mci_rpool = NULL; 262 } 263 } 264 /* 265 ** MCI_FLUSH -- flush the entire cache 266 ** 267 ** Parameters: 268 ** doquit -- if true, send QUIT protocol. 269 ** if false, just close the connection. 270 ** allbut -- but leave this one open. 271 ** 272 ** Returns: 273 ** none. 274 */ 275 276 void 277 mci_flush(doquit, allbut) 278 bool doquit; 279 MCI *allbut; 280 { 281 register int i; 282 283 if (MciCache == NULL) 284 return; 285 286 for (i = 0; i < MaxMciCache; i++) 287 { 288 if (allbut != MciCache[i]) 289 mci_uncache(&MciCache[i], doquit); 290 } 291 } 292 /* 293 ** MCI_GET -- get information about a particular host 294 ** 295 ** Parameters: 296 ** host -- host to look for. 297 ** m -- mailer. 298 ** 299 ** Returns: 300 ** mci for this host (might be new). 301 */ 302 303 MCI * 304 mci_get(host, m) 305 char *host; 306 MAILER *m; 307 { 308 register MCI *mci; 309 register STAB *s; 310 extern SOCKADDR CurHostAddr; 311 312 /* clear CurHostAddr so we don't get a bogus address with this name */ 313 memset(&CurHostAddr, '\0', sizeof CurHostAddr); 314 315 /* clear out any expired connections */ 316 (void) mci_scan(NULL); 317 318 if (m->m_mno < 0) 319 syserr("!negative mno %d (%s)", m->m_mno, m->m_name); 320 321 s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 322 mci = &s->s_mci; 323 324 /* initialize per-message data */ 325 mci->mci_retryrcpt = false; 326 mci->mci_tolist = NULL; 327 #if PIPELINING 328 mci->mci_okrcpts = 0; 329 #endif /* PIPELINING */ 330 331 if (mci->mci_rpool == NULL) 332 mci->mci_rpool = sm_rpool_new_x(NULL); 333 334 if (mci->mci_macro.mac_rpool == NULL) 335 mci->mci_macro.mac_rpool = mci->mci_rpool; 336 337 /* 338 ** We don't need to load the persistent data if we have data 339 ** already loaded in the cache. 340 */ 341 342 if (mci->mci_host == NULL && 343 (mci->mci_host = s->s_name) != NULL && 344 !mci_load_persistent(mci)) 345 { 346 if (tTd(42, 2)) 347 sm_dprintf("mci_get(%s %s): lock failed\n", 348 host, m->m_name); 349 mci->mci_exitstat = EX_TEMPFAIL; 350 mci->mci_state = MCIS_CLOSED; 351 mci->mci_statfile = NULL; 352 return mci; 353 } 354 355 if (tTd(42, 2)) 356 { 357 sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n", 358 host, m->m_name, mci->mci_state, mci->mci_flags, 359 mci->mci_exitstat, mci->mci_errno); 360 } 361 362 if (mci->mci_state == MCIS_OPEN) 363 { 364 /* poke the connection to see if it's still alive */ 365 (void) smtpprobe(mci); 366 367 /* reset the stored state in the event of a timeout */ 368 if (mci->mci_state != MCIS_OPEN) 369 { 370 mci->mci_errno = 0; 371 mci->mci_exitstat = EX_OK; 372 mci->mci_state = MCIS_CLOSED; 373 } 374 else 375 { 376 /* get peer host address */ 377 /* (this should really be in the mci struct) */ 378 SOCKADDR_LEN_T socklen = sizeof CurHostAddr; 379 380 (void) getpeername(sm_io_getinfo(mci->mci_in, 381 SM_IO_WHAT_FD, NULL), 382 (struct sockaddr *) &CurHostAddr, &socklen); 383 } 384 } 385 if (mci->mci_state == MCIS_CLOSED) 386 { 387 time_t now = curtime(); 388 389 /* if this info is stale, ignore it */ 390 if (mci->mci_lastuse + MciInfoTimeout <= now) 391 { 392 mci->mci_lastuse = now; 393 mci->mci_errno = 0; 394 mci->mci_exitstat = EX_OK; 395 } 396 } 397 398 return mci; 399 } 400 401 /* 402 ** MCI_CLOSE -- (forcefully) close files used for a connection. 403 ** Note: this is a last resort, usually smtpquit() or endmailer() 404 ** should be used to close a connection. 405 ** 406 ** Parameters: 407 ** mci -- the connection to close. 408 ** where -- where has this been called? 409 ** 410 ** Returns: 411 ** none. 412 */ 413 414 void 415 mci_close(mci, where) 416 MCI *mci; 417 char *where; 418 { 419 bool dumped; 420 421 if (mci == NULL) 422 return; 423 dumped = false; 424 if (mci->mci_out != NULL) 425 { 426 if (tTd(56, 1)) 427 { 428 sm_dprintf("mci_close: mci_out!=NULL, where=%s\n", 429 where); 430 mci_dump(sm_debug_file(), mci, false); 431 dumped = true; 432 } 433 (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 434 mci->mci_out = NULL; 435 } 436 if (mci->mci_in != NULL) 437 { 438 if (tTd(56, 1)) 439 { 440 sm_dprintf("mci_close: mci_in!=NULL, where=%s\n", 441 where); 442 if (!dumped) 443 mci_dump(sm_debug_file(), mci, false); 444 } 445 (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 446 mci->mci_in = NULL; 447 } 448 mci->mci_state = MCIS_CLOSED; 449 } 450 451 /* 452 ** MCI_NEW -- allocate new MCI structure 453 ** 454 ** Parameters: 455 ** rpool -- if non-NULL: allocate from that rpool. 456 ** 457 ** Returns: 458 ** mci (new). 459 */ 460 461 MCI * 462 mci_new(rpool) 463 SM_RPOOL_T *rpool; 464 { 465 register MCI *mci; 466 467 if (rpool == NULL) 468 mci = (MCI *) sm_malloc_x(sizeof *mci); 469 else 470 mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof *mci); 471 memset((char *) mci, '\0', sizeof *mci); 472 mci->mci_rpool = sm_rpool_new_x(NULL); 473 mci->mci_macro.mac_rpool = mci->mci_rpool; 474 return mci; 475 } 476 /* 477 ** MCI_MATCH -- check connection cache for a particular host 478 ** 479 ** Parameters: 480 ** host -- host to look for. 481 ** m -- mailer. 482 ** 483 ** Returns: 484 ** true iff open connection exists. 485 */ 486 487 bool 488 mci_match(host, m) 489 char *host; 490 MAILER *m; 491 { 492 register MCI *mci; 493 register STAB *s; 494 495 if (m->m_mno < 0 || m->m_mno > MAXMAILERS) 496 return false; 497 s = stab(host, ST_MCI + m->m_mno, ST_FIND); 498 if (s == NULL) 499 return false; 500 501 mci = &s->s_mci; 502 return mci->mci_state == MCIS_OPEN; 503 } 504 /* 505 ** MCI_SETSTAT -- set status codes in MCI structure. 506 ** 507 ** Parameters: 508 ** mci -- the MCI structure to set. 509 ** xstat -- the exit status code. 510 ** dstat -- the DSN status code. 511 ** rstat -- the SMTP status code. 512 ** 513 ** Returns: 514 ** none. 515 */ 516 517 void 518 mci_setstat(mci, xstat, dstat, rstat) 519 MCI *mci; 520 int xstat; 521 char *dstat; 522 char *rstat; 523 { 524 /* protocol errors should never be interpreted as sticky */ 525 if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL) 526 mci->mci_exitstat = xstat; 527 528 SM_FREE_CLR(mci->mci_status); 529 if (dstat != NULL) 530 mci->mci_status = sm_strdup_x(dstat); 531 532 SM_FREE_CLR(mci->mci_rstatus); 533 if (rstat != NULL) 534 mci->mci_rstatus = sm_strdup_x(rstat); 535 } 536 /* 537 ** MCI_DUMP -- dump the contents of an MCI structure. 538 ** 539 ** Parameters: 540 ** fp -- output file pointer 541 ** mci -- the MCI structure to dump. 542 ** 543 ** Returns: 544 ** none. 545 ** 546 ** Side Effects: 547 ** none. 548 */ 549 550 struct mcifbits 551 { 552 int mcif_bit; /* flag bit */ 553 char *mcif_name; /* flag name */ 554 }; 555 static struct mcifbits MciFlags[] = 556 { 557 { MCIF_VALID, "VALID" }, 558 { MCIF_CACHED, "CACHED" }, 559 { MCIF_ESMTP, "ESMTP" }, 560 { MCIF_EXPN, "EXPN" }, 561 { MCIF_SIZE, "SIZE" }, 562 { MCIF_8BITMIME, "8BITMIME" }, 563 { MCIF_7BIT, "7BIT" }, 564 { MCIF_INHEADER, "INHEADER" }, 565 { MCIF_CVT8TO7, "CVT8TO7" }, 566 { MCIF_DSN, "DSN" }, 567 { MCIF_8BITOK, "8BITOK" }, 568 { MCIF_CVT7TO8, "CVT7TO8" }, 569 { MCIF_INMIME, "INMIME" }, 570 { MCIF_AUTH, "AUTH" }, 571 { MCIF_AUTHACT, "AUTHACT" }, 572 { MCIF_ENHSTAT, "ENHSTAT" }, 573 { MCIF_PIPELINED, "PIPELINED" }, 574 #if STARTTLS 575 { MCIF_TLS, "TLS" }, 576 { MCIF_TLSACT, "TLSACT" }, 577 #endif /* STARTTLS */ 578 { MCIF_DLVR_BY, "DLVR_BY" }, 579 { 0, NULL } 580 }; 581 582 void 583 mci_dump(fp, mci, logit) 584 SM_FILE_T *fp; 585 register MCI *mci; 586 bool logit; 587 { 588 register char *p; 589 char *sep; 590 char buf[4000]; 591 592 sep = logit ? " " : "\n\t"; 593 p = buf; 594 (void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci); 595 p += strlen(p); 596 if (mci == NULL) 597 { 598 (void) sm_snprintf(p, SPACELEFT(buf, p), "NULL"); 599 goto printit; 600 } 601 (void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags); 602 p += strlen(p); 603 604 /* 605 ** The following check is just for paranoia. It protects the 606 ** assignment in the if() clause. If there's not some minimum 607 ** amount of space we can stop right now. The check will not 608 ** trigger as long as sizeof(buf)=4000. 609 */ 610 611 if (p >= buf + sizeof(buf) - 4) 612 goto printit; 613 if (mci->mci_flags != 0) 614 { 615 struct mcifbits *f; 616 617 *p++ = '<'; /* protected above */ 618 for (f = MciFlags; f->mcif_bit != 0; f++) 619 { 620 if (!bitset(f->mcif_bit, mci->mci_flags)) 621 continue; 622 (void) sm_strlcpyn(p, SPACELEFT(buf, p), 2, 623 f->mcif_name, ","); 624 p += strlen(p); 625 } 626 p[-1] = '>'; 627 } 628 629 /* Note: sm_snprintf() takes care of NULL arguments for %s */ 630 (void) sm_snprintf(p, SPACELEFT(buf, p), 631 ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 632 sep, mci->mci_errno, mci->mci_herrno, 633 mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep); 634 p += strlen(p); 635 (void) sm_snprintf(p, SPACELEFT(buf, p), 636 "maxsize=%ld, phase=%s, mailer=%s,%s", 637 mci->mci_maxsize, mci->mci_phase, 638 mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 639 sep); 640 p += strlen(p); 641 (void) sm_snprintf(p, SPACELEFT(buf, p), 642 "status=%s, rstatus=%s,%s", 643 mci->mci_status, mci->mci_rstatus, sep); 644 p += strlen(p); 645 (void) sm_snprintf(p, SPACELEFT(buf, p), 646 "host=%s, lastuse=%s", 647 mci->mci_host, ctime(&mci->mci_lastuse)); 648 printit: 649 if (logit) 650 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); 651 else 652 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s\n", buf); 653 } 654 /* 655 ** MCI_DUMP_ALL -- print the entire MCI cache 656 ** 657 ** Parameters: 658 ** fp -- output file pointer 659 ** logit -- if set, log the result instead of printing 660 ** to stdout. 661 ** 662 ** Returns: 663 ** none. 664 */ 665 666 void 667 mci_dump_all(fp, logit) 668 SM_FILE_T *fp; 669 bool logit; 670 { 671 register int i; 672 673 if (MciCache == NULL) 674 return; 675 676 for (i = 0; i < MaxMciCache; i++) 677 mci_dump(fp, MciCache[i], logit); 678 } 679 /* 680 ** MCI_LOCK_HOST -- Lock host while sending. 681 ** 682 ** If we are contacting a host, we'll need to 683 ** update the status information in the host status 684 ** file, and if we want to do that, we ought to have 685 ** locked it. This has the (according to some) 686 ** desirable effect of serializing connectivity with 687 ** remote hosts -- i.e.: one connection to a given 688 ** host at a time. 689 ** 690 ** Parameters: 691 ** mci -- containing the host we want to lock. 692 ** 693 ** Returns: 694 ** EX_OK -- got the lock. 695 ** EX_TEMPFAIL -- didn't get the lock. 696 */ 697 698 int 699 mci_lock_host(mci) 700 MCI *mci; 701 { 702 if (mci == NULL) 703 { 704 if (tTd(56, 1)) 705 sm_dprintf("mci_lock_host: NULL mci\n"); 706 return EX_OK; 707 } 708 709 if (!SingleThreadDelivery) 710 return EX_OK; 711 712 return mci_lock_host_statfile(mci); 713 } 714 715 static int 716 mci_lock_host_statfile(mci) 717 MCI *mci; 718 { 719 int save_errno = errno; 720 int retVal = EX_OK; 721 char fname[MAXPATHLEN]; 722 723 if (HostStatDir == NULL || mci->mci_host == NULL) 724 return EX_OK; 725 726 if (tTd(56, 2)) 727 sm_dprintf("mci_lock_host: attempting to lock %s\n", 728 mci->mci_host); 729 730 if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 731 true) < 0) 732 { 733 /* of course this should never happen */ 734 if (tTd(56, 2)) 735 sm_dprintf("mci_lock_host: Failed to generate host path for %s\n", 736 mci->mci_host); 737 738 retVal = EX_TEMPFAIL; 739 goto cleanup; 740 } 741 742 mci->mci_statfile = safefopen(fname, O_RDWR, FileMode, 743 SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT); 744 745 if (mci->mci_statfile == NULL) 746 { 747 syserr("mci_lock_host: cannot create host lock file %s", fname); 748 goto cleanup; 749 } 750 751 if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 752 fname, "", LOCK_EX|LOCK_NB)) 753 { 754 if (tTd(56, 2)) 755 sm_dprintf("mci_lock_host: couldn't get lock on %s\n", 756 fname); 757 (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 758 mci->mci_statfile = NULL; 759 retVal = EX_TEMPFAIL; 760 goto cleanup; 761 } 762 763 if (tTd(56, 12) && mci->mci_statfile != NULL) 764 sm_dprintf("mci_lock_host: Sanity check -- lock is good\n"); 765 766 cleanup: 767 errno = save_errno; 768 return retVal; 769 } 770 /* 771 ** MCI_UNLOCK_HOST -- unlock host 772 ** 773 ** Clean up the lock on a host, close the file, let 774 ** someone else use it. 775 ** 776 ** Parameters: 777 ** mci -- us. 778 ** 779 ** Returns: 780 ** nothing. 781 */ 782 783 void 784 mci_unlock_host(mci) 785 MCI *mci; 786 { 787 int save_errno = errno; 788 789 if (mci == NULL) 790 { 791 if (tTd(56, 1)) 792 sm_dprintf("mci_unlock_host: NULL mci\n"); 793 return; 794 } 795 796 if (HostStatDir == NULL || mci->mci_host == NULL) 797 return; 798 799 if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL) 800 { 801 if (tTd(56, 1)) 802 sm_dprintf("mci_unlock_host: stat file already locked\n"); 803 } 804 else 805 { 806 if (tTd(56, 2)) 807 sm_dprintf("mci_unlock_host: store prior to unlock\n"); 808 mci_store_persistent(mci); 809 } 810 811 if (mci->mci_statfile != NULL) 812 { 813 (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 814 mci->mci_statfile = NULL; 815 } 816 817 errno = save_errno; 818 } 819 /* 820 ** MCI_LOAD_PERSISTENT -- load persistent host info 821 ** 822 ** Load information about host that is kept 823 ** in common for all running sendmails. 824 ** 825 ** Parameters: 826 ** mci -- the host/connection to load persistent info for. 827 ** 828 ** Returns: 829 ** true -- lock was successful 830 ** false -- lock failed 831 */ 832 833 static bool 834 mci_load_persistent(mci) 835 MCI *mci; 836 { 837 int save_errno = errno; 838 bool locked = true; 839 SM_FILE_T *fp; 840 char fname[MAXPATHLEN]; 841 842 if (mci == NULL) 843 { 844 if (tTd(56, 1)) 845 sm_dprintf("mci_load_persistent: NULL mci\n"); 846 return true; 847 } 848 849 if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL) 850 return true; 851 852 /* Already have the persistent information in memory */ 853 if (SingleThreadDelivery && mci->mci_statfile != NULL) 854 return true; 855 856 if (tTd(56, 1)) 857 sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n", 858 mci->mci_host); 859 860 if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 861 false) < 0) 862 { 863 /* Not much we can do if the file isn't there... */ 864 if (tTd(56, 1)) 865 sm_dprintf("mci_load_persistent: Couldn't generate host path\n"); 866 goto cleanup; 867 } 868 869 fp = safefopen(fname, O_RDONLY, FileMode, 870 SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 871 if (fp == NULL) 872 { 873 /* I can't think of any reason this should ever happen */ 874 if (tTd(56, 1)) 875 sm_dprintf("mci_load_persistent: open(%s): %s\n", 876 fname, sm_errstring(errno)); 877 goto cleanup; 878 } 879 880 FileName = fname; 881 locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "", 882 LOCK_SH|LOCK_NB); 883 if (locked) 884 { 885 (void) mci_read_persistent(fp, mci); 886 (void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, 887 "", LOCK_UN); 888 } 889 FileName = NULL; 890 (void) sm_io_close(fp, SM_TIME_DEFAULT); 891 892 cleanup: 893 errno = save_errno; 894 return locked; 895 } 896 /* 897 ** MCI_READ_PERSISTENT -- read persistent host status file 898 ** 899 ** Parameters: 900 ** fp -- the file pointer to read. 901 ** mci -- the pointer to fill in. 902 ** 903 ** Returns: 904 ** -1 -- if the file was corrupt. 905 ** 0 -- otherwise. 906 ** 907 ** Warning: 908 ** This code makes the assumption that this data 909 ** will be read in an atomic fashion, and that the data 910 ** was written in an atomic fashion. Any other functioning 911 ** may lead to some form of insanity. This should be 912 ** perfectly safe due to underlying stdio buffering. 913 */ 914 915 static int 916 mci_read_persistent(fp, mci) 917 SM_FILE_T *fp; 918 register MCI *mci; 919 { 920 int ver; 921 register char *p; 922 int saveLineNumber = LineNumber; 923 char buf[MAXLINE]; 924 925 if (fp == NULL) 926 syserr("mci_read_persistent: NULL fp"); 927 if (mci == NULL) 928 syserr("mci_read_persistent: NULL mci"); 929 if (tTd(56, 93)) 930 { 931 sm_dprintf("mci_read_persistent: fp=%lx, mci=", 932 (unsigned long) fp); 933 } 934 935 SM_FREE_CLR(mci->mci_status); 936 SM_FREE_CLR(mci->mci_rstatus); 937 938 sm_io_rewind(fp, SM_TIME_DEFAULT); 939 ver = -1; 940 LineNumber = 0; 941 while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 942 { 943 LineNumber++; 944 p = strchr(buf, '\n'); 945 if (p != NULL) 946 *p = '\0'; 947 switch (buf[0]) 948 { 949 case 'V': /* version stamp */ 950 ver = atoi(&buf[1]); 951 if (ver < 0 || ver > 0) 952 syserr("Unknown host status version %d: %d max", 953 ver, 0); 954 break; 955 956 case 'E': /* UNIX error number */ 957 mci->mci_errno = atoi(&buf[1]); 958 break; 959 960 case 'H': /* DNS error number */ 961 mci->mci_herrno = atoi(&buf[1]); 962 break; 963 964 case 'S': /* UNIX exit status */ 965 mci->mci_exitstat = atoi(&buf[1]); 966 break; 967 968 case 'D': /* DSN status */ 969 mci->mci_status = newstr(&buf[1]); 970 break; 971 972 case 'R': /* SMTP status */ 973 mci->mci_rstatus = newstr(&buf[1]); 974 break; 975 976 case 'U': /* last usage time */ 977 mci->mci_lastuse = atol(&buf[1]); 978 break; 979 980 case '.': /* end of file */ 981 if (tTd(56, 93)) 982 mci_dump(sm_debug_file(), mci, false); 983 return 0; 984 985 default: 986 sm_syslog(LOG_CRIT, NOQID, 987 "%s: line %d: Unknown host status line \"%s\"", 988 FileName == NULL ? mci->mci_host : FileName, 989 LineNumber, buf); 990 LineNumber = saveLineNumber; 991 return -1; 992 } 993 } 994 LineNumber = saveLineNumber; 995 if (tTd(56, 93)) 996 sm_dprintf("incomplete (missing dot for EOF)\n"); 997 if (ver < 0) 998 return -1; 999 return 0; 1000 } 1001 /* 1002 ** MCI_STORE_PERSISTENT -- Store persistent MCI information 1003 ** 1004 ** Store information about host that is kept 1005 ** in common for all running sendmails. 1006 ** 1007 ** Parameters: 1008 ** mci -- the host/connection to store persistent info for. 1009 ** 1010 ** Returns: 1011 ** none. 1012 */ 1013 1014 void 1015 mci_store_persistent(mci) 1016 MCI *mci; 1017 { 1018 int save_errno = errno; 1019 1020 if (mci == NULL) 1021 { 1022 if (tTd(56, 1)) 1023 sm_dprintf("mci_store_persistent: NULL mci\n"); 1024 return; 1025 } 1026 1027 if (HostStatDir == NULL || mci->mci_host == NULL) 1028 return; 1029 1030 if (tTd(56, 1)) 1031 sm_dprintf("mci_store_persistent: Storing information for %s\n", 1032 mci->mci_host); 1033 1034 if (mci->mci_statfile == NULL) 1035 { 1036 if (tTd(56, 1)) 1037 sm_dprintf("mci_store_persistent: no statfile\n"); 1038 return; 1039 } 1040 1041 sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT); 1042 #if !NOFTRUNCATE 1043 (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 1044 (off_t) 0); 1045 #endif /* !NOFTRUNCATE */ 1046 1047 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n"); 1048 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n", 1049 mci->mci_errno); 1050 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n", 1051 mci->mci_herrno); 1052 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n", 1053 mci->mci_exitstat); 1054 if (mci->mci_status != NULL) 1055 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 1056 "D%.80s\n", 1057 denlstring(mci->mci_status, true, false)); 1058 if (mci->mci_rstatus != NULL) 1059 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 1060 "R%.80s\n", 1061 denlstring(mci->mci_rstatus, true, false)); 1062 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n", 1063 (long)(mci->mci_lastuse)); 1064 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n"); 1065 1066 (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT); 1067 1068 errno = save_errno; 1069 return; 1070 } 1071 /* 1072 ** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree 1073 ** 1074 ** Recursively find all the mci host files in `pathname'. Default to 1075 ** main host status directory if no path is provided. 1076 ** Call (*action)(pathname, host) for each file found. 1077 ** 1078 ** Note: all information is collected in a list before it is processed. 1079 ** This may not be the best way to do it, but it seems safest, since 1080 ** the file system would be touched while we are attempting to traverse 1081 ** the directory tree otherwise (during purges). 1082 ** 1083 ** Parameters: 1084 ** action -- function to call on each node. If returns < 0, 1085 ** return immediately. 1086 ** pathname -- root of tree. If null, use main host status 1087 ** directory. 1088 ** 1089 ** Returns: 1090 ** < 0 -- if any action routine returns a negative value, that 1091 ** value is returned. 1092 ** 0 -- if we successfully went to completion. 1093 ** > 0 -- return status from action() 1094 */ 1095 1096 int 1097 mci_traverse_persistent(action, pathname) 1098 int (*action)__P((char *, char *)); 1099 char *pathname; 1100 { 1101 struct stat statbuf; 1102 DIR *d; 1103 int ret; 1104 1105 if (pathname == NULL) 1106 pathname = HostStatDir; 1107 if (pathname == NULL) 1108 return -1; 1109 1110 if (tTd(56, 1)) 1111 sm_dprintf("mci_traverse: pathname is %s\n", pathname); 1112 1113 ret = stat(pathname, &statbuf); 1114 if (ret < 0) 1115 { 1116 if (tTd(56, 2)) 1117 sm_dprintf("mci_traverse: Failed to stat %s: %s\n", 1118 pathname, sm_errstring(errno)); 1119 return ret; 1120 } 1121 if (S_ISDIR(statbuf.st_mode)) 1122 { 1123 bool leftone, removedone; 1124 size_t len; 1125 char *newptr; 1126 struct dirent *e; 1127 char newpath[MAXPATHLEN]; 1128 #if MAXPATHLEN <= MAXNAMLEN - 3 1129 ERROR "MAXPATHLEN <= MAXNAMLEN - 3" 1130 #endif /* MAXPATHLEN <= MAXNAMLEN - 3 */ 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