changeset 29495:02f6f49da454

Add a function to enumerate all local IPs Adapted the XMPP prpl to include all local IPs as local streamhosts. I haven't had the chance to test if this actually works using a "non-default" interface, such as a VPN connection... Refs #10160
author Marcus Lundblad <ml@update.uu.se>
date Wed, 09 Sep 2009 19:01:03 +0000
parents d6dff41a6d4c
children f1aa01ff26dd
files ChangeLog.API libpurple/network.c libpurple/network.h libpurple/protocols/jabber/si.c
diffstat 4 files changed, 104 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Fri Sep 04 03:04:33 2009 +0000
+++ b/ChangeLog.API	Wed Sep 09 19:01:03 2009 +0000
@@ -1,6 +1,9 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 version 2.7.0 (??/??/????):
+	libpurple:
+		Added:
+		* purple_network_get_all_local_system_ips
 
 version 2.6.1 (08/18/2009):
 	No changes
--- a/libpurple/network.c	Fri Sep 04 03:04:33 2009 +0000
+++ b/libpurple/network.c	Wed Sep 09 19:01:03 2009 +0000
@@ -200,6 +200,82 @@
 	return "0.0.0.0";
 }
 
+GList *
+purple_network_get_all_local_system_ips(int fd)
+{
+	GList *result = NULL;
+	int source = fd;
+	char buffer[1024];
+	char *tmp;
+	struct ifconf ifc;
+	struct ifreq *ifr;
+
+	if (fd < 0)
+		source = socket(PF_INET,SOCK_STREAM, 0);
+	
+	ifc.ifc_len = sizeof(buffer);
+	ifc.ifc_req = (struct ifreq *)buffer;
+	ioctl(source, SIOCGIFCONF, &ifc);
+
+	if (fd < 0)
+		close(source);
+
+	/* enumerate the interfaces on IPv4 (or from source given by fd) */
+	tmp = buffer;
+	while (tmp < buffer + ifc.ifc_len) {
+		char dst[INET6_ADDRSTRLEN];
+
+		ifr = (struct ifreq *)tmp;
+		tmp += HX_SIZE_OF_IFREQ(*ifr);
+
+		if (ifr->ifr_addr.sa_family == AF_INET) {
+			struct sockaddr_in *sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
+
+			inet_ntop(AF_INET, &sinptr->sin_addr, dst,
+				sizeof(dst));
+			purple_debug_info("network", 
+				"found local i/f with address %s on IPv4\n", dst);
+			if (!purple_strequal(dst, "127.0.0.1")) {
+				result = g_list_append(result, g_strdup(dst));
+			}
+		}
+	}
+	
+	/* enumerate IPv6 interfaces (done when NOT specifying an fd,
+								  in that case use it (see above)) */
+	if (fd < 0) {
+		source = socket(PF_INET6, SOCK_STREAM, 0);
+	
+		ifc.ifc_len = sizeof(buffer);
+		ifc.ifc_req = (struct ifreq *)buffer;
+		ioctl(source, SIOCGIFCONF, &ifc);
+
+		close(source);
+
+		tmp = buffer;
+		while (tmp < buffer + ifc.ifc_len) {
+			char dst[INET6_ADDRSTRLEN];
+
+			ifr = (struct ifreq *)tmp;
+			tmp += HX_SIZE_OF_IFREQ(*ifr);
+
+			if (ifr->ifr_addr.sa_family == AF_INET6) {
+				struct sockaddr_in6 *sinptr =
+					(struct sockaddr_in6 *)&ifr->ifr_addr;
+
+				inet_ntop(AF_INET6, &sinptr->sin6_addr, dst, sizeof(dst));
+				purple_debug_info("network", 
+					"found local i/f with address %s on IPv4\n", dst);
+				if (!purple_strequal(dst, "::1")) {
+					result = g_list_append(result, g_strdup(dst));
+				}
+			}
+		}
+	}
+
+	return result;
+}
+
 const char *
 purple_network_get_my_ip(int fd)
 {
--- a/libpurple/network.h	Fri Sep 04 03:04:33 2009 +0000
+++ b/libpurple/network.h	Wed Sep 09 19:01:03 2009 +0000
@@ -88,6 +88,16 @@
 const char *purple_network_get_local_system_ip(int fd);
 
 /**
+ * Returns all IP addresses of the local system.
+ *
+ * @note The caller must free this list
+ *
+ * @param fd The fd to use to help figure out the IPs, or else -1.
+ * @return A list of local IP addresses.
+ */
+GList *purple_network_get_all_local_system_ips(int fd);
+
+/**
  * Returns the IP address that should be used anywhere a
  * public IP addresses is needed (listening for an incoming
  * file transfer, etc).
--- a/libpurple/protocols/jabber/si.c	Fri Sep 04 03:04:33 2009 +0000
+++ b/libpurple/protocols/jabber/si.c	Wed Sep 09 19:01:03 2009 +0000
@@ -853,8 +853,11 @@
 	/* If we successfully started listening locally */
 	if (sock >= 0) {
 		gchar *jid;
-		const char *local_ip, *public_ip;
-
+		GList *local_ips =
+			purple_network_get_all_local_system_ips(jsx->js->fd);
+		const char *public_ip;
+		gboolean has_public_ip = FALSE;
+		
 		jsx->local_streamhost_fd = sock;
 
 		jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
@@ -862,19 +865,24 @@
 		xfer->local_port = purple_network_get_port_from_fd(sock);
 		g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
 
-		/* Include the localhost's IP (for in-network transfers) */
-		local_ip = purple_network_get_local_system_ip(jsx->js->fd);
-		if (strcmp(local_ip, "0.0.0.0") != 0) {
+		public_ip = purple_network_get_my_ip(jsx->js->fd);
+
+		/* Include the localhost's IPs (for in-network transfers) */
+		while (local_ips) {
+			gchar *local_ip = local_ips->data;
 			streamhost_count++;
 			streamhost = xmlnode_new_child(query, "streamhost");
 			xmlnode_set_attrib(streamhost, "jid", jid);
 			xmlnode_set_attrib(streamhost, "host", local_ip);
 			xmlnode_set_attrib(streamhost, "port", port);
+			if (purple_strequal(local_ip, public_ip))
+				has_public_ip = TRUE;
+			g_free(local_ip);
+			local_ips = g_list_delete_link(local_ips, local_ips);
 		}
 
 		/* Include the public IP (assuming that there is a port mapped somehow) */
-		public_ip = purple_network_get_my_ip(jsx->js->fd);
-		if (strcmp(public_ip, local_ip) != 0 && strcmp(public_ip, "0.0.0.0") != 0) {
+		if (!has_public_ip && strcmp(public_ip, "0.0.0.0") != 0) {
 			streamhost_count++;
 			streamhost = xmlnode_new_child(query, "streamhost");
 			xmlnode_set_attrib(streamhost, "jid", jid);