⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mci.c

📁 < linux网络编程工具>>配套源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 * 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 + -