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
mci_cache(mci)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 **
mci_scan(savemci)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
mci_uncache(mcislot,doquit)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
mci_flush(doquit,allbut)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 *
mci_get(host,m)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
mci_dump(mci,logit)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
mci_dump_all(logit)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