Subject: make usr.sbin/sup IPv6 ready
To: None <tech-net@netbsd.org>
From: Jun-ichiro itojun Hagino <itojun@iijlab.net>
List: tech-net
Date: 09/11/2001 11:26:09
	the following diff makes sup IPv6 ready.  client side was tested over
	IPv6-to-IPv4 translator.  is it okay to commit it?

itojun


Index: source/scm.c
===================================================================
RCS file: /cvsroot/basesrc/usr.sbin/sup/source/scm.c,v
retrieving revision 1.10
diff -u -r1.10 scm.c
--- source/scm.c	2000/03/12 12:16:49	1.10
+++ source/scm.c	2001/09/11 02:28:04
@@ -181,6 +181,7 @@
 #else
 #include <varargs.h>
 #endif
+#include <ifaddrs.h>
 #include "supcdefs.h"
 #include "supextern.h"
 
@@ -211,7 +212,7 @@
 int netfile = -1;			/* network file descriptor */
 
 static int sock = -1;			/* socket used to make connection */
-static struct in_addr remoteaddr;	/* remote host address */
+static struct sockaddr_storage remoteaddr; /* remote host address */
 static char *remotename = NULL;		/* remote host name */
 static int swapmode;			/* byte-swapping needed on server? */
 
@@ -224,47 +225,71 @@
  ***************************************************/
 
 int
-servicesetup (server)		/* listen for clients */
+servicesetup (server, af)		/* listen for clients */
 char *server;
+int af;
 {
-	struct sockaddr_in sin;
-	struct servent *sp;
-	short port;
+	struct addrinfo hints, *res0, *res;
+	char port[NI_MAXSERV];
+	int error;
+	const char *cause = "unknown";
 	int one = 1;
 
-	if (myhost () == NULL)
-		return (scmerr (-1,"Local hostname not known"));
-	if ((sp = getservbyname(server,"tcp")) == 0) {
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = af;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_socktype = AI_PASSIVE;
+	error = getaddrinfo(NULL, server, &hints, &res0);
+	if (error) {
+		/* retry with precompiled knowledge */
 		if (strcmp(server, FILEPORT) == 0)
-			port = htons((u_short)FILEPORTNUM);
+			snprintf(port, sizeof(port), "%u", FILEPORTNUM);
 		else if (strcmp(server, DEBUGFPORT) == 0)
-			port = htons((u_short)DEBUGFPORTNUM);
+			snprintf(port, sizeof(port), "%u", DEBUGFPORTNUM);
 		else
-			return (scmerr (-1,"Can't find %s server description",server));
-		(void) scmerr (-1,"%s/tcp: unknown service: using port %d",
-					server,port);
-	} else
-		port = sp->s_port;
-	endservent ();
-	sock = socket (AF_INET,SOCK_STREAM,0);
-	if (sock < 0)
-		return (scmerr (errno,"Can't create socket for connections"));
-	if (setsockopt (sock,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(int)) < 0)
-		(void) scmerr (errno,"Can't set SO_REUSEADDR socket option");
-	(void) bzero ((char *)&sin,sizeof(sin));
-	sin.sin_family = AF_INET;
-	sin.sin_port = port;
-	if (bind (sock,(struct sockaddr *)&sin,sizeof(sin)) < 0)
-		return (scmerr (errno,"Can't bind socket for connections"));
-	if (listen (sock,NCONNECTS) < 0)
-		return (scmerr (errno,"Can't listen on socket"));
-	return (SCMOK);
+			port[0] = '\0';
+		if (port[0])
+			error = getaddrinfo(NULL, port, &hints, &res0);
+		if (error)
+			return (scmerr (-1, "%s: %s", server,
+			    gai_strerror(error)));
+	}
+	for (res = res0; res; res = res->ai_next) {
+		sock = socket(res->ai_family, res->ai_socktype,
+		    res->ai_protocol);
+		if (sock < 0) {
+			cause = "socket";
+			continue;
+		}
+		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+		    (char *)&one, sizeof(int)) < 0) {
+			cause = "setsockopt(SO_REUSEADDR)";
+			close(sock);
+			continue;
+		}
+		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+			cause = "bind";
+			close(sock);
+			continue;
+		}
+		if (listen(sock, NCONNECTS) < 0) {
+			cause = "listen";
+			close(sock);
+			continue;
+		}
+
+		freeaddrinfo(res0);
+		return SCMOK;
+	}
+
+	freeaddrinfo(res0);
+	return (scmerr (errno, "%s", cause));
 }
 
 int
 service ()
 {
-	struct sockaddr_in from;
+	struct sockaddr_storage from;
 	int x,len;
 
 	remotename = NULL;
@@ -274,7 +299,11 @@
 	} while (netfile < 0 && errno == EINTR);
 	if (netfile < 0)
 		return (scmerr (errno,"Can't accept connections"));
-	remoteaddr = from.sin_addr;
+	if (len > sizeof(remoteaddr)) {
+		close(netfile);
+		return (scmerr (errno,"Can't accept connections"));
+	}
+	memcpy(&remoteaddr, &from, len);
 	if (read(netfile,(char *)&x,sizeof(int)) != sizeof(int))
 		return (scmerr (errno,"Can't transmit data on connection"));
 	if (x == 0x01020304)
@@ -358,54 +387,65 @@
 char *hostname;
 int *retry;
 {
-	int x, backoff;
-	struct hostent *h;
-	struct servent *sp;
-	struct sockaddr_in sin, tin;
-	short port;
-
-	if ((sp = getservbyname(server,"tcp")) == 0) {
+	struct addrinfo hints, *res, *res0;
+	int error;
+	char port[NI_MAXSERV];
+	int backoff;
+	int x;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	error = getaddrinfo(hostname, server, &hints, &res0);
+	if (error) {
+		/* retry with precompiled knowledge */
 		if (strcmp(server, FILEPORT) == 0)
-			port = htons((u_short)FILEPORTNUM);
+			snprintf(port, sizeof(port), "%u", FILEPORTNUM);
 		else if (strcmp(server, DEBUGFPORT) == 0)
-			port = htons((u_short)DEBUGFPORTNUM);
+			snprintf(port, sizeof(port), "%u", DEBUGFPORTNUM);
 		else
-			return (scmerr (-1,"Can't find %s server description",
-					server));
-		if (!silent)
-		    (void) scmerr (-1,"%s/tcp: unknown service: using port %d",
-				    server,port);
-	} else
-		port = sp->s_port;
-	(void) bzero ((char *)&sin,sizeof(sin));
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = inet_addr (hostname);
-	if (sin.sin_addr.s_addr == (u_long) INADDR_NONE) {
-		if ((h = gethostbyname (hostname)) == NULL)
-			return (scmerr (-1,"Can't find host entry for %s",
-					hostname));
-		hostname = h->h_name;
-		(void) bcopy (h->h_addr,(char *)&sin.sin_addr,h->h_length);
+			port[0] = '\0';
+		if (port[0])
+			error = getaddrinfo(hostname, port, &hints, &res0);
+		if (error)
+			return (scmerr (-1, "%s: %s", server,
+			    gai_strerror(error)));
 	}
-	sin.sin_port = port;
 	backoff = 1;
-	for (;;) {
-		netfile = socket (AF_INET,SOCK_STREAM,0);
-		if (netfile < 0)
-			return (scmerr (errno,"Can't create socket"));
-		tin = sin;
-		if (connect(netfile,(struct sockaddr *)&tin,sizeof(tin)) >= 0)
+	while (1) {
+		netfile = -1;
+		for (res = res0; res; res = res->ai_next) {
+			if (res->ai_addrlen > sizeof(remoteaddr))
+				continue;
+			netfile = socket(res->ai_family, res->ai_socktype,
+			    res->ai_protocol);
+			if (netfile < 0)
+				continue;
+			if (connect(netfile, res->ai_addr, res->ai_addrlen) < 0) {
+				close(netfile);
+				netfile = -1;
+				continue;
+			}
+
 			break;
-		(void) scmerr (errno,"Can't connect to server for %s",server);
-		(void) close(netfile);
-		if (!dobackoff (retry,&backoff))
-			return (SCMERR);
+		}
+
+		if (netfile < 0) {
+			if (!dobackoff (retry, &backoff)) {
+				freeaddrinfo(res0);
+				return (SCMERR);
+			}
+			continue;
+		} else
+			break;
 	}
-	remoteaddr = sin.sin_addr;
+
+	memcpy(&remoteaddr, res->ai_addr, res->ai_addrlen);
 	remotename = salloc(hostname);
 	x = 0x01020304;
 	(void) write (netfile,(char *)&x,sizeof(int));
 	swapmode = 0;		/* swap only on server, not client */
+	freeaddrinfo(res0);
 	return (SCMOK);
 }
 
@@ -447,12 +487,13 @@
 
 char *remotehost ()	/* remote host name (if known) */
 {
-	register struct hostent *h;
+	char h1[NI_MAXHOST];
 
 	if (remotename == NULL) {
-		h = gethostbyaddr ((char *)&remoteaddr,sizeof(remoteaddr),
-				    AF_INET);
-		remotename = salloc (h ? h->h_name : inet_ntoa(remoteaddr));
+		if (getnameinfo((struct sockaddr *)&remoteaddr,
+		    remoteaddr.ss_len, h1, sizeof(h1), NULL, 0, 0))
+			return("UNKNOWN");
+		remotename = salloc (h1);
 		if (remotename == NULL)
 			return("UNKNOWN");
 	}
@@ -474,59 +515,66 @@
 
 int samehost ()		/* is remote host same as local host? */
 {
-	static struct in_addr *intp;
-	static int nint = 0;
-	struct in_addr *ifp;
-	int n;
-
-	if (nint <= 0) {
-		int s;
-		char buf[BUFSIZ];
-		struct ifconf ifc;
-		struct ifreq *ifr;
-		struct sockaddr_in sin;
-
-		if ((s = socket (AF_INET,SOCK_DGRAM,0)) < 0)
-			logquit (1,"Can't create socket for SIOCGIFCONF");
-		ifc.ifc_len = sizeof(buf);
-		ifc.ifc_buf = buf;
-		if (ioctl (s,SIOCGIFCONF,(char *)&ifc) < 0)
-			logquit (1,"SIOCGIFCONF failed");
-		(void) close(s);
-		if ((nint = ifc.ifc_len/sizeof(struct ifreq)) <= 0)
-			return (0);
-		intp = (struct in_addr *)
-			malloc ((unsigned) nint*sizeof(struct in_addr));
-		if ((ifp = intp) == 0)
-			logquit (1,"no space for interfaces");
-		for (ifr = ifc.ifc_req, n = nint; n > 0; --n, ifr++) {
-			(void) bcopy ((char *)&ifr->ifr_addr,(char *)&sin,sizeof(sin));
-			*ifp++ = sin.sin_addr;
+	struct ifaddrs *ifap, *ifa;
+	char h1[NI_MAXHOST], h2[NI_MAXHOST];
+#ifdef NI_WITHSCOPEID
+	const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
+#else
+	const int niflags = NI_NUMERICHOST;
+#endif
+
+	if (getnameinfo((struct sockaddr *)&remoteaddr, remoteaddr.ss_len,
+	    h1, sizeof(h1), NULL, 0, niflags))
+		return (0);
+	if (getifaddrs(&ifap) < 0)
+		return (0);
+	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+		if (remoteaddr.ss_family != ifa->ifa_addr->sa_family)
+			continue;
+		if (getnameinfo(ifa->ifa_addr, ifa->ifa_addr->sa_len,
+		    h2, sizeof(h2), NULL, 0, niflags))
+			continue;
+		if (strcmp(h1, h2) == 0) {
+			freeifaddrs(ifap);
+			return (1);
 		}
 	}
-	if (remoteaddr.s_addr == htonl(INADDR_LOOPBACK))
-		return (1);
-	for (ifp = intp, n = nint; n > 0; --n, ifp++)
-		if (remoteaddr.s_addr == ifp->s_addr)
-			return (1);
+	freeifaddrs(ifap);
 	return (0);
 }
 
 int matchhost (name)	/* is this name of remote host? */
 char *name;
 {
-	struct hostent *h;
-	struct in_addr addr;
-	char **ap;
-	if ((addr.s_addr = inet_addr(name)) != (u_long) INADDR_NONE)
-		return (addr.s_addr == remoteaddr.s_addr);
-	if ((h = gethostbyname (name)) == 0)
+	char h1[NI_MAXHOST], h2[NI_MAXHOST];
+#ifdef NI_WITHSCOPEID
+	const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
+#else
+	const int niflags = NI_NUMERICHOST;
+#endif
+	struct addrinfo hints, *res0, *res;
+
+	if (getnameinfo((struct sockaddr *)&remoteaddr, remoteaddr.ss_len,
+	    h1, sizeof(h1), NULL, 0, niflags))
 		return (0);
-	if (h->h_addrtype != AF_INET || h->h_length != sizeof(struct in_addr))
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
+	hints.ai_flags = AI_NUMERICHOST;
+	if (getaddrinfo(name, "0", &hints, &res0) != 0)
 		return (0);
-	for (ap = h->h_addr_list; *ap; ap++)
-		if (bcmp ((char *)&remoteaddr,*ap,h->h_length) == 0)
+	for (res = res0; res; res = res->ai_next) {
+		if (remoteaddr.ss_family != res->ai_family)
+			continue;
+		if (getnameinfo(res->ai_addr, res->ai_addrlen,
+		    h2, sizeof(h2), NULL, 0, niflags))
+			continue;
+		if (strcmp(h1, h2) == 0) {
+			freeaddrinfo(res0);
 			return (1);
+		}
+	}
+	freeaddrinfo(res0);
 	return (0);
 }
 
Index: source/supextern.h
===================================================================
RCS file: /cvsroot/basesrc/usr.sbin/sup/source/supextern.h,v
retrieving revision 1.14
diff -u -r1.14 supextern.h
--- source/supextern.h	2000/12/30 21:36:23	1.14
+++ source/supextern.h	2001/09/11 02:28:04
@@ -82,7 +82,7 @@
 void cdprefix __P((char *));
 
 /* scm.c */
-int servicesetup __P((char *));
+int servicesetup __P((char *, int));
 int service __P((void));
 int serviceprep __P((void));
 int servicekill __P((void));
Index: source/supfilesrv.c
===================================================================
RCS file: /cvsroot/basesrc/usr.sbin/sup/source/supfilesrv.c,v
retrieving revision 1.20
diff -u -r1.20 supfilesrv.c
--- source/supfilesrv.c	2001/01/16 02:50:32	1.20
+++ source/supfilesrv.c	2001/09/11 02:28:06
@@ -232,6 +232,7 @@
 #include <sys/stat.h>
 #include <sys/file.h>
 #include <sys/mount.h>
+#include <sys/socket.h>
 #ifndef HAS_POSIX_DIR
 #include <sys/dir.h>
 #else
@@ -500,9 +501,9 @@
 usage ()
 {
 #ifdef LIBWRAP
-	quit (1,"Usage: supfilesrv [ -l | -d | -P | -N | -C <max children> | -H <host> <user> <cryptfile> <supargs> ]\n");
+	quit (1,"Usage: supfilesrv [ -4 | -6 | -l | -d | -P | -N | -C <max children> | -H <host> <user> <cryptfile> <supargs> ]\n");
 #else
-	quit (1,"Usage: supfilesrv [ -d | -P | -N | -C <max children> | -H <host> <user> <cryptfile> <supargs> ]\n");
+	quit (1,"Usage: supfilesrv [ -4 | -6 | -d | -P | -N | -C <max children> | -H <host> <user> <cryptfile> <supargs> ]\n");
 #endif
 }
 
@@ -518,6 +519,7 @@
 	char buf[STRINGLENGTH];
 	int maxsleep;
 	register FILE *f;
+	int af = AF_INET;
 
 #ifdef RCS
         candorcs = FALSE;
@@ -574,6 +576,12 @@
                         candorcs = TRUE;
                         break;
 #endif
+		case '4':
+			af = AF_INET;
+			break;
+		case '6':
+			af = AF_INET6;
+			break;
 		default:
 			fprintf (stderr,"Unknown flag %s ignored\n",argv[0]);
 			break;
@@ -584,7 +592,7 @@
 	if (clienthost == NULL) {
 		if (argc != 0)
 			usage ();
-		x = servicesetup (dbgportsq ? DEBUGFPORT : FILEPORT);
+		x = servicesetup (dbgportsq ? DEBUGFPORT : FILEPORT, af);
 		if (x != SCMOK)
 			quit (1,"Error in network setup");
 		for (i = 0; i < HASHSIZE; i++)