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