"rsync --daemon" and IPv4/v6 dual stack

Jun-ichiro itojun Hagino itojun at iijlab.net
Wed Mar 5 10:38:45 EST 2003


	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);
+			}
 		}
 	}
 }


More information about the rsync mailing list