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
  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 333 334 335 336 337 338 339 340
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;

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

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

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


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

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

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

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

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

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

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

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


}

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

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

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

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

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

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

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

585 586 587
  *outcandidate = candidate;

  return HOST_CANDIDATE_SUCCESS;
588 589 590

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

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

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

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

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

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

643 644
  priv_generate_candidate_credentials (agent, candidate);
  priv_assign_foundation (agent, candidate);
645

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

  return candidate;
}

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 701 702
/*
 * 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);
    }
  }
}
703

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

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

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

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

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

756 757
  candidate->sockptr = relay_socket;
  candidate->base_addr = base_socket->addr;
758

759
  priv_generate_candidate_credentials (agent, candidate);
760

761 762 763 764
  /* 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);
765 766
  }

767 768
  priv_assign_foundation (agent, candidate);

769
  if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate))
770 771
    goto errors;

772
  component_attach_socket (component, relay_socket);
773 774
  agent_signal_new_candidate (agent, candidate);

775
  return candidate;
776 777 778 779 780 781

errors:
  nice_candidate_free (candidate);
  if (relay_socket)
    nice_socket_free (relay_socket);
  return NULL;
782 783
}

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

  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
809 810 811 812 813 814 815 816 817 818 819
  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;
  }
820 821 822
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
Youness Alaoui's avatar
Youness Alaoui committed
823
  candidate->sockptr = base_socket;
824
  candidate->base_addr = base_socket->addr;
825

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

839
  priv_assign_foundation (agent, candidate);
840

Jakub Adam's avatar
Jakub Adam committed
841 842
  if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
       agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
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 870 871
      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);
  }
872

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

  return candidate;
}


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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
905 906 907

  candidate->addr = *remote_address;
  candidate->base_addr = *remote_address;
908 909 910 911 912 913
  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 ||
914
        nicesock->type == NICE_SOCKET_TYPE_UDP_TURN)
915 916 917 918
      candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
    else
      candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
  }
Youness Alaoui's avatar
Youness Alaoui committed
919
  candidate->sockptr = nicesock;
920 921
  candidate->stream_id = stream->id;
  candidate->component_id = component->id;
922 923 924 925 926 927 928

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

940
  priv_assign_remote_foundation (agent, candidate);
941

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

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

956 957 958
    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);
959

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

965 966 967 968 969 970 971
    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);
  }
972

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

976 977
  component->remote_candidates = g_slist_append (component->remote_candidates,
      candidate);
978

979
  agent_signal_new_remote_candidate (agent, candidate);
980 981

  return candidate;
982
}
983

984
/* 
985 986 987 988 989 990 991 992
 * 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
993
static gboolean priv_discovery_tick_unlocked (gpointer pointer)
994 995 996 997 998
{
  CandidateDiscovery *cand;
  NiceAgent *agent = pointer;
  GSList *i;
  int not_done = 0; /* note: track whether to continue timer */
999
  size_t buffer_len = 0;
1000 1001 1002

  {
    static int tick_counter = 0;
1003
    if (tick_counter++ % 50 == 0)
1004
      nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
  }

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

1016
      if (nice_debug_is_enabled ()) {
1017 1018
        gchar tmpbuf[INET6_ADDRSTRLEN];
        nice_address_to_string (&cand->server, tmpbuf);
1019
        nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.",
1020
            agent, cand->type, tmpbuf);
1021 1022
      }
      if (nice_address_is_valid (&cand->server) &&
1023 1024
          (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
              cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
1025

1026
	agent_signal_component_state_change (agent,
1027 1028 1029
					     cand->stream->id,
					     cand->component->id,
					     NICE_COMPONENT_STATE_GATHERING);
1030

1031
        if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
1032
          buffer_len = stun_usage_bind_create (&cand->stun_agent,
1033 1034
              &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer));
        } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) {
1035
          uint8_t *username = (uint8_t *)cand->turn->username;
1036
          gsize username_len = strlen (cand->turn->username);