1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)mci.c 6.2 (Berkeley) 01/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); 80 81 *mcislot = mci; 82 mci->mci_flags |= MCIF_CACHED; 83 } 84 /* 85 ** MCI_SCAN -- scan the cache, flush junk, and return best slot 86 ** 87 ** Parameters: 88 ** savemci -- never flush this one. Can be null. 89 ** 90 ** Returns: 91 ** The LRU (or empty) slot. 92 */ 93 94 MCI ** 95 mci_scan(savemci) 96 MCI *savemci; 97 { 98 time_t now; 99 register MCI **bestmci; 100 register MCI *mci; 101 register int i; 102 103 if (MciCache == NULL) 104 { 105 /* first call */ 106 MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); 107 bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); 108 return (&MciCache[0]); 109 } 110 111 now = curtime(); 112 bestmci = &MciCache[0]; 113 for (i = 0; i < MaxMciCache; i++) 114 { 115 mci = MciCache[i]; 116 if (mci == NULL || mci->mci_state == MCIS_CLOSED) 117 { 118 bestmci = &MciCache[i]; 119 continue; 120 } 121 if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) 122 { 123 /* connection idle too long -- close it */ 124 bestmci = &MciCache[i]; 125 mci_uncache(bestmci); 126 continue; 127 } 128 if (*bestmci == NULL) 129 continue; 130 if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 131 bestmci = &MciCache[i]; 132 } 133 return bestmci; 134 } 135 /* 136 ** MCI_UNCACHE -- remove a connection from a slot. 137 ** 138 ** May close a connection. 139 ** 140 ** Parameters: 141 ** mcislot -- the slot to empty. 142 ** 143 ** Returns: 144 ** none. 145 */ 146 147 mci_uncache(mcislot) 148 register MCI **mcislot; 149 { 150 register MCI *mci; 151 extern ENVELOPE BlankEnvelope; 152 153 mci = *mcislot; 154 if (mci == NULL) 155 return; 156 *mcislot = NULL; 157 mci->mci_flags &= ~MCIF_CACHED; 158 159 message(Arpa_Info, "Closing connection to %s", mci->mci_host); 160 161 /* only uses the envelope to flush the transcript file */ 162 if (mci->mci_state != MCIS_CLOSED) 163 smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 164 } 165 /* 166 ** MCI_FLUSH -- flush the entire cache 167 */ 168 169 mci_flush() 170 { 171 register int i; 172 173 if (MciCache == NULL) 174 return; 175 176 for (i = 0; i < MaxMciCache; i++) 177 mci_uncache(&MciCache[i]); 178 } 179 /* 180 ** MCI_GET -- get information about a particular host 181 */ 182 183 MCI * 184 mci_get(host, m) 185 char *host; 186 MAILER *m; 187 { 188 register MCI *mci; 189 register STAB *s; 190 191 s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 192 mci = &s->s_mci; 193 mci->mci_host = s->s_name; 194 195 if (tTd(42, 2)) 196 { 197 printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", 198 host, m->m_name, mci->mci_state, mci->mci_flags, 199 mci->mci_exitstat, mci->mci_errno); 200 } 201 202 /* try poking this to see if it is still usable */ 203 switch (mci->mci_state) 204 { 205 case MCIS_OPEN: 206 smtpnoop(mci); 207 break; 208 } 209 210 return mci; 211 } 212 /* 213 ** MCI_DUMP -- dump the contents of an MCI structure. 214 ** 215 ** Parameters: 216 ** mci -- the MCI structure to dump. 217 ** 218 ** Returns: 219 ** none. 220 ** 221 ** Side Effects: 222 ** none. 223 */ 224 225 mci_dump(mci) 226 register MCI *mci; 227 { 228 extern char *ctime(); 229 230 printf("MCI@%x: ", mci); 231 if (mci == NULL) 232 { 233 printf("NULL\n"); 234 return; 235 } 236 printf("flags=%o, errno=%d, exitstat=%d, state=%d, pid=%d,\n", 237 mci->mci_flags, mci->mci_errno, mci->mci_exitstat, 238 mci->mci_state, mci->mci_pid); 239 printf("\tphase=%s, mailer=%s,\n", 240 mci->mci_phase == NULL ? "NULL" : mci->mci_phase, 241 mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name); 242 printf("\thost=%s, lastuse=%s\n", 243 mci->mci_host == NULL ? "NULL" : mci->mci_host, 244 ctime(&mci->mci_lastuse)); 245 } 246