xref: /original-bsd/usr.sbin/sendmail/src/mci.c (revision 0842ddeb)
1 /*
2  * Copyright (c) 1983, 1995 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.16 (Berkeley) 04/21/95";
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 	/*
61 	**  Find the best slot.  This may cause expired connections
62 	**  to be closed.
63 	*/
64 
65 	mcislot = mci_scan(mci);
66 	if (mcislot == NULL)
67 	{
68 		/* we don't support caching */
69 		return;
70 	}
71 
72 	/* if this is already cached, we are done */
73 	if (bitset(MCIF_CACHED, mci->mci_flags))
74 		return;
75 
76 	/* otherwise we may have to clear the slot */
77 	if (*mcislot != NULL)
78 		mci_uncache(mcislot, TRUE);
79 
80 	if (tTd(42, 5))
81 		printf("mci_cache: caching %x (%s) in slot %d\n",
82 			mci, mci->mci_host, mcislot - MciCache);
83 #ifdef LOG
84 	if (tTd(91, 100))
85 		syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d",
86 			CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
87 			mci, mci->mci_host, mcislot - MciCache);
88 #endif
89 
90 	*mcislot = mci;
91 	mci->mci_flags |= MCIF_CACHED;
92 }
93 /*
94 **  MCI_SCAN -- scan the cache, flush junk, and return best slot
95 **
96 **	Parameters:
97 **		savemci -- never flush this one.  Can be null.
98 **
99 **	Returns:
100 **		The LRU (or empty) slot.
101 */
102 
103 MCI **
104 mci_scan(savemci)
105 	MCI *savemci;
106 {
107 	time_t now;
108 	register MCI **bestmci;
109 	register MCI *mci;
110 	register int i;
111 
112 	if (MaxMciCache <= 0)
113 	{
114 		/* we don't support caching */
115 		return NULL;
116 	}
117 
118 	if (MciCache == NULL)
119 	{
120 		/* first call */
121 		MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
122 		bzero((char *) MciCache, MaxMciCache * sizeof *MciCache);
123 		return (&MciCache[0]);
124 	}
125 
126 	now = curtime();
127 	bestmci = &MciCache[0];
128 	for (i = 0; i < MaxMciCache; i++)
129 	{
130 		mci = MciCache[i];
131 		if (mci == NULL || mci->mci_state == MCIS_CLOSED)
132 		{
133 			bestmci = &MciCache[i];
134 			continue;
135 		}
136 		if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
137 		{
138 			/* connection idle too long -- close it */
139 			bestmci = &MciCache[i];
140 			mci_uncache(bestmci, TRUE);
141 			continue;
142 		}
143 		if (*bestmci == NULL)
144 			continue;
145 		if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
146 			bestmci = &MciCache[i];
147 	}
148 	return bestmci;
149 }
150 /*
151 **  MCI_UNCACHE -- remove a connection from a slot.
152 **
153 **	May close a connection.
154 **
155 **	Parameters:
156 **		mcislot -- the slot to empty.
157 **		doquit -- if TRUE, send QUIT protocol on this connection.
158 **			  if FALSE, we are assumed to be in a forked child;
159 **				all we want to do is close the file(s).
160 **
161 **	Returns:
162 **		none.
163 */
164 
165 mci_uncache(mcislot, doquit)
166 	register MCI **mcislot;
167 	bool doquit;
168 {
169 	register MCI *mci;
170 	extern ENVELOPE BlankEnvelope;
171 
172 	mci = *mcislot;
173 	if (mci == NULL)
174 		return;
175 	*mcislot = NULL;
176 
177 	if (tTd(42, 5))
178 		printf("mci_uncache: uncaching %x (%s) from slot %d (%d)\n",
179 			mci, mci->mci_host, mcislot - MciCache, doquit);
180 #ifdef LOG
181 	if (tTd(91, 100))
182 		syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)",
183 			CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
184 			mci, mci->mci_host, mcislot - MciCache, doquit);
185 #endif
186 
187 	if (doquit)
188 	{
189 		message("Closing connection to %s", mci->mci_host);
190 
191 		mci->mci_flags &= ~MCIF_CACHED;
192 
193 		/* only uses the envelope to flush the transcript file */
194 		if (mci->mci_state != MCIS_CLOSED)
195 			smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
196 #ifdef XLA
197 		xla_host_end(mci->mci_host);
198 #endif
199 	}
200 	else
201 	{
202 		if (mci->mci_in != NULL)
203 			xfclose(mci->mci_in, "mci_uncache", "mci_in");
204 		if (mci->mci_out != NULL)
205 			xfclose(mci->mci_out, "mci_uncache", "mci_out");
206 		mci->mci_in = mci->mci_out = NULL;
207 		mci->mci_state = MCIS_CLOSED;
208 		mci->mci_exitstat = EX_OK;
209 		mci->mci_errno = 0;
210 		mci->mci_flags = 0;
211 	}
212 }
213 /*
214 **  MCI_FLUSH -- flush the entire cache
215 **
216 **	Parameters:
217 **		doquit -- if TRUE, send QUIT protocol.
218 **			  if FALSE, just close the connection.
219 **		allbut -- but leave this one open.
220 **
221 **	Returns:
222 **		none.
223 */
224 
225 mci_flush(doquit, allbut)
226 	bool doquit;
227 	MCI *allbut;
228 {
229 	register int i;
230 
231 	if (MciCache == NULL)
232 		return;
233 
234 	for (i = 0; i < MaxMciCache; i++)
235 		if (allbut != MciCache[i])
236 			mci_uncache(&MciCache[i], doquit);
237 }
238 /*
239 **  MCI_GET -- get information about a particular host
240 */
241 
242 MCI *
243 mci_get(host, m)
244 	char *host;
245 	MAILER *m;
246 {
247 	register MCI *mci;
248 	register STAB *s;
249 	extern MCI **mci_scan();
250 
251 #ifdef DAEMON
252 	extern SOCKADDR CurHostAddr;
253 
254 	/* clear CurHostAddr so we don't get a bogus address with this name */
255 	bzero(&CurHostAddr, sizeof CurHostAddr);
256 #endif
257 
258 	/* clear out any expired connections */
259 	(void) mci_scan(NULL);
260 
261 	if (m->m_mno < 0)
262 		syserr("negative mno %d (%s)", m->m_mno, m->m_name);
263 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
264 	mci = &s->s_mci;
265 	mci->mci_host = s->s_name;
266 
267 	if (tTd(42, 2))
268 	{
269 		printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
270 			host, m->m_name, mci->mci_state, mci->mci_flags,
271 			mci->mci_exitstat, mci->mci_errno);
272 	}
273 
274 	if (mci->mci_state == MCIS_OPEN)
275 	{
276 		/* poke the connection to see if it's still alive */
277 		smtpprobe(mci);
278 
279 		/* reset the stored state in the event of a timeout */
280 		if (mci->mci_state != MCIS_OPEN)
281 		{
282 			mci->mci_errno = 0;
283 			mci->mci_exitstat = EX_OK;
284 			mci->mci_state = MCIS_CLOSED;
285 		}
286 		else
287 		{
288 			/* get peer host address for logging reasons only */
289 			/* (this should really be in the mci struct) */
290 			int socksize = sizeof CurHostAddr;
291 
292 			(void) getpeername(fileno(mci->mci_in),
293 				(struct sockaddr *) &CurHostAddr, &socksize);
294 		}
295 	}
296 	if (mci->mci_state == MCIS_CLOSED)
297 	{
298 		/* copy out any mailer flags needed in connection state */
299 		if (bitnset(M_7BITS, m->m_flags))
300 			mci->mci_flags |= MCIF_7BIT;
301 	}
302 
303 	return mci;
304 }
305 /*
306 **  MCI_DUMP -- dump the contents of an MCI structure.
307 **
308 **	Parameters:
309 **		mci -- the MCI structure to dump.
310 **
311 **	Returns:
312 **		none.
313 **
314 **	Side Effects:
315 **		none.
316 */
317 
318 mci_dump(mci, logit)
319 	register MCI *mci;
320 	bool logit;
321 {
322 	register char *p;
323 	char *sep;
324 	char buf[1000];
325 	extern char *ctime();
326 
327 	sep = logit ? " " : "\n\t";
328 	p = buf;
329 	sprintf(p, "MCI@%x: ", mci);
330 	p += strlen(p);
331 	if (mci == NULL)
332 	{
333 		sprintf(p, "NULL");
334 		goto printit;
335 	}
336 	sprintf(p, "flags=%x, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
337 		mci->mci_flags, mci->mci_errno, mci->mci_herrno,
338 		mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep);
339 	p += strlen(p);
340 	sprintf(p, "maxsize=%ld, phase=%s, mailer=%s,%s",
341 		mci->mci_maxsize,
342 		mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
343 		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
344 		sep);
345 	p += strlen(p);
346 	sprintf(p, "host=%s, lastuse=%s",
347 		mci->mci_host == NULL ? "NULL" : mci->mci_host,
348 		ctime(&mci->mci_lastuse));
349 printit:
350 #ifdef LOG
351 	if (logit)
352 		syslog(LOG_DEBUG, "%s", buf);
353 	else
354 #endif
355 		printf("%s\n", buf);
356 }
357 /*
358 **  MCI_DUMP_ALL -- print the entire MCI cache
359 **
360 **	Parameters:
361 **		logit -- if set, log the result instead of printing
362 **			to stdout.
363 **
364 **	Returns:
365 **		none.
366 */
367 
368 mci_dump_all(logit)
369 	bool logit;
370 {
371 	register int i;
372 
373 	if (MciCache == NULL)
374 		return;
375 
376 	for (i = 0; i < MaxMciCache; i++)
377 		mci_dump(MciCache[i], logit);
378 }
379