mci.c
上传用户:xu_441
上传日期:2007-01-04
资源大小:1640k
文件大小:29k
源码类别:

Email客户端

开发平台:

Unix_Linux

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