listener.c revision 66494
164562Sgshapiro/*
264562Sgshapiro *  Copyright (c) 1999-2000 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
1164562Sgshapiro#ifndef lint
1266494Sgshapirostatic char id[] = "@(#)$Id: listener.c,v 8.38.2.1.2.11 2000/09/01 00:49:04 ca Exp $";
1364562Sgshapiro#endif /* ! lint */
1464562Sgshapiro
1564562Sgshapiro#if _FFR_MILTER
1664562Sgshapiro/*
1764562Sgshapiro**  listener.c -- threaded network listener
1864562Sgshapiro*/
1964562Sgshapiro
2064562Sgshapiro#include "libmilter.h"
2164562Sgshapiro
2264562Sgshapiro
2364562Sgshapiro# if NETINET || NETINET6
2464562Sgshapiro#  include <arpa/inet.h>
2564562Sgshapiro# endif /* NETINET || NETINET6 */
2664562Sgshapiro/*
2764562Sgshapiro**  MI_MILTEROPEN -- setup socket to listen on
2864562Sgshapiro**
2964562Sgshapiro**	Parameters:
3064562Sgshapiro**		conn -- connection description
3164562Sgshapiro**		backlog -- listen backlog
3264562Sgshapiro**		socksize -- socksize of created socket
3364562Sgshapiro**
3464562Sgshapiro**	Returns:
3564562Sgshapiro**		socket upon success, error code otherwise.
3664562Sgshapiro*/
3764562Sgshapiro
3866494Sgshapirostatic socket_t
3964562Sgshapiromi_milteropen(conn, backlog, socksize, name)
4064562Sgshapiro	char *conn;
4164562Sgshapiro	int backlog;
4264562Sgshapiro	SOCKADDR_LEN_T *socksize;
4364562Sgshapiro	char *name;
4464562Sgshapiro{
4566494Sgshapiro	socket_t sock;
4664562Sgshapiro	int sockopt = 1;
4764562Sgshapiro	char *p;
4864562Sgshapiro	char *colon;
4964562Sgshapiro	char *at;
5064562Sgshapiro	struct hostent *hp = NULL;
5164562Sgshapiro	SOCKADDR addr;
5264562Sgshapiro
5364562Sgshapiro	if (conn == NULL || conn[0] == '\0')
5464562Sgshapiro	{
5564562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
5664562Sgshapiro			name);
5766494Sgshapiro		return INVALID_SOCKET;
5864562Sgshapiro	}
5964562Sgshapiro	(void) memset(&addr, '\0', sizeof addr);
6064562Sgshapiro
6164562Sgshapiro	/* protocol:filename or protocol:port@host */
6264562Sgshapiro	p = conn;
6364562Sgshapiro	colon = strchr(p, ':');
6464562Sgshapiro	if (colon != NULL)
6564562Sgshapiro	{
6664562Sgshapiro		*colon = '\0';
6764562Sgshapiro
6864562Sgshapiro 		if (*p == '\0')
6964562Sgshapiro		{
7064562Sgshapiro#if NETUNIX
7164562Sgshapiro			/* default to AF_UNIX */
7264562Sgshapiro 			addr.sa.sa_family = AF_UNIX;
7364562Sgshapiro			*socksize = sizeof (struct sockaddr_un);
7464562Sgshapiro#else /* NETUNIX */
7564562Sgshapiro# if NETINET
7664562Sgshapiro			/* default to AF_INET */
7764562Sgshapiro			addr.sa.sa_family = AF_INET;
7864562Sgshapiro			*socksize = sizeof addr.sin;
7964562Sgshapiro# else /* NETINET */
8064562Sgshapiro#  if NETINET6
8164562Sgshapiro			/* default to AF_INET6 */
8264562Sgshapiro			addr.sa.sa_family = AF_INET6;
8364562Sgshapiro			*socksize = sizeof addr.sin6;
8464562Sgshapiro#  else /* NETINET6 */
8564562Sgshapiro			/* no protocols available */
8664562Sgshapiro			smi_log(SMI_LOG_ERR,
8764562Sgshapiro				"%s: no valid socket protocols available",
8864562Sgshapiro				name);
8966494Sgshapiro			return INVALID_SOCKET;
9064562Sgshapiro#  endif /* NETINET6 */
9164562Sgshapiro# endif /* NETINET */
9264562Sgshapiro#endif /* NETUNIX */
9364562Sgshapiro		}
9464562Sgshapiro#if NETUNIX
9564562Sgshapiro		else if (strcasecmp(p, "unix") == 0 ||
9664562Sgshapiro			 strcasecmp(p, "local") == 0)
9764562Sgshapiro		{
9864562Sgshapiro			addr.sa.sa_family = AF_UNIX;
9964562Sgshapiro			*socksize = sizeof (struct sockaddr_un);
10064562Sgshapiro		}
10164562Sgshapiro#endif /* NETUNIX */
10264562Sgshapiro#if NETINET
10364562Sgshapiro		else if (strcasecmp(p, "inet") == 0)
10464562Sgshapiro		{
10564562Sgshapiro			addr.sa.sa_family = AF_INET;
10664562Sgshapiro			*socksize = sizeof addr.sin;
10764562Sgshapiro		}
10864562Sgshapiro#endif /* NETINET */
10964562Sgshapiro#if NETINET6
11064562Sgshapiro		else if (strcasecmp(p, "inet6") == 0)
11164562Sgshapiro		{
11264562Sgshapiro			addr.sa.sa_family = AF_INET6;
11364562Sgshapiro			*socksize = sizeof addr.sin6;
11464562Sgshapiro		}
11564562Sgshapiro#endif /* NETINET6 */
11664562Sgshapiro		else
11764562Sgshapiro		{
11864562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
11964562Sgshapiro				name, p);
12066494Sgshapiro			return INVALID_SOCKET;
12164562Sgshapiro		}
12264562Sgshapiro		*colon++ = ':';
12364562Sgshapiro	}
12464562Sgshapiro	else
12564562Sgshapiro	{
12664562Sgshapiro		colon = p;
12764562Sgshapiro#if NETUNIX
12864562Sgshapiro		/* default to AF_UNIX */
12964562Sgshapiro 		addr.sa.sa_family = AF_UNIX;
13064562Sgshapiro		*socksize = sizeof (struct sockaddr_un);
13164562Sgshapiro#else /* NETUNIX */
13264562Sgshapiro# if NETINET
13364562Sgshapiro		/* default to AF_INET */
13464562Sgshapiro		addr.sa.sa_family = AF_INET;
13564562Sgshapiro		*socksize = sizeof addr.sin;
13664562Sgshapiro# else /* NETINET */
13764562Sgshapiro#  if NETINET6
13864562Sgshapiro		/* default to AF_INET6 */
13964562Sgshapiro		addr.sa.sa_family = AF_INET6;
14064562Sgshapiro		*socksize = sizeof addr.sin6;
14164562Sgshapiro#  else /* NETINET6 */
14264562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
14364562Sgshapiro			name, p);
14466494Sgshapiro		return INVALID_SOCKET;
14564562Sgshapiro#  endif /* NETINET6 */
14664562Sgshapiro# endif /* NETINET */
14764562Sgshapiro#endif /* NETUNIX */
14864562Sgshapiro	}
14964562Sgshapiro
15064562Sgshapiro#if NETUNIX
15164562Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
15264562Sgshapiro	{
15364562Sgshapiro# if 0
15464562Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
15564562Sgshapiro# endif /* 0 */
15664562Sgshapiro
15764562Sgshapiro		at = colon;
15864562Sgshapiro		if (strlcpy(addr.sunix.sun_path, colon,
15964562Sgshapiro			    sizeof addr.sunix.sun_path) >=
16064562Sgshapiro		    sizeof addr.sunix.sun_path)
16164562Sgshapiro		{
16264562Sgshapiro			errno = EINVAL;
16364562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
16464562Sgshapiro				name, colon);
16566494Sgshapiro			return INVALID_SOCKET;
16664562Sgshapiro		}
16764562Sgshapiro# if 0
16864562Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
16964562Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
17064562Sgshapiro
17164562Sgshapiro		/* if not safe, don't create */
17264562Sgshapiro		if (errno != 0)
17364562Sgshapiro		{
17464562Sgshapiro			smi_log(SMI_LOG_ERR,
17564562Sgshapiro				"%s: UNIX socket name %s unsafe",
17664562Sgshapiro				name, colon);
17766494Sgshapiro			return INVALID_SOCKET;
17864562Sgshapiro		}
17964562Sgshapiro# endif /* 0 */
18064562Sgshapiro
18164562Sgshapiro	}
18264562Sgshapiro#endif /* NETUNIX */
18364562Sgshapiro
18464562Sgshapiro#if NETINET || NETINET6
18564562Sgshapiro	if (
18664562Sgshapiro# if NETINET
18764562Sgshapiro	    addr.sa.sa_family == AF_INET
18864562Sgshapiro# endif /* NETINET */
18964562Sgshapiro# if NETINET && NETINET6
19064562Sgshapiro	    ||
19164562Sgshapiro# endif /* NETINET && NETINET6 */
19264562Sgshapiro# if NETINET6
19364562Sgshapiro	    addr.sa.sa_family == AF_INET6
19464562Sgshapiro# endif /* NETINET6 */
19564562Sgshapiro	   )
19664562Sgshapiro	{
19764562Sgshapiro		u_short port;
19864562Sgshapiro
19964562Sgshapiro		/* Parse port@host */
20064562Sgshapiro		at = strchr(colon, '@');
20164562Sgshapiro		if (at == NULL)
20264562Sgshapiro		{
20364562Sgshapiro			switch (addr.sa.sa_family)
20464562Sgshapiro			{
20564562Sgshapiro# if NETINET
20664562Sgshapiro			  case AF_INET:
20764562Sgshapiro				addr.sin.sin_addr.s_addr = INADDR_ANY;
20864562Sgshapiro				break;
20964562Sgshapiro# endif /* NETINET */
21064562Sgshapiro
21164562Sgshapiro# if NETINET6
21264562Sgshapiro			  case AF_INET6:
21364562Sgshapiro				addr.sin6.sin6_addr = in6addr_any;
21464562Sgshapiro				break;
21564562Sgshapiro# endif /* NETINET6 */
21664562Sgshapiro			}
21764562Sgshapiro		}
21864562Sgshapiro		else
21964562Sgshapiro			*at = '\0';
22064562Sgshapiro
22164562Sgshapiro		if (isascii(*colon) && isdigit(*colon))
22264562Sgshapiro			port = htons((u_short) atoi(colon));
22364562Sgshapiro		else
22464562Sgshapiro		{
22564562Sgshapiro# ifdef NO_GETSERVBYNAME
22664562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
22764562Sgshapiro				name, colon);
22866494Sgshapiro			return INVALID_SOCKET;
22964562Sgshapiro# else /* NO_GETSERVBYNAME */
23064562Sgshapiro			register struct servent *sp;
23164562Sgshapiro
23264562Sgshapiro			sp = getservbyname(colon, "tcp");
23364562Sgshapiro			if (sp == NULL)
23464562Sgshapiro			{
23564562Sgshapiro				smi_log(SMI_LOG_ERR,
23664562Sgshapiro					"%s: unknown port name %s",
23764562Sgshapiro					name, colon);
23866494Sgshapiro				return INVALID_SOCKET;
23964562Sgshapiro			}
24064562Sgshapiro			port = sp->s_port;
24164562Sgshapiro# endif /* NO_GETSERVBYNAME */
24264562Sgshapiro		}
24364562Sgshapiro		if (at != NULL)
24464562Sgshapiro		{
24564562Sgshapiro			*at++ = '@';
24664562Sgshapiro			if (*at == '[')
24764562Sgshapiro			{
24864562Sgshapiro				char *end;
24964562Sgshapiro
25064562Sgshapiro				end = strchr(at, ']');
25164562Sgshapiro				if (end != NULL)
25264562Sgshapiro				{
25364562Sgshapiro					bool found = FALSE;
25464562Sgshapiro# if NETINET
25564562Sgshapiro					unsigned long hid = INADDR_NONE;
25664562Sgshapiro# endif /* NETINET */
25764562Sgshapiro# if NETINET6
25864562Sgshapiro					struct sockaddr_in6 hid6;
25964562Sgshapiro# endif /* NETINET6 */
26064562Sgshapiro
26164562Sgshapiro					*end = '\0';
26264562Sgshapiro# if NETINET
26364562Sgshapiro					if (addr.sa.sa_family == AF_INET &&
26464562Sgshapiro					    (hid = inet_addr(&at[1])) !=
26564562Sgshapiro					    INADDR_NONE)
26664562Sgshapiro					{
26764562Sgshapiro						addr.sin.sin_addr.s_addr = hid;
26864562Sgshapiro						addr.sin.sin_port = port;
26964562Sgshapiro						found = TRUE;
27064562Sgshapiro					}
27164562Sgshapiro# endif /* NETINET */
27264562Sgshapiro# if NETINET6
27364562Sgshapiro					(void) memset(&hid6, '\0', sizeof hid6);
27464562Sgshapiro					if (addr.sa.sa_family == AF_INET6 &&
27564562Sgshapiro					    inet_pton(AF_INET6, &at[1],
27664562Sgshapiro						      &hid6.sin6_addr) == 1)
27764562Sgshapiro					{
27864562Sgshapiro						addr.sin6.sin6_addr = hid6.sin6_addr;
27964562Sgshapiro						addr.sin6.sin6_port = port;
28064562Sgshapiro						found = TRUE;
28164562Sgshapiro					}
28264562Sgshapiro# endif /* NETINET6 */
28364562Sgshapiro					*end = ']';
28464562Sgshapiro					if (!found)
28564562Sgshapiro					{
28664562Sgshapiro						smi_log(SMI_LOG_ERR,
28764562Sgshapiro							"%s: Invalid numeric domain spec \"%s\"",
28864562Sgshapiro							name, at);
28966494Sgshapiro						return INVALID_SOCKET;
29064562Sgshapiro					}
29164562Sgshapiro				}
29264562Sgshapiro				else
29364562Sgshapiro				{
29464562Sgshapiro					smi_log(SMI_LOG_ERR,
29564562Sgshapiro						"%s: Invalid numeric domain spec \"%s\"",
29664562Sgshapiro						name, at);
29766494Sgshapiro					return INVALID_SOCKET;
29864562Sgshapiro				}
29964562Sgshapiro			}
30064562Sgshapiro			else
30164562Sgshapiro			{
30264562Sgshapiro				hp = mi_gethostbyname(at, addr.sa.sa_family);
30364562Sgshapiro				if (hp == NULL)
30464562Sgshapiro				{
30564562Sgshapiro					smi_log(SMI_LOG_ERR,
30664562Sgshapiro						"%s: Unknown host name %s",
30764562Sgshapiro						name, at);
30866494Sgshapiro					return INVALID_SOCKET;
30964562Sgshapiro				}
31064562Sgshapiro				addr.sa.sa_family = hp->h_addrtype;
31164562Sgshapiro				switch (hp->h_addrtype)
31264562Sgshapiro				{
31364562Sgshapiro# if NETINET
31464562Sgshapiro				  case AF_INET:
31564562Sgshapiro					memmove(&addr.sin.sin_addr,
31664562Sgshapiro						hp->h_addr,
31764562Sgshapiro						INADDRSZ);
31864562Sgshapiro					addr.sin.sin_port = port;
31964562Sgshapiro					break;
32064562Sgshapiro# endif /* NETINET */
32164562Sgshapiro
32264562Sgshapiro# if NETINET6
32364562Sgshapiro				  case AF_INET6:
32464562Sgshapiro					memmove(&addr.sin6.sin6_addr,
32564562Sgshapiro						hp->h_addr,
32664562Sgshapiro						IN6ADDRSZ);
32764562Sgshapiro					addr.sin6.sin6_port = port;
32864562Sgshapiro					break;
32964562Sgshapiro# endif /* NETINET6 */
33064562Sgshapiro
33164562Sgshapiro				  default:
33264562Sgshapiro					smi_log(SMI_LOG_ERR,
33364562Sgshapiro						"%s: Unknown protocol for %s (%d)",
33464562Sgshapiro						name, at, hp->h_addrtype);
33566494Sgshapiro					return INVALID_SOCKET;
33664562Sgshapiro				}
33764562Sgshapiro			}
33864562Sgshapiro		}
33964562Sgshapiro		else
34064562Sgshapiro		{
34164562Sgshapiro			switch (addr.sa.sa_family)
34264562Sgshapiro			{
34364562Sgshapiro# if NETINET
34464562Sgshapiro			  case AF_INET:
34564562Sgshapiro				addr.sin.sin_port = port;
34664562Sgshapiro				break;
34764562Sgshapiro# endif /* NETINET */
34864562Sgshapiro# if NETINET6
34964562Sgshapiro			  case AF_INET6:
35064562Sgshapiro				addr.sin6.sin6_port = port;
35164562Sgshapiro				break;
35264562Sgshapiro# endif /* NETINET6 */
35364562Sgshapiro			}
35464562Sgshapiro		}
35564562Sgshapiro	}
35664562Sgshapiro#endif /* NETINET || NETINET6 */
35764562Sgshapiro
35864562Sgshapiro	sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
35964562Sgshapiro	if (!ValidSocket(sock))
36064562Sgshapiro	{
36164562Sgshapiro		smi_log(SMI_LOG_ERR,
36264562Sgshapiro			"%s: Unable to create new socket: %s",
36364562Sgshapiro			name, strerror(errno));
36466494Sgshapiro		return INVALID_SOCKET;
36564562Sgshapiro	}
36664562Sgshapiro
36764562Sgshapiro	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
36864562Sgshapiro		       sizeof(sockopt)) == -1)
36964562Sgshapiro	{
37064562Sgshapiro		smi_log(SMI_LOG_ERR,
37164562Sgshapiro			"%s: Unable to setsockopt: %s", name, strerror(errno));
37264562Sgshapiro		(void) close(sock);
37366494Sgshapiro		return INVALID_SOCKET;
37464562Sgshapiro	}
37564562Sgshapiro
37664562Sgshapiro	if (bind(sock, &addr.sa, *socksize) < 0)
37764562Sgshapiro	{
37864562Sgshapiro		smi_log(SMI_LOG_ERR,
37964562Sgshapiro			"%s: Unable to bind to port %s: %s",
38064562Sgshapiro			name, conn, strerror(errno));
38164562Sgshapiro		(void) close(sock);
38266494Sgshapiro		return INVALID_SOCKET;
38364562Sgshapiro	}
38464562Sgshapiro
38564562Sgshapiro	if (listen(sock, backlog) < 0)
38664562Sgshapiro	{
38764562Sgshapiro		smi_log(SMI_LOG_ERR,
38864562Sgshapiro			"%s: listen call failed: %s", name, strerror(errno));
38964562Sgshapiro		(void) close(sock);
39066494Sgshapiro		return INVALID_SOCKET;
39164562Sgshapiro	}
39264562Sgshapiro
39364562Sgshapiro	return sock;
39464562Sgshapiro}
39564562Sgshapiro/*
39664562Sgshapiro**  MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
39764562Sgshapiro**
39864562Sgshapiro**	Parameters:
39964562Sgshapiro**		arg -- argument to pass to mi_handle_session()
40064562Sgshapiro**
40164562Sgshapiro**	Returns:
40264562Sgshapiro**		results from mi_handle_session()
40364562Sgshapiro*/
40464562Sgshapiro
40564562Sgshapirovoid *
40664562Sgshapiromi_thread_handle_wrapper(arg)
40764562Sgshapiro	void *arg;
40864562Sgshapiro{
40964562Sgshapiro	return (void *) mi_handle_session(arg);
41064562Sgshapiro}
41164562Sgshapiro
41266494Sgshapirostatic socket_t listenfd = INVALID_SOCKET;
41366494Sgshapiro
41464562Sgshapiro/*
41566494Sgshapiro**  MI_CLOSENER -- close listen socket
41664562Sgshapiro**
41766494Sgshapiro**	Parameters:
41866494Sgshapiro**		none.
41966494Sgshapiro**
42066494Sgshapiro**	Returns:
42166494Sgshapiro**		none.
42266494Sgshapiro*/
42366494Sgshapiro
42466494Sgshapirovoid
42566494Sgshapiromi_closener()
42666494Sgshapiro{
42766494Sgshapiro	if (ValidSocket(listenfd))
42866494Sgshapiro	{
42966494Sgshapiro		(void) close(listenfd);
43066494Sgshapiro		listenfd = INVALID_SOCKET;
43166494Sgshapiro	}
43266494Sgshapiro}
43366494Sgshapiro
43466494Sgshapiro/*
43566494Sgshapiro**  MI_LISTENER -- Generic listener harness
43666494Sgshapiro**
43764562Sgshapiro**	Open up listen port
43864562Sgshapiro**	Wait for connections
43964562Sgshapiro**
44064562Sgshapiro**	Parameters:
44164562Sgshapiro**		conn -- connection description
44264562Sgshapiro**		dbg -- debug level
44364562Sgshapiro**		smfi -- filter structure to use
44464562Sgshapiro**		timeout -- timeout for reads/writes
44564562Sgshapiro**
44664562Sgshapiro**	Returns:
44764562Sgshapiro**		MI_SUCCESS -- Exited normally
44864562Sgshapiro**			   (session finished or we were told to exit)
44964562Sgshapiro**		MI_FAILURE -- Network initialization failed.
45064562Sgshapiro*/
45164562Sgshapiro
45264562Sgshapiroint
45366494Sgshapiromi_listener(conn, dbg, smfi, timeout, backlog)
45464562Sgshapiro	char *conn;
45564562Sgshapiro	int dbg;
45664562Sgshapiro	smfiDesc_ptr smfi;
45764562Sgshapiro	time_t timeout;
45866494Sgshapiro	int backlog;
45964562Sgshapiro{
46066494Sgshapiro	socket_t connfd = INVALID_SOCKET;
46164562Sgshapiro	int sockopt = 1;
46264562Sgshapiro	int r;
46364562Sgshapiro	int ret = MI_SUCCESS;
46464562Sgshapiro	int cnt_m = 0;
46564562Sgshapiro	int cnt_t = 0;
46664562Sgshapiro	sthread_t thread_id;
46764562Sgshapiro	_SOCK_ADDR cliaddr;
46864562Sgshapiro	SOCKADDR_LEN_T socksize;
46964562Sgshapiro	SOCKADDR_LEN_T clilen;
47064562Sgshapiro	SMFICTX_PTR ctx;
47164562Sgshapiro	fd_set readset, excset;
47264562Sgshapiro	struct timeval chktime;
47364562Sgshapiro
47464562Sgshapiro	if (dbg > 0)
47564562Sgshapiro		smi_log(SMI_LOG_DEBUG,
47664562Sgshapiro			"%s: Opening listen socket on conn %s",
47764562Sgshapiro			smfi->xxfi_name, conn);
47866494Sgshapiro	listenfd = mi_milteropen(conn, backlog, &socksize, smfi->xxfi_name);
47966494Sgshapiro	if (!ValidSocket(listenfd))
48064562Sgshapiro	{
48164562Sgshapiro		smi_log(SMI_LOG_FATAL,
48264562Sgshapiro			"%s: Unable to create listening socket on conn %s",
48364562Sgshapiro			smfi->xxfi_name, conn);
48464562Sgshapiro		return MI_FAILURE;
48564562Sgshapiro	}
48664562Sgshapiro	clilen = socksize;
48764562Sgshapiro	if (listenfd >= FD_SETSIZE)
48864562Sgshapiro	{
48964562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
49064562Sgshapiro			smfi->xxfi_name, listenfd, FD_SETSIZE);
49164562Sgshapiro		return MI_FAILURE;
49264562Sgshapiro	}
49364562Sgshapiro
49464562Sgshapiro	while (mi_stop() == MILTER_CONT)
49564562Sgshapiro	{
49664562Sgshapiro		/* select on interface ports */
49764562Sgshapiro		FD_ZERO(&readset);
49864562Sgshapiro		FD_SET((u_int) listenfd, &readset);
49964562Sgshapiro		FD_ZERO(&excset);
50064562Sgshapiro		FD_SET((u_int) listenfd, &excset);
50164562Sgshapiro		chktime.tv_sec = MI_CHK_TIME;
50264562Sgshapiro		chktime.tv_usec = 0;
50364562Sgshapiro		r = select(listenfd + 1, &readset, NULL, &excset, &chktime);
50464562Sgshapiro		if (r == 0)		/* timeout */
50564562Sgshapiro			continue;	/* just check mi_stop() */
50664562Sgshapiro		if (r < 0)
50764562Sgshapiro		{
50864562Sgshapiro			if (errno == EINTR)
50964562Sgshapiro				continue;
51064562Sgshapiro			ret = MI_FAILURE;
51164562Sgshapiro			break;
51264562Sgshapiro		}
51364562Sgshapiro		if (!FD_ISSET(listenfd, &readset))
51464562Sgshapiro		{
51564562Sgshapiro			/* some error: just stop for now... */
51664562Sgshapiro			ret = MI_FAILURE;
51764562Sgshapiro			break;
51864562Sgshapiro		}
51964562Sgshapiro
52064562Sgshapiro		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
52164562Sgshapiro				&clilen);
52264562Sgshapiro
52366494Sgshapiro		if (!ValidSocket(connfd))
52464562Sgshapiro		{
52564562Sgshapiro			smi_log(SMI_LOG_ERR,
52664562Sgshapiro				"%s: accept() returned invalid socket",
52764562Sgshapiro				smfi->xxfi_name);
52864562Sgshapiro			continue;
52964562Sgshapiro		}
53064562Sgshapiro
53164562Sgshapiro		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
53264562Sgshapiro				(void *) &sockopt, sizeof sockopt) < 0)
53364562Sgshapiro		{
53464562Sgshapiro			smi_log(SMI_LOG_WARN, "%s: setsockopt() failed",
53564562Sgshapiro				smfi->xxfi_name);
53664562Sgshapiro			/* XXX: continue? */
53764562Sgshapiro		}
53864562Sgshapiro		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
53964562Sgshapiro		{
54064562Sgshapiro			(void) close(connfd);
54164562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed",
54264562Sgshapiro				smfi->xxfi_name);
54364562Sgshapiro			sleep(++cnt_m);
54464562Sgshapiro			if (cnt_m >= MAX_FAILS_M)
54564562Sgshapiro			{
54664562Sgshapiro				ret = MI_FAILURE;
54764562Sgshapiro				break;
54864562Sgshapiro			}
54964562Sgshapiro			continue;
55064562Sgshapiro		}
55164562Sgshapiro		cnt_m = 0;
55264562Sgshapiro		memset(ctx, '\0', sizeof *ctx);
55364562Sgshapiro		ctx->ctx_sd = connfd;
55464562Sgshapiro		ctx->ctx_dbg = dbg;
55564562Sgshapiro		ctx->ctx_timeout = timeout;
55664562Sgshapiro		ctx->ctx_smfi = smfi;
55764562Sgshapiro#if 0
55864562Sgshapiro		if (smfi->xxfi_eoh == NULL)
55964562Sgshapiro		if (smfi->xxfi_eom == NULL)
56064562Sgshapiro		if (smfi->xxfi_abort == NULL)
56164562Sgshapiro		if (smfi->xxfi_close == NULL)
56264562Sgshapiro#endif /* 0 */
56364562Sgshapiro		if (smfi->xxfi_connect == NULL)
56464562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOCONNECT;
56564562Sgshapiro		if (smfi->xxfi_helo == NULL)
56664562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHELO;
56764562Sgshapiro		if (smfi->xxfi_envfrom == NULL)
56864562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOMAIL;
56964562Sgshapiro		if (smfi->xxfi_envrcpt == NULL)
57064562Sgshapiro			ctx->ctx_pflags |= SMFIP_NORCPT;
57164562Sgshapiro		if (smfi->xxfi_header == NULL)
57264562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHDRS;
57364562Sgshapiro		if (smfi->xxfi_eoh == NULL)
57464562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOEOH;
57564562Sgshapiro		if (smfi->xxfi_body == NULL)
57664562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOBODY;
57764562Sgshapiro
57864562Sgshapiro		if ((r = thread_create(&thread_id,
57964562Sgshapiro					mi_thread_handle_wrapper,
58064562Sgshapiro					(void *) ctx)) != MI_SUCCESS)
58164562Sgshapiro		{
58264562Sgshapiro			smi_log(SMI_LOG_ERR,
58364562Sgshapiro				"%s: thread_create() failed: %d",
58464562Sgshapiro				smfi->xxfi_name,  r);
58564562Sgshapiro			sleep(++cnt_t);
58664562Sgshapiro			(void) close(connfd);
58764562Sgshapiro			free(ctx);
58864562Sgshapiro			if (cnt_t >= MAX_FAILS_T)
58964562Sgshapiro			{
59064562Sgshapiro				ret = MI_FAILURE;
59164562Sgshapiro				break;
59264562Sgshapiro			}
59364562Sgshapiro			continue;
59464562Sgshapiro		}
59564562Sgshapiro		cnt_t = 0;
59664562Sgshapiro	}
59764562Sgshapiro	if (ret != MI_SUCCESS)
59864562Sgshapiro		mi_stop_milters(MILTER_ABRT);
59964562Sgshapiro	if (listenfd >= 0)
60064562Sgshapiro		(void) close(listenfd);
60164562Sgshapiro	return ret;
60264562Sgshapiro}
60364562Sgshapiro#endif /* _FFR_MILTER */
604