netris (0.52-8) 09_ipv6

Summary

 inet.c |  163 ++++++++++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 107 insertions(+), 56 deletions(-)

    
download this patch

Patch contents

Description: Implement capability for IPv6.
 Migration to 'getaddrinfo()' and 'struct sockaddr_storage'
 make both address families AF_INET and AF_INET6 viable.
 .
 The preferred form of a port is as a string value in getaddrinfo(),
 so named ports are no possible, alongside numerical ports.
 .
 The goto statement is left because the previous code enforced
 a similar construct. It should really be removed.
Author: Mats Erik Andersson <debian@gisladisker.se>
Forwarded: no
Last-Updated: 2010-03-03

Index: netris-0.52/inet.c
===================================================================
--- netris-0.52.orig/inet.c
+++ netris-0.52/inet.c
@@ -49,32 +49,60 @@ ExtFunc void InitNet(void)
 
 ExtFunc int WaitForConnection(char *portStr)
 {
-	struct sockaddr_in addr;
-	struct hostent *host;
-	int sockListen;
+	struct sockaddr_storage addr;
+	struct sockaddr_in *sa4 = (struct sockaddr_in *) &addr;
+	struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &addr;
+	struct hostent *host = NULL;
+	struct addrinfo hints, *ai, *aiptr;
+	char portStrDef[12];
+	int sockListen, status;
 	socklen_t addrLen;
-	short port;
 	int val1;
 	struct linger val2;
 
-	if (portStr)
-		port = atoi(portStr);	/* XXX Error checking */
-	else
-		port = DEFAULT_PORT;
-	memset(&addr, 0, sizeof(addr));
-	addr.sin_family = AF_INET;
-	addr.sin_addr.s_addr = htonl(INADDR_ANY);
-	addr.sin_port = htons(port);
-	sockListen = socket(AF_INET, SOCK_STREAM, 0);
-	if (sockListen < 0)
+	if (!portStr || !strlen(portStr)) {
+		snprintf(portStrDef, sizeof(portStrDef), "%u", DEFAULT_PORT);
+		portStr = portStrDef;
+	}
+	/* XXX Error checking of port string. */
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_INET6;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_PASSIVE;
+
+	if ( (status = getaddrinfo(NULL, portStr, &hints, &ai)) ) {
+		fprintf(stderr, "getaddrinfo() failed: %s\n",
+				gai_strerror(status));
+		die("getaddrinfo");
+	}
+
+	for (aiptr = ai; aiptr; aiptr = aiptr->ai_next) {
+		if ( (sockListen = socket(aiptr->ai_family,
+						aiptr->ai_socktype,
+						aiptr->ai_protocol))
+				< 0 )
+			continue;
+
+		val1 = 1;
+		setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR,
+				(void *)&val1, sizeof(val1));
+		val1 = 0;
+		setsockopt(sockListen, IPPROTO_IPV6, IPV6_V6ONLY,
+				(void *)&val1, sizeof(val1));
+
+		if ( bind(sockListen, aiptr->ai_addr, aiptr->ai_addrlen)
+				== 0 )
+			if ( listen(sockListen, 1) >= 0 )
+				break;
+
+		close(sockListen);
+	}
+
+	freeaddrinfo(ai);
+	if (aiptr == NULL)
 		die("socket");
-	val1 = 1;
-	setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR,
-			(void *)&val1, sizeof(val1));
-	if (bind(sockListen, (struct sockaddr *)&addr, sizeof(addr)) < 0)
-		die("bind");
-	if (listen(sockListen, 1) < 0)
-		die("listen");
+
 	addrLen = sizeof(addr);
 	sock = accept(sockListen, (struct sockaddr *)&addr, &addrLen);
 	if (sock < 0)
@@ -86,13 +114,18 @@ ExtFunc int WaitForConnection(char *port
 			(void *)&val2, sizeof(val2));
 	netGen.fd = sock;
 	strcpy(opponentHost, "???");
-	if (addr.sin_family == AF_INET) {
-		host = gethostbyaddr((void *)&addr.sin_addr,
-				sizeof(struct in_addr), AF_INET);
-		if (host) {
-			strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1);
-			opponentHost[sizeof(opponentHost)-1] = 0;
-		}
+	switch (addr.ss_family) {
+		case AF_INET6:
+			host = gethostbyaddr((void *)&sa6->sin6_addr,
+				sizeof(struct in6_addr), addr.ss_family);
+			break;
+		case AF_INET:
+			host = gethostbyaddr((void *)&sa4->sin_addr,
+				sizeof(struct in_addr), addr.ss_family);
+	}
+	if (host) {
+		strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1);
+		opponentHost[sizeof(opponentHost)-1] = 0;
 	}
 	AddEventGen(&netGen);
 	isServer = 1;
@@ -101,36 +134,54 @@ ExtFunc int WaitForConnection(char *port
 
 ExtFunc int InitiateConnection(char *hostStr, char *portStr)
 {
-	struct sockaddr_in addr;
-	struct hostent *host;
-	short port;
-	int mySock;
-
-	if (portStr)
-		port = atoi(portStr);	/* XXX Error checking */
-	else
-		port = DEFAULT_PORT;
-	host = gethostbyname(hostStr);
-	if (!host)
-		die("gethostbyname");
-	assert(host->h_addrtype == AF_INET);
-	strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1);
-	opponentHost[sizeof(opponentHost)-1] = 0;
- again:
-	memset(&addr, 0, sizeof(addr));
-	addr.sin_family = host->h_addrtype;
-	memcpy(&addr.sin_addr, host->h_addr, host->h_length);
-	addr.sin_port = htons(port);
-	mySock = socket(AF_INET, SOCK_STREAM, 0);
-	if (mySock < 0)
-		die("socket");
-	if (connect(mySock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-		if (errno != ECONNREFUSED)
-			die("connect");
+	struct addrinfo hints, *ai, *aiptr;
+	char portStrDef[12];
+	int mySock, status;
+
+	if (!portStr || !strlen(portStr)) {
+		snprintf(portStrDef, sizeof(portStrDef), "%u", DEFAULT_PORT);
+		portStr = portStrDef;
+	}
+	/* XXX Error checking of port string. */
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME;
+
+	if ( (status = getaddrinfo(hostStr, portStr, &hints, &ai)) ) {
+		fprintf(stderr, "getaddrinfo() failed: %s\n",
+				gai_strerror(status));
+		die("getaddrinfo");
+	}
+
+	for (aiptr = ai; aiptr; aiptr = aiptr->ai_next) {
+again:
+		if ( (mySock = socket(aiptr->ai_family, aiptr->ai_socktype,
+						aiptr->ai_protocol))
+				< 0 )
+			continue;
+		while ( (status = connect(mySock, aiptr->ai_addr,
+						aiptr->ai_addrlen)) < 0
+				&& errno == ECONNREFUSED ) {
+			close(mySock);
+			sleep(1);
+			goto again;
+		}
+		if (status >= 0)
+			break;
+		/* Failure to connect. */
 		close(mySock);
-		sleep(1);
-		goto again;
 	}
+
+	if (aiptr == NULL) {
+		freeaddrinfo(ai);
+		die("socket/connect");
+	}
+
+	strncpy(opponentHost, aiptr->ai_canonname, sizeof(opponentHost)-1);
+	opponentHost[sizeof(opponentHost)-1] = 0;
+	freeaddrinfo(ai);
 	netGen.fd = sock = mySock;
 	AddEventGen(&netGen);
 	return 0;