1 /*
2 * This file is part of the Nice GLib ICE library.
3 *
4 * (C) 2006-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2006-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 * Kai Vehmanen, Nokia
26 * Youness Alaoui, Collabora Ltd.
27 * Dafydd Harries, Collabora Ltd.
28 *
29 * Alternatively, the contents of this file may be used under the terms of the
30 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
31 * case the provisions of LGPL are applicable instead of those above. If you
32 * wish to allow use of your version of this file only under the terms of the
33 * LGPL and not to allow others to use your version of this file under the
34 * MPL, indicate your decision by deleting the provisions above and replace
35 * them with the notice and other provisions required by the LGPL. If you do
36 * not delete the provisions above, a recipient may use your version of this
37 * file under either the MPL or the LGPL.
38 */
39
40 /*
41 * @file conncheck.c
42 * @brief ICE connectivity checks
43 */
44
45 #ifdef HAVE_CONFIG_H
46 # include <config.h>
47 #endif
48
49 #include <errno.h>
50 #include <string.h>
51
52 #include <glib.h>
53
54 #include "debug.h"
55
56 #include "agent.h"
57 #include "agent-priv.h"
58 #include "conncheck.h"
59 #include "discovery.h"
60 #include "stun/stun5389.h"
61 #include "stun/usages/ice.h"
62 #include "stun/usages/bind.h"
63 #include "stun/usages/turn.h"
64
65 static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream);
66 static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, NiceComponent *component);
67 static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand);
68 static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand);
69 static size_t priv_create_username (NiceAgent *agent, NiceStream *stream,
70 guint component_id, NiceCandidate *remote, NiceCandidate *local,
71 uint8_t *dest, guint dest_len, gboolean inbound);
72 static size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
73 NiceCandidate *remote, uint8_t **password);
74 static void candidate_check_pair_fail (NiceStream *stream,
75 NiceAgent *agent, CandidateCheckPair *p);
76 static void candidate_check_pair_free (NiceAgent *agent,
77 CandidateCheckPair *pair);
78 static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
79 NiceAgent *agent, guint stream_id, NiceComponent *component,
80 NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state);
81 static gboolean priv_conn_keepalive_tick_agent_locked (NiceAgent *agent,
82 gpointer pointer);
83
priv_timer_remainder(gint64 timer,gint64 now)84 static gint64 priv_timer_remainder (gint64 timer, gint64 now)
85 {
86 if (now >= timer)
87 return 0;
88
89 return (timer - now) / 1000;
90 }
91
92 static gchar
priv_state_to_gchar(NiceCheckState state)93 priv_state_to_gchar (NiceCheckState state)
94 {
95 switch (state) {
96 case NICE_CHECK_WAITING:
97 return 'W';
98 case NICE_CHECK_IN_PROGRESS:
99 return 'I';
100 case NICE_CHECK_SUCCEEDED:
101 return 'S';
102 case NICE_CHECK_FAILED:
103 return 'F';
104 case NICE_CHECK_FROZEN:
105 return 'Z';
106 case NICE_CHECK_DISCOVERED:
107 return 'D';
108 default:
109 g_assert_not_reached ();
110 }
111 }
112
113 static const gchar *
priv_state_to_string(NiceCheckState state)114 priv_state_to_string (NiceCheckState state)
115 {
116 switch (state) {
117 case NICE_CHECK_WAITING:
118 return "WAITING";
119 case NICE_CHECK_IN_PROGRESS:
120 return "IN_PROGRESS";
121 case NICE_CHECK_SUCCEEDED:
122 return "SUCCEEDED";
123 case NICE_CHECK_FAILED:
124 return "FAILED";
125 case NICE_CHECK_FROZEN:
126 return "FROZEN";
127 case NICE_CHECK_DISCOVERED:
128 return "DISCOVERED";
129 default:
130 g_assert_not_reached ();
131 }
132 }
133
134 #define SET_PAIR_STATE( a, p, s ) G_STMT_START{\
135 g_assert (p); \
136 p->state = s; \
137 nice_debug ("Agent %p : pair %p state %s (%s)", \
138 a, p, priv_state_to_string (s), G_STRFUNC); \
139 }G_STMT_END
140
141 static const gchar *
priv_ice_return_to_string(StunUsageIceReturn ice_return)142 priv_ice_return_to_string (StunUsageIceReturn ice_return)
143 {
144 switch (ice_return) {
145 case STUN_USAGE_ICE_RETURN_SUCCESS:
146 return "success";
147 case STUN_USAGE_ICE_RETURN_ERROR:
148 return "error";
149 case STUN_USAGE_ICE_RETURN_INVALID:
150 return "invalid";
151 case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT:
152 return "role conflict";
153 case STUN_USAGE_ICE_RETURN_INVALID_REQUEST:
154 return "invalid request";
155 case STUN_USAGE_ICE_RETURN_INVALID_METHOD:
156 return "invalid method";
157 case STUN_USAGE_ICE_RETURN_MEMORY_ERROR:
158 return "memory error";
159 case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS:
160 return "invalid address";
161 case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS:
162 return "no mapped address";
163 default:
164 g_assert_not_reached ();
165 }
166 }
167
168 static const gchar *
priv_socket_type_to_string(NiceSocketType type)169 priv_socket_type_to_string (NiceSocketType type)
170 {
171 switch (type) {
172 case NICE_SOCKET_TYPE_UDP_BSD:
173 return "udp";
174 case NICE_SOCKET_TYPE_TCP_BSD:
175 return "tcp";
176 case NICE_SOCKET_TYPE_PSEUDOSSL:
177 return "ssl";
178 case NICE_SOCKET_TYPE_HTTP:
179 return "http";
180 case NICE_SOCKET_TYPE_SOCKS5:
181 return "socks";
182 case NICE_SOCKET_TYPE_UDP_TURN:
183 return "udp-turn";
184 case NICE_SOCKET_TYPE_UDP_TURN_OVER_TCP:
185 return "tcp-turn";
186 case NICE_SOCKET_TYPE_TCP_ACTIVE:
187 return "tcp-act";
188 case NICE_SOCKET_TYPE_TCP_PASSIVE:
189 return "tcp-pass";
190 case NICE_SOCKET_TYPE_TCP_SO:
191 return "tcp-so";
192 default:
193 g_assert_not_reached ();
194 }
195 }
196
197 /*
198 * Dump the component list of incoming checks
199 */
200 static void
print_component_incoming_checks(NiceAgent * agent,NiceStream * stream,NiceComponent * component)201 print_component_incoming_checks (NiceAgent *agent, NiceStream *stream,
202 NiceComponent *component)
203 {
204 GList *i;
205
206 for (i = component->incoming_checks.head; i; i = i->next) {
207 IncomingCheck *icheck = i->data;
208 gchar tmpbuf1[INET6_ADDRSTRLEN] = {0};
209 gchar tmpbuf2[INET6_ADDRSTRLEN] = {0};
210
211 nice_address_to_string (&icheck->local_socket->addr, tmpbuf1);
212 nice_address_to_string (&icheck->from, tmpbuf2);
213 nice_debug ("Agent %p : *** sc=%d/%d : icheck %p : "
214 "sock %s [%s]:%u > [%s]:%u",
215 agent, stream->id, component->id, icheck,
216 priv_socket_type_to_string (icheck->local_socket->type),
217 tmpbuf1, nice_address_get_port (&icheck->local_socket->addr),
218 tmpbuf2, nice_address_get_port (&icheck->from));
219 }
220 }
221
222 /*
223 * Dump the conncheck lists of the agent
224 */
225 static void
priv_print_conn_check_lists(NiceAgent * agent,const gchar * where,const gchar * detail)226 priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail)
227 {
228 GSList *i, *k, *l;
229 guint j, m;
230 gint64 now;
231
232 if (!nice_debug_is_verbose ())
233 return;
234
235 now = g_get_monotonic_time ();
236
237 #define PRIORITY_LEN 32
238
239 nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)",
240 agent, where, detail ? detail : "");
241 nice_debug ("Agent %p : *** agent nomination mode %s, %s",
242 agent, agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
243 "aggressive" : "regular",
244 agent->controlling_mode ? "controlling" : "controlled");
245 for (i = agent->streams; i ; i = i->next) {
246 NiceStream *stream = i->data;
247 for (j = 1; j <= stream->n_components; j++) {
248 NiceComponent *component;
249 for (k = stream->conncheck_list; k ; k = k->next) {
250 CandidateCheckPair *pair = k->data;
251 if (pair->component_id == j) {
252 gchar local_addr[INET6_ADDRSTRLEN];
253 gchar remote_addr[INET6_ADDRSTRLEN];
254 gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
255
256 nice_address_to_string (&pair->local->addr, local_addr);
257 nice_address_to_string (&pair->remote->addr, remote_addr);
258 nice_candidate_pair_priority_to_string (pair->priority, priority);
259
260 nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
261 "f=%s t=%s:%s sock=%s "
262 "%s:[%s]:%u > %s:[%s]:%u prio=%s/%08x state=%c%s%s%s%s",
263 agent, pair->stream_id, pair->component_id, pair,
264 pair->foundation,
265 nice_candidate_type_to_string (pair->local->type),
266 nice_candidate_type_to_string (pair->remote->type),
267 priv_socket_type_to_string (pair->sockptr->type),
268 nice_candidate_transport_to_string (pair->local->transport),
269 local_addr, nice_address_get_port (&pair->local->addr),
270 nice_candidate_transport_to_string (pair->remote->transport),
271 remote_addr, nice_address_get_port (&pair->remote->addr),
272 priority, pair->stun_priority,
273 priv_state_to_gchar (pair->state),
274 pair->valid ? "V" : "",
275 pair->nominated ? "N" : "",
276 pair->use_candidate_on_next_check ? "C" : "",
277 g_slist_find (agent->triggered_check_queue, pair) ? "T" : "");
278
279 for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) {
280 StunTransaction *stun = l->data;
281 nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
282 "stun#=%d timer=%d/%d %" G_GINT64_FORMAT "/%dms buf=%p %s",
283 agent, pair->stream_id, pair->component_id, pair, m,
284 stun->timer.retransmissions, stun->timer.max_retransmissions,
285 stun->timer.delay - priv_timer_remainder (stun->next_tick, now),
286 stun->timer.delay,
287 stun->message.buffer,
288 (m == 0 && pair->retransmit) ? "(R)" : "");
289 }
290 }
291 }
292 if (agent_find_component (agent, stream->id, j, NULL, &component))
293 print_component_incoming_checks (agent, stream, component);
294 }
295 }
296 }
297
298 /* Add the pair to the triggered checks list, if not already present
299 */
300 static void
priv_add_pair_to_triggered_check_queue(NiceAgent * agent,CandidateCheckPair * pair)301 priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
302 {
303 g_assert (pair);
304
305 if (agent->triggered_check_queue == NULL ||
306 g_slist_find (agent->triggered_check_queue, pair) == NULL)
307 agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair);
308 }
309
310 /* Remove the pair from the triggered checks list
311 */
312 static void
priv_remove_pair_from_triggered_check_queue(NiceAgent * agent,CandidateCheckPair * pair)313 priv_remove_pair_from_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
314 {
315 g_assert (pair);
316 agent->triggered_check_queue = g_slist_remove (agent->triggered_check_queue, pair);
317 }
318
319 /* Get the pair from the triggered checks list
320 */
321 static CandidateCheckPair *
priv_get_pair_from_triggered_check_queue(NiceAgent * agent)322 priv_get_pair_from_triggered_check_queue (NiceAgent *agent)
323 {
324 CandidateCheckPair *pair = NULL;
325
326 if (agent->triggered_check_queue) {
327 pair = (CandidateCheckPair *)agent->triggered_check_queue->data;
328 priv_remove_pair_from_triggered_check_queue (agent, pair);
329 }
330 return pair;
331 }
332
333 /*
334 * Finds the next connectivity check in WAITING state.
335 */
priv_conn_check_find_next_waiting(GSList * conn_check_list)336 static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
337 {
338 GSList *i;
339
340 /* note: list is sorted in priority order to first waiting check has
341 * the highest priority */
342 for (i = conn_check_list; i ; i = i->next) {
343 CandidateCheckPair *p = i->data;
344 if (p->state == NICE_CHECK_WAITING)
345 return p;
346 }
347
348 return NULL;
349 }
350
351 /*
352 * Initiates a new connectivity check for a ICE candidate pair.
353 *
354 * @return TRUE on success, FALSE on error
355 */
356 static gboolean
priv_conn_check_initiate(NiceAgent * agent,CandidateCheckPair * pair)357 priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
358 {
359 SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS);
360 if (conn_check_send (agent, pair)) {
361 NiceStream *stream;
362 NiceComponent *component;
363
364 if (!agent_find_component (agent, pair->stream_id, pair->component_id,
365 &stream, &component)) {
366 nice_debug ("Could not find stream or component in conn_check_initiate");
367 SET_PAIR_STATE (agent, pair, NICE_CHECK_FAILED);
368 return FALSE;
369 }
370 candidate_check_pair_fail (stream, agent, pair);
371 conn_check_update_check_list_state_for_ready (agent, stream, component);
372 return FALSE;
373 }
374 return TRUE;
375 }
376
377 /*
378 * Unfreezes the next connectivity check in the list. Follows the
379 * algorithm defined in sect 6.1.2.6 (Computing Candidate Pair States)
380 * and sect 6.1.4.2 (Performing Connectivity Checks) of the ICE spec
381 * (RFC8445)
382 *
383 * Note that this algorithm is slightly simplified compared to previous
384 * version of the spec (RFC5245), and this new version is now
385 * idempotent.
386 *
387 * @return TRUE on success, and FALSE if no frozen candidates were found.
388 */
389 static gboolean
priv_conn_check_unfreeze_next(NiceAgent * agent)390 priv_conn_check_unfreeze_next (NiceAgent *agent)
391 {
392 GSList *i, *j;
393 GSList *foundation_list = NULL;
394 gboolean result = FALSE;
395
396 /* While a pair in state waiting exists, we do nothing */
397 for (i = agent->streams; i ; i = i->next) {
398 NiceStream *s = i->data;
399 for (j = s->conncheck_list; j ; j = j->next) {
400 CandidateCheckPair *p = j->data;
401
402 if (p->state == NICE_CHECK_WAITING)
403 return TRUE;
404 }
405 }
406
407 /* When there are no more pairs in waiting state, we unfreeze some
408 * pairs, so that we get a single waiting pair per foundation.
409 */
410 for (i = agent->streams; i ; i = i->next) {
411 NiceStream *s = i->data;
412 for (j = s->conncheck_list; j ; j = j->next) {
413 CandidateCheckPair *p = j->data;
414
415 if (g_slist_find_custom (foundation_list, p->foundation,
416 (GCompareFunc)strcmp))
417 continue;
418
419 if (p->state == NICE_CHECK_FROZEN) {
420 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.",
421 agent, p, p->stream_id, p->component_id, p->foundation);
422 SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
423 foundation_list = g_slist_prepend (foundation_list, p->foundation);
424 result = TRUE;
425 }
426 }
427 }
428 g_slist_free (foundation_list);
429
430 /* We dump the conncheck list when something interesting happened, ie
431 * when we unfroze some pairs.
432 */
433 if (result)
434 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
435
436 return result;
437 }
438
439 /*
440 * Unfreezes the related connectivity check in the list after
441 * check 'success_check' has successfully completed.
442 *
443 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
444 *
445 * Note that this algorithm is slightly simplified compared to previous
446 * version of the spec (RFC5245)
447 *
448 * @param agent context
449 * @param pair a pair, whose connectivity check has just succeeded
450 *
451 */
452 void
conn_check_unfreeze_related(NiceAgent * agent,CandidateCheckPair * pair)453 conn_check_unfreeze_related (NiceAgent *agent, CandidateCheckPair *pair)
454 {
455 GSList *i, *j;
456 gboolean result = FALSE;
457
458 g_assert (pair);
459 g_assert (pair->state == NICE_CHECK_SUCCEEDED);
460
461 for (i = agent->streams; i ; i = i->next) {
462 NiceStream *s = i->data;
463 for (j = s->conncheck_list; j ; j = j->next) {
464 CandidateCheckPair *p = j->data;
465
466 /* The states for all other Frozen candidates pairs in all
467 * checklists with the same foundation is set to waiting
468 */
469 if (p->state == NICE_CHECK_FROZEN &&
470 strncmp (p->foundation, pair->foundation,
471 NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
472 nice_debug ("Agent %p : Unfreezing check %p "
473 "(after successful check %p).", agent, p, pair);
474 SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
475 result = TRUE;
476 }
477 }
478 }
479 /* We dump the conncheck list when something interesting happened, ie
480 * when we unfroze some pairs.
481 */
482 if (result)
483 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
484 }
485
486 /*
487 * Unfreezes this connectivity check if its foundation is the same than
488 * the foundation of an already succeeded pair.
489 *
490 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
491 *
492 * @param agent context
493 * @param pair a pair, whose state is frozen
494 *
495 */
496 static void
priv_conn_check_unfreeze_maybe(NiceAgent * agent,CandidateCheckPair * pair)497 priv_conn_check_unfreeze_maybe (NiceAgent *agent, CandidateCheckPair *pair)
498 {
499 GSList *i, *j;
500 gboolean result = FALSE;
501
502 g_assert (pair);
503 g_assert (pair->state == NICE_CHECK_FROZEN);
504
505 for (i = agent->streams; i ; i = i->next) {
506 NiceStream *s = i->data;
507 for (j = s->conncheck_list; j ; j = j->next) {
508 CandidateCheckPair *p = j->data;
509
510 if (p->state == NICE_CHECK_SUCCEEDED &&
511 strncmp (p->foundation, pair->foundation,
512 NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
513 nice_debug ("Agent %p : Unfreezing check %p "
514 "(after successful check %p).", agent, pair, p);
515 SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING);
516 result = TRUE;
517 }
518 }
519 }
520 /* We dump the conncheck list when something interesting happened, ie
521 * when we unfroze some pairs.
522 */
523 if (result)
524 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
525 }
526
527 guint
conn_check_stun_transactions_count(NiceAgent * agent)528 conn_check_stun_transactions_count (NiceAgent *agent)
529 {
530 GSList *i, *j;
531 guint count = 0;
532
533 for (i = agent->streams; i ; i = i->next) {
534 NiceStream *s = i->data;
535 for (j = s->conncheck_list; j ; j = j->next) {
536 CandidateCheckPair *p = j->data;
537
538 if (p->stun_transactions)
539 count += g_slist_length (p->stun_transactions);
540 }
541 }
542 return count;
543 }
544
545 /*
546 * Create a new STUN transaction and add it to the list
547 * of ongoing stun transactions of a pair.
548 *
549 * @pair the pair the new stun transaction should be added to.
550 * @return the created stun transaction.
551 */
552 static StunTransaction *
priv_add_stun_transaction(CandidateCheckPair * pair)553 priv_add_stun_transaction (CandidateCheckPair *pair)
554 {
555 StunTransaction *stun = g_slice_new0 (StunTransaction);
556 pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun);
557 pair->retransmit = TRUE;
558 return stun;
559 }
560
561 /*
562 * Forget a STUN transaction.
563 *
564 * @data the stun transaction to be forgotten.
565 * @user_data the component contained the concerned stun agent.
566 */
567 static void
priv_forget_stun_transaction(gpointer data,gpointer user_data)568 priv_forget_stun_transaction (gpointer data, gpointer user_data)
569 {
570 StunTransaction *stun = data;
571 NiceComponent *component = user_data;
572 StunTransactionId id;
573
574 if (stun->message.buffer != NULL) {
575 stun_message_id (&stun->message, id);
576 stun_agent_forget_transaction (&component->stun_agent, id);
577 }
578 }
579
580 static void
priv_free_stun_transaction(gpointer data)581 priv_free_stun_transaction (gpointer data)
582 {
583 g_slice_free (StunTransaction, data);
584 }
585
586 /*
587 * Remove a STUN transaction from a pair, and forget it
588 * from the related component stun agent.
589 *
590 * @pair the pair the stun transaction should be removed from.
591 * @stun the stun transaction to be removed.
592 * @component the component containing the stun agent used to
593 * forget the stun transaction.
594 */
595 static void
priv_remove_stun_transaction(CandidateCheckPair * pair,StunTransaction * stun,NiceComponent * component)596 priv_remove_stun_transaction (CandidateCheckPair *pair,
597 StunTransaction *stun, NiceComponent *component)
598 {
599 priv_forget_stun_transaction (stun, component);
600 pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun);
601 priv_free_stun_transaction (stun);
602 if (pair->stun_transactions == NULL)
603 pair->retransmit = FALSE;
604 }
605
606 /*
607 * Remove all STUN transactions from a pair, and forget them
608 * from the related component stun agent.
609 *
610 * @pair the pair the stun list should be cleared.
611 * @component the component containing the stun agent used to
612 * forget the stun transactions.
613 */
614 static void
priv_free_all_stun_transactions(CandidateCheckPair * pair,NiceComponent * component)615 priv_free_all_stun_transactions (CandidateCheckPair *pair,
616 NiceComponent *component)
617 {
618 if (component)
619 g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component);
620 g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction);
621 pair->stun_transactions = NULL;
622 pair->retransmit = FALSE;
623 }
624
625 static void
candidate_check_pair_fail(NiceStream * stream,NiceAgent * agent,CandidateCheckPair * p)626 candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p)
627 {
628 NiceComponent *component;
629
630 component = nice_stream_find_component_by_id (stream, p->component_id);
631 SET_PAIR_STATE (agent, p, NICE_CHECK_FAILED);
632 priv_free_all_stun_transactions (p, component);
633 }
634
635 /*
636 * Helper function for connectivity check timer callback that
637 * runs through the stream specific part of the state machine.
638 *
639 * @param agent context pointer
640 * @param stream which stream (of the agent)
641 * @return will return TRUE if a new stun request has been sent
642 */
643 static gboolean
priv_conn_check_tick_stream(NiceAgent * agent,NiceStream * stream)644 priv_conn_check_tick_stream (NiceAgent *agent, NiceStream *stream)
645 {
646 gboolean pair_failed = FALSE;
647 GSList *i, *j;
648 unsigned int timeout;
649 gint64 now;
650
651 now = g_get_monotonic_time ();
652
653 /* step: process ongoing STUN transactions */
654 for (i = stream->conncheck_list; i ; i = i->next) {
655 CandidateCheckPair *p = i->data;
656 gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN];
657 NiceComponent *component;
658 guint index = 0, remaining = 0;
659
660 if (p->stun_transactions == NULL)
661 continue;
662
663 if (!agent_find_component (agent, p->stream_id, p->component_id,
664 NULL, &component))
665 continue;
666
667 j = p->stun_transactions;
668 while (j) {
669 StunTransaction *stun = j->data;
670 GSList *next = j->next;
671
672 if (now < stun->next_tick)
673 remaining++;
674 else
675 switch (stun_timer_refresh (&stun->timer)) {
676 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
677 timer_return_timeout:
678 priv_remove_stun_transaction (p, stun, component);
679 break;
680 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
681 /* case: retransmission stopped, due to the nomination of
682 * a pair with a higher priority than this in-progress pair,
683 * ICE spec, sect 8.1.2 "Updating States", item 2.2
684 */
685 if (!p->retransmit || index > 0)
686 goto timer_return_timeout;
687
688 /* case: not ready, so schedule a new timeout */
689 timeout = stun_timer_remainder (&stun->timer);
690
691 nice_debug ("Agent %p :STUN transaction retransmitted on pair %p "
692 "(timer=%d/%d %d/%dms).",
693 agent, p,
694 stun->timer.retransmissions, stun->timer.max_retransmissions,
695 stun->timer.delay - timeout, stun->timer.delay);
696
697 agent_socket_send (p->sockptr, &p->remote->addr,
698 stun_message_length (&stun->message),
699 (gchar *)stun->buffer);
700
701 /* note: convert from milli to microseconds for g_time_val_add() */
702 stun->next_tick = now + timeout * 1000;
703
704 return TRUE;
705 case STUN_USAGE_TIMER_RETURN_SUCCESS:
706 timeout = stun_timer_remainder (&stun->timer);
707 /* note: convert from milli to microseconds for g_time_val_add() */
708 stun->next_tick = now + timeout * 1000;
709 remaining++;
710 break;
711 default:
712 g_assert_not_reached();
713 break;
714 }
715 j = next;
716 index++;
717 }
718
719 if (remaining == 0) {
720 nice_address_to_string (&p->local->addr, tmpbuf1);
721 nice_address_to_string (&p->remote->addr, tmpbuf2);
722 nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
723 agent, p);
724 nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent,
725 tmpbuf1, nice_address_get_port (&p->local->addr),
726 tmpbuf2, nice_address_get_port (&p->remote->addr));
727 candidate_check_pair_fail (stream, agent, p);
728 pair_failed = TRUE;
729
730 /* perform a check if a transition state from connected to
731 * ready can be performed. This may happen here, when the last
732 * in-progress pair has expired its retransmission count
733 * in priv_conn_check_tick_stream(), which is a condition to
734 * make the transition connected to ready.
735 */
736 conn_check_update_check_list_state_for_ready (agent, stream, component);
737 }
738 }
739
740 if (pair_failed)
741 priv_print_conn_check_lists (agent, G_STRFUNC, ", retransmission failed");
742
743 return FALSE;
744 }
745
746 static gboolean
priv_conn_check_ordinary_check(NiceAgent * agent,NiceStream * stream)747 priv_conn_check_ordinary_check (NiceAgent *agent, NiceStream *stream)
748 {
749 CandidateCheckPair *pair;
750 gboolean stun_sent = FALSE;
751
752 /* step: perform an ordinary check, sec 6.1.4.2 point 3. (Performing
753 * Connectivity Checks) of ICE spec (RFC8445)
754 * note: This code is executed when the triggered checks list is
755 * empty, and when no STUN message has been sent (pacing constraint)
756 */
757 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
758 if (pair == NULL) {
759 /* step: there is no candidate in waiting state, try to unfreeze
760 * some pairs and retry, sect 6.1.4.2 point 2. (Performing Connectivity
761 * Checks) of ICE spec (RFC8445)
762 */
763 priv_conn_check_unfreeze_next (agent);
764 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
765 }
766
767 if (pair) {
768 stun_sent = priv_conn_check_initiate (agent, pair);
769 priv_print_conn_check_lists (agent, G_STRFUNC,
770 ", initiated an ordinary connection check");
771 }
772 return stun_sent;
773 }
774
775 static gboolean
priv_conn_check_triggered_check(NiceAgent * agent,NiceStream * stream)776 priv_conn_check_triggered_check (NiceAgent *agent, NiceStream *stream)
777 {
778 CandidateCheckPair *pair;
779 gboolean stun_sent = FALSE;
780
781 /* step: perform a test from the triggered checks list,
782 * sect 6.1.4.2 point 1. (Performing Connectivity Checks) of ICE
783 * spec (RFC8445)
784 */
785 pair = priv_get_pair_from_triggered_check_queue (agent);
786
787 if (pair) {
788 stun_sent = priv_conn_check_initiate (agent, pair);
789 priv_print_conn_check_lists (agent, G_STRFUNC,
790 ", initiated a connection check from triggered check list");
791 }
792 return stun_sent;
793 }
794
795
796 static gboolean
priv_conn_check_tick_stream_nominate(NiceAgent * agent,NiceStream * stream)797 priv_conn_check_tick_stream_nominate (NiceAgent *agent, NiceStream *stream)
798 {
799 gboolean keep_timer_going = FALSE;
800 /* s_xxx counters are stream-wide */
801 guint s_inprogress = 0;
802 guint s_succeeded = 0;
803 guint s_discovered = 0;
804 guint s_nominated = 0;
805 guint s_waiting_for_nomination = 0;
806 guint s_valid = 0;
807 guint s_frozen = 0;
808 guint s_waiting = 0;
809 CandidateCheckPair *other_stream_pair = NULL;
810 GSList *i, *j;
811
812 /* Search for a nominated pair (or selected to be nominated pair)
813 * from another stream.
814 */
815 for (i = agent->streams; i ; i = i->next) {
816 NiceStream *s = i->data;
817 if (s->id == stream->id)
818 continue;
819 for (j = s->conncheck_list; j ; j = j->next) {
820 CandidateCheckPair *p = j->data;
821 if (p->nominated || (p->use_candidate_on_next_check &&
822 p->state != NICE_CHECK_FAILED)) {
823 other_stream_pair = p;
824 break;
825 }
826 }
827 if (other_stream_pair)
828 break;
829 }
830
831 /* we compute some stream-wide counter values */
832 for (i = stream->conncheck_list; i ; i = i->next) {
833 CandidateCheckPair *p = i->data;
834 if (p->state == NICE_CHECK_FROZEN)
835 s_frozen++;
836 else if (p->state == NICE_CHECK_IN_PROGRESS)
837 s_inprogress++;
838 else if (p->state == NICE_CHECK_WAITING)
839 s_waiting++;
840 else if (p->state == NICE_CHECK_SUCCEEDED)
841 s_succeeded++;
842 else if (p->state == NICE_CHECK_DISCOVERED)
843 s_discovered++;
844 if (p->valid)
845 s_valid++;
846
847 if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
848 && p->nominated)
849 s_nominated++;
850 else if ((p->state == NICE_CHECK_SUCCEEDED ||
851 p->state == NICE_CHECK_DISCOVERED) && !p->nominated)
852 s_waiting_for_nomination++;
853 }
854
855 /* note: keep the timer going as long as there is work to be done */
856 if (s_inprogress)
857 keep_timer_going = TRUE;
858
859 if (s_nominated < stream->n_components &&
860 s_waiting_for_nomination) {
861 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
862 if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR &&
863 agent->controlling_mode) {
864 #define NICE_MIN_NUMBER_OF_VALID_PAIRS 2
865 /* ICE 8.1.1.1 Regular nomination
866 * we choose to nominate the valid pair of a component if
867 * - there is no pair left frozen, waiting or in-progress, or
868 * - if there are at least two valid pairs, or
869 * - if there is at least one valid pair of type HOST-HOST
870 *
871 * This is the "stopping criterion" described in 8.1.1.1, and is
872 * a "local optimization" between accumulating more valid pairs,
873 * and limiting the time spent waiting for in-progress connections
874 * checks until they finally fail.
875 */
876 for (i = stream->components; i; i = i->next) {
877 NiceComponent *component = i->data;
878 CandidateCheckPair *other_component_pair = NULL;
879 CandidateCheckPair *this_component_pair = NULL;
880 NiceCandidate *lcand1 = NULL;
881 NiceCandidate *rcand1 = NULL;
882 NiceCandidate *lcand2, *rcand2;
883 gboolean already_done = FALSE;
884 gboolean found_other_component_pair = FALSE;
885 gboolean found_other_stream_pair = FALSE;
886 gboolean first_nomination = FALSE;
887 gboolean stopping_criterion;
888 /* p_xxx counters are component-wide */
889 guint p_valid = 0;
890 guint p_frozen = 0;
891 guint p_waiting = 0;
892 guint p_inprogress = 0;
893 guint p_host_host_valid = 0;
894
895 /* we compute some component-wide counter values */
896 for (j = stream->conncheck_list; j ; j = j->next) {
897 CandidateCheckPair *p = j->data;
898 if (p->component_id == component->id) {
899 /* verify that the choice of the pair to be nominated
900 * has not already been done
901 */
902 if (p->use_candidate_on_next_check)
903 already_done = TRUE;
904 if (p->state == NICE_CHECK_FROZEN)
905 p_frozen++;
906 else if (p->state == NICE_CHECK_WAITING)
907 p_waiting++;
908 else if (p->state == NICE_CHECK_IN_PROGRESS)
909 p_inprogress++;
910 if (p->valid)
911 p_valid++;
912 if (p->valid &&
913 p->local->type == NICE_CANDIDATE_TYPE_HOST &&
914 p->remote->type == NICE_CANDIDATE_TYPE_HOST)
915 p_host_host_valid++;
916 }
917 }
918
919 if (already_done)
920 continue;
921
922 /* Search for a nominated pair (or selected to be nominated pair)
923 * from another component of this stream.
924 */
925 for (j = stream->conncheck_list; j ; j = j->next) {
926 CandidateCheckPair *p = j->data;
927 if (p->component_id == component->id)
928 continue;
929 if (p->nominated || (p->use_candidate_on_next_check &&
930 p->state != NICE_CHECK_FAILED)) {
931 other_component_pair = p;
932 break;
933 }
934 }
935
936 if (other_stream_pair == NULL && other_component_pair == NULL)
937 first_nomination = TRUE;
938
939 /* We choose a pair to be nominated in the list of valid
940 * pairs.
941 *
942 * this pair will be the one with the highest priority,
943 * when we don't have other nominated pairs in other
944 * components and in other streams
945 *
946 * this pair will be a pair compatible with another nominated
947 * pair from another component if we found one.
948 *
949 * else this pair will be a pair compatible with another
950 * nominated pair from another stream if we found one.
951 *
952 */
953 for (j = stream->conncheck_list; j ; j = j->next) {
954 CandidateCheckPair *p = j->data;
955 /* note: highest priority item selected (list always sorted) */
956 if (p->component_id == component->id &&
957 !p->nominated &&
958 !p->use_candidate_on_next_check &&
959 p->valid) {
960 /* According a ICE spec, sect 8.1.1.1. "Regular
961 * Nomination", we enqueue the check that produced this
962 * valid pair. When this pair has been discovered, we want
963 * to test its parent pair instead.
964 */
965 if (p->succeeded_pair != NULL) {
966 g_assert_cmpint (p->state, ==, NICE_CHECK_DISCOVERED);
967 p = p->succeeded_pair;
968 }
969 g_assert_cmpint (p->state, ==, NICE_CHECK_SUCCEEDED);
970
971 if (this_component_pair == NULL)
972 /* highest priority pair */
973 this_component_pair = p;
974
975 lcand1 = p->local;
976 rcand1 = p->remote;
977
978 if (first_nomination)
979 /* use the highest priority pair */
980 break;
981
982 if (other_component_pair) {
983 lcand2 = other_component_pair->local;
984 rcand2 = other_component_pair->remote;
985 }
986 if (other_component_pair &&
987 lcand1->transport == lcand2->transport &&
988 nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
989 nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
990 /* else continue the research with lower priority
991 * pairs, compatible with a nominated pair of
992 * another component
993 */
994 this_component_pair = p;
995 found_other_component_pair = TRUE;
996 break;
997 }
998
999 if (other_stream_pair) {
1000 lcand2 = other_stream_pair->local;
1001 rcand2 = other_stream_pair->remote;
1002 }
1003 if (other_stream_pair &&
1004 other_component_pair == NULL &&
1005 lcand1->transport == lcand2->transport &&
1006 nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
1007 nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
1008 /* else continue the research with lower priority
1009 * pairs, compatible with a nominated pair of
1010 * another stream
1011 */
1012 this_component_pair = p;
1013 found_other_stream_pair = TRUE;
1014 break;
1015 }
1016 }
1017 }
1018
1019 /* No valid pair for this component */
1020 if (this_component_pair == NULL)
1021 continue;
1022
1023 /* The stopping criterion tries to select a set of pairs of
1024 * the same kind (transport/type) for all components of a
1025 * stream, and for all streams, when possible (see last
1026 * paragraph).
1027 *
1028 * When no stream has nominated a pair yet, we apply the
1029 * following criterion :
1030 * - stop if we have a valid host-host pair
1031 * - or stop if we have at least "some* (2 in the current
1032 * implementation) valid pairs, and select the best one
1033 * - or stop if the conncheck cannot evolve more
1034 *
1035 * Else when the stream has a nominated pair in another
1036 * component we apply this criterion:
1037 * - stop if we have a valid pair of the same kind than this
1038 * other nominated pair.
1039 * - or stop if the conncheck cannot evolve more
1040 *
1041 * Else when another stream has a nominated pair we apply the
1042 * following criterion:
1043 * - stop if we have a valid pair of the same kind than the
1044 * other nominated pair.
1045 * - or stop if the conncheck cannot evolve more
1046 *
1047 * When no further evolution of the conncheck is possible, we
1048 * prefer to select the best valid pair we have, *even* if it
1049 * is not compatible with the transport of another stream of
1050 * component. We think it's still a better choice than marking
1051 * this component 'failed'.
1052 */
1053 stopping_criterion = FALSE;
1054 if (first_nomination && p_host_host_valid > 0) {
1055 stopping_criterion = TRUE;
1056 nice_debug ("Agent %p : stopping criterion: "
1057 "valid host-host pair", agent);
1058 } else if (first_nomination &&
1059 p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS) {
1060 stopping_criterion = TRUE;
1061 nice_debug ("Agent %p : stopping criterion: "
1062 "*some* valid pairs", agent);
1063 } else if (found_other_component_pair) {
1064 stopping_criterion = TRUE;
1065 nice_debug ("Agent %p : stopping criterion: "
1066 "matching pair in another component", agent);
1067 } else if (found_other_stream_pair) {
1068 stopping_criterion = TRUE;
1069 nice_debug ("Agent %p : stopping criterion: "
1070 "matching pair in another stream", agent);
1071 } else if (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0) {
1072 stopping_criterion = TRUE;
1073 nice_debug ("Agent %p : stopping criterion: "
1074 "no more pairs to check", agent);
1075 }
1076
1077 if (!stopping_criterion)
1078 continue;
1079
1080 /* when the stopping criterion is reached, we add the
1081 * selected pair for this component to the triggered checks
1082 * list
1083 */
1084 nice_debug ("Agent %p : restarting check of %s:%s pair %p with "
1085 "USE-CANDIDATE attrib (regular nomination) for "
1086 "stream %d component %d", agent,
1087 nice_candidate_transport_to_string (
1088 this_component_pair->local->transport),
1089 nice_candidate_transport_to_string (
1090 this_component_pair->remote->transport),
1091 this_component_pair, stream->id, component->id);
1092 this_component_pair->use_candidate_on_next_check = TRUE;
1093 priv_add_pair_to_triggered_check_queue (agent, this_component_pair);
1094 keep_timer_going = TRUE;
1095 }
1096 }
1097 } else if (agent->controlling_mode) {
1098 for (i = stream->components; i; i = i->next) {
1099 NiceComponent *component = i->data;
1100
1101 for (j = stream->conncheck_list; j ; j = j->next) {
1102 CandidateCheckPair *p = j->data;
1103 /* note: highest priority item selected (list always sorted) */
1104 if (p->component_id == component->id &&
1105 (p->state == NICE_CHECK_SUCCEEDED ||
1106 p->state == NICE_CHECK_DISCOVERED)) {
1107 nice_debug ("Agent %p : restarting check of pair %p as the "
1108 "nominated pair.", agent, p);
1109 p->nominated = TRUE;
1110 conn_check_update_selected_pair (agent, component, p);
1111 priv_add_pair_to_triggered_check_queue (agent, p);
1112 keep_timer_going = TRUE;
1113 break; /* move to the next component */
1114 }
1115 }
1116 }
1117 }
1118 }
1119 if (stream->tick_counter++ % 50 == 0)
1120 nice_debug ("Agent %p : stream %u: timer tick #%u: %u frozen, "
1121 "%u in-progress, %u waiting, %u succeeded, %u discovered, "
1122 "%u nominated, %u waiting-for-nom, %u valid",
1123 agent, stream->id, stream->tick_counter,
1124 s_frozen, s_inprogress, s_waiting, s_succeeded, s_discovered,
1125 s_nominated, s_waiting_for_nomination, s_valid);
1126
1127 return keep_timer_going;
1128
1129 }
1130
1131 static void
conn_check_stop(NiceAgent * agent)1132 conn_check_stop (NiceAgent *agent)
1133 {
1134 if (agent->conncheck_timer_source == NULL)
1135 return;
1136
1137 g_source_destroy (agent->conncheck_timer_source);
1138 g_source_unref (agent->conncheck_timer_source);
1139 agent->conncheck_timer_source = NULL;
1140 agent->conncheck_ongoing_idle_delay = 0;
1141 }
1142
1143
1144 /*
1145 * Timer callback that handles initiating and managing connectivity
1146 * checks (paced by the Ta timer).
1147 *
1148 * This function is designed for the g_timeout_add() interface.
1149 *
1150 * @return will return FALSE when no more pending timers.
1151 */
priv_conn_check_tick_agent_locked(NiceAgent * agent,gpointer user_data)1152 static gboolean priv_conn_check_tick_agent_locked (NiceAgent *agent,
1153 gpointer user_data)
1154 {
1155 gboolean keep_timer_going = FALSE;
1156 gboolean stun_sent = FALSE;
1157 GSList *i;
1158
1159 /* step: process triggered checks
1160 * these steps are ordered by priority, since a single stun request
1161 * is sent per callback, we process the important steps first.
1162 *
1163 * perform a single stun request per timer callback,
1164 * to respect stun pacing
1165 */
1166 for (i = agent->streams; i && !stun_sent; i = i->next) {
1167 NiceStream *stream = i->data;
1168
1169 stun_sent = priv_conn_check_triggered_check (agent, stream);
1170 }
1171
1172 /* step: process ongoing STUN transactions */
1173 for (i = agent->streams; i && !stun_sent; i = i->next) {
1174 NiceStream *stream = i->data;
1175
1176 stun_sent = priv_conn_check_tick_stream (agent, stream);
1177 }
1178
1179 /* step: process ordinary checks */
1180 for (i = agent->streams; i && !stun_sent; i = i->next) {
1181 NiceStream *stream = i->data;
1182
1183 stun_sent = priv_conn_check_ordinary_check (agent, stream);
1184 }
1185
1186 if (stun_sent)
1187 keep_timer_going = TRUE;
1188
1189 /* step: try to nominate a pair
1190 */
1191 for (i = agent->streams; i; i = i->next) {
1192 NiceStream *stream = i->data;
1193
1194 if (priv_conn_check_tick_stream_nominate (agent, stream))
1195 keep_timer_going = TRUE;
1196 }
1197
1198 /* note: we provide a grace period before declaring a component as
1199 * failed. Components marked connected, and then ready follow another
1200 * code path, and are not concerned by this grace period.
1201 */
1202 if (!keep_timer_going && agent->conncheck_ongoing_idle_delay == 0)
1203 nice_debug ("Agent %p : waiting %d msecs before checking "
1204 "for failed components.", agent, agent->idle_timeout);
1205
1206 if (keep_timer_going)
1207 agent->conncheck_ongoing_idle_delay = 0;
1208 else
1209 agent->conncheck_ongoing_idle_delay += agent->timer_ta;
1210
1211 /* step: stop timer if no work left */
1212 if (!keep_timer_going &&
1213 agent->conncheck_ongoing_idle_delay >= agent->idle_timeout) {
1214 nice_debug ("Agent %p : checking for failed components now.", agent);
1215 for (i = agent->streams; i; i = i->next) {
1216 NiceStream *stream = i->data;
1217 priv_update_check_list_failed_components (agent, stream);
1218 }
1219
1220 nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
1221 priv_print_conn_check_lists (agent, G_STRFUNC,
1222 ", conncheck timer stopped");
1223
1224 /* Stopping the timer so destroy the source.. this will allow
1225 the timer to be reset if we get a set_remote_candidates after this
1226 point */
1227 conn_check_stop (agent);
1228
1229 /* XXX: what to signal, is all processing now really done? */
1230 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
1231 return FALSE;
1232 }
1233
1234 return TRUE;
1235 }
1236
priv_conn_keepalive_retransmissions_tick_agent_locked(NiceAgent * agent,gpointer pointer)1237 static gboolean priv_conn_keepalive_retransmissions_tick_agent_locked (
1238 NiceAgent *agent, gpointer pointer)
1239 {
1240 CandidatePair *pair = (CandidatePair *) pointer;
1241
1242 g_source_destroy (pair->keepalive.tick_source);
1243 g_source_unref (pair->keepalive.tick_source);
1244 pair->keepalive.tick_source = NULL;
1245
1246 switch (stun_timer_refresh (&pair->keepalive.timer)) {
1247 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
1248 {
1249 /* Time out */
1250 StunTransactionId id;
1251 NiceComponent *component;
1252
1253 if (!agent_find_component (agent,
1254 pair->keepalive.stream_id, pair->keepalive.component_id,
1255 NULL, &component)) {
1256 nice_debug ("Could not find stream or component in"
1257 " priv_conn_keepalive_retransmissions_tick");
1258 return FALSE;
1259 }
1260
1261 stun_message_id (&pair->keepalive.stun_message, id);
1262 stun_agent_forget_transaction (&component->stun_agent, id);
1263 pair->keepalive.stun_message.buffer = NULL;
1264
1265 if (agent->media_after_tick) {
1266 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
1267 "but media was received. Suspecting keepalive lost because of "
1268 "network bottleneck", agent);
1269 } else {
1270 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
1271 "peer probably lost connection", agent);
1272 agent_signal_component_state_change (agent,
1273 pair->keepalive.stream_id, pair->keepalive.component_id,
1274 NICE_COMPONENT_STATE_FAILED);
1275 }
1276 break;
1277 }
1278 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
1279 /* Retransmit */
1280 agent_socket_send (pair->local->sockptr, &pair->remote->c.addr,
1281 stun_message_length (&pair->keepalive.stun_message),
1282 (gchar *)pair->keepalive.stun_buffer);
1283
1284 nice_debug ("Agent %p : Retransmitting keepalive conncheck",
1285 agent);
1286
1287 G_GNUC_FALLTHROUGH;
1288 case STUN_USAGE_TIMER_RETURN_SUCCESS:
1289 agent_timeout_add_with_context (agent,
1290 &pair->keepalive.tick_source,
1291 "Pair keepalive", stun_timer_remainder (&pair->keepalive.timer),
1292 priv_conn_keepalive_retransmissions_tick_agent_locked, pair);
1293 break;
1294 default:
1295 g_assert_not_reached();
1296 break;
1297 }
1298
1299 return FALSE;
1300 }
1301
peer_reflexive_candidate_priority(NiceAgent * agent,NiceCandidate * local_candidate)1302 static guint32 peer_reflexive_candidate_priority (NiceAgent *agent,
1303 NiceCandidate *local_candidate)
1304 {
1305 NiceCandidate *candidate_priority =
1306 nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
1307 guint32 priority;
1308
1309 candidate_priority->transport = local_candidate->transport;
1310 candidate_priority->component_id = local_candidate->component_id;
1311 candidate_priority->base_addr = local_candidate->addr;
1312 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
1313 priority = nice_candidate_jingle_priority (candidate_priority);
1314 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
1315 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
1316 priority = nice_candidate_msn_priority (candidate_priority);
1317 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1318 priority = nice_candidate_ms_ice_priority (candidate_priority,
1319 agent->reliable, FALSE);
1320 } else {
1321 priority = nice_candidate_ice_priority (candidate_priority,
1322 agent->reliable, FALSE);
1323 }
1324 nice_candidate_free (candidate_priority);
1325
1326 return priority;
1327 }
1328
1329 /* Returns the priority of a local candidate of type peer-reflexive that
1330 * would be learned as a consequence of a check from this local
1331 * candidate. See RFC 5245, section 7.1.2.1. "PRIORITY and USE-CANDIDATE".
1332 * RFC 5245 is more explanatory than RFC 8445 on this detail.
1333 *
1334 * Apply to local candidates of type host only, because candidates of type
1335 * relay are supposed to have a public IP address, that wont generate
1336 * a peer-reflexive address. Server-reflexive candidates are not
1337 * concerned too, because no STUN request is sent with a local candidate
1338 * of this type.
1339 */
stun_request_priority(NiceAgent * agent,NiceCandidate * local_candidate)1340 static guint32 stun_request_priority (NiceAgent *agent,
1341 NiceCandidate *local_candidate)
1342 {
1343 if (local_candidate->type == NICE_CANDIDATE_TYPE_HOST)
1344 return peer_reflexive_candidate_priority (agent, local_candidate);
1345 else
1346 return local_candidate->priority;
1347 }
1348
ms_ice2_legacy_conncheck_send(StunMessage * msg,NiceSocket * sock,const NiceAddress * remote_addr)1349 static void ms_ice2_legacy_conncheck_send(StunMessage *msg, NiceSocket *sock,
1350 const NiceAddress *remote_addr)
1351 {
1352 uint32_t *fingerprint_attr;
1353 uint32_t fingerprint_orig;
1354 uint16_t fingerprint_len;
1355 size_t buffer_len;
1356
1357 if (msg->agent->ms_ice2_send_legacy_connchecks == FALSE) {
1358 return;
1359 }
1360
1361 fingerprint_attr = (uint32_t *)stun_message_find (msg,
1362 STUN_ATTRIBUTE_FINGERPRINT, &fingerprint_len);
1363
1364 if (fingerprint_attr == NULL) {
1365 nice_debug ("FINGERPRINT not found.");
1366 return;
1367 }
1368
1369 if (fingerprint_len != sizeof (fingerprint_orig)) {
1370 nice_debug ("Unexpected FINGERPRINT length %u.", fingerprint_len);
1371 return;
1372 }
1373
1374 memcpy (&fingerprint_orig, fingerprint_attr, sizeof (fingerprint_orig));
1375
1376 buffer_len = stun_message_length (msg);
1377
1378 *fingerprint_attr = stun_fingerprint (msg->buffer, buffer_len, TRUE);
1379
1380 agent_socket_send (sock, remote_addr, buffer_len, (gchar *)msg->buffer);
1381
1382 memcpy (fingerprint_attr, &fingerprint_orig, sizeof (fingerprint_orig));
1383 }
1384
1385 /*
1386 * Timer callback that handles initiating and managing connectivity
1387 * checks (paced by the Ta timer).
1388 *
1389 * This function is designed for the g_timeout_add() interface.
1390 *
1391 * @return will return FALSE when no more pending timers.
1392 */
priv_conn_keepalive_tick_unlocked(NiceAgent * agent)1393 static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
1394 {
1395 GSList *i, *j, *k;
1396 int errors = 0;
1397 size_t buf_len = 0;
1398 guint64 now;
1399 guint64 min_next_tick;
1400 guint64 next_timer_tick;
1401
1402 now = g_get_monotonic_time ();
1403 min_next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1404
1405 /* case 1: session established and media flowing
1406 * (ref ICE sect 11 "Keepalives" RFC-8445)
1407 * TODO: keepalives should be send only when no packet has been sent
1408 * on that pair in the last Tr seconds, and not unconditionally.
1409 */
1410 for (i = agent->streams; i; i = i->next) {
1411
1412 NiceStream *stream = i->data;
1413 for (j = stream->components; j; j = j->next) {
1414 NiceComponent *component = j->data;
1415 if (component->selected_pair.local != NULL) {
1416 CandidatePair *p = &component->selected_pair;
1417
1418 /* Disable keepalive checks on TCP candidates unless explicitly enabled */
1419 if (p->local->c.transport != NICE_CANDIDATE_TRANSPORT_UDP &&
1420 !agent->keepalive_conncheck)
1421 continue;
1422
1423 if (p->keepalive.next_tick) {
1424 if (p->keepalive.next_tick < min_next_tick)
1425 min_next_tick = p->keepalive.next_tick;
1426 if (now < p->keepalive.next_tick)
1427 continue;
1428 }
1429
1430 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
1431 agent->keepalive_conncheck) {
1432 uint8_t uname[NICE_STREAM_MAX_UNAME];
1433 size_t uname_len =
1434 priv_create_username (agent, agent_find_stream (agent, stream->id),
1435 component->id, (NiceCandidate *) p->remote,
1436 (NiceCandidate *) p->local, uname, sizeof (uname), FALSE);
1437 uint8_t *password = NULL;
1438 size_t password_len = priv_get_password (agent,
1439 agent_find_stream (agent, stream->id),
1440 (NiceCandidate *) p->remote, &password);
1441
1442 if (p->keepalive.stun_message.buffer != NULL) {
1443 nice_debug ("Agent %p: Keepalive for s%u:c%u still"
1444 " retransmitting, not restarting", agent, stream->id,
1445 component->id);
1446 continue;
1447 }
1448
1449 if (nice_debug_is_enabled ()) {
1450 gchar tmpbuf[INET6_ADDRSTRLEN];
1451 nice_address_to_string (&p->remote->c.addr, tmpbuf);
1452 nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
1453 "(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), "
1454 "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%08x.",
1455 agent, tmpbuf, nice_address_get_port (&p->remote->c.addr),
1456 component->id, (int) uname_len, uname, uname_len,
1457 (int) password_len, password, password_len,
1458 p->stun_priority);
1459 }
1460 if (uname_len > 0) {
1461 buf_len = stun_usage_ice_conncheck_create (&component->stun_agent,
1462 &p->keepalive.stun_message, p->keepalive.stun_buffer,
1463 sizeof(p->keepalive.stun_buffer),
1464 uname, uname_len, password, password_len,
1465 agent->controlling_mode, agent->controlling_mode,
1466 p->stun_priority,
1467 agent->tie_breaker,
1468 NULL,
1469 agent_to_ice_compatibility (agent));
1470
1471 nice_debug ("Agent %p: conncheck created %zd - %p",
1472 agent, buf_len, p->keepalive.stun_message.buffer);
1473
1474 if (buf_len > 0) {
1475 stun_timer_start (&p->keepalive.timer,
1476 agent->stun_initial_timeout,
1477 agent->stun_max_retransmissions);
1478
1479 agent->media_after_tick = FALSE;
1480
1481 /* send the conncheck */
1482 agent_socket_send (p->local->sockptr, &p->remote->c.addr,
1483 buf_len, (gchar *)p->keepalive.stun_buffer);
1484
1485 p->keepalive.stream_id = stream->id;
1486 p->keepalive.component_id = component->id;
1487 p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1488
1489 agent_timeout_add_with_context (agent,
1490 &p->keepalive.tick_source, "Pair keepalive",
1491 stun_timer_remainder (&p->keepalive.timer),
1492 priv_conn_keepalive_retransmissions_tick_agent_locked, p);
1493
1494 next_timer_tick = now + agent->timer_ta * 1000;
1495 goto done;
1496 } else {
1497 ++errors;
1498 }
1499 }
1500 } else {
1501 buf_len = stun_usage_bind_keepalive (&component->stun_agent,
1502 &p->keepalive.stun_message, p->keepalive.stun_buffer,
1503 sizeof(p->keepalive.stun_buffer));
1504
1505 if (buf_len > 0) {
1506 agent_socket_send (p->local->sockptr, &p->remote->c.addr, buf_len,
1507 (gchar *)p->keepalive.stun_buffer);
1508
1509 p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1510
1511 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1512 ms_ice2_legacy_conncheck_send (&p->keepalive.stun_message,
1513 p->local->sockptr, &p->remote->c.addr);
1514 }
1515
1516 if (nice_debug_is_enabled ()) {
1517 gchar tmpbuf[INET6_ADDRSTRLEN];
1518 nice_address_to_string (&p->local->c.base_addr, tmpbuf);
1519 nice_debug ("Agent %p : resending STUN to keep the "
1520 "selected base address %s:%u alive in s%d/c%d.", agent,
1521 tmpbuf, nice_address_get_port (&p->local->c.base_addr),
1522 stream->id, component->id);
1523 }
1524
1525 next_timer_tick = now + agent->timer_ta * 1000;
1526 goto done;
1527 } else {
1528 ++errors;
1529 }
1530 }
1531 }
1532 }
1533 }
1534
1535 /* case 2: connectivity establishment ongoing
1536 * (ref ICE sect 5.1.1.4 "Keeping Candidates Alive" RFC-8445)
1537 */
1538 for (i = agent->streams; i; i = i->next) {
1539 NiceStream *stream = i->data;
1540 for (j = stream->components; j; j = j->next) {
1541 NiceComponent *component = j->data;
1542 if (component->state < NICE_COMPONENT_STATE_CONNECTED &&
1543 agent->stun_server_ip) {
1544 NiceAddress stun_server;
1545 if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
1546 StunAgent stun_agent;
1547 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
1548 StunMessage stun_message;
1549 size_t buffer_len = 0;
1550
1551 nice_address_set_port (&stun_server, agent->stun_server_port);
1552
1553 nice_agent_init_stun_agent (agent, &stun_agent);
1554
1555 buffer_len = stun_usage_bind_create (&stun_agent,
1556 &stun_message, stun_buffer, sizeof(stun_buffer));
1557
1558 for (k = component->local_candidates; k; k = k->next) {
1559 NiceCandidateImpl *candidate = (NiceCandidateImpl *) k->data;
1560 if (candidate->c.type == NICE_CANDIDATE_TYPE_HOST &&
1561 candidate->c.transport == NICE_CANDIDATE_TRANSPORT_UDP &&
1562 nice_address_ip_version (&candidate->c.addr) ==
1563 nice_address_ip_version (&stun_server)) {
1564
1565 if (candidate->keepalive_next_tick) {
1566 if (candidate->keepalive_next_tick < min_next_tick)
1567 min_next_tick = candidate->keepalive_next_tick;
1568 if (now < candidate->keepalive_next_tick)
1569 continue;
1570 }
1571
1572 /* send the conncheck */
1573 if (nice_debug_is_enabled ()) {
1574 gchar tmpbuf[INET6_ADDRSTRLEN];
1575 nice_address_to_string (&candidate->c.addr, tmpbuf);
1576 nice_debug ("Agent %p : resending STUN to keep the local "
1577 "candidate %s:%u alive in s%d/c%d.", agent,
1578 tmpbuf, nice_address_get_port (&candidate->c.addr),
1579 stream->id, component->id);
1580 }
1581 agent_socket_send (candidate->sockptr, &stun_server,
1582 buffer_len, (gchar *)stun_buffer);
1583 candidate->keepalive_next_tick = now +
1584 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1585 next_timer_tick = now + agent->timer_ta * 1000;
1586 goto done;
1587 }
1588 }
1589 }
1590 }
1591 }
1592 }
1593
1594 next_timer_tick = min_next_tick;
1595
1596 done:
1597 if (errors) {
1598 nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
1599 return FALSE;
1600 }
1601
1602 if (agent->keepalive_timer_source) {
1603 g_source_destroy (agent->keepalive_timer_source);
1604 g_source_unref (agent->keepalive_timer_source);
1605 agent->keepalive_timer_source = NULL;
1606 }
1607 agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
1608 "Connectivity keepalive timeout", (next_timer_tick - now)/ 1000,
1609 priv_conn_keepalive_tick_agent_locked, NULL);
1610 return TRUE;
1611 }
1612
priv_conn_keepalive_tick_agent_locked(NiceAgent * agent,gpointer pointer)1613 static gboolean priv_conn_keepalive_tick_agent_locked (NiceAgent *agent,
1614 gpointer pointer)
1615 {
1616 gboolean ret;
1617
1618 ret = priv_conn_keepalive_tick_unlocked (agent);
1619 if (ret == FALSE) {
1620 if (agent->keepalive_timer_source) {
1621 g_source_destroy (agent->keepalive_timer_source);
1622 g_source_unref (agent->keepalive_timer_source);
1623 agent->keepalive_timer_source = NULL;
1624 }
1625 }
1626
1627 return ret;
1628 }
1629
1630
priv_turn_allocate_refresh_retransmissions_tick_agent_locked(NiceAgent * agent,gpointer pointer)1631 static gboolean priv_turn_allocate_refresh_retransmissions_tick_agent_locked (
1632 NiceAgent *agent, gpointer pointer)
1633 {
1634 CandidateRefresh *cand = (CandidateRefresh *) pointer;
1635
1636 g_source_destroy (cand->tick_source);
1637 g_source_unref (cand->tick_source);
1638 cand->tick_source = NULL;
1639
1640 switch (stun_timer_refresh (&cand->timer)) {
1641 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
1642 {
1643 /* Time out */
1644 StunTransactionId id;
1645
1646 stun_message_id (&cand->stun_message, id);
1647 stun_agent_forget_transaction (&cand->stun_agent, id);
1648
1649 refresh_free (agent, cand);
1650 break;
1651 }
1652 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
1653 /* Retransmit */
1654 agent_socket_send (cand->nicesock, &cand->server,
1655 stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
1656
1657 G_GNUC_FALLTHROUGH;
1658 case STUN_USAGE_TIMER_RETURN_SUCCESS:
1659 agent_timeout_add_with_context (agent, &cand->tick_source,
1660 "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
1661 priv_turn_allocate_refresh_retransmissions_tick_agent_locked, cand);
1662 break;
1663 default:
1664 /* Nothing to do. */
1665 break;
1666 }
1667
1668 return G_SOURCE_REMOVE;
1669 }
1670
priv_turn_allocate_refresh_tick_unlocked(NiceAgent * agent,CandidateRefresh * cand)1671 static void priv_turn_allocate_refresh_tick_unlocked (NiceAgent *agent,
1672 CandidateRefresh *cand)
1673 {
1674 uint8_t *username;
1675 gsize username_len;
1676 uint8_t *password;
1677 gsize password_len;
1678 size_t buffer_len = 0;
1679 StunUsageTurnCompatibility turn_compat =
1680 agent_to_turn_compatibility (agent);
1681
1682 username = (uint8_t *)cand->candidate->turn->username;
1683 username_len = (size_t) strlen (cand->candidate->turn->username);
1684 password = (uint8_t *)cand->candidate->turn->password;
1685 password_len = (size_t) strlen (cand->candidate->turn->password);
1686
1687 if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
1688 turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
1689 username = cand->candidate->turn->decoded_username;
1690 password = cand->candidate->turn->decoded_password;
1691 username_len = cand->candidate->turn->decoded_username_len;
1692 password_len = cand->candidate->turn->decoded_password_len;
1693 }
1694
1695 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
1696 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
1697 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, -1,
1698 username, username_len,
1699 password, password_len,
1700 turn_compat);
1701
1702 nice_debug ("Agent %p : Sending allocate Refresh %zd", agent,
1703 buffer_len);
1704
1705 if (cand->tick_source != NULL) {
1706 g_source_destroy (cand->tick_source);
1707 g_source_unref (cand->tick_source);
1708 cand->tick_source = NULL;
1709 }
1710
1711 if (buffer_len > 0) {
1712 stun_timer_start (&cand->timer,
1713 agent->stun_initial_timeout,
1714 agent->stun_max_retransmissions);
1715
1716 /* send the refresh */
1717 agent_socket_send (cand->nicesock, &cand->server,
1718 buffer_len, (gchar *)cand->stun_buffer);
1719
1720 agent_timeout_add_with_context (agent, &cand->tick_source,
1721 "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
1722 priv_turn_allocate_refresh_retransmissions_tick_agent_locked, cand);
1723 }
1724
1725 }
1726
1727
1728 /*
1729 * Timer callback that handles refreshing TURN allocations
1730 *
1731 * This function is designed for the g_timeout_add() interface.
1732 *
1733 * @return will return FALSE when no more pending timers.
1734 */
priv_turn_allocate_refresh_tick_agent_locked(NiceAgent * agent,gpointer pointer)1735 static gboolean priv_turn_allocate_refresh_tick_agent_locked (NiceAgent *agent,
1736 gpointer pointer)
1737 {
1738 CandidateRefresh *cand = (CandidateRefresh *) pointer;
1739
1740 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
1741
1742 return G_SOURCE_REMOVE;
1743 }
1744
1745
1746 /*
1747 * Initiates the next pending connectivity check.
1748 */
conn_check_schedule_next(NiceAgent * agent)1749 void conn_check_schedule_next (NiceAgent *agent)
1750 {
1751 if (agent->discovery_unsched_items > 0)
1752 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent);
1753
1754 /* step: schedule timer if not running yet */
1755 if (agent->conncheck_timer_source == NULL) {
1756 agent_timeout_add_with_context (agent, &agent->conncheck_timer_source,
1757 "Connectivity check schedule", agent->timer_ta,
1758 priv_conn_check_tick_agent_locked, NULL);
1759 }
1760
1761 /* step: also start the keepalive timer */
1762 if (agent->keepalive_timer_source == NULL) {
1763 agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
1764 "Connectivity keepalive timeout", agent->timer_ta,
1765 priv_conn_keepalive_tick_agent_locked, NULL);
1766 }
1767 }
1768
1769 /*
1770 * Compares two connectivity check items. Checkpairs are sorted
1771 * in descending priority order, with highest priority item at
1772 * the start of the list.
1773 */
conn_check_compare(const CandidateCheckPair * a,const CandidateCheckPair * b)1774 gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b)
1775 {
1776 if (a->priority > b->priority)
1777 return -1;
1778 else if (a->priority < b->priority)
1779 return 1;
1780 return 0;
1781 }
1782
1783 /* Find a transport compatible with a given socket.
1784 *
1785 * Returns TRUE when a matching transport can be guessed from
1786 * the type of the socket in an unambiguous way.
1787 */
1788 static gboolean
nice_socket_has_compatible_transport(NiceSocket * socket,NiceCandidateTransport * transport)1789 nice_socket_has_compatible_transport (NiceSocket *socket,
1790 NiceCandidateTransport *transport)
1791 {
1792 gboolean found = TRUE;
1793
1794 g_assert (socket);
1795 g_assert (transport);
1796
1797 switch (socket->type) {
1798 case NICE_SOCKET_TYPE_TCP_BSD:
1799 if (nice_tcp_bsd_socket_get_passive_parent (socket))
1800 *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
1801 else
1802 *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1803 break;
1804 case NICE_SOCKET_TYPE_TCP_PASSIVE:
1805 *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
1806 break;
1807 case NICE_SOCKET_TYPE_TCP_ACTIVE:
1808 *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1809 break;
1810 case NICE_SOCKET_TYPE_UDP_BSD:
1811 *transport = NICE_CANDIDATE_TRANSPORT_UDP;
1812 break;
1813 default:
1814 found = FALSE;
1815 }
1816
1817 return found;
1818 }
1819
1820 /* Test if a local socket and a local candidate are compatible. This
1821 * function does supplementary tests when the address and port are not
1822 * sufficient to give a unique candidate. We try to avoid comparing
1823 * directly the sockptr value, when possible, to rely on objective
1824 * properties of the candidate and the socket instead, and we also
1825 * choose to ignore the conncheck list for the same reason.
1826 */
1827 static gboolean
local_candidate_and_socket_compatible(NiceAgent * agent,NiceCandidate * lcand,NiceSocket * socket)1828 local_candidate_and_socket_compatible (NiceAgent *agent,
1829 NiceCandidate *lcand, NiceSocket *socket)
1830 {
1831 gboolean ret = TRUE;
1832 NiceCandidateTransport transport;
1833 NiceCandidateImpl *lc = (NiceCandidateImpl *) lcand;
1834
1835 g_assert (socket);
1836 g_assert (lcand);
1837
1838 if (nice_socket_has_compatible_transport (socket, &transport)) {
1839 ret = (lcand->transport == transport);
1840 /* tcp-active discovered peer-reflexive local candidate, where
1841 * socket is the tcp connect related socket */
1842 if (ret && transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
1843 nice_address_get_port (&lcand->addr) > 0)
1844 ret = (lc->sockptr == socket);
1845 } else if (socket->type == NICE_SOCKET_TYPE_UDP_TURN)
1846 /* Socket of type udp-turn will match a unique local candidate
1847 * by its sockptr value. An an udp-turn socket doesn't carry enough
1848 * information when base socket is udp-turn-over-tcp to disambiguate
1849 * between a tcp-act and a tcp-pass local candidate.
1850 */
1851 ret = (lc->sockptr == socket);
1852
1853 return ret;
1854 }
1855
1856 /* Test if a local socket and a remote candidate are compatible.
1857 * This function is very close to its local candidate counterpart,
1858 * the difference is that we also use information from the local
1859 * candidate we may have identified previously. This is needed
1860 * to disambiguate the transport of the candidate with a socket
1861 * of type udp-turn.
1862 *
1863 */
1864 static gboolean
remote_candidate_and_socket_compatible(NiceAgent * agent,NiceCandidate * lcand,NiceCandidate * rcand,NiceSocket * socket)1865 remote_candidate_and_socket_compatible (NiceAgent *agent,
1866 NiceCandidate *lcand, NiceCandidate *rcand, NiceSocket *socket)
1867 {
1868 gboolean ret = TRUE;
1869 NiceCandidateTransport transport;
1870
1871 g_assert (socket);
1872 g_assert (rcand);
1873
1874 if (nice_socket_has_compatible_transport (socket, &transport))
1875 ret = (conn_check_match_transport (rcand->transport) == transport);
1876
1877 /* This supplementary test with the local candidate is needed with
1878 * socket of type udp-turn, the type doesn't allow to disambiguate
1879 * between a tcp-pass and a tcp-act remote candidate
1880 */
1881 if (lcand && ret)
1882 ret = (conn_check_match_transport (lcand->transport) == rcand->transport);
1883
1884 return ret;
1885 }
1886
1887 void
conn_check_remote_candidates_set(NiceAgent * agent,NiceStream * stream,NiceComponent * component)1888 conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream,
1889 NiceComponent *component)
1890 {
1891 GList *i;
1892 GSList *j;
1893 NiceCandidate *lcand = NULL, *rcand = NULL;
1894
1895 nice_debug ("Agent %p : conn_check_remote_candidates_set %u %u",
1896 agent, stream->id, component->id);
1897
1898 if (stream->remote_ufrag[0] == 0)
1899 return;
1900
1901 if (component->incoming_checks.head)
1902 nice_debug ("Agent %p : credentials have been set, "
1903 "we can process incoming checks", agent);
1904
1905 for (i = component->incoming_checks.head; i;) {
1906 IncomingCheck *icheck = i->data;
1907 GList *i_next = i->next;
1908
1909 nice_debug ("Agent %p : replaying icheck=%p (sock=%p)",
1910 agent, icheck, icheck->local_socket);
1911
1912 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
1913 * be handled separately */
1914 for (j = component->local_candidates; j; j = j->next) {
1915 NiceCandidate *cand = j->data;
1916 NiceAddress *addr;
1917
1918 if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
1919 addr = &cand->addr;
1920 else
1921 addr = &cand->base_addr;
1922
1923 if (nice_address_equal (&icheck->local_socket->addr, addr) &&
1924 local_candidate_and_socket_compatible (agent, cand,
1925 icheck->local_socket)) {
1926 lcand = cand;
1927 break;
1928 }
1929 }
1930
1931 if (lcand == NULL) {
1932 for (j = component->local_candidates; j; j = j->next) {
1933 NiceCandidate *cand = j->data;
1934 NiceAddress *addr = &cand->base_addr;
1935
1936 /* tcp-active (not peer-reflexive discovered) local candidate, where
1937 * socket is the tcp connect related socket */
1938 if (nice_address_equal_no_port (&icheck->local_socket->addr, addr) &&
1939 nice_address_get_port (&cand->addr) == 0 &&
1940 cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
1941 local_candidate_and_socket_compatible (agent, cand,
1942 icheck->local_socket)) {
1943 lcand = cand;
1944 break;
1945 }
1946 }
1947 }
1948
1949 g_assert (lcand != NULL);
1950
1951 for (j = component->remote_candidates; j; j = j->next) {
1952 NiceCandidate *cand = j->data;
1953 if (nice_address_equal (&cand->addr, &icheck->from) &&
1954 remote_candidate_and_socket_compatible (agent, lcand, cand,
1955 icheck->local_socket)) {
1956 rcand = cand;
1957 break;
1958 }
1959 }
1960
1961 if (lcand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
1962 CandidateCheckPair *pair = NULL;
1963
1964 for (j = stream->conncheck_list; j; j = j->next) {
1965 CandidateCheckPair *p = j->data;
1966 if (lcand == p->local && rcand == p->remote) {
1967 pair = p;
1968 break;
1969 }
1970 }
1971 if (pair == NULL)
1972 priv_conn_check_add_for_candidate_pair_matched (agent,
1973 stream->id, component, lcand, rcand, NICE_CHECK_WAITING);
1974 }
1975
1976 priv_schedule_triggered_check (agent, stream, component,
1977 icheck->local_socket, rcand);
1978 if (icheck->use_candidate)
1979 priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
1980
1981 if (icheck->username)
1982 g_free (icheck->username);
1983 g_slice_free (IncomingCheck, icheck);
1984 g_queue_delete_link (&component->incoming_checks, i);
1985 i = i_next;
1986 }
1987 }
1988
1989 /*
1990 * Handle any processing steps for connectivity checks after
1991 * remote credentials have been set. This function handles
1992 * the special case where answerer has sent us connectivity
1993 * checks before the answer (containing credentials information),
1994 * reaches us. The special case is documented in RFC 5245 sect 7.2.
1995 * ).
1996 */
conn_check_remote_credentials_set(NiceAgent * agent,NiceStream * stream)1997 void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream)
1998 {
1999 GSList *j;
2000
2001 for (j = stream->components; j ; j = j->next) {
2002 NiceComponent *component = j->data;
2003
2004 conn_check_remote_candidates_set(agent, stream, component);
2005 }
2006 }
2007
2008 /*
2009 * Enforces the upper limit for connectivity checks by dropping
2010 * lower-priority pairs as described RFC 8445 section 6.1.2.5. See also
2011 * conn_check_add_for_candidate().
2012 * Returns TRUE if the pair in argument is one of the deleted pairs.
2013 */
priv_limit_conn_check_list_size(NiceAgent * agent,NiceStream * stream,CandidateCheckPair * pair)2014 static gboolean priv_limit_conn_check_list_size (NiceAgent *agent,
2015 NiceStream *stream, CandidateCheckPair *pair)
2016 {
2017 guint valid = 0;
2018 guint cancelled = 0;
2019 gboolean deleted = FALSE;
2020 GSList *item = stream->conncheck_list;
2021
2022 while (item) {
2023 CandidateCheckPair *p = item->data;
2024 GSList *next = item->next;
2025
2026 valid++;
2027 /* We remove lower-priority pairs, but only the ones that can be
2028 * safely discarded without breaking an ongoing conncheck process.
2029 * This only includes pairs that are in the frozen state (those
2030 * initially added when remote candidates are received) or in failed
2031 * state. Pairs in any other state play a role in the conncheck, and
2032 * there removal may lead to a failing conncheck that would succeed
2033 * otherwise.
2034 *
2035 * We also remove failed pairs from the list unconditionally.
2036 */
2037 if ((valid > agent->max_conn_checks && p->state == NICE_CHECK_FROZEN) ||
2038 p->state == NICE_CHECK_FAILED) {
2039 if (p == pair)
2040 deleted = TRUE;
2041 nice_debug ("Agent %p : pair %p removed.", agent, p);
2042 candidate_check_pair_free (agent, p);
2043 stream->conncheck_list = g_slist_delete_link (stream->conncheck_list,
2044 item);
2045 cancelled++;
2046 }
2047 item = next;
2048 }
2049
2050 if (cancelled > 0)
2051 nice_debug ("Agent %p : Pruned %d pairs. "
2052 "Conncheck list has %d elements left. "
2053 "Maximum connchecks allowed : %d", agent, cancelled,
2054 valid - cancelled, agent->max_conn_checks);
2055
2056 return deleted;
2057 }
2058
2059 /*
2060 * Changes the selected pair for the component if 'pair'
2061 * has higher priority than the currently selected pair. See
2062 * RFC 8445 sect 8.1.1. "Nominating Pairs"
2063 */
2064 void
conn_check_update_selected_pair(NiceAgent * agent,NiceComponent * component,CandidateCheckPair * pair)2065 conn_check_update_selected_pair (NiceAgent *agent, NiceComponent *component,
2066 CandidateCheckPair *pair)
2067 {
2068 CandidatePair cpair = { 0, };
2069
2070 g_assert (component);
2071 g_assert (pair);
2072 /* pair is expected to have the nominated flag */
2073 g_assert (pair->nominated);
2074 if (pair->priority > component->selected_pair.priority) {
2075 gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2076 nice_candidate_pair_priority_to_string (pair->priority, priority);
2077 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
2078 "(prio:%s).", agent, component->id,
2079 pair->local->foundation, pair->remote->foundation, priority);
2080
2081 cpair.local = (NiceCandidateImpl *) pair->local;
2082 cpair.remote = (NiceCandidateImpl *) pair->remote;
2083 cpair.priority = pair->priority;
2084 cpair.stun_priority = pair->stun_priority;
2085
2086 nice_component_update_selected_pair (agent, component, &cpair);
2087
2088 priv_conn_keepalive_tick_unlocked (agent);
2089
2090 agent_signal_new_selected_pair (agent, pair->stream_id, component->id,
2091 pair->local, pair->remote);
2092 }
2093 }
2094
2095 /*
2096 * Updates the check list state.
2097 *
2098 * Implements parts of the algorithm described in
2099 * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any
2100 * component, all checks have been completed and have
2101 * failed to produce a nominated pair, mark that component's
2102 * state to NICE_CHECK_FAILED.
2103 *
2104 * Sends a component state changesignal via 'agent'.
2105 */
priv_update_check_list_failed_components(NiceAgent * agent,NiceStream * stream)2106 static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream)
2107 {
2108 GSList *i;
2109 gboolean completed;
2110 guint nominated;
2111 /* note: emitting a signal might cause the client
2112 * to remove the stream, thus the component count
2113 * must be fetched before entering the loop*/
2114 guint c, components = stream->n_components;
2115
2116 if (stream->conncheck_list == NULL)
2117 return;
2118
2119 for (i = agent->discovery_list; i; i = i->next) {
2120 CandidateDiscovery *d = i->data;
2121
2122 /* There is still discovery ogoing for this stream,
2123 * so don't fail any of it's candidates.
2124 */
2125 if (d->stream_id == stream->id && !d->done)
2126 return;
2127 }
2128 if (agent->discovery_list != NULL)
2129 return;
2130
2131 /* note: iterate the conncheck list for each component separately */
2132 for (c = 0; c < components; c++) {
2133 NiceComponent *component = NULL;
2134 if (!agent_find_component (agent, stream->id, c+1, NULL, &component))
2135 continue;
2136
2137 nominated = 0;
2138 completed = TRUE;
2139 for (i = stream->conncheck_list; i; i = i->next) {
2140 CandidateCheckPair *p = i->data;
2141
2142 g_assert_cmpuint (p->stream_id, ==, stream->id);
2143
2144 if (p->component_id == (c + 1)) {
2145 if (p->nominated)
2146 ++nominated;
2147 if (p->state != NICE_CHECK_FAILED &&
2148 p->state != NICE_CHECK_SUCCEEDED &&
2149 p->state != NICE_CHECK_DISCOVERED)
2150 completed = FALSE;
2151 }
2152 }
2153
2154 /* note: all pairs are either failed or succeeded, and the component
2155 * has not produced a nominated pair.
2156 * Set the component to FAILED only if it actually had remote candidates
2157 * that failed.. */
2158 if (completed && nominated == 0 &&
2159 component != NULL && component->remote_candidates != NULL)
2160 agent_signal_component_state_change (agent,
2161 stream->id,
2162 (c + 1), /* component-id */
2163 NICE_COMPONENT_STATE_FAILED);
2164 }
2165 }
2166
2167 /*
2168 * Updates the check list state for a stream component.
2169 *
2170 * Implements the algorithm described in ICE sect 8.1.2
2171 * "Updating States" (ID-19) as it applies to checks of
2172 * a certain component. If there are any nominated pairs,
2173 * ICE processing may be concluded, and component state is
2174 * changed to READY.
2175 *
2176 * Sends a component state changesignal via 'agent'.
2177 */
conn_check_update_check_list_state_for_ready(NiceAgent * agent,NiceStream * stream,NiceComponent * component)2178 void conn_check_update_check_list_state_for_ready (NiceAgent *agent,
2179 NiceStream *stream, NiceComponent *component)
2180 {
2181 GSList *i;
2182 guint valid = 0, nominated = 0;
2183
2184 g_assert (component);
2185
2186 /* step: search for at least one nominated pair */
2187 for (i = stream->conncheck_list; i; i = i->next) {
2188 CandidateCheckPair *p = i->data;
2189 if (p->component_id == component->id) {
2190 if (p->valid) {
2191 ++valid;
2192 if (p->nominated == TRUE) {
2193 ++nominated;
2194 }
2195 }
2196 }
2197 }
2198
2199 if (nominated > 0) {
2200 /* Only go to READY if no checks are left in progress. If there are
2201 * any that are kept, then this function will be called again when the
2202 * conncheck tick timer finishes them all */
2203 if (priv_prune_pending_checks (agent, stream, component) == 0) {
2204 /* Continue through the states to give client code a nice
2205 * logical progression. See http://phabricator.freedesktop.org/D218 for
2206 * discussion. */
2207 if (component->state < NICE_COMPONENT_STATE_CONNECTING ||
2208 component->state == NICE_COMPONENT_STATE_FAILED)
2209 agent_signal_component_state_change (agent, stream->id, component->id,
2210 NICE_COMPONENT_STATE_CONNECTING);
2211 if (component->state < NICE_COMPONENT_STATE_CONNECTED)
2212 agent_signal_component_state_change (agent, stream->id, component->id,
2213 NICE_COMPONENT_STATE_CONNECTED);
2214 agent_signal_component_state_change (agent, stream->id,
2215 component->id, NICE_COMPONENT_STATE_READY);
2216 }
2217 }
2218 nice_debug ("Agent %p : conn.check list status: %u nominated, %u valid, c-id %u.", agent, nominated, valid, component->id);
2219 }
2220
2221 /*
2222 * The remote party has signalled that the candidate pair
2223 * described by 'component' and 'remotecand' is nominated
2224 * for use.
2225 */
priv_mark_pair_nominated(NiceAgent * agent,NiceStream * stream,NiceComponent * component,NiceCandidate * localcand,NiceCandidate * remotecand)2226 static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand)
2227 {
2228 GSList *i;
2229
2230 g_assert (component);
2231
2232 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2233 agent->controlling_mode)
2234 return;
2235
2236 /* step: search for at least one nominated pair */
2237 for (i = stream->conncheck_list; i; i = i->next) {
2238 CandidateCheckPair *pair = i->data;
2239 if (pair->local == localcand && pair->remote == remotecand) {
2240 /* ICE, 7.2.1.5. Updating the Nominated Flag */
2241 /* note: TCP candidates typically produce peer reflexive
2242 * candidate, generating a "discovered" pair that can be
2243 * nominated.
2244 */
2245 if (pair->state == NICE_CHECK_SUCCEEDED &&
2246 pair->discovered_pair != NULL) {
2247 pair = pair->discovered_pair;
2248 g_assert_cmpint (pair->state, ==, NICE_CHECK_DISCOVERED);
2249 }
2250
2251 /* If the received Binding request triggered a new check to be
2252 * enqueued in the triggered-check queue (Section 7.3.1.4), once
2253 * the check is sent and if it generates a successful response,
2254 * and generates a valid pair, the agent sets the nominated flag
2255 * of the pair to true
2256 */
2257 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2258 if (g_slist_find (agent->triggered_check_queue, pair) ||
2259 pair->state == NICE_CHECK_IN_PROGRESS) {
2260
2261 /* This pair is not always in the triggered check list, for
2262 * example if it is in-progress with a lower priority than an
2263 * already nominated pair. Is that case, it is not rescheduled
2264 * for a connection check, see function
2265 * priv_schedule_triggered_check(), case NICE_CHECK_IN_PROGRESS.
2266 */
2267 pair->mark_nominated_on_response_arrival = TRUE;
2268 nice_debug ("Agent %p : pair %p (%s) is %s, "
2269 "will be nominated on response receipt.",
2270 agent, pair, pair->foundation,
2271 priv_state_to_string (pair->state));
2272 }
2273 }
2274
2275 if (pair->valid ||
2276 !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2277 nice_debug ("Agent %p : marking pair %p (%s) as nominated",
2278 agent, pair, pair->foundation);
2279 pair->nominated = TRUE;
2280 }
2281
2282 if (pair->valid) {
2283 /* Do not step down to CONNECTED if we're already at state READY*/
2284 if (component->state == NICE_COMPONENT_STATE_FAILED)
2285 agent_signal_component_state_change (agent,
2286 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
2287 conn_check_update_selected_pair (agent, component, pair);
2288 if (component->state == NICE_COMPONENT_STATE_CONNECTING)
2289 /* step: notify the client of a new component state (must be done
2290 * before the possible check list state update step */
2291 agent_signal_component_state_change (agent,
2292 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
2293 }
2294
2295 if (pair->nominated)
2296 conn_check_update_check_list_state_for_ready (agent, stream, component);
2297 }
2298 }
2299 }
2300
2301 /*
2302 * Creates a new connectivity check pair and adds it to
2303 * the agent's list of checks.
2304 */
priv_add_new_check_pair(NiceAgent * agent,guint stream_id,NiceComponent * component,NiceCandidateImpl * local,NiceCandidateImpl * remote,NiceCheckState initial_state)2305 static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
2306 guint stream_id, NiceComponent *component, NiceCandidateImpl *local,
2307 NiceCandidateImpl *remote, NiceCheckState initial_state)
2308 {
2309 NiceStream *stream;
2310 CandidateCheckPair *pair;
2311 guint64 priority;
2312
2313 g_assert (local != NULL);
2314 g_assert (remote != NULL);
2315
2316 priority = agent_candidate_pair_priority (agent, (NiceCandidate *) local,
2317 (NiceCandidate *) remote);
2318
2319 if (component->selected_pair.priority &&
2320 priority < component->selected_pair.priority) {
2321 gchar prio1[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2322 gchar prio2[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2323
2324 nice_candidate_pair_priority_to_string (priority, prio1);
2325 nice_candidate_pair_priority_to_string (component->selected_pair.priority,
2326 prio2);
2327 nice_debug ("Agent %p : do not create a pair that would have a priority "
2328 "%s lower than selected pair priority %s.", agent, prio1, prio2);
2329 return NULL;
2330 }
2331
2332 stream = agent_find_stream (agent, stream_id);
2333 pair = g_slice_new0 (CandidateCheckPair);
2334
2335 pair->stream_id = stream_id;
2336 pair->component_id = component->id;
2337 pair->local = (NiceCandidate *) local;
2338 pair->remote = (NiceCandidate *) remote;
2339 /* note: we use the remote sockptr only in the case
2340 * of TCP transport
2341 */
2342 if (local->c.transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
2343 remote->c.type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2344 pair->sockptr = remote->sockptr;
2345 else
2346 pair->sockptr = local->sockptr;
2347 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s",
2348 local->c.foundation, remote->c.foundation);
2349
2350 pair->priority = agent_candidate_pair_priority (agent,
2351 (NiceCandidate *) local, (NiceCandidate *) remote);
2352 nice_debug ("Agent %p : creating a new pair", agent);
2353 SET_PAIR_STATE (agent, pair, initial_state);
2354 {
2355 gchar tmpbuf1[INET6_ADDRSTRLEN];
2356 gchar tmpbuf2[INET6_ADDRSTRLEN];
2357 nice_address_to_string (&pair->local->addr, tmpbuf1);
2358 nice_address_to_string (&pair->remote->addr, tmpbuf2);
2359 nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
2360 tmpbuf1, nice_address_get_port (&pair->local->addr),
2361 tmpbuf2, nice_address_get_port (&pair->remote->addr));
2362 }
2363 pair->stun_priority = stun_request_priority (agent, (NiceCandidate *) local);
2364
2365 stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
2366 (GCompareFunc)conn_check_compare);
2367
2368 nice_debug ("Agent %p : added a new pair %p with foundation '%s' and "
2369 "transport %s:%s to stream %u component %u",
2370 agent, pair, pair->foundation,
2371 nice_candidate_transport_to_string (pair->local->transport),
2372 nice_candidate_transport_to_string (pair->remote->transport),
2373 stream_id, component->id);
2374
2375 if (initial_state == NICE_CHECK_FROZEN)
2376 priv_conn_check_unfreeze_maybe (agent, pair);
2377
2378 /* implement the hard upper limit for number of
2379 checks (see sect 5.7.3 ICE ID-19): */
2380 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) {
2381 if (priv_limit_conn_check_list_size (agent, stream, pair))
2382 return NULL;
2383 }
2384
2385 return pair;
2386 }
2387
2388 NiceCandidateTransport
conn_check_match_transport(NiceCandidateTransport transport)2389 conn_check_match_transport (NiceCandidateTransport transport)
2390 {
2391 switch (transport) {
2392 case NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
2393 return NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
2394 break;
2395 case NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
2396 return NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
2397 break;
2398 case NICE_CANDIDATE_TRANSPORT_TCP_SO:
2399 case NICE_CANDIDATE_TRANSPORT_UDP:
2400 default:
2401 return transport;
2402 break;
2403 }
2404 }
2405
priv_conn_check_add_for_candidate_pair_matched(NiceAgent * agent,guint stream_id,NiceComponent * component,NiceCandidate * local,NiceCandidate * remote,NiceCheckState initial_state)2406 static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
2407 NiceAgent *agent, guint stream_id, NiceComponent *component,
2408 NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state)
2409 {
2410 CandidateCheckPair *pair;
2411
2412 pair = priv_add_new_check_pair (agent, stream_id, component,
2413 (NiceCandidateImpl *) local, (NiceCandidateImpl *) remote, initial_state);
2414 if (pair) {
2415 if (component->state == NICE_COMPONENT_STATE_CONNECTED ||
2416 component->state == NICE_COMPONENT_STATE_READY) {
2417 agent_signal_component_state_change (agent,
2418 stream_id,
2419 component->id,
2420 NICE_COMPONENT_STATE_CONNECTED);
2421 } else {
2422 agent_signal_component_state_change (agent,
2423 stream_id,
2424 component->id,
2425 NICE_COMPONENT_STATE_CONNECTING);
2426 }
2427 }
2428
2429 return pair;
2430 }
2431
conn_check_add_for_candidate_pair(NiceAgent * agent,guint stream_id,NiceComponent * component,NiceCandidate * local,NiceCandidate * remote)2432 gboolean conn_check_add_for_candidate_pair (NiceAgent *agent,
2433 guint stream_id, NiceComponent *component, NiceCandidate *local,
2434 NiceCandidate *remote)
2435 {
2436 gboolean ret = FALSE;
2437
2438 g_assert (local != NULL);
2439 g_assert (remote != NULL);
2440
2441 /* note: do not create pairs where the local candidate is a srv-reflexive
2442 * or peer-reflexive (ICE 6.1.2.4. "Pruning the pairs" RFC 8445)
2443 */
2444 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
2445 agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
2446 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
2447 (local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
2448 local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)) {
2449 return FALSE;
2450 }
2451
2452 /* note: do not create pairs where local candidate has TCP passive transport
2453 * (ice-tcp-13 6.2. "Forming the Check Lists") */
2454 if (local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
2455 return FALSE;
2456 }
2457
2458 /* note: match pairs only if transport and address family are the same */
2459 if (local->transport == conn_check_match_transport (remote->transport) &&
2460 local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family) {
2461 if (priv_conn_check_add_for_candidate_pair_matched (agent, stream_id,
2462 component, local, remote, NICE_CHECK_FROZEN))
2463 ret = TRUE;
2464 }
2465
2466 return ret;
2467 }
2468
2469 /*
2470 * Forms new candidate pairs by matching the new remote candidate
2471 * 'remote_cand' with all existing local candidates of 'component'.
2472 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
2473 * Pairs" (ID-19).
2474 *
2475 * @param agent context
2476 * @param component pointer to the component
2477 * @param remote remote candidate to match with
2478 *
2479 * @return number of checks added, negative on fatal errors
2480 */
conn_check_add_for_candidate(NiceAgent * agent,guint stream_id,NiceComponent * component,NiceCandidate * remote)2481 int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote)
2482 {
2483 GSList *i;
2484 int added = 0;
2485 int ret = 0;
2486
2487 g_assert (remote != NULL);
2488
2489 /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates",
2490 * the agent does not pair this candidate with any local candidates.
2491 */
2492 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2493 remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2494 {
2495 return added;
2496 }
2497
2498 for (i = component->local_candidates; i ; i = i->next) {
2499 NiceCandidate *local = i->data;
2500
2501 if (agent->force_relay && local->type != NICE_CANDIDATE_TYPE_RELAYED)
2502 continue;
2503
2504 ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote);
2505
2506 if (ret) {
2507 ++added;
2508 }
2509 }
2510
2511 return added;
2512 }
2513
2514 /*
2515 * Forms new candidate pairs by matching the new local candidate
2516 * 'local_cand' with all existing remote candidates of 'component'.
2517 *
2518 * @param agent context
2519 * @param component pointer to the component
2520 * @param local local candidate to match with
2521 *
2522 * @return number of checks added, negative on fatal errors
2523 */
conn_check_add_for_local_candidate(NiceAgent * agent,guint stream_id,NiceComponent * component,NiceCandidate * local)2524 int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local)
2525 {
2526 GSList *i;
2527 int added = 0;
2528 int ret = 0;
2529
2530 g_assert (local != NULL);
2531
2532 /*
2533 * note: according to 7.1.3.2.1 "Discovering Peer Reflexive
2534 * Candidates", the peer reflexive candidate is not paired
2535 * with other remote candidates
2536 */
2537
2538 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2539 local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2540 {
2541 return added;
2542 }
2543
2544 for (i = component->remote_candidates; i ; i = i->next) {
2545
2546 NiceCandidate *remote = i->data;
2547 ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote);
2548
2549 if (ret) {
2550 ++added;
2551 }
2552 }
2553
2554 return added;
2555 }
2556
2557 /*
2558 * Frees the CandidateCheckPair structure pointer to
2559 * by 'user data'. Compatible with GDestroyNotify.
2560 */
candidate_check_pair_free(NiceAgent * agent,CandidateCheckPair * pair)2561 static void candidate_check_pair_free (NiceAgent *agent,
2562 CandidateCheckPair *pair)
2563 {
2564 priv_remove_pair_from_triggered_check_queue (agent, pair);
2565 priv_free_all_stun_transactions (pair, NULL);
2566 g_slice_free (CandidateCheckPair, pair);
2567 }
2568
2569 /*
2570 * Frees all resources of all connectivity checks.
2571 */
conn_check_free(NiceAgent * agent)2572 void conn_check_free (NiceAgent *agent)
2573 {
2574 GSList *i;
2575 for (i = agent->streams; i; i = i->next) {
2576 NiceStream *stream = i->data;
2577
2578 if (stream->conncheck_list) {
2579 GSList *item;
2580
2581 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent,
2582 stream);
2583 for (item = stream->conncheck_list; item; item = item->next)
2584 candidate_check_pair_free (agent, item->data);
2585 g_slist_free (stream->conncheck_list);
2586 stream->conncheck_list = NULL;
2587 }
2588 }
2589
2590 conn_check_stop (agent);
2591 }
2592
2593 /*
2594 * Prunes the list of connectivity checks for items related
2595 * to stream 'stream_id'.
2596 *
2597 * @return TRUE on success, FALSE on a fatal error
2598 */
conn_check_prune_stream(NiceAgent * agent,NiceStream * stream)2599 void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream)
2600 {
2601 GSList *i;
2602 gboolean keep_going = FALSE;
2603
2604 if (stream->conncheck_list) {
2605 GSList *item;
2606
2607 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, stream);
2608
2609 for (item = stream->conncheck_list; item; item = item->next)
2610 candidate_check_pair_free (agent, item->data);
2611 g_slist_free (stream->conncheck_list);
2612 stream->conncheck_list = NULL;
2613 }
2614
2615 for (i = agent->streams; i; i = i->next) {
2616 NiceStream *s = i->data;
2617 if (s->conncheck_list) {
2618 keep_going = TRUE;
2619 break;
2620 }
2621 }
2622
2623 if (!keep_going)
2624 conn_check_stop (agent);
2625 }
2626
2627 /*
2628 * Fills 'dest' with a username string for use in an outbound connectivity
2629 * checks. No more than 'dest_len' characters (including terminating
2630 * NULL) is ever written to the 'dest'.
2631 */
2632 static
priv_gen_username(NiceAgent * agent,guint component_id,gchar * remote,gchar * local,uint8_t * dest,guint dest_len)2633 size_t priv_gen_username (NiceAgent *agent, guint component_id,
2634 gchar *remote, gchar *local, uint8_t *dest, guint dest_len)
2635 {
2636 guint len = 0;
2637 gsize remote_len = strlen (remote);
2638 gsize local_len = strlen (local);
2639
2640 if (remote_len > 0 && local_len > 0) {
2641 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
2642 dest_len >= remote_len + local_len + 1) {
2643 memcpy (dest, remote, remote_len);
2644 len += remote_len;
2645 memcpy (dest + len, ":", 1);
2646 len++;
2647 memcpy (dest + len, local, local_len);
2648 len += local_len;
2649 } else if ((agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
2650 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
2651 dest_len >= remote_len + local_len + 4 ) {
2652 memcpy (dest, remote, remote_len);
2653 len += remote_len;
2654 memcpy (dest + len, ":", 1);
2655 len++;
2656 memcpy (dest + len, local, local_len);
2657 len += local_len;
2658 if (len % 4 != 0) {
2659 memset (dest + len, 0, 4 - (len % 4));
2660 len += 4 - (len % 4);
2661 }
2662 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
2663 dest_len >= remote_len + local_len) {
2664 memcpy (dest, remote, remote_len);
2665 len += remote_len;
2666 memcpy (dest + len, local, local_len);
2667 len += local_len;
2668 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2669 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
2670 gchar component_str[10];
2671 guchar *local_decoded = NULL;
2672 guchar *remote_decoded = NULL;
2673 gsize local_decoded_len;
2674 gsize remote_decoded_len;
2675 gsize total_len;
2676 int padding;
2677
2678 g_snprintf (component_str, sizeof(component_str), "%d", component_id);
2679 local_decoded = g_base64_decode (local, &local_decoded_len);
2680 remote_decoded = g_base64_decode (remote, &remote_decoded_len);
2681
2682 total_len = remote_decoded_len + local_decoded_len + 3 + 2*strlen (component_str);
2683 padding = 4 - (total_len % 4);
2684
2685 if (dest_len >= total_len + padding) {
2686 guchar pad_char[1] = {0};
2687 int i;
2688
2689 memcpy (dest, remote_decoded, remote_decoded_len);
2690 len += remote_decoded_len;
2691 memcpy (dest + len, ":", 1);
2692 len++;
2693 memcpy (dest + len, component_str, strlen (component_str));
2694 len += strlen (component_str);
2695
2696 memcpy (dest + len, ":", 1);
2697 len++;
2698
2699 memcpy (dest + len, local_decoded, local_decoded_len);
2700 len += local_decoded_len;
2701 memcpy (dest + len, ":", 1);
2702 len++;
2703 memcpy (dest + len, component_str, strlen (component_str));;
2704 len += strlen (component_str);
2705
2706 for (i = 0; i < padding; i++) {
2707 memcpy (dest + len, pad_char, 1);
2708 len++;
2709 }
2710
2711 }
2712
2713 g_free (local_decoded);
2714 g_free (remote_decoded);
2715 }
2716 }
2717
2718 return len;
2719 }
2720
2721 /*
2722 * Fills 'dest' with a username string for use in an outbound connectivity
2723 * checks. No more than 'dest_len' characters (including terminating
2724 * NULL) is ever written to the 'dest'.
2725 */
2726 static
priv_create_username(NiceAgent * agent,NiceStream * stream,guint component_id,NiceCandidate * remote,NiceCandidate * local,uint8_t * dest,guint dest_len,gboolean inbound)2727 size_t priv_create_username (NiceAgent *agent, NiceStream *stream,
2728 guint component_id, NiceCandidate *remote, NiceCandidate *local,
2729 uint8_t *dest, guint dest_len, gboolean inbound)
2730 {
2731 gchar *local_username = NULL;
2732 gchar *remote_username = NULL;
2733
2734
2735 if (remote && remote->username) {
2736 remote_username = remote->username;
2737 }
2738
2739 if (local && local->username) {
2740 local_username = local->username;
2741 }
2742
2743 if (stream) {
2744 if (remote_username == NULL) {
2745 remote_username = stream->remote_ufrag;
2746 }
2747 if (local_username == NULL) {
2748 local_username = stream->local_ufrag;
2749 }
2750 }
2751
2752 if (local_username && remote_username) {
2753 if (inbound) {
2754 return priv_gen_username (agent, component_id,
2755 local_username, remote_username, dest, dest_len);
2756 } else {
2757 return priv_gen_username (agent, component_id,
2758 remote_username, local_username, dest, dest_len);
2759 }
2760 }
2761
2762 return 0;
2763 }
2764
2765 /*
2766 * Returns a password string for use in an outbound connectivity
2767 * check.
2768 */
2769 static
priv_get_password(NiceAgent * agent,NiceStream * stream,NiceCandidate * remote,uint8_t ** password)2770 size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
2771 NiceCandidate *remote, uint8_t **password)
2772 {
2773 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
2774 return 0;
2775
2776 if (remote && remote->password) {
2777 *password = (uint8_t *)remote->password;
2778 return strlen (remote->password);
2779 }
2780
2781 if (stream) {
2782 *password = (uint8_t *)stream->remote_password;
2783 return strlen (stream->remote_password);
2784 }
2785
2786 return 0;
2787 }
2788
2789 /* Implement the computation specific in RFC 8445 section 14 */
2790
priv_compute_conncheck_timer(NiceAgent * agent,NiceStream * stream)2791 static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream)
2792 {
2793 GSList *i, *j;
2794 guint waiting_and_in_progress = 0;
2795 unsigned int rto = 0;
2796
2797 /* we can compute precisely the number of pairs in-progress or
2798 * waiting for all streams, instead of limiting the value to one
2799 * stream, and multiplying it by the number of active streams.
2800 * Since RFC8445, this number of waiting and in-progress pairs
2801 * if maxed by the number of different foundations in the conncheck
2802 * list.
2803 */
2804 for (i = agent->streams; i ; i = i->next) {
2805 NiceStream *s = i->data;
2806 for (j = s->conncheck_list; j ; j = j->next) {
2807 CandidateCheckPair *p = j->data;
2808 if (p->state == NICE_CHECK_IN_PROGRESS ||
2809 p->state == NICE_CHECK_WAITING)
2810 waiting_and_in_progress++;
2811 }
2812 }
2813
2814 rto = agent->timer_ta * waiting_and_in_progress;
2815
2816 nice_debug ("Agent %p : timer set to %dms, "
2817 "waiting+in_progress=%d", agent, MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT),
2818 waiting_and_in_progress);
2819 return MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT);
2820 }
2821
2822 /*
2823 * Sends a connectivity check over candidate pair 'pair'.
2824 *
2825 * @return zero on success, non-zero on error
2826 */
conn_check_send(NiceAgent * agent,CandidateCheckPair * pair)2827 int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
2828 {
2829
2830 /* note: following information is supplied:
2831 * - username (for USERNAME attribute)
2832 * - password (for MESSAGE-INTEGRITY)
2833 * - priority (for PRIORITY)
2834 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
2835 * - USE-CANDIDATE (if sent by the controlling agent)
2836 */
2837
2838 uint8_t uname[NICE_STREAM_MAX_UNAME];
2839 NiceStream *stream;
2840 NiceComponent *component;
2841 gsize uname_len;
2842 uint8_t *password = NULL;
2843 gsize password_len;
2844 bool controlling = agent->controlling_mode;
2845 /* XXX: add API to support different nomination modes: */
2846 bool cand_use = controlling;
2847 size_t buffer_len;
2848 unsigned int timeout;
2849 StunTransaction *stun;
2850
2851 if (!agent_find_component (agent, pair->stream_id, pair->component_id,
2852 &stream, &component))
2853 return -1;
2854
2855 uname_len = priv_create_username (agent, stream, pair->component_id,
2856 pair->remote, pair->local, uname, sizeof (uname), FALSE);
2857 password_len = priv_get_password (agent, stream, pair->remote, &password);
2858
2859 if (password != NULL &&
2860 (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2861 agent->compatibility == NICE_COMPATIBILITY_OC2007)) {
2862 password = g_base64_decode ((gchar *) password, &password_len);
2863 }
2864
2865 if (nice_debug_is_enabled ()) {
2866 gchar tmpbuf1[INET6_ADDRSTRLEN];
2867 gchar tmpbuf2[INET6_ADDRSTRLEN];
2868 nice_address_to_string (&pair->local->addr, tmpbuf1);
2869 nice_address_to_string (&pair->remote->addr, tmpbuf2);
2870 nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, "
2871 "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), "
2872 "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%08x, %s.", agent,
2873 tmpbuf1, nice_address_get_port (&pair->local->addr),
2874 tmpbuf2, nice_address_get_port (&pair->remote->addr),
2875 pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1,
2876 pair, pair->component_id,
2877 (unsigned long long)agent->tie_breaker,
2878 (int) uname_len, uname, uname_len,
2879 (int) password_len, password, password_len,
2880 pair->stun_priority,
2881 controlling ? "controlling" : "controlled");
2882 }
2883
2884 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2885 switch (agent->nomination_mode) {
2886 case NICE_NOMINATION_MODE_REGULAR:
2887 /* We are doing regular nomination, so we set the use-candidate
2888 * attrib, when the controlling agent decided which valid pair to
2889 * resend with this flag in priv_conn_check_tick_stream()
2890 */
2891 cand_use = pair->use_candidate_on_next_check;
2892 nice_debug ("Agent %p : %s: set cand_use=%d "
2893 "(regular nomination).", agent, G_STRFUNC, cand_use);
2894 break;
2895 case NICE_NOMINATION_MODE_AGGRESSIVE:
2896 /* We are doing aggressive nomination, we set the use-candidate
2897 * attrib in every check we send, when we are the controlling
2898 * agent, RFC 5245, 8.1.1.2
2899 */
2900 cand_use = controlling;
2901 nice_debug ("Agent %p : %s: set cand_use=%d "
2902 "(aggressive nomination).", agent, G_STRFUNC, cand_use);
2903 break;
2904 default:
2905 /* Nothing to do. */
2906 break;
2907 }
2908 } else if (cand_use)
2909 pair->nominated = controlling;
2910
2911 if (uname_len == 0) {
2912 nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent);
2913 return -1;
2914 }
2915
2916 stun = priv_add_stun_transaction (pair);
2917
2918 buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent,
2919 &stun->message, stun->buffer, sizeof(stun->buffer),
2920 uname, uname_len, password, password_len,
2921 cand_use, controlling, pair->stun_priority,
2922 agent->tie_breaker,
2923 pair->local->foundation,
2924 agent_to_ice_compatibility (agent));
2925
2926 nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len,
2927 stun->message.buffer);
2928
2929 if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2930 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
2931 g_free (password);
2932 }
2933
2934 if (buffer_len == 0) {
2935 nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent);
2936 priv_remove_stun_transaction (pair, stun, component);
2937 return -1;
2938 }
2939
2940 if (nice_socket_is_reliable(pair->sockptr)) {
2941 timeout = agent->stun_reliable_timeout;
2942 stun_timer_start_reliable(&stun->timer, timeout);
2943 } else {
2944 timeout = priv_compute_conncheck_timer (agent, stream);
2945 stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions);
2946 }
2947
2948 stun->next_tick = g_get_monotonic_time () + timeout * 1000;
2949
2950 /* TCP-ACTIVE candidate must create a new socket before sending
2951 * by connecting to the peer. The new socket is stored in the candidate
2952 * check pair, until we discover a new local peer reflexive */
2953 if (pair->sockptr->fileno == NULL &&
2954 pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN &&
2955 pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
2956 NiceStream *stream2 = NULL;
2957 NiceComponent *component2 = NULL;
2958 NiceSocket *new_socket;
2959
2960 if (agent_find_component (agent, pair->stream_id, pair->component_id,
2961 &stream2, &component2)) {
2962 new_socket = nice_tcp_active_socket_connect (pair->sockptr,
2963 &pair->remote->addr);
2964 if (new_socket) {
2965 nice_debug ("Agent %p: add to tcp-act socket %p a new "
2966 "tcp connect socket %p on pair %p in s/c %d/%d",
2967 agent, pair->sockptr, new_socket, pair, stream->id, component->id);
2968 pair->sockptr = new_socket;
2969 _priv_set_socket_tos (agent, pair->sockptr, stream2->tos);
2970
2971 nice_socket_set_writable_callback (pair->sockptr, _tcp_sock_is_writable,
2972 component2);
2973
2974 nice_component_attach_socket (component2, new_socket);
2975 }
2976 }
2977 }
2978 /* send the conncheck */
2979 agent_socket_send (pair->sockptr, &pair->remote->addr,
2980 buffer_len, (gchar *)stun->buffer);
2981
2982 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2)
2983 ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr,
2984 &pair->remote->addr);
2985
2986 return 0;
2987 }
2988
2989 /*
2990 * Implemented the pruning steps described in ICE sect 8.1.2
2991 * "Updating States" (ID-19) after a pair has been nominated.
2992 *
2993 * @see conn_check_update_check_list_state_failed_components()
2994 */
priv_prune_pending_checks(NiceAgent * agent,NiceStream * stream,NiceComponent * component)2995 static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, NiceComponent *component)
2996 {
2997 GSList *i;
2998 guint64 priority;
2999 guint in_progress = 0;
3000 guint triggered_check = 0;
3001 gchar prio[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
3002
3003 nice_debug ("Agent %p: Pruning pending checks for s%d/c%d",
3004 agent, stream->id, component->id);
3005
3006 /* Called when we have at least one selected pair */
3007 priority = component->selected_pair.priority;
3008 g_assert (priority > 0);
3009
3010 nice_candidate_pair_priority_to_string (priority, prio);
3011 nice_debug ("Agent %p : selected pair priority is %s", agent, prio);
3012
3013 i = stream->conncheck_list;
3014 while (i) {
3015 CandidateCheckPair *p = i->data;
3016 GSList *next = i->next;
3017
3018 if (p->component_id != component->id) {
3019 i = next;
3020 continue;
3021 }
3022
3023 /* We do not remove a pair from the conncheck list if it is also in
3024 * the triggered check queue. This is not what suggests the ICE
3025 * spec, but it proved to be more robust in the aggressive
3026 * nomination scenario, precisely because these pairs may have the
3027 * use-candidate flag set, and the peer agent may already have
3028 * selected such one.
3029 */
3030 if (g_slist_find (agent->triggered_check_queue, p) &&
3031 p->state != NICE_CHECK_IN_PROGRESS) {
3032 if (p->priority < priority) {
3033 nice_debug ("Agent %p : pair %p removed.", agent, p);
3034 candidate_check_pair_free (agent, p);
3035 stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
3036 } else
3037 triggered_check++;
3038 }
3039
3040 /* step: cancel all FROZEN and WAITING pairs for the component */
3041 else if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) {
3042 nice_debug ("Agent %p : pair %p removed.", agent, p);
3043 candidate_check_pair_free (agent, p);
3044 stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
3045 }
3046
3047 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
3048 else if (p->state == NICE_CHECK_IN_PROGRESS) {
3049 if (p->priority < priority) {
3050 priv_remove_pair_from_triggered_check_queue (agent, p);
3051 if (p->retransmit) {
3052 p->retransmit = FALSE;
3053 nice_debug ("Agent %p : pair %p will not be retransmitted.",
3054 agent, p);
3055 }
3056 } else {
3057 /* We must keep the higher priority pairs running because if a udp
3058 * packet was lost, we might end up using a bad candidate */
3059 nice_candidate_pair_priority_to_string (p->priority, prio);
3060 nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority "
3061 "%s is higher than priority of best nominated pair.", agent, p, prio);
3062 /* We may also have to enable the retransmit flag of pairs with
3063 * a higher priority than the first nominated pair
3064 */
3065 if (!p->retransmit && p->stun_transactions) {
3066 p->retransmit = TRUE;
3067 nice_debug ("Agent %p : pair %p will be retransmitted.", agent, p);
3068 }
3069 in_progress++;
3070 }
3071 }
3072 i = next;
3073 }
3074
3075 return in_progress + triggered_check;
3076 }
3077
3078 /*
3079 * Schedules a triggered check after a successfully inbound
3080 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
3081 *
3082 * @param agent self pointer
3083 * @param component the check is related to
3084 * @param local_socket socket from which the inbound check was received
3085 * @param remote_cand remote candidate from which the inbound check was sent
3086 */
priv_schedule_triggered_check(NiceAgent * agent,NiceStream * stream,NiceComponent * component,NiceSocket * local_socket,NiceCandidate * remote_cand)3087 static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand)
3088 {
3089 GSList *i;
3090 NiceCandidate *local = NULL;
3091 CandidateCheckPair *p;
3092
3093 g_assert (remote_cand != NULL);
3094
3095 nice_debug ("Agent %p : scheduling triggered check with socket=%p "
3096 "and remote cand=%p.", agent, local_socket, remote_cand);
3097
3098 for (i = stream->conncheck_list; i ; i = i->next) {
3099 p = i->data;
3100 if (p->component_id == component->id &&
3101 p->remote == remote_cand &&
3102 p->sockptr == local_socket) {
3103 /* If we match with a peer-reflexive discovered pair, we
3104 * use the parent succeeded pair instead */
3105
3106 if (p->succeeded_pair != NULL) {
3107 g_assert_cmpint (p->state, ==, NICE_CHECK_DISCOVERED);
3108 p = p->succeeded_pair;
3109 }
3110
3111 nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...",
3112 agent, p, p->foundation, priv_state_to_string (p->state));
3113
3114 switch (p->state) {
3115 case NICE_CHECK_WAITING:
3116 case NICE_CHECK_FROZEN:
3117 nice_debug ("Agent %p : pair %p added for a triggered check.",
3118 agent, p);
3119 priv_add_pair_to_triggered_check_queue (agent, p);
3120 break;
3121 case NICE_CHECK_IN_PROGRESS:
3122 /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks"
3123 * we cancel the in-progress transaction, and after the
3124 * retransmission timeout, we create a new connectivity check
3125 * for that pair. The controlling role of this new check may
3126 * be different from the role of this cancelled check.
3127 *
3128 * When another pair, with a higher priority is already
3129 * nominated, so there's no reason to recheck this pair,
3130 * since it can in no way replace the nominated one.
3131 */
3132 if (p->priority > component->selected_pair.priority) {
3133 nice_debug ("Agent %p : pair %p added for a triggered check.",
3134 agent, p);
3135 priv_add_pair_to_triggered_check_queue (agent, p);
3136 }
3137 break;
3138 case NICE_CHECK_FAILED:
3139 if (p->priority > component->selected_pair.priority) {
3140 nice_debug ("Agent %p : pair %p added for a triggered check.",
3141 agent, p);
3142 priv_add_pair_to_triggered_check_queue (agent, p);
3143 /* If the component for this pair is in failed state, move it
3144 * back to connecting, and reinitiate the timers
3145 */
3146 if (component->state == NICE_COMPONENT_STATE_FAILED) {
3147 agent_signal_component_state_change (agent, stream->id,
3148 component->id, NICE_COMPONENT_STATE_CONNECTING);
3149 conn_check_schedule_next (agent);
3150 /* If the component if in ready state, move it back to
3151 * connected as this failed pair with a higher priority
3152 * than the nominated pair requires to pursue the
3153 * conncheck
3154 */
3155 } else if (component->state == NICE_COMPONENT_STATE_READY) {
3156 agent_signal_component_state_change (agent, stream->id,
3157 component->id, NICE_COMPONENT_STATE_CONNECTED);
3158 conn_check_schedule_next (agent);
3159 }
3160 }
3161 break;
3162 case NICE_CHECK_SUCCEEDED:
3163 nice_debug ("Agent %p : nothing to do for pair %p.", agent, p);
3164 break;
3165 default:
3166 break;
3167 }
3168
3169 /* note: the spec says the we SHOULD retransmit in-progress
3170 * checks immediately, but we won't do that now */
3171
3172 return TRUE;
3173 }
3174 }
3175
3176 for (i = component->local_candidates; i ; i = i->next) {
3177 NiceCandidateImpl *lc = i->data;
3178 local = i->data;
3179 if (lc->sockptr == local_socket)
3180 break;
3181 }
3182
3183 if (i) {
3184 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
3185 p = priv_conn_check_add_for_candidate_pair_matched (agent, stream->id,
3186 component, local, remote_cand, NICE_CHECK_WAITING);
3187 if (p)
3188 priv_add_pair_to_triggered_check_queue (agent, p);
3189 return TRUE;
3190 }
3191 else {
3192 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent, remote_cand);
3193 return FALSE;
3194 }
3195 }
3196
3197
3198 /*
3199 * Sends a reply to an successfully received STUN connectivity
3200 * check request. Implements parts of the ICE spec section 7.2 (STUN
3201 * Server Procedures).
3202 *
3203 * @param agent context pointer
3204 * @param stream which stream (of the agent)
3205 * @param component which component (of the stream)
3206 * @param rcand remote candidate from which the request came, if NULL,
3207 * the response is sent immediately but no other processing is done
3208 * @param toaddr address to which reply is sent
3209 * @param socket the socket over which the request came
3210 * @param rbuf_len length of STUN message to send
3211 * @param msg the STUN message to send
3212 * @param use_candidate whether the request had USE_CANDIDATE attribute
3213 *
3214 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
3215 */
priv_reply_to_conn_check(NiceAgent * agent,NiceStream * stream,NiceComponent * component,NiceCandidate * lcand,NiceCandidate * rcand,const NiceAddress * toaddr,NiceSocket * sockptr,size_t rbuf_len,StunMessage * msg,gboolean use_candidate)3216 static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream,
3217 NiceComponent *component, NiceCandidate *lcand, NiceCandidate *rcand,
3218 const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len,
3219 StunMessage *msg, gboolean use_candidate)
3220 {
3221 g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE);
3222
3223 if (nice_debug_is_enabled ()) {
3224 gchar tmpbuf[INET6_ADDRSTRLEN];
3225 nice_address_to_string (toaddr, tmpbuf);
3226 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent,
3227 tmpbuf,
3228 nice_address_get_port (toaddr),
3229 sockptr->fileno ? g_socket_get_fd(sockptr->fileno) : -1,
3230 (unsigned)rbuf_len,
3231 rcand, component->id,
3232 (int)use_candidate);
3233 }
3234
3235 agent_socket_send (sockptr, toaddr, rbuf_len, (const gchar*)msg->buffer);
3236 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
3237 ms_ice2_legacy_conncheck_send(msg, sockptr, toaddr);
3238 }
3239
3240 /* We react to this stun request when we have the remote credentials.
3241 * When credentials are not yet known, this request is stored
3242 * in incoming_checks for later processing when returning from this
3243 * function.
3244 */
3245 if (rcand && stream->remote_ufrag[0]) {
3246 priv_schedule_triggered_check (agent, stream, component, sockptr, rcand);
3247 if (use_candidate)
3248 priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
3249 }
3250 }
3251
3252 /*
3253 * Stores information of an incoming STUN connectivity check
3254 * for later use. This is only needed when a check is received
3255 * before we get information about the remote candidates (via
3256 * SDP or other signaling means).
3257 *
3258 * @return non-zero on error, zero on success
3259 */
priv_store_pending_check(NiceAgent * agent,NiceComponent * component,const NiceAddress * from,NiceSocket * sockptr,uint8_t * username,uint16_t username_len,uint32_t priority,gboolean use_candidate)3260 static int priv_store_pending_check (NiceAgent *agent, NiceComponent *component,
3261 const NiceAddress *from, NiceSocket *sockptr, uint8_t *username,
3262 uint16_t username_len, uint32_t priority, gboolean use_candidate)
3263 {
3264 IncomingCheck *icheck;
3265 nice_debug ("Agent %p : Storing pending check.", agent);
3266
3267 if (g_queue_get_length (&component->incoming_checks) >=
3268 NICE_AGENT_MAX_REMOTE_CANDIDATES) {
3269 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent);
3270 return -1;
3271 }
3272
3273 icheck = g_slice_new0 (IncomingCheck);
3274 g_queue_push_tail (&component->incoming_checks, icheck);
3275 icheck->from = *from;
3276 icheck->local_socket = sockptr;
3277 icheck->priority = priority;
3278 icheck->use_candidate = use_candidate;
3279 icheck->username_len = username_len;
3280 icheck->username = NULL;
3281 if (username_len > 0)
3282 icheck->username = g_memdup (username, username_len);
3283
3284 return 0;
3285 }
3286
3287 /*
3288 * Adds a new pair, discovered from an incoming STUN response, to
3289 * the connectivity check list.
3290 *
3291 * @return created pair, or NULL on fatal (memory allocation) errors
3292 */
priv_add_peer_reflexive_pair(NiceAgent * agent,guint stream_id,NiceComponent * component,NiceCandidateImpl * local_cand,CandidateCheckPair * parent_pair)3293 static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidateImpl *local_cand, CandidateCheckPair *parent_pair)
3294 {
3295 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
3296 NiceStream *stream = agent_find_stream (agent, stream_id);
3297
3298 pair->stream_id = stream_id;
3299 pair->component_id = component->id;;
3300 pair->local = (NiceCandidate *) local_cand;
3301 pair->remote = parent_pair->remote;
3302 pair->sockptr = local_cand->sockptr;
3303 parent_pair->discovered_pair = pair;
3304 pair->succeeded_pair = parent_pair;
3305 nice_debug ("Agent %p : creating a new pair", agent);
3306 SET_PAIR_STATE (agent, pair, NICE_CHECK_DISCOVERED);
3307 {
3308 gchar tmpbuf1[INET6_ADDRSTRLEN];
3309 gchar tmpbuf2[INET6_ADDRSTRLEN];
3310 nice_address_to_string (&pair->local->addr, tmpbuf1);
3311 nice_address_to_string (&pair->remote->addr, tmpbuf2);
3312 nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
3313 tmpbuf1, nice_address_get_port (&pair->local->addr),
3314 tmpbuf2, nice_address_get_port (&pair->remote->addr));
3315 }
3316 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s",
3317 local_cand->c.foundation, parent_pair->remote->foundation);
3318
3319 if (agent->controlling_mode == TRUE)
3320 pair->priority = nice_candidate_pair_priority (pair->local->priority,
3321 pair->remote->priority);
3322 else
3323 pair->priority = nice_candidate_pair_priority (pair->remote->priority,
3324 pair->local->priority);
3325 pair->nominated = parent_pair->nominated;
3326 /* the peer-reflexive priority used in stun request is copied from
3327 * the parent succeeded pair. This value is not required for discovered
3328 * pair, that won't emit stun requests themselves, but may be used when
3329 * such pair becomes the selected pair, and when keepalive stun are emitted,
3330 * using the sockptr and stun_priority values from the succeeded pair.
3331 */
3332 pair->stun_priority = parent_pair->stun_priority;
3333 nice_debug ("Agent %p : added a new peer-discovered pair %p with "
3334 "foundation '%s' and transport %s:%s to stream %u component %u",
3335 agent, pair, pair->foundation,
3336 nice_candidate_transport_to_string (pair->local->transport),
3337 nice_candidate_transport_to_string (pair->remote->transport),
3338 stream_id, component->id);
3339
3340 stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
3341 (GCompareFunc)conn_check_compare);
3342
3343 return pair;
3344 }
3345
3346 /*
3347 * Recalculates priorities of all candidate pairs. This
3348 * is required after a conflict in ICE roles.
3349 */
recalculate_pair_priorities(NiceAgent * agent)3350 void recalculate_pair_priorities (NiceAgent *agent)
3351 {
3352 GSList *i, *j;
3353
3354 for (i = agent->streams; i; i = i->next) {
3355 NiceStream *stream = i->data;
3356 for (j = stream->conncheck_list; j; j = j->next) {
3357 CandidateCheckPair *p = j->data;
3358 p->priority = agent_candidate_pair_priority (agent, p->local, p->remote);
3359 }
3360 stream->conncheck_list = g_slist_sort (stream->conncheck_list,
3361 (GCompareFunc)conn_check_compare);
3362 }
3363 }
3364
3365 /*
3366 * Change the agent role if different from 'control'. Can be
3367 * initiated both by handling of incoming connectivity checks,
3368 * and by processing the responses to checks sent by us.
3369 */
priv_check_for_role_conflict(NiceAgent * agent,gboolean control)3370 static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
3371 {
3372 /* role conflict, change mode; wait for a new conn. check */
3373 if (control != agent->controlling_mode) {
3374 nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".",
3375 agent, control ? "controlling" : "controlled");
3376 agent->controlling_mode = control;
3377 /* the pair priorities depend on the roles, so recalculation
3378 * is needed */
3379 recalculate_pair_priorities (agent);
3380 }
3381 else
3382 nice_debug ("Agent %p : Role conflict, staying with role \"%s\".",
3383 agent, control ? "controlling" : "controlled");
3384 }
3385
3386 /*
3387 * Checks whether the mapped address in connectivity check response
3388 * matches any of the known local candidates. If not, apply the
3389 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
3390 *
3391 * @param agent context pointer
3392 * @param stream which stream (of the agent)
3393 * @param component which component (of the stream)
3394 * @param p the connectivity check pair for which we got a response
3395 * @param socketptr socket used to send the reply
3396 * @param mapped_sockaddr mapped address in the response
3397 *
3398 * @return pointer to a candidate pair, found in conncheck list or newly created
3399 */
priv_process_response_check_for_reflexive(NiceAgent * agent,NiceStream * stream,NiceComponent * component,CandidateCheckPair * p,NiceSocket * sockptr,struct sockaddr * mapped_sockaddr,NiceCandidate * local_candidate,NiceCandidate * remote_candidate)3400 static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *p, NiceSocket *sockptr, struct sockaddr *mapped_sockaddr, NiceCandidate *local_candidate, NiceCandidate *remote_candidate)
3401 {
3402 CandidateCheckPair *new_pair = NULL;
3403 NiceAddress mapped;
3404 GSList *i;
3405 NiceCandidate *local_cand = NULL;
3406
3407 nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
3408
3409 for (i = component->local_candidates; i; i = i->next) {
3410 NiceCandidate *cand = i->data;
3411
3412 if (nice_address_equal (&mapped, &cand->addr) &&
3413 local_candidate_and_socket_compatible (agent, cand, sockptr)) {
3414 local_cand = cand;
3415 break;
3416 }
3417 }
3418
3419 /* The mapped address allows to look for a previously discovered
3420 * peer reflexive local candidate, and its related pair. This
3421 * new_pair will be marked 'Valid', while the pair 'p' of the
3422 * initial stun request will be marked 'Succeeded'
3423 *
3424 * In the case of a tcp-act/tcp-pass pair 'p', where the local
3425 * candidate is of type tcp-act, and its port number is zero, a
3426 * conncheck on this pair *always* leads to the creation of a
3427 * discovered peer-reflexive tcp-act local candidate.
3428 */
3429 for (i = stream->conncheck_list; i; i = i->next) {
3430 CandidateCheckPair *pair = i->data;
3431 if (local_cand == pair->local && remote_candidate == pair->remote) {
3432 new_pair = pair;
3433 break;
3434 }
3435 }
3436
3437 if (new_pair) {
3438 /* note: when new_pair is distinct from p, it means new_pair is a
3439 * previously discovered peer-reflexive candidate pair, so we don't
3440 * set the valid flag on p in this case, because the valid flag is
3441 * already set on the discovered pair.
3442 */
3443 if (new_pair == p)
3444 p->valid = TRUE;
3445 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3446 priv_remove_pair_from_triggered_check_queue (agent, p);
3447 priv_free_all_stun_transactions (p, component);
3448 nice_component_add_valid_candidate (agent, component, remote_candidate);
3449 }
3450 else {
3451 if (local_cand == NULL && !agent->force_relay) {
3452 /* step: find a new local candidate, see RFC 5245 7.1.3.2.1.
3453 * "Discovering Peer Reflexive Candidates"
3454 *
3455 * The priority equal to the value of the PRIORITY attribute
3456 * in the Binding request is taken from the "parent" pair p
3457 */
3458 local_cand = discovery_add_peer_reflexive_candidate (agent,
3459 stream->id,
3460 component->id,
3461 p->stun_priority,
3462 &mapped,
3463 sockptr,
3464 local_candidate,
3465 remote_candidate);
3466 nice_debug ("Agent %p : added a new peer-reflexive local candidate %p "
3467 "with transport %s", agent, local_cand,
3468 nice_candidate_transport_to_string (local_cand->transport));
3469 }
3470
3471 /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2
3472 "Constructing a Valid Pair") */
3473 if (local_cand)
3474 new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component,
3475 (NiceCandidateImpl *) local_cand, p);
3476 /* note: this is same as "adding to VALID LIST" in the spec
3477 text */
3478 if (new_pair)
3479 new_pair->valid = TRUE;
3480 /* step: The agent sets the state of the pair that *generated* the check to
3481 * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States"
3482 */
3483 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3484 priv_remove_pair_from_triggered_check_queue (agent, p);
3485 priv_free_all_stun_transactions (p, component);
3486 }
3487
3488 if (new_pair && new_pair->valid)
3489 nice_component_add_valid_candidate (agent, component, remote_candidate);
3490
3491
3492 return new_pair;
3493 }
3494
3495 /*
3496 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
3497 * check transaction. If found, the reply is processed. Implements
3498 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
3499 *
3500 * @return TRUE if a matching transaction is found
3501 */
priv_map_reply_to_conn_check_request(NiceAgent * agent,NiceStream * stream,NiceComponent * component,NiceSocket * sockptr,const NiceAddress * from,NiceCandidate * local_candidate,NiceCandidate * remote_candidate,StunMessage * resp)3502 static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *sockptr, const NiceAddress *from, NiceCandidate *local_candidate, NiceCandidate *remote_candidate, StunMessage *resp)
3503 {
3504 union {
3505 struct sockaddr_storage storage;
3506 struct sockaddr addr;
3507 } sockaddr;
3508 socklen_t socklen = sizeof (sockaddr);
3509 GSList *i, *j;
3510 guint k;
3511 StunUsageIceReturn res;
3512 StunTransactionId discovery_id;
3513 StunTransactionId response_id;
3514 stun_message_id (resp, response_id);
3515
3516 for (i = stream->conncheck_list; i; i = i->next) {
3517 CandidateCheckPair *p = i->data;
3518
3519 for (j = p->stun_transactions, k = 0; j; j = j->next, k++) {
3520 StunTransaction *stun = j->data;
3521
3522 stun_message_id (&stun->message, discovery_id);
3523
3524 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)))
3525 continue;
3526
3527 res = stun_usage_ice_conncheck_process (resp,
3528 &sockaddr.storage, &socklen,
3529 agent_to_ice_compatibility (agent));
3530 nice_debug ("Agent %p : stun_bind_process/conncheck for %p: "
3531 "%s,res=%s,stun#=%d.",
3532 agent, p,
3533 agent->controlling_mode ? "controlling" : "controlled",
3534 priv_ice_return_to_string (res), k);
3535
3536 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
3537 res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
3538 /* case: found a matching connectivity check request */
3539
3540 CandidateCheckPair *ok_pair = NULL;
3541
3542 nice_debug ("Agent %p : pair %p MATCHED.", agent, p);
3543 priv_remove_stun_transaction (p, stun, component);
3544
3545 /* step: verify that response came from the same IP address we
3546 * sent the original request to (see 7.1.2.1. "Failure
3547 * Cases") */
3548 if (nice_address_equal (from, &p->remote->addr) == FALSE) {
3549 candidate_check_pair_fail (stream, agent, p);
3550 if (nice_debug_is_enabled ()) {
3551 gchar tmpbuf[INET6_ADDRSTRLEN];
3552 gchar tmpbuf2[INET6_ADDRSTRLEN];
3553 nice_debug ("Agent %p : pair %p FAILED"
3554 " (mismatch of source address).", agent, p);
3555 nice_address_to_string (&p->remote->addr, tmpbuf);
3556 nice_address_to_string (from, tmpbuf2);
3557 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
3558 tmpbuf, nice_address_get_port (&p->remote->addr),
3559 tmpbuf2, nice_address_get_port (from));
3560 }
3561 conn_check_update_check_list_state_for_ready (agent,
3562 stream, component);
3563 return TRUE;
3564 }
3565
3566 if (remote_candidate == NULL) {
3567 candidate_check_pair_fail (stream, agent, p);
3568 if (nice_debug_is_enabled ()) {
3569 nice_debug ("Agent %p : pair %p FAILED "
3570 "(got a matching pair without a known remote candidate).", agent, p);
3571 }
3572 conn_check_update_check_list_state_for_ready (agent,
3573 stream, component);
3574 return TRUE;
3575 }
3576
3577 /* note: CONNECTED but not yet READY, see docs */
3578
3579 /* step: handle the possible case of a peer-reflexive
3580 * candidate where the mapped-address in response does
3581 * not match any local candidate, see 7.1.2.2.1
3582 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
3583
3584 if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
3585 nice_debug ("Agent %p : Mapped address not found", agent);
3586 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3587 p->valid = TRUE;
3588 nice_component_add_valid_candidate (agent, component, p->remote);
3589 } else
3590 ok_pair = priv_process_response_check_for_reflexive (agent,
3591 stream, component, p, sockptr, &sockaddr.addr,
3592 local_candidate, remote_candidate);
3593
3594 /* note: The success of this check might also
3595 * cause the state of other checks to change as well
3596 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of
3597 * ICE spec (RFC8445).
3598 */
3599 conn_check_unfreeze_related (agent, p);
3600
3601 /* Note: this assignment helps to reduce the numbers of cases
3602 * to be tested. If ok_pair and p refer to distinct pairs, it
3603 * means that ok_pair is a discovered peer reflexive one,
3604 * caused by the check made on pair p. In that case, the
3605 * flags to be tested are on p, but the nominated flag will be
3606 * set on ok_pair. When there's no discovered pair, p and
3607 * ok_pair refer to the same pair.
3608 * To summarize : p is a SUCCEEDED pair, ok_pair is a
3609 * DISCOVERED, VALID, and eventually NOMINATED pair.
3610 */
3611 if (!ok_pair)
3612 ok_pair = p;
3613
3614 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
3615 Nominated Flag" (ID-19) */
3616 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
3617 nice_debug ("Agent %p : Updating nominated flag (%s): "
3618 "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)",
3619 agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ?
3620 "UDP" : "TCP",
3621 ok_pair, ok_pair->use_candidate_on_next_check,
3622 ok_pair->mark_nominated_on_response_arrival,
3623 p, p->use_candidate_on_next_check,
3624 p->mark_nominated_on_response_arrival);
3625
3626 if (agent->controlling_mode) {
3627 switch (agent->nomination_mode) {
3628 case NICE_NOMINATION_MODE_REGULAR:
3629 if (p->use_candidate_on_next_check) {
3630 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3631 "(regular nomination, controlling, "
3632 "use_cand_on_next_check=1).",
3633 agent, ok_pair, ok_pair->foundation);
3634 ok_pair->nominated = TRUE;
3635 }
3636 break;
3637 case NICE_NOMINATION_MODE_AGGRESSIVE:
3638 if (!p->nominated) {
3639 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3640 "(aggressive nomination, controlling).",
3641 agent, ok_pair, ok_pair->foundation);
3642 ok_pair->nominated = TRUE;
3643 }
3644 break;
3645 default:
3646 /* Nothing to do */
3647 break;
3648 }
3649 } else {
3650 if (p->mark_nominated_on_response_arrival) {
3651 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3652 "(%s nomination, controlled, mark_on_response=1).",
3653 agent, ok_pair, ok_pair->foundation,
3654 agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
3655 "aggressive" : "regular");
3656 ok_pair->nominated = TRUE;
3657 }
3658 }
3659 }
3660
3661 if (ok_pair->nominated == TRUE) {
3662 conn_check_update_selected_pair (agent, component, ok_pair);
3663 priv_print_conn_check_lists (agent, G_STRFUNC,
3664 ", got a nominated pair");
3665
3666 /* Do not step down to CONNECTED if we're already at state READY*/
3667 if (component->state != NICE_COMPONENT_STATE_READY)
3668 /* step: notify the client of a new component state (must be done
3669 * before the possible check list state update step */
3670 agent_signal_component_state_change (agent,
3671 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
3672 }
3673
3674 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
3675 states" and 8.1.2 "Updating States", ID-19) */
3676 conn_check_update_check_list_state_for_ready (agent, stream, component);
3677 } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
3678 uint64_t tie;
3679 gboolean controlled_mode;
3680
3681 if (!p->retransmit) {
3682 nice_debug ("Agent %p : Role conflict with pair %p, not restarting",
3683 agent, p);
3684 return TRUE;
3685 }
3686
3687 /* case: role conflict error, need to restart with new role */
3688 nice_debug ("Agent %p : Role conflict with pair %p, restarting",
3689 agent, p);
3690
3691 /* note: this res value indicates that the role of the peer
3692 * agent has not changed after the tie-breaker comparison, so
3693 * this is our role that must change. see ICE sect. 7.1.3.1
3694 * "Failure Cases". Our role might already have changed due to
3695 * an earlier incoming request, but if not, change role now.
3696 *
3697 * Sect. 7.1.3.1 is not clear on this point, but we choose to
3698 * put the candidate pair in the triggered check list even
3699 * when the agent did not switch its role. The reason for this
3700 * interpretation is that the reception of the stun reply, even
3701 * an error reply, is a good sign that this pair will be
3702 * valid, if we retry the check after the role of both peers
3703 * has been fixed.
3704 */
3705 controlled_mode = (stun_message_find64 (&stun->message,
3706 STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) ==
3707 STUN_MESSAGE_RETURN_SUCCESS);
3708
3709 priv_check_for_role_conflict (agent, controlled_mode);
3710 priv_remove_stun_transaction (p, stun, component);
3711 priv_add_pair_to_triggered_check_queue (agent, p);
3712 } else {
3713 /* case: STUN error, the check STUN context was freed */
3714 candidate_check_pair_fail (stream, agent, p);
3715 conn_check_update_check_list_state_for_ready (agent, stream, component);
3716 }
3717 return TRUE;
3718 }
3719 }
3720
3721 return FALSE;
3722 }
3723
3724 /*
3725 * Tries to match STUN reply in 'buf' to an existing STUN discovery
3726 * transaction. If found, a reply is sent.
3727 *
3728 * @return TRUE if a matching transaction is found
3729 */
priv_map_reply_to_discovery_request(NiceAgent * agent,StunMessage * resp)3730 static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessage *resp)
3731 {
3732 union {
3733 struct sockaddr_storage storage;
3734 struct sockaddr addr;
3735 } sockaddr;
3736 socklen_t socklen = sizeof (sockaddr);
3737
3738 union {
3739 struct sockaddr_storage storage;
3740 struct sockaddr addr;
3741 } alternate;
3742 socklen_t alternatelen = sizeof (sockaddr);
3743
3744 GSList *i;
3745 StunUsageBindReturn res;
3746 gboolean trans_found = FALSE;
3747 StunTransactionId discovery_id;
3748 StunTransactionId response_id;
3749 stun_message_id (resp, response_id);
3750
3751 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
3752 CandidateDiscovery *d = i->data;
3753
3754 if (d->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE &&
3755 d->stun_message.buffer) {
3756 stun_message_id (&d->stun_message, discovery_id);
3757
3758 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
3759 res = stun_usage_bind_process (resp, &sockaddr.addr,
3760 &socklen, &alternate.addr, &alternatelen);
3761 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
3762 agent, d, (int)res);
3763
3764 if (res == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
3765 /* handle alternate server */
3766 NiceAddress niceaddr;
3767 nice_address_set_from_sockaddr (&niceaddr, &alternate.addr);
3768 d->server = niceaddr;
3769
3770 d->pending = FALSE;
3771 agent->discovery_unsched_items++;
3772 } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
3773 /* case: successful binding discovery, create a new local candidate */
3774
3775 if (!agent->force_relay) {
3776 NiceAddress niceaddr;
3777
3778 nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr);
3779 discovery_add_server_reflexive_candidate (
3780 agent,
3781 d->stream_id,
3782 d->component_id,
3783 &niceaddr,
3784 NICE_CANDIDATE_TRANSPORT_UDP,
3785 d->nicesock,
3786 FALSE);
3787 if (agent->use_ice_tcp)
3788 discovery_discover_tcp_server_reflexive_candidates (
3789 agent,
3790 d->stream_id,
3791 d->component_id,
3792 &niceaddr,
3793 d->nicesock);
3794 }
3795 d->stun_message.buffer = NULL;
3796 d->stun_message.buffer_len = 0;
3797 d->done = TRUE;
3798 trans_found = TRUE;
3799 } else if (res == STUN_USAGE_BIND_RETURN_ERROR) {
3800 /* case: STUN error, the check STUN context was freed */
3801 d->stun_message.buffer = NULL;
3802 d->stun_message.buffer_len = 0;
3803 d->done = TRUE;
3804 trans_found = TRUE;
3805 }
3806 }
3807 }
3808 }
3809
3810 return trans_found;
3811 }
3812
3813 static guint
priv_calc_turn_timeout(guint lifetime)3814 priv_calc_turn_timeout (guint lifetime)
3815 {
3816 if (lifetime > 120)
3817 return lifetime - 60;
3818 else
3819 return lifetime / 2;
3820 }
3821
3822 static void
priv_add_new_turn_refresh(NiceAgent * agent,CandidateDiscovery * cdisco,NiceCandidateImpl * relay_cand,guint lifetime)3823 priv_add_new_turn_refresh (NiceAgent *agent, CandidateDiscovery *cdisco,
3824 NiceCandidateImpl *relay_cand, guint lifetime)
3825 {
3826 CandidateRefresh *cand;
3827
3828 if (cdisco->turn->type == NICE_RELAY_TYPE_TURN_TLS &&
3829 (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
3830 agent->compatibility == NICE_COMPATIBILITY_OC2007R2))
3831 return;
3832
3833 cand = g_slice_new0 (CandidateRefresh);
3834 agent->refresh_list = g_slist_append (agent->refresh_list, cand);
3835
3836 cand->candidate = relay_cand;
3837 cand->nicesock = cdisco->nicesock;
3838 cand->server = cdisco->server;
3839 cand->stream_id = cdisco->stream_id;
3840 cand->component_id = cdisco->component_id;
3841 memcpy (&cand->stun_agent, &cdisco->stun_agent, sizeof(StunAgent));
3842
3843 /* Use previous stun response for authentication credentials */
3844 if (cdisco->stun_resp_msg.buffer != NULL) {
3845 memcpy(cand->stun_resp_buffer, cdisco->stun_resp_buffer,
3846 sizeof(cand->stun_resp_buffer));
3847 memcpy(&cand->stun_resp_msg, &cdisco->stun_resp_msg, sizeof(StunMessage));
3848 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
3849 cand->stun_resp_msg.agent = &cand->stun_agent;
3850 cand->stun_resp_msg.key = NULL;
3851 }
3852
3853 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
3854 agent, cand, priv_calc_turn_timeout (lifetime));
3855 /* step: also start the refresh timer */
3856 /* refresh should be sent 1 minute before it expires */
3857 agent_timeout_add_seconds_with_context (agent, &cand->timer_source,
3858 "Candidate TURN refresh",
3859 priv_calc_turn_timeout (lifetime),
3860 priv_turn_allocate_refresh_tick_agent_locked, cand);
3861
3862 nice_debug ("timer source is : %p", cand->timer_source);
3863
3864 return;
3865 }
3866
priv_handle_turn_alternate_server(NiceAgent * agent,CandidateDiscovery * disco,NiceAddress server,NiceAddress alternate)3867 static void priv_handle_turn_alternate_server (NiceAgent *agent,
3868 CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate)
3869 {
3870 /* We need to cancel and reset all candidate discovery turn for the same
3871 stream and type if there is an alternate server. Otherwise, we might end up
3872 with two relay components on different servers, creating candidates with
3873 unique foundations that only contain one component.
3874 */
3875 GSList *i;
3876
3877 for (i = agent->discovery_list; i; i = i->next) {
3878 CandidateDiscovery *d = i->data;
3879
3880 if (!d->done &&
3881 d->type == disco->type &&
3882 d->stream_id == disco->stream_id &&
3883 d->turn->type == disco->turn->type &&
3884 nice_address_equal (&d->server, &server)) {
3885 gchar ip[INET6_ADDRSTRLEN];
3886 // Cancel the pending request to avoid a race condition with another
3887 // component responding with another altenrate-server
3888 d->stun_message.buffer = NULL;
3889 d->stun_message.buffer_len = 0;
3890
3891 nice_address_to_string (&server, ip);
3892 nice_debug ("Agent %p : Cancelling and setting alternate server %s for "
3893 "CandidateDiscovery %p on s%d/c%d", agent, ip, d,
3894 d->stream_id, d->component_id);
3895 d->server = alternate;
3896 d->turn->server = alternate;
3897 d->pending = FALSE;
3898 agent->discovery_unsched_items++;
3899
3900 if (d->turn->type == NICE_RELAY_TYPE_TURN_TCP ||
3901 d->turn->type == NICE_RELAY_TYPE_TURN_TLS) {
3902 NiceStream *stream;
3903 NiceComponent *component;
3904
3905 if (!agent_find_component (agent, d->stream_id, d->component_id,
3906 &stream, &component)) {
3907 nice_debug ("Could not find stream or component in "
3908 "priv_handle_turn_alternate_server");
3909 continue;
3910 }
3911 d->nicesock = agent_create_tcp_turn_socket (agent, stream, component,
3912 d->nicesock, &d->server, d->turn->type,
3913 nice_socket_is_reliable (d->nicesock));
3914
3915 nice_component_attach_socket (component, d->nicesock);
3916 }
3917 }
3918 }
3919 }
3920
3921 /*
3922 * Tries to match STUN reply in 'buf' to an existing STUN discovery
3923 * transaction. If found, a reply is sent.
3924 *
3925 * @return TRUE if a matching transaction is found
3926 */
priv_map_reply_to_relay_request(NiceAgent * agent,StunMessage * resp)3927 static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *resp)
3928 {
3929 union {
3930 struct sockaddr_storage storage;
3931 struct sockaddr addr;
3932 } sockaddr;
3933 socklen_t socklen = sizeof (sockaddr);
3934
3935 union {
3936 struct sockaddr_storage storage;
3937 struct sockaddr addr;
3938 } alternate;
3939 socklen_t alternatelen = sizeof (alternate);
3940
3941 union {
3942 struct sockaddr_storage storage;
3943 struct sockaddr addr;
3944 } relayaddr;
3945 socklen_t relayaddrlen = sizeof (relayaddr);
3946
3947 uint32_t lifetime;
3948 uint32_t bandwidth;
3949 GSList *i;
3950 StunUsageTurnReturn res;
3951 gboolean trans_found = FALSE;
3952 StunTransactionId discovery_id;
3953 StunTransactionId response_id;
3954 stun_message_id (resp, response_id);
3955
3956 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
3957 CandidateDiscovery *d = i->data;
3958
3959 if (d->type == NICE_CANDIDATE_TYPE_RELAYED &&
3960 d->stun_message.buffer) {
3961 stun_message_id (&d->stun_message, discovery_id);
3962
3963 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
3964 res = stun_usage_turn_process (resp,
3965 &relayaddr.storage, &relayaddrlen,
3966 &sockaddr.storage, &socklen,
3967 &alternate.storage, &alternatelen,
3968 &bandwidth, &lifetime, agent_to_turn_compatibility (agent));
3969 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
3970 agent, d, (int)res);
3971
3972 if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
3973 NiceAddress addr;
3974
3975 /* handle alternate server */
3976 nice_address_set_from_sockaddr (&addr, &alternate.addr);
3977 priv_handle_turn_alternate_server (agent, d, d->server, addr);
3978 trans_found = TRUE;
3979 } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
3980 res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
3981 /* case: successful allocate, create a new local candidate */
3982 NiceAddress niceaddr;
3983 NiceCandidateImpl *relay_cand;
3984
3985 nice_address_set_from_sockaddr (&niceaddr, &relayaddr.addr);
3986
3987 if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
3988 NiceAddress mappedniceaddr;
3989
3990 /* We also received our mapped address */
3991 nice_address_set_from_sockaddr (&mappedniceaddr, &sockaddr.addr);
3992
3993 /* TCP or TLS TURNS means the server-reflexive address was
3994 * on a TCP connection, which cannot be used for server-reflexive
3995 * discovery of candidates.
3996 */
3997 if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP &&
3998 !agent->force_relay) {
3999 discovery_add_server_reflexive_candidate (
4000 agent,
4001 d->stream_id,
4002 d->component_id,
4003 &mappedniceaddr,
4004 NICE_CANDIDATE_TRANSPORT_UDP,
4005 d->nicesock,
4006 FALSE);
4007 }
4008 if (agent->use_ice_tcp) {
4009 if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4010 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4011 !nice_address_equal_no_port (&niceaddr, &d->turn->server)) {
4012 nice_debug("TURN port got allocated on an alternate server, "
4013 "ignoring bogus srflx address");
4014 } else {
4015 discovery_discover_tcp_server_reflexive_candidates (
4016 agent,
4017 d->stream_id,
4018 d->component_id,
4019 &mappedniceaddr,
4020 d->nicesock);
4021 }
4022 }
4023 }
4024
4025 if (nice_socket_is_reliable (d->nicesock)) {
4026 relay_cand = discovery_add_relay_candidate (
4027 agent,
4028 d->stream_id,
4029 d->component_id,
4030 &niceaddr,
4031 NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE,
4032 d->nicesock,
4033 d->turn);
4034
4035 if (relay_cand) {
4036 if (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4037 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
4038 nice_udp_turn_socket_set_ms_realm(relay_cand->sockptr,
4039 &d->stun_message);
4040 nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
4041 resp);
4042 }
4043 priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
4044 }
4045
4046 relay_cand = discovery_add_relay_candidate (
4047 agent,
4048 d->stream_id,
4049 d->component_id,
4050 &niceaddr,
4051 NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE,
4052 d->nicesock,
4053 d->turn);
4054 } else {
4055 relay_cand = discovery_add_relay_candidate (
4056 agent,
4057 d->stream_id,
4058 d->component_id,
4059 &niceaddr,
4060 NICE_CANDIDATE_TRANSPORT_UDP,
4061 d->nicesock,
4062 d->turn);
4063 }
4064
4065 if (relay_cand) {
4066 if (d->stun_resp_msg.buffer)
4067 nice_udp_turn_socket_cache_realm_nonce (relay_cand->sockptr,
4068 &d->stun_resp_msg);
4069 if (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4070 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
4071 /* These data are needed on TURN socket when sending requests,
4072 * but never reach nice_turn_socket_parse_recv() where it could
4073 * be read directly, as the socket does not exist when allocate
4074 * response arrives to _nice_agent_recv(). We must set them right
4075 * after socket gets created in discovery_add_relay_candidate(),
4076 * so we are doing it here. */
4077 nice_udp_turn_socket_set_ms_realm(relay_cand->sockptr,
4078 &d->stun_message);
4079 nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
4080 resp);
4081 }
4082 priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
4083
4084 /* In case a new candidate has been added */
4085 conn_check_schedule_next (agent);
4086 }
4087
4088 d->stun_message.buffer = NULL;
4089 d->stun_message.buffer_len = 0;
4090 d->done = TRUE;
4091 trans_found = TRUE;
4092 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
4093 int code = -1;
4094 uint8_t *sent_realm = NULL;
4095 uint8_t *recv_realm = NULL;
4096 uint16_t sent_realm_len = 0;
4097 uint16_t recv_realm_len = 0;
4098
4099 sent_realm = (uint8_t *) stun_message_find (&d->stun_message,
4100 STUN_ATTRIBUTE_REALM, &sent_realm_len);
4101 recv_realm = (uint8_t *) stun_message_find (resp,
4102 STUN_ATTRIBUTE_REALM, &recv_realm_len);
4103
4104 if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4105 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4106 alternatelen != sizeof(alternate)) {
4107 NiceAddress addr;
4108
4109 nice_address_set_from_sockaddr (&addr, &alternate.addr);
4110
4111 if (!nice_address_equal (&addr, &d->server)) {
4112 priv_handle_turn_alternate_server (agent, d, d->server, addr);
4113 }
4114 }
4115 /* check for unauthorized error response */
4116 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
4117 agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4118 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4119 stun_message_get_class (resp) == STUN_ERROR &&
4120 stun_message_find_error (resp, &code) ==
4121 STUN_MESSAGE_RETURN_SUCCESS &&
4122 recv_realm != NULL && recv_realm_len > 0) {
4123
4124 if (code == STUN_ERROR_STALE_NONCE ||
4125 (code == STUN_ERROR_UNAUTHORIZED &&
4126 !(recv_realm_len == sent_realm_len &&
4127 sent_realm != NULL &&
4128 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
4129 d->stun_resp_msg = *resp;
4130 memcpy (d->stun_resp_buffer, resp->buffer,
4131 stun_message_length (resp));
4132 d->stun_resp_msg.buffer = d->stun_resp_buffer;
4133 d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer);
4134 d->pending = FALSE;
4135 agent->discovery_unsched_items++;
4136 } else {
4137 /* case: a real unauthorized error */
4138 d->stun_message.buffer = NULL;
4139 d->stun_message.buffer_len = 0;
4140 d->done = TRUE;
4141 }
4142 } else if (d->pending) {
4143 /* case: STUN error, the check STUN context was freed */
4144 d->stun_message.buffer = NULL;
4145 d->stun_message.buffer_len = 0;
4146 d->done = TRUE;
4147 }
4148 trans_found = TRUE;
4149 }
4150 }
4151 }
4152 }
4153
4154 return trans_found;
4155 }
4156
4157
4158 /*
4159 * Tries to match STUN reply in 'buf' to an existing STUN discovery
4160 * transaction. If found, a reply is sent.
4161 *
4162 * @return TRUE if a matching transaction is found
4163 */
priv_map_reply_to_relay_refresh(NiceAgent * agent,StunMessage * resp)4164 static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *resp)
4165 {
4166 uint32_t lifetime;
4167 GSList *i;
4168 StunUsageTurnReturn res;
4169 gboolean trans_found = FALSE;
4170 StunTransactionId refresh_id;
4171 StunTransactionId response_id;
4172 stun_message_id (resp, response_id);
4173
4174 for (i = agent->refresh_list; i && trans_found != TRUE;) {
4175 CandidateRefresh *cand = i->data;
4176 GSList *next = i->next;
4177
4178 if (!cand->disposing && cand->stun_message.buffer) {
4179 stun_message_id (&cand->stun_message, refresh_id);
4180
4181 if (memcmp (refresh_id, response_id, sizeof(StunTransactionId)) == 0) {
4182 res = stun_usage_turn_refresh_process (resp,
4183 &lifetime, agent_to_turn_compatibility (agent));
4184 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d with lifetime %u.",
4185 agent, cand, (int)res, lifetime);
4186 if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS) {
4187 /* refresh should be sent 1 minute before it expires */
4188 agent_timeout_add_seconds_with_context (agent,
4189 &cand->timer_source,
4190 "Candidate TURN refresh", priv_calc_turn_timeout (lifetime),
4191 priv_turn_allocate_refresh_tick_agent_locked, cand);
4192
4193 g_source_destroy (cand->tick_source);
4194 g_source_unref (cand->tick_source);
4195 cand->tick_source = NULL;
4196 trans_found = TRUE;
4197 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
4198 int code = -1;
4199 uint8_t *sent_realm = NULL;
4200 uint8_t *recv_realm = NULL;
4201 uint16_t sent_realm_len = 0;
4202 uint16_t recv_realm_len = 0;
4203
4204 sent_realm = (uint8_t *) stun_message_find (&cand->stun_message,
4205 STUN_ATTRIBUTE_REALM, &sent_realm_len);
4206 recv_realm = (uint8_t *) stun_message_find (resp,
4207 STUN_ATTRIBUTE_REALM, &recv_realm_len);
4208
4209 /* check for unauthorized error response */
4210 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
4211 stun_message_get_class (resp) == STUN_ERROR &&
4212 stun_message_find_error (resp, &code) ==
4213 STUN_MESSAGE_RETURN_SUCCESS &&
4214 recv_realm != NULL && recv_realm_len > 0) {
4215
4216 if (code == STUN_ERROR_STALE_NONCE ||
4217 (code == STUN_ERROR_UNAUTHORIZED &&
4218 !(recv_realm_len == sent_realm_len &&
4219 sent_realm != NULL &&
4220 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
4221 cand->stun_resp_msg = *resp;
4222 memcpy (cand->stun_resp_buffer, resp->buffer,
4223 stun_message_length (resp));
4224 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
4225 cand->stun_resp_msg.buffer_len = sizeof(cand->stun_resp_buffer);
4226 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
4227 } else {
4228 /* case: a real unauthorized error */
4229 refresh_free (agent, cand);
4230 }
4231 } else {
4232 /* case: STUN error, the check STUN context was freed */
4233 refresh_free (agent, cand);
4234 }
4235 trans_found = TRUE;
4236 }
4237 }
4238 }
4239 i = next;
4240 }
4241
4242 return trans_found;
4243 }
4244
priv_map_reply_to_relay_remove(NiceAgent * agent,StunMessage * resp)4245 static gboolean priv_map_reply_to_relay_remove (NiceAgent *agent,
4246 StunMessage *resp)
4247 {
4248 StunTransactionId response_id;
4249 GSList *i;
4250
4251 stun_message_id (resp, response_id);
4252
4253 for (i = agent->refresh_list; i; i = i->next) {
4254 CandidateRefresh *cand = i->data;
4255 StunTransactionId request_id;
4256 StunUsageTurnReturn res;
4257 uint32_t lifetime;
4258
4259 if (!cand->disposing || !cand->stun_message.buffer) {
4260 continue;
4261 }
4262
4263 stun_message_id (&cand->stun_message, request_id);
4264
4265 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
4266 res = stun_usage_turn_refresh_process (resp, &lifetime,
4267 agent_to_turn_compatibility (agent));
4268
4269 nice_debug ("Agent %p : priv_map_reply_to_relay_remove for %p res %d "
4270 "with lifetime %u.", agent, cand, res, lifetime);
4271
4272 if (res != STUN_USAGE_TURN_RETURN_INVALID) {
4273 refresh_free (agent, cand);
4274 return TRUE;
4275 }
4276 }
4277 }
4278
4279 return FALSE;
4280 }
4281
priv_map_reply_to_keepalive_conncheck(NiceAgent * agent,NiceComponent * component,StunMessage * resp)4282 static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
4283 NiceComponent *component, StunMessage *resp)
4284 {
4285 StunTransactionId conncheck_id;
4286 StunTransactionId response_id;
4287 stun_message_id (resp, response_id);
4288
4289 if (component->selected_pair.keepalive.stun_message.buffer) {
4290 stun_message_id (&component->selected_pair.keepalive.stun_message,
4291 conncheck_id);
4292 if (memcmp (conncheck_id, response_id, sizeof(StunTransactionId)) == 0) {
4293 nice_debug ("Agent %p : Keepalive for selected pair received.",
4294 agent);
4295 if (component->selected_pair.keepalive.tick_source) {
4296 g_source_destroy (component->selected_pair.keepalive.tick_source);
4297 g_source_unref (component->selected_pair.keepalive.tick_source);
4298 component->selected_pair.keepalive.tick_source = NULL;
4299 }
4300 component->selected_pair.keepalive.stun_message.buffer = NULL;
4301 return TRUE;
4302 }
4303 }
4304
4305 return FALSE;
4306 }
4307
4308
4309 typedef struct {
4310 NiceAgent *agent;
4311 NiceStream *stream;
4312 NiceComponent *component;
4313 uint8_t *password;
4314 } conncheck_validater_data;
4315
conncheck_stun_validater(StunAgent * agent,StunMessage * message,uint8_t * username,uint16_t username_len,uint8_t ** password,size_t * password_len,void * user_data)4316 static bool conncheck_stun_validater (StunAgent *agent,
4317 StunMessage *message, uint8_t *username, uint16_t username_len,
4318 uint8_t **password, size_t *password_len, void *user_data)
4319 {
4320 conncheck_validater_data *data = (conncheck_validater_data*) user_data;
4321 GSList *i;
4322 gchar *ufrag = NULL;
4323 gsize ufrag_len;
4324
4325 gboolean msn_msoc_nice_compatibility =
4326 data->agent->compatibility == NICE_COMPATIBILITY_MSN ||
4327 data->agent->compatibility == NICE_COMPATIBILITY_OC2007;
4328
4329 if (data->agent->compatibility == NICE_COMPATIBILITY_OC2007 &&
4330 stun_message_get_class (message) == STUN_RESPONSE)
4331 i = data->component->remote_candidates;
4332 else
4333 i = data->component->local_candidates;
4334
4335 for (; i; i = i->next) {
4336 NiceCandidate *cand = i->data;
4337
4338 ufrag = NULL;
4339 if (cand->username)
4340 ufrag = cand->username;
4341 else
4342 ufrag = data->stream->local_ufrag;
4343 ufrag_len = ufrag? strlen (ufrag) : 0;
4344
4345 if (ufrag && msn_msoc_nice_compatibility)
4346 ufrag = (gchar *)g_base64_decode (ufrag, &ufrag_len);
4347
4348 if (ufrag == NULL)
4349 continue;
4350
4351 stun_debug ("Comparing username/ufrag of len %d and %" G_GSIZE_FORMAT ", equal=%d",
4352 username_len, ufrag_len, username_len >= ufrag_len ?
4353 memcmp (username, ufrag, ufrag_len) : 0);
4354 stun_debug_bytes (" username: ", username, username_len);
4355 stun_debug_bytes (" ufrag: ", ufrag, ufrag_len);
4356 if (ufrag_len > 0 && username_len >= ufrag_len &&
4357 memcmp (username, ufrag, ufrag_len) == 0) {
4358 gchar *pass = NULL;
4359
4360 if (cand->password)
4361 pass = cand->password;
4362 else if (data->stream && data->stream->local_password[0])
4363 pass = data->stream->local_password;
4364
4365 if (pass) {
4366 *password = (uint8_t *) pass;
4367 *password_len = strlen (pass);
4368
4369 if (msn_msoc_nice_compatibility) {
4370 gsize pass_len;
4371
4372 data->password = g_base64_decode (pass, &pass_len);
4373 *password = data->password;
4374 *password_len = pass_len;
4375 }
4376 }
4377
4378 if (msn_msoc_nice_compatibility)
4379 g_free (ufrag);
4380
4381 stun_debug ("Found valid username, returning password: '%s'", *password);
4382 return TRUE;
4383 }
4384
4385 if (msn_msoc_nice_compatibility)
4386 g_free (ufrag);
4387 }
4388
4389 return FALSE;
4390 }
4391
4392 /*
4393 * handle RENOMINATION stun attribute
4394 * @return TRUE if nomination changed. FALSE otherwise
4395 */
conn_check_handle_renomination(NiceAgent * agent,NiceStream * stream,NiceComponent * component,StunMessage * req,NiceCandidate * remote_candidate,NiceCandidate * local_candidate)4396 static gboolean conn_check_handle_renomination (NiceAgent *agent, NiceStream *stream,
4397 NiceComponent *component, StunMessage *req,
4398 NiceCandidate *remote_candidate, NiceCandidate *local_candidate)
4399 {
4400 GSList *lst;
4401 if (!agent->controlling_mode && NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
4402 agent->support_renomination && remote_candidate && local_candidate)
4403 {
4404 uint32_t nom_value = 0;
4405 uint16_t nom_len = 0;
4406 const void *value = stun_message_find (req, STUN_ATTRIBUTE_NOMINATION, &nom_len);
4407 if (nom_len == 0) {
4408 return FALSE;
4409 }
4410 if (nom_len == 4) {
4411 memcpy (&nom_value, value, 4);
4412 nom_value = ntohl (nom_value);
4413 } else {
4414 nice_debug ("Agent %p : received NOMINATION attr with incorrect octet length %u, expected 4 bytes",
4415 agent, nom_len);
4416 return FALSE;
4417 }
4418
4419 if (nice_debug_is_enabled ()) {
4420 gchar remote_str[INET6_ADDRSTRLEN];
4421 nice_address_to_string(&remote_candidate->addr, remote_str);
4422 nice_debug ("Agent %p : received NOMINATION attr for remote candidate [%s]:%u, value is %u",
4423 agent, remote_str, nice_address_get_port (&remote_candidate->addr), nom_value);
4424 }
4425
4426 /*
4427 * If another pair is SELECTED, change this pair's priority to be greater than
4428 * selected pair's priority so this pair gets SELECTED!
4429 */
4430 if (component->selected_pair.priority &&
4431 component->selected_pair.remote && component->selected_pair.remote != (NiceCandidateImpl *) remote_candidate &&
4432 component->selected_pair.local && component->selected_pair.local != (NiceCandidateImpl *) local_candidate) {
4433 for (lst = stream->conncheck_list; lst; lst = lst->next) {
4434 CandidateCheckPair *pair = lst->data;
4435 if (pair->local == local_candidate && pair->remote == remote_candidate) {
4436 if (pair->valid) {
4437 pair->priority = component->selected_pair.priority + 1;
4438 }
4439 break;
4440 }
4441 }
4442 }
4443 priv_mark_pair_nominated (agent, stream, component, local_candidate, remote_candidate);
4444 return TRUE;
4445 }
4446 return FALSE;
4447 }
4448
4449 /*
4450 * Processing an incoming STUN message.
4451 *
4452 * @param agent self pointer
4453 * @param stream stream the packet is related to
4454 * @param component component the packet is related to
4455 * @param nicesock socket from which the packet was received
4456 * @param from address of the sender
4457 * @param buf message contents
4458 * @param buf message length
4459 *
4460 * @pre contents of 'buf' is a STUN message
4461 *
4462 * @return XXX (what FALSE means exactly?)
4463 */
conn_check_handle_inbound_stun(NiceAgent * agent,NiceStream * stream,NiceComponent * component,NiceSocket * nicesock,const NiceAddress * from,gchar * buf,guint len)4464 gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
4465 NiceComponent *component, NiceSocket *nicesock, const NiceAddress *from,
4466 gchar *buf, guint len)
4467 {
4468 union {
4469 struct sockaddr_storage storage;
4470 struct sockaddr addr;
4471 } sockaddr;
4472 uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
4473 ssize_t res;
4474 size_t rbuf_len = sizeof (rbuf);
4475 bool control = agent->controlling_mode;
4476 uint8_t uname[NICE_STREAM_MAX_UNAME];
4477 guint uname_len;
4478 uint8_t *username;
4479 uint16_t username_len;
4480 StunMessage req;
4481 StunMessage msg;
4482 StunValidationStatus valid;
4483 conncheck_validater_data validater_data = {agent, stream, component, NULL};
4484 GSList *i, *j;
4485 NiceCandidate *remote_candidate = NULL;
4486 NiceCandidate *remote_candidate2 = NULL;
4487 NiceCandidate *local_candidate = NULL;
4488 gboolean discovery_msg = FALSE;
4489
4490 nice_address_copy_to_sockaddr (from, &sockaddr.addr);
4491
4492 /* note: contents of 'buf' already validated, so it is
4493 * a valid and fully received STUN message */
4494
4495 if (nice_debug_is_enabled ()) {
4496 gchar tmpbuf[INET6_ADDRSTRLEN];
4497 nice_address_to_string (from, tmpbuf);
4498 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
4499 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from), len);
4500 }
4501
4502 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
4503
4504 valid = stun_agent_validate (&component->stun_agent, &req,
4505 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4506
4507 /* Check for discovery candidates stun agents */
4508 if (valid == STUN_VALIDATION_BAD_REQUEST ||
4509 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4510 for (i = agent->discovery_list; i; i = i->next) {
4511 CandidateDiscovery *d = i->data;
4512 if (d->stream_id == stream->id && d->component_id == component->id &&
4513 d->nicesock == nicesock) {
4514 valid = stun_agent_validate (&d->stun_agent, &req,
4515 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4516
4517 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
4518 continue;
4519
4520 discovery_msg = TRUE;
4521 break;
4522 }
4523 }
4524 }
4525 /* Check for relay refresh stun agents */
4526 if (valid == STUN_VALIDATION_BAD_REQUEST ||
4527 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4528 for (i = agent->refresh_list; i; i = i->next) {
4529 CandidateRefresh *r = i->data;
4530
4531 nice_debug_verbose ("Comparing r.sid=%u to sid=%u, r.cid=%u to cid=%u and %p and %p to %p",
4532 r->stream_id, stream->id, r->component_id, component->id, r->nicesock,
4533 r->candidate->sockptr, nicesock);
4534
4535 if (r->stream_id == stream->id && r->component_id == component->id &&
4536 (r->nicesock == nicesock || r->candidate->sockptr == nicesock)) {
4537 valid = stun_agent_validate (&r->stun_agent, &req,
4538 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4539 nice_debug ("Validating gave %d", valid);
4540 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
4541 continue;
4542 discovery_msg = TRUE;
4543 break;
4544 }
4545 }
4546 }
4547
4548 g_free (validater_data.password);
4549
4550 if (valid == STUN_VALIDATION_NOT_STUN ||
4551 valid == STUN_VALIDATION_INCOMPLETE_STUN ||
4552 valid == STUN_VALIDATION_BAD_REQUEST)
4553 {
4554 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
4555 agent);
4556 return FALSE;
4557 }
4558
4559 if (valid == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) {
4560 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent);
4561
4562 if (agent->compatibility != NICE_COMPATIBILITY_MSN &&
4563 agent->compatibility != NICE_COMPATIBILITY_OC2007) {
4564 rbuf_len = stun_agent_build_unknown_attributes_error (&component->stun_agent,
4565 &msg, rbuf, rbuf_len, &req);
4566 if (rbuf_len != 0)
4567 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4568 }
4569 return TRUE;
4570 }
4571
4572 if (valid == STUN_VALIDATION_UNAUTHORIZED) {
4573 nice_debug ("Agent %p : Integrity check failed.", agent);
4574
4575 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4576 &req, STUN_ERROR_UNAUTHORIZED)) {
4577 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4578 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4579 agent->compatibility != NICE_COMPATIBILITY_OC2007)
4580 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4581 }
4582 return TRUE;
4583 }
4584 if (valid == STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST) {
4585 nice_debug ("Agent %p : Integrity check failed - bad request.", agent);
4586 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4587 &req, STUN_ERROR_BAD_REQUEST)) {
4588 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4589 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4590 agent->compatibility != NICE_COMPATIBILITY_OC2007)
4591 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4592 }
4593 return TRUE;
4594 }
4595
4596 username = (uint8_t *) stun_message_find (&req, STUN_ATTRIBUTE_USERNAME,
4597 &username_len);
4598
4599 for (i = component->local_candidates; i; i = i->next) {
4600 NiceCandidate *cand = i->data;
4601 NiceAddress *addr;
4602
4603 if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
4604 addr = &cand->addr;
4605 else
4606 addr = &cand->base_addr;
4607
4608 if (nice_address_equal (&nicesock->addr, addr) &&
4609 local_candidate_and_socket_compatible (agent, cand, nicesock)) {
4610 local_candidate = cand;
4611 break;
4612 }
4613 }
4614
4615 for (i = component->remote_candidates; i; i = i->next) {
4616 NiceCandidate *cand = i->data;
4617 if (nice_address_equal (from, &cand->addr) &&
4618 remote_candidate_and_socket_compatible (agent, local_candidate,
4619 cand, nicesock)) {
4620 remote_candidate = cand;
4621 break;
4622 }
4623 }
4624
4625 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
4626 agent->compatibility == NICE_COMPATIBILITY_MSN ||
4627 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4628 /* We need to find which local candidate was used */
4629 for (i = component->remote_candidates;
4630 i != NULL && remote_candidate2 == NULL; i = i->next) {
4631 for (j = component->local_candidates; j; j = j->next) {
4632 gboolean inbound = TRUE;
4633 NiceCandidate *rcand = i->data;
4634 NiceCandidate *lcand = j->data;
4635
4636 /* If we receive a response, then the username is local:remote */
4637 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
4638 if (stun_message_get_class (&req) == STUN_REQUEST ||
4639 stun_message_get_class (&req) == STUN_INDICATION) {
4640 inbound = TRUE;
4641 } else {
4642 inbound = FALSE;
4643 }
4644 }
4645
4646 uname_len = priv_create_username (agent, stream,
4647 component->id, rcand, lcand,
4648 uname, sizeof (uname), inbound);
4649
4650
4651
4652 stun_debug ("Comparing usernames of size %d and %d: %d",
4653 username_len, uname_len, username && uname_len == username_len &&
4654 memcmp (username, uname, uname_len) == 0);
4655 stun_debug_bytes (" First username: ", username,
4656 username ? username_len : 0);
4657 stun_debug_bytes (" Second uname: ", uname, uname_len);
4658
4659 if (username &&
4660 uname_len == username_len &&
4661 memcmp (uname, username, username_len) == 0) {
4662 local_candidate = lcand;
4663 remote_candidate2 = rcand;
4664 break;
4665 }
4666 }
4667 }
4668 }
4669
4670 if (component->remote_candidates &&
4671 agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
4672 local_candidate == NULL &&
4673 discovery_msg == FALSE) {
4674 /* if we couldn't match the username and the stun agent has
4675 IGNORE_CREDENTIALS then we have an integrity check failing.
4676 This could happen with the race condition of receiving connchecks
4677 before the remote candidates are added. Just drop the message, and let
4678 the retransmissions make it work. */
4679 nice_debug ("Agent %p : Username check failed.", agent);
4680 return TRUE;
4681 }
4682
4683 /* This is most likely caused by a second response to a request which
4684 * already has received a valid reply.
4685 */
4686 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4687 nice_debug ("Agent %p : Valid STUN response for which we don't have a request, ignoring", agent);
4688 return TRUE;
4689 }
4690
4691 if (valid != STUN_VALIDATION_SUCCESS) {
4692 nice_debug ("Agent %p : STUN message is unsuccessful %d, ignoring", agent, valid);
4693 return FALSE;
4694 }
4695
4696 agent->media_after_tick = TRUE;
4697
4698 if (stun_message_get_class (&req) == STUN_REQUEST) {
4699 if ( agent->compatibility == NICE_COMPATIBILITY_MSN
4700 || agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4701 if (local_candidate && remote_candidate2) {
4702 gsize key_len;
4703
4704 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
4705 username = (uint8_t *) stun_message_find (&req,
4706 STUN_ATTRIBUTE_USERNAME, &username_len);
4707 uname_len = priv_create_username (agent, stream,
4708 component->id, remote_candidate2, local_candidate,
4709 uname, sizeof (uname), FALSE);
4710 memcpy (username, uname, MIN (uname_len, username_len));
4711
4712 req.key = g_base64_decode ((gchar *) remote_candidate2->password,
4713 &key_len);
4714 req.key_len = key_len;
4715 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4716 req.key = g_base64_decode ((gchar *) local_candidate->password,
4717 &key_len);
4718 req.key_len = key_len;
4719 }
4720 } else {
4721 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
4722 "Ignoring request", agent);
4723 return TRUE;
4724 }
4725 }
4726
4727 rbuf_len = sizeof (rbuf);
4728 res = stun_usage_ice_conncheck_create_reply (&component->stun_agent, &req,
4729 &msg, rbuf, &rbuf_len, &sockaddr.storage, sizeof (sockaddr),
4730 &control, agent->tie_breaker,
4731 agent_to_ice_compatibility (agent));
4732
4733 if ( agent->compatibility == NICE_COMPATIBILITY_MSN
4734 || agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4735 g_free (req.key);
4736 }
4737
4738 if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT)
4739 priv_check_for_role_conflict (agent, control);
4740
4741 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
4742 res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
4743 /* case 1: valid incoming request, send a reply/error */
4744 bool use_candidate =
4745 stun_usage_ice_conncheck_use_candidate (&req);
4746 uint32_t priority = stun_usage_ice_conncheck_priority (&req);
4747
4748 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
4749 agent->compatibility == NICE_COMPATIBILITY_MSN ||
4750 agent->compatibility == NICE_COMPATIBILITY_OC2007)
4751 use_candidate = TRUE;
4752
4753 if (stream->initial_binding_request_received != TRUE)
4754 agent_signal_initial_binding_request_received (agent, stream);
4755
4756 if (remote_candidate == NULL) {
4757 nice_debug ("Agent %p : No matching remote candidate for incoming "
4758 "check -> peer-reflexive candidate.", agent);
4759 remote_candidate = discovery_learn_remote_peer_reflexive_candidate (
4760 agent, stream, component, priority, from, nicesock,
4761 local_candidate,
4762 remote_candidate2 ? remote_candidate2 : remote_candidate);
4763 if(remote_candidate && stream->remote_ufrag[0]) {
4764 if (local_candidate &&
4765 local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
4766 priv_conn_check_add_for_candidate_pair_matched (agent,
4767 stream->id, component, local_candidate, remote_candidate,
4768 NICE_CHECK_WAITING);
4769 else
4770 conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
4771 }
4772 }
4773
4774 nice_component_add_valid_candidate (agent, component, remote_candidate);
4775
4776 priv_reply_to_conn_check (agent, stream, component, local_candidate,
4777 remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate);
4778
4779 if (stream->remote_ufrag[0] == 0) {
4780 /* case: We've got a valid binding request to a local candidate
4781 * but we do not yet know remote credentials.
4782 * As per sect 7.2 of ICE (ID-19), we send a reply
4783 * immediately but postpone all other processing until
4784 * we get information about the remote candidates */
4785
4786 /* step: send a reply immediately but postpone other processing */
4787 priv_store_pending_check (agent, component, from, nicesock,
4788 username, username_len, priority, use_candidate);
4789 priv_print_conn_check_lists (agent, G_STRFUNC, ", icheck stored");
4790 }
4791 } else {
4792 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
4793 agent, strerror(errno));
4794 return FALSE;
4795 }
4796 } else {
4797 /* case 2: not a new request, might be a reply... */
4798 gboolean trans_found = FALSE;
4799
4800 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
4801
4802 /* step: let's try to match the response to an existing check context */
4803 if (trans_found != TRUE)
4804 trans_found = priv_map_reply_to_conn_check_request (agent, stream,
4805 component, nicesock, from, local_candidate, remote_candidate, &req);
4806
4807 /* step: let's try to match the response to an existing discovery */
4808 if (trans_found != TRUE)
4809 trans_found = priv_map_reply_to_discovery_request (agent, &req);
4810
4811 /* step: let's try to match the response to an existing turn allocate */
4812 if (trans_found != TRUE)
4813 trans_found = priv_map_reply_to_relay_request (agent, &req);
4814
4815 /* step: let's try to match the response to an existing turn refresh */
4816 if (trans_found != TRUE)
4817 trans_found = priv_map_reply_to_relay_refresh (agent, &req);
4818
4819 if (trans_found != TRUE)
4820 trans_found = priv_map_reply_to_relay_remove (agent, &req);
4821
4822 /* step: let's try to match the response to an existing keepalive conncheck */
4823 if (trans_found != TRUE)
4824 trans_found = priv_map_reply_to_keepalive_conncheck (agent, component,
4825 &req);
4826
4827 if (trans_found != TRUE)
4828 nice_debug ("Agent %p : Unable to match to an existing transaction, "
4829 "probably a keepalive.", agent);
4830 }
4831
4832 /* RENOMINATION attribute support */
4833 conn_check_handle_renomination(agent, stream, component, &req, remote_candidate, local_candidate);
4834
4835 return TRUE;
4836 }
4837
4838 /* Remove all pointers to the given @sock from the connection checking process.
4839 * These are entirely NiceCandidates pointed to from various places. */
4840 void
conn_check_prune_socket(NiceAgent * agent,NiceStream * stream,NiceComponent * component,NiceSocket * sock)4841 conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component,
4842 NiceSocket *sock)
4843 {
4844 GSList *l;
4845 gboolean pair_failed = FALSE;
4846
4847 if (component->selected_pair.local &&
4848 component->selected_pair.local->sockptr == sock &&
4849 component->state == NICE_COMPONENT_STATE_READY) {
4850 nice_debug ("Agent %p: Selected pair socket %p has been destroyed, "
4851 "declaring failed", agent, sock);
4852 agent_signal_component_state_change (agent,
4853 stream->id, component->id, NICE_COMPONENT_STATE_FAILED);
4854 }
4855
4856 /* Prune from the candidate check pairs. */
4857 for (l = stream->conncheck_list; l != NULL;) {
4858 CandidateCheckPair *p = l->data;
4859 GSList *next = l->next;
4860
4861 if ((p->local != NULL && ((NiceCandidateImpl*) p->local)->sockptr == sock) ||
4862 (p->remote != NULL && ((NiceCandidateImpl*)p->remote)->sockptr == sock) ||
4863 (p->sockptr == sock)) {
4864 nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
4865 agent, p);
4866 candidate_check_pair_fail (stream, agent, p);
4867 candidate_check_pair_free (agent, p);
4868 stream->conncheck_list = g_slist_delete_link (stream->conncheck_list, l);
4869 pair_failed = TRUE;
4870 }
4871
4872 l = next;
4873 }
4874
4875 /* outside of the previous loop, because it may
4876 * remove pairs from the conncheck list
4877 */
4878 if (pair_failed)
4879 conn_check_update_check_list_state_for_ready (agent, stream, component);
4880 }
4881