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

44 45
#ifdef HAVE_CONFIG_H
# include <config.h>
46
#endif
47

48 49
#include <glib.h>

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

54 55
#include "debug.h"

56 57 58 59 60
#include "agent.h"
#include "agent-priv.h"
#include "agent-signals-marshal.h"
#include "component.h"
#include "discovery.h"
61 62
#include "stun/usages/bind.h"
#include "stun/usages/turn.h"
63
#include "socket.h"
64

65
static inline int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
66 67 68 69 70 71
{
  return (now->tv_sec == timer->tv_sec) ?
    now->tv_usec >= timer->tv_usec :
    now->tv_sec >= timer->tv_sec;
}

72
/*
73
 * Frees the CandidateDiscovery structure pointed to
74 75 76 77 78 79
 * by 'user data'. Compatible with g_slist_foreach().
 */
void discovery_free_item (gpointer data, gpointer user_data)
{
  CandidateDiscovery *cand = data;
  g_assert (user_data == NULL);
80 81
  g_free (cand->msn_turn_username);
  g_free (cand->msn_turn_password);
82 83 84
  g_slice_free (CandidateDiscovery, cand);
}

85
/*
86 87 88 89 90
 * Frees all discovery related resources for the agent.
 */
void discovery_free (NiceAgent *agent)
{
  if (agent->discovery_list) {
91 92 93 94 95
    GSList *tmp = agent->discovery_list;
    agent->discovery_list = NULL;

    g_slist_foreach (tmp, discovery_free_item, NULL);
    g_slist_free (tmp);
96 97 98

    agent->discovery_unsched_items = 0;
  }
99 100 101 102 103
  if (agent->discovery_timer_source != NULL) {
    g_source_destroy (agent->discovery_timer_source);
    g_source_unref (agent->discovery_timer_source);
    agent->discovery_timer_source = NULL;
  }
104 105
}

106
/*
107
 * Prunes the list of discovery processes for items related
Youness Alaoui's avatar
Youness Alaoui committed
108
 * to stream 'stream_id'.
109 110 111
 *
 * @return TRUE on success, FALSE on a fatal error
 */
112
void discovery_prune_stream (NiceAgent *agent, guint stream_id)
113 114 115 116
{
  GSList *i;

  for (i = agent->discovery_list; i ; ) {
117 118
    CandidateDiscovery *cand = i->data;
    GSList *next = i->next;
119 120

    if (cand->stream->id == stream_id) {
121
      agent->discovery_list = g_slist_remove (agent->discovery_list, cand);
122 123
      discovery_free_item (cand, NULL);
    }
124
    i = next;
125 126
  }

127 128 129 130 131 132
  if (agent->discovery_list == NULL) {
    /* noone using the timer anymore, clean it up */
    discovery_free (agent);
  }
}

133

134
/*
135 136 137 138 139 140
 * Frees the CandidateDiscovery structure pointed to
 * by 'user data'. Compatible with g_slist_foreach().
 */
void refresh_free_item (gpointer data, gpointer user_data)
{
  CandidateRefresh *cand = data;
141 142 143 144 145 146
  NiceAgent *agent = cand->agent;
  uint8_t *username;
  size_t username_len;
  uint8_t *password;
  size_t password_len;
  size_t buffer_len = 0;
147 148 149 150 151 152 153 154 155 156 157 158 159 160

  g_assert (user_data == NULL);

  if (cand->timer_source != NULL) {
    g_source_destroy (cand->timer_source);
    g_source_unref (cand->timer_source);
    cand->timer_source = NULL;
  }
  if (cand->tick_source != NULL) {
    g_source_destroy (cand->tick_source);
    g_source_unref (cand->tick_source);
    cand->tick_source = NULL;
  }

161 162 163 164
  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);
165

166
  if (agent_to_turn_compatibility (agent) == STUN_USAGE_TURN_COMPATIBILITY_MSN) {
167 168 169 170
    username = g_base64_decode ((gchar *)username, &username_len);
    password = g_base64_decode ((gchar *)password, &password_len);
  }

171
  buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
172 173 174 175
      &cand->stun_message,  cand->stun_buffer, sizeof(cand->stun_buffer),
      cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, 0,
      username, username_len,
      password, password_len,
176
      agent_to_turn_compatibility (agent));
177 178

  if (buffer_len > 0) {
179 180 181 182 183 184 185
    StunTransactionId id;

    /* forget the transaction since we don't care about the result and
     * we don't implement retransmissions/timeout */
    stun_message_id (&cand->stun_message, id);
    stun_agent_forget_transaction (&cand->stun_agent, id);

186 187 188 189 190 191 192 193
    /* send the refresh twice since we won't do retransmissions */
    nice_socket_send (cand->nicesock, &cand->server,
        buffer_len, (gchar *)cand->stun_buffer);
    nice_socket_send (cand->nicesock, &cand->server,
        buffer_len, (gchar *)cand->stun_buffer);

  }

194
  if (agent_to_turn_compatibility (agent) == STUN_USAGE_TURN_COMPATIBILITY_MSN) {
195 196 197
    g_free (username);
    g_free (password);
  }
198 199 200 201

  g_slice_free (CandidateRefresh, cand);
}

202
/*
203 204 205 206 207 208 209 210 211 212 213 214 215 216
 * Frees all discovery related resources for the agent.
 */
void refresh_free (NiceAgent *agent)
{
  if (agent->refresh_list) {
    GSList *tmp = agent->refresh_list;
    agent->refresh_list = NULL;

    g_slist_foreach (tmp, refresh_free_item, NULL);
    g_slist_free (tmp);

  }
}

217
/*
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
 * Prunes the list of discovery processes for items related
 * to stream 'stream_id'. 
 *
 * @return TRUE on success, FALSE on a fatal error
 */
void refresh_prune_stream (NiceAgent *agent, guint stream_id)
{
  GSList *i;

  for (i = agent->refresh_list; i ;) {
    CandidateRefresh *cand = i->data;
    GSList *next = i->next;

    if (cand->stream->id == stream_id) {
      agent->refresh_list = g_slist_remove (agent->refresh_list, cand);
      refresh_free_item (cand, NULL);
    }

    i = next;
  }

}

void refresh_cancel (CandidateRefresh *refresh)
{
  refresh->agent->refresh_list = g_slist_remove (refresh->agent->refresh_list,
      refresh);
  refresh_free_item (refresh, NULL);
}

248
/*
249
 * Adds a new local candidate. Implements the candidate pruning
250
 * defined in ICE spec section 4.1.3 "Eliminating Redundant
251
 * Candidates" (ID-19).
252 253 254
 */
static gboolean priv_add_local_candidate_pruned (Component *component, NiceCandidate *candidate)
{
255
  GSList *i;
256 257 258

  for (i = component->local_candidates; i ; i = i->next) {
    NiceCandidate *c = i->data;
259

260 261
    if (nice_address_equal (&c->base_addr, &candidate->base_addr) &&
	nice_address_equal (&c->addr, &candidate->addr)) {
262
      nice_debug ("Candidate %p (component-id %u) redundant, ignoring.", candidate, component->id);
263 264 265 266
      return FALSE;
    }
  }

267 268
  component->local_candidates = g_slist_append (component->local_candidates,
      candidate);
269 270 271 272

  return TRUE;
}

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
static guint priv_highest_remote_foundation (Component *component)
{
  GSList *i;
  guint highest = 1;
  gchar foundation[NICE_CANDIDATE_MAX_FOUNDATION];

  for (highest = 1;; highest++) {
    gboolean taken = FALSE;

    g_snprintf (foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", highest);
    for (i = component->remote_candidates; i; i = i->next) {
      NiceCandidate *cand = i->data;
      if (strncmp (foundation, cand->foundation,
              NICE_CANDIDATE_MAX_FOUNDATION) == 0) {
        taken = TRUE;
        break;
      }
    }
    if (!taken)
      return highest;
  }

  g_return_val_if_reached (highest);
}

298
/*
299 300
 * Assings a foundation to the candidate.
 *
301
 * Implements the mechanism described in ICE sect
302
 * 4.1.1.3 "Computing Foundations" (ID-19).
303
 */
304
static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
305
{
306 307 308 309 310 311 312 313 314
  GSList *i, *j, *k;

  for (i = agent->streams; i; i = i->next) {
    Stream *stream = i->data;
    for (j = stream->components; j; j = j->next) {
      Component *component = j->data;
      for (k = component->local_candidates; k; k = k->next) {
	NiceCandidate *n = k->data;
	NiceAddress temp = n->base_addr;
315 316 317 318 319

	/* note: candidate must not on the local candidate list */
	g_assert (candidate != n);

	/* note: ports are not to be compared */
320 321
	nice_address_set_port (&temp,
               nice_address_get_port (&candidate->base_addr));
322

323
	if (candidate->type == n->type &&
324
            candidate->transport == n->transport &&
325
            candidate->stream_id == n->stream_id &&
326 327
	    nice_address_equal (&candidate->base_addr, &temp) &&
            !(agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
328
                n->type == NICE_CANDIDATE_TYPE_RELAYED)) {
329 330 331 332
	  /* note: currently only one STUN/TURN server per stream at a
	   *       time is supported, so there is no need to check
	   *       for candidates that would otherwise share the
	   *       foundation, but have different STUN/TURN servers */
333 334
	  g_strlcpy (candidate->foundation, n->foundation,
              NICE_CANDIDATE_MAX_FOUNDATION);
335 336 337 338 339 340 341 342
          if (n->username) {
            g_free (candidate->username);
            candidate->username = g_strdup (n->username);
          }
          if (n->password) {
            g_free (candidate->password);
            candidate->password = g_strdup (n->password);
          }
343 344 345
	  return;
	}
      }
346 347
    }
  }
Youness Alaoui's avatar
Youness Alaoui committed
348

349 350
  g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION,
      "%u", agent->next_candidate_id++);
351 352
}

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
static void priv_assign_remote_foundation (NiceAgent *agent, NiceCandidate *candidate)
{
  GSList *i, *j, *k;
  guint next_remote_id;
  Component *component = NULL;

  for (i = agent->streams; i; i = i->next) {
    Stream *stream = i->data;
    for (j = stream->components; j; j = j->next) {
      Component *c = j->data;

      if (c->id == candidate->component_id)
        component = c;

      for (k = c->remote_candidates; k; k = k->next) {
	NiceCandidate *n = k->data;
369
	NiceAddress temp = n->addr;
370 371 372 373 374 375 376 377 378 379

	/* note: candidate must not on the remote candidate list */
	g_assert (candidate != n);

	/* note: ports are not to be compared */
	nice_address_set_port (&temp,
               nice_address_get_port (&candidate->base_addr));

	if (candidate->type == n->type &&
            candidate->stream_id == n->stream_id &&
380
	    nice_address_equal (&candidate->addr, &temp)) {
381 382 383 384
	  /* note: currently only one STUN/TURN server per stream at a
	   *       time is supported, so there is no need to check
	   *       for candidates that would otherwise share the
	   *       foundation, but have different STUN/TURN servers */
385 386
	  g_strlcpy (candidate->foundation, n->foundation,
              NICE_CANDIDATE_MAX_FOUNDATION);
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
          if (n->username) {
            g_free (candidate->username);
            candidate->username = g_strdup (n->username);
          }
          if (n->password) {
            g_free (candidate->password);
            candidate->password = g_strdup (n->password);
          }
	  return;
	}
      }
    }
  }

  if (component) {
    next_remote_id = priv_highest_remote_foundation (component);
    g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION,
        "%u", next_remote_id);
  }
}


409
static
410 411
void priv_generate_candidate_credentials (NiceAgent *agent,
    NiceCandidate *candidate)
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
{

  if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
    guchar username[32];
    guchar password[16];

    if (candidate->username)
      g_free (candidate->username);
    if (candidate->password)
      g_free (candidate->password);

    nice_rng_generate_bytes (agent->rng, 32, (gchar *)username);
    nice_rng_generate_bytes (agent->rng, 16, (gchar *)password);

    candidate->username = g_base64_encode (username, 32);
    candidate->password = g_base64_encode (password, 16);

429 430 431 432 433 434 435 436 437 438 439
  } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    gchar username[16];

    if (candidate->username)
      g_free (candidate->username);
    if (candidate->password)
      g_free (candidate->password);
    candidate->password = NULL;

    nice_rng_generate_bytes_print (agent->rng, 16, (gchar *)username);

440
    candidate->username = g_strndup (username, 16);
441 442 443 444 445
  }


}

446
/*
447 448 449 450 451 452 453 454 455 456 457 458 459 460
 * Creates a local host candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate *discovery_add_local_host_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address)
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
461
  NiceSocket *udp_socket = NULL;
462
  gboolean errors = FALSE;
463 464 465 466 467

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
    return NULL;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
468 469 470 471 472 473 474 475 476 477 478
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
  candidate->base_addr = *address;
  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN)  {
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else {
    candidate->priority = nice_candidate_ice_priority (candidate);
  }
479

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
  priv_generate_candidate_credentials (agent, candidate);
  priv_assign_foundation (agent, candidate);

  /* note: candidate username and password are left NULL as stream
     level ufrag/password are used */
  udp_socket = nice_udp_bsd_socket_new (address);
  if (udp_socket) {
    gboolean result;

    _priv_set_socket_tos (agent, udp_socket, stream->tos);
    agent_attach_stream_component_socket (agent, stream,
        component, udp_socket);

    candidate->sockptr = udp_socket;
    candidate->addr = udp_socket->addr;
    candidate->base_addr = udp_socket->addr;

    result = priv_add_local_candidate_pruned (component, candidate);

    if (result == TRUE) {
      component->sockets = g_slist_append (component->sockets, udp_socket);
      agent_signal_new_candidate (agent, candidate);
502
    } else {
503
      /* error: duplicate candidates */
504
      errors = TRUE;
505
    }
506 507 508
  } else {
    /* error: socket new */
    errors = TRUE;
509 510
  }

511 512 513 514
  /* clean up after errors */
  if (errors) {
    if (candidate)
      nice_candidate_free (candidate), candidate = NULL;
515
    if (udp_socket)
516
      nice_socket_free (udp_socket);
517
  }
Youness Alaoui's avatar
Youness Alaoui committed
518

519 520 521
  return candidate;
}

522
/*
523 524 525 526 527
 * Creates a server reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
Youness Alaoui's avatar
Youness Alaoui committed
528
NiceCandidate*
529 530 531 532 533
discovery_add_server_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
534
  NiceSocket *base_socket)
535 536 537 538
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
539
  gboolean result = FALSE;
540 541 542 543 544 545

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
    return NULL;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
  if (candidate) {
546
    if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
547
      candidate->priority = nice_candidate_jingle_priority (candidate);
548
    } else if (agent->compatibility == NICE_COMPATIBILITY_MSN)  {
549
      candidate->priority = nice_candidate_msn_priority (candidate);
550 551
    } else {
      candidate->priority =  nice_candidate_ice_priority_full
552
        (NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE, 0, component_id);
553
    }
554 555 556 557 558 559 560 561
    candidate->stream_id = stream_id;
    candidate->component_id = component_id;
    candidate->addr = *address;

    /* step: link to the base candidate+socket */
    candidate->sockptr = base_socket;
    candidate->base_addr = base_socket->addr;

562
    priv_generate_candidate_credentials (agent, candidate);
563 564
    priv_assign_foundation (agent, candidate);

565
    result = priv_add_local_candidate_pruned (component, candidate);
566 567 568 569
    if (result) {
      agent_signal_new_candidate (agent, candidate);
    }
    else {
570 571 572 573 574 575 576 577
      /* error: memory allocation, or duplicate candidatet */
      nice_candidate_free (candidate), candidate = NULL;
    }
  }

  return candidate;
}

578

579
/*
580 581 582 583 584 585 586 587 588 589 590
 * Creates a server reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate* 
discovery_add_relay_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
591 592
  NiceSocket *base_socket,
  TurnServer *turn)
593 594 595 596 597 598
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
  gboolean result = FALSE;
  gboolean errors = FALSE;
599
  NiceSocket *relay_socket = NULL;
600 601 602 603 604 605

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
    return NULL;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
  if (candidate) {
606
    if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
607
      candidate->priority = nice_candidate_jingle_priority (candidate);
608
    } else if (agent->compatibility == NICE_COMPATIBILITY_MSN)  {
609
      candidate->priority = nice_candidate_msn_priority (candidate);
610 611 612 613 614 615 616
    } else {
      candidate->priority =  nice_candidate_ice_priority_full
          (NICE_CANDIDATE_TYPE_PREF_RELAYED, 0, component_id);
    }
    candidate->stream_id = stream_id;
    candidate->component_id = component_id;
    candidate->addr = *address;
617
    candidate->turn = turn;
618 619

    /* step: link to the base candidate+socket */
620
    relay_socket = nice_turn_socket_new (agent, address,
621 622
        base_socket, &turn->server,
        turn->username, turn->password,
623
        agent_to_turn_socket_compatibility (agent));
624
    if (relay_socket) {
625 626 627 628 629 630
      candidate->sockptr = relay_socket;
      candidate->base_addr = base_socket->addr;

      priv_generate_candidate_credentials (agent, candidate);

      /* Google uses the turn username as the candidate username */
631
      if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
632
        g_free (candidate->username);
633
        candidate->username = g_strdup (turn->username);
634
      }
635

636
      priv_assign_foundation (agent, candidate);
637

638 639
      result = priv_add_local_candidate_pruned (component, candidate);
      if (result) {
640 641 642 643 644
        GSList *modified_list = g_slist_append (component->sockets, relay_socket);
        if (modified_list) {
          /* success: store a pointer to the sockaddr */
          component->sockets = modified_list;
        }
645
        agent_signal_new_candidate (agent, candidate);
646
      } else {
647
        /* error: memory allocation, or duplicate candidate */
648
        errors = TRUE;
649 650
      }
    } else {
651
      /* error: socket factory make */
652
      errors = TRUE;
653
    }
654 655 656
  } else {
    /* error: udp socket memory allocation */
    errors = TRUE;
657 658 659 660 661 662 663
  }

  /* clean up after errors */
  if (errors) {
    if (candidate)
      nice_candidate_free (candidate), candidate = NULL;
    if (relay_socket)
664
      nice_socket_free (relay_socket);
665 666 667 668
  }
  return candidate;
}

669
/*
670 671 672 673 674 675 676 677 678 679 680
 * Creates a peer reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate* 
discovery_add_peer_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
681
  NiceSocket *base_socket,
682 683
  NiceCandidate *local,
  NiceCandidate *remote)
684 685 686 687 688 689 690 691 692 693 694 695 696
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
    return NULL;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
  if (candidate) {
    gboolean result;

    candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
697
    if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
698
      candidate->priority = nice_candidate_jingle_priority (candidate);
699
    } else if (agent->compatibility == NICE_COMPATIBILITY_MSN)  {
700
      candidate->priority = nice_candidate_msn_priority (candidate);
701 702
    } else {
      candidate->priority = nice_candidate_ice_priority_full
703
        (NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 0, component_id);
704
    }
705 706 707 708
    candidate->stream_id = stream_id;
    candidate->component_id = component_id;
    candidate->addr = *address;
    candidate->base_addr = base_socket->addr;
709

710

711
    priv_assign_foundation (agent, candidate);
712

713
    if (agent->compatibility == NICE_COMPATIBILITY_MSN &&
714
	remote && local) {
715 716 717 718 719 720 721
      guchar *new_username = NULL;
      guchar *decoded_local = NULL;
      guchar *decoded_remote = NULL;
      gsize local_size;
      gsize remote_size;
      g_free(candidate->username);
      g_free(candidate->password);
722

723 724
      decoded_local = g_base64_decode (local->username, &local_size);
      decoded_remote = g_base64_decode (remote->username, &remote_size);
725 726 727 728

      new_username = g_new0(guchar, local_size + remote_size);
      memcpy(new_username, decoded_local, local_size);
      memcpy(new_username + local_size, decoded_remote, remote_size);
729

730 731 732 733
      candidate->username = g_base64_encode (new_username, local_size + remote_size);
      g_free(new_username);
      g_free(decoded_local);
      g_free(decoded_remote);
734

735 736 737 738 739 740
      candidate->password = g_strdup(local->password);
    } else if (local) {
      g_free(candidate->username);
      g_free(candidate->password);

      candidate->username = g_strdup(local->username);
741
      candidate->password = g_strdup(local->password);
742 743
    }

744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
    /* step: link to the base candidate+socket */
    candidate->sockptr = base_socket;
    candidate->base_addr = base_socket->addr;

    result = priv_add_local_candidate_pruned (component, candidate);
    if (result != TRUE) {
      /* error: memory allocation, or duplicate candidatet */
      nice_candidate_free (candidate), candidate = NULL;
    }
  }

  return candidate;
}


759
/*
760 761 762 763
 * Adds a new peer reflexive candidate to the list of known
 * remote candidates. The candidate is however not paired with
 * existing local candidates.
 *
764
 * See ICE sect 7.2.1.3 "Learning Peer Reflexive Candidates" (ID-19).
765 766 767 768 769 770 771
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
  NiceAgent *agent,
  Stream *stream,
  Component *component,
772
  guint32 priority,
773
  const NiceAddress *remote_address,
774
  NiceSocket *udp_socket,
775 776
  NiceCandidate *local,
  NiceCandidate *remote)
777 778 779
{
  NiceCandidate *candidate;

780 781 782
  /* XXX: for use compiler */
  (void)udp_socket;

783 784 785 786
  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
  if (candidate) {
    GSList *modified_list;

787
    candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
788 789
    candidate->addr = *remote_address;
    candidate->base_addr = *remote_address;
790 791 792 793 794 795 796 797 798 799 800 801 802

    /* if the check didn't contain the PRIORITY attribute, then the priority will
     * be 0, which is invalid... */
    if (priority != 0) {
      candidate->priority = priority;
    } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
      candidate->priority = nice_candidate_jingle_priority (candidate);
    } else if (agent->compatibility == NICE_COMPATIBILITY_MSN)  {
      candidate->priority = nice_candidate_msn_priority (candidate);
    } else {
      candidate->priority = nice_candidate_ice_priority_full
        (NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 0, component->id);
    }
803 804
    candidate->stream_id = stream->id;
    candidate->component_id = component->id;
805

806 807

    priv_assign_remote_foundation (agent, candidate);
808 809 810

    if (agent->compatibility == NICE_COMPATIBILITY_MSN &&
	remote && local) {
811 812 813
      guchar *new_username = NULL;
      guchar *decoded_local = NULL;
      guchar *decoded_remote = NULL;
814 815 816
      gsize local_size;
      gsize remote_size;
      g_free(candidate->username);
817
      g_free (candidate->password);
818

819 820 821
      decoded_local = g_base64_decode (local->username, &local_size);
      decoded_remote = g_base64_decode (remote->username, &remote_size);

822
      new_username = g_new0(guchar, local_size + remote_size);
823 824
      memcpy(new_username, decoded_remote, remote_size);
      memcpy(new_username + remote_size, decoded_local, local_size);
825

826 827 828 829
      candidate->username = g_base64_encode (new_username, local_size + remote_size);
      g_free(new_username);
      g_free(decoded_local);
      g_free(decoded_remote);
830

831
      candidate->password = g_strdup(remote->password);
832
    } else if (remote) {
833 834 835
      g_free (candidate->username);
      g_free (candidate->password);
      candidate->username = g_strdup(remote->username);
836
      candidate->password = g_strdup(remote->password);
837
    }
838

839

840 841 842
    candidate->sockptr = NULL; /* not stored for remote candidates */
    /* note: candidate username and password are left NULL as stream 
             level ufrag/password are used */
843

844 845 846 847 848
    modified_list = g_slist_append (component->remote_candidates,
				    candidate);
    if (modified_list) {
      component->remote_candidates = modified_list;
      agent_signal_new_remote_candidate (agent, candidate);
849
    }
850
    else { /* error: memory alloc / list */
851
      nice_candidate_free (candidate), candidate = NULL;
852
    }
853 854 855
  }

  return candidate;
856
}
857

858
/* 
859 860 861 862 863 864 865 866
 * Timer callback that handles scheduling new candidate discovery
 * processes (paced by the Ta timer), and handles running of the 
 * existing discovery processes.
 *
 * 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
867
static gboolean priv_discovery_tick_unlocked (gpointer pointer)
868 869 870 871 872
{
  CandidateDiscovery *cand;
  NiceAgent *agent = pointer;
  GSList *i;
  int not_done = 0; /* note: track whether to continue timer */
873
  size_t buffer_len = 0;
874 875 876

  {
    static int tick_counter = 0;
877
    if (tick_counter++ % 50 == 0)
878
      nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
879 880 881 882 883 884 885 886 887 888
  }

  for (i = agent->discovery_list; i ; i = i->next) {
    cand = i->data;

    if (cand->pending != TRUE) {
      cand->pending = TRUE;

      if (agent->discovery_unsched_items)
	--agent->discovery_unsched_items;
889 890 891 892

      {
        gchar tmpbuf[INET6_ADDRSTRLEN];
        nice_address_to_string (&cand->server, tmpbuf);
893 894
        nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.\n",
            agent, cand->type, tmpbuf);
895 896
      }
      if (nice_address_is_valid (&cand->server) &&
897 898
          (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
              cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
899

900
	agent_signal_component_state_change (agent,
901 902 903
					     cand->stream->id,
					     cand->component->id,
					     NICE_COMPONENT_STATE_GATHERING);
904

905
        if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
906
          buffer_len = stun_usage_bind_create (&cand->stun_agent,
907 908
              &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer));
        } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) {
909 910 911 912
          uint8_t *username = (uint8_t *)cand->turn->username;
          size_t username_len = (size_t) strlen (cand->turn->username);
          uint8_t *password = (uint8_t *)cand->turn->password;
          size_t password_len = (size_t) strlen (cand->turn->password);
913

914 915
          if (agent_to_turn_compatibility (agent) ==
              STUN_USAGE_TURN_COMPATIBILITY_MSN) {
916 917 918 919
            username = g_base64_decode ((gchar *)username, &username_len);
            password = g_base64_decode ((gchar *)password, &password_len);
          }

920
          buffer_len = stun_usage_turn_create (&cand->stun_agent,
921
              &cand->stun_message,  cand->stun_buffer, sizeof(cand->stun_buffer),
922
              cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
923
              STUN_USAGE_TURN_REQUEST_PORT_NORMAL,
924
              -1, -1,
925 926
              username, username_len,
              password, password_len,
927
              agent_to_turn_compatibility (agent));
928

929 930
          if (agent_to_turn_compatibility (agent) ==
              STUN_USAGE_TURN_COMPATIBILITY_MSN) {
931 932
            g_free (cand->msn_turn_username);
            g_free (cand->msn_turn_password);
933 934
            cand->msn_turn_username = username;
            cand->msn_turn_password = password;
935
          }
936
        }
937 938 939 940 941

	if (buffer_len > 0) {
          stun_timer_start (&cand->timer);

          /* send the conncheck */
942
          nice_socket_send (cand->nicesock, &cand->server,
943 944
              buffer_len, (gchar *)cand->stun_buffer);

945 946 947
	  /* case: success, start waiting for the result */
	  g_get_current_time (&cand->next_tick);

948
	} else {
949 950
	  /* case: error in starting discovery, start the next discovery */
	  cand->done = TRUE;
951 952
	  cand->stun_message.buffer = NULL;
	  cand->stun_message.buffer_len = 0;
953
	  continue;
954 955
	}
      }
956
      else
957 958
	/* allocate relayed candidates */
	g_assert_not_reached ();
959

960 961
      ++not_done; /* note: new discovery scheduled */
    }
Youness Alaoui's avatar
Youness Alaoui committed
962

963 964 965 966 967
    if (cand->done != TRUE) {
      GTimeVal now;

      g_get_current_time (&now);

968
      if (cand->stun_message.buffer == NULL) {
969
	nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent);
970 971
	cand->done = TRUE;
      }
972
      else if (priv_timer_expired (&cand->next_tick, &now)) {
973 974
        switch (stun_timer_refresh (&cand->timer)) {
          case STUN_USAGE_TIMER_RETURN_TIMEOUT:
975 976 977 978 979 980 981 982 983 984 985 986 987 988
            {
              /* Time out */
              /* case: error, abort processing */
              StunTransactionId id;

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

              cand->done = TRUE;
              cand->stun_message.buffer = NULL;
              cand->stun_message.buffer_len = 0;
              nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent);
              break;
            }
989
          case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
990 991 992 993 994 995 996
            {
              /* case: not ready complete, so schedule next timeout */
              unsigned int timeout = stun_timer_remainder (&cand->timer);

              stun_debug ("STUN transaction retransmitted (timeout %dms).\n",
                  timeout);

997
              /* retransmit */
998
              nice_socket_send (cand->nicesock, &cand->server,
999 1000 1001 1002 1003 1004 1005
                  stun_message_length (&cand->stun_message),
                  (gchar *)cand->stun_buffer);

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

Youness Alaoui's avatar
Youness Alaoui committed
1006 1007 1008
              ++not_done; /* note: retry later */
              break;
            }
1009
          case STUN_USAGE_TIMER_RETURN_SUCCESS:
Youness Alaoui's avatar
Youness Alaoui committed
1010
            {
1011 1012
              unsigned int timeout = stun_timer_remainder (&cand->timer);

Youness Alaoui's avatar
Youness Alaoui committed
1013 1014 1015
              cand->next_tick = now;
              g_time_val_add (&cand->next_tick, timeout * 1000);

1016 1017 1018
              ++not_done; /* note: retry later */
              break;
            }
1019
	}
1020

Youness Alaoui's avatar
Youness Alaoui committed
1021
      } else {
1022
	++not_done; /* note: discovery not expired yet */
Youness Alaoui's avatar
Youness Alaoui committed
1023
      }
1024 1025 1026 1027
    }
  }

  if (not_done == 0) {
1028
    nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent);
1029 1030 1031

    discovery_free (agent);

1032
    agent_gathering_done (agent);
1033

1034 1035 1036 1037 1038 1039 1040
    /* note: no pending timers, return FALSE to stop timer */
    return FALSE;
  }

  return TRUE;
}

Youness Alaoui's avatar
Youness Alaoui committed
1041 1042 1043 1044 1045
static gboolean priv_discovery_tick (gpointer pointer)
{
  NiceAgent *agent = pointer;
  gboolean ret;

1046 1047 1048 1049 1050 1051 1052 1053
  agent_lock();
  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in priv_discovery_tick");
    agent_unlock ();
    return FALSE;
  }

Youness Alaoui's avatar
Youness Alaoui committed
1054
  ret = priv_discovery_tick_unlocked (pointer);
1055 1056 1057 1058 1059 1060 1061
  if (ret == FALSE) {
    if (agent->discovery_timer_source != NULL) {
      g_source_destroy (agent->discovery_timer_source);
      g_source_unref (agent->discovery_timer_source);
      agent->discovery_timer_source = NULL;
    }
  }
1062
  agent_unlock();
Youness Alaoui's avatar
Youness Alaoui committed
1063 1064 1065 1066

  return ret;
}

1067
/*
1068 1069
 * Initiates the candidate discovery process by starting
 * the necessary timers.
1070 1071 1072 1073 1074 1075 1076 1077 1078
 *
 * @pre agent->discovery_list != NULL  // unsched discovery items available
 */
void discovery_schedule (NiceAgent *agent)
{
  g_assert (agent->discovery_list != NULL);

  if (agent->discovery_unsched_items > 0) {
    
1079
    if (agent->discovery_timer_source == NULL) {
1080
      /* step: run first iteration immediately */
Youness Alaoui's avatar
Youness Alaoui committed
1081
      gboolean res = priv_discovery_tick_unlocked (agent);
1082
      if (res == TRUE) {
1083
        agent->discovery_timer_source = agent_timeout_add_with_context (agent, agent->timer_ta, priv_discovery_tick, agent);
1084 1085
      }
    }
1086 1087
  }
}