xref: /original-bsd/usr.sbin/sendmail/src/mci.c (revision bacd16ee)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)mci.c	8.2 (Berkeley) 07/11/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 #ifdef XLA
172 		xla_host_end(mci->mci_host);
173 #endif
174 	}
175 	else
176 	{
177 		if (mci->mci_in != NULL)
178 			xfclose(mci->mci_in, "mci_uncache", "mci_in");
179 		if (mci->mci_out != NULL)
180 			xfclose(mci->mci_out, "mci_uncache", "mci_out");
181 		mci->mci_in = mci->mci_out = NULL;
182 		mci->mci_state = MCIS_CLOSED;
183 		mci->mci_exitstat = EX_OK;
184 		mci->mci_errno = 0;
185 		mci->mci_flags = 0;
186 	}
187 }
188 /*
189 **  MCI_FLUSH -- flush the entire cache
190 **
191 **	Parameters:
192 **		doquit -- if TRUE, send QUIT protocol.
193 **			  if FALSE, just close the connection.
194 **		allbut -- but leave this one open.
195 **
196 **	Returns:
197 **		none.
198 */
199 
200 mci_flush(doquit, allbut)
201 	bool doquit;
202 	MCI *allbut;
203 {
204 	register int i;
205 
206 	if (MciCache == NULL)
207 		return;
208 
209 	for (i = 0; i < MaxMciCache; i++)
210 		if (allbut != MciCache[i])
211 			mci_uncache(&MciCache[i], doquit);
212 }
213 /*
214 **  MCI_GET -- get information about a particular host
215 */
216 
217 MCI *
218 mci_get(host, m)
219 	char *host;
220 	MAILER *m;
221 {
222 	register MCI *mci;
223 	register STAB *s;
224 
225 #ifdef DAEMON
226 	extern SOCKADDR CurHostAddr;
227 
228 	/* clear CurHostAddr so we don't get a bogus address with this name */
229 	bzero(&CurHostAddr, sizeof CurHostAddr);
230 #endif DAEMON
231 
232 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
233 	mci = &s->s_mci;
234 	mci->mci_host = s->s_name;
235 
236 	if (tTd(42, 2))
237 	{
238 		printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
239 			host, m->m_name, mci->mci_state, mci->mci_flags,
240 			mci->mci_exitstat, mci->mci_errno);
241 	}
242 
243 	if (mci->mci_state == MCIS_OPEN)
244 	{
245 		/* poke the connection to see if it's still alive */
246 		smtpprobe(mci);
247 
248 		/* reset the stored state in the event of a timeout */
249 		if (mci->mci_state != MCIS_OPEN)
250 		{
251 			mci->mci_errno = 0;
252 			mci->mci_exitstat = EX_OK;
253 			mci->mci_state = MCIS_CLOSED;
254 		}
255 	}
256 
257 	return mci;
258 }
259 /*
260 **  MCI_DUMP -- dump the contents of an MCI structure.
261 **
262 **	Parameters:
263 **		mci -- the MCI structure to dump.
264 **
265 **	Returns:
266 **		none.
267 **
268 **	Side Effects:
269 **		none.
270 */
271 
272 mci_dump(mci)
273 	register MCI *mci;
274 {
275 	extern char *ctime();
276 
277 	printf("MCI@%x: ", mci);
278 	if (mci == NULL)
279 	{
280 		printf("NULL\n");
281 		return;
282 	}
283 	printf("flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,\n",
284 		mci->mci_flags, mci->mci_errno, mci->mci_herrno,
285 		mci->mci_exitstat, mci->mci_state, mci->mci_pid);
286 	printf("\tmaxsize=%ld, phase=%s, mailer=%s,\n",
287 		mci->mci_maxsize,
288 		mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
289 		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name);
290 	printf("\thost=%s, lastuse=%s\n",
291 		mci->mci_host == NULL ? "NULL" : mci->mci_host,
292 		ctime(&mci->mci_lastuse));
293 }
294