Mercurial > pidgin.yaz
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 *