154963Seric /*
268839Seric * Copyright (c) 1983, 1995 Eric P. Allman
362526Sbostic * Copyright (c) 1988, 1993
462526Sbostic * The Regents of the University of California. All rights reserved.
554963Seric *
654963Seric * %sccs.include.redist.c%
754963Seric */
854963Seric
954963Seric #ifndef lint
10*69748Seric static char sccsid[] = "@(#)mci.c 8.17 (Berkeley) 05/28/95";
1154963Seric #endif /* not lint */
1254963Seric
1354963Seric #include "sendmail.h"
1454963Seric
1554963Seric /*
1654967Seric ** Mail Connection Information (MCI) Caching Module.
1754967Seric **
1854967Seric ** There are actually two separate things cached. The first is
1954967Seric ** the set of all open connections -- these are stored in a
2054967Seric ** (small) list. The second is stored in the symbol table; it
2154967Seric ** has the overall status for all hosts, whether or not there
2254967Seric ** is a connection open currently.
2354967Seric **
2454967Seric ** There should never be too many connections open (since this
2554967Seric ** could flood the socket table), nor should a connection be
2654967Seric ** allowed to sit idly for too long.
2754967Seric **
2854967Seric ** MaxMciCache is the maximum number of open connections that
2954967Seric ** will be supported.
3054967Seric **
3154967Seric ** MciCacheTimeout is the time (in seconds) that a connection
3254967Seric ** is permitted to survive without activity.
3354967Seric **
3454967Seric ** We actually try any cached connections by sending a NOOP
3554967Seric ** before we use them; if the NOOP fails we close down the
3654967Seric ** connection and reopen it. Note that this means that a
3754967Seric ** server SMTP that doesn't support NOOP will hose the
3854967Seric ** algorithm -- but that doesn't seem too likely.
3954967Seric */
4054967Seric
4154967Seric MCI **MciCache; /* the open connection cache */
42*69748Seric
43*69748Seric extern void mci_uncache __P((MCI **, bool));
4454967Seric /*
4554963Seric ** MCI_CACHE -- enter a connection structure into the open connection cache
4654963Seric **
4754963Seric ** This may cause something else to be flushed.
4854963Seric **
4954963Seric ** Parameters:
5054963Seric ** mci -- the connection to cache.
5154963Seric **
5254963Seric ** Returns:
5354963Seric ** none.
5454963Seric */
5554963Seric
56*69748Seric void
mci_cache(mci)5754963Seric mci_cache(mci)
5854967Seric register MCI *mci;
5954963Seric {
6054967Seric register MCI **mcislot;
6154967Seric extern MCI **mci_scan();
6254963Seric
6354963Seric /*
6454963Seric ** Find the best slot. This may cause expired connections
6554963Seric ** to be closed.
6654963Seric */
6754963Seric
6854963Seric mcislot = mci_scan(mci);
6967139Seric if (mcislot == NULL)
7067139Seric {
7167139Seric /* we don't support caching */
7267139Seric return;
7367139Seric }
7454963Seric
7554963Seric /* if this is already cached, we are done */
7654963Seric if (bitset(MCIF_CACHED, mci->mci_flags))
7754963Seric return;
7854963Seric
7954963Seric /* otherwise we may have to clear the slot */
8054963Seric if (*mcislot != NULL)
8158082Seric mci_uncache(mcislot, TRUE);
8254963Seric
8364739Seric if (tTd(42, 5))
8464739Seric printf("mci_cache: caching %x (%s) in slot %d\n",
8564739Seric mci, mci->mci_host, mcislot - MciCache);
8664739Seric #ifdef LOG
8764739Seric if (tTd(91, 100))
8864739Seric syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d",
8964742Seric CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
9064742Seric mci, mci->mci_host, mcislot - MciCache);
9164739Seric #endif
9264739Seric
9354963Seric *mcislot = mci;
9454963Seric mci->mci_flags |= MCIF_CACHED;
9554963Seric }
9654963Seric /*
9754963Seric ** MCI_SCAN -- scan the cache, flush junk, and return best slot
9854963Seric **
9954963Seric ** Parameters:
10054963Seric ** savemci -- never flush this one. Can be null.
10154963Seric **
10254963Seric ** Returns:
10354963Seric ** The LRU (or empty) slot.
10454963Seric */
10554963Seric
10654967Seric MCI **
mci_scan(savemci)10754963Seric mci_scan(savemci)
10854967Seric MCI *savemci;
10954963Seric {
11054963Seric time_t now;
11154967Seric register MCI **bestmci;
11254967Seric register MCI *mci;
11354963Seric register int i;
11454963Seric
11567139Seric if (MaxMciCache <= 0)
11667139Seric {
11767139Seric /* we don't support caching */
11867139Seric return NULL;
11967139Seric }
12067139Seric
12154963Seric if (MciCache == NULL)
12254963Seric {
12354963Seric /* first call */
12454967Seric MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
12556215Seric bzero((char *) MciCache, MaxMciCache * sizeof *MciCache);
12654963Seric return (&MciCache[0]);
12754963Seric }
12854963Seric
12954963Seric now = curtime();
13054963Seric bestmci = &MciCache[0];
13154963Seric for (i = 0; i < MaxMciCache; i++)
13254963Seric {
13354963Seric mci = MciCache[i];
13454963Seric if (mci == NULL || mci->mci_state == MCIS_CLOSED)
13554963Seric {
13654963Seric bestmci = &MciCache[i];
13754963Seric continue;
13854963Seric }
13954963Seric if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
14054963Seric {
14154963Seric /* connection idle too long -- close it */
14254963Seric bestmci = &MciCache[i];
14358082Seric mci_uncache(bestmci, TRUE);
14454963Seric continue;
14554963Seric }
14654963Seric if (*bestmci == NULL)
14754963Seric continue;
14854963Seric if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
14954963Seric bestmci = &MciCache[i];
15054963Seric }
15154963Seric return bestmci;
15254963Seric }
15354963Seric /*
15454963Seric ** MCI_UNCACHE -- remove a connection from a slot.
15554963Seric **
15654963Seric ** May close a connection.
15754963Seric **
15854963Seric ** Parameters:
15954963Seric ** mcislot -- the slot to empty.
16058082Seric ** doquit -- if TRUE, send QUIT protocol on this connection.
16158082Seric ** if FALSE, we are assumed to be in a forked child;
16258082Seric ** all we want to do is close the file(s).
16354963Seric **
16454963Seric ** Returns:
16554963Seric ** none.
16654963Seric */
16754963Seric
168*69748Seric void
mci_uncache(mcislot,doquit)16958082Seric mci_uncache(mcislot, doquit)
17054967Seric register MCI **mcislot;
17158082Seric bool doquit;
17254963Seric {
17354967Seric register MCI *mci;
17455020Seric extern ENVELOPE BlankEnvelope;
17554963Seric
17654963Seric mci = *mcislot;
17754963Seric if (mci == NULL)
17854963Seric return;
17954963Seric *mcislot = NULL;
18054963Seric
18164739Seric if (tTd(42, 5))
18264739Seric printf("mci_uncache: uncaching %x (%s) from slot %d (%d)\n",
18364739Seric mci, mci->mci_host, mcislot - MciCache, doquit);
18464739Seric #ifdef LOG
18564739Seric if (tTd(91, 100))
18664739Seric syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)",
18764742Seric CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
18864742Seric mci, mci->mci_host, mcislot - MciCache, doquit);
18964739Seric #endif
19064739Seric
19158082Seric if (doquit)
19258082Seric {
19358151Seric message("Closing connection to %s", mci->mci_host);
19455467Seric
19558082Seric mci->mci_flags &= ~MCIF_CACHED;
19658082Seric
19758082Seric /* only uses the envelope to flush the transcript file */
19858082Seric if (mci->mci_state != MCIS_CLOSED)
19958082Seric smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
20059156Seric #ifdef XLA
20159156Seric xla_host_end(mci->mci_host);
20259156Seric #endif
20358082Seric }
20458082Seric else
20558082Seric {
20658082Seric if (mci->mci_in != NULL)
20758680Seric xfclose(mci->mci_in, "mci_uncache", "mci_in");
20858082Seric if (mci->mci_out != NULL)
20958680Seric xfclose(mci->mci_out, "mci_uncache", "mci_out");
21058082Seric mci->mci_in = mci->mci_out = NULL;
21158082Seric mci->mci_state = MCIS_CLOSED;
21258082Seric mci->mci_exitstat = EX_OK;
21358082Seric mci->mci_errno = 0;
21458082Seric mci->mci_flags = 0;
21558082Seric }
21654963Seric }
21754963Seric /*
21854963Seric ** MCI_FLUSH -- flush the entire cache
21958082Seric **
22058082Seric ** Parameters:
22158082Seric ** doquit -- if TRUE, send QUIT protocol.
22258082Seric ** if FALSE, just close the connection.
22358082Seric ** allbut -- but leave this one open.
22458082Seric **
22558082Seric ** Returns:
22658082Seric ** none.
22754963Seric */
22854963Seric
229*69748Seric void
mci_flush(doquit,allbut)23058082Seric mci_flush(doquit, allbut)
23158082Seric bool doquit;
23258082Seric MCI *allbut;
23354963Seric {
23454963Seric register int i;
23554963Seric
23654963Seric if (MciCache == NULL)
23754963Seric return;
23854963Seric
23954963Seric for (i = 0; i < MaxMciCache; i++)
24058082Seric if (allbut != MciCache[i])
24158082Seric mci_uncache(&MciCache[i], doquit);
24254963Seric }
24354963Seric /*
24454963Seric ** MCI_GET -- get information about a particular host
24554963Seric */
24654963Seric
24754967Seric MCI *
mci_get(host,m)24854963Seric mci_get(host, m)
24954963Seric char *host;
25054963Seric MAILER *m;
25154963Seric {
25254967Seric register MCI *mci;
25355467Seric register STAB *s;
25467139Seric extern MCI **mci_scan();
25554967Seric
25658907Seric #ifdef DAEMON
25758907Seric extern SOCKADDR CurHostAddr;
25858907Seric
25958907Seric /* clear CurHostAddr so we don't get a bogus address with this name */
26058907Seric bzero(&CurHostAddr, sizeof CurHostAddr);
26163937Seric #endif
26258907Seric
26366037Seric /* clear out any expired connections */
26467139Seric (void) mci_scan(NULL);
26566037Seric
26664815Seric if (m->m_mno < 0)
26764815Seric syserr("negative mno %d (%s)", m->m_mno, m->m_name);
26855467Seric s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
26955467Seric mci = &s->s_mci;
27055467Seric mci->mci_host = s->s_name;
27154967Seric
27254967Seric if (tTd(42, 2))
27354967Seric {
27454967Seric printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
27554967Seric host, m->m_name, mci->mci_state, mci->mci_flags,
27654967Seric mci->mci_exitstat, mci->mci_errno);
27754967Seric }
27854967Seric
27957977Seric if (mci->mci_state == MCIS_OPEN)
28054967Seric {
28157977Seric /* poke the connection to see if it's still alive */
28258867Seric smtpprobe(mci);
28357977Seric
28457977Seric /* reset the stored state in the event of a timeout */
28557977Seric if (mci->mci_state != MCIS_OPEN)
28657977Seric {
28757977Seric mci->mci_errno = 0;
28857977Seric mci->mci_exitstat = EX_OK;
28957977Seric mci->mci_state = MCIS_CLOSED;
29057977Seric }
29166007Seric else
29266007Seric {
29366007Seric /* get peer host address for logging reasons only */
29466007Seric /* (this should really be in the mci struct) */
29566007Seric int socksize = sizeof CurHostAddr;
29666007Seric
29766007Seric (void) getpeername(fileno(mci->mci_in),
29866007Seric (struct sockaddr *) &CurHostAddr, &socksize);
29966007Seric }
30054967Seric }
30165870Seric if (mci->mci_state == MCIS_CLOSED)
30265870Seric {
30365870Seric /* copy out any mailer flags needed in connection state */
30465870Seric if (bitnset(M_7BITS, m->m_flags))
30565870Seric mci->mci_flags |= MCIF_7BIT;
30665870Seric }
30754967Seric
30854967Seric return mci;
30954963Seric }
31057380Seric /*
31157380Seric ** MCI_DUMP -- dump the contents of an MCI structure.
31257380Seric **
31357380Seric ** Parameters:
31457380Seric ** mci -- the MCI structure to dump.
31557380Seric **
31657380Seric ** Returns:
31757380Seric ** none.
31857380Seric **
31957380Seric ** Side Effects:
32057380Seric ** none.
32157380Seric */
32257380Seric
323*69748Seric void
mci_dump(mci,logit)32464731Seric mci_dump(mci, logit)
32557380Seric register MCI *mci;
32664731Seric bool logit;
32757380Seric {
32864731Seric register char *p;
32964731Seric char *sep;
33064731Seric char buf[1000];
33157380Seric extern char *ctime();
33257380Seric
33364731Seric sep = logit ? " " : "\n\t";
33464731Seric p = buf;
33564731Seric sprintf(p, "MCI@%x: ", mci);
33664731Seric p += strlen(p);
33757380Seric if (mci == NULL)
33857380Seric {
33964731Seric sprintf(p, "NULL");
34064731Seric goto printit;
34157380Seric }
34267693Seric sprintf(p, "flags=%x, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
34363753Seric mci->mci_flags, mci->mci_errno, mci->mci_herrno,
34464731Seric mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep);
34564731Seric p += strlen(p);
34664731Seric sprintf(p, "maxsize=%ld, phase=%s, mailer=%s,%s",
34763753Seric mci->mci_maxsize,
34857380Seric mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
34964731Seric mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
35064731Seric sep);
35164731Seric p += strlen(p);
35264731Seric sprintf(p, "host=%s, lastuse=%s",
35357380Seric mci->mci_host == NULL ? "NULL" : mci->mci_host,
35457380Seric ctime(&mci->mci_lastuse));
35564731Seric printit:
35666748Seric #ifdef LOG
35764731Seric if (logit)
35865006Seric syslog(LOG_DEBUG, "%s", buf);
35964731Seric else
36066748Seric #endif
36164731Seric printf("%s\n", buf);
36257380Seric }
36364731Seric /*
36464731Seric ** MCI_DUMP_ALL -- print the entire MCI cache
36564731Seric **
36664731Seric ** Parameters:
36764731Seric ** logit -- if set, log the result instead of printing
36864731Seric ** to stdout.
36964731Seric **
37064731Seric ** Returns:
37164731Seric ** none.
37264731Seric */
37364731Seric
374*69748Seric void
mci_dump_all(logit)37564731Seric mci_dump_all(logit)
37664731Seric bool logit;
37764731Seric {
37864731Seric register int i;
37964731Seric
38065005Seric if (MciCache == NULL)
38165005Seric return;
38265005Seric
38364731Seric for (i = 0; i < MaxMciCache; i++)
38464731Seric mci_dump(MciCache[i], logit);
38564731Seric }
386