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