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