Commit | Line | Data |
---|---|---|
6f14531a RG |
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 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * 3. All advertising materials mentioning features or use of this software | |
15 | * must display the following acknowledgement: | |
16 | * This product includes software developed by the University of | |
17 | * California, Berkeley and its contributors. | |
18 | * 4. Neither the name of the University nor the names of its contributors | |
19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #ifndef lint | |
d747e748 | 36 | static char sccsid[] = "@(#)mci.c 8.6 (Berkeley) 10/23/93"; |
6f14531a RG |
37 | #endif /* not lint */ |
38 | ||
39 | #include "sendmail.h" | |
40 | ||
41 | /* | |
42 | ** Mail Connection Information (MCI) Caching Module. | |
43 | ** | |
44 | ** There are actually two separate things cached. The first is | |
45 | ** the set of all open connections -- these are stored in a | |
46 | ** (small) list. The second is stored in the symbol table; it | |
47 | ** has the overall status for all hosts, whether or not there | |
48 | ** is a connection open currently. | |
49 | ** | |
50 | ** There should never be too many connections open (since this | |
51 | ** could flood the socket table), nor should a connection be | |
52 | ** allowed to sit idly for too long. | |
53 | ** | |
54 | ** MaxMciCache is the maximum number of open connections that | |
55 | ** will be supported. | |
56 | ** | |
57 | ** MciCacheTimeout is the time (in seconds) that a connection | |
58 | ** is permitted to survive without activity. | |
59 | ** | |
60 | ** We actually try any cached connections by sending a NOOP | |
61 | ** before we use them; if the NOOP fails we close down the | |
62 | ** connection and reopen it. Note that this means that a | |
63 | ** server SMTP that doesn't support NOOP will hose the | |
64 | ** algorithm -- but that doesn't seem too likely. | |
65 | */ | |
66 | ||
67 | MCI **MciCache; /* the open connection cache */ | |
68 | \f/* | |
69 | ** MCI_CACHE -- enter a connection structure into the open connection cache | |
70 | ** | |
71 | ** This may cause something else to be flushed. | |
72 | ** | |
73 | ** Parameters: | |
74 | ** mci -- the connection to cache. | |
75 | ** | |
76 | ** Returns: | |
77 | ** none. | |
78 | */ | |
79 | ||
80 | mci_cache(mci) | |
81 | register MCI *mci; | |
82 | { | |
83 | register MCI **mcislot; | |
84 | extern MCI **mci_scan(); | |
85 | ||
86 | if (MaxMciCache <= 0) | |
87 | { | |
88 | /* we don't support caching */ | |
89 | return; | |
90 | } | |
91 | ||
92 | /* | |
93 | ** Find the best slot. This may cause expired connections | |
94 | ** to be closed. | |
95 | */ | |
96 | ||
97 | mcislot = mci_scan(mci); | |
98 | ||
99 | /* if this is already cached, we are done */ | |
100 | if (bitset(MCIF_CACHED, mci->mci_flags)) | |
101 | return; | |
102 | ||
103 | /* otherwise we may have to clear the slot */ | |
104 | if (*mcislot != NULL) | |
105 | mci_uncache(mcislot, TRUE); | |
106 | ||
d747e748 JH |
107 | if (tTd(42, 5)) |
108 | printf("mci_cache: caching %x (%s) in slot %d\n", | |
109 | mci, mci->mci_host, mcislot - MciCache); | |
110 | #ifdef LOG | |
111 | if (tTd(91, 100)) | |
112 | syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d", | |
113 | CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", | |
114 | mci, mci->mci_host, mcislot - MciCache); | |
115 | #endif | |
116 | ||
6f14531a RG |
117 | *mcislot = mci; |
118 | mci->mci_flags |= MCIF_CACHED; | |
119 | } | |
120 | \f/* | |
121 | ** MCI_SCAN -- scan the cache, flush junk, and return best slot | |
122 | ** | |
123 | ** Parameters: | |
124 | ** savemci -- never flush this one. Can be null. | |
125 | ** | |
126 | ** Returns: | |
127 | ** The LRU (or empty) slot. | |
128 | */ | |
129 | ||
130 | MCI ** | |
131 | mci_scan(savemci) | |
132 | MCI *savemci; | |
133 | { | |
134 | time_t now; | |
135 | register MCI **bestmci; | |
136 | register MCI *mci; | |
137 | register int i; | |
138 | ||
139 | if (MciCache == NULL) | |
140 | { | |
141 | /* first call */ | |
142 | MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); | |
143 | bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); | |
144 | return (&MciCache[0]); | |
145 | } | |
146 | ||
147 | now = curtime(); | |
148 | bestmci = &MciCache[0]; | |
149 | for (i = 0; i < MaxMciCache; i++) | |
150 | { | |
151 | mci = MciCache[i]; | |
152 | if (mci == NULL || mci->mci_state == MCIS_CLOSED) | |
153 | { | |
154 | bestmci = &MciCache[i]; | |
155 | continue; | |
156 | } | |
157 | if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) | |
158 | { | |
159 | /* connection idle too long -- close it */ | |
160 | bestmci = &MciCache[i]; | |
161 | mci_uncache(bestmci, TRUE); | |
162 | continue; | |
163 | } | |
164 | if (*bestmci == NULL) | |
165 | continue; | |
166 | if (mci->mci_lastuse < (*bestmci)->mci_lastuse) | |
167 | bestmci = &MciCache[i]; | |
168 | } | |
169 | return bestmci; | |
170 | } | |
171 | \f/* | |
172 | ** MCI_UNCACHE -- remove a connection from a slot. | |
173 | ** | |
174 | ** May close a connection. | |
175 | ** | |
176 | ** Parameters: | |
177 | ** mcislot -- the slot to empty. | |
178 | ** doquit -- if TRUE, send QUIT protocol on this connection. | |
179 | ** if FALSE, we are assumed to be in a forked child; | |
180 | ** all we want to do is close the file(s). | |
181 | ** | |
182 | ** Returns: | |
183 | ** none. | |
184 | */ | |
185 | ||
186 | mci_uncache(mcislot, doquit) | |
187 | register MCI **mcislot; | |
188 | bool doquit; | |
189 | { | |
190 | register MCI *mci; | |
191 | extern ENVELOPE BlankEnvelope; | |
192 | ||
193 | mci = *mcislot; | |
194 | if (mci == NULL) | |
195 | return; | |
196 | *mcislot = NULL; | |
197 | ||
d747e748 JH |
198 | if (tTd(42, 5)) |
199 | printf("mci_uncache: uncaching %x (%s) from slot %d (%d)\n", | |
200 | mci, mci->mci_host, mcislot - MciCache, doquit); | |
201 | #ifdef LOG | |
202 | if (tTd(91, 100)) | |
203 | syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)", | |
204 | CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", | |
205 | mci, mci->mci_host, mcislot - MciCache, doquit); | |
206 | #endif | |
207 | ||
6f14531a RG |
208 | if (doquit) |
209 | { | |
210 | message("Closing connection to %s", mci->mci_host); | |
211 | ||
212 | mci->mci_flags &= ~MCIF_CACHED; | |
213 | ||
214 | /* only uses the envelope to flush the transcript file */ | |
215 | if (mci->mci_state != MCIS_CLOSED) | |
216 | smtpquit(mci->mci_mailer, mci, &BlankEnvelope); | |
217 | #ifdef XLA | |
218 | xla_host_end(mci->mci_host); | |
219 | #endif | |
220 | } | |
221 | else | |
222 | { | |
223 | if (mci->mci_in != NULL) | |
224 | xfclose(mci->mci_in, "mci_uncache", "mci_in"); | |
225 | if (mci->mci_out != NULL) | |
226 | xfclose(mci->mci_out, "mci_uncache", "mci_out"); | |
227 | mci->mci_in = mci->mci_out = NULL; | |
228 | mci->mci_state = MCIS_CLOSED; | |
229 | mci->mci_exitstat = EX_OK; | |
230 | mci->mci_errno = 0; | |
231 | mci->mci_flags = 0; | |
232 | } | |
233 | } | |
234 | \f/* | |
235 | ** MCI_FLUSH -- flush the entire cache | |
236 | ** | |
237 | ** Parameters: | |
238 | ** doquit -- if TRUE, send QUIT protocol. | |
239 | ** if FALSE, just close the connection. | |
240 | ** allbut -- but leave this one open. | |
241 | ** | |
242 | ** Returns: | |
243 | ** none. | |
244 | */ | |
245 | ||
246 | mci_flush(doquit, allbut) | |
247 | bool doquit; | |
248 | MCI *allbut; | |
249 | { | |
250 | register int i; | |
251 | ||
252 | if (MciCache == NULL) | |
253 | return; | |
254 | ||
255 | for (i = 0; i < MaxMciCache; i++) | |
256 | if (allbut != MciCache[i]) | |
257 | mci_uncache(&MciCache[i], doquit); | |
258 | } | |
259 | \f/* | |
260 | ** MCI_GET -- get information about a particular host | |
261 | */ | |
262 | ||
263 | MCI * | |
264 | mci_get(host, m) | |
265 | char *host; | |
266 | MAILER *m; | |
267 | { | |
268 | register MCI *mci; | |
269 | register STAB *s; | |
270 | ||
271 | #ifdef DAEMON | |
272 | extern SOCKADDR CurHostAddr; | |
273 | ||
274 | /* clear CurHostAddr so we don't get a bogus address with this name */ | |
275 | bzero(&CurHostAddr, sizeof CurHostAddr); | |
d747e748 | 276 | #endif |
6f14531a RG |
277 | |
278 | s = stab(host, ST_MCI + m->m_mno, ST_ENTER); | |
279 | mci = &s->s_mci; | |
280 | mci->mci_host = s->s_name; | |
281 | ||
282 | if (tTd(42, 2)) | |
283 | { | |
284 | printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", | |
285 | host, m->m_name, mci->mci_state, mci->mci_flags, | |
286 | mci->mci_exitstat, mci->mci_errno); | |
287 | } | |
288 | ||
289 | if (mci->mci_state == MCIS_OPEN) | |
290 | { | |
291 | /* poke the connection to see if it's still alive */ | |
292 | smtpprobe(mci); | |
293 | ||
294 | /* reset the stored state in the event of a timeout */ | |
295 | if (mci->mci_state != MCIS_OPEN) | |
296 | { | |
297 | mci->mci_errno = 0; | |
298 | mci->mci_exitstat = EX_OK; | |
299 | mci->mci_state = MCIS_CLOSED; | |
300 | } | |
301 | } | |
302 | ||
303 | return mci; | |
304 | } | |
305 | \f/* | |
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 | ||
d747e748 | 318 | mci_dump(mci, logit) |
6f14531a | 319 | register MCI *mci; |
d747e748 | 320 | bool logit; |
6f14531a | 321 | { |
d747e748 JH |
322 | register char *p; |
323 | char *sep; | |
324 | char buf[1000]; | |
6f14531a RG |
325 | extern char *ctime(); |
326 | ||
d747e748 JH |
327 | sep = logit ? " " : "\n\t"; |
328 | p = buf; | |
329 | sprintf(p, "MCI@%x: ", mci); | |
330 | p += strlen(p); | |
6f14531a RG |
331 | if (mci == NULL) |
332 | { | |
d747e748 JH |
333 | sprintf(p, "NULL"); |
334 | goto printit; | |
6f14531a | 335 | } |
d747e748 | 336 | sprintf(p, "flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", |
3a363396 | 337 | mci->mci_flags, mci->mci_errno, mci->mci_herrno, |
d747e748 JH |
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", | |
3a363396 | 341 | mci->mci_maxsize, |
6f14531a | 342 | mci->mci_phase == NULL ? "NULL" : mci->mci_phase, |
d747e748 JH |
343 | mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, |
344 | sep); | |
345 | p += strlen(p); | |
346 | sprintf(p, "host=%s, lastuse=%s", | |
6f14531a RG |
347 | mci->mci_host == NULL ? "NULL" : mci->mci_host, |
348 | ctime(&mci->mci_lastuse)); | |
d747e748 JH |
349 | printit: |
350 | if (logit) | |
351 | syslog(LOG_INFO, "%s", buf); | |
352 | else | |
353 | printf("%s\n", buf); | |
354 | } | |
355 | \f/* | |
356 | ** MCI_DUMP_ALL -- print the entire MCI cache | |
357 | ** | |
358 | ** Parameters: | |
359 | ** logit -- if set, log the result instead of printing | |
360 | ** to stdout. | |
361 | ** | |
362 | ** Returns: | |
363 | ** none. | |
364 | */ | |
365 | ||
366 | mci_dump_all(logit) | |
367 | bool logit; | |
368 | { | |
369 | register int i; | |
370 | ||
371 | for (i = 0; i < MaxMciCache; i++) | |
372 | mci_dump(MciCache[i], logit); | |
6f14531a | 373 | } |