sendmail: Update vendor branch to v8.14.4
[dragonfly.git] / contrib / sendmail-8.14 / libmilter / listener.c
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
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
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 *
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 *)(intptr_t)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
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
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 }