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