From b0fd253afc8aae210a970d88da00f93b758b57ac Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Fri, 2 Jan 2004 18:05:51 +0000 Subject: [PATCH] Applied a slightly-tweaked version of Oliver Braun's patch that implements listening on multiple addresses and a fix for IPv6-only systems. --- socket.c | 96 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/socket.c b/socket.c index 72983517..830e1640 100644 --- a/socket.c +++ b/socket.c @@ -285,11 +285,11 @@ int open_socket_out_wrapped(char *host, int port, const char *bind_address, * @param bind_address Local address to bind, or NULL to allow it to * default. **/ -static int open_socket_in(int type, int port, const char *bind_address, - int af_hint) +static int *open_socket_in(int type, int port, const char *bind_address, + int af_hint) { int one=1; - int s; + int s, *sp, *socks, maxs; struct addrinfo hints, *all_ai, *resp; char portbuf[10]; int error; @@ -303,23 +303,41 @@ static int open_socket_in(int type, int port, const char *bind_address, if (error) { rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n", bind_address, gai_strerror(error)); - return -1; + return NULL; + } + + /* Count max number of sockets we might open. */ + for (maxs = 0, resp = all_ai; resp; resp = resp->ai_next, maxs++) {} + socks = new_array(int, maxs + 1); + if (!socks) { + rprintf(FERROR, + RSYNC_NAME "couldn't allocate memory for sockets"); + return NULL; } /* We may not be able to create the socket, if for example the * machine knows about IPv6 in the C library, but not in the * kernel. */ + sp = socks + 1; /* Leave room for count at start of array. */ for (resp = all_ai; resp; resp = resp->ai_next) { s = socket(resp->ai_family, resp->ai_socktype, resp->ai_protocol); - if (s == -1) + if (s == -1) { /* See if there's another address that will work... */ continue; + } setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof one); +#ifdef IPV6_V6ONLY + if (resp->ai_family == AF_INET6) { + setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (char *)&one, sizeof one); + } +#endif + /* Now we've got a socket - we need to bind it. */ if (bind(s, resp->ai_addr, resp->ai_addrlen) < 0) { /* Nope, try another */ @@ -327,17 +345,21 @@ static int open_socket_in(int type, int port, const char *bind_address, continue; } - freeaddrinfo(all_ai); - return s; + *sp++ = s; } + *socks = sp - socks - 1; /* Save count. */ - rprintf(FERROR, RSYNC_NAME ": open inbound socket on port %d failed: " - "%s\n", - port, - strerror(errno)); + if (all_ai) + freeaddrinfo(all_ai); - freeaddrinfo(all_ai); - return -1; + if (*socks == 0) { + rprintf(FERROR, + RSYNC_NAME ": open inbound socket on port %d failed: " + "%s\n", port, strerror(errno)); + free(socks); + return NULL; + } + return socks; } @@ -378,19 +400,29 @@ static RETSIGTYPE sigchld_handler(UNUSED(int val)) void start_accept_loop(int port, int (*fn)(int, int)) { - int s; + fd_set deffds; + int *sp, maxfd, i, j; extern char *bind_address; extern int default_af_hint; /* open an incoming socket */ - s = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint); - if (s == -1) + sp = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint); + if (sp == NULL) exit_cleanup(RERR_SOCKETIO); /* ready to listen */ - if (listen(s, 5) == -1) { - close(s); - exit_cleanup(RERR_SOCKETIO); + FD_ZERO(&deffds); + maxfd = -1; + for (i = 1; i <= *sp; i++) { + if (listen(sp[i], 5) == -1) { + for (j = 1; j <= i; j++) + close(sp[j]); + free(sp); + exit_cleanup(RERR_SOCKETIO); + } + FD_SET(sp[i], &deffds); + if (maxfd < sp[i]) + maxfd = sp[i]; } @@ -408,25 +440,32 @@ void start_accept_loop(int port, int (*fn)(int, int)) forever */ log_close(); - FD_ZERO(&fds); - FD_SET(s, &fds); - - if (select(s+1, &fds, NULL, NULL, NULL) != 1) - continue; +#ifdef FD_COPY + FD_COPY(&deffds, &fds); +#else + fds = deffds; +#endif - if (!FD_ISSET(s, &fds)) + if (select(maxfd + 1, &fds, NULL, NULL, NULL) != 1) continue; - fd = accept(s,(struct sockaddr *)&addr,&addrlen); + fd = -1; + for (i = 1; i <= *sp; i++) { + if (FD_ISSET(sp[i], &fds)) { + fd = accept(sp[i], (struct sockaddr *)&addr, + &addrlen); + break; + } + } - if (fd == -1) + if (fd < 0) continue; signal(SIGCHLD, sigchld_handler); if ((pid = fork()) == 0) { int ret; - close(s); + close(sp[i]); /* open log file in child before possibly giving up privileges */ log_open(); @@ -448,6 +487,7 @@ void start_accept_loop(int port, int (*fn)(int, int)) close(fd); } } + free(sp); } -- 2.34.1