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