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