Fix TURN MSN for WLM2009 compat
[sipe-libnice.git] / socket / turn.c
blob744fc74af231f6b77e2542191f099527d3c3a45d
1 /*
2 * This file is part of the Nice GLib ICE library.
4 * (C) 2008 Collabora Ltd.
5 * (C) 2008 Nokia Corporation
6 * Contact: Youness Alaoui
8 * The contents of this file are subject to the Mozilla Public License Version
9 * 1.1 (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS" basis,
14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15 * for the specific language governing rights and limitations under the
16 * License.
18 * The Original Code is the Nice GLib ICE library.
20 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
21 * Corporation. All Rights Reserved.
23 * Contributors:
24 * Dafydd Harries, Collabora Ltd.
25 * Youness Alaoui, Collabora Ltd.
26 * RĂ©mi Denis-Courmont, Nokia
27 * Kai Vehmanen
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.
41 * Implementation of TURN
43 #ifdef HAVE_CONFIG_H
44 # include "config.h"
45 #endif
47 #include <string.h>
48 #include <errno.h>
49 #include <fcntl.h>
51 #include "turn.h"
52 #include "stun/stunagent.h"
53 #include "stun/usages/timer.h"
54 #include "agent-priv.h"
56 typedef struct {
57 StunMessage message;
58 uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
59 StunTimer timer;
60 } TURNMessage;
63 typedef struct {
64 NiceAddress peer;
65 uint16_t channel;
66 } ChannelBinding;
68 typedef struct {
69 NiceAgent *nice;
70 StunAgent agent;
71 GList *channels;
72 GList *pending_bindings;
73 ChannelBinding *current_binding;
74 TURNMessage *current_binding_msg;
75 GSource *tick_source;
76 NiceSocket *base_socket;
77 NiceAddress server_addr;
78 uint8_t *username;
79 size_t username_len;
80 uint8_t *password;
81 size_t password_len;
82 NiceTurnSocketCompatibility compatibility;
83 } TurnPriv;
87 static void socket_close (NiceSocket *sock);
88 static gint socket_recv (NiceSocket *sock, NiceAddress *from,
89 guint len, gchar *buf);
90 static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
91 guint len, const gchar *buf);
92 static gboolean socket_is_reliable (NiceSocket *sock);
94 static void priv_process_pending_bindings (TurnPriv *priv);
95 static gboolean priv_retransmissions_tick_unlocked (TurnPriv *priv);
96 static gboolean priv_retransmissions_tick (gpointer pointer);
97 static void priv_schedule_tick (TurnPriv *priv);
98 static void priv_send_turn_message (TurnPriv *priv, TURNMessage *msg);
99 static gboolean priv_send_channel_bind (TurnPriv *priv, StunMessage *resp,
100 uint16_t channel, NiceAddress *peer);
101 static gboolean priv_add_channel_binding (TurnPriv *priv, NiceAddress *peer);
105 NiceSocket *
106 nice_turn_socket_new (NiceAgent *agent, NiceAddress *addr,
107 NiceSocket *base_socket, NiceAddress *server_addr,
108 gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility)
110 TurnPriv *priv = g_new0 (TurnPriv, 1);
111 NiceSocket *sock = g_slice_new0 (NiceSocket);
113 if (!sock) {
114 return NULL;
117 if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
118 stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
119 STUN_COMPATIBILITY_RFC5389,
120 STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS);
121 } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
122 stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
123 STUN_COMPATIBILITY_RFC3489,
124 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
125 STUN_AGENT_USAGE_NO_INDICATION_AUTH);
126 } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
127 stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
128 STUN_COMPATIBILITY_RFC3489,
129 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
130 STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
133 priv->nice = agent;
134 priv->channels = NULL;
135 priv->current_binding = NULL;
136 priv->base_socket = base_socket;
138 if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
139 priv->username = g_base64_decode (username, &priv->username_len);
140 priv->password = g_base64_decode (password, &priv->password_len);
141 } else {
142 priv->username = (uint8_t *)g_strdup (username);
143 priv->username_len = (size_t) strlen (username);
144 if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
145 priv->password = NULL;
146 priv->password_len = 0;
147 } else {
148 priv->password = (uint8_t *)g_strdup (password);
149 priv->password_len = (size_t) strlen (password);
152 priv->server_addr = *server_addr;
153 priv->compatibility = compatibility;
154 sock->addr = *addr;
155 sock->fileno = base_socket->fileno;
156 sock->send = socket_send;
157 sock->recv = socket_recv;
158 sock->is_reliable = socket_is_reliable;
159 sock->close = socket_close;
160 sock->priv = (void *) priv;
161 return sock;
165 static void
166 socket_close (NiceSocket *sock)
168 TurnPriv *priv = (TurnPriv *) sock->priv;
169 GList *i = NULL;
170 for (i = priv->channels; i; i = i->next) {
171 ChannelBinding *b = i->data;
172 g_free (b);
174 g_list_free (priv->channels);
176 for (i = priv->pending_bindings; i; i = i->next) {
177 ChannelBinding *b = i->data;
178 g_free (b);
180 g_list_free (priv->pending_bindings);
182 if (priv->tick_source != NULL) {
183 g_source_destroy (priv->tick_source);
184 g_source_unref (priv->tick_source);
185 priv->tick_source = NULL;
188 g_free (priv->current_binding);
189 g_free (priv->current_binding_msg);
190 g_free (priv->username);
191 g_free (priv->password);
192 g_free (priv);
195 static gint
196 socket_recv (NiceSocket *sock, NiceAddress *from, guint len, gchar *buf)
198 TurnPriv *priv = (TurnPriv *) sock->priv;
199 uint8_t recv_buf[STUN_MAX_MESSAGE_SIZE];
200 gint recv_len;
201 NiceAddress recv_from;
202 NiceSocket *dummy;;
204 recv_len = nice_socket_recv (priv->base_socket, &recv_from,
205 sizeof(recv_buf), (gchar *) recv_buf);
207 return nice_turn_socket_parse_recv (sock, &dummy, from, len, buf,
208 &recv_from, (gchar *) recv_buf, (guint) recv_len);
211 static gboolean
212 socket_send (NiceSocket *sock, const NiceAddress *to,
213 guint len, const gchar *buf)
215 TurnPriv *priv = (TurnPriv *) sock->priv;
216 StunMessage msg;
217 uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
218 size_t msg_len;
219 struct sockaddr_storage sa;
220 GList *i = priv->channels;
221 ChannelBinding *binding = NULL;
223 for (; i; i = i->next) {
224 ChannelBinding *b = i->data;
225 if (nice_address_equal (&b->peer, to)) {
226 binding = b;
227 break;
231 nice_address_copy_to_sockaddr (to, (struct sockaddr *)&sa);
233 if (binding) {
234 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 &&
235 len + sizeof(uint32_t) <= sizeof(buffer)) {
236 uint16_t len16 = htons ((uint16_t) len);
237 uint16_t channel16 = htons (binding->channel);
238 memcpy (buffer, &channel16, sizeof(uint16_t));
239 memcpy (buffer + sizeof(uint16_t), &len16,sizeof(uint16_t));
240 memcpy (buffer + sizeof(uint32_t), buf, len);
241 msg_len = len + sizeof(uint32_t);
242 } else {
243 return nice_socket_send (priv->base_socket, &priv->server_addr, len, buf);
245 } else {
246 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
247 if (!stun_agent_init_indication (&priv->agent, &msg,
248 buffer, sizeof(buffer), STUN_IND_SEND))
249 goto send;
250 if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_PEER_ADDRESS,
251 (struct sockaddr *)&sa, sizeof(sa)) !=
252 STUN_MESSAGE_RETURN_SUCCESS)
253 goto send;
254 } else {
255 if (!stun_agent_init_request (&priv->agent, &msg,
256 buffer, sizeof(buffer), STUN_SEND))
257 goto send;
259 if (stun_message_append32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
260 TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS)
261 goto send;
262 if (priv->username != NULL && priv->username_len > 0) {
263 if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_USERNAME,
264 priv->username, priv->username_len) !=
265 STUN_MESSAGE_RETURN_SUCCESS)
266 goto send;
268 if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_DESTINATION_ADDRESS,
269 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS)
270 goto send;
272 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE &&
273 priv->current_binding &&
274 nice_address_equal (&priv->current_binding->peer, to)) {
275 stun_message_append32 (&msg, STUN_ATTRIBUTE_OPTIONS, 1);
279 if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_DATA,
280 buf, len) != STUN_MESSAGE_RETURN_SUCCESS)
281 goto send;
283 msg_len = stun_agent_finish_message (&priv->agent, &msg,
284 priv->password, priv->password_len);
287 if (msg_len > 0) {
288 return nice_socket_send (priv->base_socket, &priv->server_addr,
289 msg_len, (gchar *)buffer);
291 send:
292 return nice_socket_send (priv->base_socket, to, len, buf);
295 static gboolean
296 socket_is_reliable (NiceSocket *sock)
298 TurnPriv *priv = (TurnPriv *) sock->priv;
299 return nice_socket_is_reliable (priv->base_socket);
304 gint
305 nice_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
306 NiceAddress *from, guint len, gchar *buf,
307 NiceAddress *recv_from, gchar *recv_buf, guint recv_len)
310 TurnPriv *priv = (TurnPriv *) sock->priv;
311 StunValidationStatus valid;
312 StunMessage msg;
313 struct sockaddr_storage sa;
314 socklen_t from_len = sizeof (sa);
315 GList *i = priv->channels;
316 ChannelBinding *binding = NULL;
318 if (nice_address_equal (&priv->server_addr, recv_from)) {
319 valid = stun_agent_validate (&priv->agent, &msg,
320 (uint8_t *) recv_buf, (size_t) recv_len, NULL, NULL);
322 if (valid == STUN_VALIDATION_SUCCESS) {
323 if (priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
324 uint32_t cookie;
325 if (stun_message_find32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
326 &cookie) != STUN_MESSAGE_RETURN_SUCCESS)
327 goto recv;
328 if (cookie != TURN_MAGIC_COOKIE)
329 goto recv;
332 if (stun_message_get_method (&msg) == STUN_SEND) {
333 if (stun_message_get_class (&msg) == STUN_RESPONSE &&
334 priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
335 uint32_t opts = 0;
336 if (stun_message_find32 (&msg, STUN_ATTRIBUTE_OPTIONS, &opts) ==
337 STUN_MESSAGE_RETURN_SUCCESS && opts & 0x1)
338 goto msn_google_lock;
340 return 0;
341 } else if (stun_message_get_method (&msg) == STUN_OLD_SET_ACTIVE_DST) {
342 StunTransactionId request_id;
343 StunTransactionId response_id;
344 if (priv->current_binding && priv->current_binding_msg) {
345 stun_message_id (&msg, response_id);
346 stun_message_id (&priv->current_binding_msg->message, request_id);
347 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
348 g_free (priv->current_binding_msg);
349 priv->current_binding_msg = NULL;
351 if (stun_message_get_class (&msg) == STUN_RESPONSE &&
352 priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
353 goto msn_google_lock;
354 } else {
355 g_free (priv->current_binding);
356 priv->current_binding = NULL;
361 return 0;
362 } else if (stun_message_get_method (&msg) == STUN_CHANNELBIND) {
363 StunTransactionId request_id;
364 StunTransactionId response_id;
365 if (priv->current_binding && priv->current_binding_msg) {
366 stun_message_id (&msg, response_id);
367 stun_message_id (&priv->current_binding_msg->message, request_id);
368 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
369 if (stun_message_get_class (&msg) == STUN_ERROR) {
370 int code = -1;
371 uint8_t *sent_realm = NULL;
372 uint8_t *recv_realm = NULL;
373 uint16_t sent_realm_len = 0;
374 uint16_t recv_realm_len = 0;
376 sent_realm = (uint8_t *) stun_message_find (
377 &priv->current_binding_msg->message,
378 STUN_ATTRIBUTE_REALM, &sent_realm_len);
379 recv_realm = (uint8_t *) stun_message_find (&msg,
380 STUN_ATTRIBUTE_REALM, &recv_realm_len);
382 /* check for unauthorized error response */
383 if (stun_message_find_error (&msg, &code) ==
384 STUN_MESSAGE_RETURN_SUCCESS &&
385 (code == 438 || (code == 401 &&
386 !(recv_realm != NULL &&
387 recv_realm_len > 0 &&
388 recv_realm_len == sent_realm_len &&
389 sent_realm != NULL &&
390 memcmp (sent_realm, recv_realm, sent_realm_len) == 0)))) {
391 g_free (priv->current_binding_msg);
392 priv->current_binding_msg = NULL;
393 if (priv->current_binding) {
394 priv_send_channel_bind (priv, &msg,
395 priv->current_binding->channel,
396 &priv->current_binding->peer);
398 } else {
399 g_free (priv->current_binding);
400 priv->current_binding = NULL;
401 g_free (priv->current_binding_msg);
402 priv->current_binding_msg = NULL;
403 priv_process_pending_bindings (priv);
405 } else if (stun_message_get_class (&msg) == STUN_RESPONSE) {
406 g_free (priv->current_binding_msg);
407 priv->current_binding_msg = NULL;
408 if (priv->current_binding) {
409 priv->channels = g_list_append (priv->channels,
410 priv->current_binding);
411 priv->current_binding = NULL;
413 priv_process_pending_bindings (priv);
417 return 0;
418 } else if (stun_message_get_class (&msg) == STUN_INDICATION &&
419 stun_message_get_method (&msg) == STUN_IND_DATA) {
420 uint16_t data_len;
421 uint8_t *data;
423 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
424 if (stun_message_find_xor_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
425 (struct sockaddr *)&sa, &from_len) !=
426 STUN_MESSAGE_RETURN_SUCCESS)
427 goto recv;
428 } else {
429 if (stun_message_find_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
430 (struct sockaddr *)&sa, &from_len) !=
431 STUN_MESSAGE_RETURN_SUCCESS)
432 goto recv;
435 data = (uint8_t *) stun_message_find (&msg, STUN_ATTRIBUTE_DATA,
436 &data_len);
438 if (data == NULL)
439 goto recv;
441 nice_address_set_from_sockaddr (from, (struct sockaddr *)&sa);
443 *from_sock = sock;
444 memmove (buf, data, len > data_len ? data_len : len);
445 return len > data_len ? data_len : len;
446 } else {
447 goto recv;
452 recv:
453 for (i = priv->channels; i; i = i->next) {
454 ChannelBinding *b = i->data;
455 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
456 if (b->channel == ntohs(((uint16_t *)recv_buf)[0])) {
457 recv_len = ntohs (((uint16_t *)recv_buf)[1]);
458 recv_buf += sizeof(uint32_t);
459 binding = b;
460 break;
462 } else {
463 binding = b;
464 break;
468 if (binding) {
469 *from = binding->peer;
470 *from_sock = sock;
471 } else {
472 *from = *recv_from;
475 memmove (buf, recv_buf, len > recv_len ? recv_len : len);
476 return len > recv_len ? recv_len : len;
478 msn_google_lock:
480 if (priv->current_binding) {
481 GList *i = priv->channels;
482 for (; i; i = i->next) {
483 ChannelBinding *b = i->data;
484 g_free (b);
486 g_list_free (priv->channels);
487 priv->channels = g_list_append (NULL, priv->current_binding);
488 priv->current_binding = NULL;
489 priv_process_pending_bindings (priv);
492 return 0;
495 gboolean
496 nice_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer)
498 TurnPriv *priv = (TurnPriv *) sock->priv;
499 return priv_add_channel_binding (priv, peer);
502 static void
503 priv_process_pending_bindings (TurnPriv *priv)
505 gboolean ret = FALSE;
506 while (priv->pending_bindings != NULL && ret == FALSE) {
507 NiceAddress *peer = priv->pending_bindings->data;
508 ret = priv_add_channel_binding (priv, peer);
509 priv->pending_bindings = g_list_remove (priv->pending_bindings, peer);
510 nice_address_free (peer);
514 static gboolean
515 priv_retransmissions_tick_unlocked (TurnPriv *priv)
517 if (priv->current_binding_msg) {
518 switch (stun_timer_refresh (&priv->current_binding_msg->timer)) {
519 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
520 /* Time out */
521 g_free (priv->current_binding);
522 priv->current_binding = NULL;
523 g_free (priv->current_binding_msg);
524 priv->current_binding_msg = NULL;
525 priv_process_pending_bindings (priv);
526 break;
527 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
528 /* Retransmit */
529 nice_socket_send (priv->base_socket, &priv->server_addr,
530 stun_message_length (&priv->current_binding_msg->message),
531 (gchar *)priv->current_binding_msg->buffer);
532 break;
533 case STUN_USAGE_TIMER_RETURN_SUCCESS:
534 break;
538 priv_schedule_tick (priv);
539 return FALSE;
543 static gboolean
544 priv_retransmissions_tick (gpointer pointer)
546 TurnPriv *priv = pointer;
547 gboolean ret;
549 g_static_rec_mutex_lock (&priv->nice->mutex);
550 ret = priv_retransmissions_tick_unlocked (priv);
551 g_static_rec_mutex_unlock (&priv->nice->mutex);
553 return ret;
556 static void
557 priv_schedule_tick (TurnPriv *priv)
559 if (priv->tick_source != NULL) {
560 g_source_destroy (priv->tick_source);
561 g_source_unref (priv->tick_source);
562 priv->tick_source = NULL;
565 if (priv->current_binding_msg) {
566 guint timeout = stun_timer_remainder (&priv->current_binding_msg->timer);
567 if (timeout > 0) {
568 priv->tick_source = agent_timeout_add_with_context (priv->nice, timeout,
569 priv_retransmissions_tick, priv);
570 } else {
571 priv_retransmissions_tick_unlocked (priv);
576 static void
577 priv_send_turn_message (TurnPriv *priv, TURNMessage *msg)
579 size_t stun_len = stun_message_length (&msg->message);
581 if (priv->current_binding_msg) {
582 g_free (priv->current_binding_msg);
583 priv->current_binding_msg = NULL;
586 nice_socket_send (priv->base_socket, &priv->server_addr,
587 stun_len, (gchar *)msg->buffer);
589 if (nice_socket_is_reliable (priv->base_socket)) {
590 stun_timer_start_reliable (&msg->timer);
591 } else {
592 stun_timer_start (&msg->timer);
595 priv->current_binding_msg = msg;
596 priv_schedule_tick (priv);
599 static gboolean
600 priv_send_channel_bind (TurnPriv *priv, StunMessage *resp,
601 uint16_t channel, NiceAddress *peer)
603 uint32_t channel_attr = channel << 16;
604 size_t stun_len;
605 struct sockaddr_storage sa;
606 TURNMessage *msg = g_new0 (TURNMessage, 1);
608 nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);
610 if (!stun_agent_init_request (&priv->agent, &msg->message,
611 msg->buffer, sizeof(msg->buffer), STUN_CHANNELBIND)) {
612 g_free (msg);
613 return FALSE;
616 if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_CHANNEL_NUMBER,
617 channel_attr) != STUN_MESSAGE_RETURN_SUCCESS) {
618 g_free (msg);
619 return FALSE;
622 if (stun_message_append_xor_addr (&msg->message, STUN_ATTRIBUTE_PEER_ADDRESS,
623 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS) {
624 g_free (msg);
625 return FALSE;
628 if (priv->username != NULL && priv->username_len > 0) {
629 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
630 priv->username, priv->username_len) != STUN_MESSAGE_RETURN_SUCCESS) {
631 g_free (msg);
632 return FALSE;
636 if (resp) {
637 uint8_t *realm;
638 uint8_t *nonce;
639 uint16_t len;
641 realm = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_REALM, &len);
642 if (realm != NULL) {
643 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM,
644 realm, len) != STUN_MESSAGE_RETURN_SUCCESS) {
645 g_free (msg);
646 return 0;
649 nonce = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_NONCE, &len);
650 if (nonce != NULL) {
651 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE,
652 nonce, len) != STUN_MESSAGE_RETURN_SUCCESS) {
653 g_free (msg);
654 return 0;
659 stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
660 priv->password, priv->password_len);
662 if (stun_len > 0) {
663 priv_send_turn_message (priv, msg);
664 return TRUE;
667 g_free (msg);
668 return FALSE;
671 static gboolean
672 priv_add_channel_binding (TurnPriv *priv, NiceAddress *peer)
674 size_t stun_len;
675 struct sockaddr_storage sa;
677 nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);
679 if (priv->current_binding) {
680 NiceAddress * pending= nice_address_new ();
681 *pending = *peer;
682 priv->pending_bindings = g_list_append (priv->pending_bindings, pending);
683 return FALSE;
686 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
687 uint16_t channel = 0x4000;
688 GList *i = priv->channels;
689 for (; i; i = i->next) {
690 ChannelBinding *b = i->data;
691 if (channel == b->channel) {
692 i = priv->channels;
693 channel++;
694 continue;
698 if (channel >= 0x4000 && channel < 0xffff) {
699 gboolean ret = priv_send_channel_bind (priv, NULL, channel, peer);
700 if (ret) {
701 priv->current_binding = g_new0 (ChannelBinding, 1);
702 priv->current_binding->channel = channel;
703 priv->current_binding->peer = *peer;
705 return ret;
707 return FALSE;
708 } else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
709 TURNMessage *msg = g_new0 (TURNMessage, 1);
710 if (!stun_agent_init_request (&priv->agent, &msg->message,
711 msg->buffer, sizeof(msg->buffer), STUN_OLD_SET_ACTIVE_DST)) {
712 g_free (msg);
713 return FALSE;
716 if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_MAGIC_COOKIE,
717 TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS) {
718 g_free (msg);
719 return FALSE;
722 if (priv->username != NULL && priv->username_len > 0) {
723 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
724 priv->username, priv->username_len) != STUN_MESSAGE_RETURN_SUCCESS) {
725 g_free (msg);
726 return FALSE;
730 if (stun_message_append_addr (&msg->message,
731 STUN_ATTRIBUTE_DESTINATION_ADDRESS,
732 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS) {
733 g_free (msg);
734 return FALSE;
737 stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
738 priv->password, priv->password_len);
740 if (stun_len > 0) {
741 priv->current_binding = g_new0 (ChannelBinding, 1);
742 priv->current_binding->channel = 0;
743 priv->current_binding->peer = *peer;
744 priv_send_turn_message (priv, msg);
745 return TRUE;
747 g_free (msg);
748 return FALSE;
749 } else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
750 priv->current_binding = g_new0 (ChannelBinding, 1);
751 priv->current_binding->channel = 0;
752 priv->current_binding->peer = *peer;
753 return TRUE;
754 } else {
755 return FALSE;
758 return FALSE;