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