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*67139Seric static char sccsid[] = "@(#)mci.c 8.14 (Berkeley) 05/15/94"; 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 /* 6154963Seric ** Find the best slot. This may cause expired connections 6254963Seric ** to be closed. 6354963Seric */ 6454963Seric 6554963Seric mcislot = mci_scan(mci); 66*67139Seric if (mcislot == NULL) 67*67139Seric { 68*67139Seric /* we don't support caching */ 69*67139Seric return; 70*67139Seric } 7154963Seric 7254963Seric /* if this is already cached, we are done */ 7354963Seric if (bitset(MCIF_CACHED, mci->mci_flags)) 7454963Seric return; 7554963Seric 7654963Seric /* otherwise we may have to clear the slot */ 7754963Seric if (*mcislot != NULL) 7858082Seric mci_uncache(mcislot, TRUE); 7954963Seric 8064739Seric if (tTd(42, 5)) 8164739Seric printf("mci_cache: caching %x (%s) in slot %d\n", 8264739Seric mci, mci->mci_host, mcislot - MciCache); 8364739Seric #ifdef LOG 8464739Seric if (tTd(91, 100)) 8564739Seric syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d", 8664742Seric CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", 8764742Seric mci, mci->mci_host, mcislot - MciCache); 8864739Seric #endif 8964739Seric 9054963Seric *mcislot = mci; 9154963Seric mci->mci_flags |= MCIF_CACHED; 9254963Seric } 9354963Seric /* 9454963Seric ** MCI_SCAN -- scan the cache, flush junk, and return best slot 9554963Seric ** 9654963Seric ** Parameters: 9754963Seric ** savemci -- never flush this one. Can be null. 9854963Seric ** 9954963Seric ** Returns: 10054963Seric ** The LRU (or empty) slot. 10154963Seric */ 10254963Seric 10354967Seric MCI ** 10454963Seric mci_scan(savemci) 10554967Seric MCI *savemci; 10654963Seric { 10754963Seric time_t now; 10854967Seric register MCI **bestmci; 10954967Seric register MCI *mci; 11054963Seric register int i; 11154963Seric 112*67139Seric if (MaxMciCache <= 0) 113*67139Seric { 114*67139Seric /* we don't support caching */ 115*67139Seric return NULL; 116*67139Seric } 117*67139Seric 11854963Seric if (MciCache == NULL) 11954963Seric { 12054963Seric /* first call */ 12154967Seric MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); 12256215Seric bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); 12354963Seric return (&MciCache[0]); 12454963Seric } 12554963Seric 12654963Seric now = curtime(); 12754963Seric bestmci = &MciCache[0]; 12854963Seric for (i = 0; i < MaxMciCache; i++) 12954963Seric { 13054963Seric mci = MciCache[i]; 13154963Seric if (mci == NULL || mci->mci_state == MCIS_CLOSED) 13254963Seric { 13354963Seric bestmci = &MciCache[i]; 13454963Seric continue; 13554963Seric } 13654963Seric if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) 13754963Seric { 13854963Seric /* connection idle too long -- close it */ 13954963Seric bestmci = &MciCache[i]; 14058082Seric mci_uncache(bestmci, TRUE); 14154963Seric continue; 14254963Seric } 14354963Seric if (*bestmci == NULL) 14454963Seric continue; 14554963Seric if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 14654963Seric bestmci = &MciCache[i]; 14754963Seric } 14854963Seric return bestmci; 14954963Seric } 15054963Seric /* 15154963Seric ** MCI_UNCACHE -- remove a connection from a slot. 15254963Seric ** 15354963Seric ** May close a connection. 15454963Seric ** 15554963Seric ** Parameters: 15654963Seric ** mcislot -- the slot to empty. 15758082Seric ** doquit -- if TRUE, send QUIT protocol on this connection. 15858082Seric ** if FALSE, we are assumed to be in a forked child; 15958082Seric ** all we want to do is close the file(s). 16054963Seric ** 16154963Seric ** Returns: 16254963Seric ** none. 16354963Seric */ 16454963Seric 16558082Seric mci_uncache(mcislot, doquit) 16654967Seric register MCI **mcislot; 16758082Seric bool doquit; 16854963Seric { 16954967Seric register MCI *mci; 17055020Seric extern ENVELOPE BlankEnvelope; 17154963Seric 17254963Seric mci = *mcislot; 17354963Seric if (mci == NULL) 17454963Seric return; 17554963Seric *mcislot = NULL; 17654963Seric 17764739Seric if (tTd(42, 5)) 17864739Seric printf("mci_uncache: uncaching %x (%s) from slot %d (%d)\n", 17964739Seric mci, mci->mci_host, mcislot - MciCache, doquit); 18064739Seric #ifdef LOG 18164739Seric if (tTd(91, 100)) 18264739Seric syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)", 18364742Seric CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", 18464742Seric mci, mci->mci_host, mcislot - MciCache, doquit); 18564739Seric #endif 18664739Seric 18758082Seric if (doquit) 18858082Seric { 18958151Seric message("Closing connection to %s", mci->mci_host); 19055467Seric 19158082Seric mci->mci_flags &= ~MCIF_CACHED; 19258082Seric 19358082Seric /* only uses the envelope to flush the transcript file */ 19458082Seric if (mci->mci_state != MCIS_CLOSED) 19558082Seric smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 19659156Seric #ifdef XLA 19759156Seric xla_host_end(mci->mci_host); 19859156Seric #endif 19958082Seric } 20058082Seric else 20158082Seric { 20258082Seric if (mci->mci_in != NULL) 20358680Seric xfclose(mci->mci_in, "mci_uncache", "mci_in"); 20458082Seric if (mci->mci_out != NULL) 20558680Seric xfclose(mci->mci_out, "mci_uncache", "mci_out"); 20658082Seric mci->mci_in = mci->mci_out = NULL; 20758082Seric mci->mci_state = MCIS_CLOSED; 20858082Seric mci->mci_exitstat = EX_OK; 20958082Seric mci->mci_errno = 0; 21058082Seric mci->mci_flags = 0; 21158082Seric } 21254963Seric } 21354963Seric /* 21454963Seric ** MCI_FLUSH -- flush the entire cache 21558082Seric ** 21658082Seric ** Parameters: 21758082Seric ** doquit -- if TRUE, send QUIT protocol. 21858082Seric ** if FALSE, just close the connection. 21958082Seric ** allbut -- but leave this one open. 22058082Seric ** 22158082Seric ** Returns: 22258082Seric ** none. 22354963Seric */ 22454963Seric 22558082Seric mci_flush(doquit, allbut) 22658082Seric bool doquit; 22758082Seric MCI *allbut; 22854963Seric { 22954963Seric register int i; 23054963Seric 23154963Seric if (MciCache == NULL) 23254963Seric return; 23354963Seric 23454963Seric for (i = 0; i < MaxMciCache; i++) 23558082Seric if (allbut != MciCache[i]) 23658082Seric mci_uncache(&MciCache[i], doquit); 23754963Seric } 23854963Seric /* 23954963Seric ** MCI_GET -- get information about a particular host 24054963Seric */ 24154963Seric 24254967Seric MCI * 24354963Seric mci_get(host, m) 24454963Seric char *host; 24554963Seric MAILER *m; 24654963Seric { 24754967Seric register MCI *mci; 24855467Seric register STAB *s; 249*67139Seric extern MCI **mci_scan(); 25054967Seric 25158907Seric #ifdef DAEMON 25258907Seric extern SOCKADDR CurHostAddr; 25358907Seric 25458907Seric /* clear CurHostAddr so we don't get a bogus address with this name */ 25558907Seric bzero(&CurHostAddr, sizeof CurHostAddr); 25663937Seric #endif 25758907Seric 25866037Seric /* clear out any expired connections */ 259*67139Seric (void) mci_scan(NULL); 26066037Seric 26164815Seric if (m->m_mno < 0) 26264815Seric syserr("negative mno %d (%s)", m->m_mno, m->m_name); 26355467Seric s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 26455467Seric mci = &s->s_mci; 26555467Seric mci->mci_host = s->s_name; 26654967Seric 26754967Seric if (tTd(42, 2)) 26854967Seric { 26954967Seric printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", 27054967Seric host, m->m_name, mci->mci_state, mci->mci_flags, 27154967Seric mci->mci_exitstat, mci->mci_errno); 27254967Seric } 27354967Seric 27457977Seric if (mci->mci_state == MCIS_OPEN) 27554967Seric { 27657977Seric /* poke the connection to see if it's still alive */ 27758867Seric smtpprobe(mci); 27857977Seric 27957977Seric /* reset the stored state in the event of a timeout */ 28057977Seric if (mci->mci_state != MCIS_OPEN) 28157977Seric { 28257977Seric mci->mci_errno = 0; 28357977Seric mci->mci_exitstat = EX_OK; 28457977Seric mci->mci_state = MCIS_CLOSED; 28557977Seric } 28666007Seric else 28766007Seric { 28866007Seric /* get peer host address for logging reasons only */ 28966007Seric /* (this should really be in the mci struct) */ 29066007Seric int socksize = sizeof CurHostAddr; 29166007Seric 29266007Seric (void) getpeername(fileno(mci->mci_in), 29366007Seric (struct sockaddr *) &CurHostAddr, &socksize); 29466007Seric } 29554967Seric } 29665870Seric if (mci->mci_state == MCIS_CLOSED) 29765870Seric { 29865870Seric /* copy out any mailer flags needed in connection state */ 29965870Seric if (bitnset(M_7BITS, m->m_flags)) 30065870Seric mci->mci_flags |= MCIF_7BIT; 30165870Seric } 30254967Seric 30354967Seric return mci; 30454963Seric } 30557380Seric /* 30657380Seric ** MCI_DUMP -- dump the contents of an MCI structure. 30757380Seric ** 30857380Seric ** Parameters: 30957380Seric ** mci -- the MCI structure to dump. 31057380Seric ** 31157380Seric ** Returns: 31257380Seric ** none. 31357380Seric ** 31457380Seric ** Side Effects: 31557380Seric ** none. 31657380Seric */ 31757380Seric 31864731Seric mci_dump(mci, logit) 31957380Seric register MCI *mci; 32064731Seric bool logit; 32157380Seric { 32264731Seric register char *p; 32364731Seric char *sep; 32464731Seric char buf[1000]; 32557380Seric extern char *ctime(); 32657380Seric 32764731Seric sep = logit ? " " : "\n\t"; 32864731Seric p = buf; 32964731Seric sprintf(p, "MCI@%x: ", mci); 33064731Seric p += strlen(p); 33157380Seric if (mci == NULL) 33257380Seric { 33364731Seric sprintf(p, "NULL"); 33464731Seric goto printit; 33557380Seric } 33664731Seric sprintf(p, "flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 33763753Seric mci->mci_flags, mci->mci_errno, mci->mci_herrno, 33864731Seric mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep); 33964731Seric p += strlen(p); 34064731Seric sprintf(p, "maxsize=%ld, phase=%s, mailer=%s,%s", 34163753Seric mci->mci_maxsize, 34257380Seric mci->mci_phase == NULL ? "NULL" : mci->mci_phase, 34364731Seric mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 34464731Seric sep); 34564731Seric p += strlen(p); 34664731Seric sprintf(p, "host=%s, lastuse=%s", 34757380Seric mci->mci_host == NULL ? "NULL" : mci->mci_host, 34857380Seric ctime(&mci->mci_lastuse)); 34964731Seric printit: 35066748Seric #ifdef LOG 35164731Seric if (logit) 35265006Seric syslog(LOG_DEBUG, "%s", buf); 35364731Seric else 35466748Seric #endif 35564731Seric printf("%s\n", buf); 35657380Seric } 35764731Seric /* 35864731Seric ** MCI_DUMP_ALL -- print the entire MCI cache 35964731Seric ** 36064731Seric ** Parameters: 36164731Seric ** logit -- if set, log the result instead of printing 36264731Seric ** to stdout. 36364731Seric ** 36464731Seric ** Returns: 36564731Seric ** none. 36664731Seric */ 36764731Seric 36864731Seric mci_dump_all(logit) 36964731Seric bool logit; 37064731Seric { 37164731Seric register int i; 37264731Seric 37365005Seric if (MciCache == NULL) 37465005Seric return; 37565005Seric 37664731Seric for (i = 0; i < MaxMciCache; i++) 37764731Seric mci_dump(MciCache[i], logit); 37864731Seric } 379