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 * Dafydd Harries, Collabora Ltd.
26 * Youness Alaoui, Collabora Ltd.
27 * Kai Vehmanen, Nokia
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 candidate.c
42 * @brief ICE candidate functions
43 */
44
45 #ifdef HAVE_CONFIG_H
46 # include <config.h>
47 #else
48 #define NICEAPI_EXPORT
49 #endif
50
51 #include <string.h>
52
53 #include "agent.h"
54 #include "component.h"
55 #include "interfaces.h"
56 #include "candidate-priv.h"
57
58 G_DEFINE_BOXED_TYPE (NiceCandidate, nice_candidate, nice_candidate_copy,
59 nice_candidate_free);
60
61 /* (ICE 4.1.1 "Gathering Candidates") ""Every candidate is a transport
62 * address. It also has a type and a base. Three types are defined and
63 * gathered by this specification - host candidates, server reflexive
64 * candidates, and relayed candidates."" (ID-19) */
65
66 NICEAPI_EXPORT NiceCandidate *
nice_candidate_new(NiceCandidateType type)67 nice_candidate_new (NiceCandidateType type)
68 {
69 NiceCandidateImpl *c;
70
71 c = g_slice_new0 (NiceCandidateImpl);
72 c->c.type = type;
73 return (NiceCandidate *) c;
74 }
75
76 NICEAPI_EXPORT void
nice_candidate_free(NiceCandidate * candidate)77 nice_candidate_free (NiceCandidate *candidate)
78 {
79 NiceCandidateImpl *c = (NiceCandidateImpl *) candidate;
80 /* better way of checking if socket is allocated? */
81
82 if (candidate->username)
83 g_free (candidate->username);
84
85 if (candidate->password)
86 g_free (candidate->password);
87
88 if (c->turn)
89 turn_server_unref (c->turn);
90
91 g_slice_free (NiceCandidateImpl, c);
92 }
93
94
95 guint32
nice_candidate_jingle_priority(NiceCandidate * candidate)96 nice_candidate_jingle_priority (NiceCandidate *candidate)
97 {
98 switch (candidate->type)
99 {
100 case NICE_CANDIDATE_TYPE_HOST: return 1000;
101 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: return 900;
102 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE: return 900;
103 case NICE_CANDIDATE_TYPE_RELAYED: return 500;
104 default: return 0;
105 }
106 }
107
108 guint32
nice_candidate_msn_priority(NiceCandidate * candidate)109 nice_candidate_msn_priority (NiceCandidate *candidate)
110 {
111 switch (candidate->type)
112 {
113 case NICE_CANDIDATE_TYPE_HOST: return 830;
114 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: return 550;
115 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE: return 550;
116 case NICE_CANDIDATE_TYPE_RELAYED: return 450;
117 default: return 0;
118 }
119 }
120
121
122 /*
123 * ICE 4.1.2.1. "Recommended Formula" (ID-19):
124 * returns number between 1 and 0x7effffff
125 */
126 guint32
nice_candidate_ice_priority_full(guint type_preference,guint local_preference,guint component_id)127 nice_candidate_ice_priority_full (
128 // must be ∈ (0, 126) (max 2^7 - 2)
129 guint type_preference,
130 // must be ∈ (0, 65535) (max 2^16 - 1)
131 guint local_preference,
132 // must be ∈ (0, 255) (max 2 ^ 8 - 1)
133 guint component_id)
134 {
135 return (
136 0x1000000 * type_preference +
137 0x100 * local_preference +
138 (0x100 - component_id));
139 }
140
141 static guint16
nice_candidate_ice_local_preference_full(guint direction_preference,guint turn_preference,guint other_preference)142 nice_candidate_ice_local_preference_full (guint direction_preference,
143 guint turn_preference, guint other_preference)
144 {
145 /*
146 * bits 0- 5: other_preference (ip local preference)
147 * 6- 8: turn_preference
148 * 9-12: <unused>
149 * 13-15: direction_preference
150 */
151 g_assert_cmpuint (other_preference, <, NICE_CANDIDATE_MAX_LOCAL_ADDRESSES);
152 g_assert_cmpuint (turn_preference, <, NICE_CANDIDATE_MAX_TURN_SERVERS);
153 g_assert_cmpuint (direction_preference, <, 8);
154
155 return (direction_preference << 13) +
156 (turn_preference << 6) +
157 other_preference;
158 }
159
160 static guint
nice_candidate_ip_local_preference(const NiceCandidate * candidate)161 nice_candidate_ip_local_preference (const NiceCandidate *candidate)
162 {
163 guint preference = 0;
164 gchar ip_string[INET6_ADDRSTRLEN];
165 GList/*<owned gchar*>*/ *ips = NULL;
166 GList/*<unowned gchar*>*/ *iter;
167
168 /* Ensure otherwise identical host candidates with only different IP addresses
169 * (multihomed host) get assigned different priorities. Position of the IP in
170 * the list obtained from nice_interfaces_get_local_ips() serves here as the
171 * distinguishing value of other_preference. Reflexive and relayed candidates
172 * are likewise differentiated by their base address.
173 *
174 * This is required by RFC 5245 Section 4.1.2.1:
175 * https://tools.ietf.org/html/rfc5245#section-4.1.2.1
176 */
177 if (candidate->type == NICE_CANDIDATE_TYPE_HOST) {
178 nice_address_to_string (&candidate->addr, ip_string);
179 } else {
180 nice_address_to_string (&candidate->base_addr, ip_string);
181 }
182
183 ips = nice_interfaces_get_local_ips (TRUE);
184
185 for (iter = ips; iter; iter = g_list_next (iter)) {
186 /* Strip the IPv6 link-local scope string */
187 gchar **tokens = g_strsplit (iter->data, "%", 2);
188 gboolean match = (g_strcmp0 (ip_string, tokens[0]) == 0);
189 g_strfreev (tokens);
190 if (match)
191 break;
192 ++preference;
193 }
194
195 g_list_free_full (ips, g_free);
196
197 return preference;
198 }
199
200 static guint16
nice_candidate_ice_local_preference(const NiceCandidate * candidate)201 nice_candidate_ice_local_preference (const NiceCandidate *candidate)
202 {
203 const NiceCandidateImpl *c = (NiceCandidateImpl *) candidate;
204 guint direction_preference = 0;
205 guint turn_preference = 0;
206
207 switch (candidate->transport)
208 {
209 case NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
210 if (candidate->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
211 candidate->type == NICE_CANDIDATE_TYPE_HOST)
212 direction_preference = 4;
213 else
214 direction_preference = 6;
215 break;
216 case NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
217 if (candidate->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
218 candidate->type == NICE_CANDIDATE_TYPE_HOST)
219 direction_preference = 2;
220 else
221 direction_preference = 4;
222 break;
223 case NICE_CANDIDATE_TRANSPORT_TCP_SO:
224 if (candidate->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
225 candidate->type == NICE_CANDIDATE_TYPE_HOST)
226 direction_preference = 6;
227 else
228 direction_preference = 2;
229 break;
230 case NICE_CANDIDATE_TRANSPORT_UDP:
231 default:
232 direction_preference = 1;
233 break;
234 }
235
236 /* Relay candidates are assigned a unique local preference at
237 * creation time.
238 */
239 if (candidate->type == NICE_CANDIDATE_TYPE_RELAYED) {
240 g_assert (c->turn);
241 turn_preference = c->turn->preference;
242 }
243
244 return nice_candidate_ice_local_preference_full (direction_preference,
245 turn_preference, nice_candidate_ip_local_preference (candidate));
246 }
247
248 static guint16
nice_candidate_ms_ice_local_preference_full(guint transport_preference,guint direction_preference,guint turn_preference,guint other_preference)249 nice_candidate_ms_ice_local_preference_full (guint transport_preference,
250 guint direction_preference, guint turn_preference, guint other_preference)
251 {
252 /*
253 * bits 0- 5: other_preference (ip local preference)
254 * 6- 8: turn_preference
255 * 9-11: direction_preference
256 * 12-15: transport_preference
257 */
258 g_assert_cmpuint (other_preference, <, NICE_CANDIDATE_MAX_LOCAL_ADDRESSES);
259 g_assert_cmpuint (turn_preference, <, NICE_CANDIDATE_MAX_TURN_SERVERS);
260 g_assert_cmpuint (direction_preference, <, 8);
261 g_assert_cmpuint (transport_preference, <, 16);
262
263 return (transport_preference << 12) +
264 (direction_preference << 9) +
265 (turn_preference << 6) +
266 other_preference;
267 }
268
269 static guint32
nice_candidate_ms_ice_local_preference(const NiceCandidate * candidate)270 nice_candidate_ms_ice_local_preference (const NiceCandidate *candidate)
271 {
272 const NiceCandidateImpl *c = (NiceCandidateImpl *) candidate;
273 guint transport_preference = 0;
274 guint direction_preference = 0;
275 guint turn_preference = 0;
276
277 switch (candidate->transport)
278 {
279 case NICE_CANDIDATE_TRANSPORT_TCP_SO:
280 case NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
281 transport_preference = NICE_CANDIDATE_TRANSPORT_MS_PREF_TCP;
282 direction_preference = NICE_CANDIDATE_DIRECTION_MS_PREF_ACTIVE;
283 break;
284 case NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
285 transport_preference = NICE_CANDIDATE_TRANSPORT_MS_PREF_TCP;
286 direction_preference = NICE_CANDIDATE_DIRECTION_MS_PREF_PASSIVE;
287 break;
288 case NICE_CANDIDATE_TRANSPORT_UDP:
289 default:
290 transport_preference = NICE_CANDIDATE_TRANSPORT_MS_PREF_UDP;
291 break;
292 }
293
294 /* Relay candidates are assigned a unique local preference at
295 * creation time.
296 */
297 if (candidate->type == NICE_CANDIDATE_TYPE_RELAYED) {
298 g_assert (c->turn);
299 turn_preference = c->turn->preference;
300 }
301
302 return nice_candidate_ms_ice_local_preference_full(transport_preference,
303 direction_preference, turn_preference,
304 nice_candidate_ip_local_preference (candidate));
305 }
306
307 static guint8
nice_candidate_ice_type_preference(const NiceCandidate * candidate,gboolean reliable,gboolean nat_assisted)308 nice_candidate_ice_type_preference (const NiceCandidate *candidate,
309 gboolean reliable, gboolean nat_assisted)
310 {
311 const NiceCandidateImpl *c = (NiceCandidateImpl *) candidate;
312 guint8 type_preference;
313
314 switch (candidate->type)
315 {
316 case NICE_CANDIDATE_TYPE_HOST:
317 type_preference = NICE_CANDIDATE_TYPE_PREF_HOST;
318 break;
319 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
320 type_preference = NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE;
321 break;
322 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
323 if (nat_assisted)
324 type_preference = NICE_CANDIDATE_TYPE_PREF_NAT_ASSISTED;
325 else
326 type_preference = NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE;
327 break;
328 case NICE_CANDIDATE_TYPE_RELAYED:
329 if (c->turn->type == NICE_RELAY_TYPE_TURN_UDP)
330 type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP;
331 else
332 type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED;
333 break;
334 default:
335 type_preference = 0;
336 break;
337 }
338
339 if ((reliable && candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP) ||
340 (!reliable && candidate->transport != NICE_CANDIDATE_TRANSPORT_UDP)) {
341 type_preference = type_preference / 2;
342 }
343
344 return type_preference;
345 }
346
347 guint32
nice_candidate_ice_priority(const NiceCandidate * candidate,gboolean reliable,gboolean nat_assisted)348 nice_candidate_ice_priority (const NiceCandidate *candidate,
349 gboolean reliable, gboolean nat_assisted)
350 {
351 guint8 type_preference;
352 guint16 local_preference;
353
354 type_preference = nice_candidate_ice_type_preference (candidate, reliable,
355 nat_assisted);
356 local_preference = nice_candidate_ice_local_preference (candidate);
357
358 return nice_candidate_ice_priority_full (type_preference, local_preference,
359 candidate->component_id);
360 }
361
362 guint32
nice_candidate_ms_ice_priority(const NiceCandidate * candidate,gboolean reliable,gboolean nat_assisted)363 nice_candidate_ms_ice_priority (const NiceCandidate *candidate,
364 gboolean reliable, gboolean nat_assisted)
365 {
366 guint8 type_preference;
367 guint16 local_preference;
368
369 type_preference = nice_candidate_ice_type_preference (candidate, reliable,
370 nat_assisted);
371 local_preference = nice_candidate_ms_ice_local_preference (candidate);
372
373 return nice_candidate_ice_priority_full (type_preference, local_preference,
374 candidate->component_id);
375 }
376
377 /*
378 * Calculates the pair priority as specified in ICE
379 * sect 5.7.2. "Computing Pair Priority and Ordering Pairs" (ID-19).
380 */
381 guint64
nice_candidate_pair_priority(guint32 o_prio,guint32 a_prio)382 nice_candidate_pair_priority (guint32 o_prio, guint32 a_prio)
383 {
384 guint32 max = o_prio > a_prio ? o_prio : a_prio;
385 guint32 min = o_prio < a_prio ? o_prio : a_prio;
386 /* These two constants are here explictly to make some version of GCC happy */
387 const guint64 one = 1;
388 const guint64 thirtytwo = 32;
389
390 return (one << thirtytwo) * min + 2 * max + (o_prio > a_prio ? 1 : 0);
391 }
392
393 void
nice_candidate_pair_priority_to_string(guint64 prio,gchar * string)394 nice_candidate_pair_priority_to_string (guint64 prio, gchar *string)
395 {
396 g_snprintf (string, NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE,
397 "%08" G_GINT64_MODIFIER "x:%08" G_GINT64_MODIFIER "x:%" G_GUINT64_FORMAT,
398 prio >> 32, (prio >> 1) & 0x7fffffff, prio & 1);
399 }
400
401 /*
402 * Copies a candidate
403 */
404 NICEAPI_EXPORT NiceCandidate *
nice_candidate_copy(const NiceCandidate * candidate)405 nice_candidate_copy (const NiceCandidate *candidate)
406 {
407 const NiceCandidateImpl *c = (NiceCandidateImpl *)candidate;
408 NiceCandidateImpl *copy;
409
410 g_return_val_if_fail (c != NULL, NULL);
411
412 copy = (NiceCandidateImpl *) nice_candidate_new (candidate->type);
413 memcpy (copy, candidate, sizeof(NiceCandidateImpl));
414
415 copy->turn = NULL;
416 copy->c.username = g_strdup (copy->c.username);
417 copy->c.password = g_strdup (copy->c.password);
418
419 return (NiceCandidate *) copy;
420 }
421
422 NICEAPI_EXPORT gboolean
nice_candidate_equal_target(const NiceCandidate * candidate1,const NiceCandidate * candidate2)423 nice_candidate_equal_target (const NiceCandidate *candidate1,
424 const NiceCandidate *candidate2)
425 {
426 g_return_val_if_fail (candidate1 != NULL, FALSE);
427 g_return_val_if_fail (candidate2 != NULL, FALSE);
428
429 return (candidate1->transport == candidate2->transport &&
430 nice_address_equal (&candidate1->addr, &candidate2->addr));
431 }
432
433
434 NICEAPI_EXPORT const gchar *
nice_candidate_type_to_string(NiceCandidateType type)435 nice_candidate_type_to_string (NiceCandidateType type)
436 {
437 switch (type) {
438 case NICE_CANDIDATE_TYPE_HOST:
439 return "host";
440 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
441 return "srflx";
442 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
443 return "prflx";
444 case NICE_CANDIDATE_TYPE_RELAYED:
445 return "relay";
446 default:
447 g_assert_not_reached ();
448 }
449 }
450
451 NICEAPI_EXPORT const gchar *
nice_candidate_transport_to_string(NiceCandidateTransport transport)452 nice_candidate_transport_to_string (NiceCandidateTransport transport)
453 {
454 switch (transport) {
455 case NICE_CANDIDATE_TRANSPORT_UDP:
456 return "udp";
457 case NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
458 return "tcp-act";
459 case NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
460 return "tcp-pass";
461 case NICE_CANDIDATE_TRANSPORT_TCP_SO:
462 return "tcp-so";
463 default:
464 g_assert_not_reached ();
465 }
466 }
467