xref: /illumos-gate/usr/src/cmd/sendmail/libmilter/listener.c (revision e9af4bc0b1cc30cea75d6ad4aa2fde97d985e9be)
1 /*
2  *  Copyright (c) 1999-2007 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  *
9  */
10 
11 #include <sm/gen.h>
12 SM_RCSID("@(#)$Id: listener.c,v 8.126 2009/12/16 16:40:23 ca Exp $")
13 
14 /*
15 **  listener.c -- threaded network listener
16 */
17 
18 #include "libmilter.h"
19 #include <sm/errstring.h>
20 
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 
24 
25 # if NETINET || NETINET6
26 #  include <arpa/inet.h>
27 # endif /* NETINET || NETINET6 */
28 # if SM_CONF_POLL
29 #  undef SM_FD_OK_SELECT
30 #  define SM_FD_OK_SELECT(fd)		true
31 # endif /* SM_CONF_POLL */
32 
33 static smutex_t L_Mutex;
34 static int L_family;
35 static SOCKADDR_LEN_T L_socksize;
36 static socket_t listenfd = INVALID_SOCKET;
37 
38 static socket_t mi_milteropen __P((char *, int, bool, char *));
39 #if !_FFR_WORKERS_POOL
40 static void *mi_thread_handle_wrapper __P((void *));
41 #endif /* !_FFR_WORKERS_POOL */
42 
43 /*
44 **  MI_OPENSOCKET -- create the socket where this filter and the MTA will meet
45 **
46 **	Parameters:
47 **		conn -- connection description
48 **		backlog -- listen backlog
49 **		dbg -- debug level
50 **		rmsocket -- if true, try to unlink() the socket first
51 **			(UNIX domain sockets only)
52 **		smfi -- filter structure to use
53 **
54 **	Return value:
55 **		MI_SUCCESS/MI_FAILURE
56 */
57 
58 int
mi_opensocket(conn,backlog,dbg,rmsocket,smfi)59 mi_opensocket(conn, backlog, dbg, rmsocket, smfi)
60 	char *conn;
61 	int backlog;
62 	int dbg;
63 	bool rmsocket;
64 	smfiDesc_ptr smfi;
65 {
66 	if (smfi == NULL || conn == NULL)
67 		return MI_FAILURE;
68 
69 	if (ValidSocket(listenfd))
70 		return MI_SUCCESS;
71 
72 	if (dbg > 0)
73 	{
74 		smi_log(SMI_LOG_DEBUG,
75 			"%s: Opening listen socket on conn %s",
76 			smfi->xxfi_name, conn);
77 	}
78 	(void) smutex_init(&L_Mutex);
79 	(void) smutex_lock(&L_Mutex);
80 	listenfd = mi_milteropen(conn, backlog, rmsocket, smfi->xxfi_name);
81 	if (!ValidSocket(listenfd))
82 	{
83 		smi_log(SMI_LOG_FATAL,
84 			"%s: Unable to create listening socket on conn %s",
85 			smfi->xxfi_name, conn);
86 		(void) smutex_unlock(&L_Mutex);
87 		return MI_FAILURE;
88 	}
89 	if (!SM_FD_OK_SELECT(listenfd))
90 	{
91 		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
92 			smfi->xxfi_name, listenfd, FD_SETSIZE);
93 		(void) smutex_unlock(&L_Mutex);
94 		return MI_FAILURE;
95 	}
96 	(void) smutex_unlock(&L_Mutex);
97 	return MI_SUCCESS;
98 }
99 
100 /*
101 **  MI_MILTEROPEN -- setup socket to listen on
102 **
103 **	Parameters:
104 **		conn -- connection description
105 **		backlog -- listen backlog
106 **		rmsocket -- if true, try to unlink() the socket first
107 **			(UNIX domain sockets only)
108 **		name -- name for logging
109 **
110 **	Returns:
111 **		socket upon success, error code otherwise.
112 **
113 **	Side effect:
114 **		sets sockpath if UNIX socket.
115 */
116 
117 #if NETUNIX
118 static char	*sockpath = NULL;
119 #endif /* NETUNIX */
120 
121 static socket_t
mi_milteropen(conn,backlog,rmsocket,name)122 mi_milteropen(conn, backlog, rmsocket, name)
123 	char *conn;
124 	int backlog;
125 	bool rmsocket;
126 	char *name;
127 {
128 	socket_t sock;
129 	int sockopt = 1;
130 	int fdflags;
131 	size_t len = 0;
132 	char *p;
133 	char *colon;
134 	char *at;
135 	SOCKADDR addr;
136 
137 	if (conn == NULL || conn[0] == '\0')
138 	{
139 		smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
140 			name);
141 		return INVALID_SOCKET;
142 	}
143 	(void) memset(&addr, '\0', sizeof addr);
144 
145 	/* protocol:filename or protocol:port@host */
146 	p = conn;
147 	colon = strchr(p, ':');
148 	if (colon != NULL)
149 	{
150 		*colon = '\0';
151 
152 		if (*p == '\0')
153 		{
154 #if NETUNIX
155 			/* default to AF_UNIX */
156 			addr.sa.sa_family = AF_UNIX;
157 			L_socksize = sizeof (struct sockaddr_un);
158 #else /* NETUNIX */
159 # if NETINET
160 			/* default to AF_INET */
161 			addr.sa.sa_family = AF_INET;
162 			L_socksize = sizeof addr.sin;
163 # else /* NETINET */
164 #  if NETINET6
165 			/* default to AF_INET6 */
166 			addr.sa.sa_family = AF_INET6;
167 			L_socksize = sizeof addr.sin6;
168 #  else /* NETINET6 */
169 			/* no protocols available */
170 			smi_log(SMI_LOG_ERR,
171 				"%s: no valid socket protocols available",
172 				name);
173 			return INVALID_SOCKET;
174 #  endif /* NETINET6 */
175 # endif /* NETINET */
176 #endif /* NETUNIX */
177 		}
178 #if NETUNIX
179 		else if (strcasecmp(p, "unix") == 0 ||
180 			 strcasecmp(p, "local") == 0)
181 		{
182 			addr.sa.sa_family = AF_UNIX;
183 			L_socksize = sizeof (struct sockaddr_un);
184 		}
185 #endif /* NETUNIX */
186 #if NETINET
187 		else if (strcasecmp(p, "inet") == 0)
188 		{
189 			addr.sa.sa_family = AF_INET;
190 			L_socksize = sizeof addr.sin;
191 		}
192 #endif /* NETINET */
193 #if NETINET6
194 		else if (strcasecmp(p, "inet6") == 0)
195 		{
196 			addr.sa.sa_family = AF_INET6;
197 			L_socksize = sizeof addr.sin6;
198 		}
199 #endif /* NETINET6 */
200 		else
201 		{
202 			smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
203 				name, p);
204 			return INVALID_SOCKET;
205 		}
206 		*colon++ = ':';
207 	}
208 	else
209 	{
210 		colon = p;
211 #if NETUNIX
212 		/* default to AF_UNIX */
213 		addr.sa.sa_family = AF_UNIX;
214 		L_socksize = sizeof (struct sockaddr_un);
215 #else /* NETUNIX */
216 # if NETINET
217 		/* default to AF_INET */
218 		addr.sa.sa_family = AF_INET;
219 		L_socksize = sizeof addr.sin;
220 # else /* NETINET */
221 #  if NETINET6
222 		/* default to AF_INET6 */
223 		addr.sa.sa_family = AF_INET6;
224 		L_socksize = sizeof addr.sin6;
225 #  else /* NETINET6 */
226 		smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
227 			name, p);
228 		return INVALID_SOCKET;
229 #  endif /* NETINET6 */
230 # endif /* NETINET */
231 #endif /* NETUNIX */
232 	}
233 
234 #if NETUNIX
235 	if (addr.sa.sa_family == AF_UNIX)
236 	{
237 # if 0
238 		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
239 # endif /* 0 */
240 
241 		at = colon;
242 		len = strlen(colon) + 1;
243 		if (len >= sizeof addr.sunix.sun_path)
244 		{
245 			errno = EINVAL;
246 			smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
247 				name, colon);
248 			return INVALID_SOCKET;
249 		}
250 		(void) sm_strlcpy(addr.sunix.sun_path, colon,
251 				sizeof addr.sunix.sun_path);
252 # if 0
253 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
254 				 S_IRUSR|S_IWUSR, NULL);
255 
256 		/* if not safe, don't create */
257 		if (errno != 0)
258 		{
259 			smi_log(SMI_LOG_ERR,
260 				"%s: UNIX socket name %s unsafe",
261 				name, colon);
262 			return INVALID_SOCKET;
263 		}
264 # endif /* 0 */
265 	}
266 #endif /* NETUNIX */
267 
268 #if NETINET || NETINET6
269 	if (
270 # if NETINET
271 	    addr.sa.sa_family == AF_INET
272 # endif /* NETINET */
273 # if NETINET && NETINET6
274 	    ||
275 # endif /* NETINET && NETINET6 */
276 # if NETINET6
277 	    addr.sa.sa_family == AF_INET6
278 # endif /* NETINET6 */
279 	   )
280 	{
281 		unsigned short port;
282 
283 		/* Parse port@host */
284 		at = strchr(colon, '@');
285 		if (at == NULL)
286 		{
287 			switch (addr.sa.sa_family)
288 			{
289 # if NETINET
290 			  case AF_INET:
291 				addr.sin.sin_addr.s_addr = INADDR_ANY;
292 				break;
293 # endif /* NETINET */
294 
295 # if NETINET6
296 			  case AF_INET6:
297 				addr.sin6.sin6_addr = in6addr_any;
298 				break;
299 # endif /* NETINET6 */
300 			}
301 		}
302 		else
303 			*at = '\0';
304 
305 		if (isascii(*colon) && isdigit(*colon))
306 			port = htons((unsigned short) atoi(colon));
307 		else
308 		{
309 # ifdef NO_GETSERVBYNAME
310 			smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
311 				name, colon);
312 			return INVALID_SOCKET;
313 # else /* NO_GETSERVBYNAME */
314 			register struct servent *sp;
315 
316 			sp = getservbyname(colon, "tcp");
317 			if (sp == NULL)
318 			{
319 				smi_log(SMI_LOG_ERR,
320 					"%s: unknown port name %s",
321 					name, colon);
322 				return INVALID_SOCKET;
323 			}
324 			port = sp->s_port;
325 # endif /* NO_GETSERVBYNAME */
326 		}
327 		if (at != NULL)
328 		{
329 			*at++ = '@';
330 			if (*at == '[')
331 			{
332 				char *end;
333 
334 				end = strchr(at, ']');
335 				if (end != NULL)
336 				{
337 					bool found = false;
338 # if NETINET
339 					unsigned long hid = INADDR_NONE;
340 # endif /* NETINET */
341 # if NETINET6
342 					struct sockaddr_in6 hid6;
343 # endif /* NETINET6 */
344 
345 					*end = '\0';
346 # if NETINET
347 					if (addr.sa.sa_family == AF_INET &&
348 					    (hid = inet_addr(&at[1])) != INADDR_NONE)
349 					{
350 						addr.sin.sin_addr.s_addr = hid;
351 						addr.sin.sin_port = port;
352 						found = true;
353 					}
354 # endif /* NETINET */
355 # if NETINET6
356 					(void) memset(&hid6, '\0', sizeof hid6);
357 					if (addr.sa.sa_family == AF_INET6 &&
358 					    mi_inet_pton(AF_INET6, &at[1],
359 							 &hid6.sin6_addr) == 1)
360 					{
361 						addr.sin6.sin6_addr = hid6.sin6_addr;
362 						addr.sin6.sin6_port = port;
363 						found = true;
364 					}
365 # endif /* NETINET6 */
366 					*end = ']';
367 					if (!found)
368 					{
369 						smi_log(SMI_LOG_ERR,
370 							"%s: Invalid numeric domain spec \"%s\"",
371 							name, at);
372 						return INVALID_SOCKET;
373 					}
374 				}
375 				else
376 				{
377 					smi_log(SMI_LOG_ERR,
378 						"%s: Invalid numeric domain spec \"%s\"",
379 						name, at);
380 					return INVALID_SOCKET;
381 				}
382 			}
383 			else
384 			{
385 				struct hostent *hp = NULL;
386 
387 				hp = mi_gethostbyname(at, addr.sa.sa_family);
388 				if (hp == NULL)
389 				{
390 					smi_log(SMI_LOG_ERR,
391 						"%s: Unknown host name %s",
392 						name, at);
393 					return INVALID_SOCKET;
394 				}
395 				addr.sa.sa_family = hp->h_addrtype;
396 				switch (hp->h_addrtype)
397 				{
398 # if NETINET
399 				  case AF_INET:
400 					(void) memmove(&addr.sin.sin_addr,
401 						       hp->h_addr,
402 						       INADDRSZ);
403 					addr.sin.sin_port = port;
404 					break;
405 # endif /* NETINET */
406 
407 # if NETINET6
408 				  case AF_INET6:
409 					(void) memmove(&addr.sin6.sin6_addr,
410 						       hp->h_addr,
411 						       IN6ADDRSZ);
412 					addr.sin6.sin6_port = port;
413 					break;
414 # endif /* NETINET6 */
415 
416 				  default:
417 					smi_log(SMI_LOG_ERR,
418 						"%s: Unknown protocol for %s (%d)",
419 						name, at, hp->h_addrtype);
420 					return INVALID_SOCKET;
421 				}
422 # if NETINET6
423 				freehostent(hp);
424 # endif /* NETINET6 */
425 			}
426 		}
427 		else
428 		{
429 			switch (addr.sa.sa_family)
430 			{
431 # if NETINET
432 			  case AF_INET:
433 				addr.sin.sin_port = port;
434 				break;
435 # endif /* NETINET */
436 # if NETINET6
437 			  case AF_INET6:
438 				addr.sin6.sin6_port = port;
439 				break;
440 # endif /* NETINET6 */
441 			}
442 		}
443 	}
444 #endif /* NETINET || NETINET6 */
445 
446 	sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
447 	if (!ValidSocket(sock))
448 	{
449 		smi_log(SMI_LOG_ERR,
450 			"%s: Unable to create new socket: %s",
451 			name, sm_errstring(errno));
452 		return INVALID_SOCKET;
453 	}
454 
455 	if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 ||
456 	    fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1)
457 	{
458 		smi_log(SMI_LOG_ERR,
459 			"%s: Unable to set close-on-exec: %s", name,
460 			sm_errstring(errno));
461 		(void) closesocket(sock);
462 		return INVALID_SOCKET;
463 	}
464 
465 	if (
466 #if NETUNIX
467 	    addr.sa.sa_family != AF_UNIX &&
468 #endif /* NETUNIX */
469 	    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
470 		       sizeof(sockopt)) == -1)
471 	{
472 		smi_log(SMI_LOG_ERR,
473 			"%s: set reuseaddr failed (%s)", name,
474 			sm_errstring(errno));
475 		(void) closesocket(sock);
476 		return INVALID_SOCKET;
477 	}
478 
479 #if NETUNIX
480 	if (addr.sa.sa_family == AF_UNIX && rmsocket)
481 	{
482 		struct stat s;
483 
484 		if (stat(colon, &s) != 0)
485 		{
486 			if (errno != ENOENT)
487 			{
488 				smi_log(SMI_LOG_ERR,
489 					"%s: Unable to stat() %s: %s",
490 					name, colon, sm_errstring(errno));
491 				(void) closesocket(sock);
492 				return INVALID_SOCKET;
493 			}
494 		}
495 		else if (!S_ISSOCK(s.st_mode))
496 		{
497 			smi_log(SMI_LOG_ERR,
498 				"%s: %s is not a UNIX domain socket",
499 				name, colon);
500 			(void) closesocket(sock);
501 			return INVALID_SOCKET;
502 		}
503 		else if (unlink(colon) != 0)
504 		{
505 			smi_log(SMI_LOG_ERR,
506 				"%s: Unable to remove %s: %s",
507 				name, colon, sm_errstring(errno));
508 			(void) closesocket(sock);
509 			return INVALID_SOCKET;
510 		}
511 	}
512 #endif /* NETUNIX */
513 
514 	if (bind(sock, &addr.sa, L_socksize) < 0)
515 	{
516 		smi_log(SMI_LOG_ERR,
517 			"%s: Unable to bind to port %s: %s",
518 			name, conn, sm_errstring(errno));
519 		(void) closesocket(sock);
520 		return INVALID_SOCKET;
521 	}
522 
523 	if (listen(sock, backlog) < 0)
524 	{
525 		smi_log(SMI_LOG_ERR,
526 			"%s: listen call failed: %s", name,
527 			sm_errstring(errno));
528 		(void) closesocket(sock);
529 		return INVALID_SOCKET;
530 	}
531 
532 #if NETUNIX
533 	if (addr.sa.sa_family == AF_UNIX && len > 0)
534 	{
535 		/*
536 		**  Set global variable sockpath so the UNIX socket can be
537 		**  unlink()ed at exit.
538 		*/
539 
540 		sockpath = (char *) malloc(len);
541 		if (sockpath != NULL)
542 			(void) sm_strlcpy(sockpath, colon, len);
543 		else
544 		{
545 			smi_log(SMI_LOG_ERR,
546 				"%s: can't malloc(%d) for sockpath: %s",
547 				name, (int) len, sm_errstring(errno));
548 			(void) closesocket(sock);
549 			return INVALID_SOCKET;
550 		}
551 	}
552 #endif /* NETUNIX */
553 	L_family = addr.sa.sa_family;
554 	return sock;
555 }
556 
557 #if !_FFR_WORKERS_POOL
558 /*
559 **  MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
560 **
561 **	Parameters:
562 **		arg -- argument to pass to mi_handle_session()
563 **
564 **	Returns:
565 **		results from mi_handle_session()
566 */
567 
568 static void *
mi_thread_handle_wrapper(arg)569 mi_thread_handle_wrapper(arg)
570 	void *arg;
571 {
572 	/*
573 	**  Note: on some systems this generates a compiler warning:
574 	**  cast to pointer from integer of different size
575 	**  You can safely ignore this warning as the result of this function
576 	**  is not used anywhere.
577 	*/
578 
579 	return (void *) mi_handle_session(arg);
580 }
581 #endif /* _FFR_WORKERS_POOL */
582 
583 /*
584 **  MI_CLOSENER -- close listen socket
585 **
586 **	Parameters:
587 **		none.
588 **
589 **	Returns:
590 **		none.
591 */
592 
593 void
mi_closener()594 mi_closener()
595 {
596 	(void) smutex_lock(&L_Mutex);
597 	if (ValidSocket(listenfd))
598 	{
599 #if NETUNIX
600 		bool removable;
601 		struct stat sockinfo;
602 		struct stat fileinfo;
603 
604 		removable = sockpath != NULL &&
605 			    geteuid() != 0 &&
606 			    fstat(listenfd, &sockinfo) == 0 &&
607 			    (S_ISFIFO(sockinfo.st_mode)
608 # ifdef S_ISSOCK
609 			     || S_ISSOCK(sockinfo.st_mode)
610 # endif /* S_ISSOCK */
611 			    );
612 #endif /* NETUNIX */
613 
614 		(void) closesocket(listenfd);
615 		listenfd = INVALID_SOCKET;
616 
617 #if NETUNIX
618 		/* XXX sleep() some time before doing this? */
619 		if (sockpath != NULL)
620 		{
621 			if (removable &&
622 			    stat(sockpath, &fileinfo) == 0 &&
623 			    ((fileinfo.st_dev == sockinfo.st_dev &&
624 			      fileinfo.st_ino == sockinfo.st_ino)
625 # ifdef S_ISSOCK
626 			     || S_ISSOCK(fileinfo.st_mode)
627 # endif /* S_ISSOCK */
628 			    )
629 			    &&
630 			    (S_ISFIFO(fileinfo.st_mode)
631 # ifdef S_ISSOCK
632 			     || S_ISSOCK(fileinfo.st_mode)
633 # endif /* S_ISSOCK */
634 			     ))
635 				(void) unlink(sockpath);
636 			free(sockpath);
637 			sockpath = NULL;
638 		}
639 #endif /* NETUNIX */
640 	}
641 	(void) smutex_unlock(&L_Mutex);
642 }
643 
644 /*
645 **  MI_LISTENER -- Generic listener harness
646 **
647 **	Open up listen port
648 **	Wait for connections
649 **
650 **	Parameters:
651 **		conn -- connection description
652 **		dbg -- debug level
653 **		smfi -- filter structure to use
654 **		timeout -- timeout for reads/writes
655 **		backlog -- listen queue backlog size
656 **
657 **	Returns:
658 **		MI_SUCCESS -- Exited normally
659 **			   (session finished or we were told to exit)
660 **		MI_FAILURE -- Network initialization failed.
661 */
662 
663 #if BROKEN_PTHREAD_SLEEP
664 
665 /*
666 **  Solaris 2.6, perhaps others, gets an internal threads library panic
667 **  when sleep() is used:
668 **
669 **  thread_create() failed, returned 11 (EINVAL)
670 **  co_enable, thr_create() returned error = 24
671 **  libthread panic: co_enable failed (PID: 17793 LWP 1)
672 **  stacktrace:
673 **	ef526b10
674 **	ef52646c
675 **	ef534cbc
676 **	156a4
677 **	14644
678 **	1413c
679 **	135e0
680 **	0
681 */
682 
683 # define MI_SLEEP(s)							\
684 {									\
685 	int rs = 0;							\
686 	struct timeval st;						\
687 									\
688 	st.tv_sec = (s);						\
689 	st.tv_usec = 0;							\
690 	if (st.tv_sec > 0)						\
691 	{								\
692 		for (;;)						\
693 		{							\
694 			rs = select(0, NULL, NULL, NULL, &st);		\
695 			if (rs < 0 && errno == EINTR)			\
696 				continue;				\
697 			if (rs != 0)					\
698 			{						\
699 				smi_log(SMI_LOG_ERR,			\
700 					"MI_SLEEP(): select() returned non-zero result %d, errno = %d",	\
701 					rs, errno);			\
702 			}						\
703 			break;						\
704 		}							\
705 	}								\
706 }
707 #else /* BROKEN_PTHREAD_SLEEP */
708 # define MI_SLEEP(s)	sleep((s))
709 #endif /* BROKEN_PTHREAD_SLEEP */
710 
711 int
mi_listener(conn,dbg,smfi,timeout,backlog)712 mi_listener(conn, dbg, smfi, timeout, backlog)
713 	char *conn;
714 	int dbg;
715 	smfiDesc_ptr smfi;
716 	time_t timeout;
717 	int backlog;
718 {
719 	socket_t connfd = INVALID_SOCKET;
720 #if _FFR_DUP_FD
721 	socket_t dupfd = INVALID_SOCKET;
722 #endif /* _FFR_DUP_FD */
723 	int sockopt = 1;
724 	int r, mistop;
725 	int ret = MI_SUCCESS;
726 	int mcnt = 0;	/* error count for malloc() failures */
727 	int tcnt = 0;	/* error count for thread_create() failures */
728 	int acnt = 0;	/* error count for accept() failures */
729 	int scnt = 0;	/* error count for select() failures */
730 	int save_errno = 0;
731 #if !_FFR_WORKERS_POOL
732 	sthread_t thread_id;
733 #endif /* !_FFR_WORKERS_POOL */
734 	_SOCK_ADDR cliaddr;
735 	SOCKADDR_LEN_T clilen;
736 	SMFICTX_PTR ctx;
737 	FD_RD_VAR(rds, excs);
738 	struct timeval chktime;
739 
740 	if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE)
741 		return MI_FAILURE;
742 
743 #if _FFR_WORKERS_POOL
744 	if (mi_pool_controller_init() == MI_FAILURE)
745 		return MI_FAILURE;
746 #endif /* _FFR_WORKERS_POOL */
747 
748 	clilen = L_socksize;
749 	while ((mistop = mi_stop()) == MILTER_CONT)
750 	{
751 		(void) smutex_lock(&L_Mutex);
752 		if (!ValidSocket(listenfd))
753 		{
754 			ret = MI_FAILURE;
755 			smi_log(SMI_LOG_ERR,
756 				"%s: listenfd=%d corrupted, terminating, errno=%d",
757 				smfi->xxfi_name, listenfd, errno);
758 			(void) smutex_unlock(&L_Mutex);
759 			break;
760 		}
761 
762 		/* select on interface ports */
763 		FD_RD_INIT(listenfd, rds, excs);
764 		chktime.tv_sec = MI_CHK_TIME;
765 		chktime.tv_usec = 0;
766 		r = FD_RD_READY(listenfd, rds, excs, &chktime);
767 		if (r == 0)		/* timeout */
768 		{
769 			(void) smutex_unlock(&L_Mutex);
770 			continue;	/* just check mi_stop() */
771 		}
772 		if (r < 0)
773 		{
774 			save_errno = errno;
775 			(void) smutex_unlock(&L_Mutex);
776 			if (save_errno == EINTR)
777 				continue;
778 			scnt++;
779 			smi_log(SMI_LOG_ERR,
780 				"%s: %s() failed (%s), %s",
781 				smfi->xxfi_name, MI_POLLSELECT,
782 				sm_errstring(save_errno),
783 				scnt >= MAX_FAILS_S ? "abort" : "try again");
784 			MI_SLEEP(scnt);
785 			if (scnt >= MAX_FAILS_S)
786 			{
787 				ret = MI_FAILURE;
788 				break;
789 			}
790 			continue;
791 		}
792 		if (!FD_IS_RD_RDY(listenfd, rds, excs))
793 		{
794 			/* some error: just stop for now... */
795 			ret = MI_FAILURE;
796 			(void) smutex_unlock(&L_Mutex);
797 			smi_log(SMI_LOG_ERR,
798 				"%s: %s() returned exception for socket, abort",
799 				smfi->xxfi_name, MI_POLLSELECT);
800 			break;
801 		}
802 		scnt = 0;	/* reset error counter for select() */
803 
804 		(void) memset(&cliaddr, '\0', sizeof cliaddr);
805 		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
806 				&clilen);
807 		save_errno = errno;
808 		(void) smutex_unlock(&L_Mutex);
809 
810 		/*
811 		**  If remote side closes before accept() finishes,
812 		**  sockaddr might not be fully filled in.
813 		*/
814 
815 		if (ValidSocket(connfd) &&
816 		    (clilen == 0 ||
817 # ifdef BSD4_4_SOCKADDR
818 		     cliaddr.sa.sa_len == 0 ||
819 # endif /* BSD4_4_SOCKADDR */
820 		     cliaddr.sa.sa_family != L_family))
821 		{
822 			(void) closesocket(connfd);
823 			connfd = INVALID_SOCKET;
824 			save_errno = EINVAL;
825 		}
826 
827 		/* check if acceptable for select() */
828 		if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd))
829 		{
830 			(void) closesocket(connfd);
831 			connfd = INVALID_SOCKET;
832 			save_errno = ERANGE;
833 		}
834 
835 		if (!ValidSocket(connfd))
836 		{
837 			if (save_errno == EINTR
838 #ifdef EAGAIN
839 			    || save_errno == EAGAIN
840 #endif /* EAGAIN */
841 #ifdef ECONNABORTED
842 			    || save_errno == ECONNABORTED
843 #endif /* ECONNABORTED */
844 #ifdef EMFILE
845 			    || save_errno == EMFILE
846 #endif /* EMFILE */
847 #ifdef ENFILE
848 			    || save_errno == ENFILE
849 #endif /* ENFILE */
850 #ifdef ENOBUFS
851 			    || save_errno == ENOBUFS
852 #endif /* ENOBUFS */
853 #ifdef ENOMEM
854 			    || save_errno == ENOMEM
855 #endif /* ENOMEM */
856 #ifdef ENOSR
857 			    || save_errno == ENOSR
858 #endif /* ENOSR */
859 #ifdef EWOULDBLOCK
860 			    || save_errno == EWOULDBLOCK
861 #endif /* EWOULDBLOCK */
862 			   )
863 				continue;
864 			acnt++;
865 			smi_log(SMI_LOG_ERR,
866 				"%s: accept() returned invalid socket (%s), %s",
867 				smfi->xxfi_name, sm_errstring(save_errno),
868 				acnt >= MAX_FAILS_A ? "abort" : "try again");
869 			MI_SLEEP(acnt);
870 			if (acnt >= MAX_FAILS_A)
871 			{
872 				ret = MI_FAILURE;
873 				break;
874 			}
875 			continue;
876 		}
877 		acnt = 0;	/* reset error counter for accept() */
878 #if _FFR_DUP_FD
879 		dupfd = fcntl(connfd, F_DUPFD, 256);
880 		if (ValidSocket(dupfd) && SM_FD_OK_SELECT(dupfd))
881 		{
882 			close(connfd);
883 			connfd = dupfd;
884 			dupfd = INVALID_SOCKET;
885 		}
886 #endif /* _FFR_DUP_FD */
887 
888 		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
889 				(void *) &sockopt, sizeof sockopt) < 0)
890 		{
891 			smi_log(SMI_LOG_WARN,
892 				"%s: set keepalive failed (%s)",
893 				smfi->xxfi_name, sm_errstring(errno));
894 			/* XXX: continue? */
895 		}
896 		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
897 		{
898 			(void) closesocket(connfd);
899 			mcnt++;
900 			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s",
901 				smfi->xxfi_name, sm_errstring(save_errno),
902 				mcnt >= MAX_FAILS_M ? "abort" : "try again");
903 			MI_SLEEP(mcnt);
904 			if (mcnt >= MAX_FAILS_M)
905 			{
906 				ret = MI_FAILURE;
907 				break;
908 			}
909 			continue;
910 		}
911 		mcnt = 0;	/* reset error counter for malloc() */
912 		(void) memset(ctx, '\0', sizeof *ctx);
913 		ctx->ctx_sd = connfd;
914 		ctx->ctx_dbg = dbg;
915 		ctx->ctx_timeout = timeout;
916 		ctx->ctx_smfi = smfi;
917 		if (smfi->xxfi_connect == NULL)
918 			ctx->ctx_pflags |= SMFIP_NOCONNECT;
919 		if (smfi->xxfi_helo == NULL)
920 			ctx->ctx_pflags |= SMFIP_NOHELO;
921 		if (smfi->xxfi_envfrom == NULL)
922 			ctx->ctx_pflags |= SMFIP_NOMAIL;
923 		if (smfi->xxfi_envrcpt == NULL)
924 			ctx->ctx_pflags |= SMFIP_NORCPT;
925 		if (smfi->xxfi_header == NULL)
926 			ctx->ctx_pflags |= SMFIP_NOHDRS;
927 		if (smfi->xxfi_eoh == NULL)
928 			ctx->ctx_pflags |= SMFIP_NOEOH;
929 		if (smfi->xxfi_body == NULL)
930 			ctx->ctx_pflags |= SMFIP_NOBODY;
931 		if (smfi->xxfi_version <= 3 || smfi->xxfi_data == NULL)
932 			ctx->ctx_pflags |= SMFIP_NODATA;
933 		if (smfi->xxfi_version <= 2 || smfi->xxfi_unknown == NULL)
934 			ctx->ctx_pflags |= SMFIP_NOUNKNOWN;
935 
936 #if _FFR_WORKERS_POOL
937 # define LOG_CRT_FAIL	"%s: mi_start_session() failed: %d, %s"
938 		if ((r = mi_start_session(ctx)) != MI_SUCCESS)
939 #else /* _FFR_WORKERS_POOL */
940 # define LOG_CRT_FAIL	"%s: thread_create() failed: %d, %s"
941 		if ((r = thread_create(&thread_id,
942 					mi_thread_handle_wrapper,
943 					(void *) ctx)) != 0)
944 #endif /* _FFR_WORKERS_POOL */
945 		{
946 			tcnt++;
947 			smi_log(SMI_LOG_ERR,
948 				LOG_CRT_FAIL,
949 				smfi->xxfi_name,  r,
950 				tcnt >= MAX_FAILS_T ? "abort" : "try again");
951 			MI_SLEEP(tcnt);
952 			(void) closesocket(connfd);
953 			free(ctx);
954 			if (tcnt >= MAX_FAILS_T)
955 			{
956 				ret = MI_FAILURE;
957 				break;
958 			}
959 			continue;
960 		}
961 		tcnt = 0;
962 	}
963 	if (ret != MI_SUCCESS)
964 		mi_stop_milters(MILTER_ABRT);
965 	else
966 	{
967 		if (mistop != MILTER_CONT)
968 			smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
969 				smfi->xxfi_name, mistop);
970 		mi_closener();
971 	}
972 	(void) smutex_destroy(&L_Mutex);
973 	return ret;
974 }
975