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