164562Sgshapiro/*
2261370Sgshapiro * Copyright (c) 1999-2009, 2012, 2013 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
464562Sgshapiro *
564562Sgshapiro * By using this file, you agree to the terms and conditions set
664562Sgshapiro * forth in the LICENSE file which can be found at the top level of
764562Sgshapiro * the sendmail distribution.
864562Sgshapiro *
964562Sgshapiro */
1064562Sgshapiro
1190792Sgshapiro#include <sendmail.h>
1264562Sgshapiro
13266711SgshapiroSM_RCSID("@(#)$Id: milter.c,v 8.281 2013-11-22 20:51:56 ca Exp $")
1464562Sgshapiro
1590792Sgshapiro#if MILTER
16168515Sgshapiro# include <sm/sendmail.h>
1790792Sgshapiro# include <libmilter/mfapi.h>
1890792Sgshapiro# include <libmilter/mfdef.h>
1990792Sgshapiro
2064562Sgshapiro# include <errno.h>
21157001Sgshapiro# include <sm/time.h>
22132943Sgshapiro# include <sys/uio.h>
2364562Sgshapiro
2464562Sgshapiro# if NETINET || NETINET6
2564562Sgshapiro#  include <arpa/inet.h>
26168515Sgshapiro#  if MILTER_NO_NAGLE
27132943Sgshapiro#   include <netinet/tcp.h>
28168515Sgshapiro#  endif /* MILTER_NO_NAGLE */
2964562Sgshapiro# endif /* NETINET || NETINET6 */
3064562Sgshapiro
3190792Sgshapiro# include <sm/fdset.h>
3264562Sgshapiro
33141858Sgshapirostatic void	milter_connect_timeout __P((int));
3490792Sgshapirostatic void	milter_error __P((struct milter *, ENVELOPE *));
3564562Sgshapirostatic int	milter_open __P((struct milter *, bool, ENVELOPE *));
3664562Sgshapirostatic void	milter_parse_timeouts __P((char *, struct milter *));
37168515Sgshapirostatic char	*milter_sysread __P((struct milter *, char *, ssize_t, time_t,
38168515Sgshapiro			ENVELOPE *, const char *));
39168515Sgshapirostatic char	*milter_read __P((struct milter *, char *, ssize_t *, time_t,
40168515Sgshapiro			ENVELOPE *, const char *));
41168515Sgshapirostatic char	*milter_write __P((struct milter *, int, char *, ssize_t,
42168515Sgshapiro			time_t, ENVELOPE *, const char *));
43168515Sgshapirostatic char	*milter_send_command __P((struct milter *, int, void *,
44168515Sgshapiro			ssize_t, ENVELOPE *, char *, const char *));
45244928Sgshapirostatic char	*milter_command __P((int, void *, ssize_t, int,
46168515Sgshapiro			ENVELOPE *, char *, const char *, bool));
47168515Sgshapirostatic char	*milter_body __P((struct milter *, ENVELOPE *, char *));
48168515Sgshapirostatic int	milter_reopen_df __P((ENVELOPE *));
49168515Sgshapirostatic int	milter_reset_df __P((ENVELOPE *));
50168515Sgshapirostatic void	milter_quit_filter __P((struct milter *, ENVELOPE *));
51168515Sgshapirostatic void	milter_abort_filter __P((struct milter *, ENVELOPE *));
52168515Sgshapirostatic void	milter_send_macros __P((struct milter *, char **, int,
53168515Sgshapiro			ENVELOPE *));
54173340Sgshapirostatic int	milter_negotiate __P((struct milter *, ENVELOPE *,
55173340Sgshapiro			milters_T *));
56168515Sgshapirostatic void	milter_per_connection_check __P((ENVELOPE *));
57168515Sgshapirostatic char	*milter_headers __P((struct milter *, ENVELOPE *, char *));
58168515Sgshapirostatic void	milter_addheader __P((struct milter *, char *, ssize_t,
59168515Sgshapiro			ENVELOPE *));
60168515Sgshapirostatic void	milter_insheader __P((struct milter *, char *, ssize_t,
61168515Sgshapiro			ENVELOPE *));
62168515Sgshapirostatic void	milter_changeheader __P((struct milter *, char *, ssize_t,
63168515Sgshapiro			ENVELOPE *));
64168515Sgshapirostatic void	milter_chgfrom __P((char *, ssize_t, ENVELOPE *));
65168515Sgshapirostatic void	milter_addrcpt __P((char *, ssize_t, ENVELOPE *));
66168515Sgshapirostatic void	milter_addrcpt_par __P((char *, ssize_t, ENVELOPE *));
67168515Sgshapirostatic void	milter_delrcpt __P((char *, ssize_t, ENVELOPE *));
68168515Sgshapirostatic int	milter_replbody __P((char *, ssize_t, bool, ENVELOPE *));
69168515Sgshapirostatic int	milter_set_macros __P((char *, char **, char *, int));
7064562Sgshapiro
71168515Sgshapiro
72168515Sgshapiro/* milter states */
73168515Sgshapiro# define SMFS_CLOSED		'C'	/* closed for all further actions */
74168515Sgshapiro# define SMFS_OPEN		'O'	/* connected to remote milter filter */
75168515Sgshapiro# define SMFS_INMSG		'M'	/* currently servicing a message */
76168515Sgshapiro# define SMFS_DONE		'D'	/* done with current message */
77168515Sgshapiro# define SMFS_CLOSABLE		'Q'	/* done with current connection */
78168515Sgshapiro# define SMFS_ERROR		'E'	/* error state */
79168515Sgshapiro# define SMFS_READY		'R'	/* ready for action */
80168515Sgshapiro# define SMFS_SKIP		'S'	/* skip body */
81168515Sgshapiro
82244928Sgshapiro/*
83244928Sgshapiro**  MilterMacros contains the milter macros for each milter and each stage.
84244928Sgshapiro**  indices are (in order): stages, milter-index, macro
85244928Sgshapiro**  milter-index == 0: "global" macros (not for a specific milter).
86244928Sgshapiro*/
87244928Sgshapiro
88244928Sgshapirostatic char *MilterMacros[SMFIM_LAST + 1][MAXFILTERS + 1][MAXFILTERMACROS + 1];
89132943Sgshapirostatic size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE;
9064562Sgshapiro
9164562Sgshapiro# define MILTER_CHECK_DONE_MSG() \
9264562Sgshapiro	if (*state == SMFIR_REPLYCODE || \
9364562Sgshapiro	    *state == SMFIR_REJECT || \
9464562Sgshapiro	    *state == SMFIR_DISCARD || \
9564562Sgshapiro	    *state == SMFIR_TEMPFAIL) \
9664562Sgshapiro	{ \
9764562Sgshapiro		/* Abort the filters to let them know we are done with msg */ \
9864562Sgshapiro		milter_abort(e); \
9964562Sgshapiro	}
10064562Sgshapiro
101244928Sgshapiro/* set state in case of an error */
102244928Sgshapiro# define MILTER_SET_STATE	\
103244928Sgshapiro	if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
104244928Sgshapiro		*state = SMFIR_TEMPFAIL; \
105244928Sgshapiro	else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \
106244928Sgshapiro		*state = SMFIR_SHUTDOWN; \
107244928Sgshapiro	else if (bitnset(SMF_REJECT, m->mf_flags)) \
108244928Sgshapiro		*state = SMFIR_REJECT
109244928Sgshapiro
110244928Sgshapiro/* flow through code maybe using continue; don't wrap in do {} while */
111132943Sgshapiro# define MILTER_CHECK_ERROR(initial, action) \
112112810Sgshapiro	if (!initial && tTd(71, 100)) \
113102528Sgshapiro	{ \
114102528Sgshapiro		if (e->e_quarmsg == NULL) \
115102528Sgshapiro		{ \
116102528Sgshapiro			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
117102528Sgshapiro							 "filter failure"); \
118102528Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
119102528Sgshapiro				  e->e_quarmsg); \
120102528Sgshapiro		} \
121102528Sgshapiro	} \
122112810Sgshapiro	else if (tTd(71, 101)) \
123112810Sgshapiro	{ \
124112810Sgshapiro		if (e->e_quarmsg == NULL) \
125112810Sgshapiro		{ \
126112810Sgshapiro			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
127112810Sgshapiro							 "filter failure"); \
128112810Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
129112810Sgshapiro				  e->e_quarmsg); \
130112810Sgshapiro		} \
131112810Sgshapiro	} \
132244928Sgshapiro	else MILTER_SET_STATE;	\
133102528Sgshapiro	else \
134102528Sgshapiro		action;
13564562Sgshapiro
13664562Sgshapiro# define MILTER_CHECK_REPLYCODE(default) \
13764562Sgshapiro	if (response == NULL || \
13864562Sgshapiro	    strlen(response) + 1 != (size_t) rlen || \
13964562Sgshapiro	    rlen < 3 || \
14064562Sgshapiro	    (response[0] != '4' && response[0] != '5') || \
14164562Sgshapiro	    !isascii(response[1]) || !isdigit(response[1]) || \
14264562Sgshapiro	    !isascii(response[2]) || !isdigit(response[2])) \
14364562Sgshapiro	{ \
14464562Sgshapiro		if (response != NULL) \
14590792Sgshapiro			sm_free(response); /* XXX */ \
14664562Sgshapiro		response = newstr(default); \
14764562Sgshapiro	} \
14864562Sgshapiro	else \
14964562Sgshapiro	{ \
15064562Sgshapiro		char *ptr = response; \
15164562Sgshapiro \
15264562Sgshapiro		/* Check for unprotected %'s in the string */ \
15364562Sgshapiro		while (*ptr != '\0') \
15464562Sgshapiro		{ \
15564562Sgshapiro			if (*ptr == '%' && *++ptr != '%') \
15664562Sgshapiro			{ \
15790792Sgshapiro				sm_free(response); /* XXX */ \
15864562Sgshapiro				response = newstr(default); \
15964562Sgshapiro				break; \
16064562Sgshapiro			} \
16164562Sgshapiro			ptr++; \
16264562Sgshapiro		} \
16364562Sgshapiro	}
16464562Sgshapiro
16564562Sgshapiro# define MILTER_DF_ERROR(msg) \
16664562Sgshapiro{ \
16764562Sgshapiro	int save_errno = errno; \
16864562Sgshapiro \
16964562Sgshapiro	if (tTd(64, 5)) \
17064562Sgshapiro	{ \
17190792Sgshapiro		sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
17290792Sgshapiro		sm_dprintf("\n"); \
17364562Sgshapiro	} \
17490792Sgshapiro	if (MilterLogLevel > 0) \
17590792Sgshapiro		sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
17690792Sgshapiro	if (SuperSafe == SAFE_REALLY) \
17764562Sgshapiro	{ \
17864562Sgshapiro		if (e->e_dfp != NULL) \
17964562Sgshapiro		{ \
18090792Sgshapiro			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
18164562Sgshapiro			e->e_dfp = NULL; \
18264562Sgshapiro		} \
18364562Sgshapiro		e->e_flags &= ~EF_HAS_DF; \
18464562Sgshapiro	} \
18564562Sgshapiro	errno = save_errno; \
18664562Sgshapiro}
18764562Sgshapiro
18864562Sgshapiro/*
18964562Sgshapiro**  MILTER_TIMEOUT -- make sure socket is ready in time
19064562Sgshapiro**
19164562Sgshapiro**	Parameters:
19264562Sgshapiro**		routine -- routine name for debug/logging
19364562Sgshapiro**		secs -- number of seconds in timeout
19464562Sgshapiro**		write -- waiting to read or write?
19590792Sgshapiro**		started -- whether this is part of a previous sequence
19664562Sgshapiro**
19764562Sgshapiro**	Assumes 'm' is a milter structure for the current socket.
19864562Sgshapiro*/
19964562Sgshapiro
200168515Sgshapiro# define MILTER_TIMEOUT(routine, secs, write, started, function) \
20164562Sgshapiro{ \
20264562Sgshapiro	int ret; \
20364562Sgshapiro	int save_errno; \
20464562Sgshapiro	fd_set fds; \
20564562Sgshapiro	struct timeval tv; \
20664562Sgshapiro \
20790792Sgshapiro	if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
20864562Sgshapiro	{ \
20964562Sgshapiro		if (tTd(64, 5)) \
21090792Sgshapiro			sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
21190792Sgshapiro				   (routine), m->mf_name, m->mf_sock, \
21290792Sgshapiro				   SM_FD_SETSIZE); \
21390792Sgshapiro		if (MilterLogLevel > 0) \
21464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
21590792Sgshapiro				  "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
21690792Sgshapiro				  m->mf_name, (routine), m->mf_sock, \
21790792Sgshapiro				  SM_FD_SETSIZE); \
21890792Sgshapiro		milter_error(m, e); \
21964562Sgshapiro		return NULL; \
22064562Sgshapiro	} \
22164562Sgshapiro \
22294334Sgshapiro	do \
22394334Sgshapiro	{ \
22494334Sgshapiro		FD_ZERO(&fds); \
22594334Sgshapiro		SM_FD_SET(m->mf_sock, &fds); \
22694334Sgshapiro		tv.tv_sec = (secs); \
22794334Sgshapiro		tv.tv_usec = 0; \
22894334Sgshapiro		ret = select(m->mf_sock + 1, \
22994334Sgshapiro			     (write) ? NULL : &fds, \
23094334Sgshapiro			     (write) ? &fds : NULL, \
23194334Sgshapiro			     NULL, &tv); \
23294334Sgshapiro	} while (ret < 0 && errno == EINTR); \
23364562Sgshapiro \
23464562Sgshapiro	switch (ret) \
23564562Sgshapiro	{ \
23664562Sgshapiro	  case 0: \
23764562Sgshapiro		if (tTd(64, 5)) \
238168515Sgshapiro			sm_dprintf("milter_%s(%s): timeout, where=%s\n", \
239168515Sgshapiro				(routine), m->mf_name, (function)); \
24090792Sgshapiro		if (MilterLogLevel > 0) \
24190792Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
242168515Sgshapiro				  "Milter (%s): timeout %s data %s, where=%s", \
243168515Sgshapiro				  m->mf_name, \
24490792Sgshapiro				  started ? "during" : "before", \
245168515Sgshapiro				  (routine), (function)); \
24690792Sgshapiro		milter_error(m, e); \
24764562Sgshapiro		return NULL; \
24864562Sgshapiro \
24964562Sgshapiro	  case -1: \
25064562Sgshapiro		save_errno = errno; \
25164562Sgshapiro		if (tTd(64, 5)) \
25290792Sgshapiro			sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
25390792Sgshapiro				   m->mf_name, sm_errstring(save_errno)); \
25490792Sgshapiro		if (MilterLogLevel > 0) \
25590792Sgshapiro		{ \
25664562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
25790792Sgshapiro				  "Milter (%s): select(%s): %s", \
25890792Sgshapiro				  m->mf_name, (routine), \
25990792Sgshapiro				  sm_errstring(save_errno)); \
26090792Sgshapiro		} \
26190792Sgshapiro		milter_error(m, e); \
26264562Sgshapiro		return NULL; \
26364562Sgshapiro \
26464562Sgshapiro	  default: \
26571345Sgshapiro		if (SM_FD_ISSET(m->mf_sock, &fds)) \
26664562Sgshapiro			break; \
26764562Sgshapiro		if (tTd(64, 5)) \
26890792Sgshapiro			sm_dprintf("milter_%s(%s): socket not ready\n", \
26990792Sgshapiro				(routine), m->mf_name); \
27090792Sgshapiro		if (MilterLogLevel > 0) \
27190792Sgshapiro		{ \
27264562Sgshapiro			sm_syslog(LOG_ERR, e->e_id, \
27390792Sgshapiro				  "Milter (%s): socket(%s) not ready", \
27490792Sgshapiro				  m->mf_name, (routine)); \
27590792Sgshapiro		} \
27690792Sgshapiro		milter_error(m, e); \
27764562Sgshapiro		return NULL; \
27864562Sgshapiro	} \
27964562Sgshapiro}
28064562Sgshapiro
28164562Sgshapiro/*
28264562Sgshapiro**  Low level functions
28364562Sgshapiro*/
28464562Sgshapiro
28590792Sgshapiro/*
28664562Sgshapiro**  MILTER_READ -- read from a remote milter filter
28764562Sgshapiro**
28864562Sgshapiro**	Parameters:
28964562Sgshapiro**		m -- milter to read from.
29064562Sgshapiro**		cmd -- return param for command read.
29164562Sgshapiro**		rlen -- return length of response string.
29264562Sgshapiro**		to -- timeout in seconds.
29364562Sgshapiro**		e -- current envelope.
29464562Sgshapiro**
29564562Sgshapiro**	Returns:
29664562Sgshapiro**		response string (may be NULL)
29764562Sgshapiro*/
29864562Sgshapiro
29964562Sgshapirostatic char *
300168515Sgshapiromilter_sysread(m, buf, sz, to, e, where)
30164562Sgshapiro	struct milter *m;
30264562Sgshapiro	char *buf;
30364562Sgshapiro	ssize_t sz;
30464562Sgshapiro	time_t to;
30564562Sgshapiro	ENVELOPE *e;
306168515Sgshapiro	const char *where;
30764562Sgshapiro{
30866494Sgshapiro	time_t readstart = 0;
30964562Sgshapiro	ssize_t len, curl;
31090792Sgshapiro	bool started = false;
31164562Sgshapiro
31264562Sgshapiro	curl = 0;
31364562Sgshapiro
31464562Sgshapiro	if (to > 0)
31564562Sgshapiro		readstart = curtime();
31664562Sgshapiro
31764562Sgshapiro	for (;;)
31864562Sgshapiro	{
31964562Sgshapiro		if (to > 0)
32064562Sgshapiro		{
32164562Sgshapiro			time_t now;
32264562Sgshapiro
32364562Sgshapiro			now = curtime();
32464562Sgshapiro			if (now - readstart >= to)
32564562Sgshapiro			{
32664562Sgshapiro				if (tTd(64, 5))
327168515Sgshapiro					sm_dprintf("milter_sys_read (%s): timeout %s data read in %s",
328168515Sgshapiro						  m->mf_name,
32990792Sgshapiro						  started ? "during" : "before",
330168515Sgshapiro						  where);
33190792Sgshapiro				if (MilterLogLevel > 0)
33264562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
333168515Sgshapiro						  "Milter (%s): timeout %s data read in %s",
334168515Sgshapiro						  m->mf_name,
33590792Sgshapiro						  started ? "during" : "before",
336168515Sgshapiro						  where);
33790792Sgshapiro				milter_error(m, e);
33864562Sgshapiro				return NULL;
33964562Sgshapiro			}
34064562Sgshapiro			to -= now - readstart;
34164562Sgshapiro			readstart = now;
342168515Sgshapiro			MILTER_TIMEOUT("read", to, false, started, where);
34364562Sgshapiro		}
34464562Sgshapiro
34564562Sgshapiro		len = read(m->mf_sock, buf + curl, sz - curl);
34664562Sgshapiro
34764562Sgshapiro		if (len < 0)
34864562Sgshapiro		{
34964562Sgshapiro			int save_errno = errno;
35064562Sgshapiro
35164562Sgshapiro			if (tTd(64, 5))
352168515Sgshapiro				sm_dprintf("milter_sys_read(%s): read returned %ld: %s\n",
35364562Sgshapiro					m->mf_name, (long) len,
35490792Sgshapiro					sm_errstring(save_errno));
35590792Sgshapiro			if (MilterLogLevel > 0)
35664562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
35790792Sgshapiro					  "Milter (%s): read returned %ld: %s",
35864562Sgshapiro					  m->mf_name, (long) len,
35990792Sgshapiro					  sm_errstring(save_errno));
36090792Sgshapiro			milter_error(m, e);
36164562Sgshapiro			return NULL;
36264562Sgshapiro		}
36364562Sgshapiro
36490792Sgshapiro		started = true;
36564562Sgshapiro		curl += len;
36673188Sgshapiro		if (len == 0 || curl >= sz)
36764562Sgshapiro			break;
36864562Sgshapiro
36964562Sgshapiro	}
37064562Sgshapiro
37164562Sgshapiro	if (curl != sz)
37264562Sgshapiro	{
37364562Sgshapiro		if (tTd(64, 5))
374168515Sgshapiro			sm_dprintf("milter_sys_read(%s): cmd read returned %ld, expecting %ld\n",
37564562Sgshapiro				m->mf_name, (long) curl, (long) sz);
37690792Sgshapiro		if (MilterLogLevel > 0)
37764562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
378168515Sgshapiro				  "milter_sys_read(%s): cmd read returned %ld, expecting %ld",
37964562Sgshapiro				  m->mf_name, (long) curl, (long) sz);
38090792Sgshapiro		milter_error(m, e);
38164562Sgshapiro		return NULL;
38264562Sgshapiro	}
38364562Sgshapiro	return buf;
38464562Sgshapiro}
38564562Sgshapiro
38664562Sgshapirostatic char *
387168515Sgshapiromilter_read(m, cmd, rlen, to, e, where)
38864562Sgshapiro	struct milter *m;
38964562Sgshapiro	char *cmd;
39064562Sgshapiro	ssize_t *rlen;
39164562Sgshapiro	time_t to;
39264562Sgshapiro	ENVELOPE *e;
393168515Sgshapiro	const char *where;
39464562Sgshapiro{
39566494Sgshapiro	time_t readstart = 0;
39664562Sgshapiro	ssize_t expl;
39764562Sgshapiro	mi_int32 i;
398168515Sgshapiro# if MILTER_NO_NAGLE && defined(TCP_CORK)
399132943Sgshapiro	int cork = 0;
400168515Sgshapiro# endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
40164562Sgshapiro	char *buf;
40264562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
40364562Sgshapiro
404147078Sgshapiro	if (m->mf_sock < 0)
405147078Sgshapiro	{
406147078Sgshapiro		if (MilterLogLevel > 0)
407147078Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
408168515Sgshapiro				  "milter_read(%s): socket closed, where=%s",
409168515Sgshapiro				  m->mf_name, where);
410147078Sgshapiro		milter_error(m, e);
411147078Sgshapiro		return NULL;
412147078Sgshapiro	}
413147078Sgshapiro
41464562Sgshapiro	*rlen = 0;
41564562Sgshapiro	*cmd = '\0';
41664562Sgshapiro
41764562Sgshapiro	if (to > 0)
41864562Sgshapiro		readstart = curtime();
41964562Sgshapiro
420168515Sgshapiro# if MILTER_NO_NAGLE && defined(TCP_CORK)
421132943Sgshapiro	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
422132943Sgshapiro		   sizeof(cork));
423168515Sgshapiro# endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
424132943Sgshapiro
425168515Sgshapiro	if (milter_sysread(m, data, sizeof(data), to, e, where) == NULL)
42664562Sgshapiro		return NULL;
42764562Sgshapiro
428168515Sgshapiro# if MILTER_NO_NAGLE && defined(TCP_CORK)
429132943Sgshapiro	cork = 1;
430132943Sgshapiro	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
431132943Sgshapiro		   sizeof(cork));
432168515Sgshapiro# endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
433132943Sgshapiro
43464562Sgshapiro	/* reset timeout */
43564562Sgshapiro	if (to > 0)
43664562Sgshapiro	{
43764562Sgshapiro		time_t now;
43864562Sgshapiro
43964562Sgshapiro		now = curtime();
44064562Sgshapiro		if (now - readstart >= to)
44164562Sgshapiro		{
44264562Sgshapiro			if (tTd(64, 5))
443168515Sgshapiro				sm_dprintf("milter_read(%s): timeout before data read, where=%s\n",
444168515Sgshapiro					m->mf_name, where);
44590792Sgshapiro			if (MilterLogLevel > 0)
44664562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
447168515Sgshapiro					  "Milter read(%s): timeout before data read, where=%s",
448168515Sgshapiro					  m->mf_name, where);
44990792Sgshapiro			milter_error(m, e);
45064562Sgshapiro			return NULL;
45164562Sgshapiro		}
45264562Sgshapiro		to -= now - readstart;
45364562Sgshapiro	}
45464562Sgshapiro
45564562Sgshapiro	*cmd = data[MILTER_LEN_BYTES];
45664562Sgshapiro	data[MILTER_LEN_BYTES] = '\0';
45764562Sgshapiro	(void) memcpy(&i, data, MILTER_LEN_BYTES);
45864562Sgshapiro	expl = ntohl(i) - 1;
45964562Sgshapiro
46064562Sgshapiro	if (tTd(64, 25))
46190792Sgshapiro		sm_dprintf("milter_read(%s): expecting %ld bytes\n",
46264562Sgshapiro			m->mf_name, (long) expl);
46364562Sgshapiro
46464562Sgshapiro	if (expl < 0)
46564562Sgshapiro	{
46664562Sgshapiro		if (tTd(64, 5))
467168515Sgshapiro			sm_dprintf("milter_read(%s): read size %ld out of range, where=%s\n",
468168515Sgshapiro				m->mf_name, (long) expl, where);
46990792Sgshapiro		if (MilterLogLevel > 0)
47064562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
471168515Sgshapiro				  "milter_read(%s): read size %ld out of range, where=%s",
472168515Sgshapiro				  m->mf_name, (long) expl, where);
47390792Sgshapiro		milter_error(m, e);
47464562Sgshapiro		return NULL;
47564562Sgshapiro	}
47664562Sgshapiro
47764562Sgshapiro	if (expl == 0)
47864562Sgshapiro		return NULL;
47964562Sgshapiro
48090792Sgshapiro	buf = (char *) xalloc(expl);
48164562Sgshapiro
482168515Sgshapiro	if (milter_sysread(m, buf, expl, to, e, where) == NULL)
48364562Sgshapiro	{
48490792Sgshapiro		sm_free(buf); /* XXX */
48564562Sgshapiro		return NULL;
48664562Sgshapiro	}
48764562Sgshapiro
48864562Sgshapiro	if (tTd(64, 50))
48990792Sgshapiro		sm_dprintf("milter_read(%s): Returning %*s\n",
49064562Sgshapiro			m->mf_name, (int) expl, buf);
49164562Sgshapiro	*rlen = expl;
49264562Sgshapiro	return buf;
49364562Sgshapiro}
494132943Sgshapiro
49590792Sgshapiro/*
49664562Sgshapiro**  MILTER_WRITE -- write to a remote milter filter
49764562Sgshapiro**
49864562Sgshapiro**	Parameters:
49964562Sgshapiro**		m -- milter to read from.
50064562Sgshapiro**		cmd -- command to send.
50164562Sgshapiro**		buf -- optional command data.
50264562Sgshapiro**		len -- length of buf.
50364562Sgshapiro**		to -- timeout in seconds.
50464562Sgshapiro**		e -- current envelope.
50564562Sgshapiro**
50664562Sgshapiro**	Returns:
50764562Sgshapiro**		buf if successful, NULL otherwise
50864562Sgshapiro**		Not actually used anywhere but function prototype
50964562Sgshapiro**			must match milter_read()
51064562Sgshapiro*/
51164562Sgshapiro
51264562Sgshapirostatic char *
513168515Sgshapiromilter_write(m, cmd, buf, len, to, e, where)
51464562Sgshapiro	struct milter *m;
515168515Sgshapiro	int cmd;
51664562Sgshapiro	char *buf;
51764562Sgshapiro	ssize_t len;
51864562Sgshapiro	time_t to;
51964562Sgshapiro	ENVELOPE *e;
520168515Sgshapiro	const char *where;
52164562Sgshapiro{
52264562Sgshapiro	ssize_t sl, i;
523132943Sgshapiro	int num_vectors;
52464562Sgshapiro	mi_int32 nl;
525168515Sgshapiro	char command = (char) cmd;
52664562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
52790792Sgshapiro	bool started = false;
528132943Sgshapiro	struct iovec vector[2];
52964562Sgshapiro
530132943Sgshapiro	/*
531132943Sgshapiro	**  At most two buffers will be written, though
532132943Sgshapiro	**  only one may actually be used (see num_vectors).
533132943Sgshapiro	**  The first is the size/command and the second is the command data.
534132943Sgshapiro	*/
535132943Sgshapiro
536132943Sgshapiro	if (len < 0 || len > MilterMaxDataSize)
53764562Sgshapiro	{
53864562Sgshapiro		if (tTd(64, 5))
539203004Sgshapiro		{
540203004Sgshapiro			sm_dprintf("milter_write(%s): length %ld out of range, cmd=%c\n",
541203004Sgshapiro				m->mf_name, (long) len, command);
542203004Sgshapiro			sm_dprintf("milter_write(%s): buf=%s\n",
543203004Sgshapiro				m->mf_name, str2prt(buf));
544203004Sgshapiro		}
54590792Sgshapiro		if (MilterLogLevel > 0)
54664562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
547203004Sgshapiro				  "milter_write(%s): length %ld out of range, cmd=%c",
548203004Sgshapiro				  m->mf_name, (long) len, command);
54990792Sgshapiro		milter_error(m, e);
55064562Sgshapiro		return NULL;
55164562Sgshapiro	}
552147078Sgshapiro	if (m->mf_sock < 0)
553147078Sgshapiro	{
554147078Sgshapiro		if (MilterLogLevel > 0)
555147078Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
556147078Sgshapiro				  "milter_write(%s): socket closed",
557147078Sgshapiro				  m->mf_name);
558147078Sgshapiro		milter_error(m, e);
559147078Sgshapiro		return NULL;
560147078Sgshapiro	}
56164562Sgshapiro
56264562Sgshapiro	if (tTd(64, 20))
56390792Sgshapiro		sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
564168515Sgshapiro			   m->mf_name, command, (long) len);
56564562Sgshapiro
566168515Sgshapiro	nl = htonl(len + 1);	/* add 1 for the command char */
56764562Sgshapiro	(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
568168515Sgshapiro	data[MILTER_LEN_BYTES] = command;
56964562Sgshapiro	sl = MILTER_LEN_BYTES + 1;
57064562Sgshapiro
571132943Sgshapiro	/* set up the vector for the size / command */
572132943Sgshapiro	vector[0].iov_base = (void *) data;
573132943Sgshapiro	vector[0].iov_len  = sl;
574132943Sgshapiro
575132943Sgshapiro	/*
576132943Sgshapiro	**  Determine if there is command data.  If so, there will be two
577132943Sgshapiro	**  vectors.  If not, there will be only one.  The vectors are set
578132943Sgshapiro	**  up here and 'num_vectors' and 'sl' are set appropriately.
579132943Sgshapiro	*/
580132943Sgshapiro
581132943Sgshapiro	/* NOTE:  len<0 has already been checked for.  Pedantic */
582132943Sgshapiro	if (len <= 0 || buf == NULL)
58364562Sgshapiro	{
584132943Sgshapiro		/* There is no command data -- only a size / command data */
585132943Sgshapiro		num_vectors = 1;
58664562Sgshapiro	}
587132943Sgshapiro	else
58864562Sgshapiro	{
589132943Sgshapiro		/*
590132943Sgshapiro		**  There is both size / command and command data.
591132943Sgshapiro		**  Set up the vector for the command data.
592132943Sgshapiro		*/
59364562Sgshapiro
594132943Sgshapiro		num_vectors = 2;
595132943Sgshapiro		sl += len;
596132943Sgshapiro		vector[1].iov_base = (void *) buf;
597132943Sgshapiro		vector[1].iov_len  = len;
598132943Sgshapiro
599132943Sgshapiro		if (tTd(64, 50))
600132943Sgshapiro			sm_dprintf("milter_write(%s): Sending %*s\n",
601132943Sgshapiro				   m->mf_name, (int) len, buf);
60264562Sgshapiro	}
60364562Sgshapiro
60464562Sgshapiro	if (to > 0)
605168515Sgshapiro		MILTER_TIMEOUT("write", to, true, started, where);
60664562Sgshapiro
607132943Sgshapiro	/* write the vector(s) */
608132943Sgshapiro	i = writev(m->mf_sock, vector, num_vectors);
609132943Sgshapiro	if (i != sl)
61064562Sgshapiro	{
61164562Sgshapiro		int save_errno = errno;
61264562Sgshapiro
61364562Sgshapiro		if (tTd(64, 5))
61490792Sgshapiro			sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
615168515Sgshapiro				   m->mf_name, command, (long) i, (long) sl,
61690792Sgshapiro				   sm_errstring(save_errno));
61790792Sgshapiro		if (MilterLogLevel > 0)
61864562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
61990792Sgshapiro				  "Milter (%s): write(%c) returned %ld, expected %ld: %s",
620168515Sgshapiro				  m->mf_name, command, (long) i, (long) sl,
62190792Sgshapiro				  sm_errstring(save_errno));
62290792Sgshapiro		milter_error(m, e);
62364562Sgshapiro		return NULL;
62464562Sgshapiro	}
62564562Sgshapiro	return buf;
62664562Sgshapiro}
62764562Sgshapiro
62864562Sgshapiro/*
62964562Sgshapiro**  Utility functions
63064562Sgshapiro*/
63164562Sgshapiro
63290792Sgshapiro/*
63364562Sgshapiro**  MILTER_OPEN -- connect to remote milter filter
63464562Sgshapiro**
63564562Sgshapiro**	Parameters:
63664562Sgshapiro**		m -- milter to connect to.
63764562Sgshapiro**		parseonly -- parse but don't connect.
63864562Sgshapiro**		e -- current envelope.
63964562Sgshapiro**
64064562Sgshapiro**	Returns:
641111823Sgshapiro**		connected socket if successful && !parseonly,
64264562Sgshapiro**		0 upon parse success if parseonly,
64364562Sgshapiro**		-1 otherwise.
64464562Sgshapiro*/
64564562Sgshapiro
64680785Sgshapirostatic jmp_buf	MilterConnectTimeout;
64780785Sgshapiro
64864562Sgshapirostatic int
64964562Sgshapiromilter_open(m, parseonly, e)
65064562Sgshapiro	struct milter *m;
65164562Sgshapiro	bool parseonly;
65264562Sgshapiro	ENVELOPE *e;
65364562Sgshapiro{
65464562Sgshapiro	int sock = 0;
65564562Sgshapiro	SOCKADDR_LEN_T addrlen = 0;
65664562Sgshapiro	int addrno = 0;
65764562Sgshapiro	int save_errno;
65864562Sgshapiro	char *p;
65964562Sgshapiro	char *colon;
66064562Sgshapiro	char *at;
66164562Sgshapiro	struct hostent *hp = NULL;
66264562Sgshapiro	SOCKADDR addr;
66364562Sgshapiro
66464562Sgshapiro	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
66564562Sgshapiro	{
66664562Sgshapiro		if (tTd(64, 5))
66790792Sgshapiro			sm_dprintf("X%s: empty or missing socket information\n",
66890792Sgshapiro				   m->mf_name);
66964562Sgshapiro		if (parseonly)
67064562Sgshapiro			syserr("X%s: empty or missing socket information",
67164562Sgshapiro			       m->mf_name);
672110560Sgshapiro		else if (MilterLogLevel > 0)
67364562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
67490792Sgshapiro				  "Milter (%s): empty or missing socket information",
67564562Sgshapiro				  m->mf_name);
67690792Sgshapiro		milter_error(m, e);
67764562Sgshapiro		return -1;
67864562Sgshapiro	}
67964562Sgshapiro
68064562Sgshapiro	/* protocol:filename or protocol:port@host */
681168515Sgshapiro	memset(&addr, '\0', sizeof(addr));
68264562Sgshapiro	p = m->mf_conn;
68364562Sgshapiro	colon = strchr(p, ':');
68464562Sgshapiro	if (colon != NULL)
68564562Sgshapiro	{
68664562Sgshapiro		*colon = '\0';
68764562Sgshapiro
68864562Sgshapiro		if (*p == '\0')
68964562Sgshapiro		{
69064562Sgshapiro# if NETUNIX
69164562Sgshapiro			/* default to AF_UNIX */
69264562Sgshapiro			addr.sa.sa_family = AF_UNIX;
69364562Sgshapiro# else /* NETUNIX */
69464562Sgshapiro#  if NETINET
69564562Sgshapiro			/* default to AF_INET */
69664562Sgshapiro			addr.sa.sa_family = AF_INET;
69764562Sgshapiro#  else /* NETINET */
69864562Sgshapiro#   if NETINET6
69964562Sgshapiro			/* default to AF_INET6 */
70064562Sgshapiro			addr.sa.sa_family = AF_INET6;
70164562Sgshapiro#   else /* NETINET6 */
70264562Sgshapiro			/* no protocols available */
703110560Sgshapiro			if (MilterLogLevel > 0)
704110560Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
705110560Sgshapiro					  "Milter (%s): no valid socket protocols available",
706110560Sgshapiro					  m->mf_name);
70790792Sgshapiro			milter_error(m, e);
70864562Sgshapiro			return -1;
70964562Sgshapiro#   endif /* NETINET6 */
71064562Sgshapiro#  endif /* NETINET */
71164562Sgshapiro# endif /* NETUNIX */
71264562Sgshapiro		}
71364562Sgshapiro# if NETUNIX
71490792Sgshapiro		else if (sm_strcasecmp(p, "unix") == 0 ||
71590792Sgshapiro			 sm_strcasecmp(p, "local") == 0)
71664562Sgshapiro			addr.sa.sa_family = AF_UNIX;
71764562Sgshapiro# endif /* NETUNIX */
71864562Sgshapiro# if NETINET
71990792Sgshapiro		else if (sm_strcasecmp(p, "inet") == 0)
72064562Sgshapiro			addr.sa.sa_family = AF_INET;
72164562Sgshapiro# endif /* NETINET */
72264562Sgshapiro# if NETINET6
72390792Sgshapiro		else if (sm_strcasecmp(p, "inet6") == 0)
72464562Sgshapiro			addr.sa.sa_family = AF_INET6;
72564562Sgshapiro# endif /* NETINET6 */
72664562Sgshapiro		else
72764562Sgshapiro		{
72864562Sgshapiro# ifdef EPROTONOSUPPORT
72964562Sgshapiro			errno = EPROTONOSUPPORT;
73064562Sgshapiro# else /* EPROTONOSUPPORT */
73164562Sgshapiro			errno = EINVAL;
73264562Sgshapiro# endif /* EPROTONOSUPPORT */
73364562Sgshapiro			if (tTd(64, 5))
73490792Sgshapiro				sm_dprintf("X%s: unknown socket type %s\n",
73564562Sgshapiro					m->mf_name, p);
73664562Sgshapiro			if (parseonly)
73764562Sgshapiro				syserr("X%s: unknown socket type %s",
73864562Sgshapiro				       m->mf_name, p);
739110560Sgshapiro			else if (MilterLogLevel > 0)
74064562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
74190792Sgshapiro					  "Milter (%s): unknown socket type %s",
74264562Sgshapiro					  m->mf_name, p);
74390792Sgshapiro			milter_error(m, e);
74464562Sgshapiro			return -1;
74564562Sgshapiro		}
74664562Sgshapiro		*colon++ = ':';
74764562Sgshapiro	}
74864562Sgshapiro	else
74964562Sgshapiro	{
75064562Sgshapiro		/* default to AF_UNIX */
75164562Sgshapiro		addr.sa.sa_family = AF_UNIX;
75264562Sgshapiro		colon = p;
75364562Sgshapiro	}
75464562Sgshapiro
75564562Sgshapiro# if NETUNIX
75664562Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
75764562Sgshapiro	{
75864562Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
75964562Sgshapiro
76064562Sgshapiro		at = colon;
761168515Sgshapiro		if (strlen(colon) >= sizeof(addr.sunix.sun_path))
76264562Sgshapiro		{
76364562Sgshapiro			if (tTd(64, 5))
76490792Sgshapiro				sm_dprintf("X%s: local socket name %s too long\n",
76564562Sgshapiro					m->mf_name, colon);
76664562Sgshapiro			errno = EINVAL;
76764562Sgshapiro			if (parseonly)
76864562Sgshapiro				syserr("X%s: local socket name %s too long",
76964562Sgshapiro				       m->mf_name, colon);
770110560Sgshapiro			else if (MilterLogLevel > 0)
77164562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
77290792Sgshapiro					  "Milter (%s): local socket name %s too long",
77364562Sgshapiro					  m->mf_name, colon);
77490792Sgshapiro			milter_error(m, e);
77564562Sgshapiro			return -1;
77664562Sgshapiro		}
77764562Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
77864562Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
77964562Sgshapiro
78064562Sgshapiro		/* if just parsing .cf file, socket doesn't need to exist */
78164562Sgshapiro		if (parseonly && errno == ENOENT)
78264562Sgshapiro		{
78364562Sgshapiro			if (OpMode == MD_DAEMON ||
78464562Sgshapiro			    OpMode == MD_FGDAEMON)
78590792Sgshapiro				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
78690792Sgshapiro						     "WARNING: X%s: local socket name %s missing\n",
78790792Sgshapiro						     m->mf_name, colon);
78864562Sgshapiro		}
78964562Sgshapiro		else if (errno != 0)
79064562Sgshapiro		{
79164562Sgshapiro			/* if not safe, don't create */
79264562Sgshapiro			save_errno = errno;
79364562Sgshapiro			if (tTd(64, 5))
79490792Sgshapiro				sm_dprintf("X%s: local socket name %s unsafe\n",
79564562Sgshapiro					m->mf_name, colon);
79664562Sgshapiro			errno = save_errno;
79764562Sgshapiro			if (parseonly)
79864562Sgshapiro			{
79964562Sgshapiro				if (OpMode == MD_DAEMON ||
80064562Sgshapiro				    OpMode == MD_FGDAEMON ||
80164562Sgshapiro				    OpMode == MD_SMTP)
80264562Sgshapiro					syserr("X%s: local socket name %s unsafe",
80364562Sgshapiro					       m->mf_name, colon);
80464562Sgshapiro			}
805110560Sgshapiro			else if (MilterLogLevel > 0)
80664562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
80790792Sgshapiro					  "Milter (%s): local socket name %s unsafe",
80864562Sgshapiro					  m->mf_name, colon);
80990792Sgshapiro			milter_error(m, e);
81064562Sgshapiro			return -1;
81164562Sgshapiro		}
81264562Sgshapiro
81390792Sgshapiro		(void) sm_strlcpy(addr.sunix.sun_path, colon,
814168515Sgshapiro			       sizeof(addr.sunix.sun_path));
815168515Sgshapiro		addrlen = sizeof(struct sockaddr_un);
81664562Sgshapiro	}
81764562Sgshapiro	else
81864562Sgshapiro# endif /* NETUNIX */
81964562Sgshapiro# if NETINET || NETINET6
82090792Sgshapiro	if (false
82164562Sgshapiro#  if NETINET
82264562Sgshapiro		 || addr.sa.sa_family == AF_INET
82364562Sgshapiro#  endif /* NETINET */
82464562Sgshapiro#  if NETINET6
82564562Sgshapiro		 || addr.sa.sa_family == AF_INET6
82664562Sgshapiro#  endif /* NETINET6 */
82764562Sgshapiro		 )
82864562Sgshapiro	{
82990792Sgshapiro		unsigned short port;
83064562Sgshapiro
83164562Sgshapiro		/* Parse port@host */
83264562Sgshapiro		at = strchr(colon, '@');
83364562Sgshapiro		if (at == NULL)
83464562Sgshapiro		{
83564562Sgshapiro			if (tTd(64, 5))
83690792Sgshapiro				sm_dprintf("X%s: bad address %s (expected port@host)\n",
83764562Sgshapiro					m->mf_name, colon);
83864562Sgshapiro			if (parseonly)
83964562Sgshapiro				syserr("X%s: bad address %s (expected port@host)",
84064562Sgshapiro				       m->mf_name, colon);
841110560Sgshapiro			else if (MilterLogLevel > 0)
84264562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
84390792Sgshapiro					  "Milter (%s): bad address %s (expected port@host)",
84464562Sgshapiro					  m->mf_name, colon);
84590792Sgshapiro			milter_error(m, e);
84664562Sgshapiro			return -1;
84764562Sgshapiro		}
84864562Sgshapiro		*at = '\0';
84964562Sgshapiro		if (isascii(*colon) && isdigit(*colon))
85090792Sgshapiro			port = htons((unsigned short) atoi(colon));
85164562Sgshapiro		else
85264562Sgshapiro		{
85364562Sgshapiro#  ifdef NO_GETSERVBYNAME
85464562Sgshapiro			if (tTd(64, 5))
85590792Sgshapiro				sm_dprintf("X%s: invalid port number %s\n",
85664562Sgshapiro					m->mf_name, colon);
85764562Sgshapiro			if (parseonly)
85864562Sgshapiro				syserr("X%s: invalid port number %s",
85964562Sgshapiro				       m->mf_name, colon);
860110560Sgshapiro			else if (MilterLogLevel > 0)
86164562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
86290792Sgshapiro					  "Milter (%s): invalid port number %s",
86364562Sgshapiro					  m->mf_name, colon);
86490792Sgshapiro			milter_error(m, e);
86564562Sgshapiro			return -1;
86664562Sgshapiro#  else /* NO_GETSERVBYNAME */
867168515Sgshapiro			struct servent *sp;
86864562Sgshapiro
86964562Sgshapiro			sp = getservbyname(colon, "tcp");
87064562Sgshapiro			if (sp == NULL)
87164562Sgshapiro			{
87264562Sgshapiro				save_errno = errno;
87364562Sgshapiro				if (tTd(64, 5))
87490792Sgshapiro					sm_dprintf("X%s: unknown port name %s\n",
87564562Sgshapiro						m->mf_name, colon);
87664562Sgshapiro				errno = save_errno;
87764562Sgshapiro				if (parseonly)
87864562Sgshapiro					syserr("X%s: unknown port name %s",
87964562Sgshapiro					       m->mf_name, colon);
880110560Sgshapiro				else if (MilterLogLevel > 0)
88164562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
88290792Sgshapiro						  "Milter (%s): unknown port name %s",
88364562Sgshapiro						  m->mf_name, colon);
88490792Sgshapiro				milter_error(m, e);
88564562Sgshapiro				return -1;
88664562Sgshapiro			}
88764562Sgshapiro			port = sp->s_port;
88864562Sgshapiro#  endif /* NO_GETSERVBYNAME */
88964562Sgshapiro		}
89064562Sgshapiro		*at++ = '@';
89164562Sgshapiro		if (*at == '[')
89264562Sgshapiro		{
89364562Sgshapiro			char *end;
89464562Sgshapiro
89564562Sgshapiro			end = strchr(at, ']');
89664562Sgshapiro			if (end != NULL)
89764562Sgshapiro			{
89890792Sgshapiro				bool found = false;
89964562Sgshapiro#  if NETINET
90064562Sgshapiro				unsigned long hid = INADDR_NONE;
90164562Sgshapiro#  endif /* NETINET */
90264562Sgshapiro#  if NETINET6
90364562Sgshapiro				struct sockaddr_in6 hid6;
90464562Sgshapiro#  endif /* NETINET6 */
90564562Sgshapiro
90664562Sgshapiro				*end = '\0';
90764562Sgshapiro#  if NETINET
90864562Sgshapiro				if (addr.sa.sa_family == AF_INET &&
90964562Sgshapiro				    (hid = inet_addr(&at[1])) != INADDR_NONE)
91064562Sgshapiro				{
91164562Sgshapiro					addr.sin.sin_addr.s_addr = hid;
91264562Sgshapiro					addr.sin.sin_port = port;
91390792Sgshapiro					found = true;
91464562Sgshapiro				}
91564562Sgshapiro#  endif /* NETINET */
91664562Sgshapiro#  if NETINET6
917168515Sgshapiro				(void) memset(&hid6, '\0', sizeof(hid6));
91864562Sgshapiro				if (addr.sa.sa_family == AF_INET6 &&
91990792Sgshapiro				    anynet_pton(AF_INET6, &at[1],
92090792Sgshapiro						&hid6.sin6_addr) == 1)
92164562Sgshapiro				{
92264562Sgshapiro					addr.sin6.sin6_addr = hid6.sin6_addr;
92364562Sgshapiro					addr.sin6.sin6_port = port;
92490792Sgshapiro					found = true;
92564562Sgshapiro				}
92664562Sgshapiro#  endif /* NETINET6 */
92764562Sgshapiro				*end = ']';
92864562Sgshapiro				if (!found)
92964562Sgshapiro				{
93064562Sgshapiro					if (tTd(64, 5))
93190792Sgshapiro						sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
93264562Sgshapiro							m->mf_name, at);
93364562Sgshapiro					if (parseonly)
93464562Sgshapiro						syserr("X%s: Invalid numeric domain spec \"%s\"",
93564562Sgshapiro						       m->mf_name, at);
936110560Sgshapiro					else if (MilterLogLevel > 0)
93764562Sgshapiro						sm_syslog(LOG_ERR, e->e_id,
93890792Sgshapiro							  "Milter (%s): Invalid numeric domain spec \"%s\"",
93964562Sgshapiro							  m->mf_name, at);
94090792Sgshapiro					milter_error(m, e);
94164562Sgshapiro					return -1;
94264562Sgshapiro				}
94364562Sgshapiro			}
94464562Sgshapiro			else
94564562Sgshapiro			{
94664562Sgshapiro				if (tTd(64, 5))
94790792Sgshapiro					sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
94864562Sgshapiro						m->mf_name, at);
94964562Sgshapiro				if (parseonly)
95064562Sgshapiro					syserr("X%s: Invalid numeric domain spec \"%s\"",
95164562Sgshapiro					       m->mf_name, at);
952110560Sgshapiro				else if (MilterLogLevel > 0)
95364562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
95490792Sgshapiro						  "Milter (%s): Invalid numeric domain spec \"%s\"",
95564562Sgshapiro						  m->mf_name, at);
95690792Sgshapiro				milter_error(m, e);
95764562Sgshapiro				return -1;
95864562Sgshapiro			}
95964562Sgshapiro		}
96064562Sgshapiro		else
96164562Sgshapiro		{
96264562Sgshapiro			hp = sm_gethostbyname(at, addr.sa.sa_family);
96364562Sgshapiro			if (hp == NULL)
96464562Sgshapiro			{
96564562Sgshapiro				save_errno = errno;
96664562Sgshapiro				if (tTd(64, 5))
96790792Sgshapiro					sm_dprintf("X%s: Unknown host name %s\n",
96890792Sgshapiro						   m->mf_name, at);
96964562Sgshapiro				errno = save_errno;
97064562Sgshapiro				if (parseonly)
97164562Sgshapiro					syserr("X%s: Unknown host name %s",
97264562Sgshapiro					       m->mf_name, at);
973110560Sgshapiro				else if (MilterLogLevel > 0)
97464562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
97590792Sgshapiro						  "Milter (%s): Unknown host name %s",
97664562Sgshapiro						  m->mf_name, at);
97790792Sgshapiro				milter_error(m, e);
97864562Sgshapiro				return -1;
97964562Sgshapiro			}
98064562Sgshapiro			addr.sa.sa_family = hp->h_addrtype;
98164562Sgshapiro			switch (hp->h_addrtype)
98264562Sgshapiro			{
98364562Sgshapiro#  if NETINET
98464562Sgshapiro			  case AF_INET:
98564562Sgshapiro				memmove(&addr.sin.sin_addr,
98690792Sgshapiro					hp->h_addr, INADDRSZ);
98764562Sgshapiro				addr.sin.sin_port = port;
988168515Sgshapiro				addrlen = sizeof(struct sockaddr_in);
98964562Sgshapiro				addrno = 1;
99064562Sgshapiro				break;
99164562Sgshapiro#  endif /* NETINET */
99264562Sgshapiro
99364562Sgshapiro#  if NETINET6
99464562Sgshapiro			  case AF_INET6:
99564562Sgshapiro				memmove(&addr.sin6.sin6_addr,
99690792Sgshapiro					hp->h_addr, IN6ADDRSZ);
99764562Sgshapiro				addr.sin6.sin6_port = port;
998168515Sgshapiro				addrlen = sizeof(struct sockaddr_in6);
99964562Sgshapiro				addrno = 1;
100064562Sgshapiro				break;
100164562Sgshapiro#  endif /* NETINET6 */
100264562Sgshapiro
100364562Sgshapiro			  default:
100464562Sgshapiro				if (tTd(64, 5))
100590792Sgshapiro					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
100690792Sgshapiro						   m->mf_name, at,
100790792Sgshapiro						   hp->h_addrtype);
100864562Sgshapiro				if (parseonly)
100964562Sgshapiro					syserr("X%s: Unknown protocol for %s (%d)",
101064562Sgshapiro					       m->mf_name, at, hp->h_addrtype);
1011110560Sgshapiro				else if (MilterLogLevel > 0)
101264562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
101390792Sgshapiro						  "Milter (%s): Unknown protocol for %s (%d)",
101464562Sgshapiro						  m->mf_name, at,
101564562Sgshapiro						  hp->h_addrtype);
101690792Sgshapiro				milter_error(m, e);
101790792Sgshapiro#  if NETINET6
101871345Sgshapiro				freehostent(hp);
101990792Sgshapiro#  endif /* NETINET6 */
102064562Sgshapiro				return -1;
102164562Sgshapiro			}
102264562Sgshapiro		}
102364562Sgshapiro	}
102464562Sgshapiro	else
102564562Sgshapiro# endif /* NETINET || NETINET6 */
102664562Sgshapiro	{
102764562Sgshapiro		if (tTd(64, 5))
102890792Sgshapiro			sm_dprintf("X%s: unknown socket protocol\n",
102990792Sgshapiro				   m->mf_name);
103064562Sgshapiro		if (parseonly)
103164562Sgshapiro			syserr("X%s: unknown socket protocol", m->mf_name);
1032110560Sgshapiro		else if (MilterLogLevel > 0)
103364562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
103490792Sgshapiro				  "Milter (%s): unknown socket protocol",
103590792Sgshapiro				  m->mf_name);
103690792Sgshapiro		milter_error(m, e);
103764562Sgshapiro		return -1;
103864562Sgshapiro	}
103964562Sgshapiro
104064562Sgshapiro	/* just parsing through? */
104164562Sgshapiro	if (parseonly)
104264562Sgshapiro	{
104364562Sgshapiro		m->mf_state = SMFS_READY;
104490792Sgshapiro# if NETINET6
104571345Sgshapiro		if (hp != NULL)
104671345Sgshapiro			freehostent(hp);
104790792Sgshapiro# endif /* NETINET6 */
104864562Sgshapiro		return 0;
104964562Sgshapiro	}
105064562Sgshapiro
105164562Sgshapiro	/* sanity check */
105264562Sgshapiro	if (m->mf_state != SMFS_READY &&
105364562Sgshapiro	    m->mf_state != SMFS_CLOSED)
105464562Sgshapiro	{
105564562Sgshapiro		/* shouldn't happen */
105664562Sgshapiro		if (tTd(64, 1))
105790792Sgshapiro			sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
105890792Sgshapiro				   m->mf_name, (char) m->mf_state);
105990792Sgshapiro		milter_error(m, e);
106090792Sgshapiro# if NETINET6
106171345Sgshapiro		if (hp != NULL)
106271345Sgshapiro			freehostent(hp);
106390792Sgshapiro# endif /* NETINET6 */
106464562Sgshapiro		return -1;
106564562Sgshapiro	}
106664562Sgshapiro
106764562Sgshapiro	/* nope, actually connecting */
106864562Sgshapiro	for (;;)
106964562Sgshapiro	{
107064562Sgshapiro		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
107164562Sgshapiro		if (sock < 0)
107264562Sgshapiro		{
107364562Sgshapiro			save_errno = errno;
107464562Sgshapiro			if (tTd(64, 5))
107590792Sgshapiro				sm_dprintf("Milter (%s): error creating socket: %s\n",
107690792Sgshapiro					   m->mf_name,
107790792Sgshapiro					   sm_errstring(save_errno));
107890792Sgshapiro			if (MilterLogLevel > 0)
107964562Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
108090792Sgshapiro					  "Milter (%s): error creating socket: %s",
108190792Sgshapiro					  m->mf_name, sm_errstring(save_errno));
108290792Sgshapiro			milter_error(m, e);
108390792Sgshapiro# if NETINET6
108471345Sgshapiro			if (hp != NULL)
108571345Sgshapiro				freehostent(hp);
108690792Sgshapiro# endif /* NETINET6 */
108764562Sgshapiro			return -1;
108864562Sgshapiro		}
108964562Sgshapiro
109080785Sgshapiro		if (setjmp(MilterConnectTimeout) == 0)
109180785Sgshapiro		{
109290792Sgshapiro			SM_EVENT *ev = NULL;
109380785Sgshapiro			int i;
109464562Sgshapiro
109580785Sgshapiro			if (m->mf_timeout[SMFTO_CONNECT] > 0)
109690792Sgshapiro				ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
109790792Sgshapiro						 milter_connect_timeout, 0);
109880785Sgshapiro
109980785Sgshapiro			i = connect(sock, (struct sockaddr *) &addr, addrlen);
110080785Sgshapiro			save_errno = errno;
110180785Sgshapiro			if (ev != NULL)
110290792Sgshapiro				sm_clrevent(ev);
110380785Sgshapiro			errno = save_errno;
110480785Sgshapiro			if (i >= 0)
110580785Sgshapiro				break;
110680785Sgshapiro		}
110780785Sgshapiro
110864562Sgshapiro		/* couldn't connect.... try next address */
110964562Sgshapiro		save_errno = errno;
111066494Sgshapiro		p = CurHostName;
111166494Sgshapiro		CurHostName = at;
111264562Sgshapiro		if (tTd(64, 5))
111390792Sgshapiro			sm_dprintf("milter_open (%s): open %s failed: %s\n",
111490792Sgshapiro				   m->mf_name, at, sm_errstring(save_errno));
111590792Sgshapiro		if (MilterLogLevel > 13)
111664562Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
111790792Sgshapiro				  "Milter (%s): open %s failed: %s",
111890792Sgshapiro				  m->mf_name, at, sm_errstring(save_errno));
111966494Sgshapiro		CurHostName = p;
112064562Sgshapiro		(void) close(sock);
112164562Sgshapiro
112264562Sgshapiro		/* try next address */
112364562Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
112464562Sgshapiro		{
112564562Sgshapiro			switch (addr.sa.sa_family)
112664562Sgshapiro			{
112764562Sgshapiro# if NETINET
112864562Sgshapiro			  case AF_INET:
112964562Sgshapiro				memmove(&addr.sin.sin_addr,
113064562Sgshapiro					hp->h_addr_list[addrno++],
113164562Sgshapiro					INADDRSZ);
113264562Sgshapiro				break;
113364562Sgshapiro# endif /* NETINET */
113464562Sgshapiro
113564562Sgshapiro# if NETINET6
113664562Sgshapiro			  case AF_INET6:
113764562Sgshapiro				memmove(&addr.sin6.sin6_addr,
113864562Sgshapiro					hp->h_addr_list[addrno++],
113964562Sgshapiro					IN6ADDRSZ);
114064562Sgshapiro				break;
114164562Sgshapiro# endif /* NETINET6 */
114264562Sgshapiro
114364562Sgshapiro			  default:
114464562Sgshapiro				if (tTd(64, 5))
114590792Sgshapiro					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
114690792Sgshapiro						   m->mf_name, at,
114790792Sgshapiro						   hp->h_addrtype);
114890792Sgshapiro				if (MilterLogLevel > 0)
114964562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
115090792Sgshapiro						  "Milter (%s): Unknown protocol for %s (%d)",
115164562Sgshapiro						  m->mf_name, at,
115264562Sgshapiro						  hp->h_addrtype);
115390792Sgshapiro				milter_error(m, e);
115490792Sgshapiro# if NETINET6
115571345Sgshapiro				freehostent(hp);
115690792Sgshapiro# endif /* NETINET6 */
115764562Sgshapiro				return -1;
115864562Sgshapiro			}
115964562Sgshapiro			continue;
116064562Sgshapiro		}
116180785Sgshapiro		p = CurHostName;
116280785Sgshapiro		CurHostName = at;
116364562Sgshapiro		if (tTd(64, 5))
116490792Sgshapiro			sm_dprintf("X%s: error connecting to filter: %s\n",
116590792Sgshapiro				   m->mf_name, sm_errstring(save_errno));
116690792Sgshapiro		if (MilterLogLevel > 0)
116764562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
116890792Sgshapiro				  "Milter (%s): error connecting to filter: %s",
116990792Sgshapiro				  m->mf_name, sm_errstring(save_errno));
117080785Sgshapiro		CurHostName = p;
117190792Sgshapiro		milter_error(m, e);
117290792Sgshapiro# if NETINET6
117371345Sgshapiro		if (hp != NULL)
117471345Sgshapiro			freehostent(hp);
117590792Sgshapiro# endif /* NETINET6 */
117664562Sgshapiro		return -1;
117764562Sgshapiro	}
117864562Sgshapiro	m->mf_state = SMFS_OPEN;
117990792Sgshapiro# if NETINET6
118071345Sgshapiro	if (hp != NULL)
118171345Sgshapiro	{
118271345Sgshapiro		freehostent(hp);
118371345Sgshapiro		hp = NULL;
118471345Sgshapiro	}
118590792Sgshapiro# endif /* NETINET6 */
1186168515Sgshapiro# if MILTER_NO_NAGLE && !defined(TCP_CORK)
1187132943Sgshapiro	{
1188132943Sgshapiro		int nodelay = 1;
1189132943Sgshapiro
1190132943Sgshapiro		setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY,
1191132943Sgshapiro			   (char *)&nodelay, sizeof(nodelay));
1192132943Sgshapiro	}
1193168515Sgshapiro# endif /* MILTER_NO_NAGLE && !defined(TCP_CORK) */
119464562Sgshapiro	return sock;
119564562Sgshapiro}
119680785Sgshapiro
119780785Sgshapirostatic void
1198141858Sgshapiromilter_connect_timeout(ignore)
1199141858Sgshapiro	int ignore;
120080785Sgshapiro{
120180785Sgshapiro	/*
120280785Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
120380785Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
120480785Sgshapiro	**	DOING.
120580785Sgshapiro	*/
120680785Sgshapiro
120780785Sgshapiro	errno = ETIMEDOUT;
120880785Sgshapiro	longjmp(MilterConnectTimeout, 1);
120980785Sgshapiro}
1210168515Sgshapiro
121190792Sgshapiro/*
121264562Sgshapiro**  MILTER_SETUP -- setup structure for a mail filter
121364562Sgshapiro**
121464562Sgshapiro**	Parameters:
121564562Sgshapiro**		line -- the options line.
121664562Sgshapiro**
121764562Sgshapiro**	Returns:
121864562Sgshapiro**		none
121964562Sgshapiro*/
122064562Sgshapiro
122164562Sgshapirovoid
122264562Sgshapiromilter_setup(line)
122364562Sgshapiro	char *line;
122464562Sgshapiro{
122564562Sgshapiro	char fcode;
1226168515Sgshapiro	char *p;
1227168515Sgshapiro	struct milter *m;
122864562Sgshapiro	STAB *s;
1229244928Sgshapiro	static int idx = 0;
123064562Sgshapiro
123166494Sgshapiro	/* collect the filter name */
123264562Sgshapiro	for (p = line;
123364562Sgshapiro	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
123464562Sgshapiro	     p++)
123564562Sgshapiro		continue;
123664562Sgshapiro	if (*p != '\0')
123764562Sgshapiro		*p++ = '\0';
123864562Sgshapiro	if (line[0] == '\0')
123964562Sgshapiro	{
124064562Sgshapiro		syserr("name required for mail filter");
124164562Sgshapiro		return;
124264562Sgshapiro	}
1243168515Sgshapiro	m = (struct milter *) xalloc(sizeof(*m));
1244168515Sgshapiro	memset((char *) m, '\0', sizeof(*m));
124564562Sgshapiro	m->mf_name = newstr(line);
124664562Sgshapiro	m->mf_state = SMFS_READY;
124764562Sgshapiro	m->mf_sock = -1;
124890792Sgshapiro	m->mf_timeout[SMFTO_CONNECT] = (time_t) 300;
124964562Sgshapiro	m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
125064562Sgshapiro	m->mf_timeout[SMFTO_READ] = (time_t) 10;
125164562Sgshapiro	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
1252168515Sgshapiro#if _FFR_MILTER_CHECK
1253168515Sgshapiro	m->mf_mta_prot_version = SMFI_PROT_VERSION;
1254168515Sgshapiro	m->mf_mta_prot_flags = SMFI_CURR_PROT;
1255168515Sgshapiro	m->mf_mta_actions = SMFI_CURR_ACTS;
1256168515Sgshapiro#endif /* _FFR_MILTER_CHECK */
125764562Sgshapiro
125864562Sgshapiro	/* now scan through and assign info from the fields */
125964562Sgshapiro	while (*p != '\0')
126064562Sgshapiro	{
126164562Sgshapiro		char *delimptr;
126264562Sgshapiro
126364562Sgshapiro		while (*p != '\0' &&
126464562Sgshapiro		       (*p == ',' || (isascii(*p) && isspace(*p))))
126564562Sgshapiro			p++;
126664562Sgshapiro
126764562Sgshapiro		/* p now points to field code */
126864562Sgshapiro		fcode = *p;
126964562Sgshapiro		while (*p != '\0' && *p != '=' && *p != ',')
127064562Sgshapiro			p++;
127164562Sgshapiro		if (*p++ != '=')
127264562Sgshapiro		{
127364562Sgshapiro			syserr("X%s: `=' expected", m->mf_name);
127464562Sgshapiro			return;
127564562Sgshapiro		}
127664562Sgshapiro		while (isascii(*p) && isspace(*p))
127764562Sgshapiro			p++;
127864562Sgshapiro
127964562Sgshapiro		/* p now points to the field body */
128064562Sgshapiro		p = munchstring(p, &delimptr, ',');
128164562Sgshapiro
128266494Sgshapiro		/* install the field into the filter struct */
128364562Sgshapiro		switch (fcode)
128464562Sgshapiro		{
128564562Sgshapiro		  case 'S':		/* socket */
128664562Sgshapiro			if (p == NULL)
128764562Sgshapiro				m->mf_conn = NULL;
128864562Sgshapiro			else
128964562Sgshapiro				m->mf_conn = newstr(p);
129064562Sgshapiro			break;
129164562Sgshapiro
129264562Sgshapiro		  case 'F':		/* Milter flags configured on MTA */
129364562Sgshapiro			for (; *p != '\0'; p++)
129464562Sgshapiro			{
129564562Sgshapiro				if (!(isascii(*p) && isspace(*p)))
129671345Sgshapiro					setbitn(bitidx(*p), m->mf_flags);
129764562Sgshapiro			}
129864562Sgshapiro			break;
129964562Sgshapiro
130064562Sgshapiro		  case 'T':		/* timeouts */
130164562Sgshapiro			milter_parse_timeouts(p, m);
130264562Sgshapiro			break;
130364562Sgshapiro
1304168515Sgshapiro#if _FFR_MILTER_CHECK
1305168515Sgshapiro		  case 'a':
1306168515Sgshapiro			m->mf_mta_actions = strtoul(p, NULL, 0);
1307168515Sgshapiro			break;
1308168515Sgshapiro		  case 'f':
1309168515Sgshapiro			m->mf_mta_prot_flags = strtoul(p, NULL, 0);
1310168515Sgshapiro			break;
1311168515Sgshapiro		  case 'v':
1312168515Sgshapiro			m->mf_mta_prot_version = strtoul(p, NULL, 0);
1313168515Sgshapiro			break;
1314168515Sgshapiro#endif /* _FFR_MILTER_CHECK */
1315168515Sgshapiro
131664562Sgshapiro		  default:
131764562Sgshapiro			syserr("X%s: unknown filter equate %c=",
131864562Sgshapiro			       m->mf_name, fcode);
131964562Sgshapiro			break;
132064562Sgshapiro		}
132164562Sgshapiro		p = delimptr;
132264562Sgshapiro	}
132364562Sgshapiro
132464562Sgshapiro	/* early check for errors */
132590792Sgshapiro	(void) milter_open(m, true, CurEnv);
132664562Sgshapiro
132766494Sgshapiro	/* enter the filter into the symbol table */
132864562Sgshapiro	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
132964562Sgshapiro	if (s->s_milter != NULL)
133064562Sgshapiro		syserr("X%s: duplicate filter definition", m->mf_name);
133164562Sgshapiro	else
1332244928Sgshapiro	{
133364562Sgshapiro		s->s_milter = m;
1334244928Sgshapiro		m->mf_idx = ++idx;
1335244928Sgshapiro	}
133664562Sgshapiro}
1337168515Sgshapiro
133890792Sgshapiro/*
133990792Sgshapiro**  MILTER_CONFIG -- parse option list into an array and check config
134064562Sgshapiro**
134164562Sgshapiro**	Called when reading configuration file.
134264562Sgshapiro**
134364562Sgshapiro**	Parameters:
134464562Sgshapiro**		spec -- the filter list.
134564562Sgshapiro**		list -- the array to fill in.
134664562Sgshapiro**		max -- the maximum number of entries in list.
134764562Sgshapiro**
134864562Sgshapiro**	Returns:
134964562Sgshapiro**		none
135064562Sgshapiro*/
135164562Sgshapiro
135264562Sgshapirovoid
135390792Sgshapiromilter_config(spec, list, max)
135464562Sgshapiro	char *spec;
135564562Sgshapiro	struct milter **list;
135664562Sgshapiro	int max;
135764562Sgshapiro{
135864562Sgshapiro	int numitems = 0;
1359168515Sgshapiro	char *p;
136064562Sgshapiro
136164562Sgshapiro	/* leave one for the NULL signifying the end of the list */
136264562Sgshapiro	max--;
136364562Sgshapiro
136464562Sgshapiro	for (p = spec; p != NULL; )
136564562Sgshapiro	{
136664562Sgshapiro		STAB *s;
136764562Sgshapiro
136864562Sgshapiro		while (isascii(*p) && isspace(*p))
136964562Sgshapiro			p++;
137064562Sgshapiro		if (*p == '\0')
137164562Sgshapiro			break;
137264562Sgshapiro		spec = p;
137364562Sgshapiro
137464562Sgshapiro		if (numitems >= max)
137564562Sgshapiro		{
137664562Sgshapiro			syserr("Too many filters defined, %d max", max);
137764562Sgshapiro			if (max > 0)
137864562Sgshapiro				list[0] = NULL;
137964562Sgshapiro			return;
138064562Sgshapiro		}
138190792Sgshapiro		p = strpbrk(p, ";,");
138264562Sgshapiro		if (p != NULL)
138364562Sgshapiro			*p++ = '\0';
138464562Sgshapiro
138564562Sgshapiro		s = stab(spec, ST_MILTER, ST_FIND);
138664562Sgshapiro		if (s == NULL)
138764562Sgshapiro		{
138864562Sgshapiro			syserr("InputFilter %s not defined", spec);
138964562Sgshapiro			ExitStat = EX_CONFIG;
139064562Sgshapiro			return;
139164562Sgshapiro		}
139264562Sgshapiro		list[numitems++] = s->s_milter;
139364562Sgshapiro	}
139464562Sgshapiro	list[numitems] = NULL;
139590792Sgshapiro
139690792Sgshapiro	/* if not set, set to LogLevel */
139790792Sgshapiro	if (MilterLogLevel == -1)
139890792Sgshapiro		MilterLogLevel = LogLevel;
139964562Sgshapiro}
1400168515Sgshapiro
140190792Sgshapiro/*
140264562Sgshapiro**  MILTER_PARSE_TIMEOUTS -- parse timeout list
140364562Sgshapiro**
140464562Sgshapiro**	Called when reading configuration file.
140564562Sgshapiro**
140664562Sgshapiro**	Parameters:
140764562Sgshapiro**		spec -- the timeout list.
140864562Sgshapiro**		m -- milter to set.
140964562Sgshapiro**
141064562Sgshapiro**	Returns:
141164562Sgshapiro**		none
141264562Sgshapiro*/
141364562Sgshapiro
141464562Sgshapirostatic void
141564562Sgshapiromilter_parse_timeouts(spec, m)
141664562Sgshapiro	char *spec;
141764562Sgshapiro	struct milter *m;
141864562Sgshapiro{
141964562Sgshapiro	char fcode;
1420132943Sgshapiro	int tcode;
1421168515Sgshapiro	char *p;
142264562Sgshapiro
142364562Sgshapiro	p = spec;
142464562Sgshapiro
142564562Sgshapiro	/* now scan through and assign info from the fields */
142664562Sgshapiro	while (*p != '\0')
142764562Sgshapiro	{
142864562Sgshapiro		char *delimptr;
142964562Sgshapiro
143064562Sgshapiro		while (*p != '\0' &&
143164562Sgshapiro		       (*p == ';' || (isascii(*p) && isspace(*p))))
143264562Sgshapiro			p++;
143364562Sgshapiro
143464562Sgshapiro		/* p now points to field code */
143564562Sgshapiro		fcode = *p;
143664562Sgshapiro		while (*p != '\0' && *p != ':')
143764562Sgshapiro			p++;
143864562Sgshapiro		if (*p++ != ':')
143964562Sgshapiro		{
144064562Sgshapiro			syserr("X%s, T=: `:' expected", m->mf_name);
144164562Sgshapiro			return;
144264562Sgshapiro		}
144364562Sgshapiro		while (isascii(*p) && isspace(*p))
144464562Sgshapiro			p++;
144564562Sgshapiro
144664562Sgshapiro		/* p now points to the field body */
144764562Sgshapiro		p = munchstring(p, &delimptr, ';');
1448132943Sgshapiro		tcode = -1;
144964562Sgshapiro
145066494Sgshapiro		/* install the field into the filter struct */
145164562Sgshapiro		switch (fcode)
145264562Sgshapiro		{
145382017Sgshapiro		  case 'C':
1454132943Sgshapiro			tcode = SMFTO_CONNECT;
145582017Sgshapiro			break;
145682017Sgshapiro
145764562Sgshapiro		  case 'S':
1458132943Sgshapiro			tcode = SMFTO_WRITE;
145964562Sgshapiro			break;
146064562Sgshapiro
146164562Sgshapiro		  case 'R':
1462132943Sgshapiro			tcode = SMFTO_READ;
146364562Sgshapiro			break;
146464562Sgshapiro
146564562Sgshapiro		  case 'E':
1466132943Sgshapiro			tcode = SMFTO_EOM;
146764562Sgshapiro			break;
146864562Sgshapiro
146964562Sgshapiro		  default:
147064562Sgshapiro			if (tTd(64, 5))
147190792Sgshapiro				sm_dprintf("X%s: %c unknown\n",
147290792Sgshapiro					   m->mf_name, fcode);
147364562Sgshapiro			syserr("X%s: unknown filter timeout %c",
147464562Sgshapiro			       m->mf_name, fcode);
147564562Sgshapiro			break;
147664562Sgshapiro		}
1477132943Sgshapiro		if (tcode >= 0)
1478132943Sgshapiro		{
1479132943Sgshapiro			m->mf_timeout[tcode] = convtime(p, 's');
1480132943Sgshapiro			if (tTd(64, 5))
1481132943Sgshapiro				sm_dprintf("X%s: %c=%ld\n",
1482132943Sgshapiro					   m->mf_name, fcode,
1483132943Sgshapiro					   (u_long) m->mf_timeout[tcode]);
1484132943Sgshapiro		}
148564562Sgshapiro		p = delimptr;
148664562Sgshapiro	}
148764562Sgshapiro}
1488168515Sgshapiro
148990792Sgshapiro/*
1490168515Sgshapiro**  MILTER_SET_MACROS -- set milter macros
1491168515Sgshapiro**
1492168515Sgshapiro**	Parameters:
1493168515Sgshapiro**		name -- name of milter.
1494168515Sgshapiro**		macros -- where to store macros.
1495168515Sgshapiro**		val -- the value of the option.
1496168515Sgshapiro**		nummac -- current number of macros
1497168515Sgshapiro**
1498168515Sgshapiro**	Returns:
1499168515Sgshapiro**		new number of macros
1500168515Sgshapiro*/
1501168515Sgshapiro
1502168515Sgshapirostatic int
1503168515Sgshapiromilter_set_macros(name, macros, val, nummac)
1504168515Sgshapiro	char *name;
1505168515Sgshapiro	char **macros;
1506168515Sgshapiro	char *val;
1507168515Sgshapiro	int nummac;
1508168515Sgshapiro{
1509168515Sgshapiro	char *p;
1510168515Sgshapiro
1511168515Sgshapiro	p = newstr(val);
1512168515Sgshapiro	while (*p != '\0')
1513168515Sgshapiro	{
1514168515Sgshapiro		char *macro;
1515168515Sgshapiro
1516168515Sgshapiro		/* Skip leading commas, spaces */
1517168515Sgshapiro		while (*p != '\0' &&
1518168515Sgshapiro		       (*p == ',' || (isascii(*p) && isspace(*p))))
1519168515Sgshapiro			p++;
1520168515Sgshapiro
1521168515Sgshapiro		if (*p == '\0')
1522168515Sgshapiro			break;
1523168515Sgshapiro
1524168515Sgshapiro		/* Find end of macro */
1525168515Sgshapiro		macro = p;
1526168515Sgshapiro		while (*p != '\0' && *p != ',' &&
1527168515Sgshapiro		       isascii(*p) && !isspace(*p))
1528168515Sgshapiro			p++;
1529168515Sgshapiro		if (*p != '\0')
1530168515Sgshapiro			*p++ = '\0';
1531168515Sgshapiro
1532168515Sgshapiro		if (nummac >= MAXFILTERMACROS)
1533168515Sgshapiro		{
1534168515Sgshapiro			syserr("milter_set_option: too many macros in Milter.%s (max %d)",
1535168515Sgshapiro			       name, MAXFILTERMACROS);
1536168515Sgshapiro			macros[nummac] = NULL;
1537168515Sgshapiro			return -1;
1538168515Sgshapiro		}
1539168515Sgshapiro		macros[nummac++] = macro;
1540168515Sgshapiro	}
1541168515Sgshapiro	macros[nummac] = NULL;
1542168515Sgshapiro	return nummac;
1543168515Sgshapiro}
1544168515Sgshapiro
1545168515Sgshapiro/*
154664562Sgshapiro**  MILTER_SET_OPTION -- set an individual milter option
154764562Sgshapiro**
154864562Sgshapiro**	Parameters:
154964562Sgshapiro**		name -- the name of the option.
155064562Sgshapiro**		val -- the value of the option.
155164562Sgshapiro**		sticky -- if set, don't let other setoptions override
155264562Sgshapiro**			this value.
155364562Sgshapiro**
155464562Sgshapiro**	Returns:
155564562Sgshapiro**		none.
155664562Sgshapiro*/
155764562Sgshapiro
155864562Sgshapiro/* set if Milter sub-option is stuck */
155964562Sgshapirostatic BITMAP256	StickyMilterOpt;
156064562Sgshapiro
156164562Sgshapirostatic struct milteropt
156264562Sgshapiro{
156390792Sgshapiro	char		*mo_name;	/* long name of milter option */
156490792Sgshapiro	unsigned char	mo_code;	/* code for option */
156564562Sgshapiro} MilterOptTab[] =
156664562Sgshapiro{
1567244928Sgshapiro	{ "macros.connect",		SMFIM_CONNECT			},
1568244928Sgshapiro	{ "macros.helo",		SMFIM_HELO			},
1569244928Sgshapiro	{ "macros.envfrom",		SMFIM_ENVFROM			},
1570244928Sgshapiro	{ "macros.envrcpt",		SMFIM_ENVRCPT			},
1571244928Sgshapiro	{ "macros.data",		SMFIM_DATA			},
1572244928Sgshapiro	{ "macros.eom",			SMFIM_EOM			},
1573244928Sgshapiro	{ "macros.eoh",			SMFIM_EOH			},
1574168515Sgshapiro
1575132943Sgshapiro# define MO_LOGLEVEL			0x07
1576132943Sgshapiro	{ "loglevel",			MO_LOGLEVEL			},
1577203004Sgshapiro# if _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE
1578168515Sgshapiro#  define MO_MAXDATASIZE		0x08
1579132943Sgshapiro	{ "maxdatasize",		MO_MAXDATASIZE			},
1580203004Sgshapiro# endif /* _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE */
1581168515Sgshapiro	{ NULL,				(unsigned char)-1		},
158264562Sgshapiro};
158364562Sgshapiro
158464562Sgshapirovoid
158564562Sgshapiromilter_set_option(name, val, sticky)
158664562Sgshapiro	char *name;
158764562Sgshapiro	char *val;
158864562Sgshapiro	bool sticky;
158964562Sgshapiro{
1590168515Sgshapiro	int nummac, r;
1591168515Sgshapiro	struct milteropt *mo;
159264562Sgshapiro	char **macros = NULL;
159364562Sgshapiro
1594168515Sgshapiro	nummac = 0;
159564562Sgshapiro	if (tTd(37, 2) || tTd(64, 5))
159690792Sgshapiro		sm_dprintf("milter_set_option(%s = %s)", name, val);
159764562Sgshapiro
159898841Sgshapiro	if (name == NULL)
159998841Sgshapiro	{
160098841Sgshapiro		syserr("milter_set_option: invalid Milter option, must specify suboption");
160198841Sgshapiro		return;
160298841Sgshapiro	}
160398841Sgshapiro
160464562Sgshapiro	for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
160564562Sgshapiro	{
160690792Sgshapiro		if (sm_strcasecmp(mo->mo_name, name) == 0)
160764562Sgshapiro			break;
160864562Sgshapiro	}
160964562Sgshapiro
161064562Sgshapiro	if (mo->mo_name == NULL)
161190792Sgshapiro	{
161264562Sgshapiro		syserr("milter_set_option: invalid Milter option %s", name);
161390792Sgshapiro		return;
161490792Sgshapiro	}
161564562Sgshapiro
161664562Sgshapiro	/*
161764562Sgshapiro	**  See if this option is preset for us.
161864562Sgshapiro	*/
161964562Sgshapiro
162064562Sgshapiro	if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
162164562Sgshapiro	{
162264562Sgshapiro		if (tTd(37, 2) || tTd(64,5))
162390792Sgshapiro			sm_dprintf(" (ignored)\n");
162464562Sgshapiro		return;
162564562Sgshapiro	}
162664562Sgshapiro
162764562Sgshapiro	if (tTd(37, 2) || tTd(64,5))
162890792Sgshapiro		sm_dprintf("\n");
162964562Sgshapiro
163064562Sgshapiro	switch (mo->mo_code)
163164562Sgshapiro	{
163290792Sgshapiro	  case MO_LOGLEVEL:
163390792Sgshapiro		MilterLogLevel = atoi(val);
163490792Sgshapiro		break;
163590792Sgshapiro
1636203004Sgshapiro# if _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE
1637132943Sgshapiro	  case MO_MAXDATASIZE:
1638203004Sgshapiro#  if _FFR_MDS_NEGOTIATE
1639132943Sgshapiro		MilterMaxDataSize = (size_t)atol(val);
1640203004Sgshapiro		if (MilterMaxDataSize != MILTER_MDS_64K &&
1641203004Sgshapiro		    MilterMaxDataSize != MILTER_MDS_256K &&
1642203004Sgshapiro		    MilterMaxDataSize != MILTER_MDS_1M)
1643203004Sgshapiro		{
1644203004Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
1645203004Sgshapiro				"WARNING: Milter.%s=%d, allowed are only %d, %d, and %d",
1646203004Sgshapiro				name, MilterMaxDataSize,
1647203004Sgshapiro				MILTER_MDS_64K, MILTER_MDS_256K,
1648203004Sgshapiro				MILTER_MDS_1M);
1649203004Sgshapiro			if (MilterMaxDataSize < MILTER_MDS_64K)
1650203004Sgshapiro				MilterMaxDataSize = MILTER_MDS_64K;
1651203004Sgshapiro			else if (MilterMaxDataSize < MILTER_MDS_256K)
1652203004Sgshapiro				MilterMaxDataSize = MILTER_MDS_256K;
1653203004Sgshapiro			else
1654203004Sgshapiro				MilterMaxDataSize = MILTER_MDS_1M;
1655203004Sgshapiro		}
1656203004Sgshapiro#  endif /* _FFR_MDS_NEGOTIATE */
1657132943Sgshapiro		break;
1658203004Sgshapiro# endif /* _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE */
1659132943Sgshapiro
1660244928Sgshapiro	  case SMFIM_CONNECT:
1661244928Sgshapiro	  case SMFIM_HELO:
1662244928Sgshapiro	  case SMFIM_ENVFROM:
1663244928Sgshapiro	  case SMFIM_ENVRCPT:
1664244928Sgshapiro	  case SMFIM_EOH:
1665244928Sgshapiro	  case SMFIM_EOM:
1666244928Sgshapiro	  case SMFIM_DATA:
1667244928Sgshapiro		macros = MilterMacros[mo->mo_code][0];
166864562Sgshapiro
1669168515Sgshapiro		r = milter_set_macros(name, macros, val, nummac);
1670168515Sgshapiro		if (r >= 0)
1671168515Sgshapiro			nummac = r;
167264562Sgshapiro		break;
167364562Sgshapiro
167464562Sgshapiro	  default:
167564562Sgshapiro		syserr("milter_set_option: invalid Milter option %s", name);
167664562Sgshapiro		break;
167764562Sgshapiro	}
167864562Sgshapiro	if (sticky)
167964562Sgshapiro		setbitn(mo->mo_code, StickyMilterOpt);
168064562Sgshapiro}
1681168515Sgshapiro
168290792Sgshapiro/*
168390792Sgshapiro**  MILTER_REOPEN_DF -- open & truncate the data file (for replbody)
168464562Sgshapiro**
168564562Sgshapiro**	Parameters:
168664562Sgshapiro**		e -- current envelope.
168764562Sgshapiro**
168864562Sgshapiro**	Returns:
168964562Sgshapiro**		0 if succesful, -1 otherwise
169064562Sgshapiro*/
169164562Sgshapiro
169264562Sgshapirostatic int
169364562Sgshapiromilter_reopen_df(e)
169464562Sgshapiro	ENVELOPE *e;
169564562Sgshapiro{
169664562Sgshapiro	char dfname[MAXPATHLEN];
169764562Sgshapiro
1698168515Sgshapiro	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
169964562Sgshapiro
170064562Sgshapiro	/*
170190792Sgshapiro	**  In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so
170264562Sgshapiro	**  close and reopen writable (later close and reopen
170364562Sgshapiro	**  read only again).
170464562Sgshapiro	**
170590792Sgshapiro	**  In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the
1706132943Sgshapiro	**  buffered file I/O descriptor, still open for writing so there
1707132943Sgshapiro	**  isn't any work to do here (except checking for consistency).
170864562Sgshapiro	*/
170964562Sgshapiro
171090792Sgshapiro	if (SuperSafe == SAFE_REALLY)
171164562Sgshapiro	{
171290792Sgshapiro		/* close read-only data file */
171364562Sgshapiro		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
171464562Sgshapiro		{
171590792Sgshapiro			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
171664562Sgshapiro			e->e_flags &= ~EF_HAS_DF;
171764562Sgshapiro		}
171864562Sgshapiro
171964562Sgshapiro		/* open writable */
172090792Sgshapiro		if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
1721120256Sgshapiro					   SM_IO_RDWR_B, NULL)) == NULL)
172264562Sgshapiro		{
172390792Sgshapiro			MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s");
172464562Sgshapiro			return -1;
172564562Sgshapiro		}
172664562Sgshapiro	}
172764562Sgshapiro	else if (e->e_dfp == NULL)
172864562Sgshapiro	{
172964562Sgshapiro		/* shouldn't happen */
173064562Sgshapiro		errno = ENOENT;
173164562Sgshapiro		MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
173264562Sgshapiro		return -1;
173364562Sgshapiro	}
173464562Sgshapiro	return 0;
173564562Sgshapiro}
1736168515Sgshapiro
173790792Sgshapiro/*
173890792Sgshapiro**  MILTER_RESET_DF -- re-open read-only the data file (for replbody)
173964562Sgshapiro**
174064562Sgshapiro**	Parameters:
174164562Sgshapiro**		e -- current envelope.
174264562Sgshapiro**
174364562Sgshapiro**	Returns:
174464562Sgshapiro**		0 if succesful, -1 otherwise
174564562Sgshapiro*/
174664562Sgshapiro
174764562Sgshapirostatic int
174864562Sgshapiromilter_reset_df(e)
174964562Sgshapiro	ENVELOPE *e;
175064562Sgshapiro{
175164562Sgshapiro	int afd;
175264562Sgshapiro	char dfname[MAXPATHLEN];
175364562Sgshapiro
1754168515Sgshapiro	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
175564562Sgshapiro
175690792Sgshapiro	if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 ||
175790792Sgshapiro	    sm_io_error(e->e_dfp))
175864562Sgshapiro	{
175964562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
176064562Sgshapiro		return -1;
176164562Sgshapiro	}
176290792Sgshapiro	else if (SuperSafe != SAFE_REALLY)
176364562Sgshapiro	{
176464562Sgshapiro		/* skip next few clauses */
176564562Sgshapiro		/* EMPTY */
176664562Sgshapiro	}
176790792Sgshapiro	else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0
176890792Sgshapiro		 && fsync(afd) < 0)
176964562Sgshapiro	{
177064562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
177164562Sgshapiro		return -1;
177264562Sgshapiro	}
177390792Sgshapiro	else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0)
177464562Sgshapiro	{
177564562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
177664562Sgshapiro		return -1;
177764562Sgshapiro	}
177890792Sgshapiro	else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
1779120256Sgshapiro					SM_IO_RDONLY_B, NULL)) == NULL)
178064562Sgshapiro	{
178164562Sgshapiro		MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
178264562Sgshapiro		return -1;
178364562Sgshapiro	}
178464562Sgshapiro	else
178564562Sgshapiro		e->e_flags |= EF_HAS_DF;
178664562Sgshapiro	return 0;
178764562Sgshapiro}
1788168515Sgshapiro
178990792Sgshapiro/*
179064562Sgshapiro**  MILTER_QUIT_FILTER -- close down a single filter
179164562Sgshapiro**
179264562Sgshapiro**	Parameters:
179364562Sgshapiro**		m -- milter structure of filter to close down.
179464562Sgshapiro**		e -- current envelope.
179564562Sgshapiro**
179664562Sgshapiro**	Returns:
179764562Sgshapiro**		none
179864562Sgshapiro*/
179964562Sgshapiro
180064562Sgshapirostatic void
180164562Sgshapiromilter_quit_filter(m, e)
180264562Sgshapiro	struct milter *m;
180364562Sgshapiro	ENVELOPE *e;
180464562Sgshapiro{
180564562Sgshapiro	if (tTd(64, 10))
180690792Sgshapiro		sm_dprintf("milter_quit_filter(%s)\n", m->mf_name);
180790792Sgshapiro	if (MilterLogLevel > 18)
180890792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter",
180990792Sgshapiro			  m->mf_name);
181064562Sgshapiro
181164562Sgshapiro	/* Never replace error state */
181264562Sgshapiro	if (m->mf_state == SMFS_ERROR)
181364562Sgshapiro		return;
181464562Sgshapiro
181564562Sgshapiro	if (m->mf_sock < 0 ||
181664562Sgshapiro	    m->mf_state == SMFS_CLOSED ||
181764562Sgshapiro	    m->mf_state == SMFS_READY)
181864562Sgshapiro	{
181964562Sgshapiro		m->mf_sock = -1;
182064562Sgshapiro		m->mf_state = SMFS_CLOSED;
182164562Sgshapiro		return;
182264562Sgshapiro	}
182364562Sgshapiro
182464562Sgshapiro	(void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
1825168515Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e, "quit_filter");
182671345Sgshapiro	if (m->mf_sock >= 0)
182771345Sgshapiro	{
182871345Sgshapiro		(void) close(m->mf_sock);
182971345Sgshapiro		m->mf_sock = -1;
183071345Sgshapiro	}
183164562Sgshapiro	if (m->mf_state != SMFS_ERROR)
183264562Sgshapiro		m->mf_state = SMFS_CLOSED;
183364562Sgshapiro}
1834168515Sgshapiro
183590792Sgshapiro/*
183664562Sgshapiro**  MILTER_ABORT_FILTER -- tell filter to abort current message
183764562Sgshapiro**
183864562Sgshapiro**	Parameters:
183964562Sgshapiro**		m -- milter structure of filter to abort.
184064562Sgshapiro**		e -- current envelope.
184164562Sgshapiro**
184264562Sgshapiro**	Returns:
184364562Sgshapiro**		none
184464562Sgshapiro*/
184564562Sgshapiro
184664562Sgshapirostatic void
184764562Sgshapiromilter_abort_filter(m, e)
184864562Sgshapiro	struct milter *m;
184964562Sgshapiro	ENVELOPE *e;
185064562Sgshapiro{
185164562Sgshapiro	if (tTd(64, 10))
185290792Sgshapiro		sm_dprintf("milter_abort_filter(%s)\n", m->mf_name);
185390792Sgshapiro	if (MilterLogLevel > 10)
185490792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter",
185590792Sgshapiro			  m->mf_name);
185664562Sgshapiro
185764562Sgshapiro	if (m->mf_sock < 0 ||
185864562Sgshapiro	    m->mf_state != SMFS_INMSG)
185964562Sgshapiro		return;
186064562Sgshapiro
186164562Sgshapiro	(void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
1862168515Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e, "abort_filter");
186364562Sgshapiro	if (m->mf_state != SMFS_ERROR)
186464562Sgshapiro		m->mf_state = SMFS_DONE;
186564562Sgshapiro}
1866168515Sgshapiro
186790792Sgshapiro/*
186864562Sgshapiro**  MILTER_SEND_MACROS -- provide macros to the filters
186964562Sgshapiro**
187064562Sgshapiro**	Parameters:
187164562Sgshapiro**		m -- milter to send macros to.
187264562Sgshapiro**		macros -- macros to send for filter smfi_getsymval().
187364562Sgshapiro**		cmd -- which command the macros are associated with.
187464562Sgshapiro**		e -- current envelope (for macro access).
187564562Sgshapiro**
187664562Sgshapiro**	Returns:
187764562Sgshapiro**		none
187864562Sgshapiro*/
187964562Sgshapiro
188064562Sgshapirostatic void
188164562Sgshapiromilter_send_macros(m, macros, cmd, e)
188264562Sgshapiro	struct milter *m;
188364562Sgshapiro	char **macros;
1884168515Sgshapiro	int cmd;
188564562Sgshapiro	ENVELOPE *e;
188664562Sgshapiro{
188764562Sgshapiro	int i;
188864562Sgshapiro	int mid;
1889168515Sgshapiro	char command = (char) cmd;
189064562Sgshapiro	char *v;
189164562Sgshapiro	char *buf, *bp;
189295154Sgshapiro	char exp[MAXLINE];
189364562Sgshapiro	ssize_t s;
189464562Sgshapiro
189564562Sgshapiro	/* sanity check */
189664562Sgshapiro	if (macros == NULL || macros[0] == NULL)
189764562Sgshapiro		return;
189864562Sgshapiro
189964562Sgshapiro	/* put together data */
190064562Sgshapiro	s = 1;			/* for the command character */
190164562Sgshapiro	for (i = 0; macros[i] != NULL; i++)
190264562Sgshapiro	{
190390792Sgshapiro		mid = macid(macros[i]);
190471345Sgshapiro		if (mid == 0)
190564562Sgshapiro			continue;
190664562Sgshapiro		v = macvalue(mid, e);
190764562Sgshapiro		if (v == NULL)
190864562Sgshapiro			continue;
190995154Sgshapiro		expand(v, exp, sizeof(exp), e);
191095154Sgshapiro		s += strlen(macros[i]) + 1 + strlen(exp) + 1;
191164562Sgshapiro	}
191264562Sgshapiro
191390792Sgshapiro	if (s < 0)
191490792Sgshapiro		return;
191590792Sgshapiro
191690792Sgshapiro	buf = (char *) xalloc(s);
191764562Sgshapiro	bp = buf;
1918168515Sgshapiro	*bp++ = command;
191964562Sgshapiro	for (i = 0; macros[i] != NULL; i++)
192064562Sgshapiro	{
192190792Sgshapiro		mid = macid(macros[i]);
192271345Sgshapiro		if (mid == 0)
192364562Sgshapiro			continue;
192464562Sgshapiro		v = macvalue(mid, e);
192564562Sgshapiro		if (v == NULL)
192664562Sgshapiro			continue;
192795154Sgshapiro		expand(v, exp, sizeof(exp), e);
192864562Sgshapiro
192964562Sgshapiro		if (tTd(64, 10))
193090792Sgshapiro			sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
1931168515Sgshapiro				m->mf_name, command, macros[i], exp);
193264562Sgshapiro
193390792Sgshapiro		(void) sm_strlcpy(bp, macros[i], s - (bp - buf));
193464562Sgshapiro		bp += strlen(bp) + 1;
193595154Sgshapiro		(void) sm_strlcpy(bp, exp, s - (bp - buf));
193664562Sgshapiro		bp += strlen(bp) + 1;
193764562Sgshapiro	}
193864562Sgshapiro	(void) milter_write(m, SMFIC_MACRO, buf, s,
1939168515Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e, "send_macros");
1940147078Sgshapiro	sm_free(buf);
194164562Sgshapiro}
194264562Sgshapiro
194390792Sgshapiro/*
194464562Sgshapiro**  MILTER_SEND_COMMAND -- send a command and return the response for a filter
194564562Sgshapiro**
194664562Sgshapiro**	Parameters:
194764562Sgshapiro**		m -- current milter filter
1948168515Sgshapiro**		cmd -- command to send.
194964562Sgshapiro**		data -- optional command data.
195064562Sgshapiro**		sz -- length of buf.
195164562Sgshapiro**		e -- current envelope (for e->e_id).
195264562Sgshapiro**		state -- return state word.
195364562Sgshapiro**
195464562Sgshapiro**	Returns:
195564562Sgshapiro**		response string (may be NULL)
195664562Sgshapiro*/
195764562Sgshapiro
195864562Sgshapirostatic char *
1959168515Sgshapiromilter_send_command(m, cmd, data, sz, e, state, where)
196064562Sgshapiro	struct milter *m;
1961168515Sgshapiro	int cmd;
196264562Sgshapiro	void *data;
196364562Sgshapiro	ssize_t sz;
196464562Sgshapiro	ENVELOPE *e;
196564562Sgshapiro	char *state;
1966168515Sgshapiro	const char *where;
196764562Sgshapiro{
196864562Sgshapiro	char rcmd;
196964562Sgshapiro	ssize_t rlen;
197090792Sgshapiro	unsigned long skipflag;
1971132943Sgshapiro	unsigned long norespflag = 0;
1972168515Sgshapiro	char command = (char) cmd;
197390792Sgshapiro	char *action;
197464562Sgshapiro	char *defresponse;
197564562Sgshapiro	char *response;
197664562Sgshapiro
197764562Sgshapiro	if (tTd(64, 10))
197890792Sgshapiro		sm_dprintf("milter_send_command(%s): cmd %c len %ld\n",
197964562Sgshapiro			m->mf_name, (char) command, (long) sz);
198064562Sgshapiro
198164562Sgshapiro	/* find skip flag and default failure */
198264562Sgshapiro	switch (command)
198364562Sgshapiro	{
198464562Sgshapiro	  case SMFIC_CONNECT:
198564562Sgshapiro		skipflag = SMFIP_NOCONNECT;
1986168515Sgshapiro		norespflag = SMFIP_NR_CONN;
198790792Sgshapiro		action = "connect";
198864562Sgshapiro		defresponse = "554 Command rejected";
198964562Sgshapiro		break;
199064562Sgshapiro
199164562Sgshapiro	  case SMFIC_HELO:
199264562Sgshapiro		skipflag = SMFIP_NOHELO;
1993168515Sgshapiro		norespflag = SMFIP_NR_HELO;
199490792Sgshapiro		action = "helo";
199564562Sgshapiro		defresponse = "550 Command rejected";
199664562Sgshapiro		break;
199764562Sgshapiro
199864562Sgshapiro	  case SMFIC_MAIL:
199964562Sgshapiro		skipflag = SMFIP_NOMAIL;
2000168515Sgshapiro		norespflag = SMFIP_NR_MAIL;
200190792Sgshapiro		action = "mail";
200264562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
200364562Sgshapiro		break;
200464562Sgshapiro
200564562Sgshapiro	  case SMFIC_RCPT:
200664562Sgshapiro		skipflag = SMFIP_NORCPT;
2007168515Sgshapiro		norespflag = SMFIP_NR_RCPT;
200890792Sgshapiro		action = "rcpt";
200964562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
201064562Sgshapiro		break;
201164562Sgshapiro
201264562Sgshapiro	  case SMFIC_HEADER:
201364562Sgshapiro		skipflag = SMFIP_NOHDRS;
2014168515Sgshapiro		norespflag = SMFIP_NR_HDR;
201590792Sgshapiro		action = "header";
201664562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
201764562Sgshapiro		break;
201864562Sgshapiro
201964562Sgshapiro	  case SMFIC_BODY:
202064562Sgshapiro		skipflag = SMFIP_NOBODY;
2021168515Sgshapiro		norespflag = SMFIP_NR_BODY;
202290792Sgshapiro		action = "body";
202364562Sgshapiro		defresponse = "554 5.7.1 Command rejected";
202464562Sgshapiro		break;
202564562Sgshapiro
202664562Sgshapiro	  case SMFIC_EOH:
202764562Sgshapiro		skipflag = SMFIP_NOEOH;
2028168515Sgshapiro		norespflag = SMFIP_NR_EOH;
202990792Sgshapiro		action = "eoh";
203064562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
203164562Sgshapiro		break;
203264562Sgshapiro
2033132943Sgshapiro	  case SMFIC_UNKNOWN:
2034157001Sgshapiro		skipflag = SMFIP_NOUNKNOWN;
2035168515Sgshapiro		norespflag = SMFIP_NR_UNKN;
2036132943Sgshapiro		action = "unknown";
2037132943Sgshapiro		defresponse = "550 5.7.1 Command rejected";
2038132943Sgshapiro		break;
2039132943Sgshapiro
2040157001Sgshapiro	  case SMFIC_DATA:
2041157001Sgshapiro		skipflag = SMFIP_NODATA;
2042168515Sgshapiro		norespflag = SMFIP_NR_DATA;
2043157001Sgshapiro		action = "data";
2044157001Sgshapiro		defresponse = "550 5.7.1 Command rejected";
2045157001Sgshapiro		break;
2046157001Sgshapiro
204764562Sgshapiro	  case SMFIC_BODYEOB:
204864562Sgshapiro	  case SMFIC_OPTNEG:
204964562Sgshapiro	  case SMFIC_MACRO:
205064562Sgshapiro	  case SMFIC_ABORT:
205164562Sgshapiro	  case SMFIC_QUIT:
205264562Sgshapiro		/* NOTE: not handled by milter_send_command() */
205364562Sgshapiro		/* FALLTHROUGH */
205464562Sgshapiro
205564562Sgshapiro	  default:
205664562Sgshapiro		skipflag = 0;
205790792Sgshapiro		action = "default";
205864562Sgshapiro		defresponse = "550 5.7.1 Command rejected";
205964562Sgshapiro		break;
206064562Sgshapiro	}
206164562Sgshapiro
2062168515Sgshapiro	if (tTd(64, 10))
2063168515Sgshapiro		sm_dprintf("milter_send_command(%s): skip=%lx, pflags=%x\n",
2064168515Sgshapiro			m->mf_name, skipflag, m->mf_pflags);
2065168515Sgshapiro
206664562Sgshapiro	/* check if filter wants this command */
2067168515Sgshapiro	if (skipflag != 0 && bitset(skipflag, m->mf_pflags))
206864562Sgshapiro		return NULL;
206964562Sgshapiro
207090792Sgshapiro	/* send the command to the filter */
207164562Sgshapiro	(void) milter_write(m, command, data, sz,
2072168515Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e, where);
207364562Sgshapiro	if (m->mf_state == SMFS_ERROR)
207464562Sgshapiro	{
2075112810Sgshapiro		MILTER_CHECK_ERROR(false, return NULL);
207664562Sgshapiro		return NULL;
207764562Sgshapiro	}
207864562Sgshapiro
2079132943Sgshapiro	/* check if filter sends response to this command */
2080132943Sgshapiro	if (norespflag != 0 && bitset(norespflag, m->mf_pflags))
2081132943Sgshapiro		return NULL;
2082132943Sgshapiro
208390792Sgshapiro	/* get the response from the filter */
208464562Sgshapiro	response = milter_read(m, &rcmd, &rlen,
2085168515Sgshapiro			       m->mf_timeout[SMFTO_READ], e, where);
208664562Sgshapiro	if (m->mf_state == SMFS_ERROR)
208764562Sgshapiro	{
2088112810Sgshapiro		MILTER_CHECK_ERROR(false, return NULL);
208964562Sgshapiro		return NULL;
209064562Sgshapiro	}
209164562Sgshapiro
209264562Sgshapiro	if (tTd(64, 10))
209390792Sgshapiro		sm_dprintf("milter_send_command(%s): returned %c\n",
209490792Sgshapiro			   m->mf_name, (char) rcmd);
209564562Sgshapiro
209664562Sgshapiro	switch (rcmd)
209764562Sgshapiro	{
209864562Sgshapiro	  case SMFIR_REPLYCODE:
209964562Sgshapiro		MILTER_CHECK_REPLYCODE(defresponse);
210090792Sgshapiro		if (MilterLogLevel > 10)
2101168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
2102168515Sgshapiro				  "milter=%s, action=%s, reject=%s",
210390792Sgshapiro				  m->mf_name, action, response);
210490792Sgshapiro		*state = rcmd;
210590792Sgshapiro		break;
210664562Sgshapiro
210764562Sgshapiro	  case SMFIR_REJECT:
210890792Sgshapiro		if (MilterLogLevel > 10)
2109168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
2110168515Sgshapiro				  "milter=%s, action=%s, reject",
211190792Sgshapiro				  m->mf_name, action);
211290792Sgshapiro		*state = rcmd;
211390792Sgshapiro		break;
211490792Sgshapiro
211564562Sgshapiro	  case SMFIR_DISCARD:
211690792Sgshapiro		if (MilterLogLevel > 10)
2117168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
2118168515Sgshapiro				  "milter=%s, action=%s, discard",
211990792Sgshapiro				  m->mf_name, action);
212090792Sgshapiro		*state = rcmd;
212190792Sgshapiro		break;
212290792Sgshapiro
212364562Sgshapiro	  case SMFIR_TEMPFAIL:
212490792Sgshapiro		if (MilterLogLevel > 10)
2125168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
2126168515Sgshapiro				  "milter=%s, action=%s, tempfail",
212790792Sgshapiro				  m->mf_name, action);
212864562Sgshapiro		*state = rcmd;
212964562Sgshapiro		break;
213064562Sgshapiro
213164562Sgshapiro	  case SMFIR_ACCEPT:
213264562Sgshapiro		/* this filter is done with message/connection */
213373188Sgshapiro		if (command == SMFIC_HELO ||
213473188Sgshapiro		    command == SMFIC_CONNECT)
213573188Sgshapiro			m->mf_state = SMFS_CLOSABLE;
213673188Sgshapiro		else
213773188Sgshapiro			m->mf_state = SMFS_DONE;
213890792Sgshapiro		if (MilterLogLevel > 10)
2139168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
2140168515Sgshapiro				  "milter=%s, action=%s, accepted",
214190792Sgshapiro				  m->mf_name, action);
214264562Sgshapiro		break;
214364562Sgshapiro
214464562Sgshapiro	  case SMFIR_CONTINUE:
214564562Sgshapiro		/* if MAIL command is ok, filter is in message state */
214664562Sgshapiro		if (command == SMFIC_MAIL)
214764562Sgshapiro			m->mf_state = SMFS_INMSG;
214890792Sgshapiro		if (MilterLogLevel > 12)
2149168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
2150168515Sgshapiro				  "milter=%s, action=%s, continue",
215190792Sgshapiro				  m->mf_name, action);
215264562Sgshapiro		break;
215364562Sgshapiro
2154168515Sgshapiro	  case SMFIR_SKIP:
2155168515Sgshapiro		if (MilterLogLevel > 12)
2156168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
2157168515Sgshapiro				  "milter=%s, action=%s, skip",
2158168515Sgshapiro				  m->mf_name, action);
2159168515Sgshapiro		m->mf_state = SMFS_SKIP;
2160168515Sgshapiro		break;
2161168515Sgshapiro
216264562Sgshapiro	  default:
216364562Sgshapiro		/* Invalid response to command */
216490792Sgshapiro		if (MilterLogLevel > 0)
216564562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
216690792Sgshapiro				  "milter_send_command(%s): action=%s returned bogus response %c",
216790792Sgshapiro				  m->mf_name, action, rcmd);
2168244928Sgshapiro		milter_error(m, e); /* NO ERROR CHECK? */
216964562Sgshapiro		break;
217064562Sgshapiro	}
217164562Sgshapiro
2172168515Sgshapiro	if (*state != SMFIR_REPLYCODE && response != NULL)
217364562Sgshapiro	{
217490792Sgshapiro		sm_free(response); /* XXX */
217564562Sgshapiro		response = NULL;
217664562Sgshapiro	}
217764562Sgshapiro	return response;
217864562Sgshapiro}
217964562Sgshapiro
218090792Sgshapiro/*
218164562Sgshapiro**  MILTER_COMMAND -- send a command and return the response for each filter
218264562Sgshapiro**
218364562Sgshapiro**	Parameters:
2184168515Sgshapiro**		cmd -- command to send.
218564562Sgshapiro**		data -- optional command data.
218664562Sgshapiro**		sz -- length of buf.
2187249865Sgshapiro**		stage -- index of macros to send for filter smfi_getsymval().
218864562Sgshapiro**		e -- current envelope (for macro access).
218964562Sgshapiro**		state -- return state word.
2190168515Sgshapiro**		where -- description of calling function (logging).
2191168515Sgshapiro**		cmd_error -- did the SMTP command cause an error?
219264562Sgshapiro**
219364562Sgshapiro**	Returns:
219464562Sgshapiro**		response string (may be NULL)
219564562Sgshapiro*/
219664562Sgshapiro
219764562Sgshapirostatic char *
2198244928Sgshapiromilter_command(cmd, data, sz, stage, e, state, where, cmd_error)
2199168515Sgshapiro	int cmd;
220064562Sgshapiro	void *data;
220164562Sgshapiro	ssize_t sz;
2202244928Sgshapiro	int stage;
220364562Sgshapiro	ENVELOPE *e;
220464562Sgshapiro	char *state;
2205168515Sgshapiro	const char *where;
2206168515Sgshapiro	bool cmd_error;
220764562Sgshapiro{
220864562Sgshapiro	int i;
2209168515Sgshapiro	char command = (char) cmd;
221064562Sgshapiro	char *response = NULL;
221190792Sgshapiro	time_t tn = 0;
221264562Sgshapiro
221364562Sgshapiro	if (tTd(64, 10))
221490792Sgshapiro		sm_dprintf("milter_command: cmd %c len %ld\n",
2215168515Sgshapiro			command, (long) sz);
221664562Sgshapiro
221764562Sgshapiro	*state = SMFIR_CONTINUE;
221864562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
221964562Sgshapiro	{
222064562Sgshapiro		struct milter *m = InputFilters[i];
222164562Sgshapiro
222271345Sgshapiro		/* previous problem? */
222371345Sgshapiro		if (m->mf_state == SMFS_ERROR)
222471345Sgshapiro		{
2225112810Sgshapiro			MILTER_CHECK_ERROR(false, continue);
222671345Sgshapiro			break;
222771345Sgshapiro		}
222871345Sgshapiro
222964562Sgshapiro		/* sanity check */
223064562Sgshapiro		if (m->mf_sock < 0 ||
223164562Sgshapiro		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
223264562Sgshapiro			continue;
223364562Sgshapiro
2234244928Sgshapiro		if (stage >= SMFIM_FIRST && stage <= SMFIM_LAST)
223564562Sgshapiro		{
2236244928Sgshapiro			int idx;
2237244928Sgshapiro			char **macros;
2238244928Sgshapiro
2239244928Sgshapiro			if ((m->mf_lflags & MI_LFLAGS_SYM(stage)) != 0)
2240244928Sgshapiro				idx = m->mf_idx;
2241244928Sgshapiro			else
2242244928Sgshapiro				idx = 0;
2243244928Sgshapiro			SM_ASSERT(idx >= 0 && idx <= MAXFILTERS);
2244244928Sgshapiro			macros = MilterMacros[stage][idx];
2245244928Sgshapiro
2246244928Sgshapiro			/* send macros (regardless of whether we send cmd) */
2247244928Sgshapiro			if (macros != NULL && macros[0] != NULL)
224864562Sgshapiro			{
2249244928Sgshapiro				milter_send_macros(m, macros, command, e);
2250244928Sgshapiro				if (m->mf_state == SMFS_ERROR)
2251244928Sgshapiro				{
2252244928Sgshapiro					MILTER_CHECK_ERROR(false, continue);
2253244928Sgshapiro					break;
2254244928Sgshapiro				}
225564562Sgshapiro			}
225664562Sgshapiro		}
225764562Sgshapiro
225890792Sgshapiro		if (MilterLogLevel > 21)
225990792Sgshapiro			tn = curtime();
226090792Sgshapiro
2261168515Sgshapiro		/*
2262168515Sgshapiro		**  send the command if
2263168515Sgshapiro		**	there is no error
2264168515Sgshapiro		**	or it's RCPT and the client asked for it:
2265168515Sgshapiro		**	!cmd_error ||
2266168515Sgshapiro		**	where == "rcpt" && m->mf_pflags & SMFIP_RCPT_REJ != 0
2267168515Sgshapiro		**  negate that condition and use continue
2268168515Sgshapiro		*/
226990792Sgshapiro
2270168515Sgshapiro		if (cmd_error &&
2271168515Sgshapiro		    (strcmp(where, "rcpt") != 0 ||
2272168515Sgshapiro		     (m->mf_pflags & SMFIP_RCPT_REJ) == 0))
2273168515Sgshapiro			continue;
2274168515Sgshapiro
2275168515Sgshapiro		response = milter_send_command(m, command, data, sz, e, state,
2276168515Sgshapiro						where);
2277168515Sgshapiro
227890792Sgshapiro		if (MilterLogLevel > 21)
227990792Sgshapiro		{
228090792Sgshapiro			/* log the time it took for the command per filter */
228190792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
228290792Sgshapiro				  "Milter (%s): time command (%c), %d",
228390792Sgshapiro				  m->mf_name, command, (int) (tn - curtime()));
228490792Sgshapiro		}
228590792Sgshapiro
228664562Sgshapiro		if (*state != SMFIR_CONTINUE)
228764562Sgshapiro			break;
228864562Sgshapiro	}
228964562Sgshapiro	return response;
229064562Sgshapiro}
2291168515Sgshapiro
2292168515Sgshapirostatic int milter_getsymlist __P((struct milter *, char *, int, int));
2293168515Sgshapiro
2294168515Sgshapirostatic int
2295168515Sgshapiromilter_getsymlist(m, buf, rlen, offset)
2296168515Sgshapiro	struct milter *m;
2297168515Sgshapiro	char *buf;
2298168515Sgshapiro	int rlen;
2299168515Sgshapiro	int offset;
2300168515Sgshapiro{
2301168515Sgshapiro	int i, r, nummac;
2302168515Sgshapiro	mi_int32 v;
2303168515Sgshapiro
2304168515Sgshapiro	SM_ASSERT(m != NULL);
2305168515Sgshapiro	SM_ASSERT(buf != NULL);
2306168515Sgshapiro
2307168515Sgshapiro	while (offset + MILTER_LEN_BYTES < rlen)
2308168515Sgshapiro	{
2309168515Sgshapiro		size_t len;
2310168515Sgshapiro		char **macros;
2311168515Sgshapiro
2312168515Sgshapiro		nummac = 0;
2313168515Sgshapiro		(void) memcpy((char *) &v, buf + offset, MILTER_LEN_BYTES);
2314168515Sgshapiro		i = ntohl(v);
2315168515Sgshapiro		if (i < SMFIM_FIRST || i > SMFIM_LAST)
2316168515Sgshapiro			return -1;
2317168515Sgshapiro		offset += MILTER_LEN_BYTES;
2318168515Sgshapiro		macros = NULL;
2319168515Sgshapiro
2320168515Sgshapiro		switch (i)
2321168515Sgshapiro		{
2322244928Sgshapiro		  case SMFIM_CONNECT:
2323244928Sgshapiro		  case SMFIM_HELO:
2324244928Sgshapiro		  case SMFIM_ENVFROM:
2325244928Sgshapiro		  case SMFIM_ENVRCPT:
2326244928Sgshapiro		  case SMFIM_EOH:
2327244928Sgshapiro		  case SMFIM_EOM:
2328244928Sgshapiro		  case SMFIM_DATA:
2329244928Sgshapiro			SM_ASSERT(m->mf_idx > 0 && m->mf_idx < MAXFILTERS);
2330244928Sgshapiro			macros = MilterMacros[i][m->mf_idx];
2331244928Sgshapiro			m->mf_lflags |= MI_LFLAGS_SYM(i);
2332168515Sgshapiro			len = strlen(buf + offset);
2333168515Sgshapiro			if (len > 0)
2334168515Sgshapiro			{
2335168515Sgshapiro				r = milter_set_macros(m->mf_name, macros,
2336168515Sgshapiro						buf + offset, nummac);
2337168515Sgshapiro				if (r >= 0)
2338168515Sgshapiro					nummac = r;
2339244928Sgshapiro				if (tTd(64, 5))
2340244928Sgshapiro					sm_dprintf("milter_getsymlist(%s, %s)=%d\n",
2341244928Sgshapiro						m->mf_name, buf + offset, r);
2342168515Sgshapiro			}
2343168515Sgshapiro			break;
2344168515Sgshapiro
2345168515Sgshapiro		  default:
2346168515Sgshapiro			return -1;
2347168515Sgshapiro		}
2348168515Sgshapiro		if (len == 0)
2349168515Sgshapiro			return -1;
2350168515Sgshapiro		offset += len + 1;
2351168515Sgshapiro	}
2352168515Sgshapiro
2353168515Sgshapiro	return 0;
2354168515Sgshapiro}
2355168515Sgshapiro
235690792Sgshapiro/*
235764562Sgshapiro**  MILTER_NEGOTIATE -- get version and flags from filter
235864562Sgshapiro**
235964562Sgshapiro**	Parameters:
236064562Sgshapiro**		m -- milter filter structure.
236164562Sgshapiro**		e -- current envelope.
2362173340Sgshapiro**		milters -- milters structure.
236364562Sgshapiro**
236464562Sgshapiro**	Returns:
236564562Sgshapiro**		0 on success, -1 otherwise
236664562Sgshapiro*/
236764562Sgshapiro
236864562Sgshapirostatic int
2369173340Sgshapiromilter_negotiate(m, e, milters)
237064562Sgshapiro	struct milter *m;
237164562Sgshapiro	ENVELOPE *e;
2372173340Sgshapiro	milters_T *milters;
237364562Sgshapiro{
237464562Sgshapiro	char rcmd;
2375168515Sgshapiro	mi_int32 fvers, fflags, pflags;
2376168515Sgshapiro	mi_int32 mta_prot_vers, mta_prot_flags, mta_actions;
2377157001Sgshapiro	ssize_t rlen;
237864562Sgshapiro	char *response;
237964562Sgshapiro	char data[MILTER_OPTLEN];
238064562Sgshapiro
238164562Sgshapiro	/* sanity check */
238264562Sgshapiro	if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
238364562Sgshapiro	{
238490792Sgshapiro		if (MilterLogLevel > 0)
238564562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
238690792Sgshapiro				  "Milter (%s): negotiate, impossible state",
238764562Sgshapiro				  m->mf_name);
238890792Sgshapiro		milter_error(m, e);
238964562Sgshapiro		return -1;
239064562Sgshapiro	}
239164562Sgshapiro
2392168515Sgshapiro#if _FFR_MILTER_CHECK
2393168515Sgshapiro	mta_prot_vers = m->mf_mta_prot_version;
2394168515Sgshapiro	mta_prot_flags = m->mf_mta_prot_flags;
2395168515Sgshapiro	mta_actions = m->mf_mta_actions;
2396168515Sgshapiro#else /* _FFR_MILTER_CHECK */
2397168515Sgshapiro	mta_prot_vers = SMFI_PROT_VERSION;
2398168515Sgshapiro	mta_prot_flags = SMFI_CURR_PROT;
2399168515Sgshapiro	mta_actions = SMFI_CURR_ACTS;
2400168515Sgshapiro#endif /* _FFR_MILTER_CHECK */
2401203004Sgshapiro#if _FFR_MDS_NEGOTIATE
2402203004Sgshapiro	if (MilterMaxDataSize == MILTER_MDS_256K)
2403203004Sgshapiro		mta_prot_flags |= SMFIP_MDS_256K;
2404203004Sgshapiro	else if (MilterMaxDataSize == MILTER_MDS_1M)
2405203004Sgshapiro		mta_prot_flags |= SMFIP_MDS_1M;
2406203004Sgshapiro#endif /* _FFR_MDS_NEGOTIATE */
2407168515Sgshapiro
2408168515Sgshapiro	fvers = htonl(mta_prot_vers);
2409168515Sgshapiro	pflags = htonl(mta_prot_flags);
2410168515Sgshapiro	fflags = htonl(mta_actions);
241164562Sgshapiro	(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
241264562Sgshapiro	(void) memcpy(data + MILTER_LEN_BYTES,
241364562Sgshapiro		      (char *) &fflags, MILTER_LEN_BYTES);
241464562Sgshapiro	(void) memcpy(data + (MILTER_LEN_BYTES * 2),
241564562Sgshapiro		      (char *) &pflags, MILTER_LEN_BYTES);
2416168515Sgshapiro	(void) milter_write(m, SMFIC_OPTNEG, data, sizeof(data),
2417168515Sgshapiro			    m->mf_timeout[SMFTO_WRITE], e, "negotiate");
241864562Sgshapiro
241964562Sgshapiro	if (m->mf_state == SMFS_ERROR)
242064562Sgshapiro		return -1;
242164562Sgshapiro
2422168515Sgshapiro	if (tTd(64, 5))
2423168515Sgshapiro		sm_dprintf("milter_negotiate(%s): send: version %lu, fflags 0x%lx, pflags 0x%lx\n",
2424168515Sgshapiro			m->mf_name, ntohl(fvers), ntohl(fflags), ntohl(pflags));
2425168515Sgshapiro
2426168515Sgshapiro	response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e,
2427168515Sgshapiro				"negotiate");
242864562Sgshapiro	if (m->mf_state == SMFS_ERROR)
242964562Sgshapiro		return -1;
243064562Sgshapiro
243164562Sgshapiro	if (rcmd != SMFIC_OPTNEG)
243264562Sgshapiro	{
243364562Sgshapiro		if (tTd(64, 5))
243490792Sgshapiro			sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n",
243564562Sgshapiro				m->mf_name, rcmd, SMFIC_OPTNEG);
243690792Sgshapiro		if (MilterLogLevel > 0)
243764562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
243890792Sgshapiro				  "Milter (%s): negotiate: returned %c instead of %c",
243964562Sgshapiro				  m->mf_name, rcmd, SMFIC_OPTNEG);
244064562Sgshapiro		if (response != NULL)
244190792Sgshapiro			sm_free(response); /* XXX */
244290792Sgshapiro		milter_error(m, e);
244364562Sgshapiro		return -1;
244464562Sgshapiro	}
244564562Sgshapiro
244664562Sgshapiro	/* Make sure we have enough bytes for the version */
244764562Sgshapiro	if (response == NULL || rlen < MILTER_LEN_BYTES)
244864562Sgshapiro	{
244964562Sgshapiro		if (tTd(64, 5))
245090792Sgshapiro			sm_dprintf("milter_negotiate(%s): did not return valid info\n",
245164562Sgshapiro				m->mf_name);
245290792Sgshapiro		if (MilterLogLevel > 0)
245364562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
245490792Sgshapiro				  "Milter (%s): negotiate: did not return valid info",
245564562Sgshapiro				  m->mf_name);
245664562Sgshapiro		if (response != NULL)
245790792Sgshapiro			sm_free(response); /* XXX */
245890792Sgshapiro		milter_error(m, e);
245964562Sgshapiro		return -1;
246064562Sgshapiro	}
246164562Sgshapiro
246264562Sgshapiro	/* extract information */
246364562Sgshapiro	(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
246464562Sgshapiro
246564562Sgshapiro	/* Now make sure we have enough for the feature bitmap */
2466168515Sgshapiro	if (rlen < MILTER_OPTLEN)
246764562Sgshapiro	{
246864562Sgshapiro		if (tTd(64, 5))
246990792Sgshapiro			sm_dprintf("milter_negotiate(%s): did not return enough info\n",
247064562Sgshapiro				m->mf_name);
247190792Sgshapiro		if (MilterLogLevel > 0)
247264562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
247390792Sgshapiro				  "Milter (%s): negotiate: did not return enough info",
247464562Sgshapiro				  m->mf_name);
247564562Sgshapiro		if (response != NULL)
247690792Sgshapiro			sm_free(response); /* XXX */
247790792Sgshapiro		milter_error(m, e);
247864562Sgshapiro		return -1;
247964562Sgshapiro	}
248064562Sgshapiro
248164562Sgshapiro	(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
248264562Sgshapiro		      MILTER_LEN_BYTES);
248364562Sgshapiro	(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
248464562Sgshapiro		      MILTER_LEN_BYTES);
248564562Sgshapiro
248664562Sgshapiro	m->mf_fvers = ntohl(fvers);
248764562Sgshapiro	m->mf_fflags = ntohl(fflags);
248864562Sgshapiro	m->mf_pflags = ntohl(pflags);
248964562Sgshapiro
249064562Sgshapiro	/* check for version compatibility */
249164562Sgshapiro	if (m->mf_fvers == 1 ||
249264562Sgshapiro	    m->mf_fvers > SMFI_VERSION)
249364562Sgshapiro	{
249464562Sgshapiro		if (tTd(64, 5))
249594334Sgshapiro			sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n",
249664562Sgshapiro				m->mf_name, m->mf_fvers, SMFI_VERSION);
249790792Sgshapiro		if (MilterLogLevel > 0)
249864562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
249994334Sgshapiro				  "Milter (%s): negotiate: version %d != MTA milter version %d",
250064562Sgshapiro				  m->mf_name, m->mf_fvers, SMFI_VERSION);
250190792Sgshapiro		milter_error(m, e);
2502168515Sgshapiro		goto error;
250364562Sgshapiro	}
250464562Sgshapiro
250564562Sgshapiro	/* check for filter feature mismatch */
2506168515Sgshapiro	if ((m->mf_fflags & mta_actions) != m->mf_fflags)
250764562Sgshapiro	{
250864562Sgshapiro		if (tTd(64, 5))
250994334Sgshapiro			sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n",
251064562Sgshapiro				m->mf_name, m->mf_fflags,
2511168515Sgshapiro				(unsigned long) mta_actions);
251290792Sgshapiro		if (MilterLogLevel > 0)
251364562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
251494334Sgshapiro				  "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx",
251564562Sgshapiro				  m->mf_name, m->mf_fflags,
2516168515Sgshapiro				  (unsigned long) mta_actions);
251790792Sgshapiro		milter_error(m, e);
2518168515Sgshapiro		goto error;
251964562Sgshapiro	}
252064562Sgshapiro
2521203004Sgshapiro#if _FFR_MDS_NEGOTIATE
2522203004Sgshapiro	/* use a table instead of sequence? */
2523203004Sgshapiro	if (bitset(SMFIP_MDS_1M, m->mf_pflags))
2524203004Sgshapiro	{
2525203004Sgshapiro		if (MilterMaxDataSize != MILTER_MDS_1M)
2526203004Sgshapiro		{
2527203004Sgshapiro			/* this should not happen... */
2528203004Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
2529203004Sgshapiro				  "WARNING: Milter.maxdatasize: configured=%d, set by libmilter=%d",
2530203004Sgshapiro		    		  MilterMaxDataSize, MILTER_MDS_1M);
2531203004Sgshapiro			MilterMaxDataSize = MILTER_MDS_1M;
2532203004Sgshapiro		}
2533203004Sgshapiro	}
2534203004Sgshapiro	else if (bitset(SMFIP_MDS_256K, m->mf_pflags))
2535203004Sgshapiro	{
2536203004Sgshapiro		if (MilterMaxDataSize != MILTER_MDS_256K)
2537203004Sgshapiro		{
2538203004Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
2539203004Sgshapiro				  "WARNING: Milter.maxdatasize: configured=%d, set by libmilter=%d",
2540203004Sgshapiro		    		  MilterMaxDataSize, MILTER_MDS_256K);
2541203004Sgshapiro			MilterMaxDataSize = MILTER_MDS_256K;
2542203004Sgshapiro		}
2543203004Sgshapiro	}
2544203004Sgshapiro	else if (MilterMaxDataSize != MILTER_MDS_64K)
2545203004Sgshapiro	{
2546203004Sgshapiro		sm_syslog(LOG_WARNING, NOQID,
2547203004Sgshapiro			  "WARNING: Milter.maxdatasize: configured=%d, set by libmilter=%d",
2548203004Sgshapiro	    		  MilterMaxDataSize, MILTER_MDS_64K);
2549203004Sgshapiro		MilterMaxDataSize = MILTER_MDS_64K;
2550203004Sgshapiro	}
2551203004Sgshapiro	m->mf_pflags &= ~SMFI_INTERNAL;
2552203004Sgshapiro#endif /* _FFR_MDS_NEGOTIATE */
2553203004Sgshapiro
255464562Sgshapiro	/* check for protocol feature mismatch */
2555168515Sgshapiro	if ((m->mf_pflags & mta_prot_flags) != m->mf_pflags)
255664562Sgshapiro	{
255764562Sgshapiro		if (tTd(64, 5))
255894334Sgshapiro			sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n",
255964562Sgshapiro				m->mf_name, m->mf_pflags,
2560168515Sgshapiro				(unsigned long) mta_prot_flags);
256190792Sgshapiro		if (MilterLogLevel > 0)
256264562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
256394334Sgshapiro				  "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx",
256464562Sgshapiro				  m->mf_name, m->mf_pflags,
2565168515Sgshapiro				  (unsigned long) mta_prot_flags);
256690792Sgshapiro		milter_error(m, e);
2567168515Sgshapiro		goto error;
256864562Sgshapiro	}
256964562Sgshapiro
2570157001Sgshapiro	if (m->mf_fvers <= 2)
2571157001Sgshapiro		m->mf_pflags |= SMFIP_NOUNKNOWN;
2572157001Sgshapiro	if (m->mf_fvers <= 3)
2573157001Sgshapiro		m->mf_pflags |= SMFIP_NODATA;
2574157001Sgshapiro
2575168515Sgshapiro	if (rlen > MILTER_OPTLEN)
2576168515Sgshapiro	{
2577168515Sgshapiro		milter_getsymlist(m, response, rlen, MILTER_OPTLEN);
2578168515Sgshapiro	}
2579168515Sgshapiro
2580173340Sgshapiro	if (bitset(SMFIF_DELRCPT, m->mf_fflags))
2581173340Sgshapiro		milters->mis_flags |= MIS_FL_DEL_RCPT;
2582173340Sgshapiro	if (!bitset(SMFIP_NORCPT, m->mf_pflags) &&
2583173340Sgshapiro	    !bitset(SMFIP_NR_RCPT, m->mf_pflags))
2584173340Sgshapiro		milters->mis_flags |= MIS_FL_REJ_RCPT;
2585173340Sgshapiro
258664562Sgshapiro	if (tTd(64, 5))
2587168515Sgshapiro		sm_dprintf("milter_negotiate(%s): received: version %u, fflags 0x%x, pflags 0x%x\n",
258864562Sgshapiro			m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
258964562Sgshapiro	return 0;
2590168515Sgshapiro
2591168515Sgshapiro  error:
2592168515Sgshapiro	if (response != NULL)
2593168515Sgshapiro		sm_free(response); /* XXX */
2594168515Sgshapiro	return -1;
259564562Sgshapiro}
2596168515Sgshapiro
259790792Sgshapiro/*
259864562Sgshapiro**  MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
259964562Sgshapiro**
260064562Sgshapiro**	Reduce code duplication by putting these checks in one place
260164562Sgshapiro**
260264562Sgshapiro**	Parameters:
260364562Sgshapiro**		e -- current envelope.
260464562Sgshapiro**
260564562Sgshapiro**	Returns:
260664562Sgshapiro**		none
260764562Sgshapiro*/
260864562Sgshapiro
260964562Sgshapirostatic void
261064562Sgshapiromilter_per_connection_check(e)
261164562Sgshapiro	ENVELOPE *e;
261264562Sgshapiro{
261364562Sgshapiro	int i;
261464562Sgshapiro
261564562Sgshapiro	/* see if we are done with any of the filters */
261664562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
261764562Sgshapiro	{
261864562Sgshapiro		struct milter *m = InputFilters[i];
261964562Sgshapiro
262073188Sgshapiro		if (m->mf_state == SMFS_CLOSABLE)
262164562Sgshapiro			milter_quit_filter(m, e);
262264562Sgshapiro	}
262364562Sgshapiro}
2624168515Sgshapiro
262590792Sgshapiro/*
262664562Sgshapiro**  MILTER_ERROR -- Put a milter filter into error state
262764562Sgshapiro**
262864562Sgshapiro**	Parameters:
262964562Sgshapiro**		m -- the broken filter.
2630141858Sgshapiro**		e -- current envelope.
263164562Sgshapiro**
263264562Sgshapiro**	Returns:
263364562Sgshapiro**		none
263464562Sgshapiro*/
263564562Sgshapiro
263664562Sgshapirostatic void
263790792Sgshapiromilter_error(m, e)
263864562Sgshapiro	struct milter *m;
263990792Sgshapiro	ENVELOPE *e;
264064562Sgshapiro{
264164562Sgshapiro	/*
2642141858Sgshapiro	**  We could send a quit here but we may have gotten here due to
2643141858Sgshapiro	**  an I/O error so we don't want to try to make things worse.
264464562Sgshapiro	*/
264564562Sgshapiro
264664562Sgshapiro	if (m->mf_sock >= 0)
264764562Sgshapiro	{
264864562Sgshapiro		(void) close(m->mf_sock);
264964562Sgshapiro		m->mf_sock = -1;
265064562Sgshapiro	}
265164562Sgshapiro	m->mf_state = SMFS_ERROR;
265290792Sgshapiro
265390792Sgshapiro	if (MilterLogLevel > 0)
265490792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state",
265590792Sgshapiro			  m->mf_name);
265664562Sgshapiro}
2657168515Sgshapiro
265890792Sgshapiro/*
265964562Sgshapiro**  MILTER_HEADERS -- send headers to a single milter filter
266064562Sgshapiro**
266164562Sgshapiro**	Parameters:
266264562Sgshapiro**		m -- current filter.
266364562Sgshapiro**		e -- current envelope.
266464562Sgshapiro**		state -- return state from response.
266564562Sgshapiro**
266664562Sgshapiro**	Returns:
266764562Sgshapiro**		response string (may be NULL)
266864562Sgshapiro*/
266964562Sgshapiro
267064562Sgshapirostatic char *
267164562Sgshapiromilter_headers(m, e, state)
267264562Sgshapiro	struct milter *m;
267364562Sgshapiro	ENVELOPE *e;
267464562Sgshapiro	char *state;
267564562Sgshapiro{
267664562Sgshapiro	char *response = NULL;
267764562Sgshapiro	HDR *h;
267864562Sgshapiro
267990792Sgshapiro	if (MilterLogLevel > 17)
268090792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send",
268190792Sgshapiro			  m->mf_name);
268290792Sgshapiro
268364562Sgshapiro	for (h = e->e_header; h != NULL; h = h->h_link)
268464562Sgshapiro	{
2685168515Sgshapiro		int len_n, len_v, len_t, len_f;
2686168515Sgshapiro		char *buf, *hv;
268764562Sgshapiro
268864562Sgshapiro		/* don't send over deleted headers */
268964562Sgshapiro		if (h->h_value == NULL)
269064562Sgshapiro		{
2691132943Sgshapiro			/* strip H_USER so not counted in milter_changeheader() */
269264562Sgshapiro			h->h_flags &= ~H_USER;
269364562Sgshapiro			continue;
269464562Sgshapiro		}
269564562Sgshapiro
269664562Sgshapiro		/* skip auto-generated */
269764562Sgshapiro		if (!bitset(H_USER, h->h_flags))
269864562Sgshapiro			continue;
269964562Sgshapiro
270064562Sgshapiro		if (tTd(64, 10))
2701168515Sgshapiro			sm_dprintf("milter_headers: %s:%s\n",
270264562Sgshapiro				h->h_field, h->h_value);
270390792Sgshapiro		if (MilterLogLevel > 21)
270490792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s",
270590792Sgshapiro				  m->mf_name, h->h_field);
270664562Sgshapiro
2707168515Sgshapiro		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)
2708168515Sgshapiro		    || *(h->h_value) != ' ')
2709168515Sgshapiro			hv = h->h_value;
2710168515Sgshapiro		else
2711168515Sgshapiro			hv = h->h_value + 1;
2712168515Sgshapiro		len_f = strlen(h->h_field) + 1;
2713168515Sgshapiro		len_t = len_f + strlen(hv) + 1;
2714168515Sgshapiro		if (len_t < 0)
271590792Sgshapiro			continue;
2716168515Sgshapiro		buf = (char *) xalloc(len_t);
271764562Sgshapiro
2718168515Sgshapiro		/*
2719168515Sgshapiro		**  Note: currently the call to dequote_internal_chars()
2720168515Sgshapiro		**  is not required as h_field is supposed to be 7-bit US-ASCII.
2721168515Sgshapiro		*/
2722168515Sgshapiro
2723168515Sgshapiro		len_n = dequote_internal_chars(h->h_field, buf, len_f);
2724168515Sgshapiro		SM_ASSERT(len_n < len_f);
2725168515Sgshapiro		len_v = dequote_internal_chars(hv, buf + len_n + 1,
2726168515Sgshapiro						len_t - len_n - 1);
2727168515Sgshapiro		SM_ASSERT(len_t >= len_n + 1 + len_v + 1);
2728168515Sgshapiro		len_t = len_n + 1 + len_v + 1;
2729168515Sgshapiro
273064562Sgshapiro		/* send it over */
273164562Sgshapiro		response = milter_send_command(m, SMFIC_HEADER, buf,
2732168515Sgshapiro					       len_t, e, state, "header");
2733168515Sgshapiro		sm_free(buf);
273464562Sgshapiro		if (m->mf_state == SMFS_ERROR ||
273564562Sgshapiro		    m->mf_state == SMFS_DONE ||
273664562Sgshapiro		    *state != SMFIR_CONTINUE)
273764562Sgshapiro			break;
273864562Sgshapiro	}
273990792Sgshapiro	if (MilterLogLevel > 17)
274090792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent",
274190792Sgshapiro			  m->mf_name);
274264562Sgshapiro	return response;
274364562Sgshapiro}
2744168515Sgshapiro
274590792Sgshapiro/*
274664562Sgshapiro**  MILTER_BODY -- send the body to a filter
274764562Sgshapiro**
274864562Sgshapiro**	Parameters:
274964562Sgshapiro**		m -- current filter.
275064562Sgshapiro**		e -- current envelope.
275164562Sgshapiro**		state -- return state from response.
275264562Sgshapiro**
275364562Sgshapiro**	Returns:
275464562Sgshapiro**		response string (may be NULL)
275564562Sgshapiro*/
275664562Sgshapiro
275764562Sgshapirostatic char *
275864562Sgshapiromilter_body(m, e, state)
275964562Sgshapiro	struct milter *m;
276064562Sgshapiro	ENVELOPE *e;
276164562Sgshapiro	char *state;
276264562Sgshapiro{
276364562Sgshapiro	char bufchar = '\0';
276464562Sgshapiro	char prevchar = '\0';
276564562Sgshapiro	int c;
276664562Sgshapiro	char *response = NULL;
276764562Sgshapiro	char *bp;
276864562Sgshapiro	char buf[MILTER_CHUNK_SIZE];
276964562Sgshapiro
277064562Sgshapiro	if (tTd(64, 10))
277190792Sgshapiro		sm_dprintf("milter_body\n");
277264562Sgshapiro
277364562Sgshapiro	if (bfrewind(e->e_dfp) < 0)
277464562Sgshapiro	{
277564562Sgshapiro		ExitStat = EX_IOERR;
277664562Sgshapiro		*state = SMFIR_TEMPFAIL;
277790792Sgshapiro		syserr("milter_body: %s/%cf%s: rewind error",
277890792Sgshapiro		       qid_printqueue(e->e_qgrp, e->e_qdir),
277990792Sgshapiro		       DATAFL_LETTER, e->e_id);
278064562Sgshapiro		return NULL;
278164562Sgshapiro	}
278264562Sgshapiro
278390792Sgshapiro	if (MilterLogLevel > 17)
278490792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send",
278590792Sgshapiro			  m->mf_name);
278664562Sgshapiro	bp = buf;
278790792Sgshapiro	while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF)
278864562Sgshapiro	{
278964562Sgshapiro		/*  Change LF to CRLF */
279064562Sgshapiro		if (c == '\n')
279164562Sgshapiro		{
2792168515Sgshapiro#if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
279364562Sgshapiro			/* Not a CRLF already? */
279464562Sgshapiro			if (prevchar != '\r')
2795168515Sgshapiro#endif /* !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */
279664562Sgshapiro			{
279764562Sgshapiro				/* Room for CR now? */
2798168515Sgshapiro				if (bp + 2 > &buf[sizeof(buf)])
279964562Sgshapiro				{
280064562Sgshapiro					/* No room, buffer LF */
280164562Sgshapiro					bufchar = c;
280264562Sgshapiro
280364562Sgshapiro					/* and send CR now */
280464562Sgshapiro					c = '\r';
280564562Sgshapiro				}
280664562Sgshapiro				else
280764562Sgshapiro				{
280864562Sgshapiro					/* Room to do it now */
280964562Sgshapiro					*bp++ = '\r';
281064562Sgshapiro					prevchar = '\r';
281164562Sgshapiro				}
281264562Sgshapiro			}
281364562Sgshapiro		}
281464562Sgshapiro		*bp++ = (char) c;
281564562Sgshapiro		prevchar = c;
2816168515Sgshapiro		if (bp >= &buf[sizeof(buf)])
281764562Sgshapiro		{
281864562Sgshapiro			/* send chunk */
281964562Sgshapiro			response = milter_send_command(m, SMFIC_BODY, buf,
2820168515Sgshapiro						       bp - buf, e, state,
2821168515Sgshapiro							"body chunk");
282264562Sgshapiro			bp = buf;
282364562Sgshapiro			if (bufchar != '\0')
282464562Sgshapiro			{
282564562Sgshapiro				*bp++ = bufchar;
282664562Sgshapiro				bufchar = '\0';
282764562Sgshapiro				prevchar = bufchar;
282864562Sgshapiro			}
282964562Sgshapiro		}
283064562Sgshapiro		if (m->mf_state == SMFS_ERROR ||
283164562Sgshapiro		    m->mf_state == SMFS_DONE ||
2832168515Sgshapiro		    m->mf_state == SMFS_SKIP ||
283364562Sgshapiro		    *state != SMFIR_CONTINUE)
283464562Sgshapiro			break;
283564562Sgshapiro	}
283664562Sgshapiro
283764562Sgshapiro	/* check for read errors */
283890792Sgshapiro	if (sm_io_error(e->e_dfp))
283964562Sgshapiro	{
284064562Sgshapiro		ExitStat = EX_IOERR;
284164562Sgshapiro		if (*state == SMFIR_CONTINUE ||
2842168515Sgshapiro		    *state == SMFIR_ACCEPT ||
2843168515Sgshapiro		    m->mf_state == SMFS_SKIP)
284464562Sgshapiro		{
284564562Sgshapiro			*state = SMFIR_TEMPFAIL;
284664562Sgshapiro			if (response != NULL)
284764562Sgshapiro			{
284890792Sgshapiro				sm_free(response); /* XXX */
284964562Sgshapiro				response = NULL;
285064562Sgshapiro			}
285164562Sgshapiro		}
285290792Sgshapiro		syserr("milter_body: %s/%cf%s: read error",
285390792Sgshapiro		       qid_printqueue(e->e_qgrp, e->e_qdir),
285490792Sgshapiro		       DATAFL_LETTER, e->e_id);
285564562Sgshapiro		return response;
285664562Sgshapiro	}
285764562Sgshapiro
285864562Sgshapiro	/* send last body chunk */
285964562Sgshapiro	if (bp > buf &&
286064562Sgshapiro	    m->mf_state != SMFS_ERROR &&
286164562Sgshapiro	    m->mf_state != SMFS_DONE &&
2862168515Sgshapiro	    m->mf_state != SMFS_SKIP &&
286364562Sgshapiro	    *state == SMFIR_CONTINUE)
286464562Sgshapiro	{
286564562Sgshapiro		/* send chunk */
286664562Sgshapiro		response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
2867168515Sgshapiro					       e, state, "last body chunk");
286864562Sgshapiro		bp = buf;
286964562Sgshapiro	}
287090792Sgshapiro	if (MilterLogLevel > 17)
287190792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent",
287290792Sgshapiro			  m->mf_name);
2873168515Sgshapiro	if (m->mf_state == SMFS_SKIP)
2874168515Sgshapiro	{
2875168515Sgshapiro		*state = SMFIR_CONTINUE;
2876168515Sgshapiro		m->mf_state = SMFS_READY;
2877168515Sgshapiro	}
2878168515Sgshapiro
287964562Sgshapiro	return response;
288064562Sgshapiro}
288164562Sgshapiro
288264562Sgshapiro/*
288364562Sgshapiro**  Actions
288464562Sgshapiro*/
288564562Sgshapiro
288690792Sgshapiro/*
2887168515Sgshapiro**  ADDLEADINGSPACE -- Add a leading space to a string
2888168515Sgshapiro**
2889168515Sgshapiro**	Parameters:
2890168515Sgshapiro**		str -- string
2891168515Sgshapiro**		rp -- resource pool for allocations
2892168515Sgshapiro**
2893168515Sgshapiro**	Returns:
2894168515Sgshapiro**		pointer to new string
2895168515Sgshapiro*/
2896168515Sgshapiro
2897168515Sgshapirostatic char *addleadingspace __P((char *, SM_RPOOL_T *));
2898168515Sgshapiro
2899168515Sgshapirostatic char *
2900168515Sgshapiroaddleadingspace(str, rp)
2901168515Sgshapiro	char *str;
2902168515Sgshapiro	SM_RPOOL_T *rp;
2903168515Sgshapiro{
2904168515Sgshapiro	size_t l;
2905168515Sgshapiro	char *new;
2906168515Sgshapiro
2907168515Sgshapiro	SM_ASSERT(str != NULL);
2908168515Sgshapiro	l = strlen(str);
2909168515Sgshapiro	SM_ASSERT(l + 2 > l);
2910168515Sgshapiro	new = sm_rpool_malloc_x(rp, l + 2);
2911168515Sgshapiro	new[0] = ' ';
2912168515Sgshapiro	new[1] = '\0';
2913168515Sgshapiro	sm_strlcpy(new + 1, str, l + 1);
2914168515Sgshapiro	return new;
2915168515Sgshapiro}
2916168515Sgshapiro
2917168515Sgshapiro/*
291864562Sgshapiro**  MILTER_ADDHEADER -- Add the supplied header to the message
291964562Sgshapiro**
292064562Sgshapiro**	Parameters:
2921168515Sgshapiro**		m -- current filter.
292264562Sgshapiro**		response -- encoded form of header/value.
292364562Sgshapiro**		rlen -- length of response.
292464562Sgshapiro**		e -- current envelope.
292564562Sgshapiro**
292664562Sgshapiro**	Returns:
292764562Sgshapiro**		none
292864562Sgshapiro*/
292964562Sgshapiro
293064562Sgshapirostatic void
2931168515Sgshapiromilter_addheader(m, response, rlen, e)
2932168515Sgshapiro	struct milter *m;
293364562Sgshapiro	char *response;
293464562Sgshapiro	ssize_t rlen;
293564562Sgshapiro	ENVELOPE *e;
293664562Sgshapiro{
2937168515Sgshapiro	int mh_v_len;
2938168515Sgshapiro	char *val, *mh_value;
293971345Sgshapiro	HDR *h;
294064562Sgshapiro
294164562Sgshapiro	if (tTd(64, 10))
294290792Sgshapiro		sm_dprintf("milter_addheader: ");
294364562Sgshapiro
294464562Sgshapiro	/* sanity checks */
294564562Sgshapiro	if (response == NULL)
294664562Sgshapiro	{
294764562Sgshapiro		if (tTd(64, 10))
294890792Sgshapiro			sm_dprintf("NULL response\n");
294964562Sgshapiro		return;
295064562Sgshapiro	}
295164562Sgshapiro
295264562Sgshapiro	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
295364562Sgshapiro	{
295464562Sgshapiro		if (tTd(64, 10))
2955168515Sgshapiro			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
2956168515Sgshapiro				   (int) strlen(response), (int) (rlen - 1));
295764562Sgshapiro		return;
295864562Sgshapiro	}
295964562Sgshapiro
296064562Sgshapiro	/* Find separating NUL */
296164562Sgshapiro	val = response + strlen(response) + 1;
296264562Sgshapiro
296364562Sgshapiro	/* another sanity check */
296464562Sgshapiro	if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
296564562Sgshapiro	{
296664562Sgshapiro		if (tTd(64, 10))
296790792Sgshapiro			sm_dprintf("didn't follow protocol (part len)\n");
296864562Sgshapiro		return;
296964562Sgshapiro	}
297064562Sgshapiro
297164562Sgshapiro	if (*response == '\0')
297264562Sgshapiro	{
297364562Sgshapiro		if (tTd(64, 10))
297490792Sgshapiro			sm_dprintf("empty field name\n");
297564562Sgshapiro		return;
297664562Sgshapiro	}
297764562Sgshapiro
297871345Sgshapiro	for (h = e->e_header; h != NULL; h = h->h_link)
297971345Sgshapiro	{
298090792Sgshapiro		if (sm_strcasecmp(h->h_field, response) == 0 &&
298171345Sgshapiro		    !bitset(H_USER, h->h_flags) &&
298271345Sgshapiro		    !bitset(H_TRACE, h->h_flags))
298371345Sgshapiro			break;
298471345Sgshapiro	}
298571345Sgshapiro
2986168515Sgshapiro	mh_v_len = 0;
2987168515Sgshapiro	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
2988168515Sgshapiro
298964562Sgshapiro	/* add to e_msgsize */
299064562Sgshapiro	e->e_msgsize += strlen(response) + 2 + strlen(val);
299164562Sgshapiro
299271345Sgshapiro	if (h != NULL)
299371345Sgshapiro	{
299471345Sgshapiro		if (tTd(64, 10))
299590792Sgshapiro			sm_dprintf("Replace default header %s value with %s\n",
2996168515Sgshapiro				   h->h_field, mh_value);
299790792Sgshapiro		if (MilterLogLevel > 8)
299890792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
299990792Sgshapiro				  "Milter change: default header %s value with %s",
3000168515Sgshapiro				  h->h_field, mh_value);
3001168515Sgshapiro		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
3002168515Sgshapiro			h->h_value = mh_value;
3003168515Sgshapiro		else
3004168515Sgshapiro		{
3005203004Sgshapiro			h->h_value = addleadingspace(mh_value, e->e_rpool);
3006168515Sgshapiro			SM_FREE(mh_value);
3007168515Sgshapiro		}
300871345Sgshapiro		h->h_flags |= H_USER;
300971345Sgshapiro	}
301071345Sgshapiro	else
301171345Sgshapiro	{
301271345Sgshapiro		if (tTd(64, 10))
3013168515Sgshapiro			sm_dprintf("Add %s: %s\n", response, mh_value);
301490792Sgshapiro		if (MilterLogLevel > 8)
3015168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
3016168515Sgshapiro				  "Milter add: header: %s: %s",
3017168515Sgshapiro				  response, mh_value);
3018168515Sgshapiro		addheader(newstr(response), mh_value, H_USER, e,
3019168515Sgshapiro			!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
3020168515Sgshapiro		SM_FREE(mh_value);
302171345Sgshapiro	}
302264562Sgshapiro}
3023168515Sgshapiro
302490792Sgshapiro/*
3025132943Sgshapiro**  MILTER_INSHEADER -- Insert the supplied header
3026132943Sgshapiro**
3027132943Sgshapiro**	Parameters:
3028168515Sgshapiro**		m -- current filter.
3029132943Sgshapiro**		response -- encoded form of header/value.
3030132943Sgshapiro**		rlen -- length of response.
3031132943Sgshapiro**		e -- current envelope.
3032132943Sgshapiro**
3033132943Sgshapiro**	Returns:
3034132943Sgshapiro**		none
3035132943Sgshapiro**
3036157001Sgshapiro**	Notes:
3037157001Sgshapiro**		Unlike milter_addheader(), this does not attempt to determine
3038157001Sgshapiro**		if the header already exists in the envelope, even a
3039157001Sgshapiro**		deleted version.  It just blindly inserts.
3040132943Sgshapiro*/
3041132943Sgshapiro
3042132943Sgshapirostatic void
3043168515Sgshapiromilter_insheader(m, response, rlen, e)
3044168515Sgshapiro	struct milter *m;
3045132943Sgshapiro	char *response;
3046132943Sgshapiro	ssize_t rlen;
3047132943Sgshapiro	ENVELOPE *e;
3048132943Sgshapiro{
3049132943Sgshapiro	mi_int32 idx, i;
3050168515Sgshapiro	int mh_v_len;
3051168515Sgshapiro	char *field, *val, *mh_value;
3052132943Sgshapiro
3053132943Sgshapiro	if (tTd(64, 10))
3054132943Sgshapiro		sm_dprintf("milter_insheader: ");
3055132943Sgshapiro
3056132943Sgshapiro	/* sanity checks */
3057132943Sgshapiro	if (response == NULL)
3058132943Sgshapiro	{
3059132943Sgshapiro		if (tTd(64, 10))
3060132943Sgshapiro			sm_dprintf("NULL response\n");
3061132943Sgshapiro		return;
3062132943Sgshapiro	}
3063132943Sgshapiro
3064132943Sgshapiro	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
3065132943Sgshapiro	{
3066132943Sgshapiro		if (tTd(64, 10))
3067132943Sgshapiro			sm_dprintf("didn't follow protocol (total len)\n");
3068132943Sgshapiro		return;
3069132943Sgshapiro	}
3070132943Sgshapiro
3071132943Sgshapiro	/* decode */
3072132943Sgshapiro	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
3073132943Sgshapiro	idx = ntohl(i);
3074132943Sgshapiro	field = response + MILTER_LEN_BYTES;
3075132943Sgshapiro	val = field + strlen(field) + 1;
3076132943Sgshapiro
3077132943Sgshapiro	/* another sanity check */
3078132943Sgshapiro	if (MILTER_LEN_BYTES + strlen(field) + 1 +
3079132943Sgshapiro	    strlen(val) + 1 != (size_t) rlen)
3080132943Sgshapiro	{
3081132943Sgshapiro		if (tTd(64, 10))
3082132943Sgshapiro			sm_dprintf("didn't follow protocol (part len)\n");
3083132943Sgshapiro		return;
3084132943Sgshapiro	}
3085132943Sgshapiro
3086132943Sgshapiro	if (*field == '\0')
3087132943Sgshapiro	{
3088132943Sgshapiro		if (tTd(64, 10))
3089132943Sgshapiro			sm_dprintf("empty field name\n");
3090132943Sgshapiro		return;
3091132943Sgshapiro	}
3092132943Sgshapiro
3093132943Sgshapiro	/* add to e_msgsize */
3094132943Sgshapiro	e->e_msgsize += strlen(response) + 2 + strlen(val);
3095132943Sgshapiro
3096132943Sgshapiro	if (tTd(64, 10))
3097168515Sgshapiro		sm_dprintf("Insert (%d) %s: %s\n", idx, field, val);
3098132943Sgshapiro	if (MilterLogLevel > 8)
3099132943Sgshapiro		sm_syslog(LOG_INFO, e->e_id,
3100157001Sgshapiro			  "Milter insert (%d): header: %s: %s",
3101132943Sgshapiro			  idx, field, val);
3102168515Sgshapiro	mh_v_len = 0;
3103168515Sgshapiro	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
3104168515Sgshapiro	insheader(idx, newstr(field), mh_value, H_USER, e,
3105168515Sgshapiro		!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
3106168515Sgshapiro	SM_FREE(mh_value);
3107132943Sgshapiro}
3108168515Sgshapiro
3109132943Sgshapiro/*
311064562Sgshapiro**  MILTER_CHANGEHEADER -- Change the supplied header in the message
311164562Sgshapiro**
311264562Sgshapiro**	Parameters:
3113168515Sgshapiro**		m -- current filter.
311464562Sgshapiro**		response -- encoded form of header/index/value.
311564562Sgshapiro**		rlen -- length of response.
311664562Sgshapiro**		e -- current envelope.
311764562Sgshapiro**
311864562Sgshapiro**	Returns:
311964562Sgshapiro**		none
312064562Sgshapiro*/
312164562Sgshapiro
312264562Sgshapirostatic void
3123168515Sgshapiromilter_changeheader(m, response, rlen, e)
3124168515Sgshapiro	struct milter *m;
312564562Sgshapiro	char *response;
312664562Sgshapiro	ssize_t rlen;
312764562Sgshapiro	ENVELOPE *e;
312864562Sgshapiro{
312964562Sgshapiro	mi_int32 i, index;
3130168515Sgshapiro	int mh_v_len;
3131168515Sgshapiro	char *field, *val, *mh_value;
313271345Sgshapiro	HDR *h, *sysheader;
313364562Sgshapiro
313464562Sgshapiro	if (tTd(64, 10))
313590792Sgshapiro		sm_dprintf("milter_changeheader: ");
313664562Sgshapiro
313764562Sgshapiro	/* sanity checks */
313864562Sgshapiro	if (response == NULL)
313964562Sgshapiro	{
314064562Sgshapiro		if (tTd(64, 10))
314190792Sgshapiro			sm_dprintf("NULL response\n");
314264562Sgshapiro		return;
314364562Sgshapiro	}
314464562Sgshapiro
314564562Sgshapiro	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
314664562Sgshapiro	{
314764562Sgshapiro		if (tTd(64, 10))
314890792Sgshapiro			sm_dprintf("didn't follow protocol (total len)\n");
314964562Sgshapiro		return;
315064562Sgshapiro	}
315164562Sgshapiro
315264562Sgshapiro	/* Find separating NUL */
315364562Sgshapiro	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
315464562Sgshapiro	index = ntohl(i);
315564562Sgshapiro	field = response + MILTER_LEN_BYTES;
315664562Sgshapiro	val = field + strlen(field) + 1;
315764562Sgshapiro
315864562Sgshapiro	/* another sanity check */
315964562Sgshapiro	if (MILTER_LEN_BYTES + strlen(field) + 1 +
316064562Sgshapiro	    strlen(val) + 1 != (size_t) rlen)
316164562Sgshapiro	{
316264562Sgshapiro		if (tTd(64, 10))
316390792Sgshapiro			sm_dprintf("didn't follow protocol (part len)\n");
316464562Sgshapiro		return;
316564562Sgshapiro	}
316664562Sgshapiro
316764562Sgshapiro	if (*field == '\0')
316864562Sgshapiro	{
316964562Sgshapiro		if (tTd(64, 10))
317090792Sgshapiro			sm_dprintf("empty field name\n");
317164562Sgshapiro		return;
317264562Sgshapiro	}
317364562Sgshapiro
3174168515Sgshapiro	mh_v_len = 0;
3175168515Sgshapiro	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
3176168515Sgshapiro
317771345Sgshapiro	sysheader = NULL;
317864562Sgshapiro	for (h = e->e_header; h != NULL; h = h->h_link)
317964562Sgshapiro	{
318090792Sgshapiro		if (sm_strcasecmp(h->h_field, field) == 0)
318171345Sgshapiro		{
3182168515Sgshapiro			if (bitset(H_USER, h->h_flags) && --index <= 0)
318371345Sgshapiro			{
318471345Sgshapiro				sysheader = NULL;
318571345Sgshapiro				break;
318671345Sgshapiro			}
318771345Sgshapiro			else if (!bitset(H_USER, h->h_flags) &&
318871345Sgshapiro				 !bitset(H_TRACE, h->h_flags))
318971345Sgshapiro			{
319071345Sgshapiro				/*
319171345Sgshapiro				**  DRUMS msg-fmt draft says can only have
319271345Sgshapiro				**  multiple occurences of trace fields,
319371345Sgshapiro				**  so make sure we replace any non-trace,
319471345Sgshapiro				**  non-user field.
319571345Sgshapiro				*/
319671345Sgshapiro
319771345Sgshapiro				sysheader = h;
319871345Sgshapiro			}
319971345Sgshapiro		}
320064562Sgshapiro	}
320164562Sgshapiro
320271345Sgshapiro	/* if not found as user-provided header at index, use sysheader */
320364562Sgshapiro	if (h == NULL)
320471345Sgshapiro		h = sysheader;
320571345Sgshapiro
320671345Sgshapiro	if (h == NULL)
320764562Sgshapiro	{
320864562Sgshapiro		if (*val == '\0')
320964562Sgshapiro		{
321064562Sgshapiro			if (tTd(64, 10))
3211141858Sgshapiro				sm_dprintf("Delete (noop) %s\n", field);
3212141858Sgshapiro			if (MilterLogLevel > 8)
3213141858Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3214141858Sgshapiro					"Milter delete (noop): header: %s"
3215141858Sgshapiro					, field);
321664562Sgshapiro		}
321764562Sgshapiro		else
321864562Sgshapiro		{
321964562Sgshapiro			/* treat modify value with no existing header as add */
322064562Sgshapiro			if (tTd(64, 10))
3221168515Sgshapiro				sm_dprintf("Add %s: %s\n", field, mh_value);
3222141858Sgshapiro			if (MilterLogLevel > 8)
3223141858Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3224141858Sgshapiro					"Milter change (add): header: %s: %s"
3225168515Sgshapiro					, field, mh_value);
3226168515Sgshapiro			addheader(newstr(field), mh_value, H_USER, e,
3227168515Sgshapiro				!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
322864562Sgshapiro		}
322964562Sgshapiro		return;
323064562Sgshapiro	}
323164562Sgshapiro
323264562Sgshapiro	if (tTd(64, 10))
323364562Sgshapiro	{
323464562Sgshapiro		if (*val == '\0')
323564562Sgshapiro		{
3236168515Sgshapiro			sm_dprintf("Delete%s %s:%s\n",
323790792Sgshapiro				   h == sysheader ? " (default header)" : "",
323890792Sgshapiro				   field,
323990792Sgshapiro				   h->h_value == NULL ? "<NULL>" : h->h_value);
324064562Sgshapiro		}
324164562Sgshapiro		else
324264562Sgshapiro		{
324390792Sgshapiro			sm_dprintf("Change%s %s: from %s to %s\n",
324490792Sgshapiro				   h == sysheader ? " (default header)" : "",
324590792Sgshapiro				   field,
324690792Sgshapiro				   h->h_value == NULL ? "<NULL>" : h->h_value,
3247168515Sgshapiro				   mh_value);
324864562Sgshapiro		}
324964562Sgshapiro	}
325064562Sgshapiro
325190792Sgshapiro	if (MilterLogLevel > 8)
325290792Sgshapiro	{
325390792Sgshapiro		if (*val == '\0')
325490792Sgshapiro		{
325590792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
3256168515Sgshapiro				  "Milter delete: header%s %s:%s",
325790792Sgshapiro				  h == sysheader ? " (default header)" : "",
325890792Sgshapiro				  field,
325990792Sgshapiro				  h->h_value == NULL ? "<NULL>" : h->h_value);
326090792Sgshapiro		}
326190792Sgshapiro		else
326290792Sgshapiro		{
326390792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
3264132943Sgshapiro				  "Milter change: header%s %s: from %s to %s",
326590792Sgshapiro				  h == sysheader ? " (default header)" : "",
326690792Sgshapiro				  field,
326790792Sgshapiro				  h->h_value == NULL ? "<NULL>" : h->h_value,
3268168515Sgshapiro				  mh_value);
326990792Sgshapiro		}
327090792Sgshapiro	}
327190792Sgshapiro
327271345Sgshapiro	if (h != sysheader && h->h_value != NULL)
327364562Sgshapiro	{
327490792Sgshapiro		size_t l;
327590792Sgshapiro
327690792Sgshapiro		l = strlen(h->h_value);
327790792Sgshapiro		if (l > e->e_msgsize)
327890792Sgshapiro			e->e_msgsize = 0;
327990792Sgshapiro		else
328090792Sgshapiro			e->e_msgsize -= l;
328190792Sgshapiro		/* rpool, don't free: sm_free(h->h_value); XXX */
328264562Sgshapiro	}
328364562Sgshapiro
328464562Sgshapiro	if (*val == '\0')
328564562Sgshapiro	{
328664562Sgshapiro		/* Remove "Field: " from message size */
328771345Sgshapiro		if (h != sysheader)
328890792Sgshapiro		{
328990792Sgshapiro			size_t l;
329090792Sgshapiro
329190792Sgshapiro			l = strlen(h->h_field) + 2;
329290792Sgshapiro			if (l > e->e_msgsize)
329390792Sgshapiro				e->e_msgsize = 0;
329490792Sgshapiro			else
329590792Sgshapiro				e->e_msgsize -= l;
329690792Sgshapiro		}
329764562Sgshapiro		h->h_value = NULL;
3298168515Sgshapiro		SM_FREE(mh_value);
329964562Sgshapiro	}
330064562Sgshapiro	else
330164562Sgshapiro	{
3302168515Sgshapiro		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
3303168515Sgshapiro			h->h_value = mh_value;
3304168515Sgshapiro		else
3305168515Sgshapiro		{
3306203004Sgshapiro			h->h_value = addleadingspace(mh_value, e->e_rpool);
3307168515Sgshapiro			SM_FREE(mh_value);
3308168515Sgshapiro		}
330971345Sgshapiro		h->h_flags |= H_USER;
331064562Sgshapiro		e->e_msgsize += strlen(h->h_value);
331164562Sgshapiro	}
331264562Sgshapiro}
3313168515Sgshapiro
331490792Sgshapiro/*
3315168515Sgshapiro**  MILTER_SPLIT_RESPONSE -- Split response into fields.
3316168515Sgshapiro**
3317168515Sgshapiro**	Parameters:
3318168515Sgshapiro**		response -- encoded repsonse.
3319168515Sgshapiro**		rlen -- length of response.
3320168515Sgshapiro**		pargc -- number of arguments (ouput)
3321168515Sgshapiro**
3322168515Sgshapiro**	Returns:
3323168515Sgshapiro**		array of pointers to the individual strings
3324168515Sgshapiro*/
3325168515Sgshapiro
3326168515Sgshapirostatic char **milter_split_response __P((char *, ssize_t, int *));
3327168515Sgshapiro
3328168515Sgshapirostatic char **
3329168515Sgshapiromilter_split_response(response, rlen, pargc)
3330168515Sgshapiro	char *response;
3331168515Sgshapiro	ssize_t rlen;
3332168515Sgshapiro	int *pargc;
3333168515Sgshapiro{
3334168515Sgshapiro	char **s;
3335168515Sgshapiro	size_t i;
3336168515Sgshapiro	int elem, nelem;
3337168515Sgshapiro
3338168515Sgshapiro	SM_ASSERT(response != NULL);
3339168515Sgshapiro	SM_ASSERT(pargc != NULL);
3340168515Sgshapiro	*pargc = 0;
3341168515Sgshapiro	if (rlen < 2 || strlen(response) >= (size_t) rlen)
3342168515Sgshapiro	{
3343168515Sgshapiro		if (tTd(64, 10))
3344168515Sgshapiro			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
3345168515Sgshapiro				   (int) strlen(response), (int) (rlen - 1));
3346168515Sgshapiro		return NULL;
3347168515Sgshapiro	}
3348168515Sgshapiro
3349168515Sgshapiro	nelem = 0;
3350168515Sgshapiro	for (i = 0; i < rlen; i++)
3351168515Sgshapiro	{
3352168515Sgshapiro		if (response[i] == '\0')
3353168515Sgshapiro			++nelem;
3354168515Sgshapiro	}
3355168515Sgshapiro	if (nelem == 0)
3356168515Sgshapiro		return NULL;
3357168515Sgshapiro
3358168515Sgshapiro	/* last entry is only for the name */
3359203004Sgshapiro	s = (char **)malloc((nelem + 1) * (sizeof(*s)));
3360168515Sgshapiro	if (s == NULL)
3361168515Sgshapiro		return NULL;
3362168515Sgshapiro	s[0] = response;
3363168515Sgshapiro	for (i = 0, elem = 0; i < rlen && elem < nelem; i++)
3364168515Sgshapiro	{
3365168515Sgshapiro		if (response[i] == '\0')
3366168515Sgshapiro		{
3367168515Sgshapiro			++elem;
3368168515Sgshapiro			if (i + 1 >= rlen)
3369168515Sgshapiro				s[elem] = NULL;
3370168515Sgshapiro			else
3371168515Sgshapiro				s[elem] = &(response[i + 1]);
3372168515Sgshapiro		}
3373168515Sgshapiro	}
3374168515Sgshapiro	*pargc = nelem;
3375168515Sgshapiro
3376168515Sgshapiro	if (tTd(64, 10))
3377168515Sgshapiro	{
3378168515Sgshapiro		for (elem = 0; elem < nelem; elem++)
3379168515Sgshapiro			sm_dprintf("argv[%d]=\"%s\"\n", elem, s[elem]);
3380168515Sgshapiro	}
3381168515Sgshapiro
3382168515Sgshapiro	/* overwrite last entry (already done above, just paranoia) */
3383168515Sgshapiro	s[elem] = NULL;
3384168515Sgshapiro	return s;
3385168515Sgshapiro}
3386168515Sgshapiro
3387168515Sgshapiro/*
3388168515Sgshapiro**  MILTER_CHGFROM -- Change the envelope sender address
3389168515Sgshapiro**
3390168515Sgshapiro**	Parameters:
3391168515Sgshapiro**		response -- encoded form of recipient address.
3392168515Sgshapiro**		rlen -- length of response.
3393168515Sgshapiro**		e -- current envelope.
3394168515Sgshapiro**
3395168515Sgshapiro**	Returns:
3396168515Sgshapiro**		none
3397168515Sgshapiro*/
3398168515Sgshapiro
3399168515Sgshapirostatic void
3400168515Sgshapiromilter_chgfrom(response, rlen, e)
3401168515Sgshapiro	char *response;
3402168515Sgshapiro	ssize_t rlen;
3403168515Sgshapiro	ENVELOPE *e;
3404168515Sgshapiro{
3405168515Sgshapiro	int olderrors, argc;
3406168515Sgshapiro	char **argv;
3407168515Sgshapiro
3408168515Sgshapiro	if (tTd(64, 10))
3409168515Sgshapiro		sm_dprintf("milter_chgfrom: ");
3410168515Sgshapiro
3411168515Sgshapiro	/* sanity checks */
3412168515Sgshapiro	if (response == NULL)
3413168515Sgshapiro	{
3414168515Sgshapiro		if (tTd(64, 10))
3415168515Sgshapiro			sm_dprintf("NULL response\n");
3416168515Sgshapiro		return;
3417168515Sgshapiro	}
3418168515Sgshapiro
3419168515Sgshapiro	if (*response == '\0' ||
3420168515Sgshapiro	    strlen(response) + 1 > (size_t) rlen)
3421168515Sgshapiro	{
3422168515Sgshapiro		if (tTd(64, 10))
3423168515Sgshapiro			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
3424168515Sgshapiro				   (int) strlen(response), (int) (rlen - 1));
3425168515Sgshapiro		return;
3426168515Sgshapiro	}
3427168515Sgshapiro
3428168515Sgshapiro	if (tTd(64, 10))
3429168515Sgshapiro		sm_dprintf("%s\n", response);
3430168515Sgshapiro	if (MilterLogLevel > 8)
3431168515Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter chgfrom: %s", response);
3432168515Sgshapiro	argv = milter_split_response(response, rlen, &argc);
3433168515Sgshapiro	if (argc < 1 || argc > 2)
3434168515Sgshapiro	{
3435168515Sgshapiro		if (tTd(64, 10))
3436168515Sgshapiro			sm_dprintf("didn't follow protocol argc=%d\n", argc);
3437168515Sgshapiro		return;
3438168515Sgshapiro	}
3439168515Sgshapiro
3440168515Sgshapiro	olderrors = Errors;
3441168515Sgshapiro	setsender(argv[0], e, NULL, '\0', false);
3442168515Sgshapiro	if (argc == 2)
3443168515Sgshapiro	{
3444168515Sgshapiro		reset_mail_esmtp_args(e);
3445168515Sgshapiro
3446168515Sgshapiro		/*
3447168515Sgshapiro		**  need "features" here: how to get those? via e?
3448168515Sgshapiro		**  "fake" it for now: allow everything.
3449168515Sgshapiro		*/
3450168515Sgshapiro
3451168515Sgshapiro		parse_esmtp_args(e, NULL, argv[0], argv[1], "MAIL", NULL,
3452168515Sgshapiro				mail_esmtp_args);
3453168515Sgshapiro	}
3454168515Sgshapiro	Errors = olderrors;
3455168515Sgshapiro	return;
3456168515Sgshapiro}
3457168515Sgshapiro
3458168515Sgshapiro/*
3459168515Sgshapiro**  MILTER_ADDRCPT_PAR -- Add the supplied recipient to the message
3460168515Sgshapiro**
3461168515Sgshapiro**	Parameters:
3462168515Sgshapiro**		response -- encoded form of recipient address.
3463168515Sgshapiro**		rlen -- length of response.
3464168515Sgshapiro**		e -- current envelope.
3465168515Sgshapiro**
3466168515Sgshapiro**	Returns:
3467168515Sgshapiro**		none
3468168515Sgshapiro*/
3469168515Sgshapiro
3470168515Sgshapirostatic void
3471168515Sgshapiromilter_addrcpt_par(response, rlen, e)
3472168515Sgshapiro	char *response;
3473168515Sgshapiro	ssize_t rlen;
3474168515Sgshapiro	ENVELOPE *e;
3475168515Sgshapiro{
3476168515Sgshapiro	int olderrors, argc;
3477168515Sgshapiro	char *delimptr;
3478168515Sgshapiro	char **argv;
3479168515Sgshapiro	ADDRESS *a;
3480168515Sgshapiro
3481168515Sgshapiro	if (tTd(64, 10))
3482168515Sgshapiro		sm_dprintf("milter_addrcpt_par: ");
3483168515Sgshapiro
3484168515Sgshapiro	/* sanity checks */
3485168515Sgshapiro	if (response == NULL)
3486168515Sgshapiro	{
3487168515Sgshapiro		if (tTd(64, 10))
3488168515Sgshapiro			sm_dprintf("NULL response\n");
3489168515Sgshapiro		return;
3490168515Sgshapiro	}
3491168515Sgshapiro
3492168515Sgshapiro	if (tTd(64, 10))
3493168515Sgshapiro		sm_dprintf("%s\n", response);
3494168515Sgshapiro	if (MilterLogLevel > 8)
3495168515Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
3496168515Sgshapiro
3497168515Sgshapiro	argv = milter_split_response(response, rlen, &argc);
3498168515Sgshapiro	if (argc < 1 || argc > 2)
3499168515Sgshapiro	{
3500168515Sgshapiro		if (tTd(64, 10))
3501168515Sgshapiro			sm_dprintf("didn't follow protocol argc=%d\n", argc);
3502168515Sgshapiro		return;
3503168515Sgshapiro	}
3504168515Sgshapiro	olderrors = Errors;
3505168515Sgshapiro
3506168515Sgshapiro	/* how to set ESMTP arguments? */
3507168515Sgshapiro	a = parseaddr(argv[0], NULLADDR, RF_COPYALL, ' ', &delimptr, e, true);
3508168515Sgshapiro
3509168515Sgshapiro	if (a != NULL && olderrors == Errors)
3510168515Sgshapiro	{
3511168515Sgshapiro		parse_esmtp_args(e, a, argv[0], argv[1], "RCPT", NULL,
3512168515Sgshapiro				rcpt_esmtp_args);
3513168515Sgshapiro		if (olderrors == Errors)
3514168515Sgshapiro			a = recipient(a, &e->e_sendqueue, 0, e);
3515168515Sgshapiro		else
3516168515Sgshapiro			sm_dprintf("olderrors=%d, Errors=%d\n",
3517168515Sgshapiro				olderrors, Errors);
3518168515Sgshapiro	}
3519168515Sgshapiro	else
3520168515Sgshapiro	{
3521168515Sgshapiro		sm_dprintf("a=%p, olderrors=%d, Errors=%d\n",
3522168515Sgshapiro			a, olderrors, Errors);
3523168515Sgshapiro	}
3524168515Sgshapiro
3525168515Sgshapiro	Errors = olderrors;
3526168515Sgshapiro	return;
3527168515Sgshapiro}
3528168515Sgshapiro
3529168515Sgshapiro/*
353064562Sgshapiro**  MILTER_ADDRCPT -- Add the supplied recipient to the message
353164562Sgshapiro**
353264562Sgshapiro**	Parameters:
353364562Sgshapiro**		response -- encoded form of recipient address.
353464562Sgshapiro**		rlen -- length of response.
353564562Sgshapiro**		e -- current envelope.
353664562Sgshapiro**
353764562Sgshapiro**	Returns:
353864562Sgshapiro**		none
353964562Sgshapiro*/
354064562Sgshapiro
354164562Sgshapirostatic void
354264562Sgshapiromilter_addrcpt(response, rlen, e)
354364562Sgshapiro	char *response;
354464562Sgshapiro	ssize_t rlen;
354564562Sgshapiro	ENVELOPE *e;
354664562Sgshapiro{
3547120256Sgshapiro	int olderrors;
3548120256Sgshapiro
354964562Sgshapiro	if (tTd(64, 10))
355090792Sgshapiro		sm_dprintf("milter_addrcpt: ");
355164562Sgshapiro
355264562Sgshapiro	/* sanity checks */
355364562Sgshapiro	if (response == NULL)
355464562Sgshapiro	{
355564562Sgshapiro		if (tTd(64, 10))
355690792Sgshapiro			sm_dprintf("NULL response\n");
355764562Sgshapiro		return;
355864562Sgshapiro	}
355964562Sgshapiro
356064562Sgshapiro	if (*response == '\0' ||
356164562Sgshapiro	    strlen(response) + 1 != (size_t) rlen)
356264562Sgshapiro	{
356364562Sgshapiro		if (tTd(64, 10))
356490792Sgshapiro			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
356590792Sgshapiro				   (int) strlen(response), (int) (rlen - 1));
356664562Sgshapiro		return;
356764562Sgshapiro	}
356864562Sgshapiro
356964562Sgshapiro	if (tTd(64, 10))
357090792Sgshapiro		sm_dprintf("%s\n", response);
357190792Sgshapiro	if (MilterLogLevel > 8)
357290792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
3573120256Sgshapiro	olderrors = Errors;
3574132943Sgshapiro	(void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
3575132943Sgshapiro	Errors = olderrors;
357664562Sgshapiro	return;
357764562Sgshapiro}
3578168515Sgshapiro
357990792Sgshapiro/*
358064562Sgshapiro**  MILTER_DELRCPT -- Delete the supplied recipient from the message
358164562Sgshapiro**
358264562Sgshapiro**	Parameters:
358364562Sgshapiro**		response -- encoded form of recipient address.
358464562Sgshapiro**		rlen -- length of response.
358564562Sgshapiro**		e -- current envelope.
358664562Sgshapiro**
358764562Sgshapiro**	Returns:
358864562Sgshapiro**		none
358964562Sgshapiro*/
359064562Sgshapiro
359164562Sgshapirostatic void
359264562Sgshapiromilter_delrcpt(response, rlen, e)
359364562Sgshapiro	char *response;
359464562Sgshapiro	ssize_t rlen;
359564562Sgshapiro	ENVELOPE *e;
359664562Sgshapiro{
359764562Sgshapiro	if (tTd(64, 10))
359890792Sgshapiro		sm_dprintf("milter_delrcpt: ");
359964562Sgshapiro
360064562Sgshapiro	/* sanity checks */
360164562Sgshapiro	if (response == NULL)
360264562Sgshapiro	{
360364562Sgshapiro		if (tTd(64, 10))
360490792Sgshapiro			sm_dprintf("NULL response\n");
360564562Sgshapiro		return;
360664562Sgshapiro	}
360764562Sgshapiro
360864562Sgshapiro	if (*response == '\0' ||
360964562Sgshapiro	    strlen(response) + 1 != (size_t) rlen)
361064562Sgshapiro	{
361164562Sgshapiro		if (tTd(64, 10))
3612168515Sgshapiro			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
3613168515Sgshapiro				   (int) strlen(response), (int) (rlen - 1));
361464562Sgshapiro		return;
361564562Sgshapiro	}
361664562Sgshapiro
361764562Sgshapiro	if (tTd(64, 10))
361890792Sgshapiro		sm_dprintf("%s\n", response);
361990792Sgshapiro	if (MilterLogLevel > 8)
362090792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s",
362190792Sgshapiro			  response);
362264562Sgshapiro	(void) removefromlist(response, &e->e_sendqueue, e);
362364562Sgshapiro	return;
362464562Sgshapiro}
3625168515Sgshapiro
362690792Sgshapiro/*
362790792Sgshapiro**  MILTER_REPLBODY -- Replace the current data file with new body
362864562Sgshapiro**
362964562Sgshapiro**	Parameters:
363064562Sgshapiro**		response -- encoded form of new body.
363164562Sgshapiro**		rlen -- length of response.
363264562Sgshapiro**		newfilter -- if first time called by a new filter
363364562Sgshapiro**		e -- current envelope.
363464562Sgshapiro**
363564562Sgshapiro**	Returns:
363664562Sgshapiro**		0 upon success, -1 upon failure
363764562Sgshapiro*/
363864562Sgshapiro
363964562Sgshapirostatic int
364064562Sgshapiromilter_replbody(response, rlen, newfilter, e)
364164562Sgshapiro	char *response;
364264562Sgshapiro	ssize_t rlen;
364364562Sgshapiro	bool newfilter;
364464562Sgshapiro	ENVELOPE *e;
364564562Sgshapiro{
364664562Sgshapiro	static char prevchar;
364764562Sgshapiro	int i;
364864562Sgshapiro
364964562Sgshapiro	if (tTd(64, 10))
365090792Sgshapiro		sm_dprintf("milter_replbody\n");
365164562Sgshapiro
365290792Sgshapiro	/* If a new filter, reset previous character and truncate data file */
365364562Sgshapiro	if (newfilter)
365464562Sgshapiro	{
365594334Sgshapiro		off_t prevsize;
365664562Sgshapiro		char dfname[MAXPATHLEN];
365764562Sgshapiro
365890792Sgshapiro		(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER),
3659168515Sgshapiro				  sizeof(dfname));
366064562Sgshapiro
366164562Sgshapiro		/* Reset prevchar */
366264562Sgshapiro		prevchar = '\0';
366364562Sgshapiro
366490792Sgshapiro		/* Get the current data file information */
366594334Sgshapiro		prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL);
366694334Sgshapiro		if (prevsize < 0)
366794334Sgshapiro			prevsize = 0;
366864562Sgshapiro
366990792Sgshapiro		/* truncate current data file */
367090792Sgshapiro		if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
367164562Sgshapiro		{
367290792Sgshapiro			if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0)
367390792Sgshapiro			{
367490792Sgshapiro				MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s");
367590792Sgshapiro				return -1;
367690792Sgshapiro			}
367764562Sgshapiro		}
367864562Sgshapiro		else
367964562Sgshapiro		{
368090792Sgshapiro			int err;
368190792Sgshapiro
368290792Sgshapiro			err = sm_io_error(e->e_dfp);
368390792Sgshapiro			(void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT);
368490792Sgshapiro
368590792Sgshapiro			/*
368690792Sgshapiro			**  Clear error if tried to fflush()
368790792Sgshapiro			**  a read-only file pointer and
368890792Sgshapiro			**  there wasn't a previous error.
368990792Sgshapiro			*/
369090792Sgshapiro
369190792Sgshapiro			if (err == 0)
369290792Sgshapiro				sm_io_clearerr(e->e_dfp);
369390792Sgshapiro
369490792Sgshapiro			/* errno is set implicitly by fseek() before return */
369590792Sgshapiro			err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT,
369690792Sgshapiro					 0, SEEK_SET);
369795154Sgshapiro			if (err < 0)
369895154Sgshapiro			{
369995154Sgshapiro				MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s");
370095154Sgshapiro				return -1;
370195154Sgshapiro			}
370295154Sgshapiro# if NOFTRUNCATE
370395154Sgshapiro			/* XXX: Not much we can do except rewind it */
370495154Sgshapiro			errno = EINVAL;
370595154Sgshapiro			MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)");
370695154Sgshapiro			return -1;
370790792Sgshapiro# else /* NOFTRUNCATE */
370890792Sgshapiro			err = ftruncate(sm_io_getinfo(e->e_dfp,
370990792Sgshapiro						      SM_IO_WHAT_FD, NULL),
371090792Sgshapiro					0);
371190792Sgshapiro			if (err < 0)
371290792Sgshapiro			{
371390792Sgshapiro				MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s");
371490792Sgshapiro				return -1;
371590792Sgshapiro			}
371695154Sgshapiro# endif /* NOFTRUNCATE */
371764562Sgshapiro		}
371890792Sgshapiro
371990792Sgshapiro		if (prevsize > e->e_msgsize)
372090792Sgshapiro			e->e_msgsize = 0;
372190792Sgshapiro		else
372290792Sgshapiro			e->e_msgsize -= prevsize;
372364562Sgshapiro	}
372464562Sgshapiro
372590792Sgshapiro	if (newfilter && MilterLogLevel > 8)
372690792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced");
372790792Sgshapiro
372864562Sgshapiro	if (response == NULL)
372964562Sgshapiro	{
373064562Sgshapiro		/* Flush the buffered '\r' */
373164562Sgshapiro		if (prevchar == '\r')
373264562Sgshapiro		{
373390792Sgshapiro			(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar);
373464562Sgshapiro			e->e_msgsize++;
373564562Sgshapiro		}
373664562Sgshapiro		return 0;
373764562Sgshapiro	}
373864562Sgshapiro
373964562Sgshapiro	for (i = 0; i < rlen; i++)
374064562Sgshapiro	{
374164562Sgshapiro		/* Buffered char from last chunk */
374264562Sgshapiro		if (i == 0 && prevchar == '\r')
374364562Sgshapiro		{
374464562Sgshapiro			/* Not CRLF, output prevchar */
374564562Sgshapiro			if (response[i] != '\n')
374664562Sgshapiro			{
374790792Sgshapiro				(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT,
374890792Sgshapiro						  prevchar);
374964562Sgshapiro				e->e_msgsize++;
375064562Sgshapiro			}
375164562Sgshapiro			prevchar = '\0';
375264562Sgshapiro		}
375364562Sgshapiro
375464562Sgshapiro		/* Turn CRLF into LF */
375564562Sgshapiro		if (response[i] == '\r')
375664562Sgshapiro		{
375764562Sgshapiro			/* check if at end of chunk */
375864562Sgshapiro			if (i + 1 < rlen)
375964562Sgshapiro			{
376064562Sgshapiro				/* If LF, strip CR */
376164562Sgshapiro				if (response[i + 1] == '\n')
376264562Sgshapiro					i++;
376364562Sgshapiro			}
376464562Sgshapiro			else
376564562Sgshapiro			{
376664562Sgshapiro				/* check next chunk */
376764562Sgshapiro				prevchar = '\r';
376864562Sgshapiro				continue;
376964562Sgshapiro			}
377064562Sgshapiro		}
377190792Sgshapiro		(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]);
377264562Sgshapiro		e->e_msgsize++;
377364562Sgshapiro	}
377464562Sgshapiro	return 0;
377564562Sgshapiro}
377664562Sgshapiro
377764562Sgshapiro/*
377864562Sgshapiro**  MTA callouts
377964562Sgshapiro*/
378064562Sgshapiro
378190792Sgshapiro/*
378264562Sgshapiro**  MILTER_INIT -- open and negotiate with all of the filters
378364562Sgshapiro**
378464562Sgshapiro**	Parameters:
378564562Sgshapiro**		e -- current envelope.
378664562Sgshapiro**		state -- return state from response.
3787173340Sgshapiro**		milters -- milters structure.
378864562Sgshapiro**
378964562Sgshapiro**	Returns:
379090792Sgshapiro**		true iff at least one filter is active
379164562Sgshapiro*/
379264562Sgshapiro
379364562Sgshapiro/* ARGSUSED */
379490792Sgshapirobool
3795173340Sgshapiromilter_init(e, state, milters)
379664562Sgshapiro	ENVELOPE *e;
379764562Sgshapiro	char *state;
3798173340Sgshapiro	milters_T *milters;
379964562Sgshapiro{
380064562Sgshapiro	int i;
380164562Sgshapiro
380264562Sgshapiro	if (tTd(64, 10))
380390792Sgshapiro		sm_dprintf("milter_init\n");
380464562Sgshapiro
3805173340Sgshapiro	memset(milters, '\0', sizeof(*milters));
380664562Sgshapiro	*state = SMFIR_CONTINUE;
380790792Sgshapiro	if (InputFilters[0] == NULL)
380890792Sgshapiro	{
380990792Sgshapiro		if (MilterLogLevel > 10)
381090792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
381190792Sgshapiro				  "Milter: no active filter");
381290792Sgshapiro		return false;
381390792Sgshapiro	}
381490792Sgshapiro
381564562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
381664562Sgshapiro	{
381764562Sgshapiro		struct milter *m = InputFilters[i];
381864562Sgshapiro
381990792Sgshapiro		m->mf_sock = milter_open(m, false, e);
382064562Sgshapiro		if (m->mf_state == SMFS_ERROR)
382164562Sgshapiro		{
3822112810Sgshapiro			MILTER_CHECK_ERROR(true, continue);
382364562Sgshapiro			break;
382464562Sgshapiro		}
382564562Sgshapiro
382664562Sgshapiro		if (m->mf_sock < 0 ||
3827173340Sgshapiro		    milter_negotiate(m, e, milters) < 0 ||
382864562Sgshapiro		    m->mf_state == SMFS_ERROR)
382964562Sgshapiro		{
383064562Sgshapiro			if (tTd(64, 5))
383190792Sgshapiro				sm_dprintf("milter_init(%s): failed to %s\n",
383290792Sgshapiro					   m->mf_name,
383390792Sgshapiro					   m->mf_sock < 0 ? "open" :
383490792Sgshapiro							    "negotiate");
383590792Sgshapiro			if (MilterLogLevel > 0)
383690792Sgshapiro				sm_syslog(LOG_ERR, e->e_id,
383790792Sgshapiro					  "Milter (%s): init failed to %s",
383890792Sgshapiro					  m->mf_name,
383990792Sgshapiro					  m->mf_sock < 0 ? "open" :
384090792Sgshapiro							   "negotiate");
384164562Sgshapiro
3842203004Sgshapiro			/* if negotiation failure, close socket */
384390792Sgshapiro			milter_error(m, e);
3844112810Sgshapiro			MILTER_CHECK_ERROR(true, continue);
3845132943Sgshapiro			continue;
384664562Sgshapiro		}
384790792Sgshapiro		if (MilterLogLevel > 9)
384890792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
384990792Sgshapiro				  "Milter (%s): init success to %s",
385090792Sgshapiro				  m->mf_name,
385190792Sgshapiro				  m->mf_sock < 0 ? "open" : "negotiate");
385264562Sgshapiro	}
385364562Sgshapiro
385464562Sgshapiro	/*
385564562Sgshapiro	**  If something temp/perm failed with one of the filters,
385664562Sgshapiro	**  we won't be using any of them, so clear any existing
385764562Sgshapiro	**  connections.
385864562Sgshapiro	*/
385964562Sgshapiro
386064562Sgshapiro	if (*state != SMFIR_CONTINUE)
386164562Sgshapiro		milter_quit(e);
386290792Sgshapiro
386390792Sgshapiro	return true;
386464562Sgshapiro}
3865168515Sgshapiro
386690792Sgshapiro/*
386764562Sgshapiro**  MILTER_CONNECT -- send connection info to milter filters
386864562Sgshapiro**
386964562Sgshapiro**	Parameters:
387064562Sgshapiro**		hostname -- hostname of remote machine.
387164562Sgshapiro**		addr -- address of remote machine.
387264562Sgshapiro**		e -- current envelope.
387364562Sgshapiro**		state -- return state from response.
387464562Sgshapiro**
387564562Sgshapiro**	Returns:
387664562Sgshapiro**		response string (may be NULL)
387764562Sgshapiro*/
387864562Sgshapiro
387964562Sgshapirochar *
388064562Sgshapiromilter_connect(hostname, addr, e, state)
388164562Sgshapiro	char *hostname;
388264562Sgshapiro	SOCKADDR addr;
388364562Sgshapiro	ENVELOPE *e;
388464562Sgshapiro	char *state;
388564562Sgshapiro{
388664562Sgshapiro	char family;
388790792Sgshapiro	unsigned short port;
388864562Sgshapiro	char *buf, *bp;
388964562Sgshapiro	char *response;
389064562Sgshapiro	char *sockinfo = NULL;
389164562Sgshapiro	ssize_t s;
389264562Sgshapiro# if NETINET6
389364562Sgshapiro	char buf6[INET6_ADDRSTRLEN];
389464562Sgshapiro# endif /* NETINET6 */
389564562Sgshapiro
389664562Sgshapiro	if (tTd(64, 10))
389790792Sgshapiro		sm_dprintf("milter_connect(%s)\n", hostname);
389890792Sgshapiro	if (MilterLogLevel > 9)
389990792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters");
390064562Sgshapiro
390164562Sgshapiro	/* gather data */
390264562Sgshapiro	switch (addr.sa.sa_family)
390364562Sgshapiro	{
390464562Sgshapiro# if NETUNIX
390564562Sgshapiro	  case AF_UNIX:
390664562Sgshapiro		family = SMFIA_UNIX;
390764562Sgshapiro		port = htons(0);
390864562Sgshapiro		sockinfo = addr.sunix.sun_path;
390964562Sgshapiro		break;
391064562Sgshapiro# endif /* NETUNIX */
391164562Sgshapiro
391264562Sgshapiro# if NETINET
391364562Sgshapiro	  case AF_INET:
391464562Sgshapiro		family = SMFIA_INET;
391594334Sgshapiro		port = addr.sin.sin_port;
391664562Sgshapiro		sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
391764562Sgshapiro		break;
391864562Sgshapiro# endif /* NETINET */
391964562Sgshapiro
392064562Sgshapiro# if NETINET6
392164562Sgshapiro	  case AF_INET6:
392280785Sgshapiro		if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr))
392380785Sgshapiro			family = SMFIA_INET;
392480785Sgshapiro		else
392580785Sgshapiro			family = SMFIA_INET6;
392694334Sgshapiro		port = addr.sin6.sin6_port;
392764562Sgshapiro		sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
3928168515Sgshapiro				       sizeof(buf6));
392964562Sgshapiro		if (sockinfo == NULL)
393064562Sgshapiro			sockinfo = "";
393164562Sgshapiro		break;
393264562Sgshapiro# endif /* NETINET6 */
393364562Sgshapiro
393464562Sgshapiro	  default:
393564562Sgshapiro		family = SMFIA_UNKNOWN;
393664562Sgshapiro		break;
393764562Sgshapiro	}
393864562Sgshapiro
393964562Sgshapiro	s = strlen(hostname) + 1 + sizeof(family);
394064562Sgshapiro	if (family != SMFIA_UNKNOWN)
394164562Sgshapiro		s += sizeof(port) + strlen(sockinfo) + 1;
394264562Sgshapiro
394390792Sgshapiro	buf = (char *) xalloc(s);
394464562Sgshapiro	bp = buf;
394564562Sgshapiro
394664562Sgshapiro	/* put together data */
394764562Sgshapiro	(void) memcpy(bp, hostname, strlen(hostname));
394864562Sgshapiro	bp += strlen(hostname);
394964562Sgshapiro	*bp++ = '\0';
3950168515Sgshapiro	(void) memcpy(bp, &family, sizeof(family));
3951168515Sgshapiro	bp += sizeof(family);
395264562Sgshapiro	if (family != SMFIA_UNKNOWN)
395364562Sgshapiro	{
3954168515Sgshapiro		(void) memcpy(bp, &port, sizeof(port));
3955168515Sgshapiro		bp += sizeof(port);
395664562Sgshapiro
395764562Sgshapiro		/* include trailing '\0' */
395864562Sgshapiro		(void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
395964562Sgshapiro	}
396064562Sgshapiro
3961244928Sgshapiro	response = milter_command(SMFIC_CONNECT, buf, s, SMFIM_CONNECT,
3962168515Sgshapiro				e, state, "connect", false);
396390792Sgshapiro	sm_free(buf); /* XXX */
396464562Sgshapiro
396564562Sgshapiro	/*
396664562Sgshapiro	**  If this message connection is done for,
396764562Sgshapiro	**  close the filters.
396864562Sgshapiro	*/
396964562Sgshapiro
397064562Sgshapiro	if (*state != SMFIR_CONTINUE)
397190792Sgshapiro	{
397290792Sgshapiro		if (MilterLogLevel > 9)
397390792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending");
397464562Sgshapiro		milter_quit(e);
397590792Sgshapiro	}
397664562Sgshapiro	else
397764562Sgshapiro		milter_per_connection_check(e);
397864562Sgshapiro
397964562Sgshapiro	/*
398064562Sgshapiro	**  SMFIR_REPLYCODE can't work with connect due to
398164562Sgshapiro	**  the requirements of SMTP.  Therefore, ignore the
398264562Sgshapiro	**  reply code text but keep the state it would reflect.
398364562Sgshapiro	*/
398464562Sgshapiro
398564562Sgshapiro	if (*state == SMFIR_REPLYCODE)
398664562Sgshapiro	{
398764562Sgshapiro		if (response != NULL &&
398864562Sgshapiro		    *response == '4')
3989110560Sgshapiro		{
3990110560Sgshapiro			if (strncmp(response, "421 ", 4) == 0)
3991110560Sgshapiro				*state = SMFIR_SHUTDOWN;
3992110560Sgshapiro			else
3993110560Sgshapiro				*state = SMFIR_TEMPFAIL;
3994110560Sgshapiro		}
399564562Sgshapiro		else
399664562Sgshapiro			*state = SMFIR_REJECT;
399764562Sgshapiro		if (response != NULL)
399864562Sgshapiro		{
399990792Sgshapiro			sm_free(response); /* XXX */
400064562Sgshapiro			response = NULL;
400164562Sgshapiro		}
400264562Sgshapiro	}
400364562Sgshapiro	return response;
400464562Sgshapiro}
4005168515Sgshapiro
400690792Sgshapiro/*
400764562Sgshapiro**  MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
400864562Sgshapiro**
400964562Sgshapiro**	Parameters:
401064562Sgshapiro**		helo -- argument to SMTP HELO/EHLO command.
401164562Sgshapiro**		e -- current envelope.
401264562Sgshapiro**		state -- return state from response.
401364562Sgshapiro**
401464562Sgshapiro**	Returns:
401564562Sgshapiro**		response string (may be NULL)
401664562Sgshapiro*/
401764562Sgshapiro
401864562Sgshapirochar *
401964562Sgshapiromilter_helo(helo, e, state)
402064562Sgshapiro	char *helo;
402164562Sgshapiro	ENVELOPE *e;
402264562Sgshapiro	char *state;
402364562Sgshapiro{
402473188Sgshapiro	int i;
402564562Sgshapiro	char *response;
402664562Sgshapiro
402764562Sgshapiro	if (tTd(64, 10))
402890792Sgshapiro		sm_dprintf("milter_helo(%s)\n", helo);
402964562Sgshapiro
403090792Sgshapiro	/* HELO/EHLO can come at any point */
403173188Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
403273188Sgshapiro	{
403373188Sgshapiro		struct milter *m = InputFilters[i];
403473188Sgshapiro
403573188Sgshapiro		switch (m->mf_state)
403673188Sgshapiro		{
403773188Sgshapiro		  case SMFS_INMSG:
403873188Sgshapiro			/* abort in message filters */
403973188Sgshapiro			milter_abort_filter(m, e);
404073188Sgshapiro			/* FALLTHROUGH */
404173188Sgshapiro
404273188Sgshapiro		  case SMFS_DONE:
404373188Sgshapiro			/* reset done filters */
404473188Sgshapiro			m->mf_state = SMFS_OPEN;
404573188Sgshapiro			break;
404673188Sgshapiro		}
404773188Sgshapiro	}
404873188Sgshapiro
404964562Sgshapiro	response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
4050247141Sgshapiro				  SMFIM_HELO, e, state, "helo", false);
405164562Sgshapiro	milter_per_connection_check(e);
405264562Sgshapiro	return response;
405364562Sgshapiro}
4054168515Sgshapiro
405590792Sgshapiro/*
405664562Sgshapiro**  MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
405764562Sgshapiro**
405864562Sgshapiro**	Parameters:
405964562Sgshapiro**		args -- SMTP MAIL command args (args[0] == sender).
406064562Sgshapiro**		e -- current envelope.
406164562Sgshapiro**		state -- return state from response.
406264562Sgshapiro**
406364562Sgshapiro**	Returns:
406464562Sgshapiro**		response string (may be NULL)
406564562Sgshapiro*/
406664562Sgshapiro
406764562Sgshapirochar *
406864562Sgshapiromilter_envfrom(args, e, state)
406964562Sgshapiro	char **args;
407064562Sgshapiro	ENVELOPE *e;
407164562Sgshapiro	char *state;
407264562Sgshapiro{
407364562Sgshapiro	int i;
407464562Sgshapiro	char *buf, *bp;
407564562Sgshapiro	char *response;
407664562Sgshapiro	ssize_t s;
407764562Sgshapiro
407864562Sgshapiro	if (tTd(64, 10))
407964562Sgshapiro	{
408090792Sgshapiro		sm_dprintf("milter_envfrom:");
408164562Sgshapiro		for (i = 0; args[i] != NULL; i++)
408290792Sgshapiro			sm_dprintf(" %s", args[i]);
408390792Sgshapiro		sm_dprintf("\n");
408464562Sgshapiro	}
408564562Sgshapiro
408664562Sgshapiro	/* sanity check */
408764562Sgshapiro	if (args[0] == NULL)
408864562Sgshapiro	{
408964562Sgshapiro		*state = SMFIR_REJECT;
409090792Sgshapiro		if (MilterLogLevel > 10)
409190792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
409290792Sgshapiro				  "Milter: reject, no sender");
409364562Sgshapiro		return NULL;
409464562Sgshapiro	}
409564562Sgshapiro
409664562Sgshapiro	/* new message, so ... */
409764562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
409864562Sgshapiro	{
409964562Sgshapiro		struct milter *m = InputFilters[i];
410064562Sgshapiro
410164562Sgshapiro		switch (m->mf_state)
410264562Sgshapiro		{
410364562Sgshapiro		  case SMFS_INMSG:
410464562Sgshapiro			/* abort in message filters */
410564562Sgshapiro			milter_abort_filter(m, e);
410664562Sgshapiro			/* FALLTHROUGH */
410764562Sgshapiro
410864562Sgshapiro		  case SMFS_DONE:
410964562Sgshapiro			/* reset done filters */
411064562Sgshapiro			m->mf_state = SMFS_OPEN;
411164562Sgshapiro			break;
411264562Sgshapiro		}
411364562Sgshapiro	}
411464562Sgshapiro
411564562Sgshapiro	/* put together data */
411664562Sgshapiro	s = 0;
411764562Sgshapiro	for (i = 0; args[i] != NULL; i++)
411864562Sgshapiro		s += strlen(args[i]) + 1;
411990792Sgshapiro
412090792Sgshapiro	if (s < 0)
412190792Sgshapiro	{
412290792Sgshapiro		*state = SMFIR_TEMPFAIL;
412390792Sgshapiro		return NULL;
412490792Sgshapiro	}
412590792Sgshapiro
412690792Sgshapiro	buf = (char *) xalloc(s);
412764562Sgshapiro	bp = buf;
412864562Sgshapiro	for (i = 0; args[i] != NULL; i++)
412964562Sgshapiro	{
413090792Sgshapiro		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
413164562Sgshapiro		bp += strlen(bp) + 1;
413264562Sgshapiro	}
413364562Sgshapiro
413490792Sgshapiro	if (MilterLogLevel > 14)
4135168515Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: sender: %s", buf);
413690792Sgshapiro
413764562Sgshapiro	/* send it over */
4138244928Sgshapiro	response = milter_command(SMFIC_MAIL, buf, s, SMFIM_ENVFROM,
4139168515Sgshapiro				e, state, "mail", false);
414090792Sgshapiro	sm_free(buf); /* XXX */
414164562Sgshapiro
414264562Sgshapiro	/*
414364562Sgshapiro	**  If filter rejects/discards a per message command,
414464562Sgshapiro	**  abort the other filters since we are done with the
414564562Sgshapiro	**  current message.
414664562Sgshapiro	*/
414764562Sgshapiro
414864562Sgshapiro	MILTER_CHECK_DONE_MSG();
414990792Sgshapiro	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
4150168515Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, sender");
415164562Sgshapiro	return response;
415264562Sgshapiro}
4153132943Sgshapiro
415490792Sgshapiro/*
415564562Sgshapiro**  MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
415664562Sgshapiro**
415764562Sgshapiro**	Parameters:
415864562Sgshapiro**		args -- SMTP MAIL command args (args[0] == recipient).
415964562Sgshapiro**		e -- current envelope.
416064562Sgshapiro**		state -- return state from response.
4161168515Sgshapiro**		rcpt_error -- does RCPT have an error?
416264562Sgshapiro**
416364562Sgshapiro**	Returns:
416464562Sgshapiro**		response string (may be NULL)
416564562Sgshapiro*/
416664562Sgshapiro
416764562Sgshapirochar *
4168168515Sgshapiromilter_envrcpt(args, e, state, rcpt_error)
416964562Sgshapiro	char **args;
417064562Sgshapiro	ENVELOPE *e;
417164562Sgshapiro	char *state;
4172168515Sgshapiro	bool rcpt_error;
417364562Sgshapiro{
417464562Sgshapiro	int i;
417564562Sgshapiro	char *buf, *bp;
417664562Sgshapiro	char *response;
417764562Sgshapiro	ssize_t s;
417864562Sgshapiro
417964562Sgshapiro	if (tTd(64, 10))
418064562Sgshapiro	{
418190792Sgshapiro		sm_dprintf("milter_envrcpt:");
418264562Sgshapiro		for (i = 0; args[i] != NULL; i++)
418390792Sgshapiro			sm_dprintf(" %s", args[i]);
418490792Sgshapiro		sm_dprintf("\n");
418564562Sgshapiro	}
418664562Sgshapiro
418764562Sgshapiro	/* sanity check */
418864562Sgshapiro	if (args[0] == NULL)
418964562Sgshapiro	{
419064562Sgshapiro		*state = SMFIR_REJECT;
419190792Sgshapiro		if (MilterLogLevel > 10)
419290792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt");
419364562Sgshapiro		return NULL;
419464562Sgshapiro	}
419564562Sgshapiro
419664562Sgshapiro	/* put together data */
419764562Sgshapiro	s = 0;
419864562Sgshapiro	for (i = 0; args[i] != NULL; i++)
419964562Sgshapiro		s += strlen(args[i]) + 1;
420090792Sgshapiro
420190792Sgshapiro	if (s < 0)
420290792Sgshapiro	{
420390792Sgshapiro		*state = SMFIR_TEMPFAIL;
420490792Sgshapiro		return NULL;
420590792Sgshapiro	}
420690792Sgshapiro
420790792Sgshapiro	buf = (char *) xalloc(s);
420864562Sgshapiro	bp = buf;
420964562Sgshapiro	for (i = 0; args[i] != NULL; i++)
421064562Sgshapiro	{
421190792Sgshapiro		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
421264562Sgshapiro		bp += strlen(bp) + 1;
421364562Sgshapiro	}
421464562Sgshapiro
421590792Sgshapiro	if (MilterLogLevel > 14)
421690792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf);
421790792Sgshapiro
421864562Sgshapiro	/* send it over */
4219244928Sgshapiro	response = milter_command(SMFIC_RCPT, buf, s, SMFIM_ENVRCPT,
4220168515Sgshapiro				e, state, "rcpt", rcpt_error);
422190792Sgshapiro	sm_free(buf); /* XXX */
422264562Sgshapiro	return response;
422364562Sgshapiro}
4224132943Sgshapiro
422590792Sgshapiro/*
4226132943Sgshapiro**  MILTER_DATA_CMD -- send SMTP DATA command info to milter filters
4227132943Sgshapiro**
4228132943Sgshapiro**	Parameters:
4229132943Sgshapiro**		e -- current envelope.
4230132943Sgshapiro**		state -- return state from response.
4231132943Sgshapiro**
4232132943Sgshapiro**	Returns:
4233132943Sgshapiro**		response string (may be NULL)
4234132943Sgshapiro*/
4235132943Sgshapiro
4236132943Sgshapirochar *
4237132943Sgshapiromilter_data_cmd(e, state)
4238132943Sgshapiro	ENVELOPE *e;
4239132943Sgshapiro	char *state;
4240132943Sgshapiro{
4241132943Sgshapiro	if (tTd(64, 10))
4242132943Sgshapiro		sm_dprintf("milter_data_cmd\n");
4243132943Sgshapiro
4244132943Sgshapiro	/* send it over */
4245244928Sgshapiro	return milter_command(SMFIC_DATA, NULL, 0, SMFIM_DATA,
4246244928Sgshapiro				e, state, "data", false);
4247132943Sgshapiro}
4248132943Sgshapiro
4249132943Sgshapiro/*
425064562Sgshapiro**  MILTER_DATA -- send message headers/body and gather final message results
425164562Sgshapiro**
425264562Sgshapiro**	Parameters:
425364562Sgshapiro**		e -- current envelope.
425464562Sgshapiro**		state -- return state from response.
425564562Sgshapiro**
425664562Sgshapiro**	Returns:
425764562Sgshapiro**		response string (may be NULL)
425864562Sgshapiro**
425964562Sgshapiro**	Side effects:
426064562Sgshapiro**		- Uses e->e_dfp for access to the body
426164562Sgshapiro**		- Can call the various milter action routines to
426264562Sgshapiro**		  modify the envelope or message.
426364562Sgshapiro*/
426464562Sgshapiro
4265244928Sgshapiro/* flow through code using continue; don't wrap in do {} while */
426664562Sgshapiro# define MILTER_CHECK_RESULTS() \
4267244928Sgshapiro	if (m->mf_state == SMFS_ERROR && *state == SMFIR_CONTINUE) \
4268244928Sgshapiro	{ \
4269244928Sgshapiro			MILTER_SET_STATE;	\
4270244928Sgshapiro	} \
427164562Sgshapiro	if (*state == SMFIR_ACCEPT || \
427264562Sgshapiro	    m->mf_state == SMFS_DONE || \
427364562Sgshapiro	    m->mf_state == SMFS_ERROR) \
427464562Sgshapiro	{ \
427564562Sgshapiro		if (m->mf_state != SMFS_ERROR) \
427664562Sgshapiro			m->mf_state = SMFS_DONE; \
427764562Sgshapiro		continue;	/* to next filter */ \
427864562Sgshapiro	} \
427964562Sgshapiro	if (*state != SMFIR_CONTINUE) \
428064562Sgshapiro	{ \
428164562Sgshapiro		m->mf_state = SMFS_DONE; \
428264562Sgshapiro		goto finishup; \
428364562Sgshapiro	}
428464562Sgshapiro
428564562Sgshapirochar *
428664562Sgshapiromilter_data(e, state)
428764562Sgshapiro	ENVELOPE *e;
428864562Sgshapiro	char *state;
428964562Sgshapiro{
429090792Sgshapiro	bool replbody = false;		/* milter_replbody() called? */
429190792Sgshapiro	bool replfailed = false;	/* milter_replbody() failed? */
429290792Sgshapiro	bool rewind = false;		/* rewind data file? */
429390792Sgshapiro	bool dfopen = false;		/* data file open for writing? */
429464562Sgshapiro	bool newfilter;			/* reset on each new filter */
429564562Sgshapiro	char rcmd;
429664562Sgshapiro	int i;
429764562Sgshapiro	int save_errno;
429864562Sgshapiro	char *response = NULL;
429964562Sgshapiro	time_t eomsent;
430064562Sgshapiro	ssize_t rlen;
430164562Sgshapiro
430264562Sgshapiro	if (tTd(64, 10))
430390792Sgshapiro		sm_dprintf("milter_data\n");
430464562Sgshapiro
430564562Sgshapiro	*state = SMFIR_CONTINUE;
430664562Sgshapiro
430764562Sgshapiro	/*
430864562Sgshapiro	**  XXX: Should actually send body chunks to each filter
430964562Sgshapiro	**  a chunk at a time instead of sending the whole body to
431064562Sgshapiro	**  each filter in turn.  However, only if the filters don't
431164562Sgshapiro	**  change the body.
431264562Sgshapiro	*/
431364562Sgshapiro
431464562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
431564562Sgshapiro	{
4316244928Sgshapiro		int idx;
4317244928Sgshapiro		char **macros;
431864562Sgshapiro		struct milter *m = InputFilters[i];
431964562Sgshapiro
432064562Sgshapiro		if (*state != SMFIR_CONTINUE &&
432164562Sgshapiro		    *state != SMFIR_ACCEPT)
432264562Sgshapiro		{
432364562Sgshapiro			/*
432464562Sgshapiro			**  A previous filter has dealt with the message,
432564562Sgshapiro			**  safe to stop processing the filters.
432664562Sgshapiro			*/
432764562Sgshapiro
432864562Sgshapiro			break;
432964562Sgshapiro		}
433064562Sgshapiro
433164562Sgshapiro		/* Now reset state for later evaluation */
433264562Sgshapiro		*state = SMFIR_CONTINUE;
433390792Sgshapiro		newfilter = true;
433464562Sgshapiro
433571345Sgshapiro		/* previous problem? */
433671345Sgshapiro		if (m->mf_state == SMFS_ERROR)
433771345Sgshapiro		{
4338112810Sgshapiro			MILTER_CHECK_ERROR(false, continue);
433971345Sgshapiro			break;
434071345Sgshapiro		}
434171345Sgshapiro
434264562Sgshapiro		/* sanity checks */
434364562Sgshapiro		if (m->mf_sock < 0 ||
434464562Sgshapiro		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
434564562Sgshapiro			continue;
434664562Sgshapiro
434764562Sgshapiro		m->mf_state = SMFS_INMSG;
434864562Sgshapiro
434964562Sgshapiro		/* check if filter wants the headers */
435064562Sgshapiro		if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
435164562Sgshapiro		{
435264562Sgshapiro			response = milter_headers(m, e, state);
435364562Sgshapiro			MILTER_CHECK_RESULTS();
435464562Sgshapiro		}
435564562Sgshapiro
435664562Sgshapiro		/* check if filter wants EOH */
435764562Sgshapiro		if (!bitset(SMFIP_NOEOH, m->mf_pflags))
435864562Sgshapiro		{
435964562Sgshapiro			if (tTd(64, 10))
436090792Sgshapiro				sm_dprintf("milter_data: eoh\n");
436164562Sgshapiro
4362244928Sgshapiro			if ((m->mf_lflags & MI_LFLAGS_SYM(SMFIM_EOH)) != 0)
4363244928Sgshapiro				idx = m->mf_idx;
4364244928Sgshapiro			else
4365244928Sgshapiro				idx = 0;
4366244928Sgshapiro			SM_ASSERT(idx >= 0 && idx <= MAXFILTERS);
4367244928Sgshapiro			macros = MilterMacros[SMFIM_EOH][idx];
4368244928Sgshapiro
4369244928Sgshapiro			if (macros != NULL)
4370168515Sgshapiro			{
4371244928Sgshapiro				milter_send_macros(m, macros, SMFIC_EOH, e);
4372168515Sgshapiro				MILTER_CHECK_RESULTS();
4373168515Sgshapiro			}
4374168515Sgshapiro
437564562Sgshapiro			/* send it over */
437664562Sgshapiro			response = milter_send_command(m, SMFIC_EOH, NULL, 0,
4377168515Sgshapiro						       e, state, "eoh");
437864562Sgshapiro			MILTER_CHECK_RESULTS();
437964562Sgshapiro		}
438064562Sgshapiro
438164562Sgshapiro		/* check if filter wants the body */
438264562Sgshapiro		if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
438364562Sgshapiro		    e->e_dfp != NULL)
438464562Sgshapiro		{
438590792Sgshapiro			rewind = true;
438664562Sgshapiro			response = milter_body(m, e, state);
438764562Sgshapiro			MILTER_CHECK_RESULTS();
438864562Sgshapiro		}
438964562Sgshapiro
4390244928Sgshapiro		if ((m->mf_lflags & MI_LFLAGS_SYM(SMFIM_EOH)) != 0)
4391244928Sgshapiro			idx = m->mf_idx;
4392244928Sgshapiro		else
4393244928Sgshapiro			idx = 0;
4394244928Sgshapiro		SM_ASSERT(idx >= 0 && idx <= MAXFILTERS);
4395244928Sgshapiro		macros = MilterMacros[SMFIM_EOM][idx];
4396244928Sgshapiro		if (macros != NULL)
4397147078Sgshapiro		{
4398244928Sgshapiro			milter_send_macros(m, macros, SMFIC_BODYEOB, e);
4399147078Sgshapiro			MILTER_CHECK_RESULTS();
4400147078Sgshapiro		}
4401125820Sgshapiro
440264562Sgshapiro		/* send the final body chunk */
440364562Sgshapiro		(void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
4404168515Sgshapiro				    m->mf_timeout[SMFTO_WRITE], e, "eom");
440564562Sgshapiro
440664562Sgshapiro		/* Get time EOM sent for timeout */
440764562Sgshapiro		eomsent = curtime();
440864562Sgshapiro
440964562Sgshapiro		/* deal with the possibility of multiple responses */
441064562Sgshapiro		while (*state == SMFIR_CONTINUE)
441164562Sgshapiro		{
441264562Sgshapiro			/* Check total timeout from EOM to final ACK/NAK */
441364562Sgshapiro			if (m->mf_timeout[SMFTO_EOM] > 0 &&
441464562Sgshapiro			    curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
441564562Sgshapiro			{
441664562Sgshapiro				if (tTd(64, 5))
441790792Sgshapiro					sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
441864562Sgshapiro						m->mf_name);
441990792Sgshapiro				if (MilterLogLevel > 0)
442064562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
442190792Sgshapiro						  "milter_data(%s): EOM ACK/NAK timeout",
442264562Sgshapiro						  m->mf_name);
442390792Sgshapiro				milter_error(m, e);
4424112810Sgshapiro				MILTER_CHECK_ERROR(false, break);
442564562Sgshapiro				break;
442664562Sgshapiro			}
442764562Sgshapiro
442864562Sgshapiro			response = milter_read(m, &rcmd, &rlen,
4429168515Sgshapiro					       m->mf_timeout[SMFTO_READ], e,
4430203004Sgshapiro						"eom");
443164562Sgshapiro			if (m->mf_state == SMFS_ERROR)
443264562Sgshapiro				break;
443364562Sgshapiro
443464562Sgshapiro			if (tTd(64, 10))
443590792Sgshapiro				sm_dprintf("milter_data(%s): state %c\n",
443690792Sgshapiro					   m->mf_name, (char) rcmd);
443764562Sgshapiro
443864562Sgshapiro			switch (rcmd)
443964562Sgshapiro			{
444064562Sgshapiro			  case SMFIR_REPLYCODE:
444164562Sgshapiro				MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
444290792Sgshapiro				if (MilterLogLevel > 12)
444390792Sgshapiro					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s",
444490792Sgshapiro						  m->mf_name, response);
444564562Sgshapiro				*state = rcmd;
444664562Sgshapiro				m->mf_state = SMFS_DONE;
444764562Sgshapiro				break;
444864562Sgshapiro
444990792Sgshapiro			  case SMFIR_REJECT: /* log msg at end of function */
445090792Sgshapiro				if (MilterLogLevel > 12)
445190792Sgshapiro					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject",
445290792Sgshapiro						  m->mf_name);
445390792Sgshapiro				*state = rcmd;
445490792Sgshapiro				m->mf_state = SMFS_DONE;
445590792Sgshapiro				break;
445690792Sgshapiro
445764562Sgshapiro			  case SMFIR_DISCARD:
445890792Sgshapiro				if (MilterLogLevel > 12)
445990792Sgshapiro					sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard",
446090792Sgshapiro						  m->mf_name);
446190792Sgshapiro				*state = rcmd;
446290792Sgshapiro				m->mf_state = SMFS_DONE;
446390792Sgshapiro				break;
446490792Sgshapiro
446564562Sgshapiro			  case SMFIR_TEMPFAIL:
446690792Sgshapiro				if (MilterLogLevel > 12)
446790792Sgshapiro					sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail",
446890792Sgshapiro						  m->mf_name);
446964562Sgshapiro				*state = rcmd;
447064562Sgshapiro				m->mf_state = SMFS_DONE;
447164562Sgshapiro				break;
447264562Sgshapiro
447364562Sgshapiro			  case SMFIR_CONTINUE:
447464562Sgshapiro			  case SMFIR_ACCEPT:
447564562Sgshapiro				/* this filter is done with message */
447664562Sgshapiro				if (replfailed)
447764562Sgshapiro					*state = SMFIR_TEMPFAIL;
447864562Sgshapiro				else
447964562Sgshapiro					*state = SMFIR_ACCEPT;
448064562Sgshapiro				m->mf_state = SMFS_DONE;
448164562Sgshapiro				break;
448264562Sgshapiro
448364562Sgshapiro			  case SMFIR_PROGRESS:
448464562Sgshapiro				break;
448564562Sgshapiro
448690792Sgshapiro			  case SMFIR_QUARANTINE:
448790792Sgshapiro				if (!bitset(SMFIF_QUARANTINE, m->mf_fflags))
448890792Sgshapiro				{
448990792Sgshapiro					if (MilterLogLevel > 9)
449090792Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
449190792Sgshapiro							  "milter_data(%s): lied about quarantining, honoring request anyway",
449290792Sgshapiro							  m->mf_name);
449390792Sgshapiro				}
449490792Sgshapiro				if (response == NULL)
449590792Sgshapiro					response = newstr("");
449690792Sgshapiro				if (MilterLogLevel > 3)
449790792Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
449890792Sgshapiro						  "milter=%s, quarantine=%s",
449990792Sgshapiro						  m->mf_name, response);
450090792Sgshapiro				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
450190792Sgshapiro								 response);
450290792Sgshapiro				macdefine(&e->e_macro, A_PERM,
450390792Sgshapiro					  macid("{quarantine}"), e->e_quarmsg);
450490792Sgshapiro				break;
450590792Sgshapiro
450664562Sgshapiro			  case SMFIR_ADDHEADER:
450764562Sgshapiro				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
450864562Sgshapiro				{
450990792Sgshapiro					if (MilterLogLevel > 9)
451064562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
451164562Sgshapiro							  "milter_data(%s): lied about adding headers, honoring request anyway",
451264562Sgshapiro							  m->mf_name);
451364562Sgshapiro				}
4514168515Sgshapiro				milter_addheader(m, response, rlen, e);
451564562Sgshapiro				break;
451664562Sgshapiro
4517132943Sgshapiro			  case SMFIR_INSHEADER:
4518132943Sgshapiro				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
4519132943Sgshapiro				{
4520132943Sgshapiro					if (MilterLogLevel > 9)
4521132943Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
4522132943Sgshapiro							  "milter_data(%s): lied about adding headers, honoring request anyway",
4523132943Sgshapiro							  m->mf_name);
4524132943Sgshapiro				}
4525168515Sgshapiro				milter_insheader(m, response, rlen, e);
4526132943Sgshapiro				break;
4527132943Sgshapiro
452864562Sgshapiro			  case SMFIR_CHGHEADER:
452964562Sgshapiro				if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
453064562Sgshapiro				{
453190792Sgshapiro					if (MilterLogLevel > 9)
453264562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
453364562Sgshapiro							  "milter_data(%s): lied about changing headers, honoring request anyway",
453464562Sgshapiro							  m->mf_name);
453564562Sgshapiro				}
4536168515Sgshapiro				milter_changeheader(m, response, rlen, e);
453764562Sgshapiro				break;
453864562Sgshapiro
4539168515Sgshapiro			  case SMFIR_CHGFROM:
4540168515Sgshapiro				if (!bitset(SMFIF_CHGFROM, m->mf_fflags))
4541168515Sgshapiro				{
4542168515Sgshapiro					if (MilterLogLevel > 9)
4543168515Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
4544168515Sgshapiro							  "milter_data(%s) lied about changing sender, honoring request anyway",
4545168515Sgshapiro							  m->mf_name);
4546168515Sgshapiro				}
4547168515Sgshapiro				milter_chgfrom(response, rlen, e);
4548168515Sgshapiro				break;
4549168515Sgshapiro
455064562Sgshapiro			  case SMFIR_ADDRCPT:
455164562Sgshapiro				if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
455264562Sgshapiro				{
455390792Sgshapiro					if (MilterLogLevel > 9)
455464562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
455564562Sgshapiro							  "milter_data(%s) lied about adding recipients, honoring request anyway",
455664562Sgshapiro							  m->mf_name);
455764562Sgshapiro				}
455864562Sgshapiro				milter_addrcpt(response, rlen, e);
455964562Sgshapiro				break;
456064562Sgshapiro
4561168515Sgshapiro			  case SMFIR_ADDRCPT_PAR:
4562168515Sgshapiro				if (!bitset(SMFIF_ADDRCPT_PAR, m->mf_fflags))
4563168515Sgshapiro				{
4564168515Sgshapiro					if (MilterLogLevel > 9)
4565168515Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
4566168515Sgshapiro							  "milter_data(%s) lied about adding recipients with parameters, honoring request anyway",
4567168515Sgshapiro							  m->mf_name);
4568168515Sgshapiro				}
4569168515Sgshapiro				milter_addrcpt_par(response, rlen, e);
4570168515Sgshapiro				break;
4571168515Sgshapiro
457264562Sgshapiro			  case SMFIR_DELRCPT:
457364562Sgshapiro				if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
457464562Sgshapiro				{
457590792Sgshapiro					if (MilterLogLevel > 9)
457664562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
457764562Sgshapiro							  "milter_data(%s): lied about removing recipients, honoring request anyway",
457864562Sgshapiro							  m->mf_name);
457964562Sgshapiro				}
458064562Sgshapiro				milter_delrcpt(response, rlen, e);
458164562Sgshapiro				break;
458264562Sgshapiro
458364562Sgshapiro			  case SMFIR_REPLBODY:
458464562Sgshapiro				if (!bitset(SMFIF_MODBODY, m->mf_fflags))
458564562Sgshapiro				{
4586132943Sgshapiro					if (MilterLogLevel > 0)
458764562Sgshapiro						sm_syslog(LOG_ERR, e->e_id,
458864562Sgshapiro							  "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
458964562Sgshapiro							  m->mf_name);
459090792Sgshapiro					replfailed = true;
459164562Sgshapiro					break;
459264562Sgshapiro				}
459364562Sgshapiro
459464562Sgshapiro				/* already failed in attempt */
459564562Sgshapiro				if (replfailed)
459664562Sgshapiro					break;
459764562Sgshapiro
459864562Sgshapiro				if (!dfopen)
459964562Sgshapiro				{
460064562Sgshapiro					if (milter_reopen_df(e) < 0)
460164562Sgshapiro					{
460290792Sgshapiro						replfailed = true;
460364562Sgshapiro						break;
460464562Sgshapiro					}
460590792Sgshapiro					dfopen = true;
460690792Sgshapiro					rewind = true;
460764562Sgshapiro				}
460864562Sgshapiro
460964562Sgshapiro				if (milter_replbody(response, rlen,
461064562Sgshapiro						    newfilter, e) < 0)
461190792Sgshapiro					replfailed = true;
461290792Sgshapiro				newfilter = false;
461390792Sgshapiro				replbody = true;
461464562Sgshapiro				break;
461564562Sgshapiro
461664562Sgshapiro			  default:
461764562Sgshapiro				/* Invalid response to command */
461890792Sgshapiro				if (MilterLogLevel > 0)
461964562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
462064562Sgshapiro						  "milter_data(%s): returned bogus response %c",
462164562Sgshapiro						  m->mf_name, rcmd);
462290792Sgshapiro				milter_error(m, e);
462364562Sgshapiro				break;
462464562Sgshapiro			}
462590792Sgshapiro			if (rcmd != SMFIR_REPLYCODE && response != NULL)
462664562Sgshapiro			{
462790792Sgshapiro				sm_free(response); /* XXX */
462864562Sgshapiro				response = NULL;
462964562Sgshapiro			}
463064562Sgshapiro
463164562Sgshapiro			if (m->mf_state == SMFS_ERROR)
463264562Sgshapiro				break;
463364562Sgshapiro		}
463464562Sgshapiro
463564562Sgshapiro		if (replbody && !replfailed)
463664562Sgshapiro		{
463764562Sgshapiro			/* flush possible buffered character */
463864562Sgshapiro			milter_replbody(NULL, 0, !replbody, e);
463990792Sgshapiro			replbody = false;
464064562Sgshapiro		}
464164562Sgshapiro
464264562Sgshapiro		if (m->mf_state == SMFS_ERROR)
464364562Sgshapiro		{
4644112810Sgshapiro			MILTER_CHECK_ERROR(false, continue);
464564562Sgshapiro			goto finishup;
464664562Sgshapiro		}
464764562Sgshapiro	}
464864562Sgshapiro
464964562Sgshapirofinishup:
465064562Sgshapiro	/* leave things in the expected state if we touched it */
465164562Sgshapiro	if (replfailed)
465264562Sgshapiro	{
465364562Sgshapiro		if (*state == SMFIR_CONTINUE ||
465464562Sgshapiro		    *state == SMFIR_ACCEPT)
465564562Sgshapiro		{
465664562Sgshapiro			*state = SMFIR_TEMPFAIL;
465790792Sgshapiro			SM_FREE_CLR(response);
465864562Sgshapiro		}
465964562Sgshapiro
466064562Sgshapiro		if (dfopen)
466164562Sgshapiro		{
466290792Sgshapiro			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
466364562Sgshapiro			e->e_dfp = NULL;
466464562Sgshapiro			e->e_flags &= ~EF_HAS_DF;
466590792Sgshapiro			dfopen = false;
466664562Sgshapiro		}
466790792Sgshapiro		rewind = false;
466864562Sgshapiro	}
466964562Sgshapiro
467064562Sgshapiro	if ((dfopen && milter_reset_df(e) < 0) ||
467164562Sgshapiro	    (rewind && bfrewind(e->e_dfp) < 0))
467264562Sgshapiro	{
467364562Sgshapiro		save_errno = errno;
467464562Sgshapiro		ExitStat = EX_IOERR;
467564562Sgshapiro
467664562Sgshapiro		/*
467764562Sgshapiro		**  If filter told us to keep message but we had
467864562Sgshapiro		**  an error, we can't really keep it, tempfail it.
467964562Sgshapiro		*/
468064562Sgshapiro
468164562Sgshapiro		if (*state == SMFIR_CONTINUE ||
468264562Sgshapiro		    *state == SMFIR_ACCEPT)
468364562Sgshapiro		{
468464562Sgshapiro			*state = SMFIR_TEMPFAIL;
468590792Sgshapiro			SM_FREE_CLR(response);
468664562Sgshapiro		}
468764562Sgshapiro
468864562Sgshapiro		errno = save_errno;
468990792Sgshapiro		syserr("milter_data: %s/%cf%s: read error",
469090792Sgshapiro		       qid_printqueue(e->e_qgrp, e->e_qdir),
469190792Sgshapiro		       DATAFL_LETTER, e->e_id);
469264562Sgshapiro	}
469390792Sgshapiro
469464562Sgshapiro	MILTER_CHECK_DONE_MSG();
469590792Sgshapiro	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
469690792Sgshapiro		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data");
469764562Sgshapiro	return response;
469864562Sgshapiro}
4699132943Sgshapiro
470090792Sgshapiro/*
4701132943Sgshapiro**  MILTER_UNKNOWN -- send any unrecognized or unimplemented command
4702132943Sgshapiro**			string to milter filters
4703132943Sgshapiro**
4704132943Sgshapiro**	Parameters:
4705168515Sgshapiro**		smtpcmd -- the string itself.
4706132943Sgshapiro**		e -- current envelope.
4707132943Sgshapiro**		state -- return state from response.
4708132943Sgshapiro**
4709132943Sgshapiro**
4710132943Sgshapiro**	Returns:
4711132943Sgshapiro**		response string (may be NULL)
4712132943Sgshapiro*/
4713132943Sgshapiro
4714132943Sgshapirochar *
4715168515Sgshapiromilter_unknown(smtpcmd, e, state)
4716168515Sgshapiro	char *smtpcmd;
4717132943Sgshapiro	ENVELOPE *e;
4718132943Sgshapiro	char *state;
4719132943Sgshapiro{
4720132943Sgshapiro	if (tTd(64, 10))
4721168515Sgshapiro		sm_dprintf("milter_unknown(%s)\n", smtpcmd);
4722132943Sgshapiro
4723168515Sgshapiro	return milter_command(SMFIC_UNKNOWN, smtpcmd, strlen(smtpcmd) + 1,
4724244928Sgshapiro				SMFIM_NOMACROS, e, state, "unknown", false);
4725132943Sgshapiro}
4726132943Sgshapiro
4727132943Sgshapiro/*
472864562Sgshapiro**  MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
472964562Sgshapiro**
473064562Sgshapiro**	Parameters:
473164562Sgshapiro**		e -- current envelope.
473264562Sgshapiro**
473364562Sgshapiro**	Returns:
473464562Sgshapiro**		none
473564562Sgshapiro*/
473664562Sgshapiro
473764562Sgshapirovoid
473864562Sgshapiromilter_quit(e)
473964562Sgshapiro	ENVELOPE *e;
474064562Sgshapiro{
474164562Sgshapiro	int i;
474264562Sgshapiro
474364562Sgshapiro	if (tTd(64, 10))
474490792Sgshapiro		sm_dprintf("milter_quit(%s)\n", e->e_id);
474564562Sgshapiro
474664562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
474764562Sgshapiro		milter_quit_filter(InputFilters[i], e);
474864562Sgshapiro}
4749168515Sgshapiro
475090792Sgshapiro/*
475164562Sgshapiro**  MILTER_ABORT -- informs the filter(s) that we are aborting current message
475264562Sgshapiro**
475364562Sgshapiro**	Parameters:
475464562Sgshapiro**		e -- current envelope.
475564562Sgshapiro**
475664562Sgshapiro**	Returns:
475764562Sgshapiro**		none
475864562Sgshapiro*/
475964562Sgshapiro
476064562Sgshapirovoid
476164562Sgshapiromilter_abort(e)
476264562Sgshapiro	ENVELOPE *e;
476364562Sgshapiro{
476464562Sgshapiro	int i;
476564562Sgshapiro
476664562Sgshapiro	if (tTd(64, 10))
476790792Sgshapiro		sm_dprintf("milter_abort\n");
476864562Sgshapiro
476964562Sgshapiro	for (i = 0; InputFilters[i] != NULL; i++)
477064562Sgshapiro	{
477164562Sgshapiro		struct milter *m = InputFilters[i];
477264562Sgshapiro
477364562Sgshapiro		/* sanity checks */
477464562Sgshapiro		if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
477564562Sgshapiro			continue;
477664562Sgshapiro
477764562Sgshapiro		milter_abort_filter(m, e);
477864562Sgshapiro	}
477964562Sgshapiro}
478090792Sgshapiro#endif /* MILTER */
4781