xref: /illumos-gate/usr/src/cmd/sendmail/src/milter.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1999-2005 Sendmail, Inc. and its suppliers.
3*7c478bd9Sstevel@tonic-gate  *	All rights reserved.
4*7c478bd9Sstevel@tonic-gate  *
5*7c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
6*7c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
7*7c478bd9Sstevel@tonic-gate  * the sendmail distribution.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  */
10*7c478bd9Sstevel@tonic-gate 
11*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
12*7c478bd9Sstevel@tonic-gate 
13*7c478bd9Sstevel@tonic-gate #include <sendmail.h>
14*7c478bd9Sstevel@tonic-gate 
15*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: milter.c,v 8.229 2005/03/02 02:32:34 ca Exp $")
16*7c478bd9Sstevel@tonic-gate 
17*7c478bd9Sstevel@tonic-gate #if MILTER
18*7c478bd9Sstevel@tonic-gate # include <libmilter/mfapi.h>
19*7c478bd9Sstevel@tonic-gate # include <libmilter/mfdef.h>
20*7c478bd9Sstevel@tonic-gate 
21*7c478bd9Sstevel@tonic-gate # include <errno.h>
22*7c478bd9Sstevel@tonic-gate # include <sys/time.h>
23*7c478bd9Sstevel@tonic-gate # include <sys/uio.h>
24*7c478bd9Sstevel@tonic-gate 
25*7c478bd9Sstevel@tonic-gate # if NETINET || NETINET6
26*7c478bd9Sstevel@tonic-gate #  include <arpa/inet.h>
27*7c478bd9Sstevel@tonic-gate #  if _FFR_MILTER_NAGLE
28*7c478bd9Sstevel@tonic-gate #   include <netinet/tcp.h>
29*7c478bd9Sstevel@tonic-gate #  endif /* _FFR_MILTER_NAGLE */
30*7c478bd9Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate # include <sm/fdset.h>
33*7c478bd9Sstevel@tonic-gate 
34*7c478bd9Sstevel@tonic-gate static void	milter_connect_timeout __P((int));
35*7c478bd9Sstevel@tonic-gate static void	milter_error __P((struct milter *, ENVELOPE *));
36*7c478bd9Sstevel@tonic-gate static int	milter_open __P((struct milter *, bool, ENVELOPE *));
37*7c478bd9Sstevel@tonic-gate static void	milter_parse_timeouts __P((char *, struct milter *));
38*7c478bd9Sstevel@tonic-gate 
39*7c478bd9Sstevel@tonic-gate static char *MilterConnectMacros[MAXFILTERMACROS + 1];
40*7c478bd9Sstevel@tonic-gate static char *MilterHeloMacros[MAXFILTERMACROS + 1];
41*7c478bd9Sstevel@tonic-gate static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
42*7c478bd9Sstevel@tonic-gate static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
43*7c478bd9Sstevel@tonic-gate static char *MilterDataMacros[MAXFILTERMACROS + 1];
44*7c478bd9Sstevel@tonic-gate static char *MilterEOMMacros[MAXFILTERMACROS + 1];
45*7c478bd9Sstevel@tonic-gate static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE;
46*7c478bd9Sstevel@tonic-gate 
47*7c478bd9Sstevel@tonic-gate # define MILTER_CHECK_DONE_MSG() \
48*7c478bd9Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE || \
49*7c478bd9Sstevel@tonic-gate 	    *state == SMFIR_REJECT || \
50*7c478bd9Sstevel@tonic-gate 	    *state == SMFIR_DISCARD || \
51*7c478bd9Sstevel@tonic-gate 	    *state == SMFIR_TEMPFAIL) \
52*7c478bd9Sstevel@tonic-gate 	{ \
53*7c478bd9Sstevel@tonic-gate 		/* Abort the filters to let them know we are done with msg */ \
54*7c478bd9Sstevel@tonic-gate 		milter_abort(e); \
55*7c478bd9Sstevel@tonic-gate 	}
56*7c478bd9Sstevel@tonic-gate 
57*7c478bd9Sstevel@tonic-gate # define MILTER_CHECK_ERROR(initial, action) \
58*7c478bd9Sstevel@tonic-gate 	if (!initial && tTd(71, 100)) \
59*7c478bd9Sstevel@tonic-gate 	{ \
60*7c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
61*7c478bd9Sstevel@tonic-gate 		{ \
62*7c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
63*7c478bd9Sstevel@tonic-gate 							 "filter failure"); \
64*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
65*7c478bd9Sstevel@tonic-gate 				  e->e_quarmsg); \
66*7c478bd9Sstevel@tonic-gate 		} \
67*7c478bd9Sstevel@tonic-gate 	} \
68*7c478bd9Sstevel@tonic-gate 	else if (tTd(71, 101)) \
69*7c478bd9Sstevel@tonic-gate 	{ \
70*7c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
71*7c478bd9Sstevel@tonic-gate 		{ \
72*7c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
73*7c478bd9Sstevel@tonic-gate 							 "filter failure"); \
74*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
75*7c478bd9Sstevel@tonic-gate 				  e->e_quarmsg); \
76*7c478bd9Sstevel@tonic-gate 		} \
77*7c478bd9Sstevel@tonic-gate 	} \
78*7c478bd9Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
79*7c478bd9Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL; \
80*7c478bd9Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \
81*7c478bd9Sstevel@tonic-gate 		*state = SMFIR_SHUTDOWN; \
82*7c478bd9Sstevel@tonic-gate 	else if (bitnset(SMF_REJECT, m->mf_flags)) \
83*7c478bd9Sstevel@tonic-gate 		*state = SMFIR_REJECT; \
84*7c478bd9Sstevel@tonic-gate 	else \
85*7c478bd9Sstevel@tonic-gate 		action;
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate # define MILTER_CHECK_REPLYCODE(default) \
88*7c478bd9Sstevel@tonic-gate 	if (response == NULL || \
89*7c478bd9Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen || \
90*7c478bd9Sstevel@tonic-gate 	    rlen < 3 || \
91*7c478bd9Sstevel@tonic-gate 	    (response[0] != '4' && response[0] != '5') || \
92*7c478bd9Sstevel@tonic-gate 	    !isascii(response[1]) || !isdigit(response[1]) || \
93*7c478bd9Sstevel@tonic-gate 	    !isascii(response[2]) || !isdigit(response[2])) \
94*7c478bd9Sstevel@tonic-gate 	{ \
95*7c478bd9Sstevel@tonic-gate 		if (response != NULL) \
96*7c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */ \
97*7c478bd9Sstevel@tonic-gate 		response = newstr(default); \
98*7c478bd9Sstevel@tonic-gate 	} \
99*7c478bd9Sstevel@tonic-gate 	else \
100*7c478bd9Sstevel@tonic-gate 	{ \
101*7c478bd9Sstevel@tonic-gate 		char *ptr = response; \
102*7c478bd9Sstevel@tonic-gate  \
103*7c478bd9Sstevel@tonic-gate 		/* Check for unprotected %'s in the string */ \
104*7c478bd9Sstevel@tonic-gate 		while (*ptr != '\0') \
105*7c478bd9Sstevel@tonic-gate 		{ \
106*7c478bd9Sstevel@tonic-gate 			if (*ptr == '%' && *++ptr != '%') \
107*7c478bd9Sstevel@tonic-gate 			{ \
108*7c478bd9Sstevel@tonic-gate 				sm_free(response); /* XXX */ \
109*7c478bd9Sstevel@tonic-gate 				response = newstr(default); \
110*7c478bd9Sstevel@tonic-gate 				break; \
111*7c478bd9Sstevel@tonic-gate 			} \
112*7c478bd9Sstevel@tonic-gate 			ptr++; \
113*7c478bd9Sstevel@tonic-gate 		} \
114*7c478bd9Sstevel@tonic-gate 	}
115*7c478bd9Sstevel@tonic-gate 
116*7c478bd9Sstevel@tonic-gate # define MILTER_DF_ERROR(msg) \
117*7c478bd9Sstevel@tonic-gate { \
118*7c478bd9Sstevel@tonic-gate 	int save_errno = errno; \
119*7c478bd9Sstevel@tonic-gate  \
120*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 5)) \
121*7c478bd9Sstevel@tonic-gate 	{ \
122*7c478bd9Sstevel@tonic-gate 		sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
123*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\n"); \
124*7c478bd9Sstevel@tonic-gate 	} \
125*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 0) \
126*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
127*7c478bd9Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY) \
128*7c478bd9Sstevel@tonic-gate 	{ \
129*7c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL) \
130*7c478bd9Sstevel@tonic-gate 		{ \
131*7c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
132*7c478bd9Sstevel@tonic-gate 			e->e_dfp = NULL; \
133*7c478bd9Sstevel@tonic-gate 		} \
134*7c478bd9Sstevel@tonic-gate 		e->e_flags &= ~EF_HAS_DF; \
135*7c478bd9Sstevel@tonic-gate 	} \
136*7c478bd9Sstevel@tonic-gate 	errno = save_errno; \
137*7c478bd9Sstevel@tonic-gate }
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate /*
140*7c478bd9Sstevel@tonic-gate **  MILTER_TIMEOUT -- make sure socket is ready in time
141*7c478bd9Sstevel@tonic-gate **
142*7c478bd9Sstevel@tonic-gate **	Parameters:
143*7c478bd9Sstevel@tonic-gate **		routine -- routine name for debug/logging
144*7c478bd9Sstevel@tonic-gate **		secs -- number of seconds in timeout
145*7c478bd9Sstevel@tonic-gate **		write -- waiting to read or write?
146*7c478bd9Sstevel@tonic-gate **		started -- whether this is part of a previous sequence
147*7c478bd9Sstevel@tonic-gate **
148*7c478bd9Sstevel@tonic-gate **	Assumes 'm' is a milter structure for the current socket.
149*7c478bd9Sstevel@tonic-gate */
150*7c478bd9Sstevel@tonic-gate 
151*7c478bd9Sstevel@tonic-gate # define MILTER_TIMEOUT(routine, secs, write, started) \
152*7c478bd9Sstevel@tonic-gate { \
153*7c478bd9Sstevel@tonic-gate 	int ret; \
154*7c478bd9Sstevel@tonic-gate 	int save_errno; \
155*7c478bd9Sstevel@tonic-gate 	fd_set fds; \
156*7c478bd9Sstevel@tonic-gate 	struct timeval tv; \
157*7c478bd9Sstevel@tonic-gate  \
158*7c478bd9Sstevel@tonic-gate 	if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
159*7c478bd9Sstevel@tonic-gate 	{ \
160*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
161*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
162*7c478bd9Sstevel@tonic-gate 				   (routine), m->mf_name, m->mf_sock, \
163*7c478bd9Sstevel@tonic-gate 				   SM_FD_SETSIZE); \
164*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
165*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
166*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
167*7c478bd9Sstevel@tonic-gate 				  m->mf_name, (routine), m->mf_sock, \
168*7c478bd9Sstevel@tonic-gate 				  SM_FD_SETSIZE); \
169*7c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
170*7c478bd9Sstevel@tonic-gate 		return NULL; \
171*7c478bd9Sstevel@tonic-gate 	} \
172*7c478bd9Sstevel@tonic-gate  \
173*7c478bd9Sstevel@tonic-gate 	do \
174*7c478bd9Sstevel@tonic-gate 	{ \
175*7c478bd9Sstevel@tonic-gate 		FD_ZERO(&fds); \
176*7c478bd9Sstevel@tonic-gate 		SM_FD_SET(m->mf_sock, &fds); \
177*7c478bd9Sstevel@tonic-gate 		tv.tv_sec = (secs); \
178*7c478bd9Sstevel@tonic-gate 		tv.tv_usec = 0; \
179*7c478bd9Sstevel@tonic-gate 		ret = select(m->mf_sock + 1, \
180*7c478bd9Sstevel@tonic-gate 			     (write) ? NULL : &fds, \
181*7c478bd9Sstevel@tonic-gate 			     (write) ? &fds : NULL, \
182*7c478bd9Sstevel@tonic-gate 			     NULL, &tv); \
183*7c478bd9Sstevel@tonic-gate 	} while (ret < 0 && errno == EINTR); \
184*7c478bd9Sstevel@tonic-gate  \
185*7c478bd9Sstevel@tonic-gate 	switch (ret) \
186*7c478bd9Sstevel@tonic-gate 	{ \
187*7c478bd9Sstevel@tonic-gate 	  case 0: \
188*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
189*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): timeout\n", (routine), \
190*7c478bd9Sstevel@tonic-gate 				   m->mf_name); \
191*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
192*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
193*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): %s %s %s %s", \
194*7c478bd9Sstevel@tonic-gate 				  m->mf_name, "timeout", \
195*7c478bd9Sstevel@tonic-gate 				  started ? "during" : "before", \
196*7c478bd9Sstevel@tonic-gate 				  "data", (routine)); \
197*7c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
198*7c478bd9Sstevel@tonic-gate 		return NULL; \
199*7c478bd9Sstevel@tonic-gate  \
200*7c478bd9Sstevel@tonic-gate 	  case -1: \
201*7c478bd9Sstevel@tonic-gate 		save_errno = errno; \
202*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
203*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
204*7c478bd9Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno)); \
205*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
206*7c478bd9Sstevel@tonic-gate 		{ \
207*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
208*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): select(%s): %s", \
209*7c478bd9Sstevel@tonic-gate 				  m->mf_name, (routine), \
210*7c478bd9Sstevel@tonic-gate 				  sm_errstring(save_errno)); \
211*7c478bd9Sstevel@tonic-gate 		} \
212*7c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
213*7c478bd9Sstevel@tonic-gate 		return NULL; \
214*7c478bd9Sstevel@tonic-gate  \
215*7c478bd9Sstevel@tonic-gate 	  default: \
216*7c478bd9Sstevel@tonic-gate 		if (SM_FD_ISSET(m->mf_sock, &fds)) \
217*7c478bd9Sstevel@tonic-gate 			break; \
218*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
219*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket not ready\n", \
220*7c478bd9Sstevel@tonic-gate 				(routine), m->mf_name); \
221*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
222*7c478bd9Sstevel@tonic-gate 		{ \
223*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
224*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): socket(%s) not ready", \
225*7c478bd9Sstevel@tonic-gate 				  m->mf_name, (routine)); \
226*7c478bd9Sstevel@tonic-gate 		} \
227*7c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
228*7c478bd9Sstevel@tonic-gate 		return NULL; \
229*7c478bd9Sstevel@tonic-gate 	} \
230*7c478bd9Sstevel@tonic-gate }
231*7c478bd9Sstevel@tonic-gate 
232*7c478bd9Sstevel@tonic-gate /*
233*7c478bd9Sstevel@tonic-gate **  Low level functions
234*7c478bd9Sstevel@tonic-gate */
235*7c478bd9Sstevel@tonic-gate 
236*7c478bd9Sstevel@tonic-gate /*
237*7c478bd9Sstevel@tonic-gate **  MILTER_READ -- read from a remote milter filter
238*7c478bd9Sstevel@tonic-gate **
239*7c478bd9Sstevel@tonic-gate **	Parameters:
240*7c478bd9Sstevel@tonic-gate **		m -- milter to read from.
241*7c478bd9Sstevel@tonic-gate **		cmd -- return param for command read.
242*7c478bd9Sstevel@tonic-gate **		rlen -- return length of response string.
243*7c478bd9Sstevel@tonic-gate **		to -- timeout in seconds.
244*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
245*7c478bd9Sstevel@tonic-gate **
246*7c478bd9Sstevel@tonic-gate **	Returns:
247*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
248*7c478bd9Sstevel@tonic-gate */
249*7c478bd9Sstevel@tonic-gate 
250*7c478bd9Sstevel@tonic-gate static char *
251*7c478bd9Sstevel@tonic-gate milter_sysread(m, buf, sz, to, e)
252*7c478bd9Sstevel@tonic-gate 	struct milter *m;
253*7c478bd9Sstevel@tonic-gate 	char *buf;
254*7c478bd9Sstevel@tonic-gate 	ssize_t sz;
255*7c478bd9Sstevel@tonic-gate 	time_t to;
256*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
257*7c478bd9Sstevel@tonic-gate {
258*7c478bd9Sstevel@tonic-gate 	time_t readstart = 0;
259*7c478bd9Sstevel@tonic-gate 	ssize_t len, curl;
260*7c478bd9Sstevel@tonic-gate 	bool started = false;
261*7c478bd9Sstevel@tonic-gate 
262*7c478bd9Sstevel@tonic-gate 	curl = 0;
263*7c478bd9Sstevel@tonic-gate 
264*7c478bd9Sstevel@tonic-gate 	if (to > 0)
265*7c478bd9Sstevel@tonic-gate 		readstart = curtime();
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate 	for (;;)
268*7c478bd9Sstevel@tonic-gate 	{
269*7c478bd9Sstevel@tonic-gate 		if (to > 0)
270*7c478bd9Sstevel@tonic-gate 		{
271*7c478bd9Sstevel@tonic-gate 			time_t now;
272*7c478bd9Sstevel@tonic-gate 
273*7c478bd9Sstevel@tonic-gate 			now = curtime();
274*7c478bd9Sstevel@tonic-gate 			if (now - readstart >= to)
275*7c478bd9Sstevel@tonic-gate 			{
276*7c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
277*7c478bd9Sstevel@tonic-gate 					sm_dprintf("milter_read (%s): %s %s %s",
278*7c478bd9Sstevel@tonic-gate 						  m->mf_name, "timeout",
279*7c478bd9Sstevel@tonic-gate 						  started ? "during" : "before",
280*7c478bd9Sstevel@tonic-gate 						  "data read");
281*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
282*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
283*7c478bd9Sstevel@tonic-gate 						  "Milter (%s): %s %s %s",
284*7c478bd9Sstevel@tonic-gate 						  m->mf_name, "timeout",
285*7c478bd9Sstevel@tonic-gate 						  started ? "during" : "before",
286*7c478bd9Sstevel@tonic-gate 						  "data read");
287*7c478bd9Sstevel@tonic-gate 				milter_error(m, e);
288*7c478bd9Sstevel@tonic-gate 				return NULL;
289*7c478bd9Sstevel@tonic-gate 			}
290*7c478bd9Sstevel@tonic-gate 			to -= now - readstart;
291*7c478bd9Sstevel@tonic-gate 			readstart = now;
292*7c478bd9Sstevel@tonic-gate 			MILTER_TIMEOUT("read", to, false, started);
293*7c478bd9Sstevel@tonic-gate 		}
294*7c478bd9Sstevel@tonic-gate 
295*7c478bd9Sstevel@tonic-gate 		len = read(m->mf_sock, buf + curl, sz - curl);
296*7c478bd9Sstevel@tonic-gate 
297*7c478bd9Sstevel@tonic-gate 		if (len < 0)
298*7c478bd9Sstevel@tonic-gate 		{
299*7c478bd9Sstevel@tonic-gate 			int save_errno = errno;
300*7c478bd9Sstevel@tonic-gate 
301*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
302*7c478bd9Sstevel@tonic-gate 				sm_dprintf("milter_read(%s): read returned %ld: %s\n",
303*7c478bd9Sstevel@tonic-gate 					m->mf_name, (long) len,
304*7c478bd9Sstevel@tonic-gate 					sm_errstring(save_errno));
305*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
306*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
307*7c478bd9Sstevel@tonic-gate 					  "Milter (%s): read returned %ld: %s",
308*7c478bd9Sstevel@tonic-gate 					  m->mf_name, (long) len,
309*7c478bd9Sstevel@tonic-gate 					  sm_errstring(save_errno));
310*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
311*7c478bd9Sstevel@tonic-gate 			return NULL;
312*7c478bd9Sstevel@tonic-gate 		}
313*7c478bd9Sstevel@tonic-gate 
314*7c478bd9Sstevel@tonic-gate 		started = true;
315*7c478bd9Sstevel@tonic-gate 		curl += len;
316*7c478bd9Sstevel@tonic-gate 		if (len == 0 || curl >= sz)
317*7c478bd9Sstevel@tonic-gate 			break;
318*7c478bd9Sstevel@tonic-gate 
319*7c478bd9Sstevel@tonic-gate 	}
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	if (curl != sz)
322*7c478bd9Sstevel@tonic-gate 	{
323*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
324*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_read(%s): cmd read returned %ld, expecting %ld\n",
325*7c478bd9Sstevel@tonic-gate 				m->mf_name, (long) curl, (long) sz);
326*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
327*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
328*7c478bd9Sstevel@tonic-gate 				  "milter_read(%s): cmd read returned %ld, expecting %ld",
329*7c478bd9Sstevel@tonic-gate 				  m->mf_name, (long) curl, (long) sz);
330*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
331*7c478bd9Sstevel@tonic-gate 		return NULL;
332*7c478bd9Sstevel@tonic-gate 	}
333*7c478bd9Sstevel@tonic-gate 	return buf;
334*7c478bd9Sstevel@tonic-gate }
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate static char *
337*7c478bd9Sstevel@tonic-gate milter_read(m, cmd, rlen, to, e)
338*7c478bd9Sstevel@tonic-gate 	struct milter *m;
339*7c478bd9Sstevel@tonic-gate 	char *cmd;
340*7c478bd9Sstevel@tonic-gate 	ssize_t *rlen;
341*7c478bd9Sstevel@tonic-gate 	time_t to;
342*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
343*7c478bd9Sstevel@tonic-gate {
344*7c478bd9Sstevel@tonic-gate 	time_t readstart = 0;
345*7c478bd9Sstevel@tonic-gate 	ssize_t expl;
346*7c478bd9Sstevel@tonic-gate 	mi_int32 i;
347*7c478bd9Sstevel@tonic-gate # if _FFR_MILTER_NAGLE
348*7c478bd9Sstevel@tonic-gate #  ifdef TCP_CORK
349*7c478bd9Sstevel@tonic-gate 	int cork = 0;
350*7c478bd9Sstevel@tonic-gate #  endif
351*7c478bd9Sstevel@tonic-gate # endif /* _FFR_MILTER_NAGLE */
352*7c478bd9Sstevel@tonic-gate 	char *buf;
353*7c478bd9Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
354*7c478bd9Sstevel@tonic-gate 
355*7c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0)
356*7c478bd9Sstevel@tonic-gate 	{
357*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
358*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
359*7c478bd9Sstevel@tonic-gate 				  "milter_read(%s): socket closed",
360*7c478bd9Sstevel@tonic-gate 				  m->mf_name);
361*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
362*7c478bd9Sstevel@tonic-gate 		return NULL;
363*7c478bd9Sstevel@tonic-gate 	}
364*7c478bd9Sstevel@tonic-gate 
365*7c478bd9Sstevel@tonic-gate 	*rlen = 0;
366*7c478bd9Sstevel@tonic-gate 	*cmd = '\0';
367*7c478bd9Sstevel@tonic-gate 
368*7c478bd9Sstevel@tonic-gate 	if (to > 0)
369*7c478bd9Sstevel@tonic-gate 		readstart = curtime();
370*7c478bd9Sstevel@tonic-gate 
371*7c478bd9Sstevel@tonic-gate # if _FFR_MILTER_NAGLE
372*7c478bd9Sstevel@tonic-gate #  ifdef TCP_CORK
373*7c478bd9Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
374*7c478bd9Sstevel@tonic-gate 		   sizeof(cork));
375*7c478bd9Sstevel@tonic-gate #  endif
376*7c478bd9Sstevel@tonic-gate # endif /* _FFR_MILTER_NAGLE */
377*7c478bd9Sstevel@tonic-gate 
378*7c478bd9Sstevel@tonic-gate 	if (milter_sysread(m, data, sizeof data, to, e) == NULL)
379*7c478bd9Sstevel@tonic-gate 		return NULL;
380*7c478bd9Sstevel@tonic-gate 
381*7c478bd9Sstevel@tonic-gate # if _FFR_MILTER_NAGLE
382*7c478bd9Sstevel@tonic-gate #  ifdef TCP_CORK
383*7c478bd9Sstevel@tonic-gate 	cork = 1;
384*7c478bd9Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
385*7c478bd9Sstevel@tonic-gate 		   sizeof(cork));
386*7c478bd9Sstevel@tonic-gate #  endif
387*7c478bd9Sstevel@tonic-gate # endif /* _FFR_MILTER_NAGLE */
388*7c478bd9Sstevel@tonic-gate 
389*7c478bd9Sstevel@tonic-gate 	/* reset timeout */
390*7c478bd9Sstevel@tonic-gate 	if (to > 0)
391*7c478bd9Sstevel@tonic-gate 	{
392*7c478bd9Sstevel@tonic-gate 		time_t now;
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate 		now = curtime();
395*7c478bd9Sstevel@tonic-gate 		if (now - readstart >= to)
396*7c478bd9Sstevel@tonic-gate 		{
397*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
398*7c478bd9Sstevel@tonic-gate 				sm_dprintf("milter_read(%s): timeout before data read\n",
399*7c478bd9Sstevel@tonic-gate 					m->mf_name);
400*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
401*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
402*7c478bd9Sstevel@tonic-gate 					  "Milter read(%s): timeout before data read",
403*7c478bd9Sstevel@tonic-gate 					  m->mf_name);
404*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
405*7c478bd9Sstevel@tonic-gate 			return NULL;
406*7c478bd9Sstevel@tonic-gate 		}
407*7c478bd9Sstevel@tonic-gate 		to -= now - readstart;
408*7c478bd9Sstevel@tonic-gate 	}
409*7c478bd9Sstevel@tonic-gate 
410*7c478bd9Sstevel@tonic-gate 	*cmd = data[MILTER_LEN_BYTES];
411*7c478bd9Sstevel@tonic-gate 	data[MILTER_LEN_BYTES] = '\0';
412*7c478bd9Sstevel@tonic-gate 	(void) memcpy(&i, data, MILTER_LEN_BYTES);
413*7c478bd9Sstevel@tonic-gate 	expl = ntohl(i) - 1;
414*7c478bd9Sstevel@tonic-gate 
415*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 25))
416*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): expecting %ld bytes\n",
417*7c478bd9Sstevel@tonic-gate 			m->mf_name, (long) expl);
418*7c478bd9Sstevel@tonic-gate 
419*7c478bd9Sstevel@tonic-gate 	if (expl < 0)
420*7c478bd9Sstevel@tonic-gate 	{
421*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
422*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_read(%s): read size %ld out of range\n",
423*7c478bd9Sstevel@tonic-gate 				m->mf_name, (long) expl);
424*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
425*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
426*7c478bd9Sstevel@tonic-gate 				  "milter_read(%s): read size %ld out of range",
427*7c478bd9Sstevel@tonic-gate 				  m->mf_name, (long) expl);
428*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
429*7c478bd9Sstevel@tonic-gate 		return NULL;
430*7c478bd9Sstevel@tonic-gate 	}
431*7c478bd9Sstevel@tonic-gate 
432*7c478bd9Sstevel@tonic-gate 	if (expl == 0)
433*7c478bd9Sstevel@tonic-gate 		return NULL;
434*7c478bd9Sstevel@tonic-gate 
435*7c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(expl);
436*7c478bd9Sstevel@tonic-gate 
437*7c478bd9Sstevel@tonic-gate 	if (milter_sysread(m, buf, expl, to, e) == NULL)
438*7c478bd9Sstevel@tonic-gate 	{
439*7c478bd9Sstevel@tonic-gate 		sm_free(buf); /* XXX */
440*7c478bd9Sstevel@tonic-gate 		return NULL;
441*7c478bd9Sstevel@tonic-gate 	}
442*7c478bd9Sstevel@tonic-gate 
443*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 50))
444*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): Returning %*s\n",
445*7c478bd9Sstevel@tonic-gate 			m->mf_name, (int) expl, buf);
446*7c478bd9Sstevel@tonic-gate 	*rlen = expl;
447*7c478bd9Sstevel@tonic-gate 	return buf;
448*7c478bd9Sstevel@tonic-gate }
449*7c478bd9Sstevel@tonic-gate 
450*7c478bd9Sstevel@tonic-gate /*
451*7c478bd9Sstevel@tonic-gate **  MILTER_WRITE -- write to a remote milter filter
452*7c478bd9Sstevel@tonic-gate **
453*7c478bd9Sstevel@tonic-gate **	Parameters:
454*7c478bd9Sstevel@tonic-gate **		m -- milter to read from.
455*7c478bd9Sstevel@tonic-gate **		cmd -- command to send.
456*7c478bd9Sstevel@tonic-gate **		buf -- optional command data.
457*7c478bd9Sstevel@tonic-gate **		len -- length of buf.
458*7c478bd9Sstevel@tonic-gate **		to -- timeout in seconds.
459*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
460*7c478bd9Sstevel@tonic-gate **
461*7c478bd9Sstevel@tonic-gate **	Returns:
462*7c478bd9Sstevel@tonic-gate **		buf if successful, NULL otherwise
463*7c478bd9Sstevel@tonic-gate **		Not actually used anywhere but function prototype
464*7c478bd9Sstevel@tonic-gate **			must match milter_read()
465*7c478bd9Sstevel@tonic-gate */
466*7c478bd9Sstevel@tonic-gate 
467*7c478bd9Sstevel@tonic-gate static char *
468*7c478bd9Sstevel@tonic-gate milter_write(m, cmd, buf, len, to, e)
469*7c478bd9Sstevel@tonic-gate 	struct milter *m;
470*7c478bd9Sstevel@tonic-gate 	char cmd;
471*7c478bd9Sstevel@tonic-gate 	char *buf;
472*7c478bd9Sstevel@tonic-gate 	ssize_t len;
473*7c478bd9Sstevel@tonic-gate 	time_t to;
474*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
475*7c478bd9Sstevel@tonic-gate {
476*7c478bd9Sstevel@tonic-gate 	time_t writestart = (time_t) 0;
477*7c478bd9Sstevel@tonic-gate 	ssize_t sl, i;
478*7c478bd9Sstevel@tonic-gate 	int num_vectors;
479*7c478bd9Sstevel@tonic-gate 	mi_int32 nl;
480*7c478bd9Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
481*7c478bd9Sstevel@tonic-gate 	bool started = false;
482*7c478bd9Sstevel@tonic-gate 	struct iovec vector[2];
483*7c478bd9Sstevel@tonic-gate 
484*7c478bd9Sstevel@tonic-gate 	/*
485*7c478bd9Sstevel@tonic-gate 	**  At most two buffers will be written, though
486*7c478bd9Sstevel@tonic-gate 	**  only one may actually be used (see num_vectors).
487*7c478bd9Sstevel@tonic-gate 	**  The first is the size/command and the second is the command data.
488*7c478bd9Sstevel@tonic-gate 	*/
489*7c478bd9Sstevel@tonic-gate 
490*7c478bd9Sstevel@tonic-gate 	if (len < 0 || len > MilterMaxDataSize)
491*7c478bd9Sstevel@tonic-gate 	{
492*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
493*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): length %ld out of range\n",
494*7c478bd9Sstevel@tonic-gate 				m->mf_name, (long) len);
495*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
496*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
497*7c478bd9Sstevel@tonic-gate 				  "milter_write(%s): length %ld out of range",
498*7c478bd9Sstevel@tonic-gate 				  m->mf_name, (long) len);
499*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
500*7c478bd9Sstevel@tonic-gate 		return NULL;
501*7c478bd9Sstevel@tonic-gate 	}
502*7c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0)
503*7c478bd9Sstevel@tonic-gate 	{
504*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
505*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
506*7c478bd9Sstevel@tonic-gate 				  "milter_write(%s): socket closed",
507*7c478bd9Sstevel@tonic-gate 				  m->mf_name);
508*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
509*7c478bd9Sstevel@tonic-gate 		return NULL;
510*7c478bd9Sstevel@tonic-gate 	}
511*7c478bd9Sstevel@tonic-gate 
512*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 20))
513*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
514*7c478bd9Sstevel@tonic-gate 			   m->mf_name, cmd, (long) len);
515*7c478bd9Sstevel@tonic-gate 
516*7c478bd9Sstevel@tonic-gate 	nl = htonl(len + 1);	/* add 1 for the cmd char */
517*7c478bd9Sstevel@tonic-gate 	(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
518*7c478bd9Sstevel@tonic-gate 	data[MILTER_LEN_BYTES] = cmd;
519*7c478bd9Sstevel@tonic-gate 	sl = MILTER_LEN_BYTES + 1;
520*7c478bd9Sstevel@tonic-gate 
521*7c478bd9Sstevel@tonic-gate 	/* set up the vector for the size / command */
522*7c478bd9Sstevel@tonic-gate 	vector[0].iov_base = (void *) data;
523*7c478bd9Sstevel@tonic-gate 	vector[0].iov_len  = sl;
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 	/*
526*7c478bd9Sstevel@tonic-gate 	**  Determine if there is command data.  If so, there will be two
527*7c478bd9Sstevel@tonic-gate 	**  vectors.  If not, there will be only one.  The vectors are set
528*7c478bd9Sstevel@tonic-gate 	**  up here and 'num_vectors' and 'sl' are set appropriately.
529*7c478bd9Sstevel@tonic-gate 	*/
530*7c478bd9Sstevel@tonic-gate 
531*7c478bd9Sstevel@tonic-gate 	/* NOTE:  len<0 has already been checked for.  Pedantic */
532*7c478bd9Sstevel@tonic-gate 	if (len <= 0 || buf == NULL)
533*7c478bd9Sstevel@tonic-gate 	{
534*7c478bd9Sstevel@tonic-gate 		/* There is no command data -- only a size / command data */
535*7c478bd9Sstevel@tonic-gate 		num_vectors = 1;
536*7c478bd9Sstevel@tonic-gate 	}
537*7c478bd9Sstevel@tonic-gate 	else
538*7c478bd9Sstevel@tonic-gate 	{
539*7c478bd9Sstevel@tonic-gate 		/*
540*7c478bd9Sstevel@tonic-gate 		**  There is both size / command and command data.
541*7c478bd9Sstevel@tonic-gate 		**  Set up the vector for the command data.
542*7c478bd9Sstevel@tonic-gate 		*/
543*7c478bd9Sstevel@tonic-gate 
544*7c478bd9Sstevel@tonic-gate 		num_vectors = 2;
545*7c478bd9Sstevel@tonic-gate 		sl += len;
546*7c478bd9Sstevel@tonic-gate 		vector[1].iov_base = (void *) buf;
547*7c478bd9Sstevel@tonic-gate 		vector[1].iov_len  = len;
548*7c478bd9Sstevel@tonic-gate 
549*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 50))
550*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): Sending %*s\n",
551*7c478bd9Sstevel@tonic-gate 				   m->mf_name, (int) len, buf);
552*7c478bd9Sstevel@tonic-gate 	}
553*7c478bd9Sstevel@tonic-gate 
554*7c478bd9Sstevel@tonic-gate 	if (to > 0)
555*7c478bd9Sstevel@tonic-gate 	{
556*7c478bd9Sstevel@tonic-gate 		writestart = curtime();
557*7c478bd9Sstevel@tonic-gate 		MILTER_TIMEOUT("write", to, true, started);
558*7c478bd9Sstevel@tonic-gate 	}
559*7c478bd9Sstevel@tonic-gate 
560*7c478bd9Sstevel@tonic-gate 	/* write the vector(s) */
561*7c478bd9Sstevel@tonic-gate 	i = writev(m->mf_sock, vector, num_vectors);
562*7c478bd9Sstevel@tonic-gate 	if (i != sl)
563*7c478bd9Sstevel@tonic-gate 	{
564*7c478bd9Sstevel@tonic-gate 		int save_errno = errno;
565*7c478bd9Sstevel@tonic-gate 
566*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
567*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
568*7c478bd9Sstevel@tonic-gate 				   m->mf_name, cmd, (long) i, (long) sl,
569*7c478bd9Sstevel@tonic-gate 				   sm_errstring(save_errno));
570*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
571*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
572*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): write(%c) returned %ld, expected %ld: %s",
573*7c478bd9Sstevel@tonic-gate 				  m->mf_name, cmd, (long) i, (long) sl,
574*7c478bd9Sstevel@tonic-gate 				  sm_errstring(save_errno));
575*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
576*7c478bd9Sstevel@tonic-gate 		return NULL;
577*7c478bd9Sstevel@tonic-gate 	}
578*7c478bd9Sstevel@tonic-gate 	return buf;
579*7c478bd9Sstevel@tonic-gate }
580*7c478bd9Sstevel@tonic-gate 
581*7c478bd9Sstevel@tonic-gate /*
582*7c478bd9Sstevel@tonic-gate **  Utility functions
583*7c478bd9Sstevel@tonic-gate */
584*7c478bd9Sstevel@tonic-gate 
585*7c478bd9Sstevel@tonic-gate /*
586*7c478bd9Sstevel@tonic-gate **  MILTER_OPEN -- connect to remote milter filter
587*7c478bd9Sstevel@tonic-gate **
588*7c478bd9Sstevel@tonic-gate **	Parameters:
589*7c478bd9Sstevel@tonic-gate **		m -- milter to connect to.
590*7c478bd9Sstevel@tonic-gate **		parseonly -- parse but don't connect.
591*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
592*7c478bd9Sstevel@tonic-gate **
593*7c478bd9Sstevel@tonic-gate **	Returns:
594*7c478bd9Sstevel@tonic-gate **		connected socket if successful && !parseonly,
595*7c478bd9Sstevel@tonic-gate **		0 upon parse success if parseonly,
596*7c478bd9Sstevel@tonic-gate **		-1 otherwise.
597*7c478bd9Sstevel@tonic-gate */
598*7c478bd9Sstevel@tonic-gate 
599*7c478bd9Sstevel@tonic-gate static jmp_buf	MilterConnectTimeout;
600*7c478bd9Sstevel@tonic-gate 
601*7c478bd9Sstevel@tonic-gate static int
602*7c478bd9Sstevel@tonic-gate milter_open(m, parseonly, e)
603*7c478bd9Sstevel@tonic-gate 	struct milter *m;
604*7c478bd9Sstevel@tonic-gate 	bool parseonly;
605*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
606*7c478bd9Sstevel@tonic-gate {
607*7c478bd9Sstevel@tonic-gate 	int sock = 0;
608*7c478bd9Sstevel@tonic-gate 	SOCKADDR_LEN_T addrlen = 0;
609*7c478bd9Sstevel@tonic-gate 	int addrno = 0;
610*7c478bd9Sstevel@tonic-gate 	int save_errno;
611*7c478bd9Sstevel@tonic-gate 	char *p;
612*7c478bd9Sstevel@tonic-gate 	char *colon;
613*7c478bd9Sstevel@tonic-gate 	char *at;
614*7c478bd9Sstevel@tonic-gate 	struct hostent *hp = NULL;
615*7c478bd9Sstevel@tonic-gate 	SOCKADDR addr;
616*7c478bd9Sstevel@tonic-gate 
617*7c478bd9Sstevel@tonic-gate 	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
618*7c478bd9Sstevel@tonic-gate 	{
619*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
620*7c478bd9Sstevel@tonic-gate 			sm_dprintf("X%s: empty or missing socket information\n",
621*7c478bd9Sstevel@tonic-gate 				   m->mf_name);
622*7c478bd9Sstevel@tonic-gate 		if (parseonly)
623*7c478bd9Sstevel@tonic-gate 			syserr("X%s: empty or missing socket information",
624*7c478bd9Sstevel@tonic-gate 			       m->mf_name);
625*7c478bd9Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
626*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
627*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): empty or missing socket information",
628*7c478bd9Sstevel@tonic-gate 				  m->mf_name);
629*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
630*7c478bd9Sstevel@tonic-gate 		return -1;
631*7c478bd9Sstevel@tonic-gate 	}
632*7c478bd9Sstevel@tonic-gate 
633*7c478bd9Sstevel@tonic-gate 	/* protocol:filename or protocol:port@host */
634*7c478bd9Sstevel@tonic-gate 	memset(&addr, '\0', sizeof addr);
635*7c478bd9Sstevel@tonic-gate 	p = m->mf_conn;
636*7c478bd9Sstevel@tonic-gate 	colon = strchr(p, ':');
637*7c478bd9Sstevel@tonic-gate 	if (colon != NULL)
638*7c478bd9Sstevel@tonic-gate 	{
639*7c478bd9Sstevel@tonic-gate 		*colon = '\0';
640*7c478bd9Sstevel@tonic-gate 
641*7c478bd9Sstevel@tonic-gate 		if (*p == '\0')
642*7c478bd9Sstevel@tonic-gate 		{
643*7c478bd9Sstevel@tonic-gate # if NETUNIX
644*7c478bd9Sstevel@tonic-gate 			/* default to AF_UNIX */
645*7c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
646*7c478bd9Sstevel@tonic-gate # else /* NETUNIX */
647*7c478bd9Sstevel@tonic-gate #  if NETINET
648*7c478bd9Sstevel@tonic-gate 			/* default to AF_INET */
649*7c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
650*7c478bd9Sstevel@tonic-gate #  else /* NETINET */
651*7c478bd9Sstevel@tonic-gate #   if NETINET6
652*7c478bd9Sstevel@tonic-gate 			/* default to AF_INET6 */
653*7c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
654*7c478bd9Sstevel@tonic-gate #   else /* NETINET6 */
655*7c478bd9Sstevel@tonic-gate 			/* no protocols available */
656*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
657*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
658*7c478bd9Sstevel@tonic-gate 					  "Milter (%s): no valid socket protocols available",
659*7c478bd9Sstevel@tonic-gate 					  m->mf_name);
660*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
661*7c478bd9Sstevel@tonic-gate 			return -1;
662*7c478bd9Sstevel@tonic-gate #   endif /* NETINET6 */
663*7c478bd9Sstevel@tonic-gate #  endif /* NETINET */
664*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
665*7c478bd9Sstevel@tonic-gate 		}
666*7c478bd9Sstevel@tonic-gate # if NETUNIX
667*7c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "unix") == 0 ||
668*7c478bd9Sstevel@tonic-gate 			 sm_strcasecmp(p, "local") == 0)
669*7c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
670*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
671*7c478bd9Sstevel@tonic-gate # if NETINET
672*7c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet") == 0)
673*7c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
674*7c478bd9Sstevel@tonic-gate # endif /* NETINET */
675*7c478bd9Sstevel@tonic-gate # if NETINET6
676*7c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet6") == 0)
677*7c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
678*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
679*7c478bd9Sstevel@tonic-gate 		else
680*7c478bd9Sstevel@tonic-gate 		{
681*7c478bd9Sstevel@tonic-gate # ifdef EPROTONOSUPPORT
682*7c478bd9Sstevel@tonic-gate 			errno = EPROTONOSUPPORT;
683*7c478bd9Sstevel@tonic-gate # else /* EPROTONOSUPPORT */
684*7c478bd9Sstevel@tonic-gate 			errno = EINVAL;
685*7c478bd9Sstevel@tonic-gate # endif /* EPROTONOSUPPORT */
686*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
687*7c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: unknown socket type %s\n",
688*7c478bd9Sstevel@tonic-gate 					m->mf_name, p);
689*7c478bd9Sstevel@tonic-gate 			if (parseonly)
690*7c478bd9Sstevel@tonic-gate 				syserr("X%s: unknown socket type %s",
691*7c478bd9Sstevel@tonic-gate 				       m->mf_name, p);
692*7c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
693*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
694*7c478bd9Sstevel@tonic-gate 					  "Milter (%s): unknown socket type %s",
695*7c478bd9Sstevel@tonic-gate 					  m->mf_name, p);
696*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
697*7c478bd9Sstevel@tonic-gate 			return -1;
698*7c478bd9Sstevel@tonic-gate 		}
699*7c478bd9Sstevel@tonic-gate 		*colon++ = ':';
700*7c478bd9Sstevel@tonic-gate 	}
701*7c478bd9Sstevel@tonic-gate 	else
702*7c478bd9Sstevel@tonic-gate 	{
703*7c478bd9Sstevel@tonic-gate 		/* default to AF_UNIX */
704*7c478bd9Sstevel@tonic-gate 		addr.sa.sa_family = AF_UNIX;
705*7c478bd9Sstevel@tonic-gate 		colon = p;
706*7c478bd9Sstevel@tonic-gate 	}
707*7c478bd9Sstevel@tonic-gate 
708*7c478bd9Sstevel@tonic-gate # if NETUNIX
709*7c478bd9Sstevel@tonic-gate 	if (addr.sa.sa_family == AF_UNIX)
710*7c478bd9Sstevel@tonic-gate 	{
711*7c478bd9Sstevel@tonic-gate 		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
712*7c478bd9Sstevel@tonic-gate 
713*7c478bd9Sstevel@tonic-gate 		at = colon;
714*7c478bd9Sstevel@tonic-gate 		if (strlen(colon) >= sizeof addr.sunix.sun_path)
715*7c478bd9Sstevel@tonic-gate 		{
716*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
717*7c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s too long\n",
718*7c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
719*7c478bd9Sstevel@tonic-gate 			errno = EINVAL;
720*7c478bd9Sstevel@tonic-gate 			if (parseonly)
721*7c478bd9Sstevel@tonic-gate 				syserr("X%s: local socket name %s too long",
722*7c478bd9Sstevel@tonic-gate 				       m->mf_name, colon);
723*7c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
724*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
725*7c478bd9Sstevel@tonic-gate 					  "Milter (%s): local socket name %s too long",
726*7c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
727*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
728*7c478bd9Sstevel@tonic-gate 			return -1;
729*7c478bd9Sstevel@tonic-gate 		}
730*7c478bd9Sstevel@tonic-gate 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
731*7c478bd9Sstevel@tonic-gate 				 S_IRUSR|S_IWUSR, NULL);
732*7c478bd9Sstevel@tonic-gate 
733*7c478bd9Sstevel@tonic-gate 		/* if just parsing .cf file, socket doesn't need to exist */
734*7c478bd9Sstevel@tonic-gate 		if (parseonly && errno == ENOENT)
735*7c478bd9Sstevel@tonic-gate 		{
736*7c478bd9Sstevel@tonic-gate 			if (OpMode == MD_DAEMON ||
737*7c478bd9Sstevel@tonic-gate 			    OpMode == MD_FGDAEMON)
738*7c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
739*7c478bd9Sstevel@tonic-gate 						     "WARNING: X%s: local socket name %s missing\n",
740*7c478bd9Sstevel@tonic-gate 						     m->mf_name, colon);
741*7c478bd9Sstevel@tonic-gate 		}
742*7c478bd9Sstevel@tonic-gate 		else if (errno != 0)
743*7c478bd9Sstevel@tonic-gate 		{
744*7c478bd9Sstevel@tonic-gate 			/* if not safe, don't create */
745*7c478bd9Sstevel@tonic-gate 			save_errno = errno;
746*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
747*7c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s unsafe\n",
748*7c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
749*7c478bd9Sstevel@tonic-gate 			errno = save_errno;
750*7c478bd9Sstevel@tonic-gate 			if (parseonly)
751*7c478bd9Sstevel@tonic-gate 			{
752*7c478bd9Sstevel@tonic-gate 				if (OpMode == MD_DAEMON ||
753*7c478bd9Sstevel@tonic-gate 				    OpMode == MD_FGDAEMON ||
754*7c478bd9Sstevel@tonic-gate 				    OpMode == MD_SMTP)
755*7c478bd9Sstevel@tonic-gate 					syserr("X%s: local socket name %s unsafe",
756*7c478bd9Sstevel@tonic-gate 					       m->mf_name, colon);
757*7c478bd9Sstevel@tonic-gate 			}
758*7c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
759*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
760*7c478bd9Sstevel@tonic-gate 					  "Milter (%s): local socket name %s unsafe",
761*7c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
762*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
763*7c478bd9Sstevel@tonic-gate 			return -1;
764*7c478bd9Sstevel@tonic-gate 		}
765*7c478bd9Sstevel@tonic-gate 
766*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(addr.sunix.sun_path, colon,
767*7c478bd9Sstevel@tonic-gate 			       sizeof addr.sunix.sun_path);
768*7c478bd9Sstevel@tonic-gate 		addrlen = sizeof (struct sockaddr_un);
769*7c478bd9Sstevel@tonic-gate 	}
770*7c478bd9Sstevel@tonic-gate 	else
771*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
772*7c478bd9Sstevel@tonic-gate # if NETINET || NETINET6
773*7c478bd9Sstevel@tonic-gate 	if (false
774*7c478bd9Sstevel@tonic-gate #  if NETINET
775*7c478bd9Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET
776*7c478bd9Sstevel@tonic-gate #  endif /* NETINET */
777*7c478bd9Sstevel@tonic-gate #  if NETINET6
778*7c478bd9Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET6
779*7c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
780*7c478bd9Sstevel@tonic-gate 		 )
781*7c478bd9Sstevel@tonic-gate 	{
782*7c478bd9Sstevel@tonic-gate 		unsigned short port;
783*7c478bd9Sstevel@tonic-gate 
784*7c478bd9Sstevel@tonic-gate 		/* Parse port@host */
785*7c478bd9Sstevel@tonic-gate 		at = strchr(colon, '@');
786*7c478bd9Sstevel@tonic-gate 		if (at == NULL)
787*7c478bd9Sstevel@tonic-gate 		{
788*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
789*7c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: bad address %s (expected port@host)\n",
790*7c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
791*7c478bd9Sstevel@tonic-gate 			if (parseonly)
792*7c478bd9Sstevel@tonic-gate 				syserr("X%s: bad address %s (expected port@host)",
793*7c478bd9Sstevel@tonic-gate 				       m->mf_name, colon);
794*7c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
795*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
796*7c478bd9Sstevel@tonic-gate 					  "Milter (%s): bad address %s (expected port@host)",
797*7c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
798*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
799*7c478bd9Sstevel@tonic-gate 			return -1;
800*7c478bd9Sstevel@tonic-gate 		}
801*7c478bd9Sstevel@tonic-gate 		*at = '\0';
802*7c478bd9Sstevel@tonic-gate 		if (isascii(*colon) && isdigit(*colon))
803*7c478bd9Sstevel@tonic-gate 			port = htons((unsigned short) atoi(colon));
804*7c478bd9Sstevel@tonic-gate 		else
805*7c478bd9Sstevel@tonic-gate 		{
806*7c478bd9Sstevel@tonic-gate #  ifdef NO_GETSERVBYNAME
807*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
808*7c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: invalid port number %s\n",
809*7c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
810*7c478bd9Sstevel@tonic-gate 			if (parseonly)
811*7c478bd9Sstevel@tonic-gate 				syserr("X%s: invalid port number %s",
812*7c478bd9Sstevel@tonic-gate 				       m->mf_name, colon);
813*7c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
814*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
815*7c478bd9Sstevel@tonic-gate 					  "Milter (%s): invalid port number %s",
816*7c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
817*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
818*7c478bd9Sstevel@tonic-gate 			return -1;
819*7c478bd9Sstevel@tonic-gate #  else /* NO_GETSERVBYNAME */
820*7c478bd9Sstevel@tonic-gate 			register struct servent *sp;
821*7c478bd9Sstevel@tonic-gate 
822*7c478bd9Sstevel@tonic-gate 			sp = getservbyname(colon, "tcp");
823*7c478bd9Sstevel@tonic-gate 			if (sp == NULL)
824*7c478bd9Sstevel@tonic-gate 			{
825*7c478bd9Sstevel@tonic-gate 				save_errno = errno;
826*7c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
827*7c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: unknown port name %s\n",
828*7c478bd9Sstevel@tonic-gate 						m->mf_name, colon);
829*7c478bd9Sstevel@tonic-gate 				errno = save_errno;
830*7c478bd9Sstevel@tonic-gate 				if (parseonly)
831*7c478bd9Sstevel@tonic-gate 					syserr("X%s: unknown port name %s",
832*7c478bd9Sstevel@tonic-gate 					       m->mf_name, colon);
833*7c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
834*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
835*7c478bd9Sstevel@tonic-gate 						  "Milter (%s): unknown port name %s",
836*7c478bd9Sstevel@tonic-gate 						  m->mf_name, colon);
837*7c478bd9Sstevel@tonic-gate 				milter_error(m, e);
838*7c478bd9Sstevel@tonic-gate 				return -1;
839*7c478bd9Sstevel@tonic-gate 			}
840*7c478bd9Sstevel@tonic-gate 			port = sp->s_port;
841*7c478bd9Sstevel@tonic-gate #  endif /* NO_GETSERVBYNAME */
842*7c478bd9Sstevel@tonic-gate 		}
843*7c478bd9Sstevel@tonic-gate 		*at++ = '@';
844*7c478bd9Sstevel@tonic-gate 		if (*at == '[')
845*7c478bd9Sstevel@tonic-gate 		{
846*7c478bd9Sstevel@tonic-gate 			char *end;
847*7c478bd9Sstevel@tonic-gate 
848*7c478bd9Sstevel@tonic-gate 			end = strchr(at, ']');
849*7c478bd9Sstevel@tonic-gate 			if (end != NULL)
850*7c478bd9Sstevel@tonic-gate 			{
851*7c478bd9Sstevel@tonic-gate 				bool found = false;
852*7c478bd9Sstevel@tonic-gate #  if NETINET
853*7c478bd9Sstevel@tonic-gate 				unsigned long hid = INADDR_NONE;
854*7c478bd9Sstevel@tonic-gate #  endif /* NETINET */
855*7c478bd9Sstevel@tonic-gate #  if NETINET6
856*7c478bd9Sstevel@tonic-gate 				struct sockaddr_in6 hid6;
857*7c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
858*7c478bd9Sstevel@tonic-gate 
859*7c478bd9Sstevel@tonic-gate 				*end = '\0';
860*7c478bd9Sstevel@tonic-gate #  if NETINET
861*7c478bd9Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET &&
862*7c478bd9Sstevel@tonic-gate 				    (hid = inet_addr(&at[1])) != INADDR_NONE)
863*7c478bd9Sstevel@tonic-gate 				{
864*7c478bd9Sstevel@tonic-gate 					addr.sin.sin_addr.s_addr = hid;
865*7c478bd9Sstevel@tonic-gate 					addr.sin.sin_port = port;
866*7c478bd9Sstevel@tonic-gate 					found = true;
867*7c478bd9Sstevel@tonic-gate 				}
868*7c478bd9Sstevel@tonic-gate #  endif /* NETINET */
869*7c478bd9Sstevel@tonic-gate #  if NETINET6
870*7c478bd9Sstevel@tonic-gate 				(void) memset(&hid6, '\0', sizeof hid6);
871*7c478bd9Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET6 &&
872*7c478bd9Sstevel@tonic-gate 				    anynet_pton(AF_INET6, &at[1],
873*7c478bd9Sstevel@tonic-gate 						&hid6.sin6_addr) == 1)
874*7c478bd9Sstevel@tonic-gate 				{
875*7c478bd9Sstevel@tonic-gate 					addr.sin6.sin6_addr = hid6.sin6_addr;
876*7c478bd9Sstevel@tonic-gate 					addr.sin6.sin6_port = port;
877*7c478bd9Sstevel@tonic-gate 					found = true;
878*7c478bd9Sstevel@tonic-gate 				}
879*7c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
880*7c478bd9Sstevel@tonic-gate 				*end = ']';
881*7c478bd9Sstevel@tonic-gate 				if (!found)
882*7c478bd9Sstevel@tonic-gate 				{
883*7c478bd9Sstevel@tonic-gate 					if (tTd(64, 5))
884*7c478bd9Sstevel@tonic-gate 						sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
885*7c478bd9Sstevel@tonic-gate 							m->mf_name, at);
886*7c478bd9Sstevel@tonic-gate 					if (parseonly)
887*7c478bd9Sstevel@tonic-gate 						syserr("X%s: Invalid numeric domain spec \"%s\"",
888*7c478bd9Sstevel@tonic-gate 						       m->mf_name, at);
889*7c478bd9Sstevel@tonic-gate 					else if (MilterLogLevel > 0)
890*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
891*7c478bd9Sstevel@tonic-gate 							  "Milter (%s): Invalid numeric domain spec \"%s\"",
892*7c478bd9Sstevel@tonic-gate 							  m->mf_name, at);
893*7c478bd9Sstevel@tonic-gate 					milter_error(m, e);
894*7c478bd9Sstevel@tonic-gate 					return -1;
895*7c478bd9Sstevel@tonic-gate 				}
896*7c478bd9Sstevel@tonic-gate 			}
897*7c478bd9Sstevel@tonic-gate 			else
898*7c478bd9Sstevel@tonic-gate 			{
899*7c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
900*7c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
901*7c478bd9Sstevel@tonic-gate 						m->mf_name, at);
902*7c478bd9Sstevel@tonic-gate 				if (parseonly)
903*7c478bd9Sstevel@tonic-gate 					syserr("X%s: Invalid numeric domain spec \"%s\"",
904*7c478bd9Sstevel@tonic-gate 					       m->mf_name, at);
905*7c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
906*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
907*7c478bd9Sstevel@tonic-gate 						  "Milter (%s): Invalid numeric domain spec \"%s\"",
908*7c478bd9Sstevel@tonic-gate 						  m->mf_name, at);
909*7c478bd9Sstevel@tonic-gate 				milter_error(m, e);
910*7c478bd9Sstevel@tonic-gate 				return -1;
911*7c478bd9Sstevel@tonic-gate 			}
912*7c478bd9Sstevel@tonic-gate 		}
913*7c478bd9Sstevel@tonic-gate 		else
914*7c478bd9Sstevel@tonic-gate 		{
915*7c478bd9Sstevel@tonic-gate 			hp = sm_gethostbyname(at, addr.sa.sa_family);
916*7c478bd9Sstevel@tonic-gate 			if (hp == NULL)
917*7c478bd9Sstevel@tonic-gate 			{
918*7c478bd9Sstevel@tonic-gate 				save_errno = errno;
919*7c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
920*7c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown host name %s\n",
921*7c478bd9Sstevel@tonic-gate 						   m->mf_name, at);
922*7c478bd9Sstevel@tonic-gate 				errno = save_errno;
923*7c478bd9Sstevel@tonic-gate 				if (parseonly)
924*7c478bd9Sstevel@tonic-gate 					syserr("X%s: Unknown host name %s",
925*7c478bd9Sstevel@tonic-gate 					       m->mf_name, at);
926*7c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
927*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
928*7c478bd9Sstevel@tonic-gate 						  "Milter (%s): Unknown host name %s",
929*7c478bd9Sstevel@tonic-gate 						  m->mf_name, at);
930*7c478bd9Sstevel@tonic-gate 				milter_error(m, e);
931*7c478bd9Sstevel@tonic-gate 				return -1;
932*7c478bd9Sstevel@tonic-gate 			}
933*7c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = hp->h_addrtype;
934*7c478bd9Sstevel@tonic-gate 			switch (hp->h_addrtype)
935*7c478bd9Sstevel@tonic-gate 			{
936*7c478bd9Sstevel@tonic-gate #  if NETINET
937*7c478bd9Sstevel@tonic-gate 			  case AF_INET:
938*7c478bd9Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
939*7c478bd9Sstevel@tonic-gate 					hp->h_addr, INADDRSZ);
940*7c478bd9Sstevel@tonic-gate 				addr.sin.sin_port = port;
941*7c478bd9Sstevel@tonic-gate 				addrlen = sizeof (struct sockaddr_in);
942*7c478bd9Sstevel@tonic-gate 				addrno = 1;
943*7c478bd9Sstevel@tonic-gate 				break;
944*7c478bd9Sstevel@tonic-gate #  endif /* NETINET */
945*7c478bd9Sstevel@tonic-gate 
946*7c478bd9Sstevel@tonic-gate #  if NETINET6
947*7c478bd9Sstevel@tonic-gate 			  case AF_INET6:
948*7c478bd9Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
949*7c478bd9Sstevel@tonic-gate 					hp->h_addr, IN6ADDRSZ);
950*7c478bd9Sstevel@tonic-gate 				addr.sin6.sin6_port = port;
951*7c478bd9Sstevel@tonic-gate 				addrlen = sizeof (struct sockaddr_in6);
952*7c478bd9Sstevel@tonic-gate 				addrno = 1;
953*7c478bd9Sstevel@tonic-gate 				break;
954*7c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
955*7c478bd9Sstevel@tonic-gate 
956*7c478bd9Sstevel@tonic-gate 			  default:
957*7c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
958*7c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
959*7c478bd9Sstevel@tonic-gate 						   m->mf_name, at,
960*7c478bd9Sstevel@tonic-gate 						   hp->h_addrtype);
961*7c478bd9Sstevel@tonic-gate 				if (parseonly)
962*7c478bd9Sstevel@tonic-gate 					syserr("X%s: Unknown protocol for %s (%d)",
963*7c478bd9Sstevel@tonic-gate 					       m->mf_name, at, hp->h_addrtype);
964*7c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
965*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
966*7c478bd9Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
967*7c478bd9Sstevel@tonic-gate 						  m->mf_name, at,
968*7c478bd9Sstevel@tonic-gate 						  hp->h_addrtype);
969*7c478bd9Sstevel@tonic-gate 				milter_error(m, e);
970*7c478bd9Sstevel@tonic-gate #  if NETINET6
971*7c478bd9Sstevel@tonic-gate 				freehostent(hp);
972*7c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
973*7c478bd9Sstevel@tonic-gate 				return -1;
974*7c478bd9Sstevel@tonic-gate 			}
975*7c478bd9Sstevel@tonic-gate 		}
976*7c478bd9Sstevel@tonic-gate 	}
977*7c478bd9Sstevel@tonic-gate 	else
978*7c478bd9Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
979*7c478bd9Sstevel@tonic-gate 	{
980*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
981*7c478bd9Sstevel@tonic-gate 			sm_dprintf("X%s: unknown socket protocol\n",
982*7c478bd9Sstevel@tonic-gate 				   m->mf_name);
983*7c478bd9Sstevel@tonic-gate 		if (parseonly)
984*7c478bd9Sstevel@tonic-gate 			syserr("X%s: unknown socket protocol", m->mf_name);
985*7c478bd9Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
986*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
987*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): unknown socket protocol",
988*7c478bd9Sstevel@tonic-gate 				  m->mf_name);
989*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
990*7c478bd9Sstevel@tonic-gate 		return -1;
991*7c478bd9Sstevel@tonic-gate 	}
992*7c478bd9Sstevel@tonic-gate 
993*7c478bd9Sstevel@tonic-gate 	/* just parsing through? */
994*7c478bd9Sstevel@tonic-gate 	if (parseonly)
995*7c478bd9Sstevel@tonic-gate 	{
996*7c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_READY;
997*7c478bd9Sstevel@tonic-gate # if NETINET6
998*7c478bd9Sstevel@tonic-gate 		if (hp != NULL)
999*7c478bd9Sstevel@tonic-gate 			freehostent(hp);
1000*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
1001*7c478bd9Sstevel@tonic-gate 		return 0;
1002*7c478bd9Sstevel@tonic-gate 	}
1003*7c478bd9Sstevel@tonic-gate 
1004*7c478bd9Sstevel@tonic-gate 	/* sanity check */
1005*7c478bd9Sstevel@tonic-gate 	if (m->mf_state != SMFS_READY &&
1006*7c478bd9Sstevel@tonic-gate 	    m->mf_state != SMFS_CLOSED)
1007*7c478bd9Sstevel@tonic-gate 	{
1008*7c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
1009*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 1))
1010*7c478bd9Sstevel@tonic-gate 			sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
1011*7c478bd9Sstevel@tonic-gate 				   m->mf_name, (char) m->mf_state);
1012*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
1013*7c478bd9Sstevel@tonic-gate # if NETINET6
1014*7c478bd9Sstevel@tonic-gate 		if (hp != NULL)
1015*7c478bd9Sstevel@tonic-gate 			freehostent(hp);
1016*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
1017*7c478bd9Sstevel@tonic-gate 		return -1;
1018*7c478bd9Sstevel@tonic-gate 	}
1019*7c478bd9Sstevel@tonic-gate 
1020*7c478bd9Sstevel@tonic-gate 	/* nope, actually connecting */
1021*7c478bd9Sstevel@tonic-gate 	for (;;)
1022*7c478bd9Sstevel@tonic-gate 	{
1023*7c478bd9Sstevel@tonic-gate 		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
1024*7c478bd9Sstevel@tonic-gate 		if (sock < 0)
1025*7c478bd9Sstevel@tonic-gate 		{
1026*7c478bd9Sstevel@tonic-gate 			save_errno = errno;
1027*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
1028*7c478bd9Sstevel@tonic-gate 				sm_dprintf("Milter (%s): error creating socket: %s\n",
1029*7c478bd9Sstevel@tonic-gate 					   m->mf_name,
1030*7c478bd9Sstevel@tonic-gate 					   sm_errstring(save_errno));
1031*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
1032*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
1033*7c478bd9Sstevel@tonic-gate 					  "Milter (%s): error creating socket: %s",
1034*7c478bd9Sstevel@tonic-gate 					  m->mf_name, sm_errstring(save_errno));
1035*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
1036*7c478bd9Sstevel@tonic-gate # if NETINET6
1037*7c478bd9Sstevel@tonic-gate 			if (hp != NULL)
1038*7c478bd9Sstevel@tonic-gate 				freehostent(hp);
1039*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
1040*7c478bd9Sstevel@tonic-gate 			return -1;
1041*7c478bd9Sstevel@tonic-gate 		}
1042*7c478bd9Sstevel@tonic-gate 
1043*7c478bd9Sstevel@tonic-gate 		if (setjmp(MilterConnectTimeout) == 0)
1044*7c478bd9Sstevel@tonic-gate 		{
1045*7c478bd9Sstevel@tonic-gate 			SM_EVENT *ev = NULL;
1046*7c478bd9Sstevel@tonic-gate 			int i;
1047*7c478bd9Sstevel@tonic-gate 
1048*7c478bd9Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_CONNECT] > 0)
1049*7c478bd9Sstevel@tonic-gate 				ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
1050*7c478bd9Sstevel@tonic-gate 						 milter_connect_timeout, 0);
1051*7c478bd9Sstevel@tonic-gate 
1052*7c478bd9Sstevel@tonic-gate 			i = connect(sock, (struct sockaddr *) &addr, addrlen);
1053*7c478bd9Sstevel@tonic-gate 			save_errno = errno;
1054*7c478bd9Sstevel@tonic-gate 			if (ev != NULL)
1055*7c478bd9Sstevel@tonic-gate 				sm_clrevent(ev);
1056*7c478bd9Sstevel@tonic-gate 			errno = save_errno;
1057*7c478bd9Sstevel@tonic-gate 			if (i >= 0)
1058*7c478bd9Sstevel@tonic-gate 				break;
1059*7c478bd9Sstevel@tonic-gate 		}
1060*7c478bd9Sstevel@tonic-gate 
1061*7c478bd9Sstevel@tonic-gate 		/* couldn't connect.... try next address */
1062*7c478bd9Sstevel@tonic-gate 		save_errno = errno;
1063*7c478bd9Sstevel@tonic-gate 		p = CurHostName;
1064*7c478bd9Sstevel@tonic-gate 		CurHostName = at;
1065*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
1066*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_open (%s): open %s failed: %s\n",
1067*7c478bd9Sstevel@tonic-gate 				   m->mf_name, at, sm_errstring(save_errno));
1068*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 13)
1069*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
1070*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): open %s failed: %s",
1071*7c478bd9Sstevel@tonic-gate 				  m->mf_name, at, sm_errstring(save_errno));
1072*7c478bd9Sstevel@tonic-gate 		CurHostName = p;
1073*7c478bd9Sstevel@tonic-gate 		(void) close(sock);
1074*7c478bd9Sstevel@tonic-gate 
1075*7c478bd9Sstevel@tonic-gate 		/* try next address */
1076*7c478bd9Sstevel@tonic-gate 		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
1077*7c478bd9Sstevel@tonic-gate 		{
1078*7c478bd9Sstevel@tonic-gate 			switch (addr.sa.sa_family)
1079*7c478bd9Sstevel@tonic-gate 			{
1080*7c478bd9Sstevel@tonic-gate # if NETINET
1081*7c478bd9Sstevel@tonic-gate 			  case AF_INET:
1082*7c478bd9Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
1083*7c478bd9Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
1084*7c478bd9Sstevel@tonic-gate 					INADDRSZ);
1085*7c478bd9Sstevel@tonic-gate 				break;
1086*7c478bd9Sstevel@tonic-gate # endif /* NETINET */
1087*7c478bd9Sstevel@tonic-gate 
1088*7c478bd9Sstevel@tonic-gate # if NETINET6
1089*7c478bd9Sstevel@tonic-gate 			  case AF_INET6:
1090*7c478bd9Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
1091*7c478bd9Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
1092*7c478bd9Sstevel@tonic-gate 					IN6ADDRSZ);
1093*7c478bd9Sstevel@tonic-gate 				break;
1094*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
1095*7c478bd9Sstevel@tonic-gate 
1096*7c478bd9Sstevel@tonic-gate 			  default:
1097*7c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
1098*7c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
1099*7c478bd9Sstevel@tonic-gate 						   m->mf_name, at,
1100*7c478bd9Sstevel@tonic-gate 						   hp->h_addrtype);
1101*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
1102*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
1103*7c478bd9Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
1104*7c478bd9Sstevel@tonic-gate 						  m->mf_name, at,
1105*7c478bd9Sstevel@tonic-gate 						  hp->h_addrtype);
1106*7c478bd9Sstevel@tonic-gate 				milter_error(m, e);
1107*7c478bd9Sstevel@tonic-gate # if NETINET6
1108*7c478bd9Sstevel@tonic-gate 				freehostent(hp);
1109*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
1110*7c478bd9Sstevel@tonic-gate 				return -1;
1111*7c478bd9Sstevel@tonic-gate 			}
1112*7c478bd9Sstevel@tonic-gate 			continue;
1113*7c478bd9Sstevel@tonic-gate 		}
1114*7c478bd9Sstevel@tonic-gate 		p = CurHostName;
1115*7c478bd9Sstevel@tonic-gate 		CurHostName = at;
1116*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
1117*7c478bd9Sstevel@tonic-gate 			sm_dprintf("X%s: error connecting to filter: %s\n",
1118*7c478bd9Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno));
1119*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
1120*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
1121*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): error connecting to filter: %s",
1122*7c478bd9Sstevel@tonic-gate 				  m->mf_name, sm_errstring(save_errno));
1123*7c478bd9Sstevel@tonic-gate 		CurHostName = p;
1124*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
1125*7c478bd9Sstevel@tonic-gate # if NETINET6
1126*7c478bd9Sstevel@tonic-gate 		if (hp != NULL)
1127*7c478bd9Sstevel@tonic-gate 			freehostent(hp);
1128*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
1129*7c478bd9Sstevel@tonic-gate 		return -1;
1130*7c478bd9Sstevel@tonic-gate 	}
1131*7c478bd9Sstevel@tonic-gate 	m->mf_state = SMFS_OPEN;
1132*7c478bd9Sstevel@tonic-gate # if NETINET6
1133*7c478bd9Sstevel@tonic-gate 	if (hp != NULL)
1134*7c478bd9Sstevel@tonic-gate 	{
1135*7c478bd9Sstevel@tonic-gate 		freehostent(hp);
1136*7c478bd9Sstevel@tonic-gate 		hp = NULL;
1137*7c478bd9Sstevel@tonic-gate 	}
1138*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
1139*7c478bd9Sstevel@tonic-gate # if _FFR_MILTER_NAGLE
1140*7c478bd9Sstevel@tonic-gate #  ifndef TCP_CORK
1141*7c478bd9Sstevel@tonic-gate 	{
1142*7c478bd9Sstevel@tonic-gate 		int nodelay = 1;
1143*7c478bd9Sstevel@tonic-gate 
1144*7c478bd9Sstevel@tonic-gate 		setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY,
1145*7c478bd9Sstevel@tonic-gate 			   (char *)&nodelay, sizeof(nodelay));
1146*7c478bd9Sstevel@tonic-gate 	}
1147*7c478bd9Sstevel@tonic-gate #  endif /* TCP_CORK */
1148*7c478bd9Sstevel@tonic-gate # endif /* _FFR_MILTER_NAGLE */
1149*7c478bd9Sstevel@tonic-gate 	return sock;
1150*7c478bd9Sstevel@tonic-gate }
1151*7c478bd9Sstevel@tonic-gate 
1152*7c478bd9Sstevel@tonic-gate static void
1153*7c478bd9Sstevel@tonic-gate milter_connect_timeout(ignore)
1154*7c478bd9Sstevel@tonic-gate 	int ignore;
1155*7c478bd9Sstevel@tonic-gate {
1156*7c478bd9Sstevel@tonic-gate 	/*
1157*7c478bd9Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
1158*7c478bd9Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
1159*7c478bd9Sstevel@tonic-gate 	**	DOING.
1160*7c478bd9Sstevel@tonic-gate 	*/
1161*7c478bd9Sstevel@tonic-gate 
1162*7c478bd9Sstevel@tonic-gate 	errno = ETIMEDOUT;
1163*7c478bd9Sstevel@tonic-gate 	longjmp(MilterConnectTimeout, 1);
1164*7c478bd9Sstevel@tonic-gate }
1165*7c478bd9Sstevel@tonic-gate /*
1166*7c478bd9Sstevel@tonic-gate **  MILTER_SETUP -- setup structure for a mail filter
1167*7c478bd9Sstevel@tonic-gate **
1168*7c478bd9Sstevel@tonic-gate **	Parameters:
1169*7c478bd9Sstevel@tonic-gate **		line -- the options line.
1170*7c478bd9Sstevel@tonic-gate **
1171*7c478bd9Sstevel@tonic-gate **	Returns:
1172*7c478bd9Sstevel@tonic-gate **		none
1173*7c478bd9Sstevel@tonic-gate */
1174*7c478bd9Sstevel@tonic-gate 
1175*7c478bd9Sstevel@tonic-gate void
1176*7c478bd9Sstevel@tonic-gate milter_setup(line)
1177*7c478bd9Sstevel@tonic-gate 	char *line;
1178*7c478bd9Sstevel@tonic-gate {
1179*7c478bd9Sstevel@tonic-gate 	char fcode;
1180*7c478bd9Sstevel@tonic-gate 	register char *p;
1181*7c478bd9Sstevel@tonic-gate 	register struct milter *m;
1182*7c478bd9Sstevel@tonic-gate 	STAB *s;
1183*7c478bd9Sstevel@tonic-gate 
1184*7c478bd9Sstevel@tonic-gate 	/* collect the filter name */
1185*7c478bd9Sstevel@tonic-gate 	for (p = line;
1186*7c478bd9Sstevel@tonic-gate 	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
1187*7c478bd9Sstevel@tonic-gate 	     p++)
1188*7c478bd9Sstevel@tonic-gate 		continue;
1189*7c478bd9Sstevel@tonic-gate 	if (*p != '\0')
1190*7c478bd9Sstevel@tonic-gate 		*p++ = '\0';
1191*7c478bd9Sstevel@tonic-gate 	if (line[0] == '\0')
1192*7c478bd9Sstevel@tonic-gate 	{
1193*7c478bd9Sstevel@tonic-gate 		syserr("name required for mail filter");
1194*7c478bd9Sstevel@tonic-gate 		return;
1195*7c478bd9Sstevel@tonic-gate 	}
1196*7c478bd9Sstevel@tonic-gate 	m = (struct milter *) xalloc(sizeof *m);
1197*7c478bd9Sstevel@tonic-gate 	memset((char *) m, '\0', sizeof *m);
1198*7c478bd9Sstevel@tonic-gate 	m->mf_name = newstr(line);
1199*7c478bd9Sstevel@tonic-gate 	m->mf_state = SMFS_READY;
1200*7c478bd9Sstevel@tonic-gate 	m->mf_sock = -1;
1201*7c478bd9Sstevel@tonic-gate 	m->mf_timeout[SMFTO_CONNECT] = (time_t) 300;
1202*7c478bd9Sstevel@tonic-gate 	m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
1203*7c478bd9Sstevel@tonic-gate 	m->mf_timeout[SMFTO_READ] = (time_t) 10;
1204*7c478bd9Sstevel@tonic-gate 	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
1205*7c478bd9Sstevel@tonic-gate 
1206*7c478bd9Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
1207*7c478bd9Sstevel@tonic-gate 	while (*p != '\0')
1208*7c478bd9Sstevel@tonic-gate 	{
1209*7c478bd9Sstevel@tonic-gate 		char *delimptr;
1210*7c478bd9Sstevel@tonic-gate 
1211*7c478bd9Sstevel@tonic-gate 		while (*p != '\0' &&
1212*7c478bd9Sstevel@tonic-gate 		       (*p == ',' || (isascii(*p) && isspace(*p))))
1213*7c478bd9Sstevel@tonic-gate 			p++;
1214*7c478bd9Sstevel@tonic-gate 
1215*7c478bd9Sstevel@tonic-gate 		/* p now points to field code */
1216*7c478bd9Sstevel@tonic-gate 		fcode = *p;
1217*7c478bd9Sstevel@tonic-gate 		while (*p != '\0' && *p != '=' && *p != ',')
1218*7c478bd9Sstevel@tonic-gate 			p++;
1219*7c478bd9Sstevel@tonic-gate 		if (*p++ != '=')
1220*7c478bd9Sstevel@tonic-gate 		{
1221*7c478bd9Sstevel@tonic-gate 			syserr("X%s: `=' expected", m->mf_name);
1222*7c478bd9Sstevel@tonic-gate 			return;
1223*7c478bd9Sstevel@tonic-gate 		}
1224*7c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
1225*7c478bd9Sstevel@tonic-gate 			p++;
1226*7c478bd9Sstevel@tonic-gate 
1227*7c478bd9Sstevel@tonic-gate 		/* p now points to the field body */
1228*7c478bd9Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ',');
1229*7c478bd9Sstevel@tonic-gate 
1230*7c478bd9Sstevel@tonic-gate 		/* install the field into the filter struct */
1231*7c478bd9Sstevel@tonic-gate 		switch (fcode)
1232*7c478bd9Sstevel@tonic-gate 		{
1233*7c478bd9Sstevel@tonic-gate 		  case 'S':		/* socket */
1234*7c478bd9Sstevel@tonic-gate 			if (p == NULL)
1235*7c478bd9Sstevel@tonic-gate 				m->mf_conn = NULL;
1236*7c478bd9Sstevel@tonic-gate 			else
1237*7c478bd9Sstevel@tonic-gate 				m->mf_conn = newstr(p);
1238*7c478bd9Sstevel@tonic-gate 			break;
1239*7c478bd9Sstevel@tonic-gate 
1240*7c478bd9Sstevel@tonic-gate 		  case 'F':		/* Milter flags configured on MTA */
1241*7c478bd9Sstevel@tonic-gate 			for (; *p != '\0'; p++)
1242*7c478bd9Sstevel@tonic-gate 			{
1243*7c478bd9Sstevel@tonic-gate 				if (!(isascii(*p) && isspace(*p)))
1244*7c478bd9Sstevel@tonic-gate 					setbitn(bitidx(*p), m->mf_flags);
1245*7c478bd9Sstevel@tonic-gate 			}
1246*7c478bd9Sstevel@tonic-gate 			break;
1247*7c478bd9Sstevel@tonic-gate 
1248*7c478bd9Sstevel@tonic-gate 		  case 'T':		/* timeouts */
1249*7c478bd9Sstevel@tonic-gate 			milter_parse_timeouts(p, m);
1250*7c478bd9Sstevel@tonic-gate 			break;
1251*7c478bd9Sstevel@tonic-gate 
1252*7c478bd9Sstevel@tonic-gate 		  default:
1253*7c478bd9Sstevel@tonic-gate 			syserr("X%s: unknown filter equate %c=",
1254*7c478bd9Sstevel@tonic-gate 			       m->mf_name, fcode);
1255*7c478bd9Sstevel@tonic-gate 			break;
1256*7c478bd9Sstevel@tonic-gate 		}
1257*7c478bd9Sstevel@tonic-gate 		p = delimptr;
1258*7c478bd9Sstevel@tonic-gate 	}
1259*7c478bd9Sstevel@tonic-gate 
1260*7c478bd9Sstevel@tonic-gate 	/* early check for errors */
1261*7c478bd9Sstevel@tonic-gate 	(void) milter_open(m, true, CurEnv);
1262*7c478bd9Sstevel@tonic-gate 
1263*7c478bd9Sstevel@tonic-gate 	/* enter the filter into the symbol table */
1264*7c478bd9Sstevel@tonic-gate 	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
1265*7c478bd9Sstevel@tonic-gate 	if (s->s_milter != NULL)
1266*7c478bd9Sstevel@tonic-gate 		syserr("X%s: duplicate filter definition", m->mf_name);
1267*7c478bd9Sstevel@tonic-gate 	else
1268*7c478bd9Sstevel@tonic-gate 		s->s_milter = m;
1269*7c478bd9Sstevel@tonic-gate }
1270*7c478bd9Sstevel@tonic-gate /*
1271*7c478bd9Sstevel@tonic-gate **  MILTER_CONFIG -- parse option list into an array and check config
1272*7c478bd9Sstevel@tonic-gate **
1273*7c478bd9Sstevel@tonic-gate **	Called when reading configuration file.
1274*7c478bd9Sstevel@tonic-gate **
1275*7c478bd9Sstevel@tonic-gate **	Parameters:
1276*7c478bd9Sstevel@tonic-gate **		spec -- the filter list.
1277*7c478bd9Sstevel@tonic-gate **		list -- the array to fill in.
1278*7c478bd9Sstevel@tonic-gate **		max -- the maximum number of entries in list.
1279*7c478bd9Sstevel@tonic-gate **
1280*7c478bd9Sstevel@tonic-gate **	Returns:
1281*7c478bd9Sstevel@tonic-gate **		none
1282*7c478bd9Sstevel@tonic-gate */
1283*7c478bd9Sstevel@tonic-gate 
1284*7c478bd9Sstevel@tonic-gate void
1285*7c478bd9Sstevel@tonic-gate milter_config(spec, list, max)
1286*7c478bd9Sstevel@tonic-gate 	char *spec;
1287*7c478bd9Sstevel@tonic-gate 	struct milter **list;
1288*7c478bd9Sstevel@tonic-gate 	int max;
1289*7c478bd9Sstevel@tonic-gate {
1290*7c478bd9Sstevel@tonic-gate 	int numitems = 0;
1291*7c478bd9Sstevel@tonic-gate 	register char *p;
1292*7c478bd9Sstevel@tonic-gate 
1293*7c478bd9Sstevel@tonic-gate 	/* leave one for the NULL signifying the end of the list */
1294*7c478bd9Sstevel@tonic-gate 	max--;
1295*7c478bd9Sstevel@tonic-gate 
1296*7c478bd9Sstevel@tonic-gate 	for (p = spec; p != NULL; )
1297*7c478bd9Sstevel@tonic-gate 	{
1298*7c478bd9Sstevel@tonic-gate 		STAB *s;
1299*7c478bd9Sstevel@tonic-gate 
1300*7c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
1301*7c478bd9Sstevel@tonic-gate 			p++;
1302*7c478bd9Sstevel@tonic-gate 		if (*p == '\0')
1303*7c478bd9Sstevel@tonic-gate 			break;
1304*7c478bd9Sstevel@tonic-gate 		spec = p;
1305*7c478bd9Sstevel@tonic-gate 
1306*7c478bd9Sstevel@tonic-gate 		if (numitems >= max)
1307*7c478bd9Sstevel@tonic-gate 		{
1308*7c478bd9Sstevel@tonic-gate 			syserr("Too many filters defined, %d max", max);
1309*7c478bd9Sstevel@tonic-gate 			if (max > 0)
1310*7c478bd9Sstevel@tonic-gate 				list[0] = NULL;
1311*7c478bd9Sstevel@tonic-gate 			return;
1312*7c478bd9Sstevel@tonic-gate 		}
1313*7c478bd9Sstevel@tonic-gate 		p = strpbrk(p, ";,");
1314*7c478bd9Sstevel@tonic-gate 		if (p != NULL)
1315*7c478bd9Sstevel@tonic-gate 			*p++ = '\0';
1316*7c478bd9Sstevel@tonic-gate 
1317*7c478bd9Sstevel@tonic-gate 		s = stab(spec, ST_MILTER, ST_FIND);
1318*7c478bd9Sstevel@tonic-gate 		if (s == NULL)
1319*7c478bd9Sstevel@tonic-gate 		{
1320*7c478bd9Sstevel@tonic-gate 			syserr("InputFilter %s not defined", spec);
1321*7c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
1322*7c478bd9Sstevel@tonic-gate 			return;
1323*7c478bd9Sstevel@tonic-gate 		}
1324*7c478bd9Sstevel@tonic-gate 		list[numitems++] = s->s_milter;
1325*7c478bd9Sstevel@tonic-gate 	}
1326*7c478bd9Sstevel@tonic-gate 	list[numitems] = NULL;
1327*7c478bd9Sstevel@tonic-gate 
1328*7c478bd9Sstevel@tonic-gate 	/* if not set, set to LogLevel */
1329*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel == -1)
1330*7c478bd9Sstevel@tonic-gate 		MilterLogLevel = LogLevel;
1331*7c478bd9Sstevel@tonic-gate }
1332*7c478bd9Sstevel@tonic-gate /*
1333*7c478bd9Sstevel@tonic-gate **  MILTER_PARSE_TIMEOUTS -- parse timeout list
1334*7c478bd9Sstevel@tonic-gate **
1335*7c478bd9Sstevel@tonic-gate **	Called when reading configuration file.
1336*7c478bd9Sstevel@tonic-gate **
1337*7c478bd9Sstevel@tonic-gate **	Parameters:
1338*7c478bd9Sstevel@tonic-gate **		spec -- the timeout list.
1339*7c478bd9Sstevel@tonic-gate **		m -- milter to set.
1340*7c478bd9Sstevel@tonic-gate **
1341*7c478bd9Sstevel@tonic-gate **	Returns:
1342*7c478bd9Sstevel@tonic-gate **		none
1343*7c478bd9Sstevel@tonic-gate */
1344*7c478bd9Sstevel@tonic-gate 
1345*7c478bd9Sstevel@tonic-gate static void
1346*7c478bd9Sstevel@tonic-gate milter_parse_timeouts(spec, m)
1347*7c478bd9Sstevel@tonic-gate 	char *spec;
1348*7c478bd9Sstevel@tonic-gate 	struct milter *m;
1349*7c478bd9Sstevel@tonic-gate {
1350*7c478bd9Sstevel@tonic-gate 	char fcode;
1351*7c478bd9Sstevel@tonic-gate 	int tcode;
1352*7c478bd9Sstevel@tonic-gate 	register char *p;
1353*7c478bd9Sstevel@tonic-gate 
1354*7c478bd9Sstevel@tonic-gate 	p = spec;
1355*7c478bd9Sstevel@tonic-gate 
1356*7c478bd9Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
1357*7c478bd9Sstevel@tonic-gate 	while (*p != '\0')
1358*7c478bd9Sstevel@tonic-gate 	{
1359*7c478bd9Sstevel@tonic-gate 		char *delimptr;
1360*7c478bd9Sstevel@tonic-gate 
1361*7c478bd9Sstevel@tonic-gate 		while (*p != '\0' &&
1362*7c478bd9Sstevel@tonic-gate 		       (*p == ';' || (isascii(*p) && isspace(*p))))
1363*7c478bd9Sstevel@tonic-gate 			p++;
1364*7c478bd9Sstevel@tonic-gate 
1365*7c478bd9Sstevel@tonic-gate 		/* p now points to field code */
1366*7c478bd9Sstevel@tonic-gate 		fcode = *p;
1367*7c478bd9Sstevel@tonic-gate 		while (*p != '\0' && *p != ':')
1368*7c478bd9Sstevel@tonic-gate 			p++;
1369*7c478bd9Sstevel@tonic-gate 		if (*p++ != ':')
1370*7c478bd9Sstevel@tonic-gate 		{
1371*7c478bd9Sstevel@tonic-gate 			syserr("X%s, T=: `:' expected", m->mf_name);
1372*7c478bd9Sstevel@tonic-gate 			return;
1373*7c478bd9Sstevel@tonic-gate 		}
1374*7c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
1375*7c478bd9Sstevel@tonic-gate 			p++;
1376*7c478bd9Sstevel@tonic-gate 
1377*7c478bd9Sstevel@tonic-gate 		/* p now points to the field body */
1378*7c478bd9Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ';');
1379*7c478bd9Sstevel@tonic-gate 		tcode = -1;
1380*7c478bd9Sstevel@tonic-gate 
1381*7c478bd9Sstevel@tonic-gate 		/* install the field into the filter struct */
1382*7c478bd9Sstevel@tonic-gate 		switch (fcode)
1383*7c478bd9Sstevel@tonic-gate 		{
1384*7c478bd9Sstevel@tonic-gate 		  case 'C':
1385*7c478bd9Sstevel@tonic-gate 			tcode = SMFTO_CONNECT;
1386*7c478bd9Sstevel@tonic-gate 			break;
1387*7c478bd9Sstevel@tonic-gate 
1388*7c478bd9Sstevel@tonic-gate 		  case 'S':
1389*7c478bd9Sstevel@tonic-gate 			tcode = SMFTO_WRITE;
1390*7c478bd9Sstevel@tonic-gate 			break;
1391*7c478bd9Sstevel@tonic-gate 
1392*7c478bd9Sstevel@tonic-gate 		  case 'R':
1393*7c478bd9Sstevel@tonic-gate 			tcode = SMFTO_READ;
1394*7c478bd9Sstevel@tonic-gate 			break;
1395*7c478bd9Sstevel@tonic-gate 
1396*7c478bd9Sstevel@tonic-gate 		  case 'E':
1397*7c478bd9Sstevel@tonic-gate 			tcode = SMFTO_EOM;
1398*7c478bd9Sstevel@tonic-gate 			break;
1399*7c478bd9Sstevel@tonic-gate 
1400*7c478bd9Sstevel@tonic-gate 		  default:
1401*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
1402*7c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: %c unknown\n",
1403*7c478bd9Sstevel@tonic-gate 					   m->mf_name, fcode);
1404*7c478bd9Sstevel@tonic-gate 			syserr("X%s: unknown filter timeout %c",
1405*7c478bd9Sstevel@tonic-gate 			       m->mf_name, fcode);
1406*7c478bd9Sstevel@tonic-gate 			break;
1407*7c478bd9Sstevel@tonic-gate 		}
1408*7c478bd9Sstevel@tonic-gate 		if (tcode >= 0)
1409*7c478bd9Sstevel@tonic-gate 		{
1410*7c478bd9Sstevel@tonic-gate 			m->mf_timeout[tcode] = convtime(p, 's');
1411*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
1412*7c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: %c=%ld\n",
1413*7c478bd9Sstevel@tonic-gate 					   m->mf_name, fcode,
1414*7c478bd9Sstevel@tonic-gate 					   (u_long) m->mf_timeout[tcode]);
1415*7c478bd9Sstevel@tonic-gate 		}
1416*7c478bd9Sstevel@tonic-gate 		p = delimptr;
1417*7c478bd9Sstevel@tonic-gate 	}
1418*7c478bd9Sstevel@tonic-gate }
1419*7c478bd9Sstevel@tonic-gate /*
1420*7c478bd9Sstevel@tonic-gate **  MILTER_SET_OPTION -- set an individual milter option
1421*7c478bd9Sstevel@tonic-gate **
1422*7c478bd9Sstevel@tonic-gate **	Parameters:
1423*7c478bd9Sstevel@tonic-gate **		name -- the name of the option.
1424*7c478bd9Sstevel@tonic-gate **		val -- the value of the option.
1425*7c478bd9Sstevel@tonic-gate **		sticky -- if set, don't let other setoptions override
1426*7c478bd9Sstevel@tonic-gate **			this value.
1427*7c478bd9Sstevel@tonic-gate **
1428*7c478bd9Sstevel@tonic-gate **	Returns:
1429*7c478bd9Sstevel@tonic-gate **		none.
1430*7c478bd9Sstevel@tonic-gate */
1431*7c478bd9Sstevel@tonic-gate 
1432*7c478bd9Sstevel@tonic-gate /* set if Milter sub-option is stuck */
1433*7c478bd9Sstevel@tonic-gate static BITMAP256	StickyMilterOpt;
1434*7c478bd9Sstevel@tonic-gate 
1435*7c478bd9Sstevel@tonic-gate static struct milteropt
1436*7c478bd9Sstevel@tonic-gate {
1437*7c478bd9Sstevel@tonic-gate 	char		*mo_name;	/* long name of milter option */
1438*7c478bd9Sstevel@tonic-gate 	unsigned char	mo_code;	/* code for option */
1439*7c478bd9Sstevel@tonic-gate } MilterOptTab[] =
1440*7c478bd9Sstevel@tonic-gate {
1441*7c478bd9Sstevel@tonic-gate # define MO_MACROS_CONNECT		0x01
1442*7c478bd9Sstevel@tonic-gate 	{ "macros.connect",		MO_MACROS_CONNECT		},
1443*7c478bd9Sstevel@tonic-gate # define MO_MACROS_HELO			0x02
1444*7c478bd9Sstevel@tonic-gate 	{ "macros.helo",		MO_MACROS_HELO			},
1445*7c478bd9Sstevel@tonic-gate # define MO_MACROS_ENVFROM		0x03
1446*7c478bd9Sstevel@tonic-gate 	{ "macros.envfrom",		MO_MACROS_ENVFROM		},
1447*7c478bd9Sstevel@tonic-gate # define MO_MACROS_ENVRCPT		0x04
1448*7c478bd9Sstevel@tonic-gate 	{ "macros.envrcpt",		MO_MACROS_ENVRCPT		},
1449*7c478bd9Sstevel@tonic-gate # define MO_MACROS_DATA			0x05
1450*7c478bd9Sstevel@tonic-gate 	{ "macros.data",		MO_MACROS_DATA			},
1451*7c478bd9Sstevel@tonic-gate # define MO_MACROS_EOM			0x06
1452*7c478bd9Sstevel@tonic-gate 	{ "macros.eom",			MO_MACROS_EOM			},
1453*7c478bd9Sstevel@tonic-gate # define MO_LOGLEVEL			0x07
1454*7c478bd9Sstevel@tonic-gate 	{ "loglevel",			MO_LOGLEVEL			},
1455*7c478bd9Sstevel@tonic-gate # if _FFR_MAXDATASIZE
1456*7c478bd9Sstevel@tonic-gate #  define MO_MAXDATASIZE			0x08
1457*7c478bd9Sstevel@tonic-gate 	{ "maxdatasize",		MO_MAXDATASIZE			},
1458*7c478bd9Sstevel@tonic-gate # endif /* _FFR_MAXDATASIZE */
1459*7c478bd9Sstevel@tonic-gate 	{ NULL,				0				},
1460*7c478bd9Sstevel@tonic-gate };
1461*7c478bd9Sstevel@tonic-gate 
1462*7c478bd9Sstevel@tonic-gate void
1463*7c478bd9Sstevel@tonic-gate milter_set_option(name, val, sticky)
1464*7c478bd9Sstevel@tonic-gate 	char *name;
1465*7c478bd9Sstevel@tonic-gate 	char *val;
1466*7c478bd9Sstevel@tonic-gate 	bool sticky;
1467*7c478bd9Sstevel@tonic-gate {
1468*7c478bd9Sstevel@tonic-gate 	int nummac = 0;
1469*7c478bd9Sstevel@tonic-gate 	register struct milteropt *mo;
1470*7c478bd9Sstevel@tonic-gate 	char *p;
1471*7c478bd9Sstevel@tonic-gate 	char **macros = NULL;
1472*7c478bd9Sstevel@tonic-gate 
1473*7c478bd9Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64, 5))
1474*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_set_option(%s = %s)", name, val);
1475*7c478bd9Sstevel@tonic-gate 
1476*7c478bd9Sstevel@tonic-gate 	if (name == NULL)
1477*7c478bd9Sstevel@tonic-gate 	{
1478*7c478bd9Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option, must specify suboption");
1479*7c478bd9Sstevel@tonic-gate 		return;
1480*7c478bd9Sstevel@tonic-gate 	}
1481*7c478bd9Sstevel@tonic-gate 
1482*7c478bd9Sstevel@tonic-gate 	for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
1483*7c478bd9Sstevel@tonic-gate 	{
1484*7c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(mo->mo_name, name) == 0)
1485*7c478bd9Sstevel@tonic-gate 			break;
1486*7c478bd9Sstevel@tonic-gate 	}
1487*7c478bd9Sstevel@tonic-gate 
1488*7c478bd9Sstevel@tonic-gate 	if (mo->mo_name == NULL)
1489*7c478bd9Sstevel@tonic-gate 	{
1490*7c478bd9Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
1491*7c478bd9Sstevel@tonic-gate 		return;
1492*7c478bd9Sstevel@tonic-gate 	}
1493*7c478bd9Sstevel@tonic-gate 
1494*7c478bd9Sstevel@tonic-gate 	/*
1495*7c478bd9Sstevel@tonic-gate 	**  See if this option is preset for us.
1496*7c478bd9Sstevel@tonic-gate 	*/
1497*7c478bd9Sstevel@tonic-gate 
1498*7c478bd9Sstevel@tonic-gate 	if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
1499*7c478bd9Sstevel@tonic-gate 	{
1500*7c478bd9Sstevel@tonic-gate 		if (tTd(37, 2) || tTd(64,5))
1501*7c478bd9Sstevel@tonic-gate 			sm_dprintf(" (ignored)\n");
1502*7c478bd9Sstevel@tonic-gate 		return;
1503*7c478bd9Sstevel@tonic-gate 	}
1504*7c478bd9Sstevel@tonic-gate 
1505*7c478bd9Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64,5))
1506*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\n");
1507*7c478bd9Sstevel@tonic-gate 
1508*7c478bd9Sstevel@tonic-gate 	switch (mo->mo_code)
1509*7c478bd9Sstevel@tonic-gate 	{
1510*7c478bd9Sstevel@tonic-gate 	  case MO_LOGLEVEL:
1511*7c478bd9Sstevel@tonic-gate 		MilterLogLevel = atoi(val);
1512*7c478bd9Sstevel@tonic-gate 		break;
1513*7c478bd9Sstevel@tonic-gate 
1514*7c478bd9Sstevel@tonic-gate #if _FFR_MAXDATASIZE
1515*7c478bd9Sstevel@tonic-gate 	  case MO_MAXDATASIZE:
1516*7c478bd9Sstevel@tonic-gate 		MilterMaxDataSize = (size_t)atol(val);
1517*7c478bd9Sstevel@tonic-gate 		break;
1518*7c478bd9Sstevel@tonic-gate #endif /* _FFR_MAXDATASIZE */
1519*7c478bd9Sstevel@tonic-gate 
1520*7c478bd9Sstevel@tonic-gate 	  case MO_MACROS_CONNECT:
1521*7c478bd9Sstevel@tonic-gate 		if (macros == NULL)
1522*7c478bd9Sstevel@tonic-gate 			macros = MilterConnectMacros;
1523*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1524*7c478bd9Sstevel@tonic-gate 
1525*7c478bd9Sstevel@tonic-gate 	  case MO_MACROS_HELO:
1526*7c478bd9Sstevel@tonic-gate 		if (macros == NULL)
1527*7c478bd9Sstevel@tonic-gate 			macros = MilterHeloMacros;
1528*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1529*7c478bd9Sstevel@tonic-gate 
1530*7c478bd9Sstevel@tonic-gate 	  case MO_MACROS_ENVFROM:
1531*7c478bd9Sstevel@tonic-gate 		if (macros == NULL)
1532*7c478bd9Sstevel@tonic-gate 			macros = MilterEnvFromMacros;
1533*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1534*7c478bd9Sstevel@tonic-gate 
1535*7c478bd9Sstevel@tonic-gate 	  case MO_MACROS_ENVRCPT:
1536*7c478bd9Sstevel@tonic-gate 		if (macros == NULL)
1537*7c478bd9Sstevel@tonic-gate 			macros = MilterEnvRcptMacros;
1538*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1539*7c478bd9Sstevel@tonic-gate 
1540*7c478bd9Sstevel@tonic-gate 	  case MO_MACROS_EOM:
1541*7c478bd9Sstevel@tonic-gate 		if (macros == NULL)
1542*7c478bd9Sstevel@tonic-gate 			macros = MilterEOMMacros;
1543*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1544*7c478bd9Sstevel@tonic-gate 
1545*7c478bd9Sstevel@tonic-gate 	  case MO_MACROS_DATA:
1546*7c478bd9Sstevel@tonic-gate 		if (macros == NULL)
1547*7c478bd9Sstevel@tonic-gate 			macros = MilterDataMacros;
1548*7c478bd9Sstevel@tonic-gate 
1549*7c478bd9Sstevel@tonic-gate 		p = newstr(val);
1550*7c478bd9Sstevel@tonic-gate 		while (*p != '\0')
1551*7c478bd9Sstevel@tonic-gate 		{
1552*7c478bd9Sstevel@tonic-gate 			char *macro;
1553*7c478bd9Sstevel@tonic-gate 
1554*7c478bd9Sstevel@tonic-gate 			/* Skip leading commas, spaces */
1555*7c478bd9Sstevel@tonic-gate 			while (*p != '\0' &&
1556*7c478bd9Sstevel@tonic-gate 			       (*p == ',' || (isascii(*p) && isspace(*p))))
1557*7c478bd9Sstevel@tonic-gate 				p++;
1558*7c478bd9Sstevel@tonic-gate 
1559*7c478bd9Sstevel@tonic-gate 			if (*p == '\0')
1560*7c478bd9Sstevel@tonic-gate 				break;
1561*7c478bd9Sstevel@tonic-gate 
1562*7c478bd9Sstevel@tonic-gate 			/* Find end of macro */
1563*7c478bd9Sstevel@tonic-gate 			macro = p;
1564*7c478bd9Sstevel@tonic-gate 			while (*p != '\0' && *p != ',' &&
1565*7c478bd9Sstevel@tonic-gate 			       isascii(*p) && !isspace(*p))
1566*7c478bd9Sstevel@tonic-gate 				p++;
1567*7c478bd9Sstevel@tonic-gate 			if (*p != '\0')
1568*7c478bd9Sstevel@tonic-gate 				*p++ = '\0';
1569*7c478bd9Sstevel@tonic-gate 
1570*7c478bd9Sstevel@tonic-gate 			if (nummac >= MAXFILTERMACROS)
1571*7c478bd9Sstevel@tonic-gate 			{
1572*7c478bd9Sstevel@tonic-gate 				syserr("milter_set_option: too many macros in Milter.%s (max %d)",
1573*7c478bd9Sstevel@tonic-gate 				       name, MAXFILTERMACROS);
1574*7c478bd9Sstevel@tonic-gate 				macros[nummac] = NULL;
1575*7c478bd9Sstevel@tonic-gate 				break;
1576*7c478bd9Sstevel@tonic-gate 			}
1577*7c478bd9Sstevel@tonic-gate 			macros[nummac++] = macro;
1578*7c478bd9Sstevel@tonic-gate 		}
1579*7c478bd9Sstevel@tonic-gate 		macros[nummac] = NULL;
1580*7c478bd9Sstevel@tonic-gate 		break;
1581*7c478bd9Sstevel@tonic-gate 
1582*7c478bd9Sstevel@tonic-gate 	  default:
1583*7c478bd9Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
1584*7c478bd9Sstevel@tonic-gate 		break;
1585*7c478bd9Sstevel@tonic-gate 	}
1586*7c478bd9Sstevel@tonic-gate 	if (sticky)
1587*7c478bd9Sstevel@tonic-gate 		setbitn(mo->mo_code, StickyMilterOpt);
1588*7c478bd9Sstevel@tonic-gate }
1589*7c478bd9Sstevel@tonic-gate /*
1590*7c478bd9Sstevel@tonic-gate **  MILTER_REOPEN_DF -- open & truncate the data file (for replbody)
1591*7c478bd9Sstevel@tonic-gate **
1592*7c478bd9Sstevel@tonic-gate **	Parameters:
1593*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
1594*7c478bd9Sstevel@tonic-gate **
1595*7c478bd9Sstevel@tonic-gate **	Returns:
1596*7c478bd9Sstevel@tonic-gate **		0 if succesful, -1 otherwise
1597*7c478bd9Sstevel@tonic-gate */
1598*7c478bd9Sstevel@tonic-gate 
1599*7c478bd9Sstevel@tonic-gate static int
1600*7c478bd9Sstevel@tonic-gate milter_reopen_df(e)
1601*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
1602*7c478bd9Sstevel@tonic-gate {
1603*7c478bd9Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
1604*7c478bd9Sstevel@tonic-gate 
1605*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname);
1606*7c478bd9Sstevel@tonic-gate 
1607*7c478bd9Sstevel@tonic-gate 	/*
1608*7c478bd9Sstevel@tonic-gate 	**  In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so
1609*7c478bd9Sstevel@tonic-gate 	**  close and reopen writable (later close and reopen
1610*7c478bd9Sstevel@tonic-gate 	**  read only again).
1611*7c478bd9Sstevel@tonic-gate 	**
1612*7c478bd9Sstevel@tonic-gate 	**  In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the
1613*7c478bd9Sstevel@tonic-gate 	**  buffered file I/O descriptor, still open for writing so there
1614*7c478bd9Sstevel@tonic-gate 	**  isn't any work to do here (except checking for consistency).
1615*7c478bd9Sstevel@tonic-gate 	*/
1616*7c478bd9Sstevel@tonic-gate 
1617*7c478bd9Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY)
1618*7c478bd9Sstevel@tonic-gate 	{
1619*7c478bd9Sstevel@tonic-gate 		/* close read-only data file */
1620*7c478bd9Sstevel@tonic-gate 		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
1621*7c478bd9Sstevel@tonic-gate 		{
1622*7c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
1623*7c478bd9Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
1624*7c478bd9Sstevel@tonic-gate 		}
1625*7c478bd9Sstevel@tonic-gate 
1626*7c478bd9Sstevel@tonic-gate 		/* open writable */
1627*7c478bd9Sstevel@tonic-gate 		if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
1628*7c478bd9Sstevel@tonic-gate 					   SM_IO_RDWR_B, NULL)) == NULL)
1629*7c478bd9Sstevel@tonic-gate 		{
1630*7c478bd9Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s");
1631*7c478bd9Sstevel@tonic-gate 			return -1;
1632*7c478bd9Sstevel@tonic-gate 		}
1633*7c478bd9Sstevel@tonic-gate 	}
1634*7c478bd9Sstevel@tonic-gate 	else if (e->e_dfp == NULL)
1635*7c478bd9Sstevel@tonic-gate 	{
1636*7c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
1637*7c478bd9Sstevel@tonic-gate 		errno = ENOENT;
1638*7c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
1639*7c478bd9Sstevel@tonic-gate 		return -1;
1640*7c478bd9Sstevel@tonic-gate 	}
1641*7c478bd9Sstevel@tonic-gate 	return 0;
1642*7c478bd9Sstevel@tonic-gate }
1643*7c478bd9Sstevel@tonic-gate /*
1644*7c478bd9Sstevel@tonic-gate **  MILTER_RESET_DF -- re-open read-only the data file (for replbody)
1645*7c478bd9Sstevel@tonic-gate **
1646*7c478bd9Sstevel@tonic-gate **	Parameters:
1647*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
1648*7c478bd9Sstevel@tonic-gate **
1649*7c478bd9Sstevel@tonic-gate **	Returns:
1650*7c478bd9Sstevel@tonic-gate **		0 if succesful, -1 otherwise
1651*7c478bd9Sstevel@tonic-gate */
1652*7c478bd9Sstevel@tonic-gate 
1653*7c478bd9Sstevel@tonic-gate static int
1654*7c478bd9Sstevel@tonic-gate milter_reset_df(e)
1655*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
1656*7c478bd9Sstevel@tonic-gate {
1657*7c478bd9Sstevel@tonic-gate 	int afd;
1658*7c478bd9Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
1659*7c478bd9Sstevel@tonic-gate 
1660*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname);
1661*7c478bd9Sstevel@tonic-gate 
1662*7c478bd9Sstevel@tonic-gate 	if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 ||
1663*7c478bd9Sstevel@tonic-gate 	    sm_io_error(e->e_dfp))
1664*7c478bd9Sstevel@tonic-gate 	{
1665*7c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
1666*7c478bd9Sstevel@tonic-gate 		return -1;
1667*7c478bd9Sstevel@tonic-gate 	}
1668*7c478bd9Sstevel@tonic-gate 	else if (SuperSafe != SAFE_REALLY)
1669*7c478bd9Sstevel@tonic-gate 	{
1670*7c478bd9Sstevel@tonic-gate 		/* skip next few clauses */
1671*7c478bd9Sstevel@tonic-gate 		/* EMPTY */
1672*7c478bd9Sstevel@tonic-gate 	}
1673*7c478bd9Sstevel@tonic-gate 	else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0
1674*7c478bd9Sstevel@tonic-gate 		 && fsync(afd) < 0)
1675*7c478bd9Sstevel@tonic-gate 	{
1676*7c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
1677*7c478bd9Sstevel@tonic-gate 		return -1;
1678*7c478bd9Sstevel@tonic-gate 	}
1679*7c478bd9Sstevel@tonic-gate 	else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0)
1680*7c478bd9Sstevel@tonic-gate 	{
1681*7c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
1682*7c478bd9Sstevel@tonic-gate 		return -1;
1683*7c478bd9Sstevel@tonic-gate 	}
1684*7c478bd9Sstevel@tonic-gate 	else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
1685*7c478bd9Sstevel@tonic-gate 					SM_IO_RDONLY_B, NULL)) == NULL)
1686*7c478bd9Sstevel@tonic-gate 	{
1687*7c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
1688*7c478bd9Sstevel@tonic-gate 		return -1;
1689*7c478bd9Sstevel@tonic-gate 	}
1690*7c478bd9Sstevel@tonic-gate 	else
1691*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
1692*7c478bd9Sstevel@tonic-gate 	return 0;
1693*7c478bd9Sstevel@tonic-gate }
1694*7c478bd9Sstevel@tonic-gate /*
1695*7c478bd9Sstevel@tonic-gate **  MILTER_CAN_DELRCPTS -- can any milter filters delete recipients?
1696*7c478bd9Sstevel@tonic-gate **
1697*7c478bd9Sstevel@tonic-gate **	Parameters:
1698*7c478bd9Sstevel@tonic-gate **		none
1699*7c478bd9Sstevel@tonic-gate **
1700*7c478bd9Sstevel@tonic-gate **	Returns:
1701*7c478bd9Sstevel@tonic-gate **		true if any filter deletes recipients, false otherwise
1702*7c478bd9Sstevel@tonic-gate */
1703*7c478bd9Sstevel@tonic-gate 
1704*7c478bd9Sstevel@tonic-gate bool
1705*7c478bd9Sstevel@tonic-gate milter_can_delrcpts()
1706*7c478bd9Sstevel@tonic-gate {
1707*7c478bd9Sstevel@tonic-gate 	bool can = false;
1708*7c478bd9Sstevel@tonic-gate 	int i;
1709*7c478bd9Sstevel@tonic-gate 
1710*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
1711*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_can_delrcpts:");
1712*7c478bd9Sstevel@tonic-gate 
1713*7c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
1714*7c478bd9Sstevel@tonic-gate 	{
1715*7c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
1716*7c478bd9Sstevel@tonic-gate 
1717*7c478bd9Sstevel@tonic-gate 		if (bitset(SMFIF_DELRCPT, m->mf_fflags))
1718*7c478bd9Sstevel@tonic-gate 		{
1719*7c478bd9Sstevel@tonic-gate 			can = true;
1720*7c478bd9Sstevel@tonic-gate 			break;
1721*7c478bd9Sstevel@tonic-gate 		}
1722*7c478bd9Sstevel@tonic-gate 	}
1723*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
1724*7c478bd9Sstevel@tonic-gate 		sm_dprintf("%s\n", can ? "true" : "false");
1725*7c478bd9Sstevel@tonic-gate 
1726*7c478bd9Sstevel@tonic-gate 	return can;
1727*7c478bd9Sstevel@tonic-gate }
1728*7c478bd9Sstevel@tonic-gate /*
1729*7c478bd9Sstevel@tonic-gate **  MILTER_QUIT_FILTER -- close down a single filter
1730*7c478bd9Sstevel@tonic-gate **
1731*7c478bd9Sstevel@tonic-gate **	Parameters:
1732*7c478bd9Sstevel@tonic-gate **		m -- milter structure of filter to close down.
1733*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
1734*7c478bd9Sstevel@tonic-gate **
1735*7c478bd9Sstevel@tonic-gate **	Returns:
1736*7c478bd9Sstevel@tonic-gate **		none
1737*7c478bd9Sstevel@tonic-gate */
1738*7c478bd9Sstevel@tonic-gate 
1739*7c478bd9Sstevel@tonic-gate static void
1740*7c478bd9Sstevel@tonic-gate milter_quit_filter(m, e)
1741*7c478bd9Sstevel@tonic-gate 	struct milter *m;
1742*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
1743*7c478bd9Sstevel@tonic-gate {
1744*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
1745*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_quit_filter(%s)\n", m->mf_name);
1746*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 18)
1747*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter",
1748*7c478bd9Sstevel@tonic-gate 			  m->mf_name);
1749*7c478bd9Sstevel@tonic-gate 
1750*7c478bd9Sstevel@tonic-gate 	/* Never replace error state */
1751*7c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
1752*7c478bd9Sstevel@tonic-gate 		return;
1753*7c478bd9Sstevel@tonic-gate 
1754*7c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
1755*7c478bd9Sstevel@tonic-gate 	    m->mf_state == SMFS_CLOSED ||
1756*7c478bd9Sstevel@tonic-gate 	    m->mf_state == SMFS_READY)
1757*7c478bd9Sstevel@tonic-gate 	{
1758*7c478bd9Sstevel@tonic-gate 		m->mf_sock = -1;
1759*7c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
1760*7c478bd9Sstevel@tonic-gate 		return;
1761*7c478bd9Sstevel@tonic-gate 	}
1762*7c478bd9Sstevel@tonic-gate 
1763*7c478bd9Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
1764*7c478bd9Sstevel@tonic-gate 			    m->mf_timeout[SMFTO_WRITE], e);
1765*7c478bd9Sstevel@tonic-gate 	if (m->mf_sock >= 0)
1766*7c478bd9Sstevel@tonic-gate 	{
1767*7c478bd9Sstevel@tonic-gate 		(void) close(m->mf_sock);
1768*7c478bd9Sstevel@tonic-gate 		m->mf_sock = -1;
1769*7c478bd9Sstevel@tonic-gate 	}
1770*7c478bd9Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
1771*7c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
1772*7c478bd9Sstevel@tonic-gate }
1773*7c478bd9Sstevel@tonic-gate /*
1774*7c478bd9Sstevel@tonic-gate **  MILTER_ABORT_FILTER -- tell filter to abort current message
1775*7c478bd9Sstevel@tonic-gate **
1776*7c478bd9Sstevel@tonic-gate **	Parameters:
1777*7c478bd9Sstevel@tonic-gate **		m -- milter structure of filter to abort.
1778*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
1779*7c478bd9Sstevel@tonic-gate **
1780*7c478bd9Sstevel@tonic-gate **	Returns:
1781*7c478bd9Sstevel@tonic-gate **		none
1782*7c478bd9Sstevel@tonic-gate */
1783*7c478bd9Sstevel@tonic-gate 
1784*7c478bd9Sstevel@tonic-gate static void
1785*7c478bd9Sstevel@tonic-gate milter_abort_filter(m, e)
1786*7c478bd9Sstevel@tonic-gate 	struct milter *m;
1787*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
1788*7c478bd9Sstevel@tonic-gate {
1789*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
1790*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_abort_filter(%s)\n", m->mf_name);
1791*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 10)
1792*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter",
1793*7c478bd9Sstevel@tonic-gate 			  m->mf_name);
1794*7c478bd9Sstevel@tonic-gate 
1795*7c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
1796*7c478bd9Sstevel@tonic-gate 	    m->mf_state != SMFS_INMSG)
1797*7c478bd9Sstevel@tonic-gate 		return;
1798*7c478bd9Sstevel@tonic-gate 
1799*7c478bd9Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
1800*7c478bd9Sstevel@tonic-gate 			    m->mf_timeout[SMFTO_WRITE], e);
1801*7c478bd9Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
1802*7c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_DONE;
1803*7c478bd9Sstevel@tonic-gate }
1804*7c478bd9Sstevel@tonic-gate /*
1805*7c478bd9Sstevel@tonic-gate **  MILTER_SEND_MACROS -- provide macros to the filters
1806*7c478bd9Sstevel@tonic-gate **
1807*7c478bd9Sstevel@tonic-gate **	Parameters:
1808*7c478bd9Sstevel@tonic-gate **		m -- milter to send macros to.
1809*7c478bd9Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
1810*7c478bd9Sstevel@tonic-gate **		cmd -- which command the macros are associated with.
1811*7c478bd9Sstevel@tonic-gate **		e -- current envelope (for macro access).
1812*7c478bd9Sstevel@tonic-gate **
1813*7c478bd9Sstevel@tonic-gate **	Returns:
1814*7c478bd9Sstevel@tonic-gate **		none
1815*7c478bd9Sstevel@tonic-gate */
1816*7c478bd9Sstevel@tonic-gate 
1817*7c478bd9Sstevel@tonic-gate static void
1818*7c478bd9Sstevel@tonic-gate milter_send_macros(m, macros, cmd, e)
1819*7c478bd9Sstevel@tonic-gate 	struct milter *m;
1820*7c478bd9Sstevel@tonic-gate 	char **macros;
1821*7c478bd9Sstevel@tonic-gate 	char cmd;
1822*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
1823*7c478bd9Sstevel@tonic-gate {
1824*7c478bd9Sstevel@tonic-gate 	int i;
1825*7c478bd9Sstevel@tonic-gate 	int mid;
1826*7c478bd9Sstevel@tonic-gate 	char *v;
1827*7c478bd9Sstevel@tonic-gate 	char *buf, *bp;
1828*7c478bd9Sstevel@tonic-gate 	char exp[MAXLINE];
1829*7c478bd9Sstevel@tonic-gate 	ssize_t s;
1830*7c478bd9Sstevel@tonic-gate 
1831*7c478bd9Sstevel@tonic-gate 	/* sanity check */
1832*7c478bd9Sstevel@tonic-gate 	if (macros == NULL || macros[0] == NULL)
1833*7c478bd9Sstevel@tonic-gate 		return;
1834*7c478bd9Sstevel@tonic-gate 
1835*7c478bd9Sstevel@tonic-gate 	/* put together data */
1836*7c478bd9Sstevel@tonic-gate 	s = 1;			/* for the command character */
1837*7c478bd9Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
1838*7c478bd9Sstevel@tonic-gate 	{
1839*7c478bd9Sstevel@tonic-gate 		mid = macid(macros[i]);
1840*7c478bd9Sstevel@tonic-gate 		if (mid == 0)
1841*7c478bd9Sstevel@tonic-gate 			continue;
1842*7c478bd9Sstevel@tonic-gate 		v = macvalue(mid, e);
1843*7c478bd9Sstevel@tonic-gate 		if (v == NULL)
1844*7c478bd9Sstevel@tonic-gate 			continue;
1845*7c478bd9Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
1846*7c478bd9Sstevel@tonic-gate 		s += strlen(macros[i]) + 1 + strlen(exp) + 1;
1847*7c478bd9Sstevel@tonic-gate 	}
1848*7c478bd9Sstevel@tonic-gate 
1849*7c478bd9Sstevel@tonic-gate 	if (s < 0)
1850*7c478bd9Sstevel@tonic-gate 		return;
1851*7c478bd9Sstevel@tonic-gate 
1852*7c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(s);
1853*7c478bd9Sstevel@tonic-gate 	bp = buf;
1854*7c478bd9Sstevel@tonic-gate 	*bp++ = cmd;
1855*7c478bd9Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
1856*7c478bd9Sstevel@tonic-gate 	{
1857*7c478bd9Sstevel@tonic-gate 		mid = macid(macros[i]);
1858*7c478bd9Sstevel@tonic-gate 		if (mid == 0)
1859*7c478bd9Sstevel@tonic-gate 			continue;
1860*7c478bd9Sstevel@tonic-gate 		v = macvalue(mid, e);
1861*7c478bd9Sstevel@tonic-gate 		if (v == NULL)
1862*7c478bd9Sstevel@tonic-gate 			continue;
1863*7c478bd9Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
1864*7c478bd9Sstevel@tonic-gate 
1865*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
1866*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
1867*7c478bd9Sstevel@tonic-gate 				m->mf_name, cmd, macros[i], exp);
1868*7c478bd9Sstevel@tonic-gate 
1869*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, macros[i], s - (bp - buf));
1870*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp) + 1;
1871*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, exp, s - (bp - buf));
1872*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp) + 1;
1873*7c478bd9Sstevel@tonic-gate 	}
1874*7c478bd9Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_MACRO, buf, s,
1875*7c478bd9Sstevel@tonic-gate 			    m->mf_timeout[SMFTO_WRITE], e);
1876*7c478bd9Sstevel@tonic-gate 	sm_free(buf);
1877*7c478bd9Sstevel@tonic-gate }
1878*7c478bd9Sstevel@tonic-gate 
1879*7c478bd9Sstevel@tonic-gate /*
1880*7c478bd9Sstevel@tonic-gate **  MILTER_SEND_COMMAND -- send a command and return the response for a filter
1881*7c478bd9Sstevel@tonic-gate **
1882*7c478bd9Sstevel@tonic-gate **	Parameters:
1883*7c478bd9Sstevel@tonic-gate **		m -- current milter filter
1884*7c478bd9Sstevel@tonic-gate **		command -- command to send.
1885*7c478bd9Sstevel@tonic-gate **		data -- optional command data.
1886*7c478bd9Sstevel@tonic-gate **		sz -- length of buf.
1887*7c478bd9Sstevel@tonic-gate **		e -- current envelope (for e->e_id).
1888*7c478bd9Sstevel@tonic-gate **		state -- return state word.
1889*7c478bd9Sstevel@tonic-gate **
1890*7c478bd9Sstevel@tonic-gate **	Returns:
1891*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
1892*7c478bd9Sstevel@tonic-gate */
1893*7c478bd9Sstevel@tonic-gate 
1894*7c478bd9Sstevel@tonic-gate static char *
1895*7c478bd9Sstevel@tonic-gate milter_send_command(m, command, data, sz, e, state)
1896*7c478bd9Sstevel@tonic-gate 	struct milter *m;
1897*7c478bd9Sstevel@tonic-gate 	char command;
1898*7c478bd9Sstevel@tonic-gate 	void *data;
1899*7c478bd9Sstevel@tonic-gate 	ssize_t sz;
1900*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
1901*7c478bd9Sstevel@tonic-gate 	char *state;
1902*7c478bd9Sstevel@tonic-gate {
1903*7c478bd9Sstevel@tonic-gate 	char rcmd;
1904*7c478bd9Sstevel@tonic-gate 	ssize_t rlen;
1905*7c478bd9Sstevel@tonic-gate 	unsigned long skipflag;
1906*7c478bd9Sstevel@tonic-gate #if _FFR_MILTER_NOHDR_RESP
1907*7c478bd9Sstevel@tonic-gate 	unsigned long norespflag = 0;
1908*7c478bd9Sstevel@tonic-gate #endif /* _FFR_MILTER_NOHDR_RESP */
1909*7c478bd9Sstevel@tonic-gate 	char *action;
1910*7c478bd9Sstevel@tonic-gate 	char *defresponse;
1911*7c478bd9Sstevel@tonic-gate 	char *response;
1912*7c478bd9Sstevel@tonic-gate 
1913*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
1914*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): cmd %c len %ld\n",
1915*7c478bd9Sstevel@tonic-gate 			m->mf_name, (char) command, (long) sz);
1916*7c478bd9Sstevel@tonic-gate 
1917*7c478bd9Sstevel@tonic-gate 	/* find skip flag and default failure */
1918*7c478bd9Sstevel@tonic-gate 	switch (command)
1919*7c478bd9Sstevel@tonic-gate 	{
1920*7c478bd9Sstevel@tonic-gate 	  case SMFIC_CONNECT:
1921*7c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOCONNECT;
1922*7c478bd9Sstevel@tonic-gate 		action = "connect";
1923*7c478bd9Sstevel@tonic-gate 		defresponse = "554 Command rejected";
1924*7c478bd9Sstevel@tonic-gate 		break;
1925*7c478bd9Sstevel@tonic-gate 
1926*7c478bd9Sstevel@tonic-gate 	  case SMFIC_HELO:
1927*7c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOHELO;
1928*7c478bd9Sstevel@tonic-gate 		action = "helo";
1929*7c478bd9Sstevel@tonic-gate 		defresponse = "550 Command rejected";
1930*7c478bd9Sstevel@tonic-gate 		break;
1931*7c478bd9Sstevel@tonic-gate 
1932*7c478bd9Sstevel@tonic-gate 	  case SMFIC_MAIL:
1933*7c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOMAIL;
1934*7c478bd9Sstevel@tonic-gate 		action = "mail";
1935*7c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
1936*7c478bd9Sstevel@tonic-gate 		break;
1937*7c478bd9Sstevel@tonic-gate 
1938*7c478bd9Sstevel@tonic-gate 	  case SMFIC_RCPT:
1939*7c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NORCPT;
1940*7c478bd9Sstevel@tonic-gate 		action = "rcpt";
1941*7c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
1942*7c478bd9Sstevel@tonic-gate 		break;
1943*7c478bd9Sstevel@tonic-gate 
1944*7c478bd9Sstevel@tonic-gate 	  case SMFIC_HEADER:
1945*7c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOHDRS;
1946*7c478bd9Sstevel@tonic-gate #if _FFR_MILTER_NOHDR_RESP
1947*7c478bd9Sstevel@tonic-gate 		norespflag = SMFIP_NOHREPL;
1948*7c478bd9Sstevel@tonic-gate #endif /* _FFR_MILTER_NOHDR_RESP */
1949*7c478bd9Sstevel@tonic-gate 		action = "header";
1950*7c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
1951*7c478bd9Sstevel@tonic-gate 		break;
1952*7c478bd9Sstevel@tonic-gate 
1953*7c478bd9Sstevel@tonic-gate 	  case SMFIC_BODY:
1954*7c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOBODY;
1955*7c478bd9Sstevel@tonic-gate 		action = "body";
1956*7c478bd9Sstevel@tonic-gate 		defresponse = "554 5.7.1 Command rejected";
1957*7c478bd9Sstevel@tonic-gate 		break;
1958*7c478bd9Sstevel@tonic-gate 
1959*7c478bd9Sstevel@tonic-gate 	  case SMFIC_EOH:
1960*7c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOEOH;
1961*7c478bd9Sstevel@tonic-gate 		action = "eoh";
1962*7c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
1963*7c478bd9Sstevel@tonic-gate 		break;
1964*7c478bd9Sstevel@tonic-gate 
1965*7c478bd9Sstevel@tonic-gate #if SMFI_VERSION > 2
1966*7c478bd9Sstevel@tonic-gate 	  case SMFIC_UNKNOWN:
1967*7c478bd9Sstevel@tonic-gate 		action = "unknown";
1968*7c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
1969*7c478bd9Sstevel@tonic-gate 		break;
1970*7c478bd9Sstevel@tonic-gate #endif /* SMFI_VERSION > 2 */
1971*7c478bd9Sstevel@tonic-gate 
1972*7c478bd9Sstevel@tonic-gate 	  case SMFIC_BODYEOB:
1973*7c478bd9Sstevel@tonic-gate 	  case SMFIC_OPTNEG:
1974*7c478bd9Sstevel@tonic-gate 	  case SMFIC_MACRO:
1975*7c478bd9Sstevel@tonic-gate 	  case SMFIC_ABORT:
1976*7c478bd9Sstevel@tonic-gate 	  case SMFIC_QUIT:
1977*7c478bd9Sstevel@tonic-gate 		/* NOTE: not handled by milter_send_command() */
1978*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1979*7c478bd9Sstevel@tonic-gate 
1980*7c478bd9Sstevel@tonic-gate 	  default:
1981*7c478bd9Sstevel@tonic-gate 		skipflag = 0;
1982*7c478bd9Sstevel@tonic-gate 		action = "default";
1983*7c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
1984*7c478bd9Sstevel@tonic-gate 		break;
1985*7c478bd9Sstevel@tonic-gate 	}
1986*7c478bd9Sstevel@tonic-gate 
1987*7c478bd9Sstevel@tonic-gate 	/* check if filter wants this command */
1988*7c478bd9Sstevel@tonic-gate 	if (skipflag != 0 &&
1989*7c478bd9Sstevel@tonic-gate 	    bitset(skipflag, m->mf_pflags))
1990*7c478bd9Sstevel@tonic-gate 		return NULL;
1991*7c478bd9Sstevel@tonic-gate 
1992*7c478bd9Sstevel@tonic-gate 	/* send the command to the filter */
1993*7c478bd9Sstevel@tonic-gate 	(void) milter_write(m, command, data, sz,
1994*7c478bd9Sstevel@tonic-gate 			    m->mf_timeout[SMFTO_WRITE], e);
1995*7c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
1996*7c478bd9Sstevel@tonic-gate 	{
1997*7c478bd9Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
1998*7c478bd9Sstevel@tonic-gate 		return NULL;
1999*7c478bd9Sstevel@tonic-gate 	}
2000*7c478bd9Sstevel@tonic-gate 
2001*7c478bd9Sstevel@tonic-gate #if _FFR_MILTER_NOHDR_RESP
2002*7c478bd9Sstevel@tonic-gate 	/* check if filter sends response to this command */
2003*7c478bd9Sstevel@tonic-gate 	if (norespflag != 0 && bitset(norespflag, m->mf_pflags))
2004*7c478bd9Sstevel@tonic-gate 		return NULL;
2005*7c478bd9Sstevel@tonic-gate #endif /* _FFR_MILTER_NOHDR_RESP */
2006*7c478bd9Sstevel@tonic-gate 
2007*7c478bd9Sstevel@tonic-gate 	/* get the response from the filter */
2008*7c478bd9Sstevel@tonic-gate 	response = milter_read(m, &rcmd, &rlen,
2009*7c478bd9Sstevel@tonic-gate 			       m->mf_timeout[SMFTO_READ], e);
2010*7c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
2011*7c478bd9Sstevel@tonic-gate 	{
2012*7c478bd9Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
2013*7c478bd9Sstevel@tonic-gate 		return NULL;
2014*7c478bd9Sstevel@tonic-gate 	}
2015*7c478bd9Sstevel@tonic-gate 
2016*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2017*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): returned %c\n",
2018*7c478bd9Sstevel@tonic-gate 			   m->mf_name, (char) rcmd);
2019*7c478bd9Sstevel@tonic-gate 
2020*7c478bd9Sstevel@tonic-gate 	switch (rcmd)
2021*7c478bd9Sstevel@tonic-gate 	{
2022*7c478bd9Sstevel@tonic-gate 	  case SMFIR_REPLYCODE:
2023*7c478bd9Sstevel@tonic-gate 		MILTER_CHECK_REPLYCODE(defresponse);
2024*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2025*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject=%s",
2026*7c478bd9Sstevel@tonic-gate 				  m->mf_name, action, response);
2027*7c478bd9Sstevel@tonic-gate 		*state = rcmd;
2028*7c478bd9Sstevel@tonic-gate 		break;
2029*7c478bd9Sstevel@tonic-gate 
2030*7c478bd9Sstevel@tonic-gate 	  case SMFIR_REJECT:
2031*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2032*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject",
2033*7c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
2034*7c478bd9Sstevel@tonic-gate 		*state = rcmd;
2035*7c478bd9Sstevel@tonic-gate 		break;
2036*7c478bd9Sstevel@tonic-gate 
2037*7c478bd9Sstevel@tonic-gate 	  case SMFIR_DISCARD:
2038*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2039*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, discard",
2040*7c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
2041*7c478bd9Sstevel@tonic-gate 		*state = rcmd;
2042*7c478bd9Sstevel@tonic-gate 		break;
2043*7c478bd9Sstevel@tonic-gate 
2044*7c478bd9Sstevel@tonic-gate 	  case SMFIR_TEMPFAIL:
2045*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2046*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, tempfail",
2047*7c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
2048*7c478bd9Sstevel@tonic-gate 		*state = rcmd;
2049*7c478bd9Sstevel@tonic-gate 		break;
2050*7c478bd9Sstevel@tonic-gate 
2051*7c478bd9Sstevel@tonic-gate 	  case SMFIR_ACCEPT:
2052*7c478bd9Sstevel@tonic-gate 		/* this filter is done with message/connection */
2053*7c478bd9Sstevel@tonic-gate 		if (command == SMFIC_HELO ||
2054*7c478bd9Sstevel@tonic-gate 		    command == SMFIC_CONNECT)
2055*7c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_CLOSABLE;
2056*7c478bd9Sstevel@tonic-gate 		else
2057*7c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_DONE;
2058*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2059*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, accepted",
2060*7c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
2061*7c478bd9Sstevel@tonic-gate 		break;
2062*7c478bd9Sstevel@tonic-gate 
2063*7c478bd9Sstevel@tonic-gate 	  case SMFIR_CONTINUE:
2064*7c478bd9Sstevel@tonic-gate 		/* if MAIL command is ok, filter is in message state */
2065*7c478bd9Sstevel@tonic-gate 		if (command == SMFIC_MAIL)
2066*7c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_INMSG;
2067*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 12)
2068*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, continue",
2069*7c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
2070*7c478bd9Sstevel@tonic-gate 		break;
2071*7c478bd9Sstevel@tonic-gate 
2072*7c478bd9Sstevel@tonic-gate 	  default:
2073*7c478bd9Sstevel@tonic-gate 		/* Invalid response to command */
2074*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
2075*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
2076*7c478bd9Sstevel@tonic-gate 				  "milter_send_command(%s): action=%s returned bogus response %c",
2077*7c478bd9Sstevel@tonic-gate 				  m->mf_name, action, rcmd);
2078*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2079*7c478bd9Sstevel@tonic-gate 		break;
2080*7c478bd9Sstevel@tonic-gate 	}
2081*7c478bd9Sstevel@tonic-gate 
2082*7c478bd9Sstevel@tonic-gate 	if (*state != SMFIR_REPLYCODE &&
2083*7c478bd9Sstevel@tonic-gate 	    response != NULL)
2084*7c478bd9Sstevel@tonic-gate 	{
2085*7c478bd9Sstevel@tonic-gate 		sm_free(response); /* XXX */
2086*7c478bd9Sstevel@tonic-gate 		response = NULL;
2087*7c478bd9Sstevel@tonic-gate 	}
2088*7c478bd9Sstevel@tonic-gate 	return response;
2089*7c478bd9Sstevel@tonic-gate }
2090*7c478bd9Sstevel@tonic-gate 
2091*7c478bd9Sstevel@tonic-gate /*
2092*7c478bd9Sstevel@tonic-gate **  MILTER_COMMAND -- send a command and return the response for each filter
2093*7c478bd9Sstevel@tonic-gate **
2094*7c478bd9Sstevel@tonic-gate **	Parameters:
2095*7c478bd9Sstevel@tonic-gate **		command -- command to send.
2096*7c478bd9Sstevel@tonic-gate **		data -- optional command data.
2097*7c478bd9Sstevel@tonic-gate **		sz -- length of buf.
2098*7c478bd9Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
2099*7c478bd9Sstevel@tonic-gate **		e -- current envelope (for macro access).
2100*7c478bd9Sstevel@tonic-gate **		state -- return state word.
2101*7c478bd9Sstevel@tonic-gate **
2102*7c478bd9Sstevel@tonic-gate **	Returns:
2103*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
2104*7c478bd9Sstevel@tonic-gate */
2105*7c478bd9Sstevel@tonic-gate 
2106*7c478bd9Sstevel@tonic-gate static char *
2107*7c478bd9Sstevel@tonic-gate milter_command(command, data, sz, macros, e, state)
2108*7c478bd9Sstevel@tonic-gate 	char command;
2109*7c478bd9Sstevel@tonic-gate 	void *data;
2110*7c478bd9Sstevel@tonic-gate 	ssize_t sz;
2111*7c478bd9Sstevel@tonic-gate 	char **macros;
2112*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2113*7c478bd9Sstevel@tonic-gate 	char *state;
2114*7c478bd9Sstevel@tonic-gate {
2115*7c478bd9Sstevel@tonic-gate 	int i;
2116*7c478bd9Sstevel@tonic-gate 	char *response = NULL;
2117*7c478bd9Sstevel@tonic-gate 	time_t tn = 0;
2118*7c478bd9Sstevel@tonic-gate 
2119*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2120*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_command: cmd %c len %ld\n",
2121*7c478bd9Sstevel@tonic-gate 			(char) command, (long) sz);
2122*7c478bd9Sstevel@tonic-gate 
2123*7c478bd9Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
2124*7c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
2125*7c478bd9Sstevel@tonic-gate 	{
2126*7c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
2127*7c478bd9Sstevel@tonic-gate 
2128*7c478bd9Sstevel@tonic-gate 		/* previous problem? */
2129*7c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
2130*7c478bd9Sstevel@tonic-gate 		{
2131*7c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
2132*7c478bd9Sstevel@tonic-gate 			break;
2133*7c478bd9Sstevel@tonic-gate 		}
2134*7c478bd9Sstevel@tonic-gate 
2135*7c478bd9Sstevel@tonic-gate 		/* sanity check */
2136*7c478bd9Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
2137*7c478bd9Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
2138*7c478bd9Sstevel@tonic-gate 			continue;
2139*7c478bd9Sstevel@tonic-gate 
2140*7c478bd9Sstevel@tonic-gate 		/* send macros (regardless of whether we send command) */
2141*7c478bd9Sstevel@tonic-gate 		if (macros != NULL && macros[0] != NULL)
2142*7c478bd9Sstevel@tonic-gate 		{
2143*7c478bd9Sstevel@tonic-gate 			milter_send_macros(m, macros, command, e);
2144*7c478bd9Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
2145*7c478bd9Sstevel@tonic-gate 			{
2146*7c478bd9Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, continue);
2147*7c478bd9Sstevel@tonic-gate 				break;
2148*7c478bd9Sstevel@tonic-gate 			}
2149*7c478bd9Sstevel@tonic-gate 		}
2150*7c478bd9Sstevel@tonic-gate 
2151*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 21)
2152*7c478bd9Sstevel@tonic-gate 			tn = curtime();
2153*7c478bd9Sstevel@tonic-gate 
2154*7c478bd9Sstevel@tonic-gate 		response = milter_send_command(m, command, data, sz, e, state);
2155*7c478bd9Sstevel@tonic-gate 
2156*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 21)
2157*7c478bd9Sstevel@tonic-gate 		{
2158*7c478bd9Sstevel@tonic-gate 			/* log the time it took for the command per filter */
2159*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
2160*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): time command (%c), %d",
2161*7c478bd9Sstevel@tonic-gate 				  m->mf_name, command, (int) (tn - curtime()));
2162*7c478bd9Sstevel@tonic-gate 		}
2163*7c478bd9Sstevel@tonic-gate 
2164*7c478bd9Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE)
2165*7c478bd9Sstevel@tonic-gate 			break;
2166*7c478bd9Sstevel@tonic-gate 	}
2167*7c478bd9Sstevel@tonic-gate 	return response;
2168*7c478bd9Sstevel@tonic-gate }
2169*7c478bd9Sstevel@tonic-gate /*
2170*7c478bd9Sstevel@tonic-gate **  MILTER_NEGOTIATE -- get version and flags from filter
2171*7c478bd9Sstevel@tonic-gate **
2172*7c478bd9Sstevel@tonic-gate **	Parameters:
2173*7c478bd9Sstevel@tonic-gate **		m -- milter filter structure.
2174*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
2175*7c478bd9Sstevel@tonic-gate **
2176*7c478bd9Sstevel@tonic-gate **	Returns:
2177*7c478bd9Sstevel@tonic-gate **		0 on success, -1 otherwise
2178*7c478bd9Sstevel@tonic-gate */
2179*7c478bd9Sstevel@tonic-gate 
2180*7c478bd9Sstevel@tonic-gate static int
2181*7c478bd9Sstevel@tonic-gate milter_negotiate(m, e)
2182*7c478bd9Sstevel@tonic-gate 	struct milter *m;
2183*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2184*7c478bd9Sstevel@tonic-gate {
2185*7c478bd9Sstevel@tonic-gate 	char rcmd;
2186*7c478bd9Sstevel@tonic-gate 	mi_int32 fvers;
2187*7c478bd9Sstevel@tonic-gate 	mi_int32 fflags;
2188*7c478bd9Sstevel@tonic-gate 	mi_int32 pflags;
2189*7c478bd9Sstevel@tonic-gate 	char *response;
2190*7c478bd9Sstevel@tonic-gate 	ssize_t rlen;
2191*7c478bd9Sstevel@tonic-gate 	char data[MILTER_OPTLEN];
2192*7c478bd9Sstevel@tonic-gate 
2193*7c478bd9Sstevel@tonic-gate 	/* sanity check */
2194*7c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
2195*7c478bd9Sstevel@tonic-gate 	{
2196*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
2197*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
2198*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate, impossible state",
2199*7c478bd9Sstevel@tonic-gate 				  m->mf_name);
2200*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2201*7c478bd9Sstevel@tonic-gate 		return -1;
2202*7c478bd9Sstevel@tonic-gate 	}
2203*7c478bd9Sstevel@tonic-gate 
2204*7c478bd9Sstevel@tonic-gate 	fvers = htonl(SMFI_VERSION);
2205*7c478bd9Sstevel@tonic-gate 	fflags = htonl(SMFI_CURR_ACTS);
2206*7c478bd9Sstevel@tonic-gate 	pflags = htonl(SMFI_CURR_PROT);
2207*7c478bd9Sstevel@tonic-gate 	(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
2208*7c478bd9Sstevel@tonic-gate 	(void) memcpy(data + MILTER_LEN_BYTES,
2209*7c478bd9Sstevel@tonic-gate 		      (char *) &fflags, MILTER_LEN_BYTES);
2210*7c478bd9Sstevel@tonic-gate 	(void) memcpy(data + (MILTER_LEN_BYTES * 2),
2211*7c478bd9Sstevel@tonic-gate 		      (char *) &pflags, MILTER_LEN_BYTES);
2212*7c478bd9Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_OPTNEG, data, sizeof data,
2213*7c478bd9Sstevel@tonic-gate 			    m->mf_timeout[SMFTO_WRITE], e);
2214*7c478bd9Sstevel@tonic-gate 
2215*7c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
2216*7c478bd9Sstevel@tonic-gate 		return -1;
2217*7c478bd9Sstevel@tonic-gate 
2218*7c478bd9Sstevel@tonic-gate 	response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e);
2219*7c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
2220*7c478bd9Sstevel@tonic-gate 		return -1;
2221*7c478bd9Sstevel@tonic-gate 
2222*7c478bd9Sstevel@tonic-gate 	if (rcmd != SMFIC_OPTNEG)
2223*7c478bd9Sstevel@tonic-gate 	{
2224*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
2225*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n",
2226*7c478bd9Sstevel@tonic-gate 				m->mf_name, rcmd, SMFIC_OPTNEG);
2227*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
2228*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
2229*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: returned %c instead of %c",
2230*7c478bd9Sstevel@tonic-gate 				  m->mf_name, rcmd, SMFIC_OPTNEG);
2231*7c478bd9Sstevel@tonic-gate 		if (response != NULL)
2232*7c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
2233*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2234*7c478bd9Sstevel@tonic-gate 		return -1;
2235*7c478bd9Sstevel@tonic-gate 	}
2236*7c478bd9Sstevel@tonic-gate 
2237*7c478bd9Sstevel@tonic-gate 	/* Make sure we have enough bytes for the version */
2238*7c478bd9Sstevel@tonic-gate 	if (response == NULL || rlen < MILTER_LEN_BYTES)
2239*7c478bd9Sstevel@tonic-gate 	{
2240*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
2241*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return valid info\n",
2242*7c478bd9Sstevel@tonic-gate 				m->mf_name);
2243*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
2244*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
2245*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return valid info",
2246*7c478bd9Sstevel@tonic-gate 				  m->mf_name);
2247*7c478bd9Sstevel@tonic-gate 		if (response != NULL)
2248*7c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
2249*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2250*7c478bd9Sstevel@tonic-gate 		return -1;
2251*7c478bd9Sstevel@tonic-gate 	}
2252*7c478bd9Sstevel@tonic-gate 
2253*7c478bd9Sstevel@tonic-gate 	/* extract information */
2254*7c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
2255*7c478bd9Sstevel@tonic-gate 
2256*7c478bd9Sstevel@tonic-gate 	/* Now make sure we have enough for the feature bitmap */
2257*7c478bd9Sstevel@tonic-gate 	if (rlen != MILTER_OPTLEN)
2258*7c478bd9Sstevel@tonic-gate 	{
2259*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
2260*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return enough info\n",
2261*7c478bd9Sstevel@tonic-gate 				m->mf_name);
2262*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
2263*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
2264*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return enough info",
2265*7c478bd9Sstevel@tonic-gate 				  m->mf_name);
2266*7c478bd9Sstevel@tonic-gate 		if (response != NULL)
2267*7c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
2268*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2269*7c478bd9Sstevel@tonic-gate 		return -1;
2270*7c478bd9Sstevel@tonic-gate 	}
2271*7c478bd9Sstevel@tonic-gate 
2272*7c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
2273*7c478bd9Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
2274*7c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
2275*7c478bd9Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
2276*7c478bd9Sstevel@tonic-gate 	sm_free(response); /* XXX */
2277*7c478bd9Sstevel@tonic-gate 	response = NULL;
2278*7c478bd9Sstevel@tonic-gate 
2279*7c478bd9Sstevel@tonic-gate 	m->mf_fvers = ntohl(fvers);
2280*7c478bd9Sstevel@tonic-gate 	m->mf_fflags = ntohl(fflags);
2281*7c478bd9Sstevel@tonic-gate 	m->mf_pflags = ntohl(pflags);
2282*7c478bd9Sstevel@tonic-gate 
2283*7c478bd9Sstevel@tonic-gate 	/* check for version compatibility */
2284*7c478bd9Sstevel@tonic-gate 	if (m->mf_fvers == 1 ||
2285*7c478bd9Sstevel@tonic-gate 	    m->mf_fvers > SMFI_VERSION)
2286*7c478bd9Sstevel@tonic-gate 	{
2287*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
2288*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n",
2289*7c478bd9Sstevel@tonic-gate 				m->mf_name, m->mf_fvers, SMFI_VERSION);
2290*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
2291*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
2292*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: version %d != MTA milter version %d",
2293*7c478bd9Sstevel@tonic-gate 				  m->mf_name, m->mf_fvers, SMFI_VERSION);
2294*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2295*7c478bd9Sstevel@tonic-gate 		return -1;
2296*7c478bd9Sstevel@tonic-gate 	}
2297*7c478bd9Sstevel@tonic-gate 
2298*7c478bd9Sstevel@tonic-gate 	/* check for filter feature mismatch */
2299*7c478bd9Sstevel@tonic-gate 	if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags)
2300*7c478bd9Sstevel@tonic-gate 	{
2301*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
2302*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n",
2303*7c478bd9Sstevel@tonic-gate 				m->mf_name, m->mf_fflags,
2304*7c478bd9Sstevel@tonic-gate 				SMFI_CURR_ACTS);
2305*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
2306*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
2307*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx",
2308*7c478bd9Sstevel@tonic-gate 				  m->mf_name, m->mf_fflags,
2309*7c478bd9Sstevel@tonic-gate 				  (unsigned long) SMFI_CURR_ACTS);
2310*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2311*7c478bd9Sstevel@tonic-gate 		return -1;
2312*7c478bd9Sstevel@tonic-gate 	}
2313*7c478bd9Sstevel@tonic-gate 
2314*7c478bd9Sstevel@tonic-gate 	/* check for protocol feature mismatch */
2315*7c478bd9Sstevel@tonic-gate 	if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags)
2316*7c478bd9Sstevel@tonic-gate 	{
2317*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
2318*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n",
2319*7c478bd9Sstevel@tonic-gate 				m->mf_name, m->mf_pflags,
2320*7c478bd9Sstevel@tonic-gate 				(unsigned long) SMFI_CURR_PROT);
2321*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
2322*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
2323*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx",
2324*7c478bd9Sstevel@tonic-gate 				  m->mf_name, m->mf_pflags,
2325*7c478bd9Sstevel@tonic-gate 				  (unsigned long) SMFI_CURR_PROT);
2326*7c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2327*7c478bd9Sstevel@tonic-gate 		return -1;
2328*7c478bd9Sstevel@tonic-gate 	}
2329*7c478bd9Sstevel@tonic-gate 
2330*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 5))
2331*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_negotiate(%s): version %u, fflags 0x%x, pflags 0x%x\n",
2332*7c478bd9Sstevel@tonic-gate 			m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
2333*7c478bd9Sstevel@tonic-gate 	return 0;
2334*7c478bd9Sstevel@tonic-gate }
2335*7c478bd9Sstevel@tonic-gate /*
2336*7c478bd9Sstevel@tonic-gate **  MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
2337*7c478bd9Sstevel@tonic-gate **
2338*7c478bd9Sstevel@tonic-gate **	Reduce code duplication by putting these checks in one place
2339*7c478bd9Sstevel@tonic-gate **
2340*7c478bd9Sstevel@tonic-gate **	Parameters:
2341*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
2342*7c478bd9Sstevel@tonic-gate **
2343*7c478bd9Sstevel@tonic-gate **	Returns:
2344*7c478bd9Sstevel@tonic-gate **		none
2345*7c478bd9Sstevel@tonic-gate */
2346*7c478bd9Sstevel@tonic-gate 
2347*7c478bd9Sstevel@tonic-gate static void
2348*7c478bd9Sstevel@tonic-gate milter_per_connection_check(e)
2349*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2350*7c478bd9Sstevel@tonic-gate {
2351*7c478bd9Sstevel@tonic-gate 	int i;
2352*7c478bd9Sstevel@tonic-gate 
2353*7c478bd9Sstevel@tonic-gate 	/* see if we are done with any of the filters */
2354*7c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
2355*7c478bd9Sstevel@tonic-gate 	{
2356*7c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
2357*7c478bd9Sstevel@tonic-gate 
2358*7c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_CLOSABLE)
2359*7c478bd9Sstevel@tonic-gate 			milter_quit_filter(m, e);
2360*7c478bd9Sstevel@tonic-gate 	}
2361*7c478bd9Sstevel@tonic-gate }
2362*7c478bd9Sstevel@tonic-gate /*
2363*7c478bd9Sstevel@tonic-gate **  MILTER_ERROR -- Put a milter filter into error state
2364*7c478bd9Sstevel@tonic-gate **
2365*7c478bd9Sstevel@tonic-gate **	Parameters:
2366*7c478bd9Sstevel@tonic-gate **		m -- the broken filter.
2367*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
2368*7c478bd9Sstevel@tonic-gate **
2369*7c478bd9Sstevel@tonic-gate **	Returns:
2370*7c478bd9Sstevel@tonic-gate **		none
2371*7c478bd9Sstevel@tonic-gate */
2372*7c478bd9Sstevel@tonic-gate 
2373*7c478bd9Sstevel@tonic-gate static void
2374*7c478bd9Sstevel@tonic-gate milter_error(m, e)
2375*7c478bd9Sstevel@tonic-gate 	struct milter *m;
2376*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2377*7c478bd9Sstevel@tonic-gate {
2378*7c478bd9Sstevel@tonic-gate 	/*
2379*7c478bd9Sstevel@tonic-gate 	**  We could send a quit here but we may have gotten here due to
2380*7c478bd9Sstevel@tonic-gate 	**  an I/O error so we don't want to try to make things worse.
2381*7c478bd9Sstevel@tonic-gate 	*/
2382*7c478bd9Sstevel@tonic-gate 
2383*7c478bd9Sstevel@tonic-gate 	if (m->mf_sock >= 0)
2384*7c478bd9Sstevel@tonic-gate 	{
2385*7c478bd9Sstevel@tonic-gate 		(void) close(m->mf_sock);
2386*7c478bd9Sstevel@tonic-gate 		m->mf_sock = -1;
2387*7c478bd9Sstevel@tonic-gate 	}
2388*7c478bd9Sstevel@tonic-gate 	m->mf_state = SMFS_ERROR;
2389*7c478bd9Sstevel@tonic-gate 
2390*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 0)
2391*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state",
2392*7c478bd9Sstevel@tonic-gate 			  m->mf_name);
2393*7c478bd9Sstevel@tonic-gate }
2394*7c478bd9Sstevel@tonic-gate /*
2395*7c478bd9Sstevel@tonic-gate **  MILTER_HEADERS -- send headers to a single milter filter
2396*7c478bd9Sstevel@tonic-gate **
2397*7c478bd9Sstevel@tonic-gate **	Parameters:
2398*7c478bd9Sstevel@tonic-gate **		m -- current filter.
2399*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
2400*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
2401*7c478bd9Sstevel@tonic-gate **
2402*7c478bd9Sstevel@tonic-gate **	Returns:
2403*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
2404*7c478bd9Sstevel@tonic-gate */
2405*7c478bd9Sstevel@tonic-gate 
2406*7c478bd9Sstevel@tonic-gate static char *
2407*7c478bd9Sstevel@tonic-gate milter_headers(m, e, state)
2408*7c478bd9Sstevel@tonic-gate 	struct milter *m;
2409*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2410*7c478bd9Sstevel@tonic-gate 	char *state;
2411*7c478bd9Sstevel@tonic-gate {
2412*7c478bd9Sstevel@tonic-gate 	char *response = NULL;
2413*7c478bd9Sstevel@tonic-gate 	HDR *h;
2414*7c478bd9Sstevel@tonic-gate 
2415*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 17)
2416*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send",
2417*7c478bd9Sstevel@tonic-gate 			  m->mf_name);
2418*7c478bd9Sstevel@tonic-gate 
2419*7c478bd9Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
2420*7c478bd9Sstevel@tonic-gate 	{
2421*7c478bd9Sstevel@tonic-gate 		char *buf;
2422*7c478bd9Sstevel@tonic-gate 		ssize_t s;
2423*7c478bd9Sstevel@tonic-gate 
2424*7c478bd9Sstevel@tonic-gate 		/* don't send over deleted headers */
2425*7c478bd9Sstevel@tonic-gate 		if (h->h_value == NULL)
2426*7c478bd9Sstevel@tonic-gate 		{
2427*7c478bd9Sstevel@tonic-gate 			/* strip H_USER so not counted in milter_changeheader() */
2428*7c478bd9Sstevel@tonic-gate 			h->h_flags &= ~H_USER;
2429*7c478bd9Sstevel@tonic-gate 			continue;
2430*7c478bd9Sstevel@tonic-gate 		}
2431*7c478bd9Sstevel@tonic-gate 
2432*7c478bd9Sstevel@tonic-gate 		/* skip auto-generated */
2433*7c478bd9Sstevel@tonic-gate 		if (!bitset(H_USER, h->h_flags))
2434*7c478bd9Sstevel@tonic-gate 			continue;
2435*7c478bd9Sstevel@tonic-gate 
2436*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2437*7c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_headers: %s: %s\n",
2438*7c478bd9Sstevel@tonic-gate 				h->h_field, h->h_value);
2439*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 21)
2440*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s",
2441*7c478bd9Sstevel@tonic-gate 				  m->mf_name, h->h_field);
2442*7c478bd9Sstevel@tonic-gate 
2443*7c478bd9Sstevel@tonic-gate 		s = strlen(h->h_field) + 1 + strlen(h->h_value) + 1;
2444*7c478bd9Sstevel@tonic-gate 		if (s < 0)
2445*7c478bd9Sstevel@tonic-gate 			continue;
2446*7c478bd9Sstevel@tonic-gate 		buf = (char *) xalloc(s);
2447*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(buf, s, "%s%c%s",
2448*7c478bd9Sstevel@tonic-gate 			h->h_field, '\0', h->h_value);
2449*7c478bd9Sstevel@tonic-gate 
2450*7c478bd9Sstevel@tonic-gate 		/* send it over */
2451*7c478bd9Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_HEADER, buf,
2452*7c478bd9Sstevel@tonic-gate 					       s, e, state);
2453*7c478bd9Sstevel@tonic-gate 		sm_free(buf); /* XXX */
2454*7c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
2455*7c478bd9Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
2456*7c478bd9Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
2457*7c478bd9Sstevel@tonic-gate 			break;
2458*7c478bd9Sstevel@tonic-gate 	}
2459*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 17)
2460*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent",
2461*7c478bd9Sstevel@tonic-gate 			  m->mf_name);
2462*7c478bd9Sstevel@tonic-gate 	return response;
2463*7c478bd9Sstevel@tonic-gate }
2464*7c478bd9Sstevel@tonic-gate /*
2465*7c478bd9Sstevel@tonic-gate **  MILTER_BODY -- send the body to a filter
2466*7c478bd9Sstevel@tonic-gate **
2467*7c478bd9Sstevel@tonic-gate **	Parameters:
2468*7c478bd9Sstevel@tonic-gate **		m -- current filter.
2469*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
2470*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
2471*7c478bd9Sstevel@tonic-gate **
2472*7c478bd9Sstevel@tonic-gate **	Returns:
2473*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
2474*7c478bd9Sstevel@tonic-gate */
2475*7c478bd9Sstevel@tonic-gate 
2476*7c478bd9Sstevel@tonic-gate static char *
2477*7c478bd9Sstevel@tonic-gate milter_body(m, e, state)
2478*7c478bd9Sstevel@tonic-gate 	struct milter *m;
2479*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2480*7c478bd9Sstevel@tonic-gate 	char *state;
2481*7c478bd9Sstevel@tonic-gate {
2482*7c478bd9Sstevel@tonic-gate 	char bufchar = '\0';
2483*7c478bd9Sstevel@tonic-gate 	char prevchar = '\0';
2484*7c478bd9Sstevel@tonic-gate 	int c;
2485*7c478bd9Sstevel@tonic-gate 	char *response = NULL;
2486*7c478bd9Sstevel@tonic-gate 	char *bp;
2487*7c478bd9Sstevel@tonic-gate 	char buf[MILTER_CHUNK_SIZE];
2488*7c478bd9Sstevel@tonic-gate 
2489*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2490*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_body\n");
2491*7c478bd9Sstevel@tonic-gate 
2492*7c478bd9Sstevel@tonic-gate 	if (bfrewind(e->e_dfp) < 0)
2493*7c478bd9Sstevel@tonic-gate 	{
2494*7c478bd9Sstevel@tonic-gate 		ExitStat = EX_IOERR;
2495*7c478bd9Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
2496*7c478bd9Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: rewind error",
2497*7c478bd9Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
2498*7c478bd9Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
2499*7c478bd9Sstevel@tonic-gate 		return NULL;
2500*7c478bd9Sstevel@tonic-gate 	}
2501*7c478bd9Sstevel@tonic-gate 
2502*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 17)
2503*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send",
2504*7c478bd9Sstevel@tonic-gate 			  m->mf_name);
2505*7c478bd9Sstevel@tonic-gate 	bp = buf;
2506*7c478bd9Sstevel@tonic-gate 	while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF)
2507*7c478bd9Sstevel@tonic-gate 	{
2508*7c478bd9Sstevel@tonic-gate 		/*  Change LF to CRLF */
2509*7c478bd9Sstevel@tonic-gate 		if (c == '\n')
2510*7c478bd9Sstevel@tonic-gate 		{
2511*7c478bd9Sstevel@tonic-gate 			/* Not a CRLF already? */
2512*7c478bd9Sstevel@tonic-gate 			if (prevchar != '\r')
2513*7c478bd9Sstevel@tonic-gate 			{
2514*7c478bd9Sstevel@tonic-gate 				/* Room for CR now? */
2515*7c478bd9Sstevel@tonic-gate 				if (bp + 2 > &buf[sizeof buf])
2516*7c478bd9Sstevel@tonic-gate 				{
2517*7c478bd9Sstevel@tonic-gate 					/* No room, buffer LF */
2518*7c478bd9Sstevel@tonic-gate 					bufchar = c;
2519*7c478bd9Sstevel@tonic-gate 
2520*7c478bd9Sstevel@tonic-gate 					/* and send CR now */
2521*7c478bd9Sstevel@tonic-gate 					c = '\r';
2522*7c478bd9Sstevel@tonic-gate 				}
2523*7c478bd9Sstevel@tonic-gate 				else
2524*7c478bd9Sstevel@tonic-gate 				{
2525*7c478bd9Sstevel@tonic-gate 					/* Room to do it now */
2526*7c478bd9Sstevel@tonic-gate 					*bp++ = '\r';
2527*7c478bd9Sstevel@tonic-gate 					prevchar = '\r';
2528*7c478bd9Sstevel@tonic-gate 				}
2529*7c478bd9Sstevel@tonic-gate 			}
2530*7c478bd9Sstevel@tonic-gate 		}
2531*7c478bd9Sstevel@tonic-gate 		*bp++ = (char) c;
2532*7c478bd9Sstevel@tonic-gate 		prevchar = c;
2533*7c478bd9Sstevel@tonic-gate 		if (bp >= &buf[sizeof buf])
2534*7c478bd9Sstevel@tonic-gate 		{
2535*7c478bd9Sstevel@tonic-gate 			/* send chunk */
2536*7c478bd9Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_BODY, buf,
2537*7c478bd9Sstevel@tonic-gate 						       bp - buf, e, state);
2538*7c478bd9Sstevel@tonic-gate 			bp = buf;
2539*7c478bd9Sstevel@tonic-gate 			if (bufchar != '\0')
2540*7c478bd9Sstevel@tonic-gate 			{
2541*7c478bd9Sstevel@tonic-gate 				*bp++ = bufchar;
2542*7c478bd9Sstevel@tonic-gate 				bufchar = '\0';
2543*7c478bd9Sstevel@tonic-gate 				prevchar = bufchar;
2544*7c478bd9Sstevel@tonic-gate 			}
2545*7c478bd9Sstevel@tonic-gate 		}
2546*7c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
2547*7c478bd9Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
2548*7c478bd9Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
2549*7c478bd9Sstevel@tonic-gate 			break;
2550*7c478bd9Sstevel@tonic-gate 	}
2551*7c478bd9Sstevel@tonic-gate 
2552*7c478bd9Sstevel@tonic-gate 	/* check for read errors */
2553*7c478bd9Sstevel@tonic-gate 	if (sm_io_error(e->e_dfp))
2554*7c478bd9Sstevel@tonic-gate 	{
2555*7c478bd9Sstevel@tonic-gate 		ExitStat = EX_IOERR;
2556*7c478bd9Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
2557*7c478bd9Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
2558*7c478bd9Sstevel@tonic-gate 		{
2559*7c478bd9Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
2560*7c478bd9Sstevel@tonic-gate 			if (response != NULL)
2561*7c478bd9Sstevel@tonic-gate 			{
2562*7c478bd9Sstevel@tonic-gate 				sm_free(response); /* XXX */
2563*7c478bd9Sstevel@tonic-gate 				response = NULL;
2564*7c478bd9Sstevel@tonic-gate 			}
2565*7c478bd9Sstevel@tonic-gate 		}
2566*7c478bd9Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: read error",
2567*7c478bd9Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
2568*7c478bd9Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
2569*7c478bd9Sstevel@tonic-gate 		return response;
2570*7c478bd9Sstevel@tonic-gate 	}
2571*7c478bd9Sstevel@tonic-gate 
2572*7c478bd9Sstevel@tonic-gate 	/* send last body chunk */
2573*7c478bd9Sstevel@tonic-gate 	if (bp > buf &&
2574*7c478bd9Sstevel@tonic-gate 	    m->mf_state != SMFS_ERROR &&
2575*7c478bd9Sstevel@tonic-gate 	    m->mf_state != SMFS_DONE &&
2576*7c478bd9Sstevel@tonic-gate 	    *state == SMFIR_CONTINUE)
2577*7c478bd9Sstevel@tonic-gate 	{
2578*7c478bd9Sstevel@tonic-gate 		/* send chunk */
2579*7c478bd9Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
2580*7c478bd9Sstevel@tonic-gate 					       e, state);
2581*7c478bd9Sstevel@tonic-gate 		bp = buf;
2582*7c478bd9Sstevel@tonic-gate 	}
2583*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 17)
2584*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent",
2585*7c478bd9Sstevel@tonic-gate 			  m->mf_name);
2586*7c478bd9Sstevel@tonic-gate 	return response;
2587*7c478bd9Sstevel@tonic-gate }
2588*7c478bd9Sstevel@tonic-gate 
2589*7c478bd9Sstevel@tonic-gate /*
2590*7c478bd9Sstevel@tonic-gate **  Actions
2591*7c478bd9Sstevel@tonic-gate */
2592*7c478bd9Sstevel@tonic-gate 
2593*7c478bd9Sstevel@tonic-gate /*
2594*7c478bd9Sstevel@tonic-gate **  MILTER_ADDHEADER -- Add the supplied header to the message
2595*7c478bd9Sstevel@tonic-gate **
2596*7c478bd9Sstevel@tonic-gate **	Parameters:
2597*7c478bd9Sstevel@tonic-gate **		response -- encoded form of header/value.
2598*7c478bd9Sstevel@tonic-gate **		rlen -- length of response.
2599*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
2600*7c478bd9Sstevel@tonic-gate **
2601*7c478bd9Sstevel@tonic-gate **	Returns:
2602*7c478bd9Sstevel@tonic-gate **		none
2603*7c478bd9Sstevel@tonic-gate */
2604*7c478bd9Sstevel@tonic-gate 
2605*7c478bd9Sstevel@tonic-gate static void
2606*7c478bd9Sstevel@tonic-gate milter_addheader(response, rlen, e)
2607*7c478bd9Sstevel@tonic-gate 	char *response;
2608*7c478bd9Sstevel@tonic-gate 	ssize_t rlen;
2609*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2610*7c478bd9Sstevel@tonic-gate {
2611*7c478bd9Sstevel@tonic-gate 	char *val;
2612*7c478bd9Sstevel@tonic-gate 	HDR *h;
2613*7c478bd9Sstevel@tonic-gate 
2614*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2615*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_addheader: ");
2616*7c478bd9Sstevel@tonic-gate 
2617*7c478bd9Sstevel@tonic-gate 	/* sanity checks */
2618*7c478bd9Sstevel@tonic-gate 	if (response == NULL)
2619*7c478bd9Sstevel@tonic-gate 	{
2620*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2621*7c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
2622*7c478bd9Sstevel@tonic-gate 		return;
2623*7c478bd9Sstevel@tonic-gate 	}
2624*7c478bd9Sstevel@tonic-gate 
2625*7c478bd9Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
2626*7c478bd9Sstevel@tonic-gate 	{
2627*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2628*7c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
2629*7c478bd9Sstevel@tonic-gate 		return;
2630*7c478bd9Sstevel@tonic-gate 	}
2631*7c478bd9Sstevel@tonic-gate 
2632*7c478bd9Sstevel@tonic-gate 	/* Find separating NUL */
2633*7c478bd9Sstevel@tonic-gate 	val = response + strlen(response) + 1;
2634*7c478bd9Sstevel@tonic-gate 
2635*7c478bd9Sstevel@tonic-gate 	/* another sanity check */
2636*7c478bd9Sstevel@tonic-gate 	if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
2637*7c478bd9Sstevel@tonic-gate 	{
2638*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2639*7c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
2640*7c478bd9Sstevel@tonic-gate 		return;
2641*7c478bd9Sstevel@tonic-gate 	}
2642*7c478bd9Sstevel@tonic-gate 
2643*7c478bd9Sstevel@tonic-gate 	if (*response == '\0')
2644*7c478bd9Sstevel@tonic-gate 	{
2645*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2646*7c478bd9Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
2647*7c478bd9Sstevel@tonic-gate 		return;
2648*7c478bd9Sstevel@tonic-gate 	}
2649*7c478bd9Sstevel@tonic-gate 
2650*7c478bd9Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
2651*7c478bd9Sstevel@tonic-gate 	{
2652*7c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, response) == 0 &&
2653*7c478bd9Sstevel@tonic-gate 		    !bitset(H_USER, h->h_flags) &&
2654*7c478bd9Sstevel@tonic-gate 		    !bitset(H_TRACE, h->h_flags))
2655*7c478bd9Sstevel@tonic-gate 			break;
2656*7c478bd9Sstevel@tonic-gate 	}
2657*7c478bd9Sstevel@tonic-gate 
2658*7c478bd9Sstevel@tonic-gate 	/* add to e_msgsize */
2659*7c478bd9Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
2660*7c478bd9Sstevel@tonic-gate 
2661*7c478bd9Sstevel@tonic-gate 	if (h != NULL)
2662*7c478bd9Sstevel@tonic-gate 	{
2663*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2664*7c478bd9Sstevel@tonic-gate 			sm_dprintf("Replace default header %s value with %s\n",
2665*7c478bd9Sstevel@tonic-gate 				   h->h_field, val);
2666*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 8)
2667*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
2668*7c478bd9Sstevel@tonic-gate 				  "Milter change: default header %s value with %s",
2669*7c478bd9Sstevel@tonic-gate 				  h->h_field, val);
2670*7c478bd9Sstevel@tonic-gate 		h->h_value = newstr(val);
2671*7c478bd9Sstevel@tonic-gate 		h->h_flags |= H_USER;
2672*7c478bd9Sstevel@tonic-gate 	}
2673*7c478bd9Sstevel@tonic-gate 	else
2674*7c478bd9Sstevel@tonic-gate 	{
2675*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2676*7c478bd9Sstevel@tonic-gate 			sm_dprintf("Add %s: %s\n", response, val);
2677*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 8)
2678*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter add: header: %s: %s",
2679*7c478bd9Sstevel@tonic-gate 				  response, val);
2680*7c478bd9Sstevel@tonic-gate 		addheader(newstr(response), val, H_USER, e);
2681*7c478bd9Sstevel@tonic-gate 	}
2682*7c478bd9Sstevel@tonic-gate }
2683*7c478bd9Sstevel@tonic-gate /*
2684*7c478bd9Sstevel@tonic-gate **  MILTER_INSHEADER -- Insert the supplied header
2685*7c478bd9Sstevel@tonic-gate **
2686*7c478bd9Sstevel@tonic-gate **	Parameters:
2687*7c478bd9Sstevel@tonic-gate **		response -- encoded form of header/value.
2688*7c478bd9Sstevel@tonic-gate **		rlen -- length of response.
2689*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
2690*7c478bd9Sstevel@tonic-gate **
2691*7c478bd9Sstevel@tonic-gate **	Returns:
2692*7c478bd9Sstevel@tonic-gate **		none
2693*7c478bd9Sstevel@tonic-gate **
2694*7c478bd9Sstevel@tonic-gate **  	Notes:
2695*7c478bd9Sstevel@tonic-gate **  		Unlike milter_addheader(), this does not attempt to determine
2696*7c478bd9Sstevel@tonic-gate **  		if the header already exists in the envelope, even a
2697*7c478bd9Sstevel@tonic-gate **  		deleted version.  It just blindly inserts.
2698*7c478bd9Sstevel@tonic-gate */
2699*7c478bd9Sstevel@tonic-gate 
2700*7c478bd9Sstevel@tonic-gate static void
2701*7c478bd9Sstevel@tonic-gate milter_insheader(response, rlen, e)
2702*7c478bd9Sstevel@tonic-gate 	char *response;
2703*7c478bd9Sstevel@tonic-gate 	ssize_t rlen;
2704*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2705*7c478bd9Sstevel@tonic-gate {
2706*7c478bd9Sstevel@tonic-gate 	mi_int32 idx, i;
2707*7c478bd9Sstevel@tonic-gate 	char *field;
2708*7c478bd9Sstevel@tonic-gate 	char *val;
2709*7c478bd9Sstevel@tonic-gate 
2710*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2711*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_insheader: ");
2712*7c478bd9Sstevel@tonic-gate 
2713*7c478bd9Sstevel@tonic-gate 	/* sanity checks */
2714*7c478bd9Sstevel@tonic-gate 	if (response == NULL)
2715*7c478bd9Sstevel@tonic-gate 	{
2716*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2717*7c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
2718*7c478bd9Sstevel@tonic-gate 		return;
2719*7c478bd9Sstevel@tonic-gate 	}
2720*7c478bd9Sstevel@tonic-gate 
2721*7c478bd9Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
2722*7c478bd9Sstevel@tonic-gate 	{
2723*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2724*7c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
2725*7c478bd9Sstevel@tonic-gate 		return;
2726*7c478bd9Sstevel@tonic-gate 	}
2727*7c478bd9Sstevel@tonic-gate 
2728*7c478bd9Sstevel@tonic-gate 	/* decode */
2729*7c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
2730*7c478bd9Sstevel@tonic-gate 	idx = ntohl(i);
2731*7c478bd9Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
2732*7c478bd9Sstevel@tonic-gate 	val = field + strlen(field) + 1;
2733*7c478bd9Sstevel@tonic-gate 
2734*7c478bd9Sstevel@tonic-gate 	/* another sanity check */
2735*7c478bd9Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
2736*7c478bd9Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
2737*7c478bd9Sstevel@tonic-gate 	{
2738*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2739*7c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
2740*7c478bd9Sstevel@tonic-gate 		return;
2741*7c478bd9Sstevel@tonic-gate 	}
2742*7c478bd9Sstevel@tonic-gate 
2743*7c478bd9Sstevel@tonic-gate 	if (*field == '\0')
2744*7c478bd9Sstevel@tonic-gate 	{
2745*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2746*7c478bd9Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
2747*7c478bd9Sstevel@tonic-gate 		return;
2748*7c478bd9Sstevel@tonic-gate 	}
2749*7c478bd9Sstevel@tonic-gate 
2750*7c478bd9Sstevel@tonic-gate 	/* add to e_msgsize */
2751*7c478bd9Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
2752*7c478bd9Sstevel@tonic-gate 
2753*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2754*7c478bd9Sstevel@tonic-gate 		sm_dprintf("Insert (%d) %s: %s\n", idx, response, val);
2755*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 8)
2756*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id,
2757*7c478bd9Sstevel@tonic-gate 		          "Milter insert (%d): header: %s: %s",
2758*7c478bd9Sstevel@tonic-gate 			  idx, field, val);
2759*7c478bd9Sstevel@tonic-gate 	insheader(idx, newstr(field), val, H_USER, e);
2760*7c478bd9Sstevel@tonic-gate }
2761*7c478bd9Sstevel@tonic-gate /*
2762*7c478bd9Sstevel@tonic-gate **  MILTER_CHANGEHEADER -- Change the supplied header in the message
2763*7c478bd9Sstevel@tonic-gate **
2764*7c478bd9Sstevel@tonic-gate **	Parameters:
2765*7c478bd9Sstevel@tonic-gate **		response -- encoded form of header/index/value.
2766*7c478bd9Sstevel@tonic-gate **		rlen -- length of response.
2767*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
2768*7c478bd9Sstevel@tonic-gate **
2769*7c478bd9Sstevel@tonic-gate **	Returns:
2770*7c478bd9Sstevel@tonic-gate **		none
2771*7c478bd9Sstevel@tonic-gate */
2772*7c478bd9Sstevel@tonic-gate 
2773*7c478bd9Sstevel@tonic-gate static void
2774*7c478bd9Sstevel@tonic-gate milter_changeheader(response, rlen, e)
2775*7c478bd9Sstevel@tonic-gate 	char *response;
2776*7c478bd9Sstevel@tonic-gate 	ssize_t rlen;
2777*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2778*7c478bd9Sstevel@tonic-gate {
2779*7c478bd9Sstevel@tonic-gate 	mi_int32 i, index;
2780*7c478bd9Sstevel@tonic-gate 	char *field, *val;
2781*7c478bd9Sstevel@tonic-gate 	HDR *h, *sysheader;
2782*7c478bd9Sstevel@tonic-gate 
2783*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2784*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_changeheader: ");
2785*7c478bd9Sstevel@tonic-gate 
2786*7c478bd9Sstevel@tonic-gate 	/* sanity checks */
2787*7c478bd9Sstevel@tonic-gate 	if (response == NULL)
2788*7c478bd9Sstevel@tonic-gate 	{
2789*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2790*7c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
2791*7c478bd9Sstevel@tonic-gate 		return;
2792*7c478bd9Sstevel@tonic-gate 	}
2793*7c478bd9Sstevel@tonic-gate 
2794*7c478bd9Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
2795*7c478bd9Sstevel@tonic-gate 	{
2796*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2797*7c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
2798*7c478bd9Sstevel@tonic-gate 		return;
2799*7c478bd9Sstevel@tonic-gate 	}
2800*7c478bd9Sstevel@tonic-gate 
2801*7c478bd9Sstevel@tonic-gate 	/* Find separating NUL */
2802*7c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
2803*7c478bd9Sstevel@tonic-gate 	index = ntohl(i);
2804*7c478bd9Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
2805*7c478bd9Sstevel@tonic-gate 	val = field + strlen(field) + 1;
2806*7c478bd9Sstevel@tonic-gate 
2807*7c478bd9Sstevel@tonic-gate 	/* another sanity check */
2808*7c478bd9Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
2809*7c478bd9Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
2810*7c478bd9Sstevel@tonic-gate 	{
2811*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2812*7c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
2813*7c478bd9Sstevel@tonic-gate 		return;
2814*7c478bd9Sstevel@tonic-gate 	}
2815*7c478bd9Sstevel@tonic-gate 
2816*7c478bd9Sstevel@tonic-gate 	if (*field == '\0')
2817*7c478bd9Sstevel@tonic-gate 	{
2818*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2819*7c478bd9Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
2820*7c478bd9Sstevel@tonic-gate 		return;
2821*7c478bd9Sstevel@tonic-gate 	}
2822*7c478bd9Sstevel@tonic-gate 
2823*7c478bd9Sstevel@tonic-gate 	sysheader = NULL;
2824*7c478bd9Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
2825*7c478bd9Sstevel@tonic-gate 	{
2826*7c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, field) == 0)
2827*7c478bd9Sstevel@tonic-gate 		{
2828*7c478bd9Sstevel@tonic-gate 			if (bitset(H_USER, h->h_flags) &&
2829*7c478bd9Sstevel@tonic-gate 			    --index <= 0)
2830*7c478bd9Sstevel@tonic-gate 			{
2831*7c478bd9Sstevel@tonic-gate 				sysheader = NULL;
2832*7c478bd9Sstevel@tonic-gate 				break;
2833*7c478bd9Sstevel@tonic-gate 			}
2834*7c478bd9Sstevel@tonic-gate 			else if (!bitset(H_USER, h->h_flags) &&
2835*7c478bd9Sstevel@tonic-gate 				 !bitset(H_TRACE, h->h_flags))
2836*7c478bd9Sstevel@tonic-gate 			{
2837*7c478bd9Sstevel@tonic-gate 				/*
2838*7c478bd9Sstevel@tonic-gate 				**  DRUMS msg-fmt draft says can only have
2839*7c478bd9Sstevel@tonic-gate 				**  multiple occurences of trace fields,
2840*7c478bd9Sstevel@tonic-gate 				**  so make sure we replace any non-trace,
2841*7c478bd9Sstevel@tonic-gate 				**  non-user field.
2842*7c478bd9Sstevel@tonic-gate 				*/
2843*7c478bd9Sstevel@tonic-gate 
2844*7c478bd9Sstevel@tonic-gate 				sysheader = h;
2845*7c478bd9Sstevel@tonic-gate 			}
2846*7c478bd9Sstevel@tonic-gate 		}
2847*7c478bd9Sstevel@tonic-gate 	}
2848*7c478bd9Sstevel@tonic-gate 
2849*7c478bd9Sstevel@tonic-gate 	/* if not found as user-provided header at index, use sysheader */
2850*7c478bd9Sstevel@tonic-gate 	if (h == NULL)
2851*7c478bd9Sstevel@tonic-gate 		h = sysheader;
2852*7c478bd9Sstevel@tonic-gate 
2853*7c478bd9Sstevel@tonic-gate 	if (h == NULL)
2854*7c478bd9Sstevel@tonic-gate 	{
2855*7c478bd9Sstevel@tonic-gate 		if (*val == '\0')
2856*7c478bd9Sstevel@tonic-gate 		{
2857*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 10))
2858*7c478bd9Sstevel@tonic-gate 				sm_dprintf("Delete (noop) %s\n", field);
2859*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 8)
2860*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
2861*7c478bd9Sstevel@tonic-gate 					"Milter delete (noop): header: %s"
2862*7c478bd9Sstevel@tonic-gate 					, field);
2863*7c478bd9Sstevel@tonic-gate 		}
2864*7c478bd9Sstevel@tonic-gate 		else
2865*7c478bd9Sstevel@tonic-gate 		{
2866*7c478bd9Sstevel@tonic-gate 			/* treat modify value with no existing header as add */
2867*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 10))
2868*7c478bd9Sstevel@tonic-gate 				sm_dprintf("Add %s: %s\n", field, val);
2869*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 8)
2870*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
2871*7c478bd9Sstevel@tonic-gate 					"Milter change (add): header: %s: %s"
2872*7c478bd9Sstevel@tonic-gate 					, field, val);
2873*7c478bd9Sstevel@tonic-gate 			addheader(newstr(field), val, H_USER, e);
2874*7c478bd9Sstevel@tonic-gate 		}
2875*7c478bd9Sstevel@tonic-gate 		return;
2876*7c478bd9Sstevel@tonic-gate 	}
2877*7c478bd9Sstevel@tonic-gate 
2878*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2879*7c478bd9Sstevel@tonic-gate 	{
2880*7c478bd9Sstevel@tonic-gate 		if (*val == '\0')
2881*7c478bd9Sstevel@tonic-gate 		{
2882*7c478bd9Sstevel@tonic-gate 			sm_dprintf("Delete%s %s: %s\n",
2883*7c478bd9Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
2884*7c478bd9Sstevel@tonic-gate 				   field,
2885*7c478bd9Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value);
2886*7c478bd9Sstevel@tonic-gate 		}
2887*7c478bd9Sstevel@tonic-gate 		else
2888*7c478bd9Sstevel@tonic-gate 		{
2889*7c478bd9Sstevel@tonic-gate 			sm_dprintf("Change%s %s: from %s to %s\n",
2890*7c478bd9Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
2891*7c478bd9Sstevel@tonic-gate 				   field,
2892*7c478bd9Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value,
2893*7c478bd9Sstevel@tonic-gate 				   val);
2894*7c478bd9Sstevel@tonic-gate 		}
2895*7c478bd9Sstevel@tonic-gate 	}
2896*7c478bd9Sstevel@tonic-gate 
2897*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 8)
2898*7c478bd9Sstevel@tonic-gate 	{
2899*7c478bd9Sstevel@tonic-gate 		if (*val == '\0')
2900*7c478bd9Sstevel@tonic-gate 		{
2901*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
2902*7c478bd9Sstevel@tonic-gate 				  "Milter delete: header%s %s: %s",
2903*7c478bd9Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
2904*7c478bd9Sstevel@tonic-gate 				  field,
2905*7c478bd9Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value);
2906*7c478bd9Sstevel@tonic-gate 		}
2907*7c478bd9Sstevel@tonic-gate 		else
2908*7c478bd9Sstevel@tonic-gate 		{
2909*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
2910*7c478bd9Sstevel@tonic-gate 				  "Milter change: header%s %s: from %s to %s",
2911*7c478bd9Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
2912*7c478bd9Sstevel@tonic-gate 				  field,
2913*7c478bd9Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value,
2914*7c478bd9Sstevel@tonic-gate 				  val);
2915*7c478bd9Sstevel@tonic-gate 		}
2916*7c478bd9Sstevel@tonic-gate 	}
2917*7c478bd9Sstevel@tonic-gate 
2918*7c478bd9Sstevel@tonic-gate 	if (h != sysheader && h->h_value != NULL)
2919*7c478bd9Sstevel@tonic-gate 	{
2920*7c478bd9Sstevel@tonic-gate 		size_t l;
2921*7c478bd9Sstevel@tonic-gate 
2922*7c478bd9Sstevel@tonic-gate 		l = strlen(h->h_value);
2923*7c478bd9Sstevel@tonic-gate 		if (l > e->e_msgsize)
2924*7c478bd9Sstevel@tonic-gate 			e->e_msgsize = 0;
2925*7c478bd9Sstevel@tonic-gate 		else
2926*7c478bd9Sstevel@tonic-gate 			e->e_msgsize -= l;
2927*7c478bd9Sstevel@tonic-gate 		/* rpool, don't free: sm_free(h->h_value); XXX */
2928*7c478bd9Sstevel@tonic-gate 	}
2929*7c478bd9Sstevel@tonic-gate 
2930*7c478bd9Sstevel@tonic-gate 	if (*val == '\0')
2931*7c478bd9Sstevel@tonic-gate 	{
2932*7c478bd9Sstevel@tonic-gate 		/* Remove "Field: " from message size */
2933*7c478bd9Sstevel@tonic-gate 		if (h != sysheader)
2934*7c478bd9Sstevel@tonic-gate 		{
2935*7c478bd9Sstevel@tonic-gate 			size_t l;
2936*7c478bd9Sstevel@tonic-gate 
2937*7c478bd9Sstevel@tonic-gate 			l = strlen(h->h_field) + 2;
2938*7c478bd9Sstevel@tonic-gate 			if (l > e->e_msgsize)
2939*7c478bd9Sstevel@tonic-gate 				e->e_msgsize = 0;
2940*7c478bd9Sstevel@tonic-gate 			else
2941*7c478bd9Sstevel@tonic-gate 				e->e_msgsize -= l;
2942*7c478bd9Sstevel@tonic-gate 		}
2943*7c478bd9Sstevel@tonic-gate 		h->h_value = NULL;
2944*7c478bd9Sstevel@tonic-gate 	}
2945*7c478bd9Sstevel@tonic-gate 	else
2946*7c478bd9Sstevel@tonic-gate 	{
2947*7c478bd9Sstevel@tonic-gate 		h->h_value = newstr(val);
2948*7c478bd9Sstevel@tonic-gate 		h->h_flags |= H_USER;
2949*7c478bd9Sstevel@tonic-gate 		e->e_msgsize += strlen(h->h_value);
2950*7c478bd9Sstevel@tonic-gate 	}
2951*7c478bd9Sstevel@tonic-gate }
2952*7c478bd9Sstevel@tonic-gate /*
2953*7c478bd9Sstevel@tonic-gate **  MILTER_ADDRCPT -- Add the supplied recipient to the message
2954*7c478bd9Sstevel@tonic-gate **
2955*7c478bd9Sstevel@tonic-gate **	Parameters:
2956*7c478bd9Sstevel@tonic-gate **		response -- encoded form of recipient address.
2957*7c478bd9Sstevel@tonic-gate **		rlen -- length of response.
2958*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
2959*7c478bd9Sstevel@tonic-gate **
2960*7c478bd9Sstevel@tonic-gate **	Returns:
2961*7c478bd9Sstevel@tonic-gate **		none
2962*7c478bd9Sstevel@tonic-gate */
2963*7c478bd9Sstevel@tonic-gate 
2964*7c478bd9Sstevel@tonic-gate static void
2965*7c478bd9Sstevel@tonic-gate milter_addrcpt(response, rlen, e)
2966*7c478bd9Sstevel@tonic-gate 	char *response;
2967*7c478bd9Sstevel@tonic-gate 	ssize_t rlen;
2968*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2969*7c478bd9Sstevel@tonic-gate {
2970*7c478bd9Sstevel@tonic-gate 	int olderrors;
2971*7c478bd9Sstevel@tonic-gate 
2972*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2973*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_addrcpt: ");
2974*7c478bd9Sstevel@tonic-gate 
2975*7c478bd9Sstevel@tonic-gate 	/* sanity checks */
2976*7c478bd9Sstevel@tonic-gate 	if (response == NULL)
2977*7c478bd9Sstevel@tonic-gate 	{
2978*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2979*7c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
2980*7c478bd9Sstevel@tonic-gate 		return;
2981*7c478bd9Sstevel@tonic-gate 	}
2982*7c478bd9Sstevel@tonic-gate 
2983*7c478bd9Sstevel@tonic-gate 	if (*response == '\0' ||
2984*7c478bd9Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
2985*7c478bd9Sstevel@tonic-gate 	{
2986*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2987*7c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
2988*7c478bd9Sstevel@tonic-gate 				   (int) strlen(response), (int) (rlen - 1));
2989*7c478bd9Sstevel@tonic-gate 		return;
2990*7c478bd9Sstevel@tonic-gate 	}
2991*7c478bd9Sstevel@tonic-gate 
2992*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
2993*7c478bd9Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
2994*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 8)
2995*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
2996*7c478bd9Sstevel@tonic-gate 	olderrors = Errors;
2997*7c478bd9Sstevel@tonic-gate 	(void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
2998*7c478bd9Sstevel@tonic-gate 	Errors = olderrors;
2999*7c478bd9Sstevel@tonic-gate 	return;
3000*7c478bd9Sstevel@tonic-gate }
3001*7c478bd9Sstevel@tonic-gate /*
3002*7c478bd9Sstevel@tonic-gate **  MILTER_DELRCPT -- Delete the supplied recipient from the message
3003*7c478bd9Sstevel@tonic-gate **
3004*7c478bd9Sstevel@tonic-gate **	Parameters:
3005*7c478bd9Sstevel@tonic-gate **		response -- encoded form of recipient address.
3006*7c478bd9Sstevel@tonic-gate **		rlen -- length of response.
3007*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
3008*7c478bd9Sstevel@tonic-gate **
3009*7c478bd9Sstevel@tonic-gate **	Returns:
3010*7c478bd9Sstevel@tonic-gate **		none
3011*7c478bd9Sstevel@tonic-gate */
3012*7c478bd9Sstevel@tonic-gate 
3013*7c478bd9Sstevel@tonic-gate static void
3014*7c478bd9Sstevel@tonic-gate milter_delrcpt(response, rlen, e)
3015*7c478bd9Sstevel@tonic-gate 	char *response;
3016*7c478bd9Sstevel@tonic-gate 	ssize_t rlen;
3017*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3018*7c478bd9Sstevel@tonic-gate {
3019*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3020*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_delrcpt: ");
3021*7c478bd9Sstevel@tonic-gate 
3022*7c478bd9Sstevel@tonic-gate 	/* sanity checks */
3023*7c478bd9Sstevel@tonic-gate 	if (response == NULL)
3024*7c478bd9Sstevel@tonic-gate 	{
3025*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
3026*7c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
3027*7c478bd9Sstevel@tonic-gate 		return;
3028*7c478bd9Sstevel@tonic-gate 	}
3029*7c478bd9Sstevel@tonic-gate 
3030*7c478bd9Sstevel@tonic-gate 	if (*response == '\0' ||
3031*7c478bd9Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
3032*7c478bd9Sstevel@tonic-gate 	{
3033*7c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
3034*7c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
3035*7c478bd9Sstevel@tonic-gate 		return;
3036*7c478bd9Sstevel@tonic-gate 	}
3037*7c478bd9Sstevel@tonic-gate 
3038*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3039*7c478bd9Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
3040*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 8)
3041*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s",
3042*7c478bd9Sstevel@tonic-gate 			  response);
3043*7c478bd9Sstevel@tonic-gate 	(void) removefromlist(response, &e->e_sendqueue, e);
3044*7c478bd9Sstevel@tonic-gate 	return;
3045*7c478bd9Sstevel@tonic-gate }
3046*7c478bd9Sstevel@tonic-gate /*
3047*7c478bd9Sstevel@tonic-gate **  MILTER_REPLBODY -- Replace the current data file with new body
3048*7c478bd9Sstevel@tonic-gate **
3049*7c478bd9Sstevel@tonic-gate **	Parameters:
3050*7c478bd9Sstevel@tonic-gate **		response -- encoded form of new body.
3051*7c478bd9Sstevel@tonic-gate **		rlen -- length of response.
3052*7c478bd9Sstevel@tonic-gate **		newfilter -- if first time called by a new filter
3053*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
3054*7c478bd9Sstevel@tonic-gate **
3055*7c478bd9Sstevel@tonic-gate **	Returns:
3056*7c478bd9Sstevel@tonic-gate **		0 upon success, -1 upon failure
3057*7c478bd9Sstevel@tonic-gate */
3058*7c478bd9Sstevel@tonic-gate 
3059*7c478bd9Sstevel@tonic-gate static int
3060*7c478bd9Sstevel@tonic-gate milter_replbody(response, rlen, newfilter, e)
3061*7c478bd9Sstevel@tonic-gate 	char *response;
3062*7c478bd9Sstevel@tonic-gate 	ssize_t rlen;
3063*7c478bd9Sstevel@tonic-gate 	bool newfilter;
3064*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3065*7c478bd9Sstevel@tonic-gate {
3066*7c478bd9Sstevel@tonic-gate 	static char prevchar;
3067*7c478bd9Sstevel@tonic-gate 	int i;
3068*7c478bd9Sstevel@tonic-gate 
3069*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3070*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_replbody\n");
3071*7c478bd9Sstevel@tonic-gate 
3072*7c478bd9Sstevel@tonic-gate 	/* If a new filter, reset previous character and truncate data file */
3073*7c478bd9Sstevel@tonic-gate 	if (newfilter)
3074*7c478bd9Sstevel@tonic-gate 	{
3075*7c478bd9Sstevel@tonic-gate 		off_t prevsize;
3076*7c478bd9Sstevel@tonic-gate 		char dfname[MAXPATHLEN];
3077*7c478bd9Sstevel@tonic-gate 
3078*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER),
3079*7c478bd9Sstevel@tonic-gate 				  sizeof dfname);
3080*7c478bd9Sstevel@tonic-gate 
3081*7c478bd9Sstevel@tonic-gate 		/* Reset prevchar */
3082*7c478bd9Sstevel@tonic-gate 		prevchar = '\0';
3083*7c478bd9Sstevel@tonic-gate 
3084*7c478bd9Sstevel@tonic-gate 		/* Get the current data file information */
3085*7c478bd9Sstevel@tonic-gate 		prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL);
3086*7c478bd9Sstevel@tonic-gate 		if (prevsize < 0)
3087*7c478bd9Sstevel@tonic-gate 			prevsize = 0;
3088*7c478bd9Sstevel@tonic-gate 
3089*7c478bd9Sstevel@tonic-gate 		/* truncate current data file */
3090*7c478bd9Sstevel@tonic-gate 		if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
3091*7c478bd9Sstevel@tonic-gate 		{
3092*7c478bd9Sstevel@tonic-gate 			if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0)
3093*7c478bd9Sstevel@tonic-gate 			{
3094*7c478bd9Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s");
3095*7c478bd9Sstevel@tonic-gate 				return -1;
3096*7c478bd9Sstevel@tonic-gate 			}
3097*7c478bd9Sstevel@tonic-gate 		}
3098*7c478bd9Sstevel@tonic-gate 		else
3099*7c478bd9Sstevel@tonic-gate 		{
3100*7c478bd9Sstevel@tonic-gate 			int err;
3101*7c478bd9Sstevel@tonic-gate 
3102*7c478bd9Sstevel@tonic-gate 			err = sm_io_error(e->e_dfp);
3103*7c478bd9Sstevel@tonic-gate 			(void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT);
3104*7c478bd9Sstevel@tonic-gate 
3105*7c478bd9Sstevel@tonic-gate 			/*
3106*7c478bd9Sstevel@tonic-gate 			**  Clear error if tried to fflush()
3107*7c478bd9Sstevel@tonic-gate 			**  a read-only file pointer and
3108*7c478bd9Sstevel@tonic-gate 			**  there wasn't a previous error.
3109*7c478bd9Sstevel@tonic-gate 			*/
3110*7c478bd9Sstevel@tonic-gate 
3111*7c478bd9Sstevel@tonic-gate 			if (err == 0)
3112*7c478bd9Sstevel@tonic-gate 				sm_io_clearerr(e->e_dfp);
3113*7c478bd9Sstevel@tonic-gate 
3114*7c478bd9Sstevel@tonic-gate 			/* errno is set implicitly by fseek() before return */
3115*7c478bd9Sstevel@tonic-gate 			err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT,
3116*7c478bd9Sstevel@tonic-gate 					 0, SEEK_SET);
3117*7c478bd9Sstevel@tonic-gate 			if (err < 0)
3118*7c478bd9Sstevel@tonic-gate 			{
3119*7c478bd9Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s");
3120*7c478bd9Sstevel@tonic-gate 				return -1;
3121*7c478bd9Sstevel@tonic-gate 			}
3122*7c478bd9Sstevel@tonic-gate # if NOFTRUNCATE
3123*7c478bd9Sstevel@tonic-gate 			/* XXX: Not much we can do except rewind it */
3124*7c478bd9Sstevel@tonic-gate 			errno = EINVAL;
3125*7c478bd9Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)");
3126*7c478bd9Sstevel@tonic-gate 			return -1;
3127*7c478bd9Sstevel@tonic-gate # else /* NOFTRUNCATE */
3128*7c478bd9Sstevel@tonic-gate 			err = ftruncate(sm_io_getinfo(e->e_dfp,
3129*7c478bd9Sstevel@tonic-gate 						      SM_IO_WHAT_FD, NULL),
3130*7c478bd9Sstevel@tonic-gate 					0);
3131*7c478bd9Sstevel@tonic-gate 			if (err < 0)
3132*7c478bd9Sstevel@tonic-gate 			{
3133*7c478bd9Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s");
3134*7c478bd9Sstevel@tonic-gate 				return -1;
3135*7c478bd9Sstevel@tonic-gate 			}
3136*7c478bd9Sstevel@tonic-gate # endif /* NOFTRUNCATE */
3137*7c478bd9Sstevel@tonic-gate 		}
3138*7c478bd9Sstevel@tonic-gate 
3139*7c478bd9Sstevel@tonic-gate 		if (prevsize > e->e_msgsize)
3140*7c478bd9Sstevel@tonic-gate 			e->e_msgsize = 0;
3141*7c478bd9Sstevel@tonic-gate 		else
3142*7c478bd9Sstevel@tonic-gate 			e->e_msgsize -= prevsize;
3143*7c478bd9Sstevel@tonic-gate 	}
3144*7c478bd9Sstevel@tonic-gate 
3145*7c478bd9Sstevel@tonic-gate 	if (newfilter && MilterLogLevel > 8)
3146*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced");
3147*7c478bd9Sstevel@tonic-gate 
3148*7c478bd9Sstevel@tonic-gate 	if (response == NULL)
3149*7c478bd9Sstevel@tonic-gate 	{
3150*7c478bd9Sstevel@tonic-gate 		/* Flush the buffered '\r' */
3151*7c478bd9Sstevel@tonic-gate 		if (prevchar == '\r')
3152*7c478bd9Sstevel@tonic-gate 		{
3153*7c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar);
3154*7c478bd9Sstevel@tonic-gate 			e->e_msgsize++;
3155*7c478bd9Sstevel@tonic-gate 		}
3156*7c478bd9Sstevel@tonic-gate 		return 0;
3157*7c478bd9Sstevel@tonic-gate 	}
3158*7c478bd9Sstevel@tonic-gate 
3159*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < rlen; i++)
3160*7c478bd9Sstevel@tonic-gate 	{
3161*7c478bd9Sstevel@tonic-gate 		/* Buffered char from last chunk */
3162*7c478bd9Sstevel@tonic-gate 		if (i == 0 && prevchar == '\r')
3163*7c478bd9Sstevel@tonic-gate 		{
3164*7c478bd9Sstevel@tonic-gate 			/* Not CRLF, output prevchar */
3165*7c478bd9Sstevel@tonic-gate 			if (response[i] != '\n')
3166*7c478bd9Sstevel@tonic-gate 			{
3167*7c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT,
3168*7c478bd9Sstevel@tonic-gate 						  prevchar);
3169*7c478bd9Sstevel@tonic-gate 				e->e_msgsize++;
3170*7c478bd9Sstevel@tonic-gate 			}
3171*7c478bd9Sstevel@tonic-gate 			prevchar = '\0';
3172*7c478bd9Sstevel@tonic-gate 		}
3173*7c478bd9Sstevel@tonic-gate 
3174*7c478bd9Sstevel@tonic-gate 		/* Turn CRLF into LF */
3175*7c478bd9Sstevel@tonic-gate 		if (response[i] == '\r')
3176*7c478bd9Sstevel@tonic-gate 		{
3177*7c478bd9Sstevel@tonic-gate 			/* check if at end of chunk */
3178*7c478bd9Sstevel@tonic-gate 			if (i + 1 < rlen)
3179*7c478bd9Sstevel@tonic-gate 			{
3180*7c478bd9Sstevel@tonic-gate 				/* If LF, strip CR */
3181*7c478bd9Sstevel@tonic-gate 				if (response[i + 1] == '\n')
3182*7c478bd9Sstevel@tonic-gate 					i++;
3183*7c478bd9Sstevel@tonic-gate 			}
3184*7c478bd9Sstevel@tonic-gate 			else
3185*7c478bd9Sstevel@tonic-gate 			{
3186*7c478bd9Sstevel@tonic-gate 				/* check next chunk */
3187*7c478bd9Sstevel@tonic-gate 				prevchar = '\r';
3188*7c478bd9Sstevel@tonic-gate 				continue;
3189*7c478bd9Sstevel@tonic-gate 			}
3190*7c478bd9Sstevel@tonic-gate 		}
3191*7c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]);
3192*7c478bd9Sstevel@tonic-gate 		e->e_msgsize++;
3193*7c478bd9Sstevel@tonic-gate 	}
3194*7c478bd9Sstevel@tonic-gate 	return 0;
3195*7c478bd9Sstevel@tonic-gate }
3196*7c478bd9Sstevel@tonic-gate 
3197*7c478bd9Sstevel@tonic-gate /*
3198*7c478bd9Sstevel@tonic-gate **  MTA callouts
3199*7c478bd9Sstevel@tonic-gate */
3200*7c478bd9Sstevel@tonic-gate 
3201*7c478bd9Sstevel@tonic-gate /*
3202*7c478bd9Sstevel@tonic-gate **  MILTER_INIT -- open and negotiate with all of the filters
3203*7c478bd9Sstevel@tonic-gate **
3204*7c478bd9Sstevel@tonic-gate **	Parameters:
3205*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
3206*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
3207*7c478bd9Sstevel@tonic-gate **
3208*7c478bd9Sstevel@tonic-gate **	Returns:
3209*7c478bd9Sstevel@tonic-gate **		true iff at least one filter is active
3210*7c478bd9Sstevel@tonic-gate */
3211*7c478bd9Sstevel@tonic-gate 
3212*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
3213*7c478bd9Sstevel@tonic-gate bool
3214*7c478bd9Sstevel@tonic-gate milter_init(e, state)
3215*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3216*7c478bd9Sstevel@tonic-gate 	char *state;
3217*7c478bd9Sstevel@tonic-gate {
3218*7c478bd9Sstevel@tonic-gate 	int i;
3219*7c478bd9Sstevel@tonic-gate 
3220*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3221*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_init\n");
3222*7c478bd9Sstevel@tonic-gate 
3223*7c478bd9Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
3224*7c478bd9Sstevel@tonic-gate 	if (InputFilters[0] == NULL)
3225*7c478bd9Sstevel@tonic-gate 	{
3226*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
3227*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
3228*7c478bd9Sstevel@tonic-gate 				  "Milter: no active filter");
3229*7c478bd9Sstevel@tonic-gate 		return false;
3230*7c478bd9Sstevel@tonic-gate 	}
3231*7c478bd9Sstevel@tonic-gate 
3232*7c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
3233*7c478bd9Sstevel@tonic-gate 	{
3234*7c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
3235*7c478bd9Sstevel@tonic-gate 
3236*7c478bd9Sstevel@tonic-gate 		m->mf_sock = milter_open(m, false, e);
3237*7c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
3238*7c478bd9Sstevel@tonic-gate 		{
3239*7c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
3240*7c478bd9Sstevel@tonic-gate 			break;
3241*7c478bd9Sstevel@tonic-gate 		}
3242*7c478bd9Sstevel@tonic-gate 
3243*7c478bd9Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
3244*7c478bd9Sstevel@tonic-gate 		    milter_negotiate(m, e) < 0 ||
3245*7c478bd9Sstevel@tonic-gate 		    m->mf_state == SMFS_ERROR)
3246*7c478bd9Sstevel@tonic-gate 		{
3247*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
3248*7c478bd9Sstevel@tonic-gate 				sm_dprintf("milter_init(%s): failed to %s\n",
3249*7c478bd9Sstevel@tonic-gate 					   m->mf_name,
3250*7c478bd9Sstevel@tonic-gate 					   m->mf_sock < 0 ? "open" :
3251*7c478bd9Sstevel@tonic-gate 							    "negotiate");
3252*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
3253*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
3254*7c478bd9Sstevel@tonic-gate 					  "Milter (%s): init failed to %s",
3255*7c478bd9Sstevel@tonic-gate 					  m->mf_name,
3256*7c478bd9Sstevel@tonic-gate 					  m->mf_sock < 0 ? "open" :
3257*7c478bd9Sstevel@tonic-gate 							   "negotiate");
3258*7c478bd9Sstevel@tonic-gate 
3259*7c478bd9Sstevel@tonic-gate 			/* if negotation failure, close socket */
3260*7c478bd9Sstevel@tonic-gate 			milter_error(m, e);
3261*7c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
3262*7c478bd9Sstevel@tonic-gate 			continue;
3263*7c478bd9Sstevel@tonic-gate 		}
3264*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 9)
3265*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
3266*7c478bd9Sstevel@tonic-gate 				  "Milter (%s): init success to %s",
3267*7c478bd9Sstevel@tonic-gate 				  m->mf_name,
3268*7c478bd9Sstevel@tonic-gate 				  m->mf_sock < 0 ? "open" : "negotiate");
3269*7c478bd9Sstevel@tonic-gate 	}
3270*7c478bd9Sstevel@tonic-gate 
3271*7c478bd9Sstevel@tonic-gate 	/*
3272*7c478bd9Sstevel@tonic-gate 	**  If something temp/perm failed with one of the filters,
3273*7c478bd9Sstevel@tonic-gate 	**  we won't be using any of them, so clear any existing
3274*7c478bd9Sstevel@tonic-gate 	**  connections.
3275*7c478bd9Sstevel@tonic-gate 	*/
3276*7c478bd9Sstevel@tonic-gate 
3277*7c478bd9Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
3278*7c478bd9Sstevel@tonic-gate 		milter_quit(e);
3279*7c478bd9Sstevel@tonic-gate 
3280*7c478bd9Sstevel@tonic-gate 	return true;
3281*7c478bd9Sstevel@tonic-gate }
3282*7c478bd9Sstevel@tonic-gate /*
3283*7c478bd9Sstevel@tonic-gate **  MILTER_CONNECT -- send connection info to milter filters
3284*7c478bd9Sstevel@tonic-gate **
3285*7c478bd9Sstevel@tonic-gate **	Parameters:
3286*7c478bd9Sstevel@tonic-gate **		hostname -- hostname of remote machine.
3287*7c478bd9Sstevel@tonic-gate **		addr -- address of remote machine.
3288*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
3289*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
3290*7c478bd9Sstevel@tonic-gate **
3291*7c478bd9Sstevel@tonic-gate **	Returns:
3292*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
3293*7c478bd9Sstevel@tonic-gate */
3294*7c478bd9Sstevel@tonic-gate 
3295*7c478bd9Sstevel@tonic-gate char *
3296*7c478bd9Sstevel@tonic-gate milter_connect(hostname, addr, e, state)
3297*7c478bd9Sstevel@tonic-gate 	char *hostname;
3298*7c478bd9Sstevel@tonic-gate 	SOCKADDR addr;
3299*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3300*7c478bd9Sstevel@tonic-gate 	char *state;
3301*7c478bd9Sstevel@tonic-gate {
3302*7c478bd9Sstevel@tonic-gate 	char family;
3303*7c478bd9Sstevel@tonic-gate 	unsigned short port;
3304*7c478bd9Sstevel@tonic-gate 	char *buf, *bp;
3305*7c478bd9Sstevel@tonic-gate 	char *response;
3306*7c478bd9Sstevel@tonic-gate 	char *sockinfo = NULL;
3307*7c478bd9Sstevel@tonic-gate 	ssize_t s;
3308*7c478bd9Sstevel@tonic-gate # if NETINET6
3309*7c478bd9Sstevel@tonic-gate 	char buf6[INET6_ADDRSTRLEN];
3310*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
3311*7c478bd9Sstevel@tonic-gate 
3312*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3313*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_connect(%s)\n", hostname);
3314*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 9)
3315*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters");
3316*7c478bd9Sstevel@tonic-gate 
3317*7c478bd9Sstevel@tonic-gate 	/* gather data */
3318*7c478bd9Sstevel@tonic-gate 	switch (addr.sa.sa_family)
3319*7c478bd9Sstevel@tonic-gate 	{
3320*7c478bd9Sstevel@tonic-gate # if NETUNIX
3321*7c478bd9Sstevel@tonic-gate 	  case AF_UNIX:
3322*7c478bd9Sstevel@tonic-gate 		family = SMFIA_UNIX;
3323*7c478bd9Sstevel@tonic-gate 		port = htons(0);
3324*7c478bd9Sstevel@tonic-gate 		sockinfo = addr.sunix.sun_path;
3325*7c478bd9Sstevel@tonic-gate 		break;
3326*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
3327*7c478bd9Sstevel@tonic-gate 
3328*7c478bd9Sstevel@tonic-gate # if NETINET
3329*7c478bd9Sstevel@tonic-gate 	  case AF_INET:
3330*7c478bd9Sstevel@tonic-gate 		family = SMFIA_INET;
3331*7c478bd9Sstevel@tonic-gate 		port = addr.sin.sin_port;
3332*7c478bd9Sstevel@tonic-gate 		sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
3333*7c478bd9Sstevel@tonic-gate 		break;
3334*7c478bd9Sstevel@tonic-gate # endif /* NETINET */
3335*7c478bd9Sstevel@tonic-gate 
3336*7c478bd9Sstevel@tonic-gate # if NETINET6
3337*7c478bd9Sstevel@tonic-gate 	  case AF_INET6:
3338*7c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr))
3339*7c478bd9Sstevel@tonic-gate 			family = SMFIA_INET;
3340*7c478bd9Sstevel@tonic-gate 		else
3341*7c478bd9Sstevel@tonic-gate 			family = SMFIA_INET6;
3342*7c478bd9Sstevel@tonic-gate 		port = addr.sin6.sin6_port;
3343*7c478bd9Sstevel@tonic-gate 		sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
3344*7c478bd9Sstevel@tonic-gate 				       sizeof buf6);
3345*7c478bd9Sstevel@tonic-gate 		if (sockinfo == NULL)
3346*7c478bd9Sstevel@tonic-gate 			sockinfo = "";
3347*7c478bd9Sstevel@tonic-gate 		break;
3348*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
3349*7c478bd9Sstevel@tonic-gate 
3350*7c478bd9Sstevel@tonic-gate 	  default:
3351*7c478bd9Sstevel@tonic-gate 		family = SMFIA_UNKNOWN;
3352*7c478bd9Sstevel@tonic-gate 		break;
3353*7c478bd9Sstevel@tonic-gate 	}
3354*7c478bd9Sstevel@tonic-gate 
3355*7c478bd9Sstevel@tonic-gate 	s = strlen(hostname) + 1 + sizeof(family);
3356*7c478bd9Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
3357*7c478bd9Sstevel@tonic-gate 		s += sizeof(port) + strlen(sockinfo) + 1;
3358*7c478bd9Sstevel@tonic-gate 
3359*7c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(s);
3360*7c478bd9Sstevel@tonic-gate 	bp = buf;
3361*7c478bd9Sstevel@tonic-gate 
3362*7c478bd9Sstevel@tonic-gate 	/* put together data */
3363*7c478bd9Sstevel@tonic-gate 	(void) memcpy(bp, hostname, strlen(hostname));
3364*7c478bd9Sstevel@tonic-gate 	bp += strlen(hostname);
3365*7c478bd9Sstevel@tonic-gate 	*bp++ = '\0';
3366*7c478bd9Sstevel@tonic-gate 	(void) memcpy(bp, &family, sizeof family);
3367*7c478bd9Sstevel@tonic-gate 	bp += sizeof family;
3368*7c478bd9Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
3369*7c478bd9Sstevel@tonic-gate 	{
3370*7c478bd9Sstevel@tonic-gate 		(void) memcpy(bp, &port, sizeof port);
3371*7c478bd9Sstevel@tonic-gate 		bp += sizeof port;
3372*7c478bd9Sstevel@tonic-gate 
3373*7c478bd9Sstevel@tonic-gate 		/* include trailing '\0' */
3374*7c478bd9Sstevel@tonic-gate 		(void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
3375*7c478bd9Sstevel@tonic-gate 	}
3376*7c478bd9Sstevel@tonic-gate 
3377*7c478bd9Sstevel@tonic-gate 	response = milter_command(SMFIC_CONNECT, buf, s,
3378*7c478bd9Sstevel@tonic-gate 				  MilterConnectMacros, e, state);
3379*7c478bd9Sstevel@tonic-gate 	sm_free(buf); /* XXX */
3380*7c478bd9Sstevel@tonic-gate 
3381*7c478bd9Sstevel@tonic-gate 	/*
3382*7c478bd9Sstevel@tonic-gate 	**  If this message connection is done for,
3383*7c478bd9Sstevel@tonic-gate 	**  close the filters.
3384*7c478bd9Sstevel@tonic-gate 	*/
3385*7c478bd9Sstevel@tonic-gate 
3386*7c478bd9Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
3387*7c478bd9Sstevel@tonic-gate 	{
3388*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 9)
3389*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending");
3390*7c478bd9Sstevel@tonic-gate 		milter_quit(e);
3391*7c478bd9Sstevel@tonic-gate 	}
3392*7c478bd9Sstevel@tonic-gate 	else
3393*7c478bd9Sstevel@tonic-gate 		milter_per_connection_check(e);
3394*7c478bd9Sstevel@tonic-gate 
3395*7c478bd9Sstevel@tonic-gate 	/*
3396*7c478bd9Sstevel@tonic-gate 	**  SMFIR_REPLYCODE can't work with connect due to
3397*7c478bd9Sstevel@tonic-gate 	**  the requirements of SMTP.  Therefore, ignore the
3398*7c478bd9Sstevel@tonic-gate 	**  reply code text but keep the state it would reflect.
3399*7c478bd9Sstevel@tonic-gate 	*/
3400*7c478bd9Sstevel@tonic-gate 
3401*7c478bd9Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE)
3402*7c478bd9Sstevel@tonic-gate 	{
3403*7c478bd9Sstevel@tonic-gate 		if (response != NULL &&
3404*7c478bd9Sstevel@tonic-gate 		    *response == '4')
3405*7c478bd9Sstevel@tonic-gate 		{
3406*7c478bd9Sstevel@tonic-gate 			if (strncmp(response, "421 ", 4) == 0)
3407*7c478bd9Sstevel@tonic-gate 				*state = SMFIR_SHUTDOWN;
3408*7c478bd9Sstevel@tonic-gate 			else
3409*7c478bd9Sstevel@tonic-gate 				*state = SMFIR_TEMPFAIL;
3410*7c478bd9Sstevel@tonic-gate 		}
3411*7c478bd9Sstevel@tonic-gate 		else
3412*7c478bd9Sstevel@tonic-gate 			*state = SMFIR_REJECT;
3413*7c478bd9Sstevel@tonic-gate 		if (response != NULL)
3414*7c478bd9Sstevel@tonic-gate 		{
3415*7c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
3416*7c478bd9Sstevel@tonic-gate 			response = NULL;
3417*7c478bd9Sstevel@tonic-gate 		}
3418*7c478bd9Sstevel@tonic-gate 	}
3419*7c478bd9Sstevel@tonic-gate 	return response;
3420*7c478bd9Sstevel@tonic-gate }
3421*7c478bd9Sstevel@tonic-gate /*
3422*7c478bd9Sstevel@tonic-gate **  MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
3423*7c478bd9Sstevel@tonic-gate **
3424*7c478bd9Sstevel@tonic-gate **	Parameters:
3425*7c478bd9Sstevel@tonic-gate **		helo -- argument to SMTP HELO/EHLO command.
3426*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
3427*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
3428*7c478bd9Sstevel@tonic-gate **
3429*7c478bd9Sstevel@tonic-gate **	Returns:
3430*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
3431*7c478bd9Sstevel@tonic-gate */
3432*7c478bd9Sstevel@tonic-gate 
3433*7c478bd9Sstevel@tonic-gate char *
3434*7c478bd9Sstevel@tonic-gate milter_helo(helo, e, state)
3435*7c478bd9Sstevel@tonic-gate 	char *helo;
3436*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3437*7c478bd9Sstevel@tonic-gate 	char *state;
3438*7c478bd9Sstevel@tonic-gate {
3439*7c478bd9Sstevel@tonic-gate 	int i;
3440*7c478bd9Sstevel@tonic-gate 	char *response;
3441*7c478bd9Sstevel@tonic-gate 
3442*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3443*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_helo(%s)\n", helo);
3444*7c478bd9Sstevel@tonic-gate 
3445*7c478bd9Sstevel@tonic-gate 	/* HELO/EHLO can come at any point */
3446*7c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
3447*7c478bd9Sstevel@tonic-gate 	{
3448*7c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
3449*7c478bd9Sstevel@tonic-gate 
3450*7c478bd9Sstevel@tonic-gate 		switch (m->mf_state)
3451*7c478bd9Sstevel@tonic-gate 		{
3452*7c478bd9Sstevel@tonic-gate 		  case SMFS_INMSG:
3453*7c478bd9Sstevel@tonic-gate 			/* abort in message filters */
3454*7c478bd9Sstevel@tonic-gate 			milter_abort_filter(m, e);
3455*7c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
3456*7c478bd9Sstevel@tonic-gate 
3457*7c478bd9Sstevel@tonic-gate 		  case SMFS_DONE:
3458*7c478bd9Sstevel@tonic-gate 			/* reset done filters */
3459*7c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
3460*7c478bd9Sstevel@tonic-gate 			break;
3461*7c478bd9Sstevel@tonic-gate 		}
3462*7c478bd9Sstevel@tonic-gate 	}
3463*7c478bd9Sstevel@tonic-gate 
3464*7c478bd9Sstevel@tonic-gate 	response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
3465*7c478bd9Sstevel@tonic-gate 				  MilterHeloMacros, e, state);
3466*7c478bd9Sstevel@tonic-gate 	milter_per_connection_check(e);
3467*7c478bd9Sstevel@tonic-gate 	return response;
3468*7c478bd9Sstevel@tonic-gate }
3469*7c478bd9Sstevel@tonic-gate /*
3470*7c478bd9Sstevel@tonic-gate **  MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
3471*7c478bd9Sstevel@tonic-gate **
3472*7c478bd9Sstevel@tonic-gate **	Parameters:
3473*7c478bd9Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == sender).
3474*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
3475*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
3476*7c478bd9Sstevel@tonic-gate **
3477*7c478bd9Sstevel@tonic-gate **	Returns:
3478*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
3479*7c478bd9Sstevel@tonic-gate */
3480*7c478bd9Sstevel@tonic-gate 
3481*7c478bd9Sstevel@tonic-gate char *
3482*7c478bd9Sstevel@tonic-gate milter_envfrom(args, e, state)
3483*7c478bd9Sstevel@tonic-gate 	char **args;
3484*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3485*7c478bd9Sstevel@tonic-gate 	char *state;
3486*7c478bd9Sstevel@tonic-gate {
3487*7c478bd9Sstevel@tonic-gate 	int i;
3488*7c478bd9Sstevel@tonic-gate 	char *buf, *bp;
3489*7c478bd9Sstevel@tonic-gate 	char *response;
3490*7c478bd9Sstevel@tonic-gate 	ssize_t s;
3491*7c478bd9Sstevel@tonic-gate 
3492*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3493*7c478bd9Sstevel@tonic-gate 	{
3494*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_envfrom:");
3495*7c478bd9Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
3496*7c478bd9Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
3497*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\n");
3498*7c478bd9Sstevel@tonic-gate 	}
3499*7c478bd9Sstevel@tonic-gate 
3500*7c478bd9Sstevel@tonic-gate 	/* sanity check */
3501*7c478bd9Sstevel@tonic-gate 	if (args[0] == NULL)
3502*7c478bd9Sstevel@tonic-gate 	{
3503*7c478bd9Sstevel@tonic-gate 		*state = SMFIR_REJECT;
3504*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
3505*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
3506*7c478bd9Sstevel@tonic-gate 				  "Milter: reject, no sender");
3507*7c478bd9Sstevel@tonic-gate 		return NULL;
3508*7c478bd9Sstevel@tonic-gate 	}
3509*7c478bd9Sstevel@tonic-gate 
3510*7c478bd9Sstevel@tonic-gate 	/* new message, so ... */
3511*7c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
3512*7c478bd9Sstevel@tonic-gate 	{
3513*7c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
3514*7c478bd9Sstevel@tonic-gate 
3515*7c478bd9Sstevel@tonic-gate 		switch (m->mf_state)
3516*7c478bd9Sstevel@tonic-gate 		{
3517*7c478bd9Sstevel@tonic-gate 		  case SMFS_INMSG:
3518*7c478bd9Sstevel@tonic-gate 			/* abort in message filters */
3519*7c478bd9Sstevel@tonic-gate 			milter_abort_filter(m, e);
3520*7c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
3521*7c478bd9Sstevel@tonic-gate 
3522*7c478bd9Sstevel@tonic-gate 		  case SMFS_DONE:
3523*7c478bd9Sstevel@tonic-gate 			/* reset done filters */
3524*7c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
3525*7c478bd9Sstevel@tonic-gate 			break;
3526*7c478bd9Sstevel@tonic-gate 		}
3527*7c478bd9Sstevel@tonic-gate 	}
3528*7c478bd9Sstevel@tonic-gate 
3529*7c478bd9Sstevel@tonic-gate 	/* put together data */
3530*7c478bd9Sstevel@tonic-gate 	s = 0;
3531*7c478bd9Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
3532*7c478bd9Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
3533*7c478bd9Sstevel@tonic-gate 
3534*7c478bd9Sstevel@tonic-gate 	if (s < 0)
3535*7c478bd9Sstevel@tonic-gate 	{
3536*7c478bd9Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
3537*7c478bd9Sstevel@tonic-gate 		return NULL;
3538*7c478bd9Sstevel@tonic-gate 	}
3539*7c478bd9Sstevel@tonic-gate 
3540*7c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(s);
3541*7c478bd9Sstevel@tonic-gate 	bp = buf;
3542*7c478bd9Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
3543*7c478bd9Sstevel@tonic-gate 	{
3544*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
3545*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp) + 1;
3546*7c478bd9Sstevel@tonic-gate 	}
3547*7c478bd9Sstevel@tonic-gate 
3548*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 14)
3549*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: senders: %s", buf);
3550*7c478bd9Sstevel@tonic-gate 
3551*7c478bd9Sstevel@tonic-gate 	/* send it over */
3552*7c478bd9Sstevel@tonic-gate 	response = milter_command(SMFIC_MAIL, buf, s,
3553*7c478bd9Sstevel@tonic-gate 				  MilterEnvFromMacros, e, state);
3554*7c478bd9Sstevel@tonic-gate 	sm_free(buf); /* XXX */
3555*7c478bd9Sstevel@tonic-gate 
3556*7c478bd9Sstevel@tonic-gate 	/*
3557*7c478bd9Sstevel@tonic-gate 	**  If filter rejects/discards a per message command,
3558*7c478bd9Sstevel@tonic-gate 	**  abort the other filters since we are done with the
3559*7c478bd9Sstevel@tonic-gate 	**  current message.
3560*7c478bd9Sstevel@tonic-gate 	*/
3561*7c478bd9Sstevel@tonic-gate 
3562*7c478bd9Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
3563*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
3564*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, senders");
3565*7c478bd9Sstevel@tonic-gate 	return response;
3566*7c478bd9Sstevel@tonic-gate }
3567*7c478bd9Sstevel@tonic-gate 
3568*7c478bd9Sstevel@tonic-gate /*
3569*7c478bd9Sstevel@tonic-gate **  MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
3570*7c478bd9Sstevel@tonic-gate **
3571*7c478bd9Sstevel@tonic-gate **	Parameters:
3572*7c478bd9Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == recipient).
3573*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
3574*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
3575*7c478bd9Sstevel@tonic-gate **
3576*7c478bd9Sstevel@tonic-gate **	Returns:
3577*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
3578*7c478bd9Sstevel@tonic-gate */
3579*7c478bd9Sstevel@tonic-gate 
3580*7c478bd9Sstevel@tonic-gate char *
3581*7c478bd9Sstevel@tonic-gate milter_envrcpt(args, e, state)
3582*7c478bd9Sstevel@tonic-gate 	char **args;
3583*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3584*7c478bd9Sstevel@tonic-gate 	char *state;
3585*7c478bd9Sstevel@tonic-gate {
3586*7c478bd9Sstevel@tonic-gate 	int i;
3587*7c478bd9Sstevel@tonic-gate 	char *buf, *bp;
3588*7c478bd9Sstevel@tonic-gate 	char *response;
3589*7c478bd9Sstevel@tonic-gate 	ssize_t s;
3590*7c478bd9Sstevel@tonic-gate 
3591*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3592*7c478bd9Sstevel@tonic-gate 	{
3593*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_envrcpt:");
3594*7c478bd9Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
3595*7c478bd9Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
3596*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\n");
3597*7c478bd9Sstevel@tonic-gate 	}
3598*7c478bd9Sstevel@tonic-gate 
3599*7c478bd9Sstevel@tonic-gate 	/* sanity check */
3600*7c478bd9Sstevel@tonic-gate 	if (args[0] == NULL)
3601*7c478bd9Sstevel@tonic-gate 	{
3602*7c478bd9Sstevel@tonic-gate 		*state = SMFIR_REJECT;
3603*7c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
3604*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt");
3605*7c478bd9Sstevel@tonic-gate 		return NULL;
3606*7c478bd9Sstevel@tonic-gate 	}
3607*7c478bd9Sstevel@tonic-gate 
3608*7c478bd9Sstevel@tonic-gate 	/* put together data */
3609*7c478bd9Sstevel@tonic-gate 	s = 0;
3610*7c478bd9Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
3611*7c478bd9Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
3612*7c478bd9Sstevel@tonic-gate 
3613*7c478bd9Sstevel@tonic-gate 	if (s < 0)
3614*7c478bd9Sstevel@tonic-gate 	{
3615*7c478bd9Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
3616*7c478bd9Sstevel@tonic-gate 		return NULL;
3617*7c478bd9Sstevel@tonic-gate 	}
3618*7c478bd9Sstevel@tonic-gate 
3619*7c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(s);
3620*7c478bd9Sstevel@tonic-gate 	bp = buf;
3621*7c478bd9Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
3622*7c478bd9Sstevel@tonic-gate 	{
3623*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
3624*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp) + 1;
3625*7c478bd9Sstevel@tonic-gate 	}
3626*7c478bd9Sstevel@tonic-gate 
3627*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 14)
3628*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf);
3629*7c478bd9Sstevel@tonic-gate 
3630*7c478bd9Sstevel@tonic-gate 	/* send it over */
3631*7c478bd9Sstevel@tonic-gate 	response = milter_command(SMFIC_RCPT, buf, s,
3632*7c478bd9Sstevel@tonic-gate 				  MilterEnvRcptMacros, e, state);
3633*7c478bd9Sstevel@tonic-gate 	sm_free(buf); /* XXX */
3634*7c478bd9Sstevel@tonic-gate 	return response;
3635*7c478bd9Sstevel@tonic-gate }
3636*7c478bd9Sstevel@tonic-gate 
3637*7c478bd9Sstevel@tonic-gate #if SMFI_VERSION > 3
3638*7c478bd9Sstevel@tonic-gate /*
3639*7c478bd9Sstevel@tonic-gate **  MILTER_DATA_CMD -- send SMTP DATA command info to milter filters
3640*7c478bd9Sstevel@tonic-gate **
3641*7c478bd9Sstevel@tonic-gate **	Parameters:
3642*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
3643*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
3644*7c478bd9Sstevel@tonic-gate **
3645*7c478bd9Sstevel@tonic-gate **	Returns:
3646*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
3647*7c478bd9Sstevel@tonic-gate */
3648*7c478bd9Sstevel@tonic-gate 
3649*7c478bd9Sstevel@tonic-gate char *
3650*7c478bd9Sstevel@tonic-gate milter_data_cmd(e, state)
3651*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3652*7c478bd9Sstevel@tonic-gate 	char *state;
3653*7c478bd9Sstevel@tonic-gate {
3654*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3655*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_data_cmd\n");
3656*7c478bd9Sstevel@tonic-gate 
3657*7c478bd9Sstevel@tonic-gate 	/* send it over */
3658*7c478bd9Sstevel@tonic-gate 	return milter_command(SMFIC_DATA, NULL, 0, MilterDataMacros, e, state);
3659*7c478bd9Sstevel@tonic-gate }
3660*7c478bd9Sstevel@tonic-gate #endif /* SMFI_VERSION > 3 */
3661*7c478bd9Sstevel@tonic-gate 
3662*7c478bd9Sstevel@tonic-gate /*
3663*7c478bd9Sstevel@tonic-gate **  MILTER_DATA -- send message headers/body and gather final message results
3664*7c478bd9Sstevel@tonic-gate **
3665*7c478bd9Sstevel@tonic-gate **	Parameters:
3666*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
3667*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
3668*7c478bd9Sstevel@tonic-gate **
3669*7c478bd9Sstevel@tonic-gate **	Returns:
3670*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
3671*7c478bd9Sstevel@tonic-gate **
3672*7c478bd9Sstevel@tonic-gate **	Side effects:
3673*7c478bd9Sstevel@tonic-gate **		- Uses e->e_dfp for access to the body
3674*7c478bd9Sstevel@tonic-gate **		- Can call the various milter action routines to
3675*7c478bd9Sstevel@tonic-gate **		  modify the envelope or message.
3676*7c478bd9Sstevel@tonic-gate */
3677*7c478bd9Sstevel@tonic-gate 
3678*7c478bd9Sstevel@tonic-gate # define MILTER_CHECK_RESULTS() \
3679*7c478bd9Sstevel@tonic-gate 	if (*state == SMFIR_ACCEPT || \
3680*7c478bd9Sstevel@tonic-gate 	    m->mf_state == SMFS_DONE || \
3681*7c478bd9Sstevel@tonic-gate 	    m->mf_state == SMFS_ERROR) \
3682*7c478bd9Sstevel@tonic-gate 	{ \
3683*7c478bd9Sstevel@tonic-gate 		if (m->mf_state != SMFS_ERROR) \
3684*7c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_DONE; \
3685*7c478bd9Sstevel@tonic-gate 		continue;	/* to next filter */ \
3686*7c478bd9Sstevel@tonic-gate 	} \
3687*7c478bd9Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE) \
3688*7c478bd9Sstevel@tonic-gate 	{ \
3689*7c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_DONE; \
3690*7c478bd9Sstevel@tonic-gate 		goto finishup; \
3691*7c478bd9Sstevel@tonic-gate 	}
3692*7c478bd9Sstevel@tonic-gate 
3693*7c478bd9Sstevel@tonic-gate char *
3694*7c478bd9Sstevel@tonic-gate milter_data(e, state)
3695*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3696*7c478bd9Sstevel@tonic-gate 	char *state;
3697*7c478bd9Sstevel@tonic-gate {
3698*7c478bd9Sstevel@tonic-gate 	bool replbody = false;		/* milter_replbody() called? */
3699*7c478bd9Sstevel@tonic-gate 	bool replfailed = false;	/* milter_replbody() failed? */
3700*7c478bd9Sstevel@tonic-gate 	bool rewind = false;		/* rewind data file? */
3701*7c478bd9Sstevel@tonic-gate 	bool dfopen = false;		/* data file open for writing? */
3702*7c478bd9Sstevel@tonic-gate 	bool newfilter;			/* reset on each new filter */
3703*7c478bd9Sstevel@tonic-gate 	char rcmd;
3704*7c478bd9Sstevel@tonic-gate 	int i;
3705*7c478bd9Sstevel@tonic-gate 	int save_errno;
3706*7c478bd9Sstevel@tonic-gate 	char *response = NULL;
3707*7c478bd9Sstevel@tonic-gate 	time_t eomsent;
3708*7c478bd9Sstevel@tonic-gate 	ssize_t rlen;
3709*7c478bd9Sstevel@tonic-gate 
3710*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3711*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_data\n");
3712*7c478bd9Sstevel@tonic-gate 
3713*7c478bd9Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
3714*7c478bd9Sstevel@tonic-gate 
3715*7c478bd9Sstevel@tonic-gate 	/*
3716*7c478bd9Sstevel@tonic-gate 	**  XXX: Should actually send body chunks to each filter
3717*7c478bd9Sstevel@tonic-gate 	**  a chunk at a time instead of sending the whole body to
3718*7c478bd9Sstevel@tonic-gate 	**  each filter in turn.  However, only if the filters don't
3719*7c478bd9Sstevel@tonic-gate 	**  change the body.
3720*7c478bd9Sstevel@tonic-gate 	*/
3721*7c478bd9Sstevel@tonic-gate 
3722*7c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
3723*7c478bd9Sstevel@tonic-gate 	{
3724*7c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
3725*7c478bd9Sstevel@tonic-gate 
3726*7c478bd9Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE &&
3727*7c478bd9Sstevel@tonic-gate 		    *state != SMFIR_ACCEPT)
3728*7c478bd9Sstevel@tonic-gate 		{
3729*7c478bd9Sstevel@tonic-gate 			/*
3730*7c478bd9Sstevel@tonic-gate 			**  A previous filter has dealt with the message,
3731*7c478bd9Sstevel@tonic-gate 			**  safe to stop processing the filters.
3732*7c478bd9Sstevel@tonic-gate 			*/
3733*7c478bd9Sstevel@tonic-gate 
3734*7c478bd9Sstevel@tonic-gate 			break;
3735*7c478bd9Sstevel@tonic-gate 		}
3736*7c478bd9Sstevel@tonic-gate 
3737*7c478bd9Sstevel@tonic-gate 		/* Now reset state for later evaluation */
3738*7c478bd9Sstevel@tonic-gate 		*state = SMFIR_CONTINUE;
3739*7c478bd9Sstevel@tonic-gate 		newfilter = true;
3740*7c478bd9Sstevel@tonic-gate 
3741*7c478bd9Sstevel@tonic-gate 		/* previous problem? */
3742*7c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
3743*7c478bd9Sstevel@tonic-gate 		{
3744*7c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
3745*7c478bd9Sstevel@tonic-gate 			break;
3746*7c478bd9Sstevel@tonic-gate 		}
3747*7c478bd9Sstevel@tonic-gate 
3748*7c478bd9Sstevel@tonic-gate 		/* sanity checks */
3749*7c478bd9Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
3750*7c478bd9Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
3751*7c478bd9Sstevel@tonic-gate 			continue;
3752*7c478bd9Sstevel@tonic-gate 
3753*7c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_INMSG;
3754*7c478bd9Sstevel@tonic-gate 
3755*7c478bd9Sstevel@tonic-gate 		/* check if filter wants the headers */
3756*7c478bd9Sstevel@tonic-gate 		if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
3757*7c478bd9Sstevel@tonic-gate 		{
3758*7c478bd9Sstevel@tonic-gate 			response = milter_headers(m, e, state);
3759*7c478bd9Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
3760*7c478bd9Sstevel@tonic-gate 		}
3761*7c478bd9Sstevel@tonic-gate 
3762*7c478bd9Sstevel@tonic-gate 		/* check if filter wants EOH */
3763*7c478bd9Sstevel@tonic-gate 		if (!bitset(SMFIP_NOEOH, m->mf_pflags))
3764*7c478bd9Sstevel@tonic-gate 		{
3765*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 10))
3766*7c478bd9Sstevel@tonic-gate 				sm_dprintf("milter_data: eoh\n");
3767*7c478bd9Sstevel@tonic-gate 
3768*7c478bd9Sstevel@tonic-gate 			/* send it over */
3769*7c478bd9Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_EOH, NULL, 0,
3770*7c478bd9Sstevel@tonic-gate 						       e, state);
3771*7c478bd9Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
3772*7c478bd9Sstevel@tonic-gate 		}
3773*7c478bd9Sstevel@tonic-gate 
3774*7c478bd9Sstevel@tonic-gate 		/* check if filter wants the body */
3775*7c478bd9Sstevel@tonic-gate 		if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
3776*7c478bd9Sstevel@tonic-gate 		    e->e_dfp != NULL)
3777*7c478bd9Sstevel@tonic-gate 		{
3778*7c478bd9Sstevel@tonic-gate 			rewind = true;
3779*7c478bd9Sstevel@tonic-gate 			response = milter_body(m, e, state);
3780*7c478bd9Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
3781*7c478bd9Sstevel@tonic-gate 		}
3782*7c478bd9Sstevel@tonic-gate 
3783*7c478bd9Sstevel@tonic-gate 		if (MilterEOMMacros[0] != NULL)
3784*7c478bd9Sstevel@tonic-gate 		{
3785*7c478bd9Sstevel@tonic-gate 			milter_send_macros(m, MilterEOMMacros,
3786*7c478bd9Sstevel@tonic-gate 					   SMFIC_BODYEOB, e);
3787*7c478bd9Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
3788*7c478bd9Sstevel@tonic-gate 		}
3789*7c478bd9Sstevel@tonic-gate 
3790*7c478bd9Sstevel@tonic-gate 		/* send the final body chunk */
3791*7c478bd9Sstevel@tonic-gate 		(void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
3792*7c478bd9Sstevel@tonic-gate 				    m->mf_timeout[SMFTO_WRITE], e);
3793*7c478bd9Sstevel@tonic-gate 
3794*7c478bd9Sstevel@tonic-gate 		/* Get time EOM sent for timeout */
3795*7c478bd9Sstevel@tonic-gate 		eomsent = curtime();
3796*7c478bd9Sstevel@tonic-gate 
3797*7c478bd9Sstevel@tonic-gate 		/* deal with the possibility of multiple responses */
3798*7c478bd9Sstevel@tonic-gate 		while (*state == SMFIR_CONTINUE)
3799*7c478bd9Sstevel@tonic-gate 		{
3800*7c478bd9Sstevel@tonic-gate 			/* Check total timeout from EOM to final ACK/NAK */
3801*7c478bd9Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_EOM] > 0 &&
3802*7c478bd9Sstevel@tonic-gate 			    curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
3803*7c478bd9Sstevel@tonic-gate 			{
3804*7c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
3805*7c478bd9Sstevel@tonic-gate 					sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
3806*7c478bd9Sstevel@tonic-gate 						m->mf_name);
3807*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
3808*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
3809*7c478bd9Sstevel@tonic-gate 						  "milter_data(%s): EOM ACK/NAK timeout",
3810*7c478bd9Sstevel@tonic-gate 						  m->mf_name);
3811*7c478bd9Sstevel@tonic-gate 				milter_error(m, e);
3812*7c478bd9Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, break);
3813*7c478bd9Sstevel@tonic-gate 				break;
3814*7c478bd9Sstevel@tonic-gate 			}
3815*7c478bd9Sstevel@tonic-gate 
3816*7c478bd9Sstevel@tonic-gate 			response = milter_read(m, &rcmd, &rlen,
3817*7c478bd9Sstevel@tonic-gate 					       m->mf_timeout[SMFTO_READ], e);
3818*7c478bd9Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
3819*7c478bd9Sstevel@tonic-gate 				break;
3820*7c478bd9Sstevel@tonic-gate 
3821*7c478bd9Sstevel@tonic-gate 			if (tTd(64, 10))
3822*7c478bd9Sstevel@tonic-gate 				sm_dprintf("milter_data(%s): state %c\n",
3823*7c478bd9Sstevel@tonic-gate 					   m->mf_name, (char) rcmd);
3824*7c478bd9Sstevel@tonic-gate 
3825*7c478bd9Sstevel@tonic-gate 			switch (rcmd)
3826*7c478bd9Sstevel@tonic-gate 			{
3827*7c478bd9Sstevel@tonic-gate 			  case SMFIR_REPLYCODE:
3828*7c478bd9Sstevel@tonic-gate 				MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
3829*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 12)
3830*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s",
3831*7c478bd9Sstevel@tonic-gate 						  m->mf_name, response);
3832*7c478bd9Sstevel@tonic-gate 				*state = rcmd;
3833*7c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
3834*7c478bd9Sstevel@tonic-gate 				break;
3835*7c478bd9Sstevel@tonic-gate 
3836*7c478bd9Sstevel@tonic-gate 			  case SMFIR_REJECT: /* log msg at end of function */
3837*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 12)
3838*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject",
3839*7c478bd9Sstevel@tonic-gate 						  m->mf_name);
3840*7c478bd9Sstevel@tonic-gate 				*state = rcmd;
3841*7c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
3842*7c478bd9Sstevel@tonic-gate 				break;
3843*7c478bd9Sstevel@tonic-gate 
3844*7c478bd9Sstevel@tonic-gate 			  case SMFIR_DISCARD:
3845*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 12)
3846*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard",
3847*7c478bd9Sstevel@tonic-gate 						  m->mf_name);
3848*7c478bd9Sstevel@tonic-gate 				*state = rcmd;
3849*7c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
3850*7c478bd9Sstevel@tonic-gate 				break;
3851*7c478bd9Sstevel@tonic-gate 
3852*7c478bd9Sstevel@tonic-gate 			  case SMFIR_TEMPFAIL:
3853*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 12)
3854*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail",
3855*7c478bd9Sstevel@tonic-gate 						  m->mf_name);
3856*7c478bd9Sstevel@tonic-gate 				*state = rcmd;
3857*7c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
3858*7c478bd9Sstevel@tonic-gate 				break;
3859*7c478bd9Sstevel@tonic-gate 
3860*7c478bd9Sstevel@tonic-gate 			  case SMFIR_CONTINUE:
3861*7c478bd9Sstevel@tonic-gate 			  case SMFIR_ACCEPT:
3862*7c478bd9Sstevel@tonic-gate 				/* this filter is done with message */
3863*7c478bd9Sstevel@tonic-gate 				if (replfailed)
3864*7c478bd9Sstevel@tonic-gate 					*state = SMFIR_TEMPFAIL;
3865*7c478bd9Sstevel@tonic-gate 				else
3866*7c478bd9Sstevel@tonic-gate 					*state = SMFIR_ACCEPT;
3867*7c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
3868*7c478bd9Sstevel@tonic-gate 				break;
3869*7c478bd9Sstevel@tonic-gate 
3870*7c478bd9Sstevel@tonic-gate 			  case SMFIR_PROGRESS:
3871*7c478bd9Sstevel@tonic-gate 				break;
3872*7c478bd9Sstevel@tonic-gate 
3873*7c478bd9Sstevel@tonic-gate 			  case SMFIR_QUARANTINE:
3874*7c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_QUARANTINE, m->mf_fflags))
3875*7c478bd9Sstevel@tonic-gate 				{
3876*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
3877*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
3878*7c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about quarantining, honoring request anyway",
3879*7c478bd9Sstevel@tonic-gate 							  m->mf_name);
3880*7c478bd9Sstevel@tonic-gate 				}
3881*7c478bd9Sstevel@tonic-gate 				if (response == NULL)
3882*7c478bd9Sstevel@tonic-gate 					response = newstr("");
3883*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 3)
3884*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
3885*7c478bd9Sstevel@tonic-gate 						  "milter=%s, quarantine=%s",
3886*7c478bd9Sstevel@tonic-gate 						  m->mf_name, response);
3887*7c478bd9Sstevel@tonic-gate 				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
3888*7c478bd9Sstevel@tonic-gate 								 response);
3889*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
3890*7c478bd9Sstevel@tonic-gate 					  macid("{quarantine}"), e->e_quarmsg);
3891*7c478bd9Sstevel@tonic-gate 				break;
3892*7c478bd9Sstevel@tonic-gate 
3893*7c478bd9Sstevel@tonic-gate 			  case SMFIR_ADDHEADER:
3894*7c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
3895*7c478bd9Sstevel@tonic-gate 				{
3896*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
3897*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
3898*7c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
3899*7c478bd9Sstevel@tonic-gate 							  m->mf_name);
3900*7c478bd9Sstevel@tonic-gate 				}
3901*7c478bd9Sstevel@tonic-gate 				milter_addheader(response, rlen, e);
3902*7c478bd9Sstevel@tonic-gate 				break;
3903*7c478bd9Sstevel@tonic-gate 
3904*7c478bd9Sstevel@tonic-gate 			  case SMFIR_INSHEADER:
3905*7c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
3906*7c478bd9Sstevel@tonic-gate 				{
3907*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
3908*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
3909*7c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
3910*7c478bd9Sstevel@tonic-gate 							  m->mf_name);
3911*7c478bd9Sstevel@tonic-gate 				}
3912*7c478bd9Sstevel@tonic-gate 				milter_insheader(response, rlen, e);
3913*7c478bd9Sstevel@tonic-gate 				break;
3914*7c478bd9Sstevel@tonic-gate 
3915*7c478bd9Sstevel@tonic-gate 			  case SMFIR_CHGHEADER:
3916*7c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
3917*7c478bd9Sstevel@tonic-gate 				{
3918*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
3919*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
3920*7c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about changing headers, honoring request anyway",
3921*7c478bd9Sstevel@tonic-gate 							  m->mf_name);
3922*7c478bd9Sstevel@tonic-gate 				}
3923*7c478bd9Sstevel@tonic-gate 				milter_changeheader(response, rlen, e);
3924*7c478bd9Sstevel@tonic-gate 				break;
3925*7c478bd9Sstevel@tonic-gate 
3926*7c478bd9Sstevel@tonic-gate 			  case SMFIR_ADDRCPT:
3927*7c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
3928*7c478bd9Sstevel@tonic-gate 				{
3929*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
3930*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
3931*7c478bd9Sstevel@tonic-gate 							  "milter_data(%s) lied about adding recipients, honoring request anyway",
3932*7c478bd9Sstevel@tonic-gate 							  m->mf_name);
3933*7c478bd9Sstevel@tonic-gate 				}
3934*7c478bd9Sstevel@tonic-gate 				milter_addrcpt(response, rlen, e);
3935*7c478bd9Sstevel@tonic-gate 				break;
3936*7c478bd9Sstevel@tonic-gate 
3937*7c478bd9Sstevel@tonic-gate 			  case SMFIR_DELRCPT:
3938*7c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
3939*7c478bd9Sstevel@tonic-gate 				{
3940*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
3941*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
3942*7c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about removing recipients, honoring request anyway",
3943*7c478bd9Sstevel@tonic-gate 							  m->mf_name);
3944*7c478bd9Sstevel@tonic-gate 				}
3945*7c478bd9Sstevel@tonic-gate 				milter_delrcpt(response, rlen, e);
3946*7c478bd9Sstevel@tonic-gate 				break;
3947*7c478bd9Sstevel@tonic-gate 
3948*7c478bd9Sstevel@tonic-gate 			  case SMFIR_REPLBODY:
3949*7c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_MODBODY, m->mf_fflags))
3950*7c478bd9Sstevel@tonic-gate 				{
3951*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 0)
3952*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
3953*7c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
3954*7c478bd9Sstevel@tonic-gate 							  m->mf_name);
3955*7c478bd9Sstevel@tonic-gate 					replfailed = true;
3956*7c478bd9Sstevel@tonic-gate 					break;
3957*7c478bd9Sstevel@tonic-gate 				}
3958*7c478bd9Sstevel@tonic-gate 
3959*7c478bd9Sstevel@tonic-gate 				/* already failed in attempt */
3960*7c478bd9Sstevel@tonic-gate 				if (replfailed)
3961*7c478bd9Sstevel@tonic-gate 					break;
3962*7c478bd9Sstevel@tonic-gate 
3963*7c478bd9Sstevel@tonic-gate 				if (!dfopen)
3964*7c478bd9Sstevel@tonic-gate 				{
3965*7c478bd9Sstevel@tonic-gate 					if (milter_reopen_df(e) < 0)
3966*7c478bd9Sstevel@tonic-gate 					{
3967*7c478bd9Sstevel@tonic-gate 						replfailed = true;
3968*7c478bd9Sstevel@tonic-gate 						break;
3969*7c478bd9Sstevel@tonic-gate 					}
3970*7c478bd9Sstevel@tonic-gate 					dfopen = true;
3971*7c478bd9Sstevel@tonic-gate 					rewind = true;
3972*7c478bd9Sstevel@tonic-gate 				}
3973*7c478bd9Sstevel@tonic-gate 
3974*7c478bd9Sstevel@tonic-gate 				if (milter_replbody(response, rlen,
3975*7c478bd9Sstevel@tonic-gate 						    newfilter, e) < 0)
3976*7c478bd9Sstevel@tonic-gate 					replfailed = true;
3977*7c478bd9Sstevel@tonic-gate 				newfilter = false;
3978*7c478bd9Sstevel@tonic-gate 				replbody = true;
3979*7c478bd9Sstevel@tonic-gate 				break;
3980*7c478bd9Sstevel@tonic-gate 
3981*7c478bd9Sstevel@tonic-gate 			  default:
3982*7c478bd9Sstevel@tonic-gate 				/* Invalid response to command */
3983*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
3984*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
3985*7c478bd9Sstevel@tonic-gate 						  "milter_data(%s): returned bogus response %c",
3986*7c478bd9Sstevel@tonic-gate 						  m->mf_name, rcmd);
3987*7c478bd9Sstevel@tonic-gate 				milter_error(m, e);
3988*7c478bd9Sstevel@tonic-gate 				break;
3989*7c478bd9Sstevel@tonic-gate 			}
3990*7c478bd9Sstevel@tonic-gate 			if (rcmd != SMFIR_REPLYCODE && response != NULL)
3991*7c478bd9Sstevel@tonic-gate 			{
3992*7c478bd9Sstevel@tonic-gate 				sm_free(response); /* XXX */
3993*7c478bd9Sstevel@tonic-gate 				response = NULL;
3994*7c478bd9Sstevel@tonic-gate 			}
3995*7c478bd9Sstevel@tonic-gate 
3996*7c478bd9Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
3997*7c478bd9Sstevel@tonic-gate 				break;
3998*7c478bd9Sstevel@tonic-gate 		}
3999*7c478bd9Sstevel@tonic-gate 
4000*7c478bd9Sstevel@tonic-gate 		if (replbody && !replfailed)
4001*7c478bd9Sstevel@tonic-gate 		{
4002*7c478bd9Sstevel@tonic-gate 			/* flush possible buffered character */
4003*7c478bd9Sstevel@tonic-gate 			milter_replbody(NULL, 0, !replbody, e);
4004*7c478bd9Sstevel@tonic-gate 			replbody = false;
4005*7c478bd9Sstevel@tonic-gate 		}
4006*7c478bd9Sstevel@tonic-gate 
4007*7c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
4008*7c478bd9Sstevel@tonic-gate 		{
4009*7c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
4010*7c478bd9Sstevel@tonic-gate 			goto finishup;
4011*7c478bd9Sstevel@tonic-gate 		}
4012*7c478bd9Sstevel@tonic-gate 	}
4013*7c478bd9Sstevel@tonic-gate 
4014*7c478bd9Sstevel@tonic-gate finishup:
4015*7c478bd9Sstevel@tonic-gate 	/* leave things in the expected state if we touched it */
4016*7c478bd9Sstevel@tonic-gate 	if (replfailed)
4017*7c478bd9Sstevel@tonic-gate 	{
4018*7c478bd9Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
4019*7c478bd9Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
4020*7c478bd9Sstevel@tonic-gate 		{
4021*7c478bd9Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
4022*7c478bd9Sstevel@tonic-gate 			SM_FREE_CLR(response);
4023*7c478bd9Sstevel@tonic-gate 		}
4024*7c478bd9Sstevel@tonic-gate 
4025*7c478bd9Sstevel@tonic-gate 		if (dfopen)
4026*7c478bd9Sstevel@tonic-gate 		{
4027*7c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
4028*7c478bd9Sstevel@tonic-gate 			e->e_dfp = NULL;
4029*7c478bd9Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
4030*7c478bd9Sstevel@tonic-gate 			dfopen = false;
4031*7c478bd9Sstevel@tonic-gate 		}
4032*7c478bd9Sstevel@tonic-gate 		rewind = false;
4033*7c478bd9Sstevel@tonic-gate 	}
4034*7c478bd9Sstevel@tonic-gate 
4035*7c478bd9Sstevel@tonic-gate 	if ((dfopen && milter_reset_df(e) < 0) ||
4036*7c478bd9Sstevel@tonic-gate 	    (rewind && bfrewind(e->e_dfp) < 0))
4037*7c478bd9Sstevel@tonic-gate 	{
4038*7c478bd9Sstevel@tonic-gate 		save_errno = errno;
4039*7c478bd9Sstevel@tonic-gate 		ExitStat = EX_IOERR;
4040*7c478bd9Sstevel@tonic-gate 
4041*7c478bd9Sstevel@tonic-gate 		/*
4042*7c478bd9Sstevel@tonic-gate 		**  If filter told us to keep message but we had
4043*7c478bd9Sstevel@tonic-gate 		**  an error, we can't really keep it, tempfail it.
4044*7c478bd9Sstevel@tonic-gate 		*/
4045*7c478bd9Sstevel@tonic-gate 
4046*7c478bd9Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
4047*7c478bd9Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
4048*7c478bd9Sstevel@tonic-gate 		{
4049*7c478bd9Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
4050*7c478bd9Sstevel@tonic-gate 			SM_FREE_CLR(response);
4051*7c478bd9Sstevel@tonic-gate 		}
4052*7c478bd9Sstevel@tonic-gate 
4053*7c478bd9Sstevel@tonic-gate 		errno = save_errno;
4054*7c478bd9Sstevel@tonic-gate 		syserr("milter_data: %s/%cf%s: read error",
4055*7c478bd9Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
4056*7c478bd9Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
4057*7c478bd9Sstevel@tonic-gate 	}
4058*7c478bd9Sstevel@tonic-gate 
4059*7c478bd9Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
4060*7c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
4061*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data");
4062*7c478bd9Sstevel@tonic-gate 	return response;
4063*7c478bd9Sstevel@tonic-gate }
4064*7c478bd9Sstevel@tonic-gate 
4065*7c478bd9Sstevel@tonic-gate #if SMFI_VERSION > 2
4066*7c478bd9Sstevel@tonic-gate /*
4067*7c478bd9Sstevel@tonic-gate **  MILTER_UNKNOWN -- send any unrecognized or unimplemented command
4068*7c478bd9Sstevel@tonic-gate **			string to milter filters
4069*7c478bd9Sstevel@tonic-gate **
4070*7c478bd9Sstevel@tonic-gate **	Parameters:
4071*7c478bd9Sstevel@tonic-gate **		cmd -- the string itself.
4072*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
4073*7c478bd9Sstevel@tonic-gate **		state -- return state from response.
4074*7c478bd9Sstevel@tonic-gate **
4075*7c478bd9Sstevel@tonic-gate **
4076*7c478bd9Sstevel@tonic-gate **	Returns:
4077*7c478bd9Sstevel@tonic-gate **		response string (may be NULL)
4078*7c478bd9Sstevel@tonic-gate */
4079*7c478bd9Sstevel@tonic-gate 
4080*7c478bd9Sstevel@tonic-gate char *
4081*7c478bd9Sstevel@tonic-gate milter_unknown(cmd, e, state)
4082*7c478bd9Sstevel@tonic-gate 	char *cmd;
4083*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
4084*7c478bd9Sstevel@tonic-gate 	char *state;
4085*7c478bd9Sstevel@tonic-gate {
4086*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
4087*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_unknown(%s)\n", cmd);
4088*7c478bd9Sstevel@tonic-gate 
4089*7c478bd9Sstevel@tonic-gate 	return milter_command(SMFIC_UNKNOWN, cmd, strlen(cmd) + 1,
4090*7c478bd9Sstevel@tonic-gate 				NULL, e, state);
4091*7c478bd9Sstevel@tonic-gate }
4092*7c478bd9Sstevel@tonic-gate #endif /* SMFI_VERSION > 2 */
4093*7c478bd9Sstevel@tonic-gate 
4094*7c478bd9Sstevel@tonic-gate /*
4095*7c478bd9Sstevel@tonic-gate **  MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
4096*7c478bd9Sstevel@tonic-gate **
4097*7c478bd9Sstevel@tonic-gate **	Parameters:
4098*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
4099*7c478bd9Sstevel@tonic-gate **
4100*7c478bd9Sstevel@tonic-gate **	Returns:
4101*7c478bd9Sstevel@tonic-gate **		none
4102*7c478bd9Sstevel@tonic-gate */
4103*7c478bd9Sstevel@tonic-gate 
4104*7c478bd9Sstevel@tonic-gate void
4105*7c478bd9Sstevel@tonic-gate milter_quit(e)
4106*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
4107*7c478bd9Sstevel@tonic-gate {
4108*7c478bd9Sstevel@tonic-gate 	int i;
4109*7c478bd9Sstevel@tonic-gate 
4110*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
4111*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_quit(%s)\n", e->e_id);
4112*7c478bd9Sstevel@tonic-gate 
4113*7c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
4114*7c478bd9Sstevel@tonic-gate 		milter_quit_filter(InputFilters[i], e);
4115*7c478bd9Sstevel@tonic-gate }
4116*7c478bd9Sstevel@tonic-gate /*
4117*7c478bd9Sstevel@tonic-gate **  MILTER_ABORT -- informs the filter(s) that we are aborting current message
4118*7c478bd9Sstevel@tonic-gate **
4119*7c478bd9Sstevel@tonic-gate **	Parameters:
4120*7c478bd9Sstevel@tonic-gate **		e -- current envelope.
4121*7c478bd9Sstevel@tonic-gate **
4122*7c478bd9Sstevel@tonic-gate **	Returns:
4123*7c478bd9Sstevel@tonic-gate **		none
4124*7c478bd9Sstevel@tonic-gate */
4125*7c478bd9Sstevel@tonic-gate 
4126*7c478bd9Sstevel@tonic-gate void
4127*7c478bd9Sstevel@tonic-gate milter_abort(e)
4128*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
4129*7c478bd9Sstevel@tonic-gate {
4130*7c478bd9Sstevel@tonic-gate 	int i;
4131*7c478bd9Sstevel@tonic-gate 
4132*7c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
4133*7c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_abort\n");
4134*7c478bd9Sstevel@tonic-gate 
4135*7c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
4136*7c478bd9Sstevel@tonic-gate 	{
4137*7c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
4138*7c478bd9Sstevel@tonic-gate 
4139*7c478bd9Sstevel@tonic-gate 		/* sanity checks */
4140*7c478bd9Sstevel@tonic-gate 		if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
4141*7c478bd9Sstevel@tonic-gate 			continue;
4142*7c478bd9Sstevel@tonic-gate 
4143*7c478bd9Sstevel@tonic-gate 		milter_abort_filter(m, e);
4144*7c478bd9Sstevel@tonic-gate 	}
4145*7c478bd9Sstevel@tonic-gate }
4146*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
4147