on systems that has separate IPv4/v6 socket layer (i.e. IPv4 packet does not get routed to AF_INET6 socket) rsync --daemon would accept IPv6 sessions only. open_socket_in() tries to deal with the situation, but it was not enough. here's the patch. (it is required on all *BSDs to accept both IPv4 and IPv6 connections with --daemon mode) itojun --- ? configure.lineno ? lib/dummy Index: socket.c ==================================================================RCS file: /cvsroot/apps/rsync/socket.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 socket.c --- socket.c 4 Mar 2003 10:29:09 -0000 1.1.1.1 +++ socket.c 4 Mar 2003 10:55:34 -0000 @@ -290,59 +290,30 @@ * @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(struct addrinfo *resp) { int one=1; int s; - struct addrinfo hints, *all_ai, *resp; - char portbuf[10]; - int error; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = af_hint; - hints.ai_socktype = type; - hints.ai_flags = AI_PASSIVE; - snprintf(portbuf, sizeof(portbuf), "%d", port); - error = getaddrinfo(bind_address, portbuf, &hints, &all_ai); - if (error) { - rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n", - bind_address, gai_strerror(error)); - return -1; - } /* 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. */ - for (resp = all_ai; resp; resp = resp->ai_next) { - s = socket(resp->ai_family, resp->ai_socktype, - resp->ai_protocol); + s = socket(resp->ai_family, resp->ai_socktype, + resp->ai_protocol); - if (s == -1) - /* See if there's another address that will work... */ - continue; - - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, - (char *)&one, sizeof one); - - /* now we've got a socket - we need to bind it */ - if (bind(s, all_ai->ai_addr, all_ai->ai_addrlen) < 0) { - /* Nope, try another */ - close(s); - continue; - } - - freeaddrinfo(all_ai); - return s; + if (s == -1) + return -1; + + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof one); + + /* now we've got a socket - we need to bind it */ + if (bind(s, resp->ai_addr, resp->ai_addrlen) < 0) { + close(s); + return -1; } - rprintf(FERROR, RSYNC_NAME ": open inbound socket on port %d failed: " - "%s\n", - port, - strerror(errno)); - - freeaddrinfo(all_ai); - return -1; + return s; } @@ -371,24 +342,52 @@ return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0); } +#define MAXSOCK 20 void start_accept_loop(int port, int (*fn)(int )) { - int s; + int s[MAXSOCK]; + int nsock = 0; + int maxsock = -1; extern char *bind_address; extern int default_af_hint; + struct addrinfo hints, *res, *res0; + char portstr[NI_MAXSERV]; + int i; - /* open an incoming socket */ - s = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint); - if (s == -1) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = default_af_hint; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + snprintf(portstr, sizeof(portstr), "%d", port); + if (getaddrinfo(bind_address, portstr, &hints, &res0) != 0) exit_cleanup(RERR_SOCKETIO); - /* ready to listen */ - if (listen(s, 5) == -1) { - close(s); - exit_cleanup(RERR_SOCKETIO); + /* open an incoming socket */ + for (res = res0; res; res = res->ai_next) { + if (nsock >= sizeof(s) / sizeof(s[0])) + break; + s[nsock] = open_socket_in(res); + if (s[nsock] == -1) + continue; + if (s[nsock] >= FD_SETSIZE) { + close(s[nsock]); + continue; + } + + /* ready to listen */ + if (listen(s[nsock], 5) == -1) { + close(s[nsock]); + continue; + } + + if (s[nsock] > maxsock) + maxsock = s[nsock]; + nsock++; } + if (nsock == 0) + exit_cleanup(RERR_SOCKETIO); /* now accept incoming connections - forking a new process for each incoming connection */ @@ -405,46 +404,51 @@ log_close(); FD_ZERO(&fds); - FD_SET(s, &fds); + for (i = 0; i < nsock; i++) + FD_SET(s[i], &fds); - if (select(s+1, &fds, NULL, NULL, NULL) != 1) { + if (select(maxsock + 1, &fds, NULL, NULL, NULL) < 0) continue; - } - if(!FD_ISSET(s, &fds)) continue; + for (i = 0; i < nsock; i++) { + if (!FD_ISSET(s[i], &fds)) + continue; - fd = accept(s,(struct sockaddr *)&addr,&addrlen); + fd = accept(s[i], (struct sockaddr *)&addr, &addrlen); - if (fd == -1) continue; + if (fd == -1) + continue; - signal(SIGCHLD, SIG_IGN); + signal(SIGCHLD, SIG_IGN); - /* we shouldn't have any children left hanging around - but I have had reports that on Digital Unix zombies - are produced, so this ensures that they are reaped */ + /* we shouldn't have any children left hanging around + but I have had reports that on Digital Unix zombies + are produced, so this ensures that they are reaped */ #ifdef WNOHANG - while (waitpid(-1, NULL, WNOHANG) > 0); + while (waitpid(-1, NULL, WNOHANG) > 0); #endif - if ((pid = fork()) == 0) { - close(s); - /* open log file in child before possibly giving - up privileges */ - log_open(); - _exit(fn(fd)); - } else if (pid < 0) { - rprintf(FERROR, - RSYNC_NAME - ": could not create child server process: %s\n", - strerror(errno)); - close(fd); - /* This might have happened because we're - * overloaded. Sleep briefly before trying to - * accept again. */ - sleep(2); - } else { - /* Parent doesn't need this fd anymore. */ - close(fd); + if ((pid = fork()) == 0) { + for (i = 0; i < nsock; i++) + close(s[i]); + /* open log file in child before possibly giving + up privileges */ + log_open(); + _exit(fn(fd)); + } else if (pid < 0) { + rprintf(FERROR, + RSYNC_NAME + ": could not create child server process: %s\n", + strerror(errno)); + close(fd); + /* This might have happened because we're + * overloaded. Sleep briefly before trying to + * accept again. */ + sleep(2); + } else { + /* Parent doesn't need this fd anymore. */ + close(fd); + } } } }