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 | |
36 | static char sccsid[] = "@(#)mci.c 8.1 (Berkeley) 6/7/93"; | |
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 | ||
107 | *mcislot = mci; | |
108 | mci->mci_flags |= MCIF_CACHED; | |
109 | } | |
110 | \f/* | |
111 | ** MCI_SCAN -- scan the cache, flush junk, and return best slot | |
112 | ** | |
113 | ** Parameters: | |
114 | ** savemci -- never flush this one. Can be null. | |
115 | ** | |
116 | ** Returns: | |
117 | ** The LRU (or empty) slot. | |
118 | */ | |
119 | ||
120 | MCI ** | |
121 | mci_scan(savemci) | |
122 | MCI *savemci; | |
123 | { | |
124 | time_t now; | |
125 | register MCI **bestmci; | |
126 | register MCI *mci; | |
127 | register int i; | |
128 | ||
129 | if (MciCache == NULL) | |
130 | { | |
131 | /* first call */ | |
132 | MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); | |
133 | bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); | |
134 | return (&MciCache[0]); | |
135 | } | |
136 | ||
137 | now = curtime(); | |
138 | bestmci = &MciCache[0]; | |
139 | for (i = 0; i < MaxMciCache; i++) | |
140 | { | |
141 | mci = MciCache[i]; | |
142 | if (mci == NULL || mci->mci_state == MCIS_CLOSED) | |
143 | { | |
144 | bestmci = &MciCache[i]; | |
145 | continue; | |
146 | } | |
147 | if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) | |
148 | { | |
149 | /* connection idle too long -- close it */ | |
150 | bestmci = &MciCache[i]; | |
151 | mci_uncache(bestmci, TRUE); | |
152 | continue; | |
153 | } | |
154 | if (*bestmci == NULL) | |
155 | continue; | |
156 | if (mci->mci_lastuse < (*bestmci)->mci_lastuse) | |
157 | bestmci = &MciCache[i]; | |
158 | } | |
159 | return bestmci; | |
160 | } | |
161 | \f/* | |
162 | ** MCI_UNCACHE -- remove a connection from a slot. | |
163 | ** | |
164 | ** May close a connection. | |
165 | ** | |
166 | ** Parameters: | |
167 | ** mcislot -- the slot to empty. | |
168 | ** doquit -- if TRUE, send QUIT protocol on this connection. | |
169 | ** if FALSE, we are assumed to be in a forked child; | |
170 | ** all we want to do is close the file(s). | |
171 | ** | |
172 | ** Returns: | |
173 | ** none. | |
174 | */ | |
175 | ||
176 | mci_uncache(mcislot, doquit) | |
177 | register MCI **mcislot; | |
178 | bool doquit; | |
179 | { | |
180 | register MCI *mci; | |
181 | extern ENVELOPE BlankEnvelope; | |
182 | ||
183 | mci = *mcislot; | |
184 | if (mci == NULL) | |
185 | return; | |
186 | *mcislot = NULL; | |
187 | ||
188 | if (doquit) | |
189 | { | |
190 | message("Closing connection to %s", mci->mci_host); | |
191 | ||
192 | mci->mci_flags &= ~MCIF_CACHED; | |
193 | ||
194 | /* only uses the envelope to flush the transcript file */ | |
195 | if (mci->mci_state != MCIS_CLOSED) | |
196 | smtpquit(mci->mci_mailer, mci, &BlankEnvelope); | |
197 | #ifdef XLA | |
198 | xla_host_end(mci->mci_host); | |
199 | #endif | |
200 | } | |
201 | else | |
202 | { | |
203 | if (mci->mci_in != NULL) | |
204 | xfclose(mci->mci_in, "mci_uncache", "mci_in"); | |
205 | if (mci->mci_out != NULL) | |
206 | xfclose(mci->mci_out, "mci_uncache", "mci_out"); | |
207 | mci->mci_in = mci->mci_out = NULL; | |
208 | mci->mci_state = MCIS_CLOSED; | |
209 | mci->mci_exitstat = EX_OK; | |
210 | mci->mci_errno = 0; | |
211 | mci->mci_flags = 0; | |
212 | } | |
213 | } | |
214 | \f/* | |
215 | ** MCI_FLUSH -- flush the entire cache | |
216 | ** | |
217 | ** Parameters: | |
218 | ** doquit -- if TRUE, send QUIT protocol. | |
219 | ** if FALSE, just close the connection. | |
220 | ** allbut -- but leave this one open. | |
221 | ** | |
222 | ** Returns: | |
223 | ** none. | |
224 | */ | |
225 | ||
226 | mci_flush(doquit, allbut) | |
227 | bool doquit; | |
228 | MCI *allbut; | |
229 | { | |
230 | register int i; | |
231 | ||
232 | if (MciCache == NULL) | |
233 | return; | |
234 | ||
235 | for (i = 0; i < MaxMciCache; i++) | |
236 | if (allbut != MciCache[i]) | |
237 | mci_uncache(&MciCache[i], doquit); | |
238 | } | |
239 | \f/* | |
240 | ** MCI_GET -- get information about a particular host | |
241 | */ | |
242 | ||
243 | MCI * | |
244 | mci_get(host, m) | |
245 | char *host; | |
246 | MAILER *m; | |
247 | { | |
248 | register MCI *mci; | |
249 | register STAB *s; | |
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 DAEMON | |
257 | ||
258 | s = stab(host, ST_MCI + m->m_mno, ST_ENTER); | |
259 | mci = &s->s_mci; | |
260 | mci->mci_host = s->s_name; | |
261 | ||
262 | if (tTd(42, 2)) | |
263 | { | |
264 | printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", | |
265 | host, m->m_name, mci->mci_state, mci->mci_flags, | |
266 | mci->mci_exitstat, mci->mci_errno); | |
267 | } | |
268 | ||
269 | if (mci->mci_state == MCIS_OPEN) | |
270 | { | |
271 | /* poke the connection to see if it's still alive */ | |
272 | smtpprobe(mci); | |
273 | ||
274 | /* reset the stored state in the event of a timeout */ | |
275 | if (mci->mci_state != MCIS_OPEN) | |
276 | { | |
277 | mci->mci_errno = 0; | |
278 | mci->mci_exitstat = EX_OK; | |
279 | mci->mci_state = MCIS_CLOSED; | |
280 | } | |
281 | } | |
282 | ||
283 | return mci; | |
284 | } | |
285 | \f/* | |
286 | ** MCI_DUMP -- dump the contents of an MCI structure. | |
287 | ** | |
288 | ** Parameters: | |
289 | ** mci -- the MCI structure to dump. | |
290 | ** | |
291 | ** Returns: | |
292 | ** none. | |
293 | ** | |
294 | ** Side Effects: | |
295 | ** none. | |
296 | */ | |
297 | ||
298 | mci_dump(mci) | |
299 | register MCI *mci; | |
300 | { | |
301 | extern char *ctime(); | |
302 | ||
303 | printf("MCI@%x: ", mci); | |
304 | if (mci == NULL) | |
305 | { | |
306 | printf("NULL\n"); | |
307 | return; | |
308 | } | |
309 | printf("flags=%o, errno=%d, exitstat=%d, state=%d, pid=%d, maxsize=%ld\n", | |
310 | mci->mci_flags, mci->mci_errno, mci->mci_exitstat, | |
311 | mci->mci_state, mci->mci_pid, mci->mci_maxsize); | |
312 | printf("\tphase=%s, mailer=%s,\n", | |
313 | mci->mci_phase == NULL ? "NULL" : mci->mci_phase, | |
314 | mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name); | |
315 | printf("\thost=%s, lastuse=%s\n", | |
316 | mci->mci_host == NULL ? "NULL" : mci->mci_host, | |
317 | ctime(&mci->mci_lastuse)); | |
318 | } |