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.5 (Berkeley) 10/21/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 if (tTd(42, 5)) 82 printf("mci_cache: caching %x (%s) in slot %d\n", 83 mci, mci->mci_host, mcislot - MciCache); 84 #ifdef LOG 85 if (tTd(91, 100)) 86 syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d", 87 CurEnv->e_id, mci, mci->mci_host, mcislot - MciCache); 88 #endif 89 90 *mcislot = mci; 91 mci->mci_flags |= MCIF_CACHED; 92 } 93 /* 94 ** MCI_SCAN -- scan the cache, flush junk, and return best slot 95 ** 96 ** Parameters: 97 ** savemci -- never flush this one. Can be null. 98 ** 99 ** Returns: 100 ** The LRU (or empty) slot. 101 */ 102 103 MCI ** 104 mci_scan(savemci) 105 MCI *savemci; 106 { 107 time_t now; 108 register MCI **bestmci; 109 register MCI *mci; 110 register int i; 111 112 if (MciCache == NULL) 113 { 114 /* first call */ 115 MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); 116 bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); 117 return (&MciCache[0]); 118 } 119 120 now = curtime(); 121 bestmci = &MciCache[0]; 122 for (i = 0; i < MaxMciCache; i++) 123 { 124 mci = MciCache[i]; 125 if (mci == NULL || mci->mci_state == MCIS_CLOSED) 126 { 127 bestmci = &MciCache[i]; 128 continue; 129 } 130 if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) 131 { 132 /* connection idle too long -- close it */ 133 bestmci = &MciCache[i]; 134 mci_uncache(bestmci, TRUE); 135 continue; 136 } 137 if (*bestmci == NULL) 138 continue; 139 if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 140 bestmci = &MciCache[i]; 141 } 142 return bestmci; 143 } 144 /* 145 ** MCI_UNCACHE -- remove a connection from a slot. 146 ** 147 ** May close a connection. 148 ** 149 ** Parameters: 150 ** mcislot -- the slot to empty. 151 ** doquit -- if TRUE, send QUIT protocol on this connection. 152 ** if FALSE, we are assumed to be in a forked child; 153 ** all we want to do is close the file(s). 154 ** 155 ** Returns: 156 ** none. 157 */ 158 159 mci_uncache(mcislot, doquit) 160 register MCI **mcislot; 161 bool doquit; 162 { 163 register MCI *mci; 164 extern ENVELOPE BlankEnvelope; 165 166 mci = *mcislot; 167 if (mci == NULL) 168 return; 169 *mcislot = NULL; 170 171 if (tTd(42, 5)) 172 printf("mci_uncache: uncaching %x (%s) from slot %d (%d)\n", 173 mci, mci->mci_host, mcislot - MciCache, doquit); 174 #ifdef LOG 175 if (tTd(91, 100)) 176 syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)", 177 CurEnv->e_id, mci, mci->mci_host, mcislot - MciCache, doquit); 178 #endif 179 180 if (doquit) 181 { 182 message("Closing connection to %s", mci->mci_host); 183 184 mci->mci_flags &= ~MCIF_CACHED; 185 186 /* only uses the envelope to flush the transcript file */ 187 if (mci->mci_state != MCIS_CLOSED) 188 smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 189 #ifdef XLA 190 xla_host_end(mci->mci_host); 191 #endif 192 } 193 else 194 { 195 if (mci->mci_in != NULL) 196 xfclose(mci->mci_in, "mci_uncache", "mci_in"); 197 if (mci->mci_out != NULL) 198 xfclose(mci->mci_out, "mci_uncache", "mci_out"); 199 mci->mci_in = mci->mci_out = NULL; 200 mci->mci_state = MCIS_CLOSED; 201 mci->mci_exitstat = EX_OK; 202 mci->mci_errno = 0; 203 mci->mci_flags = 0; 204 } 205 } 206 /* 207 ** MCI_FLUSH -- flush the entire cache 208 ** 209 ** Parameters: 210 ** doquit -- if TRUE, send QUIT protocol. 211 ** if FALSE, just close the connection. 212 ** allbut -- but leave this one open. 213 ** 214 ** Returns: 215 ** none. 216 */ 217 218 mci_flush(doquit, allbut) 219 bool doquit; 220 MCI *allbut; 221 { 222 register int i; 223 224 if (MciCache == NULL) 225 return; 226 227 for (i = 0; i < MaxMciCache; i++) 228 if (allbut != MciCache[i]) 229 mci_uncache(&MciCache[i], doquit); 230 } 231 /* 232 ** MCI_GET -- get information about a particular host 233 */ 234 235 MCI * 236 mci_get(host, m) 237 char *host; 238 MAILER *m; 239 { 240 register MCI *mci; 241 register STAB *s; 242 243 #ifdef DAEMON 244 extern SOCKADDR CurHostAddr; 245 246 /* clear CurHostAddr so we don't get a bogus address with this name */ 247 bzero(&CurHostAddr, sizeof CurHostAddr); 248 #endif 249 250 s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 251 mci = &s->s_mci; 252 mci->mci_host = s->s_name; 253 254 if (tTd(42, 2)) 255 { 256 printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", 257 host, m->m_name, mci->mci_state, mci->mci_flags, 258 mci->mci_exitstat, mci->mci_errno); 259 } 260 261 if (mci->mci_state == MCIS_OPEN) 262 { 263 /* poke the connection to see if it's still alive */ 264 smtpprobe(mci); 265 266 /* reset the stored state in the event of a timeout */ 267 if (mci->mci_state != MCIS_OPEN) 268 { 269 mci->mci_errno = 0; 270 mci->mci_exitstat = EX_OK; 271 mci->mci_state = MCIS_CLOSED; 272 } 273 } 274 275 return mci; 276 } 277 /* 278 ** MCI_DUMP -- dump the contents of an MCI structure. 279 ** 280 ** Parameters: 281 ** mci -- the MCI structure to dump. 282 ** 283 ** Returns: 284 ** none. 285 ** 286 ** Side Effects: 287 ** none. 288 */ 289 290 mci_dump(mci, logit) 291 register MCI *mci; 292 bool logit; 293 { 294 register char *p; 295 char *sep; 296 char buf[1000]; 297 extern char *ctime(); 298 299 sep = logit ? " " : "\n\t"; 300 p = buf; 301 sprintf(p, "MCI@%x: ", mci); 302 p += strlen(p); 303 if (mci == NULL) 304 { 305 sprintf(p, "NULL"); 306 goto printit; 307 } 308 sprintf(p, "flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 309 mci->mci_flags, mci->mci_errno, mci->mci_herrno, 310 mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep); 311 p += strlen(p); 312 sprintf(p, "maxsize=%ld, phase=%s, mailer=%s,%s", 313 mci->mci_maxsize, 314 mci->mci_phase == NULL ? "NULL" : mci->mci_phase, 315 mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 316 sep); 317 p += strlen(p); 318 sprintf(p, "host=%s, lastuse=%s", 319 mci->mci_host == NULL ? "NULL" : mci->mci_host, 320 ctime(&mci->mci_lastuse)); 321 printit: 322 if (logit) 323 syslog(LOG_INFO, "%s", buf); 324 else 325 printf("%s\n", buf); 326 } 327 /* 328 ** MCI_DUMP_ALL -- print the entire MCI cache 329 ** 330 ** Parameters: 331 ** logit -- if set, log the result instead of printing 332 ** to stdout. 333 ** 334 ** Returns: 335 ** none. 336 */ 337 338 mci_dump_all(logit) 339 bool logit; 340 { 341 register int i; 342 343 for (i = 0; i < MaxMciCache; i++) 344 mci_dump(MciCache[i], logit); 345 } 346