milter.c revision 94334
164562Sgshapiro/*
294334Sgshapiro * Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
464562Sgshapiro *
564562Sgshapiro * By using this file, you agree to the terms and conditions set
664562Sgshapiro * forth in the LICENSE file which can be found at the top level of
764562Sgshapiro * the sendmail distribution.
864562Sgshapiro *
964562Sgshapiro */
1064562Sgshapiro
1190792Sgshapiro#include <sendmail.h>
1264562Sgshapiro
1394334SgshapiroSM_RCSID("@(#)$Id: milter.c,v 8.194 2002/03/05 00:23:47 gshapiro Exp $")
1464562Sgshapiro
1590792Sgshapiro#if MILTER
1690792Sgshapiro# include <libmilter/mfapi.h>
1790792Sgshapiro# include <libmilter/mfdef.h>
1890792Sgshapiro
1964562Sgshapiro# include <errno.h>
2064562Sgshapiro# include <sys/time.h>
2164562Sgshapiro
2264562Sgshapiro# if NETINET || NETINET6
2364562Sgshapiro#  include <arpa/inet.h>
2464562Sgshapiro# endif /* NETINET || NETINET6 */
2564562Sgshapiro
2690792Sgshapiro# include <sm/fdset.h>
2764562Sgshapiro
2880785Sgshapirostatic void	milter_connect_timeout __P((void));
2990792Sgshapirostatic void	milter_error __P((struct milter *, ENVELOPE *));
3064562Sgshapirostatic int	milter_open __P((struct milter *, bool, ENVELOPE *));
3164562Sgshapirostatic void	milter_parse_timeouts __P((char *, struct milter *));
3264562Sgshapiro
3364562Sgshapirostatic char *MilterConnectMacros[MAXFILTERMACROS + 1];
3464562Sgshapirostatic char *MilterHeloMacros[MAXFILTERMACROS + 1];
3564562Sgshapirostatic char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
3664562Sgshapirostatic char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
3764562Sgshapiro
3864562Sgshapiro# define MILTER_CHECK_DONE_MSG() \
3964562Sgshapiro	if (*state == SMFIR_REPLYCODE || \
4064562Sgshapiro	    *state == SMFIR_REJECT || \
4164562Sgshapiro	    *state == SMFIR_DISCARD || \
4264562Sgshapiro	    *state == SMFIR_TEMPFAIL) \
4364562Sgshapiro	{ \
4464562Sgshapiro		/* Abort the filters to let them know we are done with msg */ \
4564562Sgshapiro		milter_abort(e); \
4664562Sgshapiro	}
4764562Sgshapiro
4864562Sgshapiro# define MILTER_CHECK_ERROR(action) \
4964562Sgshapiro	if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
5064562Sgshapiro		*state = SMFIR_TEMPFAIL; \
5164562Sgshapiro	else if (bitnset(SMF_REJECT, m->mf_flags)) \
5264562Sgshapiro		*state = SMFIR_REJECT; \
5364562Sgshapiro	else \
5464562Sgshapiro		action;
5564562Sgshapiro
5664562Sgshapiro# define MILTER_CHECK_REPLYCODE(default) \
5764562Sgshapiro	if (response == NULL || \
5864562Sgshapiro	    strlen(response) + 1 != (size_t) rlen || \
5964562Sgshapiro	    rlen < 3 || \
6064562Sgshapiro	    (response[0] != '4' && response[0] != '5') || \
6164562Sgshapiro	    !isascii(response[1]) || !isdigit(response[1]) || \
6264562Sgshapiro	    !isascii(response[2]) || !isdigit(response[2])) \
6364562Sgshapiro	{ \
6464562Sgshapiro		if (response != NULL) \
6590792Sgshapiro			sm_free(response); /* XXX */ \
6664562Sgshapiro		response = newstr(default); \
6764562Sgshapiro	} \
6864562Sgshapiro	else \
6964562Sgshapiro	{ \
7064562Sgshapiro		char *ptr = response; \
7164562Sgshapiro \
7264562Sgshapiro		/* Check for unprotected %'s in the string */ \
7364562Sgshapiro		while (*ptr != '\0') \
7464562Sgshapiro		{ \
7564562Sgshapiro			if (*ptr == '%' && *++ptr != '%') \
7664562Sgshapiro			{ \
7790792Sgshapiro				sm_free(response); /* XXX */ \
7864562Sgshapiro				response = newstr(default); \
7964562Sgshapiro				break; \
8064562Sgshapiro			} \
8164562Sgshapiro			ptr++; \
8264562Sgshapiro		} \
8364562Sgshapiro	}
8464562Sgshapiro
8564562Sgshapiro# define MILTER_DF_ERROR(msg) \
8664562Sgshapiro{ \
8764562Sgshapiro	int save_errno = errno; \
8864562Sgshapiro \
8964562Sgshapiro	if (tTd(64, 5)) \
9064562Sgshapiro	{ \
9190792Sgshapiro		sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
9290792Sgshapiro		sm_dprintf("\n"); \
9364562Sgshapiro	} \
9490792Sgshapiro	if (MilterLogLevel > 0) \
9590792Sgshapiro		sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
9690792Sgshapiro	if (SuperSafe == SAFE_REALLY) \
9764562Sgshapiro	{ \
9864562Sgshapiro		if (e->e_dfp != NULL) \
9964562Sgshapiro		{ \
10090792Sgshapiro			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
10164562Sgshapiro			e->e_dfp = NULL; \
10264562Sgshapiro		} \
10364562Sgshapiro		e->e_flags &= ~EF_HAS_DF; \
10464562Sgshapiro	} \
10564562Sgshapiro	errno = save_errno; \
10664562Sgshapiro}
10764562Sgshapiro
10864562Sgshapiro/*
10964562Sgshapiro**  MILTER_TIMEOUT -- make sure socket is ready in time
11064562Sgshapiro**
11164562Sgshapiro**	Parameters:
11264562Sgshapiro**		routine -- routine name for debug/logging
11364562Sgshapiro**		secs -- number of seconds in timeout
11464562Sgshapiro**		write -- waiting to read or write?
11590792Sgshapiro**		started -- whether this is part of a previous sequence
11664562Sgshapiro**
11764562Sgshapiro**	Assumes 'm' is a milter structure for the current socket.
11864562Sgshapiro*/
11964562Sgshapiro
12090792Sgshapiro# define MILTER_TIMEOUT(routine, secs, write, started) \
12164562Sgshapiro{ \
12264562Sgshapiro	int ret; \
12364562Sgshapiro	int save_errno; \
12464562Sgshapiro	fd_set fds; \
12564562Sgshapiro	struct timeval tv; \
12664562Sgshapiro \
12790792Sgshapiro	if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
12864562Sgshapiro	{ \
12964562Sgshapiro		if (tTd(64, 5)) \
13090792Sgshapiro			sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
13190792Sgshapiro				   (routine), m->mf_name, m->mf_sock, \
13290792Sgshapiro				   SM_FD_SETSIZE); \
13390792Sgshapiro		if (MilterLogLevel > 0) \
13464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
13590792Sgshapiro				  "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
13690792Sgshapiro				  m->mf_name, (routine), m->mf_sock, \
13790792Sgshapiro				  SM_FD_SETSIZE); \
13890792Sgshapiro		milter_error(m, e); \
13964562Sgshapiro		return NULL; \
14064562Sgshapiro	} \
14164562Sgshapiro \
14294334Sgshapiro	do \
14394334Sgshapiro	{ \
14494334Sgshapiro		FD_ZERO(&fds); \
14594334Sgshapiro		SM_FD_SET(m->mf_sock, &fds); \
14694334Sgshapiro		tv.tv_sec = (secs); \
14794334Sgshapiro		tv.tv_usec = 0; \
14894334Sgshapiro		ret = select(m->mf_sock + 1, \
14994334Sgshapiro			     (write) ? NULL : &fds, \
15094334Sgshapiro			     (write) ? &fds : NULL, \
15194334Sgshapiro			     NULL, &tv); \
15294334Sgshapiro	} while (ret < 0 && errno == EINTR); \
15364562Sgshapiro \
15464562Sgshapiro	switch (ret) \
15564562Sgshapiro	{ \
15664562Sgshapiro	  case 0: \
15764562Sgshapiro		if (tTd(64, 5)) \
15890792Sgshapiro			sm_dprintf("milter_%s(%s): timeout\n", (routine), \
15990792Sgshapiro				   m->mf_name); \
16090792Sgshapiro		if (MilterLogLevel > 0) \
16190792Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
16290792Sgshapiro				  "Milter (%s): %s %s %s %s", \
16390792Sgshapiro				  m->mf_name, "timeout", \
16490792Sgshapiro				  started ? "during" : "before", \
16590792Sgshapiro				  "data", (routine)); \
16690792Sgshapiro		milter_error(m, e); \
16764562Sgshapiro		return NULL; \
16864562Sgshapiro \
16964562Sgshapiro	  case -1: \
17064562Sgshapiro		save_errno = errno; \
17164562Sgshapiro		if (tTd(64, 5)) \
17290792Sgshapiro			sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
17390792Sgshapiro				   m->mf_name, sm_errstring(save_errno)); \
17490792Sgshapiro		if (MilterLogLevel > 0) \
17590792Sgshapiro		{ \
17664562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
17790792Sgshapiro				  "Milter (%s): select(%s): %s", \
17890792Sgshapiro				  m->mf_name, (routine), \
17990792Sgshapiro				  sm_errstring(save_errno)); \
18090792Sgshapiro		} \
18190792Sgshapiro		milter_error(m, e); \
18264562Sgshapiro		return NULL; \
18364562Sgshapiro \
18464562Sgshapiro	  default: \
18571345Sgshapiro		if (SM_FD_ISSET(m->mf_sock, &fds)) \
18664562Sgshapiro			break; \
18764562Sgshapiro		if (tTd(64, 5)) \
18890792Sgshapiro			sm_dprintf("milter_%s(%s): socket not ready\n", \
18990792Sgshapiro				(routine), m->mf_name); \
19090792Sgshapiro		if (MilterLogLevel > 0) \
19190792Sgshapiro		{ \
19264562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
19390792Sgshapiro				  "Milter (%s): socket(%s) not ready", \
19490792Sgshapiro				  m->mf_name, (routine)); \
19590792Sgshapiro		} \
19690792Sgshapiro		milter_error(m, e); \
19764562Sgshapiro		return NULL; \
19864562Sgshapiro	} \
19964562Sgshapiro}
20064562Sgshapiro
20164562Sgshapiro/*
20264562Sgshapiro**  Low level functions
20364562Sgshapiro*/
20464562Sgshapiro
20590792Sgshapiro/*
20664562Sgshapiro**  MILTER_READ -- read from a remote milter filter
20764562Sgshapiro**
20864562Sgshapiro**	Parameters:
20964562Sgshapiro**		m -- milter to read from.
21064562Sgshapiro**		cmd -- return param for command read.
21164562Sgshapiro**		rlen -- return length of response string.
21264562Sgshapiro**		to -- timeout in seconds.
21364562Sgshapiro**		e -- current envelope.
21464562Sgshapiro**
21564562Sgshapiro**	Returns:
21664562Sgshapiro**		response string (may be NULL)
21764562Sgshapiro*/
21864562Sgshapiro
21964562Sgshapirostatic char *
22064562Sgshapiromilter_sysread(m, buf, sz, to, e)
22164562Sgshapiro	struct milter *m;
22264562Sgshapiro	char *buf;
22364562Sgshapiro	ssize_t sz;
22464562Sgshapiro	time_t to;
22564562Sgshapiro	ENVELOPE *e;
22664562Sgshapiro{
22766494Sgshapiro	time_t readstart = 0;
22864562Sgshapiro	ssize_t len, curl;
22990792Sgshapiro	bool started = false;
23064562Sgshapiro
23164562Sgshapiro	curl = 0;
23264562Sgshapiro
23364562Sgshapiro	if (to > 0)
23464562Sgshapiro		readstart = curtime();
23564562Sgshapiro
23664562Sgshapiro	for (;;)
23764562Sgshapiro	{
23864562Sgshapiro		if (to > 0)
23964562Sgshapiro		{
24064562Sgshapiro			time_t now;
24164562Sgshapiro
24264562Sgshapiro			now = curtime();
24364562Sgshapiro			if (now - readstart >= to)
24464562Sgshapiro			{
24564562Sgshapiro				if (tTd(64, 5))
24690792Sgshapiro					sm_dprintf("milter_read (%s): %s %s %s",
24790792Sgshapiro						  m->mf_name, "timeout",
24890792Sgshapiro						  started ? "during" : "before",
24990792Sgshapiro						  "data read");
25090792Sgshapiro				if (MilterLogLevel > 0)
25164562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
25290792Sgshapiro						  "Milter (%s): %s %s %s",
25390792Sgshapiro						  m->mf_name, "timeout",
25490792Sgshapiro						  started ? "during" : "before",
25590792Sgshapiro						  "data read");
25690792Sgshapiro				milter_error(m, e);
25764562Sgshapiro				return NULL;
25864562Sgshapiro			}
25964562Sgshapiro			to -= now - readstart;
26064562Sgshapiro			readstart = now;
26190792Sgshapiro			MILTER_TIMEOUT("read", to, false, started);
26264562Sgshapiro		}
26364562Sgshapiro
26464562Sgshapiro		len = read(m->mf_sock, buf + curl, sz - curl);
26564562Sgshapiro
26664562Sgshapiro		if (len < 0)
26764562Sgshapiro		{
26864562Sgshapiro			int save_errno = errno;
26964562Sgshapiro
27064562Sgshapiro			if (tTd(64, 5))
27190792Sgshapiro				sm_dprintf("milter_read(%s): read returned %ld: %s\n",
27264562Sgshapiro					m->mf_name, (long) len,
27390792Sgshapiro					sm_errstring(save_errno));
27490792Sgshapiro			if (MilterLogLevel > 0)
27564562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
27690792Sgshapiro					  "Milter (%s): read returned %ld: %s",
27764562Sgshapiro					  m->mf_name, (long) len,
27890792Sgshapiro					  sm_errstring(save_errno));
27990792Sgshapiro			milter_error(m, e);
28064562Sgshapiro			return NULL;
28164562Sgshapiro		}
28264562Sgshapiro
28390792Sgshapiro		started = true;
28464562Sgshapiro		curl += len;
28573188Sgshapiro		if (len == 0 || curl >= sz)
28664562Sgshapiro			break;
28764562Sgshapiro
28864562Sgshapiro	}
28964562Sgshapiro
29064562Sgshapiro	if (curl != sz)
29164562Sgshapiro	{
29264562Sgshapiro		if (tTd(64, 5))
29390792Sgshapiro			sm_dprintf("milter_read(%s): cmd read returned %ld, expecting %ld\n",
29464562Sgshapiro				m->mf_name, (long) curl, (long) sz);
29590792Sgshapiro		if (MilterLogLevel > 0)
29664562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
29790792Sgshapiro				  "milter_read(%s): cmd read returned %ld, expecting %ld",
29864562Sgshapiro				  m->mf_name, (long) curl, (long) sz);
29990792Sgshapiro		milter_error(m, e);
30064562Sgshapiro		return NULL;
30164562Sgshapiro	}
30264562Sgshapiro	return buf;
30364562Sgshapiro}
30464562Sgshapiro
30564562Sgshapirostatic char *
30664562Sgshapiromilter_read(m, cmd, rlen, to, e)
30764562Sgshapiro	struct milter *m;
30864562Sgshapiro	char *cmd;
30964562Sgshapiro	ssize_t *rlen;
31064562Sgshapiro	time_t to;
31164562Sgshapiro	ENVELOPE *e;
31264562Sgshapiro{
31366494Sgshapiro	time_t readstart = 0;
31464562Sgshapiro	ssize_t expl;
31564562Sgshapiro	mi_int32 i;
31664562Sgshapiro	char *buf;
31764562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
31864562Sgshapiro
31964562Sgshapiro	*rlen = 0;
32064562Sgshapiro	*cmd = '\0';
32164562Sgshapiro
32264562Sgshapiro	if (to > 0)
32364562Sgshapiro		readstart = curtime();
32464562Sgshapiro
32564562Sgshapiro	if (milter_sysread(m, data, sizeof data, to, e) == NULL)
32664562Sgshapiro		return NULL;
32764562Sgshapiro
32864562Sgshapiro	/* reset timeout */
32964562Sgshapiro	if (to > 0)
33064562Sgshapiro	{
33164562Sgshapiro		time_t now;
33264562Sgshapiro
33364562Sgshapiro		now = curtime();
33464562Sgshapiro		if (now - readstart >= to)
33564562Sgshapiro		{
33664562Sgshapiro			if (tTd(64, 5))
33790792Sgshapiro				sm_dprintf("milter_read(%s): timeout before data read\n",
33864562Sgshapiro					m->mf_name);
33990792Sgshapiro			if (MilterLogLevel > 0)
34064562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
34190792Sgshapiro					  "Milter read(%s): timeout before data read",
34264562Sgshapiro					  m->mf_name);
34390792Sgshapiro			milter_error(m, e);
34464562Sgshapiro			return NULL;
34564562Sgshapiro		}
34664562Sgshapiro		to -= now - readstart;
34764562Sgshapiro	}
34864562Sgshapiro
34964562Sgshapiro	*cmd = data[MILTER_LEN_BYTES];
35064562Sgshapiro	data[MILTER_LEN_BYTES] = '\0';
35164562Sgshapiro	(void) memcpy(&i, data, MILTER_LEN_BYTES);
35264562Sgshapiro	expl = ntohl(i) - 1;
35364562Sgshapiro
35464562Sgshapiro	if (tTd(64, 25))
35590792Sgshapiro		sm_dprintf("milter_read(%s): expecting %ld bytes\n",
35664562Sgshapiro			m->mf_name, (long) expl);
35764562Sgshapiro
35864562Sgshapiro	if (expl < 0)
35964562Sgshapiro	{
36064562Sgshapiro		if (tTd(64, 5))
36190792Sgshapiro			sm_dprintf("milter_read(%s): read size %ld out of range\n",
36264562Sgshapiro				m->mf_name, (long) expl);
36390792Sgshapiro		if (MilterLogLevel > 0)
36464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
36564562Sgshapiro				  "milter_read(%s): read size %ld out of range",
36664562Sgshapiro				  m->mf_name, (long) expl);
36790792Sgshapiro		milter_error(m, e);
36864562Sgshapiro		return NULL;
36964562Sgshapiro	}
37064562Sgshapiro
37164562Sgshapiro	if (expl == 0)
37264562Sgshapiro		return NULL;
37364562Sgshapiro
37490792Sgshapiro	buf = (char *) xalloc(expl);
37564562Sgshapiro
37664562Sgshapiro	if (milter_sysread(m, buf, expl, to, e) == NULL)
37764562Sgshapiro	{
37890792Sgshapiro		sm_free(buf); /* XXX */
37964562Sgshapiro		return NULL;
38064562Sgshapiro	}
38164562Sgshapiro
38264562Sgshapiro	if (tTd(64, 50))
38390792Sgshapiro		sm_dprintf("milter_read(%s): Returning %*s\n",
38464562Sgshapiro			m->mf_name, (int) expl, buf);
38564562Sgshapiro	*rlen = expl;
38664562Sgshapiro	return buf;
38764562Sgshapiro}
38890792Sgshapiro/*
38964562Sgshapiro**  MILTER_WRITE -- write to a remote milter filter
39064562Sgshapiro**
39164562Sgshapiro**	Parameters:
39264562Sgshapiro**		m -- milter to read from.
39364562Sgshapiro**		cmd -- command to send.
39464562Sgshapiro**		buf -- optional command data.
39564562Sgshapiro**		len -- length of buf.
39664562Sgshapiro**		to -- timeout in seconds.
39764562Sgshapiro**		e -- current envelope.
39864562Sgshapiro**
39964562Sgshapiro**	Returns:
40064562Sgshapiro**		buf if successful, NULL otherwise
40164562Sgshapiro**		Not actually used anywhere but function prototype
40264562Sgshapiro**			must match milter_read()
40364562Sgshapiro*/
40464562Sgshapiro
40564562Sgshapirostatic char *
40664562Sgshapiromilter_write(m, cmd, buf, len, to, e)
40764562Sgshapiro	struct milter *m;
40864562Sgshapiro	char cmd;
40964562Sgshapiro	char *buf;
41064562Sgshapiro	ssize_t len;
41164562Sgshapiro	time_t to;
41264562Sgshapiro	ENVELOPE *e;
41364562Sgshapiro{
41464562Sgshapiro	time_t writestart = (time_t) 0;
41564562Sgshapiro	ssize_t sl, i;
41664562Sgshapiro	mi_int32 nl;
41764562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
41890792Sgshapiro	bool started = false;
41964562Sgshapiro
42064562Sgshapiro	if (len < 0 || len > MILTER_CHUNK_SIZE)
42164562Sgshapiro	{
42264562Sgshapiro		if (tTd(64, 5))
42390792Sgshapiro			sm_dprintf("milter_write(%s): length %ld out of range\n",
42464562Sgshapiro				m->mf_name, (long) len);
42590792Sgshapiro		if (MilterLogLevel > 0)
42664562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
42764562Sgshapiro				  "milter_write(%s): length %ld out of range",
42864562Sgshapiro				  m->mf_name, (long) len);
42990792Sgshapiro		milter_error(m, e);
43064562Sgshapiro		return NULL;
43164562Sgshapiro	}
43264562Sgshapiro
43364562Sgshapiro	if (tTd(64, 20))
43490792Sgshapiro		sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
43590792Sgshapiro			   m->mf_name, cmd, (long) len);
43664562Sgshapiro
43764562Sgshapiro	nl = htonl(len + 1);	/* add 1 for the cmd char */
43864562Sgshapiro	(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
43964562Sgshapiro	data[MILTER_LEN_BYTES] = cmd;
44064562Sgshapiro	sl = MILTER_LEN_BYTES + 1;
44164562Sgshapiro
44264562Sgshapiro	if (to > 0)
44364562Sgshapiro	{
44464562Sgshapiro		writestart = curtime();
44590792Sgshapiro		MILTER_TIMEOUT("write", to, true, started);
44664562Sgshapiro	}
44764562Sgshapiro
44864562Sgshapiro	/* use writev() instead to send the whole stuff at once? */
44964562Sgshapiro	i = write(m->mf_sock, (void *) data, sl);
45064562Sgshapiro	if (i != sl)
45164562Sgshapiro	{
45264562Sgshapiro		int save_errno = errno;
45364562Sgshapiro
45464562Sgshapiro		if (tTd(64, 5))
45590792Sgshapiro			sm_dprintf("milter_write (%s): write(%c) returned %ld, expected %ld: %s\n",
45690792Sgshapiro				   m->mf_name, cmd, (long) i, (long) sl,
45790792Sgshapiro				   sm_errstring(save_errno));
45890792Sgshapiro		if (MilterLogLevel > 0)
45964562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
46090792Sgshapiro				  "Milter (%s): write(%c) returned %ld, expected %ld: %s",
46164562Sgshapiro				  m->mf_name, cmd, (long) i, (long) sl,
46290792Sgshapiro				  sm_errstring(save_errno));
46390792Sgshapiro		milter_error(m, e);
46464562Sgshapiro		return buf;
46564562Sgshapiro	}
46664562Sgshapiro
46764562Sgshapiro	if (len <= 0 || buf == NULL)
46864562Sgshapiro		return buf;
46964562Sgshapiro
47064562Sgshapiro	if (tTd(64, 50))
47190792Sgshapiro		sm_dprintf("milter_write(%s): Sending %*s\n",
47290792Sgshapiro			   m->mf_name, (int) len, buf);
47390792Sgshapiro	started = true;
47464562Sgshapiro
47564562Sgshapiro	if (to > 0)
47664562Sgshapiro	{
47764562Sgshapiro		time_t now;
47864562Sgshapiro
47964562Sgshapiro		now = curtime();
48064562Sgshapiro		if (now - writestart >= to)
48164562Sgshapiro		{
48264562Sgshapiro			if (tTd(64, 5))
48390792Sgshapiro				sm_dprintf("milter_write(%s): timeout before data write\n",
48490792Sgshapiro					   m->mf_name);
48590792Sgshapiro			if (MilterLogLevel > 0)
48664562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
48790792Sgshapiro					  "Milter (%s): timeout before data write",
48864562Sgshapiro					  m->mf_name);
48990792Sgshapiro			milter_error(m, e);
49064562Sgshapiro			return NULL;
49164562Sgshapiro		}
49264562Sgshapiro		else
49364562Sgshapiro		{
49464562Sgshapiro			to -= now - writestart;
49590792Sgshapiro			MILTER_TIMEOUT("write", to, true, started);
49664562Sgshapiro		}
49764562Sgshapiro	}
49864562Sgshapiro
49964562Sgshapiro	i = write(m->mf_sock, (void *) buf, len);
50064562Sgshapiro	if (i != len)
50164562Sgshapiro	{
50264562Sgshapiro		int save_errno = errno;
50364562Sgshapiro
50464562Sgshapiro		if (tTd(64, 5))
50590792Sgshapiro			sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
50690792Sgshapiro				   m->mf_name, cmd, (long) i, (long) sl,
50790792Sgshapiro				   sm_errstring(save_errno));
50890792Sgshapiro		if (MilterLogLevel > 0)
50964562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
51090792Sgshapiro				  "Milter (%s): write(%c) returned %ld, expected %ld: %s",
51164562Sgshapiro				  m->mf_name, cmd, (long) i, (long) len,
51290792Sgshapiro				  sm_errstring(save_errno));
51390792Sgshapiro		milter_error(m, e);
51464562Sgshapiro		return NULL;
51564562Sgshapiro	}
51664562Sgshapiro	return buf;
51764562Sgshapiro}
51864562Sgshapiro
51964562Sgshapiro/*
52064562Sgshapiro**  Utility functions
52164562Sgshapiro*/
52264562Sgshapiro
52390792Sgshapiro/*
52464562Sgshapiro**  MILTER_OPEN -- connect to remote milter filter
52564562Sgshapiro**
52664562Sgshapiro**	Parameters:
52764562Sgshapiro**		m -- milter to connect to.
52864562Sgshapiro**		parseonly -- parse but don't connect.
52964562Sgshapiro**		e -- current envelope.
53064562Sgshapiro**
53164562Sgshapiro**	Returns:
53264562Sgshapiro**		connected socket if sucessful && !parseonly,
53364562Sgshapiro**		0 upon parse success if parseonly,
53464562Sgshapiro**		-1 otherwise.
53564562Sgshapiro*/
53664562Sgshapiro
53780785Sgshapirostatic jmp_buf	MilterConnectTimeout;
53880785Sgshapiro
53964562Sgshapirostatic int
54064562Sgshapiromilter_open(m, parseonly, e)
54164562Sgshapiro	struct milter *m;
54264562Sgshapiro	bool parseonly;
54364562Sgshapiro	ENVELOPE *e;
54464562Sgshapiro{
54564562Sgshapiro	int sock = 0;
54664562Sgshapiro	SOCKADDR_LEN_T addrlen = 0;
54764562Sgshapiro	int addrno = 0;
54864562Sgshapiro	int save_errno;
54964562Sgshapiro	char *p;
55064562Sgshapiro	char *colon;
55164562Sgshapiro	char *at;
55264562Sgshapiro	struct hostent *hp = NULL;
55364562Sgshapiro	SOCKADDR addr;
55464562Sgshapiro
55564562Sgshapiro	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
55664562Sgshapiro	{
55764562Sgshapiro		if (tTd(64, 5))
55890792Sgshapiro			sm_dprintf("X%s: empty or missing socket information\n",
55990792Sgshapiro				   m->mf_name);
56064562Sgshapiro		if (parseonly)
56164562Sgshapiro			syserr("X%s: empty or missing socket information",
56264562Sgshapiro			       m->mf_name);
56390792Sgshapiro		else if (MilterLogLevel > 10)
56464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
56590792Sgshapiro				  "Milter (%s): empty or missing socket information",
56664562Sgshapiro				  m->mf_name);
56790792Sgshapiro		milter_error(m, e);
56864562Sgshapiro		return -1;
56964562Sgshapiro	}
57064562Sgshapiro
57164562Sgshapiro	/* protocol:filename or protocol:port@host */
57294334Sgshapiro	memset(&addr, '\0', sizeof addr);
57364562Sgshapiro	p = m->mf_conn;
57464562Sgshapiro	colon = strchr(p, ':');
57564562Sgshapiro	if (colon != NULL)
57664562Sgshapiro	{
57764562Sgshapiro		*colon = '\0';
57864562Sgshapiro
57964562Sgshapiro		if (*p == '\0')
58064562Sgshapiro		{
58164562Sgshapiro# if NETUNIX
58264562Sgshapiro			/* default to AF_UNIX */
58364562Sgshapiro			addr.sa.sa_family = AF_UNIX;
58464562Sgshapiro# else /* NETUNIX */
58564562Sgshapiro#  if NETINET
58664562Sgshapiro			/* default to AF_INET */
58764562Sgshapiro			addr.sa.sa_family = AF_INET;
58864562Sgshapiro#  else /* NETINET */
58964562Sgshapiro#   if NETINET6
59064562Sgshapiro			/* default to AF_INET6 */
59164562Sgshapiro			addr.sa.sa_family = AF_INET6;
59264562Sgshapiro#   else /* NETINET6 */
59364562Sgshapiro			/* no protocols available */
59464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
59590792Sgshapiro				  "Milter (%s): no valid socket protocols available",
59664562Sgshapiro				  m->mf_name);
59790792Sgshapiro			milter_error(m, e);
59864562Sgshapiro			return -1;
59964562Sgshapiro#   endif /* NETINET6 */
60064562Sgshapiro#  endif /* NETINET */
60164562Sgshapiro# endif /* NETUNIX */
60264562Sgshapiro		}
60364562Sgshapiro# if NETUNIX
60490792Sgshapiro		else if (sm_strcasecmp(p, "unix") == 0 ||
60590792Sgshapiro			 sm_strcasecmp(p, "local") == 0)
60664562Sgshapiro			addr.sa.sa_family = AF_UNIX;
60764562Sgshapiro# endif /* NETUNIX */
60864562Sgshapiro# if NETINET
60990792Sgshapiro		else if (sm_strcasecmp(p, "inet") == 0)
61064562Sgshapiro			addr.sa.sa_family = AF_INET;
61164562Sgshapiro# endif /* NETINET */
61264562Sgshapiro# if NETINET6
61390792Sgshapiro		else if (sm_strcasecmp(p, "inet6") == 0)
61464562Sgshapiro			addr.sa.sa_family = AF_INET6;
61564562Sgshapiro# endif /* NETINET6 */
61664562Sgshapiro		else
61764562Sgshapiro		{
61864562Sgshapiro# ifdef EPROTONOSUPPORT
61964562Sgshapiro			errno = EPROTONOSUPPORT;
62064562Sgshapiro# else /* EPROTONOSUPPORT */
62164562Sgshapiro			errno = EINVAL;
62264562Sgshapiro# endif /* EPROTONOSUPPORT */
62364562Sgshapiro			if (tTd(64, 5))
62490792Sgshapiro				sm_dprintf("X%s: unknown socket type %s\n",
62564562Sgshapiro					m->mf_name, p);
62664562Sgshapiro			if (parseonly)
62764562Sgshapiro				syserr("X%s: unknown socket type %s",
62864562Sgshapiro				       m->mf_name, p);
62990792Sgshapiro			else if (MilterLogLevel > 10)
63064562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
63190792Sgshapiro					  "Milter (%s): unknown socket type %s",
63264562Sgshapiro					  m->mf_name, p);
63390792Sgshapiro			milter_error(m, e);
63464562Sgshapiro			return -1;
63564562Sgshapiro		}
63664562Sgshapiro		*colon++ = ':';
63764562Sgshapiro	}
63864562Sgshapiro	else
63964562Sgshapiro	{
64064562Sgshapiro		/* default to AF_UNIX */
64164562Sgshapiro		addr.sa.sa_family = AF_UNIX;
64264562Sgshapiro		colon = p;
64364562Sgshapiro	}
64464562Sgshapiro
64564562Sgshapiro# if NETUNIX
64664562Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
64764562Sgshapiro	{
64864562Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
64964562Sgshapiro
65064562Sgshapiro		at = colon;
65164562Sgshapiro		if (strlen(colon) >= sizeof addr.sunix.sun_path)
65264562Sgshapiro		{
65364562Sgshapiro			if (tTd(64, 5))
65490792Sgshapiro				sm_dprintf("X%s: local socket name %s too long\n",
65564562Sgshapiro					m->mf_name, colon);
65664562Sgshapiro			errno = EINVAL;
65764562Sgshapiro			if (parseonly)
65864562Sgshapiro				syserr("X%s: local socket name %s too long",
65964562Sgshapiro				       m->mf_name, colon);
66090792Sgshapiro			else if (MilterLogLevel > 10)
66164562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
66290792Sgshapiro					  "Milter (%s): local socket name %s too long",
66364562Sgshapiro					  m->mf_name, colon);
66490792Sgshapiro			milter_error(m, e);
66564562Sgshapiro			return -1;
66664562Sgshapiro		}
66764562Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
66864562Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
66964562Sgshapiro
67064562Sgshapiro		/* if just parsing .cf file, socket doesn't need to exist */
67164562Sgshapiro		if (parseonly && errno == ENOENT)
67264562Sgshapiro		{
67364562Sgshapiro			if (OpMode == MD_DAEMON ||
67464562Sgshapiro			    OpMode == MD_FGDAEMON)
67590792Sgshapiro				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
67690792Sgshapiro						     "WARNING: X%s: local socket name %s missing\n",
67790792Sgshapiro						     m->mf_name, colon);
67864562Sgshapiro		}
67964562Sgshapiro		else if (errno != 0)
68064562Sgshapiro		{
68164562Sgshapiro			/* if not safe, don't create */
68264562Sgshapiro			save_errno = errno;
68364562Sgshapiro			if (tTd(64, 5))
68490792Sgshapiro				sm_dprintf("X%s: local socket name %s unsafe\n",
68564562Sgshapiro					m->mf_name, colon);
68664562Sgshapiro			errno = save_errno;
68764562Sgshapiro			if (parseonly)
68864562Sgshapiro			{
68964562Sgshapiro				if (OpMode == MD_DAEMON ||
69064562Sgshapiro				    OpMode == MD_FGDAEMON ||
69164562Sgshapiro				    OpMode == MD_SMTP)
69264562Sgshapiro					syserr("X%s: local socket name %s unsafe",
69364562Sgshapiro					       m->mf_name, colon);
69464562Sgshapiro			}
69590792Sgshapiro			else if (MilterLogLevel > 10)
69664562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
69790792Sgshapiro					  "Milter (%s): local socket name %s unsafe",
69864562Sgshapiro					  m->mf_name, colon);
69990792Sgshapiro			milter_error(m, e);
70064562Sgshapiro			return -1;
70164562Sgshapiro		}
70264562Sgshapiro
70390792Sgshapiro		(void) sm_strlcpy(addr.sunix.sun_path, colon,
70464562Sgshapiro			       sizeof addr.sunix.sun_path);
70564562Sgshapiro		addrlen = sizeof (struct sockaddr_un);
70664562Sgshapiro	}
70764562Sgshapiro	else
70864562Sgshapiro# endif /* NETUNIX */
70964562Sgshapiro# if NETINET || NETINET6
71090792Sgshapiro	if (false
71164562Sgshapiro#  if NETINET
71264562Sgshapiro		 || addr.sa.sa_family == AF_INET
71364562Sgshapiro#  endif /* NETINET */
71464562Sgshapiro#  if NETINET6
71564562Sgshapiro		 || addr.sa.sa_family == AF_INET6
71664562Sgshapiro#  endif /* NETINET6 */
71764562Sgshapiro		 )
71864562Sgshapiro	{
71990792Sgshapiro		unsigned short port;
72064562Sgshapiro
72164562Sgshapiro		/* Parse port@host */
72264562Sgshapiro		at = strchr(colon, '@');
72364562Sgshapiro		if (at == NULL)
72464562Sgshapiro		{
72564562Sgshapiro			if (tTd(64, 5))
72690792Sgshapiro				sm_dprintf("X%s: bad address %s (expected port@host)\n",
72764562Sgshapiro					m->mf_name, colon);
72864562Sgshapiro			if (parseonly)
72964562Sgshapiro				syserr("X%s: bad address %s (expected port@host)",
73064562Sgshapiro				       m->mf_name, colon);
73190792Sgshapiro			else if (MilterLogLevel > 10)
73264562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
73390792Sgshapiro					  "Milter (%s): bad address %s (expected port@host)",
73464562Sgshapiro					  m->mf_name, colon);
73590792Sgshapiro			milter_error(m, e);
73664562Sgshapiro			return -1;
73764562Sgshapiro		}
73864562Sgshapiro		*at = '\0';
73964562Sgshapiro		if (isascii(*colon) && isdigit(*colon))
74090792Sgshapiro			port = htons((unsigned short) atoi(colon));
74164562Sgshapiro		else
74264562Sgshapiro		{
74364562Sgshapiro#  ifdef NO_GETSERVBYNAME
74464562Sgshapiro			if (tTd(64, 5))
74590792Sgshapiro				sm_dprintf("X%s: invalid port number %s\n",
74664562Sgshapiro					m->mf_name, colon);
74764562Sgshapiro			if (parseonly)
74864562Sgshapiro				syserr("X%s: invalid port number %s",
74964562Sgshapiro				       m->mf_name, colon);
75090792Sgshapiro			else if (MilterLogLevel > 10)
75164562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
75290792Sgshapiro					  "Milter (%s): invalid port number %s",
75364562Sgshapiro					  m->mf_name, colon);
75490792Sgshapiro			milter_error(m, e);
75564562Sgshapiro			return -1;
75664562Sgshapiro#  else /* NO_GETSERVBYNAME */
75764562Sgshapiro			register struct servent *sp;
75864562Sgshapiro
75964562Sgshapiro			sp = getservbyname(colon, "tcp");
76064562Sgshapiro			if (sp == NULL)
76164562Sgshapiro			{
76264562Sgshapiro				save_errno = errno;
76364562Sgshapiro				if (tTd(64, 5))
76490792Sgshapiro					sm_dprintf("X%s: unknown port name %s\n",
76564562Sgshapiro						m->mf_name, colon);
76664562Sgshapiro				errno = save_errno;
76764562Sgshapiro				if (parseonly)
76864562Sgshapiro					syserr("X%s: unknown port name %s",
76964562Sgshapiro					       m->mf_name, colon);
77090792Sgshapiro				else if (MilterLogLevel > 10)
77164562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
77290792Sgshapiro						  "Milter (%s): unknown port name %s",
77364562Sgshapiro						  m->mf_name, colon);
77490792Sgshapiro				milter_error(m, e);
77564562Sgshapiro				return -1;
77664562Sgshapiro			}
77764562Sgshapiro			port = sp->s_port;
77864562Sgshapiro#  endif /* NO_GETSERVBYNAME */
77964562Sgshapiro		}
78064562Sgshapiro		*at++ = '@';
78164562Sgshapiro		if (*at == '[')
78264562Sgshapiro		{
78364562Sgshapiro			char *end;
78464562Sgshapiro
78564562Sgshapiro			end = strchr(at, ']');
78664562Sgshapiro			if (end != NULL)
78764562Sgshapiro			{
78890792Sgshapiro				bool found = false;
78964562Sgshapiro#  if NETINET
79064562Sgshapiro				unsigned long hid = INADDR_NONE;
79164562Sgshapiro#  endif /* NETINET */
79264562Sgshapiro#  if NETINET6
79364562Sgshapiro				struct sockaddr_in6 hid6;
79464562Sgshapiro#  endif /* NETINET6 */
79564562Sgshapiro
79664562Sgshapiro				*end = '\0';
79764562Sgshapiro#  if NETINET
79864562Sgshapiro				if (addr.sa.sa_family == AF_INET &&
79964562Sgshapiro				    (hid = inet_addr(&at[1])) != INADDR_NONE)
80064562Sgshapiro				{
80164562Sgshapiro					addr.sin.sin_addr.s_addr = hid;
80264562Sgshapiro					addr.sin.sin_port = port;
80390792Sgshapiro					found = true;
80464562Sgshapiro				}
80564562Sgshapiro#  endif /* NETINET */
80664562Sgshapiro#  if NETINET6
80764562Sgshapiro				(void) memset(&hid6, '\0', sizeof hid6);
80864562Sgshapiro				if (addr.sa.sa_family == AF_INET6 &&
80990792Sgshapiro				    anynet_pton(AF_INET6, &at[1],
81090792Sgshapiro						&hid6.sin6_addr) == 1)
81164562Sgshapiro				{
81264562Sgshapiro					addr.sin6.sin6_addr = hid6.sin6_addr;
81364562Sgshapiro					addr.sin6.sin6_port = port;
81490792Sgshapiro					found = true;
81564562Sgshapiro				}
81664562Sgshapiro#  endif /* NETINET6 */
81764562Sgshapiro				*end = ']';
81864562Sgshapiro				if (!found)
81964562Sgshapiro				{
82064562Sgshapiro					if (tTd(64, 5))
82190792Sgshapiro						sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
82264562Sgshapiro							m->mf_name, at);
82364562Sgshapiro					if (parseonly)
82464562Sgshapiro						syserr("X%s: Invalid numeric domain spec \"%s\"",
82564562Sgshapiro						       m->mf_name, at);
82690792Sgshapiro					else if (MilterLogLevel > 10)
82764562Sgshapiro						sm_syslog(LOG_ERR, e->e_id,
82890792Sgshapiro							  "Milter (%s): Invalid numeric domain spec \"%s\"",
82964562Sgshapiro							  m->mf_name, at);
83090792Sgshapiro					milter_error(m, e);
83164562Sgshapiro					return -1;
83264562Sgshapiro				}
83364562Sgshapiro			}
83464562Sgshapiro			else
83564562Sgshapiro			{
83664562Sgshapiro				if (tTd(64, 5))
83790792Sgshapiro					sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
83864562Sgshapiro						m->mf_name, at);
83964562Sgshapiro				if (parseonly)
84064562Sgshapiro					syserr("X%s: Invalid numeric domain spec \"%s\"",
84164562Sgshapiro					       m->mf_name, at);
84290792Sgshapiro				else if (MilterLogLevel > 10)
84364562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
84490792Sgshapiro						  "Milter (%s): Invalid numeric domain spec \"%s\"",
84564562Sgshapiro						  m->mf_name, at);
84690792Sgshapiro				milter_error(m, e);
84764562Sgshapiro				return -1;
84864562Sgshapiro			}
84964562Sgshapiro		}
85064562Sgshapiro		else
85164562Sgshapiro		{
85264562Sgshapiro			hp = sm_gethostbyname(at, addr.sa.sa_family);
85364562Sgshapiro			if (hp == NULL)
85464562Sgshapiro			{
85564562Sgshapiro				save_errno = errno;
85664562Sgshapiro				if (tTd(64, 5))
85790792Sgshapiro					sm_dprintf("X%s: Unknown host name %s\n",
85890792Sgshapiro						   m->mf_name, at);
85964562Sgshapiro				errno = save_errno;
86064562Sgshapiro				if (parseonly)
86164562Sgshapiro					syserr("X%s: Unknown host name %s",
86264562Sgshapiro					       m->mf_name, at);
86390792Sgshapiro				else if (MilterLogLevel > 10)
86464562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
86590792Sgshapiro						  "Milter (%s): Unknown host name %s",
86664562Sgshapiro						  m->mf_name, at);
86790792Sgshapiro				milter_error(m, e);
86864562Sgshapiro				return -1;
86964562Sgshapiro			}
87064562Sgshapiro			addr.sa.sa_family = hp->h_addrtype;
87164562Sgshapiro			switch (hp->h_addrtype)
87264562Sgshapiro			{
87364562Sgshapiro#  if NETINET
87464562Sgshapiro			  case AF_INET:
87564562Sgshapiro				memmove(&addr.sin.sin_addr,
87690792Sgshapiro					hp->h_addr, INADDRSZ);
87764562Sgshapiro				addr.sin.sin_port = port;
87864562Sgshapiro				addrlen = sizeof (struct sockaddr_in);
87964562Sgshapiro				addrno = 1;
88064562Sgshapiro				break;
88164562Sgshapiro#  endif /* NETINET */
88264562Sgshapiro
88364562Sgshapiro#  if NETINET6
88464562Sgshapiro			  case AF_INET6:
88564562Sgshapiro				memmove(&addr.sin6.sin6_addr,
88690792Sgshapiro					hp->h_addr, IN6ADDRSZ);
88764562Sgshapiro				addr.sin6.sin6_port = port;
88864562Sgshapiro				addrlen = sizeof (struct sockaddr_in6);
88964562Sgshapiro				addrno = 1;
89064562Sgshapiro				break;
89164562Sgshapiro#  endif /* NETINET6 */
89264562Sgshapiro
89364562Sgshapiro			  default:
89464562Sgshapiro				if (tTd(64, 5))
89590792Sgshapiro					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
89690792Sgshapiro						   m->mf_name, at,
89790792Sgshapiro						   hp->h_addrtype);
89864562Sgshapiro				if (parseonly)
89964562Sgshapiro					syserr("X%s: Unknown protocol for %s (%d)",
90064562Sgshapiro					       m->mf_name, at, hp->h_addrtype);
90190792Sgshapiro				else if (MilterLogLevel > 10)
90264562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
90390792Sgshapiro						  "Milter (%s): Unknown protocol for %s (%d)",
90464562Sgshapiro						  m->mf_name, at,
90564562Sgshapiro						  hp->h_addrtype);
90690792Sgshapiro				milter_error(m, e);
90790792Sgshapiro#  if NETINET6
90871345Sgshapiro				freehostent(hp);
90990792Sgshapiro#  endif /* NETINET6 */
91064562Sgshapiro				return -1;
91164562Sgshapiro			}
91264562Sgshapiro		}
91364562Sgshapiro	}
91464562Sgshapiro	else
91564562Sgshapiro# endif /* NETINET || NETINET6 */
91664562Sgshapiro	{
91764562Sgshapiro		if (tTd(64, 5))
91890792Sgshapiro			sm_dprintf("X%s: unknown socket protocol\n",
91990792Sgshapiro				   m->mf_name);
92064562Sgshapiro		if (parseonly)
92164562Sgshapiro			syserr("X%s: unknown socket protocol", m->mf_name);
92290792Sgshapiro		else if (MilterLogLevel > 10)
92364562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
92490792Sgshapiro				  "Milter (%s): unknown socket protocol",
92590792Sgshapiro				  m->mf_name);
92690792Sgshapiro		milter_error(m, e);
92764562Sgshapiro		return -1;
92864562Sgshapiro	}
92964562Sgshapiro
93064562Sgshapiro	/* just parsing through? */
93164562Sgshapiro	if (parseonly)
93264562Sgshapiro	{
93364562Sgshapiro		m->mf_state = SMFS_READY;
93490792Sgshapiro# if NETINET6
93571345Sgshapiro		if (hp != NULL)
93671345Sgshapiro			freehostent(hp);
93790792Sgshapiro# endif /* NETINET6 */
93864562Sgshapiro		return 0;
93964562Sgshapiro	}
94064562Sgshapiro
94164562Sgshapiro	/* sanity check */
94264562Sgshapiro	if (m->mf_state != SMFS_READY &&
94364562Sgshapiro	    m->mf_state != SMFS_CLOSED)
94464562Sgshapiro	{
94564562Sgshapiro		/* shouldn't happen */
94664562Sgshapiro		if (tTd(64, 1))
94790792Sgshapiro			sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
94890792Sgshapiro				   m->mf_name, (char) m->mf_state);
94990792Sgshapiro		milter_error(m, e);
95090792Sgshapiro# if NETINET6
95171345Sgshapiro		if (hp != NULL)
95271345Sgshapiro			freehostent(hp);
95390792Sgshapiro# endif /* NETINET6 */
95464562Sgshapiro		return -1;
95564562Sgshapiro	}
95664562Sgshapiro
95764562Sgshapiro	/* nope, actually connecting */
95864562Sgshapiro	for (;;)
95964562Sgshapiro	{
96064562Sgshapiro		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
96164562Sgshapiro		if (sock < 0)
96264562Sgshapiro		{
96364562Sgshapiro			save_errno = errno;
96464562Sgshapiro			if (tTd(64, 5))
96590792Sgshapiro				sm_dprintf("Milter (%s): error creating socket: %s\n",
96690792Sgshapiro					   m->mf_name,
96790792Sgshapiro					   sm_errstring(save_errno));
96890792Sgshapiro			if (MilterLogLevel > 0)
96964562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
97090792Sgshapiro					  "Milter (%s): error creating socket: %s",
97190792Sgshapiro					  m->mf_name, sm_errstring(save_errno));
97290792Sgshapiro			milter_error(m, e);
97390792Sgshapiro# if NETINET6
97471345Sgshapiro			if (hp != NULL)
97571345Sgshapiro				freehostent(hp);
97690792Sgshapiro# endif /* NETINET6 */
97764562Sgshapiro			return -1;
97864562Sgshapiro		}
97964562Sgshapiro
98080785Sgshapiro		if (setjmp(MilterConnectTimeout) == 0)
98180785Sgshapiro		{
98290792Sgshapiro			SM_EVENT *ev = NULL;
98380785Sgshapiro			int i;
98464562Sgshapiro
98580785Sgshapiro			if (m->mf_timeout[SMFTO_CONNECT] > 0)
98690792Sgshapiro				ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
98790792Sgshapiro						 milter_connect_timeout, 0);
98880785Sgshapiro
98980785Sgshapiro			i = connect(sock, (struct sockaddr *) &addr, addrlen);
99080785Sgshapiro			save_errno = errno;
99180785Sgshapiro			if (ev != NULL)
99290792Sgshapiro				sm_clrevent(ev);
99380785Sgshapiro			errno = save_errno;
99480785Sgshapiro			if (i >= 0)
99580785Sgshapiro				break;
99680785Sgshapiro		}
99780785Sgshapiro
99864562Sgshapiro		/* couldn't connect.... try next address */
99964562Sgshapiro		save_errno = errno;
100066494Sgshapiro		p = CurHostName;
100166494Sgshapiro		CurHostName = at;
100264562Sgshapiro		if (tTd(64, 5))
100390792Sgshapiro			sm_dprintf("milter_open (%s): open %s failed: %s\n",
100490792Sgshapiro				   m->mf_name, at, sm_errstring(save_errno));
100590792Sgshapiro		if (MilterLogLevel > 13)
100664562Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
100790792Sgshapiro				  "Milter (%s): open %s failed: %s",
100890792Sgshapiro				  m->mf_name, at, sm_errstring(save_errno));
100966494Sgshapiro		CurHostName = p;
101064562Sgshapiro		(void) close(sock);
101164562Sgshapiro
101264562Sgshapiro		/* try next address */
101364562Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
101464562Sgshapiro		{
101564562Sgshapiro			switch (addr.sa.sa_family)
101664562Sgshapiro			{
101764562Sgshapiro# if NETINET
101864562Sgshapiro			  case AF_INET:
101964562Sgshapiro				memmove(&addr.sin.sin_addr,
102064562Sgshapiro					hp->h_addr_list[addrno++],
102164562Sgshapiro					INADDRSZ);
102264562Sgshapiro				break;
102364562Sgshapiro# endif /* NETINET */
102464562Sgshapiro
102564562Sgshapiro# if NETINET6
102664562Sgshapiro			  case AF_INET6:
102764562Sgshapiro				memmove(&addr.sin6.sin6_addr,
102864562Sgshapiro					hp->h_addr_list[addrno++],
102964562Sgshapiro					IN6ADDRSZ);
103064562Sgshapiro				break;
103164562Sgshapiro# endif /* NETINET6 */
103264562Sgshapiro
103364562Sgshapiro			  default:
103464562Sgshapiro				if (tTd(64, 5))
103590792Sgshapiro					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
103690792Sgshapiro						   m->mf_name, at,
103790792Sgshapiro						   hp->h_addrtype);
103890792Sgshapiro				if (MilterLogLevel > 0)
103964562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
104090792Sgshapiro						  "Milter (%s): Unknown protocol for %s (%d)",
104164562Sgshapiro						  m->mf_name, at,
104264562Sgshapiro						  hp->h_addrtype);
104390792Sgshapiro				milter_error(m, e);
104490792Sgshapiro# if NETINET6
104571345Sgshapiro				freehostent(hp);
104690792Sgshapiro# endif /* NETINET6 */
104764562Sgshapiro				return -1;
104864562Sgshapiro			}
104964562Sgshapiro			continue;
105064562Sgshapiro		}
105180785Sgshapiro		p = CurHostName;
105280785Sgshapiro		CurHostName = at;
105364562Sgshapiro		if (tTd(64, 5))
105490792Sgshapiro			sm_dprintf("X%s: error connecting to filter: %s\n",
105590792Sgshapiro				   m->mf_name, sm_errstring(save_errno));
105690792Sgshapiro		if (MilterLogLevel > 0)
105764562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
105890792Sgshapiro				  "Milter (%s): error connecting to filter: %s",
105990792Sgshapiro				  m->mf_name, sm_errstring(save_errno));
106080785Sgshapiro		CurHostName = p;
106190792Sgshapiro		milter_error(m, e);
106290792Sgshapiro# if NETINET6
106371345Sgshapiro		if (hp != NULL)
106471345Sgshapiro			freehostent(hp);
106590792Sgshapiro# endif /* NETINET6 */
106664562Sgshapiro		return -1;
106764562Sgshapiro	}
106864562Sgshapiro	m->mf_state = SMFS_OPEN;
106990792Sgshapiro# if NETINET6
107071345Sgshapiro	if (hp != NULL)
107171345Sgshapiro	{
107271345Sgshapiro		freehostent(hp);
107371345Sgshapiro		hp = NULL;
107471345Sgshapiro	}
107590792Sgshapiro# endif /* NETINET6 */
107664562Sgshapiro	return sock;
107764562Sgshapiro}
107880785Sgshapiro
107980785Sgshapirostatic void
108080785Sgshapiromilter_connect_timeout()
108180785Sgshapiro{
108280785Sgshapiro	/*
108380785Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
108480785Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
108580785Sgshapiro	**	DOING.
108680785Sgshapiro	*/
108780785Sgshapiro
108880785Sgshapiro	errno = ETIMEDOUT;
108980785Sgshapiro	longjmp(MilterConnectTimeout, 1);
109080785Sgshapiro}
109190792Sgshapiro/*
109264562Sgshapiro**  MILTER_SETUP -- setup structure for a mail filter
109364562Sgshapiro**
109464562Sgshapiro**	Parameters:
109564562Sgshapiro**		line -- the options line.
109664562Sgshapiro**
109764562Sgshapiro**	Returns:
109864562Sgshapiro**		none
109964562Sgshapiro*/
110064562Sgshapiro
110164562Sgshapirovoid
110264562Sgshapiromilter_setup(line)
110364562Sgshapiro	char *line;
110464562Sgshapiro{
110564562Sgshapiro	char fcode;
110664562Sgshapiro	register char *p;
110764562Sgshapiro	register struct milter *m;
110864562Sgshapiro	STAB *s;
110964562Sgshapiro
111066494Sgshapiro	/* collect the filter name */
111164562Sgshapiro	for (p = line;
111264562Sgshapiro	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
111364562Sgshapiro	     p++)
111464562Sgshapiro		continue;
111564562Sgshapiro	if (*p != '\0')
111664562Sgshapiro		*p++ = '\0';
111764562Sgshapiro	if (line[0] == '\0')
111864562Sgshapiro	{
111964562Sgshapiro		syserr("name required for mail filter");
112064562Sgshapiro		return;
112164562Sgshapiro	}
112290792Sgshapiro	m = (struct milter *) xalloc(sizeof *m);
112364562Sgshapiro	memset((char *) m, '\0', sizeof *m);
112464562Sgshapiro	m->mf_name = newstr(line);
112564562Sgshapiro	m->mf_state = SMFS_READY;
112664562Sgshapiro	m->mf_sock = -1;
112790792Sgshapiro	m->mf_timeout[SMFTO_CONNECT] = (time_t) 300;
112864562Sgshapiro	m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
112964562Sgshapiro	m->mf_timeout[SMFTO_READ] = (time_t) 10;
113064562Sgshapiro	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
113164562Sgshapiro
113264562Sgshapiro	/* now scan through and assign info from the fields */
113364562Sgshapiro	while (*p != '\0')
113464562Sgshapiro	{
113564562Sgshapiro		char *delimptr;
113664562Sgshapiro
113764562Sgshapiro		while (*p != '\0' &&
113864562Sgshapiro		       (*p == ',' || (isascii(*p) && isspace(*p))))
113964562Sgshapiro			p++;
114064562Sgshapiro
114164562Sgshapiro		/* p now points to field code */
114264562Sgshapiro		fcode = *p;
114364562Sgshapiro		while (*p != '\0' && *p != '=' && *p != ',')
114464562Sgshapiro			p++;
114564562Sgshapiro		if (*p++ != '=')
114664562Sgshapiro		{
114764562Sgshapiro			syserr("X%s: `=' expected", m->mf_name);
114864562Sgshapiro			return;
114964562Sgshapiro		}
115064562Sgshapiro		while (isascii(*p) && isspace(*p))
115164562Sgshapiro			p++;
115264562Sgshapiro
115364562Sgshapiro		/* p now points to the field body */
115464562Sgshapiro		p = munchstring(p, &delimptr, ',');
115564562Sgshapiro
115666494Sgshapiro		/* install the field into the filter struct */
115764562Sgshapiro		switch (fcode)
115864562Sgshapiro		{
115964562Sgshapiro		  case 'S':		/* socket */
116064562Sgshapiro			if (p == NULL)
116164562Sgshapiro				m->mf_conn = NULL;
116264562Sgshapiro			else
116364562Sgshapiro				m->mf_conn = newstr(p);
116464562Sgshapiro			break;
116564562Sgshapiro
116664562Sgshapiro		  case 'F':		/* Milter flags configured on MTA */
116764562Sgshapiro			for (; *p != '\0'; p++)
116864562Sgshapiro			{
116964562Sgshapiro				if (!(isascii(*p) && isspace(*p)))
117071345Sgshapiro					setbitn(bitidx(*p), m->mf_flags);
117164562Sgshapiro			}
117264562Sgshapiro			break;
117364562Sgshapiro
117464562Sgshapiro		  case 'T':		/* timeouts */
117564562Sgshapiro			milter_parse_timeouts(p, m);
117664562Sgshapiro			break;
117764562Sgshapiro
117864562Sgshapiro		  default:
117964562Sgshapiro			syserr("X%s: unknown filter equate %c=",
118064562Sgshapiro			       m->mf_name, fcode);
118164562Sgshapiro			break;
118264562Sgshapiro		}
118364562Sgshapiro		p = delimptr;
118464562Sgshapiro	}
118564562Sgshapiro
118664562Sgshapiro	/* early check for errors */
118790792Sgshapiro	(void) milter_open(m, true, CurEnv);
118864562Sgshapiro
118966494Sgshapiro	/* enter the filter into the symbol table */
119064562Sgshapiro	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
119164562Sgshapiro	if (s->s_milter != NULL)
119264562Sgshapiro		syserr("X%s: duplicate filter definition", m->mf_name);
119364562Sgshapiro	else
119464562Sgshapiro		s->s_milter = m;
119564562Sgshapiro}
119690792Sgshapiro/*
119790792Sgshapiro**  MILTER_CONFIG -- parse option list into an array and check config
119864562Sgshapiro**
119964562Sgshapiro**	Called when reading configuration file.
120064562Sgshapiro**
120164562Sgshapiro**	Parameters:
120264562Sgshapiro**		spec -- the filter list.
120364562Sgshapiro**		list -- the array to fill in.
120464562Sgshapiro**		max -- the maximum number of entries in list.
120564562Sgshapiro**
120664562Sgshapiro**	Returns:
120764562Sgshapiro**		none
120864562Sgshapiro*/
120964562Sgshapiro
121064562Sgshapirovoid
121190792Sgshapiromilter_config(spec, list, max)
121264562Sgshapiro	char *spec;
121364562Sgshapiro	struct milter **list;
121464562Sgshapiro	int max;
121564562Sgshapiro{
121664562Sgshapiro	int numitems = 0;
121764562Sgshapiro	register char *p;
121864562Sgshapiro
121964562Sgshapiro	/* leave one for the NULL signifying the end of the list */
122064562Sgshapiro	max--;
122164562Sgshapiro
122264562Sgshapiro	for (p = spec; p != NULL; )
122364562Sgshapiro	{
122464562Sgshapiro		STAB *s;
122564562Sgshapiro
122664562Sgshapiro		while (isascii(*p) && isspace(*p))
122764562Sgshapiro			p++;
122864562Sgshapiro		if (*p == '\0')
122964562Sgshapiro			break;
123064562Sgshapiro		spec = p;
123164562Sgshapiro
123264562Sgshapiro		if (numitems >= max)
123364562Sgshapiro		{
123464562Sgshapiro			syserr("Too many filters defined, %d max", max);
123564562Sgshapiro			if (max > 0)
123664562Sgshapiro				list[0] = NULL;
123764562Sgshapiro			return;
123864562Sgshapiro		}
123990792Sgshapiro#if _FFR_MILTER_PERDAEMON
124090792Sgshapiro		p = strpbrk(p, ";,");
124190792Sgshapiro#else /* _FFR_MILTER_PERDAEMON */
124264562Sgshapiro		p = strpbrk(p, ",");
124390792Sgshapiro#endif /* _FFR_MILTER_PERDAEMON */
124464562Sgshapiro		if (p != NULL)
124564562Sgshapiro			*p++ = '\0';
124664562Sgshapiro
124764562Sgshapiro		s = stab(spec, ST_MILTER, ST_FIND);
124864562Sgshapiro		if (s == NULL)
124964562Sgshapiro		{
125064562Sgshapiro			syserr("InputFilter %s not defined", spec);
125164562Sgshapiro			ExitStat = EX_CONFIG;
125264562Sgshapiro			return;
125364562Sgshapiro		}
125464562Sgshapiro		list[numitems++] = s->s_milter;
125564562Sgshapiro	}
125664562Sgshapiro	list[numitems] = NULL;
125790792Sgshapiro
125890792Sgshapiro	/* if not set, set to LogLevel */
125990792Sgshapiro	if (MilterLogLevel == -1)
126090792Sgshapiro		MilterLogLevel = LogLevel;
126164562Sgshapiro}
126290792Sgshapiro/*
126364562Sgshapiro**  MILTER_PARSE_TIMEOUTS -- parse timeout list
126464562Sgshapiro**
126564562Sgshapiro**	Called when reading configuration file.
126664562Sgshapiro**
126764562Sgshapiro**	Parameters:
126864562Sgshapiro**		spec -- the timeout list.
126964562Sgshapiro**		m -- milter to set.
127064562Sgshapiro**
127164562Sgshapiro**	Returns:
127264562Sgshapiro**		none
127364562Sgshapiro*/
127464562Sgshapiro
127564562Sgshapirostatic void
127664562Sgshapiromilter_parse_timeouts(spec, m)
127764562Sgshapiro	char *spec;
127864562Sgshapiro	struct milter *m;
127964562Sgshapiro{
128064562Sgshapiro	char fcode;
128164562Sgshapiro	register char *p;
128264562Sgshapiro
128364562Sgshapiro	p = spec;
128464562Sgshapiro
128564562Sgshapiro	/* now scan through and assign info from the fields */
128664562Sgshapiro	while (*p != '\0')
128764562Sgshapiro	{
128864562Sgshapiro		char *delimptr;
128964562Sgshapiro
129064562Sgshapiro		while (*p != '\0' &&
129164562Sgshapiro		       (*p == ';' || (isascii(*p) && isspace(*p))))
129264562Sgshapiro			p++;
129364562Sgshapiro
129464562Sgshapiro		/* p now points to field code */
129564562Sgshapiro		fcode = *p;
129664562Sgshapiro		while (*p != '\0' && *p != ':')
129764562Sgshapiro			p++;
129864562Sgshapiro		if (*p++ != ':')
129964562Sgshapiro		{
130064562Sgshapiro			syserr("X%s, T=: `:' expected", m->mf_name);
130164562Sgshapiro			return;
130264562Sgshapiro		}
130364562Sgshapiro		while (isascii(*p) && isspace(*p))
130464562Sgshapiro			p++;
130564562Sgshapiro
130664562Sgshapiro		/* p now points to the field body */
130764562Sgshapiro		p = munchstring(p, &delimptr, ';');
130864562Sgshapiro
130966494Sgshapiro		/* install the field into the filter struct */
131064562Sgshapiro		switch (fcode)
131164562Sgshapiro		{
131282017Sgshapiro		  case 'C':
131382017Sgshapiro			m->mf_timeout[SMFTO_CONNECT] = convtime(p, 's');
131482017Sgshapiro			if (tTd(64, 5))
131590792Sgshapiro				sm_dprintf("X%s: %c=%lu\n",
131690792Sgshapiro					   m->mf_name, fcode,
131790792Sgshapiro					   (unsigned long) m->mf_timeout[SMFTO_CONNECT]);
131882017Sgshapiro			break;
131982017Sgshapiro
132064562Sgshapiro		  case 'S':
132164562Sgshapiro			m->mf_timeout[SMFTO_WRITE] = convtime(p, 's');
132264562Sgshapiro			if (tTd(64, 5))
132390792Sgshapiro				sm_dprintf("X%s: %c=%lu\n",
132490792Sgshapiro					   m->mf_name, fcode,
132590792Sgshapiro					   (unsigned long) m->mf_timeout[SMFTO_WRITE]);
132664562Sgshapiro			break;
132764562Sgshapiro
132864562Sgshapiro		  case 'R':
132964562Sgshapiro			m->mf_timeout[SMFTO_READ] = convtime(p, 's');
133064562Sgshapiro			if (tTd(64, 5))
133190792Sgshapiro				sm_dprintf("X%s: %c=%lu\n",
133290792Sgshapiro					   m->mf_name, fcode,
133390792Sgshapiro					   (unsigned long) m->mf_timeout[SMFTO_READ]);
133464562Sgshapiro			break;
133564562Sgshapiro
133664562Sgshapiro		  case 'E':
133764562Sgshapiro			m->mf_timeout[SMFTO_EOM] = convtime(p, 's');
133864562Sgshapiro			if (tTd(64, 5))
133990792Sgshapiro				sm_dprintf("X%s: %c=%lu\n",
134090792Sgshapiro					   m->mf_name, fcode,
134190792Sgshapiro					   (unsigned long) m->mf_timeout[SMFTO_EOM]);
134264562Sgshapiro			break;
134364562Sgshapiro
134464562Sgshapiro		  default:
134564562Sgshapiro			if (tTd(64, 5))
134690792Sgshapiro				sm_dprintf("X%s: %c unknown\n",
134790792Sgshapiro					   m->mf_name, fcode);
134864562Sgshapiro			syserr("X%s: unknown filter timeout %c",
134964562Sgshapiro			       m->mf_name, fcode);
135064562Sgshapiro			break;
135164562Sgshapiro		}
135264562Sgshapiro		p = delimptr;
135364562Sgshapiro	}
135464562Sgshapiro}
135590792Sgshapiro/*
135664562Sgshapiro**  MILTER_SET_OPTION -- set an individual milter option
135764562Sgshapiro**
135864562Sgshapiro**	Parameters:
135964562Sgshapiro**		name -- the name of the option.
136064562Sgshapiro**		val -- the value of the option.
136164562Sgshapiro**		sticky -- if set, don't let other setoptions override
136264562Sgshapiro**			this value.
136364562Sgshapiro**
136464562Sgshapiro**	Returns:
136564562Sgshapiro**		none.
136664562Sgshapiro*/
136764562Sgshapiro
136864562Sgshapiro/* set if Milter sub-option is stuck */
136964562Sgshapirostatic BITMAP256	StickyMilterOpt;
137064562Sgshapiro
137164562Sgshapirostatic struct milteropt
137264562Sgshapiro{
137390792Sgshapiro	char		*mo_name;	/* long name of milter option */
137490792Sgshapiro	unsigned char	mo_code;	/* code for option */
137564562Sgshapiro} MilterOptTab[] =
137664562Sgshapiro{
137764562Sgshapiro# define MO_MACROS_CONNECT		0x01
137864562Sgshapiro	{ "macros.connect",		MO_MACROS_CONNECT		},
137964562Sgshapiro# define MO_MACROS_HELO			0x02
138064562Sgshapiro	{ "macros.helo",		MO_MACROS_HELO			},
138164562Sgshapiro# define MO_MACROS_ENVFROM		0x03
138264562Sgshapiro	{ "macros.envfrom",		MO_MACROS_ENVFROM		},
138364562Sgshapiro# define MO_MACROS_ENVRCPT		0x04
138464562Sgshapiro	{ "macros.envrcpt",		MO_MACROS_ENVRCPT		},
138590792Sgshapiro# define MO_LOGLEVEL			0x05
138690792Sgshapiro	{ "loglevel",			MO_LOGLEVEL			},
138764562Sgshapiro	{ NULL,				0				},
138864562Sgshapiro};
138964562Sgshapiro
139064562Sgshapirovoid
139164562Sgshapiromilter_set_option(name, val, sticky)
139264562Sgshapiro	char *name;
139364562Sgshapiro	char *val;
139464562Sgshapiro	bool sticky;
139564562Sgshapiro{
139664562Sgshapiro	int nummac = 0;
139764562Sgshapiro	register struct milteropt *mo;
139864562Sgshapiro	char *p;
139964562Sgshapiro	char **macros = NULL;
140064562Sgshapiro
140164562Sgshapiro	if (tTd(37, 2) || tTd(64, 5))
140290792Sgshapiro		sm_dprintf("milter_set_option(%s = %s)", name, val);
140364562Sgshapiro
140464562Sgshapiro	for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
140564562Sgshapiro	{
140690792Sgshapiro		if (sm_strcasecmp(mo->mo_name, name) == 0)
140764562Sgshapiro			break;
140864562Sgshapiro	}
140964562Sgshapiro
141064562Sgshapiro	if (mo->mo_name == NULL)
141190792Sgshapiro	{
141264562Sgshapiro		syserr("milter_set_option: invalid Milter option %s", name);
141390792Sgshapiro		return;
141490792Sgshapiro	}
141564562Sgshapiro
141664562Sgshapiro	/*
141764562Sgshapiro	**  See if this option is preset for us.
141864562Sgshapiro	*/
141964562Sgshapiro
142064562Sgshapiro	if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
142164562Sgshapiro	{
142264562Sgshapiro		if (tTd(37, 2) || tTd(64,5))
142390792Sgshapiro			sm_dprintf(" (ignored)\n");
142464562Sgshapiro		return;
142564562Sgshapiro	}
142664562Sgshapiro
142764562Sgshapiro	if (tTd(37, 2) || tTd(64,5))
142890792Sgshapiro		sm_dprintf("\n");
142964562Sgshapiro
143064562Sgshapiro	switch (mo->mo_code)
143164562Sgshapiro	{
143290792Sgshapiro	  case MO_LOGLEVEL:
143390792Sgshapiro		MilterLogLevel = atoi(val);
143490792Sgshapiro		break;
143590792Sgshapiro
143664562Sgshapiro	  case MO_MACROS_CONNECT:
143764562Sgshapiro		if (macros == NULL)
143864562Sgshapiro			macros = MilterConnectMacros;
143964562Sgshapiro		/* FALLTHROUGH */
144064562Sgshapiro
144164562Sgshapiro	  case MO_MACROS_HELO:
144264562Sgshapiro		if (macros == NULL)
144364562Sgshapiro			macros = MilterHeloMacros;
144464562Sgshapiro		/* FALLTHROUGH */
144564562Sgshapiro
144664562Sgshapiro	  case MO_MACROS_ENVFROM:
144764562Sgshapiro		if (macros == NULL)
144864562Sgshapiro			macros = MilterEnvFromMacros;
144964562Sgshapiro		/* FALLTHROUGH */
145064562Sgshapiro
145164562Sgshapiro	  case MO_MACROS_ENVRCPT:
145264562Sgshapiro		if (macros == NULL)
145364562Sgshapiro			macros = MilterEnvRcptMacros;
145464562Sgshapiro
145564562Sgshapiro		p = newstr(val);
145664562Sgshapiro		while (*p != '\0')
145764562Sgshapiro		{
145864562Sgshapiro			char *macro;
145964562Sgshapiro
146064562Sgshapiro			/* Skip leading commas, spaces */
146164562Sgshapiro			while (*p != '\0' &&
146264562Sgshapiro			       (*p == ',' || (isascii(*p) && isspace(*p))))
146364562Sgshapiro				p++;
146464562Sgshapiro
146564562Sgshapiro			if (*p == '\0')
146664562Sgshapiro				break;
146764562Sgshapiro
146864562Sgshapiro			/* Find end of macro */
146964562Sgshapiro			macro = p;
147064562Sgshapiro			while (*p != '\0' && *p != ',' &&
147164562Sgshapiro			       isascii(*p) && !isspace(*p))
147264562Sgshapiro				p++;
147364562Sgshapiro			if (*p != '\0')
147464562Sgshapiro				*p++ = '\0';
147564562Sgshapiro
147664562Sgshapiro			if (nummac >= MAXFILTERMACROS)
147764562Sgshapiro			{
147864562Sgshapiro				syserr("milter_set_option: too many macros in Milter.%s (max %d)",
147964562Sgshapiro				       name, MAXFILTERMACROS);
148064562Sgshapiro				macros[nummac] = NULL;
148164562Sgshapiro				break;
148264562Sgshapiro			}
148364562Sgshapiro			macros[nummac++] = macro;
148464562Sgshapiro		}
148564562Sgshapiro		macros[nummac] = NULL;
148664562Sgshapiro		break;
148764562Sgshapiro
148864562Sgshapiro	  default:
148964562Sgshapiro		syserr("milter_set_option: invalid Milter option %s", name);
149064562Sgshapiro		break;
149164562Sgshapiro	}
149264562Sgshapiro	if (sticky)
149364562Sgshapiro		setbitn(mo->mo_code, StickyMilterOpt);
149464562Sgshapiro}
149590792Sgshapiro/*
149690792Sgshapiro**  MILTER_REOPEN_DF -- open & truncate the data file (for replbody)
149764562Sgshapiro**
149864562Sgshapiro**	Parameters:
149964562Sgshapiro**		e -- current envelope.
150064562Sgshapiro**
150164562Sgshapiro**	Returns:
150264562Sgshapiro**		0 if succesful, -1 otherwise
150364562Sgshapiro*/
150464562Sgshapiro
150564562Sgshapirostatic int
150664562Sgshapiromilter_reopen_df(e)
150764562Sgshapiro	ENVELOPE *e;
150864562Sgshapiro{
150964562Sgshapiro	char dfname[MAXPATHLEN];
151064562Sgshapiro
151190792Sgshapiro	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname);
151264562Sgshapiro
151364562Sgshapiro	/*
151490792Sgshapiro	**  In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so
151564562Sgshapiro	**  close and reopen writable (later close and reopen
151664562Sgshapiro	**  read only again).
151764562Sgshapiro	**
151890792Sgshapiro	**  In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the
151964562Sgshapiro	**  buffered file I/O descriptor, still open for writing
152064562Sgshapiro	**  so there isn't as much work to do, just truncate it
152164562Sgshapiro	**  and go.
152264562Sgshapiro	*/
152364562Sgshapiro
152490792Sgshapiro	if (SuperSafe == SAFE_REALLY)
152564562Sgshapiro	{
152690792Sgshapiro		/* close read-only data file */
152764562Sgshapiro		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
152864562Sgshapiro		{
152990792Sgshapiro			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
153064562Sgshapiro			e->e_flags &= ~EF_HAS_DF;
153164562Sgshapiro		}
153264562Sgshapiro
153364562Sgshapiro		/* open writable */
153490792Sgshapiro		if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
153590792Sgshapiro					   SM_IO_RDWR, NULL)) == NULL)
153664562Sgshapiro		{
153790792Sgshapiro			MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s");
153864562Sgshapiro			return -1;
153964562Sgshapiro		}
154064562Sgshapiro	}
154164562Sgshapiro	else if (e->e_dfp == NULL)
154264562Sgshapiro	{
154364562Sgshapiro		/* shouldn't happen */
154464562Sgshapiro		errno = ENOENT;
154564562Sgshapiro		MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
154664562Sgshapiro		return -1;
154764562Sgshapiro	}
154864562Sgshapiro	return 0;
154964562Sgshapiro}
155090792Sgshapiro/*
155190792Sgshapiro**  MILTER_RESET_DF -- re-open read-only the data file (for replbody)
155264562Sgshapiro**
155364562Sgshapiro**	Parameters:
155464562Sgshapiro**		e -- current envelope.
155564562Sgshapiro**
155664562Sgshapiro**	Returns:
155764562Sgshapiro**		0 if succesful, -1 otherwise
155864562Sgshapiro*/
155964562Sgshapiro
156064562Sgshapirostatic int
156164562Sgshapiromilter_reset_df(e)
156264562Sgshapiro	ENVELOPE *e;
156364562Sgshapiro{
156464562Sgshapiro	int afd;
156564562Sgshapiro	char dfname[MAXPATHLEN];
156664562Sgshapiro
156790792Sgshapiro	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname);
156864562Sgshapiro
156990792Sgshapiro	if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 ||
157090792Sgshapiro	    sm_io_error(e->e_dfp))
157164562Sgshapiro	{
157264562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
157364562Sgshapiro		return -1;
157464562Sgshapiro	}
157590792Sgshapiro	else if (SuperSafe != SAFE_REALLY)
157664562Sgshapiro	{
157764562Sgshapiro		/* skip next few clauses */
157864562Sgshapiro		/* EMPTY */
157964562Sgshapiro	}
158090792Sgshapiro	else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0
158190792Sgshapiro		 && fsync(afd) < 0)
158264562Sgshapiro	{
158364562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
158464562Sgshapiro		return -1;
158564562Sgshapiro	}
158690792Sgshapiro	else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0)
158764562Sgshapiro	{
158864562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
158964562Sgshapiro		return -1;
159064562Sgshapiro	}
159190792Sgshapiro	else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
159290792Sgshapiro					SM_IO_RDONLY, NULL)) == NULL)
159364562Sgshapiro	{
159464562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
159564562Sgshapiro		return -1;
159664562Sgshapiro	}
159764562Sgshapiro	else
159864562Sgshapiro		e->e_flags |= EF_HAS_DF;
159964562Sgshapiro	return 0;
160064562Sgshapiro}
160190792Sgshapiro/*
160264562Sgshapiro**  MILTER_CAN_DELRCPTS -- can any milter filters delete recipients?
160364562Sgshapiro**
160464562Sgshapiro**	Parameters:
160564562Sgshapiro**		none
160664562Sgshapiro**
160764562Sgshapiro**	Returns:
160890792Sgshapiro**		true if any filter deletes recipients, false otherwise
160964562Sgshapiro*/
161064562Sgshapiro
161164562Sgshapirobool
161264562Sgshapiromilter_can_delrcpts()
161364562Sgshapiro{
161490792Sgshapiro	bool can = false;
161564562Sgshapiro	int i;
161664562Sgshapiro
161764562Sgshapiro	if (tTd(64, 10))
161890792Sgshapiro		sm_dprintf("milter_can_delrcpts:");
161964562Sgshapiro
162064562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
162164562Sgshapiro	{
162264562Sgshapiro		struct milter *m = InputFilters[i];
162364562Sgshapiro
162464562Sgshapiro		if (bitset(SMFIF_DELRCPT, m->mf_fflags))
162564562Sgshapiro		{
162690792Sgshapiro			can = true;
162764562Sgshapiro			break;
162864562Sgshapiro		}
162964562Sgshapiro	}
163064562Sgshapiro	if (tTd(64, 10))
163190792Sgshapiro		sm_dprintf("%s\n", can ? "true" : "false");
163264562Sgshapiro
163364562Sgshapiro	return can;
163464562Sgshapiro}
163590792Sgshapiro/*
163664562Sgshapiro**  MILTER_QUIT_FILTER -- close down a single filter
163764562Sgshapiro**
163864562Sgshapiro**	Parameters:
163964562Sgshapiro**		m -- milter structure of filter to close down.
164064562Sgshapiro**		e -- current envelope.
164164562Sgshapiro**
164264562Sgshapiro**	Returns:
164364562Sgshapiro**		none
164464562Sgshapiro*/
164564562Sgshapiro
164664562Sgshapirostatic void
164764562Sgshapiromilter_quit_filter(m, e)
164864562Sgshapiro	struct milter *m;
164964562Sgshapiro	ENVELOPE *e;
165064562Sgshapiro{
165164562Sgshapiro	if (tTd(64, 10))
165290792Sgshapiro		sm_dprintf("milter_quit_filter(%s)\n", m->mf_name);
165390792Sgshapiro	if (MilterLogLevel > 18)
165490792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter",
165590792Sgshapiro			  m->mf_name);
165664562Sgshapiro
165764562Sgshapiro	/* Never replace error state */
165864562Sgshapiro	if (m->mf_state == SMFS_ERROR)
165964562Sgshapiro		return;
166064562Sgshapiro
166164562Sgshapiro	if (m->mf_sock < 0 ||
166264562Sgshapiro	    m->mf_state == SMFS_CLOSED ||
166364562Sgshapiro	    m->mf_state == SMFS_READY)
166464562Sgshapiro	{
166564562Sgshapiro		m->mf_sock = -1;
166664562Sgshapiro		m->mf_state = SMFS_CLOSED;
166764562Sgshapiro		return;
166864562Sgshapiro	}
166964562Sgshapiro
167064562Sgshapiro	(void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
167164562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
167271345Sgshapiro	if (m->mf_sock >= 0)
167371345Sgshapiro	{
167471345Sgshapiro		(void) close(m->mf_sock);
167571345Sgshapiro		m->mf_sock = -1;
167671345Sgshapiro	}
167764562Sgshapiro	if (m->mf_state != SMFS_ERROR)
167864562Sgshapiro		m->mf_state = SMFS_CLOSED;
167964562Sgshapiro}
168090792Sgshapiro/*
168164562Sgshapiro**  MILTER_ABORT_FILTER -- tell filter to abort current message
168264562Sgshapiro**
168364562Sgshapiro**	Parameters:
168464562Sgshapiro**		m -- milter structure of filter to abort.
168564562Sgshapiro**		e -- current envelope.
168664562Sgshapiro**
168764562Sgshapiro**	Returns:
168864562Sgshapiro**		none
168964562Sgshapiro*/
169064562Sgshapiro
169164562Sgshapirostatic void
169264562Sgshapiromilter_abort_filter(m, e)
169364562Sgshapiro	struct milter *m;
169464562Sgshapiro	ENVELOPE *e;
169564562Sgshapiro{
169664562Sgshapiro	if (tTd(64, 10))
169790792Sgshapiro		sm_dprintf("milter_abort_filter(%s)\n", m->mf_name);
169890792Sgshapiro	if (MilterLogLevel > 10)
169990792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter",
170090792Sgshapiro			  m->mf_name);
170164562Sgshapiro
170264562Sgshapiro	if (m->mf_sock < 0 ||
170364562Sgshapiro	    m->mf_state != SMFS_INMSG)
170464562Sgshapiro		return;
170564562Sgshapiro
170664562Sgshapiro	(void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
170764562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
170864562Sgshapiro	if (m->mf_state != SMFS_ERROR)
170964562Sgshapiro		m->mf_state = SMFS_DONE;
171064562Sgshapiro}
171190792Sgshapiro/*
171264562Sgshapiro**  MILTER_SEND_MACROS -- provide macros to the filters
171364562Sgshapiro**
171464562Sgshapiro**	Parameters:
171564562Sgshapiro**		m -- milter to send macros to.
171664562Sgshapiro**		macros -- macros to send for filter smfi_getsymval().
171764562Sgshapiro**		cmd -- which command the macros are associated with.
171864562Sgshapiro**		e -- current envelope (for macro access).
171964562Sgshapiro**
172064562Sgshapiro**	Returns:
172164562Sgshapiro**		none
172264562Sgshapiro*/
172364562Sgshapiro
172464562Sgshapirostatic void
172564562Sgshapiromilter_send_macros(m, macros, cmd, e)
172664562Sgshapiro	struct milter *m;
172764562Sgshapiro	char **macros;
172864562Sgshapiro	char cmd;
172964562Sgshapiro	ENVELOPE *e;
173064562Sgshapiro{
173164562Sgshapiro	int i;
173264562Sgshapiro	int mid;
173364562Sgshapiro	char *v;
173464562Sgshapiro	char *buf, *bp;
173564562Sgshapiro	ssize_t s;
173664562Sgshapiro
173764562Sgshapiro	/* sanity check */
173864562Sgshapiro	if (macros == NULL || macros[0] == NULL)
173964562Sgshapiro		return;
174064562Sgshapiro
174164562Sgshapiro	/* put together data */
174264562Sgshapiro	s = 1;			/* for the command character */
174364562Sgshapiro	for (i = 0; macros[i] != NULL; i++)
174464562Sgshapiro	{
174590792Sgshapiro		mid = macid(macros[i]);
174671345Sgshapiro		if (mid == 0)
174764562Sgshapiro			continue;
174864562Sgshapiro		v = macvalue(mid, e);
174964562Sgshapiro		if (v == NULL)
175064562Sgshapiro			continue;
175164562Sgshapiro		s += strlen(macros[i]) + 1 + strlen(v) + 1;
175264562Sgshapiro	}
175364562Sgshapiro
175490792Sgshapiro	if (s < 0)
175590792Sgshapiro		return;
175690792Sgshapiro
175790792Sgshapiro	buf = (char *) xalloc(s);
175864562Sgshapiro	bp = buf;
175964562Sgshapiro	*bp++ = cmd;
176064562Sgshapiro	for (i = 0; macros[i] != NULL; i++)
176164562Sgshapiro	{
176290792Sgshapiro		mid = macid(macros[i]);
176371345Sgshapiro		if (mid == 0)
176464562Sgshapiro			continue;
176564562Sgshapiro		v = macvalue(mid, e);
176664562Sgshapiro		if (v == NULL)
176764562Sgshapiro			continue;
176864562Sgshapiro
176964562Sgshapiro		if (tTd(64, 10))
177090792Sgshapiro			sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
177164562Sgshapiro				m->mf_name, cmd, macros[i], v);
177264562Sgshapiro
177390792Sgshapiro		(void) sm_strlcpy(bp, macros[i], s - (bp - buf));
177464562Sgshapiro		bp += strlen(bp) + 1;
177590792Sgshapiro		(void) sm_strlcpy(bp, v, s - (bp - buf));
177664562Sgshapiro		bp += strlen(bp) + 1;
177764562Sgshapiro	}
177864562Sgshapiro	(void) milter_write(m, SMFIC_MACRO, buf, s,
177964562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
178090792Sgshapiro	sm_free(buf); /* XXX */
178164562Sgshapiro}
178264562Sgshapiro
178390792Sgshapiro/*
178464562Sgshapiro**  MILTER_SEND_COMMAND -- send a command and return the response for a filter
178564562Sgshapiro**
178664562Sgshapiro**	Parameters:
178764562Sgshapiro**		m -- current milter filter
178864562Sgshapiro**		command -- command to send.
178964562Sgshapiro**		data -- optional command data.
179064562Sgshapiro**		sz -- length of buf.
179164562Sgshapiro**		e -- current envelope (for e->e_id).
179264562Sgshapiro**		state -- return state word.
179364562Sgshapiro**
179464562Sgshapiro**	Returns:
179564562Sgshapiro**		response string (may be NULL)
179664562Sgshapiro*/
179764562Sgshapiro
179864562Sgshapirostatic char *
179964562Sgshapiromilter_send_command(m, command, data, sz, e, state)
180064562Sgshapiro	struct milter *m;
180164562Sgshapiro	char command;
180264562Sgshapiro	void *data;
180364562Sgshapiro	ssize_t sz;
180464562Sgshapiro	ENVELOPE *e;
180564562Sgshapiro	char *state;
180664562Sgshapiro{
180764562Sgshapiro	char rcmd;
180864562Sgshapiro	ssize_t rlen;
180990792Sgshapiro	unsigned long skipflag;
181090792Sgshapiro	char *action;
181164562Sgshapiro	char *defresponse;
181264562Sgshapiro	char *response;
181364562Sgshapiro
181464562Sgshapiro	if (tTd(64, 10))
181590792Sgshapiro		sm_dprintf("milter_send_command(%s): cmd %c len %ld\n",
181664562Sgshapiro			m->mf_name, (char) command, (long) sz);
181764562Sgshapiro
181864562Sgshapiro	/* find skip flag and default failure */
181964562Sgshapiro	switch (command)
182064562Sgshapiro	{
182164562Sgshapiro	  case SMFIC_CONNECT:
182264562Sgshapiro		skipflag = SMFIP_NOCONNECT;
182390792Sgshapiro		action = "connect";
182464562Sgshapiro		defresponse = "554 Command rejected";
182564562Sgshapiro		break;
182664562Sgshapiro
182764562Sgshapiro	  case SMFIC_HELO:
182864562Sgshapiro		skipflag = SMFIP_NOHELO;
182990792Sgshapiro		action = "helo";
183064562Sgshapiro		defresponse = "550 Command rejected";
183164562Sgshapiro		break;
183264562Sgshapiro
183364562Sgshapiro	  case SMFIC_MAIL:
183464562Sgshapiro		skipflag = SMFIP_NOMAIL;
183590792Sgshapiro		action = "mail";
183664562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
183764562Sgshapiro		break;
183864562Sgshapiro
183964562Sgshapiro	  case SMFIC_RCPT:
184064562Sgshapiro		skipflag = SMFIP_NORCPT;
184190792Sgshapiro		action = "rcpt";
184264562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
184364562Sgshapiro		break;
184464562Sgshapiro
184564562Sgshapiro	  case SMFIC_HEADER:
184664562Sgshapiro		skipflag = SMFIP_NOHDRS;
184790792Sgshapiro		action = "header";
184864562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
184964562Sgshapiro		break;
185064562Sgshapiro
185164562Sgshapiro	  case SMFIC_BODY:
185264562Sgshapiro		skipflag = SMFIP_NOBODY;
185390792Sgshapiro		action = "body";
185464562Sgshapiro		defresponse = "554 5.7.1 Command rejected";
185564562Sgshapiro		break;
185664562Sgshapiro
185764562Sgshapiro	  case SMFIC_EOH:
185864562Sgshapiro		skipflag = SMFIP_NOEOH;
185990792Sgshapiro		action = "eoh";
186064562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
186164562Sgshapiro		break;
186264562Sgshapiro
186364562Sgshapiro	  case SMFIC_BODYEOB:
186464562Sgshapiro	  case SMFIC_OPTNEG:
186564562Sgshapiro	  case SMFIC_MACRO:
186664562Sgshapiro	  case SMFIC_ABORT:
186764562Sgshapiro	  case SMFIC_QUIT:
186864562Sgshapiro		/* NOTE: not handled by milter_send_command() */
186964562Sgshapiro		/* FALLTHROUGH */
187064562Sgshapiro
187164562Sgshapiro	  default:
187264562Sgshapiro		skipflag = 0;
187390792Sgshapiro		action = "default";
187464562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
187564562Sgshapiro		break;
187664562Sgshapiro	}
187764562Sgshapiro
187864562Sgshapiro	/* check if filter wants this command */
187964562Sgshapiro	if (skipflag != 0 &&
188064562Sgshapiro	    bitset(skipflag, m->mf_pflags))
188164562Sgshapiro		return NULL;
188264562Sgshapiro
188390792Sgshapiro	/* send the command to the filter */
188464562Sgshapiro	(void) milter_write(m, command, data, sz,
188564562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
188664562Sgshapiro	if (m->mf_state == SMFS_ERROR)
188764562Sgshapiro	{
188894334Sgshapiro		MILTER_CHECK_ERROR(return NULL);
188964562Sgshapiro		return NULL;
189064562Sgshapiro	}
189164562Sgshapiro
189290792Sgshapiro	/* get the response from the filter */
189364562Sgshapiro	response = milter_read(m, &rcmd, &rlen,
189464562Sgshapiro			       m->mf_timeout[SMFTO_READ], e);
189564562Sgshapiro	if (m->mf_state == SMFS_ERROR)
189664562Sgshapiro	{
189794334Sgshapiro		MILTER_CHECK_ERROR(return NULL);
189864562Sgshapiro		return NULL;
189964562Sgshapiro	}
190064562Sgshapiro
190164562Sgshapiro	if (tTd(64, 10))
190290792Sgshapiro		sm_dprintf("milter_send_command(%s): returned %c\n",
190390792Sgshapiro			   m->mf_name, (char) rcmd);
190464562Sgshapiro
190564562Sgshapiro	switch (rcmd)
190664562Sgshapiro	{
190764562Sgshapiro	  case SMFIR_REPLYCODE:
190864562Sgshapiro		MILTER_CHECK_REPLYCODE(defresponse);
190990792Sgshapiro		if (MilterLogLevel > 10)
191090792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject=%s",
191190792Sgshapiro				  m->mf_name, action, response);
191290792Sgshapiro		*state = rcmd;
191390792Sgshapiro		break;
191464562Sgshapiro
191564562Sgshapiro	  case SMFIR_REJECT:
191690792Sgshapiro		if (MilterLogLevel > 10)
191790792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject",
191890792Sgshapiro				  m->mf_name, action);
191990792Sgshapiro		*state = rcmd;
192090792Sgshapiro		break;
192190792Sgshapiro
192264562Sgshapiro	  case SMFIR_DISCARD:
192390792Sgshapiro		if (MilterLogLevel > 10)
192490792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, discard",
192590792Sgshapiro				  m->mf_name, action);
192690792Sgshapiro		*state = rcmd;
192790792Sgshapiro		break;
192890792Sgshapiro
192964562Sgshapiro	  case SMFIR_TEMPFAIL:
193090792Sgshapiro		if (MilterLogLevel > 10)
193190792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, tempfail",
193290792Sgshapiro				  m->mf_name, action);
193364562Sgshapiro		*state = rcmd;
193464562Sgshapiro		break;
193564562Sgshapiro
193664562Sgshapiro	  case SMFIR_ACCEPT:
193764562Sgshapiro		/* this filter is done with message/connection */
193873188Sgshapiro		if (command == SMFIC_HELO ||
193973188Sgshapiro		    command == SMFIC_CONNECT)
194073188Sgshapiro			m->mf_state = SMFS_CLOSABLE;
194173188Sgshapiro		else
194273188Sgshapiro			m->mf_state = SMFS_DONE;
194390792Sgshapiro		if (MilterLogLevel > 10)
194490792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, accepted",
194590792Sgshapiro				  m->mf_name, action);
194664562Sgshapiro		break;
194764562Sgshapiro
194864562Sgshapiro	  case SMFIR_CONTINUE:
194964562Sgshapiro		/* if MAIL command is ok, filter is in message state */
195064562Sgshapiro		if (command == SMFIC_MAIL)
195164562Sgshapiro			m->mf_state = SMFS_INMSG;
195290792Sgshapiro		if (MilterLogLevel > 12)
195390792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, continue",
195490792Sgshapiro				  m->mf_name, action);
195564562Sgshapiro		break;
195664562Sgshapiro
195764562Sgshapiro	  default:
195864562Sgshapiro		/* Invalid response to command */
195990792Sgshapiro		if (MilterLogLevel > 0)
196064562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
196190792Sgshapiro				  "milter_send_command(%s): action=%s returned bogus response %c",
196290792Sgshapiro				  m->mf_name, action, rcmd);
196390792Sgshapiro		milter_error(m, e);
196464562Sgshapiro		break;
196564562Sgshapiro	}
196664562Sgshapiro
196764562Sgshapiro	if (*state != SMFIR_REPLYCODE &&
196864562Sgshapiro	    response != NULL)
196964562Sgshapiro	{
197090792Sgshapiro		sm_free(response); /* XXX */
197164562Sgshapiro		response = NULL;
197264562Sgshapiro	}
197364562Sgshapiro	return response;
197464562Sgshapiro}
197564562Sgshapiro
197690792Sgshapiro/*
197764562Sgshapiro**  MILTER_COMMAND -- send a command and return the response for each filter
197864562Sgshapiro**
197964562Sgshapiro**	Parameters:
198064562Sgshapiro**		command -- command to send.
198164562Sgshapiro**		data -- optional command data.
198264562Sgshapiro**		sz -- length of buf.
198364562Sgshapiro**		macros -- macros to send for filter smfi_getsymval().
198464562Sgshapiro**		e -- current envelope (for macro access).
198564562Sgshapiro**		state -- return state word.
198664562Sgshapiro**
198764562Sgshapiro**	Returns:
198864562Sgshapiro**		response string (may be NULL)
198964562Sgshapiro*/
199064562Sgshapiro
199164562Sgshapirostatic char *
199264562Sgshapiromilter_command(command, data, sz, macros, e, state)
199364562Sgshapiro	char command;
199464562Sgshapiro	void *data;
199564562Sgshapiro	ssize_t sz;
199664562Sgshapiro	char **macros;
199764562Sgshapiro	ENVELOPE *e;
199864562Sgshapiro	char *state;
199964562Sgshapiro{
200064562Sgshapiro	int i;
200164562Sgshapiro	char *response = NULL;
200290792Sgshapiro	time_t tn = 0;
200364562Sgshapiro
200464562Sgshapiro	if (tTd(64, 10))
200590792Sgshapiro		sm_dprintf("milter_command: cmd %c len %ld\n",
200664562Sgshapiro			(char) command, (long) sz);
200764562Sgshapiro
200864562Sgshapiro	*state = SMFIR_CONTINUE;
200964562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
201064562Sgshapiro	{
201164562Sgshapiro		struct milter *m = InputFilters[i];
201264562Sgshapiro
201371345Sgshapiro		/* previous problem? */
201471345Sgshapiro		if (m->mf_state == SMFS_ERROR)
201571345Sgshapiro		{
201671345Sgshapiro			MILTER_CHECK_ERROR(continue);
201771345Sgshapiro			break;
201871345Sgshapiro		}
201971345Sgshapiro
202064562Sgshapiro		/* sanity check */
202164562Sgshapiro		if (m->mf_sock < 0 ||
202264562Sgshapiro		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
202364562Sgshapiro			continue;
202464562Sgshapiro
202564562Sgshapiro		/* send macros (regardless of whether we send command) */
202664562Sgshapiro		if (macros != NULL && macros[0] != NULL)
202764562Sgshapiro		{
202864562Sgshapiro			milter_send_macros(m, macros, command, e);
202964562Sgshapiro			if (m->mf_state == SMFS_ERROR)
203064562Sgshapiro			{
203164562Sgshapiro				MILTER_CHECK_ERROR(continue);
203264562Sgshapiro				break;
203364562Sgshapiro			}
203464562Sgshapiro		}
203564562Sgshapiro
203690792Sgshapiro		if (MilterLogLevel > 21)
203790792Sgshapiro			tn = curtime();
203890792Sgshapiro
203964562Sgshapiro		response = milter_send_command(m, command, data, sz, e, state);
204090792Sgshapiro
204190792Sgshapiro		if (MilterLogLevel > 21)
204290792Sgshapiro		{
204390792Sgshapiro			/* log the time it took for the command per filter */
204490792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
204590792Sgshapiro				  "Milter (%s): time command (%c), %d",
204690792Sgshapiro				  m->mf_name, command, (int) (tn - curtime()));
204790792Sgshapiro		}
204890792Sgshapiro
204964562Sgshapiro		if (*state != SMFIR_CONTINUE)
205064562Sgshapiro			break;
205164562Sgshapiro	}
205264562Sgshapiro	return response;
205364562Sgshapiro}
205490792Sgshapiro/*
205564562Sgshapiro**  MILTER_NEGOTIATE -- get version and flags from filter
205664562Sgshapiro**
205764562Sgshapiro**	Parameters:
205864562Sgshapiro**		m -- milter filter structure.
205964562Sgshapiro**		e -- current envelope.
206064562Sgshapiro**
206164562Sgshapiro**	Returns:
206264562Sgshapiro**		0 on success, -1 otherwise
206364562Sgshapiro*/
206464562Sgshapiro
206564562Sgshapirostatic int
206664562Sgshapiromilter_negotiate(m, e)
206764562Sgshapiro	struct milter *m;
206864562Sgshapiro	ENVELOPE *e;
206964562Sgshapiro{
207064562Sgshapiro	char rcmd;
207164562Sgshapiro	mi_int32 fvers;
207264562Sgshapiro	mi_int32 fflags;
207364562Sgshapiro	mi_int32 pflags;
207464562Sgshapiro	char *response;
207564562Sgshapiro	ssize_t rlen;
207664562Sgshapiro	char data[MILTER_OPTLEN];
207764562Sgshapiro
207864562Sgshapiro	/* sanity check */
207964562Sgshapiro	if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
208064562Sgshapiro	{
208190792Sgshapiro		if (MilterLogLevel > 0)
208264562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
208390792Sgshapiro				  "Milter (%s): negotiate, impossible state",
208464562Sgshapiro				  m->mf_name);
208590792Sgshapiro		milter_error(m, e);
208664562Sgshapiro		return -1;
208764562Sgshapiro	}
208864562Sgshapiro
208964562Sgshapiro	fvers = htonl(SMFI_VERSION);
209064562Sgshapiro	fflags = htonl(SMFI_CURR_ACTS);
209164562Sgshapiro	pflags = htonl(SMFI_CURR_PROT);
209264562Sgshapiro	(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
209364562Sgshapiro	(void) memcpy(data + MILTER_LEN_BYTES,
209464562Sgshapiro		      (char *) &fflags, MILTER_LEN_BYTES);
209564562Sgshapiro	(void) memcpy(data + (MILTER_LEN_BYTES * 2),
209664562Sgshapiro		      (char *) &pflags, MILTER_LEN_BYTES);
209764562Sgshapiro	(void) milter_write(m, SMFIC_OPTNEG, data, sizeof data,
209864562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
209964562Sgshapiro
210064562Sgshapiro	if (m->mf_state == SMFS_ERROR)
210164562Sgshapiro		return -1;
210264562Sgshapiro
210364562Sgshapiro	response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e);
210464562Sgshapiro	if (m->mf_state == SMFS_ERROR)
210564562Sgshapiro		return -1;
210664562Sgshapiro
210764562Sgshapiro	if (rcmd != SMFIC_OPTNEG)
210864562Sgshapiro	{
210964562Sgshapiro		if (tTd(64, 5))
211090792Sgshapiro			sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n",
211164562Sgshapiro				m->mf_name, rcmd, SMFIC_OPTNEG);
211290792Sgshapiro		if (MilterLogLevel > 0)
211364562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
211490792Sgshapiro				  "Milter (%s): negotiate: returned %c instead of %c",
211564562Sgshapiro				  m->mf_name, rcmd, SMFIC_OPTNEG);
211664562Sgshapiro		if (response != NULL)
211790792Sgshapiro			sm_free(response); /* XXX */
211890792Sgshapiro		milter_error(m, e);
211964562Sgshapiro		return -1;
212064562Sgshapiro	}
212164562Sgshapiro
212264562Sgshapiro	/* Make sure we have enough bytes for the version */
212364562Sgshapiro	if (response == NULL || rlen < MILTER_LEN_BYTES)
212464562Sgshapiro	{
212564562Sgshapiro		if (tTd(64, 5))
212690792Sgshapiro			sm_dprintf("milter_negotiate(%s): did not return valid info\n",
212764562Sgshapiro				m->mf_name);
212890792Sgshapiro		if (MilterLogLevel > 0)
212964562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
213090792Sgshapiro				  "Milter (%s): negotiate: did not return valid info",
213164562Sgshapiro				  m->mf_name);
213264562Sgshapiro		if (response != NULL)
213390792Sgshapiro			sm_free(response); /* XXX */
213490792Sgshapiro		milter_error(m, e);
213564562Sgshapiro		return -1;
213664562Sgshapiro	}
213764562Sgshapiro
213864562Sgshapiro	/* extract information */
213964562Sgshapiro	(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
214064562Sgshapiro
214164562Sgshapiro	/* Now make sure we have enough for the feature bitmap */
214264562Sgshapiro	if (rlen != MILTER_OPTLEN)
214364562Sgshapiro	{
214464562Sgshapiro		if (tTd(64, 5))
214590792Sgshapiro			sm_dprintf("milter_negotiate(%s): did not return enough info\n",
214664562Sgshapiro				m->mf_name);
214790792Sgshapiro		if (MilterLogLevel > 0)
214864562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
214990792Sgshapiro				  "Milter (%s): negotiate: did not return enough info",
215064562Sgshapiro				  m->mf_name);
215164562Sgshapiro		if (response != NULL)
215290792Sgshapiro			sm_free(response); /* XXX */
215390792Sgshapiro		milter_error(m, e);
215464562Sgshapiro		return -1;
215564562Sgshapiro	}
215664562Sgshapiro
215764562Sgshapiro	(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
215864562Sgshapiro		      MILTER_LEN_BYTES);
215964562Sgshapiro	(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
216064562Sgshapiro		      MILTER_LEN_BYTES);
216190792Sgshapiro	sm_free(response); /* XXX */
216264562Sgshapiro	response = NULL;
216364562Sgshapiro
216464562Sgshapiro	m->mf_fvers = ntohl(fvers);
216564562Sgshapiro	m->mf_fflags = ntohl(fflags);
216664562Sgshapiro	m->mf_pflags = ntohl(pflags);
216764562Sgshapiro
216864562Sgshapiro	/* check for version compatibility */
216964562Sgshapiro	if (m->mf_fvers == 1 ||
217064562Sgshapiro	    m->mf_fvers > SMFI_VERSION)
217164562Sgshapiro	{
217264562Sgshapiro		if (tTd(64, 5))
217394334Sgshapiro			sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n",
217464562Sgshapiro				m->mf_name, m->mf_fvers, SMFI_VERSION);
217590792Sgshapiro		if (MilterLogLevel > 0)
217664562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
217794334Sgshapiro				  "Milter (%s): negotiate: version %d != MTA milter version %d",
217864562Sgshapiro				  m->mf_name, m->mf_fvers, SMFI_VERSION);
217990792Sgshapiro		milter_error(m, e);
218064562Sgshapiro		return -1;
218164562Sgshapiro	}
218264562Sgshapiro
218364562Sgshapiro	/* check for filter feature mismatch */
218464562Sgshapiro	if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags)
218564562Sgshapiro	{
218664562Sgshapiro		if (tTd(64, 5))
218794334Sgshapiro			sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n",
218864562Sgshapiro				m->mf_name, m->mf_fflags,
218994334Sgshapiro				SMFI_CURR_ACTS);
219090792Sgshapiro		if (MilterLogLevel > 0)
219164562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
219294334Sgshapiro				  "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx",
219364562Sgshapiro				  m->mf_name, m->mf_fflags,
219490792Sgshapiro				  (unsigned long) SMFI_CURR_ACTS);
219590792Sgshapiro		milter_error(m, e);
219664562Sgshapiro		return -1;
219764562Sgshapiro	}
219864562Sgshapiro
219964562Sgshapiro	/* check for protocol feature mismatch */
220064562Sgshapiro	if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags)
220164562Sgshapiro	{
220264562Sgshapiro		if (tTd(64, 5))
220394334Sgshapiro			sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n",
220464562Sgshapiro				m->mf_name, m->mf_pflags,
220590792Sgshapiro				(unsigned long) SMFI_CURR_PROT);
220690792Sgshapiro		if (MilterLogLevel > 0)
220764562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
220894334Sgshapiro				  "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx",
220964562Sgshapiro				  m->mf_name, m->mf_pflags,
221090792Sgshapiro				  (unsigned long) SMFI_CURR_PROT);
221190792Sgshapiro		milter_error(m, e);
221264562Sgshapiro		return -1;
221364562Sgshapiro	}
221464562Sgshapiro
221564562Sgshapiro	if (tTd(64, 5))
221694334Sgshapiro		sm_dprintf("milter_negotiate(%s): version %u, fflags 0x%x, pflags 0x%x\n",
221764562Sgshapiro			m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
221864562Sgshapiro	return 0;
221964562Sgshapiro}
222090792Sgshapiro/*
222164562Sgshapiro**  MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
222264562Sgshapiro**
222364562Sgshapiro**	Reduce code duplication by putting these checks in one place
222464562Sgshapiro**
222564562Sgshapiro**	Parameters:
222664562Sgshapiro**		e -- current envelope.
222764562Sgshapiro**
222864562Sgshapiro**	Returns:
222964562Sgshapiro**		none
223064562Sgshapiro*/
223164562Sgshapiro
223264562Sgshapirostatic void
223364562Sgshapiromilter_per_connection_check(e)
223464562Sgshapiro	ENVELOPE *e;
223564562Sgshapiro{
223664562Sgshapiro	int i;
223764562Sgshapiro
223864562Sgshapiro	/* see if we are done with any of the filters */
223964562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
224064562Sgshapiro	{
224164562Sgshapiro		struct milter *m = InputFilters[i];
224264562Sgshapiro
224373188Sgshapiro		if (m->mf_state == SMFS_CLOSABLE)
224464562Sgshapiro			milter_quit_filter(m, e);
224564562Sgshapiro	}
224664562Sgshapiro}
224790792Sgshapiro/*
224864562Sgshapiro**  MILTER_ERROR -- Put a milter filter into error state
224964562Sgshapiro**
225064562Sgshapiro**	Parameters:
225164562Sgshapiro**		m -- the broken filter.
225264562Sgshapiro**
225364562Sgshapiro**	Returns:
225464562Sgshapiro**		none
225564562Sgshapiro*/
225664562Sgshapiro
225764562Sgshapirostatic void
225890792Sgshapiromilter_error(m, e)
225964562Sgshapiro	struct milter *m;
226090792Sgshapiro	ENVELOPE *e;
226164562Sgshapiro{
226264562Sgshapiro	/*
226364562Sgshapiro	**  We could send a quit here but
226464562Sgshapiro	**  we may have gotten here due to
226564562Sgshapiro	**  an I/O error so we don't want
226664562Sgshapiro	**  to try to make things worse.
226764562Sgshapiro	*/
226864562Sgshapiro
226964562Sgshapiro	if (m->mf_sock >= 0)
227064562Sgshapiro	{
227164562Sgshapiro		(void) close(m->mf_sock);
227264562Sgshapiro		m->mf_sock = -1;
227364562Sgshapiro	}
227464562Sgshapiro	m->mf_state = SMFS_ERROR;
227590792Sgshapiro
227690792Sgshapiro	if (MilterLogLevel > 0)
227790792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state",
227890792Sgshapiro			  m->mf_name);
227964562Sgshapiro}
228090792Sgshapiro/*
228164562Sgshapiro**  MILTER_HEADERS -- send headers to a single milter filter
228264562Sgshapiro**
228364562Sgshapiro**	Parameters:
228464562Sgshapiro**		m -- current filter.
228564562Sgshapiro**		e -- current envelope.
228664562Sgshapiro**		state -- return state from response.
228764562Sgshapiro**
228864562Sgshapiro**	Returns:
228964562Sgshapiro**		response string (may be NULL)
229064562Sgshapiro*/
229164562Sgshapiro
229264562Sgshapirostatic char *
229364562Sgshapiromilter_headers(m, e, state)
229464562Sgshapiro	struct milter *m;
229564562Sgshapiro	ENVELOPE *e;
229664562Sgshapiro	char *state;
229764562Sgshapiro{
229864562Sgshapiro	char *response = NULL;
229964562Sgshapiro	HDR *h;
230064562Sgshapiro
230190792Sgshapiro	if (MilterLogLevel > 17)
230290792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send",
230390792Sgshapiro			  m->mf_name);
230490792Sgshapiro
230564562Sgshapiro	for (h = e->e_header; h != NULL; h = h->h_link)
230664562Sgshapiro	{
230764562Sgshapiro		char *buf;
230864562Sgshapiro		ssize_t s;
230964562Sgshapiro
231064562Sgshapiro		/* don't send over deleted headers */
231164562Sgshapiro		if (h->h_value == NULL)
231264562Sgshapiro		{
231364562Sgshapiro			/* strip H_USER so not counted in milter_chgheader() */
231464562Sgshapiro			h->h_flags &= ~H_USER;
231564562Sgshapiro			continue;
231664562Sgshapiro		}
231764562Sgshapiro
231864562Sgshapiro		/* skip auto-generated */
231964562Sgshapiro		if (!bitset(H_USER, h->h_flags))
232064562Sgshapiro			continue;
232164562Sgshapiro
232264562Sgshapiro		if (tTd(64, 10))
232390792Sgshapiro			sm_dprintf("milter_headers: %s: %s\n",
232464562Sgshapiro				h->h_field, h->h_value);
232590792Sgshapiro		if (MilterLogLevel > 21)
232690792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s",
232790792Sgshapiro				  m->mf_name, h->h_field);
232864562Sgshapiro
232990792Sgshapiro		s = strlen(h->h_field) + 1 + strlen(h->h_value) + 1;
233090792Sgshapiro		if (s < 0)
233190792Sgshapiro			continue;
233264562Sgshapiro		buf = (char *) xalloc(s);
233390792Sgshapiro		(void) sm_snprintf(buf, s, "%s%c%s",
233490792Sgshapiro			h->h_field, '\0', h->h_value);
233564562Sgshapiro
233664562Sgshapiro		/* send it over */
233764562Sgshapiro		response = milter_send_command(m, SMFIC_HEADER, buf,
233864562Sgshapiro					       s, e, state);
233990792Sgshapiro		sm_free(buf); /* XXX */
234064562Sgshapiro		if (m->mf_state == SMFS_ERROR ||
234164562Sgshapiro		    m->mf_state == SMFS_DONE ||
234264562Sgshapiro		    *state != SMFIR_CONTINUE)
234364562Sgshapiro			break;
234464562Sgshapiro	}
234590792Sgshapiro	if (MilterLogLevel > 17)
234690792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent",
234790792Sgshapiro			  m->mf_name);
234864562Sgshapiro	return response;
234964562Sgshapiro}
235090792Sgshapiro/*
235164562Sgshapiro**  MILTER_BODY -- send the body to a filter
235264562Sgshapiro**
235364562Sgshapiro**	Parameters:
235464562Sgshapiro**		m -- current filter.
235564562Sgshapiro**		e -- current envelope.
235664562Sgshapiro**		state -- return state from response.
235764562Sgshapiro**
235864562Sgshapiro**	Returns:
235964562Sgshapiro**		response string (may be NULL)
236064562Sgshapiro*/
236164562Sgshapiro
236264562Sgshapirostatic char *
236364562Sgshapiromilter_body(m, e, state)
236464562Sgshapiro	struct milter *m;
236564562Sgshapiro	ENVELOPE *e;
236664562Sgshapiro	char *state;
236764562Sgshapiro{
236864562Sgshapiro	char bufchar = '\0';
236964562Sgshapiro	char prevchar = '\0';
237064562Sgshapiro	int c;
237164562Sgshapiro	char *response = NULL;
237264562Sgshapiro	char *bp;
237364562Sgshapiro	char buf[MILTER_CHUNK_SIZE];
237464562Sgshapiro
237564562Sgshapiro	if (tTd(64, 10))
237690792Sgshapiro		sm_dprintf("milter_body\n");
237764562Sgshapiro
237864562Sgshapiro	if (bfrewind(e->e_dfp) < 0)
237964562Sgshapiro	{
238064562Sgshapiro		ExitStat = EX_IOERR;
238164562Sgshapiro		*state = SMFIR_TEMPFAIL;
238290792Sgshapiro		syserr("milter_body: %s/%cf%s: rewind error",
238390792Sgshapiro		       qid_printqueue(e->e_qgrp, e->e_qdir),
238490792Sgshapiro		       DATAFL_LETTER, e->e_id);
238564562Sgshapiro		return NULL;
238664562Sgshapiro	}
238764562Sgshapiro
238890792Sgshapiro	if (MilterLogLevel > 17)
238990792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send",
239090792Sgshapiro			  m->mf_name);
239164562Sgshapiro	bp = buf;
239290792Sgshapiro	while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF)
239364562Sgshapiro	{
239464562Sgshapiro		/*  Change LF to CRLF */
239564562Sgshapiro		if (c == '\n')
239664562Sgshapiro		{
239764562Sgshapiro			/* Not a CRLF already? */
239864562Sgshapiro			if (prevchar != '\r')
239964562Sgshapiro			{
240064562Sgshapiro				/* Room for CR now? */
240164562Sgshapiro				if (bp + 2 > &buf[sizeof buf])
240264562Sgshapiro				{
240364562Sgshapiro					/* No room, buffer LF */
240464562Sgshapiro					bufchar = c;
240564562Sgshapiro
240664562Sgshapiro					/* and send CR now */
240764562Sgshapiro					c = '\r';
240864562Sgshapiro				}
240964562Sgshapiro				else
241064562Sgshapiro				{
241164562Sgshapiro					/* Room to do it now */
241264562Sgshapiro					*bp++ = '\r';
241364562Sgshapiro					prevchar = '\r';
241464562Sgshapiro				}
241564562Sgshapiro			}
241664562Sgshapiro		}
241764562Sgshapiro		*bp++ = (char) c;
241864562Sgshapiro		prevchar = c;
241964562Sgshapiro		if (bp >= &buf[sizeof buf])
242064562Sgshapiro		{
242164562Sgshapiro			/* send chunk */
242264562Sgshapiro			response = milter_send_command(m, SMFIC_BODY, buf,
242364562Sgshapiro						       bp - buf, e, state);
242464562Sgshapiro			bp = buf;
242564562Sgshapiro			if (bufchar != '\0')
242664562Sgshapiro			{
242764562Sgshapiro				*bp++ = bufchar;
242864562Sgshapiro				bufchar = '\0';
242964562Sgshapiro				prevchar = bufchar;
243064562Sgshapiro			}
243164562Sgshapiro		}
243264562Sgshapiro		if (m->mf_state == SMFS_ERROR ||
243364562Sgshapiro		    m->mf_state == SMFS_DONE ||
243464562Sgshapiro		    *state != SMFIR_CONTINUE)
243564562Sgshapiro			break;
243664562Sgshapiro	}
243764562Sgshapiro
243864562Sgshapiro	/* check for read errors */
243990792Sgshapiro	if (sm_io_error(e->e_dfp))
244064562Sgshapiro	{
244164562Sgshapiro		ExitStat = EX_IOERR;
244264562Sgshapiro		if (*state == SMFIR_CONTINUE ||
244364562Sgshapiro		    *state == SMFIR_ACCEPT)
244464562Sgshapiro		{
244564562Sgshapiro			*state = SMFIR_TEMPFAIL;
244664562Sgshapiro			if (response != NULL)
244764562Sgshapiro			{
244890792Sgshapiro				sm_free(response); /* XXX */
244964562Sgshapiro				response = NULL;
245064562Sgshapiro			}
245164562Sgshapiro		}
245290792Sgshapiro		syserr("milter_body: %s/%cf%s: read error",
245390792Sgshapiro		       qid_printqueue(e->e_qgrp, e->e_qdir),
245490792Sgshapiro		       DATAFL_LETTER, e->e_id);
245564562Sgshapiro		return response;
245664562Sgshapiro	}
245764562Sgshapiro
245864562Sgshapiro	/* send last body chunk */
245964562Sgshapiro	if (bp > buf &&
246064562Sgshapiro	    m->mf_state != SMFS_ERROR &&
246164562Sgshapiro	    m->mf_state != SMFS_DONE &&
246264562Sgshapiro	    *state == SMFIR_CONTINUE)
246364562Sgshapiro	{
246464562Sgshapiro		/* send chunk */
246564562Sgshapiro		response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
246664562Sgshapiro					       e, state);
246764562Sgshapiro		bp = buf;
246864562Sgshapiro	}
246990792Sgshapiro	if (MilterLogLevel > 17)
247090792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent",
247190792Sgshapiro			  m->mf_name);
247264562Sgshapiro	return response;
247364562Sgshapiro}
247464562Sgshapiro
247564562Sgshapiro/*
247664562Sgshapiro**  Actions
247764562Sgshapiro*/
247864562Sgshapiro
247990792Sgshapiro/*
248064562Sgshapiro**  MILTER_ADDHEADER -- Add the supplied header to the message
248164562Sgshapiro**
248264562Sgshapiro**	Parameters:
248364562Sgshapiro**		response -- encoded form of header/value.
248464562Sgshapiro**		rlen -- length of response.
248564562Sgshapiro**		e -- current envelope.
248664562Sgshapiro**
248764562Sgshapiro**	Returns:
248864562Sgshapiro**		none
248964562Sgshapiro*/
249064562Sgshapiro
249164562Sgshapirostatic void
249264562Sgshapiromilter_addheader(response, rlen, e)
249364562Sgshapiro	char *response;
249464562Sgshapiro	ssize_t rlen;
249564562Sgshapiro	ENVELOPE *e;
249664562Sgshapiro{
249764562Sgshapiro	char *val;
249871345Sgshapiro	HDR *h;
249964562Sgshapiro
250064562Sgshapiro	if (tTd(64, 10))
250190792Sgshapiro		sm_dprintf("milter_addheader: ");
250264562Sgshapiro
250364562Sgshapiro	/* sanity checks */
250464562Sgshapiro	if (response == NULL)
250564562Sgshapiro	{
250664562Sgshapiro		if (tTd(64, 10))
250790792Sgshapiro			sm_dprintf("NULL response\n");
250864562Sgshapiro		return;
250964562Sgshapiro	}
251064562Sgshapiro
251164562Sgshapiro	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
251264562Sgshapiro	{
251364562Sgshapiro		if (tTd(64, 10))
251490792Sgshapiro			sm_dprintf("didn't follow protocol (total len)\n");
251564562Sgshapiro		return;
251664562Sgshapiro	}
251764562Sgshapiro
251864562Sgshapiro	/* Find separating NUL */
251964562Sgshapiro	val = response + strlen(response) + 1;
252064562Sgshapiro
252164562Sgshapiro	/* another sanity check */
252264562Sgshapiro	if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
252364562Sgshapiro	{
252464562Sgshapiro		if (tTd(64, 10))
252590792Sgshapiro			sm_dprintf("didn't follow protocol (part len)\n");
252664562Sgshapiro		return;
252764562Sgshapiro	}
252864562Sgshapiro
252964562Sgshapiro	if (*response == '\0')
253064562Sgshapiro	{
253164562Sgshapiro		if (tTd(64, 10))
253290792Sgshapiro			sm_dprintf("empty field name\n");
253364562Sgshapiro		return;
253464562Sgshapiro	}
253564562Sgshapiro
253671345Sgshapiro	for (h = e->e_header; h != NULL; h = h->h_link)
253771345Sgshapiro	{
253890792Sgshapiro		if (sm_strcasecmp(h->h_field, response) == 0 &&
253971345Sgshapiro		    !bitset(H_USER, h->h_flags) &&
254071345Sgshapiro		    !bitset(H_TRACE, h->h_flags))
254171345Sgshapiro			break;
254271345Sgshapiro	}
254371345Sgshapiro
254464562Sgshapiro	/* add to e_msgsize */
254564562Sgshapiro	e->e_msgsize += strlen(response) + 2 + strlen(val);
254664562Sgshapiro
254771345Sgshapiro	if (h != NULL)
254871345Sgshapiro	{
254971345Sgshapiro		if (tTd(64, 10))
255090792Sgshapiro			sm_dprintf("Replace default header %s value with %s\n",
255190792Sgshapiro				   h->h_field, val);
255290792Sgshapiro		if (MilterLogLevel > 8)
255390792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
255490792Sgshapiro				  "Milter change: default header %s value with %s",
255590792Sgshapiro				  h->h_field, val);
255671345Sgshapiro		h->h_value = newstr(val);
255771345Sgshapiro		h->h_flags |= H_USER;
255871345Sgshapiro	}
255971345Sgshapiro	else
256071345Sgshapiro	{
256171345Sgshapiro		if (tTd(64, 10))
256290792Sgshapiro			sm_dprintf("Add %s: %s\n", response, val);
256390792Sgshapiro		if (MilterLogLevel > 8)
256490792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter add: header: %s: %s",
256590792Sgshapiro				  response, val);
256690792Sgshapiro		addheader(newstr(response), val, H_USER, e);
256771345Sgshapiro	}
256864562Sgshapiro}
256990792Sgshapiro/*
257064562Sgshapiro**  MILTER_CHANGEHEADER -- Change the supplied header in the message
257164562Sgshapiro**
257264562Sgshapiro**	Parameters:
257364562Sgshapiro**		response -- encoded form of header/index/value.
257464562Sgshapiro**		rlen -- length of response.
257564562Sgshapiro**		e -- current envelope.
257664562Sgshapiro**
257764562Sgshapiro**	Returns:
257864562Sgshapiro**		none
257964562Sgshapiro*/
258064562Sgshapiro
258164562Sgshapirostatic void
258264562Sgshapiromilter_changeheader(response, rlen, e)
258364562Sgshapiro	char *response;
258464562Sgshapiro	ssize_t rlen;
258564562Sgshapiro	ENVELOPE *e;
258664562Sgshapiro{
258764562Sgshapiro	mi_int32 i, index;
258864562Sgshapiro	char *field, *val;
258971345Sgshapiro	HDR *h, *sysheader;
259064562Sgshapiro
259164562Sgshapiro	if (tTd(64, 10))
259290792Sgshapiro		sm_dprintf("milter_changeheader: ");
259364562Sgshapiro
259464562Sgshapiro	/* sanity checks */
259564562Sgshapiro	if (response == NULL)
259664562Sgshapiro	{
259764562Sgshapiro		if (tTd(64, 10))
259890792Sgshapiro			sm_dprintf("NULL response\n");
259964562Sgshapiro		return;
260064562Sgshapiro	}
260164562Sgshapiro
260264562Sgshapiro	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
260364562Sgshapiro	{
260464562Sgshapiro		if (tTd(64, 10))
260590792Sgshapiro			sm_dprintf("didn't follow protocol (total len)\n");
260664562Sgshapiro		return;
260764562Sgshapiro	}
260864562Sgshapiro
260964562Sgshapiro	/* Find separating NUL */
261064562Sgshapiro	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
261164562Sgshapiro	index = ntohl(i);
261264562Sgshapiro	field = response + MILTER_LEN_BYTES;
261364562Sgshapiro	val = field + strlen(field) + 1;
261464562Sgshapiro
261564562Sgshapiro	/* another sanity check */
261664562Sgshapiro	if (MILTER_LEN_BYTES + strlen(field) + 1 +
261764562Sgshapiro	    strlen(val) + 1 != (size_t) rlen)
261864562Sgshapiro	{
261964562Sgshapiro		if (tTd(64, 10))
262090792Sgshapiro			sm_dprintf("didn't follow protocol (part len)\n");
262164562Sgshapiro		return;
262264562Sgshapiro	}
262364562Sgshapiro
262464562Sgshapiro	if (*field == '\0')
262564562Sgshapiro	{
262664562Sgshapiro		if (tTd(64, 10))
262790792Sgshapiro			sm_dprintf("empty field name\n");
262864562Sgshapiro		return;
262964562Sgshapiro	}
263064562Sgshapiro
263171345Sgshapiro	sysheader = NULL;
263264562Sgshapiro	for (h = e->e_header; h != NULL; h = h->h_link)
263364562Sgshapiro	{
263490792Sgshapiro		if (sm_strcasecmp(h->h_field, field) == 0)
263571345Sgshapiro		{
263671345Sgshapiro			if (bitset(H_USER, h->h_flags) &&
263771345Sgshapiro			    --index <= 0)
263871345Sgshapiro			{
263971345Sgshapiro				sysheader = NULL;
264071345Sgshapiro				break;
264171345Sgshapiro			}
264271345Sgshapiro			else if (!bitset(H_USER, h->h_flags) &&
264371345Sgshapiro				 !bitset(H_TRACE, h->h_flags))
264471345Sgshapiro			{
264571345Sgshapiro				/*
264671345Sgshapiro				**  DRUMS msg-fmt draft says can only have
264771345Sgshapiro				**  multiple occurences of trace fields,
264871345Sgshapiro				**  so make sure we replace any non-trace,
264971345Sgshapiro				**  non-user field.
265071345Sgshapiro				*/
265171345Sgshapiro
265271345Sgshapiro				sysheader = h;
265371345Sgshapiro			}
265471345Sgshapiro		}
265564562Sgshapiro	}
265664562Sgshapiro
265771345Sgshapiro	/* if not found as user-provided header at index, use sysheader */
265864562Sgshapiro	if (h == NULL)
265971345Sgshapiro		h = sysheader;
266071345Sgshapiro
266171345Sgshapiro	if (h == NULL)
266264562Sgshapiro	{
266364562Sgshapiro		if (*val == '\0')
266464562Sgshapiro		{
266564562Sgshapiro			if (tTd(64, 10))
266690792Sgshapiro				sm_dprintf("Delete (noop) %s:\n", field);
266764562Sgshapiro		}
266864562Sgshapiro		else
266964562Sgshapiro		{
267064562Sgshapiro			/* treat modify value with no existing header as add */
267164562Sgshapiro			if (tTd(64, 10))
267290792Sgshapiro				sm_dprintf("Add %s: %s\n", field, val);
267390792Sgshapiro			addheader(newstr(field), val, H_USER, e);
267464562Sgshapiro		}
267564562Sgshapiro		return;
267664562Sgshapiro	}
267764562Sgshapiro
267864562Sgshapiro	if (tTd(64, 10))
267964562Sgshapiro	{
268064562Sgshapiro		if (*val == '\0')
268164562Sgshapiro		{
268290792Sgshapiro			sm_dprintf("Delete%s %s: %s\n",
268390792Sgshapiro				   h == sysheader ? " (default header)" : "",
268490792Sgshapiro				   field,
268590792Sgshapiro				   h->h_value == NULL ? "<NULL>" : h->h_value);
268664562Sgshapiro		}
268764562Sgshapiro		else
268864562Sgshapiro		{
268990792Sgshapiro			sm_dprintf("Change%s %s: from %s to %s\n",
269090792Sgshapiro				   h == sysheader ? " (default header)" : "",
269190792Sgshapiro				   field,
269290792Sgshapiro				   h->h_value == NULL ? "<NULL>" : h->h_value,
269390792Sgshapiro				   val);
269464562Sgshapiro		}
269564562Sgshapiro	}
269664562Sgshapiro
269790792Sgshapiro	if (MilterLogLevel > 8)
269890792Sgshapiro	{
269990792Sgshapiro		if (*val == '\0')
270090792Sgshapiro		{
270190792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
270290792Sgshapiro				  "Milter delete: header %s %s: %s",
270390792Sgshapiro				  h == sysheader ? " (default header)" : "",
270490792Sgshapiro				  field,
270590792Sgshapiro				  h->h_value == NULL ? "<NULL>" : h->h_value);
270690792Sgshapiro		}
270790792Sgshapiro		else
270890792Sgshapiro		{
270990792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
271090792Sgshapiro				  "Milter change: header %s %s: from %s to %s",
271190792Sgshapiro				  h == sysheader ? " (default header)" : "",
271290792Sgshapiro				  field,
271390792Sgshapiro				  h->h_value == NULL ? "<NULL>" : h->h_value,
271490792Sgshapiro				  val);
271590792Sgshapiro		}
271690792Sgshapiro	}
271790792Sgshapiro
271871345Sgshapiro	if (h != sysheader && h->h_value != NULL)
271964562Sgshapiro	{
272090792Sgshapiro		size_t l;
272190792Sgshapiro
272290792Sgshapiro		l = strlen(h->h_value);
272390792Sgshapiro		if (l > e->e_msgsize)
272490792Sgshapiro			e->e_msgsize = 0;
272590792Sgshapiro		else
272690792Sgshapiro			e->e_msgsize -= l;
272790792Sgshapiro		/* rpool, don't free: sm_free(h->h_value); XXX */
272864562Sgshapiro	}
272964562Sgshapiro
273064562Sgshapiro	if (*val == '\0')
273164562Sgshapiro	{
273264562Sgshapiro		/* Remove "Field: " from message size */
273371345Sgshapiro		if (h != sysheader)
273490792Sgshapiro		{
273590792Sgshapiro			size_t l;
273690792Sgshapiro
273790792Sgshapiro			l = strlen(h->h_field) + 2;
273890792Sgshapiro			if (l > e->e_msgsize)
273990792Sgshapiro				e->e_msgsize = 0;
274090792Sgshapiro			else
274190792Sgshapiro				e->e_msgsize -= l;
274290792Sgshapiro		}
274364562Sgshapiro		h->h_value = NULL;
274464562Sgshapiro	}
274564562Sgshapiro	else
274664562Sgshapiro	{
274764562Sgshapiro		h->h_value = newstr(val);
274871345Sgshapiro		h->h_flags |= H_USER;
274964562Sgshapiro		e->e_msgsize += strlen(h->h_value);
275064562Sgshapiro	}
275164562Sgshapiro}
275290792Sgshapiro/*
275364562Sgshapiro**  MILTER_ADDRCPT -- Add the supplied recipient to the message
275464562Sgshapiro**
275564562Sgshapiro**	Parameters:
275664562Sgshapiro**		response -- encoded form of recipient address.
275764562Sgshapiro**		rlen -- length of response.
275864562Sgshapiro**		e -- current envelope.
275964562Sgshapiro**
276064562Sgshapiro**	Returns:
276164562Sgshapiro**		none
276264562Sgshapiro*/
276364562Sgshapiro
276464562Sgshapirostatic void
276564562Sgshapiromilter_addrcpt(response, rlen, e)
276664562Sgshapiro	char *response;
276764562Sgshapiro	ssize_t rlen;
276864562Sgshapiro	ENVELOPE *e;
276964562Sgshapiro{
277064562Sgshapiro	if (tTd(64, 10))
277190792Sgshapiro		sm_dprintf("milter_addrcpt: ");
277264562Sgshapiro
277364562Sgshapiro	/* sanity checks */
277464562Sgshapiro	if (response == NULL)
277564562Sgshapiro	{
277664562Sgshapiro		if (tTd(64, 10))
277790792Sgshapiro			sm_dprintf("NULL response\n");
277864562Sgshapiro		return;
277964562Sgshapiro	}
278064562Sgshapiro
278164562Sgshapiro	if (*response == '\0' ||
278264562Sgshapiro	    strlen(response) + 1 != (size_t) rlen)
278364562Sgshapiro	{
278464562Sgshapiro		if (tTd(64, 10))
278590792Sgshapiro			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
278690792Sgshapiro				   (int) strlen(response), (int) (rlen - 1));
278764562Sgshapiro		return;
278864562Sgshapiro	}
278964562Sgshapiro
279064562Sgshapiro	if (tTd(64, 10))
279190792Sgshapiro		sm_dprintf("%s\n", response);
279290792Sgshapiro	if (MilterLogLevel > 8)
279390792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
279464562Sgshapiro	(void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
279564562Sgshapiro	return;
279664562Sgshapiro}
279790792Sgshapiro/*
279864562Sgshapiro**  MILTER_DELRCPT -- Delete the supplied recipient from the message
279964562Sgshapiro**
280064562Sgshapiro**	Parameters:
280164562Sgshapiro**		response -- encoded form of recipient address.
280264562Sgshapiro**		rlen -- length of response.
280364562Sgshapiro**		e -- current envelope.
280464562Sgshapiro**
280564562Sgshapiro**	Returns:
280664562Sgshapiro**		none
280764562Sgshapiro*/
280864562Sgshapiro
280964562Sgshapirostatic void
281064562Sgshapiromilter_delrcpt(response, rlen, e)
281164562Sgshapiro	char *response;
281264562Sgshapiro	ssize_t rlen;
281364562Sgshapiro	ENVELOPE *e;
281464562Sgshapiro{
281564562Sgshapiro	if (tTd(64, 10))
281690792Sgshapiro		sm_dprintf("milter_delrcpt: ");
281764562Sgshapiro
281864562Sgshapiro	/* sanity checks */
281964562Sgshapiro	if (response == NULL)
282064562Sgshapiro	{
282164562Sgshapiro		if (tTd(64, 10))
282290792Sgshapiro			sm_dprintf("NULL response\n");
282364562Sgshapiro		return;
282464562Sgshapiro	}
282564562Sgshapiro
282664562Sgshapiro	if (*response == '\0' ||
282764562Sgshapiro	    strlen(response) + 1 != (size_t) rlen)
282864562Sgshapiro	{
282964562Sgshapiro		if (tTd(64, 10))
283090792Sgshapiro			sm_dprintf("didn't follow protocol (total len)\n");
283164562Sgshapiro		return;
283264562Sgshapiro	}
283364562Sgshapiro
283464562Sgshapiro	if (tTd(64, 10))
283590792Sgshapiro		sm_dprintf("%s\n", response);
283690792Sgshapiro	if (MilterLogLevel > 8)
283790792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s",
283890792Sgshapiro			  response);
283964562Sgshapiro	(void) removefromlist(response, &e->e_sendqueue, e);
284064562Sgshapiro	return;
284164562Sgshapiro}
284290792Sgshapiro/*
284390792Sgshapiro**  MILTER_REPLBODY -- Replace the current data file with new body
284464562Sgshapiro**
284564562Sgshapiro**	Parameters:
284664562Sgshapiro**		response -- encoded form of new body.
284764562Sgshapiro**		rlen -- length of response.
284864562Sgshapiro**		newfilter -- if first time called by a new filter
284964562Sgshapiro**		e -- current envelope.
285064562Sgshapiro**
285164562Sgshapiro**	Returns:
285264562Sgshapiro**		0 upon success, -1 upon failure
285364562Sgshapiro*/
285464562Sgshapiro
285564562Sgshapirostatic int
285664562Sgshapiromilter_replbody(response, rlen, newfilter, e)
285764562Sgshapiro	char *response;
285864562Sgshapiro	ssize_t rlen;
285964562Sgshapiro	bool newfilter;
286064562Sgshapiro	ENVELOPE *e;
286164562Sgshapiro{
286264562Sgshapiro	static char prevchar;
286364562Sgshapiro	int i;
286464562Sgshapiro
286564562Sgshapiro	if (tTd(64, 10))
286690792Sgshapiro		sm_dprintf("milter_replbody\n");
286764562Sgshapiro
286890792Sgshapiro	/* If a new filter, reset previous character and truncate data file */
286964562Sgshapiro	if (newfilter)
287064562Sgshapiro	{
287194334Sgshapiro		off_t prevsize;
287264562Sgshapiro		char dfname[MAXPATHLEN];
287364562Sgshapiro
287490792Sgshapiro		(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER),
287590792Sgshapiro				  sizeof dfname);
287664562Sgshapiro
287764562Sgshapiro		/* Reset prevchar */
287864562Sgshapiro		prevchar = '\0';
287964562Sgshapiro
288090792Sgshapiro		/* Get the current data file information */
288194334Sgshapiro		prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL);
288294334Sgshapiro		if (prevsize < 0)
288394334Sgshapiro			prevsize = 0;
288464562Sgshapiro
288590792Sgshapiro		/* truncate current data file */
288690792Sgshapiro		if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
288764562Sgshapiro		{
288890792Sgshapiro			if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0)
288990792Sgshapiro			{
289090792Sgshapiro				MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s");
289190792Sgshapiro				return -1;
289290792Sgshapiro			}
289364562Sgshapiro		}
289464562Sgshapiro		else
289564562Sgshapiro		{
289690792Sgshapiro			int err;
289790792Sgshapiro
289890792Sgshapiro# if NOFTRUNCATE
289990792Sgshapiro			/* XXX: Not much we can do except rewind it */
290090792Sgshapiro			err = sm_io_error(e->e_dfp);
290190792Sgshapiro			(void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT);
290290792Sgshapiro
290390792Sgshapiro			/*
290490792Sgshapiro			**  Clear error if tried to fflush()
290590792Sgshapiro			**  a read-only file pointer and
290690792Sgshapiro			**  there wasn't a previous error.
290790792Sgshapiro			*/
290890792Sgshapiro
290990792Sgshapiro			if (err == 0)
291090792Sgshapiro				sm_io_clearerr(e->e_dfp);
291190792Sgshapiro
291290792Sgshapiro			/* errno is set implicitly by fseek() before return */
291390792Sgshapiro			err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT,
291490792Sgshapiro					 0, SEEK_SET);
291590792Sgshapiro# else /* NOFTRUNCATE */
291690792Sgshapiro			err = ftruncate(sm_io_getinfo(e->e_dfp,
291790792Sgshapiro						      SM_IO_WHAT_FD, NULL),
291890792Sgshapiro					0);
291990792Sgshapiro# endif /* NOFTRUNCATE */
292090792Sgshapiro			if (err < 0)
292190792Sgshapiro			{
292290792Sgshapiro				MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s");
292390792Sgshapiro				return -1;
292490792Sgshapiro			}
292564562Sgshapiro		}
292690792Sgshapiro
292790792Sgshapiro		if (prevsize > e->e_msgsize)
292890792Sgshapiro			e->e_msgsize = 0;
292990792Sgshapiro		else
293090792Sgshapiro			e->e_msgsize -= prevsize;
293164562Sgshapiro	}
293264562Sgshapiro
293390792Sgshapiro	if (newfilter && MilterLogLevel > 8)
293490792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced");
293590792Sgshapiro
293664562Sgshapiro	if (response == NULL)
293764562Sgshapiro	{
293864562Sgshapiro		/* Flush the buffered '\r' */
293964562Sgshapiro		if (prevchar == '\r')
294064562Sgshapiro		{
294190792Sgshapiro			(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar);
294264562Sgshapiro			e->e_msgsize++;
294364562Sgshapiro		}
294464562Sgshapiro		return 0;
294564562Sgshapiro	}
294664562Sgshapiro
294764562Sgshapiro	for (i = 0; i < rlen; i++)
294864562Sgshapiro	{
294964562Sgshapiro		/* Buffered char from last chunk */
295064562Sgshapiro		if (i == 0 && prevchar == '\r')
295164562Sgshapiro		{
295264562Sgshapiro			/* Not CRLF, output prevchar */
295364562Sgshapiro			if (response[i] != '\n')
295464562Sgshapiro			{
295590792Sgshapiro				(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT,
295690792Sgshapiro						  prevchar);
295764562Sgshapiro				e->e_msgsize++;
295864562Sgshapiro			}
295964562Sgshapiro			prevchar = '\0';
296064562Sgshapiro		}
296164562Sgshapiro
296264562Sgshapiro		/* Turn CRLF into LF */
296364562Sgshapiro		if (response[i] == '\r')
296464562Sgshapiro		{
296564562Sgshapiro			/* check if at end of chunk */
296664562Sgshapiro			if (i + 1 < rlen)
296764562Sgshapiro			{
296864562Sgshapiro				/* If LF, strip CR */
296964562Sgshapiro				if (response[i + 1] == '\n')
297064562Sgshapiro					i++;
297164562Sgshapiro			}
297264562Sgshapiro			else
297364562Sgshapiro			{
297464562Sgshapiro				/* check next chunk */
297564562Sgshapiro				prevchar = '\r';
297664562Sgshapiro				continue;
297764562Sgshapiro			}
297864562Sgshapiro		}
297990792Sgshapiro		(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]);
298064562Sgshapiro		e->e_msgsize++;
298164562Sgshapiro	}
298264562Sgshapiro	return 0;
298364562Sgshapiro}
298464562Sgshapiro
298564562Sgshapiro/*
298664562Sgshapiro**  MTA callouts
298764562Sgshapiro*/
298864562Sgshapiro
298990792Sgshapiro/*
299064562Sgshapiro**  MILTER_INIT -- open and negotiate with all of the filters
299164562Sgshapiro**
299264562Sgshapiro**	Parameters:
299364562Sgshapiro**		e -- current envelope.
299464562Sgshapiro**		state -- return state from response.
299564562Sgshapiro**
299664562Sgshapiro**	Returns:
299790792Sgshapiro**		true iff at least one filter is active
299864562Sgshapiro*/
299964562Sgshapiro
300064562Sgshapiro/* ARGSUSED */
300190792Sgshapirobool
300264562Sgshapiromilter_init(e, state)
300364562Sgshapiro	ENVELOPE *e;
300464562Sgshapiro	char *state;
300564562Sgshapiro{
300664562Sgshapiro	int i;
300764562Sgshapiro
300864562Sgshapiro	if (tTd(64, 10))
300990792Sgshapiro		sm_dprintf("milter_init\n");
301064562Sgshapiro
301164562Sgshapiro	*state = SMFIR_CONTINUE;
301290792Sgshapiro	if (InputFilters[0] == NULL)
301390792Sgshapiro	{
301490792Sgshapiro		if (MilterLogLevel > 10)
301590792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
301690792Sgshapiro				  "Milter: no active filter");
301790792Sgshapiro		return false;
301890792Sgshapiro	}
301990792Sgshapiro
302064562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
302164562Sgshapiro	{
302264562Sgshapiro		struct milter *m = InputFilters[i];
302364562Sgshapiro
302490792Sgshapiro		m->mf_sock = milter_open(m, false, e);
302564562Sgshapiro		if (m->mf_state == SMFS_ERROR)
302664562Sgshapiro		{
302764562Sgshapiro			MILTER_CHECK_ERROR(continue);
302864562Sgshapiro			break;
302964562Sgshapiro		}
303064562Sgshapiro
303164562Sgshapiro		if (m->mf_sock < 0 ||
303264562Sgshapiro		    milter_negotiate(m, e) < 0 ||
303364562Sgshapiro		    m->mf_state == SMFS_ERROR)
303464562Sgshapiro		{
303564562Sgshapiro			if (tTd(64, 5))
303690792Sgshapiro				sm_dprintf("milter_init(%s): failed to %s\n",
303790792Sgshapiro					   m->mf_name,
303890792Sgshapiro					   m->mf_sock < 0 ? "open" :
303990792Sgshapiro							    "negotiate");
304090792Sgshapiro			if (MilterLogLevel > 0)
304190792Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
304290792Sgshapiro					  "Milter (%s): init failed to %s",
304390792Sgshapiro					  m->mf_name,
304490792Sgshapiro					  m->mf_sock < 0 ? "open" :
304590792Sgshapiro							   "negotiate");
304664562Sgshapiro
304764562Sgshapiro			/* if negotation failure, close socket */
304890792Sgshapiro			milter_error(m, e);
304971345Sgshapiro			MILTER_CHECK_ERROR(continue);
305064562Sgshapiro		}
305190792Sgshapiro		if (MilterLogLevel > 9)
305290792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
305390792Sgshapiro				  "Milter (%s): init success to %s",
305490792Sgshapiro				  m->mf_name,
305590792Sgshapiro				  m->mf_sock < 0 ? "open" : "negotiate");
305664562Sgshapiro	}
305764562Sgshapiro
305864562Sgshapiro	/*
305964562Sgshapiro	**  If something temp/perm failed with one of the filters,
306064562Sgshapiro	**  we won't be using any of them, so clear any existing
306164562Sgshapiro	**  connections.
306264562Sgshapiro	*/
306364562Sgshapiro
306464562Sgshapiro	if (*state != SMFIR_CONTINUE)
306564562Sgshapiro		milter_quit(e);
306690792Sgshapiro
306790792Sgshapiro	return true;
306864562Sgshapiro}
306990792Sgshapiro/*
307064562Sgshapiro**  MILTER_CONNECT -- send connection info to milter filters
307164562Sgshapiro**
307264562Sgshapiro**	Parameters:
307364562Sgshapiro**		hostname -- hostname of remote machine.
307464562Sgshapiro**		addr -- address of remote machine.
307564562Sgshapiro**		e -- current envelope.
307664562Sgshapiro**		state -- return state from response.
307764562Sgshapiro**
307864562Sgshapiro**	Returns:
307964562Sgshapiro**		response string (may be NULL)
308064562Sgshapiro*/
308164562Sgshapiro
308264562Sgshapirochar *
308364562Sgshapiromilter_connect(hostname, addr, e, state)
308464562Sgshapiro	char *hostname;
308564562Sgshapiro	SOCKADDR addr;
308664562Sgshapiro	ENVELOPE *e;
308764562Sgshapiro	char *state;
308864562Sgshapiro{
308964562Sgshapiro	char family;
309090792Sgshapiro	unsigned short port;
309164562Sgshapiro	char *buf, *bp;
309264562Sgshapiro	char *response;
309364562Sgshapiro	char *sockinfo = NULL;
309464562Sgshapiro	ssize_t s;
309564562Sgshapiro# if NETINET6
309664562Sgshapiro	char buf6[INET6_ADDRSTRLEN];
309764562Sgshapiro# endif /* NETINET6 */
309864562Sgshapiro
309964562Sgshapiro	if (tTd(64, 10))
310090792Sgshapiro		sm_dprintf("milter_connect(%s)\n", hostname);
310190792Sgshapiro	if (MilterLogLevel > 9)
310290792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters");
310364562Sgshapiro
310464562Sgshapiro	/* gather data */
310564562Sgshapiro	switch (addr.sa.sa_family)
310664562Sgshapiro	{
310764562Sgshapiro# if NETUNIX
310864562Sgshapiro	  case AF_UNIX:
310964562Sgshapiro		family = SMFIA_UNIX;
311064562Sgshapiro		port = htons(0);
311164562Sgshapiro		sockinfo = addr.sunix.sun_path;
311264562Sgshapiro		break;
311364562Sgshapiro# endif /* NETUNIX */
311464562Sgshapiro
311564562Sgshapiro# if NETINET
311664562Sgshapiro	  case AF_INET:
311764562Sgshapiro		family = SMFIA_INET;
311894334Sgshapiro		port = addr.sin.sin_port;
311964562Sgshapiro		sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
312064562Sgshapiro		break;
312164562Sgshapiro# endif /* NETINET */
312264562Sgshapiro
312364562Sgshapiro# if NETINET6
312464562Sgshapiro	  case AF_INET6:
312580785Sgshapiro		if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr))
312680785Sgshapiro			family = SMFIA_INET;
312780785Sgshapiro		else
312880785Sgshapiro			family = SMFIA_INET6;
312994334Sgshapiro		port = addr.sin6.sin6_port;
313064562Sgshapiro		sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
313164562Sgshapiro				       sizeof buf6);
313264562Sgshapiro		if (sockinfo == NULL)
313364562Sgshapiro			sockinfo = "";
313464562Sgshapiro		break;
313564562Sgshapiro# endif /* NETINET6 */
313664562Sgshapiro
313764562Sgshapiro	  default:
313864562Sgshapiro		family = SMFIA_UNKNOWN;
313964562Sgshapiro		break;
314064562Sgshapiro	}
314164562Sgshapiro
314264562Sgshapiro	s = strlen(hostname) + 1 + sizeof(family);
314364562Sgshapiro	if (family != SMFIA_UNKNOWN)
314464562Sgshapiro		s += sizeof(port) + strlen(sockinfo) + 1;
314564562Sgshapiro
314690792Sgshapiro	buf = (char *) xalloc(s);
314764562Sgshapiro	bp = buf;
314864562Sgshapiro
314964562Sgshapiro	/* put together data */
315064562Sgshapiro	(void) memcpy(bp, hostname, strlen(hostname));
315164562Sgshapiro	bp += strlen(hostname);
315264562Sgshapiro	*bp++ = '\0';
315364562Sgshapiro	(void) memcpy(bp, &family, sizeof family);
315464562Sgshapiro	bp += sizeof family;
315564562Sgshapiro	if (family != SMFIA_UNKNOWN)
315664562Sgshapiro	{
315764562Sgshapiro		(void) memcpy(bp, &port, sizeof port);
315864562Sgshapiro		bp += sizeof port;
315964562Sgshapiro
316064562Sgshapiro		/* include trailing '\0' */
316164562Sgshapiro		(void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
316264562Sgshapiro	}
316364562Sgshapiro
316464562Sgshapiro	response = milter_command(SMFIC_CONNECT, buf, s,
316564562Sgshapiro				  MilterConnectMacros, e, state);
316690792Sgshapiro	sm_free(buf); /* XXX */
316764562Sgshapiro
316864562Sgshapiro	/*
316964562Sgshapiro	**  If this message connection is done for,
317064562Sgshapiro	**  close the filters.
317164562Sgshapiro	*/
317264562Sgshapiro
317364562Sgshapiro	if (*state != SMFIR_CONTINUE)
317490792Sgshapiro	{
317590792Sgshapiro		if (MilterLogLevel > 9)
317690792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending");
317764562Sgshapiro		milter_quit(e);
317890792Sgshapiro	}
317964562Sgshapiro	else
318064562Sgshapiro		milter_per_connection_check(e);
318164562Sgshapiro
318264562Sgshapiro	/*
318364562Sgshapiro	**  SMFIR_REPLYCODE can't work with connect due to
318464562Sgshapiro	**  the requirements of SMTP.  Therefore, ignore the
318564562Sgshapiro	**  reply code text but keep the state it would reflect.
318664562Sgshapiro	*/
318764562Sgshapiro
318864562Sgshapiro	if (*state == SMFIR_REPLYCODE)
318964562Sgshapiro	{
319064562Sgshapiro		if (response != NULL &&
319164562Sgshapiro		    *response == '4')
319264562Sgshapiro			*state = SMFIR_TEMPFAIL;
319364562Sgshapiro		else
319464562Sgshapiro			*state = SMFIR_REJECT;
319564562Sgshapiro		if (response != NULL)
319664562Sgshapiro		{
319790792Sgshapiro			sm_free(response); /* XXX */
319864562Sgshapiro			response = NULL;
319964562Sgshapiro		}
320064562Sgshapiro	}
320164562Sgshapiro	return response;
320264562Sgshapiro}
320390792Sgshapiro/*
320464562Sgshapiro**  MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
320564562Sgshapiro**
320664562Sgshapiro**	Parameters:
320764562Sgshapiro**		helo -- argument to SMTP HELO/EHLO command.
320864562Sgshapiro**		e -- current envelope.
320964562Sgshapiro**		state -- return state from response.
321064562Sgshapiro**
321164562Sgshapiro**	Returns:
321264562Sgshapiro**		response string (may be NULL)
321364562Sgshapiro*/
321464562Sgshapiro
321564562Sgshapirochar *
321664562Sgshapiromilter_helo(helo, e, state)
321764562Sgshapiro	char *helo;
321864562Sgshapiro	ENVELOPE *e;
321964562Sgshapiro	char *state;
322064562Sgshapiro{
322173188Sgshapiro	int i;
322264562Sgshapiro	char *response;
322364562Sgshapiro
322464562Sgshapiro	if (tTd(64, 10))
322590792Sgshapiro		sm_dprintf("milter_helo(%s)\n", helo);
322664562Sgshapiro
322790792Sgshapiro	/* HELO/EHLO can come at any point */
322873188Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
322973188Sgshapiro	{
323073188Sgshapiro		struct milter *m = InputFilters[i];
323173188Sgshapiro
323273188Sgshapiro		switch (m->mf_state)
323373188Sgshapiro		{
323473188Sgshapiro		  case SMFS_INMSG:
323573188Sgshapiro			/* abort in message filters */
323673188Sgshapiro			milter_abort_filter(m, e);
323773188Sgshapiro			/* FALLTHROUGH */
323873188Sgshapiro
323973188Sgshapiro		  case SMFS_DONE:
324073188Sgshapiro			/* reset done filters */
324173188Sgshapiro			m->mf_state = SMFS_OPEN;
324273188Sgshapiro			break;
324373188Sgshapiro		}
324473188Sgshapiro	}
324573188Sgshapiro
324664562Sgshapiro	response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
324764562Sgshapiro				  MilterHeloMacros, e, state);
324864562Sgshapiro	milter_per_connection_check(e);
324964562Sgshapiro	return response;
325064562Sgshapiro}
325190792Sgshapiro/*
325264562Sgshapiro**  MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
325364562Sgshapiro**
325464562Sgshapiro**	Parameters:
325564562Sgshapiro**		args -- SMTP MAIL command args (args[0] == sender).
325664562Sgshapiro**		e -- current envelope.
325764562Sgshapiro**		state -- return state from response.
325864562Sgshapiro**
325964562Sgshapiro**	Returns:
326064562Sgshapiro**		response string (may be NULL)
326164562Sgshapiro*/
326264562Sgshapiro
326364562Sgshapirochar *
326464562Sgshapiromilter_envfrom(args, e, state)
326564562Sgshapiro	char **args;
326664562Sgshapiro	ENVELOPE *e;
326764562Sgshapiro	char *state;
326864562Sgshapiro{
326964562Sgshapiro	int i;
327064562Sgshapiro	char *buf, *bp;
327164562Sgshapiro	char *response;
327264562Sgshapiro	ssize_t s;
327364562Sgshapiro
327464562Sgshapiro	if (tTd(64, 10))
327564562Sgshapiro	{
327690792Sgshapiro		sm_dprintf("milter_envfrom:");
327764562Sgshapiro		for (i = 0; args[i] != NULL; i++)
327890792Sgshapiro			sm_dprintf(" %s", args[i]);
327990792Sgshapiro		sm_dprintf("\n");
328064562Sgshapiro	}
328164562Sgshapiro
328264562Sgshapiro	/* sanity check */
328364562Sgshapiro	if (args[0] == NULL)
328464562Sgshapiro	{
328564562Sgshapiro		*state = SMFIR_REJECT;
328690792Sgshapiro		if (MilterLogLevel > 10)
328790792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
328890792Sgshapiro				  "Milter: reject, no sender");
328964562Sgshapiro		return NULL;
329064562Sgshapiro	}
329164562Sgshapiro
329264562Sgshapiro	/* new message, so ... */
329364562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
329464562Sgshapiro	{
329564562Sgshapiro		struct milter *m = InputFilters[i];
329664562Sgshapiro
329764562Sgshapiro		switch (m->mf_state)
329864562Sgshapiro		{
329964562Sgshapiro		  case SMFS_INMSG:
330064562Sgshapiro			/* abort in message filters */
330164562Sgshapiro			milter_abort_filter(m, e);
330264562Sgshapiro			/* FALLTHROUGH */
330364562Sgshapiro
330464562Sgshapiro		  case SMFS_DONE:
330564562Sgshapiro			/* reset done filters */
330664562Sgshapiro			m->mf_state = SMFS_OPEN;
330764562Sgshapiro			break;
330864562Sgshapiro		}
330964562Sgshapiro	}
331064562Sgshapiro
331164562Sgshapiro	/* put together data */
331264562Sgshapiro	s = 0;
331364562Sgshapiro	for (i = 0; args[i] != NULL; i++)
331464562Sgshapiro		s += strlen(args[i]) + 1;
331590792Sgshapiro
331690792Sgshapiro	if (s < 0)
331790792Sgshapiro	{
331890792Sgshapiro		*state = SMFIR_TEMPFAIL;
331990792Sgshapiro		return NULL;
332090792Sgshapiro	}
332190792Sgshapiro
332290792Sgshapiro	buf = (char *) xalloc(s);
332364562Sgshapiro	bp = buf;
332464562Sgshapiro	for (i = 0; args[i] != NULL; i++)
332564562Sgshapiro	{
332690792Sgshapiro		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
332764562Sgshapiro		bp += strlen(bp) + 1;
332864562Sgshapiro	}
332964562Sgshapiro
333090792Sgshapiro	if (MilterLogLevel > 14)
333190792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: senders: %s", buf);
333290792Sgshapiro
333364562Sgshapiro	/* send it over */
333464562Sgshapiro	response = milter_command(SMFIC_MAIL, buf, s,
333564562Sgshapiro				  MilterEnvFromMacros, e, state);
333690792Sgshapiro	sm_free(buf); /* XXX */
333764562Sgshapiro
333864562Sgshapiro	/*
333964562Sgshapiro	**  If filter rejects/discards a per message command,
334064562Sgshapiro	**  abort the other filters since we are done with the
334164562Sgshapiro	**  current message.
334264562Sgshapiro	*/
334364562Sgshapiro
334464562Sgshapiro	MILTER_CHECK_DONE_MSG();
334590792Sgshapiro	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
334690792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, senders");
334764562Sgshapiro	return response;
334864562Sgshapiro}
334990792Sgshapiro/*
335064562Sgshapiro**  MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
335164562Sgshapiro**
335264562Sgshapiro**	Parameters:
335364562Sgshapiro**		args -- SMTP MAIL command args (args[0] == recipient).
335464562Sgshapiro**		e -- current envelope.
335564562Sgshapiro**		state -- return state from response.
335664562Sgshapiro**
335764562Sgshapiro**	Returns:
335864562Sgshapiro**		response string (may be NULL)
335964562Sgshapiro*/
336064562Sgshapiro
336164562Sgshapirochar *
336264562Sgshapiromilter_envrcpt(args, e, state)
336364562Sgshapiro	char **args;
336464562Sgshapiro	ENVELOPE *e;
336564562Sgshapiro	char *state;
336664562Sgshapiro{
336764562Sgshapiro	int i;
336864562Sgshapiro	char *buf, *bp;
336964562Sgshapiro	char *response;
337064562Sgshapiro	ssize_t s;
337164562Sgshapiro
337264562Sgshapiro	if (tTd(64, 10))
337364562Sgshapiro	{
337490792Sgshapiro		sm_dprintf("milter_envrcpt:");
337564562Sgshapiro		for (i = 0; args[i] != NULL; i++)
337690792Sgshapiro			sm_dprintf(" %s", args[i]);
337790792Sgshapiro		sm_dprintf("\n");
337864562Sgshapiro	}
337964562Sgshapiro
338064562Sgshapiro	/* sanity check */
338164562Sgshapiro	if (args[0] == NULL)
338264562Sgshapiro	{
338364562Sgshapiro		*state = SMFIR_REJECT;
338490792Sgshapiro		if (MilterLogLevel > 10)
338590792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt");
338664562Sgshapiro		return NULL;
338764562Sgshapiro	}
338864562Sgshapiro
338964562Sgshapiro	/* put together data */
339064562Sgshapiro	s = 0;
339164562Sgshapiro	for (i = 0; args[i] != NULL; i++)
339264562Sgshapiro		s += strlen(args[i]) + 1;
339390792Sgshapiro
339490792Sgshapiro	if (s < 0)
339590792Sgshapiro	{
339690792Sgshapiro		*state = SMFIR_TEMPFAIL;
339790792Sgshapiro		return NULL;
339890792Sgshapiro	}
339990792Sgshapiro
340090792Sgshapiro	buf = (char *) xalloc(s);
340164562Sgshapiro	bp = buf;
340264562Sgshapiro	for (i = 0; args[i] != NULL; i++)
340364562Sgshapiro	{
340490792Sgshapiro		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
340564562Sgshapiro		bp += strlen(bp) + 1;
340664562Sgshapiro	}
340764562Sgshapiro
340890792Sgshapiro	if (MilterLogLevel > 14)
340990792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf);
341090792Sgshapiro
341164562Sgshapiro	/* send it over */
341264562Sgshapiro	response = milter_command(SMFIC_RCPT, buf, s,
341364562Sgshapiro				  MilterEnvRcptMacros, e, state);
341490792Sgshapiro	sm_free(buf); /* XXX */
341564562Sgshapiro	return response;
341664562Sgshapiro}
341790792Sgshapiro/*
341864562Sgshapiro**  MILTER_DATA -- send message headers/body and gather final message results
341964562Sgshapiro**
342064562Sgshapiro**	Parameters:
342164562Sgshapiro**		e -- current envelope.
342264562Sgshapiro**		state -- return state from response.
342364562Sgshapiro**
342464562Sgshapiro**	Returns:
342564562Sgshapiro**		response string (may be NULL)
342664562Sgshapiro**
342764562Sgshapiro**	Side effects:
342864562Sgshapiro**		- Uses e->e_dfp for access to the body
342964562Sgshapiro**		- Can call the various milter action routines to
343064562Sgshapiro**		  modify the envelope or message.
343164562Sgshapiro*/
343264562Sgshapiro
343364562Sgshapiro# define MILTER_CHECK_RESULTS() \
343464562Sgshapiro	if (*state == SMFIR_ACCEPT || \
343564562Sgshapiro	    m->mf_state == SMFS_DONE || \
343664562Sgshapiro	    m->mf_state == SMFS_ERROR) \
343764562Sgshapiro	{ \
343864562Sgshapiro		if (m->mf_state != SMFS_ERROR) \
343964562Sgshapiro			m->mf_state = SMFS_DONE; \
344064562Sgshapiro		continue;	/* to next filter */ \
344164562Sgshapiro	} \
344264562Sgshapiro	if (*state != SMFIR_CONTINUE) \
344364562Sgshapiro	{ \
344464562Sgshapiro		m->mf_state = SMFS_DONE; \
344564562Sgshapiro		goto finishup; \
344664562Sgshapiro	}
344764562Sgshapiro
344864562Sgshapirochar *
344964562Sgshapiromilter_data(e, state)
345064562Sgshapiro	ENVELOPE *e;
345164562Sgshapiro	char *state;
345264562Sgshapiro{
345390792Sgshapiro	bool replbody = false;		/* milter_replbody() called? */
345490792Sgshapiro	bool replfailed = false;	/* milter_replbody() failed? */
345590792Sgshapiro	bool rewind = false;		/* rewind data file? */
345690792Sgshapiro	bool dfopen = false;		/* data file open for writing? */
345764562Sgshapiro	bool newfilter;			/* reset on each new filter */
345864562Sgshapiro	char rcmd;
345964562Sgshapiro	int i;
346064562Sgshapiro	int save_errno;
346164562Sgshapiro	char *response = NULL;
346264562Sgshapiro	time_t eomsent;
346364562Sgshapiro	ssize_t rlen;
346464562Sgshapiro
346564562Sgshapiro	if (tTd(64, 10))
346690792Sgshapiro		sm_dprintf("milter_data\n");
346764562Sgshapiro
346864562Sgshapiro	*state = SMFIR_CONTINUE;
346964562Sgshapiro
347064562Sgshapiro	/*
347164562Sgshapiro	**  XXX: Should actually send body chunks to each filter
347264562Sgshapiro	**  a chunk at a time instead of sending the whole body to
347364562Sgshapiro	**  each filter in turn.  However, only if the filters don't
347464562Sgshapiro	**  change the body.
347564562Sgshapiro	*/
347664562Sgshapiro
347764562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
347864562Sgshapiro	{
347964562Sgshapiro		struct milter *m = InputFilters[i];
348064562Sgshapiro
348164562Sgshapiro		if (*state != SMFIR_CONTINUE &&
348264562Sgshapiro		    *state != SMFIR_ACCEPT)
348364562Sgshapiro		{
348464562Sgshapiro			/*
348564562Sgshapiro			**  A previous filter has dealt with the message,
348664562Sgshapiro			**  safe to stop processing the filters.
348764562Sgshapiro			*/
348864562Sgshapiro
348964562Sgshapiro			break;
349064562Sgshapiro		}
349164562Sgshapiro
349264562Sgshapiro		/* Now reset state for later evaluation */
349364562Sgshapiro		*state = SMFIR_CONTINUE;
349490792Sgshapiro		newfilter = true;
349564562Sgshapiro
349671345Sgshapiro		/* previous problem? */
349771345Sgshapiro		if (m->mf_state == SMFS_ERROR)
349871345Sgshapiro		{
349971345Sgshapiro			MILTER_CHECK_ERROR(continue);
350071345Sgshapiro			break;
350171345Sgshapiro		}
350271345Sgshapiro
350364562Sgshapiro		/* sanity checks */
350464562Sgshapiro		if (m->mf_sock < 0 ||
350564562Sgshapiro		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
350664562Sgshapiro			continue;
350764562Sgshapiro
350864562Sgshapiro		m->mf_state = SMFS_INMSG;
350964562Sgshapiro
351064562Sgshapiro		/* check if filter wants the headers */
351164562Sgshapiro		if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
351264562Sgshapiro		{
351364562Sgshapiro			response = milter_headers(m, e, state);
351464562Sgshapiro			MILTER_CHECK_RESULTS();
351564562Sgshapiro		}
351664562Sgshapiro
351764562Sgshapiro		/* check if filter wants EOH */
351864562Sgshapiro		if (!bitset(SMFIP_NOEOH, m->mf_pflags))
351964562Sgshapiro		{
352064562Sgshapiro			if (tTd(64, 10))
352190792Sgshapiro				sm_dprintf("milter_data: eoh\n");
352264562Sgshapiro
352364562Sgshapiro			/* send it over */
352464562Sgshapiro			response = milter_send_command(m, SMFIC_EOH, NULL, 0,
352564562Sgshapiro						       e, state);
352664562Sgshapiro			MILTER_CHECK_RESULTS();
352764562Sgshapiro		}
352864562Sgshapiro
352964562Sgshapiro		/* check if filter wants the body */
353064562Sgshapiro		if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
353164562Sgshapiro		    e->e_dfp != NULL)
353264562Sgshapiro		{
353390792Sgshapiro			rewind = true;
353464562Sgshapiro			response = milter_body(m, e, state);
353564562Sgshapiro			MILTER_CHECK_RESULTS();
353664562Sgshapiro		}
353764562Sgshapiro
353864562Sgshapiro		/* send the final body chunk */
353964562Sgshapiro		(void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
354064562Sgshapiro				    m->mf_timeout[SMFTO_WRITE], e);
354164562Sgshapiro
354264562Sgshapiro		/* Get time EOM sent for timeout */
354364562Sgshapiro		eomsent = curtime();
354464562Sgshapiro
354564562Sgshapiro		/* deal with the possibility of multiple responses */
354664562Sgshapiro		while (*state == SMFIR_CONTINUE)
354764562Sgshapiro		{
354864562Sgshapiro			/* Check total timeout from EOM to final ACK/NAK */
354964562Sgshapiro			if (m->mf_timeout[SMFTO_EOM] > 0 &&
355064562Sgshapiro			    curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
355164562Sgshapiro			{
355264562Sgshapiro				if (tTd(64, 5))
355390792Sgshapiro					sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
355464562Sgshapiro						m->mf_name);
355590792Sgshapiro				if (MilterLogLevel > 0)
355664562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
355790792Sgshapiro						  "milter_data(%s): EOM ACK/NAK timeout",
355864562Sgshapiro						  m->mf_name);
355990792Sgshapiro				milter_error(m, e);
356094334Sgshapiro				MILTER_CHECK_ERROR(break);
356164562Sgshapiro				break;
356264562Sgshapiro			}
356364562Sgshapiro
356464562Sgshapiro			response = milter_read(m, &rcmd, &rlen,
356564562Sgshapiro					       m->mf_timeout[SMFTO_READ], e);
356664562Sgshapiro			if (m->mf_state == SMFS_ERROR)
356764562Sgshapiro				break;
356864562Sgshapiro
356964562Sgshapiro			if (tTd(64, 10))
357090792Sgshapiro				sm_dprintf("milter_data(%s): state %c\n",
357190792Sgshapiro					   m->mf_name, (char) rcmd);
357264562Sgshapiro
357364562Sgshapiro			switch (rcmd)
357464562Sgshapiro			{
357564562Sgshapiro			  case SMFIR_REPLYCODE:
357664562Sgshapiro				MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
357790792Sgshapiro				if (MilterLogLevel > 12)
357890792Sgshapiro					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s",
357990792Sgshapiro						  m->mf_name, response);
358064562Sgshapiro				*state = rcmd;
358164562Sgshapiro				m->mf_state = SMFS_DONE;
358264562Sgshapiro				break;
358364562Sgshapiro
358490792Sgshapiro			  case SMFIR_REJECT: /* log msg at end of function */
358590792Sgshapiro				if (MilterLogLevel > 12)
358690792Sgshapiro					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject",
358790792Sgshapiro						  m->mf_name);
358890792Sgshapiro				*state = rcmd;
358990792Sgshapiro				m->mf_state = SMFS_DONE;
359090792Sgshapiro				break;
359190792Sgshapiro
359264562Sgshapiro			  case SMFIR_DISCARD:
359390792Sgshapiro				if (MilterLogLevel > 12)
359490792Sgshapiro					sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard",
359590792Sgshapiro						  m->mf_name);
359690792Sgshapiro				*state = rcmd;
359790792Sgshapiro				m->mf_state = SMFS_DONE;
359890792Sgshapiro				break;
359990792Sgshapiro
360064562Sgshapiro			  case SMFIR_TEMPFAIL:
360190792Sgshapiro				if (MilterLogLevel > 12)
360290792Sgshapiro					sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail",
360390792Sgshapiro						  m->mf_name);
360464562Sgshapiro				*state = rcmd;
360564562Sgshapiro				m->mf_state = SMFS_DONE;
360664562Sgshapiro				break;
360764562Sgshapiro
360864562Sgshapiro			  case SMFIR_CONTINUE:
360964562Sgshapiro			  case SMFIR_ACCEPT:
361064562Sgshapiro				/* this filter is done with message */
361164562Sgshapiro				if (replfailed)
361264562Sgshapiro					*state = SMFIR_TEMPFAIL;
361364562Sgshapiro				else
361464562Sgshapiro					*state = SMFIR_ACCEPT;
361564562Sgshapiro				m->mf_state = SMFS_DONE;
361664562Sgshapiro				break;
361764562Sgshapiro
361864562Sgshapiro			  case SMFIR_PROGRESS:
361964562Sgshapiro				break;
362064562Sgshapiro
362190792Sgshapiro# if _FFR_QUARANTINE
362290792Sgshapiro			  case SMFIR_QUARANTINE:
362390792Sgshapiro				if (!bitset(SMFIF_QUARANTINE, m->mf_fflags))
362490792Sgshapiro				{
362590792Sgshapiro					if (MilterLogLevel > 9)
362690792Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
362790792Sgshapiro							  "milter_data(%s): lied about quarantining, honoring request anyway",
362890792Sgshapiro							  m->mf_name);
362990792Sgshapiro				}
363090792Sgshapiro				if (response == NULL)
363190792Sgshapiro					response = newstr("");
363290792Sgshapiro				if (MilterLogLevel > 3)
363390792Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
363490792Sgshapiro						  "milter=%s, quarantine=%s",
363590792Sgshapiro						  m->mf_name, response);
363690792Sgshapiro				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
363790792Sgshapiro								 response);
363890792Sgshapiro				macdefine(&e->e_macro, A_PERM,
363990792Sgshapiro					  macid("{quarantine}"), e->e_quarmsg);
364090792Sgshapiro				break;
364190792Sgshapiro# endif /* _FFR_QUARANTINE */
364290792Sgshapiro
364364562Sgshapiro			  case SMFIR_ADDHEADER:
364464562Sgshapiro				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
364564562Sgshapiro				{
364690792Sgshapiro					if (MilterLogLevel > 9)
364764562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
364864562Sgshapiro							  "milter_data(%s): lied about adding headers, honoring request anyway",
364964562Sgshapiro							  m->mf_name);
365064562Sgshapiro				}
365164562Sgshapiro				milter_addheader(response, rlen, e);
365264562Sgshapiro				break;
365364562Sgshapiro
365464562Sgshapiro			  case SMFIR_CHGHEADER:
365564562Sgshapiro				if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
365664562Sgshapiro				{
365790792Sgshapiro					if (MilterLogLevel > 9)
365864562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
365964562Sgshapiro							  "milter_data(%s): lied about changing headers, honoring request anyway",
366064562Sgshapiro							  m->mf_name);
366164562Sgshapiro				}
366264562Sgshapiro				milter_changeheader(response, rlen, e);
366364562Sgshapiro				break;
366464562Sgshapiro
366564562Sgshapiro			  case SMFIR_ADDRCPT:
366664562Sgshapiro				if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
366764562Sgshapiro				{
366890792Sgshapiro					if (MilterLogLevel > 9)
366964562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
367064562Sgshapiro							  "milter_data(%s) lied about adding recipients, honoring request anyway",
367164562Sgshapiro							  m->mf_name);
367264562Sgshapiro				}
367364562Sgshapiro				milter_addrcpt(response, rlen, e);
367464562Sgshapiro				break;
367564562Sgshapiro
367664562Sgshapiro			  case SMFIR_DELRCPT:
367764562Sgshapiro				if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
367864562Sgshapiro				{
367990792Sgshapiro					if (MilterLogLevel > 9)
368064562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
368164562Sgshapiro							  "milter_data(%s): lied about removing recipients, honoring request anyway",
368264562Sgshapiro							  m->mf_name);
368364562Sgshapiro				}
368464562Sgshapiro				milter_delrcpt(response, rlen, e);
368564562Sgshapiro				break;
368664562Sgshapiro
368764562Sgshapiro			  case SMFIR_REPLBODY:
368864562Sgshapiro				if (!bitset(SMFIF_MODBODY, m->mf_fflags))
368964562Sgshapiro				{
369090792Sgshapiro					if (MilterLogLevel > 0)
369164562Sgshapiro						sm_syslog(LOG_ERR, e->e_id,
369264562Sgshapiro							  "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
369364562Sgshapiro							  m->mf_name);
369490792Sgshapiro					replfailed = true;
369564562Sgshapiro					break;
369664562Sgshapiro				}
369764562Sgshapiro
369864562Sgshapiro				/* already failed in attempt */
369964562Sgshapiro				if (replfailed)
370064562Sgshapiro					break;
370164562Sgshapiro
370264562Sgshapiro				if (!dfopen)
370364562Sgshapiro				{
370464562Sgshapiro					if (milter_reopen_df(e) < 0)
370564562Sgshapiro					{
370690792Sgshapiro						replfailed = true;
370764562Sgshapiro						break;
370864562Sgshapiro					}
370990792Sgshapiro					dfopen = true;
371090792Sgshapiro					rewind = true;
371164562Sgshapiro				}
371264562Sgshapiro
371364562Sgshapiro				if (milter_replbody(response, rlen,
371464562Sgshapiro						    newfilter, e) < 0)
371590792Sgshapiro					replfailed = true;
371690792Sgshapiro				newfilter = false;
371790792Sgshapiro				replbody = true;
371864562Sgshapiro				break;
371964562Sgshapiro
372064562Sgshapiro			  default:
372164562Sgshapiro				/* Invalid response to command */
372290792Sgshapiro				if (MilterLogLevel > 0)
372364562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
372464562Sgshapiro						  "milter_data(%s): returned bogus response %c",
372564562Sgshapiro						  m->mf_name, rcmd);
372690792Sgshapiro				milter_error(m, e);
372764562Sgshapiro				break;
372864562Sgshapiro			}
372990792Sgshapiro			if (rcmd != SMFIR_REPLYCODE && response != NULL)
373064562Sgshapiro			{
373190792Sgshapiro				sm_free(response); /* XXX */
373264562Sgshapiro				response = NULL;
373364562Sgshapiro			}
373464562Sgshapiro
373564562Sgshapiro			if (m->mf_state == SMFS_ERROR)
373664562Sgshapiro				break;
373764562Sgshapiro		}
373864562Sgshapiro
373964562Sgshapiro		if (replbody && !replfailed)
374064562Sgshapiro		{
374164562Sgshapiro			/* flush possible buffered character */
374264562Sgshapiro			milter_replbody(NULL, 0, !replbody, e);
374390792Sgshapiro			replbody = false;
374464562Sgshapiro		}
374564562Sgshapiro
374664562Sgshapiro		if (m->mf_state == SMFS_ERROR)
374764562Sgshapiro		{
374864562Sgshapiro			MILTER_CHECK_ERROR(continue);
374964562Sgshapiro			goto finishup;
375064562Sgshapiro		}
375164562Sgshapiro	}
375264562Sgshapiro
375364562Sgshapirofinishup:
375464562Sgshapiro	/* leave things in the expected state if we touched it */
375564562Sgshapiro	if (replfailed)
375664562Sgshapiro	{
375764562Sgshapiro		if (*state == SMFIR_CONTINUE ||
375864562Sgshapiro		    *state == SMFIR_ACCEPT)
375964562Sgshapiro		{
376064562Sgshapiro			*state = SMFIR_TEMPFAIL;
376190792Sgshapiro			SM_FREE_CLR(response);
376264562Sgshapiro		}
376364562Sgshapiro
376464562Sgshapiro		if (dfopen)
376564562Sgshapiro		{
376690792Sgshapiro			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
376764562Sgshapiro			e->e_dfp = NULL;
376864562Sgshapiro			e->e_flags &= ~EF_HAS_DF;
376990792Sgshapiro			dfopen = false;
377064562Sgshapiro		}
377190792Sgshapiro		rewind = false;
377264562Sgshapiro	}
377364562Sgshapiro
377464562Sgshapiro	if ((dfopen && milter_reset_df(e) < 0) ||
377564562Sgshapiro	    (rewind && bfrewind(e->e_dfp) < 0))
377664562Sgshapiro	{
377764562Sgshapiro		save_errno = errno;
377864562Sgshapiro		ExitStat = EX_IOERR;
377964562Sgshapiro
378064562Sgshapiro		/*
378164562Sgshapiro		**  If filter told us to keep message but we had
378264562Sgshapiro		**  an error, we can't really keep it, tempfail it.
378364562Sgshapiro		*/
378464562Sgshapiro
378564562Sgshapiro		if (*state == SMFIR_CONTINUE ||
378664562Sgshapiro		    *state == SMFIR_ACCEPT)
378764562Sgshapiro		{
378864562Sgshapiro			*state = SMFIR_TEMPFAIL;
378990792Sgshapiro			SM_FREE_CLR(response);
379064562Sgshapiro		}
379164562Sgshapiro
379264562Sgshapiro		errno = save_errno;
379390792Sgshapiro		syserr("milter_data: %s/%cf%s: read error",
379490792Sgshapiro		       qid_printqueue(e->e_qgrp, e->e_qdir),
379590792Sgshapiro		       DATAFL_LETTER, e->e_id);
379664562Sgshapiro	}
379790792Sgshapiro
379864562Sgshapiro	MILTER_CHECK_DONE_MSG();
379990792Sgshapiro	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
380090792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data");
380164562Sgshapiro	return response;
380264562Sgshapiro}
380390792Sgshapiro/*
380464562Sgshapiro**  MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
380564562Sgshapiro**
380664562Sgshapiro**	Parameters:
380764562Sgshapiro**		e -- current envelope.
380864562Sgshapiro**
380964562Sgshapiro**	Returns:
381064562Sgshapiro**		none
381164562Sgshapiro*/
381264562Sgshapiro
381364562Sgshapirovoid
381464562Sgshapiromilter_quit(e)
381564562Sgshapiro	ENVELOPE *e;
381664562Sgshapiro{
381764562Sgshapiro	int i;
381864562Sgshapiro
381964562Sgshapiro	if (tTd(64, 10))
382090792Sgshapiro		sm_dprintf("milter_quit(%s)\n", e->e_id);
382164562Sgshapiro
382264562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
382364562Sgshapiro		milter_quit_filter(InputFilters[i], e);
382464562Sgshapiro}
382590792Sgshapiro/*
382664562Sgshapiro**  MILTER_ABORT -- informs the filter(s) that we are aborting current message
382764562Sgshapiro**
382864562Sgshapiro**	Parameters:
382964562Sgshapiro**		e -- current envelope.
383064562Sgshapiro**
383164562Sgshapiro**	Returns:
383264562Sgshapiro**		none
383364562Sgshapiro*/
383464562Sgshapiro
383564562Sgshapirovoid
383664562Sgshapiromilter_abort(e)
383764562Sgshapiro	ENVELOPE *e;
383864562Sgshapiro{
383964562Sgshapiro	int i;
384064562Sgshapiro
384164562Sgshapiro	if (tTd(64, 10))
384290792Sgshapiro		sm_dprintf("milter_abort\n");
384364562Sgshapiro
384464562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
384564562Sgshapiro	{
384664562Sgshapiro		struct milter *m = InputFilters[i];
384764562Sgshapiro
384864562Sgshapiro		/* sanity checks */
384964562Sgshapiro		if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
385064562Sgshapiro			continue;
385164562Sgshapiro
385264562Sgshapiro		milter_abort_filter(m, e);
385364562Sgshapiro	}
385464562Sgshapiro}
385590792Sgshapiro#endif /* MILTER */
3856