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

📄 srvrsmtp.c

📁 < linux网络编程工具>>配套源码
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * 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: srvrsmtp.c,v 8.471.2.2.2.58 2000/09/21 21:52:18 ca Exp $ (with SMTP)";
# else /* SMTP */
static char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.58 2000/09/21 21:52:18 ca Exp $ (without SMTP)";
# endif /* SMTP */
#endif /* ! lint */

#if SMTP
# if SASL || STARTTLS
#  include "sfsasl.h"
# endif /* SASL || STARTTLS */
# if SASL
#  define ENC64LEN(l)	(((l) + 2) * 4 / 3 + 1)
static int saslmechs __P((sasl_conn_t *, char **));
# endif /* SASL */
# if STARTTLS
#  include <sysexits.h>
#   include <openssl/err.h>
#   include <openssl/bio.h>
#   include <openssl/pem.h>
#   ifndef HASURANDOMDEV
#    include <openssl/rand.h>
#   endif /* !HASURANDOMDEV */

static SSL	*srv_ssl = NULL;
static SSL_CTX	*srv_ctx = NULL;
#  if !TLS_NO_RSA
static RSA	*rsa = NULL;
#  endif /* !TLS_NO_RSA */
static bool	tls_ok = FALSE;
static int	tls_verify_cb __P((X509_STORE_CTX *));
#  if !TLS_NO_RSA
#   define RSA_KEYLENGTH	512
#  endif /* !TLS_NO_RSA */
# endif /* STARTTLS */

static time_t	checksmtpattack __P((volatile int *, int, bool,
				     char *, ENVELOPE *));
static void	mail_esmtp_args __P((char *, char *, ENVELOPE *));
static void	printvrfyaddr __P((ADDRESS *, bool, bool));
static void	rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
static int	runinchild __P((char *, ENVELOPE *));
static char	*skipword __P((char *volatile, char *));
extern ENVELOPE	BlankEnvelope;

/*
**  SMTP -- run the SMTP protocol.
**
**	Parameters:
**		nullserver -- if non-NULL, rejection message for
**			all SMTP commands.
**		e -- the envelope.
**
**	Returns:
**		never.
**
**	Side Effects:
**		Reads commands from the input channel and processes
**			them.
*/

struct cmd
{
	char	*cmd_name;	/* command name */
	int	cmd_code;	/* internal code, see below */
};

/* values for cmd_code */
# define CMDERROR	0	/* bad command */
# define CMDMAIL	1	/* mail -- designate sender */
# define CMDRCPT	2	/* rcpt -- designate recipient */
# define CMDDATA	3	/* data -- send message text */
# define CMDRSET	4	/* rset -- reset state */
# define CMDVRFY	5	/* vrfy -- verify address */
# define CMDEXPN	6	/* expn -- expand address */
# define CMDNOOP	7	/* noop -- do nothing */
# define CMDQUIT	8	/* quit -- close connection and die */
# define CMDHELO	9	/* helo -- be polite */
# define CMDHELP	10	/* help -- give usage info */
# define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
# define CMDETRN	12	/* etrn -- flush queue */
# if SASL
#  define CMDAUTH	13	/* auth -- SASL authenticate */
# endif /* SASL */
# if STARTTLS
#  define CMDSTLS	14	/* STARTTLS -- start TLS session */
# endif /* STARTTLS */
/* non-standard commands */
# define CMDONEX	16	/* onex -- sending one transaction only */
# define CMDVERB	17	/* verb -- go into verbose mode */
# define CMDXUSR	18	/* xusr -- initial (user) submission */
/* unimplemented commands from RFC 821 */
# define CMDUNIMPL	19	/* unimplemented rfc821 commands */
/* use this to catch and log "door handle" attempts on your system */
# define CMDLOGBOGUS	23	/* bogus command that should be logged */
/* debugging-only commands, only enabled if SMTPDEBUG is defined */
# define CMDDBGQSHOW	24	/* showq -- show send queue */
# define CMDDBGDEBUG	25	/* debug -- set debug mode */

/*
**  Note: If you change this list,
**        remember to update 'helpfile'
*/

static struct cmd	CmdTab[] =
{
	{ "mail",	CMDMAIL		},
	{ "rcpt",	CMDRCPT		},
	{ "data",	CMDDATA		},
	{ "rset",	CMDRSET		},
	{ "vrfy",	CMDVRFY		},
	{ "expn",	CMDEXPN		},
	{ "help",	CMDHELP		},
	{ "noop",	CMDNOOP		},
	{ "quit",	CMDQUIT		},
	{ "helo",	CMDHELO		},
	{ "ehlo",	CMDEHLO		},
	{ "etrn",	CMDETRN		},
	{ "verb",	CMDVERB		},
	{ "onex",	CMDONEX		},
	{ "xusr",	CMDXUSR		},
	{ "send",	CMDUNIMPL	},
	{ "saml",	CMDUNIMPL	},
	{ "soml",	CMDUNIMPL	},
	{ "turn",	CMDUNIMPL	},
# if SASL
	{ "auth",	CMDAUTH,	},
# endif /* SASL */
# if STARTTLS
	{ "starttls",	CMDSTLS,	},
# endif /* STARTTLS */
    /* remaining commands are here only to trap and log attempts to use them */
	{ "showq",	CMDDBGQSHOW	},
	{ "debug",	CMDDBGDEBUG	},
	{ "wiz",	CMDLOGBOGUS	},

	{ NULL,		CMDERROR	}
};

static bool	OneXact = FALSE;	/* one xaction only this run */
static char	*CurSmtpClient;		/* who's at the other end of channel */

# define MAXBADCOMMANDS		25	/* maximum number of bad commands */
# define MAXNOOPCOMMANDS	20	/* max "noise" commands before slowdown */
# define MAXHELOCOMMANDS	3	/* max HELO/EHLO commands before slowdown */
# define MAXVRFYCOMMANDS	6	/* max VRFY/EXPN commands before slowdown */
# define MAXETRNCOMMANDS	8	/* max ETRN commands before slowdown */
# define MAXTIMEOUT	(4 * 60)	/* max timeout for bad commands */

/* runinchild() returns */
# define RIC_INCHILD		0	/* in a child process */
# define RIC_INPARENT		1	/* still in parent process */
# define RIC_TEMPFAIL		2	/* temporary failure occurred */

void
smtp(nullserver, d_flags, e)
	char *volatile nullserver;
	BITMAP256 d_flags;
	register ENVELOPE *volatile e;
{
	register char *volatile p;
	register struct cmd *volatile c = NULL;
	char *cmd;
	auto ADDRESS *vrfyqueue;
	ADDRESS *a;
	volatile bool gotmail;		/* mail command received */
	volatile bool gothello;		/* helo command received */
	bool vrfy;			/* set if this is a vrfy command */
	char *volatile protocol;	/* sending protocol */
	char *volatile sendinghost;	/* sending hostname */
	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
	auto char *delimptr;
	char *id;
	volatile int nrcpts = 0;	/* number of RCPT commands */
	int ric;
	bool doublequeue;
	volatile bool discard;
	volatile int badcommands = 0;	/* count of bad commands */
	volatile int nverifies = 0;	/* count of VRFY/EXPN commands */
	volatile int n_etrn = 0;	/* count of ETRN commands */
	volatile int n_noop = 0;	/* count of NOOP/VERB/ONEX etc cmds */
	volatile int n_helo = 0;	/* count of HELO/EHLO commands */
	volatile int delay = 1;		/* timeout for bad commands */
	bool ok;
	volatile bool tempfail = FALSE;
# if _FFR_MILTER
	volatile bool milterize = (nullserver == NULL);
# endif /* _FFR_MILTER */
	volatile time_t wt;		/* timeout after too many commands */
	volatile time_t previous;	/* time after checksmtpattack() */
	volatile bool lognullconnection = TRUE;
	register char *q;
	char *addr;
	char *greetcode = "220";
	QUEUE_CHAR *new;
	int argno;
	char *args[MAXSMTPARGS];
	char inp[MAXLINE];
	char cmdbuf[MAXLINE];
# if SASL
	sasl_conn_t *conn;
	volatile bool sasl_ok;
	volatile int n_auth = 0;	/* count of AUTH commands */
	bool ismore;
	int result;
	volatile int authenticating;
	char *hostname;
	char *user;
	char *in, *out, *out2;
	const char *errstr;
	int inlen, out2len;
	unsigned int outlen;
	char *volatile auth_type;
	char *mechlist;
	volatile int n_mechs;
	int len;
	sasl_security_properties_t ssp;
	sasl_external_properties_t ext_ssf;
#  if SFIO
	sasl_ssf_t *ssf;
#  endif /* SFIO */
# endif /* SASL */
# if STARTTLS
	int r;
	int rfd, wfd;
	volatile bool usetls = TRUE;
	volatile bool tls_active = FALSE;
	bool saveQuickAbort;
	bool saveSuprErrs;
# endif /* STARTTLS */

	if (fileno(OutChannel) != fileno(stdout))
	{
		/* arrange for debugging output to go to remote host */
		(void) dup2(fileno(OutChannel), fileno(stdout));
	}

	settime(e);
	(void)sm_getla(e);
	peerhostname = RealHostName;
	if (peerhostname == NULL)
		peerhostname = "localhost";
	CurHostName = peerhostname;
	CurSmtpClient = macvalue('_', e);
	if (CurSmtpClient == NULL)
		CurSmtpClient = CurHostName;

	/* check_relay may have set discard bit, save for later */
	discard = bitset(EF_DISCARD, e->e_flags);

	sm_setproctitle(TRUE, e, "server %s startup", CurSmtpClient);

# if SASL
	sasl_ok = FALSE;	/* SASL can't be used (yet) */
	n_mechs = 0;

	/* SASL server new connection */
	hostname = macvalue('j', e);
#  if SASL > 10505
	/* use empty realm: only works in SASL > 1.5.5 */
	result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn);
#  else /* SASL > 10505 */
	/* use no realm -> realm is set to hostname by SASL lib */
	result = sasl_server_new("smtp", hostname, NULL, NULL, 0, &conn);
#  endif /* SASL > 10505 */
	if (result == SASL_OK)
	{
		sasl_ok = TRUE;

		/*
		**  SASL set properties for sasl
		**  set local/remote IP
		**  XXX only IPv4: Cyrus SASL doesn't support anything else
		**
		**  XXX where exactly are these used/required?
		**  Kerberos_v4
		*/

# if NETINET
		in = macvalue(macid("{daemon_family}", NULL), e);
		if (in != NULL && strcmp(in, "inet") == 0)
		{
			SOCKADDR_LEN_T addrsize;
			struct sockaddr_in saddr_l;
			struct sockaddr_in saddr_r;

			addrsize = sizeof(struct sockaddr_in);
			if (getpeername(fileno(InChannel),
					(struct sockaddr *)&saddr_r,
					&addrsize) == 0)
			{
				sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
				addrsize = sizeof(struct sockaddr_in);
				if (getsockname(fileno(InChannel),
						(struct sockaddr *)&saddr_l,
						&addrsize) == 0)
					sasl_setprop(conn, SASL_IP_LOCAL,
						     &saddr_l);
			}
		}
# endif /* NETINET */

		authenticating = SASL_NOT_AUTH;
		auth_type = NULL;
		mechlist = NULL;
		user = NULL;
#  if 0
		define(macid("{auth_author}", NULL), NULL, &BlankEnvelope);
#  endif /* 0 */

		/* set properties */
		(void) memset(&ssp, '\0', sizeof ssp);
#  if SFIO
		/* XXX should these be options settable via .cf ? */
		/* ssp.min_ssf = 0; is default due to memset() */
		{
			ssp.max_ssf = INT_MAX;
			ssp.maxbufsize = MAXOUTLEN;
		}
#  endif /* SFIO */
#  if _FFR_SASL_OPTS
		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
#  endif /* _FFR_SASL_OPTS */
		sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;

		if (sasl_ok)
		{
			/*
			**  external security strength factor;
			**  we have none so zero
#   if STARTTLS
			**  we may have to change this for STARTTLS
			**  (dynamically)
#   endif
			*/
			ext_ssf.ssf = 0;
			ext_ssf.auth_id = NULL;
			sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
					       &ext_ssf) == SASL_OK;
		}
		if (sasl_ok)
		{
			n_mechs = saslmechs(conn, &mechlist);
			sasl_ok = n_mechs > 0;
		}
	}
	else
	{
		if (LogLevel > 9)
			sm_syslog(LOG_WARNING, NOQID,
				  "SASL error: sasl_server_new failed=%d",
				  result);
	}
# endif /* SASL */

# if STARTTLS
#  if _FFR_TLS_O_T
	saveQuickAbort = QuickAbort;
	saveSuprErrs = SuprErrs;
	SuprErrs = TRUE;
	QuickAbort = FALSE;
	if (rscheck("offer_tls", CurSmtpClient, "", e, TRUE, FALSE, 8) != EX_OK
		    || Errors > 0)
		usetls = FALSE;
	QuickAbort = saveQuickAbort;
	SuprErrs = saveSuprErrs;
#  endif /* _FFR_TLS_O_T */
# endif /* STARTTLS */

# if _FFR_MILTER
	if (milterize)
	{
		char state;

		/* initialize mail filter connection */
		milter_init(e, &state);
		switch (state)
		{
		  case SMFIR_REJECT:
			greetcode = "554";
			nullserver = "Command rejected";
			milterize = FALSE;
			break;

		  case SMFIR_TEMPFAIL:
			tempfail = TRUE;
			milterize = FALSE;
			break;
		}
	}

	if (milterize && !bitset(EF_DISCARD, e->e_flags))
	{
		char state;

		(void) milter_connect(peerhostname, RealHostAddr,
				      e, &state);
		switch (state)
		{
		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
		  case SMFIR_REJECT:
			greetcode = "554";
			nullserver = "Command rejected";
			milterize = FALSE;
			break;

		  case SMFIR_TEMPFAIL:
			tempfail = TRUE;
			milterize = FALSE;
			break;
		}
	}
# endif /* _FFR_MILTER */

	/* output the first line, inserting "ESMTP" as second word */
	expand(SmtpGreeting, inp, sizeof inp, e);
	p = strchr(inp, '\n');
	if (p != NULL)
		*p++ = '\0';
	id = strchr(inp, ' ');
	if (id == NULL)
		id = &inp[strlen(inp)];
	if (p == NULL)
		snprintf(cmdbuf, sizeof cmdbuf,
			 "%s %%.*s ESMTP%%s", greetcode);
	else
		snprintf(cmdbuf, sizeof cmdbuf,
			 "%s-%%.*s ESMTP%%s", greetcode);
	message(cmdbuf, (int) (id - inp), inp, id);

	/* output remaining lines */
	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
	{
		*p++ = '\0';
		if (isascii(*id) && isspace(*id))
			id++;
		(void) snprintf(cmdbuf, sizeof cmdbuf, "%s-%%s", greetcode);
		message(cmdbuf, id);
	}
	if (id != NULL)
	{
		if (isascii(*id) && isspace(*id))
			id++;
		(void) snprintf(cmdbuf, sizeof cmdbuf, "%s %%s", greetcode);
		message(cmdbuf, id);
	}

	protocol = NULL;
	sendinghost = macvalue('s', e);
	gothello = FALSE;
	gotmail = FALSE;
	for (;;)
	{
		/* arrange for backout */
		(void) setjmp(TopFrame);
		QuickAbort = FALSE;
		HoldErrs = FALSE;
		SuprErrs = FALSE;
		LogUsrErrs = FALSE;
		OnlyOneError = TRUE;
		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);

		/* setup for the read */
		e->e_to = NULL;
		Errors = 0;
		FileName = NULL;
		(void) fflush(stdout);

		/* read the input line */
		SmtpPhase = "server cmd read";
		sm_setproctitle(TRUE, e, "server %s cmd read", CurSmtpClient);
# if SASL
		/*
		**  SMTP AUTH requires accepting any length,
		**  at least for challenge/response
		**  XXX
		*/
# endif /* SASL */

		/* handle errors */
		if (ferror(OutChannel) ||
		    (p = sfgets(inp, sizeof inp, InChannel,
				TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
		{
			char *d;

			d = macvalue(macid("{daemon_name}", NULL), e);
			if (d == NULL)
				d = "stdin";
			/* end of file, just die */
			disconnect(1, e);

# if _FFR_MILTER
			/* close out milter filters */
			milter_quit(e);
# endif /* _FFR_MILTER */

			message("421 4.4.1 %s Lost input channel from %s",
				MyHostName, CurSmtpClient);
			if (LogLevel > (gotmail ? 1 : 19))
				sm_syslog(LOG_NOTICE, e->e_id,
					  "lost input channel from %.100s to %s after %s",
					  CurSmtpClient, d,
					  (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
			/*
			**  If have not accepted mail (DATA), do not bounce
			**  bad addresses back to sender.
			*/

			if (bitset(EF_CLRQUEUE, e->e_flags))
				e->e_sendqueue = NULL;
			goto doquit;
		}

		/* clean up end of line */
		fixcrlf(inp, TRUE);

⌨️ 快捷键说明

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