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.13 (Berkeley) 04/12/94"; 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 /* clear out any expired connections */ 253 mci_scan(NULL); 254 255 if (m->m_mno < 0) 256 syserr("negative mno %d (%s)", m->m_mno, m->m_name); 257 s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 258 mci = &s->s_mci; 259 mci->mci_host = s->s_name; 260 261 if (tTd(42, 2)) 262 { 263 printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", 264 host, m->m_name, mci->mci_state, mci->mci_flags, 265 mci->mci_exitstat, mci->mci_errno); 266 } 267 268 if (mci->mci_state == MCIS_OPEN) 269 { 270 /* poke the connection to see if it's still alive */ 271 smtpprobe(mci); 272 273 /* reset the stored state in the event of a timeout */ 274 if (mci->mci_state != MCIS_OPEN) 275 { 276 mci->mci_errno = 0; 277 mci->mci_exitstat = EX_OK; 278 mci->mci_state = MCIS_CLOSED; 279 } 280 else 281 { 282 /* get peer host address for logging reasons only */ 283 /* (this should really be in the mci struct) */ 284 int socksize = sizeof CurHostAddr; 285 286 (void) getpeername(fileno(mci->mci_in), 287 (struct sockaddr *) &CurHostAddr, &socksize); 288 } 289 } 290 if (mci->mci_state == MCIS_CLOSED) 291 { 292 /* copy out any mailer flags needed in connection state */ 293 if (bitnset(M_7BITS, m->m_flags)) 294 mci->mci_flags |= MCIF_7BIT; 295 } 296 297 return mci; 298 } 299 /* 300 ** MCI_DUMP -- dump the contents of an MCI structure. 301 ** 302 ** Parameters: 303 ** mci -- the MCI structure to dump. 304 ** 305 ** Returns: 306 ** none. 307 ** 308 ** Side Effects: 309 ** none. 310 */ 311 312 mci_dump(mci, logit) 313 register MCI *mci; 314 bool logit; 315 { 316 register char *p; 317 char *sep; 318 char buf[1000]; 319 extern char *ctime(); 320 321 sep = logit ? " " : "\n\t"; 322 p = buf; 323 sprintf(p, "MCI@%x: ", mci); 324 p += strlen(p); 325 if (mci == NULL) 326 { 327 sprintf(p, "NULL"); 328 goto printit; 329 } 330 sprintf(p, "flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 331 mci->mci_flags, mci->mci_errno, mci->mci_herrno, 332 mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep); 333 p += strlen(p); 334 sprintf(p, "maxsize=%ld, phase=%s, mailer=%s,%s", 335 mci->mci_maxsize, 336 mci->mci_phase == NULL ? "NULL" : mci->mci_phase, 337 mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 338 sep); 339 p += strlen(p); 340 sprintf(p, "host=%s, lastuse=%s", 341 mci->mci_host == NULL ? "NULL" : mci->mci_host, 342 ctime(&mci->mci_lastuse)); 343 printit: 344 #ifdef LOG 345 if (logit) 346 syslog(LOG_DEBUG, "%s", buf); 347 else 348 #endif 349 printf("%s\n", buf); 350 } 351 /* 352 ** MCI_DUMP_ALL -- print the entire MCI cache 353 ** 354 ** Parameters: 355 ** logit -- if set, log the result instead of printing 356 ** to stdout. 357 ** 358 ** Returns: 359 ** none. 360 */ 361 362 mci_dump_all(logit) 363 bool logit; 364 { 365 register int i; 366 367 if (MciCache == NULL) 368 return; 369 370 for (i = 0; i < MaxMciCache; i++) 371 mci_dump(MciCache[i], logit); 372 } 373