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 "socket.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
    udp_socket = nice_udp_bsd_socket_new (address);
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
    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
434
435
436
437
438
439
440
441
442
443
444
445
446
447

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

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
  if (candidate) {
    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 */
448
449
450
451
452
      relay_socket = nice_udp_turn_socket_new (address,
          base_socket, &component->turn_server,
          component->turn_username, component->turn_password,
          priv_agent_to_udp_turn_compatibility (agent), &agent->mutex);
      if (relay_socket) {
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
        priv_assign_foundation (agent, candidate);

        result = priv_add_local_candidate_pruned (component, candidate);
        if (result) {
          agent_signal_new_candidate (agent, candidate);
469
470
        } else {
          /* error: memory allocation, or duplicate candidate */
471
          errors = TRUE;
472
473
474
        }
      } else {
        /* error: socket factory make */
475
        errors = TRUE;
476
477
478
      }
    } else {
      /* error: udp socket memory allocation */
479
      errors = TRUE;
480
    }
481
482
483
484
485
486
487
  }

  /* clean up after errors */
  if (errors) {
    if (candidate)
      nice_candidate_free (candidate), candidate = NULL;
    if (relay_socket)
488
      nice_socket_free (relay_socket);
489
490
491
492
  }
  return candidate;
}

493
494
495
496
497
498
499
500
501
502
503
504
/**
 * 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,
505
  NiceSocket *base_socket,
506
507
  NiceCandidate *local,
  NiceCandidate *remote)
508
509
510
511
512
513
514
515
516
517
518
519
520
{
  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;
521
522
523
524
525
526
    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
527
        (NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 0, component_id);
528
    }
529
530
531
532
    candidate->stream_id = stream_id;
    candidate->component_id = component_id;
    candidate->addr = *address;
    candidate->base_addr = base_socket->addr;
533

534

535
    priv_assign_foundation (agent, candidate);
536

537
    if (agent->compatibility == NICE_COMPATIBILITY_MSN &&
538
	remote && local) {
539
540
541
542
543
544
545
      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);
546

547
548
      decoded_local = g_base64_decode (local->username, &local_size);
      decoded_remote = g_base64_decode (remote->username, &remote_size);
549
550
551
552

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

554
555
556
557
      candidate->username = g_base64_encode (new_username, local_size + remote_size);
      g_free(new_username);
      g_free(decoded_local);
      g_free(decoded_remote);
558

559
      candidate->password = g_strdup(local->password);
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
590
591
592
593
594
595
    /* 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.
 *
596
 * See ICE sect 7.2.1.3 "Learning Peer Reflexive Candidates" (ID-19).
597
598
599
600
601
602
603
604
605
 *
 * @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,
606
  NiceSocket *udp_socket,
607
608
  NiceCandidate *local,
  NiceCandidate *remote)
609
610
611
{
  NiceCandidate *candidate;

612
613
614
  /* XXX: for use compiler */
  (void)udp_socket;

615
616
617
618
619
620
  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
  if (candidate) {
    GSList *modified_list;

    guint next_remote_id = priv_highest_remote_foundation (component);

621
    candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
622
623
624
625
626
    candidate->addr = *remote_address;
    candidate->base_addr = *remote_address;
    candidate->priority = priority;;
    candidate->stream_id = stream->id;
    candidate->component_id = component->id;
627

628
    g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", next_remote_id);
629
630
631

    if (agent->compatibility == NICE_COMPATIBILITY_MSN &&
	remote && local) {
632
633
634
      guchar *new_username = NULL;
      guchar *decoded_local = NULL;
      guchar *decoded_remote = NULL;
635
636
637
      gsize local_size;
      gsize remote_size;
      g_free(candidate->username);
638
      g_free (candidate->password);
639

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

643
      new_username = g_new0(guchar, local_size + remote_size);
644
645
      memcpy(new_username, decoded_remote, remote_size);
      memcpy(new_username + remote_size, decoded_local, local_size);
646

647
648
649
650
      candidate->username = g_base64_encode (new_username, local_size + remote_size);
      g_free(new_username);
      g_free(decoded_local);
      g_free(decoded_remote);
651
652

      candidate->password = g_strdup(remote->password);
653
    }
654

655
656
657
    candidate->sockptr = NULL; /* not stored for remote candidates */
    /* note: candidate username and password are left NULL as stream 
             level ufrag/password are used */
658

659
660
661
662
663
    modified_list = g_slist_append (component->remote_candidates,
				    candidate);
    if (modified_list) {
      component->remote_candidates = modified_list;
      agent_signal_new_remote_candidate (agent, candidate);
664
    }
665
    else { /* error: memory alloc / list */
666
      nice_candidate_free (candidate), candidate = NULL;
667
    }
668
669
670
  }

  return candidate;
671
}
672
673
674
675
676
677
678
679
680
681

/** 
 * 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
682
static gboolean priv_discovery_tick_unlocked (gpointer pointer)
683
684
685
686
687
{
  CandidateDiscovery *cand;
  NiceAgent *agent = pointer;
  GSList *i;
  int not_done = 0; /* note: track whether to continue timer */
688
  size_t buffer_len = 0;
689

690
#ifndef NDEBUG
691
692
  {
    static int tick_counter = 0;
693
    if (tick_counter++ % 50 == 0)
694
      nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
695
696
697
698
699
700
701
702
703
704
705
  }
#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;
706
707
708
709

      {
        gchar tmpbuf[INET6_ADDRSTRLEN];
        nice_address_to_string (&cand->server, tmpbuf);
710
        nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s and socket %d.\n", agent,
711
712
713
            cand->type, tmpbuf, cand->socket);
      }
      if (nice_address_is_valid (&cand->server) &&
714
715
          (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
              cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
716

717
	agent_signal_component_state_change (agent,
718
719
720
					     cand->stream->id,
					     cand->component->id,
					     NICE_COMPONENT_STATE_GATHERING);
721

722
723
724
725
        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) {
726
727
728
729
730
731
732
733
734
735
          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);
          }

736
          buffer_len = stun_usage_turn_create (&cand->turn_agent,
737
              &cand->stun_message,  cand->stun_buffer, sizeof(cand->stun_buffer),
738
              cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
739
740
              STUN_USAGE_TURN_REQUEST_PORT_NORMAL,
              0, 0,
741
742
              username, username_len,
              password, password_len,
743
              priv_agent_to_turn_compatibility (agent));
744
745

          if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
746
747
            cand->msn_turn_username = username;
            cand->msn_turn_password = password;
748
          }
749
        }
750
751
752
753
754

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

          /* send the conncheck */
755
          nice_socket_send (cand->nicesock, &cand->server,
756
757
              buffer_len, (gchar *)cand->stun_buffer);

758
759
760
	  /* case: success, start waiting for the result */
	  g_get_current_time (&cand->next_tick);

761
	} else {
762
763
	  /* case: error in starting discovery, start the next discovery */
	  cand->done = TRUE;
764
765
	  cand->stun_message.buffer = NULL;
	  cand->stun_message.buffer_len = 0;
766
	  continue;
767
768
	}
      }
769
      else
770
771
	/* allocate relayed candidates */
	g_assert_not_reached ();
772

773
774
      ++not_done; /* note: new discovery scheduled */
    }
Youness Alaoui's avatar
Youness Alaoui committed
775

776
777
778
779
780
    if (cand->done != TRUE) {
      GTimeVal now;

      g_get_current_time (&now);

781
      if (cand->stun_message.buffer == NULL) {
782
	nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent);
783
784
	cand->done = TRUE;
      }
785
      else if (priv_timer_expired (&cand->next_tick, &now)) {
Youness Alaoui's avatar
Youness Alaoui committed
786
787
        int timeout = stun_timer_refresh (&cand->timer);
        switch (timeout) {
788
789
790
791
792
          case -1:
            /* case: error, abort processing */
            cand->done = TRUE;
            cand->stun_message.buffer = NULL;
            cand->stun_message.buffer_len = 0;
793
            nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent);
794
795
796
797
798
799
800
801
802
803
            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 */
804
              nice_socket_send (cand->nicesock, &cand->server,
805
806
807
808
809
810
811
                  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
812
813
814
815
816
817
818
819
              ++not_done; /* note: retry later */
              break;
            }
          default:
            {
              cand->next_tick = now;
              g_time_val_add (&cand->next_tick, timeout * 1000);

820
821
822
              ++not_done; /* note: retry later */
              break;
            }
823
	}
824

Youness Alaoui's avatar
Youness Alaoui committed
825
      } else {
826
	++not_done; /* note: discovery not expired yet */
Youness Alaoui's avatar
Youness Alaoui committed
827
      }
828
829
830
831
    }
  }

  if (not_done == 0) {
832
    nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent);
833
834
835

    discovery_free (agent);

836
    agent_gathering_done (agent);
837

838
839
840
841
842
843
844
    /* note: no pending timers, return FALSE to stop timer */
    return FALSE;
  }

  return TRUE;
}

Youness Alaoui's avatar
Youness Alaoui committed
845
846
847
848
849
static gboolean priv_discovery_tick (gpointer pointer)
{
  NiceAgent *agent = pointer;
  gboolean ret;

850
  g_static_rec_mutex_lock (&agent->mutex);
Youness Alaoui's avatar
Youness Alaoui committed
851
  ret = priv_discovery_tick_unlocked (pointer);
852
  g_static_rec_mutex_unlock (&agent->mutex);
Youness Alaoui's avatar
Youness Alaoui committed
853
854
855
856

  return ret;
}

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