xref: /original-bsd/usr.sbin/sendmail/src/mci.c (revision e34b604d)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)mci.c	6.8 (Berkeley) 04/01/93";
11 #endif /* not lint */
12 
13 #include "sendmail.h"
14 
15 /*
16 **  Mail Connection Information (MCI) Caching Module.
17 **
18 **	There are actually two separate things cached.  The first is
19 **	the set of all open connections -- these are stored in a
20 **	(small) list.  The second is stored in the symbol table; it
21 **	has the overall status for all hosts, whether or not there
22 **	is a connection open currently.
23 **
24 **	There should never be too many connections open (since this
25 **	could flood the socket table), nor should a connection be
26 **	allowed to sit idly for too long.
27 **
28 **	MaxMciCache is the maximum number of open connections that
29 **	will be supported.
30 **
31 **	MciCacheTimeout is the time (in seconds) that a connection
32 **	is permitted to survive without activity.
33 **
34 **	We actually try any cached connections by sending a NOOP
35 **	before we use them; if the NOOP fails we close down the
36 **	connection and reopen it.  Note that this means that a
37 **	server SMTP that doesn't support NOOP will hose the
38 **	algorithm -- but that doesn't seem too likely.
39 */
40 
41 MCI	**MciCache;		/* the open connection cache */
42 /*
43 **  MCI_CACHE -- enter a connection structure into the open connection cache
44 **
45 **	This may cause something else to be flushed.
46 **
47 **	Parameters:
48 **		mci -- the connection to cache.
49 **
50 **	Returns:
51 **		none.
52 */
53 
54 mci_cache(mci)
55 	register MCI *mci;
56 {
57 	register MCI **mcislot;
58 	extern MCI **mci_scan();
59 
60 	if (MaxMciCache <= 0)
61 	{
62 		/* we don't support caching */
63 		return;
64 	}
65 
66 	/*
67 	**  Find the best slot.  This may cause expired connections
68 	**  to be closed.
69 	*/
70 
71 	mcislot = mci_scan(mci);
72 
73 	/* if this is already cached, we are done */
74 	if (bitset(MCIF_CACHED, mci->mci_flags))
75 		return;
76 
77 	/* otherwise we may have to clear the slot */
78 	if (*mcislot != NULL)
79 		mci_uncache(mcislot, TRUE);
80 
81 	*mcislot = mci;
82 	mci->mci_flags |= MCIF_CACHED;
83 }
84 /*
85 **  MCI_SCAN -- scan the cache, flush junk, and return best slot
86 **
87 **	Parameters:
88 **		savemci -- never flush this one.  Can be null.
89 **
90 **	Returns:
91 **		The LRU (or empty) slot.
92 */
93 
94 MCI **
95 mci_scan(savemci)
96 	MCI *savemci;
97 {
98 	time_t now;
99 	register MCI **bestmci;
100 	register MCI *mci;
101 	register int i;
102 
103 	if (MciCache == NULL)
104 	{
105 		/* first call */
106 		MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
107 		bzero((char *) MciCache, MaxMciCache * sizeof *MciCache);
108 		return (&MciCache[0]);
109 	}
110 
111 	now = curtime();
112 	bestmci = &MciCache[0];
113 	for (i = 0; i < MaxMciCache; i++)
114 	{
115 		mci = MciCache[i];
116 		if (mci == NULL || mci->mci_state == MCIS_CLOSED)
117 		{
118 			bestmci = &MciCache[i];
119 			continue;
120 		}
121 		if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
122 		{
123 			/* connection idle too long -- close it */
124 			bestmci = &MciCache[i];
125 			mci_uncache(bestmci, TRUE);
126 			continue;
127 		}
128 		if (*bestmci == NULL)
129 			continue;
130 		if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
131 			bestmci = &MciCache[i];
132 	}
133 	return bestmci;
134 }
135 /*
136 **  MCI_UNCACHE -- remove a connection from a slot.
137 **
138 **	May close a connection.
139 **
140 **	Parameters:
141 **		mcislot -- the slot to empty.
142 **		doquit -- if TRUE, send QUIT protocol on this connection.
143 **			  if FALSE, we are assumed to be in a forked child;
144 **				all we want to do is close the file(s).
145 **
146 **	Returns:
147 **		none.
148 */
149 
150 mci_uncache(mcislot, doquit)
151 	register MCI **mcislot;
152 	bool doquit;
153 {
154 	register MCI *mci;
155 	extern ENVELOPE BlankEnvelope;
156 
157 	mci = *mcislot;
158 	if (mci == NULL)
159 		return;
160 	*mcislot = NULL;
161 
162 	if (doquit)
163 	{
164 		message("Closing connection to %s", mci->mci_host);
165 
166 		mci->mci_flags &= ~MCIF_CACHED;
167 
168 		/* only uses the envelope to flush the transcript file */
169 		if (mci->mci_state != MCIS_CLOSED)
170 			smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
171 	}
172 	else
173 	{
174 		if (mci->mci_in != NULL)
175 			xfclose(mci->mci_in, "mci_uncache", "mci_in");
176 		if (mci->mci_out != NULL)
177 			xfclose(mci->mci_out, "mci_uncache", "mci_out");
178 		mci->mci_in = mci->mci_out = NULL;
179 		mci->mci_state = MCIS_CLOSED;
180 		mci->mci_exitstat = EX_OK;
181 		mci->mci_errno = 0;
182 		mci->mci_flags = 0;
183 	}
184 }
185 /*
186 **  MCI_FLUSH -- flush the entire cache
187 **
188 **	Parameters:
189 **		doquit -- if TRUE, send QUIT protocol.
190 **			  if FALSE, just close the connection.
191 **		allbut -- but leave this one open.
192 **
193 **	Returns:
194 **		none.
195 */
196 
197 mci_flush(doquit, allbut)
198 	bool doquit;
199 	MCI *allbut;
200 {
201 	register int i;
202 
203 	if (MciCache == NULL)
204 		return;
205 
206 	for (i = 0; i < MaxMciCache; i++)
207 		if (allbut != MciCache[i])
208 			mci_uncache(&MciCache[i], doquit);
209 }
210 /*
211 **  MCI_GET -- get information about a particular host
212 */
213 
214 MCI *
215 mci_get(host, m)
216 	char *host;
217 	MAILER *m;
218 {
219 	register MCI *mci;
220 	register STAB *s;
221 
222 #ifdef DAEMON
223 	extern SOCKADDR CurHostAddr;
224 
225 	/* clear CurHostAddr so we don't get a bogus address with this name */
226 	bzero(&CurHostAddr, sizeof CurHostAddr);
227 #endif DAEMON
228 
229 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
230 	mci = &s->s_mci;
231 	mci->mci_host = s->s_name;
232 
233 	if (tTd(42, 2))
234 	{
235 		printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
236 			host, m->m_name, mci->mci_state, mci->mci_flags,
237 			mci->mci_exitstat, mci->mci_errno);
238 	}
239 
240 	if (mci->mci_state == MCIS_OPEN)
241 	{
242 		/* poke the connection to see if it's still alive */
243 		smtpprobe(mci);
244 
245 		/* reset the stored state in the event of a timeout */
246 		if (mci->mci_state != MCIS_OPEN)
247 		{
248 			mci->mci_errno = 0;
249 			mci->mci_exitstat = EX_OK;
250 			mci->mci_state = MCIS_CLOSED;
251 		}
252 	}
253 
254 	return mci;
255 }
256 /*
257 **  MCI_DUMP -- dump the contents of an MCI structure.
258 **
259 **	Parameters:
260 **		mci -- the MCI structure to dump.
261 **
262 **	Returns:
263 **		none.
264 **
265 **	Side Effects:
266 **		none.
267 */
268 
269 mci_dump(mci)
270 	register MCI *mci;
271 {
272 	extern char *ctime();
273 
274 	printf("MCI@%x: ", mci);
275 	if (mci == NULL)
276 	{
277 		printf("NULL\n");
278 		return;
279 	}
280 	printf("flags=%o, errno=%d, exitstat=%d, state=%d, pid=%d,\n",
281 		mci->mci_flags, mci->mci_errno, mci->mci_exitstat,
282 		mci->mci_state, mci->mci_pid);
283 	printf("\tphase=%s, mailer=%s,\n",
284 		mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
285 		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name);
286 	printf("\thost=%s, lastuse=%s\n",
287 		mci->mci_host == NULL ? "NULL" : mci->mci_host,
288 		ctime(&mci->mci_lastuse));
289 }
290