diff options
Diffstat (limited to 'usr.sbin/sendmail/src/mci.c')
-rw-r--r-- | usr.sbin/sendmail/src/mci.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/usr.sbin/sendmail/src/mci.c b/usr.sbin/sendmail/src/mci.c new file mode 100644 index 0000000..8160b83 --- /dev/null +++ b/usr.sbin/sendmail/src/mci.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 1983 Eric P. Allman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)mci.c 8.13 (Berkeley) 4/12/94"; +#endif /* not lint */ + +#include "sendmail.h" + +/* +** Mail Connection Information (MCI) Caching Module. +** +** There are actually two separate things cached. The first is +** the set of all open connections -- these are stored in a +** (small) list. The second is stored in the symbol table; it +** has the overall status for all hosts, whether or not there +** is a connection open currently. +** +** There should never be too many connections open (since this +** could flood the socket table), nor should a connection be +** allowed to sit idly for too long. +** +** MaxMciCache is the maximum number of open connections that +** will be supported. +** +** MciCacheTimeout is the time (in seconds) that a connection +** is permitted to survive without activity. +** +** We actually try any cached connections by sending a NOOP +** before we use them; if the NOOP fails we close down the +** connection and reopen it. Note that this means that a +** server SMTP that doesn't support NOOP will hose the +** algorithm -- but that doesn't seem too likely. +*/ + +MCI **MciCache; /* the open connection cache */ +/* +** MCI_CACHE -- enter a connection structure into the open connection cache +** +** This may cause something else to be flushed. +** +** Parameters: +** mci -- the connection to cache. +** +** Returns: +** none. +*/ + +mci_cache(mci) + register MCI *mci; +{ + register MCI **mcislot; + extern MCI **mci_scan(); + + if (MaxMciCache <= 0) + { + /* we don't support caching */ + return; + } + + /* + ** Find the best slot. This may cause expired connections + ** to be closed. + */ + + mcislot = mci_scan(mci); + + /* if this is already cached, we are done */ + if (bitset(MCIF_CACHED, mci->mci_flags)) + return; + + /* otherwise we may have to clear the slot */ + if (*mcislot != NULL) + mci_uncache(mcislot, TRUE); + + if (tTd(42, 5)) + printf("mci_cache: caching %x (%s) in slot %d\n", + mci, mci->mci_host, mcislot - MciCache); +#ifdef LOG + if (tTd(91, 100)) + syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d", + CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", + mci, mci->mci_host, mcislot - MciCache); +#endif + + *mcislot = mci; + mci->mci_flags |= MCIF_CACHED; +} +/* +** MCI_SCAN -- scan the cache, flush junk, and return best slot +** +** Parameters: +** savemci -- never flush this one. Can be null. +** +** Returns: +** The LRU (or empty) slot. +*/ + +MCI ** +mci_scan(savemci) + MCI *savemci; +{ + time_t now; + register MCI **bestmci; + register MCI *mci; + register int i; + + if (MciCache == NULL) + { + /* first call */ + MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); + bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); + return (&MciCache[0]); + } + + now = curtime(); + bestmci = &MciCache[0]; + for (i = 0; i < MaxMciCache; i++) + { + mci = MciCache[i]; + if (mci == NULL || mci->mci_state == MCIS_CLOSED) + { + bestmci = &MciCache[i]; + continue; + } + if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) + { + /* connection idle too long -- close it */ + bestmci = &MciCache[i]; + mci_uncache(bestmci, TRUE); + continue; + } + if (*bestmci == NULL) + continue; + if (mci->mci_lastuse < (*bestmci)->mci_lastuse) + bestmci = &MciCache[i]; + } + return bestmci; +} +/* +** MCI_UNCACHE -- remove a connection from a slot. +** +** May close a connection. +** +** Parameters: +** mcislot -- the slot to empty. +** doquit -- if TRUE, send QUIT protocol on this connection. +** if FALSE, we are assumed to be in a forked child; +** all we want to do is close the file(s). +** +** Returns: +** none. +*/ + +mci_uncache(mcislot, doquit) + register MCI **mcislot; + bool doquit; +{ + register MCI *mci; + extern ENVELOPE BlankEnvelope; + + mci = *mcislot; + if (mci == NULL) + return; + *mcislot = NULL; + + if (tTd(42, 5)) + printf("mci_uncache: uncaching %x (%s) from slot %d (%d)\n", + mci, mci->mci_host, mcislot - MciCache, doquit); +#ifdef LOG + if (tTd(91, 100)) + syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)", + CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", + mci, mci->mci_host, mcislot - MciCache, doquit); +#endif + + if (doquit) + { + message("Closing connection to %s", mci->mci_host); + + mci->mci_flags &= ~MCIF_CACHED; + + /* only uses the envelope to flush the transcript file */ + if (mci->mci_state != MCIS_CLOSED) + smtpquit(mci->mci_mailer, mci, &BlankEnvelope); +#ifdef XLA + xla_host_end(mci->mci_host); +#endif + } + else + { + if (mci->mci_in != NULL) + xfclose(mci->mci_in, "mci_uncache", "mci_in"); + if (mci->mci_out != NULL) + xfclose(mci->mci_out, "mci_uncache", "mci_out"); + mci->mci_in = mci->mci_out = NULL; + mci->mci_state = MCIS_CLOSED; + mci->mci_exitstat = EX_OK; + mci->mci_errno = 0; + mci->mci_flags = 0; + } +} +/* +** MCI_FLUSH -- flush the entire cache +** +** Parameters: +** doquit -- if TRUE, send QUIT protocol. +** if FALSE, just close the connection. +** allbut -- but leave this one open. +** +** Returns: +** none. +*/ + +mci_flush(doquit, allbut) + bool doquit; + MCI *allbut; +{ + register int i; + + if (MciCache == NULL) + return; + + for (i = 0; i < MaxMciCache; i++) + if (allbut != MciCache[i]) + mci_uncache(&MciCache[i], doquit); +} +/* +** MCI_GET -- get information about a particular host +*/ + +MCI * +mci_get(host, m) + char *host; + MAILER *m; +{ + register MCI *mci; + register STAB *s; + +#ifdef DAEMON + extern SOCKADDR CurHostAddr; + + /* clear CurHostAddr so we don't get a bogus address with this name */ + bzero(&CurHostAddr, sizeof CurHostAddr); +#endif + + /* clear out any expired connections */ + mci_scan(NULL); + + if (m->m_mno < 0) + syserr("negative mno %d (%s)", m->m_mno, m->m_name); + s = stab(host, ST_MCI + m->m_mno, ST_ENTER); + mci = &s->s_mci; + mci->mci_host = s->s_name; + + if (tTd(42, 2)) + { + printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", + host, m->m_name, mci->mci_state, mci->mci_flags, + mci->mci_exitstat, mci->mci_errno); + } + + if (mci->mci_state == MCIS_OPEN) + { + /* poke the connection to see if it's still alive */ + smtpprobe(mci); + + /* reset the stored state in the event of a timeout */ + if (mci->mci_state != MCIS_OPEN) + { + mci->mci_errno = 0; + mci->mci_exitstat = EX_OK; + mci->mci_state = MCIS_CLOSED; + } + else + { + /* get peer host address for logging reasons only */ + /* (this should really be in the mci struct) */ + int socksize = sizeof CurHostAddr; + + (void) getpeername(fileno(mci->mci_in), + (struct sockaddr *) &CurHostAddr, &socksize); + } + } + if (mci->mci_state == MCIS_CLOSED) + { + /* copy out any mailer flags needed in connection state */ + if (bitnset(M_7BITS, m->m_flags)) + mci->mci_flags |= MCIF_7BIT; + } + + return mci; +} +/* +** MCI_DUMP -- dump the contents of an MCI structure. +** +** Parameters: +** mci -- the MCI structure to dump. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +mci_dump(mci, logit) + register MCI *mci; + bool logit; +{ + register char *p; + char *sep; + char buf[1000]; + extern char *ctime(); + + sep = logit ? " " : "\n\t"; + p = buf; + sprintf(p, "MCI@%x: ", mci); + p += strlen(p); + if (mci == NULL) + { + sprintf(p, "NULL"); + goto printit; + } + sprintf(p, "flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", + mci->mci_flags, mci->mci_errno, mci->mci_herrno, + mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep); + p += strlen(p); + sprintf(p, "maxsize=%ld, phase=%s, mailer=%s,%s", + mci->mci_maxsize, + mci->mci_phase == NULL ? "NULL" : mci->mci_phase, + mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, + sep); + p += strlen(p); + sprintf(p, "host=%s, lastuse=%s", + mci->mci_host == NULL ? "NULL" : mci->mci_host, + ctime(&mci->mci_lastuse)); +printit: +#ifdef LOG + if (logit) + syslog(LOG_DEBUG, "%s", buf); + else +#endif + printf("%s\n", buf); +} +/* +** MCI_DUMP_ALL -- print the entire MCI cache +** +** Parameters: +** logit -- if set, log the result instead of printing +** to stdout. +** +** Returns: +** none. +*/ + +mci_dump_all(logit) + bool logit; +{ + register int i; + + if (MciCache == NULL) + return; + + for (i = 0; i < MaxMciCache; i++) + mci_dump(MciCache[i], logit); +} |