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.6 (Berkeley) 03/16/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 s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 223 mci = &s->s_mci; 224 mci->mci_host = s->s_name; 225 226 if (tTd(42, 2)) 227 { 228 printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", 229 host, m->m_name, mci->mci_state, mci->mci_flags, 230 mci->mci_exitstat, mci->mci_errno); 231 } 232 233 if (mci->mci_state == MCIS_OPEN) 234 { 235 /* poke the connection to see if it's still alive */ 236 smtpnoop(mci); 237 238 /* reset the stored state in the event of a timeout */ 239 if (mci->mci_state != MCIS_OPEN) 240 { 241 mci->mci_errno = 0; 242 mci->mci_exitstat = EX_OK; 243 mci->mci_state = MCIS_CLOSED; 244 } 245 } 246 247 return mci; 248 } 249 /* 250 ** MCI_DUMP -- dump the contents of an MCI structure. 251 ** 252 ** Parameters: 253 ** mci -- the MCI structure to dump. 254 ** 255 ** Returns: 256 ** none. 257 ** 258 ** Side Effects: 259 ** none. 260 */ 261 262 mci_dump(mci) 263 register MCI *mci; 264 { 265 extern char *ctime(); 266 267 printf("MCI@%x: ", mci); 268 if (mci == NULL) 269 { 270 printf("NULL\n"); 271 return; 272 } 273 printf("flags=%o, errno=%d, exitstat=%d, state=%d, pid=%d,\n", 274 mci->mci_flags, mci->mci_errno, mci->mci_exitstat, 275 mci->mci_state, mci->mci_pid); 276 printf("\tphase=%s, mailer=%s,\n", 277 mci->mci_phase == NULL ? "NULL" : mci->mci_phase, 278 mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name); 279 printf("\thost=%s, lastuse=%s\n", 280 mci->mci_host == NULL ? "NULL" : mci->mci_host, 281 ctime(&mci->mci_lastuse)); 282 } 283