conncheck.c 99.9 KB
Newer Older
1 2 3
/*
 * This file is part of the Nice GLib ICE library.
 *
4 5 6
 * (C) 2006-2009 Collabora Ltd.
 *  Contact: Youness Alaoui
 * (C) 2006-2009 Nokia Corporation. All rights reserved.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *  Contact: Kai Vehmanen
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Nice GLib ICE library.
 *
 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
 * Corporation. All Rights Reserved.
 *
 * Contributors:
 *   Kai Vehmanen, Nokia
26
 *   Youness Alaoui, Collabora Ltd.
27 28 29 30 31 32 33 34 35 36 37 38 39
 *   Dafydd Harries, Collabora Ltd.
 *
 * Alternatively, the contents of this file may be used under the terms of the
 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
 * case the provisions of LGPL are applicable instead of those above. If you
 * wish to allow use of your version of this file only under the terms of the
 * LGPL and not to allow others to use your version of this file under the
 * MPL, indicate your decision by deleting the provisions above and replace
 * them with the notice and other provisions required by the LGPL. If you do
 * not delete the provisions above, a recipient may use your version of this
 * file under either the MPL or the LGPL.
 */

40
/*
41 42 43 44 45 46 47 48
 * @file conncheck.c
 * @brief ICE connectivity checks
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

49 50 51 52 53
#include <errno.h>
#include <string.h>

#include <glib.h>

54 55
#include "debug.h"

56 57 58 59
#include "agent.h"
#include "agent-priv.h"
#include "conncheck.h"
#include "discovery.h"
60
#include "stun/usages/ice.h"
61 62
#include "stun/usages/bind.h"
#include "stun/usages/turn.h"
63

64
static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream);
65
static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component);
66
static guint priv_prune_pending_checks (Stream *stream, guint component_id);
67
static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate);
68
static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand);
69 70 71 72 73
static size_t priv_create_username (NiceAgent *agent, Stream *stream,
    guint component_id, NiceCandidate *remote, NiceCandidate *local,
    uint8_t *dest, guint dest_len, gboolean inbound);
static size_t priv_get_password (NiceAgent *agent, Stream *stream,
    NiceCandidate *remote, uint8_t **password);
74

75
static int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
76 77 78 79 80
{
  return (now->tv_sec == timer->tv_sec) ?
    now->tv_usec >= timer->tv_usec :
    now->tv_sec >= timer->tv_sec;
}
81

82
/*
83 84 85 86 87 88
 * Finds the next connectivity check in WAITING state.
 */
static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
{
  GSList *i;

89 90
  /* note: list is sorted in priority order to first waiting check has
   *       the highest priority */
91 92 93 94 95 96 97 98 99 100

  for (i = conn_check_list; i ; i = i->next) {
    CandidateCheckPair *p = i->data;
    if (p->state == NICE_CHECK_WAITING)
      return p;
  }

  return NULL;
}

101
/*
102 103 104 105 106 107
 * 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)
{
108 109 110 111
  /* XXX: from ID-16 onwards, the checks should not be sent
   * immediately, but be put into the "triggered queue",
   * see  "7.2.1.4 Triggered Checks"
   */
112
  g_get_current_time (&pair->next_tick);
113
  g_time_val_add (&pair->next_tick, agent->timer_ta * 1000);
114
  pair->state = NICE_CHECK_IN_PROGRESS;
115
  nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair);
116 117 118 119
  conn_check_send (agent, pair);
  return TRUE;
}

120
/*
121
 * Unfreezes the next connectivity check in the list. Follows the
122
 * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
123
 * (ID-19), with some exceptions (see comments in code).
124 125 126
 *
 * See also sect 7.1.2.2.3 (Updating Pair States), and
 * priv_conn_check_unfreeze_related().
127 128 129
 * 
 * @return TRUE on success, and FALSE if no frozen candidates were found.
 */
130
static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent)
131 132
{
  CandidateCheckPair *pair = NULL;
133
  GSList *i, *j;
134

135 136 137 138 139 140 141
  /* XXX: the unfreezing is implemented a bit differently than in the
   *      current ICE spec, but should still be interoperate:
   *   - checks are not grouped by foundation
   *   - one frozen check is unfrozen (lowest component-id, highest
   *     priority)
   */

142 143 144 145
  for (i = agent->streams; i; i = i->next) {
    Stream *stream = i->data;
    guint64 max_frozen_priority = 0;

146

147 148 149 150 151 152 153 154 155 156
    for (j = stream->conncheck_list; j ; j = j->next) {
      CandidateCheckPair *p = j->data;

      /* XXX: the prio check could be removed as the pairs are sorted
       *       already */

      if (p->state == NICE_CHECK_FROZEN) {
	if (p->priority > max_frozen_priority) {
	  max_frozen_priority = p->priority;
	  pair = p;
157
	}
158 159
      }
    }
160 161 162

    if (pair) 
      break;
163 164 165
  }
  
  if (pair) {
166
    nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation);
167
    pair->state = NICE_CHECK_WAITING;
168
    nice_debug ("Agent %p : pair %p state WAITING", agent, pair);
169 170 171 172 173 174
    return TRUE;
  }

  return FALSE;
}

175
/*
176 177 178
 * Unfreezes the next next connectivity check in the list after
 * check 'success_check' has succesfully completed.
 *
179
 * See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-19).
180 181 182 183 184 185
 * 
 * @param agent context
 * @param ok_check a connectivity check that has just completed
 *
 * @return TRUE on success, and FALSE if no frozen candidates were found.
 */
186
static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, CandidateCheckPair *ok_check)
187 188 189 190 191 192
{
  GSList *i, *j;
  guint unfrozen = 0;

  g_assert (ok_check);
  g_assert (ok_check->state == NICE_CHECK_SUCCEEDED);
193 194
  g_assert (stream);
  g_assert (stream->id == ok_check->stream_id);
195 196

  /* step: perform the step (1) of 'Updating Pair States' */
197
  for (i = stream->conncheck_list; i ; i = i->next) {
198 199 200 201 202
    CandidateCheckPair *p = i->data;
   
    if (p->stream_id == ok_check->stream_id) {
      if (p->state == NICE_CHECK_FROZEN &&
	  strcmp (p->foundation, ok_check->foundation) == 0) {
203
	nice_debug ("Agent %p : Unfreezing check %p (after succesful check %p).", agent, p, ok_check);
204
	p->state = NICE_CHECK_WAITING;
205
        nice_debug ("Agent %p : pair %p state WAITING", agent, p);
206
	++unfrozen;
207 208 209 210 211 212 213 214 215 216
      }
    }
  }

  /* step: perform the step (2) of 'Updating Pair States' */
  stream = agent_find_stream (agent, ok_check->stream_id);
  if (stream_all_components_ready (stream)) {
    /* step: unfreeze checks from other streams */
    for (i = agent->streams; i ; i = i->next) {
      Stream *s = i->data;
217
      for (j = stream->conncheck_list; j ; j = j->next) {
218 219 220 221 222 223
	CandidateCheckPair *p = j->data;

	if (p->stream_id == s->id &&
	    p->stream_id != ok_check->stream_id) {
	  if (p->state == NICE_CHECK_FROZEN &&
	      strcmp (p->foundation, ok_check->foundation) == 0) {
224
	    nice_debug ("Agent %p : Unfreezing check %p from stream %u (after succesful check %p).", agent, p, s->id, ok_check);
225
	    p->state = NICE_CHECK_WAITING;
226
            nice_debug ("Agent %p : pair %p state WAITING", agent, p);
227 228 229 230 231 232 233 234 235 236 237 238
	    ++unfrozen;
					    
	  }
	}
      }
      /* note: only unfreeze check from one stream at a time */
      if (unfrozen)
	break;
    }
  }    

  if (unfrozen == 0) 
239
    priv_conn_check_unfreeze_next (agent);
240 241
}

242
/*
243 244 245 246 247 248 249 250 251 252
 * 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.
 */
static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, GTimeVal *now)
{
  gboolean keep_timer_going = FALSE;
253 254
  guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0,
      s_nominated = 0, s_waiting_for_nomination = 0;
255 256 257 258 259
  guint frozen = 0, waiting = 0;
  GSList *i, *k;

  for (i = stream->conncheck_list; i ; i = i->next) {
    CandidateCheckPair *p = i->data;
260

261
    if (p->state == NICE_CHECK_IN_PROGRESS) {
262
      if (p->stun_message.buffer == NULL) {
263
	nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent);
264
	p->state = NICE_CHECK_FAILED;
265
        nice_debug ("Agent %p : pair %p state FAILED", agent, p);
266
      } else if (priv_timer_expired (&p->next_tick, now)) {
267 268
        switch (stun_timer_refresh (&p->timer)) {
          case STUN_USAGE_TIMER_RETURN_TIMEOUT:
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
            {
              /* case: error, abort processing */
              StunTransactionId id;

              nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p);
              p->state = NICE_CHECK_FAILED;
              nice_debug ("Agent %p : pair %p state FAILED", agent, p);

              stun_message_id (&p->stun_message, id);
              stun_agent_forget_transaction (&agent->stun_agent, id);

              p->stun_message.buffer = NULL;
              p->stun_message.buffer_len = 0;


              break;
            }
286
          case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
287 288 289
            {
              /* case: not ready, so schedule a new timeout */
              unsigned int timeout = stun_timer_remainder (&p->timer);
290
              nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).",
291 292
                  agent, timeout);

293
              nice_socket_send (p->local->sockptr, &p->remote->addr,
294 295 296 297 298 299 300 301
                  stun_message_length (&p->stun_message),
                  (gchar *)p->stun_buffer);


              /* note: convert from milli to microseconds for g_time_val_add() */
              p->next_tick = *now;
              g_time_val_add (&p->next_tick, timeout * 1000);

302 303 304
              keep_timer_going = TRUE;
              break;
            }
305
          case STUN_USAGE_TIMER_RETURN_SUCCESS:
306
            {
307 308
              unsigned int timeout = stun_timer_remainder (&p->timer);

309 310 311 312
              /* note: convert from milli to microseconds for g_time_val_add() */
              p->next_tick = *now;
              g_time_val_add (&p->next_tick, timeout * 1000);

313 314 315 316
              keep_timer_going = TRUE;
              break;
            }
        }
317 318
      }
    }
319

320 321 322 323 324 325 326 327
    if (p->state == NICE_CHECK_FROZEN)
      ++frozen;
    else if (p->state == NICE_CHECK_IN_PROGRESS)
      ++s_inprogress;
    else if (p->state == NICE_CHECK_WAITING)
      ++waiting;
    else if (p->state == NICE_CHECK_SUCCEEDED)
      ++s_succeeded;
328 329
    else if (p->state == NICE_CHECK_DISCOVERED)
      ++s_discovered;
330

331 332
    if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
        && p->nominated)
333
      ++s_nominated;
334 335
    else if ((p->state == NICE_CHECK_SUCCEEDED ||
            p->state == NICE_CHECK_DISCOVERED) && !p->nominated)
336
      ++s_waiting_for_nomination;
337
  }
338

339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
    /* note: keep the timer going as long as there is work to be done */
  if (s_inprogress)
    keep_timer_going = TRUE;
  
    /* note: if some components have established connectivity,
     *       but yet no nominated pair, keep timer going */
  if (s_nominated < stream->n_components &&
      s_waiting_for_nomination) {
    keep_timer_going = TRUE;
    if (agent->controlling_mode) {
      guint n;
      for (n = 0; n < stream->n_components; n++) {
	for (k = stream->conncheck_list; k ; k = k->next) {
	  CandidateCheckPair *p = k->data;
	  /* note: highest priority item selected (list always sorted) */
	  if (p->state == NICE_CHECK_SUCCEEDED ||
	      p->state == NICE_CHECK_DISCOVERED) {
356
	    nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p);
357
	    p->nominated = TRUE;
358
	    priv_conn_check_initiate (agent, p);
359 360 361 362 363 364
	    break; /* move to the next component */
	  }
	}
      }
    }
  }
365
    {
366 367
    static int tick_counter = 0;
    if (tick_counter++ % 50 == 0 || keep_timer_going != TRUE)
368 369 370 371 372
      nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, "
          "%u waiting, %u succeeded, %u discovered, %u nominated, "
          "%u waiting-for-nom.", agent,
          tick_counter, frozen, s_inprogress, waiting, s_succeeded,
          s_discovered, s_nominated, s_waiting_for_nomination);
373 374 375 376 377 378
  }

  return keep_timer_going;

}

379

380
/*
381 382 383 384 385 386 387
 * Timer callback that handles initiating and managing connectivity
 * checks (paced by the Ta timer).
 *
 * This function is designed for the g_timeout_add() interface.
 *
 * @return will return FALSE when no more pending timers.
 */
388
static gboolean priv_conn_check_tick_unlocked (gpointer pointer)
389 390 391 392
{
  CandidateCheckPair *pair = NULL;
  NiceAgent *agent = pointer;
  gboolean keep_timer_going = FALSE;
393
  GSList *i, *j;
394
  GTimeVal now;
395 396 397

  /* step: process ongoing STUN transactions */
  g_get_current_time (&now);
398

399 400 401
  /* step: find the highest priority waiting check and send it */
  for (i = agent->streams; i ; i = i->next) {
    Stream *stream = i->data;
402

403
    pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
404 405 406
    if (pair)
      break;
  }
407 408 409 410

  if (pair) {
    priv_conn_check_initiate (agent, pair);
    keep_timer_going = TRUE;
411 412
  } else {
    keep_timer_going = priv_conn_check_unfreeze_next (agent);
413 414
  }

415 416
  for (j = agent->streams; j; j = j->next) {
    Stream *stream = j->data;
417 418 419 420
    gboolean res =
      priv_conn_check_tick_stream (stream, agent, &now);
    if (res)
      keep_timer_going = res;
421
  }
422

423
  /* step: stop timer if no work left */
424
  if (keep_timer_going != TRUE) {
425
    nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
426 427
    for (i = agent->streams; i; i = i->next) {
      Stream *stream = i->data;
428
      priv_update_check_list_failed_components (agent, stream);
429 430 431 432
      for (j = stream->components; j; j = j->next) {
        Component *component = j->data;
        priv_update_check_list_state_for_ready (agent, stream, component);
      }
433
      stream->conncheck_state = NICE_CHECKLIST_COMPLETED;
434
    }
435 436 437 438 439 440 441 442 443 444

    /* Stopping the timer so destroy the source.. this will allow
       the timer to be reset if we get a set_remote_candidates after this
       point */
    if (agent->conncheck_timer_source != NULL) {
      g_source_destroy (agent->conncheck_timer_source);
      g_source_unref (agent->conncheck_timer_source);
      agent->conncheck_timer_source = NULL;
    }

445
    /* XXX: what to signal, is all processing now really done? */
446
    nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
447 448 449 450 451
  }

  return keep_timer_going;
}

452 453 454 455
static gboolean priv_conn_check_tick (gpointer pointer)
{
  gboolean ret;

456 457 458 459 460 461 462
  agent_lock();
  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in priv_conn_check_tick");
    agent_unlock ();
    return FALSE;
  }
463
  ret = priv_conn_check_tick_unlocked (pointer);
464
  agent_unlock();
465 466 467 468

  return ret;
}

469 470 471 472
static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer)
{
  CandidatePair *pair = (CandidatePair *) pointer;

473
  agent_lock();
474

475 476 477 478
  /* A race condition might happen where the mutex above waits for the lock
   * and in the meantime another thread destroys the source.
   * In that case, we don't need to run our retransmission tick since it should
   * have been cancelled */
479 480 481 482
  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in priv_conn_keepalive_retransmissions_tick");
    agent_unlock ();
483 484 485
    return FALSE;
  }

486 487 488 489 490 491
  g_source_destroy (pair->keepalive.tick_source);
  g_source_unref (pair->keepalive.tick_source);
  pair->keepalive.tick_source = NULL;

  switch (stun_timer_refresh (&pair->keepalive.timer)) {
    case STUN_USAGE_TIMER_RETURN_TIMEOUT:
492 493 494 495 496 497 498 499
      {
        /* Time out */
        StunTransactionId id;


        stun_message_id (&pair->keepalive.stun_message, id);
        stun_agent_forget_transaction (&pair->keepalive.agent->stun_agent, id);

500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
        if (pair->keepalive.agent->media_after_tick) {
          nice_debug ("Agent %p : Keepalive conncheck timed out!! "
              "but media was received. Suspecting keepalive lost because of "
              "network bottleneck", pair->keepalive.agent);

          if (pair->keepalive.tick_source) {
            g_source_destroy (pair->keepalive.tick_source);
            g_source_unref (pair->keepalive.tick_source);
            pair->keepalive.tick_source = NULL;
          }
          pair->keepalive.stun_message.buffer = NULL;

        } else {
          nice_debug ("Agent %p : Keepalive conncheck timed out!! "
              "peer probably lost connection", pair->keepalive.agent);
          agent_signal_component_state_change (pair->keepalive.agent,
              pair->keepalive.stream_id, pair->keepalive.component_id,
              NICE_COMPONENT_STATE_FAILED);
        }
519 520
        break;
      }
521 522 523 524 525 526
    case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
      /* Retransmit */
      nice_socket_send (pair->local->sockptr, &pair->remote->addr,
          stun_message_length (&pair->keepalive.stun_message),
          (gchar *)pair->keepalive.stun_buffer);

527 528
      nice_debug ("Agent %p : Retransmitting keepalive conncheck",
          pair->keepalive.agent);
529 530 531 532 533 534 535 536 537 538 539 540 541 542
      pair->keepalive.tick_source =
          agent_timeout_add_with_context (pair->keepalive.agent,
          stun_timer_remainder (&pair->keepalive.timer),
          priv_conn_keepalive_retransmissions_tick, pair);
      break;
    case STUN_USAGE_TIMER_RETURN_SUCCESS:
      pair->keepalive.tick_source =
          agent_timeout_add_with_context (pair->keepalive.agent,
          stun_timer_remainder (&pair->keepalive.timer),
          priv_conn_keepalive_retransmissions_tick, pair);
      break;
  }


543
  agent_unlock ();
544 545 546 547
  return FALSE;
}


548
/*
549 550 551 552 553 554 555
 * Timer callback that handles initiating and managing connectivity
 * checks (paced by the Ta timer).
 *
 * This function is designed for the g_timeout_add() interface.
 *
 * @return will return FALSE when no more pending timers.
 */
556
static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
557
{
558
  GSList *i, *j, *k;
559
  int errors = 0;
560
  gboolean ret = FALSE;
561
  size_t buf_len = 0;
562 563

  /* case 1: session established and media flowing
564
   *         (ref ICE sect 10 "Keepalives" ID-19)  */
565
  for (i = agent->streams; i; i = i->next) {
566

567
    Stream *stream = i->data;
568 569
    for (j = stream->components; j; j = j->next) {
      Component *component = j->data;
570
      if (component->selected_pair.local != NULL) {
571
	CandidatePair *p = &component->selected_pair;
572

573
        if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
          guint32 priority = nice_candidate_ice_priority_full (
                  NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 1,
                  p->local->component_id);
          uint8_t uname[NICE_STREAM_MAX_UNAME];
          size_t uname_len =
              priv_create_username (agent, agent_find_stream (agent, stream->id),
                  component->id, p->remote, p->local, uname, sizeof (uname),
                  FALSE);
          uint8_t *password = NULL;
          size_t password_len = priv_get_password (agent,
              agent_find_stream (agent, stream->id), p->remote, &password);
          gchar tmpbuf[INET6_ADDRSTRLEN];

          nice_address_to_string (&p->remote->addr, tmpbuf);
          nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
              "socket=%u (c-id:%u), username='%s' (%d), "
              "password='%s' (%d), priority=%u.", agent,
591
              tmpbuf, nice_address_get_port (&p->remote->addr),
592
              ((NiceSocket *)p->local->sockptr)->fileno, component->id,
593 594 595 596 597 598 599 600 601 602 603 604 605 606
              uname, uname_len, password, password_len, priority);

          if (uname_len > 0) {
            buf_len = stun_usage_ice_conncheck_create (&agent->stun_agent,
                &p->keepalive.stun_message, p->keepalive.stun_buffer,
                sizeof(p->keepalive.stun_buffer),
                uname, uname_len, password, password_len,
                agent->controlling_mode, agent->controlling_mode, priority,
                agent->tie_breaker,
                agent_to_ice_compatibility (agent));

            nice_debug ("Agent %p: conncheck created %d - %p",
                agent, buf_len, p->keepalive.stun_message.buffer);

607 608
            if (buf_len > 0) {
              stun_timer_start (&p->keepalive.timer);
609

610 611
              agent->media_after_tick = FALSE;

612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
              /* send the conncheck */
              nice_socket_send (p->local->sockptr, &p->remote->addr,
                  buf_len, (gchar *)p->keepalive.stun_buffer);

              if (p->keepalive.tick_source != NULL) {
                g_source_destroy (p->keepalive.tick_source);
                g_source_unref (p->keepalive.tick_source);
                p->keepalive.tick_source = NULL;
              }

              p->keepalive.stream_id = stream->id;
              p->keepalive.component_id = component->id;
              p->keepalive.agent = agent;

              p->keepalive.tick_source =
                  agent_timeout_add_with_context (p->keepalive.agent,
                      stun_timer_remainder (&p->keepalive.timer),
                      priv_conn_keepalive_retransmissions_tick, p);
            } else {
              ++errors;
632 633 634 635 636 637 638
            }
          }
        } else {
          buf_len = stun_usage_bind_keepalive (&agent->stun_agent,
              &p->keepalive.stun_message, p->keepalive.stun_buffer,
              sizeof(p->keepalive.stun_buffer));

639 640 641
          if (buf_len > 0) {
            nice_socket_send (p->local->sockptr, &p->remote->addr, buf_len,
                (gchar *)p->keepalive.stun_buffer);
642

643 644 645
            nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.",
                agent, p, (int) buf_len);
          } else {
646
            ++errors;
647
          }
648
        }
649
      }
650 651
    }
  }
652

653
  /* case 2: connectivity establishment ongoing
654
   *         (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19)  */
655 656
  for (i = agent->streams; i; i = i->next) {
    Stream *stream = i->data;
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
    for (j = stream->components; j; j = j->next) {
      Component *component = j->data;
      if (component->state < NICE_COMPONENT_STATE_READY &&
          agent->stun_server_ip) {
        NiceAddress stun_server;
        if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
          StunAgent stun_agent;
          uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE];
          StunMessage stun_message;
          size_t buffer_len = 0;

          nice_address_set_port (&stun_server, agent->stun_server_port);

          stun_agent_init (&stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
              STUN_COMPATIBILITY_RFC3489, 0);

          buffer_len = stun_usage_bind_create (&stun_agent,
              &stun_message, stun_buffer, sizeof(stun_buffer));

          for (k = component->local_candidates; k; k = k->next) {
            NiceCandidate *candidate = (NiceCandidate *) k->data;
            if (candidate->type == NICE_CANDIDATE_TYPE_HOST) {
              /* send the conncheck */
              nice_debug ("Agent %p : resending STUN on %s to keep the "
                  "candidate alive.", agent, candidate->foundation);
              nice_socket_send (candidate->sockptr, &stun_server,
                  buffer_len, (gchar *)stun_buffer);
            }
          }
        }
687 688 689
      }
    }
  }
690

691
  if (errors) {
692
    nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
693
    goto done;
694 695
  }

696 697 698
  ret = TRUE;

 done:
699 700 701 702 703 704 705 706
  return ret;
}

static gboolean priv_conn_keepalive_tick (gpointer pointer)
{
  NiceAgent *agent = pointer;
  gboolean ret;

707 708 709 710 711 712 713 714
  agent_lock();
  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in priv_conn_keepalive_tick");
    agent_unlock ();
    return FALSE;
  }

715
  ret = priv_conn_keepalive_tick_unlocked (agent);
716 717 718 719 720 721 722
  if (ret == FALSE) {
    if (agent->keepalive_timer_source) {
      g_source_destroy (agent->keepalive_timer_source);
      g_source_unref (agent->keepalive_timer_source);
      agent->keepalive_timer_source = NULL;
    }
  }
723
  agent_unlock();
724
  return ret;
725 726
}

727

728 729 730 731
static gboolean priv_turn_allocate_refresh_retransmissions_tick (gpointer pointer)
{
  CandidateRefresh *cand = (CandidateRefresh *) pointer;

732
  agent_lock();
733

734 735 736 737
  /* A race condition might happen where the mutex above waits for the lock
   * and in the meantime another thread destroys the source.
   * In that case, we don't need to run our retransmission tick since it should
   * have been cancelled */
738 739 740 741
  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in priv_turn_allocate_refresh_retransmissions_tick");
    agent_unlock ();
742 743 744
    return FALSE;
  }

745

746 747 748 749
  g_source_destroy (cand->tick_source);
  g_source_unref (cand->tick_source);
  cand->tick_source = NULL;

750 751
  switch (stun_timer_refresh (&cand->timer)) {
    case STUN_USAGE_TIMER_RETURN_TIMEOUT:
752 753 754 755 756 757 758 759 760 761
      {
        /* Time out */
        StunTransactionId id;

        stun_message_id (&cand->stun_message, id);
        stun_agent_forget_transaction (&cand->stun_agent, id);

        refresh_cancel (cand);
        break;
      }
762
    case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
763 764 765 766
      /* Retransmit */
      nice_socket_send (cand->nicesock, &cand->server,
          stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);

Youness Alaoui's avatar
Youness Alaoui committed
767 768
      cand->tick_source = agent_timeout_add_with_context (cand->agent,
          stun_timer_remainder (&cand->timer),
769 770
          priv_turn_allocate_refresh_retransmissions_tick, cand);
      break;
771
    case STUN_USAGE_TIMER_RETURN_SUCCESS:
Youness Alaoui's avatar
Youness Alaoui committed
772 773
      cand->tick_source = agent_timeout_add_with_context (cand->agent,
          stun_timer_remainder (&cand->timer),
774 775 776 777 778
          priv_turn_allocate_refresh_retransmissions_tick, cand);
      break;
  }


779
  agent_unlock ();
780 781 782 783 784 785 786 787 788 789 790
  return FALSE;
}

static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand)
{
  uint8_t *username;
  size_t username_len;
  uint8_t *password;
  size_t password_len;
  size_t buffer_len = 0;

791 792 793 794
  username = (uint8_t *)cand->turn->username;
  username_len = (size_t) strlen (cand->turn->username);
  password = (uint8_t *)cand->turn->password;
  password_len = (size_t) strlen (cand->turn->password);
795

796 797
  if (agent_to_turn_compatibility (cand->agent) ==
      STUN_USAGE_TURN_COMPATIBILITY_MSN) {
798 799 800 801
    username = g_base64_decode ((gchar *)username, &username_len);
    password = g_base64_decode ((gchar *)password, &password_len);
  }

802
  buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
803 804 805 806
      &cand->stun_message,  cand->stun_buffer, sizeof(cand->stun_buffer),
      cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, -1,
      username, username_len,
      password, password_len,
807
      agent_to_turn_compatibility (cand->agent));
808

809 810
  if (agent_to_turn_compatibility (cand->agent) ==
      STUN_USAGE_TURN_COMPATIBILITY_MSN) {
811 812 813 814 815 816
    g_free (cand->msn_turn_username);
    g_free (cand->msn_turn_password);
    cand->msn_turn_username = username;
    cand->msn_turn_password = password;
  }

817
  nice_debug ("Agent %p : Sending allocate Refresh %d", cand->agent, buffer_len);
818

819 820 821 822 823 824
  if (cand->tick_source != NULL) {
    g_source_destroy (cand->tick_source);
    g_source_unref (cand->tick_source);
    cand->tick_source = NULL;
  }

825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
  if (buffer_len > 0) {
    stun_timer_start (&cand->timer);

    /* send the refresh */
    nice_socket_send (cand->nicesock, &cand->server,
        buffer_len, (gchar *)cand->stun_buffer);

    cand->tick_source = agent_timeout_add_with_context (cand->agent,
        stun_timer_remainder (&cand->timer),
        priv_turn_allocate_refresh_retransmissions_tick, cand);
  }

}


840
/*
841 842 843 844 845 846 847 848 849 850
 * Timer callback that handles refreshing TURN allocations
 *
 * This function is designed for the g_timeout_add() interface.
 *
 * @return will return FALSE when no more pending timers.
 */
static gboolean priv_turn_allocate_refresh_tick (gpointer pointer)
{
  CandidateRefresh *cand = (CandidateRefresh *) pointer;

851 852 853 854 855 856 857 858
  agent_lock();
  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in priv_turn_allocate_refresh_tick");
    agent_unlock ();
    return FALSE;
  }

859
  priv_turn_allocate_refresh_tick_unlocked (cand);
860
  agent_unlock ();
861 862 863 864 865

  return FALSE;
}


866
/*
867
 * Initiates the next pending connectivity check.
868 869
 * 
 * @return TRUE if a pending check was scheduled
870
 */
871
gboolean conn_check_schedule_next (NiceAgent *agent)
872
{
873
  gboolean res = priv_conn_check_unfreeze_next (agent);
874
  nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent, res);
875

876
  if (agent->discovery_unsched_items > 0)
877
    nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent);
878

879 880 881
  /* step: call once imediately */
  res = priv_conn_check_tick_unlocked ((gpointer) agent);
  nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent, res);
882

883 884 885 886
  /* step: schedule timer if not running yet */
  if (res && agent->conncheck_timer_source == NULL) {
    agent->conncheck_timer_source = agent_timeout_add_with_context (agent, agent->timer_ta, priv_conn_check_tick, agent);
  }
887

888 889 890
  /* step: also start the keepalive timer */
  if (agent->keepalive_timer_source == NULL) {
    agent->keepalive_timer_source = agent_timeout_add_with_context (agent, NICE_AGENT_TIMER_TR_DEFAULT, priv_conn_keepalive_tick, agent);
891
  }
892

893
  nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent, res);
894
  return res;
895 896
}

897
/*
898 899 900 901 902 903 904 905 906 907 908 909 910
 * Compares two connectivity check items. Checkpairs are sorted
 * in descending priority order, with highest priority item at
 * the start of the list.
 */
gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b)
{
  if (a->priority > b->priority)
    return -1;
  else if (a->priority < b->priority)
    return 1;
  return 0;
}

911
/*
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
 * Preprocesses a new connectivity check by going through list 
 * of a any stored early incoming connectivity checks from 
 * the remote peer. If a matching incoming check has been already
 * received, update the state of the new outgoing check 'pair'.
 * 
 * @param agent context pointer
 * @param stream which stream (of the agent)
 * @param component pointer to component object to which 'pair'has been added
 * @param pair newly added connectivity check
 */
static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, Stream *stream, Component *component, CandidateCheckPair *pair)
{
  GSList *i;
  for (i = component->incoming_checks; i; i = i->next) {
    IncomingCheck *icheck = i->data;
    if (nice_address_equal (&icheck->from, &pair->remote->addr) &&
	icheck->local_socket == pair->local->sockptr) {
929
      nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id);
930 931 932 933 934 935 936
      if (icheck->use_candidate)
	priv_mark_pair_nominated (agent, stream, component, pair->remote);
      priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate);
    }
  }
}

937
/*
938 939 940 941 942 943 944 945 946
 * Handle any processing steps for connectivity checks after
 * remote candidates have been set. This function handles
 * the special case where answerer has sent us connectivity
 * checks before the answer (containing candidate information),
 * reaches us. The special case is documented in sect 7.2 
 * if ICE spec (ID-19).
 */
void conn_check_remote_candidates_set(NiceAgent *agent)
{
947
  GSList *i, *j, *k, *l, *m, *n;
948 949 950 951 952 953 954 955 956 957 958 959 960
  for (i = agent->streams; i ; i = i->next) {
    Stream *stream = i->data;
    for (j = stream->conncheck_list; j ; j = j->next) {
      CandidateCheckPair *pair = j->data;
      Component *component = stream_find_component_by_id (stream, pair->component_id);
      gboolean match = FALSE;
      
      /* performn delayed processing of spec steps section 7.2.1.4,
	 and section 7.2.1.5 */
      priv_preprocess_conn_check_pending_data (agent, stream, component, pair);

      for (k = component->incoming_checks; k; k = k->next) {
	IncomingCheck *icheck = k->data;
961
	/* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
962 963 964 965 966 967 968 969 970
	 * be handled separately */
	for (l = component->remote_candidates; l; l = l->next) {
	  NiceCandidate *cand = l->data;
	  if (nice_address_equal (&icheck->from, &cand->addr)) {
	    match = TRUE;
	    break;
	  }
	}
	if (match != TRUE) {
971
	  /* note: we have gotten an incoming connectivity check from
972
	   *       an address that is not a known remote candidate */
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032

          NiceCandidate *local_candidate = NULL;
          NiceCandidate *remote_candidate = NULL;

          if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
              agent->compatibility == NICE_COMPATIBILITY_MSN) {
            /* We need to find which local candidate was used */
            uint8_t uname[NICE_STREAM_MAX_UNAME];
            guint uname_len;

            nice_debug ("Agent %p: We have a peer-reflexive candidate in a "
                "stored pending check", agent);

            for (m = component->remote_candidates;
                 m != NULL && remote_candidate == NULL; m = m->next) {
              for (n = component->local_candidates; n; n = n->next) {
                NiceCandidate *rcand = m->data;
                NiceCandidate *lcand = n->data;

                uname_len = priv_create_username (agent, stream,
                    component->id,  rcand, lcand,
                    uname, sizeof (uname), TRUE);

                stun_debug ("pending check, comparing username '");
                stun_debug_bytes (icheck->username,
                    icheck->username? icheck->username_len : 0);
                stun_debug ("' (%d) with '", icheck->username_len);
                stun_debug_bytes (uname, uname_len);
                stun_debug ("' (%d) : %d\n",
                    uname_len, icheck->username &&
                    uname_len == icheck->username_len &&
                    memcmp (icheck->username, uname, uname_len) == 0);

                if (icheck->username &&
                    uname_len == icheck->username_len &&
                    memcmp (uname, icheck->username, icheck->username_len) == 0) {
                  local_candidate = lcand;
                  remote_candidate = rcand;
                  break;
                }
              }
            }
          }

          if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
              local_candidate == NULL) {
            /* if we couldn't match the username, then the matching remote
             * candidate hasn't been received yet.. we must wait */
            nice_debug ("Agent %p : Username check failed. pending check has "
                "to wait to be processed", agent);
          } else {
            NiceCandidate *candidate =
                discovery_learn_remote_peer_reflexive_candidate (agent,
                    stream,
                    component,
                    icheck->priority,
                    &icheck->from,
                    icheck->local_socket,
                    local_candidate, remote_candidate);
            if (candidate) {
1033 1034
              conn_check_add_for_candidate (agent, stream->id, component, candidate);

1035 1036 1037 1038
              priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate);
            }
          }
        }
1039
      }
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050

      /* Once we process the pending checks, we should free them to avoid
       * reprocessing them again if a dribble-mode set_remote_candidates
       * is called */
      for (m = component->incoming_checks; m; m = m->next) {
        IncomingCheck *icheck = m->data;
        g_free (icheck->username);
        g_slice_free (IncomingCheck, icheck);
      }
      g_slist_free (component->incoming_checks);
      component->incoming_checks = NULL;
1051 1052 1053 1054
    }
  }
}

1055
/*
1056
 * Enforces the upper limit for connectivity checks as described
1057
 * in ICE spec section 5.7.3 (ID-19). See also 
1058 1059 1060 1061 1062 1063 1064 1065 1066
 * conn_check_add_for_candidate().
 */
static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit)
{
  guint list_len = g_slist_length (conncheck_list);
  guint c = 0;
  GSList *result = conncheck_list;

  if (list_len > upper_limit) {
Youness Alaoui's avatar
Youness Alaoui committed
1067 1068
    nice_debug ("Agent : Pruning candidates. Conncheck list has %d elements. "
        "Maximum connchecks allowed : %d", list_len, upper_limit);
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
    c = list_len - upper_limit;
    if (c == list_len) {
      /* case: delete whole list */
      g_slist_foreach (conncheck_list, conn_check_free_item, NULL);
      g_slist_free (conncheck_list),
	result = NULL;
    }
    else {
      /* case: remove 'c' items from list end (lowest priority) */
      GSList *i, *tmp;

      g_assert (c > 0);
      i = g_slist_nth (conncheck_list, c - 1);

      tmp = i->next;
      i->next = NULL;

      if (tmp) {
	/* delete the rest of the connectivity check list */
	g_slist_foreach (tmp, conn_check_free_item, NULL);
	g_slist_free (tmp);
      }
    }
  }

  return result;
}

1097
/*
1098 1099 1100
 * 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).
1101
 */
1102 1103 1104 1105 1106
static gboolean priv_update_selected_pair (NiceAgent *agent, Component *component, CandidateCheckPair *pair)
{
  g_assert (component);
  g_assert (pair);
  if (pair->priority > component->selected_pair.priority) {
1107
    nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
1108 1109
        "(prio:%" G_GUINT64_FORMAT ").", agent, component->id, pair->local->foundation,
        pair->remote->foundation, pair->priority);
1110 1111 1112 1113 1114 1115 1116 1117

    if (component->selected_pair.keepalive.tick_source != NULL) {
      g_source_destroy (component->selected_pair.keepalive.tick_source);
      g_source_unref (component->selected_pair.keepalive.tick_source);
      component->selected_pair.keepalive.tick_source = NULL;
    }

    memset (&component->selected_pair, 0, sizeof(CandidatePair));
1118 1119 1120 1121
    component->selected_pair.local = pair->local;
    component->selected_pair.remote = pair->remote;
    component->selected_pair.priority = pair->priority;

1122 1123
    priv_conn_keepalive_tick_unlocked (agent);

1124
    agent_signal_new_selected_pair (agent, pair->stream_id, component->id, pair->local->foundation, pair->remote->foundation);
1125

1126 1127 1128 1129 1130
  }

  return TRUE;
}

1131
/*
1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
 * Updates the check list state.
 *
 * Implements parts of the algorithm described in 
 * ICE sect 8.1.2. "Updating States" (ID-19): if for any 
 * component, all checks have been completed and have
 * failed, mark that component's state to NICE_CHECK_FAILED.
 *
 * Sends a component state changesignal via 'agent'.
 */
static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream)
{
  GSList *i;
  /* note: emitting a signal might cause the client 
   *       to remove the stream, thus the component count
   *       must be fetched before entering the loop*/
  guint c, components = stream->n_components;

  /* note: iterate the conncheck list for each component separately */
  for (c = 0; c < components; c++) {
1151
    Component *comp = NULL;
1152 1153
    if (!agent_find_component (agent, stream->id, c+1, NULL, &comp))
      continue;
1154

1155 1156 1157 1158 1159 1160 1161 1162 1163
    for (i = stream->conncheck_list; i; i = i->next) {
      CandidateCheckPair *p = i->data;
      
      if (p->stream_id == stream->id &&
	  p->component_id == (c + 1)) {
	if (p->state != NICE_CHECK_FAILED)
	  break;
      }
    }
1164 1165 1166 1167 1168
 
    /* note: all checks have failed
     * Set the component to FAILED only if it actually had remote candidates
     * that failed.. */
    if (i == NULL && comp != NULL && comp->remote_candidates != NULL)
1169 1170 1171 1172 1173 1174 1175
      agent_signal_component_state_change (agent, 
					   stream->id,
					   (c + 1), /* component-id */
					   NICE_COMPONENT_STATE_FAILED);
  }
}

1176
/*
1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
 * Updates the check list state for a stream component.
 *
 * Implements the algorithm described in ICE sect 8.1.2 
 * "Updating States" (ID-19) as it applies to checks of 
 * a certain component. If there are any nominated pairs, 
 * ICE processing may be concluded, and component state is 
 * changed to READY.
 *
 * Sends a component state changesignal via 'agent'.
 */
static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component)
{
  GSList *i;
  guint succeeded = 0, nominated = 0;

  g_assert (component);

  /* step: search for at least one nominated pair */
  for (i = stream->conncheck_list; i; i = i->next) {
    CandidateCheckPair *p = i->data;
    if (p->component_id == component->id) {
      if (p->state == NICE_CHECK_SUCCEEDED ||
	  p->state == NICE_CHECK_DISCOVERED) {
	++succeeded;
	if (p->nominated == TRUE) {
1202
          ++nominated;
1203 1204 1205 1206
	}
      }
    }
  }
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216

  if (nominated > 0) {
    /* 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 (stream, component->id) == 0) {
      agent_signal_component_state_change (agent, stream->id,
          component->id, NICE_COMPONENT_STATE_READY);
    }
  }
1217
  nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent, nominated, succeeded, component->id);
1218 1219
}

1220
/*
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237
 * The remote party has signalled that the candidate pair
 * described by 'component' and 'remotecand' is nominated
 * for use.
 */
static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand)
{
  GSList *i;

  g_assert (component);

  /* step: search for at least one nominated pair */
  for (i = stream->conncheck_list; i; i = i->next) {
    CandidateCheckPair *pair = i->data;
    /* XXX: hmm, how to figure out to which local candidate the 
     *      check was sent to? let's mark all matching pairs
     *      as nominated instead */
    if (pair->remote == remotecand) {
1238
      nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation);
1239 1240 1241 1242 1243 1244 1245 1246 1247
      pair->nominated = TRUE;
      if (pair->state == NICE_CHECK_SUCCEEDED ||
	  pair->state == NICE_CHECK_DISCOVERED)
	priv_update_selected_pair (agent, component, pair);
      priv_update_check_list_state_for_ready (agent, stream, component);
    }
  }
}

1248
/*
1249 1250 1251
 * Creates a new connectivity check pair and adds it to
 * the agent's list of checks.
 */
1252 1253 1254
static gboolean priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate)
{
  gboolean result = FALSE;
1255
  Stream *stream = agent_find_stream (agent, stream_id);
1256 1257
  CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
  if (pair) {
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
    GSList *modified_list = 
      g_slist_insert_sorted (stream->conncheck_list, pair, (GCompareFunc)conn_check_compare);
    if (modified_list) {
      /* step: allocation and addition succesful, do rest of the work */

      pair->agent = agent;
      pair->stream_id = stream_id;
      pair->component_id = component->id;;
      pair->local = local; 
      pair->remote = remote;
      g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation);

      pair->priority = agent_candidate_pair_priority (agent, local, remote);
      pair->state = initial_state;
1272
      nice_debug ("Agent %p : creating new pair %p state %d", agent, pair, initial_state);
1273 1274 1275 1276 1277 1278 1279 1280 1281
      pair->nominated = use_candidate;
      pair->controlling = agent->controlling_mode;
      
      /* note: for the first added check */
      if (!stream->conncheck_list)
	stream->conncheck_state = NICE_CHECKLIST_RUNNING;
      stream->conncheck_list = modified_list;

      result = TRUE;
1282
      nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent, pair, pair->foundation, stream_id);
1283 1284 1285

      /* implement the hard upper limit for number of 
	 checks (see sect 5.7.3 ICE ID-19): */
1286
      if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) {
1287 1288 1289
        stream->conncheck_list = 
            priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks);
      }
1290
      if (!stream->conncheck_list) {
1291
	stream->conncheck_state = NICE_CHECKLIST_FAILED;  
1292
	result = FALSE;
1293
      }
1294 1295 1296 1297 1298 1299
    }
    else {
      /* memory alloc failed: list insert */
      conn_check_free_item (pair, NULL);
      stream->conncheck_state = NICE_CHECKLIST_FAILED;  
    }
1300 1301
  }
  else { /* memory alloc failed: new pair */
1302
    stream->conncheck_state = NICE_CHECKLIST_FAILED;
1303 1304 1305 1306 1307
  }

  return result;
}

1308
/*
1309 1310
 * Forms new candidate pairs by matching the new remote candidate
 * 'remote_cand' with all existing local candidates of 'component'.
1311
 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
1312
 * Pairs" (ID-19).
1313 1314 1315 1316 1317
 *
 * @param agent context
 * @param component pointer to the component
 * @param remote remote candidate to match with
 *
1318
 * @return number of checks added, negative on fatal errors
1319 1320 1321 1322
 */
int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote)
{
  GSList *i;
1323
  int added = 0; 
1324 1325 1326 1327 1328

  for (i = component->local_candidates; i ; i = i->next) {

    NiceCandidate *local = i->data;

1329 1330
    /* note: match pairs only if transport and address family are the same */
    if (local->transport == remote->transport &&
1331
	local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family) {
1332

1333
      gboolean result;
1334

1335
      /* note: do not create pairs where local candidate is 
1336
       *       a srv-reflexive (ICE 5.7.3. "Pruning the Pairs" ID-19) */
1337
      if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
1338
              agent->compatibility == NICE_COMPATIBILITY_WLM2009) &&
1339
          local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE)
1340
	continue;
1341

1342 1343 1344
      result = priv_add_new_check_pair (agent, stream_id, component, local, remote, NICE_CHECK_FROZEN, FALSE);
      if (result) {
	++added;
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
        if (component->state < NICE_COMPONENT_STATE_CONNECTED) {
          agent_signal_component_state_change (agent,
              stream_id,
              component->id,
              NICE_COMPONENT_STATE_CONNECTING);
        } else {
          agent_signal_component_state_change (agent,
              stream_id,
              component->id,
              NICE_COMPONENT_STATE_CONNECTED);
        }
1356 1357 1358 1359 1360
      }
      else {
	added = -1;
	break;
      }
1361 1362 1363
    }
  }

1364
  return added;
1365 1366
}

1367
/*
1368 1369 1370 1371 1372 1373 1374
 * Frees the CandidateCheckPair structure pointer to 
 * by 'user data'. Compatible with g_slist_foreach().
 */
void conn_check_free_item (gpointer data, gpointer user_data)
{
  CandidateCheckPair *pair = data;
  g_assert (user_data == NULL);
1375 1376
  pair->stun_message.buffer = NULL;
  pair->stun_message.buffer_len = 0;
1377 1378 1379
  g_slice_free (CandidateCheckPair, pair);
}

1380
/*
1381
 * Frees all resources of all connectivity checks.
1382 1383 1384
 */
void conn_check_free (NiceAgent *agent)
{
1385 1386 1387 1388
  GSList *i;
  for (i = agent->streams; i; i = i->next) {
    Stream *stream = i->data;

1389
    nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, stream);
1390 1391 1392 1393 1394 1395
    if (stream->conncheck_list) {
      g_slist_foreach (stream->conncheck_list, conn_check_free_item, NULL);
      g_slist_free (stream->conncheck_list),
	stream->conncheck_list = NULL;
      stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
    }
1396
  }
1397

1398 1399 1400 1401
  if (agent->conncheck_timer_source != NULL) {
    g_source_destroy (agent->conncheck_timer_source);
    g_source_unref (agent->conncheck_timer_source);
    agent->conncheck_timer_source = NULL;
1402
  }
1403 1404
}

1405
/*
1406 1407 1408 1409 1410
 * Prunes the list of connectivity checks for items related
 * to stream 'stream_id'. 
 *
 * @return TRUE on success, FALSE on a fatal error
 */
1411
gboolean conn_check_prune_stream (NiceAgent *agent, Stream *stream)
1412 1413 1414 1415
{
  CandidateCheckPair *pair;
  GSList *i;

1416 1417
  for (i = stream->conncheck_list; i ; ) {
    GSList *next = i->next;
1418 1419
    pair = i->data;

1420 1421 1422 1423 1424 1425 1426 1427
    g_assert (pair->stream_id == stream->id);

    stream->conncheck_list = 
      g_slist_remove (stream->conncheck_list, pair);
    conn_check_free_item (pair, NULL);
    i = next;
    if (!stream->conncheck_list)
      break;
1428 1429
  }

1430 1431
  if (!stream->conncheck_list) {
    stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
1432 1433
    conn_check_free (agent);
  }
1434 1435

  /* return FALSE if there was a memory allocation failure */
1436
  if (stream->conncheck_list == NULL && i != NULL)
1437 1438 1439 1440 1441
    return FALSE;

  return TRUE;
}

1442
/*
1443 1444 1445 1446 1447 1448
 * Fills 'dest' with a username string for use in an outbound connectivity
 * checks. No more than 'dest_len' characters (including terminating
 * NULL) is ever written to the 'dest'.
 */
static
size_t priv_gen_username (NiceAgent *agent, guint component_id,
1449
    gchar *remote, gchar *local, uint8_t *dest, guint dest_len)
1450 1451
{
  guint len = 0;
1452 1453
  gsize remote_len = strlen (remote);
  gsize local_len = strlen (local);
1454

1455
  if (remote_len > 0 && local_len > 0) {
1456
    if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
1457 1458 1459
        dest_len >= remote_len + local_len + 1) {
      memcpy (dest, remote, remote_len);
      len += remote_len;
1460 1461
      memcpy (dest + len, ":", 1);
      len++;
1462 1463
      memcpy (dest + len, local, local_len);
      len += local_len;
1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475
    } else if (agent->compatibility == NICE_COMPATIBILITY_WLM2009 &&
        dest_len >= remote_len + local_len + 4 ) {
      memcpy (dest, remote, remote_len);
      len += remote_len;
      memcpy (dest + len, ":", 1);
      len++;
      memcpy (dest + len, local, local_len);
      len += local_len;
      if (len % 4 != 0) {
        memset (dest + len, 0, 4 - (len % 4));
        len += 4 - (len % 4);
      }
1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490
    } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
        dest_len >= remote_len + local_len) {
      memcpy (dest, remote, remote_len);
      len += remote_len;
      memcpy (dest + len, local, local_len);
      len += local_len;
    } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
      gchar component_str[10];
      guchar *local_decoded = NULL;
      guchar *remote_decoded = NULL;
      gsize local_decoded_len;
      gsize remote_decoded_len;
      gsize total_len;
      int padding;

1491
      g_snprintf (component_str, sizeof(component_str), "%d", component_id);
1492 1493 14