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

📄 usersmtp.c

📁 < linux网络编程工具>>配套源码
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 * Copyright (c) 1983, 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.
 *
 */

#include <sendmail.h>

#ifndef lint
# if SMTP
static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.13 2000/09/26 00:46:21 gshapiro Exp $ (with SMTP)";
# else /* SMTP */
static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.13 2000/09/26 00:46:21 gshapiro Exp $ (without SMTP)";
# endif /* SMTP */
#endif /* ! lint */

#include <sysexits.h>

#if SMTP


static void	datatimeout __P((void));
static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));

/*
**  USERSMTP -- run SMTP protocol from the user end.
**
**	This protocol is described in RFC821.
*/

# define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
# define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
# define SMTPCLOSING	421			/* "Service Shutting Down" */

#define ENHSCN(e, d)	(e) == NULL ? (d) : newstr(e)

static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
/*
**  SMTPINIT -- initialize SMTP.
**
**	Opens the connection and sends the initial protocol.
**
**	Parameters:
**		m -- mailer to create connection to.
**		mci -- the mailer connection info.
**		e -- the envelope.
**		onlyhelo -- send only helo command?
**
**	Returns:
**		none.
**
**	Side Effects:
**		creates connection and sends initial protocol.
*/

void
smtpinit(m, mci, e, onlyhelo)
	MAILER *m;
	register MCI *mci;
	ENVELOPE *e;
	bool onlyhelo;
{
	register int r;
	register char *p;
	register char *hn;
	char *enhsc;

	enhsc = NULL;
	if (tTd(18, 1))
	{
		dprintf("smtpinit ");
		mci_dump(mci, FALSE);
	}

	/*
	**  Open the connection to the mailer.
	*/

	SmtpError[0] = '\0';
	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
	if (CurHostName == NULL)
		CurHostName = MyHostName;
	SmtpNeedIntro = TRUE;
	switch (mci->mci_state)
	{
	  case MCIS_ACTIVE:
		/* need to clear old information */
		smtprset(m, mci, e);
		/* FALLTHROUGH */

	  case MCIS_OPEN:
		if (!onlyhelo)
			return;
		break;

	  case MCIS_ERROR:
	  case MCIS_QUITING:
	  case MCIS_SSD:
		/* shouldn't happen */
		smtpquit(m, mci, e);
		/* FALLTHROUGH */

	  case MCIS_CLOSED:
		syserr("451 4.4.0 smtpinit: state CLOSED");
		return;

	  case MCIS_OPENING:
		break;
	}
	if (onlyhelo)
		goto helo;

	mci->mci_state = MCIS_OPENING;

	/*
	**  Get the greeting message.
	**	This should appear spontaneously.  Give it five minutes to
	**	happen.
	*/

	SmtpPhase = mci->mci_phase = "client greeting";
	sm_setproctitle(TRUE, e, "%s %s: %s",
			qid_printname(e), CurHostName, mci->mci_phase);
	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL);
	if (r < 0)
		goto tempfail1;
	if (REPLYTYPE(r) == 4)
		goto tempfail2;
	if (REPLYTYPE(r) != 2)
		goto unavailable;

	/*
	**  Send the HELO command.
	**	My mother taught me to always introduce myself.
	*/

helo:
	if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
		mci->mci_flags |= MCIF_ESMTP;
	hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;

tryhelo:
	if (bitnset(M_LMTP, m->m_flags))
	{
		smtpmessage("LHLO %s", m, mci, hn);
		SmtpPhase = mci->mci_phase = "client LHLO";
	}
	else if (bitset(MCIF_ESMTP, mci->mci_flags))
	{
		smtpmessage("EHLO %s", m, mci, hn);
		SmtpPhase = mci->mci_phase = "client EHLO";
	}
	else
	{
		smtpmessage("HELO %s", m, mci, hn);
		SmtpPhase = mci->mci_phase = "client HELO";
	}
	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
			CurHostName, mci->mci_phase);
	r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL);
	if (r < 0)
		goto tempfail1;
	else if (REPLYTYPE(r) == 5)
	{
		if (bitset(MCIF_ESMTP, mci->mci_flags) &&
		    !bitnset(M_LMTP, m->m_flags))
		{
			/* try old SMTP instead */
			mci->mci_flags &= ~MCIF_ESMTP;
			goto tryhelo;
		}
		goto unavailable;
	}
	else if (REPLYTYPE(r) != 2)
		goto tempfail2;

	/*
	**  Check to see if we actually ended up talking to ourself.
	**  This means we didn't know about an alias or MX, or we managed
	**  to connect to an echo server.
	*/

	p = strchr(&SmtpReplyBuffer[4], ' ');
	if (p != NULL)
		*p = '\0';
	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
	    !bitnset(M_LMTP, m->m_flags) &&
	    strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
	{
		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
			CurHostName);
		mci_setstat(mci, EX_CONFIG, "5.3.5", "system config error");
		mci->mci_errno = 0;
		smtpquit(m, mci, e);
		return;
	}

	/*
	**  If this is expected to be another sendmail, send some internal
	**  commands.
	*/

	if (bitnset(M_INTERNAL, m->m_flags))
	{
		/* tell it to be verbose */
		smtpmessage("VERB", m, mci);
		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc);
		if (r < 0)
			goto tempfail1;
	}

	if (mci->mci_state != MCIS_CLOSED)
	{
		mci->mci_state = MCIS_OPEN;
		return;
	}

	/* got a 421 error code during startup */

  tempfail1:
	if (mci->mci_errno == 0)
		mci->mci_errno = errno;
	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
	if (mci->mci_state != MCIS_CLOSED)
		smtpquit(m, mci, e);
	return;

  tempfail2:
	if (mci->mci_errno == 0)
		mci->mci_errno = errno;
	/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
		    SmtpReplyBuffer);
	if (mci->mci_state != MCIS_CLOSED)
		smtpquit(m, mci, e);
	return;

  unavailable:
	mci->mci_errno = errno;
	mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
	smtpquit(m, mci, e);
	return;
}
/*
**  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
**
**	Parameters:
**		line -- the response line.
**		firstline -- set if this is the first line of the reply.
**		m -- the mailer.
**		mci -- the mailer connection info.
**		e -- the envelope.
**
**	Returns:
**		none.
*/

static void
esmtp_check(line, firstline, m, mci, e)
	char *line;
	bool firstline;
	MAILER *m;
	register MCI *mci;
	ENVELOPE *e;
{
	if (strstr(line, "ESMTP") != NULL)
		mci->mci_flags |= MCIF_ESMTP;
	if (strstr(line, "8BIT-OK") != NULL)
		mci->mci_flags |= MCIF_8BITOK;
}
# if SASL
/*
**  STR_UNION -- create the union of two lists
**
**	Parameters:
**		s1, s2 -- lists of items (separated by single blanks).
**
**	Returns:
**		the union of both lists.
*/

static char *
str_union(s1, s2)
	char *s1, *s2;
{
	char *hr, *h1, *h, *res;
	int l1, l2, rl;

	if (s1 == NULL || *s1 == '\0')
		return s2;
	if (s2 == NULL || *s2 == '\0')
		return s1;
	l1 = strlen(s1);
	l2 = strlen(s2);
	rl = l1 + l2;
	res = (char *)malloc(rl + 2);
	if (res == NULL)
	{
		if (l1 > l2)
			return s1;
		return s2;
	}
	(void) strlcpy(res, s1, rl);
	hr = res;
	h1 = s2;
	h = s2;

	/* walk through s2 */
	while (h != NULL && *h1 != '\0')
	{
		/* is there something after the current word? */
		if ((h = strchr(h1, ' ')) != NULL)
			*h = '\0';
		l1 = strlen(h1);

		/* does the current word appear in s1 ? */
		if (iteminlist(h1, s1, " ") == NULL)
		{
			/* add space as delimiter */
			*hr++ = ' ';

			/* copy the item */
			memcpy(hr, h1, l1);

			/* advance pointer in result list */
			hr += l1;
			*hr = '\0';
		}
		if (h != NULL)
		{
			/* there are more items */
			*h = ' ';
			h1 = h + 1;
		}
	}
	return res;
}
# endif /* SASL */
/*
**  HELO_OPTIONS -- process the options on a HELO line.
**
**	Parameters:
**		line -- the response line.
**		firstline -- set if this is the first line of the reply.
**		m -- the mailer.
**		mci -- the mailer connection info.
**		e -- the envelope.
**
**	Returns:
**		none.
*/

static void
helo_options(line, firstline, m, mci, e)
	char *line;
	bool firstline;
	MAILER *m;
	register MCI *mci;
	ENVELOPE *e;
{
	register char *p;

	if (firstline)
	{
# if SASL
		if (mci->mci_saslcap != NULL)
			free(mci->mci_saslcap);
		mci->mci_saslcap = NULL;
# endif /* SASL */
		return;
	}

	if (strlen(line) < (SIZE_T) 5)
		return;
	line += 4;
	p = strpbrk(line, " =");
	if (p != NULL)
		*p++ = '\0';
	if (strcasecmp(line, "size") == 0)
	{
		mci->mci_flags |= MCIF_SIZE;
		if (p != NULL)
			mci->mci_maxsize = atol(p);
	}
	else if (strcasecmp(line, "8bitmime") == 0)
	{
		mci->mci_flags |= MCIF_8BITMIME;
		mci->mci_flags &= ~MCIF_7BIT;
	}
	else if (strcasecmp(line, "expn") == 0)
		mci->mci_flags |= MCIF_EXPN;
	else if (strcasecmp(line, "dsn") == 0)
		mci->mci_flags |= MCIF_DSN;
	else if (strcasecmp(line, "enhancedstatuscodes") == 0)
		mci->mci_flags |= MCIF_ENHSTAT;
# if STARTTLS
	else if (strcasecmp(line, "starttls") == 0)
		mci->mci_flags |= MCIF_TLS;
# endif /* STARTTLS */
# if SASL
	else if (strcasecmp(line, "auth") == 0)
	{
		if (p != NULL && *p != '\0')
		{
			if (mci->mci_saslcap != NULL)
			{
				char *h;

				/*
				**  create the union with previous auth
				**  offerings because we recognize "auth "
				**  and "auth=" (old format).
				*/
				h = mci->mci_saslcap;
				mci->mci_saslcap = str_union(h, p);
				if (h != mci->mci_saslcap)
					free(h);
				mci->mci_flags |= MCIF_AUTH;
			}
			else
			{
				int l;

				l = strlen(p) + 1;
				mci->mci_saslcap = (char *)malloc(l);

				/* XXX this may be leaked */
				if (mci->mci_saslcap != NULL)
				{
					(void) strlcpy(mci->mci_saslcap, p, l);
					mci->mci_flags |= MCIF_AUTH;
				}
			}
		}
	}
# endif /* SASL */
}
# if SASL

/*
**  GETSASLDATA -- process the challenges from the SASL protocol
**
**	This gets the relevant sasl response data out of the reply
**	from the server
**
**	Parameters:
**		line -- the response line.
**		firstline -- set if this is the first line of the reply.
**		m -- the mailer.
**		mci -- the mailer connection info.
**		e -- the envelope.
**
**	Returns:
**		none.
*/

void
getsasldata(line, firstline, m, mci, e)
	char *line;
	bool firstline;
	MAILER *m;
	register MCI *mci;
	ENVELOPE *e;
{
	int len;
	char *out;
	int result;

	/* if not a continue we don't care about it */
	if ((strlen(line) <= 4) ||
	    (line[0] != '3') ||
	    (line[1] != '3') ||
	    (line[2] != '4'))
	{
		mci->mci_sasl_string = NULL;
		return;
	}

	/* forget about "334 " */
	line += 4;
	len = strlen(line);

	out = xalloc(len + 1);
	result = sasl_decode64(line, len, out, (u_int *)&len);
	if (result != SASL_OK)
	{
		len = 0;
		*out = '\0';
	}
	if (mci->mci_sasl_string != NULL)
	{
		if (mci->mci_sasl_string_len <= len)
		{
			free(mci->mci_sasl_string);
			mci->mci_sasl_string = xalloc(len + 1);
		}
	}
	else
		mci->mci_sasl_string = xalloc(len + 1);
	/* XXX this is probably leaked */
	memcpy(mci->mci_sasl_string, out, len);
	mci->mci_sasl_string[len] = '\0';
	mci->mci_sasl_string_len = len;
	free(out);
	return;
}

/*
**  READAUTH -- read auth value from a file
**
**	Parameters:
**		l -- line to define.
**		filename -- name of file to read.
**		safe -- if set, this is a safe read.
**
**	Returns:
**		line from file
**
**	Side Effects:
**		overwrites local static buffer. The caller should copy
**		the result.
**
*/

/* lines in authinfo file */
# define SASL_USER	1
# define SASL_AUTHID	2
# define SASL_PASSWORD	3
# define SASL_DEFREALM	4
# define SASL_MECH	5

static char *sasl_info_name[] =
{
	"",
	"user id",
	"authorization id",
	"password",
	"realm",
	"mechanism"
};

static char *
readauth(l, filename, safe)
	int l;
	char *filename;
	bool safe;
{
	FILE *f;
	long sff;
	pid_t pid;
	int lc;
	static char buf[MAXLINE];

	if (filename == NULL || filename[0] == '\0')
		return "";
#if !_FFR_ALLOW_SASLINFO
	/*
	**  make sure we don't use a program that is not
	**  accesible to the user who specified a different authinfo file.
	**  However, currently we don't pass this info (authinfo file
	**  specified by user) around, so we just turn off program access.
	*/
	if (filename[0] == '|')
	{
		auto int fd;
		int i;
		char *p;
		char *argv[MAXPV + 1];

		i = 0;
		for (p = strtok(&filename[1], " \t"); p != NULL;
		     p = strtok(NULL, " \t"))
		{
			if (i >= MAXPV)
				break;
			argv[i++] = p;
		}
		argv[i] = NULL;
		pid = prog_open(argv, &fd, CurEnv);
		if (pid < 0)
			f = NULL;
		else
			f = fdopen(fd, "r");
	}
	else
#endif /* !_FFR_ALLOW_SASLINFO */
	{
		pid = -1;
		sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
		      | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES;
		if (DontLockReadFiles)
			sff |= SFF_NOLOCK;
#if _FFR_ALLOW_SASLINFO
		/*
		**  XXX: make sure we don't read or open files that are not
		**  accesible to the user who specified a different authinfo

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -