changeset 29766:529233281dfe

network: Use getifaddrs() to enumerate local interfaces/IPs if available. On Linux, it seems SIOCGIFCONF just doesn't support IPv6, and various systems have getifaddrs(), so here we are. Fixes #10160.
author Paul Aurich <paul@darkrain42.org>
date Tue, 16 Feb 2010 02:21:33 +0000
parents 667806b1f359
children 2c955a6f1fa8
files ChangeLog.API configure.ac libpurple/network.c
diffstat 3 files changed, 49 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Tue Feb 16 00:50:34 2010 +0000
+++ b/ChangeLog.API	Tue Feb 16 02:21:33 2010 +0000
@@ -11,7 +11,10 @@
 		* purple_media_codec_copy
 		* purple_media_manager_get_backend_type
 		* purple_media_manager_set_backend_type
-		* purple_network_get_all_local_system_ips
+		* purple_network_get_all_local_system_ips, which returns all local
+		  IPs on the system.  On systems with the getifaddrs() function,
+		  this will return both IPv4 and IPv6 addresses (excluding link-local
+		  and loopback addresses).  On others, it returns just IPv4 addresses.
 		* purple_prpl_got_media_caps
 		* purple_unescape_text
 		* purple_uuid_random
--- a/configure.ac	Tue Feb 16 00:50:34 2010 +0000
+++ b/configure.ac	Tue Feb 16 02:21:33 2010 +0000
@@ -211,6 +211,7 @@
 	[AC_CHECK_LIB(socket, getaddrinfo,
 		[AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lsnl $LIBS"], , , -lnsl)])
 AC_CHECK_FUNCS(inet_ntop)
+AC_CHECK_FUNCS(getifaddrs)
 dnl Check for socklen_t (in Unix98)
 AC_MSG_CHECKING(for socklen_t)
 AC_TRY_COMPILE([
--- a/libpurple/network.c	Tue Feb 16 00:50:34 2010 +0000
+++ b/libpurple/network.c	Tue Feb 16 02:21:33 2010 +0000
@@ -32,6 +32,9 @@
 #include <netinet/in.h>
 #include <net/if.h>
 #include <sys/ioctl.h>
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
 #else
 #include <nspapi.h>
 #endif
@@ -203,6 +206,46 @@
 GList *
 purple_network_get_all_local_system_ips(void)
 {
+#ifdef HAVE_GETIFADDRS
+	GList *result = NULL;
+	struct ifaddrs *start, *ifa;
+	int ret;
+
+	ret = getifaddrs(&start);
+	if (ret < 0) {
+		purple_debug_warning("network",
+				"getifaddrs() failed: %s\n", g_strerror(errno));
+		return NULL;
+	}
+
+	for (ifa = start; ifa; ifa = ifa->ifa_next) {
+		int family = ifa->ifa_addr ? ifa->ifa_addr->sa_family : AF_UNSPEC;
+		char host[INET6_ADDRSTRLEN];
+		const char *tmp = NULL;
+
+		if ((family != AF_INET && family != AF_INET6) || ifa->ifa_flags & IFF_LOOPBACK)
+			continue;
+
+		if (family == AF_INET)
+			tmp = inet_ntop(family, &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, host, sizeof(host));
+		else {
+			struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)ifa->ifa_addr;
+			/* Peer-peer link-local communication is a big TODO.  I am not sure
+			 * how communicating link-local addresses is supposed to work, and
+			 * it seems like it would require attempting the cartesian product
+			 * of the local and remote interfaces to see if any match (eww).
+			 */
+			if (!IN6_IS_ADDR_LINKLOCAL(&sockaddr->sin6_addr))
+				tmp = inet_ntop(family, &sockaddr->sin6_addr, host, sizeof(host));
+		}
+		if (tmp != NULL)
+			result = g_list_prepend(result, g_strdup(tmp));
+	}
+
+	freeifaddrs(start);
+
+	return g_list_reverse(result);
+#else /* HAVE_GETIFADDRS */
 	GList *result = NULL;
 	int source = socket(PF_INET,SOCK_STREAM, 0);
 	char buffer[1024];
@@ -222,7 +265,6 @@
 		ifr = (struct ifreq *)tmp;
 		tmp += HX_SIZE_OF_IFREQ(*ifr);
 
-		/* TODO: handle IPv6 */
 		if (ifr->ifr_addr.sa_family == AF_INET) {
 			struct sockaddr_in *sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
 
@@ -237,6 +279,7 @@
 	}
 
 	return result;
+#endif /* HAVE_GETIFADDRS */
 }
 
 const char *