1 /*
2 * This file is part of the Nice GLib ICE library.
3 *
4 * (C) 2008-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2007-2009 Nokia Corporation. All rights reserved.
7 * Contact: Kai Vehmanen
8 *
9 * The contents of this file are subject to the Mozilla Public License Version
10 * 1.1 (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
13 *
14 * Software distributed under the License is distributed on an "AS IS" basis,
15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16 * for the specific language governing rights and limitations under the
17 * License.
18 *
19 * The Original Code is the Nice GLib ICE library.
20 *
21 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22 * Corporation. All Rights Reserved.
23 *
24 * Contributors:
25 * Youness Alaoui, Collabora Ltd.
26 * Kai Vehmanen, Nokia
27 *
28 * Alternatively, the contents of this file may be used under the terms of the
29 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30 * case the provisions of LGPL are applicable instead of those above. If you
31 * wish to allow use of your version of this file only under the terms of the
32 * LGPL and not to allow others to use your version of this file under the
33 * MPL, indicate your decision by deleting the provisions above and replace
34 * them with the notice and other provisions required by the LGPL. If you do
35 * not delete the provisions above, a recipient may use your version of this
36 * file under either the MPL or the LGPL.
37 */
38
39 /*
40 * @file discovery.c
41 * @brief ICE candidate discovery functions
42 */
43
44 #ifdef HAVE_CONFIG_H
45 # include <config.h>
46 #endif
47
48 #include <glib.h>
49
50 #include <stdlib.h>
51 #include <string.h>
52 #include <errno.h>
53
54 #include "debug.h"
55
56 #include "agent.h"
57 #include "agent-priv.h"
58 #include "component.h"
59 #include "discovery.h"
60 #include "stun/usages/bind.h"
61 #include "stun/usages/turn.h"
62 #include "socket.h"
63
64 /*
65 * Frees the CandidateDiscovery structure pointed to
66 * by 'user data'. Compatible with g_slist_free_full().
67 */
discovery_free_item(CandidateDiscovery * cand)68 static void discovery_free_item (CandidateDiscovery *cand)
69 {
70 if (cand->turn)
71 turn_server_unref (cand->turn);
72
73 g_slice_free (CandidateDiscovery, cand);
74 }
75
76 /*
77 * Frees all discovery related resources for the agent.
78 */
discovery_free(NiceAgent * agent)79 void discovery_free (NiceAgent *agent)
80 {
81 g_slist_free_full (agent->discovery_list,
82 (GDestroyNotify) discovery_free_item);
83 agent->discovery_list = NULL;
84 agent->discovery_unsched_items = 0;
85
86 if (agent->discovery_timer_source != NULL) {
87 g_source_destroy (agent->discovery_timer_source);
88 g_source_unref (agent->discovery_timer_source);
89 agent->discovery_timer_source = NULL;
90 }
91 }
92
93 /*
94 * Prunes the list of discovery processes for items related
95 * to stream 'stream_id'.
96 *
97 * @return TRUE on success, FALSE on a fatal error
98 */
discovery_prune_stream(NiceAgent * agent,guint stream_id)99 void discovery_prune_stream (NiceAgent *agent, guint stream_id)
100 {
101 GSList *i;
102
103 for (i = agent->discovery_list; i ; ) {
104 CandidateDiscovery *cand = i->data;
105 GSList *next = i->next;
106
107 if (cand->stream_id == stream_id) {
108 agent->discovery_list = g_slist_remove (agent->discovery_list, cand);
109 discovery_free_item (cand);
110 }
111 i = next;
112 }
113
114 if (agent->discovery_list == NULL) {
115 /* noone using the timer anymore, clean it up */
116 discovery_free (agent);
117 }
118 }
119
120 /*
121 * Prunes the list of discovery processes for items related
122 * to socket @sock.
123 *
124 * @return TRUE on success, FALSE on a fatal error
125 */
discovery_prune_socket(NiceAgent * agent,NiceSocket * sock)126 void discovery_prune_socket (NiceAgent *agent, NiceSocket *sock)
127 {
128 GSList *i;
129
130 for (i = agent->discovery_list; i ; ) {
131 CandidateDiscovery *discovery = i->data;
132 GSList *next = i->next;
133
134 if (discovery->nicesock == sock) {
135 agent->discovery_list = g_slist_remove (agent->discovery_list, discovery);
136 discovery_free_item (discovery);
137 }
138 i = next;
139 }
140
141 if (agent->discovery_list == NULL) {
142 /* noone using the timer anymore, clean it up */
143 discovery_free (agent);
144 }
145 }
146
147 /*
148 * Frees a CandidateRefresh and calls destroy callback if it has been set.
149 */
refresh_free(NiceAgent * agent,CandidateRefresh * cand)150 void refresh_free (NiceAgent *agent, CandidateRefresh *cand)
151 {
152 nice_debug ("Agent %p : Freeing candidate refresh %p", agent, cand);
153
154 agent->refresh_list = g_slist_remove (agent->refresh_list, cand);
155
156 if (cand->timer_source != NULL) {
157 g_source_destroy (cand->timer_source);
158 g_clear_pointer (&cand->timer_source, g_source_unref);
159 }
160
161 if (cand->tick_source) {
162 g_source_destroy (cand->tick_source);
163 g_clear_pointer (&cand->tick_source, g_source_unref);
164 }
165
166 if (cand->destroy_source) {
167 g_source_destroy (cand->destroy_source);
168 g_source_unref (cand->destroy_source);
169 }
170
171 if (cand->destroy_cb) {
172 cand->destroy_cb (cand->destroy_cb_data);
173 }
174
175 g_slice_free (CandidateRefresh, cand);
176 }
177
on_refresh_remove_timeout(NiceAgent * agent,CandidateRefresh * cand)178 static gboolean on_refresh_remove_timeout (NiceAgent *agent,
179 CandidateRefresh *cand)
180 {
181 switch (stun_timer_refresh (&cand->timer)) {
182 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
183 {
184 StunTransactionId id;
185
186 nice_debug ("Agent %p : TURN deallocate for refresh %p timed out",
187 agent, cand);
188
189 stun_message_id (&cand->stun_message, id);
190 stun_agent_forget_transaction (&cand->stun_agent, id);
191
192 refresh_free (agent, cand);
193 break;
194 }
195 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
196 nice_debug ("Agent %p : Retransmitting TURN deallocate for refresh %p",
197 agent, cand);
198
199 agent_socket_send (cand->nicesock, &cand->server,
200 stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
201
202 G_GNUC_FALLTHROUGH;
203 case STUN_USAGE_TIMER_RETURN_SUCCESS:
204 agent_timeout_add_with_context (agent, &cand->tick_source,
205 "TURN deallocate retransmission", stun_timer_remainder (&cand->timer),
206 (NiceTimeoutLockedCallback) on_refresh_remove_timeout, cand);
207 break;
208 default:
209 break;
210 }
211
212 return G_SOURCE_REMOVE;
213 }
214
215 /*
216 * Closes the port associated with the candidate refresh on the TURN server by
217 * sending a refresh request that has zero lifetime. After a response is
218 * received or the request times out, 'cand' gets freed and 'cb' is called.
219 */
refresh_remove_async(NiceAgent * agent,gpointer pointer)220 static gboolean refresh_remove_async (NiceAgent *agent, gpointer pointer)
221 {
222 uint8_t *username;
223 gsize username_len;
224 uint8_t *password;
225 gsize password_len;
226 size_t buffer_len = 0;
227 CandidateRefresh *cand = (CandidateRefresh *) pointer;
228 NiceCandidateImpl *c = (NiceCandidateImpl *) cand->candidate;
229 StunUsageTurnCompatibility turn_compat = agent_to_turn_compatibility (agent);
230
231 nice_debug ("Agent %p : Sending request to remove TURN allocation "
232 "for refresh %p", agent, cand);
233
234 if (cand->timer_source != NULL) {
235 g_source_destroy (cand->timer_source);
236 g_source_unref (cand->timer_source);
237 cand->timer_source = NULL;
238 }
239
240 g_source_destroy (cand->destroy_source);
241 g_source_unref (cand->destroy_source);
242 cand->destroy_source = NULL;
243
244 username = (uint8_t *)c->turn->username;
245 username_len = (size_t) strlen (c->turn->username);
246 password = (uint8_t *)c->turn->password;
247 password_len = (size_t) strlen (c->turn->password);
248
249 if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
250 turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
251 username = c->turn->decoded_username;
252 password = c->turn->decoded_password;
253 username_len = c->turn->decoded_username_len;
254 password_len = c->turn->decoded_password_len;
255 }
256
257 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
258 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
259 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, 0,
260 username, username_len,
261 password, password_len,
262 agent_to_turn_compatibility (agent));
263
264 if (buffer_len > 0) {
265 agent_socket_send (cand->nicesock, &cand->server, buffer_len,
266 (gchar *)cand->stun_buffer);
267
268 stun_timer_start (&cand->timer, agent->stun_initial_timeout,
269 agent->stun_max_retransmissions);
270
271 agent_timeout_add_with_context (agent, &cand->tick_source,
272 "TURN deallocate retransmission", stun_timer_remainder (&cand->timer),
273 (NiceTimeoutLockedCallback) on_refresh_remove_timeout, cand);
274 }
275 return G_SOURCE_REMOVE;
276 }
277
278 typedef struct {
279 NiceAgent *agent;
280 gpointer user_data;
281 guint items_to_free;
282 NiceTimeoutLockedCallback cb;
283 } RefreshPruneAsyncData;
284
on_refresh_removed(RefreshPruneAsyncData * data)285 static void on_refresh_removed (RefreshPruneAsyncData *data)
286 {
287 if (data->items_to_free == 0 || --(data->items_to_free) == 0) {
288 GSource *timeout_source = NULL;
289 agent_timeout_add_with_context (data->agent, &timeout_source,
290 "Async refresh prune", 0, data->cb, data->user_data);
291
292 g_source_unref (timeout_source);
293 g_free (data);
294 }
295 }
296
refresh_prune_async(NiceAgent * agent,GSList * refreshes,NiceTimeoutLockedCallback function,gpointer user_data)297 static void refresh_prune_async (NiceAgent *agent, GSList *refreshes,
298 NiceTimeoutLockedCallback function, gpointer user_data)
299 {
300 RefreshPruneAsyncData *data = g_new0 (RefreshPruneAsyncData, 1);
301 GSList *it;
302 guint timeout = 0;
303
304 data->agent = agent;
305 data->user_data = user_data;
306 data->cb = function;
307
308 for (it = refreshes; it; it = it->next) {
309 CandidateRefresh *cand = it->data;
310
311 if (cand->disposing)
312 continue;
313
314 timeout += agent->timer_ta;
315 cand->disposing = TRUE;
316 cand->destroy_cb = (GDestroyNotify) on_refresh_removed;
317 cand->destroy_cb_data = data;
318
319 agent_timeout_add_with_context(agent, &cand->destroy_source,
320 "TURN refresh remove async", timeout, refresh_remove_async, cand);
321
322 ++data->items_to_free;
323 }
324
325 if (data->items_to_free == 0) {
326 /* Stream doesn't have any refreshes to remove. Invoke our callback once to
327 * schedule client's callback function. */
328 on_refresh_removed (data);
329 }
330 }
331
refresh_prune_agent_async(NiceAgent * agent,NiceTimeoutLockedCallback function,gpointer user_data)332 void refresh_prune_agent_async (NiceAgent *agent,
333 NiceTimeoutLockedCallback function, gpointer user_data)
334 {
335 refresh_prune_async (agent, agent->refresh_list, function, user_data);
336 }
337
338 /*
339 * Removes the candidate refreshes related to 'stream' and asynchronously
340 * closes the associated port allocations on TURN server. Invokes 'function'
341 * when the process finishes.
342 */
refresh_prune_stream_async(NiceAgent * agent,NiceStream * stream,NiceTimeoutLockedCallback function)343 void refresh_prune_stream_async (NiceAgent *agent, NiceStream *stream,
344 NiceTimeoutLockedCallback function)
345 {
346 GSList *refreshes = NULL;
347 GSList *i;
348
349 for (i = agent->refresh_list; i ; i = i->next) {
350 CandidateRefresh *cand = i->data;
351
352 /* Don't free the candidate refresh to the currently selected local candidate
353 * unless the whole pair is being destroyed.
354 */
355 if (cand->stream_id == stream->id) {
356 refreshes = g_slist_append (refreshes, cand);
357 }
358 }
359
360 refresh_prune_async (agent, refreshes, function, stream);
361 g_slist_free (refreshes);
362 }
363
364 /*
365 * Removes the candidate refreshes related to 'candidate'. The function does not
366 * close any associated port allocations on TURN server. Its purpose is in
367 * situations when an error is detected in socket communication that prevents
368 * sending more requests to the server.
369 */
refresh_prune_candidate(NiceAgent * agent,NiceCandidateImpl * candidate)370 void refresh_prune_candidate (NiceAgent *agent, NiceCandidateImpl *candidate)
371 {
372 GSList *i;
373
374 for (i = agent->refresh_list; i;) {
375 GSList *next = i->next;
376 CandidateRefresh *refresh = i->data;
377
378 if (refresh->candidate == candidate) {
379 refresh_free(agent, refresh);
380 }
381
382 i = next;
383 }
384 }
385
386 /*
387 * Removes the candidate refreshes related to 'candidate' and asynchronously
388 * closes the associated port allocations on TURN server. Invokes 'function'
389 * when the process finishes.
390 */
refresh_prune_candidate_async(NiceAgent * agent,NiceCandidateImpl * candidate,NiceTimeoutLockedCallback function)391 void refresh_prune_candidate_async (NiceAgent *agent,
392 NiceCandidateImpl *candidate, NiceTimeoutLockedCallback function)
393 {
394 GSList *refreshes = NULL;
395 GSList *i;
396
397 for (i = agent->refresh_list; i; i = i->next) {
398 CandidateRefresh *refresh = i->data;
399
400 if (refresh->candidate == candidate) {
401 refreshes = g_slist_append (refreshes, refresh);
402 }
403 }
404
405 refresh_prune_async (agent, refreshes, function, candidate);
406 g_slist_free (refreshes);
407 }
408
409 /*
410 * Adds a new local candidate. Implements the candidate pruning
411 * defined in ICE spec section 4.1.3 "Eliminating Redundant
412 * Candidates" (ID-19).
413 */
priv_add_local_candidate_pruned(NiceAgent * agent,guint stream_id,NiceComponent * component,NiceCandidate * candidate)414 static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *candidate)
415 {
416 GSList *i;
417
418 g_assert (candidate != NULL);
419
420 for (i = component->local_candidates; i ; i = i->next) {
421 NiceCandidate *c = i->data;
422
423 if (nice_address_equal (&c->base_addr, &candidate->base_addr) &&
424 nice_address_equal (&c->addr, &candidate->addr) &&
425 c->transport == candidate->transport) {
426 nice_debug ("Candidate %p (component-id %u) redundant, ignoring.", candidate, component->id);
427 return FALSE;
428 }
429 }
430
431 component->local_candidates = g_slist_append (component->local_candidates,
432 candidate);
433 conn_check_add_for_local_candidate(agent, stream_id, component, candidate);
434
435 return TRUE;
436 }
437
priv_highest_remote_foundation(NiceComponent * component)438 static guint priv_highest_remote_foundation (NiceComponent *component)
439 {
440 GSList *i;
441 guint highest = 1;
442 gchar foundation[NICE_CANDIDATE_MAX_FOUNDATION];
443
444 for (highest = 1;; highest++) {
445 gboolean taken = FALSE;
446
447 g_snprintf (foundation, NICE_CANDIDATE_MAX_FOUNDATION, "remote%u",
448 highest);
449 for (i = component->remote_candidates; i; i = i->next) {
450 NiceCandidate *cand = i->data;
451 if (strncmp (foundation, cand->foundation,
452 NICE_CANDIDATE_MAX_FOUNDATION) == 0) {
453 taken = TRUE;
454 break;
455 }
456 }
457 if (!taken)
458 return highest;
459 }
460
461 g_return_val_if_reached (highest);
462 }
463
464 /* From RFC 5245 section 4.1.3:
465 *
466 * for reflexive and relayed candidates, the STUN or TURN servers
467 * used to obtain them have the same IP address.
468 */
469 static gboolean
priv_compare_turn_servers(TurnServer * turn1,TurnServer * turn2)470 priv_compare_turn_servers (TurnServer *turn1, TurnServer *turn2)
471 {
472 if (turn1 == turn2)
473 return TRUE;
474 if (turn1 == NULL || turn2 == NULL)
475 return FALSE;
476
477 return nice_address_equal_no_port (&turn1->server, &turn2->server);
478 }
479
480 /*
481 * Assings a foundation to the candidate.
482 *
483 * Implements the mechanism described in ICE sect
484 * 4.1.1.3 "Computing Foundations" (ID-19).
485 */
priv_assign_foundation(NiceAgent * agent,NiceCandidate * candidate)486 static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
487 {
488 GSList *i, *j, *k;
489 NiceCandidateImpl *c = (NiceCandidateImpl *) candidate;
490
491 for (i = agent->streams; i; i = i->next) {
492 NiceStream *stream = i->data;
493 for (j = stream->components; j; j = j->next) {
494 NiceComponent *component = j->data;
495 for (k = component->local_candidates; k; k = k->next) {
496 NiceCandidateImpl *n = k->data;
497
498 /* note: candidate must not on the local candidate list */
499 g_assert (c != n);
500
501 if (candidate->type == n->c.type &&
502 candidate->transport == n->c.transport &&
503 nice_address_equal_no_port (&candidate->base_addr, &n->c.base_addr) &&
504 (candidate->type != NICE_CANDIDATE_TYPE_RELAYED ||
505 priv_compare_turn_servers (c->turn, n->turn)) &&
506 !(agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
507 n->c.type == NICE_CANDIDATE_TYPE_RELAYED)) {
508 /* note: currently only one STUN server per stream at a
509 * time is supported, so there is no need to check
510 * for candidates that would otherwise share the
511 * foundation, but have different STUN servers */
512 g_strlcpy (candidate->foundation, n->c.foundation,
513 NICE_CANDIDATE_MAX_FOUNDATION);
514 if (n->c.username) {
515 g_free (candidate->username);
516 candidate->username = g_strdup (n->c.username);
517 }
518 if (n->c.password) {
519 g_free (candidate->password);
520 candidate->password = g_strdup (n->c.password);
521 }
522 return;
523 }
524 }
525 }
526 }
527
528 g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION,
529 "%u", agent->next_candidate_id++);
530 }
531
priv_assign_remote_foundation(NiceAgent * agent,NiceCandidate * candidate)532 static void priv_assign_remote_foundation (NiceAgent *agent, NiceCandidate *candidate)
533 {
534 GSList *i, *j, *k;
535 guint next_remote_id;
536 NiceComponent *component = NULL;
537
538 for (i = agent->streams; i; i = i->next) {
539 NiceStream *stream = i->data;
540 for (j = stream->components; j; j = j->next) {
541 NiceComponent *c = j->data;
542
543 if (c->id == candidate->component_id)
544 component = c;
545
546 for (k = c->remote_candidates; k; k = k->next) {
547 NiceCandidate *n = k->data;
548
549 /* note: candidate must not on the remote candidate list */
550 g_assert (candidate != n);
551
552 if (candidate->type == n->type &&
553 candidate->transport == n->transport &&
554 candidate->stream_id == n->stream_id &&
555 nice_address_equal_no_port (&candidate->addr, &n->addr)) {
556 /* note: No need to check for STUN/TURN servers, as these candidate
557 * will always be peer reflexive, never relayed or serve reflexive.
558 */
559 g_strlcpy (candidate->foundation, n->foundation,
560 NICE_CANDIDATE_MAX_FOUNDATION);
561 if (n->username) {
562 g_free (candidate->username);
563 candidate->username = g_strdup (n->username);
564 }
565 if (n->password) {
566 g_free (candidate->password);
567 candidate->password = g_strdup (n->password);
568 }
569 return;
570 }
571 }
572 }
573 }
574
575 if (component) {
576 next_remote_id = priv_highest_remote_foundation (component);
577 g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION,
578 "remote%u", next_remote_id);
579 }
580 }
581
582
583 static
priv_generate_candidate_credentials(NiceAgent * agent,NiceCandidate * candidate)584 void priv_generate_candidate_credentials (NiceAgent *agent,
585 NiceCandidate *candidate)
586 {
587
588 if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
589 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
590 guchar username[32];
591 guchar password[16];
592
593 g_free (candidate->username);
594 g_free (candidate->password);
595
596 nice_rng_generate_bytes (agent->rng, 32, (gchar *)username);
597 nice_rng_generate_bytes (agent->rng, 16, (gchar *)password);
598
599 candidate->username = g_base64_encode (username, 32);
600 candidate->password = g_base64_encode (password, 16);
601
602 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
603 gchar username[16];
604
605 g_free (candidate->username);
606 g_free (candidate->password);
607 candidate->password = NULL;
608
609 nice_rng_generate_bytes_print (agent->rng, 16, (gchar *)username);
610
611 candidate->username = g_strndup (username, 16);
612 }
613
614
615 }
616
617 static gboolean
priv_local_host_candidate_duplicate_port(NiceAgent * agent,NiceCandidate * candidate,gboolean accept_duplicate)618 priv_local_host_candidate_duplicate_port (NiceAgent *agent,
619 NiceCandidate *candidate, gboolean accept_duplicate)
620 {
621 GSList *i, *j, *k;
622
623 if (candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE)
624 return FALSE;
625
626 for (i = agent->streams; i; i = i->next) {
627 NiceStream *stream = i->data;
628
629 for (j = stream->components; j; j = j->next) {
630 NiceComponent *component = j->data;
631
632 for (k = component->local_candidates; k; k = k->next) {
633 NiceCandidate *c = k->data;
634
635 if (candidate->transport == c->transport &&
636 nice_address_ip_version (&candidate->addr) ==
637 nice_address_ip_version (&c->addr) &&
638 nice_address_get_port (&candidate->addr) ==
639 nice_address_get_port (&c->addr)) {
640
641 if (accept_duplicate && candidate->stream_id == stream->id &&
642 candidate->component_id == component->id) {
643 /* We accept it anyway, but with a warning! */
644 gchar ip[NICE_ADDRESS_STRING_LEN];
645 gchar ip2[NICE_ADDRESS_STRING_LEN];
646
647 nice_address_to_string (&candidate->addr, ip);
648 nice_address_to_string (&c->addr, ip2);
649 nice_debug ("Agent %p: Local host %s candidate %s"
650 " for s%d:%d will use the same port %d as %s .", agent, ip,
651 nice_candidate_transport_to_string (c->transport),
652 stream->id, component->id, nice_address_get_port (&c->addr),
653 ip2);
654 return FALSE;
655 }
656
657 return TRUE;
658 }
659 }
660 }
661 }
662 return FALSE;
663 }
664
665 /*
666 * Creates a local host candidate for 'component_id' of stream
667 * 'stream_id'.
668 *
669 * @return pointer to the created candidate, or NULL on error
670 */
discovery_add_local_host_candidate(NiceAgent * agent,guint stream_id,guint component_id,NiceAddress * address,NiceCandidateTransport transport,gboolean accept_duplicate,NiceCandidateImpl ** outcandidate)671 HostCandidateResult discovery_add_local_host_candidate (
672 NiceAgent *agent,
673 guint stream_id,
674 guint component_id,
675 NiceAddress *address,
676 NiceCandidateTransport transport,
677 gboolean accept_duplicate,
678 NiceCandidateImpl **outcandidate)
679 {
680 NiceCandidate *candidate;
681 NiceCandidateImpl *c;
682 NiceComponent *component;
683 NiceStream *stream;
684 NiceSocket *nicesock = NULL;
685 HostCandidateResult res = HOST_CANDIDATE_FAILED;
686 GError *error = NULL;
687
688 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
689 return res;
690
691 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
692 c = (NiceCandidateImpl *) candidate;
693 candidate->transport = transport;
694 candidate->stream_id = stream_id;
695 candidate->component_id = component_id;
696 candidate->addr = *address;
697 candidate->base_addr = *address;
698 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
699 candidate->priority = nice_candidate_jingle_priority (candidate);
700 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
701 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
702 candidate->priority = nice_candidate_msn_priority (candidate);
703 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
704 candidate->priority = nice_candidate_ms_ice_priority (candidate,
705 agent->reliable, FALSE);
706 } else {
707 candidate->priority = nice_candidate_ice_priority (candidate,
708 agent->reliable, FALSE);
709 }
710
711 priv_generate_candidate_credentials (agent, candidate);
712 priv_assign_foundation (agent, candidate);
713
714 /* note: candidate username and password are left NULL as stream
715 level ufrag/password are used */
716 if (transport == NICE_CANDIDATE_TRANSPORT_UDP) {
717 nicesock = nice_udp_bsd_socket_new (address);
718 } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
719 nicesock = nice_tcp_active_socket_new (agent->main_context, address);
720 } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
721 nicesock = nice_tcp_passive_socket_new (agent->main_context, address, &error);
722 } else {
723 /* TODO: Add TCP-SO */
724 }
725 if (!nicesock) {
726 if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE))
727 res = HOST_CANDIDATE_DUPLICATE_PORT;
728 else
729 res = HOST_CANDIDATE_CANT_CREATE_SOCKET;
730 g_clear_error (&error);
731 goto errors;
732 }
733
734 c->sockptr = nicesock;
735 candidate->addr = nicesock->addr;
736 candidate->base_addr = nicesock->addr;
737
738 if (priv_local_host_candidate_duplicate_port (agent, candidate, accept_duplicate)) {
739 res = HOST_CANDIDATE_DUPLICATE_PORT;
740 goto errors;
741 }
742
743 if (!priv_add_local_candidate_pruned (agent, stream_id, component,
744 candidate)) {
745 res = HOST_CANDIDATE_REDUNDANT;
746 goto errors;
747 }
748
749 _priv_set_socket_tos (agent, nicesock, stream->tos);
750 nice_component_attach_socket (component, nicesock);
751
752 *outcandidate = c;
753
754 return HOST_CANDIDATE_SUCCESS;
755
756 errors:
757 nice_candidate_free (candidate);
758 if (nicesock)
759 nice_socket_free (nicesock);
760 return res;
761 }
762
763 /*
764 * Creates a server reflexive candidate for 'component_id' of stream
765 * 'stream_id'.
766 *
767 * @return pointer to the created candidate, or NULL on error
768 */
769 NiceCandidate*
discovery_add_server_reflexive_candidate(NiceAgent * agent,guint stream_id,guint component_id,NiceAddress * address,NiceCandidateTransport transport,NiceSocket * base_socket,gboolean nat_assisted)770 discovery_add_server_reflexive_candidate (
771 NiceAgent *agent,
772 guint stream_id,
773 guint component_id,
774 NiceAddress *address,
775 NiceCandidateTransport transport,
776 NiceSocket *base_socket,
777 gboolean nat_assisted)
778 {
779 NiceCandidate *candidate;
780 NiceCandidateImpl *c;
781 NiceComponent *component;
782 NiceStream *stream;
783 gboolean result = FALSE;
784
785 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
786 return NULL;
787
788 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
789 c = (NiceCandidateImpl *) candidate;
790 candidate->transport = transport;
791 candidate->stream_id = stream_id;
792 candidate->component_id = component_id;
793 candidate->addr = *address;
794
795 /* step: link to the base candidate+socket */
796 c->sockptr = base_socket;
797 candidate->base_addr = base_socket->addr;
798
799 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
800 candidate->priority = nice_candidate_jingle_priority (candidate);
801 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
802 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
803 candidate->priority = nice_candidate_msn_priority (candidate);
804 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
805 candidate->priority = nice_candidate_ms_ice_priority (candidate,
806 agent->reliable, nat_assisted);
807 } else {
808 candidate->priority = nice_candidate_ice_priority (candidate,
809 agent->reliable, nat_assisted);
810 }
811
812 priv_generate_candidate_credentials (agent, candidate);
813 priv_assign_foundation (agent, candidate);
814
815 result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
816 if (result) {
817 agent_signal_new_candidate (agent, candidate);
818 }
819 else {
820 /* error: duplicate candidate */
821 nice_candidate_free (candidate), candidate = NULL;
822 }
823
824 return candidate;
825 }
826
827 /*
828 * Creates a server reflexive candidate for 'component_id' of stream
829 * 'stream_id' for each TCP_PASSIVE and TCP_ACTIVE candidates for each
830 * base address.
831 *
832 * @return pointer to the created candidate, or NULL on error
833 */
834 void
discovery_discover_tcp_server_reflexive_candidates(NiceAgent * agent,guint stream_id,guint component_id,NiceAddress * address,NiceSocket * base_socket)835 discovery_discover_tcp_server_reflexive_candidates (
836 NiceAgent *agent,
837 guint stream_id,
838 guint component_id,
839 NiceAddress *address,
840 NiceSocket *base_socket)
841 {
842 NiceComponent *component;
843 NiceStream *stream;
844 NiceAddress base_addr = base_socket->addr;
845 GSList *i;
846
847 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
848 return;
849
850 nice_address_set_port (&base_addr, 0);
851 for (i = component->local_candidates; i; i = i ->next) {
852 NiceCandidate *c = i->data;
853 NiceAddress caddr;
854
855 caddr = c->addr;
856 nice_address_set_port (&caddr, 0);
857 if (agent->force_relay == FALSE &&
858 c->transport != NICE_CANDIDATE_TRANSPORT_UDP &&
859 c->type == NICE_CANDIDATE_TYPE_HOST &&
860 nice_address_equal (&base_addr, &caddr)) {
861 nice_address_set_port (address, nice_address_get_port (&c->addr));
862 discovery_add_server_reflexive_candidate (
863 agent,
864 stream_id,
865 component_id,
866 address,
867 c->transport,
868 ((NiceCandidateImpl *) c)->sockptr,
869 FALSE);
870 }
871 }
872 }
873
874 /*
875 * Creates a server reflexive candidate for 'component_id' of stream
876 * 'stream_id'.
877 *
878 * @return pointer to the created candidate, or NULL on error
879 */
880 NiceCandidateImpl *
discovery_add_relay_candidate(NiceAgent * agent,guint stream_id,guint component_id,NiceAddress * address,NiceCandidateTransport transport,NiceSocket * base_socket,TurnServer * turn)881 discovery_add_relay_candidate (
882 NiceAgent *agent,
883 guint stream_id,
884 guint component_id,
885 NiceAddress *address,
886 NiceCandidateTransport transport,
887 NiceSocket *base_socket,
888 TurnServer *turn)
889 {
890 NiceCandidate *candidate;
891 NiceCandidateImpl *c;
892 NiceComponent *component;
893 NiceStream *stream;
894 NiceSocket *relay_socket = NULL;
895
896 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
897 return NULL;
898
899 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
900 c = (NiceCandidateImpl *) candidate;
901 candidate->transport = transport;
902 candidate->stream_id = stream_id;
903 candidate->component_id = component_id;
904 candidate->addr = *address;
905 c->turn = turn_server_ref (turn);
906
907 /* step: link to the base candidate+socket */
908 relay_socket = nice_udp_turn_socket_new (agent->main_context, address,
909 base_socket, &turn->server,
910 turn->username, turn->password,
911 agent_to_turn_socket_compatibility (agent));
912 if (!relay_socket)
913 goto errors;
914
915 c->sockptr = relay_socket;
916 candidate->base_addr = base_socket->addr;
917
918 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
919 candidate->priority = nice_candidate_jingle_priority (candidate);
920 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
921 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
922 candidate->priority = nice_candidate_msn_priority (candidate);
923 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
924 candidate->priority = nice_candidate_ms_ice_priority (candidate,
925 agent->reliable, FALSE);
926 } else {
927 candidate->priority = nice_candidate_ice_priority (candidate,
928 agent->reliable, FALSE);
929 }
930
931 priv_generate_candidate_credentials (agent, candidate);
932
933 /* Google uses the turn username as the candidate username */
934 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
935 g_free (candidate->username);
936 candidate->username = g_strdup (turn->username);
937 }
938
939 priv_assign_foundation (agent, candidate);
940
941 if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate))
942 goto errors;
943
944 nice_component_attach_socket (component, relay_socket);
945 agent_signal_new_candidate (agent, candidate);
946
947 return c;
948
949 errors:
950 nice_candidate_free (candidate);
951 if (relay_socket)
952 nice_socket_free (relay_socket);
953 return NULL;
954 }
955
956 /*
957 * Creates a peer reflexive candidate for 'component_id' of stream
958 * 'stream_id'.
959 *
960 * @return pointer to the created candidate, or NULL on error
961 */
962 NiceCandidate*
discovery_add_peer_reflexive_candidate(NiceAgent * agent,guint stream_id,guint component_id,guint32 priority,NiceAddress * address,NiceSocket * base_socket,NiceCandidate * local,NiceCandidate * remote)963 discovery_add_peer_reflexive_candidate (
964 NiceAgent *agent,
965 guint stream_id,
966 guint component_id,
967 guint32 priority,
968 NiceAddress *address,
969 NiceSocket *base_socket,
970 NiceCandidate *local,
971 NiceCandidate *remote)
972 {
973 NiceCandidate *candidate;
974 NiceCandidateImpl *c;
975 NiceComponent *component;
976 NiceStream *stream;
977 gboolean result;
978
979 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
980 return NULL;
981
982 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
983 c = (NiceCandidateImpl *) candidate;
984 if (local)
985 candidate->transport = local->transport;
986 else if (remote)
987 candidate->transport = conn_check_match_transport (remote->transport);
988 else {
989 if (base_socket->type == NICE_SOCKET_TYPE_UDP_BSD ||
990 base_socket->type == NICE_SOCKET_TYPE_UDP_TURN)
991 candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
992 else
993 candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
994 }
995 candidate->stream_id = stream_id;
996 candidate->component_id = component_id;
997 candidate->addr = *address;
998 c->sockptr = base_socket;
999 candidate->base_addr = base_socket->addr;
1000 /* We don't ensure priority uniqueness in this case, since the
1001 * discovered candidate receives the same priority than its
1002 * parent pair, by design, RFC 5245, sect 7.1.3.2.1.
1003 * Discovering Peer Reflexive Candidates (the priority from the
1004 * STUN Request)
1005 */
1006 candidate->priority = priority;
1007 priv_assign_foundation (agent, candidate);
1008
1009 if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
1010 agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
1011 remote && local) {
1012 guchar *new_username = NULL;
1013 guchar *decoded_local = NULL;
1014 guchar *decoded_remote = NULL;
1015 gsize local_size;
1016 gsize remote_size;
1017 g_free(candidate->username);
1018 g_free(candidate->password);
1019
1020 decoded_local = g_base64_decode (local->username, &local_size);
1021 decoded_remote = g_base64_decode (remote->username, &remote_size);
1022
1023 new_username = g_new0(guchar, local_size + remote_size);
1024 memcpy(new_username, decoded_local, local_size);
1025 memcpy(new_username + local_size, decoded_remote, remote_size);
1026
1027 candidate->username = g_base64_encode (new_username, local_size + remote_size);
1028 g_free(new_username);
1029 g_free(decoded_local);
1030 g_free(decoded_remote);
1031
1032 candidate->password = g_strdup(local->password);
1033 } else if (local) {
1034 g_free(candidate->username);
1035 g_free(candidate->password);
1036
1037 candidate->username = g_strdup(local->username);
1038 candidate->password = g_strdup(local->password);
1039 }
1040
1041 result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
1042 if (result != TRUE) {
1043 /* error: memory allocation, or duplicate candidate */
1044 nice_candidate_free (candidate), candidate = NULL;
1045 }
1046
1047 return candidate;
1048 }
1049
1050
1051 /*
1052 * Adds a new peer reflexive candidate to the list of known
1053 * remote candidates. The candidate is however not paired with
1054 * existing local candidates.
1055 *
1056 * See ICE sect 7.2.1.3 "Learning Peer Reflexive Candidates" (ID-19).
1057 *
1058 * @return pointer to the created candidate, or NULL on error
1059 */
discovery_learn_remote_peer_reflexive_candidate(NiceAgent * agent,NiceStream * stream,NiceComponent * component,guint32 priority,const NiceAddress * remote_address,NiceSocket * nicesock,NiceCandidate * local,NiceCandidate * remote)1060 NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
1061 NiceAgent *agent,
1062 NiceStream *stream,
1063 NiceComponent *component,
1064 guint32 priority,
1065 const NiceAddress *remote_address,
1066 NiceSocket *nicesock,
1067 NiceCandidate *local,
1068 NiceCandidate *remote)
1069 {
1070 NiceCandidate *candidate;
1071 NiceCandidateImpl *c;
1072
1073 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
1074 c = (NiceCandidateImpl *) candidate;
1075
1076 candidate->addr = *remote_address;
1077 candidate->base_addr = *remote_address;
1078 if (remote)
1079 candidate->transport = remote->transport;
1080 else if (local)
1081 candidate->transport = conn_check_match_transport (local->transport);
1082 else {
1083 if (nicesock->type == NICE_SOCKET_TYPE_UDP_BSD ||
1084 nicesock->type == NICE_SOCKET_TYPE_UDP_TURN)
1085 candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
1086 else
1087 candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1088 }
1089 c->sockptr = nicesock;
1090 candidate->stream_id = stream->id;
1091 candidate->component_id = component->id;
1092
1093 /* if the check didn't contain the PRIORITY attribute, then the priority will
1094 * be 0, which is invalid... */
1095 if (priority != 0) {
1096 candidate->priority = priority;
1097 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
1098 candidate->priority = nice_candidate_jingle_priority (candidate);
1099 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
1100 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
1101 candidate->priority = nice_candidate_msn_priority (candidate);
1102 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1103 candidate->priority = nice_candidate_ms_ice_priority (candidate,
1104 agent->reliable, FALSE);
1105 } else {
1106 candidate->priority = nice_candidate_ice_priority (candidate,
1107 agent->reliable, FALSE);
1108 }
1109
1110 priv_assign_remote_foundation (agent, candidate);
1111
1112 if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
1113 agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
1114 remote && local) {
1115 guchar *new_username = NULL;
1116 guchar *decoded_local = NULL;
1117 guchar *decoded_remote = NULL;
1118 gsize local_size;
1119 gsize remote_size;
1120 g_free(candidate->username);
1121 g_free (candidate->password);
1122
1123 decoded_local = g_base64_decode (local->username, &local_size);
1124 decoded_remote = g_base64_decode (remote->username, &remote_size);
1125
1126 new_username = g_new0(guchar, local_size + remote_size);
1127 memcpy(new_username, decoded_remote, remote_size);
1128 memcpy(new_username + remote_size, decoded_local, local_size);
1129
1130 candidate->username = g_base64_encode (new_username, local_size + remote_size);
1131 g_free(new_username);
1132 g_free(decoded_local);
1133 g_free(decoded_remote);
1134
1135 candidate->password = g_strdup(remote->password);
1136 } else if (remote) {
1137 g_free (candidate->username);
1138 g_free (candidate->password);
1139 candidate->username = g_strdup(remote->username);
1140 candidate->password = g_strdup(remote->password);
1141 }
1142
1143 /* note: candidate username and password are left NULL as stream
1144 level ufrag/password are used */
1145
1146 component->remote_candidates = g_slist_append (component->remote_candidates,
1147 candidate);
1148
1149 agent_signal_new_remote_candidate (agent, candidate);
1150
1151 return candidate;
1152 }
1153
1154 /*
1155 * Timer callback that handles scheduling new candidate discovery
1156 * processes (paced by the Ta timer), and handles running of the
1157 * existing discovery processes.
1158 *
1159 * This function is designed for the g_timeout_add() interface.
1160 *
1161 * @return will return FALSE when no more pending timers.
1162 */
priv_discovery_tick_unlocked(NiceAgent * agent)1163 static gboolean priv_discovery_tick_unlocked (NiceAgent *agent)
1164 {
1165 CandidateDiscovery *cand;
1166 GSList *i;
1167 int not_done = 0; /* note: track whether to continue timer */
1168 int need_pacing = 0;
1169 size_t buffer_len = 0;
1170
1171 {
1172 static int tick_counter = 0;
1173 if (tick_counter++ % 50 == 0)
1174 nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
1175 }
1176
1177 for (i = agent->discovery_list; i ; i = i->next) {
1178 cand = i->data;
1179
1180 if (cand->pending != TRUE) {
1181 cand->pending = TRUE;
1182
1183 if (agent->discovery_unsched_items)
1184 --agent->discovery_unsched_items;
1185
1186 if (nice_debug_is_enabled ()) {
1187 gchar tmpbuf[INET6_ADDRSTRLEN];
1188 nice_address_to_string (&cand->server, tmpbuf);
1189 nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.",
1190 agent, cand->type, tmpbuf);
1191 }
1192 if (nice_address_is_valid (&cand->server) &&
1193 (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
1194 cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
1195 NiceComponent *component;
1196
1197 if (agent_find_component (agent, cand->stream_id,
1198 cand->component_id, NULL, &component) &&
1199 (component->state == NICE_COMPONENT_STATE_DISCONNECTED ||
1200 component->state == NICE_COMPONENT_STATE_FAILED))
1201 agent_signal_component_state_change (agent,
1202 cand->stream_id,
1203 cand->component_id,
1204 NICE_COMPONENT_STATE_GATHERING);
1205
1206 if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
1207 buffer_len = stun_usage_bind_create (&cand->stun_agent,
1208 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer));
1209 } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) {
1210 uint8_t *username = (uint8_t *)cand->turn->username;
1211 gsize username_len = strlen (cand->turn->username);
1212 uint8_t *password = (uint8_t *)cand->turn->password;
1213 gsize password_len = strlen (cand->turn->password);
1214 StunUsageTurnCompatibility turn_compat =
1215 agent_to_turn_compatibility (agent);
1216
1217 if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
1218 turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
1219 username = cand->turn->decoded_username;
1220 password = cand->turn->decoded_password;
1221 username_len = cand->turn->decoded_username_len;
1222 password_len = cand->turn->decoded_password_len;
1223 }
1224
1225 buffer_len = stun_usage_turn_create (&cand->stun_agent,
1226 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
1227 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
1228 STUN_USAGE_TURN_REQUEST_PORT_NORMAL,
1229 -1, -1,
1230 username, username_len,
1231 password, password_len,
1232 turn_compat);
1233 }
1234
1235 if (buffer_len > 0 &&
1236 agent_socket_send (cand->nicesock, &cand->server, buffer_len,
1237 (gchar *)cand->stun_buffer) >= 0) {
1238 /* case: success, start waiting for the result */
1239 if (nice_socket_is_reliable (cand->nicesock)) {
1240 stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout);
1241 } else {
1242 stun_timer_start (&cand->timer,
1243 agent->stun_initial_timeout,
1244 agent->stun_max_retransmissions);
1245 }
1246
1247 cand->next_tick = g_get_monotonic_time ();
1248 ++need_pacing;
1249 } else {
1250 /* case: error in starting discovery, start the next discovery */
1251 nice_debug ("Agent %p : Error starting discovery, skipping the item.",
1252 agent);
1253 cand->done = TRUE;
1254 cand->stun_message.buffer = NULL;
1255 cand->stun_message.buffer_len = 0;
1256 continue;
1257 }
1258 }
1259 else
1260 /* allocate relayed candidates */
1261 g_assert_not_reached ();
1262
1263 ++not_done; /* note: new discovery scheduled */
1264 }
1265
1266 if (need_pacing)
1267 break;
1268
1269 if (cand->done != TRUE) {
1270 gint64 now = g_get_monotonic_time ();
1271
1272 if (cand->stun_message.buffer == NULL) {
1273 nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent);
1274 cand->done = TRUE;
1275 }
1276 else if (now >= cand->next_tick) {
1277 switch (stun_timer_refresh (&cand->timer)) {
1278 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
1279 {
1280 /* Time out */
1281 /* case: error, abort processing */
1282 StunTransactionId id;
1283
1284 stun_message_id (&cand->stun_message, id);
1285 stun_agent_forget_transaction (&cand->stun_agent, id);
1286
1287 cand->done = TRUE;
1288 cand->stun_message.buffer = NULL;
1289 cand->stun_message.buffer_len = 0;
1290 nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent);
1291 break;
1292 }
1293 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
1294 {
1295 /* case: not ready complete, so schedule next timeout */
1296 unsigned int timeout = stun_timer_remainder (&cand->timer);
1297
1298 stun_debug ("STUN transaction retransmitted (timeout %dms).",
1299 timeout);
1300
1301 /* retransmit */
1302 agent_socket_send (cand->nicesock, &cand->server,
1303 stun_message_length (&cand->stun_message),
1304 (gchar *)cand->stun_buffer);
1305
1306 /* note: convert from milli to microseconds for g_time_val_add() */
1307 cand->next_tick = now + (timeout * 1000);
1308
1309 ++not_done; /* note: retry later */
1310 ++need_pacing;
1311 break;
1312 }
1313 case STUN_USAGE_TIMER_RETURN_SUCCESS:
1314 {
1315 unsigned int timeout = stun_timer_remainder (&cand->timer);
1316
1317 cand->next_tick = now + (timeout * 1000);
1318
1319 ++not_done; /* note: retry later */
1320 break;
1321 }
1322 default:
1323 /* Nothing to do. */
1324 break;
1325 }
1326
1327 } else {
1328 ++not_done; /* note: discovery not expired yet */
1329 }
1330 }
1331
1332 if (need_pacing)
1333 break;
1334 }
1335
1336 if (not_done == 0) {
1337 nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent);
1338
1339 discovery_free (agent);
1340
1341 agent_gathering_done (agent);
1342
1343 /* note: no pending timers, return FALSE to stop timer */
1344 return FALSE;
1345 }
1346
1347 return TRUE;
1348 }
1349
priv_discovery_tick_agent_locked(NiceAgent * agent,gpointer pointer)1350 static gboolean priv_discovery_tick_agent_locked (NiceAgent *agent,
1351 gpointer pointer)
1352 {
1353 gboolean ret;
1354
1355 ret = priv_discovery_tick_unlocked (agent);
1356 if (ret == FALSE) {
1357 if (agent->discovery_timer_source != NULL) {
1358 g_source_destroy (agent->discovery_timer_source);
1359 g_source_unref (agent->discovery_timer_source);
1360 agent->discovery_timer_source = NULL;
1361 }
1362 }
1363
1364 return ret;
1365 }
1366
1367 /*
1368 * Initiates the candidate discovery process by starting
1369 * the necessary timers.
1370 *
1371 * @pre agent->discovery_list != NULL // unsched discovery items available
1372 */
discovery_schedule(NiceAgent * agent)1373 void discovery_schedule (NiceAgent *agent)
1374 {
1375 g_assert (agent->discovery_list != NULL);
1376
1377 if (agent->discovery_unsched_items > 0) {
1378
1379 if (agent->discovery_timer_source == NULL) {
1380 /* step: run first iteration immediately */
1381 gboolean res = priv_discovery_tick_unlocked (agent);
1382 if (res == TRUE) {
1383 agent_timeout_add_with_context (agent, &agent->discovery_timer_source,
1384 "Candidate discovery tick", agent->timer_ta,
1385 priv_discovery_tick_agent_locked, NULL);
1386 }
1387 }
1388 }
1389 }
1390