changeset 15974:1a12ce76c4f4

The network module now registers the signal 'network-configuration-changed' and emits it when a network change is detected via libnm or the win32 network monitor. The UI could also emit this signal if it knows something network.c doesn't. UPnP and NAT-PMP respond to the signal by clearing their IP address caches; changing networks without quitting/relaunching will now lead to the new IP address being (lazily) determined. This commit also enables nat-pmp and adds nat-pmp.[h|c] to the build process; please let me know if there are any problems building, as I only have OS X test machines.
author Evan Schoenberg <evan.s@dreskin.net>
date Sat, 31 Mar 2007 20:33:54 +0000
parents 71fddbec98e4
children 328ae2c041c3
files ChangeLog.API libpurple/Makefile.am libpurple/nat-pmp.c libpurple/nat-pmp.h libpurple/network.c libpurple/network.h libpurple/upnp.c libpurple/upnp.h
diffstat 8 files changed, 159 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Sat Mar 31 04:36:05 2007 +0000
+++ b/ChangeLog.API	Sat Mar 31 20:33:54 2007 +0000
@@ -416,6 +416,7 @@
 	* "gtkblist-hiding"
 	* "gtkblist-unhiding"
 	* "log-displaying"
+	* "network-configuration-changed"
 	* "savedstatus-changed"
 	* "sendto-extended-menu"
 	* "uri-handler"
--- a/libpurple/Makefile.am	Sat Mar 31 04:36:05 2007 +0000
+++ b/libpurple/Makefile.am	Sat Mar 31 20:33:54 2007 +0000
@@ -49,6 +49,7 @@
 	imgstore.c \
 	log.c \
 	mime.c \
+	nat-pmp.c \
 	network.c \
 	ntlm.c \
 	notify.c \
@@ -99,6 +100,7 @@
 	imgstore.h \
 	log.h \
 	mime.h \
+	nat-pmp.h \
 	network.h \
 	notify.h \
 	ntlm.h \
--- a/libpurple/nat-pmp.c	Sat Mar 31 04:36:05 2007 +0000
+++ b/libpurple/nat-pmp.c	Sat Mar 31 20:33:54 2007 +0000
@@ -30,6 +30,8 @@
 
 #include "nat-pmp.h"
 #include "debug.h"
+#include "signals.h"
+#include "network.h"
 
 #include <arpa/inet.h>
 #include <netinet/in.h>
@@ -87,6 +89,20 @@
 
 typedef struct _PurplePmpMapResponse PurplePmpMapResponse;
 
+typedef enum {
+	PURPLE_PMP_STATUS_UNDISCOVERED = -1,
+	PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER,
+	PURPLE_PMP_STATUS_DISCOVERING,
+	PURPLE_PMP_STATUS_DISCOVERED
+} PurpleUPnPStatus;
+
+typedef struct {
+	PurpleUPnPStatus status;
+	gchar *publicip;
+} PurplePmpInfo;
+
+static PurplePmpInfo pmp_info = {PURPLE_PMP_STATUS_UNDISCOVERED, NULL};
+
 /*
  *	Thanks to R. Matthew Emerson for the fixes on this
  */
@@ -240,11 +256,32 @@
 char *
 purple_pmp_get_public_ip()
 {
-	struct sockaddr_in *gateway = default_gw();
+	struct sockaddr_in addr, *gateway, *publicsockaddr = NULL;
+	struct timeval req_timeout;
+	socklen_t len;
+
+	PurplePmpIpRequest req;
+	PurplePmpIpResponse resp;
+	int sendfd;
+	
+	if (pmp_info.status == PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER)
+		return NULL;
+	
+	if ((pmp_info.status == PURPLE_PMP_STATUS_DISCOVERED) && (pmp_info.publicip != NULL))
+	{
+#ifdef PMP_DEBUG
+		purple_debug_info("nat-pmp", "Returning cached publicip %s",pmp_info.publicip);
+#endif
+		return pmp_info.publicip;
+	}
+
+	gateway = default_gw();
 
 	if (!gateway)
 	{
 		purple_debug_info("nat-pmp", "Cannot request public IP from a NULL gateway!\n");
+		/* If we get a NULL gateway, don't try again next time */
+		pmp_info.status = PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER;
 		return NULL;
 	}
 
@@ -252,12 +289,6 @@
 	if (gateway->sin_port != PMP_PORT)
 		gateway->sin_port = htons(PMP_PORT);
 
-	int sendfd;
-	struct timeval req_timeout;
-	PurplePmpIpRequest req;
-	PurplePmpIpResponse resp;
-	struct sockaddr_in *publicsockaddr = NULL;
-
 	req_timeout.tv_sec = 0;
 	req_timeout.tv_usec = PMP_TIMEOUT;
 
@@ -274,20 +305,19 @@
 	 * With the recommended timeout of 0.25 seconds, we're talking 511.5 seconds (8.5 minutes).
 	 * 
 	 * This seems really silly... if this were nonblocking, a couple retries might be in order, but it's not at present.
-	 * XXX Make this nonblocking.
 	 */
 #ifdef PMP_DEBUG
 	purple_debug_info("nat-pmp", "Attempting to retrieve the public ip address for the NAT device at: %s\n", inet_ntoa(gateway->sin_addr));
 	purple_debug_info("nat-pmp", "\tTimeout: %ds %dus\n", req_timeout.tv_sec, req_timeout.tv_usec);
 #endif
-	struct sockaddr_in addr;
-	socklen_t len = sizeof(struct sockaddr_in);
 
 	/* TODO: Non-blocking! */
+	
 	if (sendto(sendfd, &req, sizeof(req), 0, (struct sockaddr *)(gateway), sizeof(struct sockaddr)) < 0)
 	{
 		purple_debug_info("nat-pmp", "There was an error sending the NAT-PMP public IP request! (%s)\n", strerror(errno));
 		g_free(gateway);
+		pmp_info.status = PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER;
 		return NULL;
 	}
 
@@ -295,16 +325,19 @@
 	{
 		purple_debug_info("nat-pmp", "There was an error setting the socket's options! (%s)\n", strerror(errno));
 		g_free(gateway);
+		pmp_info.status = PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER;
 		return NULL;
 	}		
 
 	/* TODO: Non-blocking! */
+	len = sizeof(struct sockaddr_in);
 	if (recvfrom(sendfd, &resp, sizeof(PurplePmpIpResponse), 0, (struct sockaddr *)(&addr), &len) < 0)
 	{			
 		if (errno != EAGAIN)
 		{
 			purple_debug_info("nat-pmp", "There was an error receiving the response from the NAT-PMP device! (%s)\n", strerror(errno));
 			g_free(gateway);
+			pmp_info.status = PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER;
 			return NULL;
 		}
 	}
@@ -315,11 +348,15 @@
 	{
 		purple_debug_info("nat-pmp", "Response was not received from our gateway! Instead from: %s\n", inet_ntoa(addr.sin_addr));
 		g_free(gateway);
+
+		pmp_info.status = PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER;
 		return NULL;
 	}
 
 	if (!publicsockaddr) {
 		g_free(gateway);
+		
+		pmp_info.status = PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER;
 		return NULL;
 	}
 
@@ -338,6 +375,10 @@
 
 	g_free(gateway);
 
+	g_free(pmp_info.publicip);
+	pmp_info.publicip = g_strdup(inet_ntoa(publicsockaddr->sin_addr));
+	pmp_info.status = PURPLE_PMP_STATUS_DISCOVERED;
+
 	return inet_ntoa(publicsockaddr->sin_addr);
 }
 
@@ -458,6 +499,30 @@
 
 	return success;
 }
+
+static void
+purple_pmp_network_config_changed_cb(void *data)
+{
+	pmp_info.status = PURPLE_PMP_STATUS_UNDISCOVERED;
+	g_free(pmp_info.publicip);
+	pmp_info.publicip = NULL;
+}
+
+static void*
+purple_pmp_get_handle(void)
+{
+	static int handle;
+
+	return &handle;	
+}
+
+void
+purple_pmp_init()
+{
+	purple_signal_connect(purple_network_get_handle(), "network-configuration-changed",
+		  purple_pmp_get_handle(), PURPLE_CALLBACK(purple_pmp_network_config_changed_cb),
+		  GINT_TO_POINTER(0));	
+}
 #else /* #ifdef NET_RT_DUMP */
 char *
 purple_pmp_get_public_ip()
@@ -476,4 +541,10 @@
 {
 	return FALSE;
 }
+
+void
+purple_pmp_init()
+{
+
+}
 #endif /* #ifndef NET_RT_DUMP */
--- a/libpurple/nat-pmp.h	Sat Mar 31 04:36:05 2007 +0000
+++ b/libpurple/nat-pmp.h	Sat Mar 31 20:33:54 2007 +0000
@@ -36,27 +36,20 @@
 
 #define PURPLE_PMP_LIFETIME	3600	/* 3600 seconds */
 
-/*
- *	uint8_t:	version, opcodes
- *	uint16_t:	resultcode
- *	unint32_t:	epoch (seconds since mappings reset)
- */
-
 typedef enum {
 	PURPLE_PMP_TYPE_UDP,
 	PURPLE_PMP_TYPE_TCP
 } PurplePmpType;
 
 /**
+ * Initialize nat-pmp
+ */
+void purple_pmp_init(void);
+
+/**
  *
  */
-
-/*
- * TODO: This should probably cache the result of this lookup requests
- *       so that subsequent calls to this function do not require a
- *       round-trip exchange with the local router.
- */
-char *purple_pmp_get_public_ip();
+char *purple_pmp_get_public_ip(void);
 
 /**
  * Remove the NAT-PMP mapping for a specified type on a specified port
--- a/libpurple/network.c	Sat Mar 31 04:36:05 2007 +0000
+++ b/libpurple/network.c	Sat Mar 31 20:33:54 2007 +0000
@@ -42,17 +42,12 @@
 
 #include "debug.h"
 #include "account.h"
+#include "nat-pmp.h"
 #include "network.h"
 #include "prefs.h"
 #include "stun.h"
 #include "upnp.h"
 
-/* #define ENABLE_NAT_PMP 1 */
-
-#ifdef ENABLE_NAT_PMP
-#include "nat-pmp.h"
-#endif
-
 /*
  * Calling sizeof(struct ifreq) isn't always correct on
  * Mac OS X (and maybe others).
@@ -198,12 +193,10 @@
 	if (ip != NULL)
 	  return ip;
 
-#ifdef ENABLE_NAT_PMP
 	/* Attempt to get the IP from a NAT device using NAT-PMP */
 	ip = purple_pmp_get_public_ip();
 	if (ip != NULL)
 		return ip;
-#endif
 
 	/* Just fetch the IP of the local system */
 	return purple_network_get_local_system_ip(fd);
@@ -250,7 +243,6 @@
 	purple_network_listen_cancel(listen_data);
 }
 
-#ifdef ENABLE_NAT_PMP
 static gboolean
 purple_network_finish_pmp_map_cb(gpointer data)
 {
@@ -265,7 +257,6 @@
 
 	return FALSE;
 }
-#endif
 
 static PurpleNetworkListenData *
 purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
@@ -361,7 +352,6 @@
 	listen_data->cb = cb;
 	listen_data->cb_data = cb_data;
 
-#ifdef ENABLE_NAT_PMP
 	/* Attempt a NAT-PMP Mapping, which will return immediately */
 	if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
 							  actual_port, actual_port, PURPLE_PMP_LIFETIME))
@@ -371,7 +361,6 @@
 		purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
 	}
 	else
-#endif
 	{
 		/* Attempt a UPnP Mapping */
 		listen_data->mapping_data = purple_upnp_set_port_mapping(
@@ -508,6 +497,8 @@
 
 	purple_debug_info("network", "Received Network Change Notification. Current network count is %d, previous count was %d.\n", new_count, current_network_count);
 
+	purple_signal_emit(purple_network_get_handle(), "network-configuration-changed", NULL);
+
 	if (new_count > 0 && ui_ops != NULL && ui_ops->network_connected != NULL) {
 		ui_ops->network_connected();
 	} else if (new_count == 0 && current_network_count > 0 &&
@@ -616,6 +607,8 @@
 	current = libnm_glib_get_network_state(ctx);
 	purple_debug_info("network","Entering nm_callback_func!\n");
 
+	purple_signal_emit(purple_network_get_handle(), "network-configuration-changed", NULL);
+
 	switch(current)
 	{
 	case LIBNM_ACTIVE_NETWORK_CONNECTION:
@@ -641,6 +634,14 @@
 }
 #endif
 
+void *
+purple_network_get_handle(void)
+{
+	static int handle;
+	
+	return &handle;
+}
+
 void
 purple_network_init(void)
 {
@@ -673,6 +674,12 @@
 	if(nm_context)
 		nm_callback_idx = libnm_glib_register_callback(nm_context, nm_callback_func, NULL, g_main_context_default());
 #endif
+
+	purple_signal_register(purple_network_get_handle(), "network-configuration-changed",
+						   purple_marshal_VOID, NULL, 0);
+	
+	purple_pmp_init();
+	purple_upnp_init();
 }
 
 void
--- a/libpurple/network.h	Sat Mar 31 04:36:05 2007 +0000
+++ b/libpurple/network.h	Sat Mar 31 20:33:54 2007 +0000
@@ -197,6 +197,13 @@
 gboolean purple_network_is_available(void);
 
 /**
+ * Get the handle for the network system
+ *
+ * @return the handle to the network system
+ */
+void *purple_network_get_handle(void);
+
+/**
  * Initializes the network subsystem.
  */
 void purple_network_init(void);
--- a/libpurple/upnp.c	Sat Mar 31 04:36:05 2007 +0000
+++ b/libpurple/upnp.c	Sat Mar 31 20:33:54 2007 +0000
@@ -24,14 +24,15 @@
  */
 #include "internal.h"
 
-#include "debug.h"
-#include "util.h"
-#include "proxy.h"
-#include "xmlnode.h"
-#include "network.h"
-#include "eventloop.h"
 #include "upnp.h"
 
+#include "debug.h"
+#include "eventloop.h"
+#include "network.h"
+#include "proxy.h"
+#include "signals.h"
+#include "util.h"
+#include "xmlnode.h"
 
 /***************************************************************
 ** General Defines                                             *
@@ -1026,3 +1027,32 @@
 	do_port_mapping_cb(TRUE, ar);
 	return ar;
 }
+
+static void
+purple_upnp_network_config_changed_cb(void *data)
+{
+	/* Reset the control_info to default values */
+	control_info.status = PURPLE_UPNP_STATUS_UNDISCOVERED;
+	g_free(control_info.control_url);
+	control_info.control_url = NULL;
+	control_info.service_type[0] = '\0';
+	control_info.publicip[0] = '\0';
+	control_info.internalip[0] = '\0';
+	control_info.lookup_time = 0;
+}
+
+static void*
+purple_upnp_get_handle(void)
+{
+	static int handle;
+	
+	return &handle;	
+}
+
+void
+purple_upnp_init()
+{
+	purple_signal_connect(purple_network_get_handle(), "network-configuration-changed",
+						  purple_upnp_get_handle(), PURPLE_CALLBACK(purple_upnp_network_config_changed_cb),
+						  GINT_TO_POINTER(0));		
+}
--- a/libpurple/upnp.h	Sat Mar 31 04:36:05 2007 +0000
+++ b/libpurple/upnp.h	Sat Mar 31 20:33:54 2007 +0000
@@ -41,6 +41,13 @@
 
 typedef void (*PurpleUPnPCallback) (gboolean success, gpointer data);
 
+
+/**
+ * Initialize UPnP
+ */
+void purple_upnp_init(void);
+
+
 /**
  * Sends a discovery request to search for a UPnP enabled IGD that
  * contains the WANIPConnection service that will allow us to recieve the