discovery.c 31.9 KB
Newer Older
1
2
3
/*
 * This file is part of the Nice GLib ICE library.
 *
4
5
6
 * (C) 2008-2009 Collabora Ltd.
 *  Contact: Youness Alaoui
 * (C) 2007-2009 Nokia Corporation. All rights reserved.
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *  Contact: Kai Vehmanen
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Nice GLib ICE library.
 *
 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
 * Corporation. All Rights Reserved.
 *
 * Contributors:
25
 *   Youness Alaoui, Collabora Ltd.
26
27
28
29
30
31
32
33
34
35
36
37
38
 *   Kai Vehmanen, Nokia
 *
 * Alternatively, the contents of this file may be used under the terms of the
 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
 * case the provisions of LGPL are applicable instead of those above. If you
 * wish to allow use of your version of this file only under the terms of the
 * LGPL and not to allow others to use your version of this file under the
 * MPL, indicate your decision by deleting the provisions above and replace
 * them with the notice and other provisions required by the LGPL. If you do
 * not delete the provisions above, a recipient may use your version of this
 * file under either the MPL or the LGPL.
 */

39
/*
40
41
42
 * @file discovery.c
 * @brief ICE candidate discovery functions
 */
43

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

48
49
#include <glib.h>

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

54
55
#include "debug.h"

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

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

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

85
/*
86
87
88
89
 * Frees all discovery related resources for the agent.
 */
void discovery_free (NiceAgent *agent)
{
90

91
92
93
94
  g_slist_foreach (agent->discovery_list, discovery_free_item, NULL);
  g_slist_free (agent->discovery_list);
  agent->discovery_list = NULL;
  agent->discovery_unsched_items = 0;
95

96
97
98
99
100
  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;
  }
101
102
}

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

  for (i = agent->discovery_list; i ; ) {
114
115
    CandidateDiscovery *cand = i->data;
    GSList *next = i->next;
116
117

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

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

130

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

  g_assert (user_data == NULL);

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

159
160
161
162
  username = (uint8_t *)cand->turn->username;
  username_len = (size_t) strlen (cand->turn->username);
  password = (uint8_t *)cand->turn->password;
  password_len = (size_t) strlen (cand->turn->password);
163

164
165
  if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
      turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
166
167
168
169
    username = g_base64_decode ((gchar *)username, &username_len);
    password = g_base64_decode ((gchar *)password, &password_len);
  }

170
  buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
171
172
173
174
      &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,
175
      agent_to_turn_compatibility (agent));
176
177

  if (buffer_len > 0) {
178
179
180
181
182
183
184
    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);

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

  }

195
196
  if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
      turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
197
198
199
    g_free (username);
    g_free (password);
  }
200
201
202
203

  g_slice_free (CandidateRefresh, cand);
}

204
/*
205
206
207
208
 * Frees all discovery related resources for the agent.
 */
void refresh_free (NiceAgent *agent)
{
209
210
211
  g_slist_foreach (agent->refresh_list, refresh_free_item, NULL);
  g_slist_free (agent->refresh_list);
  agent->refresh_list = NULL;
212
213
}

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

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

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

    i = next;
  }

}

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

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

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

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

264
265
  component->local_candidates = g_slist_append (component->local_candidates,
      candidate);
266
  conn_check_add_for_local_candidate(agent, stream_id, component, candidate);
267
268
269
270

  return TRUE;
}

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

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

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

  g_return_val_if_reached (highest);
}

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

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

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

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

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

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

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
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;
367
	NiceAddress temp = n->addr;
368
369
370
371
372
373
374
375
376
377

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

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

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

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


407
static
408
409
void priv_generate_candidate_credentials (NiceAgent *agent,
    NiceCandidate *candidate)
410
411
{

Jakub Adam's avatar
Jakub Adam committed
412
413
  if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
      agent->compatibility == NICE_COMPATIBILITY_OC2007) {
414
415
416
    guchar username[32];
    guchar password[16];

Olivier Crête's avatar
Olivier Crête committed
417
418
    g_free (candidate->username);
    g_free (candidate->password);
419
420
421
422
423
424
425

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

426
427
428
  } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    gchar username[16];

Olivier Crête's avatar
Olivier Crête committed
429
430
    g_free (candidate->username);
    g_free (candidate->password);
431
432
433
434
    candidate->password = NULL;

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

435
    candidate->username = g_strndup (username, 16);
436
437
438
439
440
  }


}

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

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
462
463
464
465
466
467
  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
468
469
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
470
471
472
473
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else {
    candidate->priority = nice_candidate_ice_priority (candidate);
  }
474

475
476
477
478
479
480
  priv_generate_candidate_credentials (agent, candidate);
  priv_assign_foundation (agent, candidate);

  /* note: candidate username and password are left NULL as stream
     level ufrag/password are used */
  udp_socket = nice_udp_bsd_socket_new (address);
481
482
  if (!udp_socket)
    goto errors;
483

484
485
486
  candidate->sockptr = udp_socket;
  candidate->addr = udp_socket->addr;
  candidate->base_addr = udp_socket->addr;
487

488
  if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate))
489
    goto errors;
490

491
  _priv_set_socket_tos (agent, udp_socket, stream->tos);
492
  component_attach_socket (component, udp_socket);
Youness Alaoui's avatar
Youness Alaoui committed
493

494
  return candidate;
495
496
497
498
499
500

errors:
  nice_candidate_free (candidate);
  if (udp_socket)
    nice_socket_free (udp_socket);
  return NULL;
501
502
}

503
/*
504
505
506
507
508
 * 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
509
NiceCandidate*
510
511
512
513
514
discovery_add_server_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
515
  NiceSocket *base_socket)
516
517
518
519
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
520
  gboolean result = FALSE;
521
522
523
524
525

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
526
527
528

  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
Jakub Adam's avatar
Jakub Adam committed
529
530
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
531
532
533
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else {
    candidate->priority =  nice_candidate_ice_priority_full
534
        (NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE, 0, component_id);
535
536
537
538
  }
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
539

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

544
545
  priv_generate_candidate_credentials (agent, candidate);
  priv_assign_foundation (agent, candidate);
546

547
  result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
548
549
550
551
552
553
  if (result) {
    agent_signal_new_candidate (agent, candidate);
  }
  else {
    /* error: duplicate candidate */
    nice_candidate_free (candidate), candidate = NULL;
554
555
556
557
558
  }

  return candidate;
}

559

560
/*
561
562
563
564
565
566
567
568
569
570
571
 * Creates a server reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate* 
discovery_add_relay_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
572
573
  NiceSocket *base_socket,
  TurnServer *turn)
574
575
576
577
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
578
  NiceSocket *relay_socket = NULL;
579
580
581
582
583

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
584
585
586

  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
Jakub Adam's avatar
Jakub Adam committed
587
588
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
589
590
591
592
593
594
595
596
597
598
599
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else {
    candidate->priority =  nice_candidate_ice_priority_full
        (NICE_CANDIDATE_TYPE_PREF_RELAYED, 0, component_id);
  }
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
  candidate->turn = turn;

  /* step: link to the base candidate+socket */
600
  relay_socket = nice_turn_socket_new (agent->main_context, address,
601
602
603
      base_socket, &turn->server,
      turn->username, turn->password,
      agent_to_turn_socket_compatibility (agent));
604
605
  if (!relay_socket)
    goto errors;
606

607
608
  candidate->sockptr = relay_socket;
  candidate->base_addr = base_socket->addr;
609

610
  priv_generate_candidate_credentials (agent, candidate);
611

612
613
614
615
  /* 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);
616
617
  }

618
619
  priv_assign_foundation (agent, candidate);

620
  if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate))
621
622
    goto errors;

623
  component_attach_socket (component, relay_socket);
624
625
  agent_signal_new_candidate (agent, candidate);

626
  return candidate;
627
628
629
630
631
632

errors:
  nice_candidate_free (candidate);
  if (relay_socket)
    nice_socket_free (relay_socket);
  return NULL;
633
634
}

635
/*
636
637
638
639
640
 * Creates a peer reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
641
NiceCandidate*
642
643
644
645
646
discovery_add_peer_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
647
  NiceSocket *base_socket,
648
649
  NiceCandidate *local,
  NiceCandidate *remote)
650
651
652
653
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
654
  gboolean result;
655
656
657
658
659
660

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);

661
662
663
  candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
Jakub Adam's avatar
Jakub Adam committed
664
665
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
666
667
668
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else {
    candidate->priority = nice_candidate_ice_priority_full
669
        (NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 0, component_id);
670
671
672
673
674
  }
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
  candidate->base_addr = base_socket->addr;
675

676

677
  priv_assign_foundation (agent, candidate);
678

Jakub Adam's avatar
Jakub Adam committed
679
680
  if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
       agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
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
706
707
708
709
      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);
  }
710

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

715
  result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
716
  if (result != TRUE) {
717
    /* error: memory allocation, or duplicate candidate */
718
    nice_candidate_free (candidate), candidate = NULL;
719
720
721
722
723
724
  }

  return candidate;
}


725
/*
726
727
728
729
 * Adds a new peer reflexive candidate to the list of known
 * remote candidates. The candidate is however not paired with
 * existing local candidates.
 *
730
 * See ICE sect 7.2.1.3 "Learning Peer Reflexive Candidates" (ID-19).
731
732
733
734
735
736
737
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
  NiceAgent *agent,
  Stream *stream,
  Component *component,
738
  guint32 priority,
739
  const NiceAddress *remote_address,
740
  NiceSocket *udp_socket,
741
742
  NiceCandidate *local,
  NiceCandidate *remote)
743
744
745
{
  NiceCandidate *candidate;

746
747
748
  /* XXX: for use compiler */
  (void)udp_socket;

749
  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
750
751
752
753
754
755
756
757
758
759
760

  candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
  candidate->addr = *remote_address;
  candidate->base_addr = *remote_address;

  /* 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
761
762
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
763
764
765
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else {
    candidate->priority = nice_candidate_ice_priority_full
766
        (NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 0, component->id);
767
768
769
  }
  candidate->stream_id = stream->id;
  candidate->component_id = component->id;
770

771

772
  priv_assign_remote_foundation (agent, candidate);
773

Jakub Adam's avatar
Jakub Adam committed
774
775
  if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
       agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
776
777
778
779
780
781
782
783
      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);
784

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

788
789
790
    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);
791

792
793
794
795
    candidate->username = g_base64_encode (new_username, local_size + remote_size);
    g_free(new_username);
    g_free(decoded_local);
    g_free(decoded_remote);
796

797
798
799
800
801
802
803
    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);
  }
804

805
806
807
  candidate->sockptr = NULL; /* not stored for remote candidates */
  /* note: candidate username and password are left NULL as stream 
     level ufrag/password are used */
808

809
810
  component->remote_candidates = g_slist_append (component->remote_candidates,
      candidate);
811

812
  agent_signal_new_remote_candidate (agent, candidate);
813
814

  return candidate;
815
}
816

817
/* 
818
819
820
821
822
823
824
825
 * 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
826
static gboolean priv_discovery_tick_unlocked (gpointer pointer)
827
828
829
830
831
{
  CandidateDiscovery *cand;
  NiceAgent *agent = pointer;
  GSList *i;
  int not_done = 0; /* note: track whether to continue timer */
832
  size_t buffer_len = 0;
833
834
835

  {
    static int tick_counter = 0;
836
    if (tick_counter++ % 50 == 0)
837
      nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
838
839
840
841
842
843
844
845
846
847
  }

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

849
      if (nice_debug_is_enabled ()) {
850
851
        gchar tmpbuf[INET6_ADDRSTRLEN];
        nice_address_to_string (&cand->server, tmpbuf);
852
853
        nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.\n",
            agent, cand->type, tmpbuf);
854
855
      }
      if (nice_address_is_valid (&cand->server) &&
856
857
          (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
              cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
858

859
	agent_signal_component_state_change (agent,
860
861
862
					     cand->stream->id,
					     cand->component->id,
					     NICE_COMPONENT_STATE_GATHERING);
863

864
        if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
865
          buffer_len = stun_usage_bind_create (&cand->stun_agent,
866
867
              &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer));
        } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) {
868
869
870
871
          uint8_t *username = (uint8_t *)cand->turn->username;
          size_t username_len = (size_t) strlen (cand->turn->username);
          uint8_t *password = (uint8_t *)cand->turn->password;
          size_t password_len = (size_t) strlen (cand->turn->password);
872
873
          StunUsageTurnCompatibility turn_compat =
              agent_to_turn_compatibility (agent);
874

875
876
          if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
              turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
877
878
879
880
            username = g_base64_decode ((gchar *)username, &username_len);
            password = g_base64_decode ((gchar *)password, &password_len);
          }

881
          buffer_len = stun_usage_turn_create (&cand->stun_agent,
882
              &cand->stun_message,  cand->stun_buffer, sizeof(cand->stun_buffer),
883
              cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
884
              STUN_USAGE_TURN_REQUEST_PORT_NORMAL,
885
              -1, -1,
886
887
              username, username_len,
              password, password_len,
888
              turn_compat);
889

890
891
          if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
              turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
892
893
            g_free (cand->msn_turn_username);
            g_free (cand->msn_turn_password);
894
895
            cand->msn_turn_username = username;
            cand->msn_turn_password = password;
896
          }
897
        }
898
899

	if (buffer_len > 0) {
900
901
902
903
904
905
906
          if (nice_socket_is_reliable (cand->nicesock)) {
            stun_timer_start_reliable (&cand->timer,
                STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT);
          } else {
            stun_timer_start (&cand->timer, 200,
                STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
          }
907
908

          /* send the conncheck */
909
          nice_socket_send (cand->nicesock, &cand->server,
910
911
              buffer_len, (gchar *)cand->stun_buffer);

912
913
914
	  /* case: success, start waiting for the result */
	  g_get_current_time (&cand->next_tick);

915
	} else {
916
917
	  /* case: error in starting discovery, start the next discovery */
	  cand->done = TRUE;
918
919
	  cand->stun_message.buffer = NULL;
	  cand->stun_message.buffer_len = 0;
920
	  continue;
921
922
	}
      }
923
      else
924
925
	/* allocate relayed candidates */
	g_assert_not_reached ();
926

927
928
      ++not_done; /* note: new discovery scheduled */
    }
Youness Alaoui's avatar
Youness Alaoui committed
929

930
931
932
933
934
    if (cand->done != TRUE) {
      GTimeVal now;

      g_get_current_time (&now);

935
      if (cand->stun_message.buffer == NULL) {
936
	nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent);
937
938
	cand->done = TRUE;
      }
939
      else if (priv_timer_expired (&cand->next_tick, &now)) {
940
941
        switch (stun_timer_refresh (&cand->timer)) {
          case STUN_USAGE_TIMER_RETURN_TIMEOUT:
942
943
944
945
946
947
948
949
950
951
952
953
954
955
            {
              /* Time out */
              /* case: error, abort processing */
              StunTransactionId id;

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

              cand->done = TRUE;
              cand->stun_message.buffer = NULL;
              cand->stun_message.buffer_len = 0;
              nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent);
              break;
            }
956
          case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
957
958
959
960
961
962
963
            {
              /* case: not ready complete, so schedule next timeout */
              unsigned int timeout = stun_timer_remainder (&cand->timer);

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

964
              /* retransmit */
965
              nice_socket_send (cand->nicesock, &cand->server,
966
967
968
969
970
971
972
                  stun_message_length (&cand->stun_message),
                  (gchar *)cand->stun_buffer);

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

Youness Alaoui's avatar
Youness Alaoui committed
973
974
975
              ++not_done; /* note: retry later */
              break;
            }
976
          case STUN_USAGE_TIMER_RETURN_SUCCESS:
Youness Alaoui's avatar
Youness Alaoui committed
977
            {
978
979
              unsigned int timeout = stun_timer_remainder (&cand->timer);

Youness Alaoui's avatar
Youness Alaoui committed
980
981
982
              cand->next_tick = now;
              g_time_val_add (&cand->next_tick, timeout * 1000);

983
984
985
              ++not_done; /* note: retry later */
              break;
            }
986
987
988
          default:
            /* Nothing to do. */
            break;
989
	}
990

Youness Alaoui's avatar
Youness Alaoui committed
991
      } else {
992
	++not_done; /* note: discovery not expired yet */
Youness Alaoui's avatar
Youness Alaoui committed
993
      }
994
995
996
997
    }
  }

  if (not_done == 0) {
998
    nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent);
999
1000
1001

    discovery_free (agent);

1002
    agent_gathering_done (agent);
1003

1004
1005
1006
1007
1008
1009
1010
    /* note: no pending timers, return FALSE to stop timer */
    return FALSE;
  }

  return TRUE;
}

Youness Alaoui's avatar
Youness Alaoui committed
1011
1012
1013
1014
1015
static gboolean priv_discovery_tick (gpointer pointer)
{
  NiceAgent *agent = pointer;
  gboolean ret;

1016
1017
1018
1019
1020
1021
1022
1023
  agent_lock();
  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in priv_discovery_tick");
    agent_unlock ();
    return FALSE;
  }

Youness Alaoui's avatar
Youness Alaoui committed
1024
  ret = priv_discovery_tick_unlocked (pointer);
1025
1026
1027
1028
1029
1030
1031
  if (ret == FALSE) {
    if (agent->discovery_timer_source != NULL) {
      g_source_destroy (agent->discovery_timer_source);
      g_source_unref (agent->discovery_timer_source);
      agent->discovery_timer_source = NULL;
    }
  }
1032
  agent_unlock();
Youness Alaoui's avatar
Youness Alaoui committed
1033
1034
1035
1036

  return ret;
}

1037
/*
1038
1039
 * Initiates the candidate discovery process by starting
 * the necessary timers.
1040
1041
1042
1043
1044
1045
1046
1047
1048
 *
 * @pre agent->discovery_list != NULL  // unsched discovery items available
 */
void discovery_schedule (NiceAgent *agent)
{
  g_assert (agent->discovery_list != NULL);

  if (agent->discovery_unsched_items > 0) {
    
1049
    if (agent->discovery_timer_source == NULL) {
1050
      /* step: run first iteration immediately */
Youness Alaoui's avatar
Youness Alaoui committed
1051
      gboolean res = priv_discovery_tick_unlocked (agent);
1052
      if (res == TRUE) {
1053
        agent->discovery_timer_source = agent_timeout_add_with_context (agent, agent->timer_ta, priv_discovery_tick, agent);
1054
1055
      }
    }
1056
1057
  }
}