changeset 27190:38c4973b5222

Add a function to remove a port mapping (UPnP or NAT-PMP) given a file descriptor. Adapt the XMPP PRPL to use this to remove port mappings when a file transfer is done. There is still no support for removing left-over mappings at exit, since I've found no good way of calling purple_upnp_remove_port_mapping (which relies on callbacks) from purple_network_uninit (since the main thread terminates after the _uninit functions have finished)...
author Marcus Lundblad <ml@update.uu.se>
date Mon, 15 Jun 2009 21:43:25 +0000
parents 55811a205c71
children bd84462b0e17 455e679ea0db
files ChangeLog.API libpurple/network.c libpurple/network.h libpurple/protocols/jabber/si.c
diffstat 4 files changed, 93 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Fri Jun 12 21:00:44 2009 +0000
+++ b/ChangeLog.API	Mon Jun 15 21:43:25 2009 +0000
@@ -39,6 +39,7 @@
 		* purple_network_set_turn_server
 		* purple_network_get_stun_ip
 		* purple_network_get_turn_ip
+		* purple_network_remove_port_mapping
 		* purple_proxy_connect_udp
 		* purple_prpl_get_media_caps
 		* purple_prpl_got_account_actions
--- a/libpurple/network.c	Fri Jun 12 21:00:44 2009 +0000
+++ b/libpurple/network.c	Mon Jun 15 21:43:25 2009 +0000
@@ -105,6 +105,10 @@
 static gchar *stun_ip = NULL;
 static gchar *turn_ip = NULL;
 
+/* Keep track of port mappings done with UPnP and NAT-PMP */
+static GHashTable *upnp_port_mappings = NULL;
+static GHashTable *nat_pmp_port_mappings = NULL;
+
 const unsigned char *
 purple_network_ip_atoi(const char *ip)
 {
@@ -257,6 +261,15 @@
 		return;
 	}
 
+	if (success) {
+		/* add port mapping to hash table */
+		gint *key = g_new(gint, 1);
+		gint *value = g_new(gint, 1);
+		*key = purple_network_get_port_from_fd(listen_data->listenfd);
+		*value = listen_data->socket_type;
+		g_hash_table_insert(upnp_port_mappings, key, value);
+	}
+
 	if (listen_data->cb)
 		listen_data->cb(listen_data->listenfd, listen_data->cb_data);
 
@@ -892,6 +905,59 @@
 	return &handle;
 }
 
+static void
+purple_network_upnp_mapping_remove_cb(gboolean sucess, gpointer data)
+{
+	purple_debug_info("network", "done removing UPnP port mapping\n");
+}
+
+/* the reason for these functions to have these signatures is to be able to
+ use them for g_hash_table_foreach to clean remaining port mappings, which is
+ not yet done */
+static void
+purple_network_upnp_mapping_remove(gpointer key, gpointer value,
+	gpointer user_data)
+{
+	gint port = (gint) *((gint *) key);
+	gint protocol = (gint) *((gint *) value);
+	purple_debug_info("network", "removing UPnP port mapping for port %d\n",
+		port);
+	purple_upnp_remove_port_mapping(port, 
+		protocol == SOCK_STREAM ? "TCP" : "UDP", 
+		purple_network_upnp_mapping_remove_cb, NULL);
+}
+
+static void
+purple_network_nat_pmp_mapping_remove(gpointer key, gpointer value,
+	gpointer user_data)
+{
+	gint port = (gint) *((gint *) key);
+	gint protocol = (gint) *((gint *) value);
+	purple_debug_info("network", "removing NAT-PMP port mapping for port %d\n",
+		port);
+	purple_pmp_destroy_map(
+		protocol == SOCK_STREAM ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP, 
+		port);
+}
+
+void
+purple_network_remove_port_mapping(gint fd)
+{
+	int port = purple_network_get_port_from_fd(fd);
+	gint *protocol = g_hash_table_lookup(upnp_port_mappings, &port);
+
+	if (protocol) {
+		purple_network_upnp_mapping_remove(&port, protocol, NULL);
+		g_hash_table_remove(upnp_port_mappings, protocol);
+	} else {
+		protocol = g_hash_table_lookup(nat_pmp_port_mappings, &port);
+		if (protocol) {
+			purple_network_nat_pmp_mapping_remove(&port, protocol, NULL);
+			g_hash_table_remove(nat_pmp_port_mappings, protocol);
+		}
+	}
+}
+	
 void
 purple_network_init(void)
 {
@@ -964,8 +1030,15 @@
 		purple_prefs_get_string("/purple/network/stun_server"));
 	purple_network_set_turn_server(
 		purple_prefs_get_string("/purple/network/turn_server"));
+
+	upnp_port_mappings = 
+		g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
+	nat_pmp_port_mappings =
+		g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
 }
 
+
+
 void
 purple_network_uninit(void)
 {
@@ -1008,4 +1081,10 @@
 	
 	if (stun_ip)
 		g_free(stun_ip);
+
+	g_hash_table_destroy(upnp_port_mappings);
+	g_hash_table_destroy(nat_pmp_port_mappings);
+
+	/* TODO: clean up remaining port mappings, note calling 
+	 purple_upnp_remove_port_mapping from here doesn't quite work... */
 }
--- a/libpurple/network.h	Fri Jun 12 21:00:44 2009 +0000
+++ b/libpurple/network.h	Mon Jun 15 21:43:25 2009 +0000
@@ -259,7 +259,14 @@
  */
 const gchar *purple_network_get_turn_ip(void);
 		
-	
+/**
+ * Remove a port mapping (UPnP or NAT-PMP) associated with listening socket
+ *
+ * @param fd Socket to remove the port mapping for
+ * @since 2.6.0
+ */
+void purple_network_remove_port_mapping(gint fd);	
+
 /**
  * Initializes the network subsystem.
  */
--- a/libpurple/protocols/jabber/si.c	Fri Jun 12 21:00:44 2009 +0000
+++ b/libpurple/protocols/jabber/si.c	Mon Jun 15 21:43:25 2009 +0000
@@ -1321,6 +1321,11 @@
 			jabber_iq_remove_callback_by_id(js, jsx->iq_id);
 		if (jsx->local_streamhost_fd >= 0)
 			close(jsx->local_streamhost_fd);
+		if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND &&
+			xfer->fd >= 0) {
+			purple_debug_info("jabber", "remove port mapping\n");
+			purple_network_remove_port_mapping(xfer->fd);
+		}
 		if (jsx->connect_timeout > 0)
 			purple_timeout_remove(jsx->connect_timeout);
 		if (jsx->ibb_timeout_handle > 0)