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