2 * Copyright (c) 1999-2003 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.85.2.12 2003/08/04 18:47:29 ca Exp $")
15 ** listener.c -- threaded network listener
18 #include "libmilter.h"
19 #include <sm/errstring.h>
22 # if NETINET || NETINET6
23 # include <arpa/inet.h>
24 # endif /* NETINET || NETINET6 */
26 static smutex_t L_Mutex;
28 static SOCKADDR_LEN_T L_socksize;
29 static socket_t listenfd = INVALID_SOCKET;
31 static socket_t mi_milteropen __P((char *, int, char *));
34 ** MI_OPENSOCKET -- create the socket where this filter and the MTA will meet
37 ** conn -- connection description
38 ** backlog -- listen backlog
40 ** smfi -- filter structure to use
43 ** MI_SUCCESS/MI_FAILURE
47 mi_opensocket(conn, backlog, dbg, smfi)
53 if (smfi == NULL || conn == NULL)
56 if (ValidSocket(listenfd))
61 smi_log(SMI_LOG_DEBUG,
62 "%s: Opening listen socket on conn %s",
63 smfi->xxfi_name, conn);
65 (void) smutex_init(&L_Mutex);
66 (void) smutex_lock(&L_Mutex);
67 listenfd = mi_milteropen(conn, backlog, smfi->xxfi_name);
68 if (!ValidSocket(listenfd))
70 smi_log(SMI_LOG_FATAL,
71 "%s: Unable to create listening socket on conn %s",
72 smfi->xxfi_name, conn);
73 (void) smutex_unlock(&L_Mutex);
77 if (!SM_FD_OK_SELECT(listenfd))
79 smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
80 smfi->xxfi_name, listenfd, FD_SETSIZE);
81 (void) smutex_unlock(&L_Mutex);
84 #endif /* !_FFR_USE_POLL */
89 ** MI_MILTEROPEN -- setup socket to listen on
92 ** conn -- connection description
93 ** backlog -- listen backlog
94 ** name -- name for logging
97 ** socket upon success, error code otherwise.
100 ** sets sockpath if UNIX socket.
104 static char *sockpath = NULL;
108 mi_milteropen(conn, backlog, name)
122 if (conn == NULL || conn[0] == '\0')
124 smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
126 return INVALID_SOCKET;
128 (void) memset(&addr, '\0', sizeof addr);
130 /* protocol:filename or protocol:port@host */
132 colon = strchr(p, ':');
140 /* default to AF_UNIX */
141 addr.sa.sa_family = AF_UNIX;
142 L_socksize = sizeof (struct sockaddr_un);
145 /* default to AF_INET */
146 addr.sa.sa_family = AF_INET;
147 L_socksize = sizeof addr.sin;
150 /* default to AF_INET6 */
151 addr.sa.sa_family = AF_INET6;
152 L_socksize = sizeof addr.sin6;
153 # else /* NETINET6 */
154 /* no protocols available */
156 "%s: no valid socket protocols available",
158 return INVALID_SOCKET;
159 # endif /* NETINET6 */
160 # endif /* NETINET */
164 else if (strcasecmp(p, "unix") == 0 ||
165 strcasecmp(p, "local") == 0)
167 addr.sa.sa_family = AF_UNIX;
168 L_socksize = sizeof (struct sockaddr_un);
172 else if (strcasecmp(p, "inet") == 0)
174 addr.sa.sa_family = AF_INET;
175 L_socksize = sizeof addr.sin;
179 else if (strcasecmp(p, "inet6") == 0)
181 addr.sa.sa_family = AF_INET6;
182 L_socksize = sizeof addr.sin6;
184 #endif /* NETINET6 */
187 smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
189 return INVALID_SOCKET;
197 /* default to AF_UNIX */
198 addr.sa.sa_family = AF_UNIX;
199 L_socksize = sizeof (struct sockaddr_un);
202 /* default to AF_INET */
203 addr.sa.sa_family = AF_INET;
204 L_socksize = sizeof addr.sin;
207 /* default to AF_INET6 */
208 addr.sa.sa_family = AF_INET6;
209 L_socksize = sizeof addr.sin6;
210 # else /* NETINET6 */
211 smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
213 return INVALID_SOCKET;
214 # endif /* NETINET6 */
215 # endif /* NETINET */
220 if (addr.sa.sa_family == AF_UNIX)
223 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
227 len = strlen(colon) + 1;
228 if (len >= sizeof addr.sunix.sun_path)
231 smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
233 return INVALID_SOCKET;
235 (void) sm_strlcpy(addr.sunix.sun_path, colon,
236 sizeof addr.sunix.sun_path);
238 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
239 S_IRUSR|S_IWUSR, NULL);
241 /* if not safe, don't create */
245 "%s: UNIX socket name %s unsafe",
247 return INVALID_SOCKET;
253 #if NETINET || NETINET6
256 addr.sa.sa_family == AF_INET
257 # endif /* NETINET */
258 # if NETINET && NETINET6
260 # endif /* NETINET && NETINET6 */
262 addr.sa.sa_family == AF_INET6
263 # endif /* NETINET6 */
268 /* Parse port@host */
269 at = strchr(colon, '@');
272 switch (addr.sa.sa_family)
276 addr.sin.sin_addr.s_addr = INADDR_ANY;
278 # endif /* NETINET */
282 addr.sin6.sin6_addr = in6addr_any;
284 # endif /* NETINET6 */
290 if (isascii(*colon) && isdigit(*colon))
291 port = htons((unsigned short) atoi(colon));
294 # ifdef NO_GETSERVBYNAME
295 smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
297 return INVALID_SOCKET;
298 # else /* NO_GETSERVBYNAME */
299 register struct servent *sp;
301 sp = getservbyname(colon, "tcp");
305 "%s: unknown port name %s",
307 return INVALID_SOCKET;
310 # endif /* NO_GETSERVBYNAME */
319 end = strchr(at, ']');
324 unsigned long hid = INADDR_NONE;
325 # endif /* NETINET */
327 struct sockaddr_in6 hid6;
328 # endif /* NETINET6 */
332 if (addr.sa.sa_family == AF_INET &&
333 (hid = inet_addr(&at[1])) != INADDR_NONE)
335 addr.sin.sin_addr.s_addr = hid;
336 addr.sin.sin_port = port;
339 # endif /* NETINET */
341 (void) memset(&hid6, '\0', sizeof hid6);
342 if (addr.sa.sa_family == AF_INET6 &&
343 mi_inet_pton(AF_INET6, &at[1],
344 &hid6.sin6_addr) == 1)
346 addr.sin6.sin6_addr = hid6.sin6_addr;
347 addr.sin6.sin6_port = port;
350 # endif /* NETINET6 */
355 "%s: Invalid numeric domain spec \"%s\"",
357 return INVALID_SOCKET;
363 "%s: Invalid numeric domain spec \"%s\"",
365 return INVALID_SOCKET;
370 struct hostent *hp = NULL;
372 hp = mi_gethostbyname(at, addr.sa.sa_family);
376 "%s: Unknown host name %s",
378 return INVALID_SOCKET;
380 addr.sa.sa_family = hp->h_addrtype;
381 switch (hp->h_addrtype)
385 (void) memmove(&addr.sin.sin_addr,
388 addr.sin.sin_port = port;
390 # endif /* NETINET */
394 (void) memmove(&addr.sin6.sin6_addr,
397 addr.sin6.sin6_port = port;
399 # endif /* NETINET6 */
403 "%s: Unknown protocol for %s (%d)",
404 name, at, hp->h_addrtype);
405 return INVALID_SOCKET;
409 # endif /* NETINET6 */
414 switch (addr.sa.sa_family)
418 addr.sin.sin_port = port;
420 # endif /* NETINET */
423 addr.sin6.sin6_port = port;
425 # endif /* NETINET6 */
429 #endif /* NETINET || NETINET6 */
431 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
432 if (!ValidSocket(sock))
435 "%s: Unable to create new socket: %s",
436 name, sm_errstring(errno));
437 return INVALID_SOCKET;
440 if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 ||
441 fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1)
444 "%s: Unable to set close-on-exec: %s", name,
445 sm_errstring(errno));
446 (void) closesocket(sock);
447 return INVALID_SOCKET;
450 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
451 sizeof(sockopt)) == -1)
454 "%s: Unable to setsockopt: %s", name,
455 sm_errstring(errno));
456 (void) closesocket(sock);
457 return INVALID_SOCKET;
460 if (bind(sock, &addr.sa, L_socksize) < 0)
463 "%s: Unable to bind to port %s: %s",
464 name, conn, sm_errstring(errno));
465 (void) closesocket(sock);
466 return INVALID_SOCKET;
469 if (listen(sock, backlog) < 0)
472 "%s: listen call failed: %s", name,
473 sm_errstring(errno));
474 (void) closesocket(sock);
475 return INVALID_SOCKET;
479 if (addr.sa.sa_family == AF_UNIX && len > 0)
482 ** Set global variable sockpath so the UNIX socket can be
483 ** unlink()ed at exit.
486 sockpath = (char *) malloc(len);
487 if (sockpath != NULL)
488 (void) sm_strlcpy(sockpath, colon, len);
492 "%s: can't malloc(%d) for sockpath: %s",
493 name, (int) len, sm_errstring(errno));
494 (void) closesocket(sock);
495 return INVALID_SOCKET;
499 L_family = addr.sa.sa_family;
503 ** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
506 ** arg -- argument to pass to mi_handle_session()
509 ** results from mi_handle_session()
513 mi_thread_handle_wrapper(arg)
516 return (void *) mi_handle_session(arg);
520 ** MI_CLOSENER -- close listen socket
522 ** NOTE: It is assumed that this function is called from a
523 ** function that has a mutex lock (currently mi_stop_milters()).
535 (void) smutex_lock(&L_Mutex);
536 if (ValidSocket(listenfd))
540 struct stat sockinfo;
541 struct stat fileinfo;
543 removable = sockpath != NULL &&
544 #if _FFR_MILTER_ROOT_UNSAFE
546 #endif /* _FFR_MILTER_ROOT_UNSAFE */
547 fstat(listenfd, &sockinfo) == 0 &&
548 (S_ISFIFO(sockinfo.st_mode)
550 || S_ISSOCK(sockinfo.st_mode)
551 # endif /* S_ISSOCK */
555 (void) closesocket(listenfd);
556 listenfd = INVALID_SOCKET;
559 /* XXX sleep() some time before doing this? */
560 if (sockpath != NULL)
563 stat(sockpath, &fileinfo) == 0 &&
564 ((fileinfo.st_dev == sockinfo.st_dev &&
565 fileinfo.st_ino == sockinfo.st_ino)
567 || S_ISSOCK(fileinfo.st_mode)
568 # endif /* S_ISSOCK */
571 (S_ISFIFO(fileinfo.st_mode)
573 || S_ISSOCK(fileinfo.st_mode)
574 # endif /* S_ISSOCK */
576 (void) unlink(sockpath);
582 (void) smutex_unlock(&L_Mutex);
586 ** MI_LISTENER -- Generic listener harness
588 ** Open up listen port
589 ** Wait for connections
592 ** conn -- connection description
593 ** dbg -- debug level
594 ** smfi -- filter structure to use
595 ** timeout -- timeout for reads/writes
596 ** backlog -- listen queue backlog size
599 ** MI_SUCCESS -- Exited normally
600 ** (session finished or we were told to exit)
601 ** MI_FAILURE -- Network initialization failed.
604 #if BROKEN_PTHREAD_SLEEP
607 ** Solaris 2.6, perhaps others, gets an internal threads library panic
608 ** when sleep() is used:
610 ** thread_create() failed, returned 11 (EINVAL)
611 ** co_enable, thr_create() returned error = 24
612 ** libthread panic: co_enable failed (PID: 17793 LWP 1)
624 # define MI_SLEEP(s) \
635 rs = select(0, NULL, NULL, NULL, &st); \
636 if (rs < 0 && errno == EINTR) \
640 smi_log(SMI_LOG_ERR, \
641 "MI_SLEEP(): select() returned non-zero result %d, errno = %d", \
648 #else /* BROKEN_PTHREAD_SLEEP */
649 # define MI_SLEEP(s) sleep((s))
650 #endif /* BROKEN_PTHREAD_SLEEP */
653 mi_listener(conn, dbg, smfi, timeout, backlog)
660 socket_t connfd = INVALID_SOCKET;
663 int ret = MI_SUCCESS;
664 int mcnt = 0; /* error count for malloc() failures */
665 int tcnt = 0; /* error count for thread_create() failures */
666 int acnt = 0; /* error count for accept() failures */
667 int scnt = 0; /* error count for select() failures */
671 SOCKADDR_LEN_T clilen;
673 FD_RD_VAR(rds, excs);
674 struct timeval chktime;
676 if (mi_opensocket(conn, backlog, dbg, smfi) == MI_FAILURE)
680 (void) smutex_unlock(&L_Mutex);
681 while ((mistop = mi_stop()) == MILTER_CONT)
683 (void) smutex_lock(&L_Mutex);
684 if (!ValidSocket(listenfd))
688 "%s: listenfd=%d corrupted, terminating, errno=%d",
689 smfi->xxfi_name, listenfd, errno);
690 (void) smutex_unlock(&L_Mutex);
694 /* select on interface ports */
695 FD_RD_INIT(listenfd, rds, excs);
696 chktime.tv_sec = MI_CHK_TIME;
698 r = FD_RD_READY(listenfd, rds, excs, &chktime);
699 if (r == 0) /* timeout */
701 (void) smutex_unlock(&L_Mutex);
702 continue; /* just check mi_stop() */
707 (void) smutex_unlock(&L_Mutex);
708 if (save_errno == EINTR)
712 "%s: select() failed (%s), %s",
713 smfi->xxfi_name, sm_errstring(save_errno),
714 scnt >= MAX_FAILS_S ? "abort" : "try again");
716 if (scnt >= MAX_FAILS_S)
723 if (!FD_IS_RD_RDY(listenfd, rds, excs))
725 /* some error: just stop for now... */
727 (void) smutex_unlock(&L_Mutex);
729 "%s: %s() returned exception for socket, abort",
730 smfi->xxfi_name, MI_POLLSELECT);
733 scnt = 0; /* reset error counter for select() */
735 (void) memset(&cliaddr, '\0', sizeof cliaddr);
736 connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
739 (void) smutex_unlock(&L_Mutex);
742 ** If remote side closes before
743 ** accept() finishes, sockaddr
744 ** might not be fully filled in.
747 if (ValidSocket(connfd) &&
749 # ifdef BSD4_4_SOCKADDR
750 cliaddr.sa.sa_len == 0 ||
751 # endif /* BSD4_4_SOCKADDR */
752 cliaddr.sa.sa_family != L_family))
754 (void) closesocket(connfd);
755 connfd = INVALID_SOCKET;
760 /* check if acceptable for select() */
761 if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd))
763 (void) closesocket(connfd);
764 connfd = INVALID_SOCKET;
767 #endif /* !_FFR_USE_POLL */
769 if (!ValidSocket(connfd))
771 if (save_errno == EINTR)
775 "%s: accept() returned invalid socket (%s), %s",
776 smfi->xxfi_name, sm_errstring(save_errno),
777 acnt >= MAX_FAILS_A ? "abort" : "try again");
779 if (acnt >= MAX_FAILS_A)
786 acnt = 0; /* reset error counter for accept() */
788 if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
789 (void *) &sockopt, sizeof sockopt) < 0)
791 smi_log(SMI_LOG_WARN, "%s: setsockopt() failed (%s)",
792 smfi->xxfi_name, sm_errstring(errno));
795 if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
797 (void) closesocket(connfd);
799 smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s",
800 smfi->xxfi_name, sm_errstring(save_errno),
801 mcnt >= MAX_FAILS_M ? "abort" : "try again");
803 if (mcnt >= MAX_FAILS_M)
810 mcnt = 0; /* reset error counter for malloc() */
811 (void) memset(ctx, '\0', sizeof *ctx);
812 ctx->ctx_sd = connfd;
814 ctx->ctx_timeout = timeout;
815 ctx->ctx_smfi = smfi;
817 if (smfi->xxfi_eoh == NULL)
818 if (smfi->xxfi_eom == NULL)
819 if (smfi->xxfi_abort == NULL)
820 if (smfi->xxfi_close == NULL)
822 if (smfi->xxfi_connect == NULL)
823 ctx->ctx_pflags |= SMFIP_NOCONNECT;
824 if (smfi->xxfi_helo == NULL)
825 ctx->ctx_pflags |= SMFIP_NOHELO;
826 if (smfi->xxfi_envfrom == NULL)
827 ctx->ctx_pflags |= SMFIP_NOMAIL;
828 if (smfi->xxfi_envrcpt == NULL)
829 ctx->ctx_pflags |= SMFIP_NORCPT;
830 if (smfi->xxfi_header == NULL)
831 ctx->ctx_pflags |= SMFIP_NOHDRS;
832 if (smfi->xxfi_eoh == NULL)
833 ctx->ctx_pflags |= SMFIP_NOEOH;
834 if (smfi->xxfi_body == NULL)
835 ctx->ctx_pflags |= SMFIP_NOBODY;
837 if ((r = thread_create(&thread_id,
838 mi_thread_handle_wrapper,
843 "%s: thread_create() failed: %d, %s",
845 tcnt >= MAX_FAILS_T ? "abort" : "try again");
847 (void) closesocket(connfd);
849 if (tcnt >= MAX_FAILS_T)
858 if (ret != MI_SUCCESS)
859 mi_stop_milters(MILTER_ABRT);
862 if (mistop != MILTER_CONT)
863 smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
864 smfi->xxfi_name, mistop);
867 (void) smutex_destroy(&L_Mutex);