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