Applied a slightly-tweaked version of Oliver Braun's patch that
authorWayne Davison <wayned@samba.org>
Fri, 2 Jan 2004 18:05:51 +0000 (18:05 +0000)
committerWayne Davison <wayned@samba.org>
Fri, 2 Jan 2004 18:05:51 +0000 (18:05 +0000)
implements listening on multiple addresses and a fix for IPv6-only
systems.

socket.c

index 7298351..830e164 100644 (file)
--- 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);
 }