164562Sgshapiro/*
2261194Sgshapiro *  Copyright (c) 1999-2007 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
464562Sgshapiro *
564562Sgshapiro * By using this file, you agree to the terms and conditions set
664562Sgshapiro * forth in the LICENSE file which can be found at the top level of
764562Sgshapiro * the sendmail distribution.
864562Sgshapiro *
964562Sgshapiro */
1064562Sgshapiro
1190792Sgshapiro#include <sm/gen.h>
12266527SgshapiroSM_RCSID("@(#)$Id: listener.c,v 8.127 2013-11-22 20:51:36 ca Exp $")
1364562Sgshapiro
1464562Sgshapiro/*
1564562Sgshapiro**  listener.c -- threaded network listener
1664562Sgshapiro*/
1764562Sgshapiro
1864562Sgshapiro#include "libmilter.h"
1990792Sgshapiro#include <sm/errstring.h>
2064562Sgshapiro
21125820Sgshapiro#include <sys/types.h>
22125820Sgshapiro#include <sys/stat.h>
2364562Sgshapiro
24125820Sgshapiro
2564562Sgshapiro# if NETINET || NETINET6
2664562Sgshapiro#  include <arpa/inet.h>
27363466Sgshapiro# endif
28168515Sgshapiro# if SM_CONF_POLL
29168515Sgshapiro#  undef SM_FD_OK_SELECT
30168515Sgshapiro#  define SM_FD_OK_SELECT(fd)		true
31363466Sgshapiro# endif
3290792Sgshapiro
3390792Sgshapirostatic smutex_t L_Mutex;
3498121Sgshapirostatic int L_family;
3598121Sgshapirostatic SOCKADDR_LEN_T L_socksize;
3698121Sgshapirostatic socket_t listenfd = INVALID_SOCKET;
3790792Sgshapiro
38125820Sgshapirostatic socket_t mi_milteropen __P((char *, int, bool, char *));
39168515Sgshapiro#if !_FFR_WORKERS_POOL
40141858Sgshapirostatic void *mi_thread_handle_wrapper __P((void *));
41363466Sgshapiro#endif
4298121Sgshapiro
4390792Sgshapiro/*
4498121Sgshapiro**  MI_OPENSOCKET -- create the socket where this filter and the MTA will meet
4598121Sgshapiro**
46125820Sgshapiro**	Parameters:
4798121Sgshapiro**		conn -- connection description
4898121Sgshapiro**		backlog -- listen backlog
49125820Sgshapiro**		dbg -- debug level
50125820Sgshapiro**		rmsocket -- if true, try to unlink() the socket first
51132943Sgshapiro**			(UNIX domain sockets only)
5298121Sgshapiro**		smfi -- filter structure to use
5398121Sgshapiro**
54125820Sgshapiro**	Return value:
55125820Sgshapiro**		MI_SUCCESS/MI_FAILURE
5698121Sgshapiro*/
5798121Sgshapiro
5898121Sgshapiroint
59125820Sgshapiromi_opensocket(conn, backlog, dbg, rmsocket, smfi)
6098121Sgshapiro	char *conn;
6198121Sgshapiro	int backlog;
6298121Sgshapiro	int dbg;
63125820Sgshapiro	bool rmsocket;
6498121Sgshapiro	smfiDesc_ptr smfi;
6598121Sgshapiro{
6698121Sgshapiro	if (smfi == NULL || conn == NULL)
6798121Sgshapiro		return MI_FAILURE;
6898121Sgshapiro
6998121Sgshapiro	if (ValidSocket(listenfd))
7098121Sgshapiro		return MI_SUCCESS;
7198121Sgshapiro
7298121Sgshapiro	if (dbg > 0)
7398121Sgshapiro	{
7498121Sgshapiro		smi_log(SMI_LOG_DEBUG,
7598121Sgshapiro			"%s: Opening listen socket on conn %s",
7698121Sgshapiro			smfi->xxfi_name, conn);
7798121Sgshapiro	}
7898121Sgshapiro	(void) smutex_init(&L_Mutex);
7998121Sgshapiro	(void) smutex_lock(&L_Mutex);
80125820Sgshapiro	listenfd = mi_milteropen(conn, backlog, rmsocket, smfi->xxfi_name);
8198121Sgshapiro	if (!ValidSocket(listenfd))
8298121Sgshapiro	{
8398121Sgshapiro		smi_log(SMI_LOG_FATAL,
8498121Sgshapiro			"%s: Unable to create listening socket on conn %s",
8598121Sgshapiro			smfi->xxfi_name, conn);
8698121Sgshapiro		(void) smutex_unlock(&L_Mutex);
8798121Sgshapiro		return MI_FAILURE;
8898121Sgshapiro	}
89110560Sgshapiro	if (!SM_FD_OK_SELECT(listenfd))
90110560Sgshapiro	{
91110560Sgshapiro		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
92110560Sgshapiro			smfi->xxfi_name, listenfd, FD_SETSIZE);
93110560Sgshapiro		(void) smutex_unlock(&L_Mutex);
94110560Sgshapiro		return MI_FAILURE;
95110560Sgshapiro	}
96141858Sgshapiro	(void) smutex_unlock(&L_Mutex);
9798121Sgshapiro	return MI_SUCCESS;
9898121Sgshapiro}
9998121Sgshapiro
10098121Sgshapiro/*
10164562Sgshapiro**  MI_MILTEROPEN -- setup socket to listen on
10264562Sgshapiro**
10364562Sgshapiro**	Parameters:
10464562Sgshapiro**		conn -- connection description
10564562Sgshapiro**		backlog -- listen backlog
106125820Sgshapiro**		rmsocket -- if true, try to unlink() the socket first
107125820Sgshapiro**			(UNIX domain sockets only)
10873188Sgshapiro**		name -- name for logging
10964562Sgshapiro**
11064562Sgshapiro**	Returns:
11164562Sgshapiro**		socket upon success, error code otherwise.
11290792Sgshapiro**
11390792Sgshapiro**	Side effect:
11490792Sgshapiro**		sets sockpath if UNIX socket.
11564562Sgshapiro*/
11664562Sgshapiro
11790792Sgshapiro#if NETUNIX
11890792Sgshapirostatic char	*sockpath = NULL;
119363466Sgshapiro#endif
12090792Sgshapiro
12166494Sgshapirostatic socket_t
122125820Sgshapiromi_milteropen(conn, backlog, rmsocket, name)
12364562Sgshapiro	char *conn;
12464562Sgshapiro	int backlog;
125125820Sgshapiro	bool rmsocket;
12664562Sgshapiro	char *name;
12764562Sgshapiro{
12866494Sgshapiro	socket_t sock;
12964562Sgshapiro	int sockopt = 1;
13098121Sgshapiro	int fdflags;
13190792Sgshapiro	size_t len = 0;
13264562Sgshapiro	char *p;
13364562Sgshapiro	char *colon;
13464562Sgshapiro	char *at;
13564562Sgshapiro	SOCKADDR addr;
13664562Sgshapiro
13764562Sgshapiro	if (conn == NULL || conn[0] == '\0')
13864562Sgshapiro	{
13964562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
14064562Sgshapiro			name);
14166494Sgshapiro		return INVALID_SOCKET;
14264562Sgshapiro	}
14364562Sgshapiro	(void) memset(&addr, '\0', sizeof addr);
14464562Sgshapiro
14564562Sgshapiro	/* protocol:filename or protocol:port@host */
14664562Sgshapiro	p = conn;
14764562Sgshapiro	colon = strchr(p, ':');
14864562Sgshapiro	if (colon != NULL)
14964562Sgshapiro	{
15064562Sgshapiro		*colon = '\0';
15164562Sgshapiro
15294334Sgshapiro		if (*p == '\0')
15364562Sgshapiro		{
15464562Sgshapiro#if NETUNIX
15564562Sgshapiro			/* default to AF_UNIX */
15694334Sgshapiro			addr.sa.sa_family = AF_UNIX;
15798121Sgshapiro			L_socksize = sizeof (struct sockaddr_un);
15864562Sgshapiro#else /* NETUNIX */
15964562Sgshapiro# if NETINET
16064562Sgshapiro			/* default to AF_INET */
16164562Sgshapiro			addr.sa.sa_family = AF_INET;
16298121Sgshapiro			L_socksize = sizeof addr.sin;
16364562Sgshapiro# else /* NETINET */
16464562Sgshapiro#  if NETINET6
16564562Sgshapiro			/* default to AF_INET6 */
16664562Sgshapiro			addr.sa.sa_family = AF_INET6;
16798121Sgshapiro			L_socksize = sizeof addr.sin6;
16864562Sgshapiro#  else /* NETINET6 */
16964562Sgshapiro			/* no protocols available */
17064562Sgshapiro			smi_log(SMI_LOG_ERR,
17164562Sgshapiro				"%s: no valid socket protocols available",
17264562Sgshapiro				name);
17366494Sgshapiro			return INVALID_SOCKET;
17464562Sgshapiro#  endif /* NETINET6 */
17564562Sgshapiro# endif /* NETINET */
17664562Sgshapiro#endif /* NETUNIX */
17764562Sgshapiro		}
17864562Sgshapiro#if NETUNIX
17964562Sgshapiro		else if (strcasecmp(p, "unix") == 0 ||
18064562Sgshapiro			 strcasecmp(p, "local") == 0)
18164562Sgshapiro		{
18264562Sgshapiro			addr.sa.sa_family = AF_UNIX;
18398121Sgshapiro			L_socksize = sizeof (struct sockaddr_un);
18464562Sgshapiro		}
18564562Sgshapiro#endif /* NETUNIX */
18664562Sgshapiro#if NETINET
18764562Sgshapiro		else if (strcasecmp(p, "inet") == 0)
18864562Sgshapiro		{
18964562Sgshapiro			addr.sa.sa_family = AF_INET;
19098121Sgshapiro			L_socksize = sizeof addr.sin;
19164562Sgshapiro		}
19264562Sgshapiro#endif /* NETINET */
19364562Sgshapiro#if NETINET6
19464562Sgshapiro		else if (strcasecmp(p, "inet6") == 0)
19564562Sgshapiro		{
19664562Sgshapiro			addr.sa.sa_family = AF_INET6;
19798121Sgshapiro			L_socksize = sizeof addr.sin6;
19864562Sgshapiro		}
19964562Sgshapiro#endif /* NETINET6 */
20064562Sgshapiro		else
20164562Sgshapiro		{
20264562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
20364562Sgshapiro				name, p);
20466494Sgshapiro			return INVALID_SOCKET;
20564562Sgshapiro		}
20664562Sgshapiro		*colon++ = ':';
20764562Sgshapiro	}
20864562Sgshapiro	else
20964562Sgshapiro	{
21064562Sgshapiro		colon = p;
21164562Sgshapiro#if NETUNIX
21264562Sgshapiro		/* default to AF_UNIX */
21394334Sgshapiro		addr.sa.sa_family = AF_UNIX;
21498121Sgshapiro		L_socksize = sizeof (struct sockaddr_un);
21564562Sgshapiro#else /* NETUNIX */
21664562Sgshapiro# if NETINET
21764562Sgshapiro		/* default to AF_INET */
21864562Sgshapiro		addr.sa.sa_family = AF_INET;
21998121Sgshapiro		L_socksize = sizeof addr.sin;
22064562Sgshapiro# else /* NETINET */
22164562Sgshapiro#  if NETINET6
22264562Sgshapiro		/* default to AF_INET6 */
22364562Sgshapiro		addr.sa.sa_family = AF_INET6;
22498121Sgshapiro		L_socksize = sizeof addr.sin6;
22564562Sgshapiro#  else /* NETINET6 */
22664562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
22764562Sgshapiro			name, p);
22866494Sgshapiro		return INVALID_SOCKET;
22964562Sgshapiro#  endif /* NETINET6 */
23064562Sgshapiro# endif /* NETINET */
23164562Sgshapiro#endif /* NETUNIX */
23264562Sgshapiro	}
23364562Sgshapiro
23464562Sgshapiro#if NETUNIX
23564562Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
23664562Sgshapiro	{
23764562Sgshapiro# if 0
23864562Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
23964562Sgshapiro# endif /* 0 */
24064562Sgshapiro
24164562Sgshapiro		at = colon;
24290792Sgshapiro		len = strlen(colon) + 1;
24390792Sgshapiro		if (len >= sizeof addr.sunix.sun_path)
24464562Sgshapiro		{
24564562Sgshapiro			errno = EINVAL;
24664562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
24764562Sgshapiro				name, colon);
24866494Sgshapiro			return INVALID_SOCKET;
24964562Sgshapiro		}
25090792Sgshapiro		(void) sm_strlcpy(addr.sunix.sun_path, colon,
25190792Sgshapiro				sizeof addr.sunix.sun_path);
25264562Sgshapiro# if 0
25364562Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
25464562Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
25564562Sgshapiro
25664562Sgshapiro		/* if not safe, don't create */
25764562Sgshapiro		if (errno != 0)
25864562Sgshapiro		{
25964562Sgshapiro			smi_log(SMI_LOG_ERR,
26064562Sgshapiro				"%s: UNIX socket name %s unsafe",
26164562Sgshapiro				name, colon);
26266494Sgshapiro			return INVALID_SOCKET;
26364562Sgshapiro		}
26464562Sgshapiro# endif /* 0 */
26564562Sgshapiro	}
26664562Sgshapiro#endif /* NETUNIX */
26764562Sgshapiro
26864562Sgshapiro#if NETINET || NETINET6
26964562Sgshapiro	if (
27064562Sgshapiro# if NETINET
27164562Sgshapiro	    addr.sa.sa_family == AF_INET
272363466Sgshapiro# endif
27364562Sgshapiro# if NETINET && NETINET6
27464562Sgshapiro	    ||
275363466Sgshapiro# endif
27664562Sgshapiro# if NETINET6
27764562Sgshapiro	    addr.sa.sa_family == AF_INET6
278363466Sgshapiro# endif
27964562Sgshapiro	   )
28064562Sgshapiro	{
28190792Sgshapiro		unsigned short port;
28264562Sgshapiro
28364562Sgshapiro		/* Parse port@host */
28464562Sgshapiro		at = strchr(colon, '@');
28564562Sgshapiro		if (at == NULL)
28664562Sgshapiro		{
28764562Sgshapiro			switch (addr.sa.sa_family)
28864562Sgshapiro			{
28964562Sgshapiro# if NETINET
29064562Sgshapiro			  case AF_INET:
29164562Sgshapiro				addr.sin.sin_addr.s_addr = INADDR_ANY;
29264562Sgshapiro				break;
293363466Sgshapiro# endif
29464562Sgshapiro
29564562Sgshapiro# if NETINET6
29664562Sgshapiro			  case AF_INET6:
29764562Sgshapiro				addr.sin6.sin6_addr = in6addr_any;
29864562Sgshapiro				break;
299363466Sgshapiro# endif
30064562Sgshapiro			}
30164562Sgshapiro		}
30264562Sgshapiro		else
30364562Sgshapiro			*at = '\0';
30464562Sgshapiro
30564562Sgshapiro		if (isascii(*colon) && isdigit(*colon))
30690792Sgshapiro			port = htons((unsigned short) atoi(colon));
30764562Sgshapiro		else
30864562Sgshapiro		{
30964562Sgshapiro# ifdef NO_GETSERVBYNAME
31064562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
31164562Sgshapiro				name, colon);
31266494Sgshapiro			return INVALID_SOCKET;
31364562Sgshapiro# else /* NO_GETSERVBYNAME */
31464562Sgshapiro			register struct servent *sp;
31564562Sgshapiro
31664562Sgshapiro			sp = getservbyname(colon, "tcp");
31764562Sgshapiro			if (sp == NULL)
31864562Sgshapiro			{
31964562Sgshapiro				smi_log(SMI_LOG_ERR,
32064562Sgshapiro					"%s: unknown port name %s",
32164562Sgshapiro					name, colon);
32266494Sgshapiro				return INVALID_SOCKET;
32364562Sgshapiro			}
32464562Sgshapiro			port = sp->s_port;
32564562Sgshapiro# endif /* NO_GETSERVBYNAME */
32664562Sgshapiro		}
32764562Sgshapiro		if (at != NULL)
32864562Sgshapiro		{
32964562Sgshapiro			*at++ = '@';
33064562Sgshapiro			if (*at == '[')
33164562Sgshapiro			{
33264562Sgshapiro				char *end;
33364562Sgshapiro
33464562Sgshapiro				end = strchr(at, ']');
33564562Sgshapiro				if (end != NULL)
33664562Sgshapiro				{
33790792Sgshapiro					bool found = false;
33864562Sgshapiro# if NETINET
33964562Sgshapiro					unsigned long hid = INADDR_NONE;
340363466Sgshapiro# endif
34164562Sgshapiro# if NETINET6
34264562Sgshapiro					struct sockaddr_in6 hid6;
343363466Sgshapiro# endif
34464562Sgshapiro
34564562Sgshapiro					*end = '\0';
34664562Sgshapiro# if NETINET
34764562Sgshapiro					if (addr.sa.sa_family == AF_INET &&
34890792Sgshapiro					    (hid = inet_addr(&at[1])) != INADDR_NONE)
34964562Sgshapiro					{
35064562Sgshapiro						addr.sin.sin_addr.s_addr = hid;
35164562Sgshapiro						addr.sin.sin_port = port;
35290792Sgshapiro						found = true;
35364562Sgshapiro					}
35464562Sgshapiro# endif /* NETINET */
35564562Sgshapiro# if NETINET6
35664562Sgshapiro					(void) memset(&hid6, '\0', sizeof hid6);
35764562Sgshapiro					if (addr.sa.sa_family == AF_INET6 &&
35890792Sgshapiro					    mi_inet_pton(AF_INET6, &at[1],
35990792Sgshapiro							 &hid6.sin6_addr) == 1)
36064562Sgshapiro					{
36164562Sgshapiro						addr.sin6.sin6_addr = hid6.sin6_addr;
36264562Sgshapiro						addr.sin6.sin6_port = port;
36390792Sgshapiro						found = true;
36464562Sgshapiro					}
36564562Sgshapiro# endif /* NETINET6 */
36664562Sgshapiro					*end = ']';
36764562Sgshapiro					if (!found)
36864562Sgshapiro					{
36964562Sgshapiro						smi_log(SMI_LOG_ERR,
37064562Sgshapiro							"%s: Invalid numeric domain spec \"%s\"",
37164562Sgshapiro							name, at);
37266494Sgshapiro						return INVALID_SOCKET;
37364562Sgshapiro					}
37464562Sgshapiro				}
37564562Sgshapiro				else
37664562Sgshapiro				{
37764562Sgshapiro					smi_log(SMI_LOG_ERR,
37864562Sgshapiro						"%s: Invalid numeric domain spec \"%s\"",
37964562Sgshapiro						name, at);
38066494Sgshapiro					return INVALID_SOCKET;
38164562Sgshapiro				}
38264562Sgshapiro			}
38364562Sgshapiro			else
38464562Sgshapiro			{
38571345Sgshapiro				struct hostent *hp = NULL;
38671345Sgshapiro
38764562Sgshapiro				hp = mi_gethostbyname(at, addr.sa.sa_family);
38864562Sgshapiro				if (hp == NULL)
38964562Sgshapiro				{
39064562Sgshapiro					smi_log(SMI_LOG_ERR,
39164562Sgshapiro						"%s: Unknown host name %s",
39264562Sgshapiro						name, at);
39366494Sgshapiro					return INVALID_SOCKET;
39464562Sgshapiro				}
39564562Sgshapiro				addr.sa.sa_family = hp->h_addrtype;
39664562Sgshapiro				switch (hp->h_addrtype)
39764562Sgshapiro				{
39864562Sgshapiro# if NETINET
39964562Sgshapiro				  case AF_INET:
400120256Sgshapiro					(void) memmove(&addr.sin.sin_addr,
401120256Sgshapiro						       hp->h_addr,
402120256Sgshapiro						       INADDRSZ);
40364562Sgshapiro					addr.sin.sin_port = port;
40464562Sgshapiro					break;
40564562Sgshapiro# endif /* NETINET */
40664562Sgshapiro
40764562Sgshapiro# if NETINET6
40864562Sgshapiro				  case AF_INET6:
409120256Sgshapiro					(void) memmove(&addr.sin6.sin6_addr,
410120256Sgshapiro						       hp->h_addr,
411120256Sgshapiro						       IN6ADDRSZ);
41264562Sgshapiro					addr.sin6.sin6_port = port;
41364562Sgshapiro					break;
41464562Sgshapiro# endif /* NETINET6 */
41564562Sgshapiro
41664562Sgshapiro				  default:
41764562Sgshapiro					smi_log(SMI_LOG_ERR,
41864562Sgshapiro						"%s: Unknown protocol for %s (%d)",
41964562Sgshapiro						name, at, hp->h_addrtype);
42066494Sgshapiro					return INVALID_SOCKET;
42164562Sgshapiro				}
42290792Sgshapiro# if NETINET6
42371345Sgshapiro				freehostent(hp);
424363466Sgshapiro# endif
42564562Sgshapiro			}
42664562Sgshapiro		}
42764562Sgshapiro		else
42864562Sgshapiro		{
42964562Sgshapiro			switch (addr.sa.sa_family)
43064562Sgshapiro			{
43164562Sgshapiro# if NETINET
43264562Sgshapiro			  case AF_INET:
43364562Sgshapiro				addr.sin.sin_port = port;
43464562Sgshapiro				break;
435363466Sgshapiro# endif
43664562Sgshapiro# if NETINET6
43764562Sgshapiro			  case AF_INET6:
43864562Sgshapiro				addr.sin6.sin6_port = port;
43964562Sgshapiro				break;
440363466Sgshapiro# endif
44164562Sgshapiro			}
44264562Sgshapiro		}
44364562Sgshapiro	}
44464562Sgshapiro#endif /* NETINET || NETINET6 */
44564562Sgshapiro
44664562Sgshapiro	sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
44764562Sgshapiro	if (!ValidSocket(sock))
44864562Sgshapiro	{
44964562Sgshapiro		smi_log(SMI_LOG_ERR,
45064562Sgshapiro			"%s: Unable to create new socket: %s",
45190792Sgshapiro			name, sm_errstring(errno));
45266494Sgshapiro		return INVALID_SOCKET;
45364562Sgshapiro	}
45464562Sgshapiro
45598121Sgshapiro	if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 ||
45698121Sgshapiro	    fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1)
45798121Sgshapiro	{
45898121Sgshapiro		smi_log(SMI_LOG_ERR,
45998121Sgshapiro			"%s: Unable to set close-on-exec: %s", name,
46098121Sgshapiro			sm_errstring(errno));
46198121Sgshapiro		(void) closesocket(sock);
46298121Sgshapiro		return INVALID_SOCKET;
46398121Sgshapiro	}
46498121Sgshapiro
465157001Sgshapiro	if (
466157001Sgshapiro#if NETUNIX
467157001Sgshapiro	    addr.sa.sa_family != AF_UNIX &&
468363466Sgshapiro#endif
469157001Sgshapiro	    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
47064562Sgshapiro		       sizeof(sockopt)) == -1)
47164562Sgshapiro	{
47264562Sgshapiro		smi_log(SMI_LOG_ERR,
473157001Sgshapiro			"%s: set reuseaddr failed (%s)", name,
47490792Sgshapiro			sm_errstring(errno));
47590792Sgshapiro		(void) closesocket(sock);
47666494Sgshapiro		return INVALID_SOCKET;
47764562Sgshapiro	}
47864562Sgshapiro
479125820Sgshapiro#if NETUNIX
480125820Sgshapiro	if (addr.sa.sa_family == AF_UNIX && rmsocket)
481125820Sgshapiro	{
482125820Sgshapiro		struct stat s;
483125820Sgshapiro
484125820Sgshapiro		if (stat(colon, &s) != 0)
485125820Sgshapiro		{
486125820Sgshapiro			if (errno != ENOENT)
487125820Sgshapiro			{
488125820Sgshapiro				smi_log(SMI_LOG_ERR,
489125820Sgshapiro					"%s: Unable to stat() %s: %s",
490125820Sgshapiro					name, colon, sm_errstring(errno));
491125820Sgshapiro				(void) closesocket(sock);
492125820Sgshapiro				return INVALID_SOCKET;
493125820Sgshapiro			}
494125820Sgshapiro		}
495125820Sgshapiro		else if (!S_ISSOCK(s.st_mode))
496125820Sgshapiro		{
497125820Sgshapiro			smi_log(SMI_LOG_ERR,
498125820Sgshapiro				"%s: %s is not a UNIX domain socket",
499125820Sgshapiro				name, colon);
500125820Sgshapiro			(void) closesocket(sock);
501125820Sgshapiro			return INVALID_SOCKET;
502125820Sgshapiro		}
503125820Sgshapiro		else if (unlink(colon) != 0)
504125820Sgshapiro		{
505125820Sgshapiro			smi_log(SMI_LOG_ERR,
506125820Sgshapiro				"%s: Unable to remove %s: %s",
507125820Sgshapiro				name, colon, sm_errstring(errno));
508125820Sgshapiro			(void) closesocket(sock);
509125820Sgshapiro			return INVALID_SOCKET;
510125820Sgshapiro		}
511125820Sgshapiro	}
512125820Sgshapiro#endif /* NETUNIX */
513125820Sgshapiro
51498121Sgshapiro	if (bind(sock, &addr.sa, L_socksize) < 0)
51564562Sgshapiro	{
51664562Sgshapiro		smi_log(SMI_LOG_ERR,
51764562Sgshapiro			"%s: Unable to bind to port %s: %s",
51890792Sgshapiro			name, conn, sm_errstring(errno));
51990792Sgshapiro		(void) closesocket(sock);
52066494Sgshapiro		return INVALID_SOCKET;
52164562Sgshapiro	}
52264562Sgshapiro
52364562Sgshapiro	if (listen(sock, backlog) < 0)
52464562Sgshapiro	{
52564562Sgshapiro		smi_log(SMI_LOG_ERR,
52690792Sgshapiro			"%s: listen call failed: %s", name,
52790792Sgshapiro			sm_errstring(errno));
52890792Sgshapiro		(void) closesocket(sock);
52966494Sgshapiro		return INVALID_SOCKET;
53064562Sgshapiro	}
53190792Sgshapiro
53290792Sgshapiro#if NETUNIX
53390792Sgshapiro	if (addr.sa.sa_family == AF_UNIX && len > 0)
53490792Sgshapiro	{
53590792Sgshapiro		/*
53690792Sgshapiro		**  Set global variable sockpath so the UNIX socket can be
53790792Sgshapiro		**  unlink()ed at exit.
53890792Sgshapiro		*/
53990792Sgshapiro
54090792Sgshapiro		sockpath = (char *) malloc(len);
54190792Sgshapiro		if (sockpath != NULL)
54290792Sgshapiro			(void) sm_strlcpy(sockpath, colon, len);
54390792Sgshapiro		else
54490792Sgshapiro		{
54590792Sgshapiro			smi_log(SMI_LOG_ERR,
54690792Sgshapiro				"%s: can't malloc(%d) for sockpath: %s",
547110560Sgshapiro				name, (int) len, sm_errstring(errno));
54890792Sgshapiro			(void) closesocket(sock);
54990792Sgshapiro			return INVALID_SOCKET;
55090792Sgshapiro		}
55190792Sgshapiro	}
55290792Sgshapiro#endif /* NETUNIX */
55398121Sgshapiro	L_family = addr.sa.sa_family;
55464562Sgshapiro	return sock;
55564562Sgshapiro}
556168515Sgshapiro
557168515Sgshapiro#if !_FFR_WORKERS_POOL
55890792Sgshapiro/*
55964562Sgshapiro**  MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
56064562Sgshapiro**
56164562Sgshapiro**	Parameters:
56264562Sgshapiro**		arg -- argument to pass to mi_handle_session()
56364562Sgshapiro**
56464562Sgshapiro**	Returns:
56564562Sgshapiro**		results from mi_handle_session()
56664562Sgshapiro*/
56764562Sgshapiro
568141858Sgshapirostatic void *
56964562Sgshapiromi_thread_handle_wrapper(arg)
57064562Sgshapiro	void *arg;
57164562Sgshapiro{
572168515Sgshapiro	/*
573168515Sgshapiro	**  Note: on some systems this generates a compiler warning:
574168515Sgshapiro	**  cast to pointer from integer of different size
575168515Sgshapiro	**  You can safely ignore this warning as the result of this function
576168515Sgshapiro	**  is not used anywhere.
577168515Sgshapiro	*/
578168515Sgshapiro
57964562Sgshapiro	return (void *) mi_handle_session(arg);
58064562Sgshapiro}
581168515Sgshapiro#endif /* _FFR_WORKERS_POOL */
58264562Sgshapiro
58390792Sgshapiro/*
58466494Sgshapiro**  MI_CLOSENER -- close listen socket
58564562Sgshapiro**
58666494Sgshapiro**	Parameters:
58766494Sgshapiro**		none.
58866494Sgshapiro**
58966494Sgshapiro**	Returns:
59066494Sgshapiro**		none.
59166494Sgshapiro*/
59266494Sgshapiro
59366494Sgshapirovoid
59466494Sgshapiromi_closener()
59566494Sgshapiro{
59671345Sgshapiro	(void) smutex_lock(&L_Mutex);
59766494Sgshapiro	if (ValidSocket(listenfd))
59866494Sgshapiro	{
59990792Sgshapiro#if NETUNIX
60090792Sgshapiro		bool removable;
60190792Sgshapiro		struct stat sockinfo;
60290792Sgshapiro		struct stat fileinfo;
60390792Sgshapiro
60490792Sgshapiro		removable = sockpath != NULL &&
60590792Sgshapiro			    geteuid() != 0 &&
60690792Sgshapiro			    fstat(listenfd, &sockinfo) == 0 &&
60790792Sgshapiro			    (S_ISFIFO(sockinfo.st_mode)
60890792Sgshapiro# ifdef S_ISSOCK
60990792Sgshapiro			     || S_ISSOCK(sockinfo.st_mode)
610363466Sgshapiro# endif
61190792Sgshapiro			    );
61290792Sgshapiro#endif /* NETUNIX */
61390792Sgshapiro
61490792Sgshapiro		(void) closesocket(listenfd);
61566494Sgshapiro		listenfd = INVALID_SOCKET;
61690792Sgshapiro
61790792Sgshapiro#if NETUNIX
61890792Sgshapiro		/* XXX sleep() some time before doing this? */
61990792Sgshapiro		if (sockpath != NULL)
62090792Sgshapiro		{
62190792Sgshapiro			if (removable &&
62290792Sgshapiro			    stat(sockpath, &fileinfo) == 0 &&
62390792Sgshapiro			    ((fileinfo.st_dev == sockinfo.st_dev &&
62490792Sgshapiro			      fileinfo.st_ino == sockinfo.st_ino)
62590792Sgshapiro# ifdef S_ISSOCK
62690792Sgshapiro			     || S_ISSOCK(fileinfo.st_mode)
627363466Sgshapiro# endif
62890792Sgshapiro			    )
62990792Sgshapiro			    &&
63090792Sgshapiro			    (S_ISFIFO(fileinfo.st_mode)
63190792Sgshapiro# ifdef S_ISSOCK
63290792Sgshapiro			     || S_ISSOCK(fileinfo.st_mode)
633363466Sgshapiro# endif
63490792Sgshapiro			     ))
63590792Sgshapiro				(void) unlink(sockpath);
63690792Sgshapiro			free(sockpath);
63790792Sgshapiro			sockpath = NULL;
63890792Sgshapiro		}
63990792Sgshapiro#endif /* NETUNIX */
64066494Sgshapiro	}
64171345Sgshapiro	(void) smutex_unlock(&L_Mutex);
64266494Sgshapiro}
64366494Sgshapiro
64490792Sgshapiro/*
64566494Sgshapiro**  MI_LISTENER -- Generic listener harness
64666494Sgshapiro**
64764562Sgshapiro**	Open up listen port
64864562Sgshapiro**	Wait for connections
64964562Sgshapiro**
65064562Sgshapiro**	Parameters:
65164562Sgshapiro**		conn -- connection description
65264562Sgshapiro**		dbg -- debug level
65364562Sgshapiro**		smfi -- filter structure to use
65464562Sgshapiro**		timeout -- timeout for reads/writes
655125820Sgshapiro**		backlog -- listen queue backlog size
65664562Sgshapiro**
65764562Sgshapiro**	Returns:
65864562Sgshapiro**		MI_SUCCESS -- Exited normally
65964562Sgshapiro**			   (session finished or we were told to exit)
66064562Sgshapiro**		MI_FAILURE -- Network initialization failed.
66164562Sgshapiro*/
66264562Sgshapiro
66390792Sgshapiro#if BROKEN_PTHREAD_SLEEP
66473188Sgshapiro
66573188Sgshapiro/*
66673188Sgshapiro**  Solaris 2.6, perhaps others, gets an internal threads library panic
66773188Sgshapiro**  when sleep() is used:
66873188Sgshapiro**
66973188Sgshapiro**  thread_create() failed, returned 11 (EINVAL)
67073188Sgshapiro**  co_enable, thr_create() returned error = 24
67173188Sgshapiro**  libthread panic: co_enable failed (PID: 17793 LWP 1)
67273188Sgshapiro**  stacktrace:
67373188Sgshapiro**	ef526b10
67473188Sgshapiro**	ef52646c
67573188Sgshapiro**	ef534cbc
67673188Sgshapiro**	156a4
67773188Sgshapiro**	14644
67873188Sgshapiro**	1413c
67973188Sgshapiro**	135e0
68073188Sgshapiro**	0
68173188Sgshapiro*/
68273188Sgshapiro
68390792Sgshapiro# define MI_SLEEP(s)							\
68473188Sgshapiro{									\
68573188Sgshapiro	int rs = 0;							\
68673188Sgshapiro	struct timeval st;						\
68773188Sgshapiro									\
68873188Sgshapiro	st.tv_sec = (s);						\
68973188Sgshapiro	st.tv_usec = 0;							\
69073188Sgshapiro	if (st.tv_sec > 0)						\
69173188Sgshapiro	{								\
692102528Sgshapiro		for (;;)						\
693102528Sgshapiro		{							\
694102528Sgshapiro			rs = select(0, NULL, NULL, NULL, &st);		\
695102528Sgshapiro			if (rs < 0 && errno == EINTR)			\
696102528Sgshapiro				continue;				\
697102528Sgshapiro			if (rs != 0)					\
698102528Sgshapiro			{						\
699102528Sgshapiro				smi_log(SMI_LOG_ERR,			\
700110560Sgshapiro					"MI_SLEEP(): select() returned non-zero result %d, errno = %d",	\
701102528Sgshapiro					rs, errno);			\
702102528Sgshapiro			}						\
703110560Sgshapiro			break;						\
704102528Sgshapiro		}							\
70573188Sgshapiro	}								\
70673188Sgshapiro}
70790792Sgshapiro#else /* BROKEN_PTHREAD_SLEEP */
70890792Sgshapiro# define MI_SLEEP(s)	sleep((s))
70990792Sgshapiro#endif /* BROKEN_PTHREAD_SLEEP */
71073188Sgshapiro
71164562Sgshapiroint
71266494Sgshapiromi_listener(conn, dbg, smfi, timeout, backlog)
71364562Sgshapiro	char *conn;
71464562Sgshapiro	int dbg;
71564562Sgshapiro	smfiDesc_ptr smfi;
71664562Sgshapiro	time_t timeout;
71766494Sgshapiro	int backlog;
71864562Sgshapiro{
71966494Sgshapiro	socket_t connfd = INVALID_SOCKET;
720132943Sgshapiro#if _FFR_DUP_FD
721132943Sgshapiro	socket_t dupfd = INVALID_SOCKET;
722363466Sgshapiro#endif
72364562Sgshapiro	int sockopt = 1;
724120256Sgshapiro	int r, mistop;
72564562Sgshapiro	int ret = MI_SUCCESS;
72690792Sgshapiro	int mcnt = 0;	/* error count for malloc() failures */
72790792Sgshapiro	int tcnt = 0;	/* error count for thread_create() failures */
72890792Sgshapiro	int acnt = 0;	/* error count for accept() failures */
72990792Sgshapiro	int scnt = 0;	/* error count for select() failures */
73077349Sgshapiro	int save_errno = 0;
731285229Sgshapiro	int fdflags;
732168515Sgshapiro#if !_FFR_WORKERS_POOL
73364562Sgshapiro	sthread_t thread_id;
734363466Sgshapiro#endif
73564562Sgshapiro	_SOCK_ADDR cliaddr;
73664562Sgshapiro	SOCKADDR_LEN_T clilen;
73764562Sgshapiro	SMFICTX_PTR ctx;
738111823Sgshapiro	FD_RD_VAR(rds, excs);
73964562Sgshapiro	struct timeval chktime;
74064562Sgshapiro
741125820Sgshapiro	if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE)
74264562Sgshapiro		return MI_FAILURE;
74371345Sgshapiro
744168515Sgshapiro#if _FFR_WORKERS_POOL
745168515Sgshapiro	if (mi_pool_controller_init() == MI_FAILURE)
746168515Sgshapiro		return MI_FAILURE;
747363466Sgshapiro#endif
748168515Sgshapiro
74998121Sgshapiro	clilen = L_socksize;
750120256Sgshapiro	while ((mistop = mi_stop()) == MILTER_CONT)
75164562Sgshapiro	{
75271345Sgshapiro		(void) smutex_lock(&L_Mutex);
75371345Sgshapiro		if (!ValidSocket(listenfd))
75471345Sgshapiro		{
755120256Sgshapiro			ret = MI_FAILURE;
756120256Sgshapiro			smi_log(SMI_LOG_ERR,
757120256Sgshapiro				"%s: listenfd=%d corrupted, terminating, errno=%d",
758120256Sgshapiro				smfi->xxfi_name, listenfd, errno);
75971345Sgshapiro			(void) smutex_unlock(&L_Mutex);
76071345Sgshapiro			break;
76171345Sgshapiro		}
76271345Sgshapiro
76364562Sgshapiro		/* select on interface ports */
764111823Sgshapiro		FD_RD_INIT(listenfd, rds, excs);
76564562Sgshapiro		chktime.tv_sec = MI_CHK_TIME;
76664562Sgshapiro		chktime.tv_usec = 0;
767111823Sgshapiro		r = FD_RD_READY(listenfd, rds, excs, &chktime);
76864562Sgshapiro		if (r == 0)		/* timeout */
76971345Sgshapiro		{
77071345Sgshapiro			(void) smutex_unlock(&L_Mutex);
77164562Sgshapiro			continue;	/* just check mi_stop() */
77271345Sgshapiro		}
77364562Sgshapiro		if (r < 0)
77464562Sgshapiro		{
77577349Sgshapiro			save_errno = errno;
77671345Sgshapiro			(void) smutex_unlock(&L_Mutex);
77777349Sgshapiro			if (save_errno == EINTR)
77864562Sgshapiro				continue;
77990792Sgshapiro			scnt++;
78090792Sgshapiro			smi_log(SMI_LOG_ERR,
781203004Sgshapiro				"%s: %s() failed (%s), %s",
782203004Sgshapiro				smfi->xxfi_name, MI_POLLSELECT,
783203004Sgshapiro				sm_errstring(save_errno),
78490792Sgshapiro				scnt >= MAX_FAILS_S ? "abort" : "try again");
78590792Sgshapiro			MI_SLEEP(scnt);
78690792Sgshapiro			if (scnt >= MAX_FAILS_S)
78790792Sgshapiro			{
78890792Sgshapiro				ret = MI_FAILURE;
78990792Sgshapiro				break;
79090792Sgshapiro			}
79190792Sgshapiro			continue;
79264562Sgshapiro		}
793111823Sgshapiro		if (!FD_IS_RD_RDY(listenfd, rds, excs))
79464562Sgshapiro		{
79564562Sgshapiro			/* some error: just stop for now... */
79664562Sgshapiro			ret = MI_FAILURE;
79771345Sgshapiro			(void) smutex_unlock(&L_Mutex);
79890792Sgshapiro			smi_log(SMI_LOG_ERR,
799111823Sgshapiro				"%s: %s() returned exception for socket, abort",
800111823Sgshapiro				smfi->xxfi_name, MI_POLLSELECT);
80164562Sgshapiro			break;
80264562Sgshapiro		}
80390792Sgshapiro		scnt = 0;	/* reset error counter for select() */
80464562Sgshapiro
805120256Sgshapiro		(void) memset(&cliaddr, '\0', sizeof cliaddr);
80664562Sgshapiro		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
80764562Sgshapiro				&clilen);
80877349Sgshapiro		save_errno = errno;
80971345Sgshapiro		(void) smutex_unlock(&L_Mutex);
81064562Sgshapiro
81173188Sgshapiro		/*
812168515Sgshapiro		**  If remote side closes before accept() finishes,
813168515Sgshapiro		**  sockaddr might not be fully filled in.
81473188Sgshapiro		*/
81573188Sgshapiro
81673188Sgshapiro		if (ValidSocket(connfd) &&
81773188Sgshapiro		    (clilen == 0 ||
81873188Sgshapiro# ifdef BSD4_4_SOCKADDR
81973188Sgshapiro		     cliaddr.sa.sa_len == 0 ||
820363466Sgshapiro# endif
82198121Sgshapiro		     cliaddr.sa.sa_family != L_family))
82273188Sgshapiro		{
82390792Sgshapiro			(void) closesocket(connfd);
82473188Sgshapiro			connfd = INVALID_SOCKET;
82577349Sgshapiro			save_errno = EINVAL;
82673188Sgshapiro		}
82773188Sgshapiro
828110560Sgshapiro		/* check if acceptable for select() */
829110560Sgshapiro		if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd))
830110560Sgshapiro		{
831110560Sgshapiro			(void) closesocket(connfd);
832110560Sgshapiro			connfd = INVALID_SOCKET;
833110560Sgshapiro			save_errno = ERANGE;
834110560Sgshapiro		}
835110560Sgshapiro
83666494Sgshapiro		if (!ValidSocket(connfd))
83764562Sgshapiro		{
838132943Sgshapiro			if (save_errno == EINTR
839132943Sgshapiro#ifdef EAGAIN
840132943Sgshapiro			    || save_errno == EAGAIN
841363466Sgshapiro#endif
842132943Sgshapiro#ifdef ECONNABORTED
843132943Sgshapiro			    || save_errno == ECONNABORTED
844363466Sgshapiro#endif
845132943Sgshapiro#ifdef EMFILE
846132943Sgshapiro			    || save_errno == EMFILE
847363466Sgshapiro#endif
848132943Sgshapiro#ifdef ENFILE
849132943Sgshapiro			    || save_errno == ENFILE
850363466Sgshapiro#endif
851132943Sgshapiro#ifdef ENOBUFS
852132943Sgshapiro			    || save_errno == ENOBUFS
853363466Sgshapiro#endif
854132943Sgshapiro#ifdef ENOMEM
855132943Sgshapiro			    || save_errno == ENOMEM
856363466Sgshapiro#endif
857132943Sgshapiro#ifdef ENOSR
858132943Sgshapiro			    || save_errno == ENOSR
859363466Sgshapiro#endif
860132943Sgshapiro#ifdef EWOULDBLOCK
861132943Sgshapiro			    || save_errno == EWOULDBLOCK
862363466Sgshapiro#endif
863132943Sgshapiro			   )
86477349Sgshapiro				continue;
86577349Sgshapiro			acnt++;
86690792Sgshapiro			smi_log(SMI_LOG_ERR,
86790792Sgshapiro				"%s: accept() returned invalid socket (%s), %s",
86890792Sgshapiro				smfi->xxfi_name, sm_errstring(save_errno),
86990792Sgshapiro				acnt >= MAX_FAILS_A ? "abort" : "try again");
87077349Sgshapiro			MI_SLEEP(acnt);
87177349Sgshapiro			if (acnt >= MAX_FAILS_A)
87277349Sgshapiro			{
87377349Sgshapiro				ret = MI_FAILURE;
87477349Sgshapiro				break;
87577349Sgshapiro			}
87664562Sgshapiro			continue;
87764562Sgshapiro		}
87890792Sgshapiro		acnt = 0;	/* reset error counter for accept() */
879132943Sgshapiro#if _FFR_DUP_FD
880132943Sgshapiro		dupfd = fcntl(connfd, F_DUPFD, 256);
881168515Sgshapiro		if (ValidSocket(dupfd) && SM_FD_OK_SELECT(dupfd))
882132943Sgshapiro		{
883132943Sgshapiro			close(connfd);
884132943Sgshapiro			connfd = dupfd;
885132943Sgshapiro			dupfd = INVALID_SOCKET;
886132943Sgshapiro		}
887132943Sgshapiro#endif /* _FFR_DUP_FD */
88864562Sgshapiro
889363466Sgshapiro		/*
890285229Sgshapiro		**  Need to set close-on-exec for connfd in case a user's
891285229Sgshapiro		**  filter starts other applications.
892285229Sgshapiro		**  Note: errors will not stop processing (for now).
893285229Sgshapiro		*/
894285229Sgshapiro
895285229Sgshapiro		if ((fdflags = fcntl(connfd, F_GETFD, 0)) == -1 ||
896285229Sgshapiro		    fcntl(connfd, F_SETFD, fdflags | FD_CLOEXEC) == -1)
897285229Sgshapiro		{
898285229Sgshapiro			smi_log(SMI_LOG_ERR,
899285229Sgshapiro				"%s: Unable to set close-on-exec: %s",
900285229Sgshapiro				smfi->xxfi_name, sm_errstring(errno));
901285229Sgshapiro		}
902285229Sgshapiro
90364562Sgshapiro		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
90464562Sgshapiro				(void *) &sockopt, sizeof sockopt) < 0)
90564562Sgshapiro		{
906157001Sgshapiro			smi_log(SMI_LOG_WARN,
907157001Sgshapiro				"%s: set keepalive failed (%s)",
90890792Sgshapiro				smfi->xxfi_name, sm_errstring(errno));
90964562Sgshapiro			/* XXX: continue? */
91064562Sgshapiro		}
91164562Sgshapiro		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
91264562Sgshapiro		{
91390792Sgshapiro			(void) closesocket(connfd);
91473188Sgshapiro			mcnt++;
91590792Sgshapiro			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s",
91690792Sgshapiro				smfi->xxfi_name, sm_errstring(save_errno),
91790792Sgshapiro				mcnt >= MAX_FAILS_M ? "abort" : "try again");
91873188Sgshapiro			MI_SLEEP(mcnt);
91973188Sgshapiro			if (mcnt >= MAX_FAILS_M)
92064562Sgshapiro			{
92164562Sgshapiro				ret = MI_FAILURE;
92264562Sgshapiro				break;
92364562Sgshapiro			}
92464562Sgshapiro			continue;
92564562Sgshapiro		}
92690792Sgshapiro		mcnt = 0;	/* reset error counter for malloc() */
927120256Sgshapiro		(void) memset(ctx, '\0', sizeof *ctx);
92864562Sgshapiro		ctx->ctx_sd = connfd;
92964562Sgshapiro		ctx->ctx_dbg = dbg;
93064562Sgshapiro		ctx->ctx_timeout = timeout;
93164562Sgshapiro		ctx->ctx_smfi = smfi;
93264562Sgshapiro		if (smfi->xxfi_connect == NULL)
93364562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOCONNECT;
93464562Sgshapiro		if (smfi->xxfi_helo == NULL)
93564562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHELO;
93664562Sgshapiro		if (smfi->xxfi_envfrom == NULL)
93764562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOMAIL;
93864562Sgshapiro		if (smfi->xxfi_envrcpt == NULL)
93964562Sgshapiro			ctx->ctx_pflags |= SMFIP_NORCPT;
94064562Sgshapiro		if (smfi->xxfi_header == NULL)
94164562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHDRS;
94264562Sgshapiro		if (smfi->xxfi_eoh == NULL)
94364562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOEOH;
94464562Sgshapiro		if (smfi->xxfi_body == NULL)
94564562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOBODY;
946168988Sgshapiro		if (smfi->xxfi_version <= 3 || smfi->xxfi_data == NULL)
947168515Sgshapiro			ctx->ctx_pflags |= SMFIP_NODATA;
948168988Sgshapiro		if (smfi->xxfi_version <= 2 || smfi->xxfi_unknown == NULL)
949168515Sgshapiro			ctx->ctx_pflags |= SMFIP_NOUNKNOWN;
95064562Sgshapiro
951168515Sgshapiro#if _FFR_WORKERS_POOL
952168515Sgshapiro# define LOG_CRT_FAIL	"%s: mi_start_session() failed: %d, %s"
953168515Sgshapiro		if ((r = mi_start_session(ctx)) != MI_SUCCESS)
954363466Sgshapiro#else
955168515Sgshapiro# define LOG_CRT_FAIL	"%s: thread_create() failed: %d, %s"
95664562Sgshapiro		if ((r = thread_create(&thread_id,
95764562Sgshapiro					mi_thread_handle_wrapper,
95871345Sgshapiro					(void *) ctx)) != 0)
959363466Sgshapiro#endif
96064562Sgshapiro		{
96190792Sgshapiro			tcnt++;
96264562Sgshapiro			smi_log(SMI_LOG_ERR,
963168515Sgshapiro				LOG_CRT_FAIL,
96490792Sgshapiro				smfi->xxfi_name,  r,
96590792Sgshapiro				tcnt >= MAX_FAILS_T ? "abort" : "try again");
96673188Sgshapiro			MI_SLEEP(tcnt);
96790792Sgshapiro			(void) closesocket(connfd);
96864562Sgshapiro			free(ctx);
96973188Sgshapiro			if (tcnt >= MAX_FAILS_T)
97064562Sgshapiro			{
97164562Sgshapiro				ret = MI_FAILURE;
97264562Sgshapiro				break;
97364562Sgshapiro			}
97464562Sgshapiro			continue;
97564562Sgshapiro		}
97673188Sgshapiro		tcnt = 0;
97764562Sgshapiro	}
97864562Sgshapiro	if (ret != MI_SUCCESS)
97964562Sgshapiro		mi_stop_milters(MILTER_ABRT);
98071345Sgshapiro	else
981120256Sgshapiro	{
982120256Sgshapiro		if (mistop != MILTER_CONT)
983120256Sgshapiro			smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
984120256Sgshapiro				smfi->xxfi_name, mistop);
98571345Sgshapiro		mi_closener();
986120256Sgshapiro	}
98771345Sgshapiro	(void) smutex_destroy(&L_Mutex);
98864562Sgshapiro	return ret;
98964562Sgshapiro}
990