154963Seric /* 254963Seric * Copyright (c) 1983 Eric P. Allman 362526Sbostic * Copyright (c) 1988, 1993 462526Sbostic * The Regents of the University of California. All rights reserved. 554963Seric * 654963Seric * %sccs.include.redist.c% 754963Seric */ 854963Seric 954963Seric #ifndef lint 10*65006Seric static char sccsid[] = "@(#)mci.c 8.9 (Berkeley) 12/01/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 8164739Seric if (tTd(42, 5)) 8264739Seric printf("mci_cache: caching %x (%s) in slot %d\n", 8364739Seric mci, mci->mci_host, mcislot - MciCache); 8464739Seric #ifdef LOG 8564739Seric if (tTd(91, 100)) 8664739Seric syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d", 8764742Seric CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", 8864742Seric mci, mci->mci_host, mcislot - MciCache); 8964739Seric #endif 9064739Seric 9154963Seric *mcislot = mci; 9254963Seric mci->mci_flags |= MCIF_CACHED; 9354963Seric } 9454963Seric /* 9554963Seric ** MCI_SCAN -- scan the cache, flush junk, and return best slot 9654963Seric ** 9754963Seric ** Parameters: 9854963Seric ** savemci -- never flush this one. Can be null. 9954963Seric ** 10054963Seric ** Returns: 10154963Seric ** The LRU (or empty) slot. 10254963Seric */ 10354963Seric 10454967Seric MCI ** 10554963Seric mci_scan(savemci) 10654967Seric MCI *savemci; 10754963Seric { 10854963Seric time_t now; 10954967Seric register MCI **bestmci; 11054967Seric register MCI *mci; 11154963Seric register int i; 11254963Seric 11354963Seric if (MciCache == NULL) 11454963Seric { 11554963Seric /* first call */ 11654967Seric MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); 11756215Seric bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); 11854963Seric return (&MciCache[0]); 11954963Seric } 12054963Seric 12154963Seric now = curtime(); 12254963Seric bestmci = &MciCache[0]; 12354963Seric for (i = 0; i < MaxMciCache; i++) 12454963Seric { 12554963Seric mci = MciCache[i]; 12654963Seric if (mci == NULL || mci->mci_state == MCIS_CLOSED) 12754963Seric { 12854963Seric bestmci = &MciCache[i]; 12954963Seric continue; 13054963Seric } 13154963Seric if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) 13254963Seric { 13354963Seric /* connection idle too long -- close it */ 13454963Seric bestmci = &MciCache[i]; 13558082Seric mci_uncache(bestmci, TRUE); 13654963Seric continue; 13754963Seric } 13854963Seric if (*bestmci == NULL) 13954963Seric continue; 14054963Seric if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 14154963Seric bestmci = &MciCache[i]; 14254963Seric } 14354963Seric return bestmci; 14454963Seric } 14554963Seric /* 14654963Seric ** MCI_UNCACHE -- remove a connection from a slot. 14754963Seric ** 14854963Seric ** May close a connection. 14954963Seric ** 15054963Seric ** Parameters: 15154963Seric ** mcislot -- the slot to empty. 15258082Seric ** doquit -- if TRUE, send QUIT protocol on this connection. 15358082Seric ** if FALSE, we are assumed to be in a forked child; 15458082Seric ** all we want to do is close the file(s). 15554963Seric ** 15654963Seric ** Returns: 15754963Seric ** none. 15854963Seric */ 15954963Seric 16058082Seric mci_uncache(mcislot, doquit) 16154967Seric register MCI **mcislot; 16258082Seric bool doquit; 16354963Seric { 16454967Seric register MCI *mci; 16555020Seric extern ENVELOPE BlankEnvelope; 16654963Seric 16754963Seric mci = *mcislot; 16854963Seric if (mci == NULL) 16954963Seric return; 17054963Seric *mcislot = NULL; 17154963Seric 17264739Seric if (tTd(42, 5)) 17364739Seric printf("mci_uncache: uncaching %x (%s) from slot %d (%d)\n", 17464739Seric mci, mci->mci_host, mcislot - MciCache, doquit); 17564739Seric #ifdef LOG 17664739Seric if (tTd(91, 100)) 17764739Seric syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)", 17864742Seric CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", 17964742Seric mci, mci->mci_host, mcislot - MciCache, doquit); 18064739Seric #endif 18164739Seric 18258082Seric if (doquit) 18358082Seric { 18458151Seric message("Closing connection to %s", mci->mci_host); 18555467Seric 18658082Seric mci->mci_flags &= ~MCIF_CACHED; 18758082Seric 18858082Seric /* only uses the envelope to flush the transcript file */ 18958082Seric if (mci->mci_state != MCIS_CLOSED) 19058082Seric smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 19159156Seric #ifdef XLA 19259156Seric xla_host_end(mci->mci_host); 19359156Seric #endif 19458082Seric } 19558082Seric else 19658082Seric { 19758082Seric if (mci->mci_in != NULL) 19858680Seric xfclose(mci->mci_in, "mci_uncache", "mci_in"); 19958082Seric if (mci->mci_out != NULL) 20058680Seric xfclose(mci->mci_out, "mci_uncache", "mci_out"); 20158082Seric mci->mci_in = mci->mci_out = NULL; 20258082Seric mci->mci_state = MCIS_CLOSED; 20358082Seric mci->mci_exitstat = EX_OK; 20458082Seric mci->mci_errno = 0; 20558082Seric mci->mci_flags = 0; 20658082Seric } 20754963Seric } 20854963Seric /* 20954963Seric ** MCI_FLUSH -- flush the entire cache 21058082Seric ** 21158082Seric ** Parameters: 21258082Seric ** doquit -- if TRUE, send QUIT protocol. 21358082Seric ** if FALSE, just close the connection. 21458082Seric ** allbut -- but leave this one open. 21558082Seric ** 21658082Seric ** Returns: 21758082Seric ** none. 21854963Seric */ 21954963Seric 22058082Seric mci_flush(doquit, allbut) 22158082Seric bool doquit; 22258082Seric MCI *allbut; 22354963Seric { 22454963Seric register int i; 22554963Seric 22654963Seric if (MciCache == NULL) 22754963Seric return; 22854963Seric 22954963Seric for (i = 0; i < MaxMciCache; i++) 23058082Seric if (allbut != MciCache[i]) 23158082Seric mci_uncache(&MciCache[i], doquit); 23254963Seric } 23354963Seric /* 23454963Seric ** MCI_GET -- get information about a particular host 23554963Seric */ 23654963Seric 23754967Seric MCI * 23854963Seric mci_get(host, m) 23954963Seric char *host; 24054963Seric MAILER *m; 24154963Seric { 24254967Seric register MCI *mci; 24355467Seric register STAB *s; 24454967Seric 24558907Seric #ifdef DAEMON 24658907Seric extern SOCKADDR CurHostAddr; 24758907Seric 24858907Seric /* clear CurHostAddr so we don't get a bogus address with this name */ 24958907Seric bzero(&CurHostAddr, sizeof CurHostAddr); 25063937Seric #endif 25158907Seric 25264815Seric if (m->m_mno < 0) 25364815Seric syserr("negative mno %d (%s)", m->m_mno, m->m_name); 25455467Seric s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 25555467Seric mci = &s->s_mci; 25655467Seric mci->mci_host = s->s_name; 25754967Seric 25854967Seric if (tTd(42, 2)) 25954967Seric { 26054967Seric printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", 26154967Seric host, m->m_name, mci->mci_state, mci->mci_flags, 26254967Seric mci->mci_exitstat, mci->mci_errno); 26354967Seric } 26454967Seric 26557977Seric if (mci->mci_state == MCIS_OPEN) 26654967Seric { 26757977Seric /* poke the connection to see if it's still alive */ 26858867Seric smtpprobe(mci); 26957977Seric 27057977Seric /* reset the stored state in the event of a timeout */ 27157977Seric if (mci->mci_state != MCIS_OPEN) 27257977Seric { 27357977Seric mci->mci_errno = 0; 27457977Seric mci->mci_exitstat = EX_OK; 27557977Seric mci->mci_state = MCIS_CLOSED; 27657977Seric } 27754967Seric } 27854967Seric 27954967Seric return mci; 28054963Seric } 28157380Seric /* 28257380Seric ** MCI_DUMP -- dump the contents of an MCI structure. 28357380Seric ** 28457380Seric ** Parameters: 28557380Seric ** mci -- the MCI structure to dump. 28657380Seric ** 28757380Seric ** Returns: 28857380Seric ** none. 28957380Seric ** 29057380Seric ** Side Effects: 29157380Seric ** none. 29257380Seric */ 29357380Seric 29464731Seric mci_dump(mci, logit) 29557380Seric register MCI *mci; 29664731Seric bool logit; 29757380Seric { 29864731Seric register char *p; 29964731Seric char *sep; 30064731Seric char buf[1000]; 30157380Seric extern char *ctime(); 30257380Seric 30364731Seric sep = logit ? " " : "\n\t"; 30464731Seric p = buf; 30564731Seric sprintf(p, "MCI@%x: ", mci); 30664731Seric p += strlen(p); 30757380Seric if (mci == NULL) 30857380Seric { 30964731Seric sprintf(p, "NULL"); 31064731Seric goto printit; 31157380Seric } 31264731Seric sprintf(p, "flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 31363753Seric mci->mci_flags, mci->mci_errno, mci->mci_herrno, 31464731Seric mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep); 31564731Seric p += strlen(p); 31664731Seric sprintf(p, "maxsize=%ld, phase=%s, mailer=%s,%s", 31763753Seric mci->mci_maxsize, 31857380Seric mci->mci_phase == NULL ? "NULL" : mci->mci_phase, 31964731Seric mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 32064731Seric sep); 32164731Seric p += strlen(p); 32264731Seric sprintf(p, "host=%s, lastuse=%s", 32357380Seric mci->mci_host == NULL ? "NULL" : mci->mci_host, 32457380Seric ctime(&mci->mci_lastuse)); 32564731Seric printit: 32664731Seric if (logit) 327*65006Seric syslog(LOG_DEBUG, "%s", buf); 32864731Seric else 32964731Seric printf("%s\n", buf); 33057380Seric } 33164731Seric /* 33264731Seric ** MCI_DUMP_ALL -- print the entire MCI cache 33364731Seric ** 33464731Seric ** Parameters: 33564731Seric ** logit -- if set, log the result instead of printing 33664731Seric ** to stdout. 33764731Seric ** 33864731Seric ** Returns: 33964731Seric ** none. 34064731Seric */ 34164731Seric 34264731Seric mci_dump_all(logit) 34364731Seric bool logit; 34464731Seric { 34564731Seric register int i; 34664731Seric 34765005Seric if (MciCache == NULL) 34865005Seric return; 34965005Seric 35064731Seric for (i = 0; i < MaxMciCache; i++) 35164731Seric mci_dump(MciCache[i], logit); 35264731Seric } 353