2 * Copyright (c) 1999-2007 Sendmail, Inc. and its suppliers.
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.
12 SM_RCSID("@(#)$Id: listener.c,v 8.124 2007/04/23 22:22:50 ca Exp $")
15 ** listener.c -- threaded network listener
18 #include "libmilter.h"
19 #include <sm/errstring.h>
21 #include <sys/types.h>
25 # if NETINET || NETINET6
26 # include <arpa/inet.h>
27 # endif /* NETINET || NETINET6 */
29 # undef SM_FD_OK_SELECT
30 # define SM_FD_OK_SELECT(fd) true
31 # endif /* SM_CONF_POLL */
33 static smutex_t L_Mutex
;
35 static SOCKADDR_LEN_T L_socksize
;
36 static socket_t listenfd
= INVALID_SOCKET
;
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 */
44 ** MI_OPENSOCKET -- create the socket where this filter and the MTA will meet
47 ** conn -- connection description
48 ** backlog -- listen backlog
50 ** rmsocket -- if true, try to unlink() the socket first
51 ** (UNIX domain sockets only)
52 ** smfi -- filter structure to use
55 ** MI_SUCCESS/MI_FAILURE
59 mi_opensocket(conn
, backlog
, dbg
, rmsocket
, smfi
)
66 if (smfi
== NULL
|| conn
== NULL
)
69 if (ValidSocket(listenfd
))
74 smi_log(SMI_LOG_DEBUG
,
75 "%s: Opening listen socket on conn %s",
76 smfi
->xxfi_name
, conn
);
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
))
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
);
89 if (!SM_FD_OK_SELECT(listenfd
))
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
);
96 (void) smutex_unlock(&L_Mutex
);
101 ** MI_MILTEROPEN -- setup socket to listen on
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
111 ** socket upon success, error code otherwise.
114 ** sets sockpath if UNIX socket.
118 static char *sockpath
= NULL
;
122 mi_milteropen(conn
, backlog
, rmsocket
, name
)
137 if (conn
== NULL
|| conn
[0] == '\0')
139 smi_log(SMI_LOG_ERR
, "%s: empty or missing socket information",
141 return INVALID_SOCKET
;
143 (void) memset(&addr
, '\0', sizeof addr
);
145 /* protocol:filename or protocol:port@host */
147 colon
= strchr(p
, ':');
155 /* default to AF_UNIX */
156 addr
.sa
.sa_family
= AF_UNIX
;
157 L_socksize
= sizeof (struct sockaddr_un
);
160 /* default to AF_INET */
161 addr
.sa
.sa_family
= AF_INET
;
162 L_socksize
= sizeof addr
.sin
;
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 */
171 "%s: no valid socket protocols available",
173 return INVALID_SOCKET
;
174 # endif /* NETINET6 */
175 # endif /* NETINET */
179 else if (strcasecmp(p
, "unix") == 0 ||
180 strcasecmp(p
, "local") == 0)
182 addr
.sa
.sa_family
= AF_UNIX
;
183 L_socksize
= sizeof (struct sockaddr_un
);
187 else if (strcasecmp(p
, "inet") == 0)
189 addr
.sa
.sa_family
= AF_INET
;
190 L_socksize
= sizeof addr
.sin
;
194 else if (strcasecmp(p
, "inet6") == 0)
196 addr
.sa
.sa_family
= AF_INET6
;
197 L_socksize
= sizeof addr
.sin6
;
199 #endif /* NETINET6 */
202 smi_log(SMI_LOG_ERR
, "%s: unknown socket type %s",
204 return INVALID_SOCKET
;
212 /* default to AF_UNIX */
213 addr
.sa
.sa_family
= AF_UNIX
;
214 L_socksize
= sizeof (struct sockaddr_un
);
217 /* default to AF_INET */
218 addr
.sa
.sa_family
= AF_INET
;
219 L_socksize
= sizeof addr
.sin
;
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",
228 return INVALID_SOCKET
;
229 # endif /* NETINET6 */
230 # endif /* NETINET */
235 if (addr
.sa
.sa_family
== AF_UNIX
)
238 long sff
= SFF_SAFEDIRPATH
|SFF_OPENASROOT
|SFF_NOLINK
|SFF_CREAT
|SFF_MUSTOWN
;
242 len
= strlen(colon
) + 1;
243 if (len
>= sizeof addr
.sunix
.sun_path
)
246 smi_log(SMI_LOG_ERR
, "%s: UNIX socket name %s too long",
248 return INVALID_SOCKET
;
250 (void) sm_strlcpy(addr
.sunix
.sun_path
, colon
,
251 sizeof addr
.sunix
.sun_path
);
253 errno
= safefile(colon
, RunAsUid
, RunAsGid
, RunAsUserName
, sff
,
254 S_IRUSR
|S_IWUSR
, NULL
);
256 /* if not safe, don't create */
260 "%s: UNIX socket name %s unsafe",
262 return INVALID_SOCKET
;
268 #if NETINET || NETINET6
271 addr
.sa
.sa_family
== AF_INET
272 # endif /* NETINET */
273 # if NETINET && NETINET6
275 # endif /* NETINET && NETINET6 */
277 addr
.sa
.sa_family
== AF_INET6
278 # endif /* NETINET6 */
283 /* Parse port@host */
284 at
= strchr(colon
, '@');
287 switch (addr
.sa
.sa_family
)
291 addr
.sin
.sin_addr
.s_addr
= INADDR_ANY
;
293 # endif /* NETINET */
297 addr
.sin6
.sin6_addr
= in6addr_any
;
299 # endif /* NETINET6 */
305 if (isascii(*colon
) && isdigit(*colon
))
306 port
= htons((unsigned short) atoi(colon
));
309 # ifdef NO_GETSERVBYNAME
310 smi_log(SMI_LOG_ERR
, "%s: invalid port number %s",
312 return INVALID_SOCKET
;
313 # else /* NO_GETSERVBYNAME */
314 register struct servent
*sp
;
316 sp
= getservbyname(colon
, "tcp");
320 "%s: unknown port name %s",
322 return INVALID_SOCKET
;
325 # endif /* NO_GETSERVBYNAME */
334 end
= strchr(at
, ']');
339 unsigned long hid
= INADDR_NONE
;
340 # endif /* NETINET */
342 struct sockaddr_in6 hid6
;
343 # endif /* NETINET6 */
347 if (addr
.sa
.sa_family
== AF_INET
&&
348 (hid
= inet_addr(&at
[1])) != INADDR_NONE
)
350 addr
.sin
.sin_addr
.s_addr
= hid
;
351 addr
.sin
.sin_port
= port
;
354 # endif /* NETINET */
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)
361 addr
.sin6
.sin6_addr
= hid6
.sin6_addr
;
362 addr
.sin6
.sin6_port
= port
;
365 # endif /* NETINET6 */
370 "%s: Invalid numeric domain spec \"%s\"",
372 return INVALID_SOCKET
;
378 "%s: Invalid numeric domain spec \"%s\"",
380 return INVALID_SOCKET
;
385 struct hostent
*hp
= NULL
;
387 hp
= mi_gethostbyname(at
, addr
.sa
.sa_family
);
391 "%s: Unknown host name %s",
393 return INVALID_SOCKET
;
395 addr
.sa
.sa_family
= hp
->h_addrtype
;
396 switch (hp
->h_addrtype
)
400 (void) memmove(&addr
.sin
.sin_addr
,
403 addr
.sin
.sin_port
= port
;
405 # endif /* NETINET */
409 (void) memmove(&addr
.sin6
.sin6_addr
,
412 addr
.sin6
.sin6_port
= port
;
414 # endif /* NETINET6 */
418 "%s: Unknown protocol for %s (%d)",
419 name
, at
, hp
->h_addrtype
);
420 return INVALID_SOCKET
;
424 # endif /* NETINET6 */
429 switch (addr
.sa
.sa_family
)
433 addr
.sin
.sin_port
= port
;
435 # endif /* NETINET */
438 addr
.sin6
.sin6_port
= port
;
440 # endif /* NETINET6 */
444 #endif /* NETINET || NETINET6 */
446 sock
= socket(addr
.sa
.sa_family
, SOCK_STREAM
, 0);
447 if (!ValidSocket(sock
))
450 "%s: Unable to create new socket: %s",
451 name
, sm_errstring(errno
));
452 return INVALID_SOCKET
;
455 if ((fdflags
= fcntl(sock
, F_GETFD
, 0)) == -1 ||
456 fcntl(sock
, F_SETFD
, fdflags
| FD_CLOEXEC
) == -1)
459 "%s: Unable to set close-on-exec: %s", name
,
460 sm_errstring(errno
));
461 (void) closesocket(sock
);
462 return INVALID_SOCKET
;
467 addr
.sa
.sa_family
!= AF_UNIX
&&
469 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *) &sockopt
,
470 sizeof(sockopt
)) == -1)
473 "%s: set reuseaddr failed (%s)", name
,
474 sm_errstring(errno
));
475 (void) closesocket(sock
);
476 return INVALID_SOCKET
;
480 if (addr
.sa
.sa_family
== AF_UNIX
&& rmsocket
)
484 if (stat(colon
, &s
) != 0)
489 "%s: Unable to stat() %s: %s",
490 name
, colon
, sm_errstring(errno
));
491 (void) closesocket(sock
);
492 return INVALID_SOCKET
;
495 else if (!S_ISSOCK(s
.st_mode
))
498 "%s: %s is not a UNIX domain socket",
500 (void) closesocket(sock
);
501 return INVALID_SOCKET
;
503 else if (unlink(colon
) != 0)
506 "%s: Unable to remove %s: %s",
507 name
, colon
, sm_errstring(errno
));
508 (void) closesocket(sock
);
509 return INVALID_SOCKET
;
514 if (bind(sock
, &addr
.sa
, L_socksize
) < 0)
517 "%s: Unable to bind to port %s: %s",
518 name
, conn
, sm_errstring(errno
));
519 (void) closesocket(sock
);
520 return INVALID_SOCKET
;
523 if (listen(sock
, backlog
) < 0)
526 "%s: listen call failed: %s", name
,
527 sm_errstring(errno
));
528 (void) closesocket(sock
);
529 return INVALID_SOCKET
;
533 if (addr
.sa
.sa_family
== AF_UNIX
&& len
> 0)
536 ** Set global variable sockpath so the UNIX socket can be
537 ** unlink()ed at exit.
540 sockpath
= (char *) malloc(len
);
541 if (sockpath
!= NULL
)
542 (void) sm_strlcpy(sockpath
, colon
, len
);
546 "%s: can't malloc(%d) for sockpath: %s",
547 name
, (int) len
, sm_errstring(errno
));
548 (void) closesocket(sock
);
549 return INVALID_SOCKET
;
553 L_family
= addr
.sa
.sa_family
;
557 #if !_FFR_WORKERS_POOL
559 ** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
562 ** arg -- argument to pass to mi_handle_session()
565 ** results from mi_handle_session()
569 mi_thread_handle_wrapper(arg
)
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.
579 return (void *) mi_handle_session(arg
);
581 #endif /* _FFR_WORKERS_POOL */
584 ** MI_CLOSENER -- close listen socket
596 (void) smutex_lock(&L_Mutex
);
597 if (ValidSocket(listenfd
))
601 struct stat sockinfo
;
602 struct stat fileinfo
;
604 removable
= sockpath
!= NULL
&&
606 fstat(listenfd
, &sockinfo
) == 0 &&
607 (S_ISFIFO(sockinfo
.st_mode
)
609 || S_ISSOCK(sockinfo
.st_mode
)
610 # endif /* S_ISSOCK */
614 (void) closesocket(listenfd
);
615 listenfd
= INVALID_SOCKET
;
618 /* XXX sleep() some time before doing this? */
619 if (sockpath
!= NULL
)
622 stat(sockpath
, &fileinfo
) == 0 &&
623 ((fileinfo
.st_dev
== sockinfo
.st_dev
&&
624 fileinfo
.st_ino
== sockinfo
.st_ino
)
626 || S_ISSOCK(fileinfo
.st_mode
)
627 # endif /* S_ISSOCK */
630 (S_ISFIFO(fileinfo
.st_mode
)
632 || S_ISSOCK(fileinfo
.st_mode
)
633 # endif /* S_ISSOCK */
635 (void) unlink(sockpath
);
641 (void) smutex_unlock(&L_Mutex
);
645 ** MI_LISTENER -- Generic listener harness
647 ** Open up listen port
648 ** Wait for connections
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
658 ** MI_SUCCESS -- Exited normally
659 ** (session finished or we were told to exit)
660 ** MI_FAILURE -- Network initialization failed.
663 #if BROKEN_PTHREAD_SLEEP
666 ** Solaris 2.6, perhaps others, gets an internal threads library panic
667 ** when sleep() is used:
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)
683 # define MI_SLEEP(s) \
694 rs = select(0, NULL, NULL, NULL, &st); \
695 if (rs < 0 && errno == EINTR) \
699 smi_log(SMI_LOG_ERR, \
700 "MI_SLEEP(): select() returned non-zero result %d, errno = %d", \
707 #else /* BROKEN_PTHREAD_SLEEP */
708 # define MI_SLEEP(s) sleep((s))
709 #endif /* BROKEN_PTHREAD_SLEEP */
712 mi_listener(conn
, dbg
, smfi
, timeout
, backlog
)
719 socket_t connfd
= INVALID_SOCKET
;
721 socket_t dupfd
= INVALID_SOCKET
;
722 #endif /* _FFR_DUP_FD */
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 */
731 #if !_FFR_WORKERS_POOL
733 #endif /* !_FFR_WORKERS_POOL */
735 SOCKADDR_LEN_T clilen
;
737 FD_RD_VAR(rds
, excs
);
738 struct timeval chktime
;
740 if (mi_opensocket(conn
, backlog
, dbg
, false, smfi
) == MI_FAILURE
)
743 #if _FFR_WORKERS_POOL
744 if (mi_pool_controller_init() == MI_FAILURE
)
746 #endif /* _FFR_WORKERS_POOL */
749 while ((mistop
= mi_stop()) == MILTER_CONT
)
751 (void) smutex_lock(&L_Mutex
);
752 if (!ValidSocket(listenfd
))
756 "%s: listenfd=%d corrupted, terminating, errno=%d",
757 smfi
->xxfi_name
, listenfd
, errno
);
758 (void) smutex_unlock(&L_Mutex
);
762 /* select on interface ports */
763 FD_RD_INIT(listenfd
, rds
, excs
);
764 chktime
.tv_sec
= MI_CHK_TIME
;
766 r
= FD_RD_READY(listenfd
, rds
, excs
, &chktime
);
767 if (r
== 0) /* timeout */
769 (void) smutex_unlock(&L_Mutex
);
770 continue; /* just check mi_stop() */
775 (void) smutex_unlock(&L_Mutex
);
776 if (save_errno
== EINTR
)
780 "%s: select() failed (%s), %s",
781 smfi
->xxfi_name
, sm_errstring(save_errno
),
782 scnt
>= MAX_FAILS_S
? "abort" : "try again");
784 if (scnt
>= MAX_FAILS_S
)
791 if (!FD_IS_RD_RDY(listenfd
, rds
, excs
))
793 /* some error: just stop for now... */
795 (void) smutex_unlock(&L_Mutex
);
797 "%s: %s() returned exception for socket, abort",
798 smfi
->xxfi_name
, MI_POLLSELECT
);
801 scnt
= 0; /* reset error counter for select() */
803 (void) memset(&cliaddr
, '\0', sizeof cliaddr
);
804 connfd
= accept(listenfd
, (struct sockaddr
*) &cliaddr
,
807 (void) smutex_unlock(&L_Mutex
);
810 ** If remote side closes before accept() finishes,
811 ** sockaddr might not be fully filled in.
814 if (ValidSocket(connfd
) &&
816 # ifdef BSD4_4_SOCKADDR
817 cliaddr
.sa
.sa_len
== 0 ||
818 # endif /* BSD4_4_SOCKADDR */
819 cliaddr
.sa
.sa_family
!= L_family
))
821 (void) closesocket(connfd
);
822 connfd
= INVALID_SOCKET
;
826 /* check if acceptable for select() */
827 if (ValidSocket(connfd
) && !SM_FD_OK_SELECT(connfd
))
829 (void) closesocket(connfd
);
830 connfd
= INVALID_SOCKET
;
834 if (!ValidSocket(connfd
))
836 if (save_errno
== EINTR
838 || save_errno
== EAGAIN
841 || save_errno
== ECONNABORTED
842 #endif /* ECONNABORTED */
844 || save_errno
== EMFILE
847 || save_errno
== ENFILE
850 || save_errno
== ENOBUFS
853 || save_errno
== ENOMEM
856 || save_errno
== ENOSR
859 || save_errno
== EWOULDBLOCK
860 #endif /* EWOULDBLOCK */
865 "%s: accept() returned invalid socket (%s), %s",
866 smfi
->xxfi_name
, sm_errstring(save_errno
),
867 acnt
>= MAX_FAILS_A
? "abort" : "try again");
869 if (acnt
>= MAX_FAILS_A
)
876 acnt
= 0; /* reset error counter for accept() */
878 dupfd
= fcntl(connfd
, F_DUPFD
, 256);
879 if (ValidSocket(dupfd
) && SM_FD_OK_SELECT(dupfd
))
883 dupfd
= INVALID_SOCKET
;
885 #endif /* _FFR_DUP_FD */
887 if (setsockopt(connfd
, SOL_SOCKET
, SO_KEEPALIVE
,
888 (void *) &sockopt
, sizeof sockopt
) < 0)
890 smi_log(SMI_LOG_WARN
,
891 "%s: set keepalive failed (%s)",
892 smfi
->xxfi_name
, sm_errstring(errno
));
895 if ((ctx
= (SMFICTX_PTR
) malloc(sizeof *ctx
)) == NULL
)
897 (void) closesocket(connfd
);
899 smi_log(SMI_LOG_ERR
, "%s: malloc(ctx) failed (%s), %s",
900 smfi
->xxfi_name
, sm_errstring(save_errno
),
901 mcnt
>= MAX_FAILS_M
? "abort" : "try again");
903 if (mcnt
>= MAX_FAILS_M
)
910 mcnt
= 0; /* reset error counter for malloc() */
911 (void) memset(ctx
, '\0', sizeof *ctx
);
912 ctx
->ctx_sd
= connfd
;
914 ctx
->ctx_timeout
= timeout
;
915 ctx
->ctx_smfi
= smfi
;
916 if (smfi
->xxfi_connect
== NULL
)
917 ctx
->ctx_pflags
|= SMFIP_NOCONNECT
;
918 if (smfi
->xxfi_helo
== NULL
)
919 ctx
->ctx_pflags
|= SMFIP_NOHELO
;
920 if (smfi
->xxfi_envfrom
== NULL
)
921 ctx
->ctx_pflags
|= SMFIP_NOMAIL
;
922 if (smfi
->xxfi_envrcpt
== NULL
)
923 ctx
->ctx_pflags
|= SMFIP_NORCPT
;
924 if (smfi
->xxfi_header
== NULL
)
925 ctx
->ctx_pflags
|= SMFIP_NOHDRS
;
926 if (smfi
->xxfi_eoh
== NULL
)
927 ctx
->ctx_pflags
|= SMFIP_NOEOH
;
928 if (smfi
->xxfi_body
== NULL
)
929 ctx
->ctx_pflags
|= SMFIP_NOBODY
;
930 if (smfi
->xxfi_version
<= 3 || smfi
->xxfi_data
== NULL
)
931 ctx
->ctx_pflags
|= SMFIP_NODATA
;
932 if (smfi
->xxfi_version
<= 2 || smfi
->xxfi_unknown
== NULL
)
933 ctx
->ctx_pflags
|= SMFIP_NOUNKNOWN
;
935 #if _FFR_WORKERS_POOL
936 # define LOG_CRT_FAIL "%s: mi_start_session() failed: %d, %s"
937 if ((r
= mi_start_session(ctx
)) != MI_SUCCESS
)
938 #else /* _FFR_WORKERS_POOL */
939 # define LOG_CRT_FAIL "%s: thread_create() failed: %d, %s"
940 if ((r
= thread_create(&thread_id
,
941 mi_thread_handle_wrapper
,
943 #endif /* _FFR_WORKERS_POOL */
949 tcnt
>= MAX_FAILS_T
? "abort" : "try again");
951 (void) closesocket(connfd
);
953 if (tcnt
>= MAX_FAILS_T
)
962 if (ret
!= MI_SUCCESS
)
963 mi_stop_milters(MILTER_ABRT
);
966 if (mistop
!= MILTER_CONT
)
967 smi_log(SMI_LOG_INFO
, "%s: mi_stop=%d",
968 smfi
->xxfi_name
, mistop
);
971 (void) smutex_destroy(&L_Mutex
);