xref: /csrg-svn/usr.sbin/sendmail/src/mci.c (revision 67139)
154963Seric /*
254963Seric  * Copyright (c) 1983 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*67139Seric static char sccsid[] = "@(#)mci.c	8.14 (Berkeley) 05/15/94";
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 */
4254967Seric /*
4354963Seric **  MCI_CACHE -- enter a connection structure into the open connection cache
4454963Seric **
4554963Seric **	This may cause something else to be flushed.
4654963Seric **
4754963Seric **	Parameters:
4854963Seric **		mci -- the connection to cache.
4954963Seric **
5054963Seric **	Returns:
5154963Seric **		none.
5254963Seric */
5354963Seric 
5454963Seric mci_cache(mci)
5554967Seric 	register MCI *mci;
5654963Seric {
5754967Seric 	register MCI **mcislot;
5854967Seric 	extern MCI **mci_scan();
5954963Seric 
6054963Seric 	/*
6154963Seric 	**  Find the best slot.  This may cause expired connections
6254963Seric 	**  to be closed.
6354963Seric 	*/
6454963Seric 
6554963Seric 	mcislot = mci_scan(mci);
66*67139Seric 	if (mcislot == NULL)
67*67139Seric 	{
68*67139Seric 		/* we don't support caching */
69*67139Seric 		return;
70*67139Seric 	}
7154963Seric 
7254963Seric 	/* if this is already cached, we are done */
7354963Seric 	if (bitset(MCIF_CACHED, mci->mci_flags))
7454963Seric 		return;
7554963Seric 
7654963Seric 	/* otherwise we may have to clear the slot */
7754963Seric 	if (*mcislot != NULL)
7858082Seric 		mci_uncache(mcislot, TRUE);
7954963Seric 
8064739Seric 	if (tTd(42, 5))
8164739Seric 		printf("mci_cache: caching %x (%s) in slot %d\n",
8264739Seric 			mci, mci->mci_host, mcislot - MciCache);
8364739Seric #ifdef LOG
8464739Seric 	if (tTd(91, 100))
8564739Seric 		syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d",
8664742Seric 			CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
8764742Seric 			mci, mci->mci_host, mcislot - MciCache);
8864739Seric #endif
8964739Seric 
9054963Seric 	*mcislot = mci;
9154963Seric 	mci->mci_flags |= MCIF_CACHED;
9254963Seric }
9354963Seric /*
9454963Seric **  MCI_SCAN -- scan the cache, flush junk, and return best slot
9554963Seric **
9654963Seric **	Parameters:
9754963Seric **		savemci -- never flush this one.  Can be null.
9854963Seric **
9954963Seric **	Returns:
10054963Seric **		The LRU (or empty) slot.
10154963Seric */
10254963Seric 
10354967Seric MCI **
10454963Seric mci_scan(savemci)
10554967Seric 	MCI *savemci;
10654963Seric {
10754963Seric 	time_t now;
10854967Seric 	register MCI **bestmci;
10954967Seric 	register MCI *mci;
11054963Seric 	register int i;
11154963Seric 
112*67139Seric 	if (MaxMciCache <= 0)
113*67139Seric 	{
114*67139Seric 		/* we don't support caching */
115*67139Seric 		return NULL;
116*67139Seric 	}
117*67139Seric 
11854963Seric 	if (MciCache == NULL)
11954963Seric 	{
12054963Seric 		/* first call */
12154967Seric 		MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
12256215Seric 		bzero((char *) MciCache, MaxMciCache * sizeof *MciCache);
12354963Seric 		return (&MciCache[0]);
12454963Seric 	}
12554963Seric 
12654963Seric 	now = curtime();
12754963Seric 	bestmci = &MciCache[0];
12854963Seric 	for (i = 0; i < MaxMciCache; i++)
12954963Seric 	{
13054963Seric 		mci = MciCache[i];
13154963Seric 		if (mci == NULL || mci->mci_state == MCIS_CLOSED)
13254963Seric 		{
13354963Seric 			bestmci = &MciCache[i];
13454963Seric 			continue;
13554963Seric 		}
13654963Seric 		if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
13754963Seric 		{
13854963Seric 			/* connection idle too long -- close it */
13954963Seric 			bestmci = &MciCache[i];
14058082Seric 			mci_uncache(bestmci, TRUE);
14154963Seric 			continue;
14254963Seric 		}
14354963Seric 		if (*bestmci == NULL)
14454963Seric 			continue;
14554963Seric 		if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
14654963Seric 			bestmci = &MciCache[i];
14754963Seric 	}
14854963Seric 	return bestmci;
14954963Seric }
15054963Seric /*
15154963Seric **  MCI_UNCACHE -- remove a connection from a slot.
15254963Seric **
15354963Seric **	May close a connection.
15454963Seric **
15554963Seric **	Parameters:
15654963Seric **		mcislot -- the slot to empty.
15758082Seric **		doquit -- if TRUE, send QUIT protocol on this connection.
15858082Seric **			  if FALSE, we are assumed to be in a forked child;
15958082Seric **				all we want to do is close the file(s).
16054963Seric **
16154963Seric **	Returns:
16254963Seric **		none.
16354963Seric */
16454963Seric 
16558082Seric mci_uncache(mcislot, doquit)
16654967Seric 	register MCI **mcislot;
16758082Seric 	bool doquit;
16854963Seric {
16954967Seric 	register MCI *mci;
17055020Seric 	extern ENVELOPE BlankEnvelope;
17154963Seric 
17254963Seric 	mci = *mcislot;
17354963Seric 	if (mci == NULL)
17454963Seric 		return;
17554963Seric 	*mcislot = NULL;
17654963Seric 
17764739Seric 	if (tTd(42, 5))
17864739Seric 		printf("mci_uncache: uncaching %x (%s) from slot %d (%d)\n",
17964739Seric 			mci, mci->mci_host, mcislot - MciCache, doquit);
18064739Seric #ifdef LOG
18164739Seric 	if (tTd(91, 100))
18264739Seric 		syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)",
18364742Seric 			CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
18464742Seric 			mci, mci->mci_host, mcislot - MciCache, doquit);
18564739Seric #endif
18664739Seric 
18758082Seric 	if (doquit)
18858082Seric 	{
18958151Seric 		message("Closing connection to %s", mci->mci_host);
19055467Seric 
19158082Seric 		mci->mci_flags &= ~MCIF_CACHED;
19258082Seric 
19358082Seric 		/* only uses the envelope to flush the transcript file */
19458082Seric 		if (mci->mci_state != MCIS_CLOSED)
19558082Seric 			smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
19659156Seric #ifdef XLA
19759156Seric 		xla_host_end(mci->mci_host);
19859156Seric #endif
19958082Seric 	}
20058082Seric 	else
20158082Seric 	{
20258082Seric 		if (mci->mci_in != NULL)
20358680Seric 			xfclose(mci->mci_in, "mci_uncache", "mci_in");
20458082Seric 		if (mci->mci_out != NULL)
20558680Seric 			xfclose(mci->mci_out, "mci_uncache", "mci_out");
20658082Seric 		mci->mci_in = mci->mci_out = NULL;
20758082Seric 		mci->mci_state = MCIS_CLOSED;
20858082Seric 		mci->mci_exitstat = EX_OK;
20958082Seric 		mci->mci_errno = 0;
21058082Seric 		mci->mci_flags = 0;
21158082Seric 	}
21254963Seric }
21354963Seric /*
21454963Seric **  MCI_FLUSH -- flush the entire cache
21558082Seric **
21658082Seric **	Parameters:
21758082Seric **		doquit -- if TRUE, send QUIT protocol.
21858082Seric **			  if FALSE, just close the connection.
21958082Seric **		allbut -- but leave this one open.
22058082Seric **
22158082Seric **	Returns:
22258082Seric **		none.
22354963Seric */
22454963Seric 
22558082Seric mci_flush(doquit, allbut)
22658082Seric 	bool doquit;
22758082Seric 	MCI *allbut;
22854963Seric {
22954963Seric 	register int i;
23054963Seric 
23154963Seric 	if (MciCache == NULL)
23254963Seric 		return;
23354963Seric 
23454963Seric 	for (i = 0; i < MaxMciCache; i++)
23558082Seric 		if (allbut != MciCache[i])
23658082Seric 			mci_uncache(&MciCache[i], doquit);
23754963Seric }
23854963Seric /*
23954963Seric **  MCI_GET -- get information about a particular host
24054963Seric */
24154963Seric 
24254967Seric MCI *
24354963Seric mci_get(host, m)
24454963Seric 	char *host;
24554963Seric 	MAILER *m;
24654963Seric {
24754967Seric 	register MCI *mci;
24855467Seric 	register STAB *s;
249*67139Seric 	extern MCI **mci_scan();
25054967Seric 
25158907Seric #ifdef DAEMON
25258907Seric 	extern SOCKADDR CurHostAddr;
25358907Seric 
25458907Seric 	/* clear CurHostAddr so we don't get a bogus address with this name */
25558907Seric 	bzero(&CurHostAddr, sizeof CurHostAddr);
25663937Seric #endif
25758907Seric 
25866037Seric 	/* clear out any expired connections */
259*67139Seric 	(void) mci_scan(NULL);
26066037Seric 
26164815Seric 	if (m->m_mno < 0)
26264815Seric 		syserr("negative mno %d (%s)", m->m_mno, m->m_name);
26355467Seric 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
26455467Seric 	mci = &s->s_mci;
26555467Seric 	mci->mci_host = s->s_name;
26654967Seric 
26754967Seric 	if (tTd(42, 2))
26854967Seric 	{
26954967Seric 		printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
27054967Seric 			host, m->m_name, mci->mci_state, mci->mci_flags,
27154967Seric 			mci->mci_exitstat, mci->mci_errno);
27254967Seric 	}
27354967Seric 
27457977Seric 	if (mci->mci_state == MCIS_OPEN)
27554967Seric 	{
27657977Seric 		/* poke the connection to see if it's still alive */
27758867Seric 		smtpprobe(mci);
27857977Seric 
27957977Seric 		/* reset the stored state in the event of a timeout */
28057977Seric 		if (mci->mci_state != MCIS_OPEN)
28157977Seric 		{
28257977Seric 			mci->mci_errno = 0;
28357977Seric 			mci->mci_exitstat = EX_OK;
28457977Seric 			mci->mci_state = MCIS_CLOSED;
28557977Seric 		}
28666007Seric 		else
28766007Seric 		{
28866007Seric 			/* get peer host address for logging reasons only */
28966007Seric 			/* (this should really be in the mci struct) */
29066007Seric 			int socksize = sizeof CurHostAddr;
29166007Seric 
29266007Seric 			(void) getpeername(fileno(mci->mci_in),
29366007Seric 				(struct sockaddr *) &CurHostAddr, &socksize);
29466007Seric 		}
29554967Seric 	}
29665870Seric 	if (mci->mci_state == MCIS_CLOSED)
29765870Seric 	{
29865870Seric 		/* copy out any mailer flags needed in connection state */
29965870Seric 		if (bitnset(M_7BITS, m->m_flags))
30065870Seric 			mci->mci_flags |= MCIF_7BIT;
30165870Seric 	}
30254967Seric 
30354967Seric 	return mci;
30454963Seric }
30557380Seric /*
30657380Seric **  MCI_DUMP -- dump the contents of an MCI structure.
30757380Seric **
30857380Seric **	Parameters:
30957380Seric **		mci -- the MCI structure to dump.
31057380Seric **
31157380Seric **	Returns:
31257380Seric **		none.
31357380Seric **
31457380Seric **	Side Effects:
31557380Seric **		none.
31657380Seric */
31757380Seric 
31864731Seric mci_dump(mci, logit)
31957380Seric 	register MCI *mci;
32064731Seric 	bool logit;
32157380Seric {
32264731Seric 	register char *p;
32364731Seric 	char *sep;
32464731Seric 	char buf[1000];
32557380Seric 	extern char *ctime();
32657380Seric 
32764731Seric 	sep = logit ? " " : "\n\t";
32864731Seric 	p = buf;
32964731Seric 	sprintf(p, "MCI@%x: ", mci);
33064731Seric 	p += strlen(p);
33157380Seric 	if (mci == NULL)
33257380Seric 	{
33364731Seric 		sprintf(p, "NULL");
33464731Seric 		goto printit;
33557380Seric 	}
33664731Seric 	sprintf(p, "flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
33763753Seric 		mci->mci_flags, mci->mci_errno, mci->mci_herrno,
33864731Seric 		mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep);
33964731Seric 	p += strlen(p);
34064731Seric 	sprintf(p, "maxsize=%ld, phase=%s, mailer=%s,%s",
34163753Seric 		mci->mci_maxsize,
34257380Seric 		mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
34364731Seric 		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
34464731Seric 		sep);
34564731Seric 	p += strlen(p);
34664731Seric 	sprintf(p, "host=%s, lastuse=%s",
34757380Seric 		mci->mci_host == NULL ? "NULL" : mci->mci_host,
34857380Seric 		ctime(&mci->mci_lastuse));
34964731Seric printit:
35066748Seric #ifdef LOG
35164731Seric 	if (logit)
35265006Seric 		syslog(LOG_DEBUG, "%s", buf);
35364731Seric 	else
35466748Seric #endif
35564731Seric 		printf("%s\n", buf);
35657380Seric }
35764731Seric /*
35864731Seric **  MCI_DUMP_ALL -- print the entire MCI cache
35964731Seric **
36064731Seric **	Parameters:
36164731Seric **		logit -- if set, log the result instead of printing
36264731Seric **			to stdout.
36364731Seric **
36464731Seric **	Returns:
36564731Seric **		none.
36664731Seric */
36764731Seric 
36864731Seric mci_dump_all(logit)
36964731Seric 	bool logit;
37064731Seric {
37164731Seric 	register int i;
37264731Seric 
37365005Seric 	if (MciCache == NULL)
37465005Seric 		return;
37565005Seric 
37664731Seric 	for (i = 0; i < MaxMciCache; i++)
37764731Seric 		mci_dump(MciCache[i], logit);
37864731Seric }
379