milter.c revision 66494
164562Sgshapiro/*
264562Sgshapiro * Copyright (c) 1999-2000 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
1164562Sgshapiro#ifndef lint
1266494Sgshapirostatic char id[] = "@(#)$Id: milter.c,v 8.50.4.33 2000/09/19 19:40:15 gshapiro Exp $";
1364562Sgshapiro#endif /* ! lint */
1464562Sgshapiro
1564562Sgshapiro#if _FFR_MILTER
1664562Sgshapiro
1764562Sgshapiro# include <sendmail.h>
1864562Sgshapiro# include <errno.h>
1964562Sgshapiro# include <sys/time.h>
2064562Sgshapiro
2164562Sgshapiro# if NETINET || NETINET6
2264562Sgshapiro#  include <arpa/inet.h>
2364562Sgshapiro# endif /* NETINET || NETINET6 */
2464562Sgshapiro
2564562Sgshapiro
2664562Sgshapirostatic void	milter_error __P((struct milter *));
2764562Sgshapirostatic int	milter_open __P((struct milter *, bool, ENVELOPE *));
2864562Sgshapirostatic void	milter_parse_timeouts __P((char *, struct milter *));
2964562Sgshapiro
3064562Sgshapirostatic char *MilterConnectMacros[MAXFILTERMACROS + 1];
3164562Sgshapirostatic char *MilterHeloMacros[MAXFILTERMACROS + 1];
3264562Sgshapirostatic char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
3364562Sgshapirostatic char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
3464562Sgshapiro
3564562Sgshapiro# define MILTER_CHECK_DONE_MSG() \
3664562Sgshapiro	if (*state == SMFIR_REPLYCODE || \
3764562Sgshapiro	    *state == SMFIR_REJECT || \
3864562Sgshapiro	    *state == SMFIR_DISCARD || \
3964562Sgshapiro	    *state == SMFIR_TEMPFAIL) \
4064562Sgshapiro	{ \
4164562Sgshapiro		/* Abort the filters to let them know we are done with msg */ \
4264562Sgshapiro		milter_abort(e); \
4364562Sgshapiro	}
4464562Sgshapiro
4564562Sgshapiro# define MILTER_CHECK_ERROR(action) \
4664562Sgshapiro	if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
4764562Sgshapiro		*state = SMFIR_TEMPFAIL; \
4864562Sgshapiro	else if (bitnset(SMF_REJECT, m->mf_flags)) \
4964562Sgshapiro		*state = SMFIR_REJECT; \
5064562Sgshapiro	else \
5164562Sgshapiro		action;
5264562Sgshapiro
5364562Sgshapiro# define MILTER_CHECK_REPLYCODE(default) \
5464562Sgshapiro	if (response == NULL || \
5564562Sgshapiro	    strlen(response) + 1 != (size_t) rlen || \
5664562Sgshapiro	    rlen < 3 || \
5764562Sgshapiro	    (response[0] != '4' && response[0] != '5') || \
5864562Sgshapiro	    !isascii(response[1]) || !isdigit(response[1]) || \
5964562Sgshapiro	    !isascii(response[2]) || !isdigit(response[2])) \
6064562Sgshapiro	{ \
6164562Sgshapiro		if (response != NULL) \
6264562Sgshapiro			free(response); \
6364562Sgshapiro		response = newstr(default); \
6464562Sgshapiro	} \
6564562Sgshapiro	else \
6664562Sgshapiro	{ \
6764562Sgshapiro		char *ptr = response; \
6864562Sgshapiro \
6964562Sgshapiro		/* Check for unprotected %'s in the string */ \
7064562Sgshapiro		while (*ptr != '\0') \
7164562Sgshapiro		{ \
7264562Sgshapiro			if (*ptr == '%' && *++ptr != '%') \
7364562Sgshapiro			{ \
7464562Sgshapiro				free(response); \
7564562Sgshapiro				response = newstr(default); \
7664562Sgshapiro				break; \
7764562Sgshapiro			} \
7864562Sgshapiro			ptr++; \
7964562Sgshapiro		} \
8064562Sgshapiro	}
8164562Sgshapiro
8264562Sgshapiro# define MILTER_DF_ERROR(msg) \
8364562Sgshapiro{ \
8464562Sgshapiro	int save_errno = errno; \
8564562Sgshapiro \
8664562Sgshapiro	if (tTd(64, 5)) \
8764562Sgshapiro	{ \
8864562Sgshapiro		dprintf(msg, dfname, errstring(save_errno)); \
8964562Sgshapiro		dprintf("\n"); \
9064562Sgshapiro	} \
9164562Sgshapiro	if (LogLevel > 0) \
9264562Sgshapiro		sm_syslog(LOG_ERR, e->e_id, msg, dfname, errstring(save_errno)); \
9364562Sgshapiro	if (SuperSafe) \
9464562Sgshapiro	{ \
9564562Sgshapiro		if (e->e_dfp != NULL) \
9664562Sgshapiro		{ \
9764562Sgshapiro			(void) fclose(e->e_dfp); \
9864562Sgshapiro			e->e_dfp = NULL; \
9964562Sgshapiro		} \
10064562Sgshapiro		e->e_flags &= ~EF_HAS_DF; \
10164562Sgshapiro	} \
10264562Sgshapiro	errno = save_errno; \
10364562Sgshapiro}
10464562Sgshapiro
10564562Sgshapiro/*
10664562Sgshapiro**  MILTER_TIMEOUT -- make sure socket is ready in time
10764562Sgshapiro**
10864562Sgshapiro**	Parameters:
10964562Sgshapiro**		routine -- routine name for debug/logging
11064562Sgshapiro**		secs -- number of seconds in timeout
11164562Sgshapiro**		write -- waiting to read or write?
11264562Sgshapiro**
11364562Sgshapiro**	Assumes 'm' is a milter structure for the current socket.
11464562Sgshapiro*/
11564562Sgshapiro
11664562Sgshapiro
11764562Sgshapiro#  define MILTER_TIMEOUT(routine, secs, write) \
11864562Sgshapiro{ \
11964562Sgshapiro	int ret; \
12064562Sgshapiro	int save_errno; \
12164562Sgshapiro	fd_set fds; \
12264562Sgshapiro	struct timeval tv; \
12364562Sgshapiro \
12464562Sgshapiro	if (m->mf_sock >= FD_SETSIZE) \
12564562Sgshapiro	{ \
12664562Sgshapiro		if (tTd(64, 5)) \
12764562Sgshapiro			dprintf("%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
12864562Sgshapiro				routine, m->mf_name, m->mf_sock, FD_SETSIZE); \
12964562Sgshapiro		if (LogLevel > 0) \
13064562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
13164562Sgshapiro				  "%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
13264562Sgshapiro				  routine, m->mf_name, m->mf_sock, FD_SETSIZE); \
13364562Sgshapiro		milter_error(m); \
13464562Sgshapiro		return NULL; \
13564562Sgshapiro	} \
13664562Sgshapiro \
13764562Sgshapiro	FD_ZERO(&fds); \
13864562Sgshapiro	FD_SET(m->mf_sock, &fds); \
13964562Sgshapiro	tv.tv_sec = secs; \
14064562Sgshapiro	tv.tv_usec = 0; \
14164562Sgshapiro	ret = select(m->mf_sock + 1, \
14264562Sgshapiro		     write ? NULL : &fds, \
14364562Sgshapiro		     write ? &fds : NULL, \
14464562Sgshapiro		     NULL, &tv); \
14564562Sgshapiro \
14664562Sgshapiro	switch (ret) \
14764562Sgshapiro	{ \
14864562Sgshapiro	  case 0: \
14964562Sgshapiro		if (tTd(64, 5)) \
15064562Sgshapiro			dprintf("%s(%s): timeout\n", routine, m->mf_name); \
15164562Sgshapiro		if (LogLevel > 0) \
15264562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, "%s(%s): timeout\n", \
15364562Sgshapiro				  routine, m->mf_name); \
15464562Sgshapiro		milter_error(m); \
15564562Sgshapiro		return NULL; \
15664562Sgshapiro \
15764562Sgshapiro	  case -1: \
15864562Sgshapiro		save_errno = errno; \
15964562Sgshapiro		if (tTd(64, 5)) \
16064562Sgshapiro			dprintf("%s(%s): select: %s\n", \
16164562Sgshapiro				routine,  m->mf_name, errstring(save_errno)); \
16264562Sgshapiro		if (LogLevel > 0) \
16364562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
16464562Sgshapiro				  "%s(%s): select: %s\n", \
16564562Sgshapiro				  routine, m->mf_name, errstring(save_errno)); \
16664562Sgshapiro		milter_error(m); \
16764562Sgshapiro		return NULL; \
16864562Sgshapiro \
16964562Sgshapiro	  default: \
17064562Sgshapiro		if (FD_ISSET(m->mf_sock, &fds)) \
17164562Sgshapiro			break; \
17264562Sgshapiro		if (tTd(64, 5)) \
17364562Sgshapiro			dprintf("%s(%s): socket not ready\n", \
17464562Sgshapiro				routine, m->mf_name); \
17564562Sgshapiro		if (LogLevel > 0) \
17664562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
17764562Sgshapiro				  "%s(%s): socket not ready\n", \
17864562Sgshapiro				  m->mf_name, routine); \
17964562Sgshapiro		milter_error(m); \
18064562Sgshapiro		return NULL; \
18164562Sgshapiro	} \
18264562Sgshapiro}
18364562Sgshapiro
18464562Sgshapiro
18564562Sgshapiro/*
18664562Sgshapiro**  Low level functions
18764562Sgshapiro*/
18864562Sgshapiro
18964562Sgshapiro/*
19064562Sgshapiro**  MILTER_READ -- read from a remote milter filter
19164562Sgshapiro**
19264562Sgshapiro**	Parameters:
19364562Sgshapiro**		m -- milter to read from.
19464562Sgshapiro**		cmd -- return param for command read.
19564562Sgshapiro**		rlen -- return length of response string.
19664562Sgshapiro**		to -- timeout in seconds.
19764562Sgshapiro**		e -- current envelope.
19864562Sgshapiro**
19964562Sgshapiro**	Returns:
20064562Sgshapiro**		response string (may be NULL)
20164562Sgshapiro*/
20264562Sgshapiro
20364562Sgshapirostatic char *
20464562Sgshapiromilter_sysread(m, buf, sz, to, e)
20564562Sgshapiro	struct milter *m;
20664562Sgshapiro	char *buf;
20764562Sgshapiro	ssize_t sz;
20864562Sgshapiro	time_t to;
20964562Sgshapiro	ENVELOPE *e;
21064562Sgshapiro{
21166494Sgshapiro	time_t readstart = 0;
21264562Sgshapiro	ssize_t len, curl;
21364562Sgshapiro
21464562Sgshapiro	curl = 0;
21564562Sgshapiro
21664562Sgshapiro	if (to > 0)
21764562Sgshapiro		readstart = curtime();
21864562Sgshapiro
21964562Sgshapiro	for (;;)
22064562Sgshapiro	{
22164562Sgshapiro		if (to > 0)
22264562Sgshapiro		{
22364562Sgshapiro			time_t now;
22464562Sgshapiro
22564562Sgshapiro			now = curtime();
22664562Sgshapiro			if (now - readstart >= to)
22764562Sgshapiro			{
22864562Sgshapiro				if (tTd(64, 5))
22964562Sgshapiro					dprintf("milter_read(%s): timeout before data read\n",
23064562Sgshapiro						m->mf_name);
23164562Sgshapiro				if (LogLevel > 0)
23264562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
23364562Sgshapiro						  "milter_read(%s): timeout before data read\n",
23464562Sgshapiro						  m->mf_name);
23564562Sgshapiro				milter_error(m);
23664562Sgshapiro				return NULL;
23764562Sgshapiro			}
23864562Sgshapiro			to -= now - readstart;
23964562Sgshapiro			readstart = now;
24064562Sgshapiro			MILTER_TIMEOUT("milter_read", to, FALSE);
24164562Sgshapiro		}
24264562Sgshapiro
24364562Sgshapiro		len = read(m->mf_sock, buf + curl, sz - curl);
24464562Sgshapiro
24564562Sgshapiro		if (len < 0)
24664562Sgshapiro		{
24764562Sgshapiro			int save_errno = errno;
24864562Sgshapiro
24964562Sgshapiro			if (tTd(64, 5))
25064562Sgshapiro				dprintf("milter_read(%s): read returned %ld: %s\n",
25164562Sgshapiro					m->mf_name, (long) len,
25264562Sgshapiro					errstring(save_errno));
25364562Sgshapiro			if (LogLevel > 0)
25464562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
25564562Sgshapiro					  "milter_read(%s): read returned %ld: %s",
25664562Sgshapiro					  m->mf_name, (long) len,
25764562Sgshapiro					  errstring(save_errno));
25864562Sgshapiro			milter_error(m);
25964562Sgshapiro			return NULL;
26064562Sgshapiro		}
26164562Sgshapiro
26264562Sgshapiro		curl += len;
26364562Sgshapiro		if (len == 0 || len >= sz)
26464562Sgshapiro			break;
26564562Sgshapiro
26664562Sgshapiro	}
26764562Sgshapiro
26864562Sgshapiro	if (curl != sz)
26964562Sgshapiro	{
27064562Sgshapiro		if (tTd(64, 5))
27164562Sgshapiro			dprintf("milter_read(%s): read returned %ld, expecting %ld\n",
27264562Sgshapiro				m->mf_name, (long) curl, (long) sz);
27364562Sgshapiro		if (LogLevel > 0)
27464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
27564562Sgshapiro				  "milter_read(%s): read returned %ld, expecting %ld",
27664562Sgshapiro				  m->mf_name, (long) curl, (long) sz);
27764562Sgshapiro		milter_error(m);
27864562Sgshapiro		return NULL;
27964562Sgshapiro	}
28064562Sgshapiro	return buf;
28164562Sgshapiro}
28264562Sgshapiro
28364562Sgshapirostatic char *
28464562Sgshapiromilter_read(m, cmd, rlen, to, e)
28564562Sgshapiro	struct milter *m;
28664562Sgshapiro	char *cmd;
28764562Sgshapiro	ssize_t *rlen;
28864562Sgshapiro	time_t to;
28964562Sgshapiro	ENVELOPE *e;
29064562Sgshapiro{
29166494Sgshapiro	time_t readstart = 0;
29264562Sgshapiro	ssize_t expl;
29364562Sgshapiro	mi_int32 i;
29464562Sgshapiro	char *buf;
29564562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
29664562Sgshapiro
29764562Sgshapiro	*rlen = 0;
29864562Sgshapiro	*cmd = '\0';
29964562Sgshapiro
30064562Sgshapiro	if (to > 0)
30164562Sgshapiro		readstart = curtime();
30264562Sgshapiro
30364562Sgshapiro	if (milter_sysread(m, data, sizeof data, to, e) == NULL)
30464562Sgshapiro		return NULL;
30564562Sgshapiro
30664562Sgshapiro	/* reset timeout */
30764562Sgshapiro	if (to > 0)
30864562Sgshapiro	{
30964562Sgshapiro		time_t now;
31064562Sgshapiro
31164562Sgshapiro		now = curtime();
31264562Sgshapiro		if (now - readstart >= to)
31364562Sgshapiro		{
31464562Sgshapiro			if (tTd(64, 5))
31564562Sgshapiro				dprintf("milter_read(%s): timeout before data read\n",
31664562Sgshapiro					m->mf_name);
31764562Sgshapiro			if (LogLevel > 0)
31864562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
31964562Sgshapiro					  "milter_read(%s): timeout before data read\n",
32064562Sgshapiro					  m->mf_name);
32164562Sgshapiro			milter_error(m);
32264562Sgshapiro			return NULL;
32364562Sgshapiro		}
32464562Sgshapiro		to -= now - readstart;
32564562Sgshapiro	}
32664562Sgshapiro
32764562Sgshapiro	*cmd = data[MILTER_LEN_BYTES];
32864562Sgshapiro	data[MILTER_LEN_BYTES] = '\0';
32964562Sgshapiro	(void) memcpy(&i, data, MILTER_LEN_BYTES);
33064562Sgshapiro	expl = ntohl(i) - 1;
33164562Sgshapiro
33264562Sgshapiro	if (tTd(64, 25))
33364562Sgshapiro		dprintf("milter_read(%s): expecting %ld bytes\n",
33464562Sgshapiro			m->mf_name, (long) expl);
33564562Sgshapiro
33664562Sgshapiro	if (expl < 0)
33764562Sgshapiro	{
33864562Sgshapiro		if (tTd(64, 5))
33964562Sgshapiro			dprintf("milter_read(%s): read size %ld out of range\n",
34064562Sgshapiro				m->mf_name, (long) expl);
34164562Sgshapiro		if (LogLevel > 0)
34264562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
34364562Sgshapiro				  "milter_read(%s): read size %ld out of range",
34464562Sgshapiro				  m->mf_name, (long) expl);
34564562Sgshapiro		milter_error(m);
34664562Sgshapiro		return NULL;
34764562Sgshapiro	}
34864562Sgshapiro
34964562Sgshapiro	if (expl == 0)
35064562Sgshapiro		return NULL;
35164562Sgshapiro
35264562Sgshapiro	buf = (char *)xalloc(expl);
35364562Sgshapiro
35464562Sgshapiro	if (milter_sysread(m, buf, expl, to, e) == NULL)
35564562Sgshapiro	{
35664562Sgshapiro		free(buf);
35764562Sgshapiro		return NULL;
35864562Sgshapiro	}
35964562Sgshapiro
36064562Sgshapiro	if (tTd(64, 50))
36164562Sgshapiro		dprintf("milter_read(%s): Returning %*s\n",
36264562Sgshapiro			m->mf_name, (int) expl, buf);
36364562Sgshapiro	*rlen = expl;
36464562Sgshapiro	return buf;
36564562Sgshapiro}
36664562Sgshapiro/*
36764562Sgshapiro**  MILTER_WRITE -- write to a remote milter filter
36864562Sgshapiro**
36964562Sgshapiro**	Parameters:
37064562Sgshapiro**		m -- milter to read from.
37164562Sgshapiro**		cmd -- command to send.
37264562Sgshapiro**		buf -- optional command data.
37364562Sgshapiro**		len -- length of buf.
37464562Sgshapiro**		to -- timeout in seconds.
37564562Sgshapiro**		e -- current envelope.
37664562Sgshapiro**
37764562Sgshapiro**	Returns:
37864562Sgshapiro**		buf if successful, NULL otherwise
37964562Sgshapiro**		Not actually used anywhere but function prototype
38064562Sgshapiro**			must match milter_read()
38164562Sgshapiro*/
38264562Sgshapiro
38364562Sgshapirostatic char *
38464562Sgshapiromilter_write(m, cmd, buf, len, to, e)
38564562Sgshapiro	struct milter *m;
38664562Sgshapiro	char cmd;
38764562Sgshapiro	char *buf;
38864562Sgshapiro	ssize_t len;
38964562Sgshapiro	time_t to;
39064562Sgshapiro	ENVELOPE *e;
39164562Sgshapiro{
39264562Sgshapiro	time_t writestart = (time_t) 0;
39364562Sgshapiro	ssize_t sl, i;
39464562Sgshapiro	mi_int32 nl;
39564562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
39664562Sgshapiro
39764562Sgshapiro	if (len < 0 || len > MILTER_CHUNK_SIZE)
39864562Sgshapiro	{
39964562Sgshapiro		if (tTd(64, 5))
40064562Sgshapiro			dprintf("milter_write(%s): length %ld out of range\n",
40164562Sgshapiro				m->mf_name, (long) len);
40264562Sgshapiro		if (LogLevel > 0)
40364562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
40464562Sgshapiro				  "milter_write(%s): length %ld out of range",
40564562Sgshapiro				  m->mf_name, (long) len);
40664562Sgshapiro		milter_error(m);
40764562Sgshapiro		return NULL;
40864562Sgshapiro	}
40964562Sgshapiro
41064562Sgshapiro	if (tTd(64, 20))
41164562Sgshapiro		dprintf("milter_write(%s): cmd %c, len %ld\n",
41264562Sgshapiro			m->mf_name, cmd, (long) len);
41364562Sgshapiro
41464562Sgshapiro	nl = htonl(len + 1);	/* add 1 for the cmd char */
41564562Sgshapiro	(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
41664562Sgshapiro	data[MILTER_LEN_BYTES] = cmd;
41764562Sgshapiro	sl = MILTER_LEN_BYTES + 1;
41864562Sgshapiro
41964562Sgshapiro	if (to > 0)
42064562Sgshapiro	{
42164562Sgshapiro		writestart = curtime();
42264562Sgshapiro		MILTER_TIMEOUT("milter_write", to, TRUE);
42364562Sgshapiro	}
42464562Sgshapiro
42564562Sgshapiro	/* use writev() instead to send the whole stuff at once? */
42664562Sgshapiro	i = write(m->mf_sock, (void *) data, sl);
42764562Sgshapiro	if (i != sl)
42864562Sgshapiro	{
42964562Sgshapiro		int save_errno = errno;
43064562Sgshapiro
43164562Sgshapiro		if (tTd(64, 5))
43264562Sgshapiro			dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
43364562Sgshapiro				m->mf_name, cmd, (long) i, (long) sl,
43464562Sgshapiro				errstring(save_errno));
43564562Sgshapiro		if (LogLevel > 0)
43664562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
43764562Sgshapiro				  "milter_write(%s): write(%c) returned %ld, expected %ld: %s",
43864562Sgshapiro				  m->mf_name, cmd, (long) i, (long) sl,
43964562Sgshapiro				  errstring(save_errno));
44064562Sgshapiro		milter_error(m);
44164562Sgshapiro		return buf;
44264562Sgshapiro	}
44364562Sgshapiro
44464562Sgshapiro	if (len <= 0 || buf == NULL)
44564562Sgshapiro		return buf;
44664562Sgshapiro
44764562Sgshapiro	if (tTd(64, 50))
44864562Sgshapiro		dprintf("milter_write(%s): Sending %*s\n",
44964562Sgshapiro			m->mf_name, (int) len, buf);
45064562Sgshapiro
45164562Sgshapiro	if (to > 0)
45264562Sgshapiro	{
45364562Sgshapiro		time_t now;
45464562Sgshapiro
45564562Sgshapiro		now = curtime();
45664562Sgshapiro		if (now - writestart >= to)
45764562Sgshapiro		{
45864562Sgshapiro			if (tTd(64, 5))
45964562Sgshapiro				dprintf("milter_write(%s): timeout before data send\n",
46064562Sgshapiro					m->mf_name);
46164562Sgshapiro			if (LogLevel > 0)
46264562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
46364562Sgshapiro					  "milter_write(%s): timeout before data send\n",
46464562Sgshapiro					  m->mf_name);
46564562Sgshapiro			milter_error(m);
46664562Sgshapiro			return NULL;
46764562Sgshapiro		}
46864562Sgshapiro		else
46964562Sgshapiro		{
47064562Sgshapiro			to -= now - writestart;
47164562Sgshapiro			MILTER_TIMEOUT("milter_write", to, TRUE);
47264562Sgshapiro		}
47364562Sgshapiro	}
47464562Sgshapiro
47564562Sgshapiro	i = write(m->mf_sock, (void *) buf, len);
47664562Sgshapiro	if (i != len)
47764562Sgshapiro	{
47864562Sgshapiro		int save_errno = errno;
47964562Sgshapiro
48064562Sgshapiro		if (tTd(64, 5))
48164562Sgshapiro			dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
48264562Sgshapiro				m->mf_name, cmd, (long) i, (long) sl,
48364562Sgshapiro				errstring(save_errno));
48464562Sgshapiro		if (LogLevel > 0)
48564562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
48664562Sgshapiro				  "milter_write(%s): write(%c) returned %ld, expected %ld: %s",
48764562Sgshapiro				  m->mf_name, cmd, (long) i, (long) len,
48864562Sgshapiro				  errstring(save_errno));
48964562Sgshapiro		milter_error(m);
49064562Sgshapiro		return NULL;
49164562Sgshapiro	}
49264562Sgshapiro	return buf;
49364562Sgshapiro}
49464562Sgshapiro
49564562Sgshapiro/*
49664562Sgshapiro**  Utility functions
49764562Sgshapiro*/
49864562Sgshapiro
49964562Sgshapiro/*
50064562Sgshapiro**  MILTER_OPEN -- connect to remote milter filter
50164562Sgshapiro**
50264562Sgshapiro**	Parameters:
50364562Sgshapiro**		m -- milter to connect to.
50464562Sgshapiro**		parseonly -- parse but don't connect.
50564562Sgshapiro**		e -- current envelope.
50664562Sgshapiro**
50764562Sgshapiro**	Returns:
50864562Sgshapiro**		connected socket if sucessful && !parseonly,
50964562Sgshapiro**		0 upon parse success if parseonly,
51064562Sgshapiro**		-1 otherwise.
51164562Sgshapiro*/
51264562Sgshapiro
51364562Sgshapirostatic int
51464562Sgshapiromilter_open(m, parseonly, e)
51564562Sgshapiro	struct milter *m;
51664562Sgshapiro	bool parseonly;
51764562Sgshapiro	ENVELOPE *e;
51864562Sgshapiro{
51964562Sgshapiro	int sock = 0;
52064562Sgshapiro	SOCKADDR_LEN_T addrlen = 0;
52164562Sgshapiro	int addrno = 0;
52264562Sgshapiro	int save_errno;
52364562Sgshapiro	char *p;
52464562Sgshapiro	char *colon;
52564562Sgshapiro	char *at;
52664562Sgshapiro	struct hostent *hp = NULL;
52764562Sgshapiro	SOCKADDR addr;
52864562Sgshapiro
52964562Sgshapiro	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
53064562Sgshapiro	{
53164562Sgshapiro		if (tTd(64, 5))
53264562Sgshapiro			dprintf("X%s: empty or missing socket information\n",
53364562Sgshapiro				m->mf_name);
53464562Sgshapiro		if (parseonly)
53564562Sgshapiro			syserr("X%s: empty or missing socket information",
53664562Sgshapiro			       m->mf_name);
53764562Sgshapiro		else if (LogLevel > 10)
53864562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
53964562Sgshapiro				  "X%s: empty or missing socket information",
54064562Sgshapiro				  m->mf_name);
54164562Sgshapiro		milter_error(m);
54264562Sgshapiro		return -1;
54364562Sgshapiro	}
54464562Sgshapiro
54564562Sgshapiro	/* protocol:filename or protocol:port@host */
54664562Sgshapiro	p = m->mf_conn;
54764562Sgshapiro	colon = strchr(p, ':');
54864562Sgshapiro	if (colon != NULL)
54964562Sgshapiro	{
55064562Sgshapiro		*colon = '\0';
55164562Sgshapiro
55264562Sgshapiro		if (*p == '\0')
55364562Sgshapiro		{
55464562Sgshapiro# if NETUNIX
55564562Sgshapiro			/* default to AF_UNIX */
55664562Sgshapiro			addr.sa.sa_family = AF_UNIX;
55764562Sgshapiro# else /* NETUNIX */
55864562Sgshapiro#  if NETINET
55964562Sgshapiro			/* default to AF_INET */
56064562Sgshapiro			addr.sa.sa_family = AF_INET;
56164562Sgshapiro#  else /* NETINET */
56264562Sgshapiro#   if NETINET6
56364562Sgshapiro			/* default to AF_INET6 */
56464562Sgshapiro			addr.sa.sa_family = AF_INET6;
56564562Sgshapiro#   else /* NETINET6 */
56664562Sgshapiro			/* no protocols available */
56764562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
56864562Sgshapiro				  "X%s: no valid socket protocols available",
56964562Sgshapiro				  m->mf_name);
57064562Sgshapiro			milter_error(m);
57164562Sgshapiro			return -1;
57264562Sgshapiro#   endif /* NETINET6 */
57364562Sgshapiro#  endif /* NETINET */
57464562Sgshapiro# endif /* NETUNIX */
57564562Sgshapiro		}
57664562Sgshapiro# if NETUNIX
57764562Sgshapiro		else if (strcasecmp(p, "unix") == 0 ||
57864562Sgshapiro			 strcasecmp(p, "local") == 0)
57964562Sgshapiro			addr.sa.sa_family = AF_UNIX;
58064562Sgshapiro# endif /* NETUNIX */
58164562Sgshapiro# if NETINET
58264562Sgshapiro		else if (strcasecmp(p, "inet") == 0)
58364562Sgshapiro			addr.sa.sa_family = AF_INET;
58464562Sgshapiro# endif /* NETINET */
58564562Sgshapiro# if NETINET6
58664562Sgshapiro		else if (strcasecmp(p, "inet6") == 0)
58764562Sgshapiro			addr.sa.sa_family = AF_INET6;
58864562Sgshapiro# endif /* NETINET6 */
58964562Sgshapiro		else
59064562Sgshapiro		{
59164562Sgshapiro# ifdef EPROTONOSUPPORT
59264562Sgshapiro			errno = EPROTONOSUPPORT;
59364562Sgshapiro# else /* EPROTONOSUPPORT */
59464562Sgshapiro			errno = EINVAL;
59564562Sgshapiro# endif /* EPROTONOSUPPORT */
59664562Sgshapiro			if (tTd(64, 5))
59764562Sgshapiro				dprintf("X%s: unknown socket type %s\n",
59864562Sgshapiro					m->mf_name, p);
59964562Sgshapiro			if (parseonly)
60064562Sgshapiro				syserr("X%s: unknown socket type %s",
60164562Sgshapiro				       m->mf_name, p);
60264562Sgshapiro			else if (LogLevel > 10)
60364562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
60464562Sgshapiro					  "X%s: unknown socket type %s",
60564562Sgshapiro					  m->mf_name, p);
60664562Sgshapiro			milter_error(m);
60764562Sgshapiro			return -1;
60864562Sgshapiro		}
60964562Sgshapiro		*colon++ = ':';
61064562Sgshapiro	}
61164562Sgshapiro	else
61264562Sgshapiro	{
61364562Sgshapiro		/* default to AF_UNIX */
61464562Sgshapiro		addr.sa.sa_family = AF_UNIX;
61564562Sgshapiro		colon = p;
61664562Sgshapiro	}
61764562Sgshapiro
61864562Sgshapiro# if NETUNIX
61964562Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
62064562Sgshapiro	{
62164562Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
62264562Sgshapiro
62364562Sgshapiro		at = colon;
62464562Sgshapiro		if (strlen(colon) >= sizeof addr.sunix.sun_path)
62564562Sgshapiro		{
62664562Sgshapiro			if (tTd(64, 5))
62764562Sgshapiro				dprintf("X%s: local socket name %s too long\n",
62864562Sgshapiro					m->mf_name, colon);
62964562Sgshapiro			errno = EINVAL;
63064562Sgshapiro			if (parseonly)
63164562Sgshapiro				syserr("X%s: local socket name %s too long",
63264562Sgshapiro				       m->mf_name, colon);
63364562Sgshapiro			else if (LogLevel > 10)
63464562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
63564562Sgshapiro					  "X%s: local socket name %s too long",
63664562Sgshapiro					  m->mf_name, colon);
63764562Sgshapiro			milter_error(m);
63864562Sgshapiro			return -1;
63964562Sgshapiro		}
64064562Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
64164562Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
64264562Sgshapiro
64364562Sgshapiro		/* if just parsing .cf file, socket doesn't need to exist */
64464562Sgshapiro		if (parseonly && errno == ENOENT)
64564562Sgshapiro		{
64664562Sgshapiro			if (OpMode == MD_DAEMON ||
64764562Sgshapiro			    OpMode == MD_FGDAEMON)
64864562Sgshapiro				fprintf(stderr,
64964562Sgshapiro					"WARNING: X%s: local socket name %s missing\n",
65064562Sgshapiro					m->mf_name, colon);
65164562Sgshapiro		}
65264562Sgshapiro		else if (errno != 0)
65364562Sgshapiro		{
65464562Sgshapiro			/* if not safe, don't create */
65564562Sgshapiro			save_errno = errno;
65664562Sgshapiro			if (tTd(64, 5))
65764562Sgshapiro				dprintf("X%s: local socket name %s unsafe\n",
65864562Sgshapiro					m->mf_name, colon);
65964562Sgshapiro			errno = save_errno;
66064562Sgshapiro			if (parseonly)
66164562Sgshapiro			{
66264562Sgshapiro				if (OpMode == MD_DAEMON ||
66364562Sgshapiro				    OpMode == MD_FGDAEMON ||
66464562Sgshapiro				    OpMode == MD_SMTP)
66564562Sgshapiro					syserr("X%s: local socket name %s unsafe",
66664562Sgshapiro					       m->mf_name, colon);
66764562Sgshapiro			}
66864562Sgshapiro			else if (LogLevel > 10)
66964562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
67064562Sgshapiro					  "X%s: local socket name %s unsafe",
67164562Sgshapiro					  m->mf_name, colon);
67264562Sgshapiro			milter_error(m);
67364562Sgshapiro			return -1;
67464562Sgshapiro		}
67564562Sgshapiro
67664562Sgshapiro		(void) strlcpy(addr.sunix.sun_path, colon,
67764562Sgshapiro			       sizeof addr.sunix.sun_path);
67864562Sgshapiro		addrlen = sizeof (struct sockaddr_un);
67964562Sgshapiro	}
68064562Sgshapiro	else
68164562Sgshapiro# endif /* NETUNIX */
68264562Sgshapiro# if NETINET || NETINET6
68364562Sgshapiro	if (FALSE
68464562Sgshapiro#  if NETINET
68564562Sgshapiro		 || addr.sa.sa_family == AF_INET
68664562Sgshapiro#  endif /* NETINET */
68764562Sgshapiro#  if NETINET6
68864562Sgshapiro		 || addr.sa.sa_family == AF_INET6
68964562Sgshapiro#  endif /* NETINET6 */
69064562Sgshapiro		 )
69164562Sgshapiro	{
69264562Sgshapiro		u_short port;
69364562Sgshapiro
69464562Sgshapiro		/* Parse port@host */
69564562Sgshapiro		at = strchr(colon, '@');
69664562Sgshapiro		if (at == NULL)
69764562Sgshapiro		{
69864562Sgshapiro			if (tTd(64, 5))
69964562Sgshapiro				dprintf("X%s: bad address %s (expected port@host)\n",
70064562Sgshapiro					m->mf_name, colon);
70164562Sgshapiro			if (parseonly)
70264562Sgshapiro				syserr("X%s: bad address %s (expected port@host)",
70364562Sgshapiro				       m->mf_name, colon);
70464562Sgshapiro			else if (LogLevel > 10)
70564562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
70664562Sgshapiro					  "X%s: bad address %s (expected port@host)",
70764562Sgshapiro					  m->mf_name, colon);
70864562Sgshapiro			milter_error(m);
70964562Sgshapiro			return -1;
71064562Sgshapiro		}
71164562Sgshapiro		*at = '\0';
71264562Sgshapiro		if (isascii(*colon) && isdigit(*colon))
71364562Sgshapiro			port = htons((u_short) atoi(colon));
71464562Sgshapiro		else
71564562Sgshapiro		{
71664562Sgshapiro#  ifdef NO_GETSERVBYNAME
71764562Sgshapiro			if (tTd(64, 5))
71864562Sgshapiro				dprintf("X%s: invalid port number %s\n",
71964562Sgshapiro					m->mf_name, colon);
72064562Sgshapiro			if (parseonly)
72164562Sgshapiro				syserr("X%s: invalid port number %s",
72264562Sgshapiro				       m->mf_name, colon);
72364562Sgshapiro			else if (LogLevel > 10)
72464562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
72564562Sgshapiro					  "X%s: invalid port number %s",
72664562Sgshapiro					  m->mf_name, colon);
72764562Sgshapiro			milter_error(m);
72864562Sgshapiro			return -1;
72964562Sgshapiro#  else /* NO_GETSERVBYNAME */
73064562Sgshapiro			register struct servent *sp;
73164562Sgshapiro
73264562Sgshapiro			sp = getservbyname(colon, "tcp");
73364562Sgshapiro			if (sp == NULL)
73464562Sgshapiro			{
73564562Sgshapiro				save_errno = errno;
73664562Sgshapiro				if (tTd(64, 5))
73764562Sgshapiro					dprintf("X%s: unknown port name %s\n",
73864562Sgshapiro						m->mf_name, colon);
73964562Sgshapiro				errno = save_errno;
74064562Sgshapiro				if (parseonly)
74164562Sgshapiro					syserr("X%s: unknown port name %s",
74264562Sgshapiro					       m->mf_name, colon);
74364562Sgshapiro				else if (LogLevel > 10)
74464562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
74564562Sgshapiro						  "X%s: unknown port name %s",
74664562Sgshapiro						  m->mf_name, colon);
74764562Sgshapiro				milter_error(m);
74864562Sgshapiro				return -1;
74964562Sgshapiro			}
75064562Sgshapiro			port = sp->s_port;
75164562Sgshapiro#  endif /* NO_GETSERVBYNAME */
75264562Sgshapiro		}
75364562Sgshapiro		*at++ = '@';
75464562Sgshapiro		if (*at == '[')
75564562Sgshapiro		{
75664562Sgshapiro			char *end;
75764562Sgshapiro
75864562Sgshapiro			end = strchr(at, ']');
75964562Sgshapiro			if (end != NULL)
76064562Sgshapiro			{
76164562Sgshapiro				bool found = FALSE;
76264562Sgshapiro#  if NETINET
76364562Sgshapiro				unsigned long hid = INADDR_NONE;
76464562Sgshapiro#  endif /* NETINET */
76564562Sgshapiro#  if NETINET6
76664562Sgshapiro				struct sockaddr_in6 hid6;
76764562Sgshapiro#  endif /* NETINET6 */
76864562Sgshapiro
76964562Sgshapiro				*end = '\0';
77064562Sgshapiro#  if NETINET
77164562Sgshapiro				if (addr.sa.sa_family == AF_INET &&
77264562Sgshapiro				    (hid = inet_addr(&at[1])) != INADDR_NONE)
77364562Sgshapiro				{
77464562Sgshapiro					addr.sin.sin_addr.s_addr = hid;
77564562Sgshapiro					addr.sin.sin_port = port;
77664562Sgshapiro					found = TRUE;
77764562Sgshapiro				}
77864562Sgshapiro#  endif /* NETINET */
77964562Sgshapiro#  if NETINET6
78064562Sgshapiro				(void) memset(&hid6, '\0', sizeof hid6);
78164562Sgshapiro				if (addr.sa.sa_family == AF_INET6 &&
78264562Sgshapiro				    inet_pton(AF_INET6, &at[1],
78364562Sgshapiro					      &hid6.sin6_addr) == 1)
78464562Sgshapiro				{
78564562Sgshapiro					addr.sin6.sin6_addr = hid6.sin6_addr;
78664562Sgshapiro					addr.sin6.sin6_port = port;
78764562Sgshapiro					found = TRUE;
78864562Sgshapiro				}
78964562Sgshapiro#  endif /* NETINET6 */
79064562Sgshapiro				*end = ']';
79164562Sgshapiro				if (!found)
79264562Sgshapiro				{
79364562Sgshapiro					if (tTd(64, 5))
79464562Sgshapiro						dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
79564562Sgshapiro							m->mf_name, at);
79664562Sgshapiro					if (parseonly)
79764562Sgshapiro						syserr("X%s: Invalid numeric domain spec \"%s\"",
79864562Sgshapiro						       m->mf_name, at);
79964562Sgshapiro					else if (LogLevel > 10)
80064562Sgshapiro						sm_syslog(LOG_ERR, e->e_id,
80164562Sgshapiro							  "X%s: Invalid numeric domain spec \"%s\"",
80264562Sgshapiro							  m->mf_name, at);
80364562Sgshapiro					milter_error(m);
80464562Sgshapiro					return -1;
80564562Sgshapiro				}
80664562Sgshapiro			}
80764562Sgshapiro			else
80864562Sgshapiro			{
80964562Sgshapiro				if (tTd(64, 5))
81064562Sgshapiro					dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
81164562Sgshapiro						m->mf_name, at);
81264562Sgshapiro				if (parseonly)
81364562Sgshapiro					syserr("X%s: Invalid numeric domain spec \"%s\"",
81464562Sgshapiro					       m->mf_name, at);
81564562Sgshapiro				else if (LogLevel > 10)
81664562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
81764562Sgshapiro						  "X%s: Invalid numeric domain spec \"%s\"",
81864562Sgshapiro						  m->mf_name, at);
81964562Sgshapiro				milter_error(m);
82064562Sgshapiro				return -1;
82164562Sgshapiro			}
82264562Sgshapiro		}
82364562Sgshapiro		else
82464562Sgshapiro		{
82564562Sgshapiro			hp = sm_gethostbyname(at, addr.sa.sa_family);
82664562Sgshapiro			if (hp == NULL)
82764562Sgshapiro			{
82864562Sgshapiro				save_errno = errno;
82964562Sgshapiro				if (tTd(64, 5))
83064562Sgshapiro					dprintf("X%s: Unknown host name %s\n",
83164562Sgshapiro						m->mf_name, at);
83264562Sgshapiro				errno = save_errno;
83364562Sgshapiro				if (parseonly)
83464562Sgshapiro					syserr("X%s: Unknown host name %s",
83564562Sgshapiro					       m->mf_name, at);
83664562Sgshapiro				else if (LogLevel > 10)
83764562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
83864562Sgshapiro						  "X%s: Unknown host name %s",
83964562Sgshapiro						  m->mf_name, at);
84064562Sgshapiro				milter_error(m);
84164562Sgshapiro				return -1;
84264562Sgshapiro			}
84364562Sgshapiro			addr.sa.sa_family = hp->h_addrtype;
84464562Sgshapiro			switch (hp->h_addrtype)
84564562Sgshapiro			{
84664562Sgshapiro#  if NETINET
84764562Sgshapiro			  case AF_INET:
84864562Sgshapiro				memmove(&addr.sin.sin_addr,
84964562Sgshapiro					hp->h_addr,
85064562Sgshapiro					INADDRSZ);
85164562Sgshapiro				addr.sin.sin_port = port;
85264562Sgshapiro				addrlen = sizeof (struct sockaddr_in);
85364562Sgshapiro				addrno = 1;
85464562Sgshapiro				break;
85564562Sgshapiro#  endif /* NETINET */
85664562Sgshapiro
85764562Sgshapiro#  if NETINET6
85864562Sgshapiro			  case AF_INET6:
85964562Sgshapiro				memmove(&addr.sin6.sin6_addr,
86064562Sgshapiro					hp->h_addr,
86164562Sgshapiro					IN6ADDRSZ);
86264562Sgshapiro				addr.sin6.sin6_port = port;
86364562Sgshapiro				addrlen = sizeof (struct sockaddr_in6);
86464562Sgshapiro				addrno = 1;
86564562Sgshapiro				break;
86664562Sgshapiro#  endif /* NETINET6 */
86764562Sgshapiro
86864562Sgshapiro			  default:
86964562Sgshapiro				if (tTd(64, 5))
87064562Sgshapiro					dprintf("X%s: Unknown protocol for %s (%d)\n",
87164562Sgshapiro						m->mf_name, at,
87264562Sgshapiro						hp->h_addrtype);
87364562Sgshapiro				if (parseonly)
87464562Sgshapiro					syserr("X%s: Unknown protocol for %s (%d)",
87564562Sgshapiro					       m->mf_name, at, hp->h_addrtype);
87664562Sgshapiro				else if (LogLevel > 10)
87764562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
87864562Sgshapiro						  "X%s: Unknown protocol for %s (%d)",
87964562Sgshapiro						  m->mf_name, at,
88064562Sgshapiro						  hp->h_addrtype);
88164562Sgshapiro				milter_error(m);
88264562Sgshapiro				return -1;
88364562Sgshapiro			}
88464562Sgshapiro		}
88564562Sgshapiro	}
88664562Sgshapiro	else
88764562Sgshapiro# endif /* NETINET || NETINET6 */
88864562Sgshapiro	{
88964562Sgshapiro		if (tTd(64, 5))
89064562Sgshapiro			dprintf("X%s: unknown socket protocol\n", m->mf_name);
89164562Sgshapiro		if (parseonly)
89264562Sgshapiro			syserr("X%s: unknown socket protocol", m->mf_name);
89364562Sgshapiro		else if (LogLevel > 10)
89464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
89564562Sgshapiro				  "X%s: unknown socket protocol", m->mf_name);
89664562Sgshapiro		milter_error(m);
89764562Sgshapiro		return -1;
89864562Sgshapiro	}
89964562Sgshapiro
90064562Sgshapiro	/* just parsing through? */
90164562Sgshapiro	if (parseonly)
90264562Sgshapiro	{
90364562Sgshapiro		m->mf_state = SMFS_READY;
90464562Sgshapiro		return 0;
90564562Sgshapiro	}
90664562Sgshapiro
90764562Sgshapiro	/* sanity check */
90864562Sgshapiro	if (m->mf_state != SMFS_READY &&
90964562Sgshapiro	    m->mf_state != SMFS_CLOSED)
91064562Sgshapiro	{
91164562Sgshapiro		/* shouldn't happen */
91264562Sgshapiro		if (tTd(64, 1))
91364562Sgshapiro			dprintf("milter_open(%s): Trying to open filter in state %c\n",
91464562Sgshapiro				m->mf_name, (char) m->mf_state);
91564562Sgshapiro		milter_error(m);
91664562Sgshapiro		return -1;
91764562Sgshapiro	}
91864562Sgshapiro
91964562Sgshapiro	/* nope, actually connecting */
92064562Sgshapiro	for (;;)
92164562Sgshapiro	{
92264562Sgshapiro		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
92364562Sgshapiro		if (sock < 0)
92464562Sgshapiro		{
92564562Sgshapiro			save_errno = errno;
92664562Sgshapiro			if (tTd(64, 5))
92764562Sgshapiro				dprintf("X%s: error creating socket: %s\n",
92864562Sgshapiro					m->mf_name, errstring(save_errno));
92964562Sgshapiro			if (LogLevel > 0)
93064562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
93164562Sgshapiro					  "X%s: error creating socket: %s",
93264562Sgshapiro					  m->mf_name, errstring(save_errno));
93364562Sgshapiro			milter_error(m);
93464562Sgshapiro			return -1;
93564562Sgshapiro		}
93664562Sgshapiro
93764562Sgshapiro		if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
93864562Sgshapiro			break;
93964562Sgshapiro
94064562Sgshapiro		/* couldn't connect.... try next address */
94164562Sgshapiro		save_errno = errno;
94266494Sgshapiro		p = CurHostName;
94366494Sgshapiro		CurHostName = at;
94464562Sgshapiro		if (tTd(64, 5))
94564562Sgshapiro			dprintf("milter_open(%s): %s failed: %s\n",
94664562Sgshapiro				m->mf_name, at, errstring(save_errno));
94764562Sgshapiro		if (LogLevel >= 14)
94864562Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
94964562Sgshapiro				  "milter_open(%s): %s failed: %s",
95064562Sgshapiro				  m->mf_name, at, errstring(save_errno));
95166494Sgshapiro		CurHostName = p;
95264562Sgshapiro		(void) close(sock);
95364562Sgshapiro
95464562Sgshapiro		/* try next address */
95564562Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
95664562Sgshapiro		{
95764562Sgshapiro			switch (addr.sa.sa_family)
95864562Sgshapiro			{
95964562Sgshapiro# if NETINET
96064562Sgshapiro			  case AF_INET:
96164562Sgshapiro				memmove(&addr.sin.sin_addr,
96264562Sgshapiro					hp->h_addr_list[addrno++],
96364562Sgshapiro					INADDRSZ);
96464562Sgshapiro				break;
96564562Sgshapiro# endif /* NETINET */
96664562Sgshapiro
96764562Sgshapiro# if NETINET6
96864562Sgshapiro			  case AF_INET6:
96964562Sgshapiro				memmove(&addr.sin6.sin6_addr,
97064562Sgshapiro					hp->h_addr_list[addrno++],
97164562Sgshapiro					IN6ADDRSZ);
97264562Sgshapiro				break;
97364562Sgshapiro# endif /* NETINET6 */
97464562Sgshapiro
97564562Sgshapiro			  default:
97664562Sgshapiro				if (tTd(64, 5))
97764562Sgshapiro					dprintf("X%s: Unknown protocol for %s (%d)\n",
97864562Sgshapiro						m->mf_name, at,
97964562Sgshapiro						hp->h_addrtype);
98064562Sgshapiro				if (LogLevel > 0)
98164562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
98264562Sgshapiro						  "X%s: Unknown protocol for %s (%d)",
98364562Sgshapiro						  m->mf_name, at,
98464562Sgshapiro						  hp->h_addrtype);
98564562Sgshapiro				milter_error(m);
98664562Sgshapiro				return -1;
98764562Sgshapiro			}
98864562Sgshapiro			continue;
98964562Sgshapiro		}
99064562Sgshapiro		if (tTd(64, 5))
99164562Sgshapiro			dprintf("X%s: error connecting to filter\n",
99264562Sgshapiro				m->mf_name);
99364562Sgshapiro		if (LogLevel > 0)
99464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
99564562Sgshapiro				  "X%s: error connecting to filter",
99664562Sgshapiro				  m->mf_name);
99764562Sgshapiro		milter_error(m);
99864562Sgshapiro		return -1;
99964562Sgshapiro	}
100064562Sgshapiro	m->mf_state = SMFS_OPEN;
100164562Sgshapiro	return sock;
100264562Sgshapiro}
100364562Sgshapiro/*
100464562Sgshapiro**  MILTER_SETUP -- setup structure for a mail filter
100564562Sgshapiro**
100664562Sgshapiro**	Parameters:
100764562Sgshapiro**		line -- the options line.
100864562Sgshapiro**
100964562Sgshapiro**	Returns:
101064562Sgshapiro**		none
101164562Sgshapiro*/
101264562Sgshapiro
101364562Sgshapirovoid
101464562Sgshapiromilter_setup(line)
101564562Sgshapiro	char *line;
101664562Sgshapiro{
101764562Sgshapiro	char fcode;
101864562Sgshapiro	register char *p;
101964562Sgshapiro	register struct milter *m;
102064562Sgshapiro	STAB *s;
102164562Sgshapiro
102266494Sgshapiro	/* collect the filter name */
102364562Sgshapiro	for (p = line;
102464562Sgshapiro	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
102564562Sgshapiro	     p++)
102664562Sgshapiro		continue;
102764562Sgshapiro	if (*p != '\0')
102864562Sgshapiro		*p++ = '\0';
102964562Sgshapiro	if (line[0] == '\0')
103064562Sgshapiro	{
103164562Sgshapiro		syserr("name required for mail filter");
103264562Sgshapiro		return;
103364562Sgshapiro	}
103464562Sgshapiro	m = (struct milter *)xalloc(sizeof *m);
103564562Sgshapiro	memset((char *) m, '\0', sizeof *m);
103664562Sgshapiro	m->mf_name = newstr(line);
103764562Sgshapiro	m->mf_state = SMFS_READY;
103864562Sgshapiro	m->mf_sock = -1;
103964562Sgshapiro	m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
104064562Sgshapiro	m->mf_timeout[SMFTO_READ] = (time_t) 10;
104164562Sgshapiro	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
104264562Sgshapiro
104364562Sgshapiro	/* now scan through and assign info from the fields */
104464562Sgshapiro	while (*p != '\0')
104564562Sgshapiro	{
104664562Sgshapiro		char *delimptr;
104764562Sgshapiro
104864562Sgshapiro		while (*p != '\0' &&
104964562Sgshapiro		       (*p == ',' || (isascii(*p) && isspace(*p))))
105064562Sgshapiro			p++;
105164562Sgshapiro
105264562Sgshapiro		/* p now points to field code */
105364562Sgshapiro		fcode = *p;
105464562Sgshapiro		while (*p != '\0' && *p != '=' && *p != ',')
105564562Sgshapiro			p++;
105664562Sgshapiro		if (*p++ != '=')
105764562Sgshapiro		{
105864562Sgshapiro			syserr("X%s: `=' expected", m->mf_name);
105964562Sgshapiro			return;
106064562Sgshapiro		}
106164562Sgshapiro		while (isascii(*p) && isspace(*p))
106264562Sgshapiro			p++;
106364562Sgshapiro
106464562Sgshapiro		/* p now points to the field body */
106564562Sgshapiro		p = munchstring(p, &delimptr, ',');
106664562Sgshapiro
106766494Sgshapiro		/* install the field into the filter struct */
106864562Sgshapiro		switch (fcode)
106964562Sgshapiro		{
107064562Sgshapiro		  case 'S':		/* socket */
107164562Sgshapiro			if (p == NULL)
107264562Sgshapiro				m->mf_conn = NULL;
107364562Sgshapiro			else
107464562Sgshapiro				m->mf_conn = newstr(p);
107564562Sgshapiro			break;
107664562Sgshapiro
107764562Sgshapiro		  case 'F':		/* Milter flags configured on MTA */
107864562Sgshapiro			for (; *p != '\0'; p++)
107964562Sgshapiro			{
108064562Sgshapiro				if (!(isascii(*p) && isspace(*p)))
108164562Sgshapiro					setbitn(*p, m->mf_flags);
108264562Sgshapiro			}
108364562Sgshapiro			break;
108464562Sgshapiro
108564562Sgshapiro		  case 'T':		/* timeouts */
108664562Sgshapiro			milter_parse_timeouts(p, m);
108764562Sgshapiro			break;
108864562Sgshapiro
108964562Sgshapiro		  default:
109064562Sgshapiro			syserr("X%s: unknown filter equate %c=",
109164562Sgshapiro			       m->mf_name, fcode);
109264562Sgshapiro			break;
109364562Sgshapiro		}
109464562Sgshapiro		p = delimptr;
109564562Sgshapiro	}
109664562Sgshapiro
109764562Sgshapiro	/* early check for errors */
109864562Sgshapiro	(void) milter_open(m, TRUE, CurEnv);
109964562Sgshapiro
110066494Sgshapiro	/* enter the filter into the symbol table */
110164562Sgshapiro	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
110264562Sgshapiro	if (s->s_milter != NULL)
110364562Sgshapiro		syserr("X%s: duplicate filter definition", m->mf_name);
110464562Sgshapiro	else
110564562Sgshapiro		s->s_milter = m;
110664562Sgshapiro}
110764562Sgshapiro/*
110864562Sgshapiro**  MILTER_PARSE_LIST -- parse option list into an array
110964562Sgshapiro**
111064562Sgshapiro**	Called when reading configuration file.
111164562Sgshapiro**
111264562Sgshapiro**	Parameters:
111364562Sgshapiro**		spec -- the filter list.
111464562Sgshapiro**		list -- the array to fill in.
111564562Sgshapiro**		max -- the maximum number of entries in list.
111664562Sgshapiro**
111764562Sgshapiro**	Returns:
111864562Sgshapiro**		none
111964562Sgshapiro*/
112064562Sgshapiro
112164562Sgshapirovoid
112264562Sgshapiromilter_parse_list(spec, list, max)
112364562Sgshapiro	char *spec;
112464562Sgshapiro	struct milter **list;
112564562Sgshapiro	int max;
112664562Sgshapiro{
112764562Sgshapiro	int numitems = 0;
112864562Sgshapiro	register char *p;
112964562Sgshapiro
113064562Sgshapiro	/* leave one for the NULL signifying the end of the list */
113164562Sgshapiro	max--;
113264562Sgshapiro
113364562Sgshapiro	for (p = spec; p != NULL; )
113464562Sgshapiro	{
113564562Sgshapiro		STAB *s;
113664562Sgshapiro
113764562Sgshapiro		while (isascii(*p) && isspace(*p))
113864562Sgshapiro			p++;
113964562Sgshapiro		if (*p == '\0')
114064562Sgshapiro			break;
114164562Sgshapiro		spec = p;
114264562Sgshapiro
114364562Sgshapiro		if (numitems >= max)
114464562Sgshapiro		{
114564562Sgshapiro			syserr("Too many filters defined, %d max", max);
114664562Sgshapiro			if (max > 0)
114764562Sgshapiro				list[0] = NULL;
114864562Sgshapiro			return;
114964562Sgshapiro		}
115064562Sgshapiro		p = strpbrk(p, ",");
115164562Sgshapiro		if (p != NULL)
115264562Sgshapiro			*p++ = '\0';
115364562Sgshapiro
115464562Sgshapiro		s = stab(spec, ST_MILTER, ST_FIND);
115564562Sgshapiro		if (s == NULL)
115664562Sgshapiro		{
115764562Sgshapiro			syserr("InputFilter %s not defined", spec);
115864562Sgshapiro			ExitStat = EX_CONFIG;
115964562Sgshapiro			return;
116064562Sgshapiro		}
116164562Sgshapiro		list[numitems++] = s->s_milter;
116264562Sgshapiro	}
116364562Sgshapiro	list[numitems] = NULL;
116464562Sgshapiro}
116564562Sgshapiro/*
116664562Sgshapiro**  MILTER_PARSE_TIMEOUTS -- parse timeout list
116764562Sgshapiro**
116864562Sgshapiro**	Called when reading configuration file.
116964562Sgshapiro**
117064562Sgshapiro**	Parameters:
117164562Sgshapiro**		spec -- the timeout list.
117264562Sgshapiro**		m -- milter to set.
117364562Sgshapiro**
117464562Sgshapiro**	Returns:
117564562Sgshapiro**		none
117664562Sgshapiro*/
117764562Sgshapiro
117864562Sgshapirostatic void
117964562Sgshapiromilter_parse_timeouts(spec, m)
118064562Sgshapiro	char *spec;
118164562Sgshapiro	struct milter *m;
118264562Sgshapiro{
118364562Sgshapiro	char fcode;
118464562Sgshapiro	register char *p;
118564562Sgshapiro
118664562Sgshapiro	p = spec;
118764562Sgshapiro
118864562Sgshapiro	/* now scan through and assign info from the fields */
118964562Sgshapiro	while (*p != '\0')
119064562Sgshapiro	{
119164562Sgshapiro		char *delimptr;
119264562Sgshapiro
119364562Sgshapiro		while (*p != '\0' &&
119464562Sgshapiro		       (*p == ';' || (isascii(*p) && isspace(*p))))
119564562Sgshapiro			p++;
119664562Sgshapiro
119764562Sgshapiro		/* p now points to field code */
119864562Sgshapiro		fcode = *p;
119964562Sgshapiro		while (*p != '\0' && *p != ':')
120064562Sgshapiro			p++;
120164562Sgshapiro		if (*p++ != ':')
120264562Sgshapiro		{
120364562Sgshapiro			syserr("X%s, T=: `:' expected", m->mf_name);
120464562Sgshapiro			return;
120564562Sgshapiro		}
120664562Sgshapiro		while (isascii(*p) && isspace(*p))
120764562Sgshapiro			p++;
120864562Sgshapiro
120964562Sgshapiro		/* p now points to the field body */
121064562Sgshapiro		p = munchstring(p, &delimptr, ';');
121164562Sgshapiro
121266494Sgshapiro		/* install the field into the filter struct */
121364562Sgshapiro		switch (fcode)
121464562Sgshapiro		{
121564562Sgshapiro		  case 'S':
121664562Sgshapiro			m->mf_timeout[SMFTO_WRITE] = convtime(p, 's');
121764562Sgshapiro			if (tTd(64, 5))
121864562Sgshapiro				printf("X%s: %c=%ld\n",
121964562Sgshapiro				       m->mf_name, fcode,
122064562Sgshapiro				       (u_long) m->mf_timeout[SMFTO_WRITE]);
122164562Sgshapiro			break;
122264562Sgshapiro
122364562Sgshapiro		  case 'R':
122464562Sgshapiro			m->mf_timeout[SMFTO_READ] = convtime(p, 's');
122564562Sgshapiro			if (tTd(64, 5))
122664562Sgshapiro				printf("X%s: %c=%ld\n",
122764562Sgshapiro				       m->mf_name, fcode,
122864562Sgshapiro				       (u_long) m->mf_timeout[SMFTO_READ]);
122964562Sgshapiro			break;
123064562Sgshapiro
123164562Sgshapiro		  case 'E':
123264562Sgshapiro			m->mf_timeout[SMFTO_EOM] = convtime(p, 's');
123364562Sgshapiro			if (tTd(64, 5))
123464562Sgshapiro				printf("X%s: %c=%ld\n",
123564562Sgshapiro				       m->mf_name, fcode,
123664562Sgshapiro				       (u_long) m->mf_timeout[SMFTO_EOM]);
123764562Sgshapiro			break;
123864562Sgshapiro
123964562Sgshapiro		  default:
124064562Sgshapiro			if (tTd(64, 5))
124164562Sgshapiro				printf("X%s: %c unknown\n",
124264562Sgshapiro				       m->mf_name, fcode);
124364562Sgshapiro			syserr("X%s: unknown filter timeout %c",
124464562Sgshapiro			       m->mf_name, fcode);
124564562Sgshapiro			break;
124664562Sgshapiro		}
124764562Sgshapiro		p = delimptr;
124864562Sgshapiro	}
124964562Sgshapiro}
125064562Sgshapiro/*
125164562Sgshapiro**  MILTER_SET_OPTION -- set an individual milter option
125264562Sgshapiro**
125364562Sgshapiro**	Parameters:
125464562Sgshapiro**		name -- the name of the option.
125564562Sgshapiro**		val -- the value of the option.
125664562Sgshapiro**		sticky -- if set, don't let other setoptions override
125764562Sgshapiro**			this value.
125864562Sgshapiro**
125964562Sgshapiro**	Returns:
126064562Sgshapiro**		none.
126164562Sgshapiro*/
126264562Sgshapiro
126364562Sgshapiro/* set if Milter sub-option is stuck */
126464562Sgshapirostatic BITMAP256	StickyMilterOpt;
126564562Sgshapiro
126664562Sgshapirostatic struct milteropt
126764562Sgshapiro{
126864562Sgshapiro	char	*mo_name;	/* long name of milter option */
126964562Sgshapiro	u_char	mo_code;	/* code for option */
127064562Sgshapiro} MilterOptTab[] =
127164562Sgshapiro{
127264562Sgshapiro# define MO_MACROS_CONNECT		0x01
127364562Sgshapiro	{ "macros.connect",		MO_MACROS_CONNECT		},
127464562Sgshapiro# define MO_MACROS_HELO			0x02
127564562Sgshapiro	{ "macros.helo",		MO_MACROS_HELO			},
127664562Sgshapiro# define MO_MACROS_ENVFROM		0x03
127764562Sgshapiro	{ "macros.envfrom",		MO_MACROS_ENVFROM		},
127864562Sgshapiro# define MO_MACROS_ENVRCPT		0x04
127964562Sgshapiro	{ "macros.envrcpt",		MO_MACROS_ENVRCPT		},
128064562Sgshapiro	{ NULL,				0				},
128164562Sgshapiro};
128264562Sgshapiro
128364562Sgshapirovoid
128464562Sgshapiromilter_set_option(name, val, sticky)
128564562Sgshapiro	char *name;
128664562Sgshapiro	char *val;
128764562Sgshapiro	bool sticky;
128864562Sgshapiro{
128964562Sgshapiro	int nummac = 0;
129064562Sgshapiro	register struct milteropt *mo;
129164562Sgshapiro	char *p;
129264562Sgshapiro	char **macros = NULL;
129364562Sgshapiro
129464562Sgshapiro	if (tTd(37, 2) || tTd(64, 5))
129564562Sgshapiro		dprintf("milter_set_option(%s = %s)", name, val);
129664562Sgshapiro
129764562Sgshapiro	for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
129864562Sgshapiro	{
129964562Sgshapiro		if (strcasecmp(mo->mo_name, name) == 0)
130064562Sgshapiro			break;
130164562Sgshapiro	}
130264562Sgshapiro
130364562Sgshapiro	if (mo->mo_name == NULL)
130464562Sgshapiro		syserr("milter_set_option: invalid Milter option %s", name);
130564562Sgshapiro
130664562Sgshapiro	/*
130764562Sgshapiro	**  See if this option is preset for us.
130864562Sgshapiro	*/
130964562Sgshapiro
131064562Sgshapiro	if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
131164562Sgshapiro	{
131264562Sgshapiro		if (tTd(37, 2) || tTd(64,5))
131364562Sgshapiro			dprintf(" (ignored)\n");
131464562Sgshapiro		return;
131564562Sgshapiro	}
131664562Sgshapiro
131764562Sgshapiro	if (tTd(37, 2) || tTd(64,5))
131864562Sgshapiro		dprintf("\n");
131964562Sgshapiro
132064562Sgshapiro	switch (mo->mo_code)
132164562Sgshapiro	{
132264562Sgshapiro	  case MO_MACROS_CONNECT:
132364562Sgshapiro		if (macros == NULL)
132464562Sgshapiro			macros = MilterConnectMacros;
132564562Sgshapiro		/* FALLTHROUGH */
132664562Sgshapiro
132764562Sgshapiro	  case MO_MACROS_HELO:
132864562Sgshapiro		if (macros == NULL)
132964562Sgshapiro			macros = MilterHeloMacros;
133064562Sgshapiro		/* FALLTHROUGH */
133164562Sgshapiro
133264562Sgshapiro	  case MO_MACROS_ENVFROM:
133364562Sgshapiro		if (macros == NULL)
133464562Sgshapiro			macros = MilterEnvFromMacros;
133564562Sgshapiro		/* FALLTHROUGH */
133664562Sgshapiro
133764562Sgshapiro	  case MO_MACROS_ENVRCPT:
133864562Sgshapiro		if (macros == NULL)
133964562Sgshapiro			macros = MilterEnvRcptMacros;
134064562Sgshapiro
134164562Sgshapiro		p = newstr(val);
134264562Sgshapiro		while (*p != '\0')
134364562Sgshapiro		{
134464562Sgshapiro			char *macro;
134564562Sgshapiro
134664562Sgshapiro			/* Skip leading commas, spaces */
134764562Sgshapiro			while (*p != '\0' &&
134864562Sgshapiro			       (*p == ',' || (isascii(*p) && isspace(*p))))
134964562Sgshapiro				p++;
135064562Sgshapiro
135164562Sgshapiro			if (*p == '\0')
135264562Sgshapiro				break;
135364562Sgshapiro
135464562Sgshapiro			/* Find end of macro */
135564562Sgshapiro			macro = p;
135664562Sgshapiro			while (*p != '\0' && *p != ',' &&
135764562Sgshapiro			       isascii(*p) && !isspace(*p))
135864562Sgshapiro				p++;
135964562Sgshapiro			if (*p != '\0')
136064562Sgshapiro				*p++ = '\0';
136164562Sgshapiro
136264562Sgshapiro			if (nummac >= MAXFILTERMACROS)
136364562Sgshapiro			{
136464562Sgshapiro				syserr("milter_set_option: too many macros in Milter.%s (max %d)",
136564562Sgshapiro				       name, MAXFILTERMACROS);
136664562Sgshapiro				macros[nummac] = NULL;
136764562Sgshapiro				break;
136864562Sgshapiro			}
136964562Sgshapiro			macros[nummac++] = macro;
137064562Sgshapiro		}
137164562Sgshapiro		macros[nummac] = NULL;
137264562Sgshapiro		break;
137364562Sgshapiro
137464562Sgshapiro	  default:
137564562Sgshapiro		syserr("milter_set_option: invalid Milter option %s", name);
137664562Sgshapiro		break;
137764562Sgshapiro	}
137864562Sgshapiro
137964562Sgshapiro	if (sticky)
138064562Sgshapiro		setbitn(mo->mo_code, StickyMilterOpt);
138164562Sgshapiro}
138264562Sgshapiro/*
138364562Sgshapiro**  MILTER_REOPEN_DF -- open & truncate the df file (for replbody)
138464562Sgshapiro**
138564562Sgshapiro**	Parameters:
138664562Sgshapiro**		e -- current envelope.
138764562Sgshapiro**
138864562Sgshapiro**	Returns:
138964562Sgshapiro**		0 if succesful, -1 otherwise
139064562Sgshapiro*/
139164562Sgshapiro
139264562Sgshapirostatic int
139364562Sgshapiromilter_reopen_df(e)
139464562Sgshapiro	ENVELOPE *e;
139564562Sgshapiro{
139664562Sgshapiro	char dfname[MAXPATHLEN];
139764562Sgshapiro
139864562Sgshapiro	(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
139964562Sgshapiro
140064562Sgshapiro	/*
140164562Sgshapiro	**  In SuperSafe mode, e->e_dfp is a read-only FP so
140264562Sgshapiro	**  close and reopen writable (later close and reopen
140364562Sgshapiro	**  read only again).
140464562Sgshapiro	**
140564562Sgshapiro	**  In !SuperSafe mode, e->e_dfp still points at the
140664562Sgshapiro	**  buffered file I/O descriptor, still open for writing
140764562Sgshapiro	**  so there isn't as much work to do, just truncate it
140864562Sgshapiro	**  and go.
140964562Sgshapiro	*/
141064562Sgshapiro
141164562Sgshapiro	if (SuperSafe)
141264562Sgshapiro	{
141364562Sgshapiro		/* close read-only df */
141464562Sgshapiro		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
141564562Sgshapiro		{
141664562Sgshapiro			(void) fclose(e->e_dfp);
141764562Sgshapiro			e->e_flags &= ~EF_HAS_DF;
141864562Sgshapiro		}
141964562Sgshapiro
142064562Sgshapiro		/* open writable */
142164562Sgshapiro		if ((e->e_dfp = fopen(dfname, "w+")) == NULL)
142264562Sgshapiro		{
142364562Sgshapiro			MILTER_DF_ERROR("milter_reopen_df: fopen %s: %s");
142464562Sgshapiro			return -1;
142564562Sgshapiro		}
142664562Sgshapiro	}
142764562Sgshapiro	else if (e->e_dfp == NULL)
142864562Sgshapiro	{
142964562Sgshapiro		/* shouldn't happen */
143064562Sgshapiro		errno = ENOENT;
143164562Sgshapiro		MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
143264562Sgshapiro		return -1;
143364562Sgshapiro	}
143464562Sgshapiro	return 0;
143564562Sgshapiro}
143664562Sgshapiro/*
143764562Sgshapiro**  MILTER_RESET_DF -- re-open read-only the df file (for replbody)
143864562Sgshapiro**
143964562Sgshapiro**	Parameters:
144064562Sgshapiro**		e -- current envelope.
144164562Sgshapiro**
144264562Sgshapiro**	Returns:
144364562Sgshapiro**		0 if succesful, -1 otherwise
144464562Sgshapiro*/
144564562Sgshapiro
144664562Sgshapirostatic int
144764562Sgshapiromilter_reset_df(e)
144864562Sgshapiro	ENVELOPE *e;
144964562Sgshapiro{
145064562Sgshapiro	int afd;
145164562Sgshapiro	char dfname[MAXPATHLEN];
145264562Sgshapiro
145364562Sgshapiro	(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
145464562Sgshapiro
145564562Sgshapiro	if (fflush(e->e_dfp) != 0 || ferror(e->e_dfp))
145664562Sgshapiro	{
145764562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
145864562Sgshapiro		return -1;
145964562Sgshapiro	}
146064562Sgshapiro	else if (!SuperSafe)
146164562Sgshapiro	{
146264562Sgshapiro		/* skip next few clauses */
146364562Sgshapiro		/* EMPTY */
146464562Sgshapiro	}
146564562Sgshapiro	else if ((afd = fileno(e->e_dfp)) >= 0 && fsync(afd) < 0)
146664562Sgshapiro	{
146764562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
146864562Sgshapiro		return -1;
146964562Sgshapiro	}
147064562Sgshapiro	else if (fclose(e->e_dfp) < 0)
147164562Sgshapiro	{
147264562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
147364562Sgshapiro		return -1;
147464562Sgshapiro	}
147564562Sgshapiro	else if ((e->e_dfp = fopen(dfname, "r")) == NULL)
147664562Sgshapiro	{
147764562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
147864562Sgshapiro		return -1;
147964562Sgshapiro	}
148064562Sgshapiro	else
148164562Sgshapiro		e->e_flags |= EF_HAS_DF;
148264562Sgshapiro	return 0;
148364562Sgshapiro}
148464562Sgshapiro/*
148564562Sgshapiro**  MILTER_CAN_DELRCPTS -- can any milter filters delete recipients?
148664562Sgshapiro**
148764562Sgshapiro**	Parameters:
148864562Sgshapiro**		none
148964562Sgshapiro**
149064562Sgshapiro**	Returns:
149164562Sgshapiro**		TRUE if any filter deletes recipients, FALSE otherwise
149264562Sgshapiro*/
149364562Sgshapiro
149464562Sgshapirobool
149564562Sgshapiromilter_can_delrcpts()
149664562Sgshapiro{
149764562Sgshapiro	bool can = FALSE;
149864562Sgshapiro	int i;
149964562Sgshapiro
150064562Sgshapiro	if (tTd(64, 10))
150164562Sgshapiro		dprintf("milter_can_delrcpts:");
150264562Sgshapiro
150364562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
150464562Sgshapiro	{
150564562Sgshapiro		struct milter *m = InputFilters[i];
150664562Sgshapiro
150764562Sgshapiro		if (bitset(SMFIF_DELRCPT, m->mf_fflags))
150864562Sgshapiro		{
150964562Sgshapiro			can = TRUE;
151064562Sgshapiro			break;
151164562Sgshapiro		}
151264562Sgshapiro	}
151364562Sgshapiro	if (tTd(64, 10))
151464562Sgshapiro		dprintf("%s\n", can ? "TRUE" : "FALSE");
151564562Sgshapiro
151664562Sgshapiro	return can;
151764562Sgshapiro}
151864562Sgshapiro/*
151964562Sgshapiro**  MILTER_QUIT_FILTER -- close down a single filter
152064562Sgshapiro**
152164562Sgshapiro**	Parameters:
152264562Sgshapiro**		m -- milter structure of filter to close down.
152364562Sgshapiro**		e -- current envelope.
152464562Sgshapiro**
152564562Sgshapiro**	Returns:
152664562Sgshapiro**		none
152764562Sgshapiro*/
152864562Sgshapiro
152964562Sgshapirostatic void
153064562Sgshapiromilter_quit_filter(m, e)
153164562Sgshapiro	struct milter *m;
153264562Sgshapiro	ENVELOPE *e;
153364562Sgshapiro{
153464562Sgshapiro	if (tTd(64, 10))
153564562Sgshapiro		dprintf("milter_quit_filter(%s)\n", m->mf_name);
153664562Sgshapiro
153764562Sgshapiro	/* Never replace error state */
153864562Sgshapiro	if (m->mf_state == SMFS_ERROR)
153964562Sgshapiro		return;
154064562Sgshapiro
154164562Sgshapiro	if (m->mf_sock < 0 ||
154264562Sgshapiro	    m->mf_state == SMFS_CLOSED ||
154364562Sgshapiro	    m->mf_state == SMFS_READY)
154464562Sgshapiro	{
154564562Sgshapiro		m->mf_sock = -1;
154664562Sgshapiro		m->mf_state = SMFS_CLOSED;
154764562Sgshapiro		return;
154864562Sgshapiro	}
154964562Sgshapiro
155064562Sgshapiro	(void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
155164562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
155264562Sgshapiro	(void) close(m->mf_sock);
155364562Sgshapiro	m->mf_sock = -1;
155464562Sgshapiro	if (m->mf_state != SMFS_ERROR)
155564562Sgshapiro		m->mf_state = SMFS_CLOSED;
155664562Sgshapiro}
155764562Sgshapiro/*
155864562Sgshapiro**  MILTER_ABORT_FILTER -- tell filter to abort current message
155964562Sgshapiro**
156064562Sgshapiro**	Parameters:
156164562Sgshapiro**		m -- milter structure of filter to abort.
156264562Sgshapiro**		e -- current envelope.
156364562Sgshapiro**
156464562Sgshapiro**	Returns:
156564562Sgshapiro**		none
156664562Sgshapiro*/
156764562Sgshapiro
156864562Sgshapirostatic void
156964562Sgshapiromilter_abort_filter(m, e)
157064562Sgshapiro	struct milter *m;
157164562Sgshapiro	ENVELOPE *e;
157264562Sgshapiro{
157364562Sgshapiro	if (tTd(64, 10))
157464562Sgshapiro		dprintf("milter_abort_filter(%s)\n", m->mf_name);
157564562Sgshapiro
157664562Sgshapiro	if (m->mf_sock < 0 ||
157764562Sgshapiro	    m->mf_state != SMFS_INMSG)
157864562Sgshapiro		return;
157964562Sgshapiro
158064562Sgshapiro	(void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
158164562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
158264562Sgshapiro	if (m->mf_state != SMFS_ERROR)
158364562Sgshapiro		m->mf_state = SMFS_DONE;
158464562Sgshapiro}
158564562Sgshapiro/*
158664562Sgshapiro**  MILTER_SEND_MACROS -- provide macros to the filters
158764562Sgshapiro**
158864562Sgshapiro**	Parameters:
158964562Sgshapiro**		m -- milter to send macros to.
159064562Sgshapiro**		macros -- macros to send for filter smfi_getsymval().
159164562Sgshapiro**		cmd -- which command the macros are associated with.
159264562Sgshapiro**		e -- current envelope (for macro access).
159364562Sgshapiro**
159464562Sgshapiro**	Returns:
159564562Sgshapiro**		none
159664562Sgshapiro*/
159764562Sgshapiro
159864562Sgshapirostatic void
159964562Sgshapiromilter_send_macros(m, macros, cmd, e)
160064562Sgshapiro	struct milter *m;
160164562Sgshapiro	char **macros;
160264562Sgshapiro	char cmd;
160364562Sgshapiro	ENVELOPE *e;
160464562Sgshapiro{
160564562Sgshapiro	int i;
160664562Sgshapiro	int mid;
160764562Sgshapiro	char *v;
160864562Sgshapiro	char *buf, *bp;
160964562Sgshapiro	ssize_t s;
161064562Sgshapiro
161164562Sgshapiro	/* sanity check */
161264562Sgshapiro	if (macros == NULL || macros[0] == NULL)
161364562Sgshapiro		return;
161464562Sgshapiro
161564562Sgshapiro	/* put together data */
161664562Sgshapiro	s = 1;			/* for the command character */
161764562Sgshapiro	for (i = 0; macros[i] != NULL; i++)
161864562Sgshapiro	{
161964562Sgshapiro		mid = macid(macros[i], NULL);
162064562Sgshapiro		if (mid == '\0')
162164562Sgshapiro			continue;
162264562Sgshapiro		v = macvalue(mid, e);
162364562Sgshapiro		if (v == NULL)
162464562Sgshapiro			continue;
162564562Sgshapiro		s += strlen(macros[i]) + 1 + strlen(v) + 1;
162664562Sgshapiro	}
162764562Sgshapiro
162864562Sgshapiro	buf = (char *)xalloc(s);
162964562Sgshapiro	bp = buf;
163064562Sgshapiro	*bp++ = cmd;
163164562Sgshapiro	for (i = 0; macros[i] != NULL; i++)
163264562Sgshapiro	{
163364562Sgshapiro		mid = macid(macros[i], NULL);
163464562Sgshapiro		if (mid == '\0')
163564562Sgshapiro			continue;
163664562Sgshapiro		v = macvalue(mid, e);
163764562Sgshapiro		if (v == NULL)
163864562Sgshapiro			continue;
163964562Sgshapiro
164064562Sgshapiro		if (tTd(64, 10))
164164562Sgshapiro			dprintf("milter_send_macros(%s, %c): %s=%s\n",
164264562Sgshapiro				m->mf_name, cmd, macros[i], v);
164364562Sgshapiro
164464562Sgshapiro		(void) strlcpy(bp, macros[i], s - (bp - buf));
164564562Sgshapiro		bp += strlen(bp) + 1;
164664562Sgshapiro		(void) strlcpy(bp, v, s - (bp - buf));
164764562Sgshapiro		bp += strlen(bp) + 1;
164864562Sgshapiro	}
164964562Sgshapiro	(void) milter_write(m, SMFIC_MACRO, buf, s,
165064562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
165164562Sgshapiro	free(buf);
165264562Sgshapiro}
165364562Sgshapiro
165464562Sgshapiro/*
165564562Sgshapiro**  MILTER_SEND_COMMAND -- send a command and return the response for a filter
165664562Sgshapiro**
165764562Sgshapiro**	Parameters:
165864562Sgshapiro**		m -- current milter filter
165964562Sgshapiro**		command -- command to send.
166064562Sgshapiro**		data -- optional command data.
166164562Sgshapiro**		sz -- length of buf.
166264562Sgshapiro**		e -- current envelope (for e->e_id).
166364562Sgshapiro**		state -- return state word.
166464562Sgshapiro**
166564562Sgshapiro**	Returns:
166664562Sgshapiro**		response string (may be NULL)
166764562Sgshapiro*/
166864562Sgshapiro
166964562Sgshapirostatic char *
167064562Sgshapiromilter_send_command(m, command, data, sz, e, state)
167164562Sgshapiro	struct milter *m;
167264562Sgshapiro	char command;
167364562Sgshapiro	void *data;
167464562Sgshapiro	ssize_t sz;
167564562Sgshapiro	ENVELOPE *e;
167664562Sgshapiro	char *state;
167764562Sgshapiro{
167864562Sgshapiro	char rcmd;
167964562Sgshapiro	ssize_t rlen;
168064562Sgshapiro	u_long skipflag;
168164562Sgshapiro	char *defresponse;
168264562Sgshapiro	char *response;
168364562Sgshapiro
168464562Sgshapiro	if (tTd(64, 10))
168564562Sgshapiro		dprintf("milter_send_command(%s): cmd %c len %ld\n",
168664562Sgshapiro			m->mf_name, (char) command, (long) sz);
168764562Sgshapiro
168864562Sgshapiro	/* find skip flag and default failure */
168964562Sgshapiro	switch (command)
169064562Sgshapiro	{
169164562Sgshapiro	  case SMFIC_CONNECT:
169264562Sgshapiro		skipflag = SMFIP_NOCONNECT;
169364562Sgshapiro		defresponse = "554 Command rejected";
169464562Sgshapiro		break;
169564562Sgshapiro
169664562Sgshapiro	  case SMFIC_HELO:
169764562Sgshapiro		skipflag = SMFIP_NOHELO;
169864562Sgshapiro		defresponse = "550 Command rejected";
169964562Sgshapiro		break;
170064562Sgshapiro
170164562Sgshapiro	  case SMFIC_MAIL:
170264562Sgshapiro		skipflag = SMFIP_NOMAIL;
170364562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
170464562Sgshapiro		break;
170564562Sgshapiro
170664562Sgshapiro	  case SMFIC_RCPT:
170764562Sgshapiro		skipflag = SMFIP_NORCPT;
170864562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
170964562Sgshapiro		break;
171064562Sgshapiro
171164562Sgshapiro	  case SMFIC_HEADER:
171264562Sgshapiro		skipflag = SMFIP_NOHDRS;
171364562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
171464562Sgshapiro		break;
171564562Sgshapiro
171664562Sgshapiro	  case SMFIC_BODY:
171764562Sgshapiro		skipflag = SMFIP_NOBODY;
171864562Sgshapiro		defresponse = "554 5.7.1 Command rejected";
171964562Sgshapiro		break;
172064562Sgshapiro
172164562Sgshapiro	  case SMFIC_EOH:
172264562Sgshapiro		skipflag = SMFIP_NOEOH;
172364562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
172464562Sgshapiro		break;
172564562Sgshapiro
172664562Sgshapiro	  case SMFIC_BODYEOB:
172764562Sgshapiro	  case SMFIC_OPTNEG:
172864562Sgshapiro	  case SMFIC_MACRO:
172964562Sgshapiro	  case SMFIC_ABORT:
173064562Sgshapiro	  case SMFIC_QUIT:
173164562Sgshapiro		/* NOTE: not handled by milter_send_command() */
173264562Sgshapiro		/* FALLTHROUGH */
173364562Sgshapiro
173464562Sgshapiro	  default:
173564562Sgshapiro		skipflag = 0;
173664562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
173764562Sgshapiro		break;
173864562Sgshapiro	}
173964562Sgshapiro
174064562Sgshapiro	/* check if filter wants this command */
174164562Sgshapiro	if (skipflag != 0 &&
174264562Sgshapiro	    bitset(skipflag, m->mf_pflags))
174364562Sgshapiro		return NULL;
174464562Sgshapiro
174564562Sgshapiro
174664562Sgshapiro	(void) milter_write(m, command, data, sz,
174764562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
174864562Sgshapiro	if (m->mf_state == SMFS_ERROR)
174964562Sgshapiro	{
175064562Sgshapiro		MILTER_CHECK_ERROR(/* EMPTY */;);
175164562Sgshapiro		return NULL;
175264562Sgshapiro	}
175364562Sgshapiro
175464562Sgshapiro	response = milter_read(m, &rcmd, &rlen,
175564562Sgshapiro			       m->mf_timeout[SMFTO_READ], e);
175664562Sgshapiro	if (m->mf_state == SMFS_ERROR)
175764562Sgshapiro	{
175864562Sgshapiro		MILTER_CHECK_ERROR(/* EMPTY */;);
175964562Sgshapiro		return NULL;
176064562Sgshapiro	}
176164562Sgshapiro
176264562Sgshapiro	if (tTd(64, 10))
176364562Sgshapiro		dprintf("milter_send_command(%s): returned %c\n",
176464562Sgshapiro			m->mf_name, (char) rcmd);
176564562Sgshapiro
176664562Sgshapiro	switch (rcmd)
176764562Sgshapiro	{
176864562Sgshapiro	  case SMFIR_REPLYCODE:
176964562Sgshapiro		MILTER_CHECK_REPLYCODE(defresponse);
177064562Sgshapiro		/* FALLTHROUGH */
177164562Sgshapiro
177264562Sgshapiro	  case SMFIR_REJECT:
177364562Sgshapiro	  case SMFIR_DISCARD:
177464562Sgshapiro	  case SMFIR_TEMPFAIL:
177564562Sgshapiro		*state = rcmd;
177664562Sgshapiro		break;
177764562Sgshapiro
177864562Sgshapiro	  case SMFIR_ACCEPT:
177964562Sgshapiro		/* this filter is done with message/connection */
178064562Sgshapiro		m->mf_state = SMFS_DONE;
178164562Sgshapiro		break;
178264562Sgshapiro
178364562Sgshapiro	  case SMFIR_CONTINUE:
178464562Sgshapiro		/* if MAIL command is ok, filter is in message state */
178564562Sgshapiro		if (command == SMFIC_MAIL)
178664562Sgshapiro			m->mf_state = SMFS_INMSG;
178764562Sgshapiro		break;
178864562Sgshapiro
178964562Sgshapiro	  default:
179064562Sgshapiro		/* Invalid response to command */
179164562Sgshapiro		if (LogLevel > 0)
179264562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
179364562Sgshapiro				  "milter_send_command(%s): returned bogus response %c",
179464562Sgshapiro				  m->mf_name, rcmd);
179564562Sgshapiro		milter_error(m);
179664562Sgshapiro		break;
179764562Sgshapiro	}
179864562Sgshapiro
179964562Sgshapiro	if (*state != SMFIR_REPLYCODE &&
180064562Sgshapiro	    response != NULL)
180164562Sgshapiro	{
180264562Sgshapiro		free(response);
180364562Sgshapiro		response = NULL;
180464562Sgshapiro	}
180564562Sgshapiro	return response;
180664562Sgshapiro}
180764562Sgshapiro
180864562Sgshapiro/*
180964562Sgshapiro**  MILTER_COMMAND -- send a command and return the response for each filter
181064562Sgshapiro**
181164562Sgshapiro**	Parameters:
181264562Sgshapiro**		command -- command to send.
181364562Sgshapiro**		data -- optional command data.
181464562Sgshapiro**		sz -- length of buf.
181564562Sgshapiro**		macros -- macros to send for filter smfi_getsymval().
181664562Sgshapiro**		e -- current envelope (for macro access).
181764562Sgshapiro**		state -- return state word.
181864562Sgshapiro**
181964562Sgshapiro**	Returns:
182064562Sgshapiro**		response string (may be NULL)
182164562Sgshapiro*/
182264562Sgshapiro
182364562Sgshapirostatic char *
182464562Sgshapiromilter_command(command, data, sz, macros, e, state)
182564562Sgshapiro	char command;
182664562Sgshapiro	void *data;
182764562Sgshapiro	ssize_t sz;
182864562Sgshapiro	char **macros;
182964562Sgshapiro	ENVELOPE *e;
183064562Sgshapiro	char *state;
183164562Sgshapiro{
183264562Sgshapiro	int i;
183364562Sgshapiro	char *response = NULL;
183464562Sgshapiro
183564562Sgshapiro	if (tTd(64, 10))
183664562Sgshapiro		dprintf("milter_command: cmd %c len %ld\n",
183764562Sgshapiro			(char) command, (long) sz);
183864562Sgshapiro
183964562Sgshapiro	*state = SMFIR_CONTINUE;
184064562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
184164562Sgshapiro	{
184264562Sgshapiro		struct milter *m = InputFilters[i];
184364562Sgshapiro
184464562Sgshapiro		/* sanity check */
184564562Sgshapiro		if (m->mf_sock < 0 ||
184664562Sgshapiro		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
184764562Sgshapiro			continue;
184864562Sgshapiro
184964562Sgshapiro		/* send macros (regardless of whether we send command) */
185064562Sgshapiro		if (macros != NULL && macros[0] != NULL)
185164562Sgshapiro		{
185264562Sgshapiro			milter_send_macros(m, macros, command, e);
185364562Sgshapiro			if (m->mf_state == SMFS_ERROR)
185464562Sgshapiro			{
185564562Sgshapiro				MILTER_CHECK_ERROR(continue);
185664562Sgshapiro				break;
185764562Sgshapiro			}
185864562Sgshapiro		}
185964562Sgshapiro
186064562Sgshapiro		response = milter_send_command(m, command, data, sz, e, state);
186164562Sgshapiro		if (*state != SMFIR_CONTINUE)
186264562Sgshapiro			break;
186364562Sgshapiro	}
186464562Sgshapiro	return response;
186564562Sgshapiro}
186664562Sgshapiro/*
186764562Sgshapiro**  MILTER_NEGOTIATE -- get version and flags from filter
186864562Sgshapiro**
186964562Sgshapiro**	Parameters:
187064562Sgshapiro**		m -- milter filter structure.
187164562Sgshapiro**		e -- current envelope.
187264562Sgshapiro**
187364562Sgshapiro**	Returns:
187464562Sgshapiro**		0 on success, -1 otherwise
187564562Sgshapiro*/
187664562Sgshapiro
187764562Sgshapirostatic int
187864562Sgshapiromilter_negotiate(m, e)
187964562Sgshapiro	struct milter *m;
188064562Sgshapiro	ENVELOPE *e;
188164562Sgshapiro{
188264562Sgshapiro	char rcmd;
188364562Sgshapiro	mi_int32 fvers;
188464562Sgshapiro	mi_int32 fflags;
188564562Sgshapiro	mi_int32 pflags;
188664562Sgshapiro	char *response;
188764562Sgshapiro	ssize_t rlen;
188864562Sgshapiro	char data[MILTER_OPTLEN];
188964562Sgshapiro
189064562Sgshapiro	/* sanity check */
189164562Sgshapiro	if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
189264562Sgshapiro	{
189364562Sgshapiro		if (LogLevel > 0)
189464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
189564562Sgshapiro				  "milter_negotiate(%s): impossible state",
189664562Sgshapiro				  m->mf_name);
189764562Sgshapiro		milter_error(m);
189864562Sgshapiro		return -1;
189964562Sgshapiro	}
190064562Sgshapiro
190164562Sgshapiro	fvers = htonl(SMFI_VERSION);
190264562Sgshapiro	fflags = htonl(SMFI_CURR_ACTS);
190364562Sgshapiro	pflags = htonl(SMFI_CURR_PROT);
190464562Sgshapiro	(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
190564562Sgshapiro	(void) memcpy(data + MILTER_LEN_BYTES,
190664562Sgshapiro		      (char *) &fflags, MILTER_LEN_BYTES);
190764562Sgshapiro	(void) memcpy(data + (MILTER_LEN_BYTES * 2),
190864562Sgshapiro		      (char *) &pflags, MILTER_LEN_BYTES);
190964562Sgshapiro	(void) milter_write(m, SMFIC_OPTNEG, data, sizeof data,
191064562Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e);
191164562Sgshapiro
191264562Sgshapiro	if (m->mf_state == SMFS_ERROR)
191364562Sgshapiro		return -1;
191464562Sgshapiro
191564562Sgshapiro	response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e);
191664562Sgshapiro	if (m->mf_state == SMFS_ERROR)
191764562Sgshapiro		return -1;
191864562Sgshapiro
191964562Sgshapiro	if (rcmd != SMFIC_OPTNEG)
192064562Sgshapiro	{
192164562Sgshapiro		if (tTd(64, 5))
192264562Sgshapiro			dprintf("milter_negotiate(%s): returned %c instead of %c\n",
192364562Sgshapiro				m->mf_name, rcmd, SMFIC_OPTNEG);
192464562Sgshapiro		if (LogLevel > 0)
192564562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
192664562Sgshapiro				  "milter_negotiate(%s): returned %c instead of %c",
192764562Sgshapiro				  m->mf_name, rcmd, SMFIC_OPTNEG);
192864562Sgshapiro		if (response != NULL)
192964562Sgshapiro			free(response);
193064562Sgshapiro		milter_error(m);
193164562Sgshapiro		return -1;
193264562Sgshapiro	}
193364562Sgshapiro
193464562Sgshapiro	/* Make sure we have enough bytes for the version */
193564562Sgshapiro	if (response == NULL || rlen < MILTER_LEN_BYTES)
193664562Sgshapiro	{
193764562Sgshapiro		if (tTd(64, 5))
193864562Sgshapiro			dprintf("milter_negotiate(%s): did not return valid info\n",
193964562Sgshapiro				m->mf_name);
194064562Sgshapiro		if (LogLevel > 0)
194164562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
194264562Sgshapiro				  "milter_negotiate(%s): did not return valid info",
194364562Sgshapiro				  m->mf_name);
194464562Sgshapiro		if (response != NULL)
194564562Sgshapiro			free(response);
194664562Sgshapiro		milter_error(m);
194764562Sgshapiro		return -1;
194864562Sgshapiro	}
194964562Sgshapiro
195064562Sgshapiro	/* extract information */
195164562Sgshapiro	(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
195264562Sgshapiro
195364562Sgshapiro	/* Now make sure we have enough for the feature bitmap */
195464562Sgshapiro	if (rlen != MILTER_OPTLEN)
195564562Sgshapiro	{
195664562Sgshapiro		if (tTd(64, 5))
195764562Sgshapiro			dprintf("milter_negotiate(%s): did not return enough info\n",
195864562Sgshapiro				m->mf_name);
195964562Sgshapiro		if (LogLevel > 0)
196064562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
196164562Sgshapiro				  "milter_negotiate(%s): did not return enough info",
196264562Sgshapiro				  m->mf_name);
196364562Sgshapiro		if (response != NULL)
196464562Sgshapiro			free(response);
196564562Sgshapiro		milter_error(m);
196664562Sgshapiro		return -1;
196764562Sgshapiro	}
196864562Sgshapiro
196964562Sgshapiro	(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
197064562Sgshapiro		      MILTER_LEN_BYTES);
197164562Sgshapiro	(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
197264562Sgshapiro		      MILTER_LEN_BYTES);
197364562Sgshapiro	free(response);
197464562Sgshapiro	response = NULL;
197564562Sgshapiro
197664562Sgshapiro	m->mf_fvers = ntohl(fvers);
197764562Sgshapiro	m->mf_fflags = ntohl(fflags);
197864562Sgshapiro	m->mf_pflags = ntohl(pflags);
197964562Sgshapiro
198064562Sgshapiro	/* check for version compatibility */
198164562Sgshapiro	if (m->mf_fvers == 1 ||
198264562Sgshapiro	    m->mf_fvers > SMFI_VERSION)
198364562Sgshapiro	{
198464562Sgshapiro		if (tTd(64, 5))
198564562Sgshapiro			dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n",
198664562Sgshapiro				m->mf_name, m->mf_fvers, SMFI_VERSION);
198764562Sgshapiro		if (LogLevel > 0)
198864562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
198964562Sgshapiro				  "milter_negotiate(%s): version %ld != MTA milter version %d",
199064562Sgshapiro				  m->mf_name, m->mf_fvers, SMFI_VERSION);
199164562Sgshapiro		milter_error(m);
199264562Sgshapiro		return -1;
199364562Sgshapiro	}
199464562Sgshapiro
199564562Sgshapiro	/* check for filter feature mismatch */
199664562Sgshapiro	if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags)
199764562Sgshapiro	{
199864562Sgshapiro		if (tTd(64, 5))
199964562Sgshapiro			dprintf("milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
200064562Sgshapiro				m->mf_name, m->mf_fflags,
200164562Sgshapiro				(u_long) SMFI_CURR_ACTS);
200264562Sgshapiro		if (LogLevel > 0)
200364562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
200464562Sgshapiro				  "milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
200564562Sgshapiro				  m->mf_name, m->mf_fflags,
200664562Sgshapiro				  (u_long) SMFI_CURR_ACTS);
200764562Sgshapiro		milter_error(m);
200864562Sgshapiro		return -1;
200964562Sgshapiro	}
201064562Sgshapiro
201164562Sgshapiro	/* check for protocol feature mismatch */
201264562Sgshapiro	if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags)
201364562Sgshapiro	{
201464562Sgshapiro		if (tTd(64, 5))
201564562Sgshapiro			dprintf("milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
201664562Sgshapiro				m->mf_name, m->mf_pflags,
201764562Sgshapiro				(u_long) SMFI_CURR_PROT);
201864562Sgshapiro		if (LogLevel > 0)
201964562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
202064562Sgshapiro				  "milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
202164562Sgshapiro				  m->mf_name, m->mf_pflags,
202264562Sgshapiro				  (u_long) SMFI_CURR_PROT);
202364562Sgshapiro		milter_error(m);
202464562Sgshapiro		return -1;
202564562Sgshapiro	}
202664562Sgshapiro
202764562Sgshapiro	if (tTd(64, 5))
202864562Sgshapiro		dprintf("milter_negotiate(%s): version %lu, fflags 0x%lx, pflags 0x%lx\n",
202964562Sgshapiro			m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
203064562Sgshapiro	return 0;
203164562Sgshapiro}
203264562Sgshapiro/*
203364562Sgshapiro**  MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
203464562Sgshapiro**
203564562Sgshapiro**	Reduce code duplication by putting these checks in one place
203664562Sgshapiro**
203764562Sgshapiro**	Parameters:
203864562Sgshapiro**		e -- current envelope.
203964562Sgshapiro**
204064562Sgshapiro**	Returns:
204164562Sgshapiro**		none
204264562Sgshapiro*/
204364562Sgshapiro
204464562Sgshapirostatic void
204564562Sgshapiromilter_per_connection_check(e)
204664562Sgshapiro	ENVELOPE *e;
204764562Sgshapiro{
204864562Sgshapiro	int i;
204964562Sgshapiro
205064562Sgshapiro	/* see if we are done with any of the filters */
205164562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
205264562Sgshapiro	{
205364562Sgshapiro		struct milter *m = InputFilters[i];
205464562Sgshapiro
205564562Sgshapiro		if (m->mf_state == SMFS_DONE)
205664562Sgshapiro			milter_quit_filter(m, e);
205764562Sgshapiro	}
205864562Sgshapiro}
205964562Sgshapiro/*
206064562Sgshapiro**  MILTER_ERROR -- Put a milter filter into error state
206164562Sgshapiro**
206264562Sgshapiro**	Parameters:
206364562Sgshapiro**		m -- the broken filter.
206464562Sgshapiro**
206564562Sgshapiro**	Returns:
206664562Sgshapiro**		none
206764562Sgshapiro*/
206864562Sgshapiro
206964562Sgshapirostatic void
207064562Sgshapiromilter_error(m)
207164562Sgshapiro	struct milter *m;
207264562Sgshapiro{
207364562Sgshapiro	/*
207464562Sgshapiro	**  We could send a quit here but
207564562Sgshapiro	**  we may have gotten here due to
207664562Sgshapiro	**  an I/O error so we don't want
207764562Sgshapiro	**  to try to make things worse.
207864562Sgshapiro	*/
207964562Sgshapiro
208064562Sgshapiro	if (m->mf_sock >= 0)
208164562Sgshapiro	{
208264562Sgshapiro		(void) close(m->mf_sock);
208364562Sgshapiro		m->mf_sock = -1;
208464562Sgshapiro	}
208564562Sgshapiro	m->mf_state = SMFS_ERROR;
208664562Sgshapiro}
208764562Sgshapiro/*
208864562Sgshapiro**  MILTER_HEADERS -- send headers to a single milter filter
208964562Sgshapiro**
209064562Sgshapiro**	Parameters:
209164562Sgshapiro**		m -- current filter.
209264562Sgshapiro**		e -- current envelope.
209364562Sgshapiro**		state -- return state from response.
209464562Sgshapiro**
209564562Sgshapiro**	Returns:
209664562Sgshapiro**		response string (may be NULL)
209764562Sgshapiro*/
209864562Sgshapiro
209964562Sgshapirostatic char *
210064562Sgshapiromilter_headers(m, e, state)
210164562Sgshapiro	struct milter *m;
210264562Sgshapiro	ENVELOPE *e;
210364562Sgshapiro	char *state;
210464562Sgshapiro{
210564562Sgshapiro	char *response = NULL;
210664562Sgshapiro	HDR *h;
210764562Sgshapiro
210864562Sgshapiro	for (h = e->e_header; h != NULL; h = h->h_link)
210964562Sgshapiro	{
211064562Sgshapiro		char *buf;
211164562Sgshapiro		ssize_t s;
211264562Sgshapiro
211364562Sgshapiro		/* don't send over deleted headers */
211464562Sgshapiro		if (h->h_value == NULL)
211564562Sgshapiro		{
211664562Sgshapiro			/* strip H_USER so not counted in milter_chgheader() */
211764562Sgshapiro			h->h_flags &= ~H_USER;
211864562Sgshapiro			continue;
211964562Sgshapiro		}
212064562Sgshapiro
212164562Sgshapiro		/* skip auto-generated */
212264562Sgshapiro		if (!bitset(H_USER, h->h_flags))
212364562Sgshapiro			continue;
212464562Sgshapiro
212564562Sgshapiro		if (tTd(64, 10))
212664562Sgshapiro			dprintf("milter_headers: %s: %s\n",
212764562Sgshapiro				h->h_field, h->h_value);
212864562Sgshapiro
212964562Sgshapiro		s = strlen(h->h_field) + 1 +
213064562Sgshapiro			strlen(h->h_value) + 1;
213164562Sgshapiro		buf = (char *) xalloc(s);
213264562Sgshapiro		snprintf(buf, s, "%s%c%s", h->h_field, '\0', h->h_value);
213364562Sgshapiro
213464562Sgshapiro		/* send it over */
213564562Sgshapiro		response = milter_send_command(m, SMFIC_HEADER, buf,
213664562Sgshapiro					       s, e, state);
213764562Sgshapiro		free(buf);
213864562Sgshapiro		if (m->mf_state == SMFS_ERROR ||
213964562Sgshapiro		    m->mf_state == SMFS_DONE ||
214064562Sgshapiro		    *state != SMFIR_CONTINUE)
214164562Sgshapiro			break;
214264562Sgshapiro	}
214364562Sgshapiro	return response;
214464562Sgshapiro}
214564562Sgshapiro/*
214664562Sgshapiro**  MILTER_BODY -- send the body to a filter
214764562Sgshapiro**
214864562Sgshapiro**	Parameters:
214964562Sgshapiro**		m -- current filter.
215064562Sgshapiro**		e -- current envelope.
215164562Sgshapiro**		state -- return state from response.
215264562Sgshapiro**
215364562Sgshapiro**	Returns:
215464562Sgshapiro**		response string (may be NULL)
215564562Sgshapiro*/
215664562Sgshapiro
215764562Sgshapirostatic char *
215864562Sgshapiromilter_body(m, e, state)
215964562Sgshapiro	struct milter *m;
216064562Sgshapiro	ENVELOPE *e;
216164562Sgshapiro	char *state;
216264562Sgshapiro{
216364562Sgshapiro	char bufchar = '\0';
216464562Sgshapiro	char prevchar = '\0';
216564562Sgshapiro	int c;
216664562Sgshapiro	char *response = NULL;
216764562Sgshapiro	char *bp;
216864562Sgshapiro	char buf[MILTER_CHUNK_SIZE];
216964562Sgshapiro
217064562Sgshapiro	if (tTd(64, 10))
217164562Sgshapiro		dprintf("milter_body\n");
217264562Sgshapiro
217364562Sgshapiro	if (bfrewind(e->e_dfp) < 0)
217464562Sgshapiro	{
217564562Sgshapiro		ExitStat = EX_IOERR;
217664562Sgshapiro		*state = SMFIR_TEMPFAIL;
217764562Sgshapiro		syserr("milter_body: %s/df%s: rewind error",
217864562Sgshapiro		       qid_printqueue(e->e_queuedir), e->e_id);
217964562Sgshapiro		return NULL;
218064562Sgshapiro	}
218164562Sgshapiro
218264562Sgshapiro	bp = buf;
218364562Sgshapiro	while ((c = getc(e->e_dfp)) != EOF)
218464562Sgshapiro	{
218564562Sgshapiro		/*  Change LF to CRLF */
218664562Sgshapiro		if (c == '\n')
218764562Sgshapiro		{
218864562Sgshapiro			/* Not a CRLF already? */
218964562Sgshapiro			if (prevchar != '\r')
219064562Sgshapiro			{
219164562Sgshapiro				/* Room for CR now? */
219264562Sgshapiro				if (bp + 2 > &buf[sizeof buf])
219364562Sgshapiro				{
219464562Sgshapiro					/* No room, buffer LF */
219564562Sgshapiro					bufchar = c;
219664562Sgshapiro
219764562Sgshapiro					/* and send CR now */
219864562Sgshapiro					c = '\r';
219964562Sgshapiro				}
220064562Sgshapiro				else
220164562Sgshapiro				{
220264562Sgshapiro					/* Room to do it now */
220364562Sgshapiro					*bp++ = '\r';
220464562Sgshapiro					prevchar = '\r';
220564562Sgshapiro				}
220664562Sgshapiro			}
220764562Sgshapiro		}
220864562Sgshapiro		*bp++ = (char) c;
220964562Sgshapiro		prevchar = c;
221064562Sgshapiro		if (bp >= &buf[sizeof buf])
221164562Sgshapiro		{
221264562Sgshapiro			/* send chunk */
221364562Sgshapiro			response = milter_send_command(m, SMFIC_BODY, buf,
221464562Sgshapiro						       bp - buf, e, state);
221564562Sgshapiro			bp = buf;
221664562Sgshapiro			if (bufchar != '\0')
221764562Sgshapiro			{
221864562Sgshapiro				*bp++ = bufchar;
221964562Sgshapiro				bufchar = '\0';
222064562Sgshapiro				prevchar = bufchar;
222164562Sgshapiro			}
222264562Sgshapiro		}
222364562Sgshapiro		if (m->mf_state == SMFS_ERROR ||
222464562Sgshapiro		    m->mf_state == SMFS_DONE ||
222564562Sgshapiro		    *state != SMFIR_CONTINUE)
222664562Sgshapiro			break;
222764562Sgshapiro	}
222864562Sgshapiro
222964562Sgshapiro	/* check for read errors */
223064562Sgshapiro	if (ferror(e->e_dfp))
223164562Sgshapiro	{
223264562Sgshapiro		ExitStat = EX_IOERR;
223364562Sgshapiro		if (*state == SMFIR_CONTINUE ||
223464562Sgshapiro		    *state == SMFIR_ACCEPT)
223564562Sgshapiro		{
223664562Sgshapiro			*state = SMFIR_TEMPFAIL;
223764562Sgshapiro			if (response != NULL)
223864562Sgshapiro			{
223964562Sgshapiro				free(response);
224064562Sgshapiro				response = NULL;
224164562Sgshapiro			}
224264562Sgshapiro		}
224364562Sgshapiro		syserr("milter_body: %s/df%s: read error",
224464562Sgshapiro		       qid_printqueue(e->e_queuedir), e->e_id);
224564562Sgshapiro		return response;
224664562Sgshapiro	}
224764562Sgshapiro
224864562Sgshapiro	/* send last body chunk */
224964562Sgshapiro	if (bp > buf &&
225064562Sgshapiro	    m->mf_state != SMFS_ERROR &&
225164562Sgshapiro	    m->mf_state != SMFS_DONE &&
225264562Sgshapiro	    *state == SMFIR_CONTINUE)
225364562Sgshapiro	{
225464562Sgshapiro		/* send chunk */
225564562Sgshapiro		response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
225664562Sgshapiro					       e, state);
225764562Sgshapiro		bp = buf;
225864562Sgshapiro	}
225964562Sgshapiro	return response;
226064562Sgshapiro}
226164562Sgshapiro
226264562Sgshapiro/*
226364562Sgshapiro**  Actions
226464562Sgshapiro*/
226564562Sgshapiro
226664562Sgshapiro/*
226764562Sgshapiro**  MILTER_ADDHEADER -- Add the supplied header to the message
226864562Sgshapiro**
226964562Sgshapiro**	Parameters:
227064562Sgshapiro**		response -- encoded form of header/value.
227164562Sgshapiro**		rlen -- length of response.
227264562Sgshapiro**		e -- current envelope.
227364562Sgshapiro**
227464562Sgshapiro**	Returns:
227564562Sgshapiro**		none
227664562Sgshapiro*/
227764562Sgshapiro
227864562Sgshapirostatic void
227964562Sgshapiromilter_addheader(response, rlen, e)
228064562Sgshapiro	char *response;
228164562Sgshapiro	ssize_t rlen;
228264562Sgshapiro	ENVELOPE *e;
228364562Sgshapiro{
228464562Sgshapiro	char *val;
228564562Sgshapiro
228664562Sgshapiro	if (tTd(64, 10))
228764562Sgshapiro		dprintf("milter_addheader: ");
228864562Sgshapiro
228964562Sgshapiro	/* sanity checks */
229064562Sgshapiro	if (response == NULL)
229164562Sgshapiro	{
229264562Sgshapiro		if (tTd(64, 10))
229364562Sgshapiro			dprintf("NULL response\n");
229464562Sgshapiro		return;
229564562Sgshapiro	}
229664562Sgshapiro
229764562Sgshapiro	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
229864562Sgshapiro	{
229964562Sgshapiro		if (tTd(64, 10))
230064562Sgshapiro			dprintf("didn't follow protocol (total len)\n");
230164562Sgshapiro		return;
230264562Sgshapiro	}
230364562Sgshapiro
230464562Sgshapiro	/* Find separating NUL */
230564562Sgshapiro	val = response + strlen(response) + 1;
230664562Sgshapiro
230764562Sgshapiro	/* another sanity check */
230864562Sgshapiro	if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
230964562Sgshapiro	{
231064562Sgshapiro		if (tTd(64, 10))
231164562Sgshapiro			dprintf("didn't follow protocol (part len)\n");
231264562Sgshapiro		return;
231364562Sgshapiro	}
231464562Sgshapiro
231564562Sgshapiro	if (*response == '\0')
231664562Sgshapiro	{
231764562Sgshapiro		if (tTd(64, 10))
231864562Sgshapiro			dprintf("empty field name\n");
231964562Sgshapiro		return;
232064562Sgshapiro	}
232164562Sgshapiro
232264562Sgshapiro	/* add to e_msgsize */
232364562Sgshapiro	e->e_msgsize += strlen(response) + 2 + strlen(val);
232464562Sgshapiro
232564562Sgshapiro	if (tTd(64, 10))
232664562Sgshapiro		dprintf("Add %s: %s\n", response, val);
232764562Sgshapiro
232864562Sgshapiro	addheader(newstr(response), val, H_USER, &e->e_header);
232964562Sgshapiro}
233064562Sgshapiro/*
233164562Sgshapiro**  MILTER_CHANGEHEADER -- Change the supplied header in the message
233264562Sgshapiro**
233364562Sgshapiro**	Parameters:
233464562Sgshapiro**		response -- encoded form of header/index/value.
233564562Sgshapiro**		rlen -- length of response.
233664562Sgshapiro**		e -- current envelope.
233764562Sgshapiro**
233864562Sgshapiro**	Returns:
233964562Sgshapiro**		none
234064562Sgshapiro*/
234164562Sgshapiro
234264562Sgshapirostatic void
234364562Sgshapiromilter_changeheader(response, rlen, e)
234464562Sgshapiro	char *response;
234564562Sgshapiro	ssize_t rlen;
234664562Sgshapiro	ENVELOPE *e;
234764562Sgshapiro{
234864562Sgshapiro	mi_int32 i, index;
234964562Sgshapiro	char *field, *val;
235064562Sgshapiro	HDR *h;
235164562Sgshapiro
235264562Sgshapiro	if (tTd(64, 10))
235364562Sgshapiro		dprintf("milter_changeheader: ");
235464562Sgshapiro
235564562Sgshapiro	/* sanity checks */
235664562Sgshapiro	if (response == NULL)
235764562Sgshapiro	{
235864562Sgshapiro		if (tTd(64, 10))
235964562Sgshapiro			dprintf("NULL response\n");
236064562Sgshapiro		return;
236164562Sgshapiro	}
236264562Sgshapiro
236364562Sgshapiro	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
236464562Sgshapiro	{
236564562Sgshapiro		if (tTd(64, 10))
236664562Sgshapiro			dprintf("didn't follow protocol (total len)\n");
236764562Sgshapiro		return;
236864562Sgshapiro	}
236964562Sgshapiro
237064562Sgshapiro	/* Find separating NUL */
237164562Sgshapiro	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
237264562Sgshapiro	index = ntohl(i);
237364562Sgshapiro	field = response + MILTER_LEN_BYTES;
237464562Sgshapiro	val = field + strlen(field) + 1;
237564562Sgshapiro
237664562Sgshapiro	/* another sanity check */
237764562Sgshapiro	if (MILTER_LEN_BYTES + strlen(field) + 1 +
237864562Sgshapiro	    strlen(val) + 1 != (size_t) rlen)
237964562Sgshapiro	{
238064562Sgshapiro		if (tTd(64, 10))
238164562Sgshapiro			dprintf("didn't follow protocol (part len)\n");
238264562Sgshapiro		return;
238364562Sgshapiro	}
238464562Sgshapiro
238564562Sgshapiro	if (*field == '\0')
238664562Sgshapiro	{
238764562Sgshapiro		if (tTd(64, 10))
238864562Sgshapiro			dprintf("empty field name\n");
238964562Sgshapiro		return;
239064562Sgshapiro	}
239164562Sgshapiro
239264562Sgshapiro	for (h = e->e_header; h != NULL; h = h->h_link)
239364562Sgshapiro	{
239464562Sgshapiro		if (bitset(H_USER, h->h_flags) &&
239564562Sgshapiro		    strcasecmp(h->h_field, field) == 0 &&
239664562Sgshapiro		    --index <= 0)
239764562Sgshapiro			break;
239864562Sgshapiro	}
239964562Sgshapiro
240064562Sgshapiro	if (h == NULL)
240164562Sgshapiro	{
240264562Sgshapiro		if (*val == '\0')
240364562Sgshapiro		{
240464562Sgshapiro			if (tTd(64, 10))
240564562Sgshapiro				dprintf("Delete (noop) %s:\n", field);
240664562Sgshapiro		}
240764562Sgshapiro		else
240864562Sgshapiro		{
240964562Sgshapiro			/* treat modify value with no existing header as add */
241064562Sgshapiro			if (tTd(64, 10))
241164562Sgshapiro				dprintf("Add %s: %s\n",	field, val);
241264562Sgshapiro
241364562Sgshapiro			addheader(newstr(field), val, H_USER, &e->e_header);
241464562Sgshapiro		}
241564562Sgshapiro		return;
241664562Sgshapiro	}
241764562Sgshapiro
241864562Sgshapiro	if (tTd(64, 10))
241964562Sgshapiro	{
242064562Sgshapiro		if (*val == '\0')
242164562Sgshapiro		{
242264562Sgshapiro			dprintf("Delete %s: %s\n", field,
242364562Sgshapiro				h->h_value == NULL ? "<NULL>" : h->h_value);
242464562Sgshapiro		}
242564562Sgshapiro		else
242664562Sgshapiro		{
242764562Sgshapiro			dprintf("Change %s: from %s to %s\n",
242864562Sgshapiro				field,
242964562Sgshapiro				h->h_value == NULL ? "<NULL>" : h->h_value,
243064562Sgshapiro				val);
243164562Sgshapiro		}
243264562Sgshapiro	}
243364562Sgshapiro
243464562Sgshapiro	if (h->h_value != NULL)
243564562Sgshapiro	{
243664562Sgshapiro		e->e_msgsize -= strlen(h->h_value);
243764562Sgshapiro		free(h->h_value);
243864562Sgshapiro	}
243964562Sgshapiro
244064562Sgshapiro	if (*val == '\0')
244164562Sgshapiro	{
244264562Sgshapiro		/* Remove "Field: " from message size */
244364562Sgshapiro		e->e_msgsize -= strlen(h->h_field) + 2;
244464562Sgshapiro		h->h_value = NULL;
244564562Sgshapiro	}
244664562Sgshapiro	else
244764562Sgshapiro	{
244864562Sgshapiro		h->h_value = newstr(val);
244964562Sgshapiro		e->e_msgsize += strlen(h->h_value);
245064562Sgshapiro	}
245164562Sgshapiro}
245264562Sgshapiro/*
245364562Sgshapiro**  MILTER_ADDRCPT -- Add the supplied recipient to the message
245464562Sgshapiro**
245564562Sgshapiro**	Parameters:
245664562Sgshapiro**		response -- encoded form of recipient address.
245764562Sgshapiro**		rlen -- length of response.
245864562Sgshapiro**		e -- current envelope.
245964562Sgshapiro**
246064562Sgshapiro**	Returns:
246164562Sgshapiro**		none
246264562Sgshapiro*/
246364562Sgshapiro
246464562Sgshapirostatic void
246564562Sgshapiromilter_addrcpt(response, rlen, e)
246664562Sgshapiro	char *response;
246764562Sgshapiro	ssize_t rlen;
246864562Sgshapiro	ENVELOPE *e;
246964562Sgshapiro{
247064562Sgshapiro	if (tTd(64, 10))
247164562Sgshapiro		dprintf("milter_addrcpt: ");
247264562Sgshapiro
247364562Sgshapiro	/* sanity checks */
247464562Sgshapiro	if (response == NULL)
247564562Sgshapiro	{
247664562Sgshapiro		if (tTd(64, 10))
247764562Sgshapiro			dprintf("NULL response\n");
247864562Sgshapiro		return;
247964562Sgshapiro	}
248064562Sgshapiro
248164562Sgshapiro	if (*response == '\0' ||
248264562Sgshapiro	    strlen(response) + 1 != (size_t) rlen)
248364562Sgshapiro	{
248464562Sgshapiro		if (tTd(64, 10))
248564562Sgshapiro			dprintf("didn't follow protocol (total len %d != rlen %d)\n",
248664562Sgshapiro				strlen(response), rlen -1);
248764562Sgshapiro		return;
248864562Sgshapiro	}
248964562Sgshapiro
249064562Sgshapiro	if (tTd(64, 10))
249164562Sgshapiro		dprintf("%s\n", response);
249264562Sgshapiro	(void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
249364562Sgshapiro	return;
249464562Sgshapiro}
249564562Sgshapiro/*
249664562Sgshapiro**  MILTER_DELRCPT -- Delete the supplied recipient from the message
249764562Sgshapiro**
249864562Sgshapiro**	Parameters:
249964562Sgshapiro**		response -- encoded form of recipient address.
250064562Sgshapiro**		rlen -- length of response.
250164562Sgshapiro**		e -- current envelope.
250264562Sgshapiro**
250364562Sgshapiro**	Returns:
250464562Sgshapiro**		none
250564562Sgshapiro*/
250664562Sgshapiro
250764562Sgshapirostatic void
250864562Sgshapiromilter_delrcpt(response, rlen, e)
250964562Sgshapiro	char *response;
251064562Sgshapiro	ssize_t rlen;
251164562Sgshapiro	ENVELOPE *e;
251264562Sgshapiro{
251364562Sgshapiro	if (tTd(64, 10))
251464562Sgshapiro		dprintf("milter_delrcpt: ");
251564562Sgshapiro
251664562Sgshapiro	/* sanity checks */
251764562Sgshapiro	if (response == NULL)
251864562Sgshapiro	{
251964562Sgshapiro		if (tTd(64, 10))
252064562Sgshapiro			dprintf("NULL response\n");
252164562Sgshapiro		return;
252264562Sgshapiro	}
252364562Sgshapiro
252464562Sgshapiro	if (*response == '\0' ||
252564562Sgshapiro	    strlen(response) + 1 != (size_t) rlen)
252664562Sgshapiro	{
252764562Sgshapiro		if (tTd(64, 10))
252864562Sgshapiro			dprintf("didn't follow protocol (total len)\n");
252964562Sgshapiro		return;
253064562Sgshapiro	}
253164562Sgshapiro
253264562Sgshapiro	if (tTd(64, 10))
253364562Sgshapiro		dprintf("%s\n", response);
253464562Sgshapiro	(void) removefromlist(response, &e->e_sendqueue, e);
253564562Sgshapiro	return;
253664562Sgshapiro}
253764562Sgshapiro/*
253864562Sgshapiro**  MILTER_REPLBODY -- Replace the current df file with new body
253964562Sgshapiro**
254064562Sgshapiro**	Parameters:
254164562Sgshapiro**		response -- encoded form of new body.
254264562Sgshapiro**		rlen -- length of response.
254364562Sgshapiro**		newfilter -- if first time called by a new filter
254464562Sgshapiro**		e -- current envelope.
254564562Sgshapiro**
254664562Sgshapiro**	Returns:
254764562Sgshapiro**		0 upon success, -1 upon failure
254864562Sgshapiro*/
254964562Sgshapiro
255064562Sgshapirostatic int
255164562Sgshapiromilter_replbody(response, rlen, newfilter, e)
255264562Sgshapiro	char *response;
255364562Sgshapiro	ssize_t rlen;
255464562Sgshapiro	bool newfilter;
255564562Sgshapiro	ENVELOPE *e;
255664562Sgshapiro{
255764562Sgshapiro	static char prevchar;
255864562Sgshapiro	int i;
255964562Sgshapiro
256064562Sgshapiro	if (tTd(64, 10))
256164562Sgshapiro		dprintf("milter_replbody\n");
256264562Sgshapiro
256364562Sgshapiro	/* If a new filter, reset previous character and truncate df */
256464562Sgshapiro	if (newfilter)
256564562Sgshapiro	{
256664562Sgshapiro		off_t prevsize = 0;
256764562Sgshapiro		char dfname[MAXPATHLEN];
256864562Sgshapiro
256964562Sgshapiro		(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
257064562Sgshapiro
257164562Sgshapiro		/* Reset prevchar */
257264562Sgshapiro		prevchar = '\0';
257364562Sgshapiro
257464562Sgshapiro		/* Get the current df information */
257564562Sgshapiro		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
257664562Sgshapiro		{
257764562Sgshapiro			int afd;
257864562Sgshapiro			struct stat st;
257964562Sgshapiro
258064562Sgshapiro			afd = fileno(e->e_dfp);
258164562Sgshapiro			if (afd > 0 && fstat(afd, &st) == 0)
258264562Sgshapiro				prevsize = st.st_size;
258364562Sgshapiro		}
258464562Sgshapiro
258564562Sgshapiro		/* truncate current df file */
258664562Sgshapiro		if (bftruncate(e->e_dfp) < 0)
258764562Sgshapiro		{
258864562Sgshapiro			MILTER_DF_ERROR("milter_reopen_df: bftruncate %s: %s");
258964562Sgshapiro			return -1;
259064562Sgshapiro		}
259164562Sgshapiro		else
259264562Sgshapiro		{
259364562Sgshapiro			if (prevsize > e->e_msgsize)
259464562Sgshapiro				e->e_msgsize = 0;
259564562Sgshapiro			else
259664562Sgshapiro				e->e_msgsize -= prevsize;
259764562Sgshapiro		}
259864562Sgshapiro	}
259964562Sgshapiro
260064562Sgshapiro	if (response == NULL)
260164562Sgshapiro	{
260264562Sgshapiro		/* Flush the buffered '\r' */
260364562Sgshapiro		if (prevchar == '\r')
260464562Sgshapiro		{
260564562Sgshapiro			(void) putc(prevchar, e->e_dfp);
260664562Sgshapiro			e->e_msgsize++;
260764562Sgshapiro		}
260864562Sgshapiro		return 0;
260964562Sgshapiro	}
261064562Sgshapiro
261164562Sgshapiro	for (i = 0; i < rlen; i++)
261264562Sgshapiro	{
261364562Sgshapiro		/* Buffered char from last chunk */
261464562Sgshapiro		if (i == 0 && prevchar == '\r')
261564562Sgshapiro		{
261664562Sgshapiro			/* Not CRLF, output prevchar */
261764562Sgshapiro			if (response[i] != '\n')
261864562Sgshapiro			{
261964562Sgshapiro				(void) putc(prevchar, e->e_dfp);
262064562Sgshapiro				e->e_msgsize++;
262164562Sgshapiro			}
262264562Sgshapiro			prevchar = '\0';
262364562Sgshapiro		}
262464562Sgshapiro
262564562Sgshapiro		/* Turn CRLF into LF */
262664562Sgshapiro		if (response[i] == '\r')
262764562Sgshapiro		{
262864562Sgshapiro			/* check if at end of chunk */
262964562Sgshapiro			if (i + 1 < rlen)
263064562Sgshapiro			{
263164562Sgshapiro				/* If LF, strip CR */
263264562Sgshapiro				if (response[i + 1] == '\n')
263364562Sgshapiro					i++;
263464562Sgshapiro			}
263564562Sgshapiro			else
263664562Sgshapiro			{
263764562Sgshapiro				/* check next chunk */
263864562Sgshapiro				prevchar = '\r';
263964562Sgshapiro				continue;
264064562Sgshapiro			}
264164562Sgshapiro		}
264264562Sgshapiro		(void) putc(response[i], e->e_dfp);
264364562Sgshapiro		e->e_msgsize++;
264464562Sgshapiro	}
264564562Sgshapiro	return 0;
264664562Sgshapiro}
264764562Sgshapiro
264864562Sgshapiro/*
264964562Sgshapiro**  MTA callouts
265064562Sgshapiro*/
265164562Sgshapiro
265264562Sgshapiro/*
265364562Sgshapiro**  MILTER_INIT -- open and negotiate with all of the filters
265464562Sgshapiro**
265564562Sgshapiro**	Parameters:
265664562Sgshapiro**		e -- current envelope.
265764562Sgshapiro**		state -- return state from response.
265864562Sgshapiro**
265964562Sgshapiro**	Returns:
266064562Sgshapiro**		none
266164562Sgshapiro*/
266264562Sgshapiro
266364562Sgshapiro/* ARGSUSED */
266464562Sgshapirovoid
266564562Sgshapiromilter_init(e, state)
266664562Sgshapiro	ENVELOPE *e;
266764562Sgshapiro	char *state;
266864562Sgshapiro{
266964562Sgshapiro	int i;
267064562Sgshapiro
267164562Sgshapiro	if (tTd(64, 10))
267264562Sgshapiro		dprintf("milter_init\n");
267364562Sgshapiro
267464562Sgshapiro	*state = SMFIR_CONTINUE;
267564562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
267664562Sgshapiro	{
267764562Sgshapiro		struct milter *m = InputFilters[i];
267864562Sgshapiro
267964562Sgshapiro		m->mf_sock = milter_open(m, FALSE, e);
268064562Sgshapiro		if (m->mf_state == SMFS_ERROR)
268164562Sgshapiro		{
268264562Sgshapiro			MILTER_CHECK_ERROR(continue);
268364562Sgshapiro			break;
268464562Sgshapiro		}
268564562Sgshapiro
268664562Sgshapiro		if (m->mf_sock < 0 ||
268764562Sgshapiro		    milter_negotiate(m, e) < 0 ||
268864562Sgshapiro		    m->mf_state == SMFS_ERROR)
268964562Sgshapiro		{
269064562Sgshapiro			if (tTd(64, 5))
269164562Sgshapiro				dprintf("milter_init(%s): failed to %s\n",
269264562Sgshapiro					m->mf_name,
269364562Sgshapiro					m->mf_sock < 0 ? "open" : "negotiate");
269464562Sgshapiro
269564562Sgshapiro			/* if negotation failure, close socket */
269664562Sgshapiro			if (m->mf_sock >= 0)
269764562Sgshapiro			{
269864562Sgshapiro				(void) close(m->mf_sock);
269964562Sgshapiro				m->mf_sock = -1;
270064562Sgshapiro			}
270164562Sgshapiro			milter_error(m);
270264562Sgshapiro			if (m->mf_state == SMFS_ERROR)
270364562Sgshapiro			{
270464562Sgshapiro				MILTER_CHECK_ERROR(continue);
270564562Sgshapiro				break;
270664562Sgshapiro			}
270764562Sgshapiro		}
270864562Sgshapiro	}
270964562Sgshapiro
271064562Sgshapiro	/*
271164562Sgshapiro	**  If something temp/perm failed with one of the filters,
271264562Sgshapiro	**  we won't be using any of them, so clear any existing
271364562Sgshapiro	**  connections.
271464562Sgshapiro	*/
271564562Sgshapiro
271664562Sgshapiro	if (*state != SMFIR_CONTINUE)
271764562Sgshapiro		milter_quit(e);
271864562Sgshapiro}
271964562Sgshapiro/*
272064562Sgshapiro**  MILTER_CONNECT -- send connection info to milter filters
272164562Sgshapiro**
272264562Sgshapiro**	Parameters:
272364562Sgshapiro**		hostname -- hostname of remote machine.
272464562Sgshapiro**		addr -- address of remote machine.
272564562Sgshapiro**		e -- current envelope.
272664562Sgshapiro**		state -- return state from response.
272764562Sgshapiro**
272864562Sgshapiro**	Returns:
272964562Sgshapiro**		response string (may be NULL)
273064562Sgshapiro*/
273164562Sgshapiro
273264562Sgshapirochar *
273364562Sgshapiromilter_connect(hostname, addr, e, state)
273464562Sgshapiro	char *hostname;
273564562Sgshapiro	SOCKADDR addr;
273664562Sgshapiro	ENVELOPE *e;
273764562Sgshapiro	char *state;
273864562Sgshapiro{
273964562Sgshapiro	char family;
274064562Sgshapiro	u_short port;
274164562Sgshapiro	char *buf, *bp;
274264562Sgshapiro	char *response;
274364562Sgshapiro	char *sockinfo = NULL;
274464562Sgshapiro	ssize_t s;
274564562Sgshapiro# if NETINET6
274664562Sgshapiro	char buf6[INET6_ADDRSTRLEN];
274764562Sgshapiro# endif /* NETINET6 */
274864562Sgshapiro
274964562Sgshapiro	if (tTd(64, 10))
275064562Sgshapiro		dprintf("milter_connect(%s)\n", hostname);
275164562Sgshapiro
275264562Sgshapiro	/* gather data */
275364562Sgshapiro	switch (addr.sa.sa_family)
275464562Sgshapiro	{
275564562Sgshapiro# if NETUNIX
275664562Sgshapiro	  case AF_UNIX:
275764562Sgshapiro		family = SMFIA_UNIX;
275864562Sgshapiro		port = htons(0);
275964562Sgshapiro		sockinfo = addr.sunix.sun_path;
276064562Sgshapiro		break;
276164562Sgshapiro# endif /* NETUNIX */
276264562Sgshapiro
276364562Sgshapiro# if NETINET
276464562Sgshapiro	  case AF_INET:
276564562Sgshapiro		family = SMFIA_INET;
276664562Sgshapiro		port = htons(addr.sin.sin_port);
276764562Sgshapiro		sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
276864562Sgshapiro		break;
276964562Sgshapiro# endif /* NETINET */
277064562Sgshapiro
277164562Sgshapiro# if NETINET6
277264562Sgshapiro	  case AF_INET6:
277364562Sgshapiro		family = SMFIA_INET6;
277464562Sgshapiro		port = htons(addr.sin6.sin6_port);
277564562Sgshapiro		sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
277664562Sgshapiro				       sizeof buf6);
277764562Sgshapiro		if (sockinfo == NULL)
277864562Sgshapiro			sockinfo = "";
277964562Sgshapiro		break;
278064562Sgshapiro# endif /* NETINET6 */
278164562Sgshapiro
278264562Sgshapiro	  default:
278364562Sgshapiro		family = SMFIA_UNKNOWN;
278464562Sgshapiro		break;
278564562Sgshapiro	}
278664562Sgshapiro
278764562Sgshapiro	s = strlen(hostname) + 1 + sizeof(family);
278864562Sgshapiro	if (family != SMFIA_UNKNOWN)
278964562Sgshapiro		s += sizeof(port) + strlen(sockinfo) + 1;
279064562Sgshapiro
279164562Sgshapiro	buf = (char *)xalloc(s);
279264562Sgshapiro	bp = buf;
279364562Sgshapiro
279464562Sgshapiro	/* put together data */
279564562Sgshapiro	(void) memcpy(bp, hostname, strlen(hostname));
279664562Sgshapiro	bp += strlen(hostname);
279764562Sgshapiro	*bp++ = '\0';
279864562Sgshapiro	(void) memcpy(bp, &family, sizeof family);
279964562Sgshapiro	bp += sizeof family;
280064562Sgshapiro	if (family != SMFIA_UNKNOWN)
280164562Sgshapiro	{
280264562Sgshapiro		(void) memcpy(bp, &port, sizeof port);
280364562Sgshapiro		bp += sizeof port;
280464562Sgshapiro
280564562Sgshapiro		/* include trailing '\0' */
280664562Sgshapiro		(void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
280764562Sgshapiro	}
280864562Sgshapiro
280964562Sgshapiro	response = milter_command(SMFIC_CONNECT, buf, s,
281064562Sgshapiro				  MilterConnectMacros, e, state);
281164562Sgshapiro	free(buf);
281264562Sgshapiro
281364562Sgshapiro	/*
281464562Sgshapiro	**  If this message connection is done for,
281564562Sgshapiro	**  close the filters.
281664562Sgshapiro	*/
281764562Sgshapiro
281864562Sgshapiro	if (*state != SMFIR_CONTINUE)
281964562Sgshapiro		milter_quit(e);
282064562Sgshapiro	else
282164562Sgshapiro		milter_per_connection_check(e);
282264562Sgshapiro
282364562Sgshapiro	/*
282464562Sgshapiro	**  SMFIR_REPLYCODE can't work with connect due to
282564562Sgshapiro	**  the requirements of SMTP.  Therefore, ignore the
282664562Sgshapiro	**  reply code text but keep the state it would reflect.
282764562Sgshapiro	*/
282864562Sgshapiro
282964562Sgshapiro	if (*state == SMFIR_REPLYCODE)
283064562Sgshapiro	{
283164562Sgshapiro		if (response != NULL &&
283264562Sgshapiro		    *response == '4')
283364562Sgshapiro			*state = SMFIR_TEMPFAIL;
283464562Sgshapiro		else
283564562Sgshapiro			*state = SMFIR_REJECT;
283664562Sgshapiro		if (response != NULL)
283764562Sgshapiro		{
283864562Sgshapiro			free(response);
283964562Sgshapiro			response = NULL;
284064562Sgshapiro		}
284164562Sgshapiro	}
284264562Sgshapiro	return response;
284364562Sgshapiro}
284464562Sgshapiro/*
284564562Sgshapiro**  MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
284664562Sgshapiro**
284764562Sgshapiro**	Parameters:
284864562Sgshapiro**		helo -- argument to SMTP HELO/EHLO command.
284964562Sgshapiro**		e -- current envelope.
285064562Sgshapiro**		state -- return state from response.
285164562Sgshapiro**
285264562Sgshapiro**	Returns:
285364562Sgshapiro**		response string (may be NULL)
285464562Sgshapiro*/
285564562Sgshapiro
285664562Sgshapirochar *
285764562Sgshapiromilter_helo(helo, e, state)
285864562Sgshapiro	char *helo;
285964562Sgshapiro	ENVELOPE *e;
286064562Sgshapiro	char *state;
286164562Sgshapiro{
286264562Sgshapiro	char *response;
286364562Sgshapiro
286464562Sgshapiro	if (tTd(64, 10))
286564562Sgshapiro		dprintf("milter_helo(%s)\n", helo);
286664562Sgshapiro
286764562Sgshapiro	response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
286864562Sgshapiro				  MilterHeloMacros, e, state);
286964562Sgshapiro	milter_per_connection_check(e);
287064562Sgshapiro	return response;
287164562Sgshapiro}
287264562Sgshapiro/*
287364562Sgshapiro**  MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
287464562Sgshapiro**
287564562Sgshapiro**	Parameters:
287664562Sgshapiro**		args -- SMTP MAIL command args (args[0] == sender).
287764562Sgshapiro**		e -- current envelope.
287864562Sgshapiro**		state -- return state from response.
287964562Sgshapiro**
288064562Sgshapiro**	Returns:
288164562Sgshapiro**		response string (may be NULL)
288264562Sgshapiro*/
288364562Sgshapiro
288464562Sgshapirochar *
288564562Sgshapiromilter_envfrom(args, e, state)
288664562Sgshapiro	char **args;
288764562Sgshapiro	ENVELOPE *e;
288864562Sgshapiro	char *state;
288964562Sgshapiro{
289064562Sgshapiro	int i;
289164562Sgshapiro	char *buf, *bp;
289264562Sgshapiro	char *response;
289364562Sgshapiro	ssize_t s;
289464562Sgshapiro
289564562Sgshapiro	if (tTd(64, 10))
289664562Sgshapiro	{
289764562Sgshapiro		dprintf("milter_envfrom:");
289864562Sgshapiro		for (i = 0; args[i] != NULL; i++)
289964562Sgshapiro			dprintf(" %s", args[i]);
290064562Sgshapiro		dprintf("\n");
290164562Sgshapiro	}
290264562Sgshapiro
290364562Sgshapiro	/* sanity check */
290464562Sgshapiro	if (args[0] == NULL)
290564562Sgshapiro	{
290664562Sgshapiro		*state = SMFIR_REJECT;
290764562Sgshapiro		return NULL;
290864562Sgshapiro	}
290964562Sgshapiro
291064562Sgshapiro	/* new message, so ... */
291164562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
291264562Sgshapiro	{
291364562Sgshapiro		struct milter *m = InputFilters[i];
291464562Sgshapiro
291564562Sgshapiro		switch (m->mf_state)
291664562Sgshapiro		{
291764562Sgshapiro		  case SMFS_INMSG:
291864562Sgshapiro			/* abort in message filters */
291964562Sgshapiro			milter_abort_filter(m, e);
292064562Sgshapiro			/* FALLTHROUGH */
292164562Sgshapiro
292264562Sgshapiro		  case SMFS_DONE:
292364562Sgshapiro			/* reset done filters */
292464562Sgshapiro			m->mf_state = SMFS_OPEN;
292564562Sgshapiro			break;
292664562Sgshapiro		}
292764562Sgshapiro	}
292864562Sgshapiro
292964562Sgshapiro	/* put together data */
293064562Sgshapiro	s = 0;
293164562Sgshapiro	for (i = 0; args[i] != NULL; i++)
293264562Sgshapiro		s += strlen(args[i]) + 1;
293364562Sgshapiro	buf = (char *)xalloc(s);
293464562Sgshapiro	bp = buf;
293564562Sgshapiro	for (i = 0; args[i] != NULL; i++)
293664562Sgshapiro	{
293764562Sgshapiro		(void) strlcpy(bp, args[i], s - (bp - buf));
293864562Sgshapiro		bp += strlen(bp) + 1;
293964562Sgshapiro	}
294064562Sgshapiro
294164562Sgshapiro	/* send it over */
294264562Sgshapiro	response = milter_command(SMFIC_MAIL, buf, s,
294364562Sgshapiro				  MilterEnvFromMacros, e, state);
294464562Sgshapiro	free(buf);
294564562Sgshapiro
294664562Sgshapiro	/*
294764562Sgshapiro	**  If filter rejects/discards a per message command,
294864562Sgshapiro	**  abort the other filters since we are done with the
294964562Sgshapiro	**  current message.
295064562Sgshapiro	*/
295164562Sgshapiro
295264562Sgshapiro	MILTER_CHECK_DONE_MSG();
295364562Sgshapiro	return response;
295464562Sgshapiro}
295564562Sgshapiro/*
295664562Sgshapiro**  MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
295764562Sgshapiro**
295864562Sgshapiro**	Parameters:
295964562Sgshapiro**		args -- SMTP MAIL command args (args[0] == recipient).
296064562Sgshapiro**		e -- current envelope.
296164562Sgshapiro**		state -- return state from response.
296264562Sgshapiro**
296364562Sgshapiro**	Returns:
296464562Sgshapiro**		response string (may be NULL)
296564562Sgshapiro*/
296664562Sgshapiro
296764562Sgshapirochar *
296864562Sgshapiromilter_envrcpt(args, e, state)
296964562Sgshapiro	char **args;
297064562Sgshapiro	ENVELOPE *e;
297164562Sgshapiro	char *state;
297264562Sgshapiro{
297364562Sgshapiro	int i;
297464562Sgshapiro	char *buf, *bp;
297564562Sgshapiro	char *response;
297664562Sgshapiro	ssize_t s;
297764562Sgshapiro
297864562Sgshapiro	if (tTd(64, 10))
297964562Sgshapiro	{
298064562Sgshapiro		dprintf("milter_envrcpt:");
298164562Sgshapiro		for (i = 0; args[i] != NULL; i++)
298264562Sgshapiro			dprintf(" %s", args[i]);
298364562Sgshapiro		dprintf("\n");
298464562Sgshapiro	}
298564562Sgshapiro
298664562Sgshapiro	/* sanity check */
298764562Sgshapiro	if (args[0] == NULL)
298864562Sgshapiro	{
298964562Sgshapiro		*state = SMFIR_REJECT;
299064562Sgshapiro		return NULL;
299164562Sgshapiro	}
299264562Sgshapiro
299364562Sgshapiro	/* put together data */
299464562Sgshapiro	s = 0;
299564562Sgshapiro	for (i = 0; args[i] != NULL; i++)
299664562Sgshapiro		s += strlen(args[i]) + 1;
299764562Sgshapiro	buf = (char *)xalloc(s);
299864562Sgshapiro	bp = buf;
299964562Sgshapiro	for (i = 0; args[i] != NULL; i++)
300064562Sgshapiro	{
300164562Sgshapiro		(void) strlcpy(bp, args[i], s - (bp - buf));
300264562Sgshapiro		bp += strlen(bp) + 1;
300364562Sgshapiro	}
300464562Sgshapiro
300564562Sgshapiro	/* send it over */
300664562Sgshapiro	response = milter_command(SMFIC_RCPT, buf, s,
300764562Sgshapiro				  MilterEnvRcptMacros, e, state);
300864562Sgshapiro	free(buf);
300964562Sgshapiro	return response;
301064562Sgshapiro}
301164562Sgshapiro/*
301264562Sgshapiro**  MILTER_DATA -- send message headers/body and gather final message results
301364562Sgshapiro**
301464562Sgshapiro**	Parameters:
301564562Sgshapiro**		e -- current envelope.
301664562Sgshapiro**		state -- return state from response.
301764562Sgshapiro**
301864562Sgshapiro**	Returns:
301964562Sgshapiro**		response string (may be NULL)
302064562Sgshapiro**
302164562Sgshapiro**	Side effects:
302264562Sgshapiro**		- Uses e->e_dfp for access to the body
302364562Sgshapiro**		- Can call the various milter action routines to
302464562Sgshapiro**		  modify the envelope or message.
302564562Sgshapiro*/
302664562Sgshapiro
302764562Sgshapiro# define MILTER_CHECK_RESULTS() \
302864562Sgshapiro	if (*state == SMFIR_ACCEPT || \
302964562Sgshapiro	    m->mf_state == SMFS_DONE || \
303064562Sgshapiro	    m->mf_state == SMFS_ERROR) \
303164562Sgshapiro	{ \
303264562Sgshapiro		if (m->mf_state != SMFS_ERROR) \
303364562Sgshapiro			m->mf_state = SMFS_DONE; \
303464562Sgshapiro		continue;	/* to next filter */ \
303564562Sgshapiro	} \
303664562Sgshapiro	if (*state != SMFIR_CONTINUE) \
303764562Sgshapiro	{ \
303864562Sgshapiro		m->mf_state = SMFS_DONE; \
303964562Sgshapiro		goto finishup; \
304064562Sgshapiro	}
304164562Sgshapiro
304264562Sgshapirochar *
304364562Sgshapiromilter_data(e, state)
304464562Sgshapiro	ENVELOPE *e;
304564562Sgshapiro	char *state;
304664562Sgshapiro{
304764562Sgshapiro	bool replbody = FALSE;		/* milter_replbody() called? */
304864562Sgshapiro	bool replfailed = FALSE;	/* milter_replbody() failed? */
304964562Sgshapiro	bool rewind = FALSE;		/* rewind df file? */
305064562Sgshapiro	bool dfopen = FALSE;		/* df open for writing? */
305164562Sgshapiro	bool newfilter;			/* reset on each new filter */
305264562Sgshapiro	char rcmd;
305364562Sgshapiro	int i;
305464562Sgshapiro	int save_errno;
305564562Sgshapiro	char *response = NULL;
305664562Sgshapiro	time_t eomsent;
305764562Sgshapiro	ssize_t rlen;
305864562Sgshapiro
305964562Sgshapiro	if (tTd(64, 10))
306064562Sgshapiro		dprintf("milter_data\n");
306164562Sgshapiro
306264562Sgshapiro	*state = SMFIR_CONTINUE;
306364562Sgshapiro
306464562Sgshapiro	/*
306564562Sgshapiro	**  XXX: Should actually send body chunks to each filter
306664562Sgshapiro	**  a chunk at a time instead of sending the whole body to
306764562Sgshapiro	**  each filter in turn.  However, only if the filters don't
306864562Sgshapiro	**  change the body.
306964562Sgshapiro	*/
307064562Sgshapiro
307164562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
307264562Sgshapiro	{
307364562Sgshapiro		struct milter *m = InputFilters[i];
307464562Sgshapiro
307564562Sgshapiro		if (*state != SMFIR_CONTINUE &&
307664562Sgshapiro		    *state != SMFIR_ACCEPT)
307764562Sgshapiro		{
307864562Sgshapiro			/*
307964562Sgshapiro			**  A previous filter has dealt with the message,
308064562Sgshapiro			**  safe to stop processing the filters.
308164562Sgshapiro			*/
308264562Sgshapiro
308364562Sgshapiro			break;
308464562Sgshapiro		}
308564562Sgshapiro
308664562Sgshapiro		/* Now reset state for later evaluation */
308764562Sgshapiro		*state = SMFIR_CONTINUE;
308864562Sgshapiro		newfilter = TRUE;
308964562Sgshapiro
309064562Sgshapiro		/* sanity checks */
309164562Sgshapiro		if (m->mf_sock < 0 ||
309264562Sgshapiro		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
309364562Sgshapiro			continue;
309464562Sgshapiro
309564562Sgshapiro		m->mf_state = SMFS_INMSG;
309664562Sgshapiro
309764562Sgshapiro		/* check if filter wants the headers */
309864562Sgshapiro		if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
309964562Sgshapiro		{
310064562Sgshapiro			response = milter_headers(m, e, state);
310164562Sgshapiro			MILTER_CHECK_RESULTS();
310264562Sgshapiro		}
310364562Sgshapiro
310464562Sgshapiro		/* check if filter wants EOH */
310564562Sgshapiro		if (!bitset(SMFIP_NOEOH, m->mf_pflags))
310664562Sgshapiro		{
310764562Sgshapiro			if (tTd(64, 10))
310864562Sgshapiro				dprintf("milter_data: eoh\n");
310964562Sgshapiro
311064562Sgshapiro			/* send it over */
311164562Sgshapiro			response = milter_send_command(m, SMFIC_EOH, NULL, 0,
311264562Sgshapiro						       e, state);
311364562Sgshapiro			MILTER_CHECK_RESULTS();
311464562Sgshapiro		}
311564562Sgshapiro
311664562Sgshapiro		/* check if filter wants the body */
311764562Sgshapiro		if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
311864562Sgshapiro		    e->e_dfp != NULL)
311964562Sgshapiro		{
312064562Sgshapiro			rewind = TRUE;
312164562Sgshapiro			response = milter_body(m, e, state);
312264562Sgshapiro			MILTER_CHECK_RESULTS();
312364562Sgshapiro		}
312464562Sgshapiro
312564562Sgshapiro		/* send the final body chunk */
312664562Sgshapiro		(void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
312764562Sgshapiro				    m->mf_timeout[SMFTO_WRITE], e);
312864562Sgshapiro
312964562Sgshapiro		/* Get time EOM sent for timeout */
313064562Sgshapiro		eomsent = curtime();
313164562Sgshapiro
313264562Sgshapiro		/* deal with the possibility of multiple responses */
313364562Sgshapiro		while (*state == SMFIR_CONTINUE)
313464562Sgshapiro		{
313564562Sgshapiro			/* Check total timeout from EOM to final ACK/NAK */
313664562Sgshapiro			if (m->mf_timeout[SMFTO_EOM] > 0 &&
313764562Sgshapiro			    curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
313864562Sgshapiro			{
313964562Sgshapiro				if (tTd(64, 5))
314064562Sgshapiro					dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
314164562Sgshapiro						m->mf_name);
314264562Sgshapiro				if (LogLevel > 0)
314364562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
314464562Sgshapiro						  "milter_data(%s): EOM ACK/NAK timeout\n",
314564562Sgshapiro						  m->mf_name);
314664562Sgshapiro				milter_error(m);
314764562Sgshapiro				MILTER_CHECK_ERROR(continue);
314864562Sgshapiro				break;
314964562Sgshapiro			}
315064562Sgshapiro
315164562Sgshapiro			response = milter_read(m, &rcmd, &rlen,
315264562Sgshapiro					       m->mf_timeout[SMFTO_READ], e);
315364562Sgshapiro			if (m->mf_state == SMFS_ERROR)
315464562Sgshapiro				break;
315564562Sgshapiro
315664562Sgshapiro			if (tTd(64, 10))
315764562Sgshapiro				dprintf("milter_data(%s): state %c\n",
315864562Sgshapiro					m->mf_name, (char) rcmd);
315964562Sgshapiro
316064562Sgshapiro			switch (rcmd)
316164562Sgshapiro			{
316264562Sgshapiro			  case SMFIR_REPLYCODE:
316364562Sgshapiro				MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
316464562Sgshapiro				*state = rcmd;
316564562Sgshapiro				m->mf_state = SMFS_DONE;
316664562Sgshapiro				break;
316764562Sgshapiro
316864562Sgshapiro			  case SMFIR_REJECT:
316964562Sgshapiro			  case SMFIR_DISCARD:
317064562Sgshapiro			  case SMFIR_TEMPFAIL:
317164562Sgshapiro				*state = rcmd;
317264562Sgshapiro				m->mf_state = SMFS_DONE;
317364562Sgshapiro				break;
317464562Sgshapiro
317564562Sgshapiro			  case SMFIR_CONTINUE:
317664562Sgshapiro			  case SMFIR_ACCEPT:
317764562Sgshapiro				/* this filter is done with message */
317864562Sgshapiro				if (replfailed)
317964562Sgshapiro					*state = SMFIR_TEMPFAIL;
318064562Sgshapiro				else
318164562Sgshapiro					*state = SMFIR_ACCEPT;
318264562Sgshapiro				m->mf_state = SMFS_DONE;
318364562Sgshapiro				break;
318464562Sgshapiro
318564562Sgshapiro			  case SMFIR_PROGRESS:
318664562Sgshapiro				break;
318764562Sgshapiro
318864562Sgshapiro			  case SMFIR_ADDHEADER:
318964562Sgshapiro				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
319064562Sgshapiro				{
319164562Sgshapiro					if (LogLevel > 9)
319264562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
319364562Sgshapiro							  "milter_data(%s): lied about adding headers, honoring request anyway",
319464562Sgshapiro							  m->mf_name);
319564562Sgshapiro				}
319664562Sgshapiro				milter_addheader(response, rlen, e);
319764562Sgshapiro				break;
319864562Sgshapiro
319964562Sgshapiro			  case SMFIR_CHGHEADER:
320064562Sgshapiro				if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
320164562Sgshapiro				{
320264562Sgshapiro					if (LogLevel > 9)
320364562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
320464562Sgshapiro							  "milter_data(%s): lied about changing headers, honoring request anyway",
320564562Sgshapiro							  m->mf_name);
320664562Sgshapiro				}
320764562Sgshapiro				milter_changeheader(response, rlen, e);
320864562Sgshapiro				break;
320964562Sgshapiro
321064562Sgshapiro			  case SMFIR_ADDRCPT:
321164562Sgshapiro				if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
321264562Sgshapiro				{
321364562Sgshapiro					if (LogLevel > 9)
321464562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
321564562Sgshapiro							  "milter_data(%s) lied about adding recipients, honoring request anyway",
321664562Sgshapiro							  m->mf_name);
321764562Sgshapiro				}
321864562Sgshapiro				milter_addrcpt(response, rlen, e);
321964562Sgshapiro				break;
322064562Sgshapiro
322164562Sgshapiro			  case SMFIR_DELRCPT:
322264562Sgshapiro				if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
322364562Sgshapiro				{
322464562Sgshapiro					if (LogLevel > 9)
322564562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
322664562Sgshapiro							  "milter_data(%s): lied about removing recipients, honoring request anyway",
322764562Sgshapiro							  m->mf_name);
322864562Sgshapiro				}
322964562Sgshapiro				milter_delrcpt(response, rlen, e);
323064562Sgshapiro				break;
323164562Sgshapiro
323264562Sgshapiro			  case SMFIR_REPLBODY:
323364562Sgshapiro				if (!bitset(SMFIF_MODBODY, m->mf_fflags))
323464562Sgshapiro				{
323564562Sgshapiro					if (LogLevel > 0)
323664562Sgshapiro						sm_syslog(LOG_ERR, e->e_id,
323764562Sgshapiro							  "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
323864562Sgshapiro							  m->mf_name);
323964562Sgshapiro					replfailed = TRUE;
324064562Sgshapiro					break;
324164562Sgshapiro				}
324264562Sgshapiro
324364562Sgshapiro				/* already failed in attempt */
324464562Sgshapiro				if (replfailed)
324564562Sgshapiro					break;
324664562Sgshapiro
324764562Sgshapiro				if (!dfopen)
324864562Sgshapiro				{
324964562Sgshapiro					if (milter_reopen_df(e) < 0)
325064562Sgshapiro					{
325164562Sgshapiro						replfailed = TRUE;
325264562Sgshapiro						break;
325364562Sgshapiro					}
325464562Sgshapiro					dfopen = TRUE;
325564562Sgshapiro					rewind = TRUE;
325664562Sgshapiro				}
325764562Sgshapiro
325864562Sgshapiro				if (milter_replbody(response, rlen,
325964562Sgshapiro						    newfilter, e) < 0)
326064562Sgshapiro					replfailed = TRUE;
326164562Sgshapiro				newfilter = FALSE;
326264562Sgshapiro				replbody = TRUE;
326364562Sgshapiro				break;
326464562Sgshapiro
326564562Sgshapiro			  default:
326664562Sgshapiro				/* Invalid response to command */
326764562Sgshapiro				if (LogLevel > 0)
326864562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
326964562Sgshapiro						  "milter_data(%s): returned bogus response %c",
327064562Sgshapiro						  m->mf_name, rcmd);
327164562Sgshapiro				milter_error(m);
327264562Sgshapiro				break;
327364562Sgshapiro			}
327464562Sgshapiro			if (rcmd != SMFIR_REPLYCODE &&
327564562Sgshapiro			    response != NULL)
327664562Sgshapiro			{
327764562Sgshapiro				free(response);
327864562Sgshapiro				response = NULL;
327964562Sgshapiro			}
328064562Sgshapiro
328164562Sgshapiro			if (m->mf_state == SMFS_ERROR)
328264562Sgshapiro				break;
328364562Sgshapiro		}
328464562Sgshapiro
328564562Sgshapiro		if (replbody && !replfailed)
328664562Sgshapiro		{
328764562Sgshapiro			/* flush possible buffered character */
328864562Sgshapiro			milter_replbody(NULL, 0, !replbody, e);
328964562Sgshapiro			replbody = FALSE;
329064562Sgshapiro		}
329164562Sgshapiro
329264562Sgshapiro		if (m->mf_state == SMFS_ERROR)
329364562Sgshapiro		{
329464562Sgshapiro			MILTER_CHECK_ERROR(continue);
329564562Sgshapiro			goto finishup;
329664562Sgshapiro		}
329764562Sgshapiro	}
329864562Sgshapiro
329964562Sgshapirofinishup:
330064562Sgshapiro	/* leave things in the expected state if we touched it */
330164562Sgshapiro	if (replfailed)
330264562Sgshapiro	{
330364562Sgshapiro		if (*state == SMFIR_CONTINUE ||
330464562Sgshapiro		    *state == SMFIR_ACCEPT)
330564562Sgshapiro		{
330664562Sgshapiro			*state = SMFIR_TEMPFAIL;
330764562Sgshapiro			if (response != NULL)
330864562Sgshapiro			{
330964562Sgshapiro				free(response);
331064562Sgshapiro				response = NULL;
331164562Sgshapiro			}
331264562Sgshapiro		}
331364562Sgshapiro
331464562Sgshapiro		if (dfopen)
331564562Sgshapiro		{
331664562Sgshapiro			(void) fclose(e->e_dfp);
331764562Sgshapiro			e->e_dfp = NULL;
331864562Sgshapiro			e->e_flags &= ~EF_HAS_DF;
331964562Sgshapiro			dfopen = FALSE;
332064562Sgshapiro		}
332164562Sgshapiro		rewind = FALSE;
332264562Sgshapiro	}
332364562Sgshapiro
332464562Sgshapiro	if ((dfopen && milter_reset_df(e) < 0) ||
332564562Sgshapiro	    (rewind && bfrewind(e->e_dfp) < 0))
332664562Sgshapiro	{
332764562Sgshapiro		save_errno = errno;
332864562Sgshapiro		ExitStat = EX_IOERR;
332964562Sgshapiro
333064562Sgshapiro		/*
333164562Sgshapiro		**  If filter told us to keep message but we had
333264562Sgshapiro		**  an error, we can't really keep it, tempfail it.
333364562Sgshapiro		*/
333464562Sgshapiro
333564562Sgshapiro		if (*state == SMFIR_CONTINUE ||
333664562Sgshapiro		    *state == SMFIR_ACCEPT)
333764562Sgshapiro		{
333864562Sgshapiro			*state = SMFIR_TEMPFAIL;
333964562Sgshapiro			if (response != NULL)
334064562Sgshapiro			{
334164562Sgshapiro				free(response);
334264562Sgshapiro				response = NULL;
334364562Sgshapiro			}
334464562Sgshapiro		}
334564562Sgshapiro
334664562Sgshapiro		errno = save_errno;
334764562Sgshapiro		syserr("milter_data: %s/df%s: read error",
334864562Sgshapiro		       qid_printqueue(e->e_queuedir), e->e_id);
334964562Sgshapiro	}
335064562Sgshapiro	MILTER_CHECK_DONE_MSG();
335164562Sgshapiro	return response;
335264562Sgshapiro}
335364562Sgshapiro/*
335464562Sgshapiro**  MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
335564562Sgshapiro**
335664562Sgshapiro**	Parameters:
335764562Sgshapiro**		e -- current envelope.
335864562Sgshapiro**
335964562Sgshapiro**	Returns:
336064562Sgshapiro**		none
336164562Sgshapiro*/
336264562Sgshapiro
336364562Sgshapirovoid
336464562Sgshapiromilter_quit(e)
336564562Sgshapiro	ENVELOPE *e;
336664562Sgshapiro{
336764562Sgshapiro	int i;
336864562Sgshapiro
336964562Sgshapiro	if (tTd(64, 10))
337064562Sgshapiro		dprintf("milter_quit\n");
337164562Sgshapiro
337264562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
337364562Sgshapiro		milter_quit_filter(InputFilters[i], e);
337464562Sgshapiro}
337564562Sgshapiro/*
337664562Sgshapiro**  MILTER_ABORT -- informs the filter(s) that we are aborting current message
337764562Sgshapiro**
337864562Sgshapiro**	Parameters:
337964562Sgshapiro**		e -- current envelope.
338064562Sgshapiro**
338164562Sgshapiro**	Returns:
338264562Sgshapiro**		none
338364562Sgshapiro*/
338464562Sgshapiro
338564562Sgshapirovoid
338664562Sgshapiromilter_abort(e)
338764562Sgshapiro	ENVELOPE *e;
338864562Sgshapiro{
338964562Sgshapiro	int i;
339064562Sgshapiro
339164562Sgshapiro	if (tTd(64, 10))
339264562Sgshapiro		dprintf("milter_abort\n");
339364562Sgshapiro
339464562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
339564562Sgshapiro	{
339664562Sgshapiro		struct milter *m = InputFilters[i];
339764562Sgshapiro
339864562Sgshapiro		/* sanity checks */
339964562Sgshapiro		if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
340064562Sgshapiro			continue;
340164562Sgshapiro
340264562Sgshapiro		milter_abort_filter(m, e);
340364562Sgshapiro	}
340464562Sgshapiro}
340564562Sgshapiro#endif /* _FFR_MILTER */
3406