agent: Move UPnP handling to each stream
authorOlivier Crête <olivier.crete@collabora.com>
Tue, 18 Aug 2020 20:04:58 +0000 (16:04 -0400)
committerOlivier Crête <olivier.crete@ocrete.ca>
Wed, 21 Oct 2020 01:24:39 +0000 (01:24 +0000)
Also rewrite the logic a little, and try to make the code a little clearer.

agent/agent-priv.h
agent/agent.c
agent/component.c
agent/component.h
agent/stream.c
agent/stream.h

index d6c488c..8c08b28 100644 (file)
@@ -166,12 +166,10 @@ struct _NiceAgent
                                     "Determining Role" ID-19) */
   NiceCompatibility compatibility; /* property: Compatibility mode */
   gboolean media_after_tick;       /* Received media after keepalive tick */
+  gboolean upnp_enabled;           /* whether UPnP discovery is enabled */
 #ifdef HAVE_GUPNP
   GUPnPSimpleIgdThread* upnp;     /* GUPnP Single IGD agent */
-  gboolean upnp_enabled;           /* whether UPnP discovery is enabled */
   guint upnp_timeout;              /* UPnP discovery timeout */
-  GSList *upnp_mapping;            /* NiceAddresses of cands being mapped */
-  GSource *upnp_timer_source;      /* source of upnp timeout timer */
 #endif
   gchar *software_attribute;       /* SOFTWARE attribute */
   gboolean reliable;               /* property: reliable */
@@ -246,7 +244,7 @@ StunUsageIceCompatibility agent_to_ice_compatibility (NiceAgent *agent);
 StunUsageTurnCompatibility agent_to_turn_compatibility (NiceAgent *agent);
 NiceTurnSocketCompatibility agent_to_turn_socket_compatibility (NiceAgent *agent);
 
-void agent_remove_local_candidate (NiceAgent *agent,
+void agent_remove_local_candidate (NiceAgent *agent, NiceStream *stream,
     NiceCandidate *candidate);
 
 void nice_agent_init_stun_agent (NiceAgent *agent, StunAgent *stun_agent);
index e642b63..c9c9313 100644 (file)
@@ -145,8 +145,6 @@ enum
 
 static guint signals[N_SIGNALS];
 
-static void priv_stop_upnp (NiceAgent *agent);
-
 static void pseudo_tcp_socket_opened (PseudoTcpSocket *sock, gpointer user_data);
 static void pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data);
 static void pseudo_tcp_socket_writable (PseudoTcpSocket *sock, gpointer user_data);
@@ -1630,9 +1628,7 @@ nice_agent_set_property (
       break;
 
     case PROP_UPNP:
-#ifdef HAVE_GUPNP
       agent->upnp_enabled = g_value_get_boolean (value);
-#endif
       break;
 
     case PROP_RELIABLE:
@@ -2218,7 +2214,7 @@ _transport_to_string (NiceCandidateTransport type) {
 
 void agent_gathering_done (NiceAgent *agent)
 {
-
+  gboolean upnp_running = FALSE;
   GSList *i, *j, *k, *l, *m;
 
   for (i = agent->streams; i; i = i->next) {
@@ -2234,6 +2230,11 @@ void agent_gathering_done (NiceAgent *agent)
     if (!stream->gathering)
       continue;
 
+#ifdef HAVE_GUPNP
+    if (stream->upnp_timer_source != NULL)
+      upnp_running = TRUE;
+#endif
+
     for (j = stream->components; j; j = j->next) {
       NiceComponent *component = j->data;
 
@@ -2280,7 +2281,7 @@ void agent_gathering_done (NiceAgent *agent)
               "for OC2007R2 compatibility", agent);
           component->local_candidates =
               g_slist_remove (component->local_candidates, local_candidate);
-          agent_remove_local_candidate (agent, local_candidate);
+          agent_remove_local_candidate (agent, stream, local_candidate);
           nice_candidate_free (local_candidate);
           goto next_cand;
         }
@@ -2305,15 +2306,8 @@ next_cand:
     }
   }
 
-#ifdef HAVE_GUPNP
-  if (agent->discovery_timer_source == NULL &&
-      agent->upnp_timer_source == NULL) {
-    agent_signal_gathering_done (agent);
-  }
-#else
-  if (agent->discovery_timer_source == NULL)
+  if (agent->discovery_timer_source == NULL && !upnp_running)
     agent_signal_gathering_done (agent);
-#endif
 }
 
 void agent_signal_gathering_done (NiceAgent *agent)
@@ -2908,23 +2902,6 @@ nice_agent_set_relay_info(NiceAgent *agent,
 
 #ifdef HAVE_GUPNP
 
-static void agent_check_upnp_gathering_done (NiceAgent *agent);
-
-static gboolean priv_upnp_timeout_cb_agent_locked (NiceAgent *agent,
-    gpointer user_data)
-{
-  nice_debug ("Agent %p : UPnP port mapping timed out", agent);
-
-  /* We cannot free priv->upnp here as it may be holding mappings open which
-   * we are using (e.g. if some mappings were successful and others errored). */
-  g_slist_free_full (agent->upnp_mapping, (GDestroyNotify) nice_address_free);
-  agent->upnp_mapping = NULL;
-
-  agent_check_upnp_gathering_done (agent);
-
-  return FALSE;
-}
-
 /* Check whether UPnP gathering is done, which is true when the list of pending
  * mappings (upnp_mapping) is empty. When it is empty, we have heard back from
  * gupnp-igd about each of the mappings we added, either successfully or not.
@@ -2932,88 +2909,139 @@ static gboolean priv_upnp_timeout_cb_agent_locked (NiceAgent *agent,
  * Note that upnp_mapping has to be a list, rather than a counter, as the
  * mapped-external-port and error-mapping-port signals could be emitted multiple
  * times for each mapping. */
-static void agent_check_upnp_gathering_done (NiceAgent *agent)
+static void check_upnp_gathering_done (NiceAgent *agent,
+                                       NiceStream *stream)
 {
-  if (agent->upnp_mapping != NULL)
+  if (stream->upnp_mapping != NULL)
     return;
 
-  if (agent->upnp_timer_source != NULL) {
-    g_source_destroy (agent->upnp_timer_source);
-    g_source_unref (agent->upnp_timer_source);
-    agent->upnp_timer_source = NULL;
+  if (stream->upnp_timer_source != NULL) {
+    g_source_destroy (stream->upnp_timer_source);
+    g_source_unref (stream->upnp_timer_source);
+    stream->upnp_timer_source = NULL;
   }
 
   agent_gathering_done (agent);
 }
 
-static void _upnp_mapped_external_port (GUPnPSimpleIgd *self, gchar *proto,
-    gchar *external_ip, gchar *replaces_external_ip, guint external_port,
-    gchar *local_ip, guint local_port, gchar *description, gpointer user_data)
+static gboolean priv_upnp_timeout_cb_agent_locked (NiceAgent *agent,
+    gpointer user_data)
 {
-  NiceAgent *agent = (NiceAgent*)user_data;
-  NiceAddress localaddr;
-  NiceAddress externaddr;
-  NiceCandidateTransport transport;
-  GSList *i, *j, *k;
+  NiceStream *stream = user_data;
 
-  agent_lock (agent);
+  nice_debug ("Agent %p s:%d : UPnP port mapping timed out", agent,
+              stream->id);
 
-  if (agent->upnp_timer_source == NULL)
-    goto end;
+  /* Force it to be done */
+  stream->upnp_mapped = g_slist_concat (stream->upnp_mapped,
+      stream->upnp_mapping);
+  stream->upnp_mapping = NULL;
 
-  nice_debug ("Agent %p : Successfully mapped %s:%d to %s:%d", agent, local_ip,
-      local_port, external_ip, external_port);
+  check_upnp_gathering_done (agent, stream);
 
-  if (!nice_address_set_from_string (&localaddr, local_ip))
-    goto end;
-  nice_address_set_port (&localaddr, local_port);
+  return G_SOURCE_REMOVE;
+}
+
+
+static GSList *
+priv_find_upnp_candidate (GSList *upnp_list, NiceCandidate *host_candidate)
+{
+  GSList *item;
+
+  for (item = upnp_list; item; item = item->next) {
+    NiceCandidate *c = item->data;
+
+    if (!nice_candidate_equal_target (host_candidate, c))
+      continue;
 
-  if (g_strcmp0 (proto, "TCP") == 0)
-    transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
+    if ((host_candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP) !=
+        (c->transport == NICE_CANDIDATE_TRANSPORT_UDP))
+      continue;
+
+    return item;
+  }
+
+  return NULL;
+}
+
+static NiceStream *
+priv_find_candidate_for_upnp_mapping (NiceAgent *agent, gchar *proto,
+    gchar *local_ip, guint local_port, gboolean only_mapping,
+    gboolean *was_mapping, GSList **item)
+{
+  GSList *i;
+  NiceCandidate upnp_candidate = { .type = NICE_CANDIDATE_TYPE_HOST };
+
+  if (!nice_address_set_from_string (&upnp_candidate.addr, local_ip))
+    return NULL;
+
+  nice_address_set_port (&upnp_candidate.addr, local_port);
+  if (!g_strcmp0 (proto, "UDP"))
+    upnp_candidate.transport = NICE_CANDIDATE_TRANSPORT_UDP;
   else
-    transport = NICE_CANDIDATE_TRANSPORT_UDP;
+    upnp_candidate.transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
 
-  for (i = agent->upnp_mapping; i; i = i->next) {
-    NiceAddress *addr = i->data;
-    if (nice_address_equal (&localaddr, addr)) {
-      agent->upnp_mapping = g_slist_remove (agent->upnp_mapping, addr);
-      nice_address_free (addr);
-      break;
+  for (i = agent->streams; i; i = i->next) {
+    NiceStream *stream = i->data;
+    GSList *j;
+
+    j = priv_find_upnp_candidate (stream->upnp_mapping, &upnp_candidate);
+    if (was_mapping)
+      *was_mapping = (j != NULL);
+
+    if (j == NULL && !only_mapping)
+      j = priv_find_upnp_candidate (stream->upnp_mapped, &upnp_candidate);
+
+    if (j) {
+      *item = j;
+      return stream;
     }
   }
 
+  return NULL;
+}
+
+
+static void _upnp_mapped_external_port (GUPnPSimpleIgd *self, gchar *proto,
+    gchar *external_ip, gchar *replaces_external_ip, guint external_port,
+    gchar *local_ip, guint local_port, gchar *description, gpointer user_data)
+{
+  NiceAgent *agent = (NiceAgent*)user_data;
+  NiceStream *stream = NULL;
+  GSList *item;
+  gboolean was_mapping = FALSE;
+  NiceAddress externaddr;
+
+  nice_debug ("Agent %p : Successfully mapped %s:%d to %s:%d", agent, local_ip,
+      local_port, external_ip, external_port);
+
   if (!nice_address_set_from_string (&externaddr, external_ip))
-    goto end;
+    return;
   nice_address_set_port (&externaddr, external_port);
 
-  for (i = agent->streams; i; i = i->next) {
-    NiceStream *stream = i->data;
-    for (j = stream->components; j; j = j->next) {
-      NiceComponent *component = j->data;
-      for (k = component->local_candidates; k; k = k->next) {
-        NiceCandidateImpl *local_candidate = k->data;
+  agent_lock (agent);
 
-        if (agent->force_relay &&
-            local_candidate->c.type != NICE_CANDIDATE_TYPE_RELAYED)
-          continue;
+  stream = priv_find_candidate_for_upnp_mapping (agent, proto,
+      local_ip, local_port, FALSE, &was_mapping, &item);
 
-        if (nice_address_equal (&localaddr, &local_candidate->c.base_addr)) {
-          discovery_add_server_reflexive_candidate (
-              agent,
-              stream->id,
-              component->id,
-              &externaddr,
-              transport,
-              local_candidate->sockptr,
-              TRUE);
-          goto end;
-        }
-      }
+  if (stream && stream->upnp_timer_source) {
+    NiceCandidateImpl *host_candidate = item->data;
+
+    if (was_mapping) {
+      stream->upnp_mapping = g_slist_delete_link (stream->upnp_mapping, item);
+      stream->upnp_mapped = g_slist_prepend (stream->upnp_mapped,
+          host_candidate);
     }
-  }
 
- end:
-  agent_check_upnp_gathering_done (agent);
+    discovery_add_server_reflexive_candidate (agent,
+        host_candidate->c.stream_id, host_candidate->c.component_id,
+        &externaddr,
+        host_candidate->c.transport,
+        host_candidate->sockptr,
+        TRUE);
+
+    check_upnp_gathering_done (agent, stream);
+  }
 
   agent_unlock_and_emit (agent);
 }
@@ -3022,32 +3050,180 @@ static void _upnp_error_mapping_port (GUPnPSimpleIgd *self, GError *error,
     gchar *proto, guint external_port, gchar *local_ip, guint local_port,
     gchar *description, gpointer user_data)
 {
-  NiceAgent *agent = (NiceAgent*)user_data;
-  NiceAddress localaddr;
-  GSList *i;
+  NiceAgent *agent = (NiceAgent *) user_data;
+  NiceStream *stream;
+  GSList *item;
 
   agent_lock (agent);
 
   nice_debug ("Agent %p : Error mapping %s:%d to %d (%d) : %s", agent, local_ip,
       local_port, external_port, error->domain, error->message);
-  if (nice_address_set_from_string (&localaddr, local_ip)) {
-    nice_address_set_port (&localaddr, local_port);
 
-    for (i = agent->upnp_mapping; i; i = i->next) {
-      NiceAddress *addr = i->data;
-      if (nice_address_equal (&localaddr, addr)) {
-        agent->upnp_mapping = g_slist_remove (agent->upnp_mapping, addr);
-        nice_address_free (addr);
-        break;
-      }
-    }
+  stream = priv_find_candidate_for_upnp_mapping (agent, proto,
+      local_ip, local_port, TRUE, NULL, &item);
 
-    agent_check_upnp_gathering_done (agent);
+  if (stream) {
+    NiceCandidate *host_candidate = item->data;
+
+    stream->upnp_mapping = g_slist_delete_link (stream->upnp_mapping, item);
+    stream->upnp_mapped =
+        g_slist_prepend (stream->upnp_mapped, host_candidate);
+    check_upnp_gathering_done (agent, stream);
   }
 
   agent_unlock_and_emit (agent);
 }
 
+static void
+priv_add_upnp_discovery (NiceAgent *agent, NiceStream *stream,
+    NiceCandidate *host_candidate)
+{
+  gchar local_ip[NICE_ADDRESS_STRING_LEN];
+
+  if (!agent->upnp_enabled || agent->force_relay)
+    return;
+
+  if (agent->upnp == NULL) {
+    agent->upnp = gupnp_simple_igd_thread_new ();
+
+    if (agent->upnp == NULL) {
+      nice_debug ("Agent %p : Could not initialize GUPnP library", agent);
+      agent->upnp_enabled = FALSE;
+      return;
+    }
+
+    g_signal_connect (agent->upnp, "mapped-external-port",
+        G_CALLBACK (_upnp_mapped_external_port), agent);
+    g_signal_connect (agent->upnp, "error-mapping-port",
+        G_CALLBACK (_upnp_error_mapping_port), agent);
+  }
+
+  if (host_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE)
+    return;
+
+  if (priv_find_upnp_candidate (stream->upnp_mapping, host_candidate))
+    return;
+  if (priv_find_upnp_candidate (stream->upnp_mapped, host_candidate))
+    return;
+
+  nice_address_to_string (&host_candidate->addr, local_ip);
+
+  gupnp_simple_igd_add_port (GUPNP_SIMPLE_IGD (agent->upnp),
+      host_candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP ? "UDP" : "TCP",
+      0, local_ip, nice_address_get_port (&host_candidate->addr),
+      0, PACKAGE_STRING);
+  stream->upnp_mapping = g_slist_prepend (stream->upnp_mapping,
+      nice_candidate_copy (host_candidate));
+
+  if (stream->upnp_timer_source == NULL)
+    agent_timeout_add_with_context (agent, &stream->upnp_timer_source,
+        "UPnP timeout", agent->upnp_timeout,
+        priv_upnp_timeout_cb_agent_locked, stream);
+}
+
+static void
+priv_remove_upnp_mapping (NiceAgent *agent, NiceCandidate *host_candidate)
+{
+  gchar local_ip[NICE_ADDRESS_STRING_LEN] = "";
+
+  nice_address_to_string (&host_candidate->addr, local_ip);
+
+  g_print ("REMOVING %s: %d\n", local_ip, nice_address_get_port (&host_candidate->addr));
+
+  gupnp_simple_igd_remove_port_local (GUPNP_SIMPLE_IGD (agent->upnp),
+      host_candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP ? "UDP" :
+      "TCP",
+      local_ip, nice_address_get_port (&host_candidate->addr));
+}
+
+void
+agent_remove_local_candidate (NiceAgent *agent, NiceStream *stream,
+    NiceCandidate *local_candidate)
+{
+  GSList *item;
+
+  if (agent->upnp == NULL)
+    return;
+
+  if (local_candidate->type != NICE_CANDIDATE_TYPE_HOST)
+    return;
+
+  if (local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE)
+    return;
+
+  item = priv_find_upnp_candidate (stream->upnp_mapping, local_candidate);
+  if (item) {
+    nice_candidate_free (item->data);
+    stream->upnp_mapping = g_slist_delete_link (stream->upnp_mapping, item);
+  }
+
+  item = priv_find_upnp_candidate (stream->upnp_mapped, local_candidate);
+  if (item) {
+    nice_candidate_free (item->data);
+    stream->upnp_mapped = g_slist_delete_link (stream->upnp_mapped, item);
+  }
+
+  priv_remove_upnp_mapping (agent, local_candidate);
+}
+
+static void
+priv_stop_upnp (NiceAgent *agent, NiceStream *stream)
+{
+  if (agent->upnp == NULL)
+    return;
+
+  if (stream->upnp_timer_source != NULL) {
+    g_source_destroy (stream->upnp_timer_source);
+    g_source_unref (stream->upnp_timer_source);
+    stream->upnp_timer_source = NULL;
+  }
+
+  while (stream->upnp_mapping) {
+    NiceCandidate *host_candidate = stream->upnp_mapping->data;
+
+    priv_remove_upnp_mapping (agent, host_candidate);
+
+    nice_candidate_free (host_candidate);
+    stream->upnp_mapping = g_slist_delete_link (stream->upnp_mapping,
+        stream->upnp_mapping);
+  }
+
+  while (stream->upnp_mapped) {
+    NiceCandidate *host_candidate = stream->upnp_mapped->data;
+
+    priv_remove_upnp_mapping (agent, host_candidate);
+
+    nice_candidate_free (host_candidate);
+    stream->upnp_mapped = g_slist_delete_link (stream->upnp_mapped,
+        stream->upnp_mapped);
+  }
+}
+
+#else /* HAVE_GUPNP */
+
+static inline void
+priv_add_upnp_discovery (NiceAgent *agent, NiceStream *stream,
+    NiceCandidate *host_candidate)
+{
+  /* Use the upnp_enabled to print this only once */
+  if (agent->upnp_enabled) {
+    nice_debug ("Agent %p : libnice compiled without GUPnP support", agent);
+    agent->upnp_enabled = FALSE;
+  }
+}
+
+static void
+priv_stop_upnp (NiceAgent *agent, NiceStream *stream) {
+  /* Do nothing */
+}
+
+void
+agent_remove_local_candidate (NiceAgent *agent, NiceStream *stream,
+    NiceCandidate *local_candidate)
+{
+  /* Do nothing */
+}
+
 #endif
 
 NICEAPI_EXPORT gboolean
@@ -3082,25 +3258,6 @@ nice_agent_gather_candidates (
   nice_debug ("Agent %p : In %s mode, starting candidate gathering.", agent,
       agent->full_mode ? "ICE-FULL" : "ICE-LITE");
 
-#ifdef HAVE_GUPNP
-  if (agent->upnp_enabled && agent->upnp == NULL && !agent->force_relay) {
-    agent->upnp = gupnp_simple_igd_thread_new ();
-
-    if (agent->upnp) {
-      g_signal_connect (agent->upnp, "mapped-external-port",
-          G_CALLBACK (_upnp_mapped_external_port), agent);
-      g_signal_connect (agent->upnp, "error-mapping-port",
-          G_CALLBACK (_upnp_error_mapping_port), agent);
-    } else {
-      nice_debug ("Agent %p : Error creating UPnP Simple IGD agent", agent);
-    }
-  } else {
-    nice_debug ("Agent %p : UPnP property Disabled", agent);
-  }
-#else
-  nice_debug ("Agent %p : libnice compiled without UPnP support", agent);
-#endif
-
   /* if no local addresses added, generate them ourselves */
   if (agent->local_addresses == NULL) {
     GList *addresses = nice_interfaces_get_local_ips (FALSE);
@@ -3156,11 +3313,6 @@ nice_agent_gather_candidates (
       NiceAddress *addr = i->data;
       NiceCandidateImpl *host_candidate;
 
-#ifdef HAVE_GUPNP
-      gchar local_ip[NICE_ADDRESS_STRING_LEN];
-      nice_address_to_string (addr, local_ip);
-#endif
-
       for (add_type = ADD_HOST_MIN; add_type <= ADD_HOST_MAX; add_type++) {
         NiceCandidateTransport transport;
         guint current_port;
@@ -3252,23 +3404,7 @@ nice_agent_gather_candidates (
         nice_socket_set_writable_callback (host_candidate->sockptr,
             _tcp_sock_is_writable, component);
 
-#ifdef HAVE_GUPNP
-      if (agent->upnp_enabled && agent->upnp &&
-          transport != NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
-        NiceAddress *base_addr = nice_address_dup (&host_candidate->c.base_addr);
-        nice_debug ("Agent %p: Adding UPnP port %s:%d", agent, local_ip,
-            nice_address_get_port (base_addr));
-        gupnp_simple_igd_add_port (GUPNP_SIMPLE_IGD (agent->upnp),
-            transport == NICE_CANDIDATE_TRANSPORT_UDP ? "UDP" : "TCP",
-            0, local_ip, nice_address_get_port (base_addr),
-            0, PACKAGE_STRING);
-        agent->upnp_mapping = g_slist_prepend (agent->upnp_mapping, base_addr);
-
-        agent_timeout_add_with_context (agent, &agent->upnp_timer_source,
-            "UPnP timeout", agent->upnp_timeout,
-            priv_upnp_timeout_cb_agent_locked, agent);
-      }
-#endif
+        priv_add_upnp_discovery (agent, stream, (NiceCandidate *) host_candidate);
 
         /* TODO: Add server-reflexive support for TCP candidates */
         if (agent->full_mode && agent->stun_server_ip && !agent->force_relay &&
@@ -3338,7 +3474,7 @@ nice_agent_gather_candidates (
   /* note: no async discoveries pending, signal that we are ready */
   if (agent->discovery_unsched_items == 0 &&
 #ifdef HAVE_GUPNP
-      agent->upnp_mapping == NULL) {
+      stream->upnp_mapping == NULL) {
 #else
       TRUE) {
 #endif
@@ -3355,20 +3491,14 @@ nice_agent_gather_candidates (
   g_slist_free (local_addresses);
 
   if (ret == FALSE) {
-    priv_stop_upnp (agent);
+    priv_stop_upnp (agent, stream);
     for (cid = 1; cid <= stream->n_components; cid++) {
       NiceComponent *component = nice_stream_find_component_by_id (stream, cid);
 
       nice_component_free_socket_sources (component);
 
-      for (i = component->local_candidates; i; i = i->next) {
-        NiceCandidate *candidate = i->data;
-
-        agent_remove_local_candidate (agent, candidate);
-
-        nice_candidate_free (candidate);
-      }
-      g_slist_free (component->local_candidates);
+      g_slist_free_full (component->local_candidates,
+          (GDestroyNotify) nice_candidate_free);
       component->local_candidates = NULL;
     }
     discovery_prune_stream (agent, stream_id);
@@ -3379,44 +3509,6 @@ nice_agent_gather_candidates (
   return ret;
 }
 
-void agent_remove_local_candidate (NiceAgent *agent, NiceCandidate *candidate)
-{
-#ifdef HAVE_GUPNP
-  gchar local_ip[NICE_ADDRESS_STRING_LEN];
-
-  if (agent->upnp == NULL)
-    return;
-
-  if (candidate->type != NICE_CANDIDATE_TYPE_HOST)
-    return;
-
-  if (nice_address_get_port (&candidate->addr) == 0)
-    return;
-
-  nice_address_to_string (&candidate->addr, local_ip);
-
-  gupnp_simple_igd_remove_port_local (GUPNP_SIMPLE_IGD (agent->upnp), "UDP",
-      local_ip, nice_address_get_port (&candidate->addr));
-#endif
-}
-
-static void priv_stop_upnp (NiceAgent *agent)
-{
-#ifdef HAVE_GUPNP
-  if (!agent->upnp)
-    return;
-
-  g_slist_free_full (agent->upnp_mapping, (GDestroyNotify) nice_address_free);
-  agent->upnp_mapping = NULL;
-
-  if (agent->upnp_timer_source != NULL) {
-    g_source_destroy (agent->upnp_timer_source);
-    g_source_unref (agent->upnp_timer_source);
-    agent->upnp_timer_source = NULL;
-  }
-#endif
-}
-
 static void priv_remove_keepalive_timer (NiceAgent *agent)
 {
   if (agent->keepalive_timer_source != NULL) {
@@ -3469,6 +3561,8 @@ nice_agent_remove_stream (
     return;
   }
 
+  priv_stop_upnp (agent, stream);
+
   /* note: remove items with matching stream_ids from both lists */
   conn_check_prune_stream (agent, stream);
   discovery_prune_stream (agent, stream_id);
@@ -5458,6 +5552,7 @@ nice_agent_dispose (GObject *object)
   while (agent->streams) {
     NiceStream *s = agent->streams->data;
 
+    priv_stop_upnp (agent, s);
     nice_stream_close (agent, s);
     g_object_unref (s);
 
@@ -5491,8 +5586,6 @@ nice_agent_dispose (GObject *object)
   nice_rng_free (agent->rng);
   agent->rng = NULL;
 
-  priv_stop_upnp (agent);
-
 #ifdef HAVE_GUPNP
   if (agent->upnp) {
     g_object_unref (agent->upnp);
index b0e9fe0..1854779 100644 (file)
@@ -197,8 +197,8 @@ nice_component_remove_socket (NiceAgent *agent, NiceComponent *cmp,
       conn_check_prune_socket (agent, stream, cmp, candidate->sockptr);
       nice_component_detach_socket (cmp, candidate->sockptr);
     }
-    agent_remove_local_candidate (agent, (NiceCandidate *) candidate);
-    nice_candidate_free ((NiceCandidate *) candidate);
+    agent_remove_local_candidate (agent, stream, (NiceCandidate *) candidate);
+    nice_candidate_free ((NiceCandidate *)candidate);
 
     cmp->local_candidates = g_slist_delete_link (cmp->local_candidates, i);
     i = next;
@@ -289,7 +289,7 @@ nice_component_clean_turn_servers (NiceAgent *agent, NiceComponent *cmp)
       cmp->selected_pair.priority = 0;
       cmp->turn_candidate = candidate;
     } else {
-      agent_remove_local_candidate (agent, (NiceCandidate *) candidate);
+      agent_remove_local_candidate (agent, stream, (NiceCandidate *) candidate);
       relay_candidates = g_slist_append(relay_candidates, candidate);
     }
     cmp->local_candidates = g_slist_delete_link (cmp->local_candidates, i);
@@ -324,7 +324,7 @@ nice_component_clear_selected_pair (NiceComponent *component)
 /* Must be called with the agent lock held as it touches internal Component
  * state. */
 void
-nice_component_close (NiceAgent *agent, NiceComponent *cmp)
+nice_component_close (NiceAgent *agent, NiceStream *stream, NiceComponent *cmp)
 {
   IOCallbackData *data;
   GOutputVector *vec;
@@ -353,7 +353,7 @@ nice_component_close (NiceAgent *agent, NiceComponent *cmp)
         cmp->turn_candidate = NULL;
 
   while (cmp->local_candidates) {
-    agent_remove_local_candidate (agent, cmp->local_candidates->data);
+    agent_remove_local_candidate (agent, stream, cmp->local_candidates->data);
     nice_candidate_free (cmp->local_candidates->data);
     cmp->local_candidates = g_slist_delete_link (cmp->local_candidates,
         cmp->local_candidates);
index a36cc94..2e6c496 100644 (file)
@@ -236,7 +236,8 @@ NiceComponent *
 nice_component_new (guint component_id, NiceAgent *agent, NiceStream *stream);
 
 void
-nice_component_close (NiceAgent *agent, NiceComponent *component);
+nice_component_close (NiceAgent *agent, NiceStream *stream,
+    NiceComponent *component);
 
 gboolean
 nice_component_find_pair (NiceComponent *component, NiceAgent *agent,
index b8f42dc..2ec4feb 100644 (file)
@@ -89,7 +89,7 @@ nice_stream_close (NiceAgent *agent, NiceStream *stream)
 
   for (i = stream->components; i; i = i->next) {
     NiceComponent *component = i->data;
-    nice_component_close (agent, component);
+    nice_component_close (agent, stream, component);
   }
 }
 
index de1d456..b41f67a 100644 (file)
@@ -90,6 +90,12 @@ struct _NiceStream {
   gboolean peer_gathering_done;
   gint tos;
   guint tick_counter;
+
+#ifdef HAVE_GUPNP
+  GSList *upnp_mapping;            /* NiceCandidate being mapped */
+  GSList *upnp_mapped;             /* NiceCandidate mapped with UPnP */
+  GSource *upnp_timer_source;      /* source of upnp timeout timer */
+#endif
 };
 
 typedef struct {