$NetBSD: patch-aj,v 1.1 2002/01/10 16:43:29 skrll Exp $

--- uquake/net_udp.c.orig	Mon Jan 10 16:59:39 2000
+++ uquake/net_udp.c
@@ -32,12 +32,17 @@
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <errno.h>
+#include <net/if.h>
 
 #ifdef __sun__
 #include <sys/filio.h>
 #undef model_t
 #endif
 
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK         (u_int32_t)0x7f000001
+#endif
+
 #if defined(sgi) && defined(sa_family)
 /* Get rid of problematic SGI #def */
 #undef sa_family
@@ -62,6 +67,91 @@
 #include "net_udp.h"
 
 //=============================================================================
+/* get any of my non-loopback addr. */
+static int
+grab_myaddr(family, sa)
+	int family;
+	struct sockaddr *sa;
+{
+	int s;
+	unsigned int maxif;
+	struct ifreq *iflist;
+	struct ifconf ifconf;
+	struct ifreq *ifr, *ifr_end;
+	struct myaddrs *p;
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+
+#if 0
+	maxif = if_maxindex() + 1;
+#else
+	maxif = 10;
+#endif
+	iflist = (struct ifreq *)malloc(maxif * BUFSIZ);	/* XXX */
+	if (!iflist) {
+		Sys_Error("grab_myaddr: not enough core\n");
+		/*NOTREACHED*/
+	}
+
+	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+		Sys_Error("socket(SOCK_DGRAM)\n");
+		/*NOTREACHED*/
+	}
+	memset(&ifconf, 0, sizeof(ifconf));
+	ifconf.ifc_req = iflist;
+	ifconf.ifc_len = maxif * BUFSIZ;	/* XXX */
+	if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) {
+		Sys_Error("ioctl(SIOCGIFCONF)\n");
+		/*NOTREACHED*/
+	}
+	close(s);
+
+	/* Look for this interface in the list */
+	ifr_end = (struct ifreq *) (ifconf.ifc_buf + ifconf.ifc_len);
+	for (ifr = ifconf.ifc_req;
+	     ifr < ifr_end;
+	     ifr = (struct ifreq *) ((char *) &ifr->ifr_addr
+				    + ifr->ifr_addr.sa_len)) {
+		switch (ifr->ifr_addr.sa_family) {
+		case AF_INET:
+			sin = (struct sockaddr_in *)&ifr->ifr_addr;
+			if (sin->sin_addr.s_addr == htonl(0))
+				continue;
+			if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
+				continue;
+			break;
+		case AF_INET6:
+			sin6 = (struct sockaddr_in6 *)&ifr->ifr_addr;
+			if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
+				continue;
+			if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
+				continue;
+			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+				continue;
+			if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
+				continue;
+			break;
+		}
+		if (ifr->ifr_addr.sa_family != family)
+			continue;
+
+		memcpy(sa, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
+
+	    {
+		char hbuf[NI_MAXHOST];
+		if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0,
+		    NI_NUMERICHOST) == 0)
+			printf("got %s as local address\n", hbuf);
+		else {
+			printf("non-printable local address, family %d\n",
+			    family);
+		}
+	    }
+		break;
+	}
+
+	free(iflist);
+}
 
 int UDP_Init (void)
 {
@@ -74,9 +164,18 @@
 		return -1;
 
 	// determine my name & address
+	memset(&myAddr, 0, sizeof(myAddr));
+#if 0
 	gethostname(buff, MAXHOSTNAMELEN);
 	local = gethostbyname(buff);
 	myAddr = *(int *)local->h_addr_list[0];
+#else
+    {
+	struct sockaddr_in sin;
+	if (grab_myaddr(AF_INET, (struct sockaddr *)&sin) == 0)
+		memcpy(&myAddr, &sin.sin_addr, sizeof(myAddr));
+    }
+#endif
 
 	// if the quake hostname isn't set, set it to the machine name
 	if (Q_strcmp(hostname.string, "UNNAMED") == 0)
@@ -137,25 +236,45 @@
 
 int UDP_OpenSocket (int port)
 {
+	struct addrinfo hints, *res = NULL;
+	int error;
+	char pbuf[NI_MAXSERV];
 	int newsocket;
-	struct sockaddr_in address;
 	qboolean _true = true;
+	const int false = 0;
 
-	if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+	snprintf(pbuf, sizeof(pbuf), "%d", port);
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_INET6;
+	hints.ai_socktype = SOCK_DGRAM;
+	hints.ai_flags = AI_PASSIVE;
+	error = getaddrinfo(NULL, pbuf, &hints, &res);
+	if (error)
 		return -1;
 
-	if (ioctl (newsocket, FIONBIO, (char *)&_true) == -1)
+	if ((newsocket = socket (res->ai_family, res->ai_socktype,
+	    res->ai_protocol)) < 0)
+		return -1;
+
+	if (ioctl (newsocket, FIONBIO, (char *)&_true) < 0)
 		goto ErrorReturn;
 
-	address.sin_family = AF_INET;
-	address.sin_addr.s_addr = INADDR_ANY;
-	address.sin_port = htons(port);
-	if( bind (newsocket, (void *)&address, sizeof(address)) == -1)
+#ifdef IPV6_BINDV6ONLY
+	if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_BINDV6ONLY, &false,
+	    sizeof(false)) < 0) {
+		/* I don't care */
+	}
+#endif
+
+	if (bind (newsocket, res->ai_addr, res->ai_addrlen) < 0)
 		goto ErrorReturn;
 
+	freeaddrinfo(res);
 	return newsocket;
 
 ErrorReturn:
+	if (res)
+		freeaddrinfo(res);
 	close (newsocket);
 	return -1;
 }
