listener.c revision 132943
164562Sgshapiro/*
2111823Sgshapiro *  Copyright (c) 1999-2003 Sendmail, 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>
12132943SgshapiroSM_RCSID("@(#)$Id: listener.c,v 8.109 2004/02/04 22:55:59 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>
2764562Sgshapiro# endif /* NETINET || NETINET6 */
2890792Sgshapiro
2990792Sgshapirostatic smutex_t L_Mutex;
3098121Sgshapirostatic int L_family;
3198121Sgshapirostatic SOCKADDR_LEN_T L_socksize;
3298121Sgshapirostatic socket_t listenfd = INVALID_SOCKET;
3390792Sgshapiro
34125820Sgshapirostatic socket_t mi_milteropen __P((char *, int, bool, char *));
3598121Sgshapiro
3690792Sgshapiro/*
3798121Sgshapiro**  MI_OPENSOCKET -- create the socket where this filter and the MTA will meet
3898121Sgshapiro**
39125820Sgshapiro**	Parameters:
4098121Sgshapiro**		conn -- connection description
4198121Sgshapiro**		backlog -- listen backlog
42125820Sgshapiro**		dbg -- debug level
43125820Sgshapiro**		rmsocket -- if true, try to unlink() the socket first
44132943Sgshapiro**			(UNIX domain sockets only)
4598121Sgshapiro**		smfi -- filter structure to use
4698121Sgshapiro**
47125820Sgshapiro**	Return value:
48125820Sgshapiro**		MI_SUCCESS/MI_FAILURE
4998121Sgshapiro*/
5098121Sgshapiro
5198121Sgshapiroint
52125820Sgshapiromi_opensocket(conn, backlog, dbg, rmsocket, smfi)
5398121Sgshapiro	char *conn;
5498121Sgshapiro	int backlog;
5598121Sgshapiro	int dbg;
56125820Sgshapiro	bool rmsocket;
5798121Sgshapiro	smfiDesc_ptr smfi;
5898121Sgshapiro{
5998121Sgshapiro	if (smfi == NULL || conn == NULL)
6098121Sgshapiro		return MI_FAILURE;
6198121Sgshapiro
6298121Sgshapiro	if (ValidSocket(listenfd))
6398121Sgshapiro		return MI_SUCCESS;
6498121Sgshapiro
6598121Sgshapiro	if (dbg > 0)
6698121Sgshapiro	{
6798121Sgshapiro		smi_log(SMI_LOG_DEBUG,
6898121Sgshapiro			"%s: Opening listen socket on conn %s",
6998121Sgshapiro			smfi->xxfi_name, conn);
7098121Sgshapiro	}
7198121Sgshapiro	(void) smutex_init(&L_Mutex);
7298121Sgshapiro	(void) smutex_lock(&L_Mutex);
73125820Sgshapiro	listenfd = mi_milteropen(conn, backlog, rmsocket, smfi->xxfi_name);
7498121Sgshapiro	if (!ValidSocket(listenfd))
7598121Sgshapiro	{
7698121Sgshapiro		smi_log(SMI_LOG_FATAL,
7798121Sgshapiro			"%s: Unable to create listening socket on conn %s",
7898121Sgshapiro			smfi->xxfi_name, conn);
7998121Sgshapiro		(void) smutex_unlock(&L_Mutex);
8098121Sgshapiro		return MI_FAILURE;
8198121Sgshapiro	}
82132943Sgshapiro#if !SM_CONF_POLL
83110560Sgshapiro	if (!SM_FD_OK_SELECT(listenfd))
84110560Sgshapiro	{
85110560Sgshapiro		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
86110560Sgshapiro			smfi->xxfi_name, listenfd, FD_SETSIZE);
87110560Sgshapiro		(void) smutex_unlock(&L_Mutex);
88110560Sgshapiro		return MI_FAILURE;
89110560Sgshapiro	}
90132943Sgshapiro#endif /* !SM_CONF_POLL */
9198121Sgshapiro	return MI_SUCCESS;
9298121Sgshapiro}
9398121Sgshapiro
9498121Sgshapiro/*
9564562Sgshapiro**  MI_MILTEROPEN -- setup socket to listen on
9664562Sgshapiro**
9764562Sgshapiro**	Parameters:
9864562Sgshapiro**		conn -- connection description
9964562Sgshapiro**		backlog -- listen backlog
100125820Sgshapiro**		rmsocket -- if true, try to unlink() the socket first
101125820Sgshapiro**			(UNIX domain sockets only)
10273188Sgshapiro**		name -- name for logging
10364562Sgshapiro**
10464562Sgshapiro**	Returns:
10564562Sgshapiro**		socket upon success, error code otherwise.
10690792Sgshapiro**
10790792Sgshapiro**	Side effect:
10890792Sgshapiro**		sets sockpath if UNIX socket.
10964562Sgshapiro*/
11064562Sgshapiro
11190792Sgshapiro#if NETUNIX
11290792Sgshapirostatic char	*sockpath = NULL;
11390792Sgshapiro#endif /* NETUNIX */
11490792Sgshapiro
11566494Sgshapirostatic socket_t
116125820Sgshapiromi_milteropen(conn, backlog, rmsocket, name)
11764562Sgshapiro	char *conn;
11864562Sgshapiro	int backlog;
119125820Sgshapiro	bool rmsocket;
12064562Sgshapiro	char *name;
12164562Sgshapiro{
12266494Sgshapiro	socket_t sock;
12364562Sgshapiro	int sockopt = 1;
12498121Sgshapiro	int fdflags;
12590792Sgshapiro	size_t len = 0;
12664562Sgshapiro	char *p;
12764562Sgshapiro	char *colon;
12864562Sgshapiro	char *at;
12964562Sgshapiro	SOCKADDR addr;
13064562Sgshapiro
13164562Sgshapiro	if (conn == NULL || conn[0] == '\0')
13264562Sgshapiro	{
13364562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
13464562Sgshapiro			name);
13566494Sgshapiro		return INVALID_SOCKET;
13664562Sgshapiro	}
13764562Sgshapiro	(void) memset(&addr, '\0', sizeof addr);
13864562Sgshapiro
13964562Sgshapiro	/* protocol:filename or protocol:port@host */
14064562Sgshapiro	p = conn;
14164562Sgshapiro	colon = strchr(p, ':');
14264562Sgshapiro	if (colon != NULL)
14364562Sgshapiro	{
14464562Sgshapiro		*colon = '\0';
14564562Sgshapiro
14694334Sgshapiro		if (*p == '\0')
14764562Sgshapiro		{
14864562Sgshapiro#if NETUNIX
14964562Sgshapiro			/* default to AF_UNIX */
15094334Sgshapiro			addr.sa.sa_family = AF_UNIX;
15198121Sgshapiro			L_socksize = sizeof (struct sockaddr_un);
15264562Sgshapiro#else /* NETUNIX */
15364562Sgshapiro# if NETINET
15464562Sgshapiro			/* default to AF_INET */
15564562Sgshapiro			addr.sa.sa_family = AF_INET;
15698121Sgshapiro			L_socksize = sizeof addr.sin;
15764562Sgshapiro# else /* NETINET */
15864562Sgshapiro#  if NETINET6
15964562Sgshapiro			/* default to AF_INET6 */
16064562Sgshapiro			addr.sa.sa_family = AF_INET6;
16198121Sgshapiro			L_socksize = sizeof addr.sin6;
16264562Sgshapiro#  else /* NETINET6 */
16364562Sgshapiro			/* no protocols available */
16464562Sgshapiro			smi_log(SMI_LOG_ERR,
16564562Sgshapiro				"%s: no valid socket protocols available",
16664562Sgshapiro				name);
16766494Sgshapiro			return INVALID_SOCKET;
16864562Sgshapiro#  endif /* NETINET6 */
16964562Sgshapiro# endif /* NETINET */
17064562Sgshapiro#endif /* NETUNIX */
17164562Sgshapiro		}
17264562Sgshapiro#if NETUNIX
17364562Sgshapiro		else if (strcasecmp(p, "unix") == 0 ||
17464562Sgshapiro			 strcasecmp(p, "local") == 0)
17564562Sgshapiro		{
17664562Sgshapiro			addr.sa.sa_family = AF_UNIX;
17798121Sgshapiro			L_socksize = sizeof (struct sockaddr_un);
17864562Sgshapiro		}
17964562Sgshapiro#endif /* NETUNIX */
18064562Sgshapiro#if NETINET
18164562Sgshapiro		else if (strcasecmp(p, "inet") == 0)
18264562Sgshapiro		{
18364562Sgshapiro			addr.sa.sa_family = AF_INET;
18498121Sgshapiro			L_socksize = sizeof addr.sin;
18564562Sgshapiro		}
18664562Sgshapiro#endif /* NETINET */
18764562Sgshapiro#if NETINET6
18864562Sgshapiro		else if (strcasecmp(p, "inet6") == 0)
18964562Sgshapiro		{
19064562Sgshapiro			addr.sa.sa_family = AF_INET6;
19198121Sgshapiro			L_socksize = sizeof addr.sin6;
19264562Sgshapiro		}
19364562Sgshapiro#endif /* NETINET6 */
19464562Sgshapiro		else
19564562Sgshapiro		{
19664562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
19764562Sgshapiro				name, p);
19866494Sgshapiro			return INVALID_SOCKET;
19964562Sgshapiro		}
20064562Sgshapiro		*colon++ = ':';
20164562Sgshapiro	}
20264562Sgshapiro	else
20364562Sgshapiro	{
20464562Sgshapiro		colon = p;
20564562Sgshapiro#if NETUNIX
20664562Sgshapiro		/* default to AF_UNIX */
20794334Sgshapiro		addr.sa.sa_family = AF_UNIX;
20898121Sgshapiro		L_socksize = sizeof (struct sockaddr_un);
20964562Sgshapiro#else /* NETUNIX */
21064562Sgshapiro# if NETINET
21164562Sgshapiro		/* default to AF_INET */
21264562Sgshapiro		addr.sa.sa_family = AF_INET;
21398121Sgshapiro		L_socksize = sizeof addr.sin;
21464562Sgshapiro# else /* NETINET */
21564562Sgshapiro#  if NETINET6
21664562Sgshapiro		/* default to AF_INET6 */
21764562Sgshapiro		addr.sa.sa_family = AF_INET6;
21898121Sgshapiro		L_socksize = sizeof addr.sin6;
21964562Sgshapiro#  else /* NETINET6 */
22064562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
22164562Sgshapiro			name, p);
22266494Sgshapiro		return INVALID_SOCKET;
22364562Sgshapiro#  endif /* NETINET6 */
22464562Sgshapiro# endif /* NETINET */
22564562Sgshapiro#endif /* NETUNIX */
22664562Sgshapiro	}
22764562Sgshapiro
22864562Sgshapiro#if NETUNIX
22964562Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
23064562Sgshapiro	{
23164562Sgshapiro# if 0
23264562Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
23364562Sgshapiro# endif /* 0 */
23464562Sgshapiro
23564562Sgshapiro		at = colon;
23690792Sgshapiro		len = strlen(colon) + 1;
23790792Sgshapiro		if (len >= sizeof addr.sunix.sun_path)
23864562Sgshapiro		{
23964562Sgshapiro			errno = EINVAL;
24064562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
24164562Sgshapiro				name, colon);
24266494Sgshapiro			return INVALID_SOCKET;
24364562Sgshapiro		}
24490792Sgshapiro		(void) sm_strlcpy(addr.sunix.sun_path, colon,
24590792Sgshapiro				sizeof addr.sunix.sun_path);
24664562Sgshapiro# if 0
24764562Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
24864562Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
24964562Sgshapiro
25064562Sgshapiro		/* if not safe, don't create */
25164562Sgshapiro		if (errno != 0)
25264562Sgshapiro		{
25364562Sgshapiro			smi_log(SMI_LOG_ERR,
25464562Sgshapiro				"%s: UNIX socket name %s unsafe",
25564562Sgshapiro				name, colon);
25666494Sgshapiro			return INVALID_SOCKET;
25764562Sgshapiro		}
25864562Sgshapiro# endif /* 0 */
25964562Sgshapiro	}
26064562Sgshapiro#endif /* NETUNIX */
26164562Sgshapiro
26264562Sgshapiro#if NETINET || NETINET6
26364562Sgshapiro	if (
26464562Sgshapiro# if NETINET
26564562Sgshapiro	    addr.sa.sa_family == AF_INET
26664562Sgshapiro# endif /* NETINET */
26764562Sgshapiro# if NETINET && NETINET6
26864562Sgshapiro	    ||
26964562Sgshapiro# endif /* NETINET && NETINET6 */
27064562Sgshapiro# if NETINET6
27164562Sgshapiro	    addr.sa.sa_family == AF_INET6
27264562Sgshapiro# endif /* NETINET6 */
27364562Sgshapiro	   )
27464562Sgshapiro	{
27590792Sgshapiro		unsigned short port;
27664562Sgshapiro
27764562Sgshapiro		/* Parse port@host */
27864562Sgshapiro		at = strchr(colon, '@');
27964562Sgshapiro		if (at == NULL)
28064562Sgshapiro		{
28164562Sgshapiro			switch (addr.sa.sa_family)
28264562Sgshapiro			{
28364562Sgshapiro# if NETINET
28464562Sgshapiro			  case AF_INET:
28564562Sgshapiro				addr.sin.sin_addr.s_addr = INADDR_ANY;
28664562Sgshapiro				break;
28764562Sgshapiro# endif /* NETINET */
28864562Sgshapiro
28964562Sgshapiro# if NETINET6
29064562Sgshapiro			  case AF_INET6:
29164562Sgshapiro				addr.sin6.sin6_addr = in6addr_any;
29264562Sgshapiro				break;
29364562Sgshapiro# endif /* NETINET6 */
29464562Sgshapiro			}
29564562Sgshapiro		}
29664562Sgshapiro		else
29764562Sgshapiro			*at = '\0';
29864562Sgshapiro
29964562Sgshapiro		if (isascii(*colon) && isdigit(*colon))
30090792Sgshapiro			port = htons((unsigned short) atoi(colon));
30164562Sgshapiro		else
30264562Sgshapiro		{
30364562Sgshapiro# ifdef NO_GETSERVBYNAME
30464562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
30564562Sgshapiro				name, colon);
30666494Sgshapiro			return INVALID_SOCKET;
30764562Sgshapiro# else /* NO_GETSERVBYNAME */
30864562Sgshapiro			register struct servent *sp;
30964562Sgshapiro
31064562Sgshapiro			sp = getservbyname(colon, "tcp");
31164562Sgshapiro			if (sp == NULL)
31264562Sgshapiro			{
31364562Sgshapiro				smi_log(SMI_LOG_ERR,
31464562Sgshapiro					"%s: unknown port name %s",
31564562Sgshapiro					name, colon);
31666494Sgshapiro				return INVALID_SOCKET;
31764562Sgshapiro			}
31864562Sgshapiro			port = sp->s_port;
31964562Sgshapiro# endif /* NO_GETSERVBYNAME */
32064562Sgshapiro		}
32164562Sgshapiro		if (at != NULL)
32264562Sgshapiro		{
32364562Sgshapiro			*at++ = '@';
32464562Sgshapiro			if (*at == '[')
32564562Sgshapiro			{
32664562Sgshapiro				char *end;
32764562Sgshapiro
32864562Sgshapiro				end = strchr(at, ']');
32964562Sgshapiro				if (end != NULL)
33064562Sgshapiro				{
33190792Sgshapiro					bool found = false;
33264562Sgshapiro# if NETINET
33364562Sgshapiro					unsigned long hid = INADDR_NONE;
33464562Sgshapiro# endif /* NETINET */
33564562Sgshapiro# if NETINET6
33664562Sgshapiro					struct sockaddr_in6 hid6;
33764562Sgshapiro# endif /* NETINET6 */
33864562Sgshapiro
33964562Sgshapiro					*end = '\0';
34064562Sgshapiro# if NETINET
34164562Sgshapiro					if (addr.sa.sa_family == AF_INET &&
34290792Sgshapiro					    (hid = inet_addr(&at[1])) != INADDR_NONE)
34364562Sgshapiro					{
34464562Sgshapiro						addr.sin.sin_addr.s_addr = hid;
34564562Sgshapiro						addr.sin.sin_port = port;
34690792Sgshapiro						found = true;
34764562Sgshapiro					}
34864562Sgshapiro# endif /* NETINET */
34964562Sgshapiro# if NETINET6
35064562Sgshapiro					(void) memset(&hid6, '\0', sizeof hid6);
35164562Sgshapiro					if (addr.sa.sa_family == AF_INET6 &&
35290792Sgshapiro					    mi_inet_pton(AF_INET6, &at[1],
35390792Sgshapiro							 &hid6.sin6_addr) == 1)
35464562Sgshapiro					{
35564562Sgshapiro						addr.sin6.sin6_addr = hid6.sin6_addr;
35664562Sgshapiro						addr.sin6.sin6_port = port;
35790792Sgshapiro						found = true;
35864562Sgshapiro					}
35964562Sgshapiro# endif /* NETINET6 */
36064562Sgshapiro					*end = ']';
36164562Sgshapiro					if (!found)
36264562Sgshapiro					{
36364562Sgshapiro						smi_log(SMI_LOG_ERR,
36464562Sgshapiro							"%s: Invalid numeric domain spec \"%s\"",
36564562Sgshapiro							name, at);
36666494Sgshapiro						return INVALID_SOCKET;
36764562Sgshapiro					}
36864562Sgshapiro				}
36964562Sgshapiro				else
37064562Sgshapiro				{
37164562Sgshapiro					smi_log(SMI_LOG_ERR,
37264562Sgshapiro						"%s: Invalid numeric domain spec \"%s\"",
37364562Sgshapiro						name, at);
37466494Sgshapiro					return INVALID_SOCKET;
37564562Sgshapiro				}
37664562Sgshapiro			}
37764562Sgshapiro			else
37864562Sgshapiro			{
37971345Sgshapiro				struct hostent *hp = NULL;
38071345Sgshapiro
38164562Sgshapiro				hp = mi_gethostbyname(at, addr.sa.sa_family);
38264562Sgshapiro				if (hp == NULL)
38364562Sgshapiro				{
38464562Sgshapiro					smi_log(SMI_LOG_ERR,
38564562Sgshapiro						"%s: Unknown host name %s",
38664562Sgshapiro						name, at);
38766494Sgshapiro					return INVALID_SOCKET;
38864562Sgshapiro				}
38964562Sgshapiro				addr.sa.sa_family = hp->h_addrtype;
39064562Sgshapiro				switch (hp->h_addrtype)
39164562Sgshapiro				{
39264562Sgshapiro# if NETINET
39364562Sgshapiro				  case AF_INET:
394120256Sgshapiro					(void) memmove(&addr.sin.sin_addr,
395120256Sgshapiro						       hp->h_addr,
396120256Sgshapiro						       INADDRSZ);
39764562Sgshapiro					addr.sin.sin_port = port;
39864562Sgshapiro					break;
39964562Sgshapiro# endif /* NETINET */
40064562Sgshapiro
40164562Sgshapiro# if NETINET6
40264562Sgshapiro				  case AF_INET6:
403120256Sgshapiro					(void) memmove(&addr.sin6.sin6_addr,
404120256Sgshapiro						       hp->h_addr,
405120256Sgshapiro						       IN6ADDRSZ);
40664562Sgshapiro					addr.sin6.sin6_port = port;
40764562Sgshapiro					break;
40864562Sgshapiro# endif /* NETINET6 */
40964562Sgshapiro
41064562Sgshapiro				  default:
41164562Sgshapiro					smi_log(SMI_LOG_ERR,
41264562Sgshapiro						"%s: Unknown protocol for %s (%d)",
41364562Sgshapiro						name, at, hp->h_addrtype);
41466494Sgshapiro					return INVALID_SOCKET;
41564562Sgshapiro				}
41690792Sgshapiro# if NETINET6
41771345Sgshapiro				freehostent(hp);
41890792Sgshapiro# endif /* NETINET6 */
41964562Sgshapiro			}
42064562Sgshapiro		}
42164562Sgshapiro		else
42264562Sgshapiro		{
42364562Sgshapiro			switch (addr.sa.sa_family)
42464562Sgshapiro			{
42564562Sgshapiro# if NETINET
42664562Sgshapiro			  case AF_INET:
42764562Sgshapiro				addr.sin.sin_port = port;
42864562Sgshapiro				break;
42964562Sgshapiro# endif /* NETINET */
43064562Sgshapiro# if NETINET6
43164562Sgshapiro			  case AF_INET6:
43264562Sgshapiro				addr.sin6.sin6_port = port;
43364562Sgshapiro				break;
43464562Sgshapiro# endif /* NETINET6 */
43564562Sgshapiro			}
43664562Sgshapiro		}
43764562Sgshapiro	}
43864562Sgshapiro#endif /* NETINET || NETINET6 */
43964562Sgshapiro
44064562Sgshapiro	sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
44164562Sgshapiro	if (!ValidSocket(sock))
44264562Sgshapiro	{
44364562Sgshapiro		smi_log(SMI_LOG_ERR,
44464562Sgshapiro			"%s: Unable to create new socket: %s",
44590792Sgshapiro			name, sm_errstring(errno));
44666494Sgshapiro		return INVALID_SOCKET;
44764562Sgshapiro	}
44864562Sgshapiro
44998121Sgshapiro	if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 ||
45098121Sgshapiro	    fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1)
45198121Sgshapiro	{
45298121Sgshapiro		smi_log(SMI_LOG_ERR,
45398121Sgshapiro			"%s: Unable to set close-on-exec: %s", name,
45498121Sgshapiro			sm_errstring(errno));
45598121Sgshapiro		(void) closesocket(sock);
45698121Sgshapiro		return INVALID_SOCKET;
45798121Sgshapiro	}
45898121Sgshapiro
45964562Sgshapiro	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
46064562Sgshapiro		       sizeof(sockopt)) == -1)
46164562Sgshapiro	{
46264562Sgshapiro		smi_log(SMI_LOG_ERR,
46390792Sgshapiro			"%s: Unable to setsockopt: %s", name,
46490792Sgshapiro			sm_errstring(errno));
46590792Sgshapiro		(void) closesocket(sock);
46666494Sgshapiro		return INVALID_SOCKET;
46764562Sgshapiro	}
46864562Sgshapiro
469125820Sgshapiro#if NETUNIX
470125820Sgshapiro	if (addr.sa.sa_family == AF_UNIX && rmsocket)
471125820Sgshapiro	{
472125820Sgshapiro		struct stat s;
473125820Sgshapiro
474125820Sgshapiro		if (stat(colon, &s) != 0)
475125820Sgshapiro		{
476125820Sgshapiro			if (errno != ENOENT)
477125820Sgshapiro			{
478125820Sgshapiro				smi_log(SMI_LOG_ERR,
479125820Sgshapiro					"%s: Unable to stat() %s: %s",
480125820Sgshapiro					name, colon, sm_errstring(errno));
481125820Sgshapiro				(void) closesocket(sock);
482125820Sgshapiro				return INVALID_SOCKET;
483125820Sgshapiro			}
484125820Sgshapiro		}
485125820Sgshapiro		else if (!S_ISSOCK(s.st_mode))
486125820Sgshapiro		{
487125820Sgshapiro			smi_log(SMI_LOG_ERR,
488125820Sgshapiro				"%s: %s is not a UNIX domain socket",
489125820Sgshapiro				name, colon);
490125820Sgshapiro			(void) closesocket(sock);
491125820Sgshapiro			return INVALID_SOCKET;
492125820Sgshapiro		}
493125820Sgshapiro		else if (unlink(colon) != 0)
494125820Sgshapiro		{
495125820Sgshapiro			smi_log(SMI_LOG_ERR,
496125820Sgshapiro				"%s: Unable to remove %s: %s",
497125820Sgshapiro				name, colon, sm_errstring(errno));
498125820Sgshapiro			(void) closesocket(sock);
499125820Sgshapiro			return INVALID_SOCKET;
500125820Sgshapiro		}
501125820Sgshapiro	}
502125820Sgshapiro#endif /* NETUNIX */
503125820Sgshapiro
50498121Sgshapiro	if (bind(sock, &addr.sa, L_socksize) < 0)
50564562Sgshapiro	{
50664562Sgshapiro		smi_log(SMI_LOG_ERR,
50764562Sgshapiro			"%s: Unable to bind to port %s: %s",
50890792Sgshapiro			name, conn, sm_errstring(errno));
50990792Sgshapiro		(void) closesocket(sock);
51066494Sgshapiro		return INVALID_SOCKET;
51164562Sgshapiro	}
51264562Sgshapiro
51364562Sgshapiro	if (listen(sock, backlog) < 0)
51464562Sgshapiro	{
51564562Sgshapiro		smi_log(SMI_LOG_ERR,
51690792Sgshapiro			"%s: listen call failed: %s", name,
51790792Sgshapiro			sm_errstring(errno));
51890792Sgshapiro		(void) closesocket(sock);
51966494Sgshapiro		return INVALID_SOCKET;
52064562Sgshapiro	}
52190792Sgshapiro
52290792Sgshapiro#if NETUNIX
52390792Sgshapiro	if (addr.sa.sa_family == AF_UNIX && len > 0)
52490792Sgshapiro	{
52590792Sgshapiro		/*
52690792Sgshapiro		**  Set global variable sockpath so the UNIX socket can be
52790792Sgshapiro		**  unlink()ed at exit.
52890792Sgshapiro		*/
52990792Sgshapiro
53090792Sgshapiro		sockpath = (char *) malloc(len);
53190792Sgshapiro		if (sockpath != NULL)
53290792Sgshapiro			(void) sm_strlcpy(sockpath, colon, len);
53390792Sgshapiro		else
53490792Sgshapiro		{
53590792Sgshapiro			smi_log(SMI_LOG_ERR,
53690792Sgshapiro				"%s: can't malloc(%d) for sockpath: %s",
537110560Sgshapiro				name, (int) len, sm_errstring(errno));
53890792Sgshapiro			(void) closesocket(sock);
53990792Sgshapiro			return INVALID_SOCKET;
54090792Sgshapiro		}
54190792Sgshapiro	}
54290792Sgshapiro#endif /* NETUNIX */
54398121Sgshapiro	L_family = addr.sa.sa_family;
54464562Sgshapiro	return sock;
54564562Sgshapiro}
54690792Sgshapiro/*
54764562Sgshapiro**  MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
54864562Sgshapiro**
54964562Sgshapiro**	Parameters:
55064562Sgshapiro**		arg -- argument to pass to mi_handle_session()
55164562Sgshapiro**
55264562Sgshapiro**	Returns:
55364562Sgshapiro**		results from mi_handle_session()
55464562Sgshapiro*/
55564562Sgshapiro
55664562Sgshapirovoid *
55764562Sgshapiromi_thread_handle_wrapper(arg)
55864562Sgshapiro	void *arg;
55964562Sgshapiro{
56064562Sgshapiro	return (void *) mi_handle_session(arg);
56164562Sgshapiro}
56264562Sgshapiro
56390792Sgshapiro/*
56466494Sgshapiro**  MI_CLOSENER -- close listen socket
56564562Sgshapiro**
56690792Sgshapiro**	NOTE: It is assumed that this function is called from a
56790792Sgshapiro**	      function that has a mutex lock (currently mi_stop_milters()).
56890792Sgshapiro**
56966494Sgshapiro**	Parameters:
57066494Sgshapiro**		none.
57166494Sgshapiro**
57266494Sgshapiro**	Returns:
57366494Sgshapiro**		none.
57466494Sgshapiro*/
57566494Sgshapiro
57666494Sgshapirovoid
57766494Sgshapiromi_closener()
57866494Sgshapiro{
57971345Sgshapiro	(void) smutex_lock(&L_Mutex);
58066494Sgshapiro	if (ValidSocket(listenfd))
58166494Sgshapiro	{
58290792Sgshapiro#if NETUNIX
58390792Sgshapiro		bool removable;
58490792Sgshapiro		struct stat sockinfo;
58590792Sgshapiro		struct stat fileinfo;
58690792Sgshapiro
58790792Sgshapiro		removable = sockpath != NULL &&
58890792Sgshapiro			    geteuid() != 0 &&
58990792Sgshapiro			    fstat(listenfd, &sockinfo) == 0 &&
59090792Sgshapiro			    (S_ISFIFO(sockinfo.st_mode)
59190792Sgshapiro# ifdef S_ISSOCK
59290792Sgshapiro			     || S_ISSOCK(sockinfo.st_mode)
59390792Sgshapiro# endif /* S_ISSOCK */
59490792Sgshapiro			    );
59590792Sgshapiro#endif /* NETUNIX */
59690792Sgshapiro
59790792Sgshapiro		(void) closesocket(listenfd);
59866494Sgshapiro		listenfd = INVALID_SOCKET;
59990792Sgshapiro
60090792Sgshapiro#if NETUNIX
60190792Sgshapiro		/* XXX sleep() some time before doing this? */
60290792Sgshapiro		if (sockpath != NULL)
60390792Sgshapiro		{
60490792Sgshapiro			if (removable &&
60590792Sgshapiro			    stat(sockpath, &fileinfo) == 0 &&
60690792Sgshapiro			    ((fileinfo.st_dev == sockinfo.st_dev &&
60790792Sgshapiro			      fileinfo.st_ino == sockinfo.st_ino)
60890792Sgshapiro# ifdef S_ISSOCK
60990792Sgshapiro			     || S_ISSOCK(fileinfo.st_mode)
61090792Sgshapiro# endif /* S_ISSOCK */
61190792Sgshapiro			    )
61290792Sgshapiro			    &&
61390792Sgshapiro			    (S_ISFIFO(fileinfo.st_mode)
61490792Sgshapiro# ifdef S_ISSOCK
61590792Sgshapiro			     || S_ISSOCK(fileinfo.st_mode)
61690792Sgshapiro# endif /* S_ISSOCK */
61790792Sgshapiro			     ))
61890792Sgshapiro				(void) unlink(sockpath);
61990792Sgshapiro			free(sockpath);
62090792Sgshapiro			sockpath = NULL;
62190792Sgshapiro		}
62290792Sgshapiro#endif /* NETUNIX */
62366494Sgshapiro	}
62471345Sgshapiro	(void) smutex_unlock(&L_Mutex);
62566494Sgshapiro}
62666494Sgshapiro
62790792Sgshapiro/*
62866494Sgshapiro**  MI_LISTENER -- Generic listener harness
62966494Sgshapiro**
63064562Sgshapiro**	Open up listen port
63164562Sgshapiro**	Wait for connections
63264562Sgshapiro**
63364562Sgshapiro**	Parameters:
63464562Sgshapiro**		conn -- connection description
63564562Sgshapiro**		dbg -- debug level
63664562Sgshapiro**		smfi -- filter structure to use
63764562Sgshapiro**		timeout -- timeout for reads/writes
638125820Sgshapiro**		backlog -- listen queue backlog size
63964562Sgshapiro**
64064562Sgshapiro**	Returns:
64164562Sgshapiro**		MI_SUCCESS -- Exited normally
64264562Sgshapiro**			   (session finished or we were told to exit)
64364562Sgshapiro**		MI_FAILURE -- Network initialization failed.
64464562Sgshapiro*/
64564562Sgshapiro
64690792Sgshapiro#if BROKEN_PTHREAD_SLEEP
64773188Sgshapiro
64873188Sgshapiro/*
64973188Sgshapiro**  Solaris 2.6, perhaps others, gets an internal threads library panic
65073188Sgshapiro**  when sleep() is used:
65173188Sgshapiro**
65273188Sgshapiro**  thread_create() failed, returned 11 (EINVAL)
65373188Sgshapiro**  co_enable, thr_create() returned error = 24
65473188Sgshapiro**  libthread panic: co_enable failed (PID: 17793 LWP 1)
65573188Sgshapiro**  stacktrace:
65673188Sgshapiro**	ef526b10
65773188Sgshapiro**	ef52646c
65873188Sgshapiro**	ef534cbc
65973188Sgshapiro**	156a4
66073188Sgshapiro**	14644
66173188Sgshapiro**	1413c
66273188Sgshapiro**	135e0
66373188Sgshapiro**	0
66473188Sgshapiro*/
66573188Sgshapiro
66690792Sgshapiro# define MI_SLEEP(s)							\
66773188Sgshapiro{									\
66873188Sgshapiro	int rs = 0;							\
66973188Sgshapiro	struct timeval st;						\
67073188Sgshapiro									\
67173188Sgshapiro	st.tv_sec = (s);						\
67273188Sgshapiro	st.tv_usec = 0;							\
67373188Sgshapiro	if (st.tv_sec > 0)						\
67473188Sgshapiro	{								\
675102528Sgshapiro		for (;;)						\
676102528Sgshapiro		{							\
677102528Sgshapiro			rs = select(0, NULL, NULL, NULL, &st);		\
678102528Sgshapiro			if (rs < 0 && errno == EINTR)			\
679102528Sgshapiro				continue;				\
680102528Sgshapiro			if (rs != 0)					\
681102528Sgshapiro			{						\
682102528Sgshapiro				smi_log(SMI_LOG_ERR,			\
683110560Sgshapiro					"MI_SLEEP(): select() returned non-zero result %d, errno = %d",	\
684102528Sgshapiro					rs, errno);			\
685102528Sgshapiro			}						\
686110560Sgshapiro			break;						\
687102528Sgshapiro		}							\
68873188Sgshapiro	}								\
68973188Sgshapiro}
69090792Sgshapiro#else /* BROKEN_PTHREAD_SLEEP */
69190792Sgshapiro# define MI_SLEEP(s)	sleep((s))
69290792Sgshapiro#endif /* BROKEN_PTHREAD_SLEEP */
69373188Sgshapiro
69464562Sgshapiroint
69566494Sgshapiromi_listener(conn, dbg, smfi, timeout, backlog)
69664562Sgshapiro	char *conn;
69764562Sgshapiro	int dbg;
69864562Sgshapiro	smfiDesc_ptr smfi;
69964562Sgshapiro	time_t timeout;
70066494Sgshapiro	int backlog;
70164562Sgshapiro{
70266494Sgshapiro	socket_t connfd = INVALID_SOCKET;
703132943Sgshapiro#if _FFR_DUP_FD
704132943Sgshapiro	socket_t dupfd = INVALID_SOCKET;
705132943Sgshapiro#endif /* _FFR_DUP_FD */
70664562Sgshapiro	int sockopt = 1;
707120256Sgshapiro	int r, mistop;
70864562Sgshapiro	int ret = MI_SUCCESS;
70990792Sgshapiro	int mcnt = 0;	/* error count for malloc() failures */
71090792Sgshapiro	int tcnt = 0;	/* error count for thread_create() failures */
71190792Sgshapiro	int acnt = 0;	/* error count for accept() failures */
71290792Sgshapiro	int scnt = 0;	/* error count for select() failures */
71377349Sgshapiro	int save_errno = 0;
71464562Sgshapiro	sthread_t thread_id;
71564562Sgshapiro	_SOCK_ADDR cliaddr;
71664562Sgshapiro	SOCKADDR_LEN_T clilen;
71764562Sgshapiro	SMFICTX_PTR ctx;
718111823Sgshapiro	FD_RD_VAR(rds, excs);
71964562Sgshapiro	struct timeval chktime;
72064562Sgshapiro
721125820Sgshapiro	if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE)
72264562Sgshapiro		return MI_FAILURE;
72371345Sgshapiro
72498121Sgshapiro	clilen = L_socksize;
72571345Sgshapiro	(void) smutex_unlock(&L_Mutex);
726120256Sgshapiro	while ((mistop = mi_stop()) == MILTER_CONT)
72764562Sgshapiro	{
72871345Sgshapiro		(void) smutex_lock(&L_Mutex);
72971345Sgshapiro		if (!ValidSocket(listenfd))
73071345Sgshapiro		{
731120256Sgshapiro			ret = MI_FAILURE;
732120256Sgshapiro			smi_log(SMI_LOG_ERR,
733120256Sgshapiro				"%s: listenfd=%d corrupted, terminating, errno=%d",
734120256Sgshapiro				smfi->xxfi_name, listenfd, errno);
73571345Sgshapiro			(void) smutex_unlock(&L_Mutex);
73671345Sgshapiro			break;
73771345Sgshapiro		}
73871345Sgshapiro
73964562Sgshapiro		/* select on interface ports */
740111823Sgshapiro		FD_RD_INIT(listenfd, rds, excs);
74164562Sgshapiro		chktime.tv_sec = MI_CHK_TIME;
74264562Sgshapiro		chktime.tv_usec = 0;
743111823Sgshapiro		r = FD_RD_READY(listenfd, rds, excs, &chktime);
74464562Sgshapiro		if (r == 0)		/* timeout */
74571345Sgshapiro		{
74671345Sgshapiro			(void) smutex_unlock(&L_Mutex);
74764562Sgshapiro			continue;	/* just check mi_stop() */
74871345Sgshapiro		}
74964562Sgshapiro		if (r < 0)
75064562Sgshapiro		{
75177349Sgshapiro			save_errno = errno;
75271345Sgshapiro			(void) smutex_unlock(&L_Mutex);
75377349Sgshapiro			if (save_errno == EINTR)
75464562Sgshapiro				continue;
75590792Sgshapiro			scnt++;
75690792Sgshapiro			smi_log(SMI_LOG_ERR,
75790792Sgshapiro				"%s: select() failed (%s), %s",
75890792Sgshapiro				smfi->xxfi_name, sm_errstring(save_errno),
75990792Sgshapiro				scnt >= MAX_FAILS_S ? "abort" : "try again");
76090792Sgshapiro			MI_SLEEP(scnt);
76190792Sgshapiro			if (scnt >= MAX_FAILS_S)
76290792Sgshapiro			{
76390792Sgshapiro				ret = MI_FAILURE;
76490792Sgshapiro				break;
76590792Sgshapiro			}
76690792Sgshapiro			continue;
76764562Sgshapiro		}
768111823Sgshapiro		if (!FD_IS_RD_RDY(listenfd, rds, excs))
76964562Sgshapiro		{
77064562Sgshapiro			/* some error: just stop for now... */
77164562Sgshapiro			ret = MI_FAILURE;
77271345Sgshapiro			(void) smutex_unlock(&L_Mutex);
77390792Sgshapiro			smi_log(SMI_LOG_ERR,
774111823Sgshapiro				"%s: %s() returned exception for socket, abort",
775111823Sgshapiro				smfi->xxfi_name, MI_POLLSELECT);
77664562Sgshapiro			break;
77764562Sgshapiro		}
77890792Sgshapiro		scnt = 0;	/* reset error counter for select() */
77964562Sgshapiro
780120256Sgshapiro		(void) memset(&cliaddr, '\0', sizeof cliaddr);
78164562Sgshapiro		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
78264562Sgshapiro				&clilen);
78377349Sgshapiro		save_errno = errno;
78471345Sgshapiro		(void) smutex_unlock(&L_Mutex);
78564562Sgshapiro
78673188Sgshapiro		/*
78773188Sgshapiro		**  If remote side closes before
78873188Sgshapiro		**  accept() finishes, sockaddr
78973188Sgshapiro		**  might not be fully filled in.
79073188Sgshapiro		*/
79173188Sgshapiro
79273188Sgshapiro		if (ValidSocket(connfd) &&
79373188Sgshapiro		    (clilen == 0 ||
79473188Sgshapiro# ifdef BSD4_4_SOCKADDR
79573188Sgshapiro		     cliaddr.sa.sa_len == 0 ||
79673188Sgshapiro# endif /* BSD4_4_SOCKADDR */
79798121Sgshapiro		     cliaddr.sa.sa_family != L_family))
79873188Sgshapiro		{
79990792Sgshapiro			(void) closesocket(connfd);
80073188Sgshapiro			connfd = INVALID_SOCKET;
80177349Sgshapiro			save_errno = EINVAL;
80273188Sgshapiro		}
80373188Sgshapiro
804132943Sgshapiro#if !SM_CONF_POLL
805110560Sgshapiro		/* check if acceptable for select() */
806110560Sgshapiro		if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd))
807110560Sgshapiro		{
808110560Sgshapiro			(void) closesocket(connfd);
809110560Sgshapiro			connfd = INVALID_SOCKET;
810110560Sgshapiro			save_errno = ERANGE;
811110560Sgshapiro		}
812132943Sgshapiro#endif /* !SM_CONF_POLL */
813110560Sgshapiro
81466494Sgshapiro		if (!ValidSocket(connfd))
81564562Sgshapiro		{
816132943Sgshapiro			if (save_errno == EINTR
817132943Sgshapiro#ifdef EAGAIN
818132943Sgshapiro			    || save_errno == EAGAIN
819132943Sgshapiro#endif /* EAGAIN */
820132943Sgshapiro#ifdef ECONNABORTED
821132943Sgshapiro			    || save_errno == ECONNABORTED
822132943Sgshapiro#endif /* ECONNABORTED */
823132943Sgshapiro#ifdef EMFILE
824132943Sgshapiro			    || save_errno == EMFILE
825132943Sgshapiro#endif /* EMFILE */
826132943Sgshapiro#ifdef ENFILE
827132943Sgshapiro			    || save_errno == ENFILE
828132943Sgshapiro#endif /* ENFILE */
829132943Sgshapiro#ifdef ENOBUFS
830132943Sgshapiro			    || save_errno == ENOBUFS
831132943Sgshapiro#endif /* ENOBUFS */
832132943Sgshapiro#ifdef ENOMEM
833132943Sgshapiro			    || save_errno == ENOMEM
834132943Sgshapiro#endif /* ENOMEM */
835132943Sgshapiro#ifdef ENOSR
836132943Sgshapiro			    || save_errno == ENOSR
837132943Sgshapiro#endif /* ENOSR */
838132943Sgshapiro#ifdef EWOULDBLOCK
839132943Sgshapiro			    || save_errno == EWOULDBLOCK
840132943Sgshapiro#endif /* EWOULDBLOCK */
841132943Sgshapiro			   )
84277349Sgshapiro				continue;
84377349Sgshapiro			acnt++;
84490792Sgshapiro			smi_log(SMI_LOG_ERR,
84590792Sgshapiro				"%s: accept() returned invalid socket (%s), %s",
84690792Sgshapiro				smfi->xxfi_name, sm_errstring(save_errno),
84790792Sgshapiro				acnt >= MAX_FAILS_A ? "abort" : "try again");
84877349Sgshapiro			MI_SLEEP(acnt);
84977349Sgshapiro			if (acnt >= MAX_FAILS_A)
85077349Sgshapiro			{
85177349Sgshapiro				ret = MI_FAILURE;
85277349Sgshapiro				break;
85377349Sgshapiro			}
85464562Sgshapiro			continue;
85564562Sgshapiro		}
85690792Sgshapiro		acnt = 0;	/* reset error counter for accept() */
857132943Sgshapiro#if _FFR_DUP_FD
858132943Sgshapiro		dupfd = fcntl(connfd, F_DUPFD, 256);
859132943Sgshapiro		if (ValidSocket(dupfd)
860132943Sgshapiro# if !SM_CONF_POLL
861132943Sgshapiro		    && SM_FD_OK_SELECT(dupfd)
862132943Sgshapiro# endif /* !SM_CONF_POLL */
863132943Sgshapiro		   )
864132943Sgshapiro		{
865132943Sgshapiro			close(connfd);
866132943Sgshapiro			connfd = dupfd;
867132943Sgshapiro			dupfd = INVALID_SOCKET;
868132943Sgshapiro		}
869132943Sgshapiro#endif /* _FFR_DUP_FD */
87064562Sgshapiro
87164562Sgshapiro		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
87264562Sgshapiro				(void *) &sockopt, sizeof sockopt) < 0)
87364562Sgshapiro		{
87490792Sgshapiro			smi_log(SMI_LOG_WARN, "%s: setsockopt() failed (%s)",
87590792Sgshapiro				smfi->xxfi_name, sm_errstring(errno));
87664562Sgshapiro			/* XXX: continue? */
87764562Sgshapiro		}
87864562Sgshapiro		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
87964562Sgshapiro		{
88090792Sgshapiro			(void) closesocket(connfd);
88173188Sgshapiro			mcnt++;
88290792Sgshapiro			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s",
88390792Sgshapiro				smfi->xxfi_name, sm_errstring(save_errno),
88490792Sgshapiro				mcnt >= MAX_FAILS_M ? "abort" : "try again");
88573188Sgshapiro			MI_SLEEP(mcnt);
88673188Sgshapiro			if (mcnt >= MAX_FAILS_M)
88764562Sgshapiro			{
88864562Sgshapiro				ret = MI_FAILURE;
88964562Sgshapiro				break;
89064562Sgshapiro			}
89164562Sgshapiro			continue;
89264562Sgshapiro		}
89390792Sgshapiro		mcnt = 0;	/* reset error counter for malloc() */
894120256Sgshapiro		(void) memset(ctx, '\0', sizeof *ctx);
89564562Sgshapiro		ctx->ctx_sd = connfd;
89664562Sgshapiro		ctx->ctx_dbg = dbg;
89764562Sgshapiro		ctx->ctx_timeout = timeout;
89864562Sgshapiro		ctx->ctx_smfi = smfi;
89964562Sgshapiro#if 0
90064562Sgshapiro		if (smfi->xxfi_eoh == NULL)
90164562Sgshapiro		if (smfi->xxfi_eom == NULL)
90264562Sgshapiro		if (smfi->xxfi_abort == NULL)
90364562Sgshapiro		if (smfi->xxfi_close == NULL)
90464562Sgshapiro#endif /* 0 */
90564562Sgshapiro		if (smfi->xxfi_connect == NULL)
90664562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOCONNECT;
90764562Sgshapiro		if (smfi->xxfi_helo == NULL)
90864562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHELO;
90964562Sgshapiro		if (smfi->xxfi_envfrom == NULL)
91064562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOMAIL;
91164562Sgshapiro		if (smfi->xxfi_envrcpt == NULL)
91264562Sgshapiro			ctx->ctx_pflags |= SMFIP_NORCPT;
91364562Sgshapiro		if (smfi->xxfi_header == NULL)
91464562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHDRS;
91564562Sgshapiro		if (smfi->xxfi_eoh == NULL)
91664562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOEOH;
91764562Sgshapiro		if (smfi->xxfi_body == NULL)
91864562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOBODY;
91964562Sgshapiro
92064562Sgshapiro		if ((r = thread_create(&thread_id,
92164562Sgshapiro					mi_thread_handle_wrapper,
92271345Sgshapiro					(void *) ctx)) != 0)
92364562Sgshapiro		{
92490792Sgshapiro			tcnt++;
92564562Sgshapiro			smi_log(SMI_LOG_ERR,
92690792Sgshapiro				"%s: thread_create() failed: %d, %s",
92790792Sgshapiro				smfi->xxfi_name,  r,
92890792Sgshapiro				tcnt >= MAX_FAILS_T ? "abort" : "try again");
92973188Sgshapiro			MI_SLEEP(tcnt);
93090792Sgshapiro			(void) closesocket(connfd);
93164562Sgshapiro			free(ctx);
93273188Sgshapiro			if (tcnt >= MAX_FAILS_T)
93364562Sgshapiro			{
93464562Sgshapiro				ret = MI_FAILURE;
93564562Sgshapiro				break;
93664562Sgshapiro			}
93764562Sgshapiro			continue;
93864562Sgshapiro		}
93973188Sgshapiro		tcnt = 0;
94064562Sgshapiro	}
94164562Sgshapiro	if (ret != MI_SUCCESS)
94264562Sgshapiro		mi_stop_milters(MILTER_ABRT);
94371345Sgshapiro	else
944120256Sgshapiro	{
945120256Sgshapiro		if (mistop != MILTER_CONT)
946120256Sgshapiro			smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
947120256Sgshapiro				smfi->xxfi_name, mistop);
94871345Sgshapiro		mi_closener();
949120256Sgshapiro	}
95071345Sgshapiro	(void) smutex_destroy(&L_Mutex);
95164562Sgshapiro	return ret;
95264562Sgshapiro}
953