xref: /illumos-gate/usr/src/cmd/sendmail/src/milter.c (revision 058561cb)
17c478bd9Sstevel@tonic-gate /*
2*058561cbSjbeck  * Copyright (c) 1999-2006 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate  *	All rights reserved.
47c478bd9Sstevel@tonic-gate  *
57c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
67c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
77c478bd9Sstevel@tonic-gate  * the sendmail distribution.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  */
107c478bd9Sstevel@tonic-gate 
117c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
127c478bd9Sstevel@tonic-gate 
137c478bd9Sstevel@tonic-gate #include <sendmail.h>
147c478bd9Sstevel@tonic-gate 
15*058561cbSjbeck SM_RCSID("@(#)$Id: milter.c,v 8.266 2006/11/29 00:20:41 ca Exp $")
167c478bd9Sstevel@tonic-gate 
177c478bd9Sstevel@tonic-gate #if MILTER
18*058561cbSjbeck # include <sm/sendmail.h>
197c478bd9Sstevel@tonic-gate # include <libmilter/mfapi.h>
207c478bd9Sstevel@tonic-gate # include <libmilter/mfdef.h>
217c478bd9Sstevel@tonic-gate 
227c478bd9Sstevel@tonic-gate # include <errno.h>
2349218d4fSjbeck # include <sm/time.h>
247c478bd9Sstevel@tonic-gate # include <sys/uio.h>
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate # if NETINET || NETINET6
277c478bd9Sstevel@tonic-gate #  include <arpa/inet.h>
28*058561cbSjbeck #  if MILTER_NO_NAGLE
297c478bd9Sstevel@tonic-gate #   include <netinet/tcp.h>
30*058561cbSjbeck #  endif /* MILTER_NO_NAGLE */
317c478bd9Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate # include <sm/fdset.h>
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate static void	milter_connect_timeout __P((int));
367c478bd9Sstevel@tonic-gate static void	milter_error __P((struct milter *, ENVELOPE *));
377c478bd9Sstevel@tonic-gate static int	milter_open __P((struct milter *, bool, ENVELOPE *));
387c478bd9Sstevel@tonic-gate static void	milter_parse_timeouts __P((char *, struct milter *));
39*058561cbSjbeck static char	*milter_sysread __P((struct milter *, char *, ssize_t, time_t,
40*058561cbSjbeck 			ENVELOPE *, const char *));
41*058561cbSjbeck static char	*milter_read __P((struct milter *, char *, ssize_t *, time_t,
42*058561cbSjbeck 			ENVELOPE *, const char *));
43*058561cbSjbeck static char	*milter_write __P((struct milter *, int, char *, ssize_t,
44*058561cbSjbeck 			time_t, ENVELOPE *, const char *));
45*058561cbSjbeck static char	*milter_send_command __P((struct milter *, int, void *,
46*058561cbSjbeck 			ssize_t, ENVELOPE *, char *, const char *));
47*058561cbSjbeck static char	*milter_command __P((int, void *, ssize_t, char **,
48*058561cbSjbeck 			ENVELOPE *, char *, const char *, bool));
49*058561cbSjbeck static char	*milter_body __P((struct milter *, ENVELOPE *, char *));
50*058561cbSjbeck static int	milter_reopen_df __P((ENVELOPE *));
51*058561cbSjbeck static int	milter_reset_df __P((ENVELOPE *));
52*058561cbSjbeck static void	milter_quit_filter __P((struct milter *, ENVELOPE *));
53*058561cbSjbeck static void	milter_abort_filter __P((struct milter *, ENVELOPE *));
54*058561cbSjbeck static void	milter_send_macros __P((struct milter *, char **, int,
55*058561cbSjbeck 			ENVELOPE *));
56*058561cbSjbeck static int	milter_negotiate __P((struct milter *, ENVELOPE *));
57*058561cbSjbeck static void	milter_per_connection_check __P((ENVELOPE *));
58*058561cbSjbeck static char	*milter_headers __P((struct milter *, ENVELOPE *, char *));
59*058561cbSjbeck static void	milter_addheader __P((struct milter *, char *, ssize_t,
60*058561cbSjbeck 			ENVELOPE *));
61*058561cbSjbeck static void	milter_insheader __P((struct milter *, char *, ssize_t,
62*058561cbSjbeck 			ENVELOPE *));
63*058561cbSjbeck static void	milter_changeheader __P((struct milter *, char *, ssize_t,
64*058561cbSjbeck 			ENVELOPE *));
65*058561cbSjbeck static void	milter_chgfrom __P((char *, ssize_t, ENVELOPE *));
66*058561cbSjbeck static void	milter_addrcpt __P((char *, ssize_t, ENVELOPE *));
67*058561cbSjbeck static void	milter_addrcpt_par __P((char *, ssize_t, ENVELOPE *));
68*058561cbSjbeck static void	milter_delrcpt __P((char *, ssize_t, ENVELOPE *));
69*058561cbSjbeck static int	milter_replbody __P((char *, ssize_t, bool, ENVELOPE *));
70*058561cbSjbeck static int	milter_set_macros __P((char *, char **, char *, int));
71*058561cbSjbeck 
72*058561cbSjbeck 
73*058561cbSjbeck /* milter states */
74*058561cbSjbeck # define SMFS_CLOSED		'C'	/* closed for all further actions */
75*058561cbSjbeck # define SMFS_OPEN		'O'	/* connected to remote milter filter */
76*058561cbSjbeck # define SMFS_INMSG		'M'	/* currently servicing a message */
77*058561cbSjbeck # define SMFS_DONE		'D'	/* done with current message */
78*058561cbSjbeck # define SMFS_CLOSABLE		'Q'	/* done with current connection */
79*058561cbSjbeck # define SMFS_ERROR		'E'	/* error state */
80*058561cbSjbeck # define SMFS_READY		'R'	/* ready for action */
81*058561cbSjbeck # define SMFS_SKIP		'S'	/* skip body */
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate static char *MilterConnectMacros[MAXFILTERMACROS + 1];
847c478bd9Sstevel@tonic-gate static char *MilterHeloMacros[MAXFILTERMACROS + 1];
857c478bd9Sstevel@tonic-gate static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
867c478bd9Sstevel@tonic-gate static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
877c478bd9Sstevel@tonic-gate static char *MilterDataMacros[MAXFILTERMACROS + 1];
887c478bd9Sstevel@tonic-gate static char *MilterEOMMacros[MAXFILTERMACROS + 1];
89*058561cbSjbeck static char *MilterEOHMacros[MAXFILTERMACROS + 1];
907c478bd9Sstevel@tonic-gate static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE;
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate # define MILTER_CHECK_DONE_MSG() \
937c478bd9Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE || \
947c478bd9Sstevel@tonic-gate 	    *state == SMFIR_REJECT || \
957c478bd9Sstevel@tonic-gate 	    *state == SMFIR_DISCARD || \
967c478bd9Sstevel@tonic-gate 	    *state == SMFIR_TEMPFAIL) \
977c478bd9Sstevel@tonic-gate 	{ \
987c478bd9Sstevel@tonic-gate 		/* Abort the filters to let them know we are done with msg */ \
997c478bd9Sstevel@tonic-gate 		milter_abort(e); \
1007c478bd9Sstevel@tonic-gate 	}
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate # define MILTER_CHECK_ERROR(initial, action) \
1037c478bd9Sstevel@tonic-gate 	if (!initial && tTd(71, 100)) \
1047c478bd9Sstevel@tonic-gate 	{ \
1057c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1067c478bd9Sstevel@tonic-gate 		{ \
1077c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1087c478bd9Sstevel@tonic-gate 							 "filter failure"); \
1097c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1107c478bd9Sstevel@tonic-gate 				  e->e_quarmsg); \
1117c478bd9Sstevel@tonic-gate 		} \
1127c478bd9Sstevel@tonic-gate 	} \
1137c478bd9Sstevel@tonic-gate 	else if (tTd(71, 101)) \
1147c478bd9Sstevel@tonic-gate 	{ \
1157c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1167c478bd9Sstevel@tonic-gate 		{ \
1177c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1187c478bd9Sstevel@tonic-gate 							 "filter failure"); \
1197c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1207c478bd9Sstevel@tonic-gate 				  e->e_quarmsg); \
1217c478bd9Sstevel@tonic-gate 		} \
1227c478bd9Sstevel@tonic-gate 	} \
1237c478bd9Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
1247c478bd9Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL; \
1257c478bd9Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \
1267c478bd9Sstevel@tonic-gate 		*state = SMFIR_SHUTDOWN; \
1277c478bd9Sstevel@tonic-gate 	else if (bitnset(SMF_REJECT, m->mf_flags)) \
1287c478bd9Sstevel@tonic-gate 		*state = SMFIR_REJECT; \
1297c478bd9Sstevel@tonic-gate 	else \
1307c478bd9Sstevel@tonic-gate 		action;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate # define MILTER_CHECK_REPLYCODE(default) \
1337c478bd9Sstevel@tonic-gate 	if (response == NULL || \
1347c478bd9Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen || \
1357c478bd9Sstevel@tonic-gate 	    rlen < 3 || \
1367c478bd9Sstevel@tonic-gate 	    (response[0] != '4' && response[0] != '5') || \
1377c478bd9Sstevel@tonic-gate 	    !isascii(response[1]) || !isdigit(response[1]) || \
1387c478bd9Sstevel@tonic-gate 	    !isascii(response[2]) || !isdigit(response[2])) \
1397c478bd9Sstevel@tonic-gate 	{ \
1407c478bd9Sstevel@tonic-gate 		if (response != NULL) \
1417c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */ \
1427c478bd9Sstevel@tonic-gate 		response = newstr(default); \
1437c478bd9Sstevel@tonic-gate 	} \
1447c478bd9Sstevel@tonic-gate 	else \
1457c478bd9Sstevel@tonic-gate 	{ \
1467c478bd9Sstevel@tonic-gate 		char *ptr = response; \
1477c478bd9Sstevel@tonic-gate  \
1487c478bd9Sstevel@tonic-gate 		/* Check for unprotected %'s in the string */ \
1497c478bd9Sstevel@tonic-gate 		while (*ptr != '\0') \
1507c478bd9Sstevel@tonic-gate 		{ \
1517c478bd9Sstevel@tonic-gate 			if (*ptr == '%' && *++ptr != '%') \
1527c478bd9Sstevel@tonic-gate 			{ \
1537c478bd9Sstevel@tonic-gate 				sm_free(response); /* XXX */ \
1547c478bd9Sstevel@tonic-gate 				response = newstr(default); \
1557c478bd9Sstevel@tonic-gate 				break; \
1567c478bd9Sstevel@tonic-gate 			} \
1577c478bd9Sstevel@tonic-gate 			ptr++; \
1587c478bd9Sstevel@tonic-gate 		} \
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate # define MILTER_DF_ERROR(msg) \
1627c478bd9Sstevel@tonic-gate { \
1637c478bd9Sstevel@tonic-gate 	int save_errno = errno; \
1647c478bd9Sstevel@tonic-gate  \
1657c478bd9Sstevel@tonic-gate 	if (tTd(64, 5)) \
1667c478bd9Sstevel@tonic-gate 	{ \
1677c478bd9Sstevel@tonic-gate 		sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
1687c478bd9Sstevel@tonic-gate 		sm_dprintf("\n"); \
1697c478bd9Sstevel@tonic-gate 	} \
1707c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 0) \
1717c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
1727c478bd9Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY) \
1737c478bd9Sstevel@tonic-gate 	{ \
1747c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL) \
1757c478bd9Sstevel@tonic-gate 		{ \
1767c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
1777c478bd9Sstevel@tonic-gate 			e->e_dfp = NULL; \
1787c478bd9Sstevel@tonic-gate 		} \
1797c478bd9Sstevel@tonic-gate 		e->e_flags &= ~EF_HAS_DF; \
1807c478bd9Sstevel@tonic-gate 	} \
1817c478bd9Sstevel@tonic-gate 	errno = save_errno; \
1827c478bd9Sstevel@tonic-gate }
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate /*
1857c478bd9Sstevel@tonic-gate **  MILTER_TIMEOUT -- make sure socket is ready in time
1867c478bd9Sstevel@tonic-gate **
1877c478bd9Sstevel@tonic-gate **	Parameters:
1887c478bd9Sstevel@tonic-gate **		routine -- routine name for debug/logging
1897c478bd9Sstevel@tonic-gate **		secs -- number of seconds in timeout
1907c478bd9Sstevel@tonic-gate **		write -- waiting to read or write?
1917c478bd9Sstevel@tonic-gate **		started -- whether this is part of a previous sequence
1927c478bd9Sstevel@tonic-gate **
1937c478bd9Sstevel@tonic-gate **	Assumes 'm' is a milter structure for the current socket.
1947c478bd9Sstevel@tonic-gate */
1957c478bd9Sstevel@tonic-gate 
196*058561cbSjbeck # define MILTER_TIMEOUT(routine, secs, write, started, function) \
1977c478bd9Sstevel@tonic-gate { \
1987c478bd9Sstevel@tonic-gate 	int ret; \
1997c478bd9Sstevel@tonic-gate 	int save_errno; \
2007c478bd9Sstevel@tonic-gate 	fd_set fds; \
2017c478bd9Sstevel@tonic-gate 	struct timeval tv; \
2027c478bd9Sstevel@tonic-gate  \
2037c478bd9Sstevel@tonic-gate 	if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
2047c478bd9Sstevel@tonic-gate 	{ \
2057c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
2067c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
2077c478bd9Sstevel@tonic-gate 				   (routine), m->mf_name, m->mf_sock, \
2087c478bd9Sstevel@tonic-gate 				   SM_FD_SETSIZE); \
2097c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2107c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2117c478bd9Sstevel@tonic-gate 				  "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
2127c478bd9Sstevel@tonic-gate 				  m->mf_name, (routine), m->mf_sock, \
2137c478bd9Sstevel@tonic-gate 				  SM_FD_SETSIZE); \
2147c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
2157c478bd9Sstevel@tonic-gate 		return NULL; \
2167c478bd9Sstevel@tonic-gate 	} \
2177c478bd9Sstevel@tonic-gate  \
2187c478bd9Sstevel@tonic-gate 	do \
2197c478bd9Sstevel@tonic-gate 	{ \
2207c478bd9Sstevel@tonic-gate 		FD_ZERO(&fds); \
2217c478bd9Sstevel@tonic-gate 		SM_FD_SET(m->mf_sock, &fds); \
2227c478bd9Sstevel@tonic-gate 		tv.tv_sec = (secs); \
2237c478bd9Sstevel@tonic-gate 		tv.tv_usec = 0; \
2247c478bd9Sstevel@tonic-gate 		ret = select(m->mf_sock + 1, \
2257c478bd9Sstevel@tonic-gate 			     (write) ? NULL : &fds, \
2267c478bd9Sstevel@tonic-gate 			     (write) ? &fds : NULL, \
2277c478bd9Sstevel@tonic-gate 			     NULL, &tv); \
2287c478bd9Sstevel@tonic-gate 	} while (ret < 0 && errno == EINTR); \
2297c478bd9Sstevel@tonic-gate  \
2307c478bd9Sstevel@tonic-gate 	switch (ret) \
2317c478bd9Sstevel@tonic-gate 	{ \
2327c478bd9Sstevel@tonic-gate 	  case 0: \
2337c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
234*058561cbSjbeck 			sm_dprintf("milter_%s(%s): timeout, where=%s\n", \
235*058561cbSjbeck 				(routine), m->mf_name, (function)); \
2367c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2377c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
238*058561cbSjbeck 				  "Milter (%s): timeout %s data %s, where=%s", \
239*058561cbSjbeck 				  m->mf_name, \
2407c478bd9Sstevel@tonic-gate 				  started ? "during" : "before", \
241*058561cbSjbeck 				  (routine), (function)); \
2427c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
2437c478bd9Sstevel@tonic-gate 		return NULL; \
2447c478bd9Sstevel@tonic-gate  \
2457c478bd9Sstevel@tonic-gate 	  case -1: \
2467c478bd9Sstevel@tonic-gate 		save_errno = errno; \
2477c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
2487c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
2497c478bd9Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno)); \
2507c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2517c478bd9Sstevel@tonic-gate 		{ \
2527c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2537c478bd9Sstevel@tonic-gate 				  "Milter (%s): select(%s): %s", \
2547c478bd9Sstevel@tonic-gate 				  m->mf_name, (routine), \
2557c478bd9Sstevel@tonic-gate 				  sm_errstring(save_errno)); \
2567c478bd9Sstevel@tonic-gate 		} \
2577c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
2587c478bd9Sstevel@tonic-gate 		return NULL; \
2597c478bd9Sstevel@tonic-gate  \
2607c478bd9Sstevel@tonic-gate 	  default: \
2617c478bd9Sstevel@tonic-gate 		if (SM_FD_ISSET(m->mf_sock, &fds)) \
2627c478bd9Sstevel@tonic-gate 			break; \
2637c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
2647c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket not ready\n", \
2657c478bd9Sstevel@tonic-gate 				(routine), m->mf_name); \
2667c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2677c478bd9Sstevel@tonic-gate 		{ \
2687c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2697c478bd9Sstevel@tonic-gate 				  "Milter (%s): socket(%s) not ready", \
2707c478bd9Sstevel@tonic-gate 				  m->mf_name, (routine)); \
2717c478bd9Sstevel@tonic-gate 		} \
2727c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
2737c478bd9Sstevel@tonic-gate 		return NULL; \
2747c478bd9Sstevel@tonic-gate 	} \
2757c478bd9Sstevel@tonic-gate }
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate /*
2787c478bd9Sstevel@tonic-gate **  Low level functions
2797c478bd9Sstevel@tonic-gate */
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate **  MILTER_READ -- read from a remote milter filter
2837c478bd9Sstevel@tonic-gate **
2847c478bd9Sstevel@tonic-gate **	Parameters:
2857c478bd9Sstevel@tonic-gate **		m -- milter to read from.
2867c478bd9Sstevel@tonic-gate **		cmd -- return param for command read.
2877c478bd9Sstevel@tonic-gate **		rlen -- return length of response string.
2887c478bd9Sstevel@tonic-gate **		to -- timeout in seconds.
2897c478bd9Sstevel@tonic-gate **		e -- current envelope.
2907c478bd9Sstevel@tonic-gate **
2917c478bd9Sstevel@tonic-gate **	Returns:
2927c478bd9Sstevel@tonic-gate **		response string (may be NULL)
2937c478bd9Sstevel@tonic-gate */
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate static char *
296*058561cbSjbeck milter_sysread(m, buf, sz, to, e, where)
2977c478bd9Sstevel@tonic-gate 	struct milter *m;
2987c478bd9Sstevel@tonic-gate 	char *buf;
2997c478bd9Sstevel@tonic-gate 	ssize_t sz;
3007c478bd9Sstevel@tonic-gate 	time_t to;
3017c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
302*058561cbSjbeck 	const char *where;
3037c478bd9Sstevel@tonic-gate {
3047c478bd9Sstevel@tonic-gate 	time_t readstart = 0;
3057c478bd9Sstevel@tonic-gate 	ssize_t len, curl;
3067c478bd9Sstevel@tonic-gate 	bool started = false;
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	curl = 0;
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 	if (to > 0)
3117c478bd9Sstevel@tonic-gate 		readstart = curtime();
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	for (;;)
3147c478bd9Sstevel@tonic-gate 	{
3157c478bd9Sstevel@tonic-gate 		if (to > 0)
3167c478bd9Sstevel@tonic-gate 		{
3177c478bd9Sstevel@tonic-gate 			time_t now;
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 			now = curtime();
3207c478bd9Sstevel@tonic-gate 			if (now - readstart >= to)
3217c478bd9Sstevel@tonic-gate 			{
3227c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
323*058561cbSjbeck 					sm_dprintf("milter_sys_read (%s): timeout %s data read in %s",
324*058561cbSjbeck 						  m->mf_name,
3257c478bd9Sstevel@tonic-gate 						  started ? "during" : "before",
326*058561cbSjbeck 						  where);
3277c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
3287c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
329*058561cbSjbeck 						  "Milter (%s): timeout %s data read in %s",
330*058561cbSjbeck 						  m->mf_name,
3317c478bd9Sstevel@tonic-gate 						  started ? "during" : "before",
332*058561cbSjbeck 						  where);
3337c478bd9Sstevel@tonic-gate 				milter_error(m, e);
3347c478bd9Sstevel@tonic-gate 				return NULL;
3357c478bd9Sstevel@tonic-gate 			}
3367c478bd9Sstevel@tonic-gate 			to -= now - readstart;
3377c478bd9Sstevel@tonic-gate 			readstart = now;
338*058561cbSjbeck 			MILTER_TIMEOUT("read", to, false, started, where);
3397c478bd9Sstevel@tonic-gate 		}
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 		len = read(m->mf_sock, buf + curl, sz - curl);
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 		if (len < 0)
3447c478bd9Sstevel@tonic-gate 		{
3457c478bd9Sstevel@tonic-gate 			int save_errno = errno;
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
348*058561cbSjbeck 				sm_dprintf("milter_sys_read(%s): read returned %ld: %s\n",
3497c478bd9Sstevel@tonic-gate 					m->mf_name, (long) len,
3507c478bd9Sstevel@tonic-gate 					sm_errstring(save_errno));
3517c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
3527c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
3537c478bd9Sstevel@tonic-gate 					  "Milter (%s): read returned %ld: %s",
3547c478bd9Sstevel@tonic-gate 					  m->mf_name, (long) len,
3557c478bd9Sstevel@tonic-gate 					  sm_errstring(save_errno));
3567c478bd9Sstevel@tonic-gate 			milter_error(m, e);
3577c478bd9Sstevel@tonic-gate 			return NULL;
3587c478bd9Sstevel@tonic-gate 		}
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 		started = true;
3617c478bd9Sstevel@tonic-gate 		curl += len;
3627c478bd9Sstevel@tonic-gate 		if (len == 0 || curl >= sz)
3637c478bd9Sstevel@tonic-gate 			break;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	}
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	if (curl != sz)
3687c478bd9Sstevel@tonic-gate 	{
3697c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
370*058561cbSjbeck 			sm_dprintf("milter_sys_read(%s): cmd read returned %ld, expecting %ld\n",
3717c478bd9Sstevel@tonic-gate 				m->mf_name, (long) curl, (long) sz);
3727c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
3737c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
374*058561cbSjbeck 				  "milter_sys_read(%s): cmd read returned %ld, expecting %ld",
3757c478bd9Sstevel@tonic-gate 				  m->mf_name, (long) curl, (long) sz);
3767c478bd9Sstevel@tonic-gate 		milter_error(m, e);
3777c478bd9Sstevel@tonic-gate 		return NULL;
3787c478bd9Sstevel@tonic-gate 	}
3797c478bd9Sstevel@tonic-gate 	return buf;
3807c478bd9Sstevel@tonic-gate }
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate static char *
383*058561cbSjbeck milter_read(m, cmd, rlen, to, e, where)
3847c478bd9Sstevel@tonic-gate 	struct milter *m;
3857c478bd9Sstevel@tonic-gate 	char *cmd;
3867c478bd9Sstevel@tonic-gate 	ssize_t *rlen;
3877c478bd9Sstevel@tonic-gate 	time_t to;
3887c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
389*058561cbSjbeck 	const char *where;
3907c478bd9Sstevel@tonic-gate {
3917c478bd9Sstevel@tonic-gate 	time_t readstart = 0;
3927c478bd9Sstevel@tonic-gate 	ssize_t expl;
3937c478bd9Sstevel@tonic-gate 	mi_int32 i;
394*058561cbSjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
3957c478bd9Sstevel@tonic-gate 	int cork = 0;
396*058561cbSjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
3977c478bd9Sstevel@tonic-gate 	char *buf;
3987c478bd9Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0)
4017c478bd9Sstevel@tonic-gate 	{
4027c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4037c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
404*058561cbSjbeck 				  "milter_read(%s): socket closed, where=%s",
405*058561cbSjbeck 				  m->mf_name, where);
4067c478bd9Sstevel@tonic-gate 		milter_error(m, e);
4077c478bd9Sstevel@tonic-gate 		return NULL;
4087c478bd9Sstevel@tonic-gate 	}
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	*rlen = 0;
4117c478bd9Sstevel@tonic-gate 	*cmd = '\0';
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	if (to > 0)
4147c478bd9Sstevel@tonic-gate 		readstart = curtime();
4157c478bd9Sstevel@tonic-gate 
416*058561cbSjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4177c478bd9Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4187c478bd9Sstevel@tonic-gate 		   sizeof(cork));
419*058561cbSjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
4207c478bd9Sstevel@tonic-gate 
421*058561cbSjbeck 	if (milter_sysread(m, data, sizeof(data), to, e, where) == NULL)
4227c478bd9Sstevel@tonic-gate 		return NULL;
4237c478bd9Sstevel@tonic-gate 
424*058561cbSjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4257c478bd9Sstevel@tonic-gate 	cork = 1;
4267c478bd9Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4277c478bd9Sstevel@tonic-gate 		   sizeof(cork));
428*058561cbSjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	/* reset timeout */
4317c478bd9Sstevel@tonic-gate 	if (to > 0)
4327c478bd9Sstevel@tonic-gate 	{
4337c478bd9Sstevel@tonic-gate 		time_t now;
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 		now = curtime();
4367c478bd9Sstevel@tonic-gate 		if (now - readstart >= to)
4377c478bd9Sstevel@tonic-gate 		{
4387c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
439*058561cbSjbeck 				sm_dprintf("milter_read(%s): timeout before data read, where=%s\n",
440*058561cbSjbeck 					m->mf_name, where);
4417c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
4427c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
443*058561cbSjbeck 					  "Milter read(%s): timeout before data read, where=%s",
444*058561cbSjbeck 					  m->mf_name, where);
4457c478bd9Sstevel@tonic-gate 			milter_error(m, e);
4467c478bd9Sstevel@tonic-gate 			return NULL;
4477c478bd9Sstevel@tonic-gate 		}
4487c478bd9Sstevel@tonic-gate 		to -= now - readstart;
4497c478bd9Sstevel@tonic-gate 	}
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	*cmd = data[MILTER_LEN_BYTES];
4527c478bd9Sstevel@tonic-gate 	data[MILTER_LEN_BYTES] = '\0';
4537c478bd9Sstevel@tonic-gate 	(void) memcpy(&i, data, MILTER_LEN_BYTES);
4547c478bd9Sstevel@tonic-gate 	expl = ntohl(i) - 1;
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	if (tTd(64, 25))
4577c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): expecting %ld bytes\n",
4587c478bd9Sstevel@tonic-gate 			m->mf_name, (long) expl);
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 	if (expl < 0)
4617c478bd9Sstevel@tonic-gate 	{
4627c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
463*058561cbSjbeck 			sm_dprintf("milter_read(%s): read size %ld out of range, where=%s\n",
464*058561cbSjbeck 				m->mf_name, (long) expl, where);
4657c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4667c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
467*058561cbSjbeck 				  "milter_read(%s): read size %ld out of range, where=%s",
468*058561cbSjbeck 				  m->mf_name, (long) expl, where);
4697c478bd9Sstevel@tonic-gate 		milter_error(m, e);
4707c478bd9Sstevel@tonic-gate 		return NULL;
4717c478bd9Sstevel@tonic-gate 	}
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	if (expl == 0)
4747c478bd9Sstevel@tonic-gate 		return NULL;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(expl);
4777c478bd9Sstevel@tonic-gate 
478*058561cbSjbeck 	if (milter_sysread(m, buf, expl, to, e, where) == NULL)
4797c478bd9Sstevel@tonic-gate 	{
4807c478bd9Sstevel@tonic-gate 		sm_free(buf); /* XXX */
4817c478bd9Sstevel@tonic-gate 		return NULL;
4827c478bd9Sstevel@tonic-gate 	}
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate 	if (tTd(64, 50))
4857c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): Returning %*s\n",
4867c478bd9Sstevel@tonic-gate 			m->mf_name, (int) expl, buf);
4877c478bd9Sstevel@tonic-gate 	*rlen = expl;
4887c478bd9Sstevel@tonic-gate 	return buf;
4897c478bd9Sstevel@tonic-gate }
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate /*
4927c478bd9Sstevel@tonic-gate **  MILTER_WRITE -- write to a remote milter filter
4937c478bd9Sstevel@tonic-gate **
4947c478bd9Sstevel@tonic-gate **	Parameters:
4957c478bd9Sstevel@tonic-gate **		m -- milter to read from.
4967c478bd9Sstevel@tonic-gate **		cmd -- command to send.
4977c478bd9Sstevel@tonic-gate **		buf -- optional command data.
4987c478bd9Sstevel@tonic-gate **		len -- length of buf.
4997c478bd9Sstevel@tonic-gate **		to -- timeout in seconds.
5007c478bd9Sstevel@tonic-gate **		e -- current envelope.
5017c478bd9Sstevel@tonic-gate **
5027c478bd9Sstevel@tonic-gate **	Returns:
5037c478bd9Sstevel@tonic-gate **		buf if successful, NULL otherwise
5047c478bd9Sstevel@tonic-gate **		Not actually used anywhere but function prototype
5057c478bd9Sstevel@tonic-gate **			must match milter_read()
5067c478bd9Sstevel@tonic-gate */
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate static char *
509*058561cbSjbeck milter_write(m, cmd, buf, len, to, e, where)
5107c478bd9Sstevel@tonic-gate 	struct milter *m;
511*058561cbSjbeck 	int cmd;
5127c478bd9Sstevel@tonic-gate 	char *buf;
5137c478bd9Sstevel@tonic-gate 	ssize_t len;
5147c478bd9Sstevel@tonic-gate 	time_t to;
5157c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
516*058561cbSjbeck 	const char *where;
5177c478bd9Sstevel@tonic-gate {
5187c478bd9Sstevel@tonic-gate 	time_t writestart = (time_t) 0;
5197c478bd9Sstevel@tonic-gate 	ssize_t sl, i;
5207c478bd9Sstevel@tonic-gate 	int num_vectors;
5217c478bd9Sstevel@tonic-gate 	mi_int32 nl;
522*058561cbSjbeck 	char command = (char) cmd;
5237c478bd9Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
5247c478bd9Sstevel@tonic-gate 	bool started = false;
5257c478bd9Sstevel@tonic-gate 	struct iovec vector[2];
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	/*
5287c478bd9Sstevel@tonic-gate 	**  At most two buffers will be written, though
5297c478bd9Sstevel@tonic-gate 	**  only one may actually be used (see num_vectors).
5307c478bd9Sstevel@tonic-gate 	**  The first is the size/command and the second is the command data.
5317c478bd9Sstevel@tonic-gate 	*/
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	if (len < 0 || len > MilterMaxDataSize)
5347c478bd9Sstevel@tonic-gate 	{
5357c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
5367c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): length %ld out of range\n",
5377c478bd9Sstevel@tonic-gate 				m->mf_name, (long) len);
5387c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5397c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
5407c478bd9Sstevel@tonic-gate 				  "milter_write(%s): length %ld out of range",
5417c478bd9Sstevel@tonic-gate 				  m->mf_name, (long) len);
5427c478bd9Sstevel@tonic-gate 		milter_error(m, e);
5437c478bd9Sstevel@tonic-gate 		return NULL;
5447c478bd9Sstevel@tonic-gate 	}
5457c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0)
5467c478bd9Sstevel@tonic-gate 	{
5477c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5487c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
5497c478bd9Sstevel@tonic-gate 				  "milter_write(%s): socket closed",
5507c478bd9Sstevel@tonic-gate 				  m->mf_name);
5517c478bd9Sstevel@tonic-gate 		milter_error(m, e);
5527c478bd9Sstevel@tonic-gate 		return NULL;
5537c478bd9Sstevel@tonic-gate 	}
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	if (tTd(64, 20))
5567c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
557*058561cbSjbeck 			   m->mf_name, command, (long) len);
5587c478bd9Sstevel@tonic-gate 
559*058561cbSjbeck 	nl = htonl(len + 1);	/* add 1 for the command char */
5607c478bd9Sstevel@tonic-gate 	(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
561*058561cbSjbeck 	data[MILTER_LEN_BYTES] = command;
5627c478bd9Sstevel@tonic-gate 	sl = MILTER_LEN_BYTES + 1;
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 	/* set up the vector for the size / command */
5657c478bd9Sstevel@tonic-gate 	vector[0].iov_base = (void *) data;
5667c478bd9Sstevel@tonic-gate 	vector[0].iov_len  = sl;
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 	/*
5697c478bd9Sstevel@tonic-gate 	**  Determine if there is command data.  If so, there will be two
5707c478bd9Sstevel@tonic-gate 	**  vectors.  If not, there will be only one.  The vectors are set
5717c478bd9Sstevel@tonic-gate 	**  up here and 'num_vectors' and 'sl' are set appropriately.
5727c478bd9Sstevel@tonic-gate 	*/
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	/* NOTE:  len<0 has already been checked for.  Pedantic */
5757c478bd9Sstevel@tonic-gate 	if (len <= 0 || buf == NULL)
5767c478bd9Sstevel@tonic-gate 	{
5777c478bd9Sstevel@tonic-gate 		/* There is no command data -- only a size / command data */
5787c478bd9Sstevel@tonic-gate 		num_vectors = 1;
5797c478bd9Sstevel@tonic-gate 	}
5807c478bd9Sstevel@tonic-gate 	else
5817c478bd9Sstevel@tonic-gate 	{
5827c478bd9Sstevel@tonic-gate 		/*
5837c478bd9Sstevel@tonic-gate 		**  There is both size / command and command data.
5847c478bd9Sstevel@tonic-gate 		**  Set up the vector for the command data.
5857c478bd9Sstevel@tonic-gate 		*/
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 		num_vectors = 2;
5887c478bd9Sstevel@tonic-gate 		sl += len;
5897c478bd9Sstevel@tonic-gate 		vector[1].iov_base = (void *) buf;
5907c478bd9Sstevel@tonic-gate 		vector[1].iov_len  = len;
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 		if (tTd(64, 50))
5937c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): Sending %*s\n",
5947c478bd9Sstevel@tonic-gate 				   m->mf_name, (int) len, buf);
5957c478bd9Sstevel@tonic-gate 	}
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	if (to > 0)
5987c478bd9Sstevel@tonic-gate 	{
5997c478bd9Sstevel@tonic-gate 		writestart = curtime();
600*058561cbSjbeck 		MILTER_TIMEOUT("write", to, true, started, where);
6017c478bd9Sstevel@tonic-gate 	}
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	/* write the vector(s) */
6047c478bd9Sstevel@tonic-gate 	i = writev(m->mf_sock, vector, num_vectors);
6057c478bd9Sstevel@tonic-gate 	if (i != sl)
6067c478bd9Sstevel@tonic-gate 	{
6077c478bd9Sstevel@tonic-gate 		int save_errno = errno;
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
6107c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
611*058561cbSjbeck 				   m->mf_name, command, (long) i, (long) sl,
6127c478bd9Sstevel@tonic-gate 				   sm_errstring(save_errno));
6137c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
6147c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6157c478bd9Sstevel@tonic-gate 				  "Milter (%s): write(%c) returned %ld, expected %ld: %s",
616*058561cbSjbeck 				  m->mf_name, command, (long) i, (long) sl,
6177c478bd9Sstevel@tonic-gate 				  sm_errstring(save_errno));
6187c478bd9Sstevel@tonic-gate 		milter_error(m, e);
6197c478bd9Sstevel@tonic-gate 		return NULL;
6207c478bd9Sstevel@tonic-gate 	}
6217c478bd9Sstevel@tonic-gate 	return buf;
6227c478bd9Sstevel@tonic-gate }
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate /*
6257c478bd9Sstevel@tonic-gate **  Utility functions
6267c478bd9Sstevel@tonic-gate */
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate /*
6297c478bd9Sstevel@tonic-gate **  MILTER_OPEN -- connect to remote milter filter
6307c478bd9Sstevel@tonic-gate **
6317c478bd9Sstevel@tonic-gate **	Parameters:
6327c478bd9Sstevel@tonic-gate **		m -- milter to connect to.
6337c478bd9Sstevel@tonic-gate **		parseonly -- parse but don't connect.
6347c478bd9Sstevel@tonic-gate **		e -- current envelope.
6357c478bd9Sstevel@tonic-gate **
6367c478bd9Sstevel@tonic-gate **	Returns:
6377c478bd9Sstevel@tonic-gate **		connected socket if successful && !parseonly,
6387c478bd9Sstevel@tonic-gate **		0 upon parse success if parseonly,
6397c478bd9Sstevel@tonic-gate **		-1 otherwise.
6407c478bd9Sstevel@tonic-gate */
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate static jmp_buf	MilterConnectTimeout;
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate static int
6457c478bd9Sstevel@tonic-gate milter_open(m, parseonly, e)
6467c478bd9Sstevel@tonic-gate 	struct milter *m;
6477c478bd9Sstevel@tonic-gate 	bool parseonly;
6487c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
6497c478bd9Sstevel@tonic-gate {
6507c478bd9Sstevel@tonic-gate 	int sock = 0;
6517c478bd9Sstevel@tonic-gate 	SOCKADDR_LEN_T addrlen = 0;
6527c478bd9Sstevel@tonic-gate 	int addrno = 0;
6537c478bd9Sstevel@tonic-gate 	int save_errno;
6547c478bd9Sstevel@tonic-gate 	char *p;
6557c478bd9Sstevel@tonic-gate 	char *colon;
6567c478bd9Sstevel@tonic-gate 	char *at;
6577c478bd9Sstevel@tonic-gate 	struct hostent *hp = NULL;
6587c478bd9Sstevel@tonic-gate 	SOCKADDR addr;
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
6617c478bd9Sstevel@tonic-gate 	{
6627c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
6637c478bd9Sstevel@tonic-gate 			sm_dprintf("X%s: empty or missing socket information\n",
6647c478bd9Sstevel@tonic-gate 				   m->mf_name);
6657c478bd9Sstevel@tonic-gate 		if (parseonly)
6667c478bd9Sstevel@tonic-gate 			syserr("X%s: empty or missing socket information",
6677c478bd9Sstevel@tonic-gate 			       m->mf_name);
6687c478bd9Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
6697c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6707c478bd9Sstevel@tonic-gate 				  "Milter (%s): empty or missing socket information",
6717c478bd9Sstevel@tonic-gate 				  m->mf_name);
6727c478bd9Sstevel@tonic-gate 		milter_error(m, e);
6737c478bd9Sstevel@tonic-gate 		return -1;
6747c478bd9Sstevel@tonic-gate 	}
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	/* protocol:filename or protocol:port@host */
677*058561cbSjbeck 	memset(&addr, '\0', sizeof(addr));
6787c478bd9Sstevel@tonic-gate 	p = m->mf_conn;
6797c478bd9Sstevel@tonic-gate 	colon = strchr(p, ':');
6807c478bd9Sstevel@tonic-gate 	if (colon != NULL)
6817c478bd9Sstevel@tonic-gate 	{
6827c478bd9Sstevel@tonic-gate 		*colon = '\0';
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 		if (*p == '\0')
6857c478bd9Sstevel@tonic-gate 		{
6867c478bd9Sstevel@tonic-gate # if NETUNIX
6877c478bd9Sstevel@tonic-gate 			/* default to AF_UNIX */
6887c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
6897c478bd9Sstevel@tonic-gate # else /* NETUNIX */
6907c478bd9Sstevel@tonic-gate #  if NETINET
6917c478bd9Sstevel@tonic-gate 			/* default to AF_INET */
6927c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
6937c478bd9Sstevel@tonic-gate #  else /* NETINET */
6947c478bd9Sstevel@tonic-gate #   if NETINET6
6957c478bd9Sstevel@tonic-gate 			/* default to AF_INET6 */
6967c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
6977c478bd9Sstevel@tonic-gate #   else /* NETINET6 */
6987c478bd9Sstevel@tonic-gate 			/* no protocols available */
6997c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
7007c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7017c478bd9Sstevel@tonic-gate 					  "Milter (%s): no valid socket protocols available",
7027c478bd9Sstevel@tonic-gate 					  m->mf_name);
7037c478bd9Sstevel@tonic-gate 			milter_error(m, e);
7047c478bd9Sstevel@tonic-gate 			return -1;
7057c478bd9Sstevel@tonic-gate #   endif /* NETINET6 */
7067c478bd9Sstevel@tonic-gate #  endif /* NETINET */
7077c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
7087c478bd9Sstevel@tonic-gate 		}
7097c478bd9Sstevel@tonic-gate # if NETUNIX
7107c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "unix") == 0 ||
7117c478bd9Sstevel@tonic-gate 			 sm_strcasecmp(p, "local") == 0)
7127c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
7137c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
7147c478bd9Sstevel@tonic-gate # if NETINET
7157c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet") == 0)
7167c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
7177c478bd9Sstevel@tonic-gate # endif /* NETINET */
7187c478bd9Sstevel@tonic-gate # if NETINET6
7197c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet6") == 0)
7207c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
7217c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
7227c478bd9Sstevel@tonic-gate 		else
7237c478bd9Sstevel@tonic-gate 		{
7247c478bd9Sstevel@tonic-gate # ifdef EPROTONOSUPPORT
7257c478bd9Sstevel@tonic-gate 			errno = EPROTONOSUPPORT;
7267c478bd9Sstevel@tonic-gate # else /* EPROTONOSUPPORT */
7277c478bd9Sstevel@tonic-gate 			errno = EINVAL;
7287c478bd9Sstevel@tonic-gate # endif /* EPROTONOSUPPORT */
7297c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
7307c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: unknown socket type %s\n",
7317c478bd9Sstevel@tonic-gate 					m->mf_name, p);
7327c478bd9Sstevel@tonic-gate 			if (parseonly)
7337c478bd9Sstevel@tonic-gate 				syserr("X%s: unknown socket type %s",
7347c478bd9Sstevel@tonic-gate 				       m->mf_name, p);
7357c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7367c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7377c478bd9Sstevel@tonic-gate 					  "Milter (%s): unknown socket type %s",
7387c478bd9Sstevel@tonic-gate 					  m->mf_name, p);
7397c478bd9Sstevel@tonic-gate 			milter_error(m, e);
7407c478bd9Sstevel@tonic-gate 			return -1;
7417c478bd9Sstevel@tonic-gate 		}
7427c478bd9Sstevel@tonic-gate 		*colon++ = ':';
7437c478bd9Sstevel@tonic-gate 	}
7447c478bd9Sstevel@tonic-gate 	else
7457c478bd9Sstevel@tonic-gate 	{
7467c478bd9Sstevel@tonic-gate 		/* default to AF_UNIX */
7477c478bd9Sstevel@tonic-gate 		addr.sa.sa_family = AF_UNIX;
7487c478bd9Sstevel@tonic-gate 		colon = p;
7497c478bd9Sstevel@tonic-gate 	}
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate # if NETUNIX
7527c478bd9Sstevel@tonic-gate 	if (addr.sa.sa_family == AF_UNIX)
7537c478bd9Sstevel@tonic-gate 	{
7547c478bd9Sstevel@tonic-gate 		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7557c478bd9Sstevel@tonic-gate 
7567c478bd9Sstevel@tonic-gate 		at = colon;
757*058561cbSjbeck 		if (strlen(colon) >= sizeof(addr.sunix.sun_path))
7587c478bd9Sstevel@tonic-gate 		{
7597c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
7607c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s too long\n",
7617c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
7627c478bd9Sstevel@tonic-gate 			errno = EINVAL;
7637c478bd9Sstevel@tonic-gate 			if (parseonly)
7647c478bd9Sstevel@tonic-gate 				syserr("X%s: local socket name %s too long",
7657c478bd9Sstevel@tonic-gate 				       m->mf_name, colon);
7667c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7677c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7687c478bd9Sstevel@tonic-gate 					  "Milter (%s): local socket name %s too long",
7697c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
7707c478bd9Sstevel@tonic-gate 			milter_error(m, e);
7717c478bd9Sstevel@tonic-gate 			return -1;
7727c478bd9Sstevel@tonic-gate 		}
7737c478bd9Sstevel@tonic-gate 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7747c478bd9Sstevel@tonic-gate 				 S_IRUSR|S_IWUSR, NULL);
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 		/* if just parsing .cf file, socket doesn't need to exist */
7777c478bd9Sstevel@tonic-gate 		if (parseonly && errno == ENOENT)
7787c478bd9Sstevel@tonic-gate 		{
7797c478bd9Sstevel@tonic-gate 			if (OpMode == MD_DAEMON ||
7807c478bd9Sstevel@tonic-gate 			    OpMode == MD_FGDAEMON)
7817c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
7827c478bd9Sstevel@tonic-gate 						     "WARNING: X%s: local socket name %s missing\n",
7837c478bd9Sstevel@tonic-gate 						     m->mf_name, colon);
7847c478bd9Sstevel@tonic-gate 		}
7857c478bd9Sstevel@tonic-gate 		else if (errno != 0)
7867c478bd9Sstevel@tonic-gate 		{
7877c478bd9Sstevel@tonic-gate 			/* if not safe, don't create */
7887c478bd9Sstevel@tonic-gate 			save_errno = errno;
7897c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
7907c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s unsafe\n",
7917c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
7927c478bd9Sstevel@tonic-gate 			errno = save_errno;
7937c478bd9Sstevel@tonic-gate 			if (parseonly)
7947c478bd9Sstevel@tonic-gate 			{
7957c478bd9Sstevel@tonic-gate 				if (OpMode == MD_DAEMON ||
7967c478bd9Sstevel@tonic-gate 				    OpMode == MD_FGDAEMON ||
7977c478bd9Sstevel@tonic-gate 				    OpMode == MD_SMTP)
7987c478bd9Sstevel@tonic-gate 					syserr("X%s: local socket name %s unsafe",
7997c478bd9Sstevel@tonic-gate 					       m->mf_name, colon);
8007c478bd9Sstevel@tonic-gate 			}
8017c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8027c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8037c478bd9Sstevel@tonic-gate 					  "Milter (%s): local socket name %s unsafe",
8047c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
8057c478bd9Sstevel@tonic-gate 			milter_error(m, e);
8067c478bd9Sstevel@tonic-gate 			return -1;
8077c478bd9Sstevel@tonic-gate 		}
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(addr.sunix.sun_path, colon,
810*058561cbSjbeck 			       sizeof(addr.sunix.sun_path));
8117c478bd9Sstevel@tonic-gate 		addrlen = sizeof(struct sockaddr_un);
8127c478bd9Sstevel@tonic-gate 	}
8137c478bd9Sstevel@tonic-gate 	else
8147c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
8157c478bd9Sstevel@tonic-gate # if NETINET || NETINET6
8167c478bd9Sstevel@tonic-gate 	if (false
8177c478bd9Sstevel@tonic-gate #  if NETINET
8187c478bd9Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET
8197c478bd9Sstevel@tonic-gate #  endif /* NETINET */
8207c478bd9Sstevel@tonic-gate #  if NETINET6
8217c478bd9Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET6
8227c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
8237c478bd9Sstevel@tonic-gate 		 )
8247c478bd9Sstevel@tonic-gate 	{
8257c478bd9Sstevel@tonic-gate 		unsigned short port;
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 		/* Parse port@host */
8287c478bd9Sstevel@tonic-gate 		at = strchr(colon, '@');
8297c478bd9Sstevel@tonic-gate 		if (at == NULL)
8307c478bd9Sstevel@tonic-gate 		{
8317c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
8327c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: bad address %s (expected port@host)\n",
8337c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
8347c478bd9Sstevel@tonic-gate 			if (parseonly)
8357c478bd9Sstevel@tonic-gate 				syserr("X%s: bad address %s (expected port@host)",
8367c478bd9Sstevel@tonic-gate 				       m->mf_name, colon);
8377c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8387c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8397c478bd9Sstevel@tonic-gate 					  "Milter (%s): bad address %s (expected port@host)",
8407c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
8417c478bd9Sstevel@tonic-gate 			milter_error(m, e);
8427c478bd9Sstevel@tonic-gate 			return -1;
8437c478bd9Sstevel@tonic-gate 		}
8447c478bd9Sstevel@tonic-gate 		*at = '\0';
8457c478bd9Sstevel@tonic-gate 		if (isascii(*colon) && isdigit(*colon))
8467c478bd9Sstevel@tonic-gate 			port = htons((unsigned short) atoi(colon));
8477c478bd9Sstevel@tonic-gate 		else
8487c478bd9Sstevel@tonic-gate 		{
8497c478bd9Sstevel@tonic-gate #  ifdef NO_GETSERVBYNAME
8507c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
8517c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: invalid port number %s\n",
8527c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
8537c478bd9Sstevel@tonic-gate 			if (parseonly)
8547c478bd9Sstevel@tonic-gate 				syserr("X%s: invalid port number %s",
8557c478bd9Sstevel@tonic-gate 				       m->mf_name, colon);
8567c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8577c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8587c478bd9Sstevel@tonic-gate 					  "Milter (%s): invalid port number %s",
8597c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
8607c478bd9Sstevel@tonic-gate 			milter_error(m, e);
8617c478bd9Sstevel@tonic-gate 			return -1;
8627c478bd9Sstevel@tonic-gate #  else /* NO_GETSERVBYNAME */
863*058561cbSjbeck 			struct servent *sp;
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 			sp = getservbyname(colon, "tcp");
8667c478bd9Sstevel@tonic-gate 			if (sp == NULL)
8677c478bd9Sstevel@tonic-gate 			{
8687c478bd9Sstevel@tonic-gate 				save_errno = errno;
8697c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
8707c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: unknown port name %s\n",
8717c478bd9Sstevel@tonic-gate 						m->mf_name, colon);
8727c478bd9Sstevel@tonic-gate 				errno = save_errno;
8737c478bd9Sstevel@tonic-gate 				if (parseonly)
8747c478bd9Sstevel@tonic-gate 					syserr("X%s: unknown port name %s",
8757c478bd9Sstevel@tonic-gate 					       m->mf_name, colon);
8767c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
8777c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
8787c478bd9Sstevel@tonic-gate 						  "Milter (%s): unknown port name %s",
8797c478bd9Sstevel@tonic-gate 						  m->mf_name, colon);
8807c478bd9Sstevel@tonic-gate 				milter_error(m, e);
8817c478bd9Sstevel@tonic-gate 				return -1;
8827c478bd9Sstevel@tonic-gate 			}
8837c478bd9Sstevel@tonic-gate 			port = sp->s_port;
8847c478bd9Sstevel@tonic-gate #  endif /* NO_GETSERVBYNAME */
8857c478bd9Sstevel@tonic-gate 		}
8867c478bd9Sstevel@tonic-gate 		*at++ = '@';
8877c478bd9Sstevel@tonic-gate 		if (*at == '[')
8887c478bd9Sstevel@tonic-gate 		{
8897c478bd9Sstevel@tonic-gate 			char *end;
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate 			end = strchr(at, ']');
8927c478bd9Sstevel@tonic-gate 			if (end != NULL)
8937c478bd9Sstevel@tonic-gate 			{
8947c478bd9Sstevel@tonic-gate 				bool found = false;
8957c478bd9Sstevel@tonic-gate #  if NETINET
8967c478bd9Sstevel@tonic-gate 				unsigned long hid = INADDR_NONE;
8977c478bd9Sstevel@tonic-gate #  endif /* NETINET */
8987c478bd9Sstevel@tonic-gate #  if NETINET6
8997c478bd9Sstevel@tonic-gate 				struct sockaddr_in6 hid6;
9007c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 				*end = '\0';
9037c478bd9Sstevel@tonic-gate #  if NETINET
9047c478bd9Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET &&
9057c478bd9Sstevel@tonic-gate 				    (hid = inet_addr(&at[1])) != INADDR_NONE)
9067c478bd9Sstevel@tonic-gate 				{
9077c478bd9Sstevel@tonic-gate 					addr.sin.sin_addr.s_addr = hid;
9087c478bd9Sstevel@tonic-gate 					addr.sin.sin_port = port;
9097c478bd9Sstevel@tonic-gate 					found = true;
9107c478bd9Sstevel@tonic-gate 				}
9117c478bd9Sstevel@tonic-gate #  endif /* NETINET */
9127c478bd9Sstevel@tonic-gate #  if NETINET6
913*058561cbSjbeck 				(void) memset(&hid6, '\0', sizeof(hid6));
9147c478bd9Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET6 &&
9157c478bd9Sstevel@tonic-gate 				    anynet_pton(AF_INET6, &at[1],
9167c478bd9Sstevel@tonic-gate 						&hid6.sin6_addr) == 1)
9177c478bd9Sstevel@tonic-gate 				{
9187c478bd9Sstevel@tonic-gate 					addr.sin6.sin6_addr = hid6.sin6_addr;
9197c478bd9Sstevel@tonic-gate 					addr.sin6.sin6_port = port;
9207c478bd9Sstevel@tonic-gate 					found = true;
9217c478bd9Sstevel@tonic-gate 				}
9227c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
9237c478bd9Sstevel@tonic-gate 				*end = ']';
9247c478bd9Sstevel@tonic-gate 				if (!found)
9257c478bd9Sstevel@tonic-gate 				{
9267c478bd9Sstevel@tonic-gate 					if (tTd(64, 5))
9277c478bd9Sstevel@tonic-gate 						sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9287c478bd9Sstevel@tonic-gate 							m->mf_name, at);
9297c478bd9Sstevel@tonic-gate 					if (parseonly)
9307c478bd9Sstevel@tonic-gate 						syserr("X%s: Invalid numeric domain spec \"%s\"",
9317c478bd9Sstevel@tonic-gate 						       m->mf_name, at);
9327c478bd9Sstevel@tonic-gate 					else if (MilterLogLevel > 0)
9337c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
9347c478bd9Sstevel@tonic-gate 							  "Milter (%s): Invalid numeric domain spec \"%s\"",
9357c478bd9Sstevel@tonic-gate 							  m->mf_name, at);
9367c478bd9Sstevel@tonic-gate 					milter_error(m, e);
9377c478bd9Sstevel@tonic-gate 					return -1;
9387c478bd9Sstevel@tonic-gate 				}
9397c478bd9Sstevel@tonic-gate 			}
9407c478bd9Sstevel@tonic-gate 			else
9417c478bd9Sstevel@tonic-gate 			{
9427c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
9437c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9447c478bd9Sstevel@tonic-gate 						m->mf_name, at);
9457c478bd9Sstevel@tonic-gate 				if (parseonly)
9467c478bd9Sstevel@tonic-gate 					syserr("X%s: Invalid numeric domain spec \"%s\"",
9477c478bd9Sstevel@tonic-gate 					       m->mf_name, at);
9487c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9497c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9507c478bd9Sstevel@tonic-gate 						  "Milter (%s): Invalid numeric domain spec \"%s\"",
9517c478bd9Sstevel@tonic-gate 						  m->mf_name, at);
9527c478bd9Sstevel@tonic-gate 				milter_error(m, e);
9537c478bd9Sstevel@tonic-gate 				return -1;
9547c478bd9Sstevel@tonic-gate 			}
9557c478bd9Sstevel@tonic-gate 		}
9567c478bd9Sstevel@tonic-gate 		else
9577c478bd9Sstevel@tonic-gate 		{
9587c478bd9Sstevel@tonic-gate 			hp = sm_gethostbyname(at, addr.sa.sa_family);
9597c478bd9Sstevel@tonic-gate 			if (hp == NULL)
9607c478bd9Sstevel@tonic-gate 			{
9617c478bd9Sstevel@tonic-gate 				save_errno = errno;
9627c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
9637c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown host name %s\n",
9647c478bd9Sstevel@tonic-gate 						   m->mf_name, at);
9657c478bd9Sstevel@tonic-gate 				errno = save_errno;
9667c478bd9Sstevel@tonic-gate 				if (parseonly)
9677c478bd9Sstevel@tonic-gate 					syserr("X%s: Unknown host name %s",
9687c478bd9Sstevel@tonic-gate 					       m->mf_name, at);
9697c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9707c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9717c478bd9Sstevel@tonic-gate 						  "Milter (%s): Unknown host name %s",
9727c478bd9Sstevel@tonic-gate 						  m->mf_name, at);
9737c478bd9Sstevel@tonic-gate 				milter_error(m, e);
9747c478bd9Sstevel@tonic-gate 				return -1;
9757c478bd9Sstevel@tonic-gate 			}
9767c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = hp->h_addrtype;
9777c478bd9Sstevel@tonic-gate 			switch (hp->h_addrtype)
9787c478bd9Sstevel@tonic-gate 			{
9797c478bd9Sstevel@tonic-gate #  if NETINET
9807c478bd9Sstevel@tonic-gate 			  case AF_INET:
9817c478bd9Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
9827c478bd9Sstevel@tonic-gate 					hp->h_addr, INADDRSZ);
9837c478bd9Sstevel@tonic-gate 				addr.sin.sin_port = port;
9847c478bd9Sstevel@tonic-gate 				addrlen = sizeof(struct sockaddr_in);
9857c478bd9Sstevel@tonic-gate 				addrno = 1;
9867c478bd9Sstevel@tonic-gate 				break;
9877c478bd9Sstevel@tonic-gate #  endif /* NETINET */
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate #  if NETINET6
9907c478bd9Sstevel@tonic-gate 			  case AF_INET6:
9917c478bd9Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
9927c478bd9Sstevel@tonic-gate 					hp->h_addr, IN6ADDRSZ);
9937c478bd9Sstevel@tonic-gate 				addr.sin6.sin6_port = port;
9947c478bd9Sstevel@tonic-gate 				addrlen = sizeof(struct sockaddr_in6);
9957c478bd9Sstevel@tonic-gate 				addrno = 1;
9967c478bd9Sstevel@tonic-gate 				break;
9977c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 			  default:
10007c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
10017c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
10027c478bd9Sstevel@tonic-gate 						   m->mf_name, at,
10037c478bd9Sstevel@tonic-gate 						   hp->h_addrtype);
10047c478bd9Sstevel@tonic-gate 				if (parseonly)
10057c478bd9Sstevel@tonic-gate 					syserr("X%s: Unknown protocol for %s (%d)",
10067c478bd9Sstevel@tonic-gate 					       m->mf_name, at, hp->h_addrtype);
10077c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
10087c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
10097c478bd9Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
10107c478bd9Sstevel@tonic-gate 						  m->mf_name, at,
10117c478bd9Sstevel@tonic-gate 						  hp->h_addrtype);
10127c478bd9Sstevel@tonic-gate 				milter_error(m, e);
10137c478bd9Sstevel@tonic-gate #  if NETINET6
10147c478bd9Sstevel@tonic-gate 				freehostent(hp);
10157c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
10167c478bd9Sstevel@tonic-gate 				return -1;
10177c478bd9Sstevel@tonic-gate 			}
10187c478bd9Sstevel@tonic-gate 		}
10197c478bd9Sstevel@tonic-gate 	}
10207c478bd9Sstevel@tonic-gate 	else
10217c478bd9Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
10227c478bd9Sstevel@tonic-gate 	{
10237c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
10247c478bd9Sstevel@tonic-gate 			sm_dprintf("X%s: unknown socket protocol\n",
10257c478bd9Sstevel@tonic-gate 				   m->mf_name);
10267c478bd9Sstevel@tonic-gate 		if (parseonly)
10277c478bd9Sstevel@tonic-gate 			syserr("X%s: unknown socket protocol", m->mf_name);
10287c478bd9Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
10297c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
10307c478bd9Sstevel@tonic-gate 				  "Milter (%s): unknown socket protocol",
10317c478bd9Sstevel@tonic-gate 				  m->mf_name);
10327c478bd9Sstevel@tonic-gate 		milter_error(m, e);
10337c478bd9Sstevel@tonic-gate 		return -1;
10347c478bd9Sstevel@tonic-gate 	}
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 	/* just parsing through? */
10377c478bd9Sstevel@tonic-gate 	if (parseonly)
10387c478bd9Sstevel@tonic-gate 	{
10397c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_READY;
10407c478bd9Sstevel@tonic-gate # if NETINET6
10417c478bd9Sstevel@tonic-gate 		if (hp != NULL)
10427c478bd9Sstevel@tonic-gate 			freehostent(hp);
10437c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
10447c478bd9Sstevel@tonic-gate 		return 0;
10457c478bd9Sstevel@tonic-gate 	}
10467c478bd9Sstevel@tonic-gate 
10477c478bd9Sstevel@tonic-gate 	/* sanity check */
10487c478bd9Sstevel@tonic-gate 	if (m->mf_state != SMFS_READY &&
10497c478bd9Sstevel@tonic-gate 	    m->mf_state != SMFS_CLOSED)
10507c478bd9Sstevel@tonic-gate 	{
10517c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
10527c478bd9Sstevel@tonic-gate 		if (tTd(64, 1))
10537c478bd9Sstevel@tonic-gate 			sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
10547c478bd9Sstevel@tonic-gate 				   m->mf_name, (char) m->mf_state);
10557c478bd9Sstevel@tonic-gate 		milter_error(m, e);
10567c478bd9Sstevel@tonic-gate # if NETINET6
10577c478bd9Sstevel@tonic-gate 		if (hp != NULL)
10587c478bd9Sstevel@tonic-gate 			freehostent(hp);
10597c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
10607c478bd9Sstevel@tonic-gate 		return -1;
10617c478bd9Sstevel@tonic-gate 	}
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 	/* nope, actually connecting */
10647c478bd9Sstevel@tonic-gate 	for (;;)
10657c478bd9Sstevel@tonic-gate 	{
10667c478bd9Sstevel@tonic-gate 		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
10677c478bd9Sstevel@tonic-gate 		if (sock < 0)
10687c478bd9Sstevel@tonic-gate 		{
10697c478bd9Sstevel@tonic-gate 			save_errno = errno;
10707c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
10717c478bd9Sstevel@tonic-gate 				sm_dprintf("Milter (%s): error creating socket: %s\n",
10727c478bd9Sstevel@tonic-gate 					   m->mf_name,
10737c478bd9Sstevel@tonic-gate 					   sm_errstring(save_errno));
10747c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
10757c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
10767c478bd9Sstevel@tonic-gate 					  "Milter (%s): error creating socket: %s",
10777c478bd9Sstevel@tonic-gate 					  m->mf_name, sm_errstring(save_errno));
10787c478bd9Sstevel@tonic-gate 			milter_error(m, e);
10797c478bd9Sstevel@tonic-gate # if NETINET6
10807c478bd9Sstevel@tonic-gate 			if (hp != NULL)
10817c478bd9Sstevel@tonic-gate 				freehostent(hp);
10827c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
10837c478bd9Sstevel@tonic-gate 			return -1;
10847c478bd9Sstevel@tonic-gate 		}
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate 		if (setjmp(MilterConnectTimeout) == 0)
10877c478bd9Sstevel@tonic-gate 		{
10887c478bd9Sstevel@tonic-gate 			SM_EVENT *ev = NULL;
10897c478bd9Sstevel@tonic-gate 			int i;
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_CONNECT] > 0)
10927c478bd9Sstevel@tonic-gate 				ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
10937c478bd9Sstevel@tonic-gate 						 milter_connect_timeout, 0);
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate 			i = connect(sock, (struct sockaddr *) &addr, addrlen);
10967c478bd9Sstevel@tonic-gate 			save_errno = errno;
10977c478bd9Sstevel@tonic-gate 			if (ev != NULL)
10987c478bd9Sstevel@tonic-gate 				sm_clrevent(ev);
10997c478bd9Sstevel@tonic-gate 			errno = save_errno;
11007c478bd9Sstevel@tonic-gate 			if (i >= 0)
11017c478bd9Sstevel@tonic-gate 				break;
11027c478bd9Sstevel@tonic-gate 		}
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 		/* couldn't connect.... try next address */
11057c478bd9Sstevel@tonic-gate 		save_errno = errno;
11067c478bd9Sstevel@tonic-gate 		p = CurHostName;
11077c478bd9Sstevel@tonic-gate 		CurHostName = at;
11087c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
11097c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_open (%s): open %s failed: %s\n",
11107c478bd9Sstevel@tonic-gate 				   m->mf_name, at, sm_errstring(save_errno));
11117c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 13)
11127c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
11137c478bd9Sstevel@tonic-gate 				  "Milter (%s): open %s failed: %s",
11147c478bd9Sstevel@tonic-gate 				  m->mf_name, at, sm_errstring(save_errno));
11157c478bd9Sstevel@tonic-gate 		CurHostName = p;
11167c478bd9Sstevel@tonic-gate 		(void) close(sock);
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 		/* try next address */
11197c478bd9Sstevel@tonic-gate 		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
11207c478bd9Sstevel@tonic-gate 		{
11217c478bd9Sstevel@tonic-gate 			switch (addr.sa.sa_family)
11227c478bd9Sstevel@tonic-gate 			{
11237c478bd9Sstevel@tonic-gate # if NETINET
11247c478bd9Sstevel@tonic-gate 			  case AF_INET:
11257c478bd9Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
11267c478bd9Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11277c478bd9Sstevel@tonic-gate 					INADDRSZ);
11287c478bd9Sstevel@tonic-gate 				break;
11297c478bd9Sstevel@tonic-gate # endif /* NETINET */
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate # if NETINET6
11327c478bd9Sstevel@tonic-gate 			  case AF_INET6:
11337c478bd9Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
11347c478bd9Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11357c478bd9Sstevel@tonic-gate 					IN6ADDRSZ);
11367c478bd9Sstevel@tonic-gate 				break;
11377c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate 			  default:
11407c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
11417c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
11427c478bd9Sstevel@tonic-gate 						   m->mf_name, at,
11437c478bd9Sstevel@tonic-gate 						   hp->h_addrtype);
11447c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
11457c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
11467c478bd9Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
11477c478bd9Sstevel@tonic-gate 						  m->mf_name, at,
11487c478bd9Sstevel@tonic-gate 						  hp->h_addrtype);
11497c478bd9Sstevel@tonic-gate 				milter_error(m, e);
11507c478bd9Sstevel@tonic-gate # if NETINET6
11517c478bd9Sstevel@tonic-gate 				freehostent(hp);
11527c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
11537c478bd9Sstevel@tonic-gate 				return -1;
11547c478bd9Sstevel@tonic-gate 			}
11557c478bd9Sstevel@tonic-gate 			continue;
11567c478bd9Sstevel@tonic-gate 		}
11577c478bd9Sstevel@tonic-gate 		p = CurHostName;
11587c478bd9Sstevel@tonic-gate 		CurHostName = at;
11597c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
11607c478bd9Sstevel@tonic-gate 			sm_dprintf("X%s: error connecting to filter: %s\n",
11617c478bd9Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno));
11627c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
11637c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
11647c478bd9Sstevel@tonic-gate 				  "Milter (%s): error connecting to filter: %s",
11657c478bd9Sstevel@tonic-gate 				  m->mf_name, sm_errstring(save_errno));
11667c478bd9Sstevel@tonic-gate 		CurHostName = p;
11677c478bd9Sstevel@tonic-gate 		milter_error(m, e);
11687c478bd9Sstevel@tonic-gate # if NETINET6
11697c478bd9Sstevel@tonic-gate 		if (hp != NULL)
11707c478bd9Sstevel@tonic-gate 			freehostent(hp);
11717c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
11727c478bd9Sstevel@tonic-gate 		return -1;
11737c478bd9Sstevel@tonic-gate 	}
11747c478bd9Sstevel@tonic-gate 	m->mf_state = SMFS_OPEN;
11757c478bd9Sstevel@tonic-gate # if NETINET6
11767c478bd9Sstevel@tonic-gate 	if (hp != NULL)
11777c478bd9Sstevel@tonic-gate 	{
11787c478bd9Sstevel@tonic-gate 		freehostent(hp);
11797c478bd9Sstevel@tonic-gate 		hp = NULL;
11807c478bd9Sstevel@tonic-gate 	}
11817c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
1182*058561cbSjbeck # if MILTER_NO_NAGLE && !defined(TCP_CORK)
11837c478bd9Sstevel@tonic-gate 	{
11847c478bd9Sstevel@tonic-gate 		int nodelay = 1;
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 		setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY,
11877c478bd9Sstevel@tonic-gate 			   (char *)&nodelay, sizeof(nodelay));
11887c478bd9Sstevel@tonic-gate 	}
1189*058561cbSjbeck # endif /* MILTER_NO_NAGLE && !defined(TCP_CORK) */
11907c478bd9Sstevel@tonic-gate 	return sock;
11917c478bd9Sstevel@tonic-gate }
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate static void
11947c478bd9Sstevel@tonic-gate milter_connect_timeout(ignore)
11957c478bd9Sstevel@tonic-gate 	int ignore;
11967c478bd9Sstevel@tonic-gate {
11977c478bd9Sstevel@tonic-gate 	/*
11987c478bd9Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
11997c478bd9Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
12007c478bd9Sstevel@tonic-gate 	**	DOING.
12017c478bd9Sstevel@tonic-gate 	*/
12027c478bd9Sstevel@tonic-gate 
12037c478bd9Sstevel@tonic-gate 	errno = ETIMEDOUT;
12047c478bd9Sstevel@tonic-gate 	longjmp(MilterConnectTimeout, 1);
12057c478bd9Sstevel@tonic-gate }
1206*058561cbSjbeck 
12077c478bd9Sstevel@tonic-gate /*
12087c478bd9Sstevel@tonic-gate **  MILTER_SETUP -- setup structure for a mail filter
12097c478bd9Sstevel@tonic-gate **
12107c478bd9Sstevel@tonic-gate **	Parameters:
12117c478bd9Sstevel@tonic-gate **		line -- the options line.
12127c478bd9Sstevel@tonic-gate **
12137c478bd9Sstevel@tonic-gate **	Returns:
12147c478bd9Sstevel@tonic-gate **		none
12157c478bd9Sstevel@tonic-gate */
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate void
12187c478bd9Sstevel@tonic-gate milter_setup(line)
12197c478bd9Sstevel@tonic-gate 	char *line;
12207c478bd9Sstevel@tonic-gate {
12217c478bd9Sstevel@tonic-gate 	char fcode;
1222*058561cbSjbeck 	char *p;
1223*058561cbSjbeck 	struct milter *m;
12247c478bd9Sstevel@tonic-gate 	STAB *s;
12257c478bd9Sstevel@tonic-gate 
12267c478bd9Sstevel@tonic-gate 	/* collect the filter name */
12277c478bd9Sstevel@tonic-gate 	for (p = line;
12287c478bd9Sstevel@tonic-gate 	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
12297c478bd9Sstevel@tonic-gate 	     p++)
12307c478bd9Sstevel@tonic-gate 		continue;
12317c478bd9Sstevel@tonic-gate 	if (*p != '\0')
12327c478bd9Sstevel@tonic-gate 		*p++ = '\0';
12337c478bd9Sstevel@tonic-gate 	if (line[0] == '\0')
12347c478bd9Sstevel@tonic-gate 	{
12357c478bd9Sstevel@tonic-gate 		syserr("name required for mail filter");
12367c478bd9Sstevel@tonic-gate 		return;
12377c478bd9Sstevel@tonic-gate 	}
1238*058561cbSjbeck 	m = (struct milter *) xalloc(sizeof(*m));
1239*058561cbSjbeck 	memset((char *) m, '\0', sizeof(*m));
12407c478bd9Sstevel@tonic-gate 	m->mf_name = newstr(line);
12417c478bd9Sstevel@tonic-gate 	m->mf_state = SMFS_READY;
12427c478bd9Sstevel@tonic-gate 	m->mf_sock = -1;
12437c478bd9Sstevel@tonic-gate 	m->mf_timeout[SMFTO_CONNECT] = (time_t) 300;
12447c478bd9Sstevel@tonic-gate 	m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
12457c478bd9Sstevel@tonic-gate 	m->mf_timeout[SMFTO_READ] = (time_t) 10;
12467c478bd9Sstevel@tonic-gate 	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
1247*058561cbSjbeck #if MILTER_CHECK
1248*058561cbSjbeck 	m->mf_mta_prot_version = SMFI_PROT_VERSION;
1249*058561cbSjbeck 	m->mf_mta_prot_flags = SMFI_CURR_PROT;
1250*058561cbSjbeck 	m->mf_mta_actions = SMFI_CURR_ACTS;
1251*058561cbSjbeck #endif /* MILTER_CHECK */
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
12547c478bd9Sstevel@tonic-gate 	while (*p != '\0')
12557c478bd9Sstevel@tonic-gate 	{
12567c478bd9Sstevel@tonic-gate 		char *delimptr;
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 		while (*p != '\0' &&
12597c478bd9Sstevel@tonic-gate 		       (*p == ',' || (isascii(*p) && isspace(*p))))
12607c478bd9Sstevel@tonic-gate 			p++;
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 		/* p now points to field code */
12637c478bd9Sstevel@tonic-gate 		fcode = *p;
12647c478bd9Sstevel@tonic-gate 		while (*p != '\0' && *p != '=' && *p != ',')
12657c478bd9Sstevel@tonic-gate 			p++;
12667c478bd9Sstevel@tonic-gate 		if (*p++ != '=')
12677c478bd9Sstevel@tonic-gate 		{
12687c478bd9Sstevel@tonic-gate 			syserr("X%s: `=' expected", m->mf_name);
12697c478bd9Sstevel@tonic-gate 			return;
12707c478bd9Sstevel@tonic-gate 		}
12717c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
12727c478bd9Sstevel@tonic-gate 			p++;
12737c478bd9Sstevel@tonic-gate 
12747c478bd9Sstevel@tonic-gate 		/* p now points to the field body */
12757c478bd9Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ',');
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate 		/* install the field into the filter struct */
12787c478bd9Sstevel@tonic-gate 		switch (fcode)
12797c478bd9Sstevel@tonic-gate 		{
12807c478bd9Sstevel@tonic-gate 		  case 'S':		/* socket */
12817c478bd9Sstevel@tonic-gate 			if (p == NULL)
12827c478bd9Sstevel@tonic-gate 				m->mf_conn = NULL;
12837c478bd9Sstevel@tonic-gate 			else
12847c478bd9Sstevel@tonic-gate 				m->mf_conn = newstr(p);
12857c478bd9Sstevel@tonic-gate 			break;
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 		  case 'F':		/* Milter flags configured on MTA */
12887c478bd9Sstevel@tonic-gate 			for (; *p != '\0'; p++)
12897c478bd9Sstevel@tonic-gate 			{
12907c478bd9Sstevel@tonic-gate 				if (!(isascii(*p) && isspace(*p)))
12917c478bd9Sstevel@tonic-gate 					setbitn(bitidx(*p), m->mf_flags);
12927c478bd9Sstevel@tonic-gate 			}
12937c478bd9Sstevel@tonic-gate 			break;
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 		  case 'T':		/* timeouts */
12967c478bd9Sstevel@tonic-gate 			milter_parse_timeouts(p, m);
12977c478bd9Sstevel@tonic-gate 			break;
12987c478bd9Sstevel@tonic-gate 
1299*058561cbSjbeck #if MILTER_CHECK
1300*058561cbSjbeck 		  case 'a':
1301*058561cbSjbeck 			m->mf_mta_actions = strtoul(p, NULL, 0);
1302*058561cbSjbeck 			break;
1303*058561cbSjbeck 		  case 'f':
1304*058561cbSjbeck 			m->mf_mta_prot_flags = strtoul(p, NULL, 0);
1305*058561cbSjbeck 			break;
1306*058561cbSjbeck 		  case 'v':
1307*058561cbSjbeck 			m->mf_mta_prot_version = strtoul(p, NULL, 0);
1308*058561cbSjbeck 			break;
1309*058561cbSjbeck #endif /* MILTER_CHECK */
1310*058561cbSjbeck 
13117c478bd9Sstevel@tonic-gate 		  default:
13127c478bd9Sstevel@tonic-gate 			syserr("X%s: unknown filter equate %c=",
13137c478bd9Sstevel@tonic-gate 			       m->mf_name, fcode);
13147c478bd9Sstevel@tonic-gate 			break;
13157c478bd9Sstevel@tonic-gate 		}
13167c478bd9Sstevel@tonic-gate 		p = delimptr;
13177c478bd9Sstevel@tonic-gate 	}
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate 	/* early check for errors */
13207c478bd9Sstevel@tonic-gate 	(void) milter_open(m, true, CurEnv);
13217c478bd9Sstevel@tonic-gate 
13227c478bd9Sstevel@tonic-gate 	/* enter the filter into the symbol table */
13237c478bd9Sstevel@tonic-gate 	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
13247c478bd9Sstevel@tonic-gate 	if (s->s_milter != NULL)
13257c478bd9Sstevel@tonic-gate 		syserr("X%s: duplicate filter definition", m->mf_name);
13267c478bd9Sstevel@tonic-gate 	else
13277c478bd9Sstevel@tonic-gate 		s->s_milter = m;
13287c478bd9Sstevel@tonic-gate }
1329*058561cbSjbeck 
13307c478bd9Sstevel@tonic-gate /*
13317c478bd9Sstevel@tonic-gate **  MILTER_CONFIG -- parse option list into an array and check config
13327c478bd9Sstevel@tonic-gate **
13337c478bd9Sstevel@tonic-gate **	Called when reading configuration file.
13347c478bd9Sstevel@tonic-gate **
13357c478bd9Sstevel@tonic-gate **	Parameters:
13367c478bd9Sstevel@tonic-gate **		spec -- the filter list.
13377c478bd9Sstevel@tonic-gate **		list -- the array to fill in.
13387c478bd9Sstevel@tonic-gate **		max -- the maximum number of entries in list.
13397c478bd9Sstevel@tonic-gate **
13407c478bd9Sstevel@tonic-gate **	Returns:
13417c478bd9Sstevel@tonic-gate **		none
13427c478bd9Sstevel@tonic-gate */
13437c478bd9Sstevel@tonic-gate 
13447c478bd9Sstevel@tonic-gate void
13457c478bd9Sstevel@tonic-gate milter_config(spec, list, max)
13467c478bd9Sstevel@tonic-gate 	char *spec;
13477c478bd9Sstevel@tonic-gate 	struct milter **list;
13487c478bd9Sstevel@tonic-gate 	int max;
13497c478bd9Sstevel@tonic-gate {
13507c478bd9Sstevel@tonic-gate 	int numitems = 0;
1351*058561cbSjbeck 	char *p;
13527c478bd9Sstevel@tonic-gate 
13537c478bd9Sstevel@tonic-gate 	/* leave one for the NULL signifying the end of the list */
13547c478bd9Sstevel@tonic-gate 	max--;
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 	for (p = spec; p != NULL; )
13577c478bd9Sstevel@tonic-gate 	{
13587c478bd9Sstevel@tonic-gate 		STAB *s;
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
13617c478bd9Sstevel@tonic-gate 			p++;
13627c478bd9Sstevel@tonic-gate 		if (*p == '\0')
13637c478bd9Sstevel@tonic-gate 			break;
13647c478bd9Sstevel@tonic-gate 		spec = p;
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate 		if (numitems >= max)
13677c478bd9Sstevel@tonic-gate 		{
13687c478bd9Sstevel@tonic-gate 			syserr("Too many filters defined, %d max", max);
13697c478bd9Sstevel@tonic-gate 			if (max > 0)
13707c478bd9Sstevel@tonic-gate 				list[0] = NULL;
13717c478bd9Sstevel@tonic-gate 			return;
13727c478bd9Sstevel@tonic-gate 		}
13737c478bd9Sstevel@tonic-gate 		p = strpbrk(p, ";,");
13747c478bd9Sstevel@tonic-gate 		if (p != NULL)
13757c478bd9Sstevel@tonic-gate 			*p++ = '\0';
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate 		s = stab(spec, ST_MILTER, ST_FIND);
13787c478bd9Sstevel@tonic-gate 		if (s == NULL)
13797c478bd9Sstevel@tonic-gate 		{
13807c478bd9Sstevel@tonic-gate 			syserr("InputFilter %s not defined", spec);
13817c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
13827c478bd9Sstevel@tonic-gate 			return;
13837c478bd9Sstevel@tonic-gate 		}
13847c478bd9Sstevel@tonic-gate 		list[numitems++] = s->s_milter;
13857c478bd9Sstevel@tonic-gate 	}
13867c478bd9Sstevel@tonic-gate 	list[numitems] = NULL;
13877c478bd9Sstevel@tonic-gate 
13887c478bd9Sstevel@tonic-gate 	/* if not set, set to LogLevel */
13897c478bd9Sstevel@tonic-gate 	if (MilterLogLevel == -1)
13907c478bd9Sstevel@tonic-gate 		MilterLogLevel = LogLevel;
13917c478bd9Sstevel@tonic-gate }
1392*058561cbSjbeck 
13937c478bd9Sstevel@tonic-gate /*
13947c478bd9Sstevel@tonic-gate **  MILTER_PARSE_TIMEOUTS -- parse timeout list
13957c478bd9Sstevel@tonic-gate **
13967c478bd9Sstevel@tonic-gate **	Called when reading configuration file.
13977c478bd9Sstevel@tonic-gate **
13987c478bd9Sstevel@tonic-gate **	Parameters:
13997c478bd9Sstevel@tonic-gate **		spec -- the timeout list.
14007c478bd9Sstevel@tonic-gate **		m -- milter to set.
14017c478bd9Sstevel@tonic-gate **
14027c478bd9Sstevel@tonic-gate **	Returns:
14037c478bd9Sstevel@tonic-gate **		none
14047c478bd9Sstevel@tonic-gate */
14057c478bd9Sstevel@tonic-gate 
14067c478bd9Sstevel@tonic-gate static void
14077c478bd9Sstevel@tonic-gate milter_parse_timeouts(spec, m)
14087c478bd9Sstevel@tonic-gate 	char *spec;
14097c478bd9Sstevel@tonic-gate 	struct milter *m;
14107c478bd9Sstevel@tonic-gate {
14117c478bd9Sstevel@tonic-gate 	char fcode;
14127c478bd9Sstevel@tonic-gate 	int tcode;
1413*058561cbSjbeck 	char *p;
14147c478bd9Sstevel@tonic-gate 
14157c478bd9Sstevel@tonic-gate 	p = spec;
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
14187c478bd9Sstevel@tonic-gate 	while (*p != '\0')
14197c478bd9Sstevel@tonic-gate 	{
14207c478bd9Sstevel@tonic-gate 		char *delimptr;
14217c478bd9Sstevel@tonic-gate 
14227c478bd9Sstevel@tonic-gate 		while (*p != '\0' &&
14237c478bd9Sstevel@tonic-gate 		       (*p == ';' || (isascii(*p) && isspace(*p))))
14247c478bd9Sstevel@tonic-gate 			p++;
14257c478bd9Sstevel@tonic-gate 
14267c478bd9Sstevel@tonic-gate 		/* p now points to field code */
14277c478bd9Sstevel@tonic-gate 		fcode = *p;
14287c478bd9Sstevel@tonic-gate 		while (*p != '\0' && *p != ':')
14297c478bd9Sstevel@tonic-gate 			p++;
14307c478bd9Sstevel@tonic-gate 		if (*p++ != ':')
14317c478bd9Sstevel@tonic-gate 		{
14327c478bd9Sstevel@tonic-gate 			syserr("X%s, T=: `:' expected", m->mf_name);
14337c478bd9Sstevel@tonic-gate 			return;
14347c478bd9Sstevel@tonic-gate 		}
14357c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
14367c478bd9Sstevel@tonic-gate 			p++;
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 		/* p now points to the field body */
14397c478bd9Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ';');
14407c478bd9Sstevel@tonic-gate 		tcode = -1;
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 		/* install the field into the filter struct */
14437c478bd9Sstevel@tonic-gate 		switch (fcode)
14447c478bd9Sstevel@tonic-gate 		{
14457c478bd9Sstevel@tonic-gate 		  case 'C':
14467c478bd9Sstevel@tonic-gate 			tcode = SMFTO_CONNECT;
14477c478bd9Sstevel@tonic-gate 			break;
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 		  case 'S':
14507c478bd9Sstevel@tonic-gate 			tcode = SMFTO_WRITE;
14517c478bd9Sstevel@tonic-gate 			break;
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 		  case 'R':
14547c478bd9Sstevel@tonic-gate 			tcode = SMFTO_READ;
14557c478bd9Sstevel@tonic-gate 			break;
14567c478bd9Sstevel@tonic-gate 
14577c478bd9Sstevel@tonic-gate 		  case 'E':
14587c478bd9Sstevel@tonic-gate 			tcode = SMFTO_EOM;
14597c478bd9Sstevel@tonic-gate 			break;
14607c478bd9Sstevel@tonic-gate 
14617c478bd9Sstevel@tonic-gate 		  default:
14627c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
14637c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: %c unknown\n",
14647c478bd9Sstevel@tonic-gate 					   m->mf_name, fcode);
14657c478bd9Sstevel@tonic-gate 			syserr("X%s: unknown filter timeout %c",
14667c478bd9Sstevel@tonic-gate 			       m->mf_name, fcode);
14677c478bd9Sstevel@tonic-gate 			break;
14687c478bd9Sstevel@tonic-gate 		}
14697c478bd9Sstevel@tonic-gate 		if (tcode >= 0)
14707c478bd9Sstevel@tonic-gate 		{
14717c478bd9Sstevel@tonic-gate 			m->mf_timeout[tcode] = convtime(p, 's');
14727c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
14737c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: %c=%ld\n",
14747c478bd9Sstevel@tonic-gate 					   m->mf_name, fcode,
14757c478bd9Sstevel@tonic-gate 					   (u_long) m->mf_timeout[tcode]);
14767c478bd9Sstevel@tonic-gate 		}
14777c478bd9Sstevel@tonic-gate 		p = delimptr;
14787c478bd9Sstevel@tonic-gate 	}
14797c478bd9Sstevel@tonic-gate }
1480*058561cbSjbeck 
1481*058561cbSjbeck /*
1482*058561cbSjbeck **  MILTER_SET_MACROS -- set milter macros
1483*058561cbSjbeck **
1484*058561cbSjbeck **	Parameters:
1485*058561cbSjbeck **		name -- name of milter.
1486*058561cbSjbeck **		macros -- where to store macros.
1487*058561cbSjbeck **		val -- the value of the option.
1488*058561cbSjbeck **		nummac -- current number of macros
1489*058561cbSjbeck **
1490*058561cbSjbeck **	Returns:
1491*058561cbSjbeck **		new number of macros
1492*058561cbSjbeck */
1493*058561cbSjbeck 
1494*058561cbSjbeck static int
1495*058561cbSjbeck milter_set_macros(name, macros, val, nummac)
1496*058561cbSjbeck 	char *name;
1497*058561cbSjbeck 	char **macros;
1498*058561cbSjbeck 	char *val;
1499*058561cbSjbeck 	int nummac;
1500*058561cbSjbeck {
1501*058561cbSjbeck 	char *p;
1502*058561cbSjbeck 
1503*058561cbSjbeck 	p = newstr(val);
1504*058561cbSjbeck 	while (*p != '\0')
1505*058561cbSjbeck 	{
1506*058561cbSjbeck 		char *macro;
1507*058561cbSjbeck 
1508*058561cbSjbeck 		/* Skip leading commas, spaces */
1509*058561cbSjbeck 		while (*p != '\0' &&
1510*058561cbSjbeck 		       (*p == ',' || (isascii(*p) && isspace(*p))))
1511*058561cbSjbeck 			p++;
1512*058561cbSjbeck 
1513*058561cbSjbeck 		if (*p == '\0')
1514*058561cbSjbeck 			break;
1515*058561cbSjbeck 
1516*058561cbSjbeck 		/* Find end of macro */
1517*058561cbSjbeck 		macro = p;
1518*058561cbSjbeck 		while (*p != '\0' && *p != ',' &&
1519*058561cbSjbeck 		       isascii(*p) && !isspace(*p))
1520*058561cbSjbeck 			p++;
1521*058561cbSjbeck 		if (*p != '\0')
1522*058561cbSjbeck 			*p++ = '\0';
1523*058561cbSjbeck 
1524*058561cbSjbeck 		if (nummac >= MAXFILTERMACROS)
1525*058561cbSjbeck 		{
1526*058561cbSjbeck 			syserr("milter_set_option: too many macros in Milter.%s (max %d)",
1527*058561cbSjbeck 			       name, MAXFILTERMACROS);
1528*058561cbSjbeck 			macros[nummac] = NULL;
1529*058561cbSjbeck 			return -1;
1530*058561cbSjbeck 		}
1531*058561cbSjbeck 		macros[nummac++] = macro;
1532*058561cbSjbeck 	}
1533*058561cbSjbeck 	macros[nummac] = NULL;
1534*058561cbSjbeck 	return nummac;
1535*058561cbSjbeck }
1536*058561cbSjbeck 
15377c478bd9Sstevel@tonic-gate /*
15387c478bd9Sstevel@tonic-gate **  MILTER_SET_OPTION -- set an individual milter option
15397c478bd9Sstevel@tonic-gate **
15407c478bd9Sstevel@tonic-gate **	Parameters:
15417c478bd9Sstevel@tonic-gate **		name -- the name of the option.
15427c478bd9Sstevel@tonic-gate **		val -- the value of the option.
15437c478bd9Sstevel@tonic-gate **		sticky -- if set, don't let other setoptions override
15447c478bd9Sstevel@tonic-gate **			this value.
15457c478bd9Sstevel@tonic-gate **
15467c478bd9Sstevel@tonic-gate **	Returns:
15477c478bd9Sstevel@tonic-gate **		none.
15487c478bd9Sstevel@tonic-gate */
15497c478bd9Sstevel@tonic-gate 
15507c478bd9Sstevel@tonic-gate /* set if Milter sub-option is stuck */
15517c478bd9Sstevel@tonic-gate static BITMAP256	StickyMilterOpt;
15527c478bd9Sstevel@tonic-gate 
15537c478bd9Sstevel@tonic-gate static struct milteropt
15547c478bd9Sstevel@tonic-gate {
15557c478bd9Sstevel@tonic-gate 	char		*mo_name;	/* long name of milter option */
15567c478bd9Sstevel@tonic-gate 	unsigned char	mo_code;	/* code for option */
15577c478bd9Sstevel@tonic-gate } MilterOptTab[] =
15587c478bd9Sstevel@tonic-gate {
1559*058561cbSjbeck # define MO_MACROS_CONNECT		SMFIM_CONNECT
15607c478bd9Sstevel@tonic-gate 	{ "macros.connect",		MO_MACROS_CONNECT		},
1561*058561cbSjbeck # define MO_MACROS_HELO			SMFIM_HELO
15627c478bd9Sstevel@tonic-gate 	{ "macros.helo",		MO_MACROS_HELO			},
1563*058561cbSjbeck # define MO_MACROS_ENVFROM		SMFIM_ENVFROM
15647c478bd9Sstevel@tonic-gate 	{ "macros.envfrom",		MO_MACROS_ENVFROM		},
1565*058561cbSjbeck # define MO_MACROS_ENVRCPT		SMFIM_ENVRCPT
15667c478bd9Sstevel@tonic-gate 	{ "macros.envrcpt",		MO_MACROS_ENVRCPT		},
1567*058561cbSjbeck # define MO_MACROS_DATA			SMFIM_DATA
15687c478bd9Sstevel@tonic-gate 	{ "macros.data",		MO_MACROS_DATA			},
1569*058561cbSjbeck # define MO_MACROS_EOM			SMFIM_EOM
15707c478bd9Sstevel@tonic-gate 	{ "macros.eom",			MO_MACROS_EOM			},
1571*058561cbSjbeck # define MO_MACROS_EOH			SMFIM_EOH
1572*058561cbSjbeck 	{ "macros.eoh",			MO_MACROS_EOH			},
1573*058561cbSjbeck 
15747c478bd9Sstevel@tonic-gate # define MO_LOGLEVEL			0x07
15757c478bd9Sstevel@tonic-gate 	{ "loglevel",			MO_LOGLEVEL			},
15767c478bd9Sstevel@tonic-gate # if _FFR_MAXDATASIZE
15777c478bd9Sstevel@tonic-gate #  define MO_MAXDATASIZE		0x08
15787c478bd9Sstevel@tonic-gate 	{ "maxdatasize",		MO_MAXDATASIZE			},
15797c478bd9Sstevel@tonic-gate # endif /* _FFR_MAXDATASIZE */
1580*058561cbSjbeck 	{ NULL,				(unsigned char)-1		},
15817c478bd9Sstevel@tonic-gate };
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate void
15847c478bd9Sstevel@tonic-gate milter_set_option(name, val, sticky)
15857c478bd9Sstevel@tonic-gate 	char *name;
15867c478bd9Sstevel@tonic-gate 	char *val;
15877c478bd9Sstevel@tonic-gate 	bool sticky;
15887c478bd9Sstevel@tonic-gate {
1589*058561cbSjbeck 	int nummac, r;
1590*058561cbSjbeck 	struct milteropt *mo;
15917c478bd9Sstevel@tonic-gate 	char **macros = NULL;
15927c478bd9Sstevel@tonic-gate 
1593*058561cbSjbeck 	nummac = 0;
15947c478bd9Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64, 5))
15957c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_set_option(%s = %s)", name, val);
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate 	if (name == NULL)
15987c478bd9Sstevel@tonic-gate 	{
15997c478bd9Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option, must specify suboption");
16007c478bd9Sstevel@tonic-gate 		return;
16017c478bd9Sstevel@tonic-gate 	}
16027c478bd9Sstevel@tonic-gate 
16037c478bd9Sstevel@tonic-gate 	for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
16047c478bd9Sstevel@tonic-gate 	{
16057c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(mo->mo_name, name) == 0)
16067c478bd9Sstevel@tonic-gate 			break;
16077c478bd9Sstevel@tonic-gate 	}
16087c478bd9Sstevel@tonic-gate 
16097c478bd9Sstevel@tonic-gate 	if (mo->mo_name == NULL)
16107c478bd9Sstevel@tonic-gate 	{
16117c478bd9Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
16127c478bd9Sstevel@tonic-gate 		return;
16137c478bd9Sstevel@tonic-gate 	}
16147c478bd9Sstevel@tonic-gate 
16157c478bd9Sstevel@tonic-gate 	/*
16167c478bd9Sstevel@tonic-gate 	**  See if this option is preset for us.
16177c478bd9Sstevel@tonic-gate 	*/
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate 	if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
16207c478bd9Sstevel@tonic-gate 	{
16217c478bd9Sstevel@tonic-gate 		if (tTd(37, 2) || tTd(64,5))
16227c478bd9Sstevel@tonic-gate 			sm_dprintf(" (ignored)\n");
16237c478bd9Sstevel@tonic-gate 		return;
16247c478bd9Sstevel@tonic-gate 	}
16257c478bd9Sstevel@tonic-gate 
16267c478bd9Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64,5))
16277c478bd9Sstevel@tonic-gate 		sm_dprintf("\n");
16287c478bd9Sstevel@tonic-gate 
16297c478bd9Sstevel@tonic-gate 	switch (mo->mo_code)
16307c478bd9Sstevel@tonic-gate 	{
16317c478bd9Sstevel@tonic-gate 	  case MO_LOGLEVEL:
16327c478bd9Sstevel@tonic-gate 		MilterLogLevel = atoi(val);
16337c478bd9Sstevel@tonic-gate 		break;
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate #if _FFR_MAXDATASIZE
16367c478bd9Sstevel@tonic-gate 	  case MO_MAXDATASIZE:
16377c478bd9Sstevel@tonic-gate 		MilterMaxDataSize = (size_t)atol(val);
16387c478bd9Sstevel@tonic-gate 		break;
16397c478bd9Sstevel@tonic-gate #endif /* _FFR_MAXDATASIZE */
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 	  case MO_MACROS_CONNECT:
16427c478bd9Sstevel@tonic-gate 		if (macros == NULL)
16437c478bd9Sstevel@tonic-gate 			macros = MilterConnectMacros;
16447c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
16457c478bd9Sstevel@tonic-gate 
16467c478bd9Sstevel@tonic-gate 	  case MO_MACROS_HELO:
16477c478bd9Sstevel@tonic-gate 		if (macros == NULL)
16487c478bd9Sstevel@tonic-gate 			macros = MilterHeloMacros;
16497c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 	  case MO_MACROS_ENVFROM:
16527c478bd9Sstevel@tonic-gate 		if (macros == NULL)
16537c478bd9Sstevel@tonic-gate 			macros = MilterEnvFromMacros;
16547c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate 	  case MO_MACROS_ENVRCPT:
16577c478bd9Sstevel@tonic-gate 		if (macros == NULL)
16587c478bd9Sstevel@tonic-gate 			macros = MilterEnvRcptMacros;
16597c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
16607c478bd9Sstevel@tonic-gate 
1661*058561cbSjbeck 	  case MO_MACROS_EOH:
1662*058561cbSjbeck 		if (macros == NULL)
1663*058561cbSjbeck 			macros = MilterEOHMacros;
1664*058561cbSjbeck 		/* FALLTHROUGH */
1665*058561cbSjbeck 
16667c478bd9Sstevel@tonic-gate 	  case MO_MACROS_EOM:
16677c478bd9Sstevel@tonic-gate 		if (macros == NULL)
16687c478bd9Sstevel@tonic-gate 			macros = MilterEOMMacros;
16697c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
16707c478bd9Sstevel@tonic-gate 
16717c478bd9Sstevel@tonic-gate 	  case MO_MACROS_DATA:
16727c478bd9Sstevel@tonic-gate 		if (macros == NULL)
16737c478bd9Sstevel@tonic-gate 			macros = MilterDataMacros;
16747c478bd9Sstevel@tonic-gate 
1675*058561cbSjbeck 		r = milter_set_macros(name, macros, val, nummac);
1676*058561cbSjbeck 		if (r >= 0)
1677*058561cbSjbeck 			nummac = r;
16787c478bd9Sstevel@tonic-gate 		break;
16797c478bd9Sstevel@tonic-gate 
16807c478bd9Sstevel@tonic-gate 	  default:
16817c478bd9Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
16827c478bd9Sstevel@tonic-gate 		break;
16837c478bd9Sstevel@tonic-gate 	}
16847c478bd9Sstevel@tonic-gate 	if (sticky)
16857c478bd9Sstevel@tonic-gate 		setbitn(mo->mo_code, StickyMilterOpt);
16867c478bd9Sstevel@tonic-gate }
1687*058561cbSjbeck 
16887c478bd9Sstevel@tonic-gate /*
16897c478bd9Sstevel@tonic-gate **  MILTER_REOPEN_DF -- open & truncate the data file (for replbody)
16907c478bd9Sstevel@tonic-gate **
16917c478bd9Sstevel@tonic-gate **	Parameters:
16927c478bd9Sstevel@tonic-gate **		e -- current envelope.
16937c478bd9Sstevel@tonic-gate **
16947c478bd9Sstevel@tonic-gate **	Returns:
16957c478bd9Sstevel@tonic-gate **		0 if succesful, -1 otherwise
16967c478bd9Sstevel@tonic-gate */
16977c478bd9Sstevel@tonic-gate 
16987c478bd9Sstevel@tonic-gate static int
16997c478bd9Sstevel@tonic-gate milter_reopen_df(e)
17007c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
17017c478bd9Sstevel@tonic-gate {
17027c478bd9Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
17037c478bd9Sstevel@tonic-gate 
1704*058561cbSjbeck 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate 	/*
17077c478bd9Sstevel@tonic-gate 	**  In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so
17087c478bd9Sstevel@tonic-gate 	**  close and reopen writable (later close and reopen
17097c478bd9Sstevel@tonic-gate 	**  read only again).
17107c478bd9Sstevel@tonic-gate 	**
17117c478bd9Sstevel@tonic-gate 	**  In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the
17127c478bd9Sstevel@tonic-gate 	**  buffered file I/O descriptor, still open for writing so there
17137c478bd9Sstevel@tonic-gate 	**  isn't any work to do here (except checking for consistency).
17147c478bd9Sstevel@tonic-gate 	*/
17157c478bd9Sstevel@tonic-gate 
17167c478bd9Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY)
17177c478bd9Sstevel@tonic-gate 	{
17187c478bd9Sstevel@tonic-gate 		/* close read-only data file */
17197c478bd9Sstevel@tonic-gate 		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
17207c478bd9Sstevel@tonic-gate 		{
17217c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
17227c478bd9Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
17237c478bd9Sstevel@tonic-gate 		}
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate 		/* open writable */
17267c478bd9Sstevel@tonic-gate 		if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
17277c478bd9Sstevel@tonic-gate 					   SM_IO_RDWR_B, NULL)) == NULL)
17287c478bd9Sstevel@tonic-gate 		{
17297c478bd9Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s");
17307c478bd9Sstevel@tonic-gate 			return -1;
17317c478bd9Sstevel@tonic-gate 		}
17327c478bd9Sstevel@tonic-gate 	}
17337c478bd9Sstevel@tonic-gate 	else if (e->e_dfp == NULL)
17347c478bd9Sstevel@tonic-gate 	{
17357c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
17367c478bd9Sstevel@tonic-gate 		errno = ENOENT;
17377c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
17387c478bd9Sstevel@tonic-gate 		return -1;
17397c478bd9Sstevel@tonic-gate 	}
17407c478bd9Sstevel@tonic-gate 	return 0;
17417c478bd9Sstevel@tonic-gate }
1742*058561cbSjbeck 
17437c478bd9Sstevel@tonic-gate /*
17447c478bd9Sstevel@tonic-gate **  MILTER_RESET_DF -- re-open read-only the data file (for replbody)
17457c478bd9Sstevel@tonic-gate **
17467c478bd9Sstevel@tonic-gate **	Parameters:
17477c478bd9Sstevel@tonic-gate **		e -- current envelope.
17487c478bd9Sstevel@tonic-gate **
17497c478bd9Sstevel@tonic-gate **	Returns:
17507c478bd9Sstevel@tonic-gate **		0 if succesful, -1 otherwise
17517c478bd9Sstevel@tonic-gate */
17527c478bd9Sstevel@tonic-gate 
17537c478bd9Sstevel@tonic-gate static int
17547c478bd9Sstevel@tonic-gate milter_reset_df(e)
17557c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
17567c478bd9Sstevel@tonic-gate {
17577c478bd9Sstevel@tonic-gate 	int afd;
17587c478bd9Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
17597c478bd9Sstevel@tonic-gate 
1760*058561cbSjbeck 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 	if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 ||
17637c478bd9Sstevel@tonic-gate 	    sm_io_error(e->e_dfp))
17647c478bd9Sstevel@tonic-gate 	{
17657c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
17667c478bd9Sstevel@tonic-gate 		return -1;
17677c478bd9Sstevel@tonic-gate 	}
17687c478bd9Sstevel@tonic-gate 	else if (SuperSafe != SAFE_REALLY)
17697c478bd9Sstevel@tonic-gate 	{
17707c478bd9Sstevel@tonic-gate 		/* skip next few clauses */
17717c478bd9Sstevel@tonic-gate 		/* EMPTY */
17727c478bd9Sstevel@tonic-gate 	}
17737c478bd9Sstevel@tonic-gate 	else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0
17747c478bd9Sstevel@tonic-gate 		 && fsync(afd) < 0)
17757c478bd9Sstevel@tonic-gate 	{
17767c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
17777c478bd9Sstevel@tonic-gate 		return -1;
17787c478bd9Sstevel@tonic-gate 	}
17797c478bd9Sstevel@tonic-gate 	else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0)
17807c478bd9Sstevel@tonic-gate 	{
17817c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
17827c478bd9Sstevel@tonic-gate 		return -1;
17837c478bd9Sstevel@tonic-gate 	}
17847c478bd9Sstevel@tonic-gate 	else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
17857c478bd9Sstevel@tonic-gate 					SM_IO_RDONLY_B, NULL)) == NULL)
17867c478bd9Sstevel@tonic-gate 	{
17877c478bd9Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
17887c478bd9Sstevel@tonic-gate 		return -1;
17897c478bd9Sstevel@tonic-gate 	}
17907c478bd9Sstevel@tonic-gate 	else
17917c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
17927c478bd9Sstevel@tonic-gate 	return 0;
17937c478bd9Sstevel@tonic-gate }
1794*058561cbSjbeck 
17957c478bd9Sstevel@tonic-gate /*
17967c478bd9Sstevel@tonic-gate **  MILTER_CAN_DELRCPTS -- can any milter filters delete recipients?
17977c478bd9Sstevel@tonic-gate **
17987c478bd9Sstevel@tonic-gate **	Parameters:
17997c478bd9Sstevel@tonic-gate **		none
18007c478bd9Sstevel@tonic-gate **
18017c478bd9Sstevel@tonic-gate **	Returns:
18027c478bd9Sstevel@tonic-gate **		true if any filter deletes recipients, false otherwise
18037c478bd9Sstevel@tonic-gate */
18047c478bd9Sstevel@tonic-gate 
18057c478bd9Sstevel@tonic-gate bool
18067c478bd9Sstevel@tonic-gate milter_can_delrcpts()
18077c478bd9Sstevel@tonic-gate {
18087c478bd9Sstevel@tonic-gate 	bool can = false;
18097c478bd9Sstevel@tonic-gate 	int i;
18107c478bd9Sstevel@tonic-gate 
18117c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
18127c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_can_delrcpts:");
18137c478bd9Sstevel@tonic-gate 
18147c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
18157c478bd9Sstevel@tonic-gate 	{
18167c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate 		if (bitset(SMFIF_DELRCPT, m->mf_fflags))
18197c478bd9Sstevel@tonic-gate 		{
18207c478bd9Sstevel@tonic-gate 			can = true;
18217c478bd9Sstevel@tonic-gate 			break;
18227c478bd9Sstevel@tonic-gate 		}
18237c478bd9Sstevel@tonic-gate 	}
18247c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
18257c478bd9Sstevel@tonic-gate 		sm_dprintf("%s\n", can ? "true" : "false");
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate 	return can;
18287c478bd9Sstevel@tonic-gate }
1829*058561cbSjbeck 
18307c478bd9Sstevel@tonic-gate /*
18317c478bd9Sstevel@tonic-gate **  MILTER_QUIT_FILTER -- close down a single filter
18327c478bd9Sstevel@tonic-gate **
18337c478bd9Sstevel@tonic-gate **	Parameters:
18347c478bd9Sstevel@tonic-gate **		m -- milter structure of filter to close down.
18357c478bd9Sstevel@tonic-gate **		e -- current envelope.
18367c478bd9Sstevel@tonic-gate **
18377c478bd9Sstevel@tonic-gate **	Returns:
18387c478bd9Sstevel@tonic-gate **		none
18397c478bd9Sstevel@tonic-gate */
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate static void
18427c478bd9Sstevel@tonic-gate milter_quit_filter(m, e)
18437c478bd9Sstevel@tonic-gate 	struct milter *m;
18447c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
18457c478bd9Sstevel@tonic-gate {
18467c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
18477c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_quit_filter(%s)\n", m->mf_name);
18487c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 18)
18497c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter",
18507c478bd9Sstevel@tonic-gate 			  m->mf_name);
18517c478bd9Sstevel@tonic-gate 
18527c478bd9Sstevel@tonic-gate 	/* Never replace error state */
18537c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
18547c478bd9Sstevel@tonic-gate 		return;
18557c478bd9Sstevel@tonic-gate 
18567c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
18577c478bd9Sstevel@tonic-gate 	    m->mf_state == SMFS_CLOSED ||
18587c478bd9Sstevel@tonic-gate 	    m->mf_state == SMFS_READY)
18597c478bd9Sstevel@tonic-gate 	{
18607c478bd9Sstevel@tonic-gate 		m->mf_sock = -1;
18617c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
18627c478bd9Sstevel@tonic-gate 		return;
18637c478bd9Sstevel@tonic-gate 	}
18647c478bd9Sstevel@tonic-gate 
18657c478bd9Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
1866*058561cbSjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "quit_filter");
18677c478bd9Sstevel@tonic-gate 	if (m->mf_sock >= 0)
18687c478bd9Sstevel@tonic-gate 	{
18697c478bd9Sstevel@tonic-gate 		(void) close(m->mf_sock);
18707c478bd9Sstevel@tonic-gate 		m->mf_sock = -1;
18717c478bd9Sstevel@tonic-gate 	}
18727c478bd9Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
18737c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
18747c478bd9Sstevel@tonic-gate }
1875*058561cbSjbeck 
18767c478bd9Sstevel@tonic-gate /*
18777c478bd9Sstevel@tonic-gate **  MILTER_ABORT_FILTER -- tell filter to abort current message
18787c478bd9Sstevel@tonic-gate **
18797c478bd9Sstevel@tonic-gate **	Parameters:
18807c478bd9Sstevel@tonic-gate **		m -- milter structure of filter to abort.
18817c478bd9Sstevel@tonic-gate **		e -- current envelope.
18827c478bd9Sstevel@tonic-gate **
18837c478bd9Sstevel@tonic-gate **	Returns:
18847c478bd9Sstevel@tonic-gate **		none
18857c478bd9Sstevel@tonic-gate */
18867c478bd9Sstevel@tonic-gate 
18877c478bd9Sstevel@tonic-gate static void
18887c478bd9Sstevel@tonic-gate milter_abort_filter(m, e)
18897c478bd9Sstevel@tonic-gate 	struct milter *m;
18907c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
18917c478bd9Sstevel@tonic-gate {
18927c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
18937c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_abort_filter(%s)\n", m->mf_name);
18947c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 10)
18957c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter",
18967c478bd9Sstevel@tonic-gate 			  m->mf_name);
18977c478bd9Sstevel@tonic-gate 
18987c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
18997c478bd9Sstevel@tonic-gate 	    m->mf_state != SMFS_INMSG)
19007c478bd9Sstevel@tonic-gate 		return;
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
1903*058561cbSjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "abort_filter");
19047c478bd9Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
19057c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_DONE;
19067c478bd9Sstevel@tonic-gate }
1907*058561cbSjbeck 
19087c478bd9Sstevel@tonic-gate /*
19097c478bd9Sstevel@tonic-gate **  MILTER_SEND_MACROS -- provide macros to the filters
19107c478bd9Sstevel@tonic-gate **
19117c478bd9Sstevel@tonic-gate **	Parameters:
19127c478bd9Sstevel@tonic-gate **		m -- milter to send macros to.
19137c478bd9Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
19147c478bd9Sstevel@tonic-gate **		cmd -- which command the macros are associated with.
19157c478bd9Sstevel@tonic-gate **		e -- current envelope (for macro access).
19167c478bd9Sstevel@tonic-gate **
19177c478bd9Sstevel@tonic-gate **	Returns:
19187c478bd9Sstevel@tonic-gate **		none
19197c478bd9Sstevel@tonic-gate */
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate static void
19227c478bd9Sstevel@tonic-gate milter_send_macros(m, macros, cmd, e)
19237c478bd9Sstevel@tonic-gate 	struct milter *m;
19247c478bd9Sstevel@tonic-gate 	char **macros;
1925*058561cbSjbeck 	int cmd;
19267c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
19277c478bd9Sstevel@tonic-gate {
19287c478bd9Sstevel@tonic-gate 	int i;
19297c478bd9Sstevel@tonic-gate 	int mid;
1930*058561cbSjbeck 	char command = (char) cmd;
19317c478bd9Sstevel@tonic-gate 	char *v;
19327c478bd9Sstevel@tonic-gate 	char *buf, *bp;
19337c478bd9Sstevel@tonic-gate 	char exp[MAXLINE];
19347c478bd9Sstevel@tonic-gate 	ssize_t s;
19357c478bd9Sstevel@tonic-gate 
19367c478bd9Sstevel@tonic-gate 	/* sanity check */
19377c478bd9Sstevel@tonic-gate 	if (macros == NULL || macros[0] == NULL)
19387c478bd9Sstevel@tonic-gate 		return;
19397c478bd9Sstevel@tonic-gate 
19407c478bd9Sstevel@tonic-gate 	/* put together data */
19417c478bd9Sstevel@tonic-gate 	s = 1;			/* for the command character */
19427c478bd9Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
19437c478bd9Sstevel@tonic-gate 	{
19447c478bd9Sstevel@tonic-gate 		mid = macid(macros[i]);
19457c478bd9Sstevel@tonic-gate 		if (mid == 0)
19467c478bd9Sstevel@tonic-gate 			continue;
19477c478bd9Sstevel@tonic-gate 		v = macvalue(mid, e);
19487c478bd9Sstevel@tonic-gate 		if (v == NULL)
19497c478bd9Sstevel@tonic-gate 			continue;
19507c478bd9Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
19517c478bd9Sstevel@tonic-gate 		s += strlen(macros[i]) + 1 + strlen(exp) + 1;
19527c478bd9Sstevel@tonic-gate 	}
19537c478bd9Sstevel@tonic-gate 
19547c478bd9Sstevel@tonic-gate 	if (s < 0)
19557c478bd9Sstevel@tonic-gate 		return;
19567c478bd9Sstevel@tonic-gate 
19577c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(s);
19587c478bd9Sstevel@tonic-gate 	bp = buf;
1959*058561cbSjbeck 	*bp++ = command;
19607c478bd9Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
19617c478bd9Sstevel@tonic-gate 	{
19627c478bd9Sstevel@tonic-gate 		mid = macid(macros[i]);
19637c478bd9Sstevel@tonic-gate 		if (mid == 0)
19647c478bd9Sstevel@tonic-gate 			continue;
19657c478bd9Sstevel@tonic-gate 		v = macvalue(mid, e);
19667c478bd9Sstevel@tonic-gate 		if (v == NULL)
19677c478bd9Sstevel@tonic-gate 			continue;
19687c478bd9Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
19717c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
1972*058561cbSjbeck 				m->mf_name, command, macros[i], exp);
19737c478bd9Sstevel@tonic-gate 
19747c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, macros[i], s - (bp - buf));
19757c478bd9Sstevel@tonic-gate 		bp += strlen(bp) + 1;
19767c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, exp, s - (bp - buf));
19777c478bd9Sstevel@tonic-gate 		bp += strlen(bp) + 1;
19787c478bd9Sstevel@tonic-gate 	}
19797c478bd9Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_MACRO, buf, s,
1980*058561cbSjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "send_macros");
19817c478bd9Sstevel@tonic-gate 	sm_free(buf);
19827c478bd9Sstevel@tonic-gate }
19837c478bd9Sstevel@tonic-gate 
19847c478bd9Sstevel@tonic-gate /*
19857c478bd9Sstevel@tonic-gate **  MILTER_SEND_COMMAND -- send a command and return the response for a filter
19867c478bd9Sstevel@tonic-gate **
19877c478bd9Sstevel@tonic-gate **	Parameters:
19887c478bd9Sstevel@tonic-gate **		m -- current milter filter
1989*058561cbSjbeck **		cmd -- command to send.
19907c478bd9Sstevel@tonic-gate **		data -- optional command data.
19917c478bd9Sstevel@tonic-gate **		sz -- length of buf.
19927c478bd9Sstevel@tonic-gate **		e -- current envelope (for e->e_id).
19937c478bd9Sstevel@tonic-gate **		state -- return state word.
19947c478bd9Sstevel@tonic-gate **
19957c478bd9Sstevel@tonic-gate **	Returns:
19967c478bd9Sstevel@tonic-gate **		response string (may be NULL)
19977c478bd9Sstevel@tonic-gate */
19987c478bd9Sstevel@tonic-gate 
19997c478bd9Sstevel@tonic-gate static char *
2000*058561cbSjbeck milter_send_command(m, cmd, data, sz, e, state, where)
20017c478bd9Sstevel@tonic-gate 	struct milter *m;
2002*058561cbSjbeck 	int cmd;
20037c478bd9Sstevel@tonic-gate 	void *data;
20047c478bd9Sstevel@tonic-gate 	ssize_t sz;
20057c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
20067c478bd9Sstevel@tonic-gate 	char *state;
2007*058561cbSjbeck 	const char *where;
20087c478bd9Sstevel@tonic-gate {
20097c478bd9Sstevel@tonic-gate 	char rcmd;
20107c478bd9Sstevel@tonic-gate 	ssize_t rlen;
20117c478bd9Sstevel@tonic-gate 	unsigned long skipflag;
20127c478bd9Sstevel@tonic-gate 	unsigned long norespflag = 0;
2013*058561cbSjbeck 	char command = (char) cmd;
20147c478bd9Sstevel@tonic-gate 	char *action;
20157c478bd9Sstevel@tonic-gate 	char *defresponse;
20167c478bd9Sstevel@tonic-gate 	char *response;
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
20197c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): cmd %c len %ld\n",
20207c478bd9Sstevel@tonic-gate 			m->mf_name, (char) command, (long) sz);
20217c478bd9Sstevel@tonic-gate 
20227c478bd9Sstevel@tonic-gate 	/* find skip flag and default failure */
20237c478bd9Sstevel@tonic-gate 	switch (command)
20247c478bd9Sstevel@tonic-gate 	{
20257c478bd9Sstevel@tonic-gate 	  case SMFIC_CONNECT:
20267c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOCONNECT;
2027*058561cbSjbeck 		norespflag = SMFIP_NR_CONN;
20287c478bd9Sstevel@tonic-gate 		action = "connect";
20297c478bd9Sstevel@tonic-gate 		defresponse = "554 Command rejected";
20307c478bd9Sstevel@tonic-gate 		break;
20317c478bd9Sstevel@tonic-gate 
20327c478bd9Sstevel@tonic-gate 	  case SMFIC_HELO:
20337c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOHELO;
2034*058561cbSjbeck 		norespflag = SMFIP_NR_HELO;
20357c478bd9Sstevel@tonic-gate 		action = "helo";
20367c478bd9Sstevel@tonic-gate 		defresponse = "550 Command rejected";
20377c478bd9Sstevel@tonic-gate 		break;
20387c478bd9Sstevel@tonic-gate 
20397c478bd9Sstevel@tonic-gate 	  case SMFIC_MAIL:
20407c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOMAIL;
2041*058561cbSjbeck 		norespflag = SMFIP_NR_MAIL;
20427c478bd9Sstevel@tonic-gate 		action = "mail";
20437c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20447c478bd9Sstevel@tonic-gate 		break;
20457c478bd9Sstevel@tonic-gate 
20467c478bd9Sstevel@tonic-gate 	  case SMFIC_RCPT:
20477c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NORCPT;
2048*058561cbSjbeck 		norespflag = SMFIP_NR_RCPT;
20497c478bd9Sstevel@tonic-gate 		action = "rcpt";
20507c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20517c478bd9Sstevel@tonic-gate 		break;
20527c478bd9Sstevel@tonic-gate 
20537c478bd9Sstevel@tonic-gate 	  case SMFIC_HEADER:
20547c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOHDRS;
2055*058561cbSjbeck 		norespflag = SMFIP_NR_HDR;
20567c478bd9Sstevel@tonic-gate 		action = "header";
20577c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20587c478bd9Sstevel@tonic-gate 		break;
20597c478bd9Sstevel@tonic-gate 
20607c478bd9Sstevel@tonic-gate 	  case SMFIC_BODY:
20617c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOBODY;
2062*058561cbSjbeck 		norespflag = SMFIP_NR_BODY;
20637c478bd9Sstevel@tonic-gate 		action = "body";
20647c478bd9Sstevel@tonic-gate 		defresponse = "554 5.7.1 Command rejected";
20657c478bd9Sstevel@tonic-gate 		break;
20667c478bd9Sstevel@tonic-gate 
20677c478bd9Sstevel@tonic-gate 	  case SMFIC_EOH:
20687c478bd9Sstevel@tonic-gate 		skipflag = SMFIP_NOEOH;
2069*058561cbSjbeck 		norespflag = SMFIP_NR_EOH;
20707c478bd9Sstevel@tonic-gate 		action = "eoh";
20717c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20727c478bd9Sstevel@tonic-gate 		break;
20737c478bd9Sstevel@tonic-gate 
20747c478bd9Sstevel@tonic-gate 	  case SMFIC_UNKNOWN:
207549218d4fSjbeck 		skipflag = SMFIP_NOUNKNOWN;
2076*058561cbSjbeck 		norespflag = SMFIP_NR_UNKN;
20777c478bd9Sstevel@tonic-gate 		action = "unknown";
20787c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20797c478bd9Sstevel@tonic-gate 		break;
20807c478bd9Sstevel@tonic-gate 
208149218d4fSjbeck 	  case SMFIC_DATA:
208249218d4fSjbeck 		skipflag = SMFIP_NODATA;
2083*058561cbSjbeck 		norespflag = SMFIP_NR_DATA;
208449218d4fSjbeck 		action = "data";
208549218d4fSjbeck 		defresponse = "550 5.7.1 Command rejected";
208649218d4fSjbeck 		break;
208749218d4fSjbeck 
20887c478bd9Sstevel@tonic-gate 	  case SMFIC_BODYEOB:
20897c478bd9Sstevel@tonic-gate 	  case SMFIC_OPTNEG:
20907c478bd9Sstevel@tonic-gate 	  case SMFIC_MACRO:
20917c478bd9Sstevel@tonic-gate 	  case SMFIC_ABORT:
20927c478bd9Sstevel@tonic-gate 	  case SMFIC_QUIT:
20937c478bd9Sstevel@tonic-gate 		/* NOTE: not handled by milter_send_command() */
20947c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
20957c478bd9Sstevel@tonic-gate 
20967c478bd9Sstevel@tonic-gate 	  default:
20977c478bd9Sstevel@tonic-gate 		skipflag = 0;
20987c478bd9Sstevel@tonic-gate 		action = "default";
20997c478bd9Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
21007c478bd9Sstevel@tonic-gate 		break;
21017c478bd9Sstevel@tonic-gate 	}
21027c478bd9Sstevel@tonic-gate 
2103*058561cbSjbeck 	if (tTd(64, 10))
2104*058561cbSjbeck 		sm_dprintf("milter_send_command(%s): skip=%lx, pflags=%x\n",
2105*058561cbSjbeck 			m->mf_name, skipflag, m->mf_pflags);
2106*058561cbSjbeck 
21077c478bd9Sstevel@tonic-gate 	/* check if filter wants this command */
2108*058561cbSjbeck 	if (skipflag != 0 && bitset(skipflag, m->mf_pflags))
21097c478bd9Sstevel@tonic-gate 		return NULL;
21107c478bd9Sstevel@tonic-gate 
21117c478bd9Sstevel@tonic-gate 	/* send the command to the filter */
21127c478bd9Sstevel@tonic-gate 	(void) milter_write(m, command, data, sz,
2113*058561cbSjbeck 			    m->mf_timeout[SMFTO_WRITE], e, where);
21147c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
21157c478bd9Sstevel@tonic-gate 	{
21167c478bd9Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
21177c478bd9Sstevel@tonic-gate 		return NULL;
21187c478bd9Sstevel@tonic-gate 	}
21197c478bd9Sstevel@tonic-gate 
21207c478bd9Sstevel@tonic-gate 	/* check if filter sends response to this command */
21217c478bd9Sstevel@tonic-gate 	if (norespflag != 0 && bitset(norespflag, m->mf_pflags))
21227c478bd9Sstevel@tonic-gate 		return NULL;
21237c478bd9Sstevel@tonic-gate 
21247c478bd9Sstevel@tonic-gate 	/* get the response from the filter */
21257c478bd9Sstevel@tonic-gate 	response = milter_read(m, &rcmd, &rlen,
2126*058561cbSjbeck 			       m->mf_timeout[SMFTO_READ], e, where);
21277c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
21287c478bd9Sstevel@tonic-gate 	{
21297c478bd9Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
21307c478bd9Sstevel@tonic-gate 		return NULL;
21317c478bd9Sstevel@tonic-gate 	}
21327c478bd9Sstevel@tonic-gate 
21337c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
21347c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): returned %c\n",
21357c478bd9Sstevel@tonic-gate 			   m->mf_name, (char) rcmd);
21367c478bd9Sstevel@tonic-gate 
21377c478bd9Sstevel@tonic-gate 	switch (rcmd)
21387c478bd9Sstevel@tonic-gate 	{
21397c478bd9Sstevel@tonic-gate 	  case SMFIR_REPLYCODE:
21407c478bd9Sstevel@tonic-gate 		MILTER_CHECK_REPLYCODE(defresponse);
21417c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2142*058561cbSjbeck 			sm_syslog(LOG_INFO, e->e_id,
2143*058561cbSjbeck 				  "milter=%s, action=%s, reject=%s",
21447c478bd9Sstevel@tonic-gate 				  m->mf_name, action, response);
21457c478bd9Sstevel@tonic-gate 		*state = rcmd;
21467c478bd9Sstevel@tonic-gate 		break;
21477c478bd9Sstevel@tonic-gate 
21487c478bd9Sstevel@tonic-gate 	  case SMFIR_REJECT:
21497c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2150*058561cbSjbeck 			sm_syslog(LOG_INFO, e->e_id,
2151*058561cbSjbeck 				  "milter=%s, action=%s, reject",
21527c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
21537c478bd9Sstevel@tonic-gate 		*state = rcmd;
21547c478bd9Sstevel@tonic-gate 		break;
21557c478bd9Sstevel@tonic-gate 
21567c478bd9Sstevel@tonic-gate 	  case SMFIR_DISCARD:
21577c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2158*058561cbSjbeck 			sm_syslog(LOG_INFO, e->e_id,
2159*058561cbSjbeck 				  "milter=%s, action=%s, discard",
21607c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
21617c478bd9Sstevel@tonic-gate 		*state = rcmd;
21627c478bd9Sstevel@tonic-gate 		break;
21637c478bd9Sstevel@tonic-gate 
21647c478bd9Sstevel@tonic-gate 	  case SMFIR_TEMPFAIL:
21657c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2166*058561cbSjbeck 			sm_syslog(LOG_INFO, e->e_id,
2167*058561cbSjbeck 				  "milter=%s, action=%s, tempfail",
21687c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
21697c478bd9Sstevel@tonic-gate 		*state = rcmd;
21707c478bd9Sstevel@tonic-gate 		break;
21717c478bd9Sstevel@tonic-gate 
21727c478bd9Sstevel@tonic-gate 	  case SMFIR_ACCEPT:
21737c478bd9Sstevel@tonic-gate 		/* this filter is done with message/connection */
21747c478bd9Sstevel@tonic-gate 		if (command == SMFIC_HELO ||
21757c478bd9Sstevel@tonic-gate 		    command == SMFIC_CONNECT)
21767c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_CLOSABLE;
21777c478bd9Sstevel@tonic-gate 		else
21787c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_DONE;
21797c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2180*058561cbSjbeck 			sm_syslog(LOG_INFO, e->e_id,
2181*058561cbSjbeck 				  "milter=%s, action=%s, accepted",
21827c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
21837c478bd9Sstevel@tonic-gate 		break;
21847c478bd9Sstevel@tonic-gate 
21857c478bd9Sstevel@tonic-gate 	  case SMFIR_CONTINUE:
21867c478bd9Sstevel@tonic-gate 		/* if MAIL command is ok, filter is in message state */
21877c478bd9Sstevel@tonic-gate 		if (command == SMFIC_MAIL)
21887c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_INMSG;
21897c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 12)
2190*058561cbSjbeck 			sm_syslog(LOG_INFO, e->e_id,
2191*058561cbSjbeck 				  "milter=%s, action=%s, continue",
21927c478bd9Sstevel@tonic-gate 				  m->mf_name, action);
21937c478bd9Sstevel@tonic-gate 		break;
21947c478bd9Sstevel@tonic-gate 
2195*058561cbSjbeck 	  case SMFIR_SKIP:
2196*058561cbSjbeck 		if (MilterLogLevel > 12)
2197*058561cbSjbeck 			sm_syslog(LOG_INFO, e->e_id,
2198*058561cbSjbeck 				  "milter=%s, action=%s, skip",
2199*058561cbSjbeck 				  m->mf_name, action);
2200*058561cbSjbeck 		m->mf_state = SMFS_SKIP;
2201*058561cbSjbeck 		break;
2202*058561cbSjbeck 
22037c478bd9Sstevel@tonic-gate 	  default:
22047c478bd9Sstevel@tonic-gate 		/* Invalid response to command */
22057c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
22067c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
22077c478bd9Sstevel@tonic-gate 				  "milter_send_command(%s): action=%s returned bogus response %c",
22087c478bd9Sstevel@tonic-gate 				  m->mf_name, action, rcmd);
22097c478bd9Sstevel@tonic-gate 		milter_error(m, e);
22107c478bd9Sstevel@tonic-gate 		break;
22117c478bd9Sstevel@tonic-gate 	}
22127c478bd9Sstevel@tonic-gate 
2213*058561cbSjbeck 	if (*state != SMFIR_REPLYCODE && response != NULL)
22147c478bd9Sstevel@tonic-gate 	{
22157c478bd9Sstevel@tonic-gate 		sm_free(response); /* XXX */
22167c478bd9Sstevel@tonic-gate 		response = NULL;
22177c478bd9Sstevel@tonic-gate 	}
22187c478bd9Sstevel@tonic-gate 	return response;
22197c478bd9Sstevel@tonic-gate }
22207c478bd9Sstevel@tonic-gate 
22217c478bd9Sstevel@tonic-gate /*
22227c478bd9Sstevel@tonic-gate **  MILTER_COMMAND -- send a command and return the response for each filter
22237c478bd9Sstevel@tonic-gate **
22247c478bd9Sstevel@tonic-gate **	Parameters:
2225*058561cbSjbeck **		cmd -- command to send.
22267c478bd9Sstevel@tonic-gate **		data -- optional command data.
22277c478bd9Sstevel@tonic-gate **		sz -- length of buf.
22287c478bd9Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
22297c478bd9Sstevel@tonic-gate **		e -- current envelope (for macro access).
22307c478bd9Sstevel@tonic-gate **		state -- return state word.
2231*058561cbSjbeck **		where -- description of calling function (logging).
2232*058561cbSjbeck **		cmd_error -- did the SMTP command cause an error?
22337c478bd9Sstevel@tonic-gate **
22347c478bd9Sstevel@tonic-gate **	Returns:
22357c478bd9Sstevel@tonic-gate **		response string (may be NULL)
22367c478bd9Sstevel@tonic-gate */
22377c478bd9Sstevel@tonic-gate 
22387c478bd9Sstevel@tonic-gate static char *
2239*058561cbSjbeck milter_command(cmd, data, sz, macros, e, state, where, cmd_error)
2240*058561cbSjbeck 	int cmd;
22417c478bd9Sstevel@tonic-gate 	void *data;
22427c478bd9Sstevel@tonic-gate 	ssize_t sz;
22437c478bd9Sstevel@tonic-gate 	char **macros;
22447c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
22457c478bd9Sstevel@tonic-gate 	char *state;
2246*058561cbSjbeck 	const char *where;
2247*058561cbSjbeck 	bool cmd_error;
22487c478bd9Sstevel@tonic-gate {
22497c478bd9Sstevel@tonic-gate 	int i;
2250*058561cbSjbeck 	char command = (char) cmd;
22517c478bd9Sstevel@tonic-gate 	char *response = NULL;
22527c478bd9Sstevel@tonic-gate 	time_t tn = 0;
22537c478bd9Sstevel@tonic-gate 
22547c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
22557c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_command: cmd %c len %ld\n",
2256*058561cbSjbeck 			command, (long) sz);
22577c478bd9Sstevel@tonic-gate 
22587c478bd9Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
22597c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
22607c478bd9Sstevel@tonic-gate 	{
22617c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 		/* previous problem? */
22647c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
22657c478bd9Sstevel@tonic-gate 		{
22667c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
22677c478bd9Sstevel@tonic-gate 			break;
22687c478bd9Sstevel@tonic-gate 		}
22697c478bd9Sstevel@tonic-gate 
22707c478bd9Sstevel@tonic-gate 		/* sanity check */
22717c478bd9Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
22727c478bd9Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
22737c478bd9Sstevel@tonic-gate 			continue;
22747c478bd9Sstevel@tonic-gate 
22757c478bd9Sstevel@tonic-gate 		/* send macros (regardless of whether we send command) */
22767c478bd9Sstevel@tonic-gate 		if (macros != NULL && macros[0] != NULL)
22777c478bd9Sstevel@tonic-gate 		{
22787c478bd9Sstevel@tonic-gate 			milter_send_macros(m, macros, command, e);
22797c478bd9Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
22807c478bd9Sstevel@tonic-gate 			{
22817c478bd9Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, continue);
22827c478bd9Sstevel@tonic-gate 				break;
22837c478bd9Sstevel@tonic-gate 			}
22847c478bd9Sstevel@tonic-gate 		}
22857c478bd9Sstevel@tonic-gate 
22867c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 21)
22877c478bd9Sstevel@tonic-gate 			tn = curtime();
22887c478bd9Sstevel@tonic-gate 
2289*058561cbSjbeck 		/*
2290*058561cbSjbeck 		**  send the command if
2291*058561cbSjbeck 		**	there is no error
2292*058561cbSjbeck 		**	or it's RCPT and the client asked for it:
2293*058561cbSjbeck 		**	!cmd_error ||
2294*058561cbSjbeck 		**	where == "rcpt" && m->mf_pflags & SMFIP_RCPT_REJ != 0
2295*058561cbSjbeck 		**  negate that condition and use continue
2296*058561cbSjbeck 		*/
2297*058561cbSjbeck 
2298*058561cbSjbeck 		if (cmd_error &&
2299*058561cbSjbeck 		    (strcmp(where, "rcpt") != 0 ||
2300*058561cbSjbeck 		     (m->mf_pflags & SMFIP_RCPT_REJ) == 0))
2301*058561cbSjbeck 			continue;
2302*058561cbSjbeck 
2303*058561cbSjbeck 		response = milter_send_command(m, command, data, sz, e, state,
2304*058561cbSjbeck 						where);
23057c478bd9Sstevel@tonic-gate 
23067c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 21)
23077c478bd9Sstevel@tonic-gate 		{
23087c478bd9Sstevel@tonic-gate 			/* log the time it took for the command per filter */
23097c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
23107c478bd9Sstevel@tonic-gate 				  "Milter (%s): time command (%c), %d",
23117c478bd9Sstevel@tonic-gate 				  m->mf_name, command, (int) (tn - curtime()));
23127c478bd9Sstevel@tonic-gate 		}
23137c478bd9Sstevel@tonic-gate 
23147c478bd9Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE)
23157c478bd9Sstevel@tonic-gate 			break;
23167c478bd9Sstevel@tonic-gate 	}
23177c478bd9Sstevel@tonic-gate 	return response;
23187c478bd9Sstevel@tonic-gate }
2319*058561cbSjbeck 
2320*058561cbSjbeck static int milter_getsymlist __P((struct milter *, char *, int, int));
2321*058561cbSjbeck 
2322*058561cbSjbeck static int
2323*058561cbSjbeck milter_getsymlist(m, buf, rlen, offset)
2324*058561cbSjbeck 	struct milter *m;
2325*058561cbSjbeck 	char *buf;
2326*058561cbSjbeck 	int rlen;
2327*058561cbSjbeck 	int offset;
2328*058561cbSjbeck {
2329*058561cbSjbeck 	int i, r, nummac;
2330*058561cbSjbeck 	mi_int32 v;
2331*058561cbSjbeck 
2332*058561cbSjbeck 	SM_ASSERT(m != NULL);
2333*058561cbSjbeck 	SM_ASSERT(buf != NULL);
2334*058561cbSjbeck 
2335*058561cbSjbeck 	while (offset + MILTER_LEN_BYTES < rlen)
2336*058561cbSjbeck 	{
2337*058561cbSjbeck 		size_t len;
2338*058561cbSjbeck 		char **macros;
2339*058561cbSjbeck 
2340*058561cbSjbeck 		nummac = 0;
2341*058561cbSjbeck 		(void) memcpy((char *) &v, buf + offset, MILTER_LEN_BYTES);
2342*058561cbSjbeck 		i = ntohl(v);
2343*058561cbSjbeck 		if (i < SMFIM_FIRST || i > SMFIM_LAST)
2344*058561cbSjbeck 			return -1;
2345*058561cbSjbeck 		offset += MILTER_LEN_BYTES;
2346*058561cbSjbeck 		macros = NULL;
2347*058561cbSjbeck 
2348*058561cbSjbeck 		switch (i)
2349*058561cbSjbeck 		{
2350*058561cbSjbeck 		  case MO_MACROS_CONNECT:
2351*058561cbSjbeck 			if (macros == NULL)
2352*058561cbSjbeck 				macros = MilterConnectMacros;
2353*058561cbSjbeck 			/* FALLTHROUGH */
2354*058561cbSjbeck 
2355*058561cbSjbeck 		  case MO_MACROS_HELO:
2356*058561cbSjbeck 			if (macros == NULL)
2357*058561cbSjbeck 				macros = MilterHeloMacros;
2358*058561cbSjbeck 			/* FALLTHROUGH */
2359*058561cbSjbeck 
2360*058561cbSjbeck 		  case MO_MACROS_ENVFROM:
2361*058561cbSjbeck 			if (macros == NULL)
2362*058561cbSjbeck 				macros = MilterEnvFromMacros;
2363*058561cbSjbeck 			/* FALLTHROUGH */
2364*058561cbSjbeck 
2365*058561cbSjbeck 		  case MO_MACROS_ENVRCPT:
2366*058561cbSjbeck 			if (macros == NULL)
2367*058561cbSjbeck 				macros = MilterEnvRcptMacros;
2368*058561cbSjbeck 			/* FALLTHROUGH */
2369*058561cbSjbeck 
2370*058561cbSjbeck 		  case MO_MACROS_EOM:
2371*058561cbSjbeck 			if (macros == NULL)
2372*058561cbSjbeck 				macros = MilterEOMMacros;
2373*058561cbSjbeck 			/* FALLTHROUGH */
2374*058561cbSjbeck 
2375*058561cbSjbeck 		  case MO_MACROS_EOH:
2376*058561cbSjbeck 			if (macros == NULL)
2377*058561cbSjbeck 				macros = MilterEOHMacros;
2378*058561cbSjbeck 			/* FALLTHROUGH */
2379*058561cbSjbeck 
2380*058561cbSjbeck 		  case MO_MACROS_DATA:
2381*058561cbSjbeck 			if (macros == NULL)
2382*058561cbSjbeck 				macros = MilterDataMacros;
2383*058561cbSjbeck 
2384*058561cbSjbeck 			len = strlen(buf + offset);
2385*058561cbSjbeck 			if (len > 0)
2386*058561cbSjbeck 			{
2387*058561cbSjbeck 				r = milter_set_macros(m->mf_name, macros,
2388*058561cbSjbeck 						buf + offset, nummac);
2389*058561cbSjbeck 				if (r >= 0)
2390*058561cbSjbeck 					nummac = r;
2391*058561cbSjbeck 			}
2392*058561cbSjbeck 			break;
2393*058561cbSjbeck 
2394*058561cbSjbeck 		  default:
2395*058561cbSjbeck 			return -1;
2396*058561cbSjbeck 		}
2397*058561cbSjbeck 		if (len == 0)
2398*058561cbSjbeck 			return -1;
2399*058561cbSjbeck 		offset += len + 1;
2400*058561cbSjbeck 	}
2401*058561cbSjbeck 
2402*058561cbSjbeck 	return 0;
2403*058561cbSjbeck }
2404*058561cbSjbeck 
24057c478bd9Sstevel@tonic-gate /*
24067c478bd9Sstevel@tonic-gate **  MILTER_NEGOTIATE -- get version and flags from filter
24077c478bd9Sstevel@tonic-gate **
24087c478bd9Sstevel@tonic-gate **	Parameters:
24097c478bd9Sstevel@tonic-gate **		m -- milter filter structure.
24107c478bd9Sstevel@tonic-gate **		e -- current envelope.
24117c478bd9Sstevel@tonic-gate **
24127c478bd9Sstevel@tonic-gate **	Returns:
24137c478bd9Sstevel@tonic-gate **		0 on success, -1 otherwise
24147c478bd9Sstevel@tonic-gate */
24157c478bd9Sstevel@tonic-gate 
24167c478bd9Sstevel@tonic-gate static int
24177c478bd9Sstevel@tonic-gate milter_negotiate(m, e)
24187c478bd9Sstevel@tonic-gate 	struct milter *m;
24197c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
24207c478bd9Sstevel@tonic-gate {
24217c478bd9Sstevel@tonic-gate 	char rcmd;
2422*058561cbSjbeck 	mi_int32 fvers, fflags, pflags;
2423*058561cbSjbeck 	mi_int32 mta_prot_vers, mta_prot_flags, mta_actions;
24247c478bd9Sstevel@tonic-gate 	ssize_t rlen;
242549218d4fSjbeck 	char *response;
24267c478bd9Sstevel@tonic-gate 	char data[MILTER_OPTLEN];
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 	/* sanity check */
24297c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
24307c478bd9Sstevel@tonic-gate 	{
24317c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24327c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24337c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate, impossible state",
24347c478bd9Sstevel@tonic-gate 				  m->mf_name);
24357c478bd9Sstevel@tonic-gate 		milter_error(m, e);
24367c478bd9Sstevel@tonic-gate 		return -1;
24377c478bd9Sstevel@tonic-gate 	}
24387c478bd9Sstevel@tonic-gate 
2439*058561cbSjbeck #if MILTER_CHECK
2440*058561cbSjbeck 	mta_prot_vers = m->mf_mta_prot_version;
2441*058561cbSjbeck 	mta_prot_flags = m->mf_mta_prot_flags;
2442*058561cbSjbeck 	mta_actions = m->mf_mta_actions;
2443*058561cbSjbeck #else /* MILTER_CHECK */
2444*058561cbSjbeck 	mta_prot_vers = SMFI_PROT_VERSION;
2445*058561cbSjbeck 	mta_prot_flags = SMFI_CURR_PROT;
2446*058561cbSjbeck 	mta_actions = SMFI_CURR_ACTS;
2447*058561cbSjbeck #endif /* MILTER_CHECK */
2448*058561cbSjbeck 
2449*058561cbSjbeck 	fvers = htonl(mta_prot_vers);
2450*058561cbSjbeck 	pflags = htonl(mta_prot_flags);
2451*058561cbSjbeck 	fflags = htonl(mta_actions);
24527c478bd9Sstevel@tonic-gate 	(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
24537c478bd9Sstevel@tonic-gate 	(void) memcpy(data + MILTER_LEN_BYTES,
24547c478bd9Sstevel@tonic-gate 		      (char *) &fflags, MILTER_LEN_BYTES);
24557c478bd9Sstevel@tonic-gate 	(void) memcpy(data + (MILTER_LEN_BYTES * 2),
24567c478bd9Sstevel@tonic-gate 		      (char *) &pflags, MILTER_LEN_BYTES);
2457*058561cbSjbeck 	(void) milter_write(m, SMFIC_OPTNEG, data, sizeof(data),
2458*058561cbSjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "negotiate");
24597c478bd9Sstevel@tonic-gate 
24607c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
24617c478bd9Sstevel@tonic-gate 		return -1;
24627c478bd9Sstevel@tonic-gate 
2463*058561cbSjbeck 	if (tTd(64, 5))
2464*058561cbSjbeck 		sm_dprintf("milter_negotiate(%s): send: version %lu, fflags 0x%lx, pflags 0x%lx\n",
2465*058561cbSjbeck 			m->mf_name, ntohl(fvers), ntohl(fflags), ntohl(pflags));
2466*058561cbSjbeck 
2467*058561cbSjbeck 	response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e,
2468*058561cbSjbeck 				"negotiate");
24697c478bd9Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
24707c478bd9Sstevel@tonic-gate 		return -1;
24717c478bd9Sstevel@tonic-gate 
24727c478bd9Sstevel@tonic-gate 	if (rcmd != SMFIC_OPTNEG)
24737c478bd9Sstevel@tonic-gate 	{
24747c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
24757c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n",
24767c478bd9Sstevel@tonic-gate 				m->mf_name, rcmd, SMFIC_OPTNEG);
24777c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24787c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24797c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: returned %c instead of %c",
24807c478bd9Sstevel@tonic-gate 				  m->mf_name, rcmd, SMFIC_OPTNEG);
24817c478bd9Sstevel@tonic-gate 		if (response != NULL)
24827c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
24837c478bd9Sstevel@tonic-gate 		milter_error(m, e);
24847c478bd9Sstevel@tonic-gate 		return -1;
24857c478bd9Sstevel@tonic-gate 	}
24867c478bd9Sstevel@tonic-gate 
24877c478bd9Sstevel@tonic-gate 	/* Make sure we have enough bytes for the version */
24887c478bd9Sstevel@tonic-gate 	if (response == NULL || rlen < MILTER_LEN_BYTES)
24897c478bd9Sstevel@tonic-gate 	{
24907c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
24917c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return valid info\n",
24927c478bd9Sstevel@tonic-gate 				m->mf_name);
24937c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24947c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24957c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return valid info",
24967c478bd9Sstevel@tonic-gate 				  m->mf_name);
24977c478bd9Sstevel@tonic-gate 		if (response != NULL)
24987c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
24997c478bd9Sstevel@tonic-gate 		milter_error(m, e);
25007c478bd9Sstevel@tonic-gate 		return -1;
25017c478bd9Sstevel@tonic-gate 	}
25027c478bd9Sstevel@tonic-gate 
25037c478bd9Sstevel@tonic-gate 	/* extract information */
25047c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
25057c478bd9Sstevel@tonic-gate 
25067c478bd9Sstevel@tonic-gate 	/* Now make sure we have enough for the feature bitmap */
2507*058561cbSjbeck 	if (rlen < MILTER_OPTLEN)
25087c478bd9Sstevel@tonic-gate 	{
25097c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
25107c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return enough info\n",
25117c478bd9Sstevel@tonic-gate 				m->mf_name);
25127c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25137c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25147c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return enough info",
25157c478bd9Sstevel@tonic-gate 				  m->mf_name);
25167c478bd9Sstevel@tonic-gate 		if (response != NULL)
25177c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
25187c478bd9Sstevel@tonic-gate 		milter_error(m, e);
25197c478bd9Sstevel@tonic-gate 		return -1;
25207c478bd9Sstevel@tonic-gate 	}
25217c478bd9Sstevel@tonic-gate 
25227c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
25237c478bd9Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
25247c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
25257c478bd9Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 	m->mf_fvers = ntohl(fvers);
25287c478bd9Sstevel@tonic-gate 	m->mf_fflags = ntohl(fflags);
25297c478bd9Sstevel@tonic-gate 	m->mf_pflags = ntohl(pflags);
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate 	/* check for version compatibility */
25327c478bd9Sstevel@tonic-gate 	if (m->mf_fvers == 1 ||
25337c478bd9Sstevel@tonic-gate 	    m->mf_fvers > SMFI_VERSION)
25347c478bd9Sstevel@tonic-gate 	{
25357c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
25367c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n",
25377c478bd9Sstevel@tonic-gate 				m->mf_name, m->mf_fvers, SMFI_VERSION);
25387c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25397c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25407c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: version %d != MTA milter version %d",
25417c478bd9Sstevel@tonic-gate 				  m->mf_name, m->mf_fvers, SMFI_VERSION);
25427c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2543*058561cbSjbeck 		goto error;
25447c478bd9Sstevel@tonic-gate 	}
25457c478bd9Sstevel@tonic-gate 
25467c478bd9Sstevel@tonic-gate 	/* check for filter feature mismatch */
2547*058561cbSjbeck 	if ((m->mf_fflags & mta_actions) != m->mf_fflags)
25487c478bd9Sstevel@tonic-gate 	{
25497c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
25507c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n",
25517c478bd9Sstevel@tonic-gate 				m->mf_name, m->mf_fflags,
2552*058561cbSjbeck 				(unsigned long) mta_actions);
25537c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25547c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25557c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx",
25567c478bd9Sstevel@tonic-gate 				  m->mf_name, m->mf_fflags,
2557*058561cbSjbeck 				  (unsigned long) mta_actions);
25587c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2559*058561cbSjbeck 		goto error;
25607c478bd9Sstevel@tonic-gate 	}
25617c478bd9Sstevel@tonic-gate 
25627c478bd9Sstevel@tonic-gate 	/* check for protocol feature mismatch */
2563*058561cbSjbeck 	if ((m->mf_pflags & mta_prot_flags) != m->mf_pflags)
25647c478bd9Sstevel@tonic-gate 	{
25657c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
25667c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n",
25677c478bd9Sstevel@tonic-gate 				m->mf_name, m->mf_pflags,
2568*058561cbSjbeck 				(unsigned long) mta_prot_flags);
25697c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25707c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25717c478bd9Sstevel@tonic-gate 				  "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx",
25727c478bd9Sstevel@tonic-gate 				  m->mf_name, m->mf_pflags,
2573*058561cbSjbeck 				  (unsigned long) mta_prot_flags);
25747c478bd9Sstevel@tonic-gate 		milter_error(m, e);
2575*058561cbSjbeck 		goto error;
25767c478bd9Sstevel@tonic-gate 	}
25777c478bd9Sstevel@tonic-gate 
257849218d4fSjbeck 	if (m->mf_fvers <= 2)
257949218d4fSjbeck 		m->mf_pflags |= SMFIP_NOUNKNOWN;
258049218d4fSjbeck 	if (m->mf_fvers <= 3)
258149218d4fSjbeck 		m->mf_pflags |= SMFIP_NODATA;
258249218d4fSjbeck 
2583*058561cbSjbeck 	if (rlen > MILTER_OPTLEN)
2584*058561cbSjbeck 	{
2585*058561cbSjbeck 		milter_getsymlist(m, response, rlen, MILTER_OPTLEN);
2586*058561cbSjbeck 	}
2587*058561cbSjbeck 
25887c478bd9Sstevel@tonic-gate 	if (tTd(64, 5))
2589*058561cbSjbeck 		sm_dprintf("milter_negotiate(%s): received: version %u, fflags 0x%x, pflags 0x%x\n",
25907c478bd9Sstevel@tonic-gate 			m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
25917c478bd9Sstevel@tonic-gate 	return 0;
2592*058561cbSjbeck 
2593*058561cbSjbeck   error:
2594*058561cbSjbeck 	if (response != NULL)
2595*058561cbSjbeck 		sm_free(response); /* XXX */
2596*058561cbSjbeck 	return -1;
25977c478bd9Sstevel@tonic-gate }
2598*058561cbSjbeck 
25997c478bd9Sstevel@tonic-gate /*
26007c478bd9Sstevel@tonic-gate **  MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
26017c478bd9Sstevel@tonic-gate **
26027c478bd9Sstevel@tonic-gate **	Reduce code duplication by putting these checks in one place
26037c478bd9Sstevel@tonic-gate **
26047c478bd9Sstevel@tonic-gate **	Parameters:
26057c478bd9Sstevel@tonic-gate **		e -- current envelope.
26067c478bd9Sstevel@tonic-gate **
26077c478bd9Sstevel@tonic-gate **	Returns:
26087c478bd9Sstevel@tonic-gate **		none
26097c478bd9Sstevel@tonic-gate */
26107c478bd9Sstevel@tonic-gate 
26117c478bd9Sstevel@tonic-gate static void
26127c478bd9Sstevel@tonic-gate milter_per_connection_check(e)
26137c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
26147c478bd9Sstevel@tonic-gate {
26157c478bd9Sstevel@tonic-gate 	int i;
26167c478bd9Sstevel@tonic-gate 
26177c478bd9Sstevel@tonic-gate 	/* see if we are done with any of the filters */
26187c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
26197c478bd9Sstevel@tonic-gate 	{
26207c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
26217c478bd9Sstevel@tonic-gate 
26227c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_CLOSABLE)
26237c478bd9Sstevel@tonic-gate 			milter_quit_filter(m, e);
26247c478bd9Sstevel@tonic-gate 	}
26257c478bd9Sstevel@tonic-gate }
2626*058561cbSjbeck 
26277c478bd9Sstevel@tonic-gate /*
26287c478bd9Sstevel@tonic-gate **  MILTER_ERROR -- Put a milter filter into error state
26297c478bd9Sstevel@tonic-gate **
26307c478bd9Sstevel@tonic-gate **	Parameters:
26317c478bd9Sstevel@tonic-gate **		m -- the broken filter.
26327c478bd9Sstevel@tonic-gate **		e -- current envelope.
26337c478bd9Sstevel@tonic-gate **
26347c478bd9Sstevel@tonic-gate **	Returns:
26357c478bd9Sstevel@tonic-gate **		none
26367c478bd9Sstevel@tonic-gate */
26377c478bd9Sstevel@tonic-gate 
26387c478bd9Sstevel@tonic-gate static void
26397c478bd9Sstevel@tonic-gate milter_error(m, e)
26407c478bd9Sstevel@tonic-gate 	struct milter *m;
26417c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
26427c478bd9Sstevel@tonic-gate {
26437c478bd9Sstevel@tonic-gate 	/*
26447c478bd9Sstevel@tonic-gate 	**  We could send a quit here but we may have gotten here due to
26457c478bd9Sstevel@tonic-gate 	**  an I/O error so we don't want to try to make things worse.
26467c478bd9Sstevel@tonic-gate 	*/
26477c478bd9Sstevel@tonic-gate 
26487c478bd9Sstevel@tonic-gate 	if (m->mf_sock >= 0)
26497c478bd9Sstevel@tonic-gate 	{
26507c478bd9Sstevel@tonic-gate 		(void) close(m->mf_sock);
26517c478bd9Sstevel@tonic-gate 		m->mf_sock = -1;
26527c478bd9Sstevel@tonic-gate 	}
26537c478bd9Sstevel@tonic-gate 	m->mf_state = SMFS_ERROR;
26547c478bd9Sstevel@tonic-gate 
26557c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 0)
26567c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state",
26577c478bd9Sstevel@tonic-gate 			  m->mf_name);
26587c478bd9Sstevel@tonic-gate }
2659*058561cbSjbeck 
26607c478bd9Sstevel@tonic-gate /*
26617c478bd9Sstevel@tonic-gate **  MILTER_HEADERS -- send headers to a single milter filter
26627c478bd9Sstevel@tonic-gate **
26637c478bd9Sstevel@tonic-gate **	Parameters:
26647c478bd9Sstevel@tonic-gate **		m -- current filter.
26657c478bd9Sstevel@tonic-gate **		e -- current envelope.
26667c478bd9Sstevel@tonic-gate **		state -- return state from response.
26677c478bd9Sstevel@tonic-gate **
26687c478bd9Sstevel@tonic-gate **	Returns:
26697c478bd9Sstevel@tonic-gate **		response string (may be NULL)
26707c478bd9Sstevel@tonic-gate */
26717c478bd9Sstevel@tonic-gate 
26727c478bd9Sstevel@tonic-gate static char *
26737c478bd9Sstevel@tonic-gate milter_headers(m, e, state)
26747c478bd9Sstevel@tonic-gate 	struct milter *m;
26757c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
26767c478bd9Sstevel@tonic-gate 	char *state;
26777c478bd9Sstevel@tonic-gate {
26787c478bd9Sstevel@tonic-gate 	char *response = NULL;
26797c478bd9Sstevel@tonic-gate 	HDR *h;
26807c478bd9Sstevel@tonic-gate 
26817c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 17)
26827c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send",
26837c478bd9Sstevel@tonic-gate 			  m->mf_name);
26847c478bd9Sstevel@tonic-gate 
26857c478bd9Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
26867c478bd9Sstevel@tonic-gate 	{
2687*058561cbSjbeck 		int len_n, len_v, len_t, len_f;
2688*058561cbSjbeck 		char *buf, *hv;
26897c478bd9Sstevel@tonic-gate 
26907c478bd9Sstevel@tonic-gate 		/* don't send over deleted headers */
26917c478bd9Sstevel@tonic-gate 		if (h->h_value == NULL)
26927c478bd9Sstevel@tonic-gate 		{
26937c478bd9Sstevel@tonic-gate 			/* strip H_USER so not counted in milter_changeheader() */
26947c478bd9Sstevel@tonic-gate 			h->h_flags &= ~H_USER;
26957c478bd9Sstevel@tonic-gate 			continue;
26967c478bd9Sstevel@tonic-gate 		}
26977c478bd9Sstevel@tonic-gate 
26987c478bd9Sstevel@tonic-gate 		/* skip auto-generated */
26997c478bd9Sstevel@tonic-gate 		if (!bitset(H_USER, h->h_flags))
27007c478bd9Sstevel@tonic-gate 			continue;
27017c478bd9Sstevel@tonic-gate 
27027c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
27037c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_headers: %s:%s\n",
27047c478bd9Sstevel@tonic-gate 				h->h_field, h->h_value);
27057c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 21)
27067c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s",
27077c478bd9Sstevel@tonic-gate 				  m->mf_name, h->h_field);
27087c478bd9Sstevel@tonic-gate 
2709*058561cbSjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)
2710*058561cbSjbeck 		    || *(h->h_value) != ' ')
2711*058561cbSjbeck 			hv = h->h_value;
2712*058561cbSjbeck 		else
2713*058561cbSjbeck 			hv = h->h_value + 1;
2714*058561cbSjbeck 		len_f = strlen(h->h_field) + 1;
2715*058561cbSjbeck 		len_t = len_f + strlen(hv) + 1;
2716*058561cbSjbeck 		if (len_t < 0)
27177c478bd9Sstevel@tonic-gate 			continue;
2718*058561cbSjbeck 		buf = (char *) xalloc(len_t);
2719*058561cbSjbeck 
2720*058561cbSjbeck 		/*
2721*058561cbSjbeck 		**  Note: currently the call to dequote_internal_chars()
2722*058561cbSjbeck 		**  is not required as h_field is supposed to be 7-bit US-ASCII.
2723*058561cbSjbeck 		*/
2724*058561cbSjbeck 
2725*058561cbSjbeck 		len_n = dequote_internal_chars(h->h_field, buf, len_f);
2726*058561cbSjbeck 		SM_ASSERT(len_n < len_f);
2727*058561cbSjbeck 		len_v = dequote_internal_chars(hv, buf + len_n + 1,
2728*058561cbSjbeck 						len_t - len_n - 1);
2729*058561cbSjbeck 		SM_ASSERT(len_t >= len_n + 1 + len_v + 1);
2730*058561cbSjbeck 		len_t = len_n + 1 + len_v + 1;
27317c478bd9Sstevel@tonic-gate 
27327c478bd9Sstevel@tonic-gate 		/* send it over */
27337c478bd9Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_HEADER, buf,
2734*058561cbSjbeck 					       len_t, e, state, "header");
2735*058561cbSjbeck 		sm_free(buf);
27367c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
27377c478bd9Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
27387c478bd9Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
27397c478bd9Sstevel@tonic-gate 			break;
27407c478bd9Sstevel@tonic-gate 	}
27417c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 17)
27427c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent",
27437c478bd9Sstevel@tonic-gate 			  m->mf_name);
27447c478bd9Sstevel@tonic-gate 	return response;
27457c478bd9Sstevel@tonic-gate }
2746*058561cbSjbeck 
27477c478bd9Sstevel@tonic-gate /*
27487c478bd9Sstevel@tonic-gate **  MILTER_BODY -- send the body to a filter
27497c478bd9Sstevel@tonic-gate **
27507c478bd9Sstevel@tonic-gate **	Parameters:
27517c478bd9Sstevel@tonic-gate **		m -- current filter.
27527c478bd9Sstevel@tonic-gate **		e -- current envelope.
27537c478bd9Sstevel@tonic-gate **		state -- return state from response.
27547c478bd9Sstevel@tonic-gate **
27557c478bd9Sstevel@tonic-gate **	Returns:
27567c478bd9Sstevel@tonic-gate **		response string (may be NULL)
27577c478bd9Sstevel@tonic-gate */
27587c478bd9Sstevel@tonic-gate 
27597c478bd9Sstevel@tonic-gate static char *
27607c478bd9Sstevel@tonic-gate milter_body(m, e, state)
27617c478bd9Sstevel@tonic-gate 	struct milter *m;
27627c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
27637c478bd9Sstevel@tonic-gate 	char *state;
27647c478bd9Sstevel@tonic-gate {
27657c478bd9Sstevel@tonic-gate 	char bufchar = '\0';
27667c478bd9Sstevel@tonic-gate 	char prevchar = '\0';
27677c478bd9Sstevel@tonic-gate 	int c;
27687c478bd9Sstevel@tonic-gate 	char *response = NULL;
27697c478bd9Sstevel@tonic-gate 	char *bp;
27707c478bd9Sstevel@tonic-gate 	char buf[MILTER_CHUNK_SIZE];
27717c478bd9Sstevel@tonic-gate 
27727c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
27737c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_body\n");
27747c478bd9Sstevel@tonic-gate 
27757c478bd9Sstevel@tonic-gate 	if (bfrewind(e->e_dfp) < 0)
27767c478bd9Sstevel@tonic-gate 	{
27777c478bd9Sstevel@tonic-gate 		ExitStat = EX_IOERR;
27787c478bd9Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
27797c478bd9Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: rewind error",
27807c478bd9Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
27817c478bd9Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
27827c478bd9Sstevel@tonic-gate 		return NULL;
27837c478bd9Sstevel@tonic-gate 	}
27847c478bd9Sstevel@tonic-gate 
27857c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 17)
27867c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send",
27877c478bd9Sstevel@tonic-gate 			  m->mf_name);
27887c478bd9Sstevel@tonic-gate 	bp = buf;
27897c478bd9Sstevel@tonic-gate 	while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF)
27907c478bd9Sstevel@tonic-gate 	{
27917c478bd9Sstevel@tonic-gate 		/*  Change LF to CRLF */
27927c478bd9Sstevel@tonic-gate 		if (c == '\n')
27937c478bd9Sstevel@tonic-gate 		{
2794*058561cbSjbeck #if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
27957c478bd9Sstevel@tonic-gate 			/* Not a CRLF already? */
27967c478bd9Sstevel@tonic-gate 			if (prevchar != '\r')
2797*058561cbSjbeck #endif /* !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */
27987c478bd9Sstevel@tonic-gate 			{
27997c478bd9Sstevel@tonic-gate 				/* Room for CR now? */
2800*058561cbSjbeck 				if (bp + 2 > &buf[sizeof(buf)])
28017c478bd9Sstevel@tonic-gate 				{
28027c478bd9Sstevel@tonic-gate 					/* No room, buffer LF */
28037c478bd9Sstevel@tonic-gate 					bufchar = c;
28047c478bd9Sstevel@tonic-gate 
28057c478bd9Sstevel@tonic-gate 					/* and send CR now */
28067c478bd9Sstevel@tonic-gate 					c = '\r';
28077c478bd9Sstevel@tonic-gate 				}
28087c478bd9Sstevel@tonic-gate 				else
28097c478bd9Sstevel@tonic-gate 				{
28107c478bd9Sstevel@tonic-gate 					/* Room to do it now */
28117c478bd9Sstevel@tonic-gate 					*bp++ = '\r';
28127c478bd9Sstevel@tonic-gate 					prevchar = '\r';
28137c478bd9Sstevel@tonic-gate 				}
28147c478bd9Sstevel@tonic-gate 			}
28157c478bd9Sstevel@tonic-gate 		}
28167c478bd9Sstevel@tonic-gate 		*bp++ = (char) c;
28177c478bd9Sstevel@tonic-gate 		prevchar = c;
2818*058561cbSjbeck 		if (bp >= &buf[sizeof(buf)])
28197c478bd9Sstevel@tonic-gate 		{
28207c478bd9Sstevel@tonic-gate 			/* send chunk */
28217c478bd9Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_BODY, buf,
2822*058561cbSjbeck 						       bp - buf, e, state,
2823*058561cbSjbeck 							"body chunk");
28247c478bd9Sstevel@tonic-gate 			bp = buf;
28257c478bd9Sstevel@tonic-gate 			if (bufchar != '\0')
28267c478bd9Sstevel@tonic-gate 			{
28277c478bd9Sstevel@tonic-gate 				*bp++ = bufchar;
28287c478bd9Sstevel@tonic-gate 				bufchar = '\0';
28297c478bd9Sstevel@tonic-gate 				prevchar = bufchar;
28307c478bd9Sstevel@tonic-gate 			}
28317c478bd9Sstevel@tonic-gate 		}
28327c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
28337c478bd9Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
2834*058561cbSjbeck 		    m->mf_state == SMFS_SKIP ||
28357c478bd9Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
28367c478bd9Sstevel@tonic-gate 			break;
28377c478bd9Sstevel@tonic-gate 	}
28387c478bd9Sstevel@tonic-gate 
28397c478bd9Sstevel@tonic-gate 	/* check for read errors */
28407c478bd9Sstevel@tonic-gate 	if (sm_io_error(e->e_dfp))
28417c478bd9Sstevel@tonic-gate 	{
28427c478bd9Sstevel@tonic-gate 		ExitStat = EX_IOERR;
28437c478bd9Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
2844*058561cbSjbeck 		    *state == SMFIR_ACCEPT ||
2845*058561cbSjbeck 		    m->mf_state == SMFS_SKIP)
28467c478bd9Sstevel@tonic-gate 		{
28477c478bd9Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
28487c478bd9Sstevel@tonic-gate 			if (response != NULL)
28497c478bd9Sstevel@tonic-gate 			{
28507c478bd9Sstevel@tonic-gate 				sm_free(response); /* XXX */
28517c478bd9Sstevel@tonic-gate 				response = NULL;
28527c478bd9Sstevel@tonic-gate 			}
28537c478bd9Sstevel@tonic-gate 		}
28547c478bd9Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: read error",
28557c478bd9Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
28567c478bd9Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
28577c478bd9Sstevel@tonic-gate 		return response;
28587c478bd9Sstevel@tonic-gate 	}
28597c478bd9Sstevel@tonic-gate 
28607c478bd9Sstevel@tonic-gate 	/* send last body chunk */
28617c478bd9Sstevel@tonic-gate 	if (bp > buf &&
28627c478bd9Sstevel@tonic-gate 	    m->mf_state != SMFS_ERROR &&
28637c478bd9Sstevel@tonic-gate 	    m->mf_state != SMFS_DONE &&
2864*058561cbSjbeck 	    m->mf_state != SMFS_SKIP &&
28657c478bd9Sstevel@tonic-gate 	    *state == SMFIR_CONTINUE)
28667c478bd9Sstevel@tonic-gate 	{
28677c478bd9Sstevel@tonic-gate 		/* send chunk */
28687c478bd9Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
2869*058561cbSjbeck 					       e, state, "last body chunk");
28707c478bd9Sstevel@tonic-gate 		bp = buf;
28717c478bd9Sstevel@tonic-gate 	}
28727c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 17)
28737c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent",
28747c478bd9Sstevel@tonic-gate 			  m->mf_name);
2875*058561cbSjbeck 	if (m->mf_state == SMFS_SKIP)
2876*058561cbSjbeck 	{
2877*058561cbSjbeck 		*state = SMFIR_CONTINUE;
2878*058561cbSjbeck 		m->mf_state = SMFS_READY;
2879*058561cbSjbeck 	}
2880*058561cbSjbeck 
28817c478bd9Sstevel@tonic-gate 	return response;
28827c478bd9Sstevel@tonic-gate }
28837c478bd9Sstevel@tonic-gate 
28847c478bd9Sstevel@tonic-gate /*
28857c478bd9Sstevel@tonic-gate **  Actions
28867c478bd9Sstevel@tonic-gate */
28877c478bd9Sstevel@tonic-gate 
28887c478bd9Sstevel@tonic-gate /*
2889*058561cbSjbeck **  ADDLEADINGSPACE -- Add a leading space to a string
2890*058561cbSjbeck **
2891*058561cbSjbeck **	Parameters:
2892*058561cbSjbeck **		str -- string
2893*058561cbSjbeck **		rp -- resource pool for allocations
2894*058561cbSjbeck **
2895*058561cbSjbeck **	Returns:
2896*058561cbSjbeck **		pointer to new string
2897*058561cbSjbeck */
2898*058561cbSjbeck 
2899*058561cbSjbeck static char *addleadingspace __P((char *, SM_RPOOL_T *));
2900*058561cbSjbeck 
2901*058561cbSjbeck static char *
2902*058561cbSjbeck addleadingspace(str, rp)
2903*058561cbSjbeck 	char *str;
2904*058561cbSjbeck 	SM_RPOOL_T *rp;
2905*058561cbSjbeck {
2906*058561cbSjbeck 	size_t l;
2907*058561cbSjbeck 	char *new;
2908*058561cbSjbeck 
2909*058561cbSjbeck 	SM_ASSERT(str != NULL);
2910*058561cbSjbeck 	l = strlen(str);
2911*058561cbSjbeck 	SM_ASSERT(l + 2 > l);
2912*058561cbSjbeck 	new = sm_rpool_malloc_x(rp, l + 2);
2913*058561cbSjbeck 	new[0] = ' ';
2914*058561cbSjbeck 	new[1] = '\0';
2915*058561cbSjbeck 	sm_strlcpy(new + 1, str, l + 1);
2916*058561cbSjbeck 	return new;
2917*058561cbSjbeck }
2918*058561cbSjbeck 
2919*058561cbSjbeck /*
29207c478bd9Sstevel@tonic-gate **  MILTER_ADDHEADER -- Add the supplied header to the message
29217c478bd9Sstevel@tonic-gate **
29227c478bd9Sstevel@tonic-gate **	Parameters:
2923*058561cbSjbeck **		m -- current filter.
29247c478bd9Sstevel@tonic-gate **		response -- encoded form of header/value.
29257c478bd9Sstevel@tonic-gate **		rlen -- length of response.
29267c478bd9Sstevel@tonic-gate **		e -- current envelope.
29277c478bd9Sstevel@tonic-gate **
29287c478bd9Sstevel@tonic-gate **	Returns:
29297c478bd9Sstevel@tonic-gate **		none
29307c478bd9Sstevel@tonic-gate */
29317c478bd9Sstevel@tonic-gate 
29327c478bd9Sstevel@tonic-gate static void
2933*058561cbSjbeck milter_addheader(m, response, rlen, e)
2934*058561cbSjbeck 	struct milter *m;
29357c478bd9Sstevel@tonic-gate 	char *response;
29367c478bd9Sstevel@tonic-gate 	ssize_t rlen;
29377c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
29387c478bd9Sstevel@tonic-gate {
2939*058561cbSjbeck 	int mh_v_len;
2940*058561cbSjbeck 	char *val, *mh_value;
29417c478bd9Sstevel@tonic-gate 	HDR *h;
29427c478bd9Sstevel@tonic-gate 
29437c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
29447c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_addheader: ");
29457c478bd9Sstevel@tonic-gate 
29467c478bd9Sstevel@tonic-gate 	/* sanity checks */
29477c478bd9Sstevel@tonic-gate 	if (response == NULL)
29487c478bd9Sstevel@tonic-gate 	{
29497c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
29507c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
29517c478bd9Sstevel@tonic-gate 		return;
29527c478bd9Sstevel@tonic-gate 	}
29537c478bd9Sstevel@tonic-gate 
29547c478bd9Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
29557c478bd9Sstevel@tonic-gate 	{
29567c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
2957*058561cbSjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
2958*058561cbSjbeck 				   (int) strlen(response), (int) (rlen - 1));
29597c478bd9Sstevel@tonic-gate 		return;
29607c478bd9Sstevel@tonic-gate 	}
29617c478bd9Sstevel@tonic-gate 
29627c478bd9Sstevel@tonic-gate 	/* Find separating NUL */
29637c478bd9Sstevel@tonic-gate 	val = response + strlen(response) + 1;
29647c478bd9Sstevel@tonic-gate 
29657c478bd9Sstevel@tonic-gate 	/* another sanity check */
29667c478bd9Sstevel@tonic-gate 	if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
29677c478bd9Sstevel@tonic-gate 	{
29687c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
29697c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
29707c478bd9Sstevel@tonic-gate 		return;
29717c478bd9Sstevel@tonic-gate 	}
29727c478bd9Sstevel@tonic-gate 
29737c478bd9Sstevel@tonic-gate 	if (*response == '\0')
29747c478bd9Sstevel@tonic-gate 	{
29757c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
29767c478bd9Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
29777c478bd9Sstevel@tonic-gate 		return;
29787c478bd9Sstevel@tonic-gate 	}
29797c478bd9Sstevel@tonic-gate 
29807c478bd9Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
29817c478bd9Sstevel@tonic-gate 	{
29827c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, response) == 0 &&
29837c478bd9Sstevel@tonic-gate 		    !bitset(H_USER, h->h_flags) &&
29847c478bd9Sstevel@tonic-gate 		    !bitset(H_TRACE, h->h_flags))
29857c478bd9Sstevel@tonic-gate 			break;
29867c478bd9Sstevel@tonic-gate 	}
29877c478bd9Sstevel@tonic-gate 
2988*058561cbSjbeck 	mh_v_len = 0;
2989*058561cbSjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
2990*058561cbSjbeck 
29917c478bd9Sstevel@tonic-gate 	/* add to e_msgsize */
29927c478bd9Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
29937c478bd9Sstevel@tonic-gate 
29947c478bd9Sstevel@tonic-gate 	if (h != NULL)
29957c478bd9Sstevel@tonic-gate 	{
29967c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
29977c478bd9Sstevel@tonic-gate 			sm_dprintf("Replace default header %s value with %s\n",
2998*058561cbSjbeck 				   h->h_field, mh_value);
29997c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 8)
30007c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
30017c478bd9Sstevel@tonic-gate 				  "Milter change: default header %s value with %s",
3002*058561cbSjbeck 				  h->h_field, mh_value);
3003*058561cbSjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
3004*058561cbSjbeck 			h->h_value = mh_value;
3005*058561cbSjbeck 		else
3006*058561cbSjbeck 		{
3007*058561cbSjbeck 			h->h_value = addleadingspace (mh_value, e->e_rpool);
3008*058561cbSjbeck 			SM_FREE(mh_value);
3009*058561cbSjbeck 		}
30107c478bd9Sstevel@tonic-gate 		h->h_flags |= H_USER;
30117c478bd9Sstevel@tonic-gate 	}
30127c478bd9Sstevel@tonic-gate 	else
30137c478bd9Sstevel@tonic-gate 	{
30147c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
3015*058561cbSjbeck 			sm_dprintf("Add %s: %s\n", response, mh_value);
30167c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 8)
3017*058561cbSjbeck 			sm_syslog(LOG_INFO, e->e_id,
3018*058561cbSjbeck 				  "Milter add: header: %s: %s",
3019*058561cbSjbeck 				  response, mh_value);
3020*058561cbSjbeck 		addheader(newstr(response), mh_value, H_USER, e,
3021*058561cbSjbeck 			!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
3022*058561cbSjbeck 		SM_FREE(mh_value);
30237c478bd9Sstevel@tonic-gate 	}
30247c478bd9Sstevel@tonic-gate }
3025*058561cbSjbeck 
30267c478bd9Sstevel@tonic-gate /*
30277c478bd9Sstevel@tonic-gate **  MILTER_INSHEADER -- Insert the supplied header
30287c478bd9Sstevel@tonic-gate **
30297c478bd9Sstevel@tonic-gate **	Parameters:
3030*058561cbSjbeck **		m -- current filter.
30317c478bd9Sstevel@tonic-gate **		response -- encoded form of header/value.
30327c478bd9Sstevel@tonic-gate **		rlen -- length of response.
30337c478bd9Sstevel@tonic-gate **		e -- current envelope.
30347c478bd9Sstevel@tonic-gate **
30357c478bd9Sstevel@tonic-gate **	Returns:
30367c478bd9Sstevel@tonic-gate **		none
30377c478bd9Sstevel@tonic-gate **
30387c478bd9Sstevel@tonic-gate **	Notes:
30397c478bd9Sstevel@tonic-gate **		Unlike milter_addheader(), this does not attempt to determine
30407c478bd9Sstevel@tonic-gate **		if the header already exists in the envelope, even a
30417c478bd9Sstevel@tonic-gate **		deleted version.  It just blindly inserts.
30427c478bd9Sstevel@tonic-gate */
30437c478bd9Sstevel@tonic-gate 
30447c478bd9Sstevel@tonic-gate static void
3045*058561cbSjbeck milter_insheader(m, response, rlen, e)
3046*058561cbSjbeck 	struct milter *m;
30477c478bd9Sstevel@tonic-gate 	char *response;
30487c478bd9Sstevel@tonic-gate 	ssize_t rlen;
30497c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
30507c478bd9Sstevel@tonic-gate {
30517c478bd9Sstevel@tonic-gate 	mi_int32 idx, i;
3052*058561cbSjbeck 	int mh_v_len;
3053*058561cbSjbeck 	char *field, *val, *mh_value;
30547c478bd9Sstevel@tonic-gate 
30557c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
30567c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_insheader: ");
30577c478bd9Sstevel@tonic-gate 
30587c478bd9Sstevel@tonic-gate 	/* sanity checks */
30597c478bd9Sstevel@tonic-gate 	if (response == NULL)
30607c478bd9Sstevel@tonic-gate 	{
30617c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
30627c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
30637c478bd9Sstevel@tonic-gate 		return;
30647c478bd9Sstevel@tonic-gate 	}
30657c478bd9Sstevel@tonic-gate 
30667c478bd9Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
30677c478bd9Sstevel@tonic-gate 	{
30687c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
30697c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
30707c478bd9Sstevel@tonic-gate 		return;
30717c478bd9Sstevel@tonic-gate 	}
30727c478bd9Sstevel@tonic-gate 
30737c478bd9Sstevel@tonic-gate 	/* decode */
30747c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
30757c478bd9Sstevel@tonic-gate 	idx = ntohl(i);
30767c478bd9Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
30777c478bd9Sstevel@tonic-gate 	val = field + strlen(field) + 1;
30787c478bd9Sstevel@tonic-gate 
30797c478bd9Sstevel@tonic-gate 	/* another sanity check */
30807c478bd9Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
30817c478bd9Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
30827c478bd9Sstevel@tonic-gate 	{
30837c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
30847c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
30857c478bd9Sstevel@tonic-gate 		return;
30867c478bd9Sstevel@tonic-gate 	}
30877c478bd9Sstevel@tonic-gate 
30887c478bd9Sstevel@tonic-gate 	if (*field == '\0')
30897c478bd9Sstevel@tonic-gate 	{
30907c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
30917c478bd9Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
30927c478bd9Sstevel@tonic-gate 		return;
30937c478bd9Sstevel@tonic-gate 	}
30947c478bd9Sstevel@tonic-gate 
30957c478bd9Sstevel@tonic-gate 	/* add to e_msgsize */
30967c478bd9Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
30977c478bd9Sstevel@tonic-gate 
30987c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
3099*058561cbSjbeck 		sm_dprintf("Insert (%d) %s: %s\n", idx, field, val);
31007c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 8)
31017c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id,
31027c478bd9Sstevel@tonic-gate 			  "Milter insert (%d): header: %s: %s",
31037c478bd9Sstevel@tonic-gate 			  idx, field, val);
3104*058561cbSjbeck 	mh_v_len = 0;
3105*058561cbSjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
3106*058561cbSjbeck 	insheader(idx, newstr(field), mh_value, H_USER, e,
3107*058561cbSjbeck 		!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
3108*058561cbSjbeck 	SM_FREE(mh_value);
31097c478bd9Sstevel@tonic-gate }
3110*058561cbSjbeck 
31117c478bd9Sstevel@tonic-gate /*
31127c478bd9Sstevel@tonic-gate **  MILTER_CHANGEHEADER -- Change the supplied header in the message
31137c478bd9Sstevel@tonic-gate **
31147c478bd9Sstevel@tonic-gate **	Parameters:
3115*058561cbSjbeck **		m -- current filter.
31167c478bd9Sstevel@tonic-gate **		response -- encoded form of header/index/value.
31177c478bd9Sstevel@tonic-gate **		rlen -- length of response.
31187c478bd9Sstevel@tonic-gate **		e -- current envelope.
31197c478bd9Sstevel@tonic-gate **
31207c478bd9Sstevel@tonic-gate **	Returns:
31217c478bd9Sstevel@tonic-gate **		none
31227c478bd9Sstevel@tonic-gate */
31237c478bd9Sstevel@tonic-gate 
31247c478bd9Sstevel@tonic-gate static void
3125*058561cbSjbeck milter_changeheader(m, response, rlen, e)
3126*058561cbSjbeck 	struct milter *m;
31277c478bd9Sstevel@tonic-gate 	char *response;
31287c478bd9Sstevel@tonic-gate 	ssize_t rlen;
31297c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
31307c478bd9Sstevel@tonic-gate {
31317c478bd9Sstevel@tonic-gate 	mi_int32 i, index;
3132*058561cbSjbeck 	int mh_v_len;
3133*058561cbSjbeck 	char *field, *val, *mh_value;
31347c478bd9Sstevel@tonic-gate 	HDR *h, *sysheader;
31357c478bd9Sstevel@tonic-gate 
31367c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
31377c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_changeheader: ");
31387c478bd9Sstevel@tonic-gate 
31397c478bd9Sstevel@tonic-gate 	/* sanity checks */
31407c478bd9Sstevel@tonic-gate 	if (response == NULL)
31417c478bd9Sstevel@tonic-gate 	{
31427c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
31437c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
31447c478bd9Sstevel@tonic-gate 		return;
31457c478bd9Sstevel@tonic-gate 	}
31467c478bd9Sstevel@tonic-gate 
31477c478bd9Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
31487c478bd9Sstevel@tonic-gate 	{
31497c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
31507c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
31517c478bd9Sstevel@tonic-gate 		return;
31527c478bd9Sstevel@tonic-gate 	}
31537c478bd9Sstevel@tonic-gate 
31547c478bd9Sstevel@tonic-gate 	/* Find separating NUL */
31557c478bd9Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
31567c478bd9Sstevel@tonic-gate 	index = ntohl(i);
31577c478bd9Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
31587c478bd9Sstevel@tonic-gate 	val = field + strlen(field) + 1;
31597c478bd9Sstevel@tonic-gate 
31607c478bd9Sstevel@tonic-gate 	/* another sanity check */
31617c478bd9Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
31627c478bd9Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
31637c478bd9Sstevel@tonic-gate 	{
31647c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
31657c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
31667c478bd9Sstevel@tonic-gate 		return;
31677c478bd9Sstevel@tonic-gate 	}
31687c478bd9Sstevel@tonic-gate 
31697c478bd9Sstevel@tonic-gate 	if (*field == '\0')
31707c478bd9Sstevel@tonic-gate 	{
31717c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
31727c478bd9Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
31737c478bd9Sstevel@tonic-gate 		return;
31747c478bd9Sstevel@tonic-gate 	}
31757c478bd9Sstevel@tonic-gate 
3176*058561cbSjbeck 	mh_v_len = 0;
3177*058561cbSjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
3178*058561cbSjbeck 
31797c478bd9Sstevel@tonic-gate 	sysheader = NULL;
31807c478bd9Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
31817c478bd9Sstevel@tonic-gate 	{
31827c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, field) == 0)
31837c478bd9Sstevel@tonic-gate 		{
3184*058561cbSjbeck 			if (bitset(H_USER, h->h_flags) && --index <= 0)
31857c478bd9Sstevel@tonic-gate 			{
31867c478bd9Sstevel@tonic-gate 				sysheader = NULL;
31877c478bd9Sstevel@tonic-gate 				break;
31887c478bd9Sstevel@tonic-gate 			}
31897c478bd9Sstevel@tonic-gate 			else if (!bitset(H_USER, h->h_flags) &&
31907c478bd9Sstevel@tonic-gate 				 !bitset(H_TRACE, h->h_flags))
31917c478bd9Sstevel@tonic-gate 			{
31927c478bd9Sstevel@tonic-gate 				/*
31937c478bd9Sstevel@tonic-gate 				**  DRUMS msg-fmt draft says can only have
31947c478bd9Sstevel@tonic-gate 				**  multiple occurences of trace fields,
31957c478bd9Sstevel@tonic-gate 				**  so make sure we replace any non-trace,
31967c478bd9Sstevel@tonic-gate 				**  non-user field.
31977c478bd9Sstevel@tonic-gate 				*/
31987c478bd9Sstevel@tonic-gate 
31997c478bd9Sstevel@tonic-gate 				sysheader = h;
32007c478bd9Sstevel@tonic-gate 			}
32017c478bd9Sstevel@tonic-gate 		}
32027c478bd9Sstevel@tonic-gate 	}
32037c478bd9Sstevel@tonic-gate 
32047c478bd9Sstevel@tonic-gate 	/* if not found as user-provided header at index, use sysheader */
32057c478bd9Sstevel@tonic-gate 	if (h == NULL)
32067c478bd9Sstevel@tonic-gate 		h = sysheader;
32077c478bd9Sstevel@tonic-gate 
32087c478bd9Sstevel@tonic-gate 	if (h == NULL)
32097c478bd9Sstevel@tonic-gate 	{
32107c478bd9Sstevel@tonic-gate 		if (*val == '\0')
32117c478bd9Sstevel@tonic-gate 		{
32127c478bd9Sstevel@tonic-gate 			if (tTd(64, 10))
32137c478bd9Sstevel@tonic-gate 				sm_dprintf("Delete (noop) %s\n", field);
32147c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 8)
32157c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
32167c478bd9Sstevel@tonic-gate 					"Milter delete (noop): header: %s"
32177c478bd9Sstevel@tonic-gate 					, field);
32187c478bd9Sstevel@tonic-gate 		}
32197c478bd9Sstevel@tonic-gate 		else
32207c478bd9Sstevel@tonic-gate 		{
32217c478bd9Sstevel@tonic-gate 			/* treat modify value with no existing header as add */
32227c478bd9Sstevel@tonic-gate 			if (tTd(64, 10))
3223*058561cbSjbeck 				sm_dprintf("Add %s: %s\n", field, mh_value);
32247c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 8)
32257c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
32267c478bd9Sstevel@tonic-gate 					"Milter change (add): header: %s: %s"
3227*058561cbSjbeck 					, field, mh_value);
3228*058561cbSjbeck 			addheader(newstr(field), mh_value, H_USER, e,
3229*058561cbSjbeck 				!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
32307c478bd9Sstevel@tonic-gate 		}
32317c478bd9Sstevel@tonic-gate 		return;
32327c478bd9Sstevel@tonic-gate 	}
32337c478bd9Sstevel@tonic-gate 
32347c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
32357c478bd9Sstevel@tonic-gate 	{
32367c478bd9Sstevel@tonic-gate 		if (*val == '\0')
32377c478bd9Sstevel@tonic-gate 		{
32387c478bd9Sstevel@tonic-gate 			sm_dprintf("Delete%s %s:%s\n",
32397c478bd9Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
32407c478bd9Sstevel@tonic-gate 				   field,
32417c478bd9Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value);
32427c478bd9Sstevel@tonic-gate 		}
32437c478bd9Sstevel@tonic-gate 		else
32447c478bd9Sstevel@tonic-gate 		{
32457c478bd9Sstevel@tonic-gate 			sm_dprintf("Change%s %s: from %s to %s\n",
32467c478bd9Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
32477c478bd9Sstevel@tonic-gate 				   field,
32487c478bd9Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value,
3249*058561cbSjbeck 				   mh_value);
32507c478bd9Sstevel@tonic-gate 		}
32517c478bd9Sstevel@tonic-gate 	}
32527c478bd9Sstevel@tonic-gate 
32537c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 8)
32547c478bd9Sstevel@tonic-gate 	{
32557c478bd9Sstevel@tonic-gate 		if (*val == '\0')
32567c478bd9Sstevel@tonic-gate 		{
32577c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
32587c478bd9Sstevel@tonic-gate 				  "Milter delete: header%s %s:%s",
32597c478bd9Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
32607c478bd9Sstevel@tonic-gate 				  field,
32617c478bd9Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value);
32627c478bd9Sstevel@tonic-gate 		}
32637c478bd9Sstevel@tonic-gate 		else
32647c478bd9Sstevel@tonic-gate 		{
32657c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
32667c478bd9Sstevel@tonic-gate 				  "Milter change: header%s %s: from %s to %s",
32677c478bd9Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
32687c478bd9Sstevel@tonic-gate 				  field,
32697c478bd9Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value,
3270*058561cbSjbeck 				  mh_value);
32717c478bd9Sstevel@tonic-gate 		}
32727c478bd9Sstevel@tonic-gate 	}
32737c478bd9Sstevel@tonic-gate 
32747c478bd9Sstevel@tonic-gate 	if (h != sysheader && h->h_value != NULL)
32757c478bd9Sstevel@tonic-gate 	{
32767c478bd9Sstevel@tonic-gate 		size_t l;
32777c478bd9Sstevel@tonic-gate 
32787c478bd9Sstevel@tonic-gate 		l = strlen(h->h_value);
32797c478bd9Sstevel@tonic-gate 		if (l > e->e_msgsize)
32807c478bd9Sstevel@tonic-gate 			e->e_msgsize = 0;
32817c478bd9Sstevel@tonic-gate 		else
32827c478bd9Sstevel@tonic-gate 			e->e_msgsize -= l;
32837c478bd9Sstevel@tonic-gate 		/* rpool, don't free: sm_free(h->h_value); XXX */
32847c478bd9Sstevel@tonic-gate 	}
32857c478bd9Sstevel@tonic-gate 
32867c478bd9Sstevel@tonic-gate 	if (*val == '\0')
32877c478bd9Sstevel@tonic-gate 	{
32887c478bd9Sstevel@tonic-gate 		/* Remove "Field: " from message size */
32897c478bd9Sstevel@tonic-gate 		if (h != sysheader)
32907c478bd9Sstevel@tonic-gate 		{
32917c478bd9Sstevel@tonic-gate 			size_t l;
32927c478bd9Sstevel@tonic-gate 
32937c478bd9Sstevel@tonic-gate 			l = strlen(h->h_field) + 2;
32947c478bd9Sstevel@tonic-gate 			if (l > e->e_msgsize)
32957c478bd9Sstevel@tonic-gate 				e->e_msgsize = 0;
32967c478bd9Sstevel@tonic-gate 			else
32977c478bd9Sstevel@tonic-gate 				e->e_msgsize -= l;
32987c478bd9Sstevel@tonic-gate 		}
32997c478bd9Sstevel@tonic-gate 		h->h_value = NULL;
3300*058561cbSjbeck 		SM_FREE(mh_value);
33017c478bd9Sstevel@tonic-gate 	}
33027c478bd9Sstevel@tonic-gate 	else
33037c478bd9Sstevel@tonic-gate 	{
3304*058561cbSjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
3305*058561cbSjbeck 			h->h_value = mh_value;
3306*058561cbSjbeck 		else
3307*058561cbSjbeck 		{
3308*058561cbSjbeck 			h->h_value = addleadingspace (mh_value, e->e_rpool);
3309*058561cbSjbeck 			SM_FREE(mh_value);
3310*058561cbSjbeck 		}
33117c478bd9Sstevel@tonic-gate 		h->h_flags |= H_USER;
33127c478bd9Sstevel@tonic-gate 		e->e_msgsize += strlen(h->h_value);
33137c478bd9Sstevel@tonic-gate 	}
33147c478bd9Sstevel@tonic-gate }
3315*058561cbSjbeck 
3316*058561cbSjbeck /*
3317*058561cbSjbeck **  MILTER_SPLIT_RESPONSE -- Split response into fields.
3318*058561cbSjbeck **
3319*058561cbSjbeck **	Parameters:
3320*058561cbSjbeck **		response -- encoded repsonse.
3321*058561cbSjbeck **		rlen -- length of response.
3322*058561cbSjbeck **		pargc -- number of arguments (ouput)
3323*058561cbSjbeck **
3324*058561cbSjbeck **	Returns:
3325*058561cbSjbeck **		array of pointers to the individual strings
3326*058561cbSjbeck */
3327*058561cbSjbeck 
3328*058561cbSjbeck static char **milter_split_response __P((char *, ssize_t, int *));
3329*058561cbSjbeck 
3330*058561cbSjbeck static char **
3331*058561cbSjbeck milter_split_response(response, rlen, pargc)
3332*058561cbSjbeck 	char *response;
3333*058561cbSjbeck 	ssize_t rlen;
3334*058561cbSjbeck 	int *pargc;
3335*058561cbSjbeck {
3336*058561cbSjbeck 	char **s;
3337*058561cbSjbeck 	size_t i;
3338*058561cbSjbeck 	int elem, nelem;
3339*058561cbSjbeck 
3340*058561cbSjbeck 	SM_ASSERT(response != NULL);
3341*058561cbSjbeck 	SM_ASSERT(pargc != NULL);
3342*058561cbSjbeck 	*pargc = 0;
3343*058561cbSjbeck 	if (rlen < 2 || strlen(response) >= (size_t) rlen)
3344*058561cbSjbeck 	{
3345*058561cbSjbeck 		if (tTd(64, 10))
3346*058561cbSjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
3347*058561cbSjbeck 				   (int) strlen(response), (int) (rlen - 1));
3348*058561cbSjbeck 		return NULL;
3349*058561cbSjbeck 	}
3350*058561cbSjbeck 
3351*058561cbSjbeck 	nelem = 0;
3352*058561cbSjbeck 	for (i = 0; i < rlen; i++)
3353*058561cbSjbeck 	{
3354*058561cbSjbeck 		if (response[i] == '\0')
3355*058561cbSjbeck 			++nelem;
3356*058561cbSjbeck 	}
3357*058561cbSjbeck 	if (nelem == 0)
3358*058561cbSjbeck 		return NULL;
3359*058561cbSjbeck 
3360*058561cbSjbeck 	/* last entry is only for the name */
3361*058561cbSjbeck 	s = (char **)malloc(nelem * (sizeof(*s)));
3362*058561cbSjbeck 	if (s == NULL)
3363*058561cbSjbeck 		return NULL;
3364*058561cbSjbeck 	s[0] = response;
3365*058561cbSjbeck 	for (i = 0, elem = 0; i < rlen && elem < nelem; i++)
3366*058561cbSjbeck 	{
3367*058561cbSjbeck 		if (response[i] == '\0')
3368*058561cbSjbeck 		{
3369*058561cbSjbeck 			++elem;
3370*058561cbSjbeck 			if (i + 1 >= rlen)
3371*058561cbSjbeck 				s[elem] = NULL;
3372*058561cbSjbeck 			else
3373*058561cbSjbeck 				s[elem] = &(response[i + 1]);
3374*058561cbSjbeck 		}
3375*058561cbSjbeck 	}
3376*058561cbSjbeck 	*pargc = nelem;
3377*058561cbSjbeck 
3378*058561cbSjbeck 	if (tTd(64, 10))
3379*058561cbSjbeck 	{
3380*058561cbSjbeck 		for (elem = 0; elem < nelem; elem++)
3381*058561cbSjbeck 			sm_dprintf("argv[%d]=\"%s\"\n", elem, s[elem]);
3382*058561cbSjbeck 	}
3383*058561cbSjbeck 
3384*058561cbSjbeck 	/* overwrite last entry (already done above, just paranoia) */
3385*058561cbSjbeck 	s[elem] = NULL;
3386*058561cbSjbeck 	return s;
3387*058561cbSjbeck }
3388*058561cbSjbeck 
3389*058561cbSjbeck /*
3390*058561cbSjbeck **  MILTER_CHGFROM -- Change the envelope sender address
3391*058561cbSjbeck **
3392*058561cbSjbeck **	Parameters:
3393*058561cbSjbeck **		response -- encoded form of recipient address.
3394*058561cbSjbeck **		rlen -- length of response.
3395*058561cbSjbeck **		e -- current envelope.
3396*058561cbSjbeck **
3397*058561cbSjbeck **	Returns:
3398*058561cbSjbeck **		none
3399*058561cbSjbeck */
3400*058561cbSjbeck 
3401*058561cbSjbeck static void
3402*058561cbSjbeck milter_chgfrom(response, rlen, e)
3403*058561cbSjbeck 	char *response;
3404*058561cbSjbeck 	ssize_t rlen;
3405*058561cbSjbeck 	ENVELOPE *e;
3406*058561cbSjbeck {
3407*058561cbSjbeck 	int olderrors, argc;
3408*058561cbSjbeck 	char **argv;
3409*058561cbSjbeck 
3410*058561cbSjbeck 	if (tTd(64, 10))
3411*058561cbSjbeck 		sm_dprintf("milter_chgfrom: ");
3412*058561cbSjbeck 
3413*058561cbSjbeck 	/* sanity checks */
3414*058561cbSjbeck 	if (response == NULL)
3415*058561cbSjbeck 	{
3416*058561cbSjbeck 		if (tTd(64, 10))
3417*058561cbSjbeck 			sm_dprintf("NULL response\n");
3418*058561cbSjbeck 		return;
3419*058561cbSjbeck 	}
3420*058561cbSjbeck 
3421*058561cbSjbeck 	if (*response == '\0' ||
3422*058561cbSjbeck 	    strlen(response) + 1 > (size_t) rlen)
3423*058561cbSjbeck 	{
3424*058561cbSjbeck 		if (tTd(64, 10))
3425*058561cbSjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
3426*058561cbSjbeck 				   (int) strlen(response), (int) (rlen - 1));
3427*058561cbSjbeck 		return;
3428*058561cbSjbeck 	}
3429*058561cbSjbeck 
3430*058561cbSjbeck 	if (tTd(64, 10))
3431*058561cbSjbeck 		sm_dprintf("%s\n", response);
3432*058561cbSjbeck 	if (MilterLogLevel > 8)
3433*058561cbSjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter chgfrom: %s", response);
3434*058561cbSjbeck 	argv = milter_split_response(response, rlen, &argc);
3435*058561cbSjbeck 	if (argc < 1 || argc > 2)
3436*058561cbSjbeck 	{
3437*058561cbSjbeck 		if (tTd(64, 10))
3438*058561cbSjbeck 			sm_dprintf("didn't follow protocol argc=%d\n", argc);
3439*058561cbSjbeck 		return;
3440*058561cbSjbeck 	}
3441*058561cbSjbeck 
3442*058561cbSjbeck 	olderrors = Errors;
3443*058561cbSjbeck 	setsender(argv[0], e, NULL, '\0', false);
3444*058561cbSjbeck 	if (argc == 2)
3445*058561cbSjbeck 	{
3446*058561cbSjbeck 		reset_mail_esmtp_args(e);
3447*058561cbSjbeck 
3448*058561cbSjbeck 		/*
3449*058561cbSjbeck 		**  need "features" here: how to get those? via e?
3450*058561cbSjbeck 		**  "fake" it for now: allow everything.
3451*058561cbSjbeck 		*/
3452*058561cbSjbeck 
3453*058561cbSjbeck 		parse_esmtp_args(e, NULL, argv[0], argv[1], "MAIL", NULL,
3454*058561cbSjbeck 				mail_esmtp_args);
3455*058561cbSjbeck 	}
3456*058561cbSjbeck 	Errors = olderrors;
3457*058561cbSjbeck 	return;
3458*058561cbSjbeck }
3459*058561cbSjbeck 
3460*058561cbSjbeck /*
3461*058561cbSjbeck **  MILTER_ADDRCPT_PAR -- Add the supplied recipient to the message
3462*058561cbSjbeck **
3463*058561cbSjbeck **	Parameters:
3464*058561cbSjbeck **		response -- encoded form of recipient address.
3465*058561cbSjbeck **		rlen -- length of response.
3466*058561cbSjbeck **		e -- current envelope.
3467*058561cbSjbeck **
3468*058561cbSjbeck **	Returns:
3469*058561cbSjbeck **		none
3470*058561cbSjbeck */
3471*058561cbSjbeck 
3472*058561cbSjbeck static void
3473*058561cbSjbeck milter_addrcpt_par(response, rlen, e)
3474*058561cbSjbeck 	char *response;
3475*058561cbSjbeck 	ssize_t rlen;
3476*058561cbSjbeck 	ENVELOPE *e;
3477*058561cbSjbeck {
3478*058561cbSjbeck 	int olderrors, argc;
3479*058561cbSjbeck 	char *delimptr;
3480*058561cbSjbeck 	char **argv;
3481*058561cbSjbeck 	ADDRESS *a;
3482*058561cbSjbeck 
3483*058561cbSjbeck 	if (tTd(64, 10))
3484*058561cbSjbeck 		sm_dprintf("milter_addrcpt_par: ");
3485*058561cbSjbeck 
3486*058561cbSjbeck 	/* sanity checks */
3487*058561cbSjbeck 	if (response == NULL)
3488*058561cbSjbeck 	{
3489*058561cbSjbeck 		if (tTd(64, 10))
3490*058561cbSjbeck 			sm_dprintf("NULL response\n");
3491*058561cbSjbeck 		return;
3492*058561cbSjbeck 	}
3493*058561cbSjbeck 
3494*058561cbSjbeck 	if (tTd(64, 10))
3495*058561cbSjbeck 		sm_dprintf("%s\n", response);
3496*058561cbSjbeck 	if (MilterLogLevel > 8)
3497*058561cbSjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
3498*058561cbSjbeck 
3499*058561cbSjbeck 	argv = milter_split_response(response, rlen, &argc);
3500*058561cbSjbeck 	if (argc < 1 || argc > 2)
3501*058561cbSjbeck 	{
3502*058561cbSjbeck 		if (tTd(64, 10))
3503*058561cbSjbeck 			sm_dprintf("didn't follow protocol argc=%d\n", argc);
3504*058561cbSjbeck 		return;
3505*058561cbSjbeck 	}
3506*058561cbSjbeck 	olderrors = Errors;
3507*058561cbSjbeck 
3508*058561cbSjbeck 	/* how to set ESMTP arguments? */
3509*058561cbSjbeck 	a = parseaddr(argv[0], NULLADDR, RF_COPYALL, ' ', &delimptr, e, true);
3510*058561cbSjbeck 
3511*058561cbSjbeck 	if (a != NULL && olderrors == Errors)
3512*058561cbSjbeck 	{
3513*058561cbSjbeck 		parse_esmtp_args(e, a, argv[0], argv[1], "RCPT", NULL,
3514*058561cbSjbeck 				rcpt_esmtp_args);
3515*058561cbSjbeck 		if (olderrors == Errors)
3516*058561cbSjbeck 			a = recipient(a, &e->e_sendqueue, 0, e);
3517*058561cbSjbeck 		else
3518*058561cbSjbeck 			sm_dprintf("olderrors=%d, Errors=%d\n",
3519*058561cbSjbeck 				olderrors, Errors);
3520*058561cbSjbeck 	}
3521*058561cbSjbeck 	else
3522*058561cbSjbeck 	{
3523*058561cbSjbeck 		sm_dprintf("a=%p, olderrors=%d, Errors=%d\n",
3524*058561cbSjbeck 			a, olderrors, Errors);
3525*058561cbSjbeck 	}
3526*058561cbSjbeck 
3527*058561cbSjbeck 	Errors = olderrors;
3528*058561cbSjbeck 	return;
3529*058561cbSjbeck }
3530*058561cbSjbeck 
35317c478bd9Sstevel@tonic-gate /*
35327c478bd9Sstevel@tonic-gate **  MILTER_ADDRCPT -- Add the supplied recipient to the message
35337c478bd9Sstevel@tonic-gate **
35347c478bd9Sstevel@tonic-gate **	Parameters:
35357c478bd9Sstevel@tonic-gate **		response -- encoded form of recipient address.
35367c478bd9Sstevel@tonic-gate **		rlen -- length of response.
35377c478bd9Sstevel@tonic-gate **		e -- current envelope.
35387c478bd9Sstevel@tonic-gate **
35397c478bd9Sstevel@tonic-gate **	Returns:
35407c478bd9Sstevel@tonic-gate **		none
35417c478bd9Sstevel@tonic-gate */
35427c478bd9Sstevel@tonic-gate 
35437c478bd9Sstevel@tonic-gate static void
35447c478bd9Sstevel@tonic-gate milter_addrcpt(response, rlen, e)
35457c478bd9Sstevel@tonic-gate 	char *response;
35467c478bd9Sstevel@tonic-gate 	ssize_t rlen;
35477c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
35487c478bd9Sstevel@tonic-gate {
35497c478bd9Sstevel@tonic-gate 	int olderrors;
35507c478bd9Sstevel@tonic-gate 
35517c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
35527c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_addrcpt: ");
35537c478bd9Sstevel@tonic-gate 
35547c478bd9Sstevel@tonic-gate 	/* sanity checks */
35557c478bd9Sstevel@tonic-gate 	if (response == NULL)
35567c478bd9Sstevel@tonic-gate 	{
35577c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
35587c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
35597c478bd9Sstevel@tonic-gate 		return;
35607c478bd9Sstevel@tonic-gate 	}
35617c478bd9Sstevel@tonic-gate 
35627c478bd9Sstevel@tonic-gate 	if (*response == '\0' ||
35637c478bd9Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
35647c478bd9Sstevel@tonic-gate 	{
35657c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
35667c478bd9Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
35677c478bd9Sstevel@tonic-gate 				   (int) strlen(response), (int) (rlen - 1));
35687c478bd9Sstevel@tonic-gate 		return;
35697c478bd9Sstevel@tonic-gate 	}
35707c478bd9Sstevel@tonic-gate 
35717c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
35727c478bd9Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
35737c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 8)
35747c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
35757c478bd9Sstevel@tonic-gate 	olderrors = Errors;
35767c478bd9Sstevel@tonic-gate 	(void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
35777c478bd9Sstevel@tonic-gate 	Errors = olderrors;
35787c478bd9Sstevel@tonic-gate 	return;
35797c478bd9Sstevel@tonic-gate }
3580*058561cbSjbeck 
35817c478bd9Sstevel@tonic-gate /*
35827c478bd9Sstevel@tonic-gate **  MILTER_DELRCPT -- Delete the supplied recipient from the message
35837c478bd9Sstevel@tonic-gate **
35847c478bd9Sstevel@tonic-gate **	Parameters:
35857c478bd9Sstevel@tonic-gate **		response -- encoded form of recipient address.
35867c478bd9Sstevel@tonic-gate **		rlen -- length of response.
35877c478bd9Sstevel@tonic-gate **		e -- current envelope.
35887c478bd9Sstevel@tonic-gate **
35897c478bd9Sstevel@tonic-gate **	Returns:
35907c478bd9Sstevel@tonic-gate **		none
35917c478bd9Sstevel@tonic-gate */
35927c478bd9Sstevel@tonic-gate 
35937c478bd9Sstevel@tonic-gate static void
35947c478bd9Sstevel@tonic-gate milter_delrcpt(response, rlen, e)
35957c478bd9Sstevel@tonic-gate 	char *response;
35967c478bd9Sstevel@tonic-gate 	ssize_t rlen;
35977c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
35987c478bd9Sstevel@tonic-gate {
35997c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
36007c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_delrcpt: ");
36017c478bd9Sstevel@tonic-gate 
36027c478bd9Sstevel@tonic-gate 	/* sanity checks */
36037c478bd9Sstevel@tonic-gate 	if (response == NULL)
36047c478bd9Sstevel@tonic-gate 	{
36057c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
36067c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
36077c478bd9Sstevel@tonic-gate 		return;
36087c478bd9Sstevel@tonic-gate 	}
36097c478bd9Sstevel@tonic-gate 
36107c478bd9Sstevel@tonic-gate 	if (*response == '\0' ||
36117c478bd9Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
36127c478bd9Sstevel@tonic-gate 	{
36137c478bd9Sstevel@tonic-gate 		if (tTd(64, 10))
3614*058561cbSjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
3615*058561cbSjbeck 				   (int) strlen(response), (int) (rlen - 1));
36167c478bd9Sstevel@tonic-gate 		return;
36177c478bd9Sstevel@tonic-gate 	}
36187c478bd9Sstevel@tonic-gate 
36197c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
36207c478bd9Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
36217c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 8)
36227c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s",
36237c478bd9Sstevel@tonic-gate 			  response);
36247c478bd9Sstevel@tonic-gate 	(void) removefromlist(response, &e->e_sendqueue, e);
36257c478bd9Sstevel@tonic-gate 	return;
36267c478bd9Sstevel@tonic-gate }
3627*058561cbSjbeck 
36287c478bd9Sstevel@tonic-gate /*
36297c478bd9Sstevel@tonic-gate **  MILTER_REPLBODY -- Replace the current data file with new body
36307c478bd9Sstevel@tonic-gate **
36317c478bd9Sstevel@tonic-gate **	Parameters:
36327c478bd9Sstevel@tonic-gate **		response -- encoded form of new body.
36337c478bd9Sstevel@tonic-gate **		rlen -- length of response.
36347c478bd9Sstevel@tonic-gate **		newfilter -- if first time called by a new filter
36357c478bd9Sstevel@tonic-gate **		e -- current envelope.
36367c478bd9Sstevel@tonic-gate **
36377c478bd9Sstevel@tonic-gate **	Returns:
36387c478bd9Sstevel@tonic-gate **		0 upon success, -1 upon failure
36397c478bd9Sstevel@tonic-gate */
36407c478bd9Sstevel@tonic-gate 
36417c478bd9Sstevel@tonic-gate static int
36427c478bd9Sstevel@tonic-gate milter_replbody(response, rlen, newfilter, e)
36437c478bd9Sstevel@tonic-gate 	char *response;
36447c478bd9Sstevel@tonic-gate 	ssize_t rlen;
36457c478bd9Sstevel@tonic-gate 	bool newfilter;
36467c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
36477c478bd9Sstevel@tonic-gate {
36487c478bd9Sstevel@tonic-gate 	static char prevchar;
36497c478bd9Sstevel@tonic-gate 	int i;
36507c478bd9Sstevel@tonic-gate 
36517c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
36527c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_replbody\n");
36537c478bd9Sstevel@tonic-gate 
36547c478bd9Sstevel@tonic-gate 	/* If a new filter, reset previous character and truncate data file */
36557c478bd9Sstevel@tonic-gate 	if (newfilter)
36567c478bd9Sstevel@tonic-gate 	{
36577c478bd9Sstevel@tonic-gate 		off_t prevsize;
36587c478bd9Sstevel@tonic-gate 		char dfname[MAXPATHLEN];
36597c478bd9Sstevel@tonic-gate 
36607c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER),
3661*058561cbSjbeck 				  sizeof(dfname));
36627c478bd9Sstevel@tonic-gate 
36637c478bd9Sstevel@tonic-gate 		/* Reset prevchar */
36647c478bd9Sstevel@tonic-gate 		prevchar = '\0';
36657c478bd9Sstevel@tonic-gate 
36667c478bd9Sstevel@tonic-gate 		/* Get the current data file information */
36677c478bd9Sstevel@tonic-gate 		prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL);
36687c478bd9Sstevel@tonic-gate 		if (prevsize < 0)
36697c478bd9Sstevel@tonic-gate 			prevsize = 0;
36707c478bd9Sstevel@tonic-gate 
36717c478bd9Sstevel@tonic-gate 		/* truncate current data file */
36727c478bd9Sstevel@tonic-gate 		if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
36737c478bd9Sstevel@tonic-gate 		{
36747c478bd9Sstevel@tonic-gate 			if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0)
36757c478bd9Sstevel@tonic-gate 			{
36767c478bd9Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s");
36777c478bd9Sstevel@tonic-gate 				return -1;
36787c478bd9Sstevel@tonic-gate 			}
36797c478bd9Sstevel@tonic-gate 		}
36807c478bd9Sstevel@tonic-gate 		else
36817c478bd9Sstevel@tonic-gate 		{
36827c478bd9Sstevel@tonic-gate 			int err;
36837c478bd9Sstevel@tonic-gate 
36847c478bd9Sstevel@tonic-gate 			err = sm_io_error(e->e_dfp);
36857c478bd9Sstevel@tonic-gate 			(void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT);
36867c478bd9Sstevel@tonic-gate 
36877c478bd9Sstevel@tonic-gate 			/*
36887c478bd9Sstevel@tonic-gate 			**  Clear error if tried to fflush()
36897c478bd9Sstevel@tonic-gate 			**  a read-only file pointer and
36907c478bd9Sstevel@tonic-gate 			**  there wasn't a previous error.
36917c478bd9Sstevel@tonic-gate 			*/
36927c478bd9Sstevel@tonic-gate 
36937c478bd9Sstevel@tonic-gate 			if (err == 0)
36947c478bd9Sstevel@tonic-gate 				sm_io_clearerr(e->e_dfp);
36957c478bd9Sstevel@tonic-gate 
36967c478bd9Sstevel@tonic-gate 			/* errno is set implicitly by fseek() before return */
36977c478bd9Sstevel@tonic-gate 			err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT,
36987c478bd9Sstevel@tonic-gate 					 0, SEEK_SET);
36997c478bd9Sstevel@tonic-gate 			if (err < 0)
37007c478bd9Sstevel@tonic-gate 			{
37017c478bd9Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s");
37027c478bd9Sstevel@tonic-gate 				return -1;
37037c478bd9Sstevel@tonic-gate 			}
37047c478bd9Sstevel@tonic-gate # if NOFTRUNCATE
37057c478bd9Sstevel@tonic-gate 			/* XXX: Not much we can do except rewind it */
37067c478bd9Sstevel@tonic-gate 			errno = EINVAL;
37077c478bd9Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)");
37087c478bd9Sstevel@tonic-gate 			return -1;
37097c478bd9Sstevel@tonic-gate # else /* NOFTRUNCATE */
37107c478bd9Sstevel@tonic-gate 			err = ftruncate(sm_io_getinfo(e->e_dfp,
37117c478bd9Sstevel@tonic-gate 						      SM_IO_WHAT_FD, NULL),
37127c478bd9Sstevel@tonic-gate 					0);
37137c478bd9Sstevel@tonic-gate 			if (err < 0)
37147c478bd9Sstevel@tonic-gate 			{
37157c478bd9Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s");
37167c478bd9Sstevel@tonic-gate 				return -1;
37177c478bd9Sstevel@tonic-gate 			}
37187c478bd9Sstevel@tonic-gate # endif /* NOFTRUNCATE */
37197c478bd9Sstevel@tonic-gate 		}
37207c478bd9Sstevel@tonic-gate 
37217c478bd9Sstevel@tonic-gate 		if (prevsize > e->e_msgsize)
37227c478bd9Sstevel@tonic-gate 			e->e_msgsize = 0;
37237c478bd9Sstevel@tonic-gate 		else
37247c478bd9Sstevel@tonic-gate 			e->e_msgsize -= prevsize;
37257c478bd9Sstevel@tonic-gate 	}
37267c478bd9Sstevel@tonic-gate 
37277c478bd9Sstevel@tonic-gate 	if (newfilter && MilterLogLevel > 8)
37287c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced");
37297c478bd9Sstevel@tonic-gate 
37307c478bd9Sstevel@tonic-gate 	if (response == NULL)
37317c478bd9Sstevel@tonic-gate 	{
37327c478bd9Sstevel@tonic-gate 		/* Flush the buffered '\r' */
37337c478bd9Sstevel@tonic-gate 		if (prevchar == '\r')
37347c478bd9Sstevel@tonic-gate 		{
37357c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar);
37367c478bd9Sstevel@tonic-gate 			e->e_msgsize++;
37377c478bd9Sstevel@tonic-gate 		}
37387c478bd9Sstevel@tonic-gate 		return 0;
37397c478bd9Sstevel@tonic-gate 	}
37407c478bd9Sstevel@tonic-gate 
37417c478bd9Sstevel@tonic-gate 	for (i = 0; i < rlen; i++)
37427c478bd9Sstevel@tonic-gate 	{
37437c478bd9Sstevel@tonic-gate 		/* Buffered char from last chunk */
37447c478bd9Sstevel@tonic-gate 		if (i == 0 && prevchar == '\r')
37457c478bd9Sstevel@tonic-gate 		{
37467c478bd9Sstevel@tonic-gate 			/* Not CRLF, output prevchar */
37477c478bd9Sstevel@tonic-gate 			if (response[i] != '\n')
37487c478bd9Sstevel@tonic-gate 			{
37497c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT,
37507c478bd9Sstevel@tonic-gate 						  prevchar);
37517c478bd9Sstevel@tonic-gate 				e->e_msgsize++;
37527c478bd9Sstevel@tonic-gate 			}
37537c478bd9Sstevel@tonic-gate 			prevchar = '\0';
37547c478bd9Sstevel@tonic-gate 		}
37557c478bd9Sstevel@tonic-gate 
37567c478bd9Sstevel@tonic-gate 		/* Turn CRLF into LF */
37577c478bd9Sstevel@tonic-gate 		if (response[i] == '\r')
37587c478bd9Sstevel@tonic-gate 		{
37597c478bd9Sstevel@tonic-gate 			/* check if at end of chunk */
37607c478bd9Sstevel@tonic-gate 			if (i + 1 < rlen)
37617c478bd9Sstevel@tonic-gate 			{
37627c478bd9Sstevel@tonic-gate 				/* If LF, strip CR */
37637c478bd9Sstevel@tonic-gate 				if (response[i + 1] == '\n')
37647c478bd9Sstevel@tonic-gate 					i++;
37657c478bd9Sstevel@tonic-gate 			}
37667c478bd9Sstevel@tonic-gate 			else
37677c478bd9Sstevel@tonic-gate 			{
37687c478bd9Sstevel@tonic-gate 				/* check next chunk */
37697c478bd9Sstevel@tonic-gate 				prevchar = '\r';
37707c478bd9Sstevel@tonic-gate 				continue;
37717c478bd9Sstevel@tonic-gate 			}
37727c478bd9Sstevel@tonic-gate 		}
37737c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]);
37747c478bd9Sstevel@tonic-gate 		e->e_msgsize++;
37757c478bd9Sstevel@tonic-gate 	}
37767c478bd9Sstevel@tonic-gate 	return 0;
37777c478bd9Sstevel@tonic-gate }
37787c478bd9Sstevel@tonic-gate 
37797c478bd9Sstevel@tonic-gate /*
37807c478bd9Sstevel@tonic-gate **  MTA callouts
37817c478bd9Sstevel@tonic-gate */
37827c478bd9Sstevel@tonic-gate 
37837c478bd9Sstevel@tonic-gate /*
37847c478bd9Sstevel@tonic-gate **  MILTER_INIT -- open and negotiate with all of the filters
37857c478bd9Sstevel@tonic-gate **
37867c478bd9Sstevel@tonic-gate **	Parameters:
37877c478bd9Sstevel@tonic-gate **		e -- current envelope.
37887c478bd9Sstevel@tonic-gate **		state -- return state from response.
37897c478bd9Sstevel@tonic-gate **
37907c478bd9Sstevel@tonic-gate **	Returns:
37917c478bd9Sstevel@tonic-gate **		true iff at least one filter is active
37927c478bd9Sstevel@tonic-gate */
37937c478bd9Sstevel@tonic-gate 
37947c478bd9Sstevel@tonic-gate /* ARGSUSED */
37957c478bd9Sstevel@tonic-gate bool
37967c478bd9Sstevel@tonic-gate milter_init(e, state)
37977c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
37987c478bd9Sstevel@tonic-gate 	char *state;
37997c478bd9Sstevel@tonic-gate {
38007c478bd9Sstevel@tonic-gate 	int i;
38017c478bd9Sstevel@tonic-gate 
38027c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
38037c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_init\n");
38047c478bd9Sstevel@tonic-gate 
38057c478bd9Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
38067c478bd9Sstevel@tonic-gate 	if (InputFilters[0] == NULL)
38077c478bd9Sstevel@tonic-gate 	{
38087c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
38097c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
38107c478bd9Sstevel@tonic-gate 				  "Milter: no active filter");
38117c478bd9Sstevel@tonic-gate 		return false;
38127c478bd9Sstevel@tonic-gate 	}
38137c478bd9Sstevel@tonic-gate 
38147c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
38157c478bd9Sstevel@tonic-gate 	{
38167c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
38177c478bd9Sstevel@tonic-gate 
38187c478bd9Sstevel@tonic-gate 		m->mf_sock = milter_open(m, false, e);
38197c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
38207c478bd9Sstevel@tonic-gate 		{
38217c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
38227c478bd9Sstevel@tonic-gate 			break;
38237c478bd9Sstevel@tonic-gate 		}
38247c478bd9Sstevel@tonic-gate 
38257c478bd9Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
38267c478bd9Sstevel@tonic-gate 		    milter_negotiate(m, e) < 0 ||
38277c478bd9Sstevel@tonic-gate 		    m->mf_state == SMFS_ERROR)
38287c478bd9Sstevel@tonic-gate 		{
38297c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
38307c478bd9Sstevel@tonic-gate 				sm_dprintf("milter_init(%s): failed to %s\n",
38317c478bd9Sstevel@tonic-gate 					   m->mf_name,
38327c478bd9Sstevel@tonic-gate 					   m->mf_sock < 0 ? "open" :
38337c478bd9Sstevel@tonic-gate 							    "negotiate");
38347c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
38357c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
38367c478bd9Sstevel@tonic-gate 					  "Milter (%s): init failed to %s",
38377c478bd9Sstevel@tonic-gate 					  m->mf_name,
38387c478bd9Sstevel@tonic-gate 					  m->mf_sock < 0 ? "open" :
38397c478bd9Sstevel@tonic-gate 							   "negotiate");
38407c478bd9Sstevel@tonic-gate 
38417c478bd9Sstevel@tonic-gate 			/* if negotation failure, close socket */
38427c478bd9Sstevel@tonic-gate 			milter_error(m, e);
38437c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
38447c478bd9Sstevel@tonic-gate 			continue;
38457c478bd9Sstevel@tonic-gate 		}
38467c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 9)
38477c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
38487c478bd9Sstevel@tonic-gate 				  "Milter (%s): init success to %s",
38497c478bd9Sstevel@tonic-gate 				  m->mf_name,
38507c478bd9Sstevel@tonic-gate 				  m->mf_sock < 0 ? "open" : "negotiate");
38517c478bd9Sstevel@tonic-gate 	}
38527c478bd9Sstevel@tonic-gate 
38537c478bd9Sstevel@tonic-gate 	/*
38547c478bd9Sstevel@tonic-gate 	**  If something temp/perm failed with one of the filters,
38557c478bd9Sstevel@tonic-gate 	**  we won't be using any of them, so clear any existing
38567c478bd9Sstevel@tonic-gate 	**  connections.
38577c478bd9Sstevel@tonic-gate 	*/
38587c478bd9Sstevel@tonic-gate 
38597c478bd9Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
38607c478bd9Sstevel@tonic-gate 		milter_quit(e);
38617c478bd9Sstevel@tonic-gate 
38627c478bd9Sstevel@tonic-gate 	return true;
38637c478bd9Sstevel@tonic-gate }
3864*058561cbSjbeck 
38657c478bd9Sstevel@tonic-gate /*
38667c478bd9Sstevel@tonic-gate **  MILTER_CONNECT -- send connection info to milter filters
38677c478bd9Sstevel@tonic-gate **
38687c478bd9Sstevel@tonic-gate **	Parameters:
38697c478bd9Sstevel@tonic-gate **		hostname -- hostname of remote machine.
38707c478bd9Sstevel@tonic-gate **		addr -- address of remote machine.
38717c478bd9Sstevel@tonic-gate **		e -- current envelope.
38727c478bd9Sstevel@tonic-gate **		state -- return state from response.
38737c478bd9Sstevel@tonic-gate **
38747c478bd9Sstevel@tonic-gate **	Returns:
38757c478bd9Sstevel@tonic-gate **		response string (may be NULL)
38767c478bd9Sstevel@tonic-gate */
38777c478bd9Sstevel@tonic-gate 
38787c478bd9Sstevel@tonic-gate char *
38797c478bd9Sstevel@tonic-gate milter_connect(hostname, addr, e, state)
38807c478bd9Sstevel@tonic-gate 	char *hostname;
38817c478bd9Sstevel@tonic-gate 	SOCKADDR addr;
38827c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
38837c478bd9Sstevel@tonic-gate 	char *state;
38847c478bd9Sstevel@tonic-gate {
38857c478bd9Sstevel@tonic-gate 	char family;
38867c478bd9Sstevel@tonic-gate 	unsigned short port;
38877c478bd9Sstevel@tonic-gate 	char *buf, *bp;
38887c478bd9Sstevel@tonic-gate 	char *response;
38897c478bd9Sstevel@tonic-gate 	char *sockinfo = NULL;
38907c478bd9Sstevel@tonic-gate 	ssize_t s;
38917c478bd9Sstevel@tonic-gate # if NETINET6
38927c478bd9Sstevel@tonic-gate 	char buf6[INET6_ADDRSTRLEN];
38937c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
38947c478bd9Sstevel@tonic-gate 
38957c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
38967c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_connect(%s)\n", hostname);
38977c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 9)
38987c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters");
38997c478bd9Sstevel@tonic-gate 
39007c478bd9Sstevel@tonic-gate 	/* gather data */
39017c478bd9Sstevel@tonic-gate 	switch (addr.sa.sa_family)
39027c478bd9Sstevel@tonic-gate 	{
39037c478bd9Sstevel@tonic-gate # if NETUNIX
39047c478bd9Sstevel@tonic-gate 	  case AF_UNIX:
39057c478bd9Sstevel@tonic-gate 		family = SMFIA_UNIX;
39067c478bd9Sstevel@tonic-gate 		port = htons(0);
39077c478bd9Sstevel@tonic-gate 		sockinfo = addr.sunix.sun_path;
39087c478bd9Sstevel@tonic-gate 		break;
39097c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
39107c478bd9Sstevel@tonic-gate 
39117c478bd9Sstevel@tonic-gate # if NETINET
39127c478bd9Sstevel@tonic-gate 	  case AF_INET:
39137c478bd9Sstevel@tonic-gate 		family = SMFIA_INET;
39147c478bd9Sstevel@tonic-gate 		port = addr.sin.sin_port;
39157c478bd9Sstevel@tonic-gate 		sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
39167c478bd9Sstevel@tonic-gate 		break;
39177c478bd9Sstevel@tonic-gate # endif /* NETINET */
39187c478bd9Sstevel@tonic-gate 
39197c478bd9Sstevel@tonic-gate # if NETINET6
39207c478bd9Sstevel@tonic-gate 	  case AF_INET6:
39217c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr))
39227c478bd9Sstevel@tonic-gate 			family = SMFIA_INET;
39237c478bd9Sstevel@tonic-gate 		else
39247c478bd9Sstevel@tonic-gate 			family = SMFIA_INET6;
39257c478bd9Sstevel@tonic-gate 		port = addr.sin6.sin6_port;
39267c478bd9Sstevel@tonic-gate 		sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
3927*058561cbSjbeck 				       sizeof(buf6));
39287c478bd9Sstevel@tonic-gate 		if (sockinfo == NULL)
39297c478bd9Sstevel@tonic-gate 			sockinfo = "";
39307c478bd9Sstevel@tonic-gate 		break;
39317c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
39327c478bd9Sstevel@tonic-gate 
39337c478bd9Sstevel@tonic-gate 	  default:
39347c478bd9Sstevel@tonic-gate 		family = SMFIA_UNKNOWN;
39357c478bd9Sstevel@tonic-gate 		break;
39367c478bd9Sstevel@tonic-gate 	}
39377c478bd9Sstevel@tonic-gate 
39387c478bd9Sstevel@tonic-gate 	s = strlen(hostname) + 1 + sizeof(family);
39397c478bd9Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
39407c478bd9Sstevel@tonic-gate 		s += sizeof(port) + strlen(sockinfo) + 1;
39417c478bd9Sstevel@tonic-gate 
39427c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(s);
39437c478bd9Sstevel@tonic-gate 	bp = buf;
39447c478bd9Sstevel@tonic-gate 
39457c478bd9Sstevel@tonic-gate 	/* put together data */
39467c478bd9Sstevel@tonic-gate 	(void) memcpy(bp, hostname, strlen(hostname));
39477c478bd9Sstevel@tonic-gate 	bp += strlen(hostname);
39487c478bd9Sstevel@tonic-gate 	*bp++ = '\0';
3949*058561cbSjbeck 	(void) memcpy(bp, &family, sizeof(family));
3950*058561cbSjbeck 	bp += sizeof(family);
39517c478bd9Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
39527c478bd9Sstevel@tonic-gate 	{
3953*058561cbSjbeck 		(void) memcpy(bp, &port, sizeof(port));
3954*058561cbSjbeck 		bp += sizeof(port);
39557c478bd9Sstevel@tonic-gate 
39567c478bd9Sstevel@tonic-gate 		/* include trailing '\0' */
39577c478bd9Sstevel@tonic-gate 		(void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
39587c478bd9Sstevel@tonic-gate 	}
39597c478bd9Sstevel@tonic-gate 
3960*058561cbSjbeck 	response = milter_command(SMFIC_CONNECT, buf, s, MilterConnectMacros,
3961*058561cbSjbeck 				e, state, "connect", false);
39627c478bd9Sstevel@tonic-gate 	sm_free(buf); /* XXX */
39637c478bd9Sstevel@tonic-gate 
39647c478bd9Sstevel@tonic-gate 	/*
39657c478bd9Sstevel@tonic-gate 	**  If this message connection is done for,
39667c478bd9Sstevel@tonic-gate 	**  close the filters.
39677c478bd9Sstevel@tonic-gate 	*/
39687c478bd9Sstevel@tonic-gate 
39697c478bd9Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
39707c478bd9Sstevel@tonic-gate 	{
39717c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 9)
39727c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending");
39737c478bd9Sstevel@tonic-gate 		milter_quit(e);
39747c478bd9Sstevel@tonic-gate 	}
39757c478bd9Sstevel@tonic-gate 	else
39767c478bd9Sstevel@tonic-gate 		milter_per_connection_check(e);
39777c478bd9Sstevel@tonic-gate 
39787c478bd9Sstevel@tonic-gate 	/*
39797c478bd9Sstevel@tonic-gate 	**  SMFIR_REPLYCODE can't work with connect due to
39807c478bd9Sstevel@tonic-gate 	**  the requirements of SMTP.  Therefore, ignore the
39817c478bd9Sstevel@tonic-gate 	**  reply code text but keep the state it would reflect.
39827c478bd9Sstevel@tonic-gate 	*/
39837c478bd9Sstevel@tonic-gate 
39847c478bd9Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE)
39857c478bd9Sstevel@tonic-gate 	{
39867c478bd9Sstevel@tonic-gate 		if (response != NULL &&
39877c478bd9Sstevel@tonic-gate 		    *response == '4')
39887c478bd9Sstevel@tonic-gate 		{
39897c478bd9Sstevel@tonic-gate 			if (strncmp(response, "421 ", 4) == 0)
39907c478bd9Sstevel@tonic-gate 				*state = SMFIR_SHUTDOWN;
39917c478bd9Sstevel@tonic-gate 			else
39927c478bd9Sstevel@tonic-gate 				*state = SMFIR_TEMPFAIL;
39937c478bd9Sstevel@tonic-gate 		}
39947c478bd9Sstevel@tonic-gate 		else
39957c478bd9Sstevel@tonic-gate 			*state = SMFIR_REJECT;
39967c478bd9Sstevel@tonic-gate 		if (response != NULL)
39977c478bd9Sstevel@tonic-gate 		{
39987c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
39997c478bd9Sstevel@tonic-gate 			response = NULL;
40007c478bd9Sstevel@tonic-gate 		}
40017c478bd9Sstevel@tonic-gate 	}
40027c478bd9Sstevel@tonic-gate 	return response;
40037c478bd9Sstevel@tonic-gate }
4004*058561cbSjbeck 
40057c478bd9Sstevel@tonic-gate /*
40067c478bd9Sstevel@tonic-gate **  MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
40077c478bd9Sstevel@tonic-gate **
40087c478bd9Sstevel@tonic-gate **	Parameters:
40097c478bd9Sstevel@tonic-gate **		helo -- argument to SMTP HELO/EHLO command.
40107c478bd9Sstevel@tonic-gate **		e -- current envelope.
40117c478bd9Sstevel@tonic-gate **		state -- return state from response.
40127c478bd9Sstevel@tonic-gate **
40137c478bd9Sstevel@tonic-gate **	Returns:
40147c478bd9Sstevel@tonic-gate **		response string (may be NULL)
40157c478bd9Sstevel@tonic-gate */
40167c478bd9Sstevel@tonic-gate 
40177c478bd9Sstevel@tonic-gate char *
40187c478bd9Sstevel@tonic-gate milter_helo(helo, e, state)
40197c478bd9Sstevel@tonic-gate 	char *helo;
40207c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
40217c478bd9Sstevel@tonic-gate 	char *state;
40227c478bd9Sstevel@tonic-gate {
40237c478bd9Sstevel@tonic-gate 	int i;
40247c478bd9Sstevel@tonic-gate 	char *response;
40257c478bd9Sstevel@tonic-gate 
40267c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
40277c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_helo(%s)\n", helo);
40287c478bd9Sstevel@tonic-gate 
40297c478bd9Sstevel@tonic-gate 	/* HELO/EHLO can come at any point */
40307c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
40317c478bd9Sstevel@tonic-gate 	{
40327c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
40337c478bd9Sstevel@tonic-gate 
40347c478bd9Sstevel@tonic-gate 		switch (m->mf_state)
40357c478bd9Sstevel@tonic-gate 		{
40367c478bd9Sstevel@tonic-gate 		  case SMFS_INMSG:
40377c478bd9Sstevel@tonic-gate 			/* abort in message filters */
40387c478bd9Sstevel@tonic-gate 			milter_abort_filter(m, e);
40397c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
40407c478bd9Sstevel@tonic-gate 
40417c478bd9Sstevel@tonic-gate 		  case SMFS_DONE:
40427c478bd9Sstevel@tonic-gate 			/* reset done filters */
40437c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
40447c478bd9Sstevel@tonic-gate 			break;
40457c478bd9Sstevel@tonic-gate 		}
40467c478bd9Sstevel@tonic-gate 	}
40477c478bd9Sstevel@tonic-gate 
40487c478bd9Sstevel@tonic-gate 	response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
4049*058561cbSjbeck 				  MilterHeloMacros, e, state, "helo", false);
40507c478bd9Sstevel@tonic-gate 	milter_per_connection_check(e);
40517c478bd9Sstevel@tonic-gate 	return response;
40527c478bd9Sstevel@tonic-gate }
4053*058561cbSjbeck 
40547c478bd9Sstevel@tonic-gate /*
40557c478bd9Sstevel@tonic-gate **  MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
40567c478bd9Sstevel@tonic-gate **
40577c478bd9Sstevel@tonic-gate **	Parameters:
40587c478bd9Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == sender).
40597c478bd9Sstevel@tonic-gate **		e -- current envelope.
40607c478bd9Sstevel@tonic-gate **		state -- return state from response.
40617c478bd9Sstevel@tonic-gate **
40627c478bd9Sstevel@tonic-gate **	Returns:
40637c478bd9Sstevel@tonic-gate **		response string (may be NULL)
40647c478bd9Sstevel@tonic-gate */
40657c478bd9Sstevel@tonic-gate 
40667c478bd9Sstevel@tonic-gate char *
40677c478bd9Sstevel@tonic-gate milter_envfrom(args, e, state)
40687c478bd9Sstevel@tonic-gate 	char **args;
40697c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
40707c478bd9Sstevel@tonic-gate 	char *state;
40717c478bd9Sstevel@tonic-gate {
40727c478bd9Sstevel@tonic-gate 	int i;
40737c478bd9Sstevel@tonic-gate 	char *buf, *bp;
40747c478bd9Sstevel@tonic-gate 	char *response;
40757c478bd9Sstevel@tonic-gate 	ssize_t s;
40767c478bd9Sstevel@tonic-gate 
40777c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
40787c478bd9Sstevel@tonic-gate 	{
40797c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_envfrom:");
40807c478bd9Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
40817c478bd9Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
40827c478bd9Sstevel@tonic-gate 		sm_dprintf("\n");
40837c478bd9Sstevel@tonic-gate 	}
40847c478bd9Sstevel@tonic-gate 
40857c478bd9Sstevel@tonic-gate 	/* sanity check */
40867c478bd9Sstevel@tonic-gate 	if (args[0] == NULL)
40877c478bd9Sstevel@tonic-gate 	{
40887c478bd9Sstevel@tonic-gate 		*state = SMFIR_REJECT;
40897c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
40907c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
40917c478bd9Sstevel@tonic-gate 				  "Milter: reject, no sender");
40927c478bd9Sstevel@tonic-gate 		return NULL;
40937c478bd9Sstevel@tonic-gate 	}
40947c478bd9Sstevel@tonic-gate 
40957c478bd9Sstevel@tonic-gate 	/* new message, so ... */
40967c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
40977c478bd9Sstevel@tonic-gate 	{
40987c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
40997c478bd9Sstevel@tonic-gate 
41007c478bd9Sstevel@tonic-gate 		switch (m->mf_state)
41017c478bd9Sstevel@tonic-gate 		{
41027c478bd9Sstevel@tonic-gate 		  case SMFS_INMSG:
41037c478bd9Sstevel@tonic-gate 			/* abort in message filters */
41047c478bd9Sstevel@tonic-gate 			milter_abort_filter(m, e);
41057c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
41067c478bd9Sstevel@tonic-gate 
41077c478bd9Sstevel@tonic-gate 		  case SMFS_DONE:
41087c478bd9Sstevel@tonic-gate 			/* reset done filters */
41097c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
41107c478bd9Sstevel@tonic-gate 			break;
41117c478bd9Sstevel@tonic-gate 		}
41127c478bd9Sstevel@tonic-gate 	}
41137c478bd9Sstevel@tonic-gate 
41147c478bd9Sstevel@tonic-gate 	/* put together data */
41157c478bd9Sstevel@tonic-gate 	s = 0;
41167c478bd9Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41177c478bd9Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
41187c478bd9Sstevel@tonic-gate 
41197c478bd9Sstevel@tonic-gate 	if (s < 0)
41207c478bd9Sstevel@tonic-gate 	{
41217c478bd9Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
41227c478bd9Sstevel@tonic-gate 		return NULL;
41237c478bd9Sstevel@tonic-gate 	}
41247c478bd9Sstevel@tonic-gate 
41257c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(s);
41267c478bd9Sstevel@tonic-gate 	bp = buf;
41277c478bd9Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41287c478bd9Sstevel@tonic-gate 	{
41297c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
41307c478bd9Sstevel@tonic-gate 		bp += strlen(bp) + 1;
41317c478bd9Sstevel@tonic-gate 	}
41327c478bd9Sstevel@tonic-gate 
41337c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 14)
4134*058561cbSjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter: sender: %s", buf);
41357c478bd9Sstevel@tonic-gate 
41367c478bd9Sstevel@tonic-gate 	/* send it over */
4137*058561cbSjbeck 	response = milter_command(SMFIC_MAIL, buf, s, MilterEnvFromMacros,
4138*058561cbSjbeck 				e, state, "mail", false);
41397c478bd9Sstevel@tonic-gate 	sm_free(buf); /* XXX */
41407c478bd9Sstevel@tonic-gate 
41417c478bd9Sstevel@tonic-gate 	/*
41427c478bd9Sstevel@tonic-gate 	**  If filter rejects/discards a per message command,
41437c478bd9Sstevel@tonic-gate 	**  abort the other filters since we are done with the
41447c478bd9Sstevel@tonic-gate 	**  current message.
41457c478bd9Sstevel@tonic-gate 	*/
41467c478bd9Sstevel@tonic-gate 
41477c478bd9Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
41487c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
4149*058561cbSjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, sender");
41507c478bd9Sstevel@tonic-gate 	return response;
41517c478bd9Sstevel@tonic-gate }
41527c478bd9Sstevel@tonic-gate 
41537c478bd9Sstevel@tonic-gate /*
41547c478bd9Sstevel@tonic-gate **  MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
41557c478bd9Sstevel@tonic-gate **
41567c478bd9Sstevel@tonic-gate **	Parameters:
41577c478bd9Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == recipient).
41587c478bd9Sstevel@tonic-gate **		e -- current envelope.
41597c478bd9Sstevel@tonic-gate **		state -- return state from response.
4160*058561cbSjbeck **		rcpt_error -- does RCPT have an error?
41617c478bd9Sstevel@tonic-gate **
41627c478bd9Sstevel@tonic-gate **	Returns:
41637c478bd9Sstevel@tonic-gate **		response string (may be NULL)
41647c478bd9Sstevel@tonic-gate */
41657c478bd9Sstevel@tonic-gate 
41667c478bd9Sstevel@tonic-gate char *
4167*058561cbSjbeck milter_envrcpt(args, e, state, rcpt_error)
41687c478bd9Sstevel@tonic-gate 	char **args;
41697c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
41707c478bd9Sstevel@tonic-gate 	char *state;
4171*058561cbSjbeck 	bool rcpt_error;
41727c478bd9Sstevel@tonic-gate {
41737c478bd9Sstevel@tonic-gate 	int i;
41747c478bd9Sstevel@tonic-gate 	char *buf, *bp;
41757c478bd9Sstevel@tonic-gate 	char *response;
41767c478bd9Sstevel@tonic-gate 	ssize_t s;
41777c478bd9Sstevel@tonic-gate 
41787c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
41797c478bd9Sstevel@tonic-gate 	{
41807c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_envrcpt:");
41817c478bd9Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
41827c478bd9Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
41837c478bd9Sstevel@tonic-gate 		sm_dprintf("\n");
41847c478bd9Sstevel@tonic-gate 	}
41857c478bd9Sstevel@tonic-gate 
41867c478bd9Sstevel@tonic-gate 	/* sanity check */
41877c478bd9Sstevel@tonic-gate 	if (args[0] == NULL)
41887c478bd9Sstevel@tonic-gate 	{
41897c478bd9Sstevel@tonic-gate 		*state = SMFIR_REJECT;
41907c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 10)
41917c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt");
41927c478bd9Sstevel@tonic-gate 		return NULL;
41937c478bd9Sstevel@tonic-gate 	}
41947c478bd9Sstevel@tonic-gate 
41957c478bd9Sstevel@tonic-gate 	/* put together data */
41967c478bd9Sstevel@tonic-gate 	s = 0;
41977c478bd9Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41987c478bd9Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
41997c478bd9Sstevel@tonic-gate 
42007c478bd9Sstevel@tonic-gate 	if (s < 0)
42017c478bd9Sstevel@tonic-gate 	{
42027c478bd9Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
42037c478bd9Sstevel@tonic-gate 		return NULL;
42047c478bd9Sstevel@tonic-gate 	}
42057c478bd9Sstevel@tonic-gate 
42067c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(s);
42077c478bd9Sstevel@tonic-gate 	bp = buf;
42087c478bd9Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
42097c478bd9Sstevel@tonic-gate 	{
42107c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
42117c478bd9Sstevel@tonic-gate 		bp += strlen(bp) + 1;
42127c478bd9Sstevel@tonic-gate 	}
42137c478bd9Sstevel@tonic-gate 
42147c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 14)
42157c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf);
42167c478bd9Sstevel@tonic-gate 
42177c478bd9Sstevel@tonic-gate 	/* send it over */
4218*058561cbSjbeck 	response = milter_command(SMFIC_RCPT, buf, s, MilterEnvRcptMacros,
4219*058561cbSjbeck 				e, state, "rcpt", rcpt_error);
42207c478bd9Sstevel@tonic-gate 	sm_free(buf); /* XXX */
42217c478bd9Sstevel@tonic-gate 	return response;
42227c478bd9Sstevel@tonic-gate }
42237c478bd9Sstevel@tonic-gate 
42247c478bd9Sstevel@tonic-gate /*
42257c478bd9Sstevel@tonic-gate **  MILTER_DATA_CMD -- send SMTP DATA command info to milter filters
42267c478bd9Sstevel@tonic-gate **
42277c478bd9Sstevel@tonic-gate **	Parameters:
42287c478bd9Sstevel@tonic-gate **		e -- current envelope.
42297c478bd9Sstevel@tonic-gate **		state -- return state from response.
42307c478bd9Sstevel@tonic-gate **
42317c478bd9Sstevel@tonic-gate **	Returns:
42327c478bd9Sstevel@tonic-gate **		response string (may be NULL)
42337c478bd9Sstevel@tonic-gate */
42347c478bd9Sstevel@tonic-gate 
42357c478bd9Sstevel@tonic-gate char *
42367c478bd9Sstevel@tonic-gate milter_data_cmd(e, state)
42377c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
42387c478bd9Sstevel@tonic-gate 	char *state;
42397c478bd9Sstevel@tonic-gate {
42407c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
42417c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_data_cmd\n");
42427c478bd9Sstevel@tonic-gate 
42437c478bd9Sstevel@tonic-gate 	/* send it over */
4244*058561cbSjbeck 	return milter_command(SMFIC_DATA, NULL, 0, MilterDataMacros, e, state,
4245*058561cbSjbeck 				"data", false);
42467c478bd9Sstevel@tonic-gate }
42477c478bd9Sstevel@tonic-gate 
42487c478bd9Sstevel@tonic-gate /*
42497c478bd9Sstevel@tonic-gate **  MILTER_DATA -- send message headers/body and gather final message results
42507c478bd9Sstevel@tonic-gate **
42517c478bd9Sstevel@tonic-gate **	Parameters:
42527c478bd9Sstevel@tonic-gate **		e -- current envelope.
42537c478bd9Sstevel@tonic-gate **		state -- return state from response.
42547c478bd9Sstevel@tonic-gate **
42557c478bd9Sstevel@tonic-gate **	Returns:
42567c478bd9Sstevel@tonic-gate **		response string (may be NULL)
42577c478bd9Sstevel@tonic-gate **
42587c478bd9Sstevel@tonic-gate **	Side effects:
42597c478bd9Sstevel@tonic-gate **		- Uses e->e_dfp for access to the body
42607c478bd9Sstevel@tonic-gate **		- Can call the various milter action routines to
42617c478bd9Sstevel@tonic-gate **		  modify the envelope or message.
42627c478bd9Sstevel@tonic-gate */
42637c478bd9Sstevel@tonic-gate 
42647c478bd9Sstevel@tonic-gate # define MILTER_CHECK_RESULTS() \
42657c478bd9Sstevel@tonic-gate 	if (*state == SMFIR_ACCEPT || \
42667c478bd9Sstevel@tonic-gate 	    m->mf_state == SMFS_DONE || \
42677c478bd9Sstevel@tonic-gate 	    m->mf_state == SMFS_ERROR) \
42687c478bd9Sstevel@tonic-gate 	{ \
42697c478bd9Sstevel@tonic-gate 		if (m->mf_state != SMFS_ERROR) \
42707c478bd9Sstevel@tonic-gate 			m->mf_state = SMFS_DONE; \
42717c478bd9Sstevel@tonic-gate 		continue;	/* to next filter */ \
42727c478bd9Sstevel@tonic-gate 	} \
42737c478bd9Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE) \
42747c478bd9Sstevel@tonic-gate 	{ \
42757c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_DONE; \
42767c478bd9Sstevel@tonic-gate 		goto finishup; \
42777c478bd9Sstevel@tonic-gate 	}
42787c478bd9Sstevel@tonic-gate 
42797c478bd9Sstevel@tonic-gate char *
42807c478bd9Sstevel@tonic-gate milter_data(e, state)
42817c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
42827c478bd9Sstevel@tonic-gate 	char *state;
42837c478bd9Sstevel@tonic-gate {
42847c478bd9Sstevel@tonic-gate 	bool replbody = false;		/* milter_replbody() called? */
42857c478bd9Sstevel@tonic-gate 	bool replfailed = false;	/* milter_replbody() failed? */
42867c478bd9Sstevel@tonic-gate 	bool rewind = false;		/* rewind data file? */
42877c478bd9Sstevel@tonic-gate 	bool dfopen = false;		/* data file open for writing? */
42887c478bd9Sstevel@tonic-gate 	bool newfilter;			/* reset on each new filter */
42897c478bd9Sstevel@tonic-gate 	char rcmd;
42907c478bd9Sstevel@tonic-gate 	int i;
42917c478bd9Sstevel@tonic-gate 	int save_errno;
42927c478bd9Sstevel@tonic-gate 	char *response = NULL;
42937c478bd9Sstevel@tonic-gate 	time_t eomsent;
42947c478bd9Sstevel@tonic-gate 	ssize_t rlen;
42957c478bd9Sstevel@tonic-gate 
42967c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
42977c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_data\n");
42987c478bd9Sstevel@tonic-gate 
42997c478bd9Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
43007c478bd9Sstevel@tonic-gate 
43017c478bd9Sstevel@tonic-gate 	/*
43027c478bd9Sstevel@tonic-gate 	**  XXX: Should actually send body chunks to each filter
43037c478bd9Sstevel@tonic-gate 	**  a chunk at a time instead of sending the whole body to
43047c478bd9Sstevel@tonic-gate 	**  each filter in turn.  However, only if the filters don't
43057c478bd9Sstevel@tonic-gate 	**  change the body.
43067c478bd9Sstevel@tonic-gate 	*/
43077c478bd9Sstevel@tonic-gate 
43087c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
43097c478bd9Sstevel@tonic-gate 	{
43107c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
43117c478bd9Sstevel@tonic-gate 
43127c478bd9Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE &&
43137c478bd9Sstevel@tonic-gate 		    *state != SMFIR_ACCEPT)
43147c478bd9Sstevel@tonic-gate 		{
43157c478bd9Sstevel@tonic-gate 			/*
43167c478bd9Sstevel@tonic-gate 			**  A previous filter has dealt with the message,
43177c478bd9Sstevel@tonic-gate 			**  safe to stop processing the filters.
43187c478bd9Sstevel@tonic-gate 			*/
43197c478bd9Sstevel@tonic-gate 
43207c478bd9Sstevel@tonic-gate 			break;
43217c478bd9Sstevel@tonic-gate 		}
43227c478bd9Sstevel@tonic-gate 
43237c478bd9Sstevel@tonic-gate 		/* Now reset state for later evaluation */
43247c478bd9Sstevel@tonic-gate 		*state = SMFIR_CONTINUE;
43257c478bd9Sstevel@tonic-gate 		newfilter = true;
43267c478bd9Sstevel@tonic-gate 
43277c478bd9Sstevel@tonic-gate 		/* previous problem? */
43287c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
43297c478bd9Sstevel@tonic-gate 		{
43307c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
43317c478bd9Sstevel@tonic-gate 			break;
43327c478bd9Sstevel@tonic-gate 		}
43337c478bd9Sstevel@tonic-gate 
43347c478bd9Sstevel@tonic-gate 		/* sanity checks */
43357c478bd9Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
43367c478bd9Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
43377c478bd9Sstevel@tonic-gate 			continue;
43387c478bd9Sstevel@tonic-gate 
43397c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_INMSG;
43407c478bd9Sstevel@tonic-gate 
43417c478bd9Sstevel@tonic-gate 		/* check if filter wants the headers */
43427c478bd9Sstevel@tonic-gate 		if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
43437c478bd9Sstevel@tonic-gate 		{
43447c478bd9Sstevel@tonic-gate 			response = milter_headers(m, e, state);
43457c478bd9Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43467c478bd9Sstevel@tonic-gate 		}
43477c478bd9Sstevel@tonic-gate 
43487c478bd9Sstevel@tonic-gate 		/* check if filter wants EOH */
43497c478bd9Sstevel@tonic-gate 		if (!bitset(SMFIP_NOEOH, m->mf_pflags))
43507c478bd9Sstevel@tonic-gate 		{
43517c478bd9Sstevel@tonic-gate 			if (tTd(64, 10))
43527c478bd9Sstevel@tonic-gate 				sm_dprintf("milter_data: eoh\n");
43537c478bd9Sstevel@tonic-gate 
4354*058561cbSjbeck 			if (MilterEOHMacros[0] != NULL)
4355*058561cbSjbeck 			{
4356*058561cbSjbeck 				milter_send_macros(m, MilterEOHMacros,
4357*058561cbSjbeck 					   SMFIC_EOH, e);
4358*058561cbSjbeck 				MILTER_CHECK_RESULTS();
4359*058561cbSjbeck 			}
4360*058561cbSjbeck 
43617c478bd9Sstevel@tonic-gate 			/* send it over */
43627c478bd9Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_EOH, NULL, 0,
4363*058561cbSjbeck 						       e, state, "eoh");
43647c478bd9Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43657c478bd9Sstevel@tonic-gate 		}
43667c478bd9Sstevel@tonic-gate 
43677c478bd9Sstevel@tonic-gate 		/* check if filter wants the body */
43687c478bd9Sstevel@tonic-gate 		if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
43697c478bd9Sstevel@tonic-gate 		    e->e_dfp != NULL)
43707c478bd9Sstevel@tonic-gate 		{
43717c478bd9Sstevel@tonic-gate 			rewind = true;
43727c478bd9Sstevel@tonic-gate 			response = milter_body(m, e, state);
43737c478bd9Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43747c478bd9Sstevel@tonic-gate 		}
43757c478bd9Sstevel@tonic-gate 
43767c478bd9Sstevel@tonic-gate 		if (MilterEOMMacros[0] != NULL)
43777c478bd9Sstevel@tonic-gate 		{
43787c478bd9Sstevel@tonic-gate 			milter_send_macros(m, MilterEOMMacros,
43797c478bd9Sstevel@tonic-gate 					   SMFIC_BODYEOB, e);
43807c478bd9Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43817c478bd9Sstevel@tonic-gate 		}
43827c478bd9Sstevel@tonic-gate 
43837c478bd9Sstevel@tonic-gate 		/* send the final body chunk */
43847c478bd9Sstevel@tonic-gate 		(void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
4385*058561cbSjbeck 				    m->mf_timeout[SMFTO_WRITE], e, "eom");
43867c478bd9Sstevel@tonic-gate 
43877c478bd9Sstevel@tonic-gate 		/* Get time EOM sent for timeout */
43887c478bd9Sstevel@tonic-gate 		eomsent = curtime();
43897c478bd9Sstevel@tonic-gate 
43907c478bd9Sstevel@tonic-gate 		/* deal with the possibility of multiple responses */
43917c478bd9Sstevel@tonic-gate 		while (*state == SMFIR_CONTINUE)
43927c478bd9Sstevel@tonic-gate 		{
43937c478bd9Sstevel@tonic-gate 			/* Check total timeout from EOM to final ACK/NAK */
43947c478bd9Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_EOM] > 0 &&
43957c478bd9Sstevel@tonic-gate 			    curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
43967c478bd9Sstevel@tonic-gate 			{
43977c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
43987c478bd9Sstevel@tonic-gate 					sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
43997c478bd9Sstevel@tonic-gate 						m->mf_name);
44007c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
44017c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
44027c478bd9Sstevel@tonic-gate 						  "milter_data(%s): EOM ACK/NAK timeout",
44037c478bd9Sstevel@tonic-gate 						  m->mf_name);
44047c478bd9Sstevel@tonic-gate 				milter_error(m, e);
44057c478bd9Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, break);
44067c478bd9Sstevel@tonic-gate 				break;
44077c478bd9Sstevel@tonic-gate 			}
44087c478bd9Sstevel@tonic-gate 
44097c478bd9Sstevel@tonic-gate 			response = milter_read(m, &rcmd, &rlen,
4410*058561cbSjbeck 					       m->mf_timeout[SMFTO_READ], e,
4411*058561cbSjbeck 						"body");
44127c478bd9Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
44137c478bd9Sstevel@tonic-gate 				break;
44147c478bd9Sstevel@tonic-gate 
44157c478bd9Sstevel@tonic-gate 			if (tTd(64, 10))
44167c478bd9Sstevel@tonic-gate 				sm_dprintf("milter_data(%s): state %c\n",
44177c478bd9Sstevel@tonic-gate 					   m->mf_name, (char) rcmd);
44187c478bd9Sstevel@tonic-gate 
44197c478bd9Sstevel@tonic-gate 			switch (rcmd)
44207c478bd9Sstevel@tonic-gate 			{
44217c478bd9Sstevel@tonic-gate 			  case SMFIR_REPLYCODE:
44227c478bd9Sstevel@tonic-gate 				MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
44237c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44247c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s",
44257c478bd9Sstevel@tonic-gate 						  m->mf_name, response);
44267c478bd9Sstevel@tonic-gate 				*state = rcmd;
44277c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44287c478bd9Sstevel@tonic-gate 				break;
44297c478bd9Sstevel@tonic-gate 
44307c478bd9Sstevel@tonic-gate 			  case SMFIR_REJECT: /* log msg at end of function */
44317c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44327c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject",
44337c478bd9Sstevel@tonic-gate 						  m->mf_name);
44347c478bd9Sstevel@tonic-gate 				*state = rcmd;
44357c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44367c478bd9Sstevel@tonic-gate 				break;
44377c478bd9Sstevel@tonic-gate 
44387c478bd9Sstevel@tonic-gate 			  case SMFIR_DISCARD:
44397c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44407c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard",
44417c478bd9Sstevel@tonic-gate 						  m->mf_name);
44427c478bd9Sstevel@tonic-gate 				*state = rcmd;
44437c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44447c478bd9Sstevel@tonic-gate 				break;
44457c478bd9Sstevel@tonic-gate 
44467c478bd9Sstevel@tonic-gate 			  case SMFIR_TEMPFAIL:
44477c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44487c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail",
44497c478bd9Sstevel@tonic-gate 						  m->mf_name);
44507c478bd9Sstevel@tonic-gate 				*state = rcmd;
44517c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44527c478bd9Sstevel@tonic-gate 				break;
44537c478bd9Sstevel@tonic-gate 
44547c478bd9Sstevel@tonic-gate 			  case SMFIR_CONTINUE:
44557c478bd9Sstevel@tonic-gate 			  case SMFIR_ACCEPT:
44567c478bd9Sstevel@tonic-gate 				/* this filter is done with message */
44577c478bd9Sstevel@tonic-gate 				if (replfailed)
44587c478bd9Sstevel@tonic-gate 					*state = SMFIR_TEMPFAIL;
44597c478bd9Sstevel@tonic-gate 				else
44607c478bd9Sstevel@tonic-gate 					*state = SMFIR_ACCEPT;
44617c478bd9Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44627c478bd9Sstevel@tonic-gate 				break;
44637c478bd9Sstevel@tonic-gate 
44647c478bd9Sstevel@tonic-gate 			  case SMFIR_PROGRESS:
44657c478bd9Sstevel@tonic-gate 				break;
44667c478bd9Sstevel@tonic-gate 
44677c478bd9Sstevel@tonic-gate 			  case SMFIR_QUARANTINE:
44687c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_QUARANTINE, m->mf_fflags))
44697c478bd9Sstevel@tonic-gate 				{
44707c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
44717c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
44727c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about quarantining, honoring request anyway",
44737c478bd9Sstevel@tonic-gate 							  m->mf_name);
44747c478bd9Sstevel@tonic-gate 				}
44757c478bd9Sstevel@tonic-gate 				if (response == NULL)
44767c478bd9Sstevel@tonic-gate 					response = newstr("");
44777c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 3)
44787c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
44797c478bd9Sstevel@tonic-gate 						  "milter=%s, quarantine=%s",
44807c478bd9Sstevel@tonic-gate 						  m->mf_name, response);
44817c478bd9Sstevel@tonic-gate 				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
44827c478bd9Sstevel@tonic-gate 								 response);
44837c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
44847c478bd9Sstevel@tonic-gate 					  macid("{quarantine}"), e->e_quarmsg);
44857c478bd9Sstevel@tonic-gate 				break;
44867c478bd9Sstevel@tonic-gate 
44877c478bd9Sstevel@tonic-gate 			  case SMFIR_ADDHEADER:
44887c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
44897c478bd9Sstevel@tonic-gate 				{
44907c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
44917c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
44927c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
44937c478bd9Sstevel@tonic-gate 							  m->mf_name);
44947c478bd9Sstevel@tonic-gate 				}
4495*058561cbSjbeck 				milter_addheader(m, response, rlen, e);
44967c478bd9Sstevel@tonic-gate 				break;
44977c478bd9Sstevel@tonic-gate 
44987c478bd9Sstevel@tonic-gate 			  case SMFIR_INSHEADER:
44997c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
45007c478bd9Sstevel@tonic-gate 				{
45017c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45027c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45037c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
45047c478bd9Sstevel@tonic-gate 							  m->mf_name);
45057c478bd9Sstevel@tonic-gate 				}
4506*058561cbSjbeck 				milter_insheader(m, response, rlen, e);
45077c478bd9Sstevel@tonic-gate 				break;
45087c478bd9Sstevel@tonic-gate 
45097c478bd9Sstevel@tonic-gate 			  case SMFIR_CHGHEADER:
45107c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
45117c478bd9Sstevel@tonic-gate 				{
45127c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45137c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45147c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about changing headers, honoring request anyway",
45157c478bd9Sstevel@tonic-gate 							  m->mf_name);
45167c478bd9Sstevel@tonic-gate 				}
4517*058561cbSjbeck 				milter_changeheader(m, response, rlen, e);
4518*058561cbSjbeck 				break;
4519*058561cbSjbeck 
4520*058561cbSjbeck 			  case SMFIR_CHGFROM:
4521*058561cbSjbeck 				if (!bitset(SMFIF_CHGFROM, m->mf_fflags))
4522*058561cbSjbeck 				{
4523*058561cbSjbeck 					if (MilterLogLevel > 9)
4524*058561cbSjbeck 						sm_syslog(LOG_WARNING, e->e_id,
4525*058561cbSjbeck 							  "milter_data(%s) lied about changing sender, honoring request anyway",
4526*058561cbSjbeck 							  m->mf_name);
4527*058561cbSjbeck 				}
4528*058561cbSjbeck 				milter_chgfrom(response, rlen, e);
45297c478bd9Sstevel@tonic-gate 				break;
45307c478bd9Sstevel@tonic-gate 
45317c478bd9Sstevel@tonic-gate 			  case SMFIR_ADDRCPT:
45327c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
45337c478bd9Sstevel@tonic-gate 				{
45347c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45357c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45367c478bd9Sstevel@tonic-gate 							  "milter_data(%s) lied about adding recipients, honoring request anyway",
45377c478bd9Sstevel@tonic-gate 							  m->mf_name);
45387c478bd9Sstevel@tonic-gate 				}
45397c478bd9Sstevel@tonic-gate 				milter_addrcpt(response, rlen, e);
45407c478bd9Sstevel@tonic-gate 				break;
45417c478bd9Sstevel@tonic-gate 
4542*058561cbSjbeck 			  case SMFIR_ADDRCPT_PAR:
4543*058561cbSjbeck 				if (!bitset(SMFIF_ADDRCPT_PAR, m->mf_fflags))
4544*058561cbSjbeck 				{
4545*058561cbSjbeck 					if (MilterLogLevel > 9)
4546*058561cbSjbeck 						sm_syslog(LOG_WARNING, e->e_id,
4547*058561cbSjbeck 							  "milter_data(%s) lied about adding recipients with parameters, honoring request anyway",
4548*058561cbSjbeck 							  m->mf_name);
4549*058561cbSjbeck 				}
4550*058561cbSjbeck 				milter_addrcpt_par(response, rlen, e);
4551*058561cbSjbeck 				break;
4552*058561cbSjbeck 
45537c478bd9Sstevel@tonic-gate 			  case SMFIR_DELRCPT:
45547c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
45557c478bd9Sstevel@tonic-gate 				{
45567c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45577c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45587c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about removing recipients, honoring request anyway",
45597c478bd9Sstevel@tonic-gate 							  m->mf_name);
45607c478bd9Sstevel@tonic-gate 				}
45617c478bd9Sstevel@tonic-gate 				milter_delrcpt(response, rlen, e);
45627c478bd9Sstevel@tonic-gate 				break;
45637c478bd9Sstevel@tonic-gate 
45647c478bd9Sstevel@tonic-gate 			  case SMFIR_REPLBODY:
45657c478bd9Sstevel@tonic-gate 				if (!bitset(SMFIF_MODBODY, m->mf_fflags))
45667c478bd9Sstevel@tonic-gate 				{
45677c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 0)
45687c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
45697c478bd9Sstevel@tonic-gate 							  "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
45707c478bd9Sstevel@tonic-gate 							  m->mf_name);
45717c478bd9Sstevel@tonic-gate 					replfailed = true;
45727c478bd9Sstevel@tonic-gate 					break;
45737c478bd9Sstevel@tonic-gate 				}
45747c478bd9Sstevel@tonic-gate 
45757c478bd9Sstevel@tonic-gate 				/* already failed in attempt */
45767c478bd9Sstevel@tonic-gate 				if (replfailed)
45777c478bd9Sstevel@tonic-gate 					break;
45787c478bd9Sstevel@tonic-gate 
45797c478bd9Sstevel@tonic-gate 				if (!dfopen)
45807c478bd9Sstevel@tonic-gate 				{
45817c478bd9Sstevel@tonic-gate 					if (milter_reopen_df(e) < 0)
45827c478bd9Sstevel@tonic-gate 					{
45837c478bd9Sstevel@tonic-gate 						replfailed = true;
45847c478bd9Sstevel@tonic-gate 						break;
45857c478bd9Sstevel@tonic-gate 					}
45867c478bd9Sstevel@tonic-gate 					dfopen = true;
45877c478bd9Sstevel@tonic-gate 					rewind = true;
45887c478bd9Sstevel@tonic-gate 				}
45897c478bd9Sstevel@tonic-gate 
45907c478bd9Sstevel@tonic-gate 				if (milter_replbody(response, rlen,
45917c478bd9Sstevel@tonic-gate 						    newfilter, e) < 0)
45927c478bd9Sstevel@tonic-gate 					replfailed = true;
45937c478bd9Sstevel@tonic-gate 				newfilter = false;
45947c478bd9Sstevel@tonic-gate 				replbody = true;
45957c478bd9Sstevel@tonic-gate 				break;
45967c478bd9Sstevel@tonic-gate 
45977c478bd9Sstevel@tonic-gate 			  default:
45987c478bd9Sstevel@tonic-gate 				/* Invalid response to command */
45997c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
46007c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
46017c478bd9Sstevel@tonic-gate 						  "milter_data(%s): returned bogus response %c",
46027c478bd9Sstevel@tonic-gate 						  m->mf_name, rcmd);
46037c478bd9Sstevel@tonic-gate 				milter_error(m, e);
46047c478bd9Sstevel@tonic-gate 				break;
46057c478bd9Sstevel@tonic-gate 			}
46067c478bd9Sstevel@tonic-gate 			if (rcmd != SMFIR_REPLYCODE && response != NULL)
46077c478bd9Sstevel@tonic-gate 			{
46087c478bd9Sstevel@tonic-gate 				sm_free(response); /* XXX */
46097c478bd9Sstevel@tonic-gate 				response = NULL;
46107c478bd9Sstevel@tonic-gate 			}
46117c478bd9Sstevel@tonic-gate 
46127c478bd9Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
46137c478bd9Sstevel@tonic-gate 				break;
46147c478bd9Sstevel@tonic-gate 		}
46157c478bd9Sstevel@tonic-gate 
46167c478bd9Sstevel@tonic-gate 		if (replbody && !replfailed)
46177c478bd9Sstevel@tonic-gate 		{
46187c478bd9Sstevel@tonic-gate 			/* flush possible buffered character */
46197c478bd9Sstevel@tonic-gate 			milter_replbody(NULL, 0, !replbody, e);
46207c478bd9Sstevel@tonic-gate 			replbody = false;
46217c478bd9Sstevel@tonic-gate 		}
46227c478bd9Sstevel@tonic-gate 
46237c478bd9Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
46247c478bd9Sstevel@tonic-gate 		{
46257c478bd9Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
46267c478bd9Sstevel@tonic-gate 			goto finishup;
46277c478bd9Sstevel@tonic-gate 		}
46287c478bd9Sstevel@tonic-gate 	}
46297c478bd9Sstevel@tonic-gate 
46307c478bd9Sstevel@tonic-gate finishup:
46317c478bd9Sstevel@tonic-gate 	/* leave things in the expected state if we touched it */
46327c478bd9Sstevel@tonic-gate 	if (replfailed)
46337c478bd9Sstevel@tonic-gate 	{
46347c478bd9Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
46357c478bd9Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
46367c478bd9Sstevel@tonic-gate 		{
46377c478bd9Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
46387c478bd9Sstevel@tonic-gate 			SM_FREE_CLR(response);
46397c478bd9Sstevel@tonic-gate 		}
46407c478bd9Sstevel@tonic-gate 
46417c478bd9Sstevel@tonic-gate 		if (dfopen)
46427c478bd9Sstevel@tonic-gate 		{
46437c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
46447c478bd9Sstevel@tonic-gate 			e->e_dfp = NULL;
46457c478bd9Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
46467c478bd9Sstevel@tonic-gate 			dfopen = false;
46477c478bd9Sstevel@tonic-gate 		}
46487c478bd9Sstevel@tonic-gate 		rewind = false;
46497c478bd9Sstevel@tonic-gate 	}
46507c478bd9Sstevel@tonic-gate 
46517c478bd9Sstevel@tonic-gate 	if ((dfopen && milter_reset_df(e) < 0) ||
46527c478bd9Sstevel@tonic-gate 	    (rewind && bfrewind(e->e_dfp) < 0))
46537c478bd9Sstevel@tonic-gate 	{
46547c478bd9Sstevel@tonic-gate 		save_errno = errno;
46557c478bd9Sstevel@tonic-gate 		ExitStat = EX_IOERR;
46567c478bd9Sstevel@tonic-gate 
46577c478bd9Sstevel@tonic-gate 		/*
46587c478bd9Sstevel@tonic-gate 		**  If filter told us to keep message but we had
46597c478bd9Sstevel@tonic-gate 		**  an error, we can't really keep it, tempfail it.
46607c478bd9Sstevel@tonic-gate 		*/
46617c478bd9Sstevel@tonic-gate 
46627c478bd9Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
46637c478bd9Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
46647c478bd9Sstevel@tonic-gate 		{
46657c478bd9Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
46667c478bd9Sstevel@tonic-gate 			SM_FREE_CLR(response);
46677c478bd9Sstevel@tonic-gate 		}
46687c478bd9Sstevel@tonic-gate 
46697c478bd9Sstevel@tonic-gate 		errno = save_errno;
46707c478bd9Sstevel@tonic-gate 		syserr("milter_data: %s/%cf%s: read error",
46717c478bd9Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
46727c478bd9Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
46737c478bd9Sstevel@tonic-gate 	}
46747c478bd9Sstevel@tonic-gate 
46757c478bd9Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
46767c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
46777c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data");
46787c478bd9Sstevel@tonic-gate 	return response;
46797c478bd9Sstevel@tonic-gate }
46807c478bd9Sstevel@tonic-gate 
46817c478bd9Sstevel@tonic-gate /*
46827c478bd9Sstevel@tonic-gate **  MILTER_UNKNOWN -- send any unrecognized or unimplemented command
46837c478bd9Sstevel@tonic-gate **			string to milter filters
46847c478bd9Sstevel@tonic-gate **
46857c478bd9Sstevel@tonic-gate **	Parameters:
4686*058561cbSjbeck **		smtpcmd -- the string itself.
46877c478bd9Sstevel@tonic-gate **		e -- current envelope.
46887c478bd9Sstevel@tonic-gate **		state -- return state from response.
46897c478bd9Sstevel@tonic-gate **
46907c478bd9Sstevel@tonic-gate **
46917c478bd9Sstevel@tonic-gate **	Returns:
46927c478bd9Sstevel@tonic-gate **		response string (may be NULL)
46937c478bd9Sstevel@tonic-gate */
46947c478bd9Sstevel@tonic-gate 
46957c478bd9Sstevel@tonic-gate char *
4696*058561cbSjbeck milter_unknown(smtpcmd, e, state)
4697*058561cbSjbeck 	char *smtpcmd;
46987c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
46997c478bd9Sstevel@tonic-gate 	char *state;
47007c478bd9Sstevel@tonic-gate {
47017c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
4702*058561cbSjbeck 		sm_dprintf("milter_unknown(%s)\n", smtpcmd);
47037c478bd9Sstevel@tonic-gate 
4704*058561cbSjbeck 	return milter_command(SMFIC_UNKNOWN, smtpcmd, strlen(smtpcmd) + 1,
4705*058561cbSjbeck 				NULL, e, state, "unknown", false);
47067c478bd9Sstevel@tonic-gate }
47077c478bd9Sstevel@tonic-gate 
47087c478bd9Sstevel@tonic-gate /*
47097c478bd9Sstevel@tonic-gate **  MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
47107c478bd9Sstevel@tonic-gate **
47117c478bd9Sstevel@tonic-gate **	Parameters:
47127c478bd9Sstevel@tonic-gate **		e -- current envelope.
47137c478bd9Sstevel@tonic-gate **
47147c478bd9Sstevel@tonic-gate **	Returns:
47157c478bd9Sstevel@tonic-gate **		none
47167c478bd9Sstevel@tonic-gate */
47177c478bd9Sstevel@tonic-gate 
47187c478bd9Sstevel@tonic-gate void
47197c478bd9Sstevel@tonic-gate milter_quit(e)
47207c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
47217c478bd9Sstevel@tonic-gate {
47227c478bd9Sstevel@tonic-gate 	int i;
47237c478bd9Sstevel@tonic-gate 
47247c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
47257c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_quit(%s)\n", e->e_id);
47267c478bd9Sstevel@tonic-gate 
47277c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
47287c478bd9Sstevel@tonic-gate 		milter_quit_filter(InputFilters[i], e);
47297c478bd9Sstevel@tonic-gate }
4730*058561cbSjbeck 
47317c478bd9Sstevel@tonic-gate /*
47327c478bd9Sstevel@tonic-gate **  MILTER_ABORT -- informs the filter(s) that we are aborting current message
47337c478bd9Sstevel@tonic-gate **
47347c478bd9Sstevel@tonic-gate **	Parameters:
47357c478bd9Sstevel@tonic-gate **		e -- current envelope.
47367c478bd9Sstevel@tonic-gate **
47377c478bd9Sstevel@tonic-gate **	Returns:
47387c478bd9Sstevel@tonic-gate **		none
47397c478bd9Sstevel@tonic-gate */
47407c478bd9Sstevel@tonic-gate 
47417c478bd9Sstevel@tonic-gate void
47427c478bd9Sstevel@tonic-gate milter_abort(e)
47437c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
47447c478bd9Sstevel@tonic-gate {
47457c478bd9Sstevel@tonic-gate 	int i;
47467c478bd9Sstevel@tonic-gate 
47477c478bd9Sstevel@tonic-gate 	if (tTd(64, 10))
47487c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_abort\n");
47497c478bd9Sstevel@tonic-gate 
47507c478bd9Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
47517c478bd9Sstevel@tonic-gate 	{
47527c478bd9Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
47537c478bd9Sstevel@tonic-gate 
47547c478bd9Sstevel@tonic-gate 		/* sanity checks */
47557c478bd9Sstevel@tonic-gate 		if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
47567c478bd9Sstevel@tonic-gate 			continue;
47577c478bd9Sstevel@tonic-gate 
47587c478bd9Sstevel@tonic-gate 		milter_abort_filter(m, e);
47597c478bd9Sstevel@tonic-gate 	}
47607c478bd9Sstevel@tonic-gate }
47617c478bd9Sstevel@tonic-gate #endif /* MILTER */
4762