bin/88821: [PATCH] IPv6 support for ggate

Craig Boston craig at tobuj.gank.org
Thu Nov 10 17:10:17 PST 2005


>Number:         88821
>Category:       bin
>Synopsis:       [PATCH] IPv6 support for ggate
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Nov 11 01:10:16 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Craig Boston
>Release:        FreeBSD 6.0-STABLE i386
>Organization:
I wish I had some
>Environment:
System: FreeBSD hostname.gank.org 6.0-STABLE FreeBSD 6.0-STABLE #0: Sat Nov 5 20:59:05 CST 2005 root at hostname.gank.org:/compile/obj/compile/src/sys/HOSTNAME i386

>Description:
	Adds full IPv6 support to ggated and complete address family
	independence to ggatec.

>How-To-Repeat:
	n/a

>Fix:

--- ggate_ipv6_2.patch begins here ---
diff -ruN ggate.orig/ggatec/ggatec.c ggate/ggatec/ggatec.c
--- ggate.orig/ggatec/ggatec.c	Thu Nov 10 18:53:21 2005
+++ ggate/ggatec/ggatec.c	Thu Nov 10 18:53:25 2005
@@ -50,6 +50,7 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 
 #include <geom/gate/g_gate.h>
 #include "ggate.h"
@@ -63,7 +64,7 @@
 static unsigned flags = 0;
 static int force = 0;
 static unsigned queue_size = G_GATE_QUEUE_SIZE;
-static unsigned port = G_GATE_PORT;
+static const char *port = G_GATE_PORT_STR;
 static off_t mediasize;
 static unsigned sectorsize = 0;
 static unsigned timeout = G_GATE_TIMEOUT;
@@ -244,37 +245,55 @@
 	struct g_gate_version ver;
 	struct g_gate_cinit cinit;
 	struct g_gate_sinit sinit;
-	struct sockaddr_in serv;
+	struct addrinfo hints;
+	struct addrinfo *res, *p;
 	int sfd;
 
 	/*
 	 * Do the network stuff.
 	 */
-	bzero(&serv, sizeof(serv));
-	serv.sin_family = AF_INET;
-	serv.sin_addr.s_addr = g_gate_str2ip(host);
-	if (serv.sin_addr.s_addr == INADDR_NONE) {
-		g_gate_log(LOG_DEBUG, "Invalid IP/host name: %s.", host);
+	bzero(&hints, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	sfd = -1;
+	if (getaddrinfo(host, port, &hints, &res) != 0) {
+		g_gate_log(LOG_DEBUG, "Invalid IP/host name or port: %s (%s).",
+		    host, port);
 		return (-1);
-	}
-	serv.sin_port = htons(port);
-	sfd = socket(AF_INET, SOCK_STREAM, 0);
-	if (sfd == -1) {
-		g_gate_log(LOG_DEBUG, "Cannot open socket: %s.",
-		    strerror(errno));
-		return (-1);
-	}
+	} else {
+		p = res;
+		while (p) {
+			sfd = socket(p->ai_family, p->ai_socktype, 0);
+			if (sfd == -1) {
+				g_gate_log(LOG_DEBUG, "Cannot open socket: %s.",
+				    strerror(errno));
+				freeaddrinfo(res);
+				return (-1);
+			}
 
-	g_gate_socket_settings(sfd);
+			g_gate_socket_settings(sfd);
 
-	if (connect(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) {
-		g_gate_log(LOG_DEBUG, "Cannot connect to server: %s.",
-		    strerror(errno));
-		close(sfd);
+			if (connect(sfd, p->ai_addr, p->ai_addrlen) == 0)
+				break;
+
+			/* Non-critical error, try next address */
+		        g_gate_log(LOG_DEBUG, "Cannot connect to server: %s.",
+				strerror(errno));
+			close(sfd);
+			sfd = -1;
+
+			p = p->ai_next;
+		}
+	}
+	freeaddrinfo(res);
+
+	if (sfd == -1) {
+		g_gate_log(LOG_DEBUG, "Invalid IP/host name or port: %s (%s).",
+		    host, port);
 		return (-1);
 	}
 
-	g_gate_log(LOG_INFO, "Connected to the server: %s:%d.", host, port);
+	g_gate_log(LOG_INFO, "Connected to the server: %s (%s).", host, port);
 
 	/*
 	 * Create and send version packet.
@@ -454,7 +473,7 @@
 	ggioc.gctl_maxcount = queue_size;
 	ggioc.gctl_timeout = timeout;
 	ggioc.gctl_unit = unit;
-	snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s:%u %s", host,
+	snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s (%s) %s", host,
 	    port, path);
 	g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
 	if (unit == -1)
@@ -535,9 +554,7 @@
 			if (action != CREATE && action != RESCUE)
 				usage();
 			errno = 0;
-			port = strtoul(optarg, NULL, 10);
-			if (port == 0 && errno != 0)
-				errx(EXIT_FAILURE, "Invalid port.");
+			port = optarg;
 			break;
 		case 'q':
 			if (action != CREATE)
diff -ruN ggate.orig/ggated/ggated.8 ggate/ggated/ggated.8
--- ggate.orig/ggated/ggated.8	Thu Nov 10 18:53:21 2005
+++ ggate/ggated/ggated.8	Thu Nov 10 18:53:25 2005
@@ -55,7 +55,10 @@
 Available options:
 .Bl -tag -width ".Ar exports\ file"
 .It Fl a Ar address
-Specifies an IP address to bind to.
+Specifies an IP address to bind to.  To bind to multiple addresses,
+specify each address with a separate
+.Fl a
+option.
 .It Fl h
 Print available options.
 .It Fl n
@@ -74,7 +77,7 @@
 Size of send buffer to use.
 Default is 131072 (128kB).
 .It Fl v
-Do not fork, run in foreground and print debug informations on standard
+Do not fork, run in foreground and print debug information on standard
 output.
 .It Ar "exports file"
 An alternate location for the exports file.
@@ -82,9 +85,10 @@
 .Pp
 The format of an exports file is as follows:
 .Bd -literal -offset indent
-1.2.3.4		RO	/dev/acd0
-1.2.3.0/24	RW	/tmp/test.img
-hostname	WO	/tmp/image
+1.2.3.4			RO	/dev/acd0
+1.2.3.0/24		RW	/tmp/test.img
+2001:DB8:17C0::/64	RW	/tmp/foo
+hostname		WO	/tmp/image
 .Ed
 .Sh EXIT STATUS
 Exit status is 0 on success, or 1 if the command fails.
diff -ruN ggate.orig/ggated/ggated.c ggate/ggated/ggated.c
--- ggate.orig/ggated/ggated.c	Thu Nov 10 18:53:21 2005
+++ ggate/ggated/ggated.c	Thu Nov 10 18:53:25 2005
@@ -44,6 +44,7 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 #include <signal.h>
 #include <assert.h>
 #include <err.h>
@@ -68,7 +69,7 @@
 	time_t		 c_birthtime;
 	char		*c_path;
 	uint64_t	 c_token;
-	in_addr_t	 c_srcip;
+	struct sockaddr_storage	 c_srcaddr;
 	LIST_ENTRY(ggd_connection) c_next;
 };
 
@@ -83,16 +84,22 @@
 #define	r_error		r_hdr.gh_error
 
 struct ggd_export {
-	char		*e_path;	/* path to device/file */
-	in_addr_t	 e_ip;		/* remote IP address */
-	in_addr_t	 e_mask;	/* IP mask */
-	unsigned	 e_flags;	/* flags (RO/RW) */
-	SLIST_ENTRY(ggd_export) e_next;
+	char		        *e_path;	/* path to device/file */
+	struct sockaddr_storage	 e_addr;	/* remote IP address */
+	struct sockaddr_storage	 e_mask;	/* IP mask */
+	unsigned		 e_flags;	/* flags (RO/RW) */
+	SLIST_ENTRY(ggd_export)	 e_next;
+};
+
+struct ggd_listen {
+	const char			*l_name;   /* host name / address */
+	struct sockaddr_storage		 l_addr;   /* bind address & port */
+	int				 l_fd;	   /* socket */
+	SLIST_ENTRY(ggd_listen)		 l_next;
 };
 
 static const char *exports_file = GGATED_EXPORT_FILE;
 static int got_sighup = 0;
-in_addr_t bindaddr;
 
 static TAILQ_HEAD(, ggd_request) inqueue = TAILQ_HEAD_INITIALIZER(inqueue);
 static TAILQ_HEAD(, ggd_request) outqueue = TAILQ_HEAD_INITIALIZER(outqueue);
@@ -115,71 +122,88 @@
 	exit(EXIT_FAILURE);
 }
 
-static char *
-ip2str(in_addr_t ip)
+static const char *
+ip2str(struct sockaddr *addr)
 {
-	static char sip[16];
+	static char sip[64];
+
+	if (getnameinfo(addr, addr->sa_len, sip, sizeof(sip),
+	    NULL, 0, NI_NUMERICHOST) == 0)
+		return (sip);
 
-	snprintf(sip, sizeof(sip), "%u.%u.%u.%u",
-	    ((ip >> 24) & 0xff),
-	    ((ip >> 16) & 0xff),
-	    ((ip >> 8) & 0xff),
-	    (ip & 0xff));
-	return (sip);
+	return ("Unknown");
 }
 
-static in_addr_t
-countmask(unsigned m)
+static struct sockaddr_storage
+countmask(struct sockaddr* addr, int mask)	/* also normalizes addr */
 {
-	in_addr_t mask;
+	struct sockaddr_storage ss;
+	int i, alen;
+	unsigned char *mp, *ap;
 
-	if (m == 0) {
-		mask = 0x0;
-	} else {
-		mask = 1 << (32 - m);
-		mask--;
-		mask = ~mask;
+	bzero(&ss, sizeof(ss));
+	ss.ss_family = addr->sa_family;
+	ss.ss_len = addr->sa_len;
+
+	switch (addr->sa_family) {
+		case AF_INET:
+			alen = 4;	/* 32 bits */
+			ap = (unsigned char*)&((struct sockaddr_in*)addr)->sin_addr.s_addr;
+			mp = (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr;
+			break;
+		case AF_INET6:
+			alen = 16;	/* 128 bits */
+			ap = (unsigned char*)&((struct sockaddr_in6*)addr)->sin6_addr.s6_addr;
+			mp = (unsigned char*)&((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr;
+			break;
+		default:
+			g_gate_xlog("Unknown address family in countmask");
+	}
+
+	i = 0;
+	while (mask > 0 && i < alen) {
+		if (mask < 8) {
+			mp[i] = ~(0xff >> mask);
+			ap[i] &= mp[i];
+		} else
+			mp[i] = 0xff;
+		i++;
+		mask -= 8;
 	}
-	return (mask);
+	while (i < alen) {		/* zero out remaining bits of addr */
+		ap[i] = 0;
+		i++;
+	}
+
+	return (ss);
 }
 
 static void
 line_parse(char *line, unsigned lineno)
 {
 	struct ggd_export *ex;
-	char *word, *path, *sflags;
-	unsigned flags, i, vmask;
-	in_addr_t ip, mask;
+	char *pmask, *word, *path, *sflags;
+	unsigned flags, i;
+	int vmask;
+	struct addrinfo hints, *res, *p;
 
-	ip = mask = flags = vmask = 0;
+	flags = vmask = 0;
 	path = NULL;
 	sflags = NULL;
+	pmask = NULL;
 
 	for (i = 0, word = strtok(line, " \t"); word != NULL;
 	    i++, word = strtok(NULL, " \t")) {
 		switch (i) {
 		case 0: /* IP address or host name */
-			ip = g_gate_str2ip(strsep(&word, "/"));
-			if (ip == INADDR_NONE) {
+			bzero(&hints, sizeof(hints));
+			hints.ai_family = PF_UNSPEC;
+			hints.ai_socktype = SOCK_STREAM;
+			if (getaddrinfo(strsep(&word, "/"), NULL, &hints,
+			    &res) != 0)
 				g_gate_xlog("Invalid IP/host name at line %u.",
 				    lineno);
-			}
-			ip = ntohl(ip);
-			if (word == NULL)
-				vmask = 32;
-			else {
-				errno = 0;
-				vmask = strtoul(word, NULL, 10);
-				if (vmask == 0 && errno != 0) {
-					g_gate_xlog("Invalid IP mask value at "
-					    "line %u.", lineno);
-				}
-				if ((unsigned)vmask > 32) {
-					g_gate_xlog("Invalid IP mask value at line %u.",
-					    lineno);
-				}
-			}
-			mask = countmask(vmask);
+			pmask = word;
 			break;
 		case 1:	/* flags */
 			if (strcasecmp("rd", word) == 0 ||
@@ -209,22 +233,46 @@
 	if (i != 3)
 		g_gate_xlog("Too few arguments at line %u.", lineno);
 
-	ex = malloc(sizeof(*ex));
-	if (ex == NULL)
-		g_gate_xlog("No enough memory.");
-	ex->e_path = strdup(path);
-	if (ex->e_path == NULL)
-		g_gate_xlog("No enough memory.");
-
-	/* Made 'and' here. */
-	ex->e_ip = (ip & mask);
-	ex->e_mask = mask;
-	ex->e_flags = flags;
+	p = res;
+	while (p) {
+		ex = malloc(sizeof(*ex));
+		if (ex == NULL)
+			g_gate_xlog("Not enough memory.");
+		ex->e_path = strdup(path);
+		if (ex->e_path == NULL)
+			g_gate_xlog("Not enough memory.");
+
+		if (pmask == NULL && p->ai_family == AF_INET6)
+			vmask = 128;
+		else if (pmask == NULL)
+			vmask = 32;
+		else {
+			errno = 0;
+			vmask = strtoul(pmask, NULL, 10);
+			if (vmask == 0 && errno != 0) {
+				g_gate_xlog("Invalid IP mask value at "
+				    "line %u.", lineno);
+			}
+		}
+
+		if ((vmask > 32 && p->ai_family == AF_INET) ||
+		    (vmask > 128 && p->ai_family == AF_INET6))
+			g_gate_xlog("Invalid IP mask value at line %u",
+			    lineno);
+
+		memcpy(&ex->e_addr, p->ai_addr, p->ai_addrlen);
+		ex->e_mask = countmask((struct sockaddr*)&ex->e_addr, vmask);
+		ex->e_flags = flags;
+
+		SLIST_INSERT_HEAD(&exports, ex, e_next);
 
-	SLIST_INSERT_HEAD(&exports, ex, e_next);
+		g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.",
+		    ip2str((struct sockaddr*)&ex->e_addr), vmask, path, sflags);
 
-	g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.",
-	    ip2str(ex->e_ip), vmask, path, sflags);
+		p = p->ai_next;
+	}
+
+	freeaddrinfo(res);
 }
 
 static void
@@ -302,12 +350,12 @@
 exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit,
     struct ggd_connection *conn)
 {
-	char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */
+	char ipmask[80]; /* 80 == strlen("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")+1 */
 	int error = 0, flags;
 
-	strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask));
+	strlcpy(ipmask, ip2str((struct sockaddr*)&ex->e_addr), sizeof(ipmask));
 	strlcat(ipmask, "/", sizeof(ipmask));
-	strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask));
+	strlcat(ipmask, ip2str((struct sockaddr*)&ex->e_mask), sizeof(ipmask));
 	if ((cinit->gc_flags & GGATE_FLAG_RDONLY) != 0) {
 		if (ex->e_flags == O_WRONLY) {
 			g_gate_log(LOG_WARNING, "Read-only access requested, "
@@ -355,17 +403,53 @@
 	return (0);
 }
 
+static int
+mask_compare(struct sockaddr *a, struct sockaddr *b, struct sockaddr *m) {
+	unsigned char *ap, *bp, *mp;
+	int alen;
+	if (a->sa_family != m->sa_family || b->sa_family != m->sa_family)
+		return (0);
+
+	switch (m->sa_family) {
+		case AF_INET:
+			alen = 4;
+			ap = (unsigned char*)&((struct sockaddr_in*)a)->sin_addr.s_addr;
+			bp = (unsigned char*)&((struct sockaddr_in*)b)->sin_addr.s_addr;
+			mp = (unsigned char*)&((struct sockaddr_in*)m)->sin_addr.s_addr;
+			break;
+		case AF_INET6:
+			alen = 16;
+			ap = (unsigned char*)&((struct sockaddr_in6*)a)->sin6_addr.s6_addr;
+			bp = (unsigned char*)&((struct sockaddr_in6*)b)->sin6_addr.s6_addr;
+			mp = (unsigned char*)&((struct sockaddr_in6*)m)->sin6_addr.s6_addr;
+			break;
+		default:
+			return (0);
+	}
+
+	while (alen > 0) {
+		if ((*ap & *mp) != (*bp & *mp))
+			return (0);
+		
+		ap++;
+		bp++;
+		mp++;
+		alen--;
+	}
+
+	return (1);
+}
+
 static struct ggd_export *
 exports_find(struct sockaddr *s, struct g_gate_cinit *cinit,
     struct ggd_connection *conn)
 {
 	struct ggd_export *ex;
-	in_addr_t ip;
 	int error;
 
-	ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr);
 	SLIST_FOREACH(ex, &exports, e_next) {
-		if ((ip & ex->e_mask) != ex->e_ip) {
+		if (!mask_compare(s, (struct sockaddr*)&ex->e_addr,
+				 (struct sockaddr*)&ex->e_mask)) {
 			g_gate_log(LOG_DEBUG, "exports[%s]: IP mismatch.",
 			    ex->e_path);
 			continue;
@@ -384,7 +468,7 @@
 		}
 	}
 	g_gate_log(LOG_WARNING, "Unauthorized connection from: %s.",
-	    ip2str(ip));
+	    ip2str(s));
 	errno = EPERM;
 	return (NULL);
 }
@@ -404,7 +488,8 @@
 			LIST_REMOVE(conn, c_next);
 			g_gate_log(LOG_NOTICE,
 			    "Connection from %s [%s] removed.",
-			    ip2str(conn->c_srcip), conn->c_path);
+			    ip2str((struct sockaddr*)&conn->c_srcaddr),
+			    conn->c_path);
 			close(conn->c_diskfd);
 			close(conn->c_sendfd);
 			close(conn->c_recvfd);
@@ -430,7 +515,6 @@
 connection_new(struct g_gate_cinit *cinit, struct sockaddr *s, int sfd)
 {
 	struct ggd_connection *conn;
-	in_addr_t ip;
 
 	/*
 	 * First, look for old connections.
@@ -449,8 +533,7 @@
 		return (NULL);
 	}
 	conn->c_token = cinit->gc_token;
-	ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr);
-	conn->c_srcip = ip;
+	memcpy(&conn->c_srcaddr, s, s->sa_len);
 	conn->c_sendfd = conn->c_recvfd = -1;
 	if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0)
 		conn->c_sendfd = sfd;
@@ -461,7 +544,7 @@
 	time(&conn->c_birthtime);
 	conn->c_flags = cinit->gc_flags;
 	LIST_INSERT_HEAD(&connections, conn, c_next);
-	g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip),
+	g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(s),
 	    conn->c_path);
 	return (conn);
 }
@@ -470,13 +553,10 @@
 connection_add(struct ggd_connection *conn, struct g_gate_cinit *cinit,
     struct sockaddr *s, int sfd)
 {
-	in_addr_t ip;
-
-	ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr);
 	if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) {
 		if (conn->c_sendfd != -1) {
 			g_gate_log(LOG_WARNING,
-			    "Send socket already exists [%s, %s].", ip2str(ip),
+			    "Send socket already exists [%s, %s].", ip2str(s),
 			    conn->c_path);
 			return (EEXIST);
 		}
@@ -485,12 +565,12 @@
 		if (conn->c_recvfd != -1) {
 			g_gate_log(LOG_WARNING,
 			    "Receive socket already exists [%s, %s].",
-			    ip2str(ip), conn->c_path);
+			    ip2str(s), conn->c_path);
 			return (EEXIST);
 		}
 		conn->c_recvfd = sfd;
 	}
-	g_gate_log(LOG_DEBUG, "Connection added [%s, %s].", ip2str(ip),
+	g_gate_log(LOG_DEBUG, "Connection added [%s, %s].", ip2str(s),
 	    conn->c_path);
 	return (0);
 }
@@ -505,7 +585,7 @@
 
 	LIST_REMOVE(conn, c_next);
 	g_gate_log(LOG_DEBUG, "Connection removed [%s %s].",
-	    ip2str(conn->c_srcip), conn->c_path);
+	    ip2str((struct sockaddr*)&conn->c_srcaddr), conn->c_path);
 	if (conn->c_sendfd != -1)
 		close(conn->c_sendfd);
 	if (conn->c_recvfd != -1)
@@ -815,10 +895,7 @@
 static void
 log_connection(struct sockaddr *from)
 {
-	in_addr_t ip;
-
-	ip = htonl(((struct sockaddr_in *)(void *)from)->sin_addr.s_addr);
-	g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip));
+	g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(from));
 }
 
 static int
@@ -940,14 +1017,16 @@
 int
 main(int argc, char *argv[])
 {
-	struct sockaddr_in serv;
-	struct sockaddr from;
+	SLIST_HEAD(, ggd_listen) listens = SLIST_HEAD_INITIALIZER(&listens);
+	struct ggd_listen *cl, *nl;
+	struct addrinfo hints, *res, *p;
+	struct sockaddr_storage from;
 	socklen_t fromlen;
-	int sfd, tmpsfd;
-	unsigned port;
+	int maxfd, tmpsfd;
+	fd_set listenfds;
+	const char *port;
 
-	bindaddr = htonl(INADDR_ANY);
-	port = G_GATE_PORT;
+	port = G_GATE_PORT_STR;
 	for (;;) {
 		int ch;
 
@@ -956,20 +1035,17 @@
 			break;
 		switch (ch) {
 		case 'a':
-			bindaddr = g_gate_str2ip(optarg);
-			if (bindaddr == INADDR_NONE) {
-				errx(EXIT_FAILURE,
-				    "Invalid IP/host name to bind to.");
-			}
+			nl = malloc(sizeof(*nl));
+			bzero(nl, sizeof(*nl));
+			nl->l_name = optarg;
+			/* delay resolution until we know port number */
+			SLIST_INSERT_HEAD(&listens, nl, l_next);
 			break;
 		case 'n':
 			nagle = 0;
 			break;
 		case 'p':
-			errno = 0;
-			port = strtoul(optarg, NULL, 10);
-			if (port == 0 && errno != 0)
-				errx(EXIT_FAILURE, "Invalid port.");
+			port = optarg;
 			break;
 		case 'R':
 			errno = 0;
@@ -998,6 +1074,79 @@
 		exports_file = argv[0];
 	exports_get();
 
+	if (SLIST_EMPTY(&listens)) {
+		/* Bind to all address families */
+		bzero(&hints, sizeof(hints));
+		hints.ai_family = PF_UNSPEC;
+		hints.ai_flags = AI_PASSIVE;
+		hints.ai_socktype = SOCK_STREAM;
+		if (getaddrinfo(NULL, port, &hints, &res))
+			g_gate_xlog("Cannot get passive address: %s",
+			    strerror(errno));
+
+		p = res;
+		while (p) {
+			nl = malloc(sizeof(*nl));
+			bzero(nl, sizeof(*nl));
+			memcpy(&nl->l_addr, p->ai_addr, p->ai_addrlen);
+			SLIST_INSERT_HEAD(&listens, nl, l_next);
+			
+			p = p->ai_next;
+		}
+		freeaddrinfo(res);
+	} else {
+		/* Bind to some specific addresses */
+		SLIST_FOREACH(cl, &listens, l_next) {
+			bzero(&hints, sizeof(hints));
+			hints.ai_family = PF_UNSPEC;
+			hints.ai_socktype = SOCK_STREAM;
+			if (getaddrinfo(cl->l_name, port, &hints, &res))
+				g_gate_xlog("Invalid IP/host name to bind to: "
+				    "%s", cl->l_name);
+
+			/* Re-use current list entry for first match, add
+			 * new ones after that */
+			p = res;
+			nl = cl;
+			while (p) {
+				if (p != res) {
+					nl = malloc(sizeof(*nl));
+					bzero(nl, sizeof(*nl));
+				}
+
+				memcpy(&nl->l_addr, p->ai_addr, p->ai_addrlen);
+
+				if (p != res) {
+					SLIST_INSERT_HEAD(&listens, nl, l_next);
+				}
+				p = p->ai_next;
+			}
+			freeaddrinfo(res);
+		}
+	}
+
+	/* Actually create sockets and bind to them */
+	maxfd = 0;
+	SLIST_FOREACH(cl, &listens, l_next) {
+		cl->l_fd = socket(cl->l_addr.ss_family, SOCK_STREAM, 0);
+		if (cl->l_fd == -1)
+			g_gate_xlog("Cannot open stream socket: %s.",
+			    strerror(errno));
+		g_gate_socket_settings(cl->l_fd);
+
+		if (bind(cl->l_fd, (struct sockaddr *)&cl->l_addr,
+		    cl->l_addr.ss_len) == -1)
+			g_gate_xlog("bind(): %s.", strerror(errno));
+		if (listen(cl->l_fd, 5) == -1)
+			g_gate_xlog("listen(): %s.", strerror(errno));
+
+		if (maxfd <= cl->l_fd)
+			maxfd = cl->l_fd + 1;
+
+		g_gate_log(LOG_INFO, "Listen on address: %s (%s).",
+			ip2str((struct sockaddr *)&cl->l_addr), port);
+	}
+
 	if (!g_gate_verbose) {
 		/* Run in daemon mode. */
 		if (daemon(0, 0) == -1)
@@ -1005,40 +1154,44 @@
 	}
 
 	signal(SIGCHLD, SIG_IGN);
-
-	sfd = socket(AF_INET, SOCK_STREAM, 0);
-	if (sfd == -1)
-		g_gate_xlog("Cannot open stream socket: %s.", strerror(errno));
-	bzero(&serv, sizeof(serv));
-	serv.sin_family = AF_INET;
-	serv.sin_addr.s_addr = bindaddr;
-	serv.sin_port = htons(port);
-
-	g_gate_socket_settings(sfd);
-
-	if (bind(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1)
-		g_gate_xlog("bind(): %s.", strerror(errno));
-	if (listen(sfd, 5) == -1)
-		g_gate_xlog("listen(): %s.", strerror(errno));
-
-	g_gate_log(LOG_INFO, "Listen on port: %d.", port);
-
 	signal(SIGHUP, huphandler);
 
 	for (;;) {
-		fromlen = sizeof(from);
-		tmpsfd = accept(sfd, &from, &fromlen);
-		if (tmpsfd == -1)
-			g_gate_xlog("accept(): %s.", strerror(errno));
+		FD_ZERO(&listenfds);
+		SLIST_FOREACH(cl, &listens, l_next) {
+			FD_SET(cl->l_fd, &listenfds);
+		}
+
+		select(maxfd, &listenfds, NULL, NULL, NULL);
 
 		if (got_sighup) {
 			got_sighup = 0;
 			exports_get();
 		}
 
-		if (!handshake(&from, tmpsfd))
-			close(tmpsfd);
+		SLIST_FOREACH(cl, &listens, l_next) {
+			if (!FD_ISSET(cl->l_fd, &listenfds))
+				continue;
+
+			fromlen = sizeof(from);
+			tmpsfd = accept(cl->l_fd, (struct sockaddr*)&from,
+			    &fromlen);
+
+			if (tmpsfd == -1) {
+				g_gate_log(LOG_WARNING, "accept(): %s.",
+				    strerror(errno));
+				continue;
+			}
+
+			if (!handshake((struct sockaddr*)&from, tmpsfd))
+				close(tmpsfd);
+		}
+	}
+	while (!SLIST_EMPTY(&listens)) {
+		cl = SLIST_FIRST(&listens);
+		close(cl->l_fd);
+		SLIST_REMOVE_HEAD(&listens, l_next);
+		free(cl);
 	}
-	close(sfd);
 	exit(EXIT_SUCCESS);
 }
diff -ruN ggate.orig/shared/ggate.c ggate/shared/ggate.c
--- ggate.orig/shared/ggate.c	Thu Nov 10 18:53:21 2005
+++ ggate/shared/ggate.c	Thu Nov 10 18:53:25 2005
@@ -375,21 +375,3 @@
 	exit(EXIT_SUCCESS);
 }
 #endif	/* LIBGEOM */
-
-in_addr_t
-g_gate_str2ip(const char *str)
-{
-	struct hostent *hp;
-	in_addr_t ip;
-
-	ip = inet_addr(str);
-	if (ip != INADDR_NONE) {
-		/* It is a valid IP address. */
-		return (ip);
-	}
-	/* Check if it is a valid host name. */
-	hp = gethostbyname(str);
-	if (hp == NULL)
-		return (INADDR_NONE);
-	return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
-}
diff -ruN ggate.orig/shared/ggate.h ggate/shared/ggate.h
--- ggate.orig/shared/ggate.h	Thu Nov 10 18:53:21 2005
+++ ggate/shared/ggate.h	Thu Nov 10 18:53:25 2005
@@ -33,6 +33,7 @@
 #include <stdarg.h>
 
 #define	G_GATE_PORT		3080
+#define	G_GATE_PORT_STR		"3080"
 
 #define	G_GATE_RCVBUF		131072
 #define	G_GATE_SNDBUF		131072
@@ -110,7 +111,6 @@
 #ifdef LIBGEOM
 void	g_gate_list(int unit, int verbose);
 #endif
-in_addr_t g_gate_str2ip(const char *str);
 
 /*
  * g_gate_swap2h_* - functions swap bytes to host byte order (from big endian).
--- ggate_ipv6_2.patch ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list