discovery.c 36.7 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
#include "agent.h"
#include "agent-priv.h"
#include "component.h"
#include "discovery.h"
60 61
#include "stun/usages/bind.h"
#include "stun/usages/turn.h"
62
#include "socket.h"
63

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

71
/*
72
 * Frees the CandidateDiscovery structure pointed to
73
 * by 'user data'. Compatible with g_slist_free_full().
74
 */
75
static void discovery_free_item (CandidateDiscovery *cand)
76
{
77 78 79
  if (cand->turn)
    turn_server_unref (cand->turn);

80 81 82
  g_slice_free (CandidateDiscovery, cand);
}

83
/*
84 85 86 87
 * Frees all discovery related resources for the agent.
 */
void discovery_free (NiceAgent *agent)
{
88 89
  g_slist_free_full (agent->discovery_list,
      (GDestroyNotify) discovery_free_item);
90 91
  agent->discovery_list = NULL;
  agent->discovery_unsched_items = 0;
92

93 94 95 96 97
  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;
  }
98 99
}

100
/*
101
 * Prunes the list of discovery processes for items related
Youness Alaoui's avatar
Youness Alaoui committed
102
 * to stream 'stream_id'.
103 104 105
 *
 * @return TRUE on success, FALSE on a fatal error
 */
106
void discovery_prune_stream (NiceAgent *agent, guint stream_id)
107 108 109 110
{
  GSList *i;

  for (i = agent->discovery_list; i ; ) {
111 112
    CandidateDiscovery *cand = i->data;
    GSList *next = i->next;
113 114

    if (cand->stream->id == stream_id) {
115
      agent->discovery_list = g_slist_remove (agent->discovery_list, cand);
116
      discovery_free_item (cand);
117
    }
118
    i = next;
119 120
  }

121 122 123 124 125 126
  if (agent->discovery_list == NULL) {
    /* noone using the timer anymore, clean it up */
    discovery_free (agent);
  }
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
/*
 * Prunes the list of discovery processes for items related
 * to socket @sock.
 *
 * @return TRUE on success, FALSE on a fatal error
 */
void discovery_prune_socket (NiceAgent *agent, NiceSocket *sock)
{
  GSList *i;

  for (i = agent->discovery_list; i ; ) {
    CandidateDiscovery *discovery = i->data;
    GSList *next = i->next;

    if (discovery->nicesock == sock) {
      agent->discovery_list = g_slist_remove (agent->discovery_list, discovery);
      discovery_free_item (discovery);
    }
    i = next;
  }

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

154

155
/*
156
 * Frees the CandidateDiscovery structure pointed to
157
 * by 'user data'. Compatible with g_slist_free_full().
158
 */
159
static void refresh_free_item (CandidateRefresh *cand)
160
{
161 162
  NiceAgent *agent = cand->agent;
  uint8_t *username;
163
  gsize username_len;
164
  uint8_t *password;
165
  gsize password_len;
166
  size_t buffer_len = 0;
167
  StunUsageTurnCompatibility turn_compat = agent_to_turn_compatibility (agent);
168 169 170 171 172 173 174 175 176 177 178 179

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

180 181 182 183
  username = (uint8_t *)cand->candidate->turn->username;
  username_len = (size_t) strlen (cand->candidate->turn->username);
  password = (uint8_t *)cand->candidate->turn->password;
  password_len = (size_t) strlen (cand->candidate->turn->password);
184

185 186
  if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
      turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
187 188 189 190
    username = g_base64_decode ((gchar *)username, &username_len);
    password = g_base64_decode ((gchar *)password, &password_len);
  }

191
  buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
192 193 194 195
      &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,
196
      agent_to_turn_compatibility (agent));
197 198

  if (buffer_len > 0) {
199 200 201 202 203 204 205
    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);

206
    /* send the refresh twice since we won't do retransmissions */
207
    agent_socket_send (cand->nicesock, &cand->server,
208
        buffer_len, (gchar *)cand->stun_buffer);
209
    if (!nice_socket_is_reliable (cand->nicesock)) {
210
      agent_socket_send (cand->nicesock, &cand->server,
211 212
          buffer_len, (gchar *)cand->stun_buffer);
    }
213 214 215

  }

216 217
  if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
      turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
218 219 220
    g_free (username);
    g_free (password);
  }
221 222 223 224

  g_slice_free (CandidateRefresh, cand);
}

225
/*
226 227 228 229
 * Frees all discovery related resources for the agent.
 */
void refresh_free (NiceAgent *agent)
{
230
  g_slist_free_full (agent->refresh_list, (GDestroyNotify) refresh_free_item);
231
  agent->refresh_list = NULL;
232 233
}

234
/*
235
 * Prunes the list of discovery processes for items related
236
 * to stream 'stream_id'.
237 238 239 240 241 242 243 244 245 246 247
 *
 * @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;

248 249 250
    /* Don't free the candidate refresh to the currently selected local candidate
     * unless the whole pair is being destroyed.
     */
251
    if (cand->stream->id == stream_id) {
252 253
      agent->refresh_list = g_slist_delete_link (agent->refresh_list, i);
      refresh_free_item (cand);
254 255 256 257 258 259 260
    }

    i = next;
  }

}

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
void refresh_prune_candidate (NiceAgent *agent, NiceCandidate *candidate)
{
  GSList *i;

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

    if (refresh->candidate == candidate) {
      agent->refresh_list = g_slist_delete_link (agent->refresh_list, i);
      refresh_free_item (refresh);
    }

    i = next;
  }
}

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
void refresh_prune_socket (NiceAgent *agent, NiceSocket *sock)
{
  GSList *i;

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

    if (refresh->nicesock == sock) {
      agent->refresh_list = g_slist_delete_link (agent->refresh_list, i);
      refresh_free_item (refresh);
    }

    i = next;
  }
}

295 296 297 298
void refresh_cancel (CandidateRefresh *refresh)
{
  refresh->agent->refresh_list = g_slist_remove (refresh->agent->refresh_list,
      refresh);
299
  refresh_free_item (refresh);
300 301
}

302

303
/*
304
 * Adds a new local candidate. Implements the candidate pruning
305
 * defined in ICE spec section 4.1.3 "Eliminating Redundant
306
 * Candidates" (ID-19).
307
 */
308
static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *candidate)
309
{
310
  GSList *i;
311 312 313

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

315
    if (nice_address_equal (&c->base_addr, &candidate->base_addr) &&
316 317
        nice_address_equal (&c->addr, &candidate->addr) &&
        c->transport == candidate->transport) {
318
      nice_debug ("Candidate %p (component-id %u) redundant, ignoring.", candidate, component->id);
319 320 321 322
      return FALSE;
    }
  }

323 324
  component->local_candidates = g_slist_append (component->local_candidates,
      candidate);
325
  conn_check_add_for_local_candidate(agent, stream_id, component, candidate);
326 327 328 329

  return TRUE;
}

330 331 332 333 334 335 336 337 338
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;

339 340
    g_snprintf (foundation, NICE_CANDIDATE_MAX_FOUNDATION, "remote-%u",
        highest);
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
    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);
}

356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
/* From RFC 5245 section 4.1.3:
 *
 *   for reflexive and relayed candidates, the STUN or TURN servers
 *   used to obtain them have the same IP address.
 */
static gboolean
priv_compare_turn_servers (TurnServer *turn1, TurnServer *turn2)
{
  if (turn1 == turn2)
    return TRUE;
  if (turn1 == NULL || turn2 == NULL)
    return FALSE;

  return nice_address_equal_no_port (&turn1->server, &turn2->server);
}

372
/*
373 374
 * Assings a foundation to the candidate.
 *
375
 * Implements the mechanism described in ICE sect
376
 * 4.1.1.3 "Computing Foundations" (ID-19).
377
 */
378
static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
379
{
380 381 382 383 384 385 386 387
  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;
388 389 390 391

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

392
	if (candidate->type == n->type &&
393
            candidate->transport == n->transport &&
394
            candidate->stream_id == n->stream_id &&
395
	    nice_address_equal_no_port (&candidate->base_addr, &n->base_addr) &&
396 397
            (candidate->type != NICE_CANDIDATE_TYPE_RELAYED ||
                priv_compare_turn_servers (candidate->turn, n->turn)) &&
398
            !(agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
399
                n->type == NICE_CANDIDATE_TYPE_RELAYED)) {
400
	  /* note: currently only one STUN server per stream at a
401 402
	   *       time is supported, so there is no need to check
	   *       for candidates that would otherwise share the
403
	   *       foundation, but have different STUN servers */
404 405
	  g_strlcpy (candidate->foundation, n->foundation,
              NICE_CANDIDATE_MAX_FOUNDATION);
406 407 408 409 410 411 412 413
          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);
          }
414 415 416
	  return;
	}
      }
417 418
    }
  }
Youness Alaoui's avatar
Youness Alaoui committed
419

420 421
  g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION,
      "%u", agent->next_candidate_id++);
422 423
}

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
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;

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

	if (candidate->type == n->type &&
445
            candidate->transport == n->transport &&
446
            candidate->stream_id == n->stream_id &&
447
	    nice_address_equal_no_port (&candidate->addr, &n->addr)) {
448 449 450
	  /* note: No need to check for STUN/TURN servers, as these candidate
           * will always be peer reflexive, never relayed or serve reflexive.
           */
451 452
	  g_strlcpy (candidate->foundation, n->foundation,
              NICE_CANDIDATE_MAX_FOUNDATION);
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
          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,
470
        "remote-%u", next_remote_id);
471 472 473 474
  }
}


475
static
476 477
void priv_generate_candidate_credentials (NiceAgent *agent,
    NiceCandidate *candidate)
478 479
{

Jakub Adam's avatar
Jakub Adam committed
480 481
  if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
      agent->compatibility == NICE_COMPATIBILITY_OC2007) {
482 483 484
    guchar username[32];
    guchar password[16];

Olivier Crête's avatar
Olivier Crête committed
485 486
    g_free (candidate->username);
    g_free (candidate->password);
487 488 489 490 491 492 493

    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);

494 495 496
  } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    gchar username[16];

Olivier Crête's avatar
Olivier Crête committed
497 498
    g_free (candidate->username);
    g_free (candidate->password);
499 500 501 502
    candidate->password = NULL;

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

503
    candidate->username = g_strndup (username, 16);
504 505 506 507 508
  }


}

509
/*
510 511 512 513 514
 * Creates a local host candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
515
HostCandidateResult discovery_add_local_host_candidate (
516 517 518
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
519
  NiceAddress *address,
520 521
  NiceCandidateTransport transport,
  NiceCandidate **outcandidate)
522 523 524 525
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
526
  NiceSocket *nicesock = NULL;
527
  HostCandidateResult res = HOST_CANDIDATE_FAILED;
528 529

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
530
    return res;
531 532

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
533
  candidate->transport = transport;
534 535 536 537 538 539
  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);
Jakub Adam's avatar
Jakub Adam committed
540 541
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
542
    candidate->priority = nice_candidate_msn_priority (candidate);
543 544 545
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
        agent->reliable, FALSE);
546
  } else {
547 548
    candidate->priority = nice_candidate_ice_priority (candidate,
        agent->reliable, FALSE);
549
  }
550

551 552 553 554 555
  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 */
556 557
  if (transport == NICE_CANDIDATE_TRANSPORT_UDP) {
    nicesock = nice_udp_bsd_socket_new (address);
Youness Alaoui's avatar
Youness Alaoui committed
558
  } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
559
    nicesock = nice_tcp_active_socket_new (agent->main_context, address);
Youness Alaoui's avatar
Youness Alaoui committed
560
  } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
561
    nicesock = nice_tcp_passive_socket_new (agent->main_context, address);
562
  } else {
Youness Alaoui's avatar
Youness Alaoui committed
563
    /* TODO: Add TCP-SO */
564
  }
565 566
  if (!nicesock) {
    res = HOST_CANDIDATE_CANT_CREATE_SOCKET;
567
    goto errors;
568
  }
569

570 571 572
  candidate->sockptr = nicesock;
  candidate->addr = nicesock->addr;
  candidate->base_addr = nicesock->addr;
573

574 575 576
  if (!priv_add_local_candidate_pruned (agent, stream_id, component,
          candidate)) {
    res = HOST_CANDIDATE_REDUNDANT;
577
    goto errors;
578
  }
579

580 581
  _priv_set_socket_tos (agent, nicesock, stream->tos);
  component_attach_socket (component, nicesock);
Youness Alaoui's avatar
Youness Alaoui committed
582

583 584 585
  *outcandidate = candidate;

  return HOST_CANDIDATE_SUCCESS;
586 587 588

errors:
  nice_candidate_free (candidate);
589 590
  if (nicesock)
    nice_socket_free (nicesock);
591
  return res;
592 593
}

594
/*
595 596 597 598 599
 * 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
600
NiceCandidate*
601 602 603 604 605
discovery_add_server_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
606
  NiceCandidateTransport transport,
607 608
  NiceSocket *base_socket,
  gboolean nat_assisted)
609 610 611 612
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
613
  gboolean result = FALSE;
614 615 616 617 618

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
619 620 621 622
  candidate->transport = transport;
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
623 624 625

  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
Jakub Adam's avatar
Jakub Adam committed
626 627
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
628
    candidate->priority = nice_candidate_msn_priority (candidate);
629 630 631
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
        agent->reliable, nat_assisted);
632
  } else {
633 634
    candidate->priority =  nice_candidate_ice_priority (candidate,
        agent->reliable, nat_assisted);
635
  }
636

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

641 642
  priv_generate_candidate_credentials (agent, candidate);
  priv_assign_foundation (agent, candidate);
643

644
  result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
645 646 647 648 649 650
  if (result) {
    agent_signal_new_candidate (agent, candidate);
  }
  else {
    /* error: duplicate candidate */
    nice_candidate_free (candidate), candidate = NULL;
651 652 653 654 655
  }

  return candidate;
}

656 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 687 688 689 690 691 692 693 694 695 696 697 698 699 700
/*
 * Creates a server reflexive candidate for 'component_id' of stream
 * 'stream_id' for each TCP_PASSIVE and TCP_ACTIVE candidates for each
 * base address.
 *
 * @return pointer to the created candidate, or NULL on error
 */
void
discovery_discover_tcp_server_reflexive_candidates (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
  NiceSocket *base_socket)
{
  Component *component;
  Stream *stream;
  NiceAddress base_addr = base_socket->addr;
  GSList *i;

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

  nice_address_set_port (&base_addr, 0);
  for (i = component->local_candidates; i; i = i ->next) {
    NiceCandidate *c = i->data;
    NiceAddress caddr;

    caddr = c->addr;
    nice_address_set_port (&caddr, 0);
    if (c->transport != NICE_CANDIDATE_TRANSPORT_UDP &&
        c->type == NICE_CANDIDATE_TYPE_HOST &&
        nice_address_equal (&base_addr, &caddr)) {
      nice_address_set_port (address, nice_address_get_port (&c->addr));
      discovery_add_server_reflexive_candidate (
          agent,
          stream_id,
          component_id,
          address,
          c->transport,
          c->sockptr,
          FALSE);
    }
  }
}
701

702
/*
703 704 705 706 707
 * Creates a server reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
708
NiceCandidate*
709 710 711 712 713
discovery_add_relay_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
714
  NiceCandidateTransport transport,
715 716
  NiceSocket *base_socket,
  TurnServer *turn)
717 718 719 720
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
721
  NiceSocket *relay_socket = NULL;
722 723 724 725 726

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
727 728 729 730 731
  candidate->transport = transport;
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
  candidate->turn = turn_server_ref (turn);
732 733 734

  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
Jakub Adam's avatar
Jakub Adam committed
735 736
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
737
    candidate->priority = nice_candidate_msn_priority (candidate);
738 739 740
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
        agent->reliable, FALSE);
741
  } else {
742 743
    candidate->priority =  nice_candidate_ice_priority (candidate,
        agent->reliable, FALSE);
744 745 746
  }

  /* step: link to the base candidate+socket */
747
  relay_socket = nice_udp_turn_socket_new (agent->main_context, address,
748 749 750
      base_socket, &turn->server,
      turn->username, turn->password,
      agent_to_turn_socket_compatibility (agent));
751 752
  if (!relay_socket)
    goto errors;
753

754 755
  candidate->sockptr = relay_socket;
  candidate->base_addr = base_socket->addr;
756

757
  priv_generate_candidate_credentials (agent, candidate);
758

759 760 761 762
  /* Google uses the turn username as the candidate username */
  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    g_free (candidate->username);
    candidate->username = g_strdup (turn->username);
763 764
  }

765 766
  priv_assign_foundation (agent, candidate);

767
  if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate))
768 769
    goto errors;

770
  component_attach_socket (component, relay_socket);
771 772
  agent_signal_new_candidate (agent, candidate);

773
  return candidate;
774 775 776 777 778 779

errors:
  nice_candidate_free (candidate);
  if (relay_socket)
    nice_socket_free (relay_socket);
  return NULL;
780 781
}

782
/*
783 784 785 786 787
 * Creates a peer reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
788
NiceCandidate*
789 790 791 792 793
discovery_add_peer_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
794
  NiceSocket *base_socket,
795 796
  NiceCandidate *local,
  NiceCandidate *remote)
797 798 799 800
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
801
  gboolean result;
802 803 804 805 806

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
Youness Alaoui's avatar
Youness Alaoui committed
807 808 809 810 811 812 813 814 815 816 817
  if (local)
    candidate->transport = local->transport;
  else if (remote)
    candidate->transport = conn_check_match_transport (remote->transport);
  else {
    if (base_socket->type == NICE_SOCKET_TYPE_UDP_BSD ||
        base_socket->type == NICE_SOCKET_TYPE_UDP_TURN)
      candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
    else
      candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
  }
818 819 820
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
Youness Alaoui's avatar
Youness Alaoui committed
821
  candidate->sockptr = base_socket;
822
  candidate->base_addr = base_socket->addr;
823

824 825
  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
Jakub Adam's avatar
Jakub Adam committed
826 827
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
828
    candidate->priority = nice_candidate_msn_priority (candidate);
829 830 831
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
        agent->reliable, FALSE);
832
  } else {
833 834
    candidate->priority = nice_candidate_ice_priority (candidate,
        agent->reliable, FALSE);
835
  }
836

837
  priv_assign_foundation (agent, candidate);
838

Jakub Adam's avatar
Jakub Adam committed
839 840
  if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
       agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
      remote && local) {
    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);

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

    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);

    candidate->username = g_base64_encode (new_username, local_size + remote_size);
    g_free(new_username);
    g_free(decoded_local);
    g_free(decoded_remote);

    candidate->password = g_strdup(local->password);
  } else if (local) {
    g_free(candidate->username);
    g_free(candidate->password);

    candidate->username = g_strdup(local->username);
    candidate->password = g_strdup(local->password);
  }
870

871
  result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
872
  if (result != TRUE) {
873
    /* error: memory allocation, or duplicate candidate */
874
    nice_candidate_free (candidate), candidate = NULL;
875 876 877 878 879 880
  }

  return candidate;
}


881
/*
882 883 884 885
 * Adds a new peer reflexive candidate to the list of known
 * remote candidates. The candidate is however not paired with
 * existing local candidates.
 *
886
 * See ICE sect 7.2.1.3 "Learning Peer Reflexive Candidates" (ID-19).
887 888 889 890 891 892 893
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
  NiceAgent *agent,
  Stream *stream,
  Component *component,
894
  guint32 priority,
895
  const NiceAddress *remote_address,
896
  NiceSocket *nicesock,
897 898
  NiceCandidate *local,
  NiceCandidate *remote)
899 900 901 902
{
  NiceCandidate *candidate;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
903 904 905

  candidate->addr = *remote_address;
  candidate->base_addr = *remote_address;
906 907 908 909 910 911
  if (remote)
    candidate->transport = remote->transport;
  else if (local)
    candidate->transport = conn_check_match_transport (local->transport);
  else {
    if (nicesock->type == NICE_SOCKET_TYPE_UDP_BSD ||
912
        nicesock->type == NICE_SOCKET_TYPE_UDP_TURN)
913 914 915 916
      candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
    else
      candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
  }
Youness Alaoui's avatar
Youness Alaoui committed
917
  candidate->sockptr = nicesock;
918 919
  candidate->stream_id = stream->id;
  candidate->component_id = component->id;
920 921 922 923 924 925 926

  /* 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);
Jakub Adam's avatar
Jakub Adam committed
927 928
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
929
    candidate->priority = nice_candidate_msn_priority (candidate);
930 931 932
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
        agent->reliable, FALSE);
933
  } else {
934 935
    candidate->priority = nice_candidate_ice_priority (candidate,
        agent->reliable, FALSE);
936
  }
937

938
  priv_assign_remote_foundation (agent, candidate);
939

Jakub Adam's avatar
Jakub Adam committed
940 941
  if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
       agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
942 943 944 945 946 947 948 949
      remote && local) {
    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);
950

951 952
    decoded_local = g_base64_decode (local->username, &local_size);
    decoded_remote = g_base64_decode (remote->username, &remote_size);
953

954 955 956
    new_username = g_new0(guchar, local_size + remote_size);
    memcpy(new_username, decoded_remote, remote_size);
    memcpy(new_username + remote_size, decoded_local, local_size);
957

958 959 960 961
    candidate->username = g_base64_encode (new_username, local_size + remote_size);
    g_free(new_username);
    g_free(decoded_local);
    g_free(decoded_remote);
962

963 964 965 966 967 968 969
    candidate->password = g_strdup(remote->password);
  } else if (remote) {
    g_free (candidate->username);
    g_free (candidate->password);
    candidate->username = g_strdup(remote->username);
    candidate->password = g_strdup(remote->password);
  }
970

971 972
  /* note: candidate username and password are left NULL as stream 
     level ufrag/password are used */
973

974 975
  component->remote_candidates = g_slist_append (component->remote_candidates,
      candidate);