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