xref: /onnv-gate/usr/src/cmd/sendmail/src/milter.c (revision 11440:802724e2906a)
10Sstevel@tonic-gate /*
2*11440SJohn.Beck@Sun.COM  * Copyright (c) 1999-2009 Sendmail, Inc. and its suppliers.
30Sstevel@tonic-gate  *	All rights reserved.
40Sstevel@tonic-gate  *
50Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
60Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
70Sstevel@tonic-gate  * the sendmail distribution.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  */
100Sstevel@tonic-gate 
110Sstevel@tonic-gate #include <sendmail.h>
120Sstevel@tonic-gate 
13*11440SJohn.Beck@Sun.COM SM_RCSID("@(#)$Id: milter.c,v 8.277 2009/11/06 00:57:06 ca Exp $")
140Sstevel@tonic-gate 
150Sstevel@tonic-gate #if MILTER
163544Sjbeck # include <sm/sendmail.h>
170Sstevel@tonic-gate # include <libmilter/mfapi.h>
180Sstevel@tonic-gate # include <libmilter/mfdef.h>
190Sstevel@tonic-gate 
200Sstevel@tonic-gate # include <errno.h>
21616Sjbeck # include <sm/time.h>
220Sstevel@tonic-gate # include <sys/uio.h>
230Sstevel@tonic-gate 
240Sstevel@tonic-gate # if NETINET || NETINET6
250Sstevel@tonic-gate #  include <arpa/inet.h>
263544Sjbeck #  if MILTER_NO_NAGLE
270Sstevel@tonic-gate #   include <netinet/tcp.h>
283544Sjbeck #  endif /* MILTER_NO_NAGLE */
290Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
300Sstevel@tonic-gate 
310Sstevel@tonic-gate # include <sm/fdset.h>
320Sstevel@tonic-gate 
330Sstevel@tonic-gate static void	milter_connect_timeout __P((int));
340Sstevel@tonic-gate static void	milter_error __P((struct milter *, ENVELOPE *));
350Sstevel@tonic-gate static int	milter_open __P((struct milter *, bool, ENVELOPE *));
360Sstevel@tonic-gate static void	milter_parse_timeouts __P((char *, struct milter *));
373544Sjbeck static char	*milter_sysread __P((struct milter *, char *, ssize_t, time_t,
383544Sjbeck 			ENVELOPE *, const char *));
393544Sjbeck static char	*milter_read __P((struct milter *, char *, ssize_t *, time_t,
403544Sjbeck 			ENVELOPE *, const char *));
413544Sjbeck static char	*milter_write __P((struct milter *, int, char *, ssize_t,
423544Sjbeck 			time_t, ENVELOPE *, const char *));
433544Sjbeck static char	*milter_send_command __P((struct milter *, int, void *,
443544Sjbeck 			ssize_t, ENVELOPE *, char *, const char *));
453544Sjbeck static char	*milter_command __P((int, void *, ssize_t, char **,
463544Sjbeck 			ENVELOPE *, char *, const char *, bool));
473544Sjbeck static char	*milter_body __P((struct milter *, ENVELOPE *, char *));
483544Sjbeck static int	milter_reopen_df __P((ENVELOPE *));
493544Sjbeck static int	milter_reset_df __P((ENVELOPE *));
503544Sjbeck static void	milter_quit_filter __P((struct milter *, ENVELOPE *));
513544Sjbeck static void	milter_abort_filter __P((struct milter *, ENVELOPE *));
523544Sjbeck static void	milter_send_macros __P((struct milter *, char **, int,
533544Sjbeck 			ENVELOPE *));
545402Sjbeck static int	milter_negotiate __P((struct milter *, ENVELOPE *,
555402Sjbeck 			milters_T *));
563544Sjbeck static void	milter_per_connection_check __P((ENVELOPE *));
573544Sjbeck static char	*milter_headers __P((struct milter *, ENVELOPE *, char *));
583544Sjbeck static void	milter_addheader __P((struct milter *, char *, ssize_t,
593544Sjbeck 			ENVELOPE *));
603544Sjbeck static void	milter_insheader __P((struct milter *, char *, ssize_t,
613544Sjbeck 			ENVELOPE *));
623544Sjbeck static void	milter_changeheader __P((struct milter *, char *, ssize_t,
633544Sjbeck 			ENVELOPE *));
643544Sjbeck static void	milter_chgfrom __P((char *, ssize_t, ENVELOPE *));
653544Sjbeck static void	milter_addrcpt __P((char *, ssize_t, ENVELOPE *));
663544Sjbeck static void	milter_addrcpt_par __P((char *, ssize_t, ENVELOPE *));
673544Sjbeck static void	milter_delrcpt __P((char *, ssize_t, ENVELOPE *));
683544Sjbeck static int	milter_replbody __P((char *, ssize_t, bool, ENVELOPE *));
693544Sjbeck static int	milter_set_macros __P((char *, char **, char *, int));
703544Sjbeck 
713544Sjbeck 
723544Sjbeck /* milter states */
733544Sjbeck # define SMFS_CLOSED		'C'	/* closed for all further actions */
743544Sjbeck # define SMFS_OPEN		'O'	/* connected to remote milter filter */
753544Sjbeck # define SMFS_INMSG		'M'	/* currently servicing a message */
763544Sjbeck # define SMFS_DONE		'D'	/* done with current message */
773544Sjbeck # define SMFS_CLOSABLE		'Q'	/* done with current connection */
783544Sjbeck # define SMFS_ERROR		'E'	/* error state */
793544Sjbeck # define SMFS_READY		'R'	/* ready for action */
803544Sjbeck # define SMFS_SKIP		'S'	/* skip body */
810Sstevel@tonic-gate 
820Sstevel@tonic-gate static char *MilterConnectMacros[MAXFILTERMACROS + 1];
830Sstevel@tonic-gate static char *MilterHeloMacros[MAXFILTERMACROS + 1];
840Sstevel@tonic-gate static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
850Sstevel@tonic-gate static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
860Sstevel@tonic-gate static char *MilterDataMacros[MAXFILTERMACROS + 1];
870Sstevel@tonic-gate static char *MilterEOMMacros[MAXFILTERMACROS + 1];
883544Sjbeck static char *MilterEOHMacros[MAXFILTERMACROS + 1];
890Sstevel@tonic-gate static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate # define MILTER_CHECK_DONE_MSG() \
920Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE || \
930Sstevel@tonic-gate 	    *state == SMFIR_REJECT || \
940Sstevel@tonic-gate 	    *state == SMFIR_DISCARD || \
950Sstevel@tonic-gate 	    *state == SMFIR_TEMPFAIL) \
960Sstevel@tonic-gate 	{ \
970Sstevel@tonic-gate 		/* Abort the filters to let them know we are done with msg */ \
980Sstevel@tonic-gate 		milter_abort(e); \
990Sstevel@tonic-gate 	}
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate # define MILTER_CHECK_ERROR(initial, action) \
1020Sstevel@tonic-gate 	if (!initial && tTd(71, 100)) \
1030Sstevel@tonic-gate 	{ \
1040Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1050Sstevel@tonic-gate 		{ \
1060Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1070Sstevel@tonic-gate 							 "filter failure"); \
1080Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1090Sstevel@tonic-gate 				  e->e_quarmsg); \
1100Sstevel@tonic-gate 		} \
1110Sstevel@tonic-gate 	} \
1120Sstevel@tonic-gate 	else if (tTd(71, 101)) \
1130Sstevel@tonic-gate 	{ \
1140Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1150Sstevel@tonic-gate 		{ \
1160Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1170Sstevel@tonic-gate 							 "filter failure"); \
1180Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1190Sstevel@tonic-gate 				  e->e_quarmsg); \
1200Sstevel@tonic-gate 		} \
1210Sstevel@tonic-gate 	} \
1220Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
1230Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL; \
1240Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \
1250Sstevel@tonic-gate 		*state = SMFIR_SHUTDOWN; \
1260Sstevel@tonic-gate 	else if (bitnset(SMF_REJECT, m->mf_flags)) \
1270Sstevel@tonic-gate 		*state = SMFIR_REJECT; \
1280Sstevel@tonic-gate 	else \
1290Sstevel@tonic-gate 		action;
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate # define MILTER_CHECK_REPLYCODE(default) \
1320Sstevel@tonic-gate 	if (response == NULL || \
1330Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen || \
1340Sstevel@tonic-gate 	    rlen < 3 || \
1350Sstevel@tonic-gate 	    (response[0] != '4' && response[0] != '5') || \
1360Sstevel@tonic-gate 	    !isascii(response[1]) || !isdigit(response[1]) || \
1370Sstevel@tonic-gate 	    !isascii(response[2]) || !isdigit(response[2])) \
1380Sstevel@tonic-gate 	{ \
1390Sstevel@tonic-gate 		if (response != NULL) \
1400Sstevel@tonic-gate 			sm_free(response); /* XXX */ \
1410Sstevel@tonic-gate 		response = newstr(default); \
1420Sstevel@tonic-gate 	} \
1430Sstevel@tonic-gate 	else \
1440Sstevel@tonic-gate 	{ \
1450Sstevel@tonic-gate 		char *ptr = response; \
1460Sstevel@tonic-gate  \
1470Sstevel@tonic-gate 		/* Check for unprotected %'s in the string */ \
1480Sstevel@tonic-gate 		while (*ptr != '\0') \
1490Sstevel@tonic-gate 		{ \
1500Sstevel@tonic-gate 			if (*ptr == '%' && *++ptr != '%') \
1510Sstevel@tonic-gate 			{ \
1520Sstevel@tonic-gate 				sm_free(response); /* XXX */ \
1530Sstevel@tonic-gate 				response = newstr(default); \
1540Sstevel@tonic-gate 				break; \
1550Sstevel@tonic-gate 			} \
1560Sstevel@tonic-gate 			ptr++; \
1570Sstevel@tonic-gate 		} \
1580Sstevel@tonic-gate 	}
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate # define MILTER_DF_ERROR(msg) \
1610Sstevel@tonic-gate { \
1620Sstevel@tonic-gate 	int save_errno = errno; \
1630Sstevel@tonic-gate  \
1640Sstevel@tonic-gate 	if (tTd(64, 5)) \
1650Sstevel@tonic-gate 	{ \
1660Sstevel@tonic-gate 		sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
1670Sstevel@tonic-gate 		sm_dprintf("\n"); \
1680Sstevel@tonic-gate 	} \
1690Sstevel@tonic-gate 	if (MilterLogLevel > 0) \
1700Sstevel@tonic-gate 		sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
1710Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY) \
1720Sstevel@tonic-gate 	{ \
1730Sstevel@tonic-gate 		if (e->e_dfp != NULL) \
1740Sstevel@tonic-gate 		{ \
1750Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
1760Sstevel@tonic-gate 			e->e_dfp = NULL; \
1770Sstevel@tonic-gate 		} \
1780Sstevel@tonic-gate 		e->e_flags &= ~EF_HAS_DF; \
1790Sstevel@tonic-gate 	} \
1800Sstevel@tonic-gate 	errno = save_errno; \
1810Sstevel@tonic-gate }
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate /*
1840Sstevel@tonic-gate **  MILTER_TIMEOUT -- make sure socket is ready in time
1850Sstevel@tonic-gate **
1860Sstevel@tonic-gate **	Parameters:
1870Sstevel@tonic-gate **		routine -- routine name for debug/logging
1880Sstevel@tonic-gate **		secs -- number of seconds in timeout
1890Sstevel@tonic-gate **		write -- waiting to read or write?
1900Sstevel@tonic-gate **		started -- whether this is part of a previous sequence
1910Sstevel@tonic-gate **
1920Sstevel@tonic-gate **	Assumes 'm' is a milter structure for the current socket.
1930Sstevel@tonic-gate */
1940Sstevel@tonic-gate 
1953544Sjbeck # define MILTER_TIMEOUT(routine, secs, write, started, function) \
1960Sstevel@tonic-gate { \
1970Sstevel@tonic-gate 	int ret; \
1980Sstevel@tonic-gate 	int save_errno; \
1990Sstevel@tonic-gate 	fd_set fds; \
2000Sstevel@tonic-gate 	struct timeval tv; \
2010Sstevel@tonic-gate  \
2020Sstevel@tonic-gate 	if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
2030Sstevel@tonic-gate 	{ \
2040Sstevel@tonic-gate 		if (tTd(64, 5)) \
2050Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
2060Sstevel@tonic-gate 				   (routine), m->mf_name, m->mf_sock, \
2070Sstevel@tonic-gate 				   SM_FD_SETSIZE); \
2080Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2090Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2100Sstevel@tonic-gate 				  "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
2110Sstevel@tonic-gate 				  m->mf_name, (routine), m->mf_sock, \
2120Sstevel@tonic-gate 				  SM_FD_SETSIZE); \
2130Sstevel@tonic-gate 		milter_error(m, e); \
2140Sstevel@tonic-gate 		return NULL; \
2150Sstevel@tonic-gate 	} \
2160Sstevel@tonic-gate  \
2170Sstevel@tonic-gate 	do \
2180Sstevel@tonic-gate 	{ \
2190Sstevel@tonic-gate 		FD_ZERO(&fds); \
2200Sstevel@tonic-gate 		SM_FD_SET(m->mf_sock, &fds); \
2210Sstevel@tonic-gate 		tv.tv_sec = (secs); \
2220Sstevel@tonic-gate 		tv.tv_usec = 0; \
2230Sstevel@tonic-gate 		ret = select(m->mf_sock + 1, \
2240Sstevel@tonic-gate 			     (write) ? NULL : &fds, \
2250Sstevel@tonic-gate 			     (write) ? &fds : NULL, \
2260Sstevel@tonic-gate 			     NULL, &tv); \
2270Sstevel@tonic-gate 	} while (ret < 0 && errno == EINTR); \
2280Sstevel@tonic-gate  \
2290Sstevel@tonic-gate 	switch (ret) \
2300Sstevel@tonic-gate 	{ \
2310Sstevel@tonic-gate 	  case 0: \
2320Sstevel@tonic-gate 		if (tTd(64, 5)) \
2333544Sjbeck 			sm_dprintf("milter_%s(%s): timeout, where=%s\n", \
2343544Sjbeck 				(routine), m->mf_name, (function)); \
2350Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2360Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2373544Sjbeck 				  "Milter (%s): timeout %s data %s, where=%s", \
2383544Sjbeck 				  m->mf_name, \
2390Sstevel@tonic-gate 				  started ? "during" : "before", \
2403544Sjbeck 				  (routine), (function)); \
2410Sstevel@tonic-gate 		milter_error(m, e); \
2420Sstevel@tonic-gate 		return NULL; \
2430Sstevel@tonic-gate  \
2440Sstevel@tonic-gate 	  case -1: \
2450Sstevel@tonic-gate 		save_errno = errno; \
2460Sstevel@tonic-gate 		if (tTd(64, 5)) \
2470Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
2480Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno)); \
2490Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2500Sstevel@tonic-gate 		{ \
2510Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2520Sstevel@tonic-gate 				  "Milter (%s): select(%s): %s", \
2530Sstevel@tonic-gate 				  m->mf_name, (routine), \
2540Sstevel@tonic-gate 				  sm_errstring(save_errno)); \
2550Sstevel@tonic-gate 		} \
2560Sstevel@tonic-gate 		milter_error(m, e); \
2570Sstevel@tonic-gate 		return NULL; \
2580Sstevel@tonic-gate  \
2590Sstevel@tonic-gate 	  default: \
2600Sstevel@tonic-gate 		if (SM_FD_ISSET(m->mf_sock, &fds)) \
2610Sstevel@tonic-gate 			break; \
2620Sstevel@tonic-gate 		if (tTd(64, 5)) \
2630Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket not ready\n", \
2640Sstevel@tonic-gate 				(routine), m->mf_name); \
2650Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2660Sstevel@tonic-gate 		{ \
2670Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2680Sstevel@tonic-gate 				  "Milter (%s): socket(%s) not ready", \
2690Sstevel@tonic-gate 				  m->mf_name, (routine)); \
2700Sstevel@tonic-gate 		} \
2710Sstevel@tonic-gate 		milter_error(m, e); \
2720Sstevel@tonic-gate 		return NULL; \
2730Sstevel@tonic-gate 	} \
2740Sstevel@tonic-gate }
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate /*
2770Sstevel@tonic-gate **  Low level functions
2780Sstevel@tonic-gate */
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate /*
2810Sstevel@tonic-gate **  MILTER_READ -- read from a remote milter filter
2820Sstevel@tonic-gate **
2830Sstevel@tonic-gate **	Parameters:
2840Sstevel@tonic-gate **		m -- milter to read from.
2850Sstevel@tonic-gate **		cmd -- return param for command read.
2860Sstevel@tonic-gate **		rlen -- return length of response string.
2870Sstevel@tonic-gate **		to -- timeout in seconds.
2880Sstevel@tonic-gate **		e -- current envelope.
2890Sstevel@tonic-gate **
2900Sstevel@tonic-gate **	Returns:
2910Sstevel@tonic-gate **		response string (may be NULL)
2920Sstevel@tonic-gate */
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate static char *
milter_sysread(m,buf,sz,to,e,where)2953544Sjbeck milter_sysread(m, buf, sz, to, e, where)
2960Sstevel@tonic-gate 	struct milter *m;
2970Sstevel@tonic-gate 	char *buf;
2980Sstevel@tonic-gate 	ssize_t sz;
2990Sstevel@tonic-gate 	time_t to;
3000Sstevel@tonic-gate 	ENVELOPE *e;
3013544Sjbeck 	const char *where;
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate 	time_t readstart = 0;
3040Sstevel@tonic-gate 	ssize_t len, curl;
3050Sstevel@tonic-gate 	bool started = false;
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 	curl = 0;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	if (to > 0)
3100Sstevel@tonic-gate 		readstart = curtime();
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	for (;;)
3130Sstevel@tonic-gate 	{
3140Sstevel@tonic-gate 		if (to > 0)
3150Sstevel@tonic-gate 		{
3160Sstevel@tonic-gate 			time_t now;
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 			now = curtime();
3190Sstevel@tonic-gate 			if (now - readstart >= to)
3200Sstevel@tonic-gate 			{
3210Sstevel@tonic-gate 				if (tTd(64, 5))
3223544Sjbeck 					sm_dprintf("milter_sys_read (%s): timeout %s data read in %s",
3233544Sjbeck 						  m->mf_name,
3240Sstevel@tonic-gate 						  started ? "during" : "before",
3253544Sjbeck 						  where);
3260Sstevel@tonic-gate 				if (MilterLogLevel > 0)
3270Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
3283544Sjbeck 						  "Milter (%s): timeout %s data read in %s",
3293544Sjbeck 						  m->mf_name,
3300Sstevel@tonic-gate 						  started ? "during" : "before",
3313544Sjbeck 						  where);
3320Sstevel@tonic-gate 				milter_error(m, e);
3330Sstevel@tonic-gate 				return NULL;
3340Sstevel@tonic-gate 			}
3350Sstevel@tonic-gate 			to -= now - readstart;
3360Sstevel@tonic-gate 			readstart = now;
3373544Sjbeck 			MILTER_TIMEOUT("read", to, false, started, where);
3380Sstevel@tonic-gate 		}
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 		len = read(m->mf_sock, buf + curl, sz - curl);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 		if (len < 0)
3430Sstevel@tonic-gate 		{
3440Sstevel@tonic-gate 			int save_errno = errno;
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 			if (tTd(64, 5))
3473544Sjbeck 				sm_dprintf("milter_sys_read(%s): read returned %ld: %s\n",
3480Sstevel@tonic-gate 					m->mf_name, (long) len,
3490Sstevel@tonic-gate 					sm_errstring(save_errno));
3500Sstevel@tonic-gate 			if (MilterLogLevel > 0)
3510Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
3520Sstevel@tonic-gate 					  "Milter (%s): read returned %ld: %s",
3530Sstevel@tonic-gate 					  m->mf_name, (long) len,
3540Sstevel@tonic-gate 					  sm_errstring(save_errno));
3550Sstevel@tonic-gate 			milter_error(m, e);
3560Sstevel@tonic-gate 			return NULL;
3570Sstevel@tonic-gate 		}
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 		started = true;
3600Sstevel@tonic-gate 		curl += len;
3610Sstevel@tonic-gate 		if (len == 0 || curl >= sz)
3620Sstevel@tonic-gate 			break;
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	}
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	if (curl != sz)
3670Sstevel@tonic-gate 	{
3680Sstevel@tonic-gate 		if (tTd(64, 5))
3693544Sjbeck 			sm_dprintf("milter_sys_read(%s): cmd read returned %ld, expecting %ld\n",
3700Sstevel@tonic-gate 				m->mf_name, (long) curl, (long) sz);
3710Sstevel@tonic-gate 		if (MilterLogLevel > 0)
3720Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
3733544Sjbeck 				  "milter_sys_read(%s): cmd read returned %ld, expecting %ld",
3740Sstevel@tonic-gate 				  m->mf_name, (long) curl, (long) sz);
3750Sstevel@tonic-gate 		milter_error(m, e);
3760Sstevel@tonic-gate 		return NULL;
3770Sstevel@tonic-gate 	}
3780Sstevel@tonic-gate 	return buf;
3790Sstevel@tonic-gate }
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate static char *
milter_read(m,cmd,rlen,to,e,where)3823544Sjbeck milter_read(m, cmd, rlen, to, e, where)
3830Sstevel@tonic-gate 	struct milter *m;
3840Sstevel@tonic-gate 	char *cmd;
3850Sstevel@tonic-gate 	ssize_t *rlen;
3860Sstevel@tonic-gate 	time_t to;
3870Sstevel@tonic-gate 	ENVELOPE *e;
3883544Sjbeck 	const char *where;
3890Sstevel@tonic-gate {
3900Sstevel@tonic-gate 	time_t readstart = 0;
3910Sstevel@tonic-gate 	ssize_t expl;
3920Sstevel@tonic-gate 	mi_int32 i;
3933544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
3940Sstevel@tonic-gate 	int cork = 0;
3953544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
3960Sstevel@tonic-gate 	char *buf;
3970Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 	if (m->mf_sock < 0)
4000Sstevel@tonic-gate 	{
4010Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4020Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
4033544Sjbeck 				  "milter_read(%s): socket closed, where=%s",
4043544Sjbeck 				  m->mf_name, where);
4050Sstevel@tonic-gate 		milter_error(m, e);
4060Sstevel@tonic-gate 		return NULL;
4070Sstevel@tonic-gate 	}
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 	*rlen = 0;
4100Sstevel@tonic-gate 	*cmd = '\0';
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 	if (to > 0)
4130Sstevel@tonic-gate 		readstart = curtime();
4140Sstevel@tonic-gate 
4153544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4160Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4170Sstevel@tonic-gate 		   sizeof(cork));
4183544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
4193544Sjbeck 
4203544Sjbeck 	if (milter_sysread(m, data, sizeof(data), to, e, where) == NULL)
4210Sstevel@tonic-gate 		return NULL;
4220Sstevel@tonic-gate 
4233544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4240Sstevel@tonic-gate 	cork = 1;
4250Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4260Sstevel@tonic-gate 		   sizeof(cork));
4273544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	/* reset timeout */
4300Sstevel@tonic-gate 	if (to > 0)
4310Sstevel@tonic-gate 	{
4320Sstevel@tonic-gate 		time_t now;
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 		now = curtime();
4350Sstevel@tonic-gate 		if (now - readstart >= to)
4360Sstevel@tonic-gate 		{
4370Sstevel@tonic-gate 			if (tTd(64, 5))
4383544Sjbeck 				sm_dprintf("milter_read(%s): timeout before data read, where=%s\n",
4393544Sjbeck 					m->mf_name, where);
4400Sstevel@tonic-gate 			if (MilterLogLevel > 0)
4410Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
4423544Sjbeck 					  "Milter read(%s): timeout before data read, where=%s",
4433544Sjbeck 					  m->mf_name, where);
4440Sstevel@tonic-gate 			milter_error(m, e);
4450Sstevel@tonic-gate 			return NULL;
4460Sstevel@tonic-gate 		}
4470Sstevel@tonic-gate 		to -= now - readstart;
4480Sstevel@tonic-gate 	}
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	*cmd = data[MILTER_LEN_BYTES];
4510Sstevel@tonic-gate 	data[MILTER_LEN_BYTES] = '\0';
4520Sstevel@tonic-gate 	(void) memcpy(&i, data, MILTER_LEN_BYTES);
4530Sstevel@tonic-gate 	expl = ntohl(i) - 1;
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	if (tTd(64, 25))
4560Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): expecting %ld bytes\n",
4570Sstevel@tonic-gate 			m->mf_name, (long) expl);
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	if (expl < 0)
4600Sstevel@tonic-gate 	{
4610Sstevel@tonic-gate 		if (tTd(64, 5))
4623544Sjbeck 			sm_dprintf("milter_read(%s): read size %ld out of range, where=%s\n",
4633544Sjbeck 				m->mf_name, (long) expl, where);
4640Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4650Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
4663544Sjbeck 				  "milter_read(%s): read size %ld out of range, where=%s",
4673544Sjbeck 				  m->mf_name, (long) expl, where);
4680Sstevel@tonic-gate 		milter_error(m, e);
4690Sstevel@tonic-gate 		return NULL;
4700Sstevel@tonic-gate 	}
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 	if (expl == 0)
4730Sstevel@tonic-gate 		return NULL;
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	buf = (char *) xalloc(expl);
4760Sstevel@tonic-gate 
4773544Sjbeck 	if (milter_sysread(m, buf, expl, to, e, where) == NULL)
4780Sstevel@tonic-gate 	{
4790Sstevel@tonic-gate 		sm_free(buf); /* XXX */
4800Sstevel@tonic-gate 		return NULL;
4810Sstevel@tonic-gate 	}
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	if (tTd(64, 50))
4840Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): Returning %*s\n",
4850Sstevel@tonic-gate 			m->mf_name, (int) expl, buf);
4860Sstevel@tonic-gate 	*rlen = expl;
4870Sstevel@tonic-gate 	return buf;
4880Sstevel@tonic-gate }
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate /*
4910Sstevel@tonic-gate **  MILTER_WRITE -- write to a remote milter filter
4920Sstevel@tonic-gate **
4930Sstevel@tonic-gate **	Parameters:
4940Sstevel@tonic-gate **		m -- milter to read from.
4950Sstevel@tonic-gate **		cmd -- command to send.
4960Sstevel@tonic-gate **		buf -- optional command data.
4970Sstevel@tonic-gate **		len -- length of buf.
4980Sstevel@tonic-gate **		to -- timeout in seconds.
4990Sstevel@tonic-gate **		e -- current envelope.
5000Sstevel@tonic-gate **
5010Sstevel@tonic-gate **	Returns:
5020Sstevel@tonic-gate **		buf if successful, NULL otherwise
5030Sstevel@tonic-gate **		Not actually used anywhere but function prototype
5040Sstevel@tonic-gate **			must match milter_read()
5050Sstevel@tonic-gate */
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate static char *
milter_write(m,cmd,buf,len,to,e,where)5083544Sjbeck milter_write(m, cmd, buf, len, to, e, where)
5090Sstevel@tonic-gate 	struct milter *m;
5103544Sjbeck 	int cmd;
5110Sstevel@tonic-gate 	char *buf;
5120Sstevel@tonic-gate 	ssize_t len;
5130Sstevel@tonic-gate 	time_t to;
5140Sstevel@tonic-gate 	ENVELOPE *e;
5153544Sjbeck 	const char *where;
5160Sstevel@tonic-gate {
5170Sstevel@tonic-gate 	ssize_t sl, i;
5180Sstevel@tonic-gate 	int num_vectors;
5190Sstevel@tonic-gate 	mi_int32 nl;
5203544Sjbeck 	char command = (char) cmd;
5210Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
5220Sstevel@tonic-gate 	bool started = false;
5230Sstevel@tonic-gate 	struct iovec vector[2];
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	/*
5260Sstevel@tonic-gate 	**  At most two buffers will be written, though
5270Sstevel@tonic-gate 	**  only one may actually be used (see num_vectors).
5280Sstevel@tonic-gate 	**  The first is the size/command and the second is the command data.
5290Sstevel@tonic-gate 	*/
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	if (len < 0 || len > MilterMaxDataSize)
5320Sstevel@tonic-gate 	{
5330Sstevel@tonic-gate 		if (tTd(64, 5))
534*11440SJohn.Beck@Sun.COM 		{
535*11440SJohn.Beck@Sun.COM 			sm_dprintf("milter_write(%s): length %ld out of range, cmd=%c\n",
536*11440SJohn.Beck@Sun.COM 				m->mf_name, (long) len, command);
537*11440SJohn.Beck@Sun.COM 			sm_dprintf("milter_write(%s): buf=%s\n",
538*11440SJohn.Beck@Sun.COM 				m->mf_name, str2prt(buf));
539*11440SJohn.Beck@Sun.COM 		}
5400Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5410Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
542*11440SJohn.Beck@Sun.COM 				  "milter_write(%s): length %ld out of range, cmd=%c",
543*11440SJohn.Beck@Sun.COM 				  m->mf_name, (long) len, command);
5440Sstevel@tonic-gate 		milter_error(m, e);
5450Sstevel@tonic-gate 		return NULL;
5460Sstevel@tonic-gate 	}
5470Sstevel@tonic-gate 	if (m->mf_sock < 0)
5480Sstevel@tonic-gate 	{
5490Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5500Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
5510Sstevel@tonic-gate 				  "milter_write(%s): socket closed",
5520Sstevel@tonic-gate 				  m->mf_name);
5530Sstevel@tonic-gate 		milter_error(m, e);
5540Sstevel@tonic-gate 		return NULL;
5550Sstevel@tonic-gate 	}
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	if (tTd(64, 20))
5580Sstevel@tonic-gate 		sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
5593544Sjbeck 			   m->mf_name, command, (long) len);
5603544Sjbeck 
5613544Sjbeck 	nl = htonl(len + 1);	/* add 1 for the command char */
5620Sstevel@tonic-gate 	(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
5633544Sjbeck 	data[MILTER_LEN_BYTES] = command;
5640Sstevel@tonic-gate 	sl = MILTER_LEN_BYTES + 1;
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	/* set up the vector for the size / command */
5670Sstevel@tonic-gate 	vector[0].iov_base = (void *) data;
5680Sstevel@tonic-gate 	vector[0].iov_len  = sl;
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 	/*
5710Sstevel@tonic-gate 	**  Determine if there is command data.  If so, there will be two
5720Sstevel@tonic-gate 	**  vectors.  If not, there will be only one.  The vectors are set
5730Sstevel@tonic-gate 	**  up here and 'num_vectors' and 'sl' are set appropriately.
5740Sstevel@tonic-gate 	*/
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 	/* NOTE:  len<0 has already been checked for.  Pedantic */
5770Sstevel@tonic-gate 	if (len <= 0 || buf == NULL)
5780Sstevel@tonic-gate 	{
5790Sstevel@tonic-gate 		/* There is no command data -- only a size / command data */
5800Sstevel@tonic-gate 		num_vectors = 1;
5810Sstevel@tonic-gate 	}
5820Sstevel@tonic-gate 	else
5830Sstevel@tonic-gate 	{
5840Sstevel@tonic-gate 		/*
5850Sstevel@tonic-gate 		**  There is both size / command and command data.
5860Sstevel@tonic-gate 		**  Set up the vector for the command data.
5870Sstevel@tonic-gate 		*/
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 		num_vectors = 2;
5900Sstevel@tonic-gate 		sl += len;
5910Sstevel@tonic-gate 		vector[1].iov_base = (void *) buf;
5920Sstevel@tonic-gate 		vector[1].iov_len  = len;
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 		if (tTd(64, 50))
5950Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): Sending %*s\n",
5960Sstevel@tonic-gate 				   m->mf_name, (int) len, buf);
5970Sstevel@tonic-gate 	}
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 	if (to > 0)
6003544Sjbeck 		MILTER_TIMEOUT("write", to, true, started, where);
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	/* write the vector(s) */
6030Sstevel@tonic-gate 	i = writev(m->mf_sock, vector, num_vectors);
6040Sstevel@tonic-gate 	if (i != sl)
6050Sstevel@tonic-gate 	{
6060Sstevel@tonic-gate 		int save_errno = errno;
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 		if (tTd(64, 5))
6090Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
6103544Sjbeck 				   m->mf_name, command, (long) i, (long) sl,
6110Sstevel@tonic-gate 				   sm_errstring(save_errno));
6120Sstevel@tonic-gate 		if (MilterLogLevel > 0)
6130Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6140Sstevel@tonic-gate 				  "Milter (%s): write(%c) returned %ld, expected %ld: %s",
6153544Sjbeck 				  m->mf_name, command, (long) i, (long) sl,
6160Sstevel@tonic-gate 				  sm_errstring(save_errno));
6170Sstevel@tonic-gate 		milter_error(m, e);
6180Sstevel@tonic-gate 		return NULL;
6190Sstevel@tonic-gate 	}
6200Sstevel@tonic-gate 	return buf;
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate /*
6240Sstevel@tonic-gate **  Utility functions
6250Sstevel@tonic-gate */
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate /*
6280Sstevel@tonic-gate **  MILTER_OPEN -- connect to remote milter filter
6290Sstevel@tonic-gate **
6300Sstevel@tonic-gate **	Parameters:
6310Sstevel@tonic-gate **		m -- milter to connect to.
6320Sstevel@tonic-gate **		parseonly -- parse but don't connect.
6330Sstevel@tonic-gate **		e -- current envelope.
6340Sstevel@tonic-gate **
6350Sstevel@tonic-gate **	Returns:
6360Sstevel@tonic-gate **		connected socket if successful && !parseonly,
6370Sstevel@tonic-gate **		0 upon parse success if parseonly,
6380Sstevel@tonic-gate **		-1 otherwise.
6390Sstevel@tonic-gate */
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate static jmp_buf	MilterConnectTimeout;
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate static int
milter_open(m,parseonly,e)6440Sstevel@tonic-gate milter_open(m, parseonly, e)
6450Sstevel@tonic-gate 	struct milter *m;
6460Sstevel@tonic-gate 	bool parseonly;
6470Sstevel@tonic-gate 	ENVELOPE *e;
6480Sstevel@tonic-gate {
6490Sstevel@tonic-gate 	int sock = 0;
6500Sstevel@tonic-gate 	SOCKADDR_LEN_T addrlen = 0;
6510Sstevel@tonic-gate 	int addrno = 0;
6520Sstevel@tonic-gate 	int save_errno;
6530Sstevel@tonic-gate 	char *p;
6540Sstevel@tonic-gate 	char *colon;
6550Sstevel@tonic-gate 	char *at;
6560Sstevel@tonic-gate 	struct hostent *hp = NULL;
6570Sstevel@tonic-gate 	SOCKADDR addr;
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
6600Sstevel@tonic-gate 	{
6610Sstevel@tonic-gate 		if (tTd(64, 5))
6620Sstevel@tonic-gate 			sm_dprintf("X%s: empty or missing socket information\n",
6630Sstevel@tonic-gate 				   m->mf_name);
6640Sstevel@tonic-gate 		if (parseonly)
6650Sstevel@tonic-gate 			syserr("X%s: empty or missing socket information",
6660Sstevel@tonic-gate 			       m->mf_name);
6670Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
6680Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6690Sstevel@tonic-gate 				  "Milter (%s): empty or missing socket information",
6700Sstevel@tonic-gate 				  m->mf_name);
6710Sstevel@tonic-gate 		milter_error(m, e);
6720Sstevel@tonic-gate 		return -1;
6730Sstevel@tonic-gate 	}
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	/* protocol:filename or protocol:port@host */
6763544Sjbeck 	memset(&addr, '\0', sizeof(addr));
6770Sstevel@tonic-gate 	p = m->mf_conn;
6780Sstevel@tonic-gate 	colon = strchr(p, ':');
6790Sstevel@tonic-gate 	if (colon != NULL)
6800Sstevel@tonic-gate 	{
6810Sstevel@tonic-gate 		*colon = '\0';
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 		if (*p == '\0')
6840Sstevel@tonic-gate 		{
6850Sstevel@tonic-gate # if NETUNIX
6860Sstevel@tonic-gate 			/* default to AF_UNIX */
6870Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
6880Sstevel@tonic-gate # else /* NETUNIX */
6890Sstevel@tonic-gate #  if NETINET
6900Sstevel@tonic-gate 			/* default to AF_INET */
6910Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
6920Sstevel@tonic-gate #  else /* NETINET */
6930Sstevel@tonic-gate #   if NETINET6
6940Sstevel@tonic-gate 			/* default to AF_INET6 */
6950Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
6960Sstevel@tonic-gate #   else /* NETINET6 */
6970Sstevel@tonic-gate 			/* no protocols available */
6980Sstevel@tonic-gate 			if (MilterLogLevel > 0)
6990Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7000Sstevel@tonic-gate 					  "Milter (%s): no valid socket protocols available",
7010Sstevel@tonic-gate 					  m->mf_name);
7020Sstevel@tonic-gate 			milter_error(m, e);
7030Sstevel@tonic-gate 			return -1;
7040Sstevel@tonic-gate #   endif /* NETINET6 */
7050Sstevel@tonic-gate #  endif /* NETINET */
7060Sstevel@tonic-gate # endif /* NETUNIX */
7070Sstevel@tonic-gate 		}
7080Sstevel@tonic-gate # if NETUNIX
7090Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "unix") == 0 ||
7100Sstevel@tonic-gate 			 sm_strcasecmp(p, "local") == 0)
7110Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
7120Sstevel@tonic-gate # endif /* NETUNIX */
7130Sstevel@tonic-gate # if NETINET
7140Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet") == 0)
7150Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
7160Sstevel@tonic-gate # endif /* NETINET */
7170Sstevel@tonic-gate # if NETINET6
7180Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet6") == 0)
7190Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
7200Sstevel@tonic-gate # endif /* NETINET6 */
7210Sstevel@tonic-gate 		else
7220Sstevel@tonic-gate 		{
7230Sstevel@tonic-gate # ifdef EPROTONOSUPPORT
7240Sstevel@tonic-gate 			errno = EPROTONOSUPPORT;
7250Sstevel@tonic-gate # else /* EPROTONOSUPPORT */
7260Sstevel@tonic-gate 			errno = EINVAL;
7270Sstevel@tonic-gate # endif /* EPROTONOSUPPORT */
7280Sstevel@tonic-gate 			if (tTd(64, 5))
7290Sstevel@tonic-gate 				sm_dprintf("X%s: unknown socket type %s\n",
7300Sstevel@tonic-gate 					m->mf_name, p);
7310Sstevel@tonic-gate 			if (parseonly)
7320Sstevel@tonic-gate 				syserr("X%s: unknown socket type %s",
7330Sstevel@tonic-gate 				       m->mf_name, p);
7340Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7350Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7360Sstevel@tonic-gate 					  "Milter (%s): unknown socket type %s",
7370Sstevel@tonic-gate 					  m->mf_name, p);
7380Sstevel@tonic-gate 			milter_error(m, e);
7390Sstevel@tonic-gate 			return -1;
7400Sstevel@tonic-gate 		}
7410Sstevel@tonic-gate 		*colon++ = ':';
7420Sstevel@tonic-gate 	}
7430Sstevel@tonic-gate 	else
7440Sstevel@tonic-gate 	{
7450Sstevel@tonic-gate 		/* default to AF_UNIX */
7460Sstevel@tonic-gate 		addr.sa.sa_family = AF_UNIX;
7470Sstevel@tonic-gate 		colon = p;
7480Sstevel@tonic-gate 	}
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate # if NETUNIX
7510Sstevel@tonic-gate 	if (addr.sa.sa_family == AF_UNIX)
7520Sstevel@tonic-gate 	{
7530Sstevel@tonic-gate 		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 		at = colon;
7563544Sjbeck 		if (strlen(colon) >= sizeof(addr.sunix.sun_path))
7570Sstevel@tonic-gate 		{
7580Sstevel@tonic-gate 			if (tTd(64, 5))
7590Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s too long\n",
7600Sstevel@tonic-gate 					m->mf_name, colon);
7610Sstevel@tonic-gate 			errno = EINVAL;
7620Sstevel@tonic-gate 			if (parseonly)
7630Sstevel@tonic-gate 				syserr("X%s: local socket name %s too long",
7640Sstevel@tonic-gate 				       m->mf_name, colon);
7650Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7660Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7670Sstevel@tonic-gate 					  "Milter (%s): local socket name %s too long",
7680Sstevel@tonic-gate 					  m->mf_name, colon);
7690Sstevel@tonic-gate 			milter_error(m, e);
7700Sstevel@tonic-gate 			return -1;
7710Sstevel@tonic-gate 		}
7720Sstevel@tonic-gate 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7730Sstevel@tonic-gate 				 S_IRUSR|S_IWUSR, NULL);
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 		/* if just parsing .cf file, socket doesn't need to exist */
7760Sstevel@tonic-gate 		if (parseonly && errno == ENOENT)
7770Sstevel@tonic-gate 		{
7780Sstevel@tonic-gate 			if (OpMode == MD_DAEMON ||
7790Sstevel@tonic-gate 			    OpMode == MD_FGDAEMON)
7800Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
7810Sstevel@tonic-gate 						     "WARNING: X%s: local socket name %s missing\n",
7820Sstevel@tonic-gate 						     m->mf_name, colon);
7830Sstevel@tonic-gate 		}
7840Sstevel@tonic-gate 		else if (errno != 0)
7850Sstevel@tonic-gate 		{
7860Sstevel@tonic-gate 			/* if not safe, don't create */
7870Sstevel@tonic-gate 			save_errno = errno;
7880Sstevel@tonic-gate 			if (tTd(64, 5))
7890Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s unsafe\n",
7900Sstevel@tonic-gate 					m->mf_name, colon);
7910Sstevel@tonic-gate 			errno = save_errno;
7920Sstevel@tonic-gate 			if (parseonly)
7930Sstevel@tonic-gate 			{
7940Sstevel@tonic-gate 				if (OpMode == MD_DAEMON ||
7950Sstevel@tonic-gate 				    OpMode == MD_FGDAEMON ||
7960Sstevel@tonic-gate 				    OpMode == MD_SMTP)
7970Sstevel@tonic-gate 					syserr("X%s: local socket name %s unsafe",
7980Sstevel@tonic-gate 					       m->mf_name, colon);
7990Sstevel@tonic-gate 			}
8000Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8010Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8020Sstevel@tonic-gate 					  "Milter (%s): local socket name %s unsafe",
8030Sstevel@tonic-gate 					  m->mf_name, colon);
8040Sstevel@tonic-gate 			milter_error(m, e);
8050Sstevel@tonic-gate 			return -1;
8060Sstevel@tonic-gate 		}
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 		(void) sm_strlcpy(addr.sunix.sun_path, colon,
8093544Sjbeck 			       sizeof(addr.sunix.sun_path));
8103544Sjbeck 		addrlen = sizeof(struct sockaddr_un);
8110Sstevel@tonic-gate 	}
8120Sstevel@tonic-gate 	else
8130Sstevel@tonic-gate # endif /* NETUNIX */
8140Sstevel@tonic-gate # if NETINET || NETINET6
8150Sstevel@tonic-gate 	if (false
8160Sstevel@tonic-gate #  if NETINET
8170Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET
8180Sstevel@tonic-gate #  endif /* NETINET */
8190Sstevel@tonic-gate #  if NETINET6
8200Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET6
8210Sstevel@tonic-gate #  endif /* NETINET6 */
8220Sstevel@tonic-gate 		 )
8230Sstevel@tonic-gate 	{
8240Sstevel@tonic-gate 		unsigned short port;
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 		/* Parse port@host */
8270Sstevel@tonic-gate 		at = strchr(colon, '@');
8280Sstevel@tonic-gate 		if (at == NULL)
8290Sstevel@tonic-gate 		{
8300Sstevel@tonic-gate 			if (tTd(64, 5))
8310Sstevel@tonic-gate 				sm_dprintf("X%s: bad address %s (expected port@host)\n",
8320Sstevel@tonic-gate 					m->mf_name, colon);
8330Sstevel@tonic-gate 			if (parseonly)
8340Sstevel@tonic-gate 				syserr("X%s: bad address %s (expected port@host)",
8350Sstevel@tonic-gate 				       m->mf_name, colon);
8360Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8370Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8380Sstevel@tonic-gate 					  "Milter (%s): bad address %s (expected port@host)",
8390Sstevel@tonic-gate 					  m->mf_name, colon);
8400Sstevel@tonic-gate 			milter_error(m, e);
8410Sstevel@tonic-gate 			return -1;
8420Sstevel@tonic-gate 		}
8430Sstevel@tonic-gate 		*at = '\0';
8440Sstevel@tonic-gate 		if (isascii(*colon) && isdigit(*colon))
8450Sstevel@tonic-gate 			port = htons((unsigned short) atoi(colon));
8460Sstevel@tonic-gate 		else
8470Sstevel@tonic-gate 		{
8480Sstevel@tonic-gate #  ifdef NO_GETSERVBYNAME
8490Sstevel@tonic-gate 			if (tTd(64, 5))
8500Sstevel@tonic-gate 				sm_dprintf("X%s: invalid port number %s\n",
8510Sstevel@tonic-gate 					m->mf_name, colon);
8520Sstevel@tonic-gate 			if (parseonly)
8530Sstevel@tonic-gate 				syserr("X%s: invalid port number %s",
8540Sstevel@tonic-gate 				       m->mf_name, colon);
8550Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8560Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8570Sstevel@tonic-gate 					  "Milter (%s): invalid port number %s",
8580Sstevel@tonic-gate 					  m->mf_name, colon);
8590Sstevel@tonic-gate 			milter_error(m, e);
8600Sstevel@tonic-gate 			return -1;
8610Sstevel@tonic-gate #  else /* NO_GETSERVBYNAME */
8623544Sjbeck 			struct servent *sp;
8630Sstevel@tonic-gate 
8640Sstevel@tonic-gate 			sp = getservbyname(colon, "tcp");
8650Sstevel@tonic-gate 			if (sp == NULL)
8660Sstevel@tonic-gate 			{
8670Sstevel@tonic-gate 				save_errno = errno;
8680Sstevel@tonic-gate 				if (tTd(64, 5))
8690Sstevel@tonic-gate 					sm_dprintf("X%s: unknown port name %s\n",
8700Sstevel@tonic-gate 						m->mf_name, colon);
8710Sstevel@tonic-gate 				errno = save_errno;
8720Sstevel@tonic-gate 				if (parseonly)
8730Sstevel@tonic-gate 					syserr("X%s: unknown port name %s",
8740Sstevel@tonic-gate 					       m->mf_name, colon);
8750Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
8760Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
8770Sstevel@tonic-gate 						  "Milter (%s): unknown port name %s",
8780Sstevel@tonic-gate 						  m->mf_name, colon);
8790Sstevel@tonic-gate 				milter_error(m, e);
8800Sstevel@tonic-gate 				return -1;
8810Sstevel@tonic-gate 			}
8820Sstevel@tonic-gate 			port = sp->s_port;
8830Sstevel@tonic-gate #  endif /* NO_GETSERVBYNAME */
8840Sstevel@tonic-gate 		}
8850Sstevel@tonic-gate 		*at++ = '@';
8860Sstevel@tonic-gate 		if (*at == '[')
8870Sstevel@tonic-gate 		{
8880Sstevel@tonic-gate 			char *end;
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 			end = strchr(at, ']');
8910Sstevel@tonic-gate 			if (end != NULL)
8920Sstevel@tonic-gate 			{
8930Sstevel@tonic-gate 				bool found = false;
8940Sstevel@tonic-gate #  if NETINET
8950Sstevel@tonic-gate 				unsigned long hid = INADDR_NONE;
8960Sstevel@tonic-gate #  endif /* NETINET */
8970Sstevel@tonic-gate #  if NETINET6
8980Sstevel@tonic-gate 				struct sockaddr_in6 hid6;
8990Sstevel@tonic-gate #  endif /* NETINET6 */
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate 				*end = '\0';
9020Sstevel@tonic-gate #  if NETINET
9030Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET &&
9040Sstevel@tonic-gate 				    (hid = inet_addr(&at[1])) != INADDR_NONE)
9050Sstevel@tonic-gate 				{
9060Sstevel@tonic-gate 					addr.sin.sin_addr.s_addr = hid;
9070Sstevel@tonic-gate 					addr.sin.sin_port = port;
9080Sstevel@tonic-gate 					found = true;
9090Sstevel@tonic-gate 				}
9100Sstevel@tonic-gate #  endif /* NETINET */
9110Sstevel@tonic-gate #  if NETINET6
9123544Sjbeck 				(void) memset(&hid6, '\0', sizeof(hid6));
9130Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET6 &&
9140Sstevel@tonic-gate 				    anynet_pton(AF_INET6, &at[1],
9150Sstevel@tonic-gate 						&hid6.sin6_addr) == 1)
9160Sstevel@tonic-gate 				{
9170Sstevel@tonic-gate 					addr.sin6.sin6_addr = hid6.sin6_addr;
9180Sstevel@tonic-gate 					addr.sin6.sin6_port = port;
9190Sstevel@tonic-gate 					found = true;
9200Sstevel@tonic-gate 				}
9210Sstevel@tonic-gate #  endif /* NETINET6 */
9220Sstevel@tonic-gate 				*end = ']';
9230Sstevel@tonic-gate 				if (!found)
9240Sstevel@tonic-gate 				{
9250Sstevel@tonic-gate 					if (tTd(64, 5))
9260Sstevel@tonic-gate 						sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9270Sstevel@tonic-gate 							m->mf_name, at);
9280Sstevel@tonic-gate 					if (parseonly)
9290Sstevel@tonic-gate 						syserr("X%s: Invalid numeric domain spec \"%s\"",
9300Sstevel@tonic-gate 						       m->mf_name, at);
9310Sstevel@tonic-gate 					else if (MilterLogLevel > 0)
9320Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
9330Sstevel@tonic-gate 							  "Milter (%s): Invalid numeric domain spec \"%s\"",
9340Sstevel@tonic-gate 							  m->mf_name, at);
9350Sstevel@tonic-gate 					milter_error(m, e);
9360Sstevel@tonic-gate 					return -1;
9370Sstevel@tonic-gate 				}
9380Sstevel@tonic-gate 			}
9390Sstevel@tonic-gate 			else
9400Sstevel@tonic-gate 			{
9410Sstevel@tonic-gate 				if (tTd(64, 5))
9420Sstevel@tonic-gate 					sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9430Sstevel@tonic-gate 						m->mf_name, at);
9440Sstevel@tonic-gate 				if (parseonly)
9450Sstevel@tonic-gate 					syserr("X%s: Invalid numeric domain spec \"%s\"",
9460Sstevel@tonic-gate 					       m->mf_name, at);
9470Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9480Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9490Sstevel@tonic-gate 						  "Milter (%s): Invalid numeric domain spec \"%s\"",
9500Sstevel@tonic-gate 						  m->mf_name, at);
9510Sstevel@tonic-gate 				milter_error(m, e);
9520Sstevel@tonic-gate 				return -1;
9530Sstevel@tonic-gate 			}
9540Sstevel@tonic-gate 		}
9550Sstevel@tonic-gate 		else
9560Sstevel@tonic-gate 		{
9570Sstevel@tonic-gate 			hp = sm_gethostbyname(at, addr.sa.sa_family);
9580Sstevel@tonic-gate 			if (hp == NULL)
9590Sstevel@tonic-gate 			{
9600Sstevel@tonic-gate 				save_errno = errno;
9610Sstevel@tonic-gate 				if (tTd(64, 5))
9620Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown host name %s\n",
9630Sstevel@tonic-gate 						   m->mf_name, at);
9640Sstevel@tonic-gate 				errno = save_errno;
9650Sstevel@tonic-gate 				if (parseonly)
9660Sstevel@tonic-gate 					syserr("X%s: Unknown host name %s",
9670Sstevel@tonic-gate 					       m->mf_name, at);
9680Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9690Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9700Sstevel@tonic-gate 						  "Milter (%s): Unknown host name %s",
9710Sstevel@tonic-gate 						  m->mf_name, at);
9720Sstevel@tonic-gate 				milter_error(m, e);
9730Sstevel@tonic-gate 				return -1;
9740Sstevel@tonic-gate 			}
9750Sstevel@tonic-gate 			addr.sa.sa_family = hp->h_addrtype;
9760Sstevel@tonic-gate 			switch (hp->h_addrtype)
9770Sstevel@tonic-gate 			{
9780Sstevel@tonic-gate #  if NETINET
9790Sstevel@tonic-gate 			  case AF_INET:
9800Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
9810Sstevel@tonic-gate 					hp->h_addr, INADDRSZ);
9820Sstevel@tonic-gate 				addr.sin.sin_port = port;
9833544Sjbeck 				addrlen = sizeof(struct sockaddr_in);
9840Sstevel@tonic-gate 				addrno = 1;
9850Sstevel@tonic-gate 				break;
9860Sstevel@tonic-gate #  endif /* NETINET */
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate #  if NETINET6
9890Sstevel@tonic-gate 			  case AF_INET6:
9900Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
9910Sstevel@tonic-gate 					hp->h_addr, IN6ADDRSZ);
9920Sstevel@tonic-gate 				addr.sin6.sin6_port = port;
9933544Sjbeck 				addrlen = sizeof(struct sockaddr_in6);
9940Sstevel@tonic-gate 				addrno = 1;
9950Sstevel@tonic-gate 				break;
9960Sstevel@tonic-gate #  endif /* NETINET6 */
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 			  default:
9990Sstevel@tonic-gate 				if (tTd(64, 5))
10000Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
10010Sstevel@tonic-gate 						   m->mf_name, at,
10020Sstevel@tonic-gate 						   hp->h_addrtype);
10030Sstevel@tonic-gate 				if (parseonly)
10040Sstevel@tonic-gate 					syserr("X%s: Unknown protocol for %s (%d)",
10050Sstevel@tonic-gate 					       m->mf_name, at, hp->h_addrtype);
10060Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
10070Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
10080Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
10090Sstevel@tonic-gate 						  m->mf_name, at,
10100Sstevel@tonic-gate 						  hp->h_addrtype);
10110Sstevel@tonic-gate 				milter_error(m, e);
10120Sstevel@tonic-gate #  if NETINET6
10130Sstevel@tonic-gate 				freehostent(hp);
10140Sstevel@tonic-gate #  endif /* NETINET6 */
10150Sstevel@tonic-gate 				return -1;
10160Sstevel@tonic-gate 			}
10170Sstevel@tonic-gate 		}
10180Sstevel@tonic-gate 	}
10190Sstevel@tonic-gate 	else
10200Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
10210Sstevel@tonic-gate 	{
10220Sstevel@tonic-gate 		if (tTd(64, 5))
10230Sstevel@tonic-gate 			sm_dprintf("X%s: unknown socket protocol\n",
10240Sstevel@tonic-gate 				   m->mf_name);
10250Sstevel@tonic-gate 		if (parseonly)
10260Sstevel@tonic-gate 			syserr("X%s: unknown socket protocol", m->mf_name);
10270Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
10280Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
10290Sstevel@tonic-gate 				  "Milter (%s): unknown socket protocol",
10300Sstevel@tonic-gate 				  m->mf_name);
10310Sstevel@tonic-gate 		milter_error(m, e);
10320Sstevel@tonic-gate 		return -1;
10330Sstevel@tonic-gate 	}
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate 	/* just parsing through? */
10360Sstevel@tonic-gate 	if (parseonly)
10370Sstevel@tonic-gate 	{
10380Sstevel@tonic-gate 		m->mf_state = SMFS_READY;
10390Sstevel@tonic-gate # if NETINET6
10400Sstevel@tonic-gate 		if (hp != NULL)
10410Sstevel@tonic-gate 			freehostent(hp);
10420Sstevel@tonic-gate # endif /* NETINET6 */
10430Sstevel@tonic-gate 		return 0;
10440Sstevel@tonic-gate 	}
10450Sstevel@tonic-gate 
10460Sstevel@tonic-gate 	/* sanity check */
10470Sstevel@tonic-gate 	if (m->mf_state != SMFS_READY &&
10480Sstevel@tonic-gate 	    m->mf_state != SMFS_CLOSED)
10490Sstevel@tonic-gate 	{
10500Sstevel@tonic-gate 		/* shouldn't happen */
10510Sstevel@tonic-gate 		if (tTd(64, 1))
10520Sstevel@tonic-gate 			sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
10530Sstevel@tonic-gate 				   m->mf_name, (char) m->mf_state);
10540Sstevel@tonic-gate 		milter_error(m, e);
10550Sstevel@tonic-gate # if NETINET6
10560Sstevel@tonic-gate 		if (hp != NULL)
10570Sstevel@tonic-gate 			freehostent(hp);
10580Sstevel@tonic-gate # endif /* NETINET6 */
10590Sstevel@tonic-gate 		return -1;
10600Sstevel@tonic-gate 	}
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	/* nope, actually connecting */
10630Sstevel@tonic-gate 	for (;;)
10640Sstevel@tonic-gate 	{
10650Sstevel@tonic-gate 		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
10660Sstevel@tonic-gate 		if (sock < 0)
10670Sstevel@tonic-gate 		{
10680Sstevel@tonic-gate 			save_errno = errno;
10690Sstevel@tonic-gate 			if (tTd(64, 5))
10700Sstevel@tonic-gate 				sm_dprintf("Milter (%s): error creating socket: %s\n",
10710Sstevel@tonic-gate 					   m->mf_name,
10720Sstevel@tonic-gate 					   sm_errstring(save_errno));
10730Sstevel@tonic-gate 			if (MilterLogLevel > 0)
10740Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
10750Sstevel@tonic-gate 					  "Milter (%s): error creating socket: %s",
10760Sstevel@tonic-gate 					  m->mf_name, sm_errstring(save_errno));
10770Sstevel@tonic-gate 			milter_error(m, e);
10780Sstevel@tonic-gate # if NETINET6
10790Sstevel@tonic-gate 			if (hp != NULL)
10800Sstevel@tonic-gate 				freehostent(hp);
10810Sstevel@tonic-gate # endif /* NETINET6 */
10820Sstevel@tonic-gate 			return -1;
10830Sstevel@tonic-gate 		}
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 		if (setjmp(MilterConnectTimeout) == 0)
10860Sstevel@tonic-gate 		{
10870Sstevel@tonic-gate 			SM_EVENT *ev = NULL;
10880Sstevel@tonic-gate 			int i;
10890Sstevel@tonic-gate 
10900Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_CONNECT] > 0)
10910Sstevel@tonic-gate 				ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
10920Sstevel@tonic-gate 						 milter_connect_timeout, 0);
10930Sstevel@tonic-gate 
10940Sstevel@tonic-gate 			i = connect(sock, (struct sockaddr *) &addr, addrlen);
10950Sstevel@tonic-gate 			save_errno = errno;
10960Sstevel@tonic-gate 			if (ev != NULL)
10970Sstevel@tonic-gate 				sm_clrevent(ev);
10980Sstevel@tonic-gate 			errno = save_errno;
10990Sstevel@tonic-gate 			if (i >= 0)
11000Sstevel@tonic-gate 				break;
11010Sstevel@tonic-gate 		}
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 		/* couldn't connect.... try next address */
11040Sstevel@tonic-gate 		save_errno = errno;
11050Sstevel@tonic-gate 		p = CurHostName;
11060Sstevel@tonic-gate 		CurHostName = at;
11070Sstevel@tonic-gate 		if (tTd(64, 5))
11080Sstevel@tonic-gate 			sm_dprintf("milter_open (%s): open %s failed: %s\n",
11090Sstevel@tonic-gate 				   m->mf_name, at, sm_errstring(save_errno));
11100Sstevel@tonic-gate 		if (MilterLogLevel > 13)
11110Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
11120Sstevel@tonic-gate 				  "Milter (%s): open %s failed: %s",
11130Sstevel@tonic-gate 				  m->mf_name, at, sm_errstring(save_errno));
11140Sstevel@tonic-gate 		CurHostName = p;
11150Sstevel@tonic-gate 		(void) close(sock);
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 		/* try next address */
11180Sstevel@tonic-gate 		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
11190Sstevel@tonic-gate 		{
11200Sstevel@tonic-gate 			switch (addr.sa.sa_family)
11210Sstevel@tonic-gate 			{
11220Sstevel@tonic-gate # if NETINET
11230Sstevel@tonic-gate 			  case AF_INET:
11240Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
11250Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11260Sstevel@tonic-gate 					INADDRSZ);
11270Sstevel@tonic-gate 				break;
11280Sstevel@tonic-gate # endif /* NETINET */
11290Sstevel@tonic-gate 
11300Sstevel@tonic-gate # if NETINET6
11310Sstevel@tonic-gate 			  case AF_INET6:
11320Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
11330Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11340Sstevel@tonic-gate 					IN6ADDRSZ);
11350Sstevel@tonic-gate 				break;
11360Sstevel@tonic-gate # endif /* NETINET6 */
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 			  default:
11390Sstevel@tonic-gate 				if (tTd(64, 5))
11400Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
11410Sstevel@tonic-gate 						   m->mf_name, at,
11420Sstevel@tonic-gate 						   hp->h_addrtype);
11430Sstevel@tonic-gate 				if (MilterLogLevel > 0)
11440Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
11450Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
11460Sstevel@tonic-gate 						  m->mf_name, at,
11470Sstevel@tonic-gate 						  hp->h_addrtype);
11480Sstevel@tonic-gate 				milter_error(m, e);
11490Sstevel@tonic-gate # if NETINET6
11500Sstevel@tonic-gate 				freehostent(hp);
11510Sstevel@tonic-gate # endif /* NETINET6 */
11520Sstevel@tonic-gate 				return -1;
11530Sstevel@tonic-gate 			}
11540Sstevel@tonic-gate 			continue;
11550Sstevel@tonic-gate 		}
11560Sstevel@tonic-gate 		p = CurHostName;
11570Sstevel@tonic-gate 		CurHostName = at;
11580Sstevel@tonic-gate 		if (tTd(64, 5))
11590Sstevel@tonic-gate 			sm_dprintf("X%s: error connecting to filter: %s\n",
11600Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno));
11610Sstevel@tonic-gate 		if (MilterLogLevel > 0)
11620Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
11630Sstevel@tonic-gate 				  "Milter (%s): error connecting to filter: %s",
11640Sstevel@tonic-gate 				  m->mf_name, sm_errstring(save_errno));
11650Sstevel@tonic-gate 		CurHostName = p;
11660Sstevel@tonic-gate 		milter_error(m, e);
11670Sstevel@tonic-gate # if NETINET6
11680Sstevel@tonic-gate 		if (hp != NULL)
11690Sstevel@tonic-gate 			freehostent(hp);
11700Sstevel@tonic-gate # endif /* NETINET6 */
11710Sstevel@tonic-gate 		return -1;
11720Sstevel@tonic-gate 	}
11730Sstevel@tonic-gate 	m->mf_state = SMFS_OPEN;
11740Sstevel@tonic-gate # if NETINET6
11750Sstevel@tonic-gate 	if (hp != NULL)
11760Sstevel@tonic-gate 	{
11770Sstevel@tonic-gate 		freehostent(hp);
11780Sstevel@tonic-gate 		hp = NULL;
11790Sstevel@tonic-gate 	}
11800Sstevel@tonic-gate # endif /* NETINET6 */
11813544Sjbeck # if MILTER_NO_NAGLE && !defined(TCP_CORK)
11820Sstevel@tonic-gate 	{
11830Sstevel@tonic-gate 		int nodelay = 1;
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate 		setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY,
11860Sstevel@tonic-gate 			   (char *)&nodelay, sizeof(nodelay));
11870Sstevel@tonic-gate 	}
11883544Sjbeck # endif /* MILTER_NO_NAGLE && !defined(TCP_CORK) */
11890Sstevel@tonic-gate 	return sock;
11900Sstevel@tonic-gate }
11910Sstevel@tonic-gate 
11920Sstevel@tonic-gate static void
milter_connect_timeout(ignore)11930Sstevel@tonic-gate milter_connect_timeout(ignore)
11940Sstevel@tonic-gate 	int ignore;
11950Sstevel@tonic-gate {
11960Sstevel@tonic-gate 	/*
11970Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
11980Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
11990Sstevel@tonic-gate 	**	DOING.
12000Sstevel@tonic-gate 	*/
12010Sstevel@tonic-gate 
12020Sstevel@tonic-gate 	errno = ETIMEDOUT;
12030Sstevel@tonic-gate 	longjmp(MilterConnectTimeout, 1);
12040Sstevel@tonic-gate }
12053544Sjbeck 
12060Sstevel@tonic-gate /*
12070Sstevel@tonic-gate **  MILTER_SETUP -- setup structure for a mail filter
12080Sstevel@tonic-gate **
12090Sstevel@tonic-gate **	Parameters:
12100Sstevel@tonic-gate **		line -- the options line.
12110Sstevel@tonic-gate **
12120Sstevel@tonic-gate **	Returns:
12130Sstevel@tonic-gate **		none
12140Sstevel@tonic-gate */
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate void
milter_setup(line)12170Sstevel@tonic-gate milter_setup(line)
12180Sstevel@tonic-gate 	char *line;
12190Sstevel@tonic-gate {
12200Sstevel@tonic-gate 	char fcode;
12213544Sjbeck 	char *p;
12223544Sjbeck 	struct milter *m;
12230Sstevel@tonic-gate 	STAB *s;
12240Sstevel@tonic-gate 
12250Sstevel@tonic-gate 	/* collect the filter name */
12260Sstevel@tonic-gate 	for (p = line;
12270Sstevel@tonic-gate 	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
12280Sstevel@tonic-gate 	     p++)
12290Sstevel@tonic-gate 		continue;
12300Sstevel@tonic-gate 	if (*p != '\0')
12310Sstevel@tonic-gate 		*p++ = '\0';
12320Sstevel@tonic-gate 	if (line[0] == '\0')
12330Sstevel@tonic-gate 	{
12340Sstevel@tonic-gate 		syserr("name required for mail filter");
12350Sstevel@tonic-gate 		return;
12360Sstevel@tonic-gate 	}
12373544Sjbeck 	m = (struct milter *) xalloc(sizeof(*m));
12383544Sjbeck 	memset((char *) m, '\0', sizeof(*m));
12390Sstevel@tonic-gate 	m->mf_name = newstr(line);
12400Sstevel@tonic-gate 	m->mf_state = SMFS_READY;
12410Sstevel@tonic-gate 	m->mf_sock = -1;
12420Sstevel@tonic-gate 	m->mf_timeout[SMFTO_CONNECT] = (time_t) 300;
12430Sstevel@tonic-gate 	m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
12440Sstevel@tonic-gate 	m->mf_timeout[SMFTO_READ] = (time_t) 10;
12450Sstevel@tonic-gate 	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
12463966Sjbeck #if _FFR_MILTER_CHECK
12473544Sjbeck 	m->mf_mta_prot_version = SMFI_PROT_VERSION;
12483544Sjbeck 	m->mf_mta_prot_flags = SMFI_CURR_PROT;
12493544Sjbeck 	m->mf_mta_actions = SMFI_CURR_ACTS;
12503966Sjbeck #endif /* _FFR_MILTER_CHECK */
12510Sstevel@tonic-gate 
12520Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
12530Sstevel@tonic-gate 	while (*p != '\0')
12540Sstevel@tonic-gate 	{
12550Sstevel@tonic-gate 		char *delimptr;
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 		while (*p != '\0' &&
12580Sstevel@tonic-gate 		       (*p == ',' || (isascii(*p) && isspace(*p))))
12590Sstevel@tonic-gate 			p++;
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 		/* p now points to field code */
12620Sstevel@tonic-gate 		fcode = *p;
12630Sstevel@tonic-gate 		while (*p != '\0' && *p != '=' && *p != ',')
12640Sstevel@tonic-gate 			p++;
12650Sstevel@tonic-gate 		if (*p++ != '=')
12660Sstevel@tonic-gate 		{
12670Sstevel@tonic-gate 			syserr("X%s: `=' expected", m->mf_name);
12680Sstevel@tonic-gate 			return;
12690Sstevel@tonic-gate 		}
12700Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
12710Sstevel@tonic-gate 			p++;
12720Sstevel@tonic-gate 
12730Sstevel@tonic-gate 		/* p now points to the field body */
12740Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ',');
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 		/* install the field into the filter struct */
12770Sstevel@tonic-gate 		switch (fcode)
12780Sstevel@tonic-gate 		{
12790Sstevel@tonic-gate 		  case 'S':		/* socket */
12800Sstevel@tonic-gate 			if (p == NULL)
12810Sstevel@tonic-gate 				m->mf_conn = NULL;
12820Sstevel@tonic-gate 			else
12830Sstevel@tonic-gate 				m->mf_conn = newstr(p);
12840Sstevel@tonic-gate 			break;
12850Sstevel@tonic-gate 
12860Sstevel@tonic-gate 		  case 'F':		/* Milter flags configured on MTA */
12870Sstevel@tonic-gate 			for (; *p != '\0'; p++)
12880Sstevel@tonic-gate 			{
12890Sstevel@tonic-gate 				if (!(isascii(*p) && isspace(*p)))
12900Sstevel@tonic-gate 					setbitn(bitidx(*p), m->mf_flags);
12910Sstevel@tonic-gate 			}
12920Sstevel@tonic-gate 			break;
12930Sstevel@tonic-gate 
12940Sstevel@tonic-gate 		  case 'T':		/* timeouts */
12950Sstevel@tonic-gate 			milter_parse_timeouts(p, m);
12960Sstevel@tonic-gate 			break;
12970Sstevel@tonic-gate 
12983966Sjbeck #if _FFR_MILTER_CHECK
12993544Sjbeck 		  case 'a':
13003544Sjbeck 			m->mf_mta_actions = strtoul(p, NULL, 0);
13013544Sjbeck 			break;
13023544Sjbeck 		  case 'f':
13033544Sjbeck 			m->mf_mta_prot_flags = strtoul(p, NULL, 0);
13043544Sjbeck 			break;
13053544Sjbeck 		  case 'v':
13063544Sjbeck 			m->mf_mta_prot_version = strtoul(p, NULL, 0);
13073544Sjbeck 			break;
13083966Sjbeck #endif /* _FFR_MILTER_CHECK */
13093544Sjbeck 
13100Sstevel@tonic-gate 		  default:
13110Sstevel@tonic-gate 			syserr("X%s: unknown filter equate %c=",
13120Sstevel@tonic-gate 			       m->mf_name, fcode);
13130Sstevel@tonic-gate 			break;
13140Sstevel@tonic-gate 		}
13150Sstevel@tonic-gate 		p = delimptr;
13160Sstevel@tonic-gate 	}
13170Sstevel@tonic-gate 
13180Sstevel@tonic-gate 	/* early check for errors */
13190Sstevel@tonic-gate 	(void) milter_open(m, true, CurEnv);
13200Sstevel@tonic-gate 
13210Sstevel@tonic-gate 	/* enter the filter into the symbol table */
13220Sstevel@tonic-gate 	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
13230Sstevel@tonic-gate 	if (s->s_milter != NULL)
13240Sstevel@tonic-gate 		syserr("X%s: duplicate filter definition", m->mf_name);
13250Sstevel@tonic-gate 	else
13260Sstevel@tonic-gate 		s->s_milter = m;
13270Sstevel@tonic-gate }
13283544Sjbeck 
13290Sstevel@tonic-gate /*
13300Sstevel@tonic-gate **  MILTER_CONFIG -- parse option list into an array and check config
13310Sstevel@tonic-gate **
13320Sstevel@tonic-gate **	Called when reading configuration file.
13330Sstevel@tonic-gate **
13340Sstevel@tonic-gate **	Parameters:
13350Sstevel@tonic-gate **		spec -- the filter list.
13360Sstevel@tonic-gate **		list -- the array to fill in.
13370Sstevel@tonic-gate **		max -- the maximum number of entries in list.
13380Sstevel@tonic-gate **
13390Sstevel@tonic-gate **	Returns:
13400Sstevel@tonic-gate **		none
13410Sstevel@tonic-gate */
13420Sstevel@tonic-gate 
13430Sstevel@tonic-gate void
milter_config(spec,list,max)13440Sstevel@tonic-gate milter_config(spec, list, max)
13450Sstevel@tonic-gate 	char *spec;
13460Sstevel@tonic-gate 	struct milter **list;
13470Sstevel@tonic-gate 	int max;
13480Sstevel@tonic-gate {
13490Sstevel@tonic-gate 	int numitems = 0;
13503544Sjbeck 	char *p;
13510Sstevel@tonic-gate 
13520Sstevel@tonic-gate 	/* leave one for the NULL signifying the end of the list */
13530Sstevel@tonic-gate 	max--;
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 	for (p = spec; p != NULL; )
13560Sstevel@tonic-gate 	{
13570Sstevel@tonic-gate 		STAB *s;
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
13600Sstevel@tonic-gate 			p++;
13610Sstevel@tonic-gate 		if (*p == '\0')
13620Sstevel@tonic-gate 			break;
13630Sstevel@tonic-gate 		spec = p;
13640Sstevel@tonic-gate 
13650Sstevel@tonic-gate 		if (numitems >= max)
13660Sstevel@tonic-gate 		{
13670Sstevel@tonic-gate 			syserr("Too many filters defined, %d max", max);
13680Sstevel@tonic-gate 			if (max > 0)
13690Sstevel@tonic-gate 				list[0] = NULL;
13700Sstevel@tonic-gate 			return;
13710Sstevel@tonic-gate 		}
13720Sstevel@tonic-gate 		p = strpbrk(p, ";,");
13730Sstevel@tonic-gate 		if (p != NULL)
13740Sstevel@tonic-gate 			*p++ = '\0';
13750Sstevel@tonic-gate 
13760Sstevel@tonic-gate 		s = stab(spec, ST_MILTER, ST_FIND);
13770Sstevel@tonic-gate 		if (s == NULL)
13780Sstevel@tonic-gate 		{
13790Sstevel@tonic-gate 			syserr("InputFilter %s not defined", spec);
13800Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
13810Sstevel@tonic-gate 			return;
13820Sstevel@tonic-gate 		}
13830Sstevel@tonic-gate 		list[numitems++] = s->s_milter;
13840Sstevel@tonic-gate 	}
13850Sstevel@tonic-gate 	list[numitems] = NULL;
13860Sstevel@tonic-gate 
13870Sstevel@tonic-gate 	/* if not set, set to LogLevel */
13880Sstevel@tonic-gate 	if (MilterLogLevel == -1)
13890Sstevel@tonic-gate 		MilterLogLevel = LogLevel;
13900Sstevel@tonic-gate }
13913544Sjbeck 
13920Sstevel@tonic-gate /*
13930Sstevel@tonic-gate **  MILTER_PARSE_TIMEOUTS -- parse timeout list
13940Sstevel@tonic-gate **
13950Sstevel@tonic-gate **	Called when reading configuration file.
13960Sstevel@tonic-gate **
13970Sstevel@tonic-gate **	Parameters:
13980Sstevel@tonic-gate **		spec -- the timeout list.
13990Sstevel@tonic-gate **		m -- milter to set.
14000Sstevel@tonic-gate **
14010Sstevel@tonic-gate **	Returns:
14020Sstevel@tonic-gate **		none
14030Sstevel@tonic-gate */
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate static void
milter_parse_timeouts(spec,m)14060Sstevel@tonic-gate milter_parse_timeouts(spec, m)
14070Sstevel@tonic-gate 	char *spec;
14080Sstevel@tonic-gate 	struct milter *m;
14090Sstevel@tonic-gate {
14100Sstevel@tonic-gate 	char fcode;
14110Sstevel@tonic-gate 	int tcode;
14123544Sjbeck 	char *p;
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate 	p = spec;
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
14170Sstevel@tonic-gate 	while (*p != '\0')
14180Sstevel@tonic-gate 	{
14190Sstevel@tonic-gate 		char *delimptr;
14200Sstevel@tonic-gate 
14210Sstevel@tonic-gate 		while (*p != '\0' &&
14220Sstevel@tonic-gate 		       (*p == ';' || (isascii(*p) && isspace(*p))))
14230Sstevel@tonic-gate 			p++;
14240Sstevel@tonic-gate 
14250Sstevel@tonic-gate 		/* p now points to field code */
14260Sstevel@tonic-gate 		fcode = *p;
14270Sstevel@tonic-gate 		while (*p != '\0' && *p != ':')
14280Sstevel@tonic-gate 			p++;
14290Sstevel@tonic-gate 		if (*p++ != ':')
14300Sstevel@tonic-gate 		{
14310Sstevel@tonic-gate 			syserr("X%s, T=: `:' expected", m->mf_name);
14320Sstevel@tonic-gate 			return;
14330Sstevel@tonic-gate 		}
14340Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
14350Sstevel@tonic-gate 			p++;
14360Sstevel@tonic-gate 
14370Sstevel@tonic-gate 		/* p now points to the field body */
14380Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ';');
14390Sstevel@tonic-gate 		tcode = -1;
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 		/* install the field into the filter struct */
14420Sstevel@tonic-gate 		switch (fcode)
14430Sstevel@tonic-gate 		{
14440Sstevel@tonic-gate 		  case 'C':
14450Sstevel@tonic-gate 			tcode = SMFTO_CONNECT;
14460Sstevel@tonic-gate 			break;
14470Sstevel@tonic-gate 
14480Sstevel@tonic-gate 		  case 'S':
14490Sstevel@tonic-gate 			tcode = SMFTO_WRITE;
14500Sstevel@tonic-gate 			break;
14510Sstevel@tonic-gate 
14520Sstevel@tonic-gate 		  case 'R':
14530Sstevel@tonic-gate 			tcode = SMFTO_READ;
14540Sstevel@tonic-gate 			break;
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate 		  case 'E':
14570Sstevel@tonic-gate 			tcode = SMFTO_EOM;
14580Sstevel@tonic-gate 			break;
14590Sstevel@tonic-gate 
14600Sstevel@tonic-gate 		  default:
14610Sstevel@tonic-gate 			if (tTd(64, 5))
14620Sstevel@tonic-gate 				sm_dprintf("X%s: %c unknown\n",
14630Sstevel@tonic-gate 					   m->mf_name, fcode);
14640Sstevel@tonic-gate 			syserr("X%s: unknown filter timeout %c",
14650Sstevel@tonic-gate 			       m->mf_name, fcode);
14660Sstevel@tonic-gate 			break;
14670Sstevel@tonic-gate 		}
14680Sstevel@tonic-gate 		if (tcode >= 0)
14690Sstevel@tonic-gate 		{
14700Sstevel@tonic-gate 			m->mf_timeout[tcode] = convtime(p, 's');
14710Sstevel@tonic-gate 			if (tTd(64, 5))
14720Sstevel@tonic-gate 				sm_dprintf("X%s: %c=%ld\n",
14730Sstevel@tonic-gate 					   m->mf_name, fcode,
14740Sstevel@tonic-gate 					   (u_long) m->mf_timeout[tcode]);
14750Sstevel@tonic-gate 		}
14760Sstevel@tonic-gate 		p = delimptr;
14770Sstevel@tonic-gate 	}
14780Sstevel@tonic-gate }
14793544Sjbeck 
14803544Sjbeck /*
14813544Sjbeck **  MILTER_SET_MACROS -- set milter macros
14823544Sjbeck **
14833544Sjbeck **	Parameters:
14843544Sjbeck **		name -- name of milter.
14853544Sjbeck **		macros -- where to store macros.
14863544Sjbeck **		val -- the value of the option.
14873544Sjbeck **		nummac -- current number of macros
14883544Sjbeck **
14893544Sjbeck **	Returns:
14903544Sjbeck **		new number of macros
14913544Sjbeck */
14923544Sjbeck 
14933544Sjbeck static int
milter_set_macros(name,macros,val,nummac)14943544Sjbeck milter_set_macros(name, macros, val, nummac)
14953544Sjbeck 	char *name;
14963544Sjbeck 	char **macros;
14973544Sjbeck 	char *val;
14983544Sjbeck 	int nummac;
14993544Sjbeck {
15003544Sjbeck 	char *p;
15013544Sjbeck 
15023544Sjbeck 	p = newstr(val);
15033544Sjbeck 	while (*p != '\0')
15043544Sjbeck 	{
15053544Sjbeck 		char *macro;
15063544Sjbeck 
15073544Sjbeck 		/* Skip leading commas, spaces */
15083544Sjbeck 		while (*p != '\0' &&
15093544Sjbeck 		       (*p == ',' || (isascii(*p) && isspace(*p))))
15103544Sjbeck 			p++;
15113544Sjbeck 
15123544Sjbeck 		if (*p == '\0')
15133544Sjbeck 			break;
15143544Sjbeck 
15153544Sjbeck 		/* Find end of macro */
15163544Sjbeck 		macro = p;
15173544Sjbeck 		while (*p != '\0' && *p != ',' &&
15183544Sjbeck 		       isascii(*p) && !isspace(*p))
15193544Sjbeck 			p++;
15203544Sjbeck 		if (*p != '\0')
15213544Sjbeck 			*p++ = '\0';
15223544Sjbeck 
15233544Sjbeck 		if (nummac >= MAXFILTERMACROS)
15243544Sjbeck 		{
15253544Sjbeck 			syserr("milter_set_option: too many macros in Milter.%s (max %d)",
15263544Sjbeck 			       name, MAXFILTERMACROS);
15273544Sjbeck 			macros[nummac] = NULL;
15283544Sjbeck 			return -1;
15293544Sjbeck 		}
15303544Sjbeck 		macros[nummac++] = macro;
15313544Sjbeck 	}
15323544Sjbeck 	macros[nummac] = NULL;
15333544Sjbeck 	return nummac;
15343544Sjbeck }
15353544Sjbeck 
15360Sstevel@tonic-gate /*
15370Sstevel@tonic-gate **  MILTER_SET_OPTION -- set an individual milter option
15380Sstevel@tonic-gate **
15390Sstevel@tonic-gate **	Parameters:
15400Sstevel@tonic-gate **		name -- the name of the option.
15410Sstevel@tonic-gate **		val -- the value of the option.
15420Sstevel@tonic-gate **		sticky -- if set, don't let other setoptions override
15430Sstevel@tonic-gate **			this value.
15440Sstevel@tonic-gate **
15450Sstevel@tonic-gate **	Returns:
15460Sstevel@tonic-gate **		none.
15470Sstevel@tonic-gate */
15480Sstevel@tonic-gate 
15490Sstevel@tonic-gate /* set if Milter sub-option is stuck */
15500Sstevel@tonic-gate static BITMAP256	StickyMilterOpt;
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate static struct milteropt
15530Sstevel@tonic-gate {
15540Sstevel@tonic-gate 	char		*mo_name;	/* long name of milter option */
15550Sstevel@tonic-gate 	unsigned char	mo_code;	/* code for option */
15560Sstevel@tonic-gate } MilterOptTab[] =
15570Sstevel@tonic-gate {
15583544Sjbeck # define MO_MACROS_CONNECT		SMFIM_CONNECT
15590Sstevel@tonic-gate 	{ "macros.connect",		MO_MACROS_CONNECT		},
15603544Sjbeck # define MO_MACROS_HELO			SMFIM_HELO
15610Sstevel@tonic-gate 	{ "macros.helo",		MO_MACROS_HELO			},
15623544Sjbeck # define MO_MACROS_ENVFROM		SMFIM_ENVFROM
15630Sstevel@tonic-gate 	{ "macros.envfrom",		MO_MACROS_ENVFROM		},
15643544Sjbeck # define MO_MACROS_ENVRCPT		SMFIM_ENVRCPT
15650Sstevel@tonic-gate 	{ "macros.envrcpt",		MO_MACROS_ENVRCPT		},
15663544Sjbeck # define MO_MACROS_DATA			SMFIM_DATA
15670Sstevel@tonic-gate 	{ "macros.data",		MO_MACROS_DATA			},
15683544Sjbeck # define MO_MACROS_EOM			SMFIM_EOM
15690Sstevel@tonic-gate 	{ "macros.eom",			MO_MACROS_EOM			},
15703544Sjbeck # define MO_MACROS_EOH			SMFIM_EOH
15713544Sjbeck 	{ "macros.eoh",			MO_MACROS_EOH			},
15723544Sjbeck 
15730Sstevel@tonic-gate # define MO_LOGLEVEL			0x07
15740Sstevel@tonic-gate 	{ "loglevel",			MO_LOGLEVEL			},
1575*11440SJohn.Beck@Sun.COM # if _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE
15763544Sjbeck #  define MO_MAXDATASIZE		0x08
15770Sstevel@tonic-gate 	{ "maxdatasize",		MO_MAXDATASIZE			},
1578*11440SJohn.Beck@Sun.COM # endif /* _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE */
15793544Sjbeck 	{ NULL,				(unsigned char)-1		},
15800Sstevel@tonic-gate };
15810Sstevel@tonic-gate 
15820Sstevel@tonic-gate void
milter_set_option(name,val,sticky)15830Sstevel@tonic-gate milter_set_option(name, val, sticky)
15840Sstevel@tonic-gate 	char *name;
15850Sstevel@tonic-gate 	char *val;
15860Sstevel@tonic-gate 	bool sticky;
15870Sstevel@tonic-gate {
15883544Sjbeck 	int nummac, r;
15893544Sjbeck 	struct milteropt *mo;
15900Sstevel@tonic-gate 	char **macros = NULL;
15910Sstevel@tonic-gate 
15923544Sjbeck 	nummac = 0;
15930Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64, 5))
15940Sstevel@tonic-gate 		sm_dprintf("milter_set_option(%s = %s)", name, val);
15950Sstevel@tonic-gate 
15960Sstevel@tonic-gate 	if (name == NULL)
15970Sstevel@tonic-gate 	{
15980Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option, must specify suboption");
15990Sstevel@tonic-gate 		return;
16000Sstevel@tonic-gate 	}
16010Sstevel@tonic-gate 
16020Sstevel@tonic-gate 	for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
16030Sstevel@tonic-gate 	{
16040Sstevel@tonic-gate 		if (sm_strcasecmp(mo->mo_name, name) == 0)
16050Sstevel@tonic-gate 			break;
16060Sstevel@tonic-gate 	}
16070Sstevel@tonic-gate 
16080Sstevel@tonic-gate 	if (mo->mo_name == NULL)
16090Sstevel@tonic-gate 	{
16100Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
16110Sstevel@tonic-gate 		return;
16120Sstevel@tonic-gate 	}
16130Sstevel@tonic-gate 
16140Sstevel@tonic-gate 	/*
16150Sstevel@tonic-gate 	**  See if this option is preset for us.
16160Sstevel@tonic-gate 	*/
16170Sstevel@tonic-gate 
16180Sstevel@tonic-gate 	if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
16190Sstevel@tonic-gate 	{
16200Sstevel@tonic-gate 		if (tTd(37, 2) || tTd(64,5))
16210Sstevel@tonic-gate 			sm_dprintf(" (ignored)\n");
16220Sstevel@tonic-gate 		return;
16230Sstevel@tonic-gate 	}
16240Sstevel@tonic-gate 
16250Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64,5))
16260Sstevel@tonic-gate 		sm_dprintf("\n");
16270Sstevel@tonic-gate 
16280Sstevel@tonic-gate 	switch (mo->mo_code)
16290Sstevel@tonic-gate 	{
16300Sstevel@tonic-gate 	  case MO_LOGLEVEL:
16310Sstevel@tonic-gate 		MilterLogLevel = atoi(val);
16320Sstevel@tonic-gate 		break;
16330Sstevel@tonic-gate 
1634*11440SJohn.Beck@Sun.COM # if _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE
16350Sstevel@tonic-gate 	  case MO_MAXDATASIZE:
1636*11440SJohn.Beck@Sun.COM #  if _FFR_MDS_NEGOTIATE
16370Sstevel@tonic-gate 		MilterMaxDataSize = (size_t)atol(val);
1638*11440SJohn.Beck@Sun.COM 		if (MilterMaxDataSize != MILTER_MDS_64K &&
1639*11440SJohn.Beck@Sun.COM 		    MilterMaxDataSize != MILTER_MDS_256K &&
1640*11440SJohn.Beck@Sun.COM 		    MilterMaxDataSize != MILTER_MDS_1M)
1641*11440SJohn.Beck@Sun.COM 		{
1642*11440SJohn.Beck@Sun.COM 			sm_syslog(LOG_WARNING, NOQID,
1643*11440SJohn.Beck@Sun.COM 				"WARNING: Milter.%s=%d, allowed are only %d, %d, and %d",
1644*11440SJohn.Beck@Sun.COM 				name, MilterMaxDataSize,
1645*11440SJohn.Beck@Sun.COM 				MILTER_MDS_64K, MILTER_MDS_256K,
1646*11440SJohn.Beck@Sun.COM 				MILTER_MDS_1M);
1647*11440SJohn.Beck@Sun.COM 			if (MilterMaxDataSize < MILTER_MDS_64K)
1648*11440SJohn.Beck@Sun.COM 				MilterMaxDataSize = MILTER_MDS_64K;
1649*11440SJohn.Beck@Sun.COM 			else if (MilterMaxDataSize < MILTER_MDS_256K)
1650*11440SJohn.Beck@Sun.COM 				MilterMaxDataSize = MILTER_MDS_256K;
1651*11440SJohn.Beck@Sun.COM 			else
1652*11440SJohn.Beck@Sun.COM 				MilterMaxDataSize = MILTER_MDS_1M;
1653*11440SJohn.Beck@Sun.COM 		}
1654*11440SJohn.Beck@Sun.COM #  endif /* _FFR_MDS_NEGOTIATE */
16550Sstevel@tonic-gate 		break;
1656*11440SJohn.Beck@Sun.COM # endif /* _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE */
16570Sstevel@tonic-gate 
16580Sstevel@tonic-gate 	  case MO_MACROS_CONNECT:
16590Sstevel@tonic-gate 		if (macros == NULL)
16600Sstevel@tonic-gate 			macros = MilterConnectMacros;
16610Sstevel@tonic-gate 		/* FALLTHROUGH */
16620Sstevel@tonic-gate 
16630Sstevel@tonic-gate 	  case MO_MACROS_HELO:
16640Sstevel@tonic-gate 		if (macros == NULL)
16650Sstevel@tonic-gate 			macros = MilterHeloMacros;
16660Sstevel@tonic-gate 		/* FALLTHROUGH */
16670Sstevel@tonic-gate 
16680Sstevel@tonic-gate 	  case MO_MACROS_ENVFROM:
16690Sstevel@tonic-gate 		if (macros == NULL)
16700Sstevel@tonic-gate 			macros = MilterEnvFromMacros;
16710Sstevel@tonic-gate 		/* FALLTHROUGH */
16720Sstevel@tonic-gate 
16730Sstevel@tonic-gate 	  case MO_MACROS_ENVRCPT:
16740Sstevel@tonic-gate 		if (macros == NULL)
16750Sstevel@tonic-gate 			macros = MilterEnvRcptMacros;
16760Sstevel@tonic-gate 		/* FALLTHROUGH */
16770Sstevel@tonic-gate 
16783544Sjbeck 	  case MO_MACROS_EOH:
16793544Sjbeck 		if (macros == NULL)
16803544Sjbeck 			macros = MilterEOHMacros;
16813544Sjbeck 		/* FALLTHROUGH */
16823544Sjbeck 
16830Sstevel@tonic-gate 	  case MO_MACROS_EOM:
16840Sstevel@tonic-gate 		if (macros == NULL)
16850Sstevel@tonic-gate 			macros = MilterEOMMacros;
16860Sstevel@tonic-gate 		/* FALLTHROUGH */
16870Sstevel@tonic-gate 
16880Sstevel@tonic-gate 	  case MO_MACROS_DATA:
16890Sstevel@tonic-gate 		if (macros == NULL)
16900Sstevel@tonic-gate 			macros = MilterDataMacros;
16910Sstevel@tonic-gate 
16923544Sjbeck 		r = milter_set_macros(name, macros, val, nummac);
16933544Sjbeck 		if (r >= 0)
16943544Sjbeck 			nummac = r;
16950Sstevel@tonic-gate 		break;
16960Sstevel@tonic-gate 
16970Sstevel@tonic-gate 	  default:
16980Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
16990Sstevel@tonic-gate 		break;
17000Sstevel@tonic-gate 	}
17010Sstevel@tonic-gate 	if (sticky)
17020Sstevel@tonic-gate 		setbitn(mo->mo_code, StickyMilterOpt);
17030Sstevel@tonic-gate }
17043544Sjbeck 
17050Sstevel@tonic-gate /*
17060Sstevel@tonic-gate **  MILTER_REOPEN_DF -- open & truncate the data file (for replbody)
17070Sstevel@tonic-gate **
17080Sstevel@tonic-gate **	Parameters:
17090Sstevel@tonic-gate **		e -- current envelope.
17100Sstevel@tonic-gate **
17110Sstevel@tonic-gate **	Returns:
17120Sstevel@tonic-gate **		0 if succesful, -1 otherwise
17130Sstevel@tonic-gate */
17140Sstevel@tonic-gate 
17150Sstevel@tonic-gate static int
milter_reopen_df(e)17160Sstevel@tonic-gate milter_reopen_df(e)
17170Sstevel@tonic-gate 	ENVELOPE *e;
17180Sstevel@tonic-gate {
17190Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
17200Sstevel@tonic-gate 
17213544Sjbeck 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
17220Sstevel@tonic-gate 
17230Sstevel@tonic-gate 	/*
17240Sstevel@tonic-gate 	**  In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so
17250Sstevel@tonic-gate 	**  close and reopen writable (later close and reopen
17260Sstevel@tonic-gate 	**  read only again).
17270Sstevel@tonic-gate 	**
17280Sstevel@tonic-gate 	**  In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the
17290Sstevel@tonic-gate 	**  buffered file I/O descriptor, still open for writing so there
17300Sstevel@tonic-gate 	**  isn't any work to do here (except checking for consistency).
17310Sstevel@tonic-gate 	*/
17320Sstevel@tonic-gate 
17330Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY)
17340Sstevel@tonic-gate 	{
17350Sstevel@tonic-gate 		/* close read-only data file */
17360Sstevel@tonic-gate 		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
17370Sstevel@tonic-gate 		{
17380Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
17390Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
17400Sstevel@tonic-gate 		}
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 		/* open writable */
17430Sstevel@tonic-gate 		if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
17440Sstevel@tonic-gate 					   SM_IO_RDWR_B, NULL)) == NULL)
17450Sstevel@tonic-gate 		{
17460Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s");
17470Sstevel@tonic-gate 			return -1;
17480Sstevel@tonic-gate 		}
17490Sstevel@tonic-gate 	}
17500Sstevel@tonic-gate 	else if (e->e_dfp == NULL)
17510Sstevel@tonic-gate 	{
17520Sstevel@tonic-gate 		/* shouldn't happen */
17530Sstevel@tonic-gate 		errno = ENOENT;
17540Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
17550Sstevel@tonic-gate 		return -1;
17560Sstevel@tonic-gate 	}
17570Sstevel@tonic-gate 	return 0;
17580Sstevel@tonic-gate }
17593544Sjbeck 
17600Sstevel@tonic-gate /*
17610Sstevel@tonic-gate **  MILTER_RESET_DF -- re-open read-only the data file (for replbody)
17620Sstevel@tonic-gate **
17630Sstevel@tonic-gate **	Parameters:
17640Sstevel@tonic-gate **		e -- current envelope.
17650Sstevel@tonic-gate **
17660Sstevel@tonic-gate **	Returns:
17670Sstevel@tonic-gate **		0 if succesful, -1 otherwise
17680Sstevel@tonic-gate */
17690Sstevel@tonic-gate 
17700Sstevel@tonic-gate static int
milter_reset_df(e)17710Sstevel@tonic-gate milter_reset_df(e)
17720Sstevel@tonic-gate 	ENVELOPE *e;
17730Sstevel@tonic-gate {
17740Sstevel@tonic-gate 	int afd;
17750Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
17760Sstevel@tonic-gate 
17773544Sjbeck 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
17780Sstevel@tonic-gate 
17790Sstevel@tonic-gate 	if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 ||
17800Sstevel@tonic-gate 	    sm_io_error(e->e_dfp))
17810Sstevel@tonic-gate 	{
17820Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
17830Sstevel@tonic-gate 		return -1;
17840Sstevel@tonic-gate 	}
17850Sstevel@tonic-gate 	else if (SuperSafe != SAFE_REALLY)
17860Sstevel@tonic-gate 	{
17870Sstevel@tonic-gate 		/* skip next few clauses */
17880Sstevel@tonic-gate 		/* EMPTY */
17890Sstevel@tonic-gate 	}
17900Sstevel@tonic-gate 	else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0
17910Sstevel@tonic-gate 		 && fsync(afd) < 0)
17920Sstevel@tonic-gate 	{
17930Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
17940Sstevel@tonic-gate 		return -1;
17950Sstevel@tonic-gate 	}
17960Sstevel@tonic-gate 	else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0)
17970Sstevel@tonic-gate 	{
17980Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
17990Sstevel@tonic-gate 		return -1;
18000Sstevel@tonic-gate 	}
18010Sstevel@tonic-gate 	else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
18020Sstevel@tonic-gate 					SM_IO_RDONLY_B, NULL)) == NULL)
18030Sstevel@tonic-gate 	{
18040Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
18050Sstevel@tonic-gate 		return -1;
18060Sstevel@tonic-gate 	}
18070Sstevel@tonic-gate 	else
18080Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
18090Sstevel@tonic-gate 	return 0;
18100Sstevel@tonic-gate }
18113544Sjbeck 
18120Sstevel@tonic-gate /*
18130Sstevel@tonic-gate **  MILTER_QUIT_FILTER -- close down a single filter
18140Sstevel@tonic-gate **
18150Sstevel@tonic-gate **	Parameters:
18160Sstevel@tonic-gate **		m -- milter structure of filter to close down.
18170Sstevel@tonic-gate **		e -- current envelope.
18180Sstevel@tonic-gate **
18190Sstevel@tonic-gate **	Returns:
18200Sstevel@tonic-gate **		none
18210Sstevel@tonic-gate */
18220Sstevel@tonic-gate 
18230Sstevel@tonic-gate static void
milter_quit_filter(m,e)18240Sstevel@tonic-gate milter_quit_filter(m, e)
18250Sstevel@tonic-gate 	struct milter *m;
18260Sstevel@tonic-gate 	ENVELOPE *e;
18270Sstevel@tonic-gate {
18280Sstevel@tonic-gate 	if (tTd(64, 10))
18290Sstevel@tonic-gate 		sm_dprintf("milter_quit_filter(%s)\n", m->mf_name);
18300Sstevel@tonic-gate 	if (MilterLogLevel > 18)
18310Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter",
18320Sstevel@tonic-gate 			  m->mf_name);
18330Sstevel@tonic-gate 
18340Sstevel@tonic-gate 	/* Never replace error state */
18350Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
18360Sstevel@tonic-gate 		return;
18370Sstevel@tonic-gate 
18380Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
18390Sstevel@tonic-gate 	    m->mf_state == SMFS_CLOSED ||
18400Sstevel@tonic-gate 	    m->mf_state == SMFS_READY)
18410Sstevel@tonic-gate 	{
18420Sstevel@tonic-gate 		m->mf_sock = -1;
18430Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
18440Sstevel@tonic-gate 		return;
18450Sstevel@tonic-gate 	}
18460Sstevel@tonic-gate 
18470Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
18483544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "quit_filter");
18490Sstevel@tonic-gate 	if (m->mf_sock >= 0)
18500Sstevel@tonic-gate 	{
18510Sstevel@tonic-gate 		(void) close(m->mf_sock);
18520Sstevel@tonic-gate 		m->mf_sock = -1;
18530Sstevel@tonic-gate 	}
18540Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
18550Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
18560Sstevel@tonic-gate }
18573544Sjbeck 
18580Sstevel@tonic-gate /*
18590Sstevel@tonic-gate **  MILTER_ABORT_FILTER -- tell filter to abort current message
18600Sstevel@tonic-gate **
18610Sstevel@tonic-gate **	Parameters:
18620Sstevel@tonic-gate **		m -- milter structure of filter to abort.
18630Sstevel@tonic-gate **		e -- current envelope.
18640Sstevel@tonic-gate **
18650Sstevel@tonic-gate **	Returns:
18660Sstevel@tonic-gate **		none
18670Sstevel@tonic-gate */
18680Sstevel@tonic-gate 
18690Sstevel@tonic-gate static void
milter_abort_filter(m,e)18700Sstevel@tonic-gate milter_abort_filter(m, e)
18710Sstevel@tonic-gate 	struct milter *m;
18720Sstevel@tonic-gate 	ENVELOPE *e;
18730Sstevel@tonic-gate {
18740Sstevel@tonic-gate 	if (tTd(64, 10))
18750Sstevel@tonic-gate 		sm_dprintf("milter_abort_filter(%s)\n", m->mf_name);
18760Sstevel@tonic-gate 	if (MilterLogLevel > 10)
18770Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter",
18780Sstevel@tonic-gate 			  m->mf_name);
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
18810Sstevel@tonic-gate 	    m->mf_state != SMFS_INMSG)
18820Sstevel@tonic-gate 		return;
18830Sstevel@tonic-gate 
18840Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
18853544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "abort_filter");
18860Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
18870Sstevel@tonic-gate 		m->mf_state = SMFS_DONE;
18880Sstevel@tonic-gate }
18893544Sjbeck 
18900Sstevel@tonic-gate /*
18910Sstevel@tonic-gate **  MILTER_SEND_MACROS -- provide macros to the filters
18920Sstevel@tonic-gate **
18930Sstevel@tonic-gate **	Parameters:
18940Sstevel@tonic-gate **		m -- milter to send macros to.
18950Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
18960Sstevel@tonic-gate **		cmd -- which command the macros are associated with.
18970Sstevel@tonic-gate **		e -- current envelope (for macro access).
18980Sstevel@tonic-gate **
18990Sstevel@tonic-gate **	Returns:
19000Sstevel@tonic-gate **		none
19010Sstevel@tonic-gate */
19020Sstevel@tonic-gate 
19030Sstevel@tonic-gate static void
milter_send_macros(m,macros,cmd,e)19040Sstevel@tonic-gate milter_send_macros(m, macros, cmd, e)
19050Sstevel@tonic-gate 	struct milter *m;
19060Sstevel@tonic-gate 	char **macros;
19073544Sjbeck 	int cmd;
19080Sstevel@tonic-gate 	ENVELOPE *e;
19090Sstevel@tonic-gate {
19100Sstevel@tonic-gate 	int i;
19110Sstevel@tonic-gate 	int mid;
19123544Sjbeck 	char command = (char) cmd;
19130Sstevel@tonic-gate 	char *v;
19140Sstevel@tonic-gate 	char *buf, *bp;
19150Sstevel@tonic-gate 	char exp[MAXLINE];
19160Sstevel@tonic-gate 	ssize_t s;
19170Sstevel@tonic-gate 
19180Sstevel@tonic-gate 	/* sanity check */
19190Sstevel@tonic-gate 	if (macros == NULL || macros[0] == NULL)
19200Sstevel@tonic-gate 		return;
19210Sstevel@tonic-gate 
19220Sstevel@tonic-gate 	/* put together data */
19230Sstevel@tonic-gate 	s = 1;			/* for the command character */
19240Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
19250Sstevel@tonic-gate 	{
19260Sstevel@tonic-gate 		mid = macid(macros[i]);
19270Sstevel@tonic-gate 		if (mid == 0)
19280Sstevel@tonic-gate 			continue;
19290Sstevel@tonic-gate 		v = macvalue(mid, e);
19300Sstevel@tonic-gate 		if (v == NULL)
19310Sstevel@tonic-gate 			continue;
19320Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
19330Sstevel@tonic-gate 		s += strlen(macros[i]) + 1 + strlen(exp) + 1;
19340Sstevel@tonic-gate 	}
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 	if (s < 0)
19370Sstevel@tonic-gate 		return;
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate 	buf = (char *) xalloc(s);
19400Sstevel@tonic-gate 	bp = buf;
19413544Sjbeck 	*bp++ = command;
19420Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
19430Sstevel@tonic-gate 	{
19440Sstevel@tonic-gate 		mid = macid(macros[i]);
19450Sstevel@tonic-gate 		if (mid == 0)
19460Sstevel@tonic-gate 			continue;
19470Sstevel@tonic-gate 		v = macvalue(mid, e);
19480Sstevel@tonic-gate 		if (v == NULL)
19490Sstevel@tonic-gate 			continue;
19500Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
19510Sstevel@tonic-gate 
19520Sstevel@tonic-gate 		if (tTd(64, 10))
19530Sstevel@tonic-gate 			sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
19543544Sjbeck 				m->mf_name, command, macros[i], exp);
19550Sstevel@tonic-gate 
19560Sstevel@tonic-gate 		(void) sm_strlcpy(bp, macros[i], s - (bp - buf));
19570Sstevel@tonic-gate 		bp += strlen(bp) + 1;
19580Sstevel@tonic-gate 		(void) sm_strlcpy(bp, exp, s - (bp - buf));
19590Sstevel@tonic-gate 		bp += strlen(bp) + 1;
19600Sstevel@tonic-gate 	}
19610Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_MACRO, buf, s,
19623544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "send_macros");
19630Sstevel@tonic-gate 	sm_free(buf);
19640Sstevel@tonic-gate }
19650Sstevel@tonic-gate 
19660Sstevel@tonic-gate /*
19670Sstevel@tonic-gate **  MILTER_SEND_COMMAND -- send a command and return the response for a filter
19680Sstevel@tonic-gate **
19690Sstevel@tonic-gate **	Parameters:
19700Sstevel@tonic-gate **		m -- current milter filter
19713544Sjbeck **		cmd -- command to send.
19720Sstevel@tonic-gate **		data -- optional command data.
19730Sstevel@tonic-gate **		sz -- length of buf.
19740Sstevel@tonic-gate **		e -- current envelope (for e->e_id).
19750Sstevel@tonic-gate **		state -- return state word.
19760Sstevel@tonic-gate **
19770Sstevel@tonic-gate **	Returns:
19780Sstevel@tonic-gate **		response string (may be NULL)
19790Sstevel@tonic-gate */
19800Sstevel@tonic-gate 
19810Sstevel@tonic-gate static char *
milter_send_command(m,cmd,data,sz,e,state,where)19823544Sjbeck milter_send_command(m, cmd, data, sz, e, state, where)
19830Sstevel@tonic-gate 	struct milter *m;
19843544Sjbeck 	int cmd;
19850Sstevel@tonic-gate 	void *data;
19860Sstevel@tonic-gate 	ssize_t sz;
19870Sstevel@tonic-gate 	ENVELOPE *e;
19880Sstevel@tonic-gate 	char *state;
19893544Sjbeck 	const char *where;
19900Sstevel@tonic-gate {
19910Sstevel@tonic-gate 	char rcmd;
19920Sstevel@tonic-gate 	ssize_t rlen;
19930Sstevel@tonic-gate 	unsigned long skipflag;
19940Sstevel@tonic-gate 	unsigned long norespflag = 0;
19953544Sjbeck 	char command = (char) cmd;
19960Sstevel@tonic-gate 	char *action;
19970Sstevel@tonic-gate 	char *defresponse;
19980Sstevel@tonic-gate 	char *response;
19990Sstevel@tonic-gate 
20000Sstevel@tonic-gate 	if (tTd(64, 10))
20010Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): cmd %c len %ld\n",
20020Sstevel@tonic-gate 			m->mf_name, (char) command, (long) sz);
20030Sstevel@tonic-gate 
20040Sstevel@tonic-gate 	/* find skip flag and default failure */
20050Sstevel@tonic-gate 	switch (command)
20060Sstevel@tonic-gate 	{
20070Sstevel@tonic-gate 	  case SMFIC_CONNECT:
20080Sstevel@tonic-gate 		skipflag = SMFIP_NOCONNECT;
20093544Sjbeck 		norespflag = SMFIP_NR_CONN;
20100Sstevel@tonic-gate 		action = "connect";
20110Sstevel@tonic-gate 		defresponse = "554 Command rejected";
20120Sstevel@tonic-gate 		break;
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate 	  case SMFIC_HELO:
20150Sstevel@tonic-gate 		skipflag = SMFIP_NOHELO;
20163544Sjbeck 		norespflag = SMFIP_NR_HELO;
20170Sstevel@tonic-gate 		action = "helo";
20180Sstevel@tonic-gate 		defresponse = "550 Command rejected";
20190Sstevel@tonic-gate 		break;
20200Sstevel@tonic-gate 
20210Sstevel@tonic-gate 	  case SMFIC_MAIL:
20220Sstevel@tonic-gate 		skipflag = SMFIP_NOMAIL;
20233544Sjbeck 		norespflag = SMFIP_NR_MAIL;
20240Sstevel@tonic-gate 		action = "mail";
20250Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20260Sstevel@tonic-gate 		break;
20270Sstevel@tonic-gate 
20280Sstevel@tonic-gate 	  case SMFIC_RCPT:
20290Sstevel@tonic-gate 		skipflag = SMFIP_NORCPT;
20303544Sjbeck 		norespflag = SMFIP_NR_RCPT;
20310Sstevel@tonic-gate 		action = "rcpt";
20320Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20330Sstevel@tonic-gate 		break;
20340Sstevel@tonic-gate 
20350Sstevel@tonic-gate 	  case SMFIC_HEADER:
20360Sstevel@tonic-gate 		skipflag = SMFIP_NOHDRS;
20373544Sjbeck 		norespflag = SMFIP_NR_HDR;
20380Sstevel@tonic-gate 		action = "header";
20390Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20400Sstevel@tonic-gate 		break;
20410Sstevel@tonic-gate 
20420Sstevel@tonic-gate 	  case SMFIC_BODY:
20430Sstevel@tonic-gate 		skipflag = SMFIP_NOBODY;
20443544Sjbeck 		norespflag = SMFIP_NR_BODY;
20450Sstevel@tonic-gate 		action = "body";
20460Sstevel@tonic-gate 		defresponse = "554 5.7.1 Command rejected";
20470Sstevel@tonic-gate 		break;
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate 	  case SMFIC_EOH:
20500Sstevel@tonic-gate 		skipflag = SMFIP_NOEOH;
20513544Sjbeck 		norespflag = SMFIP_NR_EOH;
20520Sstevel@tonic-gate 		action = "eoh";
20530Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20540Sstevel@tonic-gate 		break;
20550Sstevel@tonic-gate 
20560Sstevel@tonic-gate 	  case SMFIC_UNKNOWN:
2057616Sjbeck 		skipflag = SMFIP_NOUNKNOWN;
20583544Sjbeck 		norespflag = SMFIP_NR_UNKN;
20590Sstevel@tonic-gate 		action = "unknown";
20600Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20610Sstevel@tonic-gate 		break;
20623544Sjbeck 
2063616Sjbeck 	  case SMFIC_DATA:
2064616Sjbeck 		skipflag = SMFIP_NODATA;
20653544Sjbeck 		norespflag = SMFIP_NR_DATA;
2066616Sjbeck 		action = "data";
2067616Sjbeck 		defresponse = "550 5.7.1 Command rejected";
2068616Sjbeck 		break;
2069616Sjbeck 
20700Sstevel@tonic-gate 	  case SMFIC_BODYEOB:
20710Sstevel@tonic-gate 	  case SMFIC_OPTNEG:
20720Sstevel@tonic-gate 	  case SMFIC_MACRO:
20730Sstevel@tonic-gate 	  case SMFIC_ABORT:
20740Sstevel@tonic-gate 	  case SMFIC_QUIT:
20750Sstevel@tonic-gate 		/* NOTE: not handled by milter_send_command() */
20760Sstevel@tonic-gate 		/* FALLTHROUGH */
20770Sstevel@tonic-gate 
20780Sstevel@tonic-gate 	  default:
20790Sstevel@tonic-gate 		skipflag = 0;
20800Sstevel@tonic-gate 		action = "default";
20810Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20820Sstevel@tonic-gate 		break;
20830Sstevel@tonic-gate 	}
20840Sstevel@tonic-gate 
20853544Sjbeck 	if (tTd(64, 10))
20863544Sjbeck 		sm_dprintf("milter_send_command(%s): skip=%lx, pflags=%x\n",
20873544Sjbeck 			m->mf_name, skipflag, m->mf_pflags);
20883544Sjbeck 
20890Sstevel@tonic-gate 	/* check if filter wants this command */
20903544Sjbeck 	if (skipflag != 0 && bitset(skipflag, m->mf_pflags))
20910Sstevel@tonic-gate 		return NULL;
20920Sstevel@tonic-gate 
20930Sstevel@tonic-gate 	/* send the command to the filter */
20940Sstevel@tonic-gate 	(void) milter_write(m, command, data, sz,
20953544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, where);
20960Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
20970Sstevel@tonic-gate 	{
20980Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
20990Sstevel@tonic-gate 		return NULL;
21000Sstevel@tonic-gate 	}
21010Sstevel@tonic-gate 
21020Sstevel@tonic-gate 	/* check if filter sends response to this command */
21030Sstevel@tonic-gate 	if (norespflag != 0 && bitset(norespflag, m->mf_pflags))
21040Sstevel@tonic-gate 		return NULL;
21050Sstevel@tonic-gate 
21060Sstevel@tonic-gate 	/* get the response from the filter */
21070Sstevel@tonic-gate 	response = milter_read(m, &rcmd, &rlen,
21083544Sjbeck 			       m->mf_timeout[SMFTO_READ], e, where);
21090Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
21100Sstevel@tonic-gate 	{
21110Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
21120Sstevel@tonic-gate 		return NULL;
21130Sstevel@tonic-gate 	}
21140Sstevel@tonic-gate 
21150Sstevel@tonic-gate 	if (tTd(64, 10))
21160Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): returned %c\n",
21170Sstevel@tonic-gate 			   m->mf_name, (char) rcmd);
21180Sstevel@tonic-gate 
21190Sstevel@tonic-gate 	switch (rcmd)
21200Sstevel@tonic-gate 	{
21210Sstevel@tonic-gate 	  case SMFIR_REPLYCODE:
21220Sstevel@tonic-gate 		MILTER_CHECK_REPLYCODE(defresponse);
21230Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21243544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21253544Sjbeck 				  "milter=%s, action=%s, reject=%s",
21260Sstevel@tonic-gate 				  m->mf_name, action, response);
21270Sstevel@tonic-gate 		*state = rcmd;
21280Sstevel@tonic-gate 		break;
21290Sstevel@tonic-gate 
21300Sstevel@tonic-gate 	  case SMFIR_REJECT:
21310Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21323544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21333544Sjbeck 				  "milter=%s, action=%s, reject",
21340Sstevel@tonic-gate 				  m->mf_name, action);
21350Sstevel@tonic-gate 		*state = rcmd;
21360Sstevel@tonic-gate 		break;
21370Sstevel@tonic-gate 
21380Sstevel@tonic-gate 	  case SMFIR_DISCARD:
21390Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21403544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21413544Sjbeck 				  "milter=%s, action=%s, discard",
21420Sstevel@tonic-gate 				  m->mf_name, action);
21430Sstevel@tonic-gate 		*state = rcmd;
21440Sstevel@tonic-gate 		break;
21450Sstevel@tonic-gate 
21460Sstevel@tonic-gate 	  case SMFIR_TEMPFAIL:
21470Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21483544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21493544Sjbeck 				  "milter=%s, action=%s, tempfail",
21500Sstevel@tonic-gate 				  m->mf_name, action);
21510Sstevel@tonic-gate 		*state = rcmd;
21520Sstevel@tonic-gate 		break;
21530Sstevel@tonic-gate 
21540Sstevel@tonic-gate 	  case SMFIR_ACCEPT:
21550Sstevel@tonic-gate 		/* this filter is done with message/connection */
21560Sstevel@tonic-gate 		if (command == SMFIC_HELO ||
21570Sstevel@tonic-gate 		    command == SMFIC_CONNECT)
21580Sstevel@tonic-gate 			m->mf_state = SMFS_CLOSABLE;
21590Sstevel@tonic-gate 		else
21600Sstevel@tonic-gate 			m->mf_state = SMFS_DONE;
21610Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21623544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21633544Sjbeck 				  "milter=%s, action=%s, accepted",
21640Sstevel@tonic-gate 				  m->mf_name, action);
21650Sstevel@tonic-gate 		break;
21660Sstevel@tonic-gate 
21670Sstevel@tonic-gate 	  case SMFIR_CONTINUE:
21680Sstevel@tonic-gate 		/* if MAIL command is ok, filter is in message state */
21690Sstevel@tonic-gate 		if (command == SMFIC_MAIL)
21700Sstevel@tonic-gate 			m->mf_state = SMFS_INMSG;
21710Sstevel@tonic-gate 		if (MilterLogLevel > 12)
21723544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21733544Sjbeck 				  "milter=%s, action=%s, continue",
21740Sstevel@tonic-gate 				  m->mf_name, action);
21750Sstevel@tonic-gate 		break;
21760Sstevel@tonic-gate 
21773544Sjbeck 	  case SMFIR_SKIP:
21783544Sjbeck 		if (MilterLogLevel > 12)
21793544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21803544Sjbeck 				  "milter=%s, action=%s, skip",
21813544Sjbeck 				  m->mf_name, action);
21823544Sjbeck 		m->mf_state = SMFS_SKIP;
21833544Sjbeck 		break;
21843544Sjbeck 
21850Sstevel@tonic-gate 	  default:
21860Sstevel@tonic-gate 		/* Invalid response to command */
21870Sstevel@tonic-gate 		if (MilterLogLevel > 0)
21880Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
21890Sstevel@tonic-gate 				  "milter_send_command(%s): action=%s returned bogus response %c",
21900Sstevel@tonic-gate 				  m->mf_name, action, rcmd);
21910Sstevel@tonic-gate 		milter_error(m, e);
21920Sstevel@tonic-gate 		break;
21930Sstevel@tonic-gate 	}
21940Sstevel@tonic-gate 
21953544Sjbeck 	if (*state != SMFIR_REPLYCODE && response != NULL)
21960Sstevel@tonic-gate 	{
21970Sstevel@tonic-gate 		sm_free(response); /* XXX */
21980Sstevel@tonic-gate 		response = NULL;
21990Sstevel@tonic-gate 	}
22000Sstevel@tonic-gate 	return response;
22010Sstevel@tonic-gate }
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate /*
22040Sstevel@tonic-gate **  MILTER_COMMAND -- send a command and return the response for each filter
22050Sstevel@tonic-gate **
22060Sstevel@tonic-gate **	Parameters:
22073544Sjbeck **		cmd -- command to send.
22080Sstevel@tonic-gate **		data -- optional command data.
22090Sstevel@tonic-gate **		sz -- length of buf.
22100Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
22110Sstevel@tonic-gate **		e -- current envelope (for macro access).
22120Sstevel@tonic-gate **		state -- return state word.
22133544Sjbeck **		where -- description of calling function (logging).
22143544Sjbeck **		cmd_error -- did the SMTP command cause an error?
22150Sstevel@tonic-gate **
22160Sstevel@tonic-gate **	Returns:
22170Sstevel@tonic-gate **		response string (may be NULL)
22180Sstevel@tonic-gate */
22190Sstevel@tonic-gate 
22200Sstevel@tonic-gate static char *
milter_command(cmd,data,sz,macros,e,state,where,cmd_error)22213544Sjbeck milter_command(cmd, data, sz, macros, e, state, where, cmd_error)
22223544Sjbeck 	int cmd;
22230Sstevel@tonic-gate 	void *data;
22240Sstevel@tonic-gate 	ssize_t sz;
22250Sstevel@tonic-gate 	char **macros;
22260Sstevel@tonic-gate 	ENVELOPE *e;
22270Sstevel@tonic-gate 	char *state;
22283544Sjbeck 	const char *where;
22293544Sjbeck 	bool cmd_error;
22300Sstevel@tonic-gate {
22310Sstevel@tonic-gate 	int i;
22323544Sjbeck 	char command = (char) cmd;
22330Sstevel@tonic-gate 	char *response = NULL;
22340Sstevel@tonic-gate 	time_t tn = 0;
22350Sstevel@tonic-gate 
22360Sstevel@tonic-gate 	if (tTd(64, 10))
22370Sstevel@tonic-gate 		sm_dprintf("milter_command: cmd %c len %ld\n",
22383544Sjbeck 			command, (long) sz);
22390Sstevel@tonic-gate 
22400Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
22410Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
22420Sstevel@tonic-gate 	{
22430Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
22440Sstevel@tonic-gate 
22450Sstevel@tonic-gate 		/* previous problem? */
22460Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
22470Sstevel@tonic-gate 		{
22480Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
22490Sstevel@tonic-gate 			break;
22500Sstevel@tonic-gate 		}
22510Sstevel@tonic-gate 
22520Sstevel@tonic-gate 		/* sanity check */
22530Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
22540Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
22550Sstevel@tonic-gate 			continue;
22560Sstevel@tonic-gate 
22570Sstevel@tonic-gate 		/* send macros (regardless of whether we send command) */
22580Sstevel@tonic-gate 		if (macros != NULL && macros[0] != NULL)
22590Sstevel@tonic-gate 		{
22600Sstevel@tonic-gate 			milter_send_macros(m, macros, command, e);
22610Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
22620Sstevel@tonic-gate 			{
22630Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, continue);
22640Sstevel@tonic-gate 				break;
22650Sstevel@tonic-gate 			}
22660Sstevel@tonic-gate 		}
22670Sstevel@tonic-gate 
22680Sstevel@tonic-gate 		if (MilterLogLevel > 21)
22690Sstevel@tonic-gate 			tn = curtime();
22700Sstevel@tonic-gate 
22713544Sjbeck 		/*
22723544Sjbeck 		**  send the command if
22733544Sjbeck 		**	there is no error
22743544Sjbeck 		**	or it's RCPT and the client asked for it:
22753544Sjbeck 		**	!cmd_error ||
22763544Sjbeck 		**	where == "rcpt" && m->mf_pflags & SMFIP_RCPT_REJ != 0
22773544Sjbeck 		**  negate that condition and use continue
22783544Sjbeck 		*/
22793544Sjbeck 
22803544Sjbeck 		if (cmd_error &&
22813544Sjbeck 		    (strcmp(where, "rcpt") != 0 ||
22823544Sjbeck 		     (m->mf_pflags & SMFIP_RCPT_REJ) == 0))
22833544Sjbeck 			continue;
22843544Sjbeck 
22853544Sjbeck 		response = milter_send_command(m, command, data, sz, e, state,
22863544Sjbeck 						where);
22870Sstevel@tonic-gate 
22880Sstevel@tonic-gate 		if (MilterLogLevel > 21)
22890Sstevel@tonic-gate 		{
22900Sstevel@tonic-gate 			/* log the time it took for the command per filter */
22910Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
22920Sstevel@tonic-gate 				  "Milter (%s): time command (%c), %d",
22930Sstevel@tonic-gate 				  m->mf_name, command, (int) (tn - curtime()));
22940Sstevel@tonic-gate 		}
22950Sstevel@tonic-gate 
22960Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE)
22970Sstevel@tonic-gate 			break;
22980Sstevel@tonic-gate 	}
22990Sstevel@tonic-gate 	return response;
23000Sstevel@tonic-gate }
23013544Sjbeck 
23023544Sjbeck static int milter_getsymlist __P((struct milter *, char *, int, int));
23033544Sjbeck 
23043544Sjbeck static int
milter_getsymlist(m,buf,rlen,offset)23053544Sjbeck milter_getsymlist(m, buf, rlen, offset)
23063544Sjbeck 	struct milter *m;
23073544Sjbeck 	char *buf;
23083544Sjbeck 	int rlen;
23093544Sjbeck 	int offset;
23103544Sjbeck {
23113544Sjbeck 	int i, r, nummac;
23123544Sjbeck 	mi_int32 v;
23133544Sjbeck 
23143544Sjbeck 	SM_ASSERT(m != NULL);
23153544Sjbeck 	SM_ASSERT(buf != NULL);
23163544Sjbeck 
23173544Sjbeck 	while (offset + MILTER_LEN_BYTES < rlen)
23183544Sjbeck 	{
23193544Sjbeck 		size_t len;
23203544Sjbeck 		char **macros;
23213544Sjbeck 
23223544Sjbeck 		nummac = 0;
23233544Sjbeck 		(void) memcpy((char *) &v, buf + offset, MILTER_LEN_BYTES);
23243544Sjbeck 		i = ntohl(v);
23253544Sjbeck 		if (i < SMFIM_FIRST || i > SMFIM_LAST)
23263544Sjbeck 			return -1;
23273544Sjbeck 		offset += MILTER_LEN_BYTES;
23283544Sjbeck 		macros = NULL;
23293544Sjbeck 
23303544Sjbeck 		switch (i)
23313544Sjbeck 		{
23323544Sjbeck 		  case MO_MACROS_CONNECT:
23333544Sjbeck 			if (macros == NULL)
23343544Sjbeck 				macros = MilterConnectMacros;
23353544Sjbeck 			/* FALLTHROUGH */
23363544Sjbeck 
23373544Sjbeck 		  case MO_MACROS_HELO:
23383544Sjbeck 			if (macros == NULL)
23393544Sjbeck 				macros = MilterHeloMacros;
23403544Sjbeck 			/* FALLTHROUGH */
23413544Sjbeck 
23423544Sjbeck 		  case MO_MACROS_ENVFROM:
23433544Sjbeck 			if (macros == NULL)
23443544Sjbeck 				macros = MilterEnvFromMacros;
23453544Sjbeck 			/* FALLTHROUGH */
23463544Sjbeck 
23473544Sjbeck 		  case MO_MACROS_ENVRCPT:
23483544Sjbeck 			if (macros == NULL)
23493544Sjbeck 				macros = MilterEnvRcptMacros;
23503544Sjbeck 			/* FALLTHROUGH */
23513544Sjbeck 
23523544Sjbeck 		  case MO_MACROS_EOM:
23533544Sjbeck 			if (macros == NULL)
23543544Sjbeck 				macros = MilterEOMMacros;
23553544Sjbeck 			/* FALLTHROUGH */
23563544Sjbeck 
23573544Sjbeck 		  case MO_MACROS_EOH:
23583544Sjbeck 			if (macros == NULL)
23593544Sjbeck 				macros = MilterEOHMacros;
23603544Sjbeck 			/* FALLTHROUGH */
23613544Sjbeck 
23623544Sjbeck 		  case MO_MACROS_DATA:
23633544Sjbeck 			if (macros == NULL)
23643544Sjbeck 				macros = MilterDataMacros;
23653544Sjbeck 
23663544Sjbeck 			len = strlen(buf + offset);
23673544Sjbeck 			if (len > 0)
23683544Sjbeck 			{
23693544Sjbeck 				r = milter_set_macros(m->mf_name, macros,
23703544Sjbeck 						buf + offset, nummac);
23713544Sjbeck 				if (r >= 0)
23723544Sjbeck 					nummac = r;
23733544Sjbeck 			}
23743544Sjbeck 			break;
23753544Sjbeck 
23763544Sjbeck 		  default:
23773544Sjbeck 			return -1;
23783544Sjbeck 		}
23793544Sjbeck 		if (len == 0)
23803544Sjbeck 			return -1;
23813544Sjbeck 		offset += len + 1;
23823544Sjbeck 	}
23833544Sjbeck 
23843544Sjbeck 	return 0;
23853544Sjbeck }
23863544Sjbeck 
23870Sstevel@tonic-gate /*
23880Sstevel@tonic-gate **  MILTER_NEGOTIATE -- get version and flags from filter
23890Sstevel@tonic-gate **
23900Sstevel@tonic-gate **	Parameters:
23910Sstevel@tonic-gate **		m -- milter filter structure.
23920Sstevel@tonic-gate **		e -- current envelope.
23935402Sjbeck **		milters -- milters structure.
23940Sstevel@tonic-gate **
23950Sstevel@tonic-gate **	Returns:
23960Sstevel@tonic-gate **		0 on success, -1 otherwise
23970Sstevel@tonic-gate */
23980Sstevel@tonic-gate 
23990Sstevel@tonic-gate static int
milter_negotiate(m,e,milters)24005402Sjbeck milter_negotiate(m, e, milters)
24010Sstevel@tonic-gate 	struct milter *m;
24020Sstevel@tonic-gate 	ENVELOPE *e;
24035402Sjbeck 	milters_T *milters;
24040Sstevel@tonic-gate {
24050Sstevel@tonic-gate 	char rcmd;
24063544Sjbeck 	mi_int32 fvers, fflags, pflags;
24073544Sjbeck 	mi_int32 mta_prot_vers, mta_prot_flags, mta_actions;
2408616Sjbeck 	ssize_t rlen;
24090Sstevel@tonic-gate 	char *response;
24100Sstevel@tonic-gate 	char data[MILTER_OPTLEN];
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate 	/* sanity check */
24130Sstevel@tonic-gate 	if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
24140Sstevel@tonic-gate 	{
24150Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24160Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24170Sstevel@tonic-gate 				  "Milter (%s): negotiate, impossible state",
24180Sstevel@tonic-gate 				  m->mf_name);
24190Sstevel@tonic-gate 		milter_error(m, e);
24200Sstevel@tonic-gate 		return -1;
24210Sstevel@tonic-gate 	}
24220Sstevel@tonic-gate 
24233966Sjbeck #if _FFR_MILTER_CHECK
24243544Sjbeck 	mta_prot_vers = m->mf_mta_prot_version;
24253544Sjbeck 	mta_prot_flags = m->mf_mta_prot_flags;
24263544Sjbeck 	mta_actions = m->mf_mta_actions;
24273966Sjbeck #else /* _FFR_MILTER_CHECK */
24283544Sjbeck 	mta_prot_vers = SMFI_PROT_VERSION;
24293544Sjbeck 	mta_prot_flags = SMFI_CURR_PROT;
24303544Sjbeck 	mta_actions = SMFI_CURR_ACTS;
24313966Sjbeck #endif /* _FFR_MILTER_CHECK */
2432*11440SJohn.Beck@Sun.COM #if _FFR_MDS_NEGOTIATE
2433*11440SJohn.Beck@Sun.COM 	if (MilterMaxDataSize == MILTER_MDS_256K)
2434*11440SJohn.Beck@Sun.COM 		mta_prot_flags |= SMFIP_MDS_256K;
2435*11440SJohn.Beck@Sun.COM 	else if (MilterMaxDataSize == MILTER_MDS_1M)
2436*11440SJohn.Beck@Sun.COM 		mta_prot_flags |= SMFIP_MDS_1M;
2437*11440SJohn.Beck@Sun.COM #endif /* _FFR_MDS_NEGOTIATE */
24383544Sjbeck 
24393544Sjbeck 	fvers = htonl(mta_prot_vers);
24403544Sjbeck 	pflags = htonl(mta_prot_flags);
24413544Sjbeck 	fflags = htonl(mta_actions);
24420Sstevel@tonic-gate 	(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
24430Sstevel@tonic-gate 	(void) memcpy(data + MILTER_LEN_BYTES,
24440Sstevel@tonic-gate 		      (char *) &fflags, MILTER_LEN_BYTES);
24450Sstevel@tonic-gate 	(void) memcpy(data + (MILTER_LEN_BYTES * 2),
24460Sstevel@tonic-gate 		      (char *) &pflags, MILTER_LEN_BYTES);
24473544Sjbeck 	(void) milter_write(m, SMFIC_OPTNEG, data, sizeof(data),
24483544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "negotiate");
24490Sstevel@tonic-gate 
24500Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
24510Sstevel@tonic-gate 		return -1;
24520Sstevel@tonic-gate 
24533544Sjbeck 	if (tTd(64, 5))
24543544Sjbeck 		sm_dprintf("milter_negotiate(%s): send: version %lu, fflags 0x%lx, pflags 0x%lx\n",
24553544Sjbeck 			m->mf_name, ntohl(fvers), ntohl(fflags), ntohl(pflags));
24563544Sjbeck 
24573544Sjbeck 	response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e,
24583544Sjbeck 				"negotiate");
24590Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
24600Sstevel@tonic-gate 		return -1;
24610Sstevel@tonic-gate 
24620Sstevel@tonic-gate 	if (rcmd != SMFIC_OPTNEG)
24630Sstevel@tonic-gate 	{
24640Sstevel@tonic-gate 		if (tTd(64, 5))
24650Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n",
24660Sstevel@tonic-gate 				m->mf_name, rcmd, SMFIC_OPTNEG);
24670Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24680Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24690Sstevel@tonic-gate 				  "Milter (%s): negotiate: returned %c instead of %c",
24700Sstevel@tonic-gate 				  m->mf_name, rcmd, SMFIC_OPTNEG);
24710Sstevel@tonic-gate 		if (response != NULL)
24720Sstevel@tonic-gate 			sm_free(response); /* XXX */
24730Sstevel@tonic-gate 		milter_error(m, e);
24740Sstevel@tonic-gate 		return -1;
24750Sstevel@tonic-gate 	}
24760Sstevel@tonic-gate 
24770Sstevel@tonic-gate 	/* Make sure we have enough bytes for the version */
24780Sstevel@tonic-gate 	if (response == NULL || rlen < MILTER_LEN_BYTES)
24790Sstevel@tonic-gate 	{
24800Sstevel@tonic-gate 		if (tTd(64, 5))
24810Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return valid info\n",
24820Sstevel@tonic-gate 				m->mf_name);
24830Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24840Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24850Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return valid info",
24860Sstevel@tonic-gate 				  m->mf_name);
24870Sstevel@tonic-gate 		if (response != NULL)
24880Sstevel@tonic-gate 			sm_free(response); /* XXX */
24890Sstevel@tonic-gate 		milter_error(m, e);
24900Sstevel@tonic-gate 		return -1;
24910Sstevel@tonic-gate 	}
24920Sstevel@tonic-gate 
24930Sstevel@tonic-gate 	/* extract information */
24940Sstevel@tonic-gate 	(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
24950Sstevel@tonic-gate 
24960Sstevel@tonic-gate 	/* Now make sure we have enough for the feature bitmap */
24973544Sjbeck 	if (rlen < MILTER_OPTLEN)
24980Sstevel@tonic-gate 	{
24990Sstevel@tonic-gate 		if (tTd(64, 5))
25000Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return enough info\n",
25010Sstevel@tonic-gate 				m->mf_name);
25020Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25030Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25040Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return enough info",
25050Sstevel@tonic-gate 				  m->mf_name);
25060Sstevel@tonic-gate 		if (response != NULL)
25070Sstevel@tonic-gate 			sm_free(response); /* XXX */
25080Sstevel@tonic-gate 		milter_error(m, e);
25090Sstevel@tonic-gate 		return -1;
25100Sstevel@tonic-gate 	}
25110Sstevel@tonic-gate 
25120Sstevel@tonic-gate 	(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
25130Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
25140Sstevel@tonic-gate 	(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
25150Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
25160Sstevel@tonic-gate 
25170Sstevel@tonic-gate 	m->mf_fvers = ntohl(fvers);
25180Sstevel@tonic-gate 	m->mf_fflags = ntohl(fflags);
25190Sstevel@tonic-gate 	m->mf_pflags = ntohl(pflags);
25200Sstevel@tonic-gate 
25210Sstevel@tonic-gate 	/* check for version compatibility */
25220Sstevel@tonic-gate 	if (m->mf_fvers == 1 ||
25230Sstevel@tonic-gate 	    m->mf_fvers > SMFI_VERSION)
25240Sstevel@tonic-gate 	{
25250Sstevel@tonic-gate 		if (tTd(64, 5))
25260Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n",
25270Sstevel@tonic-gate 				m->mf_name, m->mf_fvers, SMFI_VERSION);
25280Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25290Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25300Sstevel@tonic-gate 				  "Milter (%s): negotiate: version %d != MTA milter version %d",
25310Sstevel@tonic-gate 				  m->mf_name, m->mf_fvers, SMFI_VERSION);
25320Sstevel@tonic-gate 		milter_error(m, e);
25333544Sjbeck 		goto error;
25340Sstevel@tonic-gate 	}
25350Sstevel@tonic-gate 
25360Sstevel@tonic-gate 	/* check for filter feature mismatch */
25373544Sjbeck 	if ((m->mf_fflags & mta_actions) != m->mf_fflags)
25380Sstevel@tonic-gate 	{
25390Sstevel@tonic-gate 		if (tTd(64, 5))
25400Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n",
25410Sstevel@tonic-gate 				m->mf_name, m->mf_fflags,
25423544Sjbeck 				(unsigned long) mta_actions);
25430Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25440Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25450Sstevel@tonic-gate 				  "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx",
25460Sstevel@tonic-gate 				  m->mf_name, m->mf_fflags,
25473544Sjbeck 				  (unsigned long) mta_actions);
25480Sstevel@tonic-gate 		milter_error(m, e);
25493544Sjbeck 		goto error;
25500Sstevel@tonic-gate 	}
25510Sstevel@tonic-gate 
2552*11440SJohn.Beck@Sun.COM #if _FFR_MDS_NEGOTIATE
2553*11440SJohn.Beck@Sun.COM 	/* use a table instead of sequence? */
2554*11440SJohn.Beck@Sun.COM 	if (bitset(SMFIP_MDS_1M, m->mf_pflags))
2555*11440SJohn.Beck@Sun.COM 	{
2556*11440SJohn.Beck@Sun.COM 		if (MilterMaxDataSize != MILTER_MDS_1M)
2557*11440SJohn.Beck@Sun.COM 		{
2558*11440SJohn.Beck@Sun.COM 			/* this should not happen... */
2559*11440SJohn.Beck@Sun.COM 			sm_syslog(LOG_WARNING, NOQID,
2560*11440SJohn.Beck@Sun.COM 				  "WARNING: Milter.maxdatasize: configured=%d, set by libmilter=%d",
2561*11440SJohn.Beck@Sun.COM 		    		  MilterMaxDataSize, MILTER_MDS_1M);
2562*11440SJohn.Beck@Sun.COM 			MilterMaxDataSize = MILTER_MDS_1M;
2563*11440SJohn.Beck@Sun.COM 		}
2564*11440SJohn.Beck@Sun.COM 	}
2565*11440SJohn.Beck@Sun.COM 	else if (bitset(SMFIP_MDS_256K, m->mf_pflags))
2566*11440SJohn.Beck@Sun.COM 	{
2567*11440SJohn.Beck@Sun.COM 		if (MilterMaxDataSize != MILTER_MDS_256K)
2568*11440SJohn.Beck@Sun.COM 		{
2569*11440SJohn.Beck@Sun.COM 			sm_syslog(LOG_WARNING, NOQID,
2570*11440SJohn.Beck@Sun.COM 				  "WARNING: Milter.maxdatasize: configured=%d, set by libmilter=%d",
2571*11440SJohn.Beck@Sun.COM 		    		  MilterMaxDataSize, MILTER_MDS_256K);
2572*11440SJohn.Beck@Sun.COM 			MilterMaxDataSize = MILTER_MDS_256K;
2573*11440SJohn.Beck@Sun.COM 		}
2574*11440SJohn.Beck@Sun.COM 	}
2575*11440SJohn.Beck@Sun.COM 	else if (MilterMaxDataSize != MILTER_MDS_64K)
2576*11440SJohn.Beck@Sun.COM 	{
2577*11440SJohn.Beck@Sun.COM 		sm_syslog(LOG_WARNING, NOQID,
2578*11440SJohn.Beck@Sun.COM 			  "WARNING: Milter.maxdatasize: configured=%d, set by libmilter=%d",
2579*11440SJohn.Beck@Sun.COM 	    		  MilterMaxDataSize, MILTER_MDS_64K);
2580*11440SJohn.Beck@Sun.COM 		MilterMaxDataSize = MILTER_MDS_64K;
2581*11440SJohn.Beck@Sun.COM 	}
2582*11440SJohn.Beck@Sun.COM 	m->mf_pflags &= ~SMFI_INTERNAL;
2583*11440SJohn.Beck@Sun.COM #endif /* _FFR_MDS_NEGOTIATE */
2584*11440SJohn.Beck@Sun.COM 
25850Sstevel@tonic-gate 	/* check for protocol feature mismatch */
25863544Sjbeck 	if ((m->mf_pflags & mta_prot_flags) != m->mf_pflags)
25870Sstevel@tonic-gate 	{
25880Sstevel@tonic-gate 		if (tTd(64, 5))
25890Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n",
25900Sstevel@tonic-gate 				m->mf_name, m->mf_pflags,
25913544Sjbeck 				(unsigned long) mta_prot_flags);
25920Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25930Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25940Sstevel@tonic-gate 				  "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx",
25950Sstevel@tonic-gate 				  m->mf_name, m->mf_pflags,
25963544Sjbeck 				  (unsigned long) mta_prot_flags);
25970Sstevel@tonic-gate 		milter_error(m, e);
25983544Sjbeck 		goto error;
25990Sstevel@tonic-gate 	}
26000Sstevel@tonic-gate 
2601616Sjbeck 	if (m->mf_fvers <= 2)
2602616Sjbeck 		m->mf_pflags |= SMFIP_NOUNKNOWN;
2603616Sjbeck 	if (m->mf_fvers <= 3)
2604616Sjbeck 		m->mf_pflags |= SMFIP_NODATA;
2605616Sjbeck 
26063544Sjbeck 	if (rlen > MILTER_OPTLEN)
26073544Sjbeck 	{
26083544Sjbeck 		milter_getsymlist(m, response, rlen, MILTER_OPTLEN);
26093544Sjbeck 	}
26103544Sjbeck 
26115402Sjbeck 	if (bitset(SMFIF_DELRCPT, m->mf_fflags))
26125402Sjbeck 		milters->mis_flags |= MIS_FL_DEL_RCPT;
26135402Sjbeck 	if (!bitset(SMFIP_NORCPT, m->mf_pflags) &&
26145402Sjbeck 	    !bitset(SMFIP_NR_RCPT, m->mf_pflags))
26155402Sjbeck 		milters->mis_flags |= MIS_FL_REJ_RCPT;
26165402Sjbeck 
26170Sstevel@tonic-gate 	if (tTd(64, 5))
26183544Sjbeck 		sm_dprintf("milter_negotiate(%s): received: version %u, fflags 0x%x, pflags 0x%x\n",
26190Sstevel@tonic-gate 			m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
26200Sstevel@tonic-gate 	return 0;
26213544Sjbeck 
26223544Sjbeck   error:
26233544Sjbeck 	if (response != NULL)
26243544Sjbeck 		sm_free(response); /* XXX */
26253544Sjbeck 	return -1;
26260Sstevel@tonic-gate }
26273544Sjbeck 
26280Sstevel@tonic-gate /*
26290Sstevel@tonic-gate **  MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
26300Sstevel@tonic-gate **
26310Sstevel@tonic-gate **	Reduce code duplication by putting these checks in one place
26320Sstevel@tonic-gate **
26330Sstevel@tonic-gate **	Parameters:
26340Sstevel@tonic-gate **		e -- current envelope.
26350Sstevel@tonic-gate **
26360Sstevel@tonic-gate **	Returns:
26370Sstevel@tonic-gate **		none
26380Sstevel@tonic-gate */
26390Sstevel@tonic-gate 
26400Sstevel@tonic-gate static void
milter_per_connection_check(e)26410Sstevel@tonic-gate milter_per_connection_check(e)
26420Sstevel@tonic-gate 	ENVELOPE *e;
26430Sstevel@tonic-gate {
26440Sstevel@tonic-gate 	int i;
26450Sstevel@tonic-gate 
26460Sstevel@tonic-gate 	/* see if we are done with any of the filters */
26470Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
26480Sstevel@tonic-gate 	{
26490Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
26500Sstevel@tonic-gate 
26510Sstevel@tonic-gate 		if (m->mf_state == SMFS_CLOSABLE)
26520Sstevel@tonic-gate 			milter_quit_filter(m, e);
26530Sstevel@tonic-gate 	}
26540Sstevel@tonic-gate }
26553544Sjbeck 
26560Sstevel@tonic-gate /*
26570Sstevel@tonic-gate **  MILTER_ERROR -- Put a milter filter into error state
26580Sstevel@tonic-gate **
26590Sstevel@tonic-gate **	Parameters:
26600Sstevel@tonic-gate **		m -- the broken filter.
26610Sstevel@tonic-gate **		e -- current envelope.
26620Sstevel@tonic-gate **
26630Sstevel@tonic-gate **	Returns:
26640Sstevel@tonic-gate **		none
26650Sstevel@tonic-gate */
26660Sstevel@tonic-gate 
26670Sstevel@tonic-gate static void
milter_error(m,e)26680Sstevel@tonic-gate milter_error(m, e)
26690Sstevel@tonic-gate 	struct milter *m;
26700Sstevel@tonic-gate 	ENVELOPE *e;
26710Sstevel@tonic-gate {
26720Sstevel@tonic-gate 	/*
26730Sstevel@tonic-gate 	**  We could send a quit here but we may have gotten here due to
26740Sstevel@tonic-gate 	**  an I/O error so we don't want to try to make things worse.
26750Sstevel@tonic-gate 	*/
26760Sstevel@tonic-gate 
26770Sstevel@tonic-gate 	if (m->mf_sock >= 0)
26780Sstevel@tonic-gate 	{
26790Sstevel@tonic-gate 		(void) close(m->mf_sock);
26800Sstevel@tonic-gate 		m->mf_sock = -1;
26810Sstevel@tonic-gate 	}
26820Sstevel@tonic-gate 	m->mf_state = SMFS_ERROR;
26830Sstevel@tonic-gate 
26840Sstevel@tonic-gate 	if (MilterLogLevel > 0)
26850Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state",
26860Sstevel@tonic-gate 			  m->mf_name);
26870Sstevel@tonic-gate }
26883544Sjbeck 
26890Sstevel@tonic-gate /*
26900Sstevel@tonic-gate **  MILTER_HEADERS -- send headers to a single milter filter
26910Sstevel@tonic-gate **
26920Sstevel@tonic-gate **	Parameters:
26930Sstevel@tonic-gate **		m -- current filter.
26940Sstevel@tonic-gate **		e -- current envelope.
26950Sstevel@tonic-gate **		state -- return state from response.
26960Sstevel@tonic-gate **
26970Sstevel@tonic-gate **	Returns:
26980Sstevel@tonic-gate **		response string (may be NULL)
26990Sstevel@tonic-gate */
27000Sstevel@tonic-gate 
27010Sstevel@tonic-gate static char *
milter_headers(m,e,state)27020Sstevel@tonic-gate milter_headers(m, e, state)
27030Sstevel@tonic-gate 	struct milter *m;
27040Sstevel@tonic-gate 	ENVELOPE *e;
27050Sstevel@tonic-gate 	char *state;
27060Sstevel@tonic-gate {
27070Sstevel@tonic-gate 	char *response = NULL;
27080Sstevel@tonic-gate 	HDR *h;
27090Sstevel@tonic-gate 
27100Sstevel@tonic-gate 	if (MilterLogLevel > 17)
27110Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send",
27120Sstevel@tonic-gate 			  m->mf_name);
27130Sstevel@tonic-gate 
27140Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
27150Sstevel@tonic-gate 	{
27163544Sjbeck 		int len_n, len_v, len_t, len_f;
27173544Sjbeck 		char *buf, *hv;
27180Sstevel@tonic-gate 
27190Sstevel@tonic-gate 		/* don't send over deleted headers */
27200Sstevel@tonic-gate 		if (h->h_value == NULL)
27210Sstevel@tonic-gate 		{
27220Sstevel@tonic-gate 			/* strip H_USER so not counted in milter_changeheader() */
27230Sstevel@tonic-gate 			h->h_flags &= ~H_USER;
27240Sstevel@tonic-gate 			continue;
27250Sstevel@tonic-gate 		}
27260Sstevel@tonic-gate 
27270Sstevel@tonic-gate 		/* skip auto-generated */
27280Sstevel@tonic-gate 		if (!bitset(H_USER, h->h_flags))
27290Sstevel@tonic-gate 			continue;
27300Sstevel@tonic-gate 
27310Sstevel@tonic-gate 		if (tTd(64, 10))
27323544Sjbeck 			sm_dprintf("milter_headers: %s:%s\n",
27330Sstevel@tonic-gate 				h->h_field, h->h_value);
27340Sstevel@tonic-gate 		if (MilterLogLevel > 21)
27350Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s",
27360Sstevel@tonic-gate 				  m->mf_name, h->h_field);
27370Sstevel@tonic-gate 
27383544Sjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)
27393544Sjbeck 		    || *(h->h_value) != ' ')
27403544Sjbeck 			hv = h->h_value;
27413544Sjbeck 		else
27423544Sjbeck 			hv = h->h_value + 1;
27433544Sjbeck 		len_f = strlen(h->h_field) + 1;
27443544Sjbeck 		len_t = len_f + strlen(hv) + 1;
27453544Sjbeck 		if (len_t < 0)
27460Sstevel@tonic-gate 			continue;
27473544Sjbeck 		buf = (char *) xalloc(len_t);
27483544Sjbeck 
27493544Sjbeck 		/*
27503544Sjbeck 		**  Note: currently the call to dequote_internal_chars()
27513544Sjbeck 		**  is not required as h_field is supposed to be 7-bit US-ASCII.
27523544Sjbeck 		*/
27533544Sjbeck 
27543544Sjbeck 		len_n = dequote_internal_chars(h->h_field, buf, len_f);
27553544Sjbeck 		SM_ASSERT(len_n < len_f);
27563544Sjbeck 		len_v = dequote_internal_chars(hv, buf + len_n + 1,
27573544Sjbeck 						len_t - len_n - 1);
27583544Sjbeck 		SM_ASSERT(len_t >= len_n + 1 + len_v + 1);
27593544Sjbeck 		len_t = len_n + 1 + len_v + 1;
27600Sstevel@tonic-gate 
27610Sstevel@tonic-gate 		/* send it over */
27620Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_HEADER, buf,
27633544Sjbeck 					       len_t, e, state, "header");
27643544Sjbeck 		sm_free(buf);
27650Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
27660Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
27670Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
27680Sstevel@tonic-gate 			break;
27690Sstevel@tonic-gate 	}
27700Sstevel@tonic-gate 	if (MilterLogLevel > 17)
27710Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent",
27720Sstevel@tonic-gate 			  m->mf_name);
27730Sstevel@tonic-gate 	return response;
27740Sstevel@tonic-gate }
27753544Sjbeck 
27760Sstevel@tonic-gate /*
27770Sstevel@tonic-gate **  MILTER_BODY -- send the body to a filter
27780Sstevel@tonic-gate **
27790Sstevel@tonic-gate **	Parameters:
27800Sstevel@tonic-gate **		m -- current filter.
27810Sstevel@tonic-gate **		e -- current envelope.
27820Sstevel@tonic-gate **		state -- return state from response.
27830Sstevel@tonic-gate **
27840Sstevel@tonic-gate **	Returns:
27850Sstevel@tonic-gate **		response string (may be NULL)
27860Sstevel@tonic-gate */
27870Sstevel@tonic-gate 
27880Sstevel@tonic-gate static char *
milter_body(m,e,state)27890Sstevel@tonic-gate milter_body(m, e, state)
27900Sstevel@tonic-gate 	struct milter *m;
27910Sstevel@tonic-gate 	ENVELOPE *e;
27920Sstevel@tonic-gate 	char *state;
27930Sstevel@tonic-gate {
27940Sstevel@tonic-gate 	char bufchar = '\0';
27950Sstevel@tonic-gate 	char prevchar = '\0';
27960Sstevel@tonic-gate 	int c;
27970Sstevel@tonic-gate 	char *response = NULL;
27980Sstevel@tonic-gate 	char *bp;
27990Sstevel@tonic-gate 	char buf[MILTER_CHUNK_SIZE];
28000Sstevel@tonic-gate 
28010Sstevel@tonic-gate 	if (tTd(64, 10))
28020Sstevel@tonic-gate 		sm_dprintf("milter_body\n");
28030Sstevel@tonic-gate 
28040Sstevel@tonic-gate 	if (bfrewind(e->e_dfp) < 0)
28050Sstevel@tonic-gate 	{
28060Sstevel@tonic-gate 		ExitStat = EX_IOERR;
28070Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
28080Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: rewind error",
28090Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
28100Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
28110Sstevel@tonic-gate 		return NULL;
28120Sstevel@tonic-gate 	}
28130Sstevel@tonic-gate 
28140Sstevel@tonic-gate 	if (MilterLogLevel > 17)
28150Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send",
28160Sstevel@tonic-gate 			  m->mf_name);
28170Sstevel@tonic-gate 	bp = buf;
28180Sstevel@tonic-gate 	while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF)
28190Sstevel@tonic-gate 	{
28200Sstevel@tonic-gate 		/*  Change LF to CRLF */
28210Sstevel@tonic-gate 		if (c == '\n')
28220Sstevel@tonic-gate 		{
28233544Sjbeck #if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
28240Sstevel@tonic-gate 			/* Not a CRLF already? */
28250Sstevel@tonic-gate 			if (prevchar != '\r')
28263544Sjbeck #endif /* !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */
28270Sstevel@tonic-gate 			{
28280Sstevel@tonic-gate 				/* Room for CR now? */
28293544Sjbeck 				if (bp + 2 > &buf[sizeof(buf)])
28300Sstevel@tonic-gate 				{
28310Sstevel@tonic-gate 					/* No room, buffer LF */
28320Sstevel@tonic-gate 					bufchar = c;
28330Sstevel@tonic-gate 
28340Sstevel@tonic-gate 					/* and send CR now */
28350Sstevel@tonic-gate 					c = '\r';
28360Sstevel@tonic-gate 				}
28370Sstevel@tonic-gate 				else
28380Sstevel@tonic-gate 				{
28390Sstevel@tonic-gate 					/* Room to do it now */
28400Sstevel@tonic-gate 					*bp++ = '\r';
28410Sstevel@tonic-gate 					prevchar = '\r';
28420Sstevel@tonic-gate 				}
28430Sstevel@tonic-gate 			}
28440Sstevel@tonic-gate 		}
28450Sstevel@tonic-gate 		*bp++ = (char) c;
28460Sstevel@tonic-gate 		prevchar = c;
28473544Sjbeck 		if (bp >= &buf[sizeof(buf)])
28480Sstevel@tonic-gate 		{
28490Sstevel@tonic-gate 			/* send chunk */
28500Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_BODY, buf,
28513544Sjbeck 						       bp - buf, e, state,
28523544Sjbeck 							"body chunk");
28530Sstevel@tonic-gate 			bp = buf;
28540Sstevel@tonic-gate 			if (bufchar != '\0')
28550Sstevel@tonic-gate 			{
28560Sstevel@tonic-gate 				*bp++ = bufchar;
28570Sstevel@tonic-gate 				bufchar = '\0';
28580Sstevel@tonic-gate 				prevchar = bufchar;
28590Sstevel@tonic-gate 			}
28600Sstevel@tonic-gate 		}
28610Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
28620Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
28633544Sjbeck 		    m->mf_state == SMFS_SKIP ||
28640Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
28650Sstevel@tonic-gate 			break;
28660Sstevel@tonic-gate 	}
28670Sstevel@tonic-gate 
28680Sstevel@tonic-gate 	/* check for read errors */
28690Sstevel@tonic-gate 	if (sm_io_error(e->e_dfp))
28700Sstevel@tonic-gate 	{
28710Sstevel@tonic-gate 		ExitStat = EX_IOERR;
28720Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
28733544Sjbeck 		    *state == SMFIR_ACCEPT ||
28743544Sjbeck 		    m->mf_state == SMFS_SKIP)
28750Sstevel@tonic-gate 		{
28760Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
28770Sstevel@tonic-gate 			if (response != NULL)
28780Sstevel@tonic-gate 			{
28790Sstevel@tonic-gate 				sm_free(response); /* XXX */
28800Sstevel@tonic-gate 				response = NULL;
28810Sstevel@tonic-gate 			}
28820Sstevel@tonic-gate 		}
28830Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: read error",
28840Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
28850Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
28860Sstevel@tonic-gate 		return response;
28870Sstevel@tonic-gate 	}
28880Sstevel@tonic-gate 
28890Sstevel@tonic-gate 	/* send last body chunk */
28900Sstevel@tonic-gate 	if (bp > buf &&
28910Sstevel@tonic-gate 	    m->mf_state != SMFS_ERROR &&
28920Sstevel@tonic-gate 	    m->mf_state != SMFS_DONE &&
28933544Sjbeck 	    m->mf_state != SMFS_SKIP &&
28940Sstevel@tonic-gate 	    *state == SMFIR_CONTINUE)
28950Sstevel@tonic-gate 	{
28960Sstevel@tonic-gate 		/* send chunk */
28970Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
28983544Sjbeck 					       e, state, "last body chunk");
28990Sstevel@tonic-gate 		bp = buf;
29000Sstevel@tonic-gate 	}
29010Sstevel@tonic-gate 	if (MilterLogLevel > 17)
29020Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent",
29030Sstevel@tonic-gate 			  m->mf_name);
29043544Sjbeck 	if (m->mf_state == SMFS_SKIP)
29053544Sjbeck 	{
29063544Sjbeck 		*state = SMFIR_CONTINUE;
29073544Sjbeck 		m->mf_state = SMFS_READY;
29083544Sjbeck 	}
29093544Sjbeck 
29100Sstevel@tonic-gate 	return response;
29110Sstevel@tonic-gate }
29120Sstevel@tonic-gate 
29130Sstevel@tonic-gate /*
29140Sstevel@tonic-gate **  Actions
29150Sstevel@tonic-gate */
29160Sstevel@tonic-gate 
29170Sstevel@tonic-gate /*
29183544Sjbeck **  ADDLEADINGSPACE -- Add a leading space to a string
29193544Sjbeck **
29203544Sjbeck **	Parameters:
29213544Sjbeck **		str -- string
29223544Sjbeck **		rp -- resource pool for allocations
29233544Sjbeck **
29243544Sjbeck **	Returns:
29253544Sjbeck **		pointer to new string
29263544Sjbeck */
29273544Sjbeck 
29283544Sjbeck static char *addleadingspace __P((char *, SM_RPOOL_T *));
29293544Sjbeck 
29303544Sjbeck static char *
addleadingspace(str,rp)29313544Sjbeck addleadingspace(str, rp)
29323544Sjbeck 	char *str;
29333544Sjbeck 	SM_RPOOL_T *rp;
29343544Sjbeck {
29353544Sjbeck 	size_t l;
29363544Sjbeck 	char *new;
29373544Sjbeck 
29383544Sjbeck 	SM_ASSERT(str != NULL);
29393544Sjbeck 	l = strlen(str);
29403544Sjbeck 	SM_ASSERT(l + 2 > l);
29413544Sjbeck 	new = sm_rpool_malloc_x(rp, l + 2);
29423544Sjbeck 	new[0] = ' ';
29433544Sjbeck 	new[1] = '\0';
29443544Sjbeck 	sm_strlcpy(new + 1, str, l + 1);
29453544Sjbeck 	return new;
29463544Sjbeck }
29473544Sjbeck 
29483544Sjbeck /*
29490Sstevel@tonic-gate **  MILTER_ADDHEADER -- Add the supplied header to the message
29500Sstevel@tonic-gate **
29510Sstevel@tonic-gate **	Parameters:
29523544Sjbeck **		m -- current filter.
29530Sstevel@tonic-gate **		response -- encoded form of header/value.
29540Sstevel@tonic-gate **		rlen -- length of response.
29550Sstevel@tonic-gate **		e -- current envelope.
29560Sstevel@tonic-gate **
29570Sstevel@tonic-gate **	Returns:
29580Sstevel@tonic-gate **		none
29590Sstevel@tonic-gate */
29600Sstevel@tonic-gate 
29610Sstevel@tonic-gate static void
milter_addheader(m,response,rlen,e)29623544Sjbeck milter_addheader(m, response, rlen, e)
29633544Sjbeck 	struct milter *m;
29640Sstevel@tonic-gate 	char *response;
29650Sstevel@tonic-gate 	ssize_t rlen;
29660Sstevel@tonic-gate 	ENVELOPE *e;
29670Sstevel@tonic-gate {
29683544Sjbeck 	int mh_v_len;
29693544Sjbeck 	char *val, *mh_value;
29700Sstevel@tonic-gate 	HDR *h;
29710Sstevel@tonic-gate 
29720Sstevel@tonic-gate 	if (tTd(64, 10))
29730Sstevel@tonic-gate 		sm_dprintf("milter_addheader: ");
29740Sstevel@tonic-gate 
29750Sstevel@tonic-gate 	/* sanity checks */
29760Sstevel@tonic-gate 	if (response == NULL)
29770Sstevel@tonic-gate 	{
29780Sstevel@tonic-gate 		if (tTd(64, 10))
29790Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
29800Sstevel@tonic-gate 		return;
29810Sstevel@tonic-gate 	}
29820Sstevel@tonic-gate 
29830Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
29840Sstevel@tonic-gate 	{
29850Sstevel@tonic-gate 		if (tTd(64, 10))
29863544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
29873544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
29880Sstevel@tonic-gate 		return;
29890Sstevel@tonic-gate 	}
29900Sstevel@tonic-gate 
29910Sstevel@tonic-gate 	/* Find separating NUL */
29920Sstevel@tonic-gate 	val = response + strlen(response) + 1;
29930Sstevel@tonic-gate 
29940Sstevel@tonic-gate 	/* another sanity check */
29950Sstevel@tonic-gate 	if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
29960Sstevel@tonic-gate 	{
29970Sstevel@tonic-gate 		if (tTd(64, 10))
29980Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
29990Sstevel@tonic-gate 		return;
30000Sstevel@tonic-gate 	}
30010Sstevel@tonic-gate 
30020Sstevel@tonic-gate 	if (*response == '\0')
30030Sstevel@tonic-gate 	{
30040Sstevel@tonic-gate 		if (tTd(64, 10))
30050Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
30060Sstevel@tonic-gate 		return;
30070Sstevel@tonic-gate 	}
30080Sstevel@tonic-gate 
30090Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
30100Sstevel@tonic-gate 	{
30110Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, response) == 0 &&
30120Sstevel@tonic-gate 		    !bitset(H_USER, h->h_flags) &&
30130Sstevel@tonic-gate 		    !bitset(H_TRACE, h->h_flags))
30140Sstevel@tonic-gate 			break;
30150Sstevel@tonic-gate 	}
30160Sstevel@tonic-gate 
30173544Sjbeck 	mh_v_len = 0;
30183544Sjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
30193544Sjbeck 
30200Sstevel@tonic-gate 	/* add to e_msgsize */
30210Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
30220Sstevel@tonic-gate 
30230Sstevel@tonic-gate 	if (h != NULL)
30240Sstevel@tonic-gate 	{
30250Sstevel@tonic-gate 		if (tTd(64, 10))
30260Sstevel@tonic-gate 			sm_dprintf("Replace default header %s value with %s\n",
30273544Sjbeck 				   h->h_field, mh_value);
30280Sstevel@tonic-gate 		if (MilterLogLevel > 8)
30290Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
30300Sstevel@tonic-gate 				  "Milter change: default header %s value with %s",
30313544Sjbeck 				  h->h_field, mh_value);
30323544Sjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
30333544Sjbeck 			h->h_value = mh_value;
30343544Sjbeck 		else
30353544Sjbeck 		{
3036*11440SJohn.Beck@Sun.COM 			h->h_value = addleadingspace(mh_value, e->e_rpool);
30373544Sjbeck 			SM_FREE(mh_value);
30383544Sjbeck 		}
30390Sstevel@tonic-gate 		h->h_flags |= H_USER;
30400Sstevel@tonic-gate 	}
30410Sstevel@tonic-gate 	else
30420Sstevel@tonic-gate 	{
30430Sstevel@tonic-gate 		if (tTd(64, 10))
30443544Sjbeck 			sm_dprintf("Add %s: %s\n", response, mh_value);
30450Sstevel@tonic-gate 		if (MilterLogLevel > 8)
30463544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
30473544Sjbeck 				  "Milter add: header: %s: %s",
30483544Sjbeck 				  response, mh_value);
30493544Sjbeck 		addheader(newstr(response), mh_value, H_USER, e,
30503544Sjbeck 			!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
30513544Sjbeck 		SM_FREE(mh_value);
30520Sstevel@tonic-gate 	}
30530Sstevel@tonic-gate }
30543544Sjbeck 
30550Sstevel@tonic-gate /*
30560Sstevel@tonic-gate **  MILTER_INSHEADER -- Insert the supplied header
30570Sstevel@tonic-gate **
30580Sstevel@tonic-gate **	Parameters:
30593544Sjbeck **		m -- current filter.
30600Sstevel@tonic-gate **		response -- encoded form of header/value.
30610Sstevel@tonic-gate **		rlen -- length of response.
30620Sstevel@tonic-gate **		e -- current envelope.
30630Sstevel@tonic-gate **
30640Sstevel@tonic-gate **	Returns:
30650Sstevel@tonic-gate **		none
30660Sstevel@tonic-gate **
3067616Sjbeck **	Notes:
3068616Sjbeck **		Unlike milter_addheader(), this does not attempt to determine
3069616Sjbeck **		if the header already exists in the envelope, even a
3070616Sjbeck **		deleted version.  It just blindly inserts.
30710Sstevel@tonic-gate */
30720Sstevel@tonic-gate 
30730Sstevel@tonic-gate static void
milter_insheader(m,response,rlen,e)30743544Sjbeck milter_insheader(m, response, rlen, e)
30753544Sjbeck 	struct milter *m;
30760Sstevel@tonic-gate 	char *response;
30770Sstevel@tonic-gate 	ssize_t rlen;
30780Sstevel@tonic-gate 	ENVELOPE *e;
30790Sstevel@tonic-gate {
30800Sstevel@tonic-gate 	mi_int32 idx, i;
30813544Sjbeck 	int mh_v_len;
30823544Sjbeck 	char *field, *val, *mh_value;
30830Sstevel@tonic-gate 
30840Sstevel@tonic-gate 	if (tTd(64, 10))
30850Sstevel@tonic-gate 		sm_dprintf("milter_insheader: ");
30860Sstevel@tonic-gate 
30870Sstevel@tonic-gate 	/* sanity checks */
30880Sstevel@tonic-gate 	if (response == NULL)
30890Sstevel@tonic-gate 	{
30900Sstevel@tonic-gate 		if (tTd(64, 10))
30910Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
30920Sstevel@tonic-gate 		return;
30930Sstevel@tonic-gate 	}
30940Sstevel@tonic-gate 
30950Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
30960Sstevel@tonic-gate 	{
30970Sstevel@tonic-gate 		if (tTd(64, 10))
30980Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
30990Sstevel@tonic-gate 		return;
31000Sstevel@tonic-gate 	}
31010Sstevel@tonic-gate 
31020Sstevel@tonic-gate 	/* decode */
31030Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
31040Sstevel@tonic-gate 	idx = ntohl(i);
31050Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
31060Sstevel@tonic-gate 	val = field + strlen(field) + 1;
31070Sstevel@tonic-gate 
31080Sstevel@tonic-gate 	/* another sanity check */
31090Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
31100Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
31110Sstevel@tonic-gate 	{
31120Sstevel@tonic-gate 		if (tTd(64, 10))
31130Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
31140Sstevel@tonic-gate 		return;
31150Sstevel@tonic-gate 	}
31160Sstevel@tonic-gate 
31170Sstevel@tonic-gate 	if (*field == '\0')
31180Sstevel@tonic-gate 	{
31190Sstevel@tonic-gate 		if (tTd(64, 10))
31200Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
31210Sstevel@tonic-gate 		return;
31220Sstevel@tonic-gate 	}
31230Sstevel@tonic-gate 
31240Sstevel@tonic-gate 	/* add to e_msgsize */
31250Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
31260Sstevel@tonic-gate 
31270Sstevel@tonic-gate 	if (tTd(64, 10))
31283544Sjbeck 		sm_dprintf("Insert (%d) %s: %s\n", idx, field, val);
31290Sstevel@tonic-gate 	if (MilterLogLevel > 8)
31300Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id,
3131616Sjbeck 			  "Milter insert (%d): header: %s: %s",
31320Sstevel@tonic-gate 			  idx, field, val);
31333544Sjbeck 	mh_v_len = 0;
31343544Sjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
31353544Sjbeck 	insheader(idx, newstr(field), mh_value, H_USER, e,
31363544Sjbeck 		!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
31373544Sjbeck 	SM_FREE(mh_value);
31380Sstevel@tonic-gate }
31393544Sjbeck 
31400Sstevel@tonic-gate /*
31410Sstevel@tonic-gate **  MILTER_CHANGEHEADER -- Change the supplied header in the message
31420Sstevel@tonic-gate **
31430Sstevel@tonic-gate **	Parameters:
31443544Sjbeck **		m -- current filter.
31450Sstevel@tonic-gate **		response -- encoded form of header/index/value.
31460Sstevel@tonic-gate **		rlen -- length of response.
31470Sstevel@tonic-gate **		e -- current envelope.
31480Sstevel@tonic-gate **
31490Sstevel@tonic-gate **	Returns:
31500Sstevel@tonic-gate **		none
31510Sstevel@tonic-gate */
31520Sstevel@tonic-gate 
31530Sstevel@tonic-gate static void
milter_changeheader(m,response,rlen,e)31543544Sjbeck milter_changeheader(m, response, rlen, e)
31553544Sjbeck 	struct milter *m;
31560Sstevel@tonic-gate 	char *response;
31570Sstevel@tonic-gate 	ssize_t rlen;
31580Sstevel@tonic-gate 	ENVELOPE *e;
31590Sstevel@tonic-gate {
31600Sstevel@tonic-gate 	mi_int32 i, index;
31613544Sjbeck 	int mh_v_len;
31623544Sjbeck 	char *field, *val, *mh_value;
31630Sstevel@tonic-gate 	HDR *h, *sysheader;
31640Sstevel@tonic-gate 
31650Sstevel@tonic-gate 	if (tTd(64, 10))
31660Sstevel@tonic-gate 		sm_dprintf("milter_changeheader: ");
31670Sstevel@tonic-gate 
31680Sstevel@tonic-gate 	/* sanity checks */
31690Sstevel@tonic-gate 	if (response == NULL)
31700Sstevel@tonic-gate 	{
31710Sstevel@tonic-gate 		if (tTd(64, 10))
31720Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
31730Sstevel@tonic-gate 		return;
31740Sstevel@tonic-gate 	}
31750Sstevel@tonic-gate 
31760Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
31770Sstevel@tonic-gate 	{
31780Sstevel@tonic-gate 		if (tTd(64, 10))
31790Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
31800Sstevel@tonic-gate 		return;
31810Sstevel@tonic-gate 	}
31820Sstevel@tonic-gate 
31830Sstevel@tonic-gate 	/* Find separating NUL */
31840Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
31850Sstevel@tonic-gate 	index = ntohl(i);
31860Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
31870Sstevel@tonic-gate 	val = field + strlen(field) + 1;
31880Sstevel@tonic-gate 
31890Sstevel@tonic-gate 	/* another sanity check */
31900Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
31910Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
31920Sstevel@tonic-gate 	{
31930Sstevel@tonic-gate 		if (tTd(64, 10))
31940Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
31950Sstevel@tonic-gate 		return;
31960Sstevel@tonic-gate 	}
31970Sstevel@tonic-gate 
31980Sstevel@tonic-gate 	if (*field == '\0')
31990Sstevel@tonic-gate 	{
32000Sstevel@tonic-gate 		if (tTd(64, 10))
32010Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
32020Sstevel@tonic-gate 		return;
32030Sstevel@tonic-gate 	}
32040Sstevel@tonic-gate 
32053544Sjbeck 	mh_v_len = 0;
32063544Sjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
32073544Sjbeck 
32080Sstevel@tonic-gate 	sysheader = NULL;
32090Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
32100Sstevel@tonic-gate 	{
32110Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, field) == 0)
32120Sstevel@tonic-gate 		{
32133544Sjbeck 			if (bitset(H_USER, h->h_flags) && --index <= 0)
32140Sstevel@tonic-gate 			{
32150Sstevel@tonic-gate 				sysheader = NULL;
32160Sstevel@tonic-gate 				break;
32170Sstevel@tonic-gate 			}
32180Sstevel@tonic-gate 			else if (!bitset(H_USER, h->h_flags) &&
32190Sstevel@tonic-gate 				 !bitset(H_TRACE, h->h_flags))
32200Sstevel@tonic-gate 			{
32210Sstevel@tonic-gate 				/*
32220Sstevel@tonic-gate 				**  DRUMS msg-fmt draft says can only have
32230Sstevel@tonic-gate 				**  multiple occurences of trace fields,
32240Sstevel@tonic-gate 				**  so make sure we replace any non-trace,
32250Sstevel@tonic-gate 				**  non-user field.
32260Sstevel@tonic-gate 				*/
32270Sstevel@tonic-gate 
32280Sstevel@tonic-gate 				sysheader = h;
32290Sstevel@tonic-gate 			}
32300Sstevel@tonic-gate 		}
32310Sstevel@tonic-gate 	}
32320Sstevel@tonic-gate 
32330Sstevel@tonic-gate 	/* if not found as user-provided header at index, use sysheader */
32340Sstevel@tonic-gate 	if (h == NULL)
32350Sstevel@tonic-gate 		h = sysheader;
32360Sstevel@tonic-gate 
32370Sstevel@tonic-gate 	if (h == NULL)
32380Sstevel@tonic-gate 	{
32390Sstevel@tonic-gate 		if (*val == '\0')
32400Sstevel@tonic-gate 		{
32410Sstevel@tonic-gate 			if (tTd(64, 10))
32420Sstevel@tonic-gate 				sm_dprintf("Delete (noop) %s\n", field);
32430Sstevel@tonic-gate 			if (MilterLogLevel > 8)
32440Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
32450Sstevel@tonic-gate 					"Milter delete (noop): header: %s"
32460Sstevel@tonic-gate 					, field);
32470Sstevel@tonic-gate 		}
32480Sstevel@tonic-gate 		else
32490Sstevel@tonic-gate 		{
32500Sstevel@tonic-gate 			/* treat modify value with no existing header as add */
32510Sstevel@tonic-gate 			if (tTd(64, 10))
32523544Sjbeck 				sm_dprintf("Add %s: %s\n", field, mh_value);
32530Sstevel@tonic-gate 			if (MilterLogLevel > 8)
32540Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
32550Sstevel@tonic-gate 					"Milter change (add): header: %s: %s"
32563544Sjbeck 					, field, mh_value);
32573544Sjbeck 			addheader(newstr(field), mh_value, H_USER, e,
32583544Sjbeck 				!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
32590Sstevel@tonic-gate 		}
32600Sstevel@tonic-gate 		return;
32610Sstevel@tonic-gate 	}
32620Sstevel@tonic-gate 
32630Sstevel@tonic-gate 	if (tTd(64, 10))
32640Sstevel@tonic-gate 	{
32650Sstevel@tonic-gate 		if (*val == '\0')
32660Sstevel@tonic-gate 		{
32673544Sjbeck 			sm_dprintf("Delete%s %s:%s\n",
32680Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
32690Sstevel@tonic-gate 				   field,
32700Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value);
32710Sstevel@tonic-gate 		}
32720Sstevel@tonic-gate 		else
32730Sstevel@tonic-gate 		{
32740Sstevel@tonic-gate 			sm_dprintf("Change%s %s: from %s to %s\n",
32750Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
32760Sstevel@tonic-gate 				   field,
32770Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value,
32783544Sjbeck 				   mh_value);
32790Sstevel@tonic-gate 		}
32800Sstevel@tonic-gate 	}
32810Sstevel@tonic-gate 
32820Sstevel@tonic-gate 	if (MilterLogLevel > 8)
32830Sstevel@tonic-gate 	{
32840Sstevel@tonic-gate 		if (*val == '\0')
32850Sstevel@tonic-gate 		{
32860Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
32873544Sjbeck 				  "Milter delete: header%s %s:%s",
32880Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
32890Sstevel@tonic-gate 				  field,
32900Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value);
32910Sstevel@tonic-gate 		}
32920Sstevel@tonic-gate 		else
32930Sstevel@tonic-gate 		{
32940Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
32950Sstevel@tonic-gate 				  "Milter change: header%s %s: from %s to %s",
32960Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
32970Sstevel@tonic-gate 				  field,
32980Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value,
32993544Sjbeck 				  mh_value);
33000Sstevel@tonic-gate 		}
33010Sstevel@tonic-gate 	}
33020Sstevel@tonic-gate 
33030Sstevel@tonic-gate 	if (h != sysheader && h->h_value != NULL)
33040Sstevel@tonic-gate 	{
33050Sstevel@tonic-gate 		size_t l;
33060Sstevel@tonic-gate 
33070Sstevel@tonic-gate 		l = strlen(h->h_value);
33080Sstevel@tonic-gate 		if (l > e->e_msgsize)
33090Sstevel@tonic-gate 			e->e_msgsize = 0;
33100Sstevel@tonic-gate 		else
33110Sstevel@tonic-gate 			e->e_msgsize -= l;
33120Sstevel@tonic-gate 		/* rpool, don't free: sm_free(h->h_value); XXX */
33130Sstevel@tonic-gate 	}
33140Sstevel@tonic-gate 
33150Sstevel@tonic-gate 	if (*val == '\0')
33160Sstevel@tonic-gate 	{
33170Sstevel@tonic-gate 		/* Remove "Field: " from message size */
33180Sstevel@tonic-gate 		if (h != sysheader)
33190Sstevel@tonic-gate 		{
33200Sstevel@tonic-gate 			size_t l;
33210Sstevel@tonic-gate 
33220Sstevel@tonic-gate 			l = strlen(h->h_field) + 2;
33230Sstevel@tonic-gate 			if (l > e->e_msgsize)
33240Sstevel@tonic-gate 				e->e_msgsize = 0;
33250Sstevel@tonic-gate 			else
33260Sstevel@tonic-gate 				e->e_msgsize -= l;
33270Sstevel@tonic-gate 		}
33280Sstevel@tonic-gate 		h->h_value = NULL;
33293544Sjbeck 		SM_FREE(mh_value);
33300Sstevel@tonic-gate 	}
33310Sstevel@tonic-gate 	else
33320Sstevel@tonic-gate 	{
33333544Sjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
33343544Sjbeck 			h->h_value = mh_value;
33353544Sjbeck 		else
33363544Sjbeck 		{
3337*11440SJohn.Beck@Sun.COM 			h->h_value = addleadingspace(mh_value, e->e_rpool);
33383544Sjbeck 			SM_FREE(mh_value);
33393544Sjbeck 		}
33400Sstevel@tonic-gate 		h->h_flags |= H_USER;
33410Sstevel@tonic-gate 		e->e_msgsize += strlen(h->h_value);
33420Sstevel@tonic-gate 	}
33430Sstevel@tonic-gate }
33443544Sjbeck 
33453544Sjbeck /*
33463544Sjbeck **  MILTER_SPLIT_RESPONSE -- Split response into fields.
33473544Sjbeck **
33483544Sjbeck **	Parameters:
33493544Sjbeck **		response -- encoded repsonse.
33503544Sjbeck **		rlen -- length of response.
33513544Sjbeck **		pargc -- number of arguments (ouput)
33523544Sjbeck **
33533544Sjbeck **	Returns:
33543544Sjbeck **		array of pointers to the individual strings
33553544Sjbeck */
33563544Sjbeck 
33573544Sjbeck static char **milter_split_response __P((char *, ssize_t, int *));
33583544Sjbeck 
33593544Sjbeck static char **
milter_split_response(response,rlen,pargc)33603544Sjbeck milter_split_response(response, rlen, pargc)
33613544Sjbeck 	char *response;
33623544Sjbeck 	ssize_t rlen;
33633544Sjbeck 	int *pargc;
33643544Sjbeck {
33653544Sjbeck 	char **s;
33663544Sjbeck 	size_t i;
33673544Sjbeck 	int elem, nelem;
33683544Sjbeck 
33693544Sjbeck 	SM_ASSERT(response != NULL);
33703544Sjbeck 	SM_ASSERT(pargc != NULL);
33713544Sjbeck 	*pargc = 0;
33723544Sjbeck 	if (rlen < 2 || strlen(response) >= (size_t) rlen)
33733544Sjbeck 	{
33743544Sjbeck 		if (tTd(64, 10))
33753544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
33763544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
33773544Sjbeck 		return NULL;
33783544Sjbeck 	}
33793544Sjbeck 
33803544Sjbeck 	nelem = 0;
33813544Sjbeck 	for (i = 0; i < rlen; i++)
33823544Sjbeck 	{
33833544Sjbeck 		if (response[i] == '\0')
33843544Sjbeck 			++nelem;
33853544Sjbeck 	}
33863544Sjbeck 	if (nelem == 0)
33873544Sjbeck 		return NULL;
33883544Sjbeck 
33893544Sjbeck 	/* last entry is only for the name */
3390*11440SJohn.Beck@Sun.COM 	s = (char **)malloc((nelem + 1) * (sizeof(*s)));
33913544Sjbeck 	if (s == NULL)
33923544Sjbeck 		return NULL;
33933544Sjbeck 	s[0] = response;
33943544Sjbeck 	for (i = 0, elem = 0; i < rlen && elem < nelem; i++)
33953544Sjbeck 	{
33963544Sjbeck 		if (response[i] == '\0')
33973544Sjbeck 		{
33983544Sjbeck 			++elem;
33993544Sjbeck 			if (i + 1 >= rlen)
34003544Sjbeck 				s[elem] = NULL;
34013544Sjbeck 			else
34023544Sjbeck 				s[elem] = &(response[i + 1]);
34033544Sjbeck 		}
34043544Sjbeck 	}
34053544Sjbeck 	*pargc = nelem;
34063544Sjbeck 
34073544Sjbeck 	if (tTd(64, 10))
34083544Sjbeck 	{
34093544Sjbeck 		for (elem = 0; elem < nelem; elem++)
34103544Sjbeck 			sm_dprintf("argv[%d]=\"%s\"\n", elem, s[elem]);
34113544Sjbeck 	}
34123544Sjbeck 
34133544Sjbeck 	/* overwrite last entry (already done above, just paranoia) */
34143544Sjbeck 	s[elem] = NULL;
34153544Sjbeck 	return s;
34163544Sjbeck }
34173544Sjbeck 
34183544Sjbeck /*
34193544Sjbeck **  MILTER_CHGFROM -- Change the envelope sender address
34203544Sjbeck **
34213544Sjbeck **	Parameters:
34223544Sjbeck **		response -- encoded form of recipient address.
34233544Sjbeck **		rlen -- length of response.
34243544Sjbeck **		e -- current envelope.
34253544Sjbeck **
34263544Sjbeck **	Returns:
34273544Sjbeck **		none
34283544Sjbeck */
34293544Sjbeck 
34303544Sjbeck static void
milter_chgfrom(response,rlen,e)34313544Sjbeck milter_chgfrom(response, rlen, e)
34323544Sjbeck 	char *response;
34333544Sjbeck 	ssize_t rlen;
34343544Sjbeck 	ENVELOPE *e;
34353544Sjbeck {
34363544Sjbeck 	int olderrors, argc;
34373544Sjbeck 	char **argv;
34383544Sjbeck 
34393544Sjbeck 	if (tTd(64, 10))
34403544Sjbeck 		sm_dprintf("milter_chgfrom: ");
34413544Sjbeck 
34423544Sjbeck 	/* sanity checks */
34433544Sjbeck 	if (response == NULL)
34443544Sjbeck 	{
34453544Sjbeck 		if (tTd(64, 10))
34463544Sjbeck 			sm_dprintf("NULL response\n");
34473544Sjbeck 		return;
34483544Sjbeck 	}
34493544Sjbeck 
34503544Sjbeck 	if (*response == '\0' ||
34513544Sjbeck 	    strlen(response) + 1 > (size_t) rlen)
34523544Sjbeck 	{
34533544Sjbeck 		if (tTd(64, 10))
34543544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
34553544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
34563544Sjbeck 		return;
34573544Sjbeck 	}
34583544Sjbeck 
34593544Sjbeck 	if (tTd(64, 10))
34603544Sjbeck 		sm_dprintf("%s\n", response);
34613544Sjbeck 	if (MilterLogLevel > 8)
34623544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter chgfrom: %s", response);
34633544Sjbeck 	argv = milter_split_response(response, rlen, &argc);
34643544Sjbeck 	if (argc < 1 || argc > 2)
34653544Sjbeck 	{
34663544Sjbeck 		if (tTd(64, 10))
34673544Sjbeck 			sm_dprintf("didn't follow protocol argc=%d\n", argc);
34683544Sjbeck 		return;
34693544Sjbeck 	}
34703544Sjbeck 
34713544Sjbeck 	olderrors = Errors;
34723544Sjbeck 	setsender(argv[0], e, NULL, '\0', false);
34733544Sjbeck 	if (argc == 2)
34743544Sjbeck 	{
34753544Sjbeck 		reset_mail_esmtp_args(e);
34763544Sjbeck 
34773544Sjbeck 		/*
34783544Sjbeck 		**  need "features" here: how to get those? via e?
34793544Sjbeck 		**  "fake" it for now: allow everything.
34803544Sjbeck 		*/
34813544Sjbeck 
34823544Sjbeck 		parse_esmtp_args(e, NULL, argv[0], argv[1], "MAIL", NULL,
34833544Sjbeck 				mail_esmtp_args);
34843544Sjbeck 	}
34853544Sjbeck 	Errors = olderrors;
34863544Sjbeck 	return;
34873544Sjbeck }
34883544Sjbeck 
34893544Sjbeck /*
34903544Sjbeck **  MILTER_ADDRCPT_PAR -- Add the supplied recipient to the message
34913544Sjbeck **
34923544Sjbeck **	Parameters:
34933544Sjbeck **		response -- encoded form of recipient address.
34943544Sjbeck **		rlen -- length of response.
34953544Sjbeck **		e -- current envelope.
34963544Sjbeck **
34973544Sjbeck **	Returns:
34983544Sjbeck **		none
34993544Sjbeck */
35003544Sjbeck 
35013544Sjbeck static void
milter_addrcpt_par(response,rlen,e)35023544Sjbeck milter_addrcpt_par(response, rlen, e)
35033544Sjbeck 	char *response;
35043544Sjbeck 	ssize_t rlen;
35053544Sjbeck 	ENVELOPE *e;
35063544Sjbeck {
35073544Sjbeck 	int olderrors, argc;
35083544Sjbeck 	char *delimptr;
35093544Sjbeck 	char **argv;
35103544Sjbeck 	ADDRESS *a;
35113544Sjbeck 
35123544Sjbeck 	if (tTd(64, 10))
35133544Sjbeck 		sm_dprintf("milter_addrcpt_par: ");
35143544Sjbeck 
35153544Sjbeck 	/* sanity checks */
35163544Sjbeck 	if (response == NULL)
35173544Sjbeck 	{
35183544Sjbeck 		if (tTd(64, 10))
35193544Sjbeck 			sm_dprintf("NULL response\n");
35203544Sjbeck 		return;
35213544Sjbeck 	}
35223544Sjbeck 
35233544Sjbeck 	if (tTd(64, 10))
35243544Sjbeck 		sm_dprintf("%s\n", response);
35253544Sjbeck 	if (MilterLogLevel > 8)
35263544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
35273544Sjbeck 
35283544Sjbeck 	argv = milter_split_response(response, rlen, &argc);
35293544Sjbeck 	if (argc < 1 || argc > 2)
35303544Sjbeck 	{
35313544Sjbeck 		if (tTd(64, 10))
35323544Sjbeck 			sm_dprintf("didn't follow protocol argc=%d\n", argc);
35333544Sjbeck 		return;
35343544Sjbeck 	}
35353544Sjbeck 	olderrors = Errors;
35363544Sjbeck 
35373544Sjbeck 	/* how to set ESMTP arguments? */
35383544Sjbeck 	a = parseaddr(argv[0], NULLADDR, RF_COPYALL, ' ', &delimptr, e, true);
35393544Sjbeck 
35403544Sjbeck 	if (a != NULL && olderrors == Errors)
35413544Sjbeck 	{
35423544Sjbeck 		parse_esmtp_args(e, a, argv[0], argv[1], "RCPT", NULL,
35433544Sjbeck 				rcpt_esmtp_args);
35443544Sjbeck 		if (olderrors == Errors)
35453544Sjbeck 			a = recipient(a, &e->e_sendqueue, 0, e);
35463544Sjbeck 		else
35473544Sjbeck 			sm_dprintf("olderrors=%d, Errors=%d\n",
35483544Sjbeck 				olderrors, Errors);
35493544Sjbeck 	}
35503544Sjbeck 	else
35513544Sjbeck 	{
35523544Sjbeck 		sm_dprintf("a=%p, olderrors=%d, Errors=%d\n",
35533544Sjbeck 			a, olderrors, Errors);
35543544Sjbeck 	}
35553544Sjbeck 
35563544Sjbeck 	Errors = olderrors;
35573544Sjbeck 	return;
35583544Sjbeck }
35593544Sjbeck 
35600Sstevel@tonic-gate /*
35610Sstevel@tonic-gate **  MILTER_ADDRCPT -- Add the supplied recipient to the message
35620Sstevel@tonic-gate **
35630Sstevel@tonic-gate **	Parameters:
35640Sstevel@tonic-gate **		response -- encoded form of recipient address.
35650Sstevel@tonic-gate **		rlen -- length of response.
35660Sstevel@tonic-gate **		e -- current envelope.
35670Sstevel@tonic-gate **
35680Sstevel@tonic-gate **	Returns:
35690Sstevel@tonic-gate **		none
35700Sstevel@tonic-gate */
35710Sstevel@tonic-gate 
35720Sstevel@tonic-gate static void
milter_addrcpt(response,rlen,e)35730Sstevel@tonic-gate milter_addrcpt(response, rlen, e)
35740Sstevel@tonic-gate 	char *response;
35750Sstevel@tonic-gate 	ssize_t rlen;
35760Sstevel@tonic-gate 	ENVELOPE *e;
35770Sstevel@tonic-gate {
35780Sstevel@tonic-gate 	int olderrors;
35790Sstevel@tonic-gate 
35800Sstevel@tonic-gate 	if (tTd(64, 10))
35810Sstevel@tonic-gate 		sm_dprintf("milter_addrcpt: ");
35820Sstevel@tonic-gate 
35830Sstevel@tonic-gate 	/* sanity checks */
35840Sstevel@tonic-gate 	if (response == NULL)
35850Sstevel@tonic-gate 	{
35860Sstevel@tonic-gate 		if (tTd(64, 10))
35870Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
35880Sstevel@tonic-gate 		return;
35890Sstevel@tonic-gate 	}
35900Sstevel@tonic-gate 
35910Sstevel@tonic-gate 	if (*response == '\0' ||
35920Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
35930Sstevel@tonic-gate 	{
35940Sstevel@tonic-gate 		if (tTd(64, 10))
35950Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
35960Sstevel@tonic-gate 				   (int) strlen(response), (int) (rlen - 1));
35970Sstevel@tonic-gate 		return;
35980Sstevel@tonic-gate 	}
35990Sstevel@tonic-gate 
36000Sstevel@tonic-gate 	if (tTd(64, 10))
36010Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
36020Sstevel@tonic-gate 	if (MilterLogLevel > 8)
36030Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
36040Sstevel@tonic-gate 	olderrors = Errors;
36050Sstevel@tonic-gate 	(void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
36060Sstevel@tonic-gate 	Errors = olderrors;
36070Sstevel@tonic-gate 	return;
36080Sstevel@tonic-gate }
36093544Sjbeck 
36100Sstevel@tonic-gate /*
36110Sstevel@tonic-gate **  MILTER_DELRCPT -- Delete the supplied recipient from the message
36120Sstevel@tonic-gate **
36130Sstevel@tonic-gate **	Parameters:
36140Sstevel@tonic-gate **		response -- encoded form of recipient address.
36150Sstevel@tonic-gate **		rlen -- length of response.
36160Sstevel@tonic-gate **		e -- current envelope.
36170Sstevel@tonic-gate **
36180Sstevel@tonic-gate **	Returns:
36190Sstevel@tonic-gate **		none
36200Sstevel@tonic-gate */
36210Sstevel@tonic-gate 
36220Sstevel@tonic-gate static void
milter_delrcpt(response,rlen,e)36230Sstevel@tonic-gate milter_delrcpt(response, rlen, e)
36240Sstevel@tonic-gate 	char *response;
36250Sstevel@tonic-gate 	ssize_t rlen;
36260Sstevel@tonic-gate 	ENVELOPE *e;
36270Sstevel@tonic-gate {
36280Sstevel@tonic-gate 	if (tTd(64, 10))
36290Sstevel@tonic-gate 		sm_dprintf("milter_delrcpt: ");
36300Sstevel@tonic-gate 
36310Sstevel@tonic-gate 	/* sanity checks */
36320Sstevel@tonic-gate 	if (response == NULL)
36330Sstevel@tonic-gate 	{
36340Sstevel@tonic-gate 		if (tTd(64, 10))
36350Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
36360Sstevel@tonic-gate 		return;
36370Sstevel@tonic-gate 	}
36380Sstevel@tonic-gate 
36390Sstevel@tonic-gate 	if (*response == '\0' ||
36400Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
36410Sstevel@tonic-gate 	{
36420Sstevel@tonic-gate 		if (tTd(64, 10))
36433544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
36443544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
36450Sstevel@tonic-gate 		return;
36460Sstevel@tonic-gate 	}
36470Sstevel@tonic-gate 
36480Sstevel@tonic-gate 	if (tTd(64, 10))
36490Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
36500Sstevel@tonic-gate 	if (MilterLogLevel > 8)
36510Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s",
36520Sstevel@tonic-gate 			  response);
36530Sstevel@tonic-gate 	(void) removefromlist(response, &e->e_sendqueue, e);
36540Sstevel@tonic-gate 	return;
36550Sstevel@tonic-gate }
36563544Sjbeck 
36570Sstevel@tonic-gate /*
36580Sstevel@tonic-gate **  MILTER_REPLBODY -- Replace the current data file with new body
36590Sstevel@tonic-gate **
36600Sstevel@tonic-gate **	Parameters:
36610Sstevel@tonic-gate **		response -- encoded form of new body.
36620Sstevel@tonic-gate **		rlen -- length of response.
36630Sstevel@tonic-gate **		newfilter -- if first time called by a new filter
36640Sstevel@tonic-gate **		e -- current envelope.
36650Sstevel@tonic-gate **
36660Sstevel@tonic-gate **	Returns:
36670Sstevel@tonic-gate **		0 upon success, -1 upon failure
36680Sstevel@tonic-gate */
36690Sstevel@tonic-gate 
36700Sstevel@tonic-gate static int
milter_replbody(response,rlen,newfilter,e)36710Sstevel@tonic-gate milter_replbody(response, rlen, newfilter, e)
36720Sstevel@tonic-gate 	char *response;
36730Sstevel@tonic-gate 	ssize_t rlen;
36740Sstevel@tonic-gate 	bool newfilter;
36750Sstevel@tonic-gate 	ENVELOPE *e;
36760Sstevel@tonic-gate {
36770Sstevel@tonic-gate 	static char prevchar;
36780Sstevel@tonic-gate 	int i;
36790Sstevel@tonic-gate 
36800Sstevel@tonic-gate 	if (tTd(64, 10))
36810Sstevel@tonic-gate 		sm_dprintf("milter_replbody\n");
36820Sstevel@tonic-gate 
36830Sstevel@tonic-gate 	/* If a new filter, reset previous character and truncate data file */
36840Sstevel@tonic-gate 	if (newfilter)
36850Sstevel@tonic-gate 	{
36860Sstevel@tonic-gate 		off_t prevsize;
36870Sstevel@tonic-gate 		char dfname[MAXPATHLEN];
36880Sstevel@tonic-gate 
36890Sstevel@tonic-gate 		(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER),
36903544Sjbeck 				  sizeof(dfname));
36910Sstevel@tonic-gate 
36920Sstevel@tonic-gate 		/* Reset prevchar */
36930Sstevel@tonic-gate 		prevchar = '\0';
36940Sstevel@tonic-gate 
36950Sstevel@tonic-gate 		/* Get the current data file information */
36960Sstevel@tonic-gate 		prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL);
36970Sstevel@tonic-gate 		if (prevsize < 0)
36980Sstevel@tonic-gate 			prevsize = 0;
36990Sstevel@tonic-gate 
37000Sstevel@tonic-gate 		/* truncate current data file */
37010Sstevel@tonic-gate 		if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
37020Sstevel@tonic-gate 		{
37030Sstevel@tonic-gate 			if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0)
37040Sstevel@tonic-gate 			{
37050Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s");
37060Sstevel@tonic-gate 				return -1;
37070Sstevel@tonic-gate 			}
37080Sstevel@tonic-gate 		}
37090Sstevel@tonic-gate 		else
37100Sstevel@tonic-gate 		{
37110Sstevel@tonic-gate 			int err;
37120Sstevel@tonic-gate 
37130Sstevel@tonic-gate 			err = sm_io_error(e->e_dfp);
37140Sstevel@tonic-gate 			(void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT);
37150Sstevel@tonic-gate 
37160Sstevel@tonic-gate 			/*
37170Sstevel@tonic-gate 			**  Clear error if tried to fflush()
37180Sstevel@tonic-gate 			**  a read-only file pointer and
37190Sstevel@tonic-gate 			**  there wasn't a previous error.
37200Sstevel@tonic-gate 			*/
37210Sstevel@tonic-gate 
37220Sstevel@tonic-gate 			if (err == 0)
37230Sstevel@tonic-gate 				sm_io_clearerr(e->e_dfp);
37240Sstevel@tonic-gate 
37250Sstevel@tonic-gate 			/* errno is set implicitly by fseek() before return */
37260Sstevel@tonic-gate 			err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT,
37270Sstevel@tonic-gate 					 0, SEEK_SET);
37280Sstevel@tonic-gate 			if (err < 0)
37290Sstevel@tonic-gate 			{
37300Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s");
37310Sstevel@tonic-gate 				return -1;
37320Sstevel@tonic-gate 			}
37330Sstevel@tonic-gate # if NOFTRUNCATE
37340Sstevel@tonic-gate 			/* XXX: Not much we can do except rewind it */
37350Sstevel@tonic-gate 			errno = EINVAL;
37360Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)");
37370Sstevel@tonic-gate 			return -1;
37380Sstevel@tonic-gate # else /* NOFTRUNCATE */
37390Sstevel@tonic-gate 			err = ftruncate(sm_io_getinfo(e->e_dfp,
37400Sstevel@tonic-gate 						      SM_IO_WHAT_FD, NULL),
37410Sstevel@tonic-gate 					0);
37420Sstevel@tonic-gate 			if (err < 0)
37430Sstevel@tonic-gate 			{
37440Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s");
37450Sstevel@tonic-gate 				return -1;
37460Sstevel@tonic-gate 			}
37470Sstevel@tonic-gate # endif /* NOFTRUNCATE */
37480Sstevel@tonic-gate 		}
37490Sstevel@tonic-gate 
37500Sstevel@tonic-gate 		if (prevsize > e->e_msgsize)
37510Sstevel@tonic-gate 			e->e_msgsize = 0;
37520Sstevel@tonic-gate 		else
37530Sstevel@tonic-gate 			e->e_msgsize -= prevsize;
37540Sstevel@tonic-gate 	}
37550Sstevel@tonic-gate 
37560Sstevel@tonic-gate 	if (newfilter && MilterLogLevel > 8)
37570Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced");
37580Sstevel@tonic-gate 
37590Sstevel@tonic-gate 	if (response == NULL)
37600Sstevel@tonic-gate 	{
37610Sstevel@tonic-gate 		/* Flush the buffered '\r' */
37620Sstevel@tonic-gate 		if (prevchar == '\r')
37630Sstevel@tonic-gate 		{
37640Sstevel@tonic-gate 			(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar);
37650Sstevel@tonic-gate 			e->e_msgsize++;
37660Sstevel@tonic-gate 		}
37670Sstevel@tonic-gate 		return 0;
37680Sstevel@tonic-gate 	}
37690Sstevel@tonic-gate 
37700Sstevel@tonic-gate 	for (i = 0; i < rlen; i++)
37710Sstevel@tonic-gate 	{
37720Sstevel@tonic-gate 		/* Buffered char from last chunk */
37730Sstevel@tonic-gate 		if (i == 0 && prevchar == '\r')
37740Sstevel@tonic-gate 		{
37750Sstevel@tonic-gate 			/* Not CRLF, output prevchar */
37760Sstevel@tonic-gate 			if (response[i] != '\n')
37770Sstevel@tonic-gate 			{
37780Sstevel@tonic-gate 				(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT,
37790Sstevel@tonic-gate 						  prevchar);
37800Sstevel@tonic-gate 				e->e_msgsize++;
37810Sstevel@tonic-gate 			}
37820Sstevel@tonic-gate 			prevchar = '\0';
37830Sstevel@tonic-gate 		}
37840Sstevel@tonic-gate 
37850Sstevel@tonic-gate 		/* Turn CRLF into LF */
37860Sstevel@tonic-gate 		if (response[i] == '\r')
37870Sstevel@tonic-gate 		{
37880Sstevel@tonic-gate 			/* check if at end of chunk */
37890Sstevel@tonic-gate 			if (i + 1 < rlen)
37900Sstevel@tonic-gate 			{
37910Sstevel@tonic-gate 				/* If LF, strip CR */
37920Sstevel@tonic-gate 				if (response[i + 1] == '\n')
37930Sstevel@tonic-gate 					i++;
37940Sstevel@tonic-gate 			}
37950Sstevel@tonic-gate 			else
37960Sstevel@tonic-gate 			{
37970Sstevel@tonic-gate 				/* check next chunk */
37980Sstevel@tonic-gate 				prevchar = '\r';
37990Sstevel@tonic-gate 				continue;
38000Sstevel@tonic-gate 			}
38010Sstevel@tonic-gate 		}
38020Sstevel@tonic-gate 		(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]);
38030Sstevel@tonic-gate 		e->e_msgsize++;
38040Sstevel@tonic-gate 	}
38050Sstevel@tonic-gate 	return 0;
38060Sstevel@tonic-gate }
38070Sstevel@tonic-gate 
38080Sstevel@tonic-gate /*
38090Sstevel@tonic-gate **  MTA callouts
38100Sstevel@tonic-gate */
38110Sstevel@tonic-gate 
38120Sstevel@tonic-gate /*
38130Sstevel@tonic-gate **  MILTER_INIT -- open and negotiate with all of the filters
38140Sstevel@tonic-gate **
38150Sstevel@tonic-gate **	Parameters:
38160Sstevel@tonic-gate **		e -- current envelope.
38170Sstevel@tonic-gate **		state -- return state from response.
38185402Sjbeck **		milters -- milters structure.
38190Sstevel@tonic-gate **
38200Sstevel@tonic-gate **	Returns:
38210Sstevel@tonic-gate **		true iff at least one filter is active
38220Sstevel@tonic-gate */
38230Sstevel@tonic-gate 
38240Sstevel@tonic-gate /* ARGSUSED */
38250Sstevel@tonic-gate bool
milter_init(e,state,milters)38265402Sjbeck milter_init(e, state, milters)
38270Sstevel@tonic-gate 	ENVELOPE *e;
38280Sstevel@tonic-gate 	char *state;
38295402Sjbeck 	milters_T *milters;
38300Sstevel@tonic-gate {
38310Sstevel@tonic-gate 	int i;
38320Sstevel@tonic-gate 
38330Sstevel@tonic-gate 	if (tTd(64, 10))
38340Sstevel@tonic-gate 		sm_dprintf("milter_init\n");
38350Sstevel@tonic-gate 
38365402Sjbeck 	memset(milters, '\0', sizeof(*milters));
38370Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
38380Sstevel@tonic-gate 	if (InputFilters[0] == NULL)
38390Sstevel@tonic-gate 	{
38400Sstevel@tonic-gate 		if (MilterLogLevel > 10)
38410Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
38420Sstevel@tonic-gate 				  "Milter: no active filter");
38430Sstevel@tonic-gate 		return false;
38440Sstevel@tonic-gate 	}
38450Sstevel@tonic-gate 
38460Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
38470Sstevel@tonic-gate 	{
38480Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
38490Sstevel@tonic-gate 
38500Sstevel@tonic-gate 		m->mf_sock = milter_open(m, false, e);
38510Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
38520Sstevel@tonic-gate 		{
38530Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
38540Sstevel@tonic-gate 			break;
38550Sstevel@tonic-gate 		}
38560Sstevel@tonic-gate 
38570Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
38585402Sjbeck 		    milter_negotiate(m, e, milters) < 0 ||
38590Sstevel@tonic-gate 		    m->mf_state == SMFS_ERROR)
38600Sstevel@tonic-gate 		{
38610Sstevel@tonic-gate 			if (tTd(64, 5))
38620Sstevel@tonic-gate 				sm_dprintf("milter_init(%s): failed to %s\n",
38630Sstevel@tonic-gate 					   m->mf_name,
38640Sstevel@tonic-gate 					   m->mf_sock < 0 ? "open" :
38650Sstevel@tonic-gate 							    "negotiate");
38660Sstevel@tonic-gate 			if (MilterLogLevel > 0)
38670Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
38680Sstevel@tonic-gate 					  "Milter (%s): init failed to %s",
38690Sstevel@tonic-gate 					  m->mf_name,
38700Sstevel@tonic-gate 					  m->mf_sock < 0 ? "open" :
38710Sstevel@tonic-gate 							   "negotiate");
38720Sstevel@tonic-gate 
3873*11440SJohn.Beck@Sun.COM 			/* if negotiation failure, close socket */
38740Sstevel@tonic-gate 			milter_error(m, e);
38750Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
38760Sstevel@tonic-gate 			continue;
38770Sstevel@tonic-gate 		}
38780Sstevel@tonic-gate 		if (MilterLogLevel > 9)
38790Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
38800Sstevel@tonic-gate 				  "Milter (%s): init success to %s",
38810Sstevel@tonic-gate 				  m->mf_name,
38820Sstevel@tonic-gate 				  m->mf_sock < 0 ? "open" : "negotiate");
38830Sstevel@tonic-gate 	}
38840Sstevel@tonic-gate 
38850Sstevel@tonic-gate 	/*
38860Sstevel@tonic-gate 	**  If something temp/perm failed with one of the filters,
38870Sstevel@tonic-gate 	**  we won't be using any of them, so clear any existing
38880Sstevel@tonic-gate 	**  connections.
38890Sstevel@tonic-gate 	*/
38900Sstevel@tonic-gate 
38910Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
38920Sstevel@tonic-gate 		milter_quit(e);
38930Sstevel@tonic-gate 
38940Sstevel@tonic-gate 	return true;
38950Sstevel@tonic-gate }
38963544Sjbeck 
38970Sstevel@tonic-gate /*
38980Sstevel@tonic-gate **  MILTER_CONNECT -- send connection info to milter filters
38990Sstevel@tonic-gate **
39000Sstevel@tonic-gate **	Parameters:
39010Sstevel@tonic-gate **		hostname -- hostname of remote machine.
39020Sstevel@tonic-gate **		addr -- address of remote machine.
39030Sstevel@tonic-gate **		e -- current envelope.
39040Sstevel@tonic-gate **		state -- return state from response.
39050Sstevel@tonic-gate **
39060Sstevel@tonic-gate **	Returns:
39070Sstevel@tonic-gate **		response string (may be NULL)
39080Sstevel@tonic-gate */
39090Sstevel@tonic-gate 
39100Sstevel@tonic-gate char *
milter_connect(hostname,addr,e,state)39110Sstevel@tonic-gate milter_connect(hostname, addr, e, state)
39120Sstevel@tonic-gate 	char *hostname;
39130Sstevel@tonic-gate 	SOCKADDR addr;
39140Sstevel@tonic-gate 	ENVELOPE *e;
39150Sstevel@tonic-gate 	char *state;
39160Sstevel@tonic-gate {
39170Sstevel@tonic-gate 	char family;
39180Sstevel@tonic-gate 	unsigned short port;
39190Sstevel@tonic-gate 	char *buf, *bp;
39200Sstevel@tonic-gate 	char *response;
39210Sstevel@tonic-gate 	char *sockinfo = NULL;
39220Sstevel@tonic-gate 	ssize_t s;
39230Sstevel@tonic-gate # if NETINET6
39240Sstevel@tonic-gate 	char buf6[INET6_ADDRSTRLEN];
39250Sstevel@tonic-gate # endif /* NETINET6 */
39260Sstevel@tonic-gate 
39270Sstevel@tonic-gate 	if (tTd(64, 10))
39280Sstevel@tonic-gate 		sm_dprintf("milter_connect(%s)\n", hostname);
39290Sstevel@tonic-gate 	if (MilterLogLevel > 9)
39300Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters");
39310Sstevel@tonic-gate 
39320Sstevel@tonic-gate 	/* gather data */
39330Sstevel@tonic-gate 	switch (addr.sa.sa_family)
39340Sstevel@tonic-gate 	{
39350Sstevel@tonic-gate # if NETUNIX
39360Sstevel@tonic-gate 	  case AF_UNIX:
39370Sstevel@tonic-gate 		family = SMFIA_UNIX;
39380Sstevel@tonic-gate 		port = htons(0);
39390Sstevel@tonic-gate 		sockinfo = addr.sunix.sun_path;
39400Sstevel@tonic-gate 		break;
39410Sstevel@tonic-gate # endif /* NETUNIX */
39420Sstevel@tonic-gate 
39430Sstevel@tonic-gate # if NETINET
39440Sstevel@tonic-gate 	  case AF_INET:
39450Sstevel@tonic-gate 		family = SMFIA_INET;
39460Sstevel@tonic-gate 		port = addr.sin.sin_port;
39470Sstevel@tonic-gate 		sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
39480Sstevel@tonic-gate 		break;
39490Sstevel@tonic-gate # endif /* NETINET */
39500Sstevel@tonic-gate 
39510Sstevel@tonic-gate # if NETINET6
39520Sstevel@tonic-gate 	  case AF_INET6:
39530Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr))
39540Sstevel@tonic-gate 			family = SMFIA_INET;
39550Sstevel@tonic-gate 		else
39560Sstevel@tonic-gate 			family = SMFIA_INET6;
39570Sstevel@tonic-gate 		port = addr.sin6.sin6_port;
39580Sstevel@tonic-gate 		sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
39593544Sjbeck 				       sizeof(buf6));
39600Sstevel@tonic-gate 		if (sockinfo == NULL)
39610Sstevel@tonic-gate 			sockinfo = "";
39620Sstevel@tonic-gate 		break;
39630Sstevel@tonic-gate # endif /* NETINET6 */
39640Sstevel@tonic-gate 
39650Sstevel@tonic-gate 	  default:
39660Sstevel@tonic-gate 		family = SMFIA_UNKNOWN;
39670Sstevel@tonic-gate 		break;
39680Sstevel@tonic-gate 	}
39690Sstevel@tonic-gate 
39700Sstevel@tonic-gate 	s = strlen(hostname) + 1 + sizeof(family);
39710Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
39720Sstevel@tonic-gate 		s += sizeof(port) + strlen(sockinfo) + 1;
39730Sstevel@tonic-gate 
39740Sstevel@tonic-gate 	buf = (char *) xalloc(s);
39750Sstevel@tonic-gate 	bp = buf;
39760Sstevel@tonic-gate 
39770Sstevel@tonic-gate 	/* put together data */
39780Sstevel@tonic-gate 	(void) memcpy(bp, hostname, strlen(hostname));
39790Sstevel@tonic-gate 	bp += strlen(hostname);
39800Sstevel@tonic-gate 	*bp++ = '\0';
39813544Sjbeck 	(void) memcpy(bp, &family, sizeof(family));
39823544Sjbeck 	bp += sizeof(family);
39830Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
39840Sstevel@tonic-gate 	{
39853544Sjbeck 		(void) memcpy(bp, &port, sizeof(port));
39863544Sjbeck 		bp += sizeof(port);
39870Sstevel@tonic-gate 
39880Sstevel@tonic-gate 		/* include trailing '\0' */
39890Sstevel@tonic-gate 		(void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
39900Sstevel@tonic-gate 	}
39910Sstevel@tonic-gate 
39923544Sjbeck 	response = milter_command(SMFIC_CONNECT, buf, s, MilterConnectMacros,
39933544Sjbeck 				e, state, "connect", false);
39940Sstevel@tonic-gate 	sm_free(buf); /* XXX */
39950Sstevel@tonic-gate 
39960Sstevel@tonic-gate 	/*
39970Sstevel@tonic-gate 	**  If this message connection is done for,
39980Sstevel@tonic-gate 	**  close the filters.
39990Sstevel@tonic-gate 	*/
40000Sstevel@tonic-gate 
40010Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
40020Sstevel@tonic-gate 	{
40030Sstevel@tonic-gate 		if (MilterLogLevel > 9)
40040Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending");
40050Sstevel@tonic-gate 		milter_quit(e);
40060Sstevel@tonic-gate 	}
40070Sstevel@tonic-gate 	else
40080Sstevel@tonic-gate 		milter_per_connection_check(e);
40090Sstevel@tonic-gate 
40100Sstevel@tonic-gate 	/*
40110Sstevel@tonic-gate 	**  SMFIR_REPLYCODE can't work with connect due to
40120Sstevel@tonic-gate 	**  the requirements of SMTP.  Therefore, ignore the
40130Sstevel@tonic-gate 	**  reply code text but keep the state it would reflect.
40140Sstevel@tonic-gate 	*/
40150Sstevel@tonic-gate 
40160Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE)
40170Sstevel@tonic-gate 	{
40180Sstevel@tonic-gate 		if (response != NULL &&
40190Sstevel@tonic-gate 		    *response == '4')
40200Sstevel@tonic-gate 		{
40210Sstevel@tonic-gate 			if (strncmp(response, "421 ", 4) == 0)
40220Sstevel@tonic-gate 				*state = SMFIR_SHUTDOWN;
40230Sstevel@tonic-gate 			else
40240Sstevel@tonic-gate 				*state = SMFIR_TEMPFAIL;
40250Sstevel@tonic-gate 		}
40260Sstevel@tonic-gate 		else
40270Sstevel@tonic-gate 			*state = SMFIR_REJECT;
40280Sstevel@tonic-gate 		if (response != NULL)
40290Sstevel@tonic-gate 		{
40300Sstevel@tonic-gate 			sm_free(response); /* XXX */
40310Sstevel@tonic-gate 			response = NULL;
40320Sstevel@tonic-gate 		}
40330Sstevel@tonic-gate 	}
40340Sstevel@tonic-gate 	return response;
40350Sstevel@tonic-gate }
40363544Sjbeck 
40370Sstevel@tonic-gate /*
40380Sstevel@tonic-gate **  MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
40390Sstevel@tonic-gate **
40400Sstevel@tonic-gate **	Parameters:
40410Sstevel@tonic-gate **		helo -- argument to SMTP HELO/EHLO command.
40420Sstevel@tonic-gate **		e -- current envelope.
40430Sstevel@tonic-gate **		state -- return state from response.
40440Sstevel@tonic-gate **
40450Sstevel@tonic-gate **	Returns:
40460Sstevel@tonic-gate **		response string (may be NULL)
40470Sstevel@tonic-gate */
40480Sstevel@tonic-gate 
40490Sstevel@tonic-gate char *
milter_helo(helo,e,state)40500Sstevel@tonic-gate milter_helo(helo, e, state)
40510Sstevel@tonic-gate 	char *helo;
40520Sstevel@tonic-gate 	ENVELOPE *e;
40530Sstevel@tonic-gate 	char *state;
40540Sstevel@tonic-gate {
40550Sstevel@tonic-gate 	int i;
40560Sstevel@tonic-gate 	char *response;
40570Sstevel@tonic-gate 
40580Sstevel@tonic-gate 	if (tTd(64, 10))
40590Sstevel@tonic-gate 		sm_dprintf("milter_helo(%s)\n", helo);
40600Sstevel@tonic-gate 
40610Sstevel@tonic-gate 	/* HELO/EHLO can come at any point */
40620Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
40630Sstevel@tonic-gate 	{
40640Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
40650Sstevel@tonic-gate 
40660Sstevel@tonic-gate 		switch (m->mf_state)
40670Sstevel@tonic-gate 		{
40680Sstevel@tonic-gate 		  case SMFS_INMSG:
40690Sstevel@tonic-gate 			/* abort in message filters */
40700Sstevel@tonic-gate 			milter_abort_filter(m, e);
40710Sstevel@tonic-gate 			/* FALLTHROUGH */
40720Sstevel@tonic-gate 
40730Sstevel@tonic-gate 		  case SMFS_DONE:
40740Sstevel@tonic-gate 			/* reset done filters */
40750Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
40760Sstevel@tonic-gate 			break;
40770Sstevel@tonic-gate 		}
40780Sstevel@tonic-gate 	}
40790Sstevel@tonic-gate 
40800Sstevel@tonic-gate 	response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
40813544Sjbeck 				  MilterHeloMacros, e, state, "helo", false);
40820Sstevel@tonic-gate 	milter_per_connection_check(e);
40830Sstevel@tonic-gate 	return response;
40840Sstevel@tonic-gate }
40853544Sjbeck 
40860Sstevel@tonic-gate /*
40870Sstevel@tonic-gate **  MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
40880Sstevel@tonic-gate **
40890Sstevel@tonic-gate **	Parameters:
40900Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == sender).
40910Sstevel@tonic-gate **		e -- current envelope.
40920Sstevel@tonic-gate **		state -- return state from response.
40930Sstevel@tonic-gate **
40940Sstevel@tonic-gate **	Returns:
40950Sstevel@tonic-gate **		response string (may be NULL)
40960Sstevel@tonic-gate */
40970Sstevel@tonic-gate 
40980Sstevel@tonic-gate char *
milter_envfrom(args,e,state)40990Sstevel@tonic-gate milter_envfrom(args, e, state)
41000Sstevel@tonic-gate 	char **args;
41010Sstevel@tonic-gate 	ENVELOPE *e;
41020Sstevel@tonic-gate 	char *state;
41030Sstevel@tonic-gate {
41040Sstevel@tonic-gate 	int i;
41050Sstevel@tonic-gate 	char *buf, *bp;
41060Sstevel@tonic-gate 	char *response;
41070Sstevel@tonic-gate 	ssize_t s;
41080Sstevel@tonic-gate 
41090Sstevel@tonic-gate 	if (tTd(64, 10))
41100Sstevel@tonic-gate 	{
41110Sstevel@tonic-gate 		sm_dprintf("milter_envfrom:");
41120Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
41130Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
41140Sstevel@tonic-gate 		sm_dprintf("\n");
41150Sstevel@tonic-gate 	}
41160Sstevel@tonic-gate 
41170Sstevel@tonic-gate 	/* sanity check */
41180Sstevel@tonic-gate 	if (args[0] == NULL)
41190Sstevel@tonic-gate 	{
41200Sstevel@tonic-gate 		*state = SMFIR_REJECT;
41210Sstevel@tonic-gate 		if (MilterLogLevel > 10)
41220Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
41230Sstevel@tonic-gate 				  "Milter: reject, no sender");
41240Sstevel@tonic-gate 		return NULL;
41250Sstevel@tonic-gate 	}
41260Sstevel@tonic-gate 
41270Sstevel@tonic-gate 	/* new message, so ... */
41280Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
41290Sstevel@tonic-gate 	{
41300Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
41310Sstevel@tonic-gate 
41320Sstevel@tonic-gate 		switch (m->mf_state)
41330Sstevel@tonic-gate 		{
41340Sstevel@tonic-gate 		  case SMFS_INMSG:
41350Sstevel@tonic-gate 			/* abort in message filters */
41360Sstevel@tonic-gate 			milter_abort_filter(m, e);
41370Sstevel@tonic-gate 			/* FALLTHROUGH */
41380Sstevel@tonic-gate 
41390Sstevel@tonic-gate 		  case SMFS_DONE:
41400Sstevel@tonic-gate 			/* reset done filters */
41410Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
41420Sstevel@tonic-gate 			break;
41430Sstevel@tonic-gate 		}
41440Sstevel@tonic-gate 	}
41450Sstevel@tonic-gate 
41460Sstevel@tonic-gate 	/* put together data */
41470Sstevel@tonic-gate 	s = 0;
41480Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41490Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
41500Sstevel@tonic-gate 
41510Sstevel@tonic-gate 	if (s < 0)
41520Sstevel@tonic-gate 	{
41530Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
41540Sstevel@tonic-gate 		return NULL;
41550Sstevel@tonic-gate 	}
41560Sstevel@tonic-gate 
41570Sstevel@tonic-gate 	buf = (char *) xalloc(s);
41580Sstevel@tonic-gate 	bp = buf;
41590Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41600Sstevel@tonic-gate 	{
41610Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
41620Sstevel@tonic-gate 		bp += strlen(bp) + 1;
41630Sstevel@tonic-gate 	}
41640Sstevel@tonic-gate 
41650Sstevel@tonic-gate 	if (MilterLogLevel > 14)
41663544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter: sender: %s", buf);
41670Sstevel@tonic-gate 
41680Sstevel@tonic-gate 	/* send it over */
41693544Sjbeck 	response = milter_command(SMFIC_MAIL, buf, s, MilterEnvFromMacros,
41703544Sjbeck 				e, state, "mail", false);
41710Sstevel@tonic-gate 	sm_free(buf); /* XXX */
41720Sstevel@tonic-gate 
41730Sstevel@tonic-gate 	/*
41740Sstevel@tonic-gate 	**  If filter rejects/discards a per message command,
41750Sstevel@tonic-gate 	**  abort the other filters since we are done with the
41760Sstevel@tonic-gate 	**  current message.
41770Sstevel@tonic-gate 	*/
41780Sstevel@tonic-gate 
41790Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
41800Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
41813544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, sender");
41820Sstevel@tonic-gate 	return response;
41830Sstevel@tonic-gate }
41840Sstevel@tonic-gate 
41850Sstevel@tonic-gate /*
41860Sstevel@tonic-gate **  MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
41870Sstevel@tonic-gate **
41880Sstevel@tonic-gate **	Parameters:
41890Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == recipient).
41900Sstevel@tonic-gate **		e -- current envelope.
41910Sstevel@tonic-gate **		state -- return state from response.
41923544Sjbeck **		rcpt_error -- does RCPT have an error?
41930Sstevel@tonic-gate **
41940Sstevel@tonic-gate **	Returns:
41950Sstevel@tonic-gate **		response string (may be NULL)
41960Sstevel@tonic-gate */
41970Sstevel@tonic-gate 
41980Sstevel@tonic-gate char *
milter_envrcpt(args,e,state,rcpt_error)41993544Sjbeck milter_envrcpt(args, e, state, rcpt_error)
42000Sstevel@tonic-gate 	char **args;
42010Sstevel@tonic-gate 	ENVELOPE *e;
42020Sstevel@tonic-gate 	char *state;
42033544Sjbeck 	bool rcpt_error;
42040Sstevel@tonic-gate {
42050Sstevel@tonic-gate 	int i;
42060Sstevel@tonic-gate 	char *buf, *bp;
42070Sstevel@tonic-gate 	char *response;
42080Sstevel@tonic-gate 	ssize_t s;
42090Sstevel@tonic-gate 
42100Sstevel@tonic-gate 	if (tTd(64, 10))
42110Sstevel@tonic-gate 	{
42120Sstevel@tonic-gate 		sm_dprintf("milter_envrcpt:");
42130Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
42140Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
42150Sstevel@tonic-gate 		sm_dprintf("\n");
42160Sstevel@tonic-gate 	}
42170Sstevel@tonic-gate 
42180Sstevel@tonic-gate 	/* sanity check */
42190Sstevel@tonic-gate 	if (args[0] == NULL)
42200Sstevel@tonic-gate 	{
42210Sstevel@tonic-gate 		*state = SMFIR_REJECT;
42220Sstevel@tonic-gate 		if (MilterLogLevel > 10)
42230Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt");
42240Sstevel@tonic-gate 		return NULL;
42250Sstevel@tonic-gate 	}
42260Sstevel@tonic-gate 
42270Sstevel@tonic-gate 	/* put together data */
42280Sstevel@tonic-gate 	s = 0;
42290Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
42300Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
42310Sstevel@tonic-gate 
42320Sstevel@tonic-gate 	if (s < 0)
42330Sstevel@tonic-gate 	{
42340Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
42350Sstevel@tonic-gate 		return NULL;
42360Sstevel@tonic-gate 	}
42370Sstevel@tonic-gate 
42380Sstevel@tonic-gate 	buf = (char *) xalloc(s);
42390Sstevel@tonic-gate 	bp = buf;
42400Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
42410Sstevel@tonic-gate 	{
42420Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
42430Sstevel@tonic-gate 		bp += strlen(bp) + 1;
42440Sstevel@tonic-gate 	}
42450Sstevel@tonic-gate 
42460Sstevel@tonic-gate 	if (MilterLogLevel > 14)
42470Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf);
42480Sstevel@tonic-gate 
42490Sstevel@tonic-gate 	/* send it over */
42503544Sjbeck 	response = milter_command(SMFIC_RCPT, buf, s, MilterEnvRcptMacros,
42513544Sjbeck 				e, state, "rcpt", rcpt_error);
42520Sstevel@tonic-gate 	sm_free(buf); /* XXX */
42530Sstevel@tonic-gate 	return response;
42540Sstevel@tonic-gate }
42550Sstevel@tonic-gate 
42560Sstevel@tonic-gate /*
42570Sstevel@tonic-gate **  MILTER_DATA_CMD -- send SMTP DATA command info to milter filters
42580Sstevel@tonic-gate **
42590Sstevel@tonic-gate **	Parameters:
42600Sstevel@tonic-gate **		e -- current envelope.
42610Sstevel@tonic-gate **		state -- return state from response.
42620Sstevel@tonic-gate **
42630Sstevel@tonic-gate **	Returns:
42640Sstevel@tonic-gate **		response string (may be NULL)
42650Sstevel@tonic-gate */
42660Sstevel@tonic-gate 
42670Sstevel@tonic-gate char *
milter_data_cmd(e,state)42680Sstevel@tonic-gate milter_data_cmd(e, state)
42690Sstevel@tonic-gate 	ENVELOPE *e;
42700Sstevel@tonic-gate 	char *state;
42710Sstevel@tonic-gate {
42720Sstevel@tonic-gate 	if (tTd(64, 10))
42730Sstevel@tonic-gate 		sm_dprintf("milter_data_cmd\n");
42740Sstevel@tonic-gate 
42750Sstevel@tonic-gate 	/* send it over */
42763544Sjbeck 	return milter_command(SMFIC_DATA, NULL, 0, MilterDataMacros, e, state,
42773544Sjbeck 				"data", false);
42780Sstevel@tonic-gate }
42790Sstevel@tonic-gate 
42800Sstevel@tonic-gate /*
42810Sstevel@tonic-gate **  MILTER_DATA -- send message headers/body and gather final message results
42820Sstevel@tonic-gate **
42830Sstevel@tonic-gate **	Parameters:
42840Sstevel@tonic-gate **		e -- current envelope.
42850Sstevel@tonic-gate **		state -- return state from response.
42860Sstevel@tonic-gate **
42870Sstevel@tonic-gate **	Returns:
42880Sstevel@tonic-gate **		response string (may be NULL)
42890Sstevel@tonic-gate **
42900Sstevel@tonic-gate **	Side effects:
42910Sstevel@tonic-gate **		- Uses e->e_dfp for access to the body
42920Sstevel@tonic-gate **		- Can call the various milter action routines to
42930Sstevel@tonic-gate **		  modify the envelope or message.
42940Sstevel@tonic-gate */
42950Sstevel@tonic-gate 
42960Sstevel@tonic-gate # define MILTER_CHECK_RESULTS() \
42970Sstevel@tonic-gate 	if (*state == SMFIR_ACCEPT || \
42980Sstevel@tonic-gate 	    m->mf_state == SMFS_DONE || \
42990Sstevel@tonic-gate 	    m->mf_state == SMFS_ERROR) \
43000Sstevel@tonic-gate 	{ \
43010Sstevel@tonic-gate 		if (m->mf_state != SMFS_ERROR) \
43020Sstevel@tonic-gate 			m->mf_state = SMFS_DONE; \
43030Sstevel@tonic-gate 		continue;	/* to next filter */ \
43040Sstevel@tonic-gate 	} \
43050Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE) \
43060Sstevel@tonic-gate 	{ \
43070Sstevel@tonic-gate 		m->mf_state = SMFS_DONE; \
43080Sstevel@tonic-gate 		goto finishup; \
43090Sstevel@tonic-gate 	}
43100Sstevel@tonic-gate 
43110Sstevel@tonic-gate char *
milter_data(e,state)43120Sstevel@tonic-gate milter_data(e, state)
43130Sstevel@tonic-gate 	ENVELOPE *e;
43140Sstevel@tonic-gate 	char *state;
43150Sstevel@tonic-gate {
43160Sstevel@tonic-gate 	bool replbody = false;		/* milter_replbody() called? */
43170Sstevel@tonic-gate 	bool replfailed = false;	/* milter_replbody() failed? */
43180Sstevel@tonic-gate 	bool rewind = false;		/* rewind data file? */
43190Sstevel@tonic-gate 	bool dfopen = false;		/* data file open for writing? */
43200Sstevel@tonic-gate 	bool newfilter;			/* reset on each new filter */
43210Sstevel@tonic-gate 	char rcmd;
43220Sstevel@tonic-gate 	int i;
43230Sstevel@tonic-gate 	int save_errno;
43240Sstevel@tonic-gate 	char *response = NULL;
43250Sstevel@tonic-gate 	time_t eomsent;
43260Sstevel@tonic-gate 	ssize_t rlen;
43270Sstevel@tonic-gate 
43280Sstevel@tonic-gate 	if (tTd(64, 10))
43290Sstevel@tonic-gate 		sm_dprintf("milter_data\n");
43300Sstevel@tonic-gate 
43310Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
43320Sstevel@tonic-gate 
43330Sstevel@tonic-gate 	/*
43340Sstevel@tonic-gate 	**  XXX: Should actually send body chunks to each filter
43350Sstevel@tonic-gate 	**  a chunk at a time instead of sending the whole body to
43360Sstevel@tonic-gate 	**  each filter in turn.  However, only if the filters don't
43370Sstevel@tonic-gate 	**  change the body.
43380Sstevel@tonic-gate 	*/
43390Sstevel@tonic-gate 
43400Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
43410Sstevel@tonic-gate 	{
43420Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
43430Sstevel@tonic-gate 
43440Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE &&
43450Sstevel@tonic-gate 		    *state != SMFIR_ACCEPT)
43460Sstevel@tonic-gate 		{
43470Sstevel@tonic-gate 			/*
43480Sstevel@tonic-gate 			**  A previous filter has dealt with the message,
43490Sstevel@tonic-gate 			**  safe to stop processing the filters.
43500Sstevel@tonic-gate 			*/
43510Sstevel@tonic-gate 
43520Sstevel@tonic-gate 			break;
43530Sstevel@tonic-gate 		}
43540Sstevel@tonic-gate 
43550Sstevel@tonic-gate 		/* Now reset state for later evaluation */
43560Sstevel@tonic-gate 		*state = SMFIR_CONTINUE;
43570Sstevel@tonic-gate 		newfilter = true;
43580Sstevel@tonic-gate 
43590Sstevel@tonic-gate 		/* previous problem? */
43600Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
43610Sstevel@tonic-gate 		{
43620Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
43630Sstevel@tonic-gate 			break;
43640Sstevel@tonic-gate 		}
43650Sstevel@tonic-gate 
43660Sstevel@tonic-gate 		/* sanity checks */
43670Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
43680Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
43690Sstevel@tonic-gate 			continue;
43700Sstevel@tonic-gate 
43710Sstevel@tonic-gate 		m->mf_state = SMFS_INMSG;
43720Sstevel@tonic-gate 
43730Sstevel@tonic-gate 		/* check if filter wants the headers */
43740Sstevel@tonic-gate 		if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
43750Sstevel@tonic-gate 		{
43760Sstevel@tonic-gate 			response = milter_headers(m, e, state);
43770Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43780Sstevel@tonic-gate 		}
43790Sstevel@tonic-gate 
43800Sstevel@tonic-gate 		/* check if filter wants EOH */
43810Sstevel@tonic-gate 		if (!bitset(SMFIP_NOEOH, m->mf_pflags))
43820Sstevel@tonic-gate 		{
43830Sstevel@tonic-gate 			if (tTd(64, 10))
43840Sstevel@tonic-gate 				sm_dprintf("milter_data: eoh\n");
43850Sstevel@tonic-gate 
43863544Sjbeck 			if (MilterEOHMacros[0] != NULL)
43873544Sjbeck 			{
43883544Sjbeck 				milter_send_macros(m, MilterEOHMacros,
43893544Sjbeck 					   SMFIC_EOH, e);
43903544Sjbeck 				MILTER_CHECK_RESULTS();
43913544Sjbeck 			}
43923544Sjbeck 
43930Sstevel@tonic-gate 			/* send it over */
43940Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_EOH, NULL, 0,
43953544Sjbeck 						       e, state, "eoh");
43960Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43970Sstevel@tonic-gate 		}
43980Sstevel@tonic-gate 
43990Sstevel@tonic-gate 		/* check if filter wants the body */
44000Sstevel@tonic-gate 		if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
44010Sstevel@tonic-gate 		    e->e_dfp != NULL)
44020Sstevel@tonic-gate 		{
44030Sstevel@tonic-gate 			rewind = true;
44040Sstevel@tonic-gate 			response = milter_body(m, e, state);
44050Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
44060Sstevel@tonic-gate 		}
44070Sstevel@tonic-gate 
44080Sstevel@tonic-gate 		if (MilterEOMMacros[0] != NULL)
44090Sstevel@tonic-gate 		{
44100Sstevel@tonic-gate 			milter_send_macros(m, MilterEOMMacros,
44110Sstevel@tonic-gate 					   SMFIC_BODYEOB, e);
44120Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
44130Sstevel@tonic-gate 		}
44140Sstevel@tonic-gate 
44150Sstevel@tonic-gate 		/* send the final body chunk */
44160Sstevel@tonic-gate 		(void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
44173544Sjbeck 				    m->mf_timeout[SMFTO_WRITE], e, "eom");
44180Sstevel@tonic-gate 
44190Sstevel@tonic-gate 		/* Get time EOM sent for timeout */
44200Sstevel@tonic-gate 		eomsent = curtime();
44210Sstevel@tonic-gate 
44220Sstevel@tonic-gate 		/* deal with the possibility of multiple responses */
44230Sstevel@tonic-gate 		while (*state == SMFIR_CONTINUE)
44240Sstevel@tonic-gate 		{
44250Sstevel@tonic-gate 			/* Check total timeout from EOM to final ACK/NAK */
44260Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_EOM] > 0 &&
44270Sstevel@tonic-gate 			    curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
44280Sstevel@tonic-gate 			{
44290Sstevel@tonic-gate 				if (tTd(64, 5))
44300Sstevel@tonic-gate 					sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
44310Sstevel@tonic-gate 						m->mf_name);
44320Sstevel@tonic-gate 				if (MilterLogLevel > 0)
44330Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
44340Sstevel@tonic-gate 						  "milter_data(%s): EOM ACK/NAK timeout",
44350Sstevel@tonic-gate 						  m->mf_name);
44360Sstevel@tonic-gate 				milter_error(m, e);
44370Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, break);
44380Sstevel@tonic-gate 				break;
44390Sstevel@tonic-gate 			}
44400Sstevel@tonic-gate 
44410Sstevel@tonic-gate 			response = milter_read(m, &rcmd, &rlen,
44423544Sjbeck 					       m->mf_timeout[SMFTO_READ], e,
4443*11440SJohn.Beck@Sun.COM 						"eom");
44440Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
44450Sstevel@tonic-gate 				break;
44460Sstevel@tonic-gate 
44470Sstevel@tonic-gate 			if (tTd(64, 10))
44480Sstevel@tonic-gate 				sm_dprintf("milter_data(%s): state %c\n",
44490Sstevel@tonic-gate 					   m->mf_name, (char) rcmd);
44500Sstevel@tonic-gate 
44510Sstevel@tonic-gate 			switch (rcmd)
44520Sstevel@tonic-gate 			{
44530Sstevel@tonic-gate 			  case SMFIR_REPLYCODE:
44540Sstevel@tonic-gate 				MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
44550Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44560Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s",
44570Sstevel@tonic-gate 						  m->mf_name, response);
44580Sstevel@tonic-gate 				*state = rcmd;
44590Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44600Sstevel@tonic-gate 				break;
44610Sstevel@tonic-gate 
44620Sstevel@tonic-gate 			  case SMFIR_REJECT: /* log msg at end of function */
44630Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44640Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject",
44650Sstevel@tonic-gate 						  m->mf_name);
44660Sstevel@tonic-gate 				*state = rcmd;
44670Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44680Sstevel@tonic-gate 				break;
44690Sstevel@tonic-gate 
44700Sstevel@tonic-gate 			  case SMFIR_DISCARD:
44710Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44720Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard",
44730Sstevel@tonic-gate 						  m->mf_name);
44740Sstevel@tonic-gate 				*state = rcmd;
44750Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44760Sstevel@tonic-gate 				break;
44770Sstevel@tonic-gate 
44780Sstevel@tonic-gate 			  case SMFIR_TEMPFAIL:
44790Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44800Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail",
44810Sstevel@tonic-gate 						  m->mf_name);
44820Sstevel@tonic-gate 				*state = rcmd;
44830Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44840Sstevel@tonic-gate 				break;
44850Sstevel@tonic-gate 
44860Sstevel@tonic-gate 			  case SMFIR_CONTINUE:
44870Sstevel@tonic-gate 			  case SMFIR_ACCEPT:
44880Sstevel@tonic-gate 				/* this filter is done with message */
44890Sstevel@tonic-gate 				if (replfailed)
44900Sstevel@tonic-gate 					*state = SMFIR_TEMPFAIL;
44910Sstevel@tonic-gate 				else
44920Sstevel@tonic-gate 					*state = SMFIR_ACCEPT;
44930Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44940Sstevel@tonic-gate 				break;
44950Sstevel@tonic-gate 
44960Sstevel@tonic-gate 			  case SMFIR_PROGRESS:
44970Sstevel@tonic-gate 				break;
44980Sstevel@tonic-gate 
44990Sstevel@tonic-gate 			  case SMFIR_QUARANTINE:
45000Sstevel@tonic-gate 				if (!bitset(SMFIF_QUARANTINE, m->mf_fflags))
45010Sstevel@tonic-gate 				{
45020Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45030Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45040Sstevel@tonic-gate 							  "milter_data(%s): lied about quarantining, honoring request anyway",
45050Sstevel@tonic-gate 							  m->mf_name);
45060Sstevel@tonic-gate 				}
45070Sstevel@tonic-gate 				if (response == NULL)
45080Sstevel@tonic-gate 					response = newstr("");
45090Sstevel@tonic-gate 				if (MilterLogLevel > 3)
45100Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
45110Sstevel@tonic-gate 						  "milter=%s, quarantine=%s",
45120Sstevel@tonic-gate 						  m->mf_name, response);
45130Sstevel@tonic-gate 				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
45140Sstevel@tonic-gate 								 response);
45150Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
45160Sstevel@tonic-gate 					  macid("{quarantine}"), e->e_quarmsg);
45170Sstevel@tonic-gate 				break;
45180Sstevel@tonic-gate 
45190Sstevel@tonic-gate 			  case SMFIR_ADDHEADER:
45200Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
45210Sstevel@tonic-gate 				{
45220Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45230Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45240Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
45250Sstevel@tonic-gate 							  m->mf_name);
45260Sstevel@tonic-gate 				}
45273544Sjbeck 				milter_addheader(m, response, rlen, e);
45280Sstevel@tonic-gate 				break;
45290Sstevel@tonic-gate 
45300Sstevel@tonic-gate 			  case SMFIR_INSHEADER:
45310Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
45320Sstevel@tonic-gate 				{
45330Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45340Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45350Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
45360Sstevel@tonic-gate 							  m->mf_name);
45370Sstevel@tonic-gate 				}
45383544Sjbeck 				milter_insheader(m, response, rlen, e);
45390Sstevel@tonic-gate 				break;
45400Sstevel@tonic-gate 
45410Sstevel@tonic-gate 			  case SMFIR_CHGHEADER:
45420Sstevel@tonic-gate 				if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
45430Sstevel@tonic-gate 				{
45440Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45450Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45460Sstevel@tonic-gate 							  "milter_data(%s): lied about changing headers, honoring request anyway",
45470Sstevel@tonic-gate 							  m->mf_name);
45480Sstevel@tonic-gate 				}
45493544Sjbeck 				milter_changeheader(m, response, rlen, e);
45503544Sjbeck 				break;
45513544Sjbeck 
45523544Sjbeck 			  case SMFIR_CHGFROM:
45533544Sjbeck 				if (!bitset(SMFIF_CHGFROM, m->mf_fflags))
45543544Sjbeck 				{
45553544Sjbeck 					if (MilterLogLevel > 9)
45563544Sjbeck 						sm_syslog(LOG_WARNING, e->e_id,
45573544Sjbeck 							  "milter_data(%s) lied about changing sender, honoring request anyway",
45583544Sjbeck 							  m->mf_name);
45593544Sjbeck 				}
45603544Sjbeck 				milter_chgfrom(response, rlen, e);
45610Sstevel@tonic-gate 				break;
45620Sstevel@tonic-gate 
45630Sstevel@tonic-gate 			  case SMFIR_ADDRCPT:
45640Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
45650Sstevel@tonic-gate 				{
45660Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45670Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45680Sstevel@tonic-gate 							  "milter_data(%s) lied about adding recipients, honoring request anyway",
45690Sstevel@tonic-gate 							  m->mf_name);
45700Sstevel@tonic-gate 				}
45710Sstevel@tonic-gate 				milter_addrcpt(response, rlen, e);
45720Sstevel@tonic-gate 				break;
45730Sstevel@tonic-gate 
45743544Sjbeck 			  case SMFIR_ADDRCPT_PAR:
45753544Sjbeck 				if (!bitset(SMFIF_ADDRCPT_PAR, m->mf_fflags))
45763544Sjbeck 				{
45773544Sjbeck 					if (MilterLogLevel > 9)
45783544Sjbeck 						sm_syslog(LOG_WARNING, e->e_id,
45793544Sjbeck 							  "milter_data(%s) lied about adding recipients with parameters, honoring request anyway",
45803544Sjbeck 							  m->mf_name);
45813544Sjbeck 				}
45823544Sjbeck 				milter_addrcpt_par(response, rlen, e);
45833544Sjbeck 				break;
45843544Sjbeck 
45850Sstevel@tonic-gate 			  case SMFIR_DELRCPT:
45860Sstevel@tonic-gate 				if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
45870Sstevel@tonic-gate 				{
45880Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45890Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45900Sstevel@tonic-gate 							  "milter_data(%s): lied about removing recipients, honoring request anyway",
45910Sstevel@tonic-gate 							  m->mf_name);
45920Sstevel@tonic-gate 				}
45930Sstevel@tonic-gate 				milter_delrcpt(response, rlen, e);
45940Sstevel@tonic-gate 				break;
45950Sstevel@tonic-gate 
45960Sstevel@tonic-gate 			  case SMFIR_REPLBODY:
45970Sstevel@tonic-gate 				if (!bitset(SMFIF_MODBODY, m->mf_fflags))
45980Sstevel@tonic-gate 				{
45990Sstevel@tonic-gate 					if (MilterLogLevel > 0)
46000Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
46010Sstevel@tonic-gate 							  "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
46020Sstevel@tonic-gate 							  m->mf_name);
46030Sstevel@tonic-gate 					replfailed = true;
46040Sstevel@tonic-gate 					break;
46050Sstevel@tonic-gate 				}
46060Sstevel@tonic-gate 
46070Sstevel@tonic-gate 				/* already failed in attempt */
46080Sstevel@tonic-gate 				if (replfailed)
46090Sstevel@tonic-gate 					break;
46100Sstevel@tonic-gate 
46110Sstevel@tonic-gate 				if (!dfopen)
46120Sstevel@tonic-gate 				{
46130Sstevel@tonic-gate 					if (milter_reopen_df(e) < 0)
46140Sstevel@tonic-gate 					{
46150Sstevel@tonic-gate 						replfailed = true;
46160Sstevel@tonic-gate 						break;
46170Sstevel@tonic-gate 					}
46180Sstevel@tonic-gate 					dfopen = true;
46190Sstevel@tonic-gate 					rewind = true;
46200Sstevel@tonic-gate 				}
46210Sstevel@tonic-gate 
46220Sstevel@tonic-gate 				if (milter_replbody(response, rlen,
46230Sstevel@tonic-gate 						    newfilter, e) < 0)
46240Sstevel@tonic-gate 					replfailed = true;
46250Sstevel@tonic-gate 				newfilter = false;
46260Sstevel@tonic-gate 				replbody = true;
46270Sstevel@tonic-gate 				break;
46280Sstevel@tonic-gate 
46290Sstevel@tonic-gate 			  default:
46300Sstevel@tonic-gate 				/* Invalid response to command */
46310Sstevel@tonic-gate 				if (MilterLogLevel > 0)
46320Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
46330Sstevel@tonic-gate 						  "milter_data(%s): returned bogus response %c",
46340Sstevel@tonic-gate 						  m->mf_name, rcmd);
46350Sstevel@tonic-gate 				milter_error(m, e);
46360Sstevel@tonic-gate 				break;
46370Sstevel@tonic-gate 			}
46380Sstevel@tonic-gate 			if (rcmd != SMFIR_REPLYCODE && response != NULL)
46390Sstevel@tonic-gate 			{
46400Sstevel@tonic-gate 				sm_free(response); /* XXX */
46410Sstevel@tonic-gate 				response = NULL;
46420Sstevel@tonic-gate 			}
46430Sstevel@tonic-gate 
46440Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
46450Sstevel@tonic-gate 				break;
46460Sstevel@tonic-gate 		}
46470Sstevel@tonic-gate 
46480Sstevel@tonic-gate 		if (replbody && !replfailed)
46490Sstevel@tonic-gate 		{
46500Sstevel@tonic-gate 			/* flush possible buffered character */
46510Sstevel@tonic-gate 			milter_replbody(NULL, 0, !replbody, e);
46520Sstevel@tonic-gate 			replbody = false;
46530Sstevel@tonic-gate 		}
46540Sstevel@tonic-gate 
46550Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
46560Sstevel@tonic-gate 		{
46570Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
46580Sstevel@tonic-gate 			goto finishup;
46590Sstevel@tonic-gate 		}
46600Sstevel@tonic-gate 	}
46610Sstevel@tonic-gate 
46620Sstevel@tonic-gate finishup:
46630Sstevel@tonic-gate 	/* leave things in the expected state if we touched it */
46640Sstevel@tonic-gate 	if (replfailed)
46650Sstevel@tonic-gate 	{
46660Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
46670Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
46680Sstevel@tonic-gate 		{
46690Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
46700Sstevel@tonic-gate 			SM_FREE_CLR(response);
46710Sstevel@tonic-gate 		}
46720Sstevel@tonic-gate 
46730Sstevel@tonic-gate 		if (dfopen)
46740Sstevel@tonic-gate 		{
46750Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
46760Sstevel@tonic-gate 			e->e_dfp = NULL;
46770Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
46780Sstevel@tonic-gate 			dfopen = false;
46790Sstevel@tonic-gate 		}
46800Sstevel@tonic-gate 		rewind = false;
46810Sstevel@tonic-gate 	}
46820Sstevel@tonic-gate 
46830Sstevel@tonic-gate 	if ((dfopen && milter_reset_df(e) < 0) ||
46840Sstevel@tonic-gate 	    (rewind && bfrewind(e->e_dfp) < 0))
46850Sstevel@tonic-gate 	{
46860Sstevel@tonic-gate 		save_errno = errno;
46870Sstevel@tonic-gate 		ExitStat = EX_IOERR;
46880Sstevel@tonic-gate 
46890Sstevel@tonic-gate 		/*
46900Sstevel@tonic-gate 		**  If filter told us to keep message but we had
46910Sstevel@tonic-gate 		**  an error, we can't really keep it, tempfail it.
46920Sstevel@tonic-gate 		*/
46930Sstevel@tonic-gate 
46940Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
46950Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
46960Sstevel@tonic-gate 		{
46970Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
46980Sstevel@tonic-gate 			SM_FREE_CLR(response);
46990Sstevel@tonic-gate 		}
47000Sstevel@tonic-gate 
47010Sstevel@tonic-gate 		errno = save_errno;
47020Sstevel@tonic-gate 		syserr("milter_data: %s/%cf%s: read error",
47030Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
47040Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
47050Sstevel@tonic-gate 	}
47060Sstevel@tonic-gate 
47070Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
47080Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
47090Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data");
47100Sstevel@tonic-gate 	return response;
47110Sstevel@tonic-gate }
47120Sstevel@tonic-gate 
47130Sstevel@tonic-gate /*
47140Sstevel@tonic-gate **  MILTER_UNKNOWN -- send any unrecognized or unimplemented command
47150Sstevel@tonic-gate **			string to milter filters
47160Sstevel@tonic-gate **
47170Sstevel@tonic-gate **	Parameters:
47183544Sjbeck **		smtpcmd -- the string itself.
47190Sstevel@tonic-gate **		e -- current envelope.
47200Sstevel@tonic-gate **		state -- return state from response.
47210Sstevel@tonic-gate **
47220Sstevel@tonic-gate **
47230Sstevel@tonic-gate **	Returns:
47240Sstevel@tonic-gate **		response string (may be NULL)
47250Sstevel@tonic-gate */
47260Sstevel@tonic-gate 
47270Sstevel@tonic-gate char *
milter_unknown(smtpcmd,e,state)47283544Sjbeck milter_unknown(smtpcmd, e, state)
47293544Sjbeck 	char *smtpcmd;
47300Sstevel@tonic-gate 	ENVELOPE *e;
47310Sstevel@tonic-gate 	char *state;
47320Sstevel@tonic-gate {
47330Sstevel@tonic-gate 	if (tTd(64, 10))
47343544Sjbeck 		sm_dprintf("milter_unknown(%s)\n", smtpcmd);
47353544Sjbeck 
47363544Sjbeck 	return milter_command(SMFIC_UNKNOWN, smtpcmd, strlen(smtpcmd) + 1,
47373544Sjbeck 				NULL, e, state, "unknown", false);
47380Sstevel@tonic-gate }
47390Sstevel@tonic-gate 
47400Sstevel@tonic-gate /*
47410Sstevel@tonic-gate **  MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
47420Sstevel@tonic-gate **
47430Sstevel@tonic-gate **	Parameters:
47440Sstevel@tonic-gate **		e -- current envelope.
47450Sstevel@tonic-gate **
47460Sstevel@tonic-gate **	Returns:
47470Sstevel@tonic-gate **		none
47480Sstevel@tonic-gate */
47490Sstevel@tonic-gate 
47500Sstevel@tonic-gate void
milter_quit(e)47510Sstevel@tonic-gate milter_quit(e)
47520Sstevel@tonic-gate 	ENVELOPE *e;
47530Sstevel@tonic-gate {
47540Sstevel@tonic-gate 	int i;
47550Sstevel@tonic-gate 
47560Sstevel@tonic-gate 	if (tTd(64, 10))
47570Sstevel@tonic-gate 		sm_dprintf("milter_quit(%s)\n", e->e_id);
47580Sstevel@tonic-gate 
47590Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
47600Sstevel@tonic-gate 		milter_quit_filter(InputFilters[i], e);
47610Sstevel@tonic-gate }
47623544Sjbeck 
47630Sstevel@tonic-gate /*
47640Sstevel@tonic-gate **  MILTER_ABORT -- informs the filter(s) that we are aborting current message
47650Sstevel@tonic-gate **
47660Sstevel@tonic-gate **	Parameters:
47670Sstevel@tonic-gate **		e -- current envelope.
47680Sstevel@tonic-gate **
47690Sstevel@tonic-gate **	Returns:
47700Sstevel@tonic-gate **		none
47710Sstevel@tonic-gate */
47720Sstevel@tonic-gate 
47730Sstevel@tonic-gate void
milter_abort(e)47740Sstevel@tonic-gate milter_abort(e)
47750Sstevel@tonic-gate 	ENVELOPE *e;
47760Sstevel@tonic-gate {
47770Sstevel@tonic-gate 	int i;
47780Sstevel@tonic-gate 
47790Sstevel@tonic-gate 	if (tTd(64, 10))
47800Sstevel@tonic-gate 		sm_dprintf("milter_abort\n");
47810Sstevel@tonic-gate 
47820Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
47830Sstevel@tonic-gate 	{
47840Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
47850Sstevel@tonic-gate 
47860Sstevel@tonic-gate 		/* sanity checks */
47870Sstevel@tonic-gate 		if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
47880Sstevel@tonic-gate 			continue;
47890Sstevel@tonic-gate 
47900Sstevel@tonic-gate 		milter_abort_filter(m, e);
47910Sstevel@tonic-gate 	}
47920Sstevel@tonic-gate }
47930Sstevel@tonic-gate #endif /* MILTER */
4794