154963Seric /* 254963Seric * Copyright (c) 1983 Eric P. Allman 354963Seric * Copyright (c) 1988 Regents of the University of California. 454963Seric * All rights reserved. 554963Seric * 654963Seric * %sccs.include.redist.c% 754963Seric */ 854963Seric 954963Seric #ifndef lint 10*58680Seric static char sccsid[] = "@(#)mci.c 6.6 (Berkeley) 03/16/93"; 1154963Seric #endif /* not lint */ 1254963Seric 1354963Seric #include "sendmail.h" 1454963Seric 1554963Seric /* 1654967Seric ** Mail Connection Information (MCI) Caching Module. 1754967Seric ** 1854967Seric ** There are actually two separate things cached. The first is 1954967Seric ** the set of all open connections -- these are stored in a 2054967Seric ** (small) list. The second is stored in the symbol table; it 2154967Seric ** has the overall status for all hosts, whether or not there 2254967Seric ** is a connection open currently. 2354967Seric ** 2454967Seric ** There should never be too many connections open (since this 2554967Seric ** could flood the socket table), nor should a connection be 2654967Seric ** allowed to sit idly for too long. 2754967Seric ** 2854967Seric ** MaxMciCache is the maximum number of open connections that 2954967Seric ** will be supported. 3054967Seric ** 3154967Seric ** MciCacheTimeout is the time (in seconds) that a connection 3254967Seric ** is permitted to survive without activity. 3354967Seric ** 3454967Seric ** We actually try any cached connections by sending a NOOP 3554967Seric ** before we use them; if the NOOP fails we close down the 3654967Seric ** connection and reopen it. Note that this means that a 3754967Seric ** server SMTP that doesn't support NOOP will hose the 3854967Seric ** algorithm -- but that doesn't seem too likely. 3954967Seric */ 4054967Seric 4154967Seric MCI **MciCache; /* the open connection cache */ 4254967Seric /* 4354963Seric ** MCI_CACHE -- enter a connection structure into the open connection cache 4454963Seric ** 4554963Seric ** This may cause something else to be flushed. 4654963Seric ** 4754963Seric ** Parameters: 4854963Seric ** mci -- the connection to cache. 4954963Seric ** 5054963Seric ** Returns: 5154963Seric ** none. 5254963Seric */ 5354963Seric 5454963Seric mci_cache(mci) 5554967Seric register MCI *mci; 5654963Seric { 5754967Seric register MCI **mcislot; 5854967Seric extern MCI **mci_scan(); 5954963Seric 6054963Seric if (MaxMciCache <= 0) 6154963Seric { 6254963Seric /* we don't support caching */ 6354963Seric return; 6454963Seric } 6554963Seric 6654963Seric /* 6754963Seric ** Find the best slot. This may cause expired connections 6854963Seric ** to be closed. 6954963Seric */ 7054963Seric 7154963Seric mcislot = mci_scan(mci); 7254963Seric 7354963Seric /* if this is already cached, we are done */ 7454963Seric if (bitset(MCIF_CACHED, mci->mci_flags)) 7554963Seric return; 7654963Seric 7754963Seric /* otherwise we may have to clear the slot */ 7854963Seric if (*mcislot != NULL) 7958082Seric mci_uncache(mcislot, TRUE); 8054963Seric 8154963Seric *mcislot = mci; 8254963Seric mci->mci_flags |= MCIF_CACHED; 8354963Seric } 8454963Seric /* 8554963Seric ** MCI_SCAN -- scan the cache, flush junk, and return best slot 8654963Seric ** 8754963Seric ** Parameters: 8854963Seric ** savemci -- never flush this one. Can be null. 8954963Seric ** 9054963Seric ** Returns: 9154963Seric ** The LRU (or empty) slot. 9254963Seric */ 9354963Seric 9454967Seric MCI ** 9554963Seric mci_scan(savemci) 9654967Seric MCI *savemci; 9754963Seric { 9854963Seric time_t now; 9954967Seric register MCI **bestmci; 10054967Seric register MCI *mci; 10154963Seric register int i; 10254963Seric 10354963Seric if (MciCache == NULL) 10454963Seric { 10554963Seric /* first call */ 10654967Seric MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); 10756215Seric bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); 10854963Seric return (&MciCache[0]); 10954963Seric } 11054963Seric 11154963Seric now = curtime(); 11254963Seric bestmci = &MciCache[0]; 11354963Seric for (i = 0; i < MaxMciCache; i++) 11454963Seric { 11554963Seric mci = MciCache[i]; 11654963Seric if (mci == NULL || mci->mci_state == MCIS_CLOSED) 11754963Seric { 11854963Seric bestmci = &MciCache[i]; 11954963Seric continue; 12054963Seric } 12154963Seric if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) 12254963Seric { 12354963Seric /* connection idle too long -- close it */ 12454963Seric bestmci = &MciCache[i]; 12558082Seric mci_uncache(bestmci, TRUE); 12654963Seric continue; 12754963Seric } 12854963Seric if (*bestmci == NULL) 12954963Seric continue; 13054963Seric if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 13154963Seric bestmci = &MciCache[i]; 13254963Seric } 13354963Seric return bestmci; 13454963Seric } 13554963Seric /* 13654963Seric ** MCI_UNCACHE -- remove a connection from a slot. 13754963Seric ** 13854963Seric ** May close a connection. 13954963Seric ** 14054963Seric ** Parameters: 14154963Seric ** mcislot -- the slot to empty. 14258082Seric ** doquit -- if TRUE, send QUIT protocol on this connection. 14358082Seric ** if FALSE, we are assumed to be in a forked child; 14458082Seric ** all we want to do is close the file(s). 14554963Seric ** 14654963Seric ** Returns: 14754963Seric ** none. 14854963Seric */ 14954963Seric 15058082Seric mci_uncache(mcislot, doquit) 15154967Seric register MCI **mcislot; 15258082Seric bool doquit; 15354963Seric { 15454967Seric register MCI *mci; 15555020Seric extern ENVELOPE BlankEnvelope; 15654963Seric 15754963Seric mci = *mcislot; 15854963Seric if (mci == NULL) 15954963Seric return; 16054963Seric *mcislot = NULL; 16154963Seric 16258082Seric if (doquit) 16358082Seric { 16458151Seric message("Closing connection to %s", mci->mci_host); 16555467Seric 16658082Seric mci->mci_flags &= ~MCIF_CACHED; 16758082Seric 16858082Seric /* only uses the envelope to flush the transcript file */ 16958082Seric if (mci->mci_state != MCIS_CLOSED) 17058082Seric smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 17158082Seric } 17258082Seric else 17358082Seric { 17458082Seric if (mci->mci_in != NULL) 175*58680Seric xfclose(mci->mci_in, "mci_uncache", "mci_in"); 17658082Seric if (mci->mci_out != NULL) 177*58680Seric xfclose(mci->mci_out, "mci_uncache", "mci_out"); 17858082Seric mci->mci_in = mci->mci_out = NULL; 17958082Seric mci->mci_state = MCIS_CLOSED; 18058082Seric mci->mci_exitstat = EX_OK; 18158082Seric mci->mci_errno = 0; 18258082Seric mci->mci_flags = 0; 18358082Seric } 18454963Seric } 18554963Seric /* 18654963Seric ** MCI_FLUSH -- flush the entire cache 18758082Seric ** 18858082Seric ** Parameters: 18958082Seric ** doquit -- if TRUE, send QUIT protocol. 19058082Seric ** if FALSE, just close the connection. 19158082Seric ** allbut -- but leave this one open. 19258082Seric ** 19358082Seric ** Returns: 19458082Seric ** none. 19554963Seric */ 19654963Seric 19758082Seric mci_flush(doquit, allbut) 19858082Seric bool doquit; 19958082Seric MCI *allbut; 20054963Seric { 20154963Seric register int i; 20254963Seric 20354963Seric if (MciCache == NULL) 20454963Seric return; 20554963Seric 20654963Seric for (i = 0; i < MaxMciCache; i++) 20758082Seric if (allbut != MciCache[i]) 20858082Seric mci_uncache(&MciCache[i], doquit); 20954963Seric } 21054963Seric /* 21154963Seric ** MCI_GET -- get information about a particular host 21254963Seric */ 21354963Seric 21454967Seric MCI * 21554963Seric mci_get(host, m) 21654963Seric char *host; 21754963Seric MAILER *m; 21854963Seric { 21954967Seric register MCI *mci; 22055467Seric register STAB *s; 22154967Seric 22255467Seric s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 22355467Seric mci = &s->s_mci; 22455467Seric mci->mci_host = s->s_name; 22554967Seric 22654967Seric if (tTd(42, 2)) 22754967Seric { 22854967Seric printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", 22954967Seric host, m->m_name, mci->mci_state, mci->mci_flags, 23054967Seric mci->mci_exitstat, mci->mci_errno); 23154967Seric } 23254967Seric 23357977Seric if (mci->mci_state == MCIS_OPEN) 23454967Seric { 23557977Seric /* poke the connection to see if it's still alive */ 23654967Seric smtpnoop(mci); 23757977Seric 23857977Seric /* reset the stored state in the event of a timeout */ 23957977Seric if (mci->mci_state != MCIS_OPEN) 24057977Seric { 24157977Seric mci->mci_errno = 0; 24257977Seric mci->mci_exitstat = EX_OK; 24357977Seric mci->mci_state = MCIS_CLOSED; 24457977Seric } 24554967Seric } 24654967Seric 24754967Seric return mci; 24854963Seric } 24957380Seric /* 25057380Seric ** MCI_DUMP -- dump the contents of an MCI structure. 25157380Seric ** 25257380Seric ** Parameters: 25357380Seric ** mci -- the MCI structure to dump. 25457380Seric ** 25557380Seric ** Returns: 25657380Seric ** none. 25757380Seric ** 25857380Seric ** Side Effects: 25957380Seric ** none. 26057380Seric */ 26157380Seric 26257380Seric mci_dump(mci) 26357380Seric register MCI *mci; 26457380Seric { 26557380Seric extern char *ctime(); 26657380Seric 26757380Seric printf("MCI@%x: ", mci); 26857380Seric if (mci == NULL) 26957380Seric { 27057380Seric printf("NULL\n"); 27157380Seric return; 27257380Seric } 27357380Seric printf("flags=%o, errno=%d, exitstat=%d, state=%d, pid=%d,\n", 27457380Seric mci->mci_flags, mci->mci_errno, mci->mci_exitstat, 27557380Seric mci->mci_state, mci->mci_pid); 27657380Seric printf("\tphase=%s, mailer=%s,\n", 27757380Seric mci->mci_phase == NULL ? "NULL" : mci->mci_phase, 27857380Seric mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name); 27957380Seric printf("\thost=%s, lastuse=%s\n", 28057380Seric mci->mci_host == NULL ? "NULL" : mci->mci_host, 28157380Seric ctime(&mci->mci_lastuse)); 28257380Seric } 283