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