Imported Upstream version 0.1.17
[platform/upstream/libnice.git] / agent / conncheck.c
index e931e24..4ad49d4 100644 (file)
@@ -63,8 +63,7 @@
 #include "stun/usages/turn.h"
 
 static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream);
-static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component);
-static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id);
+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, NiceComponent *component);
 static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand);
 static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand);
 static size_t priv_create_username (NiceAgent *agent, NiceStream *stream,
@@ -77,25 +76,15 @@ static void candidate_check_pair_free (NiceAgent *agent,
 static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
     NiceAgent *agent, guint stream_id, NiceComponent *component,
     NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state);
-static gboolean priv_update_selected_pair (NiceAgent *agent,
-    NiceComponent *component, CandidateCheckPair *pair);
-
-static int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
-{
-  return (now->tv_sec == timer->tv_sec) ?
-    now->tv_usec >= timer->tv_usec :
-    now->tv_sec >= timer->tv_sec;
-}
+static gboolean priv_conn_keepalive_tick_agent_locked (NiceAgent *agent,
+    gpointer pointer);
 
-static unsigned int priv_timer_remainder (GTimeVal *timer, GTimeVal *now)
+static gint64 priv_timer_remainder (gint64 timer, gint64 now)
 {
-  unsigned int delay;
-  if (now->tv_sec > timer->tv_sec ||
-      (now->tv_sec == timer->tv_sec && now->tv_usec > timer->tv_usec))
+  if (now >= timer)
     return 0;
-  delay = (timer->tv_sec - now->tv_sec) * 1000;
-  delay += ((signed long)(timer->tv_usec - now->tv_usec)) / 1000;
-  return delay;
+
+  return (timer - now) / 1000;
 }
 
 static gchar
@@ -191,6 +180,77 @@ priv_candidate_type_to_string (NiceCandidateType type)
   }
 }
 
+static const gchar *
+priv_candidate_transport_to_string (NiceCandidateTransport transport)
+{
+  switch (transport) {
+    case NICE_CANDIDATE_TRANSPORT_UDP:
+      return "udp";
+    case NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
+      return "tcp-act";
+    case NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
+      return "tcp-pass";
+    case NICE_CANDIDATE_TRANSPORT_TCP_SO:
+      return "tcp-so";
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+static const gchar *
+priv_socket_type_to_string (NiceSocketType type)
+{
+  switch (type) {
+    case NICE_SOCKET_TYPE_UDP_BSD:
+      return "udp";
+    case NICE_SOCKET_TYPE_TCP_BSD:
+      return "tcp";
+    case NICE_SOCKET_TYPE_PSEUDOSSL:
+      return "ssl";
+    case NICE_SOCKET_TYPE_HTTP:
+      return "http";
+    case NICE_SOCKET_TYPE_SOCKS5:
+      return "socks";
+    case NICE_SOCKET_TYPE_UDP_TURN:
+      return "udp-turn";
+    case NICE_SOCKET_TYPE_UDP_TURN_OVER_TCP:
+      return "tcp-turn";
+    case NICE_SOCKET_TYPE_TCP_ACTIVE:
+      return "tcp-act";
+    case NICE_SOCKET_TYPE_TCP_PASSIVE:
+      return "tcp-pass";
+    case NICE_SOCKET_TYPE_TCP_SO:
+      return "tcp-so";
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+/*
+ * Dump the component list of incoming checks
+ */
+static void
+print_component_incoming_checks (NiceAgent *agent, NiceStream *stream,
+  NiceComponent *component)
+{
+  GList *i;
+
+  for (i = component->incoming_checks.head; i; i = i->next) {
+    IncomingCheck *icheck = i->data;
+    gchar tmpbuf1[INET6_ADDRSTRLEN] = {0};
+    gchar tmpbuf2[INET6_ADDRSTRLEN] = {0};
+
+    nice_address_to_string (&icheck->local_socket->addr, tmpbuf1);
+    nice_address_to_string (&icheck->from, tmpbuf2);
+    nice_debug ("Agent %p : *** sc=%d/%d : icheck %p : "
+      "sock %s [%s]:%u > [%s]:%u",
+      agent, stream->id, component->id, icheck,
+      priv_socket_type_to_string (icheck->local_socket->type),
+      tmpbuf1, nice_address_get_port (&icheck->local_socket->addr),
+      tmpbuf2, nice_address_get_port (&icheck->from));
+  }
+}
+
 /*
  * Dump the conncheck lists of the agent
  */
@@ -199,55 +259,70 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *
 {
   GSList *i, *k, *l;
   guint j, m;
-  GTimeVal now;
+  gint64 now;
 
   if (!nice_debug_is_verbose ())
     return;
 
-  g_get_current_time (&now);
+  now = g_get_monotonic_time ();
 
 #define PRIORITY_LEN 32
 
   nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)",
       agent, where, detail ? detail : "");
+  nice_debug ("Agent %p : *** agent nomination mode %s, %s",
+      agent, agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
+      "aggressive" : "regular",
+      agent->controlling_mode ? "controlling" : "controlled");
   for (i = agent->streams; i ; i = i->next) {
     NiceStream *stream = i->data;
     for (j = 1; j <= stream->n_components; j++) {
+      NiceComponent *component;
       for (k = stream->conncheck_list; k ; k = k->next) {
         CandidateCheckPair *pair = k->data;
         if (pair->component_id == j) {
           gchar local_addr[INET6_ADDRSTRLEN];
           gchar remote_addr[INET6_ADDRSTRLEN];
+          gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
 
           nice_address_to_string (&pair->local->addr, local_addr);
           nice_address_to_string (&pair->remote->addr, remote_addr);
+          nice_candidate_pair_priority_to_string (pair->priority, priority);
 
           nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
-              "f=%s t=%s:%s [%s]:%u > [%s]:%u state=%c%s%s%s",
+              "f=%s t=%s:%s sock=%s "
+              "%s:[%s]:%u > %s:[%s]:%u prio=%s/%08x state=%c%s%s%s%s",
               agent, pair->stream_id, pair->component_id, pair,
               pair->foundation,
               priv_candidate_type_to_string (pair->local->type),
               priv_candidate_type_to_string (pair->remote->type),
+              priv_socket_type_to_string (pair->sockptr->type),
+              priv_candidate_transport_to_string (pair->local->transport),
               local_addr, nice_address_get_port (&pair->local->addr),
+              priv_candidate_transport_to_string (pair->remote->transport),
               remote_addr, nice_address_get_port (&pair->remote->addr),
+              priority, pair->stun_priority,
               priv_state_to_gchar (pair->state),
               pair->valid ? "V" : "",
               pair->nominated ? "N" : "",
+              pair->use_candidate_on_next_check ? "C" : "",
               g_slist_find (agent->triggered_check_queue, pair) ? "T" : "");
 
           for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) {
             StunTransaction *stun = l->data;
-            nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
-                "stun#=%d timer=%d/%d %d/%dms buf=%p %s",
+            nice_debug ("Agent %p : *** sc=%d/%d : pair %p :   "
+                "stun#=%d timer=%d/%d %" G_GINT64_FORMAT "/%dms buf=%p %s",
                 agent, pair->stream_id, pair->component_id, pair, m,
                 stun->timer.retransmissions, stun->timer.max_retransmissions,
-                stun->timer.delay - priv_timer_remainder (&stun->next_tick, &now),
+                stun->timer.delay - priv_timer_remainder (stun->next_tick, now),
                 stun->timer.delay,
                 stun->message.buffer,
                 (m == 0 && pair->retransmit) ? "(R)" : "");
           }
         }
       }
+      if (agent_find_component (agent, stream->id, j, NULL, &component))
+        print_component_incoming_checks (agent, stream, component);
     }
   }
 }
@@ -259,7 +334,6 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa
 {
   g_assert (pair);
 
-  SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS);
   if (agent->triggered_check_queue == NULL ||
       g_slist_find (agent->triggered_check_queue, pair) == NULL)
     agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair);
@@ -289,89 +363,6 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent)
 }
 
 /*
- * Check if the conncheck list if Active according to
- * ICE spec, 5.7.4 (Computing States)
- *
- * note: the ICE spec in unclear about that, but the check list should
- * be considered active when there is at least a pair in Waiting state
- * OR a pair in In-Progress state.
- */
-static gboolean
-priv_is_checklist_active (NiceStream *stream)
-{
-  GSList *i;
-
-  for (i = stream->conncheck_list; i ; i = i->next) {
-    CandidateCheckPair *p = i->data;
-    if (p->state == NICE_CHECK_WAITING || p->state == NICE_CHECK_IN_PROGRESS)
-      return TRUE;
-  }
-  return FALSE;
-}
-
-/*
- * Check if the conncheck list if Frozen according to
- * ICE spec, 5.7.4 (Computing States)
- */
-static gboolean
-priv_is_checklist_frozen (NiceStream *stream)
-{
-  GSList *i;
-
-  if (stream->conncheck_list == NULL)
-    return FALSE;
-
-  for (i = stream->conncheck_list; i ; i = i->next) {
-    CandidateCheckPair *p = i->data;
-    if (p->state != NICE_CHECK_FROZEN)
-      return FALSE;
-  }
-  return TRUE;
-}
-
-/*
- * Check if all components of the stream have
- * a valid pair (used for ICE spec, 7.1.3.2.3, point 2.)
- */
-static gboolean
-priv_all_components_have_valid_pair (NiceStream *stream)
-{
-  guint i;
-  GSList *j;
-
-  for (i = 1; i <= stream->n_components; i++) {
-    for (j = stream->conncheck_list; j ; j = j->next) {
-      CandidateCheckPair *p = j->data;
-      if (p->component_id == i && p->valid)
-        break;
-    }
-    if (j == NULL)
-      return FALSE;
-  }
-  return TRUE;
-}
-
-/*
- * Check if the foundation in parameter matches the foundation
- * of a valid pair in the conncheck list [of stream] (used for ICE spec,
- * 7.1.3.2.3, point 2.)
- */
-static gboolean
-priv_foundation_matches_a_valid_pair (const gchar *foundation, NiceStream *stream)
-{
-  GSList *i;
-
-  for (i = stream->conncheck_list; i ; i = i->next) {
-    CandidateCheckPair *p = i->data;
-    if (p->valid &&
-        strncmp (p->foundation, foundation,
-            NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0)
-      return TRUE;
-  }
-  return FALSE;
-}
-
-/*
  * Finds the next connectivity check in WAITING state.
  */
 static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
@@ -390,62 +381,12 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
 }
 
 /*
- * Finds the next connectivity check in FROZEN state.
- */
-static CandidateCheckPair *
-priv_conn_check_find_next_frozen (GSList *conn_check_list)
-{
-  GSList *i;
-
-  /* note: list is sorted in priority order to first frozen check has
-   *       the highest priority */
-  for (i = conn_check_list; i ; i = i->next) {
-    CandidateCheckPair *p = i->data;
-    if (p->state == NICE_CHECK_FROZEN)
-      return p;
-  }
-
-  return NULL;
-}
-
-/*
- * Returns the number of active check lists of the agent
- */
-static guint
-priv_number_of_active_check_lists (NiceAgent *agent)
-{
-  guint n = 0;
-  GSList *i;
-
-  for (i = agent->streams; i ; i = i->next)
-    if (priv_is_checklist_active (i->data))
-      n++;
-  return n;
-}
-
-/*
- * Returns the first stream of the agent having a Frozen
- * connection check list
- */
-static NiceStream *
-priv_find_first_frozen_check_list (NiceAgent *agent)
-{
-  GSList *i;
-
-  for (i = agent->streams; i ; i = i->next) {
-    NiceStream *stream = i->data;
-    if (priv_is_checklist_frozen (stream))
-      return stream;
-  }
-  return NULL;
-}
-
-/*
  * Initiates a new connectivity check for a ICE candidate pair.
  *
  * @return TRUE on success, FALSE on error
  */
-static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
+static gboolean
+priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
 {
   SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS);
   if (conn_check_send (agent, pair)) {
@@ -457,140 +398,170 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *
 
 /*
  * Unfreezes the next connectivity check in the list. Follows the
- * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
- * (RFC5245)
+ * algorithm defined in sect 6.1.2.6 (Computing Candidate Pair States)
+ * and sect 6.1.4.2 (Performing Connectivity Checks) of the ICE spec
+ * (RFC8445)
  *
- * See also sect 7.1.2.2.3 (Updating Pair States), and
- * priv_conn_check_unfreeze_related().
+ * Note that this algorithm is slightly simplified compared to previous
+ * version of the spec (RFC5245), and this new version is now
+ * idempotent.
  * 
  * @return TRUE on success, and FALSE if no frozen candidates were found.
  */
-static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *stream)
+static gboolean
+priv_conn_check_unfreeze_next (NiceAgent *agent)
 {
   GSList *i, *j;
-  GSList *found_list = NULL;
+  GSList *foundation_list = NULL;
   gboolean result = FALSE;
 
-  priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
+  /* While a pair in state waiting exists, we do nothing */
+  for (i = agent->streams; i ; i = i->next) {
+    NiceStream *s = i->data;
+    for (j = s->conncheck_list; j ; j = j->next) {
+      CandidateCheckPair *p = j->data;
 
-  for (i = stream->conncheck_list; i ; i = i->next) {
-    CandidateCheckPair *p1 = i->data;
-    CandidateCheckPair *pair = NULL;
-    guint lowest_component_id = stream->n_components + 1;
-    guint64 highest_priority = 0;
+      if (p->state == NICE_CHECK_WAITING)
+        return TRUE;
+    }
+  }
 
-    if (g_slist_find_custom (found_list, p1->foundation, (GCompareFunc)strcmp))
-      continue;
-    found_list = g_slist_prepend (found_list, p1->foundation);
+  /* When there are no more pairs in waiting state, we unfreeze some
+   * pairs, so that we get a single waiting pair per foundation.
+   */
+  for (i = agent->streams; i ; i = i->next) {
+    NiceStream *s = i->data;
+    for (j = s->conncheck_list; j ; j = j->next) {
+      CandidateCheckPair *p = j->data;
 
-    for (j = stream->conncheck_list; j ; j = j->next) {
-      CandidateCheckPair *p2 = i->data;
-      if (strncmp (p2->foundation, p1->foundation,
-              NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
-        if (p2->component_id < lowest_component_id ||
-            (p2->component_id == lowest_component_id &&
-             p2->priority > highest_priority)) {
-          pair = p2;
-          lowest_component_id = p2->component_id;
-          highest_priority = p2->priority;
-        }
-      }
-    }
+      if (g_slist_find_custom (foundation_list, p->foundation,
+          (GCompareFunc)strcmp))
+        continue;
 
-    if (pair) {
-      nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.",
-          agent, pair, pair->stream_id, pair->component_id, pair->foundation);
-      SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING);
-      result = TRUE;
+      if (p->state == NICE_CHECK_FROZEN) {
+        nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.",
+            agent, p, p->stream_id, p->component_id, p->foundation);
+        SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
+        foundation_list = g_slist_prepend (foundation_list, p->foundation);
+        result = TRUE;
+      }
     }
   }
-  g_slist_free (found_list);
+  g_slist_free (foundation_list);
+
+  /* We dump the conncheck list when something interesting happened, ie
+   * when we unfroze some pairs.
+   */
+  if (result)
+    priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
+
   return result;
 }
 
 /*
- * Unfreezes the next next connectivity check in the list after
+ * Unfreezes the related connectivity check in the list after
  * check 'success_check' has successfully completed.
  *
- * See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-19).
+ * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
+ *
+ * Note that this algorithm is slightly simplified compared to previous
+ * version of the spec (RFC5245)
  * 
  * @param agent context
- * @param ok_check a connectivity check that has just completed
+ * @param pair a pair, whose connectivity check has just succeeded
  *
- * @return TRUE on success, and FALSE if no frozen candidates were found.
  */
-static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check)
+void
+conn_check_unfreeze_related (NiceAgent *agent, CandidateCheckPair *pair)
 {
   GSList *i, *j;
+  gboolean result = FALSE;
 
-  g_assert (ok_check);
-  g_assert (ok_check->state == NICE_CHECK_SUCCEEDED);
-  g_assert (stream);
-  g_assert (stream->id == ok_check->stream_id);
-
-  priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
+  g_assert (pair);
+  g_assert (pair->state == NICE_CHECK_SUCCEEDED);
 
-  /* step: perform the step (1) of 'Updating Pair States' */
-  for (i = stream->conncheck_list; i ; i = i->next) {
-    CandidateCheckPair *p = i->data;
+  for (i = agent->streams; i ; i = i->next) {
+    NiceStream *s = i->data;
+    for (j = s->conncheck_list; j ; j = j->next) {
+      CandidateCheckPair *p = j->data;
    
-    if (p->stream_id == ok_check->stream_id) {
+      /* The states for all other Frozen candidates pairs in all
+       * checklists with the same foundation is set to waiting
+       */
       if (p->state == NICE_CHECK_FROZEN &&
-          strncmp (p->foundation, ok_check->foundation,
+          strncmp (p->foundation, pair->foundation,
               NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
-       nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check);
-       SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
+        nice_debug ("Agent %p : Unfreezing check %p "
+            "(after successful check %p).", agent, p, pair);
+        SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
+        result = TRUE;
       }
     }
   }
+  /* We dump the conncheck list when something interesting happened, ie
+   * when we unfroze some pairs.
+   */
+  if (result)
+    priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
+}
 
-  /* step: perform the step (2) of 'Updating Pair States' */
-  stream = agent_find_stream (agent, ok_check->stream_id);
-  if (priv_all_components_have_valid_pair (stream)) {
-    for (i = agent->streams; i ; i = i->next) {
-      /* the agent examines the check list for each other
-       * media stream in turn
-       */
-      NiceStream *s = i->data;
-      if (s->id == ok_check->stream_id)
-        continue;
-      if (priv_is_checklist_active (s)) {
-        /* checklist is Active
-         */
-        for (j = s->conncheck_list; j ; j = j->next) {
-         CandidateCheckPair *p = j->data;
-          if (p->state == NICE_CHECK_FROZEN &&
-              priv_foundation_matches_a_valid_pair (p->foundation, stream)) {
-           nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check);
-           SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
-          }
-        }
-      } else if (priv_is_checklist_frozen (s)) {
-        /* checklist is Frozen
-         */
-        gboolean match_found = FALSE;
-        /* check if there is one pair in the check list whose
-         * foundation matches a pair in the valid list under
-         * consideration
-         */
-        for (j = s->conncheck_list; j ; j = j->next) {
-         CandidateCheckPair *p = j->data;
-          if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) {
-            match_found = TRUE;
-            nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check);
-            SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
-          }
-        }
+/*
+ * Unfreezes this connectivity check if its foundation is the same than
+ * the foundation of an already succeeded pair.
+ *
+ * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
+ *
+ * @param agent context
+ * @param pair a pair, whose state is frozen
+ *
+ */
+static void
+priv_conn_check_unfreeze_maybe (NiceAgent *agent, CandidateCheckPair *pair)
+{
+  GSList *i, *j;
+  gboolean result = FALSE;
 
-        if (!match_found) {
-          /* set the pair with the lowest component ID
-           * and highest priority to Waiting
-           */
-          priv_conn_check_unfreeze_next (agent, s);
-        }
+  g_assert (pair);
+  g_assert (pair->state == NICE_CHECK_FROZEN);
+
+  for (i = agent->streams; i ; i = i->next) {
+    NiceStream *s = i->data;
+    for (j = s->conncheck_list; j ; j = j->next) {
+      CandidateCheckPair *p = j->data;
+
+      if (p->state == NICE_CHECK_SUCCEEDED &&
+          strncmp (p->foundation, pair->foundation,
+              NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
+        nice_debug ("Agent %p : Unfreezing check %p "
+            "(after successful check %p).", agent, pair, p);
+        SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING);
+        result = TRUE;
       }
     }
-  }    
+  }
+  /* We dump the conncheck list when something interesting happened, ie
+   * when we unfroze some pairs.
+   */
+  if (result)
+    priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
+}
+
+guint
+conn_check_stun_transactions_count (NiceAgent *agent)
+{
+  GSList *i, *j;
+  guint count = 0;
+
+  for (i = agent->streams; i ; i = i->next) {
+    NiceStream *s = i->data;
+    for (j = s->conncheck_list; j ; j = j->next) {
+      CandidateCheckPair *p = j->data;
+
+      if (p->stun_transactions)
+        count += g_slist_length (p->stun_transactions);
+    }
+  }
+  return count;
 }
 
 /*
@@ -650,6 +621,8 @@ priv_remove_stun_transaction (CandidateCheckPair *pair,
   priv_forget_stun_transaction (stun, component);
   pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun);
   priv_free_stun_transaction (stun);
+  if (pair->stun_transactions == NULL)
+    pair->retransmit = FALSE;
 }
 
 /*
@@ -668,6 +641,7 @@ priv_free_all_stun_transactions (CandidateCheckPair *pair,
     g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component);
   g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction);
   pair->stun_transactions = NULL;
+  pair->retransmit = FALSE;
 }
 
 static void
@@ -684,26 +658,26 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP
  * Helper function for connectivity check timer callback that
  * runs through the stream specific part of the state machine. 
  *
- * @param schedule if TRUE, schedule a new check
- *
- * @return will return FALSE when no more pending timers.
+ * @param agent context pointer
+ * @param stream which stream (of the agent)
+ * @return will return TRUE if a new stun request has been sent
  */
-static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent)
+static gboolean
+priv_conn_check_tick_stream (NiceAgent *agent, NiceStream *stream)
 {
-  gboolean keep_timer_going = FALSE;
+  gboolean pair_failed = FALSE;
   GSList *i, *j;
-  CandidateCheckPair *pair;
   unsigned int timeout;
-  GTimeVal now;
+  gint64 now;
 
-  g_get_current_time (&now);
+  now = g_get_monotonic_time ();
 
   /* step: process ongoing STUN transactions */
   for (i = stream->conncheck_list; i ; i = i->next) {
     CandidateCheckPair *p = i->data;
     gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN];
     NiceComponent *component;
-    StunTransaction *stun;
+    guint index = 0, remaining = 0;
 
     if (p->stun_transactions == NULL)
       continue;
@@ -712,180 +686,195 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen
         NULL, &component))
       continue;
 
-    /* The first stun transaction of the list may eventually be
-     * retransmitted, other stun transactions just have their
-     * timer updated.
-     */
-
-    j = p->stun_transactions->next;
-
-    /* process all stun transactions except the first one */
+    j = p->stun_transactions;
     while (j) {
-      StunTransaction *s = j->data;
+      StunTransaction *stun = j->data;
       GSList *next = j->next;
 
-      if (priv_timer_expired (&s->next_tick, &now))
-        switch (stun_timer_refresh (&s->timer)) {
+      if (now < stun->next_tick)
+        remaining++;
+      else
+        switch (stun_timer_refresh (&stun->timer)) {
           case STUN_USAGE_TIMER_RETURN_TIMEOUT:
-            priv_remove_stun_transaction (p, s, component);
+timer_return_timeout:
+            priv_remove_stun_transaction (p, stun, component);
             break;
           case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
-            timeout = stun_timer_remainder (&s->timer);
-            s->next_tick = now;
-            g_time_val_add (&s->next_tick, timeout * 1000);
-            break;
-          default:
-            break;
-      }
-      j = next;
-    }
+            /* case: retransmission stopped, due to the nomination of
+             * a pair with a higher priority than this in-progress pair,
+             * ICE spec, sect 8.1.2 "Updating States", item 2.2
+             */
+            if (!p->retransmit || index > 0)
+              goto timer_return_timeout;
 
-    if (p->state != NICE_CHECK_IN_PROGRESS)
-      continue;
+            /* case: not ready, so schedule a new timeout */
+            timeout = stun_timer_remainder (&stun->timer);
 
-    /* process the first stun transaction of the list */
-    stun = p->stun_transactions->data;
-    if (!priv_timer_expired (&stun->next_tick, &now))
-      continue;
+            nice_debug ("Agent %p :STUN transaction retransmitted on pair %p "
+                "(timer=%d/%d %d/%dms).",
+                agent, p,
+                stun->timer.retransmissions, stun->timer.max_retransmissions,
+                stun->timer.delay - timeout, stun->timer.delay);
 
-    switch (stun_timer_refresh (&stun->timer)) {
-      case STUN_USAGE_TIMER_RETURN_TIMEOUT:
-timer_return_timeout:
-        /* case: error, abort processing */
-        nice_address_to_string (&p->local->addr, tmpbuf1);
-        nice_address_to_string (&p->remote->addr, tmpbuf2);
-        nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
-            agent, p);
-        nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent,
-            tmpbuf1, nice_address_get_port (&p->local->addr),
-            tmpbuf2, nice_address_get_port (&p->remote->addr));
-        candidate_check_pair_fail (stream, agent, p);
-        priv_print_conn_check_lists (agent, G_STRFUNC,
-            ", retransmission failed");
-
-        /* perform a check if a transition state from connected to
-         * ready can be performed. This may happen here, when the last
-         * in-progress pair has expired its retransmission count
-         * in priv_conn_check_tick_stream(), which is a condition to
-         * make the transition connected to ready.
-         */
-        priv_update_check_list_state_for_ready (agent, stream, component);
-        break;
-      case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
-        /* case: retransmission stopped, due to the nomination of
-         * a pair with a higher priority than this in-progress pair,
-         * ICE spec, sect 8.1.2 "Updating States", item 2.2
-         */
-        if (!p->retransmit)
-          goto timer_return_timeout;
+            agent_socket_send (p->sockptr, &p->remote->addr,
+                stun_message_length (&stun->message),
+                (gchar *)stun->buffer);
 
-        /* case: not ready, so schedule a new timeout */
-        timeout = stun_timer_remainder (&stun->timer);
+            /* note: convert from milli to microseconds for g_time_val_add() */
+            stun->next_tick = now + timeout * 1000;
 
-        nice_debug ("Agent %p :STUN transaction retransmitted on pair %p "
-            "(timer=%d/%d %d/%dms).",
-            agent, p,
-            stun->timer.retransmissions, stun->timer.max_retransmissions,
-            stun->timer.delay - timeout, stun->timer.delay);
+            return TRUE;
+          case STUN_USAGE_TIMER_RETURN_SUCCESS:
+            timeout = stun_timer_remainder (&stun->timer);
+            /* note: convert from milli to microseconds for g_time_val_add() */
+            stun->next_tick = now + timeout * 1000;
+            remaining++;
+            break;
+          default:
+            g_assert_not_reached();
+            break;
+        }
+      j = next;
+      index++;
+    }
 
-        agent_socket_send (p->sockptr, &p->remote->addr,
-            stun_message_length (&stun->message),
-            (gchar *)stun->buffer);
+    if (remaining == 0) {
+      nice_address_to_string (&p->local->addr, tmpbuf1);
+      nice_address_to_string (&p->remote->addr, tmpbuf2);
+      nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
+          agent, p);
+      nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent,
+          tmpbuf1, nice_address_get_port (&p->local->addr),
+          tmpbuf2, nice_address_get_port (&p->remote->addr));
+      candidate_check_pair_fail (stream, agent, p);
+      pair_failed = TRUE;
 
-        /* note: convert from milli to microseconds for g_time_val_add() */
-        stun->next_tick = now;
-        g_time_val_add (&stun->next_tick, timeout * 1000);
+      /* perform a check if a transition state from connected to
+       * ready can be performed. This may happen here, when the last
+       * in-progress pair has expired its retransmission count
+       * in priv_conn_check_tick_stream(), which is a condition to
+       * make the transition connected to ready.
+       */
+      conn_check_update_check_list_state_for_ready (agent, stream, component);
+    }
+  }
 
-        return TRUE;
-      case STUN_USAGE_TIMER_RETURN_SUCCESS:
-        timeout = stun_timer_remainder (&stun->timer);
+  if (pair_failed)
+    priv_print_conn_check_lists (agent, G_STRFUNC, ", retransmission failed");
 
-        /* note: convert from milli to microseconds for g_time_val_add() */
-        stun->next_tick = now;
-        g_time_val_add (&stun->next_tick, timeout * 1000);
+  return FALSE;
+}
 
-        keep_timer_going = TRUE;
-        break;
-      default:
-        /* Nothing to do. */
-        break;
-    }
-  }
+static gboolean
+priv_conn_check_ordinary_check (NiceAgent *agent, NiceStream *stream)
+{
+  CandidateCheckPair *pair;
+  gboolean stun_sent = FALSE;
 
-  /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks"
+  /* step: perform an ordinary check, sec 6.1.4.2 point 3. (Performing
+   * Connectivity Checks) of ICE spec (RFC8445)
    * note: This code is executed when the triggered checks list is
    * empty, and when no STUN message has been sent (pacing constraint)
    */
   pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
+  if (pair == NULL) {
+    /* step: there is no candidate in waiting state, try to unfreeze
+     * some pairs and retry, sect 6.1.4.2 point 2. (Performing Connectivity
+     * Checks) of ICE spec (RFC8445)
+     */
+    priv_conn_check_unfreeze_next (agent);
+    pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
+  }
+
   if (pair) {
+    stun_sent = priv_conn_check_initiate (agent, pair);
     priv_print_conn_check_lists (agent, G_STRFUNC,
-        ", got a pair in Waiting state");
-    priv_conn_check_initiate (agent, pair);
-    return TRUE;
+        ", initiated an ordinary connection check");
   }
+  return stun_sent;
+}
 
-  /* note: this is unclear in the ICE spec, but a check list in Frozen
-   * state (where all pairs are in Frozen state) is not supposed to
-   * change its state by an ordinary check, but only by the success of
-   * checks in other check lists, in priv_conn_check_unfreeze_related().
-   * The underlying idea is to concentrate the checks on a single check
-   * list initially.
-   */
-  if (priv_is_checklist_frozen (stream))
-    return keep_timer_going;
+static gboolean
+priv_conn_check_triggered_check (NiceAgent *agent, NiceStream *stream)
+{
+  CandidateCheckPair *pair;
+  gboolean stun_sent = FALSE;
 
-  /* step: ordinary check continued, if there's no pair in the waiting
-   * state, pick a pair in the frozen state
+  /* step: perform a test from the triggered checks list,
+   * sect 6.1.4.2 point 1. (Performing Connectivity Checks) of ICE
+   * spec (RFC8445)
    */
-  pair = priv_conn_check_find_next_frozen (stream->conncheck_list);
+  pair = priv_get_pair_from_triggered_check_queue (agent);
+
   if (pair) {
+    stun_sent = priv_conn_check_initiate (agent, pair);
     priv_print_conn_check_lists (agent, G_STRFUNC,
-        ", got a pair in Frozen state");
-    SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING);
-    priv_conn_check_initiate (agent, pair);
-    return TRUE;
+        ", initiated a connection check from triggered check list");
   }
-  return keep_timer_going;
+  return stun_sent;
 }
 
+
 static gboolean
-priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent)
+priv_conn_check_tick_stream_nominate (NiceAgent *agent, NiceStream *stream)
 {
   gboolean keep_timer_going = FALSE;
+  /* s_xxx counters are stream-wide */
   guint s_inprogress = 0;
   guint s_succeeded = 0;
   guint s_discovered = 0;
   guint s_nominated = 0;
   guint s_waiting_for_nomination = 0;
   guint s_valid = 0;
-  guint frozen = 0;
-  guint waiting = 0;
-  GSList *i, *k;
+  guint s_frozen = 0;
+  guint s_waiting = 0;
+  CandidateCheckPair *other_stream_pair = NULL;
+  GSList *i, *j;
+
+  /* Search for a nominated pair (or selected to be nominated pair)
+   * from another stream.
+   */
+  for (i = agent->streams; i ; i = i->next) {
+    NiceStream *s = i->data;
+    if (s->id == stream->id)
+      continue;
+    for (j = s->conncheck_list; j ; j = j->next) {
+      CandidateCheckPair *p = j->data;
+      if (p->nominated || (p->use_candidate_on_next_check &&
+          p->state != NICE_CHECK_FAILED)) {
+        other_stream_pair = p;
+        break;
+      }
+    }
+    if (other_stream_pair)
+      break;
+  }
 
+  /* we compute some stream-wide counter values */
   for (i = stream->conncheck_list; i ; i = i->next) {
     CandidateCheckPair *p = i->data;
     if (p->state == NICE_CHECK_FROZEN)
-      ++frozen;
+      s_frozen++;
     else if (p->state == NICE_CHECK_IN_PROGRESS)
-      ++s_inprogress;
+      s_inprogress++;
     else if (p->state == NICE_CHECK_WAITING)
-      ++waiting;
+      s_waiting++;
     else if (p->state == NICE_CHECK_SUCCEEDED)
-      ++s_succeeded;
+      s_succeeded++;
     else if (p->state == NICE_CHECK_DISCOVERED)
-      ++s_discovered;
+      s_discovered++;
     if (p->valid)
-      ++s_valid;
+      s_valid++;
 
     if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
         && p->nominated)
-      ++s_nominated;
+      s_nominated++;
     else if ((p->state == NICE_CHECK_SUCCEEDED ||
             p->state == NICE_CHECK_DISCOVERED) && !p->nominated)
-      ++s_waiting_for_nomination;
+      s_waiting_for_nomination++;
   }
 
-    /* note: keep the timer going as long as there is work to be done */
+  /* note: keep the timer going as long as there is work to be done */
   if (s_inprogress)
     keep_timer_going = TRUE;
   
@@ -906,25 +895,32 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent)
          * and limiting the time spent waiting for in-progress connections
          * checks until they finally fail.
          */
-        GSList *component_item;
-
-        for (component_item = stream->components; component_item;
-             component_item = component_item->next) {
-          NiceComponent *component = component_item->data;
+        for (i = stream->components; i; i = i->next) {
+          NiceComponent *component = i->data;
+          CandidateCheckPair *other_component_pair = NULL;
+          CandidateCheckPair *this_component_pair = NULL;
+          NiceCandidate *lcand1 = NULL;
+          NiceCandidate *rcand1 = NULL;
+          NiceCandidate *lcand2, *rcand2;
           gboolean already_done = FALSE;
-          gboolean stopping_criterion = FALSE;
+          gboolean found_other_component_pair = FALSE;
+          gboolean found_other_stream_pair = FALSE;
+          gboolean first_nomination = FALSE;
+          gboolean stopping_criterion;
+          /* p_xxx counters are component-wide */
           guint p_valid = 0;
           guint p_frozen = 0;
           guint p_waiting = 0;
           guint p_inprogress = 0;
           guint p_host_host_valid = 0;
 
-          /* verify that the choice of the pair to be nominated
-           * has not already been done
-           */
-          for (k = stream->conncheck_list; k ; k = k->next) {
-            CandidateCheckPair *p = k->data;
+          /* we compute some component-wide counter values */
+          for (j = stream->conncheck_list; j ; j = j->next) {
+            CandidateCheckPair *p = j->data;
             if (p->component_id == component->id) {
+              /* verify that the choice of the pair to be nominated
+               * has not already been done
+               */
               if (p->use_candidate_on_next_check)
                 already_done = TRUE;
               if (p->state == NICE_CHECK_FROZEN)
@@ -945,20 +941,39 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent)
           if (already_done)
             continue;
 
-          stopping_criterion =
-              (p_host_host_valid > 0 ||
-               p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS ||
-               (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0));
-
-          if (!stopping_criterion)
-            continue;
+          /* Search for a nominated pair (or selected to be nominated pair)
+           * from another component of this stream.
+           */
+          for (j = stream->conncheck_list; j ; j = j->next) {
+            CandidateCheckPair *p = j->data;
+            if (p->component_id == component->id)
+              continue;
+            if (p->nominated || (p->use_candidate_on_next_check &&
+                p->state != NICE_CHECK_FAILED)) {
+              other_component_pair = p;
+              break;
+            }
+          }
 
-          /* when the stopping criterion is satisfied, we choose
-           * a pair to be nominated in the list of valid pairs,
-           * and add it to the triggered checks list
+          if (other_stream_pair == NULL && other_component_pair == NULL)
+            first_nomination = TRUE;
+
+          /* We choose a pair to be nominated in the list of valid
+           * pairs.
+           *
+           * this pair will be the one with the highest priority,
+           * when we don't have other nominated pairs in other
+           * components and in other streams
+           *
+           * this pair will be a pair compatible with another nominated
+           * pair from another component if we found one.
+           *
+           * else this pair will be a pair compatible with another
+           * nominated pair from another stream if we found one.
+           *
            */
-          for (k = stream->conncheck_list; k ; k = k->next) {
-            CandidateCheckPair *p = k->data;
+          for (j = stream->conncheck_list; j ; j = j->next) {
+            CandidateCheckPair *p = j->data;
             /* note: highest priority item selected (list always sorted) */
             if (p->component_id == component->id &&
                 !p->nominated &&
@@ -970,29 +985,143 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent)
                * to test its parent pair instead.
                */
               if (p->succeeded_pair != NULL) {
-                g_assert (p->state == NICE_CHECK_DISCOVERED);
+                g_assert_cmpint (p->state, ==, NICE_CHECK_DISCOVERED);
                 p = p->succeeded_pair;
               }
-              g_assert (p->state == NICE_CHECK_SUCCEEDED);
-              nice_debug ("Agent %p : restarting check of pair %p with "
-                  "USE-CANDIDATE attrib (regular nomination)", agent, p);
-              p->use_candidate_on_next_check = TRUE;
-              priv_add_pair_to_triggered_check_queue (agent, p);
-              keep_timer_going = TRUE;
-              break; /* move to the next component */
+              g_assert_cmpint (p->state, ==, NICE_CHECK_SUCCEEDED);
+
+              if (this_component_pair == NULL)
+                /* highest priority pair */
+                this_component_pair = p;
+
+              lcand1 = p->local;
+              rcand1 = p->remote;
+
+              if (first_nomination)
+                /* use the highest priority pair */
+                break;
+
+              if (other_component_pair) {
+                lcand2 = other_component_pair->local;
+                rcand2 = other_component_pair->remote;
+              }
+              if (other_component_pair &&
+                  lcand1->transport == lcand2->transport &&
+                  nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
+                  nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
+                /* else continue the research with lower priority
+                 * pairs, compatible with a nominated pair of
+                 * another component
+                 */
+                this_component_pair = p;
+                found_other_component_pair = TRUE;
+                break;
+              }
+
+              if (other_stream_pair) {
+                lcand2 = other_stream_pair->local;
+                rcand2 = other_stream_pair->remote;
+              }
+              if (other_stream_pair &&
+                  other_component_pair == NULL &&
+                  lcand1->transport == lcand2->transport &&
+                  nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
+                  nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
+                /* else continue the research with lower priority
+                 * pairs, compatible with a nominated pair of
+                 * another stream
+                 */
+                this_component_pair = p;
+                found_other_stream_pair = TRUE;
+                break;
+              }
             }
           }
+
+          /* No valid pair for this component */
+          if (this_component_pair == NULL)
+            continue;
+
+          /* The stopping criterion tries to select a set of pairs of
+           * the same kind (transport/type) for all components of a
+           * stream, and for all streams, when possible (see last
+           * paragraph).
+           *
+           * When no stream has nominated a pair yet, we apply the
+           * following criterion :
+           *   - stop if we have a valid host-host pair
+           *   - or stop if we have at least "some* (2 in the current
+           *     implementation) valid pairs, and select the best one
+           *   - or stop if the conncheck cannot evolve more
+           *
+           * Else when the stream has a nominated pair in another
+           * component we apply this criterion:
+           *   - stop if we have a valid pair of the same kind than this
+           *     other nominated pair.
+           *   - or stop if the conncheck cannot evolve more
+           *
+           * Else when another stream has a nominated pair we apply the
+           * following criterion:
+           *   - stop if we have a valid pair of the same kind than the
+           *     other nominated pair.
+           *   - or stop if the conncheck cannot evolve more
+           *
+           * When no further evolution of the conncheck is possible, we
+           * prefer to select the best valid pair we have, *even* if it
+           * is not compatible with the transport of another stream of
+           * component. We think it's still a better choice than marking
+           * this component 'failed'.
+           */
+          stopping_criterion = FALSE;
+          if (first_nomination && p_host_host_valid > 0) {
+            stopping_criterion = TRUE;
+            nice_debug ("Agent %p : stopping criterion: "
+                "valid host-host pair", agent);
+          } else if (first_nomination &&
+              p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS) {
+            stopping_criterion = TRUE;
+            nice_debug ("Agent %p : stopping criterion: "
+                "*some* valid pairs", agent);
+          } else if (found_other_component_pair) {
+            stopping_criterion = TRUE;
+            nice_debug ("Agent %p : stopping criterion: "
+                "matching pair in another component", agent);
+          } else if (found_other_stream_pair) {
+            stopping_criterion = TRUE;
+            nice_debug ("Agent %p : stopping criterion: "
+                "matching pair in another stream", agent);
+          } else if (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0) {
+            stopping_criterion = TRUE;
+            nice_debug ("Agent %p : stopping criterion: "
+                "no more pairs to check", agent);
+          }
+
+          if (!stopping_criterion)
+            continue;
+
+          /* when the stopping criterion is reached, we add the
+           * selected pair for this component to the triggered checks
+           * list
+           */
+          nice_debug ("Agent %p : restarting check of %s:%s pair %p with "
+              "USE-CANDIDATE attrib (regular nomination) for "
+              "stream %d component %d", agent,
+              priv_candidate_transport_to_string (
+                  this_component_pair->local->transport),
+              priv_candidate_transport_to_string (
+                  this_component_pair->remote->transport),
+              this_component_pair, stream->id, component->id);
+          this_component_pair->use_candidate_on_next_check = TRUE;
+          priv_add_pair_to_triggered_check_queue (agent, this_component_pair);
+          keep_timer_going = TRUE;
         }
       }
     } else if (agent->controlling_mode) {
-      GSList *component_item;
-
-      for (component_item = stream->components; component_item;
-           component_item = component_item->next) {
-        NiceComponent *component = component_item->data;
+      for (i = stream->components; i; i = i->next) {
+        NiceComponent *component = i->data;
 
-       for (k = stream->conncheck_list; k ; k = k->next) {
-         CandidateCheckPair *p = k->data;
+       for (j = stream->conncheck_list; j ; j = j->next) {
+         CandidateCheckPair *p = j->data;
          /* note: highest priority item selected (list always sorted) */
          if (p->component_id == component->id &&
               (p->state == NICE_CHECK_SUCCEEDED ||
@@ -1000,7 +1129,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent)
            nice_debug ("Agent %p : restarting check of pair %p as the "
                 "nominated pair.", agent, p);
            p->nominated = TRUE;
-            priv_update_selected_pair (agent, component, p);
+            conn_check_update_selected_pair (agent, component, p);
             priv_add_pair_to_triggered_check_queue (agent, p);
             keep_timer_going = TRUE;
            break; /* move to the next component */
@@ -1010,11 +1139,12 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent)
     }
   }
   if (stream->tick_counter++ % 50 == 0)
-    nice_debug ("Agent %p : stream %u: timer tick #%u: %u frozen, %u in-progress, "
-        "%u waiting, %u succeeded, %u discovered, %u nominated, "
-        "%u waiting-for-nom, %u valid.", agent, stream->id,
-        stream->tick_counter, frozen, s_inprogress, waiting, s_succeeded,
-        s_discovered, s_nominated, s_waiting_for_nomination, s_valid);
+    nice_debug ("Agent %p : stream %u: timer tick #%u: %u frozen, "
+        "%u in-progress, %u waiting, %u succeeded, %u discovered, "
+        "%u nominated, %u waiting-for-nom, %u valid",
+        agent, stream->id, stream->tick_counter,
+        s_frozen, s_inprogress, s_waiting, s_succeeded, s_discovered,
+        s_nominated, s_waiting_for_nomination, s_valid);
 
   return keep_timer_going;
 
@@ -1029,7 +1159,7 @@ conn_check_stop (NiceAgent *agent)
   g_source_destroy (agent->conncheck_timer_source);
   g_source_unref (agent->conncheck_timer_source);
   agent->conncheck_timer_source = NULL;
-  agent->conncheck_timer_grace_period = 0;
+  agent->conncheck_ongoing_idle_delay = 0;
 }
 
 
@@ -1044,97 +1174,72 @@ conn_check_stop (NiceAgent *agent)
 static gboolean priv_conn_check_tick_agent_locked (NiceAgent *agent,
     gpointer user_data)
 {
-  CandidateCheckPair *pair = NULL;
   gboolean keep_timer_going = FALSE;
+  gboolean stun_sent = FALSE;
   GSList *i, *j;
 
-  /* configure the initial state of the check lists of the agent
-   * as described in ICE spec, 5.7.4
+  /* step: process triggered checks
+   * these steps are ordered by priority, since a single stun request
+   * is sent per callback, we process the important steps first.
    *
-   * if all pairs in all check lists are in frozen state, then
-   * we are in the initial state (5.7.4, point 1.)
+   * perform a single stun request per timer callback,
+   * to respect stun pacing
    */
-  if (priv_number_of_active_check_lists (agent) == 0) {
-    /* set some pairs of the first stream in the waiting state
-     * ICE spec, 5.7.4, point 2.
-     *
-     * note: we adapt the ICE spec here, by selecting the first
-     * frozen check list, which is not necessarily the check
-     * list of the first stream (the first stream may be completed)
-     */
-    NiceStream *stream = priv_find_first_frozen_check_list (agent);
-    if (stream)
-      priv_conn_check_unfreeze_next (agent, stream);
+  for (i = agent->streams; i && !stun_sent; i = i->next) {
+    NiceStream *stream = i->data;
+
+    stun_sent = priv_conn_check_triggered_check (agent, stream);
   }
 
-  /* step: perform a test from the triggered checks list,
-   * ICE spec, 5.8 "Scheduling Checks"
-   */
-  pair = priv_get_pair_from_triggered_check_queue (agent);
+  /* step: process ongoing STUN transactions */
+  for (i = agent->streams; i && !stun_sent; i = i->next) {
+    NiceStream *stream = i->data;
 
-  if (pair) {
-    priv_print_conn_check_lists (agent, G_STRFUNC,
-        ", got a pair from triggered check list");
-    if (conn_check_send (agent, pair)) {
-      SET_PAIR_STATE (agent, pair, NICE_CHECK_FAILED);
-      return FALSE;
-    }
-    return TRUE;
+    stun_sent = priv_conn_check_tick_stream (agent, stream);
   }
 
-  /* step: process ongoing STUN transactions and
-   * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks"
-   */
-  for (i = agent->streams; i ; i = i->next) {
+  /* step: process ordinary checks */
+  for (i = agent->streams; i && !stun_sent; i = i->next) {
     NiceStream *stream = i->data;
-    if (priv_conn_check_tick_stream (stream, agent))
-      keep_timer_going = TRUE;
-    if (priv_conn_check_tick_stream_nominate (stream, agent))
-      keep_timer_going = TRUE;
+
+    stun_sent = priv_conn_check_ordinary_check (agent, stream);
   }
 
-  /* step: if no work left and a conncheck list of a stream is still
-   * frozen, set the pairs to waiting, according to ICE SPEC, sect
-   * 7.1.3.3.  "Check List and Timer State Updates"
+  if (stun_sent)
+    keep_timer_going = TRUE;
+
+  /* step: try to nominate a pair
    */
-  if (!keep_timer_going) {
-    for (i = agent->streams; i ; i = i->next) {
-      NiceStream *stream = i->data;
-      if (priv_is_checklist_frozen (stream)) {
-        nice_debug ("Agent %p : stream %d conncheck list is still "
-            "frozen, while other lists are completed. Unfreeze it.",
-            agent, stream->id);
-        keep_timer_going = priv_conn_check_unfreeze_next (agent, stream);
-      }
-      if (!keep_timer_going && !stream->peer_gathering_done) {
-        keep_timer_going = TRUE;
-      }
-    }
+  for (i = agent->streams; i; i = i->next) {
+    NiceStream *stream = i->data;
+
+    if (priv_conn_check_tick_stream_nominate (agent, stream))
+      keep_timer_going = TRUE;
   }
 
   /* note: we provide a grace period before declaring a component as
    * failed. Components marked connected, and then ready follow another
    * code path, and are not concerned by this grace period.
    */
-  if (!keep_timer_going && agent->conncheck_timer_grace_period == 0)
+  if (!keep_timer_going && agent->conncheck_ongoing_idle_delay == 0)
     nice_debug ("Agent %p : waiting %d msecs before checking "
-        "for failed components.", agent, NICE_AGENT_MAX_TIMER_GRACE_PERIOD);
+        "for failed components.", agent, agent->idle_timeout);
 
   if (keep_timer_going)
-    agent->conncheck_timer_grace_period = 0;
+    agent->conncheck_ongoing_idle_delay = 0;
   else
-    agent->conncheck_timer_grace_period += agent->timer_ta;
+    agent->conncheck_ongoing_idle_delay += agent->timer_ta;
 
   /* step: stop timer if no work left */
   if (!keep_timer_going &&
-      agent->conncheck_timer_grace_period >= NICE_AGENT_MAX_TIMER_GRACE_PERIOD) {
+      agent->conncheck_ongoing_idle_delay >= agent->idle_timeout) {
     nice_debug ("Agent %p : checking for failed components now.", agent);
     for (i = agent->streams; i; i = i->next) {
       NiceStream *stream = i->data;
       priv_update_check_list_failed_components (agent, stream);
       for (j = stream->components; j; j = j->next) {
         NiceComponent *component = j->data;
-        priv_update_check_list_state_for_ready (agent, stream, component);
+        conn_check_update_check_list_state_for_ready (agent, stream, component);
       }
     }
 
@@ -1204,11 +1309,8 @@ static gboolean priv_conn_keepalive_retransmissions_tick_agent_locked (
 
       nice_debug ("Agent %p : Retransmitting keepalive conncheck",
           agent);
-      agent_timeout_add_with_context (agent,
-          &pair->keepalive.tick_source,
-          "Pair keepalive", stun_timer_remainder (&pair->keepalive.timer),
-          priv_conn_keepalive_retransmissions_tick_agent_locked, pair);
-      break;
+
+      G_GNUC_FALLTHROUGH;
     case STUN_USAGE_TIMER_RETURN_SUCCESS:
       agent_timeout_add_with_context (agent,
           &pair->keepalive.tick_source,
@@ -1250,6 +1352,26 @@ static guint32 peer_reflexive_candidate_priority (NiceAgent *agent,
   return priority;
 }
 
+/* Returns the priority of a local candidate of type peer-reflexive that
+ * would be learned as a consequence of a check from this local
+ * candidate. See RFC 5245, section 7.1.2.1. "PRIORITY and USE-CANDIDATE".
+ * RFC 5245 is more explanatory than RFC 8445 on this detail.
+ *
+ * Apply to local candidates of type host only, because candidates of type
+ * relay are supposed to have a public IP address, that wont generate
+ * a peer-reflexive address. Server-reflexive candidates are not
+ * concerned too, because no STUN request is sent with a local candidate
+ * of this type.
+ */
+static guint32 stun_request_priority (NiceAgent *agent,
+    NiceCandidate *local_candidate)
+{
+  if (local_candidate->type == NICE_CANDIDATE_TYPE_HOST)
+    return peer_reflexive_candidate_priority (agent, local_candidate);
+  else
+    return local_candidate->priority;
+}
+
 static void ms_ice2_legacy_conncheck_send(StunMessage *msg, NiceSocket *sock,
     const NiceAddress *remote_addr)
 {
@@ -1298,11 +1420,19 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
 {
   GSList *i, *j, *k;
   int errors = 0;
-  gboolean ret = FALSE;
   size_t buf_len = 0;
+  guint64 now;
+  guint64 min_next_tick;
+  guint64 next_timer_tick;
+
+  now = g_get_monotonic_time ();
+  min_next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
 
   /* case 1: session established and media flowing
-   *         (ref ICE sect 10 "Keepalives" ID-19)  */
+   *         (ref ICE sect 11 "Keepalives" RFC-8445)
+   * TODO: keepalives should be send only when no packet has been sent
+   * on that pair in the last Tr seconds, and not unconditionally.
+   */
   for (i = agent->streams; i; i = i->next) {
 
     NiceStream *stream = i->data;
@@ -1316,6 +1446,13 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
             !agent->keepalive_conncheck)
           continue;
 
+        if (p->keepalive.next_tick) {
+          if (p->keepalive.next_tick < min_next_tick)
+            min_next_tick = p->keepalive.next_tick;
+          if (now < p->keepalive.next_tick)
+            continue;
+        }
+
         if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
             agent->keepalive_conncheck) {
           uint8_t uname[NICE_STREAM_MAX_UNAME];
@@ -1339,11 +1476,11 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
             nice_address_to_string (&p->remote->addr, tmpbuf);
             nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
                 "(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), "
-                "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%u.", agent,
-                tmpbuf, nice_address_get_port (&p->remote->addr),
+                "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%08x.",
+                agent, tmpbuf, nice_address_get_port (&p->remote->addr),
                 component->id, (int) uname_len, uname, uname_len,
                 (int) password_len, password, password_len,
-                p->prflx_priority);
+                p->stun_priority);
           }
           if (uname_len > 0) {
             buf_len = stun_usage_ice_conncheck_create (&component->stun_agent,
@@ -1351,7 +1488,7 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
                 sizeof(p->keepalive.stun_buffer),
                 uname, uname_len, password, password_len,
                 agent->controlling_mode, agent->controlling_mode,
-                p->prflx_priority,
+                p->stun_priority,
                 agent->tie_breaker,
                 NULL,
                 agent_to_ice_compatibility (agent));
@@ -1372,11 +1509,15 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
 
               p->keepalive.stream_id = stream->id;
               p->keepalive.component_id = component->id;
+              p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
 
               agent_timeout_add_with_context (agent,
                   &p->keepalive.tick_source, "Pair keepalive",
                   stun_timer_remainder (&p->keepalive.timer),
                   priv_conn_keepalive_retransmissions_tick_agent_locked, p);
+
+              next_timer_tick = now + agent->timer_ta * 1000;
+              goto done;
             } else {
               ++errors;
             }
@@ -1390,13 +1531,24 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
             agent_socket_send (p->local->sockptr, &p->remote->addr, buf_len,
                 (gchar *)p->keepalive.stun_buffer);
 
+            p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
+
             if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
               ms_ice2_legacy_conncheck_send (&p->keepalive.stun_message,
                   p->local->sockptr, &p->remote->addr);
             }
 
-            nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.",
-                agent, p, (int) buf_len);
+            if (nice_debug_is_enabled ()) {
+              gchar tmpbuf[INET6_ADDRSTRLEN];
+              nice_address_to_string (&p->local->base_addr, tmpbuf);
+              nice_debug ("Agent %p : resending STUN to keep the "
+                  "selected base address %s:%u alive in s%d/c%d.", agent,
+                  tmpbuf, nice_address_get_port (&p->local->base_addr),
+                  stream->id, component->id);
+            }
+
+            next_timer_tick = now + agent->timer_ta * 1000;
+            goto done;
           } else {
             ++errors;
           }
@@ -1406,12 +1558,13 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
   }
 
   /* case 2: connectivity establishment ongoing
-   *         (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19)  */
+   *         (ref ICE sect 5.1.1.4 "Keeping Candidates Alive" RFC-8445)
+   */
   for (i = agent->streams; i; i = i->next) {
     NiceStream *stream = i->data;
     for (j = stream->components; j; j = j->next) {
       NiceComponent *component = j->data;
-      if (component->state < NICE_COMPONENT_STATE_READY &&
+      if (component->state < NICE_COMPONENT_STATE_CONNECTED &&
           agent->stun_server_ip) {
         NiceAddress stun_server;
         if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
@@ -1433,11 +1586,29 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
                 candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP &&
                 nice_address_ip_version (&candidate->addr) ==
                 nice_address_ip_version (&stun_server)) {
+
+              if (candidate->keepalive_next_tick) {
+                if (candidate->keepalive_next_tick < min_next_tick)
+                  min_next_tick = candidate->keepalive_next_tick;
+                if (now < candidate->keepalive_next_tick)
+                continue;
+              }
+
               /* send the conncheck */
-              nice_debug ("Agent %p : resending STUN on %s to keep the "
-                  "candidate alive.", agent, candidate->foundation);
+              if (nice_debug_is_enabled ()) {
+                gchar tmpbuf[INET6_ADDRSTRLEN];
+                nice_address_to_string (&candidate->addr, tmpbuf);
+                nice_debug ("Agent %p : resending STUN to keep the local "
+                    "candidate %s:%u alive in s%d/c%d.", agent,
+                    tmpbuf, nice_address_get_port (&candidate->addr),
+                    stream->id, component->id);
+              }
               agent_socket_send (candidate->sockptr, &stun_server,
                   buffer_len, (gchar *)stun_buffer);
+              candidate->keepalive_next_tick = now +
+                  1000 * NICE_AGENT_TIMER_TR_DEFAULT;
+              next_timer_tick = now + agent->timer_ta * 1000;
+              goto done;
             }
           }
         }
@@ -1445,15 +1616,23 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
     }
   }
 
+  next_timer_tick = min_next_tick;
+
+  done:
   if (errors) {
     nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
-    goto done;
+    return FALSE;
   }
 
-  ret = TRUE;
-
- done:
-  return ret;
+  if (agent->keepalive_timer_source) {
+    g_source_destroy (agent->keepalive_timer_source);
+    g_source_unref (agent->keepalive_timer_source);
+    agent->keepalive_timer_source = NULL;
+  }
+  agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
+      "Connectivity keepalive timeout", (next_timer_tick - now)/ 1000,
+      priv_conn_keepalive_tick_agent_locked, NULL);
+  return TRUE;
 }
 
 static gboolean priv_conn_keepalive_tick_agent_locked (NiceAgent *agent,
@@ -1500,10 +1679,7 @@ static gboolean priv_turn_allocate_refresh_retransmissions_tick_agent_locked (
       agent_socket_send (cand->nicesock, &cand->server,
           stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
 
-      agent_timeout_add_with_context (agent, &cand->tick_source,
-          "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
-          priv_turn_allocate_refresh_retransmissions_tick_agent_locked, cand);
-      break;
+      G_GNUC_FALLTHROUGH;
     case STUN_USAGE_TIMER_RETURN_SUCCESS:
       agent_timeout_add_with_context (agent, &cand->tick_source,
           "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
@@ -1535,8 +1711,10 @@ static void priv_turn_allocate_refresh_tick_unlocked (NiceAgent *agent,
 
   if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
       turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
-    username = g_base64_decode ((gchar *)username, &username_len);
-    password = g_base64_decode ((gchar *)password, &password_len);
+    username = cand->candidate->turn->decoded_username;
+    password = cand->candidate->turn->decoded_password;
+    username_len = cand->candidate->turn->decoded_username_len;
+    password_len = cand->candidate->turn->decoded_password_len;
   }
 
   buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
@@ -1546,12 +1724,6 @@ static void priv_turn_allocate_refresh_tick_unlocked (NiceAgent *agent,
       password, password_len,
       turn_compat);
 
-  if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
-      turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
-    g_free (username);
-    g_free (password);
-  }
-
   nice_debug ("Agent %p : Sending allocate Refresh %zd", agent,
       buffer_len);
 
@@ -1614,7 +1786,7 @@ void conn_check_schedule_next (NiceAgent *agent)
   /* step: also start the keepalive timer */
   if (agent->keepalive_timer_source == NULL) {
     agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
-        "Connectivity keepalive timeout", NICE_AGENT_TIMER_TR_DEFAULT,
+        "Connectivity keepalive timeout", agent->timer_ta,
         priv_conn_keepalive_tick_agent_locked, NULL);
   }
 }
@@ -1633,45 +1805,208 @@ gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *
   return 0;
 }
 
+/* Find a transport compatible with a given socket.
+ *
+ * Returns TRUE when a matching transport can be guessed from
+ * the type of the socket in an unambiguous way.
+ */
+static gboolean
+nice_socket_has_compatible_transport (NiceSocket *socket,
+    NiceCandidateTransport *transport)
+{
+  gboolean found = TRUE;
+
+  g_assert (socket);
+  g_assert (transport);
+
+  switch (socket->type) {
+    case NICE_SOCKET_TYPE_TCP_BSD:
+      if (nice_tcp_bsd_socket_get_passive_parent (socket))
+        *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
+      else
+        *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
+      break;
+    case NICE_SOCKET_TYPE_TCP_PASSIVE:
+      *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
+      break;
+    case NICE_SOCKET_TYPE_TCP_ACTIVE:
+      *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
+      break;
+    case NICE_SOCKET_TYPE_UDP_BSD:
+      *transport = NICE_CANDIDATE_TRANSPORT_UDP;
+      break;
+    default:
+      found = FALSE;
+  }
+
+  return found;
+}
+
+/* Test if a local socket and a local candidate are compatible. This
+ * function does supplementary tests when the address and port are not
+ * sufficient to give a unique candidate. We try to avoid comparing
+ * directly the sockptr value, when possible, to rely on objective
+ * properties of the candidate and the socket instead, and we also
+ * choose to ignore the conncheck list for the same reason.
+ */
+static gboolean
+local_candidate_and_socket_compatible (NiceAgent *agent,
+    NiceCandidate *lcand, NiceSocket *socket)
+{
+  gboolean ret = TRUE;
+  NiceCandidateTransport transport;
+
+  g_assert (socket);
+  g_assert (lcand);
+
+  if (nice_socket_has_compatible_transport (socket, &transport)) {
+    ret = (lcand->transport == transport);
+    /* tcp-active discovered peer-reflexive local candidate, where
+     * socket is the tcp connect related socket */
+    if (ret && transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
+        nice_address_get_port (&lcand->addr) > 0)
+      ret = (lcand->sockptr == socket);
+  } else if (socket->type == NICE_SOCKET_TYPE_UDP_TURN)
+    /* Socket of type udp-turn will match a unique local candidate
+     * by its sockptr value. An an udp-turn socket doesn't carry enough
+     * information when base socket is udp-turn-over-tcp to disambiguate
+     * between a tcp-act and a tcp-pass local candidate.
+     */
+    ret = (lcand->sockptr == socket);
+
+  return ret;
+}
+
+/* Test if a local socket and a remote candidate are compatible.
+ * This function is very close to its local candidate counterpart,
+ * the difference is that we also use information from the local
+ * candidate we may have identified previously. This is needed
+ * to disambiguate the transport of the candidate with a socket
+ * of type udp-turn.
+ *
+ */
+static gboolean
+remote_candidate_and_socket_compatible (NiceAgent *agent,
+    NiceCandidate *lcand, NiceCandidate *rcand, NiceSocket *socket)
+{
+  gboolean ret = TRUE;
+  NiceCandidateTransport transport;
+
+  g_assert (socket);
+  g_assert (rcand);
+
+  if (nice_socket_has_compatible_transport (socket, &transport))
+    ret = (conn_check_match_transport (rcand->transport) == transport);
+
+  /* This supplementary test with the local candidate is needed with
+   * socket of type udp-turn, the type doesn't allow to disambiguate
+   * between a tcp-pass and a tcp-act remote candidate
+   */
+  if (lcand && ret)
+    ret = (conn_check_match_transport (lcand->transport) == rcand->transport);
+
+  return ret;
+}
+
 void
 conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream,
     NiceComponent *component)
 {
-  GSList *l, *m;
-  GList *k;
+  GList *i;
+  GSList *j;
+  NiceCandidate *lcand = NULL, *rcand = NULL;
+
+  nice_debug ("Agent %p : conn_check_remote_candidates_set %u %u",
+    agent, stream->id, component->id);
+
+  if (stream->remote_ufrag[0] == 0)
+    return;
 
-  for (k = component->incoming_checks.head; k;) {
-    IncomingCheck *icheck = k->data;
-    GList *k_next = k->next;
+  if (component->incoming_checks.head)
+    nice_debug ("Agent %p : credentials have been set, "
+      "we can process incoming checks", agent);
+
+  for (i = component->incoming_checks.head; i;) {
+    IncomingCheck *icheck = i->data;
+    GList *i_next = i->next;
+
+    nice_debug ("Agent %p : replaying icheck=%p (sock=%p)",
+        agent, icheck, icheck->local_socket);
 
     /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
      * be handled separately */
-    for (l = component->remote_candidates; l; l = l->next) {
-      NiceCandidate *rcand = l->data;
-      NiceCandidate *lcand = NULL;
-
-      if (nice_address_equal (&rcand->addr, &icheck->from)) {
-        for (m = component->local_candidates; m; m = m->next) {
-          NiceCandidate *cand = m->data;
+    for (j = component->local_candidates; j; j = j->next) {
+      NiceCandidate *cand = j->data;
+      NiceAddress *addr;
+
+      if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
+        addr = &cand->addr;
+      else
+        addr = &cand->base_addr;
+
+      if (nice_address_equal (&icheck->local_socket->addr, addr) &&
+          local_candidate_and_socket_compatible (agent, cand,
+          icheck->local_socket)) {
+        lcand = cand;
+        break;
+      }
+    }
 
-          if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) {
-            lcand = cand;
-            break;
-          }
+    if (lcand == NULL) {
+      for (j = component->local_candidates; j; j = j->next) {
+        NiceCandidate *cand = j->data;
+        NiceAddress *addr = &cand->base_addr;
+
+        /* tcp-active (not peer-reflexive discovered) local candidate, where
+         * socket is the tcp connect related socket */
+        if (nice_address_equal_no_port (&icheck->local_socket->addr, addr) &&
+            nice_address_get_port (&cand->addr) == 0 &&
+            cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
+            local_candidate_and_socket_compatible (agent, cand,
+            icheck->local_socket)) {
+          lcand = cand;
+          break;
         }
+      }
+    }
 
-        g_assert (lcand != NULL);
-        priv_schedule_triggered_check (agent, stream, component,
-            icheck->local_socket, rcand);
-        if (icheck->use_candidate)
-          priv_mark_pair_nominated (agent, stream, component,
-              lcand, rcand);
+    g_assert (lcand != NULL);
 
-        g_queue_delete_link (&component->incoming_checks, k);
+    for (j = component->remote_candidates; j; j = j->next) {
+      NiceCandidate *cand = j->data;
+      if (nice_address_equal (&cand->addr, &icheck->from) &&
+          remote_candidate_and_socket_compatible (agent, lcand, cand,
+          icheck->local_socket)) {
+        rcand = cand;
         break;
       }
     }
-    k = k_next;
+
+    if (lcand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
+      CandidateCheckPair *pair = NULL;
+
+      for (j = stream->conncheck_list; j; j = j->next) {
+        CandidateCheckPair *p = j->data;
+        if (lcand == p->local && rcand == p->remote) {
+          pair = p;
+          break;
+        }
+      }
+      if (pair == NULL)
+        priv_conn_check_add_for_candidate_pair_matched (agent,
+            stream->id, component, lcand, rcand, NICE_CHECK_WAITING);
+    }
+
+    priv_schedule_triggered_check (agent, stream, component,
+        icheck->local_socket, rcand);
+    if (icheck->use_candidate)
+      priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
+
+    if (icheck->username)
+      g_free (icheck->username);
+    g_slice_free (IncomingCheck, icheck);
+    g_queue_delete_link (&component->incoming_checks, i);
+    i = i_next;
   }
 }
 
@@ -1695,57 +2030,82 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream)
 }
 
 /*
- * Enforces the upper limit for connectivity checks as described
- * in ICE spec section 5.7.3 (ID-19). See also 
+ * Enforces the upper limit for connectivity checks by dropping
+ * lower-priority pairs as described RFC 8445 section 6.1.2.5. See also
  * conn_check_add_for_candidate().
+ * Returns TRUE if the pair in argument is one of the deleted pairs.
  */
-static GSList *priv_limit_conn_check_list_size (NiceAgent *agent,
-    GSList *conncheck_list, guint upper_limit)
+static gboolean priv_limit_conn_check_list_size (NiceAgent *agent,
+    NiceStream *stream, CandidateCheckPair *pair)
 {
   guint valid = 0;
   guint cancelled = 0;
-  GSList *item = conncheck_list;
+  gboolean deleted = FALSE;
+  GSList *item = stream->conncheck_list;
 
   while (item) {
-    CandidateCheckPair *pair = item->data;
+    CandidateCheckPair *p = item->data;
     GSList *next = item->next;
 
     valid++;
-    if (valid > upper_limit) {
-        candidate_check_pair_free (agent, pair);
-        conncheck_list = g_slist_delete_link (conncheck_list, item);
-        cancelled++;
+    /* We remove lower-priority pairs, but only the ones that can be
+     * safely discarded without breaking an ongoing conncheck process.
+     * This only includes pairs that are in the frozen state (those
+     * initially added when remote candidates are received) or in failed
+     * state. Pairs in any other state play a role in the conncheck, and
+     * there removal may lead to a failing conncheck that would succeed
+     * otherwise.
+     *
+     * We also remove failed pairs from the list unconditionally.
+     */
+    if ((valid > agent->max_conn_checks && p->state == NICE_CHECK_FROZEN) ||
+        p->state == NICE_CHECK_FAILED) {
+      if (p == pair)
+        deleted = TRUE;
+      nice_debug ("Agent %p : pair %p removed.", agent, p);
+      candidate_check_pair_free (agent, p);
+      stream->conncheck_list = g_slist_delete_link (stream->conncheck_list,
+          item);
+      cancelled++;
     }
     item = next;
   }
 
   if (cancelled > 0)
-    nice_debug ("Agent : Pruned %d candidates. Conncheck list has %d elements"
-        " left. Maximum connchecks allowed : %d", cancelled, valid,
-        upper_limit);
-  return conncheck_list;
+    nice_debug ("Agent %p : Pruned %d pairs. "
+        "Conncheck list has %d elements left. "
+        "Maximum connchecks allowed : %d", agent, cancelled,
+        valid - cancelled, agent->max_conn_checks);
+
+  return deleted;
 }
 
 /*
- * Changes the selected pair for the component if 'pair' is nominated
- * and has higher priority than the currently selected pair. See
- * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
+ * Changes the selected pair for the component if 'pair'
+ * has higher priority than the currently selected pair. See
+ * RFC 8445 sect 8.1.1. "Nominating Pairs"
  */
-static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *component, CandidateCheckPair *pair)
+void
+conn_check_update_selected_pair (NiceAgent *agent, NiceComponent *component,
+    CandidateCheckPair *pair)
 {
   CandidatePair cpair = { 0, };
 
   g_assert (component);
   g_assert (pair);
+  /* pair is expected to have the nominated flag */
+  g_assert (pair->nominated);
   if (pair->priority > component->selected_pair.priority) {
+    gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
+    nice_candidate_pair_priority_to_string (pair->priority, priority);
     nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
-        "(prio:%" G_GUINT64_FORMAT ").", agent, component->id,
-        pair->local->foundation, pair->remote->foundation, pair->priority);
+        "(prio:%s).", agent, component->id,
+        pair->local->foundation, pair->remote->foundation, priority);
 
     cpair.local = pair->local;
     cpair.remote = pair->remote;
     cpair.priority = pair->priority;
-    /* cpair.keepalive is not used by nice_component_update_selected_pair() */
+    cpair.stun_priority = pair->stun_priority;
 
     nice_component_update_selected_pair (agent, component, &cpair);
 
@@ -1753,10 +2113,7 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *comp
 
     agent_signal_new_selected_pair (agent, pair->stream_id, component->id,
         pair->local, pair->remote);
-
   }
-
-  return TRUE;
 }
 
 /*
@@ -1806,7 +2163,7 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre
     for (i = stream->conncheck_list; i; i = i->next) {
       CandidateCheckPair *p = i->data;
 
-      g_assert (p->stream_id == stream->id);
+      g_assert_cmpuint (p->stream_id, ==, stream->id);
 
       if (p->component_id == (c + 1)) {
         if (p->nominated)
@@ -1842,7 +2199,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre
  *
  * Sends a component state changesignal via 'agent'.
  */
-static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component)
+void conn_check_update_check_list_state_for_ready (NiceAgent *agent,
+    NiceStream *stream, NiceComponent *component)
 {
   GSList *i;
   guint valid = 0, nominated = 0;
@@ -1866,7 +2224,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream
     /* Only go to READY if no checks are left in progress. If there are
      * any that are kept, then this function will be called again when the
      * conncheck tick timer finishes them all */
-    if (priv_prune_pending_checks (agent, stream, component->id) == 0) {
+    if (priv_prune_pending_checks (agent, stream, component) == 0) {
       /* Continue through the states to give client code a nice
        * logical progression. See http://phabricator.freedesktop.org/D218 for
        * discussion. */
@@ -1911,19 +2269,31 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice
       if (pair->state == NICE_CHECK_SUCCEEDED &&
           pair->discovered_pair != NULL) {
         pair = pair->discovered_pair;
-        g_assert (pair->state == NICE_CHECK_DISCOVERED);
+        g_assert_cmpint (pair->state, ==, NICE_CHECK_DISCOVERED);
       }
 
-      /* If the state of this pair is In-Progress, [...] the resulting
-       * valid pair has its nominated flag set when the response
-       * arrives.
+      /* If the received Binding request triggered a new check to be
+       * enqueued in the triggered-check queue (Section 7.3.1.4), once
+       * the check is sent and if it generates a successful response,
+       * and generates a valid pair, the agent sets the nominated flag
+       * of the pair to true
        */
-      if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
-          pair->state == NICE_CHECK_IN_PROGRESS) {
+      if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
+        if (g_slist_find (agent->triggered_check_queue, pair) ||
+            pair->state == NICE_CHECK_IN_PROGRESS) {
+
+        /* This pair is not always in the triggered check list, for
+         * example if it is in-progress with a lower priority than an
+         * already nominated pair.  Is that case, it is not rescheduled
+         * for a connection check, see function
+         * priv_schedule_triggered_check(), case NICE_CHECK_IN_PROGRESS.
+         */
         pair->mark_nominated_on_response_arrival = TRUE;
-        nice_debug ("Agent %p : pair %p (%s) is in-progress, "
+        nice_debug ("Agent %p : pair %p (%s) is %s, "
             "will be nominated on response receipt.",
-            agent, pair, pair->foundation);
+            agent, pair, pair->foundation,
+            priv_state_to_string (pair->state));
+        }
       }
 
       if (pair->valid ||
@@ -1938,7 +2308,7 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice
         if (component->state == NICE_COMPONENT_STATE_FAILED)
           agent_signal_component_state_change (agent,
               stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
-        priv_update_selected_pair (agent, component, pair);
+        conn_check_update_selected_pair (agent, component, pair);
         if (component->state == NICE_COMPONENT_STATE_CONNECTING)
           /* step: notify the client of a new component state (must be done
            *       before the possible check list state update step */
@@ -1947,44 +2317,11 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice
       }
 
       if (pair->nominated)
-        priv_update_check_list_state_for_ready (agent, stream, component);
-    }
-  }
-}
-
-guint32
-ensure_unique_priority (NiceStream *stream, NiceComponent *component,
-    guint32 priority)
-{
-  GSList *item;
-
- again:
-  if (priority == 0)
-    priority--;
-
-  for (item = component->local_candidates; item; item = item->next) {
-    NiceCandidate *cand = item->data;
-
-    if (cand->priority == priority) {
-      priority--;
-      goto again;
-    }
-  }
-
-  for (item = stream->conncheck_list; item; item = item->next) {
-    CandidateCheckPair *p = item->data;
-
-    if (p->component_id == component->id &&
-        p->prflx_priority == priority) {
-      priority--;
-      goto again;
+        conn_check_update_check_list_state_for_ready (agent, stream, component);
     }
   }
-
-  return priority;
 }
 
-
 /*
  * Creates a new connectivity check pair and adds it to
  * the agent's list of checks.
@@ -1995,10 +2332,26 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
 {
   NiceStream *stream;
   CandidateCheckPair *pair;
+  guint64 priority;
 
   g_assert (local != NULL);
   g_assert (remote != NULL);
 
+  priority = agent_candidate_pair_priority (agent, local, remote);
+
+  if (component->selected_pair.priority &&
+      priority < component->selected_pair.priority) {
+    gchar prio1[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
+    gchar prio2[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
+
+    nice_candidate_pair_priority_to_string (priority, prio1);
+    nice_candidate_pair_priority_to_string (component->selected_pair.priority,
+        prio2);
+    nice_debug ("Agent %p : do not create a pair that would have a priority "
+        "%s lower than selected pair priority %s.", agent, prio1, prio2);
+    return NULL;
+  }
+
   stream = agent_find_stream (agent, stream_id);
   pair = g_slice_new0 (CandidateCheckPair);
 
@@ -2028,21 +2381,26 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
           tmpbuf1, nice_address_get_port (&pair->local->addr),
           tmpbuf2, nice_address_get_port (&pair->remote->addr));
   }
-  pair->prflx_priority = ensure_unique_priority (stream, component,
-      peer_reflexive_candidate_priority (agent, local));
+  pair->stun_priority = stun_request_priority (agent, local);
 
   stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
       (GCompareFunc)conn_check_compare);
 
-  nice_debug ("Agent %p : added a new pair %p with foundation '%s' to "
-      "stream %u component %u.", agent, pair, pair->foundation, stream_id,
-      component->id);
+  nice_debug ("Agent %p : added a new pair %p with foundation '%s' and "
+      "transport %s:%s to stream %u component %u",
+      agent, pair, pair->foundation,
+      priv_candidate_transport_to_string (pair->local->transport),
+      priv_candidate_transport_to_string (pair->remote->transport),
+      stream_id, component->id);
+
+  if (initial_state == NICE_CHECK_FROZEN)
+    priv_conn_check_unfreeze_maybe (agent, pair);
 
   /* implement the hard upper limit for number of
      checks (see sect 5.7.3 ICE ID-19): */
   if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) {
-    stream->conncheck_list = priv_limit_conn_check_list_size (agent,
-        stream->conncheck_list, agent->max_conn_checks);
+    if (priv_limit_conn_check_list_size (agent, stream, pair))
+      return NULL;
   }
 
   return pair;
@@ -2074,17 +2432,19 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
 
   pair = priv_add_new_check_pair (agent, stream_id, component, local, remote,
       initial_state);
-  if (component->state == NICE_COMPONENT_STATE_CONNECTED ||
-      component->state == NICE_COMPONENT_STATE_READY) {
-    agent_signal_component_state_change (agent,
-        stream_id,
-        component->id,
-        NICE_COMPONENT_STATE_CONNECTED);
-  } else {
-    agent_signal_component_state_change (agent,
-        stream_id,
-        component->id,
-        NICE_COMPONENT_STATE_CONNECTING);
+  if (pair) {
+    if (component->state == NICE_COMPONENT_STATE_CONNECTED ||
+        component->state == NICE_COMPONENT_STATE_READY) {
+      agent_signal_component_state_change (agent,
+          stream_id,
+          component->id,
+          NICE_COMPONENT_STATE_CONNECTED);
+    } else {
+      agent_signal_component_state_change (agent,
+          stream_id,
+          component->id,
+          NICE_COMPONENT_STATE_CONNECTING);
+    }
   }
 
   return pair;
@@ -2099,12 +2459,14 @@ gboolean conn_check_add_for_candidate_pair (NiceAgent *agent,
   g_assert (local != NULL);
   g_assert (remote != NULL);
 
-  /* note: do not create pairs where the local candidate is
-   *       a srv-reflexive (ICE 5.7.3. "Pruning the pairs" ID-9) */
+  /* note: do not create pairs where the local candidate is a srv-reflexive
+   * or peer-reflexive (ICE 6.1.2.4. "Pruning the pairs" RFC 8445)
+   */
   if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
       agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
       agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
-      local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
+      (local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
+      local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)) {
     return FALSE;
   }
 
@@ -2117,9 +2479,9 @@ gboolean conn_check_add_for_candidate_pair (NiceAgent *agent,
   /* note: match pairs only if transport and address family are the same */
   if (local->transport == conn_check_match_transport (remote->transport) &&
      local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family) {
-    priv_conn_check_add_for_candidate_pair_matched (agent, stream_id, component,
-        local, remote, NICE_CHECK_FROZEN);
-    ret = TRUE;
+    if (priv_conn_check_add_for_candidate_pair_matched (agent, stream_id,
+        component, local, remote, NICE_CHECK_FROZEN))
+      ret = TRUE;
   }
 
   return ret;
@@ -2148,7 +2510,7 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceCompone
   /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates",
    * the agent does not pair this candidate with any local candidates.
    */
-  if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
+  if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
       remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
   {
     return added;
@@ -2194,7 +2556,7 @@ int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceC
    * with other remote candidates
    */
 
-  if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
+  if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
       local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
   {
     return added;
@@ -2445,34 +2807,37 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
   return 0;
 }
 
-/* Implement the computation specific in RFC 5245 section 16 */
+/* Implement the computation specific in RFC 8445 section 14 */
 
 static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream)
 {
-  GSList *i;
+  GSList *i, *j;
   guint waiting_and_in_progress = 0;
-  guint n = 0;
   unsigned int rto = 0;
 
-  for (i = stream->conncheck_list; i ; i = i->next) {
-    CandidateCheckPair *p = i->data;
-    if (p->state == NICE_CHECK_IN_PROGRESS ||
-        p->state == NICE_CHECK_WAITING)
-      waiting_and_in_progress++;
+  /* we can compute precisely the number of pairs in-progress or
+   * waiting for all streams, instead of limiting the value to one
+   * stream, and multiplying it by the number of active streams.
+   * Since RFC8445, this number of waiting and in-progress pairs
+   * if maxed by the number of different foundations in the conncheck
+   * list.
+   */
+  for (i = agent->streams; i ; i = i->next) {
+    NiceStream *s = i->data;
+    for (j = s->conncheck_list; j ; j = j->next) {
+      CandidateCheckPair *p = j->data;
+      if (p->state == NICE_CHECK_IN_PROGRESS ||
+          p->state == NICE_CHECK_WAITING)
+        waiting_and_in_progress++;
+    }
   }
 
-  n = priv_number_of_active_check_lists (agent);
-  rto = agent->timer_ta  * n * waiting_and_in_progress;
+  rto = agent->timer_ta  * waiting_and_in_progress;
 
-  /* We assume non-reliable streams are RTP, so we use 100 as the max */
   nice_debug ("Agent %p : timer set to %dms, "
-    "waiting+in_progress=%d, nb_active=%d",
-    agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100),
-    waiting_and_in_progress, n);
-  if (agent->reliable)
-    return MAX (rto, 500);
-  else
-    return MAX (rto, 100);
+    "waiting+in_progress=%d", agent, MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT),
+    waiting_and_in_progress);
+  return MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT);
 }
 
 /*
@@ -2525,7 +2890,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
     nice_address_to_string (&pair->remote->addr, tmpbuf2);
     nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, "
         "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), "
-        "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent,
+        "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%08x, %s.", agent,
             tmpbuf1, nice_address_get_port (&pair->local->addr),
             tmpbuf2, nice_address_get_port (&pair->remote->addr),
              pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1,
@@ -2533,7 +2898,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
             (unsigned long long)agent->tie_breaker,
         (int) uname_len, uname, uname_len,
         (int) password_len, password, password_len,
-        pair->prflx_priority,
+        pair->stun_priority,
         controlling ? "controlling" : "controlled");
   }
 
@@ -2574,7 +2939,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
   buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent,
       &stun->message, stun->buffer, sizeof(stun->buffer),
       uname, uname_len, password, password_len,
-      cand_use, controlling, pair->prflx_priority,
+      cand_use, controlling, pair->stun_priority,
       agent->tie_breaker,
       pair->local->foundation,
       agent_to_ice_compatibility (agent));
@@ -2601,8 +2966,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
     stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions);
   }
 
-  g_get_current_time (&stun->next_tick);
-  g_time_val_add (&stun->next_tick, timeout * 1000);
+  stun->next_tick = g_get_monotonic_time () + timeout * 1000;
 
   /* TCP-ACTIVE candidate must create a new socket before sending
    * by connecting to the peer. The new socket is stored in the candidate
@@ -2619,6 +2983,9 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
       new_socket = nice_tcp_active_socket_connect (pair->sockptr,
           &pair->remote->addr);
       if (new_socket) {
+        nice_debug ("Agent %p: add to tcp-act socket %p a new "
+            "tcp connect socket %p on pair %p in s/c %d/%d",
+            agent, pair->sockptr, new_socket, pair, stream->id, component->id);
         pair->sockptr = new_socket;
         _priv_set_socket_tos (agent, pair->sockptr, stream2->tos);
 
@@ -2644,64 +3011,89 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
  * Implemented the pruning steps described in ICE sect 8.1.2
  * "Updating States" (ID-19) after a pair has been nominated.
  *
- * @see priv_update_check_list_state_failed_components()
+ * @see conn_check_update_check_list_state_failed_components()
  */
-static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id)
+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, NiceComponent *component)
 {
   GSList *i;
-  guint64 highest_nominated_priority = 0;
+  guint64 priority;
   guint in_progress = 0;
+  guint triggered_check = 0;
+  gchar prio[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
 
-  nice_debug ("Agent %p: Finding highest priority for component %d",
-      agent, component_id);
+  nice_debug ("Agent %p: Pruning pending checks for s%d/c%d",
+      agent, stream->id, component->id);
 
-  for (i = stream->conncheck_list; i; i = i->next) {
-    CandidateCheckPair *p = i->data;
-    if (p->component_id == component_id && p->valid == TRUE &&
-        p->nominated == TRUE) {
-      if (p->priority > highest_nominated_priority) {
-        highest_nominated_priority = p->priority;
-      }
-    }
-  }
+  /* Called when we have at least one selected pair */
+  priority = component->selected_pair.priority;
+  g_assert (priority > 0);
 
-  nice_debug ("Agent %p: Pruning pending checks. Highest nominated priority "
-      "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority);
+  nice_candidate_pair_priority_to_string (priority, prio);
+  nice_debug ("Agent %p : selected pair priority is %s", agent, prio);
 
-  /* step: cancel all FROZEN and WAITING pairs for the component */
   i = stream->conncheck_list;
   while (i) {
     CandidateCheckPair *p = i->data;
     GSList *next = i->next;
 
-    if (p->component_id == component_id) {
-      if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) {
+    if (p->component_id != component->id) {
+      i = next;
+      continue;
+    }
+
+    /* We do not remove a pair from the conncheck list if it is also in
+     * the triggered check queue.  This is not what suggests the ICE
+     * spec, but it proved to be more robust in the aggressive
+     * nomination scenario, precisely because these pairs may have the
+     * use-candidate flag set, and the peer agent may already have
+     * selected such one.
+     */
+    if (g_slist_find (agent->triggered_check_queue, p) &&
+        p->state != NICE_CHECK_IN_PROGRESS) {
+      if (p->priority < priority) {
         nice_debug ("Agent %p : pair %p removed.", agent, p);
         candidate_check_pair_free (agent, p);
         stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
-      }
+      } else
+        triggered_check++;
+    }
 
-      /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
-      else if (p->state == NICE_CHECK_IN_PROGRESS) {
-        if (p->priority < highest_nominated_priority) {
-          p->retransmit  = FALSE;
+    /* step: cancel all FROZEN and WAITING pairs for the component */
+    else if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) {
+      nice_debug ("Agent %p : pair %p removed.", agent, p);
+      candidate_check_pair_free (agent, p);
+      stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
+    }
+
+    /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
+    else if (p->state == NICE_CHECK_IN_PROGRESS) {
+      if (p->priority < priority) {
+        priv_remove_pair_from_triggered_check_queue (agent, p);
+        if (p->retransmit) {
+          p->retransmit = FALSE;
           nice_debug ("Agent %p : pair %p will not be retransmitted.",
               agent, p);
-        } else {
-          /* We must keep the higher priority pairs running because if a udp
-           * packet was lost, we might end up using a bad candidate */
-          nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority %"
-              G_GUINT64_FORMAT " is higher than currently nominated pair %"
-              G_GUINT64_FORMAT, agent,
-              p, p->priority, highest_nominated_priority);
-          in_progress++;
         }
+      } else {
+        /* We must keep the higher priority pairs running because if a udp
+         * packet was lost, we might end up using a bad candidate */
+        nice_candidate_pair_priority_to_string (p->priority, prio);
+        nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority "
+            "%s is higher than priority of best nominated pair.", agent, p, prio);
+        /* We may also have to enable the retransmit flag of pairs with
+         * a higher priority than the first nominated pair
+         */
+        if (!p->retransmit && p->stun_transactions) {
+          p->retransmit = TRUE;
+          nice_debug ("Agent %p : pair %p will be retransmitted.", agent, p);
+        }
+        in_progress++;
       }
     }
     i = next;
   }
 
-  return in_progress;
+  return in_progress + triggered_check;
 }
 
 /*
@@ -2721,20 +3113,19 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str
 
   g_assert (remote_cand != NULL);
 
+  nice_debug ("Agent %p : scheduling triggered check with socket=%p "
+      "and remote cand=%p.", agent, local_socket, remote_cand);
+
   for (i = stream->conncheck_list; i ; i = i->next) {
       p = i->data;
       if (p->component_id == component->id &&
          p->remote == remote_cand &&
-          ((p->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
-              p->sockptr == local_socket) ||
-              (p->local->transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
-                  p->local->sockptr == local_socket))) {
-        /* We don't check for p->sockptr because in the case of
-         * tcp-active we don't want to retrigger a check on a pair that
-         * was FAILED when a peer-reflexive pair was created */
+          p->sockptr == local_socket) {
+        /* If we match with a peer-reflexive discovered pair, we
+         * use the parent succeeded pair instead */
 
         if (p->succeeded_pair != NULL) {
-          g_assert (p->state == NICE_CHECK_DISCOVERED);
+          g_assert_cmpint (p->state, ==, NICE_CHECK_DISCOVERED);
           p = p->succeeded_pair;
         }
 
@@ -2755,19 +3146,18 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str
              * for that pair.  The controlling role of this new check may
              * be different from the role of this cancelled check.
              *
-             * note: the flag retransmit unset means that
-             * another pair, with a higher priority is already nominated,
-             * so there's no reason to recheck this pair, since it can in
-             * no way replace the nominated one.
+             * When another pair, with a higher priority is already
+             * nominated, so there's no reason to recheck this pair,
+             * since it can in no way replace the nominated one.
              */
-            if (!nice_socket_is_reliable (p->sockptr) && p->retransmit) {
+            if (p->priority > component->selected_pair.priority) {
               nice_debug ("Agent %p : pair %p added for a triggered check.",
                   agent, p);
               priv_add_pair_to_triggered_check_queue (agent, p);
             }
             break;
           case NICE_CHECK_FAILED:
-            if (p->retransmit) {
+            if (p->priority > component->selected_pair.priority) {
                 nice_debug ("Agent %p : pair %p added for a triggered check.",
                     agent, p);
                 priv_add_pair_to_triggered_check_queue (agent, p);
@@ -2778,19 +3168,20 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str
                   agent_signal_component_state_change (agent, stream->id,
                       component->id, NICE_COMPONENT_STATE_CONNECTING);
                   conn_check_schedule_next (agent);
+                /* If the component if in ready state, move it back to
+                 * connected as this failed pair with a higher priority
+                 * than the nominated pair requires to pursue the
+                 * conncheck
+                 */
+                } else if (component->state == NICE_COMPONENT_STATE_READY) {
+                  agent_signal_component_state_change (agent, stream->id,
+                      component->id, NICE_COMPONENT_STATE_CONNECTED);
+                  conn_check_schedule_next (agent);
                 }
             }
             break;
          case NICE_CHECK_SUCCEEDED:
             nice_debug ("Agent %p : nothing to do for pair %p.", agent, p);
-            /* note: this is a bit unsure corner-case -- let's do the
-               same state update as for processing responses to our own checks */
-            /* note: this update is required by the trickle test, to
-             * ensure the transition ready -> connected -> ready, because
-             * an incoming stun request generates a discovered peer reflexive,
-             * that causes the ready -> connected transition.
-             */
-            priv_update_check_list_state_for_ready (agent, stream, component);
             break;
           default:
             break;
@@ -2813,7 +3204,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str
     nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
     p = priv_conn_check_add_for_candidate_pair_matched (agent, stream->id,
         component, local, remote_cand, NICE_CHECK_WAITING);
-    priv_add_pair_to_triggered_check_queue (agent, p);
+    if (p)
+      priv_add_pair_to_triggered_check_queue (agent, p);
     return TRUE;
   }
   else {
@@ -2943,6 +3335,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint
   }
   g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s",
       local_cand->foundation, parent_pair->remote->foundation);
+
   if (agent->controlling_mode == TRUE)
     pair->priority = nice_candidate_pair_priority (pair->local->priority,
         pair->remote->priority);
@@ -2950,10 +3343,19 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint
     pair->priority = nice_candidate_pair_priority (pair->remote->priority,
         pair->local->priority);
   pair->nominated = parent_pair->nominated;
-  pair->prflx_priority = ensure_unique_priority (stream, component,
-      peer_reflexive_candidate_priority (agent, local_cand));
-  nice_debug ("Agent %p : added a new peer-discovered pair with "
-      "foundation '%s'.",  agent, pair->foundation);
+  /* the peer-reflexive priority used in stun request is copied from
+   * the parent succeeded pair. This value is not required for discovered
+   * pair, that won't emit stun requests themselves, but may be used when
+   * such pair becomes the selected pair, and when keepalive stun are emitted,
+   * using the sockptr and stun_priority values from the succeeded pair.
+   */
+  pair->stun_priority = parent_pair->stun_priority;
+  nice_debug ("Agent %p : added a new peer-discovered pair %p with "
+      "foundation '%s' and transport %s:%s to stream %u component %u",
+      agent, pair, pair->foundation,
+      priv_candidate_transport_to_string (pair->local->transport),
+      priv_candidate_transport_to_string (pair->remote->transport),
+      stream_id, component->id);
 
   stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
       (GCompareFunc)conn_check_compare);
@@ -3019,27 +3421,35 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *
 {
   CandidateCheckPair *new_pair = NULL;
   NiceAddress mapped;
-  GSList *i, *j;
+  GSList *i;
   NiceCandidate *local_cand = NULL;
 
   nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
 
-  for (j = component->local_candidates; j; j = j->next) {
-    NiceCandidate *cand = j->data;
-    if (nice_address_equal (&mapped, &cand->addr)) {
+  for (i = component->local_candidates; i; i = i->next) {
+    NiceCandidate *cand = i->data;
+
+    if (nice_address_equal (&mapped, &cand->addr) &&
+        local_candidate_and_socket_compatible (agent, cand, sockptr)) {
       local_cand = cand;
+      break;
+    }
+  }
 
-      /* We always need to select the peer-reflexive Candidate Pair in the case
-       * of a TCP-ACTIVE local candidate, so we find it even if an incoming
-       * check matched an existing pair because it could be the original
-       * ACTIVE-PASSIVE candidate pair which was retriggered */
-      for (i = stream->conncheck_list; i; i = i->next) {
-        CandidateCheckPair *pair = i->data;
-        if (pair->local == cand && remote_candidate == pair->remote) {
-          new_pair = pair;
-          break;
-        }
-      }
+  /* The mapped address allows to look for a previously discovered
+   * peer reflexive local candidate, and its related pair. This
+   * new_pair will be marked 'Valid', while the pair 'p' of the
+   * initial stun request will be marked 'Succeeded'
+   *
+   * In the case of a tcp-act/tcp-pass pair 'p', where the local
+   * candidate is of type tcp-act, and its port number is zero, a
+   * conncheck on this pair *always* leads to the creation of a
+   * discovered peer-reflexive tcp-act local candidate.
+   */
+  for (i = stream->conncheck_list; i; i = i->next) {
+    CandidateCheckPair *pair = i->data;
+    if (local_cand == pair->local && remote_candidate == pair->remote) {
+      new_pair = pair;
       break;
     }
   }
@@ -3058,21 +3468,24 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *
     nice_component_add_valid_candidate (agent, component, remote_candidate);
   }
   else {
-    if (!local_cand) {
-      if (!agent->force_relay) {
-        /* step: find a new local candidate, see RFC 5245 7.1.3.2.1.
-         * "Discovering Peer Reflexive Candidates"
-         */
-        local_cand = discovery_add_peer_reflexive_candidate (agent,
-                                                             stream->id,
-                                                             component->id,
-                                                            &mapped,
-                                                             sockptr,
-                                                             local_candidate,
-                                                             remote_candidate);
-        nice_debug ("Agent %p : added a new peer-reflexive local candidate %p",
-            agent, local_cand);
-      }
+    if (local_cand == NULL && !agent->force_relay) {
+      /* step: find a new local candidate, see RFC 5245 7.1.3.2.1.
+       * "Discovering Peer Reflexive Candidates"
+       *
+       * The priority equal to the value of the PRIORITY attribute
+       * in the Binding request is taken from the "parent" pair p
+       */
+      local_cand = discovery_add_peer_reflexive_candidate (agent,
+                                                           stream->id,
+                                                           component->id,
+                                                           p->stun_priority,
+                                                          &mapped,
+                                                           sockptr,
+                                                           local_candidate,
+                                                           remote_candidate);
+      nice_debug ("Agent %p : added a new peer-reflexive local candidate %p "
+          "with transport %s", agent, local_cand,
+          priv_candidate_transport_to_string (local_cand->transport));
     }
 
     /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2
@@ -3168,6 +3581,15 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre
          return TRUE;
        }
 
+        if (remote_candidate == NULL) {
+          candidate_check_pair_fail (stream, agent, p);
+          if (nice_debug_is_enabled ()) {
+            nice_debug ("Agent %p : pair %p FAILED "
+                "(got a matching pair without a known remote candidate).", agent, p);
+          }
+          return TRUE;
+        }
+
        /* note: CONNECTED but not yet READY, see docs */
 
        /* step: handle the possible case of a peer-reflexive
@@ -3186,10 +3608,11 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre
               local_candidate, remote_candidate);
 
        /* note: The success of this check might also
-        * cause the state of other checks to change as well, ICE
-        * spec 7.1.3.2.3
+        * cause the state of other checks to change as well
+         * See sect 7.2.5.3.3 (Updating Candidate Pair States) of
+         * ICE spec (RFC8445).
         */
-       priv_conn_check_unfreeze_related (agent, stream, p);
+       conn_check_unfreeze_related (agent, p);
 
        /* Note: this assignment helps to reduce the numbers of cases
         * to be tested. If ok_pair and p refer to distinct pairs, it
@@ -3252,7 +3675,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre
        }
 
        if (ok_pair->nominated == TRUE) {
-         priv_update_selected_pair (agent, component, ok_pair);
+          conn_check_update_selected_pair (agent, component, ok_pair);
          priv_print_conn_check_lists (agent, G_STRFUNC,
              ", got a nominated pair");
 
@@ -3266,11 +3689,17 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre
 
        /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
           states" and 8.1.2 "Updating States", ID-19) */
-       priv_update_check_list_state_for_ready (agent, stream, component);
+       conn_check_update_check_list_state_for_ready (agent, stream, component);
       } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
         uint64_t tie;
         gboolean controlled_mode;
 
+        if (!p->retransmit) {
+          nice_debug ("Agent %p : Role conflict with pair %p, not restarting",
+              agent, p);
+          return TRUE;
+        }
+
        /* case: role conflict error, need to restart with new role */
        nice_debug ("Agent %p : Role conflict with pair %p, restarting",
             agent, p);
@@ -3405,12 +3834,17 @@ priv_calc_turn_timeout (guint lifetime)
     return lifetime / 2;
 }
 
-static CandidateRefresh *
+static void
 priv_add_new_turn_refresh (NiceAgent *agent, CandidateDiscovery *cdisco,
     NiceCandidate *relay_cand, guint lifetime)
 {
   CandidateRefresh *cand;
 
+  if (cdisco->turn->type == NICE_RELAY_TYPE_TURN_TLS &&
+      (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
+       agent->compatibility == NICE_COMPATIBILITY_OC2007R2))
+    return;
+
   cand = g_slice_new0 (CandidateRefresh);
   agent->refresh_list = g_slist_append (agent->refresh_list, cand);
 
@@ -3427,7 +3861,7 @@ priv_add_new_turn_refresh (NiceAgent *agent, CandidateDiscovery *cdisco,
         sizeof(cand->stun_resp_buffer));
     memcpy(&cand->stun_resp_msg, &cdisco->stun_resp_msg, sizeof(StunMessage));
     cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
-    cand->stun_resp_msg.agent = NULL;
+    cand->stun_resp_msg.agent = &cand->stun_agent;
     cand->stun_resp_msg.key = NULL;
   }
 
@@ -3442,7 +3876,7 @@ priv_add_new_turn_refresh (NiceAgent *agent, CandidateDiscovery *cdisco,
 
   nice_debug ("timer source is : %p", cand->timer_source);
 
-  return cand;
+  return;
 }
 
 static void priv_handle_turn_alternate_server (NiceAgent *agent,
@@ -3471,11 +3905,30 @@ static void priv_handle_turn_alternate_server (NiceAgent *agent,
 
       nice_address_to_string (&server, ip);
       nice_debug ("Agent %p : Cancelling and setting alternate server %s for "
-          "CandidateDiscovery %p", agent, ip, d);
+          "CandidateDiscovery %p on s%d/c%d", agent, ip, d,
+          d->stream_id, d->component_id);
       d->server = alternate;
       d->turn->server = alternate;
       d->pending = FALSE;
       agent->discovery_unsched_items++;
+
+      if (d->turn->type == NICE_RELAY_TYPE_TURN_TCP ||
+          d->turn->type == NICE_RELAY_TYPE_TURN_TLS) {
+        NiceStream *stream;
+        NiceComponent *component;
+
+        if (!agent_find_component (agent, d->stream_id, d->component_id,
+            &stream, &component)) {
+          nice_debug ("Could not find stream or component in "
+              "priv_handle_turn_alternate_server");
+          continue;
+        }
+        d->nicesock = agent_create_tcp_turn_socket (agent, stream, component,
+            d->nicesock, &d->server, d->turn->type,
+            nice_socket_is_reliable (d->nicesock));
+
+        nice_component_attach_socket (component, d->nicesock);
+      }
     }
   }
 }
@@ -3537,6 +3990,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
           /* handle alternate server */
           nice_address_set_from_sockaddr (&addr, &alternate.addr);
           priv_handle_turn_alternate_server (agent, d, d->server, addr);
+          trans_found = TRUE;
         } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
                    res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
           /* case: successful allocate, create a new local candidate */
@@ -3600,9 +4054,8 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
                     &d->stun_message);
                 nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
                     resp);
-              } else {
-                priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
               }
+              priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
             }
 
             relay_cand = discovery_add_relay_candidate (
@@ -3640,9 +4093,8 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
                   &d->stun_message);
               nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
                   resp);
-            } else {
-              priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
             }
+            priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
 
             /* In case a new candidate has been added */
             conn_check_schedule_next (agent);
@@ -3702,7 +4154,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
               d->stun_message.buffer_len = 0;
               d->done = TRUE;
             }
-          } else {
+          } else if (d->pending) {
             /* case: STUN error, the check STUN context was freed */
             d->stun_message.buffer = NULL;
             d->stun_message.buffer_len = 0;
@@ -3734,8 +4186,9 @@ static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *
   StunTransactionId response_id;
   stun_message_id (resp, response_id);
 
-  for (i = agent->refresh_list; i && trans_found != TRUE; i = i->next) {
+  for (i = agent->refresh_list; i && trans_found != TRUE;) {
     CandidateRefresh *cand = i->data;
+    GSList *next = i->next;
 
     if (!cand->disposing && cand->stun_message.buffer) {
       stun_message_id (&cand->stun_message, refresh_id);
@@ -3755,6 +4208,7 @@ static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *
           g_source_destroy (cand->tick_source);
           g_source_unref (cand->tick_source);
           cand->tick_source = NULL;
+          trans_found = TRUE;
         } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
           int code = -1;
           uint8_t *sent_realm = NULL;
@@ -3797,6 +4251,7 @@ static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *
         }
       }
     }
+    i = next;
   }
 
   return trans_found;
@@ -4156,17 +4611,28 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
   username = (uint8_t *) stun_message_find (&req, STUN_ATTRIBUTE_USERNAME,
                                            &username_len);
 
-  for (i = component->remote_candidates; i; i = i->next) {
+  for (i = component->local_candidates; i; i = i->next) {
     NiceCandidate *cand = i->data;
-    if (nice_address_equal (from, &cand->addr)) {
-      remote_candidate = cand;
+    NiceAddress *addr;
+
+    if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
+      addr = &cand->addr;
+    else
+      addr = &cand->base_addr;
+
+    if (nice_address_equal (&nicesock->addr, addr) &&
+        local_candidate_and_socket_compatible (agent, cand, nicesock)) {
+      local_candidate = cand;
       break;
     }
   }
-  for (i = component->local_candidates; i; i = i->next) {
+
+  for (i = component->remote_candidates; i; i = i->next) {
     NiceCandidate *cand = i->data;
-    if (nice_address_equal (&nicesock->addr, &cand->addr)) {
-      local_candidate = cand;
+    if (nice_address_equal (from, &cand->addr) &&
+        remote_candidate_and_socket_compatible (agent, local_candidate,
+        cand, nicesock)) {
+      remote_candidate = cand;
       break;
     }
   }
@@ -4229,6 +4695,14 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
     return TRUE;
   }
 
+  /* This is most likely caused by a second response to a request which
+   * already has received a valid reply.
+   */
+  if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
+    nice_debug ("Agent %p : Valid STUN response for which we don't have a request, ignoring", agent);
+    return TRUE;
+  }
+
   if (valid != STUN_VALIDATION_SUCCESS) {
     nice_debug ("Agent %p : STUN message is unsuccessful %d, ignoring", agent, valid);
     return FALSE;
@@ -4302,16 +4776,11 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
             remote_candidate2 ? remote_candidate2 : remote_candidate);
         if(remote_candidate && stream->remote_ufrag[0]) {
           if (local_candidate &&
-              local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
-            CandidateCheckPair *pair;
-
-            pair = priv_conn_check_add_for_candidate_pair_matched (agent,
+              local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
+            priv_conn_check_add_for_candidate_pair_matched (agent,
                 stream->id, component, local_candidate, remote_candidate,
-                NICE_CHECK_SUCCEEDED);
-            if (pair) {
-              pair->valid = TRUE;
-            }
-          } else
+                NICE_CHECK_WAITING);
+          else
             conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
         }
       }
@@ -4331,6 +4800,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
         /* step: send a reply immediately but postpone other processing */
         priv_store_pending_check (agent, component, from, nicesock,
             username, username_len, priority, use_candidate);
+        priv_print_conn_check_lists (agent, G_STRFUNC, ", icheck stored");
       }
     } else {
       nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
@@ -4374,7 +4844,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
   }
 
   /* RENOMINATION attribute support */
-  conn_check_handle_renomination(agent, stream, component, &req, local_candidate, remote_candidate);
+  conn_check_handle_renomination(agent, stream, component, &req, remote_candidate, local_candidate);
 
   return TRUE;
 }