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 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)mci.c 8.2 (Berkeley) 07/11/93"; 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 ** 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) 55 register MCI *mci; 56 { 57 register MCI **mcislot; 58 extern MCI **mci_scan(); 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) 79 mci_uncache(mcislot, TRUE); 80 81 *mcislot = mci; 82 mci->mci_flags |= MCIF_CACHED; 83 } 84 /* 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 94 MCI ** 95 mci_scan(savemci) 96 MCI *savemci; 97 { 98 time_t now; 99 register MCI **bestmci; 100 register MCI *mci; 101 register int i; 102 103 if (MciCache == NULL) 104 { 105 /* first call */ 106 MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); 107 bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); 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]; 125 mci_uncache(bestmci, TRUE); 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 /* 136 ** MCI_UNCACHE -- remove a connection from a slot. 137 ** 138 ** May close a connection. 139 ** 140 ** Parameters: 141 ** mcislot -- the slot to empty. 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). 145 ** 146 ** Returns: 147 ** none. 148 */ 149 150 mci_uncache(mcislot, doquit) 151 register MCI **mcislot; 152 bool doquit; 153 { 154 register MCI *mci; 155 extern ENVELOPE BlankEnvelope; 156 157 mci = *mcislot; 158 if (mci == NULL) 159 return; 160 *mcislot = NULL; 161 162 if (doquit) 163 { 164 message("Closing connection to %s", mci->mci_host); 165 166 mci->mci_flags &= ~MCIF_CACHED; 167 168 /* only uses the envelope to flush the transcript file */ 169 if (mci->mci_state != MCIS_CLOSED) 170 smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 171 #ifdef XLA 172 xla_host_end(mci->mci_host); 173 #endif 174 } 175 else 176 { 177 if (mci->mci_in != NULL) 178 xfclose(mci->mci_in, "mci_uncache", "mci_in"); 179 if (mci->mci_out != NULL) 180 xfclose(mci->mci_out, "mci_uncache", "mci_out"); 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 } 187 } 188 /* 189 ** MCI_FLUSH -- flush the entire cache 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. 198 */ 199 200 mci_flush(doquit, allbut) 201 bool doquit; 202 MCI *allbut; 203 { 204 register int i; 205 206 if (MciCache == NULL) 207 return; 208 209 for (i = 0; i < MaxMciCache; i++) 210 if (allbut != MciCache[i]) 211 mci_uncache(&MciCache[i], doquit); 212 } 213 /* 214 ** MCI_GET -- get information about a particular host 215 */ 216 217 MCI * 218 mci_get(host, m) 219 char *host; 220 MAILER *m; 221 { 222 register MCI *mci; 223 register STAB *s; 224 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); 230 #endif DAEMON 231 232 s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 233 mci = &s->s_mci; 234 mci->mci_host = s->s_name; 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 243 if (mci->mci_state == MCIS_OPEN) 244 { 245 /* poke the connection to see if it's still alive */ 246 smtpprobe(mci); 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 } 255 } 256 257 return mci; 258 } 259 /* 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 } 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, 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 } 294