conncheck.c 97.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 * This file is part of the Nice GLib ICE library.
 *
 * (C) 2006, 2007 Collabora Ltd.
 *  Contact: Dafydd Harries
 * (C) 2006, 2007 Nokia Corporation. All rights reserved.
 *  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
 *   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.
 */

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

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

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

#include <glib.h>

53 54
#include "debug.h"

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

63
static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream);
64
static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component);
65
static guint priv_prune_pending_checks (Stream *stream, guint component_id);
66
static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate);
67
static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand);
68 69 70 71 72
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);
73

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

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

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

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

  return NULL;
}

100
/*
101 102 103 104 105 106
 * 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)
{
107 108 109 110
  /* 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"
   */
111
  g_get_current_time (&pair->next_tick);
112
  g_time_val_add (&pair->next_tick, agent->timer_ta * 1000);
113
  pair->state = NICE_CHECK_IN_PROGRESS;
114
  nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair);
115 116 117 118
  conn_check_send (agent, pair);
  return TRUE;
}

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

134 135 136 137 138 139 140
  /* 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)
   */

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

145

146 147 148 149 150 151 152 153 154 155
    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;
156
	}
157 158
      }
    }
159 160 161

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

  return FALSE;
}

174
/*
175 176 177
 * Unfreezes the next next connectivity check in the list after
 * check 'success_check' has succesfully completed.
 *
178
 * See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-19).
179 180 181 182 183 184
 * 
 * @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.
 */
185
static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, CandidateCheckPair *ok_check)
186 187 188 189 190 191
{
  GSList *i, *j;
  guint unfrozen = 0;

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

  /* step: perform the step (1) of 'Updating Pair States' */
196
  for (i = stream->conncheck_list; i ; i = i->next) {
197 198 199 200 201
    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) {
202
	nice_debug ("Agent %p : Unfreezing check %p (after succesful check %p).", agent, p, ok_check);
203
	p->state = NICE_CHECK_WAITING;
204
        nice_debug ("Agent %p : pair %p state WAITING", agent, p);
205
	++unfrozen;
206 207 208 209 210 211 212 213 214 215
      }
    }
  }

  /* 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;
216
      for (j = stream->conncheck_list; j ; j = j->next) {
217 218 219 220 221 222
	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) {
223
	    nice_debug ("Agent %p : Unfreezing check %p from stream %u (after succesful check %p).", agent, p, s->id, ok_check);
224
	    p->state = NICE_CHECK_WAITING;
225
            nice_debug ("Agent %p : pair %p state WAITING", agent, p);
226 227 228 229 230 231 232 233 234 235 236 237
	    ++unfrozen;
					    
	  }
	}
      }
      /* note: only unfreeze check from one stream at a time */
      if (unfrozen)
	break;
    }
  }    

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

241
/*
242 243 244 245 246 247 248 249 250 251
 * 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;
252 253
  guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0,
      s_nominated = 0, s_waiting_for_nomination = 0;
254 255 256 257 258
  guint frozen = 0, waiting = 0;
  GSList *i, *k;

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

260
    if (p->state == NICE_CHECK_IN_PROGRESS) {
261
      if (p->stun_message.buffer == NULL) {
262
	nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent);
263
	p->state = NICE_CHECK_FAILED;
264
        nice_debug ("Agent %p : pair %p state FAILED", agent, p);
265
      } else if (priv_timer_expired (&p->next_tick, now)) {
266 267
        switch (stun_timer_refresh (&p->timer)) {
          case STUN_USAGE_TIMER_RETURN_TIMEOUT:
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
            {
              /* 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;
            }
285
          case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
286 287 288
            {
              /* case: not ready, so schedule a new timeout */
              unsigned int timeout = stun_timer_remainder (&p->timer);
289
              nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).",
290 291
                  agent, timeout);

292
              nice_socket_send (p->local->sockptr, &p->remote->addr,
293 294 295 296 297 298 299 300
                  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);

Youness Alaoui's avatar
Youness Alaoui committed
301 302 303
              keep_timer_going = TRUE;
              break;
            }
304
          case STUN_USAGE_TIMER_RETURN_SUCCESS:
Youness Alaoui's avatar
Youness Alaoui committed
305
            {
306 307
              unsigned int timeout = stun_timer_remainder (&p->timer);

Youness Alaoui's avatar
Youness Alaoui committed
308 309 310 311
              /* note: convert from milli to microseconds for g_time_val_add() */
              p->next_tick = *now;
              g_time_val_add (&p->next_tick, timeout * 1000);

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

319 320 321 322 323 324 325 326
    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;
327 328
    else if (p->state == NICE_CHECK_DISCOVERED)
      ++s_discovered;
329

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

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
    /* 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) {
355
	    nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p);
356
	    p->nominated = TRUE;
357
	    priv_conn_check_initiate (agent, p);
358 359 360 361 362 363
	    break; /* move to the next component */
	  }
	}
      }
    }
  }
364
    {
365 366
    static int tick_counter = 0;
    if (tick_counter++ % 50 == 0 || keep_timer_going != TRUE)
367 368 369 370 371
      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);
372 373 374 375 376 377
  }

  return keep_timer_going;

}

Youness Alaoui's avatar
Youness Alaoui committed
378

379
/*
380 381 382 383 384 385 386
 * 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.
 */
Youness Alaoui's avatar
Youness Alaoui committed
387
static gboolean priv_conn_check_tick_unlocked (gpointer pointer)
388 389 390 391
{
  CandidateCheckPair *pair = NULL;
  NiceAgent *agent = pointer;
  gboolean keep_timer_going = FALSE;
392
  GSList *i, *j;
393
  GTimeVal now;
394 395 396

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

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

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

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

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

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

    /* 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;
    }

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

  return keep_timer_going;
}

Youness Alaoui's avatar
Youness Alaoui committed
451 452 453 454
static gboolean priv_conn_check_tick (gpointer pointer)
{
  gboolean ret;

455 456 457 458 459 460 461
  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;
  }
Youness Alaoui's avatar
Youness Alaoui committed
462
  ret = priv_conn_check_tick_unlocked (pointer);
463
  agent_unlock();
Youness Alaoui's avatar
Youness Alaoui committed
464 465 466 467

  return ret;
}

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

472
  agent_lock();
473

474 475 476 477
  /* 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 */
478 479 480 481
  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 ();
482 483 484
    return FALSE;
  }

485 486 487 488 489 490
  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:
491 492 493 494 495 496 497 498
      {
        /* Time out */
        StunTransactionId id;


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

499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
        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);
        }
518 519
        break;
      }
520 521 522 523 524 525
    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);

526 527
      nice_debug ("Agent %p : Retransmitting keepalive conncheck",
          pair->keepalive.agent);
528 529 530 531 532 533 534 535 536 537 538 539 540 541
      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;
  }


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


547
/*
548 549 550 551 552 553 554
 * 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.
 */
555
static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
556
{
557
  GSList *i, *j;
558
  int errors = 0;
559
  gboolean ret = FALSE;
560
  size_t buf_len = 0;
561 562

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

566
    Stream *stream = i->data;
567 568
    for (j = stream->components; j; j = j->next) {
      Component *component = j->data;
569
      if (component->selected_pair.local != NULL) {
570 571
	CandidatePair *p = &component->selected_pair;
	struct sockaddr sockaddr;
Youness Alaoui's avatar
Youness Alaoui committed
572

573 574
	memset (&sockaddr, 0, sizeof (sockaddr));
	nice_address_copy_to_sockaddr (&p->remote->addr, &sockaddr);
Youness Alaoui's avatar
Youness Alaoui committed
575

576
        if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
          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,
              tmpbuf, ntohs(((struct sockaddr_in*)(&sockaddr))->sin_port),
              p->local->sockptr->fileno, component->id,
              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);

610 611
            if (buf_len > 0) {
              stun_timer_start (&p->keepalive.timer);
612

613 614
              agent->media_after_tick = FALSE;

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
              /* 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;
635 636 637 638 639 640 641
            }
          }
        } else {
          buf_len = stun_usage_bind_keepalive (&agent->stun_agent,
              &p->keepalive.stun_message, p->keepalive.stun_buffer,
              sizeof(p->keepalive.stun_buffer));

642 643 644
          if (buf_len > 0) {
            nice_socket_send (p->local->sockptr, &p->remote->addr, buf_len,
                (gchar *)p->keepalive.stun_buffer);
645

646 647 648
            nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.",
                agent, p, (int) buf_len);
          } else {
649
            ++errors;
650
          }
651
        }
652
      }
653 654
    }
  }
Youness Alaoui's avatar
Youness Alaoui committed
655

656
  /* case 2: connectivity establishment ongoing
657
   *         (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19)  */
658 659 660
  for (i = agent->streams; i; i = i->next) {
    Stream *stream = i->data;
    if (stream->conncheck_state == NICE_CHECKLIST_RUNNING) {
661 662
      for (j = stream->conncheck_list; j ; j = j->next) {
	CandidateCheckPair *p = j->data;
Youness Alaoui's avatar
Youness Alaoui committed
663

664 665
        nice_debug ("Agent %p : resending STUN-CC to keep the candidate alive (pair %p).", agent, p);
        conn_check_send (agent, p);
666 667 668
      }
    }
  }
Youness Alaoui's avatar
Youness Alaoui committed
669

670
  if (errors) {
671
    nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
672
    goto done;
673 674
  }

675 676 677
  ret = TRUE;

 done:
678 679 680 681 682 683 684 685
  return ret;
}

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

686 687 688 689 690 691 692 693
  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;
  }

694
  ret = priv_conn_keepalive_tick_unlocked (agent);
695 696 697 698 699 700 701
  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;
    }
  }
702
  agent_unlock();
703
  return ret;
704 705
}

706

707 708 709 710
static gboolean priv_turn_allocate_refresh_retransmissions_tick (gpointer pointer)
{
  CandidateRefresh *cand = (CandidateRefresh *) pointer;

711
  agent_lock();
712

713 714 715 716
  /* 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 */
717 718 719 720
  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 ();
721 722 723
    return FALSE;
  }

724

725 726 727 728
  g_source_destroy (cand->tick_source);
  g_source_unref (cand->tick_source);
  cand->tick_source = NULL;

729 730
  switch (stun_timer_refresh (&cand->timer)) {
    case STUN_USAGE_TIMER_RETURN_TIMEOUT:
731 732 733 734 735 736 737 738 739 740
      {
        /* Time out */
        StunTransactionId id;

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

        refresh_cancel (cand);
        break;
      }
741
    case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
742 743 744 745
      /* 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
746 747
      cand->tick_source = agent_timeout_add_with_context (cand->agent,
          stun_timer_remainder (&cand->timer),
748 749
          priv_turn_allocate_refresh_retransmissions_tick, cand);
      break;
750
    case STUN_USAGE_TIMER_RETURN_SUCCESS:
Youness Alaoui's avatar
Youness Alaoui committed
751 752
      cand->tick_source = agent_timeout_add_with_context (cand->agent,
          stun_timer_remainder (&cand->timer),
753 754 755 756 757
          priv_turn_allocate_refresh_retransmissions_tick, cand);
      break;
  }


758
  agent_unlock ();
759 760 761 762 763 764 765 766 767 768 769
  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;

770 771 772 773
  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);
774

775 776
  if (agent_to_turn_compatibility (cand->agent) ==
      STUN_USAGE_TURN_COMPATIBILITY_MSN) {
777 778 779 780
    username = g_base64_decode ((gchar *)username, &username_len);
    password = g_base64_decode ((gchar *)password, &password_len);
  }

781
  buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
782 783 784 785
      &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,
786
      agent_to_turn_compatibility (cand->agent));
787

788 789
  if (agent_to_turn_compatibility (cand->agent) ==
      STUN_USAGE_TURN_COMPATIBILITY_MSN) {
790 791 792 793 794 795
    g_free (cand->msn_turn_username);
    g_free (cand->msn_turn_password);
    cand->msn_turn_username = username;
    cand->msn_turn_password = password;
  }

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

798 799 800 801 802 803
  if (cand->tick_source != NULL) {
    g_source_destroy (cand->tick_source);
    g_source_unref (cand->tick_source);
    cand->tick_source = NULL;
  }

804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
  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);
  }

}


819
/*
820 821 822 823 824 825 826 827 828 829
 * 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;

830 831 832 833 834 835 836 837
  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;
  }

838
  priv_turn_allocate_refresh_tick_unlocked (cand);
839
  agent_unlock ();
840 841 842 843 844

  return FALSE;
}


845
/*
846
 * Initiates the next pending connectivity check.
847 848
 * 
 * @return TRUE if a pending check was scheduled
849
 */
850
gboolean conn_check_schedule_next (NiceAgent *agent)
851
{
852
  gboolean res = priv_conn_check_unfreeze_next (agent);
853
  nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent, res);
854

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

858
  if (res == TRUE) {
859
    /* step: call once imediately */
Youness Alaoui's avatar
Youness Alaoui committed
860
    res = priv_conn_check_tick_unlocked ((gpointer) agent);
861
    nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent, res);
862 863

    /* step: schedule timer if not running yet */
864 865
    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);
866
    }
867 868

    /* step: also start the keepalive timer */
869 870
    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);
871
    }
872

873
  }
874

875
  nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent, res);
876
  return res;
877 878
}

879
/*
880 881 882 883 884 885 886 887 888 889 890 891 892
 * 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;
}

893
/*
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
 * 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) {
911
      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);
912 913 914 915 916 917 918
      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);
    }
  }
}

919
/*
920 921 922 923 924 925 926 927 928
 * 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)
{
929
  GSList *i, *j, *k, *l, *m, *n;
930 931 932 933 934 935 936 937 938 939 940 941 942
  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;
943
	/* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
944 945 946 947 948 949 950 951 952
	 * 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) {
953
	  /* note: we have gotten an incoming connectivity check from
954
	   *       an address that is not a known remote candidate */
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 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

          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) {
              priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate);
            }
          }
        }
1019 1020 1021 1022 1023
      }
    }
  }
}

1024
/*
1025
 * Enforces the upper limit for connectivity checks as described
1026
 * in ICE spec section 5.7.3 (ID-19). See also 
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
 * 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) {
    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;
}

1064
/*
1065 1066 1067
 * 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).