1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)mci.c 6.8 (Berkeley) 04/01/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 } 172 else 173 { 174 if (mci->mci_in != NULL) 175 xfclose(mci->mci_in, "mci_uncache", "mci_in"); 176 if (mci->mci_out != NULL) 177 xfclose(mci->mci_out, "mci_uncache", "mci_out"); 178 mci->mci_in = mci->mci_out = NULL; 179 mci->mci_state = MCIS_CLOSED; 180 mci->mci_exitstat = EX_OK; 181 mci->mci_errno = 0; 182 mci->mci_flags = 0; 183 } 184 } 185 /* 186 ** MCI_FLUSH -- flush the entire cache 187 ** 188 ** Parameters: 189 ** doquit -- if TRUE, send QUIT protocol. 190 ** if FALSE, just close the connection. 191 ** allbut -- but leave this one open. 192 ** 193 ** Returns: 194 ** none. 195 */ 196 197 mci_flush(doquit, allbut) 198 bool doquit; 199 MCI *allbut; 200 { 201 register int i; 202 203 if (MciCache == NULL) 204 return; 205 206 for (i = 0; i < MaxMciCache; i++) 207 if (allbut != MciCache[i]) 208 mci_uncache(&MciCache[i], doquit); 209 } 210 /* 211 ** MCI_GET -- get information about a particular host 212 */ 213 214 MCI * 215 mci_get(host, m) 216 char *host; 217 MAILER *m; 218 { 219 register MCI *mci; 220 register STAB *s; 221 222 #ifdef DAEMON 223 extern SOCKADDR CurHostAddr; 224 225 /* clear CurHostAddr so we don't get a bogus address with this name */ 226 bzero(&CurHostAddr, sizeof CurHostAddr); 227 #endif DAEMON 228 229 s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 230 mci = &s->s_mci; 231 mci->mci_host = s->s_name; 232 233 if (tTd(42, 2)) 234 { 235 printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", 236 host, m->m_name, mci->mci_state, mci->mci_flags, 237 mci->mci_exitstat, mci->mci_errno); 238 } 239 240 if (mci->mci_state == MCIS_OPEN) 241 { 242 /* poke the connection to see if it's still alive */ 243 smtpprobe(mci); 244 245 /* reset the stored state in the event of a timeout */ 246 if (mci->mci_state != MCIS_OPEN) 247 { 248 mci->mci_errno = 0; 249 mci->mci_exitstat = EX_OK; 250 mci->mci_state = MCIS_CLOSED; 251 } 252 } 253 254 return mci; 255 } 256 /* 257 ** MCI_DUMP -- dump the contents of an MCI structure. 258 ** 259 ** Parameters: 260 ** mci -- the MCI structure to dump. 261 ** 262 ** Returns: 263 ** none. 264 ** 265 ** Side Effects: 266 ** none. 267 */ 268 269 mci_dump(mci) 270 register MCI *mci; 271 { 272 extern char *ctime(); 273 274 printf("MCI@%x: ", mci); 275 if (mci == NULL) 276 { 277 printf("NULL\n"); 278 return; 279 } 280 printf("flags=%o, errno=%d, exitstat=%d, state=%d, pid=%d,\n", 281 mci->mci_flags, mci->mci_errno, mci->mci_exitstat, 282 mci->mci_state, mci->mci_pid); 283 printf("\tphase=%s, mailer=%s,\n", 284 mci->mci_phase == NULL ? "NULL" : mci->mci_phase, 285 mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name); 286 printf("\thost=%s, lastuse=%s\n", 287 mci->mci_host == NULL ? "NULL" : mci->mci_host, 288 ctime(&mci->mci_lastuse)); 289 } 290