Commit | Line | Data |
---|---|---|
eb980753 EA |
1 | /* |
2 | * Copyright (c) 1983 Eric P. Allman | |
eb0bafab KB |
3 | * Copyright (c) 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
eb980753 EA |
5 | * |
6 | * %sccs.include.redist.c% | |
7 | */ | |
8 | ||
9 | #ifndef lint | |
e6cb9fc4 | 10 | static char sccsid[] = "@(#)mci.c 8.3 (Berkeley) %G%"; |
eb980753 EA |
11 | #endif /* not lint */ |
12 | ||
13 | #include "sendmail.h" | |
14 | ||
15 | /* | |
f2e44ded EA |
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 | \f/* | |
eb980753 EA |
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) | |
f2e44ded | 55 | register MCI *mci; |
eb980753 | 56 | { |
f2e44ded EA |
57 | register MCI **mcislot; |
58 | extern MCI **mci_scan(); | |
eb980753 EA |
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) | |
1c7897ef | 79 | mci_uncache(mcislot, TRUE); |
eb980753 EA |
80 | |
81 | *mcislot = mci; | |
82 | mci->mci_flags |= MCIF_CACHED; | |
83 | } | |
84 | \f/* | |
85 | ** MCI_SCAN -- scan the cache, flush junk, and return best slot | |
86 | ** | |
87 | ** Parameters: | |
88 | ** savemci -- never flush this one. Can be null. | |
89 | ** | |
90 | ** Returns: | |
91 | ** The LRU (or empty) slot. | |
92 | */ | |
93 | ||
f2e44ded | 94 | MCI ** |
eb980753 | 95 | mci_scan(savemci) |
f2e44ded | 96 | MCI *savemci; |
eb980753 EA |
97 | { |
98 | time_t now; | |
f2e44ded EA |
99 | register MCI **bestmci; |
100 | register MCI *mci; | |
eb980753 EA |
101 | register int i; |
102 | ||
103 | if (MciCache == NULL) | |
104 | { | |
105 | /* first call */ | |
f2e44ded | 106 | MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); |
5031c0bb | 107 | bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); |
eb980753 EA |
108 | return (&MciCache[0]); |
109 | } | |
110 | ||
111 | now = curtime(); | |
112 | bestmci = &MciCache[0]; | |
113 | for (i = 0; i < MaxMciCache; i++) | |
114 | { | |
115 | mci = MciCache[i]; | |
116 | if (mci == NULL || mci->mci_state == MCIS_CLOSED) | |
117 | { | |
118 | bestmci = &MciCache[i]; | |
119 | continue; | |
120 | } | |
121 | if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) | |
122 | { | |
123 | /* connection idle too long -- close it */ | |
124 | bestmci = &MciCache[i]; | |
1c7897ef | 125 | mci_uncache(bestmci, TRUE); |
eb980753 EA |
126 | continue; |
127 | } | |
128 | if (*bestmci == NULL) | |
129 | continue; | |
130 | if (mci->mci_lastuse < (*bestmci)->mci_lastuse) | |
131 | bestmci = &MciCache[i]; | |
132 | } | |
133 | return bestmci; | |
134 | } | |
135 | \f/* | |
136 | ** MCI_UNCACHE -- remove a connection from a slot. | |
137 | ** | |
138 | ** May close a connection. | |
139 | ** | |
140 | ** Parameters: | |
141 | ** mcislot -- the slot to empty. | |
1c7897ef EA |
142 | ** doquit -- if TRUE, send QUIT protocol on this connection. |
143 | ** if FALSE, we are assumed to be in a forked child; | |
144 | ** all we want to do is close the file(s). | |
eb980753 EA |
145 | ** |
146 | ** Returns: | |
147 | ** none. | |
148 | */ | |
149 | ||
1c7897ef | 150 | mci_uncache(mcislot, doquit) |
f2e44ded | 151 | register MCI **mcislot; |
1c7897ef | 152 | bool doquit; |
eb980753 | 153 | { |
f2e44ded | 154 | register MCI *mci; |
0cf5a7ba | 155 | extern ENVELOPE BlankEnvelope; |
eb980753 EA |
156 | |
157 | mci = *mcislot; | |
158 | if (mci == NULL) | |
159 | return; | |
160 | *mcislot = NULL; | |
eb980753 | 161 | |
1c7897ef EA |
162 | if (doquit) |
163 | { | |
b6edea3d | 164 | message("Closing connection to %s", mci->mci_host); |
1c7897ef EA |
165 | |
166 | mci->mci_flags &= ~MCIF_CACHED; | |
4e20c4d2 | 167 | |
1c7897ef EA |
168 | /* only uses the envelope to flush the transcript file */ |
169 | if (mci->mci_state != MCIS_CLOSED) | |
170 | smtpquit(mci->mci_mailer, mci, &BlankEnvelope); | |
b4f81c5d EA |
171 | #ifdef XLA |
172 | xla_host_end(mci->mci_host); | |
173 | #endif | |
1c7897ef EA |
174 | } |
175 | else | |
176 | { | |
177 | if (mci->mci_in != NULL) | |
bc854e30 | 178 | xfclose(mci->mci_in, "mci_uncache", "mci_in"); |
1c7897ef | 179 | if (mci->mci_out != NULL) |
bc854e30 | 180 | xfclose(mci->mci_out, "mci_uncache", "mci_out"); |
1c7897ef EA |
181 | mci->mci_in = mci->mci_out = NULL; |
182 | mci->mci_state = MCIS_CLOSED; | |
183 | mci->mci_exitstat = EX_OK; | |
184 | mci->mci_errno = 0; | |
185 | mci->mci_flags = 0; | |
186 | } | |
eb980753 EA |
187 | } |
188 | \f/* | |
189 | ** MCI_FLUSH -- flush the entire cache | |
1c7897ef EA |
190 | ** |
191 | ** Parameters: | |
192 | ** doquit -- if TRUE, send QUIT protocol. | |
193 | ** if FALSE, just close the connection. | |
194 | ** allbut -- but leave this one open. | |
195 | ** | |
196 | ** Returns: | |
197 | ** none. | |
eb980753 EA |
198 | */ |
199 | ||
1c7897ef EA |
200 | mci_flush(doquit, allbut) |
201 | bool doquit; | |
202 | MCI *allbut; | |
eb980753 EA |
203 | { |
204 | register int i; | |
205 | ||
206 | if (MciCache == NULL) | |
207 | return; | |
208 | ||
209 | for (i = 0; i < MaxMciCache; i++) | |
1c7897ef EA |
210 | if (allbut != MciCache[i]) |
211 | mci_uncache(&MciCache[i], doquit); | |
eb980753 EA |
212 | } |
213 | \f/* | |
214 | ** MCI_GET -- get information about a particular host | |
215 | */ | |
216 | ||
f2e44ded | 217 | MCI * |
eb980753 EA |
218 | mci_get(host, m) |
219 | char *host; | |
220 | MAILER *m; | |
221 | { | |
f2e44ded | 222 | register MCI *mci; |
4e20c4d2 | 223 | register STAB *s; |
f2e44ded | 224 | |
3f2dcbd6 EA |
225 | #ifdef DAEMON |
226 | extern SOCKADDR CurHostAddr; | |
227 | ||
228 | /* clear CurHostAddr so we don't get a bogus address with this name */ | |
229 | bzero(&CurHostAddr, sizeof CurHostAddr); | |
e6cb9fc4 | 230 | #endif |
3f2dcbd6 | 231 | |
4e20c4d2 EA |
232 | s = stab(host, ST_MCI + m->m_mno, ST_ENTER); |
233 | mci = &s->s_mci; | |
234 | mci->mci_host = s->s_name; | |
f2e44ded EA |
235 | |
236 | if (tTd(42, 2)) | |
237 | { | |
238 | printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", | |
239 | host, m->m_name, mci->mci_state, mci->mci_flags, | |
240 | mci->mci_exitstat, mci->mci_errno); | |
241 | } | |
242 | ||
aa102c71 | 243 | if (mci->mci_state == MCIS_OPEN) |
f2e44ded | 244 | { |
aa102c71 | 245 | /* poke the connection to see if it's still alive */ |
c428b1b2 | 246 | smtpprobe(mci); |
aa102c71 EA |
247 | |
248 | /* reset the stored state in the event of a timeout */ | |
249 | if (mci->mci_state != MCIS_OPEN) | |
250 | { | |
251 | mci->mci_errno = 0; | |
252 | mci->mci_exitstat = EX_OK; | |
253 | mci->mci_state = MCIS_CLOSED; | |
254 | } | |
f2e44ded EA |
255 | } |
256 | ||
257 | return mci; | |
eb980753 | 258 | } |
fdd3df4f EA |
259 | \f/* |
260 | ** MCI_DUMP -- dump the contents of an MCI structure. | |
261 | ** | |
262 | ** Parameters: | |
263 | ** mci -- the MCI structure to dump. | |
264 | ** | |
265 | ** Returns: | |
266 | ** none. | |
267 | ** | |
268 | ** Side Effects: | |
269 | ** none. | |
270 | */ | |
271 | ||
272 | mci_dump(mci) | |
273 | register MCI *mci; | |
274 | { | |
275 | extern char *ctime(); | |
276 | ||
277 | printf("MCI@%x: ", mci); | |
278 | if (mci == NULL) | |
279 | { | |
280 | printf("NULL\n"); | |
281 | return; | |
282 | } | |
8e5c6745 EA |
283 | printf("flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,\n", |
284 | mci->mci_flags, mci->mci_errno, mci->mci_herrno, | |
285 | mci->mci_exitstat, mci->mci_state, mci->mci_pid); | |
286 | printf("\tmaxsize=%ld, phase=%s, mailer=%s,\n", | |
287 | mci->mci_maxsize, | |
fdd3df4f EA |
288 | mci->mci_phase == NULL ? "NULL" : mci->mci_phase, |
289 | mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name); | |
290 | printf("\thost=%s, lastuse=%s\n", | |
291 | mci->mci_host == NULL ? "NULL" : mci->mci_host, | |
292 | ctime(&mci->mci_lastuse)); | |
293 | } |