📄 mci.c
字号:
/*
* Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
*/
#ifndef lint
static char id[] = "@(#)$Id: mci.c,v 8.133.10.3 2000/06/23 16:17:06 ca Exp $";
#endif /* ! lint */
#include <sendmail.h>
#if NETINET || NETINET6
# include <arpa/inet.h>
#endif /* NETINET || NETINET6 */
#include <dirent.h>
static int mci_generate_persistent_path __P((const char *, char *,
int, bool));
static bool mci_load_persistent __P((MCI *));
static void mci_uncache __P((MCI **, bool));
static int mci_lock_host_statfile __P((MCI *));
static int mci_read_persistent __P((FILE *, MCI *));
/*
** 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.
**
** The persistent MCI code is donated by Mark Lovell and Paul
** Vixie. It is based on the long term host status code in KJS
** written by Paul but has been adapted by Mark to fit into the
** MCI structure.
*/
static 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.
*/
void
mci_cache(mci)
register MCI *mci;
{
register MCI **mcislot;
/*
** Find the best slot. This may cause expired connections
** to be closed.
*/
mcislot = mci_scan(mci);
if (mcislot == NULL)
{
/* we don't support caching */
return;
}
if (mci->mci_host == NULL)
return;
/* 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))
dprintf("mci_cache: caching %lx (%s) in slot %d\n",
(u_long) mci, mci->mci_host,
(int)(mcislot - MciCache));
if (tTd(91, 100))
sm_syslog(LOG_DEBUG, CurEnv->e_id,
"mci_cache: caching %lx (%.100s) in slot %d",
(u_long) mci, mci->mci_host, mcislot - MciCache);
*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 (MaxMciCache <= 0)
{
/* we don't support caching */
return NULL;
}
if (MciCache == NULL)
{
/* first call */
MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
memset((char *) MciCache, '\0', 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->mci_mailer != NULL &&
mci->mci_mailer->m_maxdeliveries > 0 &&
mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&&
mci != savemci)
{
/* connection idle too long or too many deliveries */
bestmci = &MciCache[i];
/* close it */
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.
*/
static void
mci_uncache(mcislot, doquit)
register MCI **mcislot;
bool doquit;
{
register MCI *mci;
extern ENVELOPE BlankEnvelope;
mci = *mcislot;
if (mci == NULL)
return;
*mcislot = NULL;
if (mci->mci_host == NULL)
return;
mci_unlock_host(mci);
if (tTd(42, 5))
dprintf("mci_uncache: uncaching %lx (%s) from slot %d (%d)\n",
(u_long) mci, mci->mci_host,
(int)(mcislot - MciCache), doquit);
if (tTd(91, 100))
sm_syslog(LOG_DEBUG, CurEnv->e_id,
"mci_uncache: uncaching %lx (%.100s) from slot %d (%d)",
(u_long) mci, mci->mci_host,
mcislot - MciCache, doquit);
mci->mci_deliveries = 0;
#if SMTP
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 /* XLA */
}
else
#endif /* SMTP */
{
if (mci->mci_in != NULL)
(void) fclose(mci->mci_in);
if (mci->mci_out != NULL)
(void) fclose(mci->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.
*/
void
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;
#if DAEMON
extern SOCKADDR CurHostAddr;
/* clear CurHostAddr so we don't get a bogus address with this name */
memset(&CurHostAddr, '\0', sizeof CurHostAddr);
#endif /* DAEMON */
/* clear out any expired connections */
(void) 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;
/*
** We don't need to load the peristent data if we have data
** already loaded in the cache.
*/
if (mci->mci_host == NULL &&
(mci->mci_host = s->s_name) != NULL &&
!mci_load_persistent(mci))
{
if (tTd(42, 2))
dprintf("mci_get(%s %s): lock failed\n",
host, m->m_name);
mci->mci_exitstat = EX_TEMPFAIL;
mci->mci_state = MCIS_CLOSED;
mci->mci_statfile = NULL;
return mci;
}
if (tTd(42, 2))
{
dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n",
host, m->m_name, mci->mci_state, mci->mci_flags,
mci->mci_exitstat, mci->mci_errno);
}
#if SMTP
if (mci->mci_state == MCIS_OPEN)
{
/* poke the connection to see if it's still alive */
(void) 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;
}
# if DAEMON
else
{
/* get peer host address for logging reasons only */
/* (this should really be in the mci struct) */
SOCKADDR_LEN_T socklen = sizeof CurHostAddr;
(void) getpeername(fileno(mci->mci_in),
(struct sockaddr *) &CurHostAddr, &socklen);
}
# endif /* DAEMON */
}
#endif /* SMTP */
if (mci->mci_state == MCIS_CLOSED)
{
time_t now = curtime();
/* if this info is stale, ignore it */
if (now > mci->mci_lastuse + MciInfoTimeout)
{
mci->mci_lastuse = now;
mci->mci_errno = 0;
mci->mci_exitstat = EX_OK;
}
}
return mci;
}
/*
** MCI_MATCH -- check connection cache for a particular host
*/
bool
mci_match(host, m)
char *host;
MAILER *m;
{
register MCI *mci;
register STAB *s;
if (m->m_mno < 0)
return FALSE;
s = stab(host, ST_MCI + m->m_mno, ST_FIND);
if (s == NULL)
return FALSE;
mci = &s->s_mci;
if (mci->mci_state == MCIS_OPEN)
return TRUE;
return FALSE;
}
/*
** MCI_SETSTAT -- set status codes in MCI structure.
**
** Parameters:
** mci -- the MCI structure to set.
** xstat -- the exit status code.
** dstat -- the DSN status code.
** rstat -- the SMTP status code.
**
** Returns:
** none.
*/
void
mci_setstat(mci, xstat, dstat, rstat)
MCI *mci;
int xstat;
char *dstat;
char *rstat;
{
/* protocol errors should never be interpreted as sticky */
if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL)
mci->mci_exitstat = xstat;
mci->mci_status = dstat;
if (mci->mci_rstatus != NULL)
free(mci->mci_rstatus);
if (rstat != NULL)
rstat = newstr(rstat);
mci->mci_rstatus = rstat;
}
/*
** MCI_DUMP -- dump the contents of an MCI structure.
**
** Parameters:
** mci -- the MCI structure to dump.
**
** Returns:
** none.
**
** Side Effects:
** none.
*/
struct mcifbits
{
int mcif_bit; /* flag bit */
char *mcif_name; /* flag name */
};
static struct mcifbits MciFlags[] =
{
{ MCIF_VALID, "VALID" },
{ MCIF_TEMP, "TEMP" },
{ MCIF_CACHED, "CACHED" },
{ MCIF_ESMTP, "ESMTP" },
{ MCIF_EXPN, "EXPN" },
{ MCIF_SIZE, "SIZE" },
{ MCIF_8BITMIME, "8BITMIME" },
{ MCIF_7BIT, "7BIT" },
{ MCIF_MULTSTAT, "MULTSTAT" },
{ MCIF_INHEADER, "INHEADER" },
{ MCIF_CVT8TO7, "CVT8TO7" },
{ MCIF_DSN, "DSN" },
{ MCIF_8BITOK, "8BITOK" },
{ MCIF_CVT7TO8, "CVT7TO8" },
{ MCIF_INMIME, "INMIME" },
{ 0, NULL }
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -