discovery.c 26.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
 * This file is part of the Nice GLib ICE library.
 *
 * (C) 2007 Nokia Corporation. All rights reserved.
 *  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:
 *   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.
 */

36
37
38
39
/**
 * @file discovery.c
 * @brief ICE candidate discovery functions
 */
40

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

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

#include <netinet/in.h>
#include <arpa/inet.h>

#include <glib.h>

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 "udp-turn.h"
64

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

72
73
static StunUsageTurnCompatibility
priv_agent_to_turn_compatibility (NiceAgent *agent) {
74
  return agent->compatibility == NICE_COMPATIBILITY_DRAFT19 ?
75
      STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 :
76
77
78
      agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
      STUN_USAGE_TURN_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
79
80
81
82
83
84
85
86
87
88
89
90
      STUN_USAGE_TURN_COMPATIBILITY_MSN : STUN_USAGE_TURN_COMPATIBILITY_DRAFT9;
}

static NiceUdpTurnSocketCompatibility
priv_agent_to_udp_turn_compatibility (NiceAgent *agent) {
  return agent->compatibility == NICE_COMPATIBILITY_DRAFT19 ?
      NICE_UDP_TURN_SOCKET_COMPATIBILITY_DRAFT9 :
      agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
      NICE_UDP_TURN_SOCKET_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
      NICE_UDP_TURN_SOCKET_COMPATIBILITY_MSN :
      NICE_UDP_TURN_SOCKET_COMPATIBILITY_DRAFT9;
91
92
}

93
94
95
96
97
98
99
100
/**
 * Frees the CandidateDiscovery structure pointed to 
 * 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);
101
102
  g_free (cand->msn_turn_username);
  g_free (cand->msn_turn_password);
103
104
105
106
107
108
109
110
111
  g_slice_free (CandidateDiscovery, cand);
}

/**
 * Frees all discovery related resources for the agent.
 */
void discovery_free (NiceAgent *agent)
{
  if (agent->discovery_list) {
112
113
114
115
116
    GSList *tmp = agent->discovery_list;
    agent->discovery_list = NULL;

    g_slist_foreach (tmp, discovery_free_item, NULL);
    g_slist_free (tmp);
117
118
119

    agent->discovery_unsched_items = 0;
  }
120
121
122
123
124
  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;
  }
125
126
127
128
129
130
131
132
}

/**
 * Prunes the list of discovery processes for items related
 * to stream 'stream_id'. 
 *
 * @return TRUE on success, FALSE on a fatal error
 */
133
void discovery_prune_stream (NiceAgent *agent, guint stream_id)
134
135
136
137
{
  GSList *i;

  for (i = agent->discovery_list; i ; ) {
138
139
    CandidateDiscovery *cand = i->data;
    GSList *next = i->next;
140
141

    if (cand->stream->id == stream_id) {
142
      agent->discovery_list = g_slist_remove (agent->discovery_list, cand);
143
144
      discovery_free_item (cand, NULL);
    }
145
    i = next;
146
147
  }

148
149
150
151
152
153
154
155
  if (agent->discovery_list == NULL) {
    /* noone using the timer anymore, clean it up */
    discovery_free (agent);
  }
}

/**
 * Adds a new local candidate. Implements the candidate pruning
156
 * defined in ICE spec section 4.1.3 "Eliminating Redundant
157
 * Candidates" (ID-19).
158
159
160
161
162
163
164
165
166
167
 */
static gboolean priv_add_local_candidate_pruned (Component *component, NiceCandidate *candidate)
{
  GSList *modified_list, *i;

  for (i = component->local_candidates; i ; i = i->next) {
    NiceCandidate *c = i->data;
    
    if (nice_address_equal (&c->base_addr, &candidate->base_addr) &&
	nice_address_equal (&c->addr, &candidate->addr)) {
168
      nice_debug ("Candidate %p (component-id %u) redundant, ignoring.", candidate, component->id);
169
170
171
172
      return FALSE;
    }
  }

Olivier Crête's avatar
Olivier Crête committed
173
  modified_list = g_slist_append (component->local_candidates,
174
175
176
177
				 candidate);
  if (modified_list) {
    component->local_candidates = modified_list;
  }
178
179
180
181

  return TRUE;
}

182
183
184
/**
 * Assings a foundation to the candidate.
 *
Kai Vehmanen's avatar
Kai Vehmanen committed
185
 * Implements the mechanism described in ICE sect 
186
 * 4.1.1.3 "Computing Foundations" (ID-19).
187
 */
188
static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
189
{
190
191
192
193
194
195
196
197
198
  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;
199
200
201
202
203

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

	/* note: ports are not to be compared */
204
205
206
207
208
209
210
211
212
213
	nice_address_set_port (&temp,
               nice_address_get_port (&candidate->base_addr));
	
	if (candidate->type == n->type &&
	    nice_address_equal (&candidate->base_addr, &temp)) {
	  /* 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 */
	  memcpy (candidate->foundation, n->foundation, NICE_CANDIDATE_MAX_FOUNDATION);
214
215
216
217
218
219
220
221
          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);
          }
222
223
224
	  return;
	}
      }
225
226
    }
  }
227
      
228
229
230
  g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", agent->next_candidate_id++);
}

231
static
232
233
void priv_generate_candidate_credentials (NiceAgent *agent,
    NiceCandidate *candidate)
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
{

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

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

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

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

251
252
253
254
255
256
257
258
259
260
261
262
263
  } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    gchar username[16];

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

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

    candidate->username = g_strdup (username);

264
265
266
267
268
  }


}

269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/**
 * 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;
284
  NiceSocket *udp_socket = NULL;
285
  gboolean errors = FALSE;
286
287
288
289
290
291

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
  if (candidate) {
292
293
294
295
296
297
298
299
300
301
302
    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) * 1000;
    } else if (agent->compatibility == NICE_COMPATIBILITY_MSN)  {
      candidate->priority = nice_candidate_msn_priority (candidate) * 1000;
    } else {
      candidate->priority = nice_candidate_ice_priority (candidate);
    }
303

304
305
    priv_generate_candidate_credentials (agent, candidate);
    priv_assign_foundation (agent, candidate);
306

307
      /* note: candidate username and password are left NULL as stream
308
	 level ufrag/password are used */
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
    udp_socket = nice_socket_new (agent->udp_socket_factory, address);
    if (udp_socket) {
      gboolean result;

      priv_attach_stream_component_socket (agent, stream, component, udp_socket);

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

      result = priv_add_local_candidate_pruned (component, candidate);

      if (result == TRUE) {
        GSList *modified_list = g_slist_append (component->sockets, udp_socket);
        if (modified_list) {
          /* success: store a pointer to the sockaddr */
          component->sockets = modified_list;
          agent_signal_new_candidate (agent, candidate);
        } else { /* error: list memory allocation */
          candidate = NULL; /* note: candidate already owned by component */
        }
      } else {
        /* error: memory allocation, or duplicate candidates */
        errors = TRUE;
333
      }
334
335
    } else {
      /* error: socket new */
336
      errors = TRUE;
337
    }
338
339
  }

340
341
342
343
  /* clean up after errors */
  if (errors) {
    if (candidate)
      nice_candidate_free (candidate), candidate = NULL;
344
    if (udp_socket)
345
      nice_socket_free (udp_socket);
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  }
  
  return candidate;
}

/**
 * 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_server_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
363
  NiceSocket *base_socket)
364
365
366
367
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
368
  gboolean result = FALSE;
369
370
371
372
373
374

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
  if (candidate) {
375
376
377
378
379
380
    if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
      candidate->priority = nice_candidate_jingle_priority (candidate) * 1000;
    } else if (agent->compatibility == NICE_COMPATIBILITY_MSN)  {
      candidate->priority = nice_candidate_msn_priority (candidate) * 1000;
    } else {
      candidate->priority =  nice_candidate_ice_priority_full
381
        (NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE, 0, component_id);
382
    }
383
384
385
386
387
388
389
390
    candidate->stream_id = stream_id;
    candidate->component_id = component_id;
    candidate->addr = *address;

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

391
    priv_generate_candidate_credentials (agent, candidate);
392
393
    priv_assign_foundation (agent, candidate);

394
    result = priv_add_local_candidate_pruned (component, candidate);
395
396
397
398
    if (result) {
      agent_signal_new_candidate (agent, candidate);
    }
    else {
399
400
401
402
403
404
405
406
      /* error: memory allocation, or duplicate candidatet */
      nice_candidate_free (candidate), candidate = NULL;
    }
  }

  return candidate;
}

407
408
409
410
411
412
413
414
415
416
417
418
419

/**
 * 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,
420
  NiceSocket *base_socket)
421
422
423
424
425
426
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;
  gboolean result = FALSE;
  gboolean errors = FALSE;
427
  NiceSocket *relay_socket = NULL;
428
429
430
431
432
433

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
  if (candidate) {
434
    relay_socket = g_slice_new0 (NiceSocket);
435
436
437
438
439
440
441
442
443
444
445
446
447
448
    if (relay_socket) {
      if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
        candidate->priority = nice_candidate_jingle_priority (candidate) * 1000;
      } else if (agent->compatibility == NICE_COMPATIBILITY_MSN)  {
        candidate->priority = nice_candidate_msn_priority (candidate) * 1000;
      } 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;

      /* step: link to the base candidate+socket */
449
      if (nice_udp_turn_create_socket_full (agent->relay_socket_factory,
450
451
              relay_socket, address, base_socket, &component->turn_server,
              component->turn_username, component->turn_password,
452
              priv_agent_to_udp_turn_compatibility (agent))) {
453
454
455
        candidate->sockptr = relay_socket;
        candidate->base_addr = base_socket->addr;

456
457
458
459
460
461
462
463
        priv_generate_candidate_credentials (agent, candidate);

        /* Google uses the turn username as the candidate username */
        if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
          g_free (candidate->username);
          candidate->username = g_strdup (component->turn_username);
        }

464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
        priv_assign_foundation (agent, candidate);

        result = priv_add_local_candidate_pruned (component, candidate);
        if (result) {
          agent_signal_new_candidate (agent, candidate);
        } else /* error: memory allocation, or duplicate candidate */
          errors = TRUE;
      } else /* error: socket factory make */
        errors = TRUE;
    } else /* error: udp socket memory allocation */
      errors = TRUE;
  }

  /* clean up after errors */
  if (errors) {
    if (candidate)
      nice_candidate_free (candidate), candidate = NULL;
    if (relay_socket)
482
      g_slice_free (NiceSocket, relay_socket);
483
484
485
486
  }
  return candidate;
}

487
488
489
490
491
492
493
494
495
496
497
498
/**
 * Creates a peer reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate* 
discovery_add_peer_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
499
  NiceSocket *base_socket,
500
501
  NiceCandidate *local,
  NiceCandidate *remote)
502
503
504
505
506
507
508
509
510
511
512
513
514
{
  NiceCandidate *candidate;
  Component *component;
  Stream *stream;

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

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

    candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
515
516
517
518
519
520
    if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
      candidate->priority = nice_candidate_jingle_priority (candidate) * 1000;
    } else if (agent->compatibility == NICE_COMPATIBILITY_MSN)  {
      candidate->priority = nice_candidate_msn_priority (candidate) * 1000;
    } else {
      candidate->priority = nice_candidate_ice_priority_full
521
        (NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 0, component_id);
522
    }
523
524
525
526
    candidate->stream_id = stream_id;
    candidate->component_id = component_id;
    candidate->addr = *address;
    candidate->base_addr = base_socket->addr;
527

528

529
    priv_assign_foundation (agent, candidate);
530

531
    if (agent->compatibility == NICE_COMPATIBILITY_MSN &&
532
	remote && local) {
533
534
535
536
537
538
539
      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);
540

541
542
      decoded_local = g_base64_decode (local->username, &local_size);
      decoded_remote = g_base64_decode (remote->username, &remote_size);
543
544
545
546

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

548
549
550
551
      candidate->username = g_base64_encode (new_username, local_size + remote_size);
      g_free(new_username);
      g_free(decoded_local);
      g_free(decoded_remote);
552

553
      candidate->password = g_strdup(local->password);
554
555
    }

556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
    /* step: link to the base candidate+socket */
    candidate->sockptr = base_socket;
    candidate->base_addr = base_socket->addr;

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

  return candidate;
}

static guint priv_highest_remote_foundation (Component *component)
{
  GSList *i;
  guint highest = 0;

  for (i = component->remote_candidates; i; i = i->next) {
    NiceCandidate *cand = i->data;
    guint foundation_id = (guint)atoi (cand->foundation);
    if (foundation_id > highest)
      highest = foundation_id;
  }

  return highest;
}

/**
 * Adds a new peer reflexive candidate to the list of known
 * remote candidates. The candidate is however not paired with
 * existing local candidates.
 *
590
 * See ICE sect 7.2.1.3 "Learning Peer Reflexive Candidates" (ID-19).
591
592
593
594
595
596
597
598
599
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
  NiceAgent *agent,
  Stream *stream,
  Component *component,
  guint32 priority, 
  const NiceAddress *remote_address,
600
  NiceSocket *udp_socket,
601
602
  NiceCandidate *local,
  NiceCandidate *remote)
603
604
605
{
  NiceCandidate *candidate;

606
607
608
  /* XXX: for use compiler */
  (void)udp_socket;

609
610
611
612
613
614
  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
  if (candidate) {
    GSList *modified_list;

    guint next_remote_id = priv_highest_remote_foundation (component);

615
    candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
616
617
618
619
620
    candidate->addr = *remote_address;
    candidate->base_addr = *remote_address;
    candidate->priority = priority;;
    candidate->stream_id = stream->id;
    candidate->component_id = component->id;
621

622
    g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", next_remote_id);
623
624
625

    if (agent->compatibility == NICE_COMPATIBILITY_MSN &&
	remote && local) {
626
627
628
      guchar *new_username = NULL;
      guchar *decoded_local = NULL;
      guchar *decoded_remote = NULL;
629
630
631
      gsize local_size;
      gsize remote_size;
      g_free(candidate->username);
632
      g_free (candidate->password);
633

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

637
      new_username = g_new0(guchar, local_size + remote_size);
638
639
      memcpy(new_username, decoded_remote, remote_size);
      memcpy(new_username + remote_size, decoded_local, local_size);
640

641
642
643
644
      candidate->username = g_base64_encode (new_username, local_size + remote_size);
      g_free(new_username);
      g_free(decoded_local);
      g_free(decoded_remote);
645
646

      candidate->password = g_strdup(remote->password);
647
    }
648

649
650
651
    candidate->sockptr = NULL; /* not stored for remote candidates */
    /* note: candidate username and password are left NULL as stream 
             level ufrag/password are used */
652

653
654
655
656
657
    modified_list = g_slist_append (component->remote_candidates,
				    candidate);
    if (modified_list) {
      component->remote_candidates = modified_list;
      agent_signal_new_remote_candidate (agent, candidate);
658
    }
659
    else { /* error: memory alloc / list */
660
      nice_candidate_free (candidate), candidate = NULL;
661
    }
662
663
664
  }

  return candidate;
665
}
666
667
668
669
670
671
672
673
674
675

/** 
 * 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
676
static gboolean priv_discovery_tick_unlocked (gpointer pointer)
677
678
679
680
681
{
  CandidateDiscovery *cand;
  NiceAgent *agent = pointer;
  GSList *i;
  int not_done = 0; /* note: track whether to continue timer */
682
  size_t buffer_len = 0;
683

684
#ifndef NDEBUG
685
686
  {
    static int tick_counter = 0;
687
    if (tick_counter++ % 50 == 0)
688
      nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
689
690
691
692
693
694
695
696
697
698
699
  }
#endif

  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;
700
701
702
703

      {
        gchar tmpbuf[INET6_ADDRSTRLEN];
        nice_address_to_string (&cand->server, tmpbuf);
704
        nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s and socket %d.\n", agent,
705
706
707
            cand->type, tmpbuf, cand->socket);
      }
      if (nice_address_is_valid (&cand->server) &&
708
709
          (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
              cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
710

711
	agent_signal_component_state_change (agent,
712
713
714
					     cand->stream->id,
					     cand->component->id,
					     NICE_COMPONENT_STATE_GATHERING);
715

716
717
718
719
        if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
          buffer_len = stun_usage_bind_create (&agent->stun_agent,
              &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer));
        } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) {
720
721
722
723
724
725
726
727
728
729
          uint8_t *username = (uint8_t *)cand->component->turn_username;
          size_t username_len = (size_t) strlen (cand->component->turn_username);
          uint8_t *password = (uint8_t *)cand->component->turn_password;
          size_t password_len = (size_t) strlen (cand->component->turn_password);

          if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
            username = g_base64_decode ((gchar *)username, &username_len);
            password = g_base64_decode ((gchar *)password, &password_len);
          }

730
          buffer_len = stun_usage_turn_create (&cand->turn_agent,
731
              &cand->stun_message,  cand->stun_buffer, sizeof(cand->stun_buffer),
732
              cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
733
734
              STUN_USAGE_TURN_REQUEST_PORT_NORMAL,
              0, 0,
735
736
              username, username_len,
              password, password_len,
737
              priv_agent_to_turn_compatibility (agent));
738
739

          if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
740
741
            cand->msn_turn_username = username;
            cand->msn_turn_password = password;
742
          }
743
        }
744
745
746
747
748

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

          /* send the conncheck */
749
          nice_socket_send (cand->nicesock, &cand->server,
750
751
              buffer_len, (gchar *)cand->stun_buffer);

752
753
754
	  /* case: success, start waiting for the result */
	  g_get_current_time (&cand->next_tick);

755
	} else {
756
757
	  /* case: error in starting discovery, start the next discovery */
	  cand->done = TRUE;
758
759
	  cand->stun_message.buffer = NULL;
	  cand->stun_message.buffer_len = 0;
760
	  continue;
761
762
	}
      }
763
      else
764
765
	/* allocate relayed candidates */
	g_assert_not_reached ();
766

767
768
      ++not_done; /* note: new discovery scheduled */
    }
Youness Alaoui's avatar
Youness Alaoui committed
769

770
771
772
773
774
    if (cand->done != TRUE) {
      GTimeVal now;

      g_get_current_time (&now);

775
      if (cand->stun_message.buffer == NULL) {
776
	nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent);
777
778
	cand->done = TRUE;
      }
779
      else if (priv_timer_expired (&cand->next_tick, &now)) {
Youness Alaoui's avatar
Youness Alaoui committed
780
781
        int timeout = stun_timer_refresh (&cand->timer);
        switch (timeout) {
782
783
784
785
786
          case -1:
            /* case: error, abort processing */
            cand->done = TRUE;
            cand->stun_message.buffer = NULL;
            cand->stun_message.buffer_len = 0;
787
            nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent);
788
789
790
791
792
793
794
795
796
797
            break;
          case 0:
            {
              /* 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);

              /* TODO retransmit */
798
              nice_socket_send (cand->nicesock, &cand->server,
799
800
801
802
803
804
805
                  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
806
807
808
809
810
811
812
813
              ++not_done; /* note: retry later */
              break;
            }
          default:
            {
              cand->next_tick = now;
              g_time_val_add (&cand->next_tick, timeout * 1000);

814
815
816
              ++not_done; /* note: retry later */
              break;
            }
817
	}
818

Youness Alaoui's avatar
Youness Alaoui committed
819
      } else {
820
	++not_done; /* note: discovery not expired yet */
Youness Alaoui's avatar
Youness Alaoui committed
821
      }
822
823
824
825
    }
  }

  if (not_done == 0) {
826
    nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent);
827
828
829

    discovery_free (agent);

830
    agent_gathering_done (agent);
831

832
833
834
835
836
837
838
    /* note: no pending timers, return FALSE to stop timer */
    return FALSE;
  }

  return TRUE;
}

Youness Alaoui's avatar
Youness Alaoui committed
839
840
841
842
843
static gboolean priv_discovery_tick (gpointer pointer)
{
  NiceAgent *agent = pointer;
  gboolean ret;

844
  g_static_rec_mutex_lock (&agent->mutex);
Youness Alaoui's avatar
Youness Alaoui committed
845
  ret = priv_discovery_tick_unlocked (pointer);
846
  g_static_rec_mutex_unlock (&agent->mutex);
Youness Alaoui's avatar
Youness Alaoui committed
847
848
849
850

  return ret;
}

851
/**
852
853
 * Initiates the candidate discovery process by starting
 * the necessary timers.
854
855
856
857
858
859
860
861
862
 *
 * @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) {
    
863
    if (agent->discovery_timer_source == NULL) {
864
      /* step: run first iteration immediately */
Youness Alaoui's avatar
Youness Alaoui committed
865
      gboolean res = priv_discovery_tick_unlocked (agent);
866
      if (res == TRUE) {
867
        agent->discovery_timer_source = agent_timeout_add_with_context (agent, agent->timer_ta, priv_discovery_tick, agent);
868
869
      }
    }
870
871
  }
}