2d76ff0df6121963654490cf7eec253a05aae4b0
[platform/upstream/libnice.git] / stun / usages / ice.c
1 /*
2  * This file is part of the Nice GLib ICE library.
3  *
4  * (C) 2008-2009 Collabora Ltd.
5  *  Contact: Youness Alaoui
6  * (C) 2007-2009 Nokia Corporation. All rights reserved.
7  *  Contact: Rémi Denis-Courmont
8  *
9  * The contents of this file are subject to the Mozilla Public License Version
10  * 1.1 (the "License"); you may not use this file except in compliance with
11  * the License. You may obtain a copy of the License at
12  * http://www.mozilla.org/MPL/
13  *
14  * Software distributed under the License is distributed on an "AS IS" basis,
15  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16  * for the specific language governing rights and limitations under the
17  * License.
18  *
19  * The Original Code is the Nice GLib ICE library.
20  *
21  * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22  * Corporation. All Rights Reserved.
23  *
24  * Contributors:
25  *   Youness Alaoui, Collabora Ltd.
26  *   Rémi Denis-Courmont, Nokia
27  *
28  * Alternatively, the contents of this file may be used under the terms of the
29  * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30  * case the provisions of LGPL are applicable instead of those above. If you
31  * wish to allow use of your version of this file only under the terms of the
32  * LGPL and not to allow others to use your version of this file under the
33  * MPL, indicate your decision by deleting the provisions above and replace
34  * them with the notice and other provisions required by the LGPL. If you do
35  * not delete the provisions above, a recipient may use your version of this
36  * file under either the MPL or the LGPL.
37  */
38
39 #ifdef HAVE_CONFIG_H
40 # include <config.h>
41 #endif
42
43 #include <string.h>
44 #include <assert.h>
45 #include <stdlib.h>
46
47 #ifdef _WIN32
48 #include <winsock2.h>
49 #else
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <arpa/inet.h>
53 #endif
54
55
56 #include "stunagent.h"
57
58 /** ICE connectivity checks **/
59 #include "ice.h"
60
61
62 size_t
63 stun_usage_ice_conncheck_create (StunAgent *agent, StunMessage *msg,
64     uint8_t *buffer, size_t buffer_len,
65     const uint8_t *username, const size_t username_len,
66     const uint8_t *password, const size_t password_len,
67     bool cand_use, bool controlling, uint32_t priority,
68     uint64_t tie, const char *candidate_identifier,
69     StunUsageIceCompatibility compatibility)
70 {
71   StunMessageReturn val;
72
73   stun_agent_init_request (agent, msg, buffer, buffer_len, STUN_BINDING);
74
75   if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_RFC5245 ||
76       compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2) {
77     if (cand_use)
78     {
79       val = stun_message_append_flag (msg, STUN_ATTRIBUTE_USE_CANDIDATE);
80       if (val != STUN_MESSAGE_RETURN_SUCCESS)
81         return 0;
82     }
83
84     val = stun_message_append32 (msg, STUN_ATTRIBUTE_PRIORITY, priority);
85     if (val != STUN_MESSAGE_RETURN_SUCCESS)
86       return 0;
87
88     if (controlling)
89       val = stun_message_append64 (msg, STUN_ATTRIBUTE_ICE_CONTROLLING, tie);
90     else
91       val = stun_message_append64 (msg, STUN_ATTRIBUTE_ICE_CONTROLLED, tie);
92     if (val != STUN_MESSAGE_RETURN_SUCCESS)
93       return 0;
94   }
95
96   if (username && username_len > 0) {
97     val = stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
98         username, username_len);
99     if (val != STUN_MESSAGE_RETURN_SUCCESS)
100       return 0;
101   }
102
103   if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2 &&
104       candidate_identifier) {
105     size_t identifier_len = strlen(candidate_identifier);
106     size_t attribute_len = identifier_len;
107     int modulo4 = identifier_len % 4;
108     uint8_t* buf;
109
110     if (modulo4)
111         attribute_len += 4 - modulo4;
112
113     // Avoid a coverify false positive
114     assert (attribute_len >= identifier_len);
115     buf = malloc(attribute_len);
116     memset(buf, 0, attribute_len);
117     memcpy(buf, candidate_identifier, identifier_len);
118
119     val = stun_message_append_bytes (msg, STUN_ATTRIBUTE_CANDIDATE_IDENTIFIER,
120             buf, attribute_len);
121
122     free(buf);
123
124     if (val != STUN_MESSAGE_RETURN_SUCCESS)
125                 return 0;
126
127     val = stun_message_append32 (msg,
128         STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION, 2);
129
130     if (val != STUN_MESSAGE_RETURN_SUCCESS)
131       return 0;
132   }
133
134   return stun_agent_finish_message (agent, msg, password, password_len);
135
136 }
137
138
139 StunUsageIceReturn stun_usage_ice_conncheck_process (StunMessage *msg,
140     struct sockaddr_storage *addr, socklen_t *addrlen,
141     StunUsageIceCompatibility compatibility)
142 {
143   int code = -1;
144   StunMessageReturn val;
145
146   if (stun_message_get_method (msg) != STUN_BINDING)
147     return STUN_USAGE_ICE_RETURN_INVALID;
148
149   switch (stun_message_get_class (msg))
150   {
151     case STUN_REQUEST:
152     case STUN_INDICATION:
153       return STUN_USAGE_ICE_RETURN_INVALID;
154
155     case STUN_RESPONSE:
156       break;
157
158     case STUN_ERROR:
159     default:
160       if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
161         /* missing ERROR-CODE: ignore message */
162         return STUN_USAGE_ICE_RETURN_INVALID;
163       }
164
165       if (code  == STUN_ERROR_ROLE_CONFLICT)
166         return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT;
167
168       /* NOTE: currently we ignore unauthenticated messages if the context
169        * is authenticated, for security reasons. */
170       stun_debug (" STUN error message received (code: %d)", code);
171
172       return STUN_USAGE_ICE_RETURN_ERROR;
173   }
174
175   stun_debug ("Received %u-bytes STUN message", stun_message_length (msg));
176
177   if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) {
178     union {
179       StunTransactionId u8;
180       uint32_t u32[STUN_MESSAGE_TRANS_ID_LEN / 4];
181     } transid;
182     uint32_t magic_cookie;
183     stun_message_id (msg, transid.u8);
184     magic_cookie = *(transid.u32);
185
186     val = stun_message_find_xor_addr_full (msg,
187         STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen, htonl (magic_cookie));
188   } else {
189     val = stun_message_find_xor_addr (msg,
190         STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen);
191   }
192   if (val != STUN_MESSAGE_RETURN_SUCCESS)
193   {
194     stun_debug (" No XOR-MAPPED-ADDRESS: %d", val);
195     val = stun_message_find_addr (msg,
196         STUN_ATTRIBUTE_MAPPED_ADDRESS, addr, addrlen);
197     if (val != STUN_MESSAGE_RETURN_SUCCESS)
198     {
199       stun_debug (" No MAPPED-ADDRESS: %d", val);
200       return STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS;
201     }
202   }
203
204   stun_debug ("Mapped address found!");
205   return STUN_USAGE_ICE_RETURN_SUCCESS;
206 }
207
208 static int
209 stun_bind_error (StunAgent *agent, StunMessage *msg,
210     uint8_t *buf, size_t *plen, const StunMessage *req,
211     StunError code)
212 {
213   size_t len = *plen;
214   int val;
215
216   *plen = 0;
217   stun_debug ("STUN Error Reply (buffer size: %u)...", (unsigned)len);
218
219   val = stun_agent_init_error (agent, msg, buf, len, req, code);
220   if (!val)
221     return val;
222
223   len = stun_agent_finish_message (agent, msg, NULL, 0);
224   if (len == 0)
225     return 0;
226
227   *plen = len;
228   stun_debug (" Error response (%u) of %u bytes", (unsigned)code,
229        (unsigned)*plen);
230   return 1;
231 }
232
233 StunUsageIceReturn
234 stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req,
235     StunMessage *msg, uint8_t *buf, size_t *plen,
236     const struct sockaddr_storage *src, socklen_t srclen,
237     bool *control, uint64_t tie,
238     StunUsageIceCompatibility compatibility)
239 {
240   const char *username = NULL;
241   uint16_t username_len;
242   size_t len = *plen;
243   uint64_t q;
244   StunMessageReturn val = STUN_MESSAGE_RETURN_SUCCESS;
245   StunUsageIceReturn ret = STUN_USAGE_ICE_RETURN_SUCCESS;
246
247
248 #define err( code ) \
249   stun_bind_error (agent, msg, buf, &len, req, code); \
250   *plen = len
251
252   *plen = 0;
253   stun_debug ("STUN Reply (buffer size = %u)...", (unsigned)len);
254
255   if (stun_message_get_class (req) != STUN_REQUEST)
256   {
257     stun_debug (" Unhandled non-request (class %u) message.",
258          stun_message_get_class (req));
259     return STUN_USAGE_ICE_RETURN_INVALID_REQUEST;
260   }
261
262   if (stun_message_get_method (req) != STUN_BINDING)
263   {
264     stun_debug (" Bad request (method %u) message.",
265          stun_message_get_method (req));
266     err (STUN_ERROR_BAD_REQUEST);
267     return STUN_USAGE_ICE_RETURN_INVALID_METHOD;
268   }
269
270   /* Role conflict handling */
271   assert (control != NULL);
272   if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING
273           : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS)
274   {
275     /* we have the ice-controlling/controlled attribute,
276      * and there's a role conflict
277      */
278     stun_debug ("STUN Role Conflict detected:");
279
280     /* According to ICE RFC 5245, section 7.2.1.1, we consider the four
281      * possible cases when a role conflict is detected: two cases are
282      * resolved by switching role locally, and the two other cases are
283      * handled by responding with a STUN error.
284      */
285     if ((tie < q && *control) || (tie >= q && !*control))
286     {
287       stun_debug (" switching role from \"controll%s\" to \"controll%s\"",
288            *control ? "ing" : "ed", *control ? "ed" : "ing");
289       *control = !*control;
290       ret = STUN_USAGE_ICE_RETURN_ROLE_CONFLICT;
291     }
292     else
293     {
294       stun_debug (" staying \"controll%s\" (sending error)",
295            *control ? "ing" : "ed");
296       err (STUN_ERROR_ROLE_CONFLICT);
297       return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT;
298     }
299   } else {
300     if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLED
301             : STUN_ATTRIBUTE_ICE_CONTROLLING, &q) != STUN_MESSAGE_RETURN_SUCCESS)
302     {
303       /* we don't have the expected ice-controlling/controlled
304        * attribute
305        */
306       if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_RFC5245 ||
307           compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2)
308       {
309         stun_debug ("STUN Role not specified by peer!");
310       }
311     }
312   }
313
314   if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) {
315     stun_debug ("Unable to create response");
316     goto failure;
317   }
318   if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) {
319     union {
320       StunTransactionId transid;
321       uint32_t magic_cookie;
322     } conv;
323
324     stun_message_id (msg, conv.transid);
325
326     val = stun_message_append_xor_addr_full (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
327         src, srclen, htonl (conv.magic_cookie));
328   } else if (stun_message_has_cookie (msg) &&
329       compatibility != STUN_USAGE_ICE_COMPATIBILITY_GOOGLE) {
330     val = stun_message_append_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
331         src, srclen);
332   } else {
333     val = stun_message_append_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS,
334         (struct sockaddr *) src, srclen);
335   }
336
337   if (val != STUN_MESSAGE_RETURN_SUCCESS) {
338     stun_debug (" Mapped address problem: %d", val);
339     goto failure;
340   }
341
342   username = (const char *)stun_message_find (req,
343       STUN_ATTRIBUTE_USERNAME, &username_len);
344   if (username) {
345     val = stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
346         username, username_len);
347   }
348
349   if (val != STUN_MESSAGE_RETURN_SUCCESS) {
350     stun_debug ("Error appending username: %d", val);
351     goto failure;
352   }
353
354   if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2) {
355     val = stun_message_append32 (msg,
356         STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION, 2);
357
358     if (val != STUN_MESSAGE_RETURN_SUCCESS) {
359       stun_debug ("Error appending implementation version: %d", val);
360       goto failure;
361     }
362   }
363
364   /* the stun agent will automatically use the password of the request */
365   len = stun_agent_finish_message (agent, msg, NULL, 0);
366   if (len == 0)
367     goto failure;
368
369   *plen = len;
370   stun_debug (" All done (response size: %u)", (unsigned)len);
371   return ret;
372
373 failure:
374   assert (*plen == 0);
375   stun_debug (" Fatal error formatting Response: %d", val);
376
377   switch (val)
378   {
379     case STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE:
380       return STUN_USAGE_ICE_RETURN_MEMORY_ERROR;
381     case STUN_MESSAGE_RETURN_INVALID:
382     case STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS:
383       return STUN_USAGE_ICE_RETURN_INVALID_ADDRESS;
384     case STUN_MESSAGE_RETURN_SUCCESS:
385       assert (0);  /* shouldn’t be reached */
386     case STUN_MESSAGE_RETURN_NOT_FOUND:
387     default:
388       return STUN_USAGE_ICE_RETURN_ERROR;
389   }
390 }
391 #undef err
392
393
394 uint32_t stun_usage_ice_conncheck_priority (const StunMessage *msg)
395 {
396   uint32_t value;
397
398   if (stun_message_find32 (msg, STUN_ATTRIBUTE_PRIORITY, &value)
399       != STUN_MESSAGE_RETURN_SUCCESS)
400     return 0;
401   return value;
402 }
403
404
405 bool stun_usage_ice_conncheck_use_candidate (const StunMessage *msg)
406 {
407   return (stun_message_find_flag (msg,
408           STUN_ATTRIBUTE_USE_CANDIDATE) == STUN_MESSAGE_RETURN_SUCCESS);
409 }
410