discovery.c 37.1 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, NiceComponent *component, NiceCandidate *candidate)
309
{
310
  GSList *i;
311

312 313
  g_assert (candidate != NULL);

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

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

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

  return TRUE;
}

332
static guint priv_highest_remote_foundation (NiceComponent *component)
333 334 335 336 337 338 339 340
{
  GSList *i;
  guint highest = 1;
  gchar foundation[NICE_CANDIDATE_MAX_FOUNDATION];

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

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

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
/* 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);
}

374
/*
375 376
 * Assings a foundation to the candidate.
 *
377
 * Implements the mechanism described in ICE sect
378
 * 4.1.1.3 "Computing Foundations" (ID-19).
379
 */
380
static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
381
{
382 383 384
  GSList *i, *j, *k;

  for (i = agent->streams; i; i = i->next) {
385
    NiceStream *stream = i->data;
386
    for (j = stream->components; j; j = j->next) {
387
      NiceComponent *component = j->data;
388 389
      for (k = component->local_candidates; k; k = k->next) {
	NiceCandidate *n = k->data;
390 391 392 393

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

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

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

425 426 427 428
static void priv_assign_remote_foundation (NiceAgent *agent, NiceCandidate *candidate)
{
  GSList *i, *j, *k;
  guint next_remote_id;
429
  NiceComponent *component = NULL;
430 431

  for (i = agent->streams; i; i = i->next) {
432
    NiceStream *stream = i->data;
433
    for (j = stream->components; j; j = j->next) {
434
      NiceComponent *c = j->data;
435 436 437 438 439 440 441 442 443 444 445

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


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

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

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

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

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

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

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

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


}

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

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

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

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

573 574 575
  candidate->sockptr = nicesock;
  candidate->addr = nicesock->addr;
  candidate->base_addr = nicesock->addr;
576

577 578 579
  if (!priv_add_local_candidate_pruned (agent, stream_id, component,
          candidate)) {
    res = HOST_CANDIDATE_REDUNDANT;
580
    goto errors;
581
  }
582

583
  _priv_set_socket_tos (agent, nicesock, stream->tos);
584
  nice_component_attach_socket (component, nicesock);
Youness Alaoui's avatar
Youness Alaoui committed
585

586 587 588
  *outcandidate = candidate;

  return HOST_CANDIDATE_SUCCESS;
589 590 591

errors:
  nice_candidate_free (candidate);
592 593
  if (nicesock)
    nice_socket_free (nicesock);
594
  return res;
595 596
}

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

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

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

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

631 632
  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
Jakub Adam's avatar
Jakub Adam committed
633 634
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
635
    candidate->priority = nice_candidate_msn_priority (candidate);
636 637
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
638
        FALSE, nat_assisted);
639
  } else {
640
    candidate->priority =  nice_candidate_ice_priority (candidate,
641
        FALSE, nat_assisted);
642
  }
643

644 645
  candidate->priority = ensure_unique_priority (component,
      candidate->priority);
646 647
  priv_generate_candidate_credentials (agent, candidate);
  priv_assign_foundation (agent, candidate);
648

649
  result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
650 651 652 653 654 655
  if (result) {
    agent_signal_new_candidate (agent, candidate);
  }
  else {
    /* error: duplicate candidate */
    nice_candidate_free (candidate), candidate = NULL;
656 657 658 659 660
  }

  return candidate;
}

661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
/*
 * 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)
{
676
  NiceComponent *component;
677
  NiceStream *stream;
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
  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);
    }
  }
}
706

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

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
732 733 734 735 736
  candidate->transport = transport;
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
  candidate->turn = turn_server_ref (turn);
737

738 739 740 741 742 743 744 745 746 747 748
  /* step: link to the base candidate+socket */
  relay_socket = nice_udp_turn_socket_new (agent->main_context, address,
      base_socket, &turn->server,
      turn->username, turn->password,
      agent_to_turn_socket_compatibility (agent));
  if (!relay_socket)
    goto errors;

  candidate->sockptr = relay_socket;
  candidate->base_addr = base_socket->addr;

749 750
  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
Jakub Adam's avatar
Jakub Adam committed
751 752
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
753
    candidate->priority = nice_candidate_msn_priority (candidate);
754 755
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
756
        FALSE, FALSE);
757
  } else {
758
    candidate->priority =  nice_candidate_ice_priority (candidate,
759
        FALSE, FALSE);
760 761
  }

762 763
  candidate->priority = ensure_unique_priority (component,
      candidate->priority);
764
  priv_generate_candidate_credentials (agent, candidate);
765

766 767 768 769
  /* 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);
770 771
  }

772 773
  priv_assign_foundation (agent, candidate);

774
  if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate))
775 776
    goto errors;

777
  nice_component_attach_socket (component, relay_socket);
778 779
  agent_signal_new_candidate (agent, candidate);

780
  return candidate;
781 782 783 784 785 786

errors:
  nice_candidate_free (candidate);
  if (relay_socket)
    nice_socket_free (relay_socket);
  return NULL;
787 788
}

789
/*
790 791 792 793 794
 * Creates a peer reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
795
NiceCandidate*
796 797 798 799 800
discovery_add_peer_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
801
  NiceSocket *base_socket,
802 803
  NiceCandidate *local,
  NiceCandidate *remote)
804 805
{
  NiceCandidate *candidate;
806
  NiceComponent *component;
807
  NiceStream *stream;
808
  gboolean result;
809 810 811 812 813

  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
814 815 816 817 818 819 820 821 822 823 824
  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;
  }
825 826 827
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
Youness Alaoui's avatar
Youness Alaoui committed
828
  candidate->sockptr = base_socket;
829
  candidate->base_addr = base_socket->addr;
830

831 832
  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
Jakub Adam's avatar
Jakub Adam committed
833 834
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
835
    candidate->priority = nice_candidate_msn_priority (candidate);
836 837
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
838
        FALSE, FALSE);
839
  } else {
840
    candidate->priority = nice_candidate_ice_priority (candidate,
841
        FALSE, FALSE);
842
  }
843

844 845
  candidate->priority = ensure_unique_priority (component,
      candidate->priority);
846
  priv_assign_foundation (agent, candidate);
847

Jakub Adam's avatar
Jakub Adam committed
848 849
  if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
       agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
      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);
  }
879

880
  result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
881
  if (result != TRUE) {
882
    /* error: memory allocation, or duplicate candidate */
883
    nice_candidate_free (candidate), candidate = NULL;
884 885 886 887 888 889
  }

  return candidate;
}


890
/*
891 892 893 894
 * Adds a new peer reflexive candidate to the list of known
 * remote candidates. The candidate is however not paired with
 * existing local candidates.
 *
895
 * See ICE sect 7.2.1.3 "Learning Peer Reflexive Candidates" (ID-19).
896 897 898 899 900
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
  NiceAgent *agent,
901
  NiceStream *stream,
902
  NiceComponent *component,
903
  guint32 priority,
904
  const NiceAddress *remote_address,
905
  NiceSocket *nicesock,
906 907
  NiceCandidate *local,
  NiceCandidate *remote)
908 909 910 911
{
  NiceCandidate *candidate;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
912 913 914

  candidate->addr = *remote_address;
  candidate->base_addr = *remote_address;
915 916 917 918 919 920
  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 ||
921
        nicesock->type == NICE_SOCKET_TYPE_UDP_TURN)
922 923 924 925
      candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
    else
      candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
  }
Youness Alaoui's avatar
Youness Alaoui committed
926
  candidate->sockptr = nicesock;
927 928
  candidate->stream_id = stream->id;
  candidate->component_id = component->id;
929 930 931 932 933 934 935

  /* 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
936 937
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
938