Version 0.0.10
[sipe-libnice.git] / socket / turn.c
blobeea55dd6e5703fc12dccda2f101486cb5af38257
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 #define STUN_END_TIMEOUT 8000
59 typedef struct {
60 StunMessage message;
61 uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
62 StunTimer timer;
63 } TURNMessage;
65 typedef struct {
66 NiceAddress peer;
67 uint16_t channel;
68 } ChannelBinding;
70 typedef struct {
71 NiceAgent *nice;
72 StunAgent agent;
73 GList *channels;
74 GList *pending_bindings;
75 ChannelBinding *current_binding;
76 TURNMessage *current_binding_msg;
77 GSource *tick_source;
78 NiceSocket *base_socket;
79 NiceAddress server_addr;
80 uint8_t *username;
81 size_t username_len;
82 uint8_t *password;
83 size_t password_len;
84 NiceTurnSocketCompatibility compatibility;
85 GQueue *send_requests;
86 } TurnPriv;
89 typedef struct {
90 StunTransactionId id;
91 GSource *source;
92 TurnPriv *priv;
93 } SendRequest;
95 static void socket_close (NiceSocket *sock);
96 static gint socket_recv (NiceSocket *sock, NiceAddress *from,
97 guint len, gchar *buf);
98 static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
99 guint len, const gchar *buf);
100 static gboolean socket_is_reliable (NiceSocket *sock);
102 static void priv_process_pending_bindings (TurnPriv *priv);
103 static gboolean priv_retransmissions_tick_unlocked (TurnPriv *priv);
104 static gboolean priv_retransmissions_tick (gpointer pointer);
105 static void priv_schedule_tick (TurnPriv *priv);
106 static void priv_send_turn_message (TurnPriv *priv, TURNMessage *msg);
107 static gboolean priv_send_channel_bind (TurnPriv *priv, StunMessage *resp,
108 uint16_t channel, NiceAddress *peer);
109 static gboolean priv_add_channel_binding (TurnPriv *priv, NiceAddress *peer);
110 static gboolean priv_forget_send_request (gpointer pointer);
113 NiceSocket *
114 nice_turn_socket_new (NiceAgent *agent, NiceAddress *addr,
115 NiceSocket *base_socket, NiceAddress *server_addr,
116 gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility)
118 TurnPriv *priv = g_new0 (TurnPriv, 1);
119 NiceSocket *sock = g_slice_new0 (NiceSocket);
121 if (!sock) {
122 return NULL;
125 if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
126 stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
127 STUN_COMPATIBILITY_RFC5389,
128 STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS);
129 } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
130 stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
131 STUN_COMPATIBILITY_RFC3489,
132 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
133 STUN_AGENT_USAGE_NO_INDICATION_AUTH);
134 } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
135 stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
136 STUN_COMPATIBILITY_RFC3489,
137 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
138 STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
141 priv->nice = agent;
142 priv->channels = NULL;
143 priv->current_binding = NULL;
144 priv->base_socket = base_socket;
146 if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
147 priv->username = g_base64_decode (username, &priv->username_len);
148 priv->password = g_base64_decode (password, &priv->password_len);
149 } else {
150 priv->username = (uint8_t *)g_strdup (username);
151 priv->username_len = (size_t) strlen (username);
152 if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
153 priv->password = NULL;
154 priv->password_len = 0;
155 } else {
156 priv->password = (uint8_t *)g_strdup (password);
157 priv->password_len = (size_t) strlen (password);
160 priv->server_addr = *server_addr;
161 priv->compatibility = compatibility;
162 priv->send_requests = g_queue_new ();
163 sock->addr = *addr;
164 sock->fileno = base_socket->fileno;
165 sock->send = socket_send;
166 sock->recv = socket_recv;
167 sock->is_reliable = socket_is_reliable;
168 sock->close = socket_close;
169 sock->priv = (void *) priv;
170 return sock;
174 static void
175 socket_close (NiceSocket *sock)
177 TurnPriv *priv = (TurnPriv *) sock->priv;
178 GList *i = NULL;
180 for (i = priv->channels; i; i = i->next) {
181 ChannelBinding *b = i->data;
182 g_free (b);
184 g_list_free (priv->channels);
186 for (i = priv->pending_bindings; i; i = i->next) {
187 ChannelBinding *b = i->data;
188 g_free (b);
190 g_list_free (priv->pending_bindings);
192 if (priv->tick_source != NULL) {
193 g_source_destroy (priv->tick_source);
194 g_source_unref (priv->tick_source);
195 priv->tick_source = NULL;
198 for (i = g_queue_peek_head_link (priv->send_requests); i; i = i->next) {
199 SendRequest *r = i->data;
200 g_source_destroy (r->source);
201 g_source_unref (r->source);
202 r->source = NULL;
204 stun_agent_forget_transaction (&priv->agent, r->id);
206 g_slice_free (SendRequest, r);
209 g_queue_free (priv->send_requests);
212 g_free (priv->current_binding);
213 g_free (priv->current_binding_msg);
214 g_free (priv->username);
215 g_free (priv->password);
216 g_free (priv);
219 static gint
220 socket_recv (NiceSocket *sock, NiceAddress *from, guint len, gchar *buf)
222 TurnPriv *priv = (TurnPriv *) sock->priv;
223 uint8_t recv_buf[STUN_MAX_MESSAGE_SIZE];
224 gint recv_len;
225 NiceAddress recv_from;
226 NiceSocket *dummy;;
228 recv_len = nice_socket_recv (priv->base_socket, &recv_from,
229 sizeof(recv_buf), (gchar *) recv_buf);
231 if (recv_len > 0)
232 return nice_turn_socket_parse_recv (sock, &dummy, from, len, buf,
233 &recv_from, (gchar *) recv_buf, (guint) recv_len);
234 else
235 return recv_len;
238 static gboolean
239 socket_send (NiceSocket *sock, const NiceAddress *to,
240 guint len, const gchar *buf)
242 TurnPriv *priv = (TurnPriv *) sock->priv;
243 StunMessage msg;
244 uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
245 size_t msg_len;
246 struct sockaddr_storage sa;
247 GList *i = priv->channels;
248 ChannelBinding *binding = NULL;
250 for (; i; i = i->next) {
251 ChannelBinding *b = i->data;
252 if (nice_address_equal (&b->peer, to)) {
253 binding = b;
254 break;
258 nice_address_copy_to_sockaddr (to, (struct sockaddr *)&sa);
260 if (binding) {
261 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
262 if (len + sizeof(uint32_t) <= sizeof(buffer)) {
263 uint16_t len16 = htons ((uint16_t) len);
264 uint16_t channel16 = htons (binding->channel);
265 memcpy (buffer, &channel16, sizeof(uint16_t));
266 memcpy (buffer + sizeof(uint16_t), &len16,sizeof(uint16_t));
267 memcpy (buffer + sizeof(uint32_t), buf, len);
268 msg_len = len + sizeof(uint32_t);
269 } else {
270 return 0;
272 } else {
273 return nice_socket_send (priv->base_socket, &priv->server_addr, len, buf);
275 } else {
276 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
277 if (!stun_agent_init_indication (&priv->agent, &msg,
278 buffer, sizeof(buffer), STUN_IND_SEND))
279 goto send;
280 if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_PEER_ADDRESS,
281 (struct sockaddr *)&sa, sizeof(sa)) !=
282 STUN_MESSAGE_RETURN_SUCCESS)
283 goto send;
284 } else {
285 if (!stun_agent_init_request (&priv->agent, &msg,
286 buffer, sizeof(buffer), STUN_SEND))
287 goto send;
289 if (stun_message_append32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
290 TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS)
291 goto send;
292 if (priv->username != NULL && priv->username_len > 0) {
293 if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_USERNAME,
294 priv->username, priv->username_len) !=
295 STUN_MESSAGE_RETURN_SUCCESS)
296 goto send;
298 if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_DESTINATION_ADDRESS,
299 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS)
300 goto send;
302 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE &&
303 priv->current_binding &&
304 nice_address_equal (&priv->current_binding->peer, to)) {
305 stun_message_append32 (&msg, STUN_ATTRIBUTE_OPTIONS, 1);
309 if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_DATA,
310 buf, len) != STUN_MESSAGE_RETURN_SUCCESS)
311 goto send;
313 msg_len = stun_agent_finish_message (&priv->agent, &msg,
314 priv->password, priv->password_len);
315 if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST) {
316 SendRequest *req = g_slice_new0 (SendRequest);
318 req->priv = priv;
319 stun_message_id (&msg, req->id);
320 req->source = agent_timeout_add_with_context (priv->nice, STUN_END_TIMEOUT,
321 priv_forget_send_request, req);
322 g_queue_push_tail (priv->send_requests, req);
326 if (msg_len > 0) {
327 return nice_socket_send (priv->base_socket, &priv->server_addr,
328 msg_len, (gchar *)buffer);
330 send:
331 return nice_socket_send (priv->base_socket, to, len, buf);
334 static gboolean
335 socket_is_reliable (NiceSocket *sock)
337 TurnPriv *priv = (TurnPriv *) sock->priv;
338 return nice_socket_is_reliable (priv->base_socket);
341 static gboolean
342 priv_forget_send_request (gpointer pointer)
344 SendRequest *req = pointer;
346 agent_lock ();
348 if (g_source_is_destroyed (g_main_current_source ())) {
349 nice_debug ("Source was destroyed. "
350 "Avoided race condition in turn.c:priv_forget_send_request");
351 agent_unlock ();
352 return FALSE;
355 stun_agent_forget_transaction (&req->priv->agent, req->id);
357 g_queue_remove (req->priv->send_requests, req);
359 g_source_destroy (req->source);
360 g_source_unref (req->source);
361 req->source = NULL;
363 agent_unlock ();
365 g_slice_free (SendRequest, req);
367 return FALSE;
371 gint
372 nice_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
373 NiceAddress *from, guint len, gchar *buf,
374 NiceAddress *recv_from, gchar *recv_buf, guint recv_len)
377 TurnPriv *priv = (TurnPriv *) sock->priv;
378 StunValidationStatus valid;
379 StunMessage msg;
380 struct sockaddr_storage sa;
381 socklen_t from_len = sizeof (sa);
382 GList *i = priv->channels;
383 ChannelBinding *binding = NULL;
385 if (nice_address_equal (&priv->server_addr, recv_from)) {
386 valid = stun_agent_validate (&priv->agent, &msg,
387 (uint8_t *) recv_buf, (size_t) recv_len, NULL, NULL);
389 if (valid == STUN_VALIDATION_SUCCESS) {
390 if (priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
391 uint32_t cookie;
392 if (stun_message_find32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
393 &cookie) != STUN_MESSAGE_RETURN_SUCCESS)
394 goto recv;
395 if (cookie != TURN_MAGIC_COOKIE)
396 goto recv;
399 if (stun_message_get_method (&msg) == STUN_SEND) {
400 if (stun_message_get_class (&msg) == STUN_RESPONSE) {
401 SendRequest *req = NULL;
402 GList *i = g_queue_peek_head_link (priv->send_requests);
403 StunTransactionId msg_id;
405 stun_message_id (&msg, msg_id);
407 for (; i; i = i->next) {
408 SendRequest *r = i->data;
409 if (memcmp (&r->id, msg_id, sizeof(StunTransactionId)) == 0) {
410 req = r;
411 break;
415 if (req) {
416 g_source_destroy (req->source);
417 g_source_unref (req->source);
418 req->source = NULL;
420 g_queue_remove (priv->send_requests, req);
422 g_slice_free (SendRequest, req);
425 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
426 uint32_t opts = 0;
427 if (stun_message_find32 (&msg, STUN_ATTRIBUTE_OPTIONS, &opts) ==
428 STUN_MESSAGE_RETURN_SUCCESS && opts & 0x1)
429 goto msn_google_lock;
432 return 0;
433 } else if (stun_message_get_method (&msg) == STUN_OLD_SET_ACTIVE_DST) {
434 StunTransactionId request_id;
435 StunTransactionId response_id;
436 if (priv->current_binding && priv->current_binding_msg) {
437 stun_message_id (&msg, response_id);
438 stun_message_id (&priv->current_binding_msg->message, request_id);
439 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
440 g_free (priv->current_binding_msg);
441 priv->current_binding_msg = NULL;
443 if (stun_message_get_class (&msg) == STUN_RESPONSE &&
444 priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
445 goto msn_google_lock;
446 } else {
447 g_free (priv->current_binding);
448 priv->current_binding = NULL;
453 return 0;
454 } else if (stun_message_get_method (&msg) == STUN_CHANNELBIND) {
455 StunTransactionId request_id;
456 StunTransactionId response_id;
457 if (priv->current_binding && priv->current_binding_msg) {
458 stun_message_id (&msg, response_id);
459 stun_message_id (&priv->current_binding_msg->message, request_id);
460 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
461 if (stun_message_get_class (&msg) == STUN_ERROR) {
462 int code = -1;
463 uint8_t *sent_realm = NULL;
464 uint8_t *recv_realm = NULL;
465 uint16_t sent_realm_len = 0;
466 uint16_t recv_realm_len = 0;
468 sent_realm = (uint8_t *) stun_message_find (
469 &priv->current_binding_msg->message,
470 STUN_ATTRIBUTE_REALM, &sent_realm_len);
471 recv_realm = (uint8_t *) stun_message_find (&msg,
472 STUN_ATTRIBUTE_REALM, &recv_realm_len);
474 /* check for unauthorized error response */
475 if (stun_message_find_error (&msg, &code) ==
476 STUN_MESSAGE_RETURN_SUCCESS &&
477 (code == 438 || (code == 401 &&
478 !(recv_realm != NULL &&
479 recv_realm_len > 0 &&
480 recv_realm_len == sent_realm_len &&
481 sent_realm != NULL &&
482 memcmp (sent_realm, recv_realm, sent_realm_len) == 0)))) {
483 g_free (priv->current_binding_msg);
484 priv->current_binding_msg = NULL;
485 if (priv->current_binding) {
486 priv_send_channel_bind (priv, &msg,
487 priv->current_binding->channel,
488 &priv->current_binding->peer);
490 } else {
491 g_free (priv->current_binding);
492 priv->current_binding = NULL;
493 g_free (priv->current_binding_msg);
494 priv->current_binding_msg = NULL;
495 priv_process_pending_bindings (priv);
497 } else if (stun_message_get_class (&msg) == STUN_RESPONSE) {
498 g_free (priv->current_binding_msg);
499 priv->current_binding_msg = NULL;
500 if (priv->current_binding) {
501 priv->channels = g_list_append (priv->channels,
502 priv->current_binding);
503 priv->current_binding = NULL;
505 priv_process_pending_bindings (priv);
509 return 0;
510 } else if (stun_message_get_class (&msg) == STUN_INDICATION &&
511 stun_message_get_method (&msg) == STUN_IND_DATA) {
512 uint16_t data_len;
513 uint8_t *data;
515 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
516 if (stun_message_find_xor_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
517 (struct sockaddr *)&sa, &from_len) !=
518 STUN_MESSAGE_RETURN_SUCCESS)
519 goto recv;
520 } else {
521 if (stun_message_find_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
522 (struct sockaddr *)&sa, &from_len) !=
523 STUN_MESSAGE_RETURN_SUCCESS)
524 goto recv;
527 data = (uint8_t *) stun_message_find (&msg, STUN_ATTRIBUTE_DATA,
528 &data_len);
530 if (data == NULL)
531 goto recv;
533 nice_address_set_from_sockaddr (from, (struct sockaddr *)&sa);
535 *from_sock = sock;
536 memmove (buf, data, len > data_len ? data_len : len);
537 return len > data_len ? data_len : len;
538 } else {
539 goto recv;
544 recv:
545 for (i = priv->channels; i; i = i->next) {
546 ChannelBinding *b = i->data;
547 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
548 if (b->channel == ntohs(((uint16_t *)recv_buf)[0])) {
549 recv_len = ntohs (((uint16_t *)recv_buf)[1]);
550 recv_buf += sizeof(uint32_t);
551 binding = b;
552 break;
554 } else {
555 binding = b;
556 break;
560 if (binding) {
561 *from = binding->peer;
562 *from_sock = sock;
563 } else {
564 *from = *recv_from;
567 memmove (buf, recv_buf, len > recv_len ? recv_len : len);
568 return len > recv_len ? recv_len : len;
570 msn_google_lock:
572 if (priv->current_binding) {
573 GList *i = priv->channels;
574 for (; i; i = i->next) {
575 ChannelBinding *b = i->data;
576 g_free (b);
578 g_list_free (priv->channels);
579 priv->channels = g_list_append (NULL, priv->current_binding);
580 priv->current_binding = NULL;
581 priv_process_pending_bindings (priv);
584 return 0;
587 gboolean
588 nice_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer)
590 TurnPriv *priv = (TurnPriv *) sock->priv;
591 return priv_add_channel_binding (priv, peer);
594 static void
595 priv_process_pending_bindings (TurnPriv *priv)
597 gboolean ret = FALSE;
598 while (priv->pending_bindings != NULL && ret == FALSE) {
599 NiceAddress *peer = priv->pending_bindings->data;
600 ret = priv_add_channel_binding (priv, peer);
601 priv->pending_bindings = g_list_remove (priv->pending_bindings, peer);
602 nice_address_free (peer);
606 static gboolean
607 priv_retransmissions_tick_unlocked (TurnPriv *priv)
609 if (priv->current_binding_msg) {
610 switch (stun_timer_refresh (&priv->current_binding_msg->timer)) {
611 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
613 /* Time out */
614 StunTransactionId id;
616 g_free (priv->current_binding);
617 priv->current_binding = NULL;
618 g_free (priv->current_binding_msg);
619 priv->current_binding_msg = NULL;
621 stun_message_id (&priv->current_binding_msg->message, id);
622 stun_agent_forget_transaction (&priv->agent, id);
624 priv_process_pending_bindings (priv);
625 break;
627 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
628 /* Retransmit */
629 nice_socket_send (priv->base_socket, &priv->server_addr,
630 stun_message_length (&priv->current_binding_msg->message),
631 (gchar *)priv->current_binding_msg->buffer);
632 break;
633 case STUN_USAGE_TIMER_RETURN_SUCCESS:
634 break;
638 priv_schedule_tick (priv);
639 return FALSE;
643 static gboolean
644 priv_retransmissions_tick (gpointer pointer)
646 TurnPriv *priv = pointer;
647 gboolean ret;
649 agent_lock ();
650 if (g_source_is_destroyed (g_main_current_source ())) {
651 nice_debug ("Source was destroyed. "
652 "Avoided race condition in turn.c:priv_retransmissions_tick");
653 agent_unlock ();
654 return FALSE;
657 ret = priv_retransmissions_tick_unlocked (priv);
658 if (ret == FALSE) {
659 if (priv->tick_source != NULL) {
660 g_source_destroy (priv->tick_source);
661 g_source_unref (priv->tick_source);
662 priv->tick_source = NULL;
665 agent_unlock ();
667 return ret;
670 static void
671 priv_schedule_tick (TurnPriv *priv)
673 if (priv->tick_source != NULL) {
674 g_source_destroy (priv->tick_source);
675 g_source_unref (priv->tick_source);
676 priv->tick_source = NULL;
679 if (priv->current_binding_msg) {
680 guint timeout = stun_timer_remainder (&priv->current_binding_msg->timer);
681 if (timeout > 0) {
682 priv->tick_source = agent_timeout_add_with_context (priv->nice, timeout,
683 priv_retransmissions_tick, priv);
684 } else {
685 priv_retransmissions_tick_unlocked (priv);
690 static void
691 priv_send_turn_message (TurnPriv *priv, TURNMessage *msg)
693 size_t stun_len = stun_message_length (&msg->message);
695 if (priv->current_binding_msg) {
696 g_free (priv->current_binding_msg);
697 priv->current_binding_msg = NULL;
700 nice_socket_send (priv->base_socket, &priv->server_addr,
701 stun_len, (gchar *)msg->buffer);
703 if (nice_socket_is_reliable (priv->base_socket)) {
704 stun_timer_start_reliable (&msg->timer);
705 } else {
706 stun_timer_start (&msg->timer);
709 priv->current_binding_msg = msg;
710 priv_schedule_tick (priv);
713 static gboolean
714 priv_send_channel_bind (TurnPriv *priv, StunMessage *resp,
715 uint16_t channel, NiceAddress *peer)
717 uint32_t channel_attr = channel << 16;
718 size_t stun_len;
719 struct sockaddr_storage sa;
720 TURNMessage *msg = g_new0 (TURNMessage, 1);
722 nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);
724 if (!stun_agent_init_request (&priv->agent, &msg->message,
725 msg->buffer, sizeof(msg->buffer), STUN_CHANNELBIND)) {
726 g_free (msg);
727 return FALSE;
730 if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_CHANNEL_NUMBER,
731 channel_attr) != STUN_MESSAGE_RETURN_SUCCESS) {
732 g_free (msg);
733 return FALSE;
736 if (stun_message_append_xor_addr (&msg->message, STUN_ATTRIBUTE_PEER_ADDRESS,
737 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS) {
738 g_free (msg);
739 return FALSE;
742 if (priv->username != NULL && priv->username_len > 0) {
743 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
744 priv->username, priv->username_len) != STUN_MESSAGE_RETURN_SUCCESS) {
745 g_free (msg);
746 return FALSE;
750 if (resp) {
751 uint8_t *realm;
752 uint8_t *nonce;
753 uint16_t len;
755 realm = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_REALM, &len);
756 if (realm != NULL) {
757 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM,
758 realm, len) != STUN_MESSAGE_RETURN_SUCCESS) {
759 g_free (msg);
760 return 0;
763 nonce = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_NONCE, &len);
764 if (nonce != NULL) {
765 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE,
766 nonce, len) != STUN_MESSAGE_RETURN_SUCCESS) {
767 g_free (msg);
768 return 0;
773 stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
774 priv->password, priv->password_len);
776 if (stun_len > 0) {
777 priv_send_turn_message (priv, msg);
778 return TRUE;
781 g_free (msg);
782 return FALSE;
785 static gboolean
786 priv_add_channel_binding (TurnPriv *priv, NiceAddress *peer)
788 size_t stun_len;
789 struct sockaddr_storage sa;
791 nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);
793 if (priv->current_binding) {
794 NiceAddress * pending= nice_address_new ();
795 *pending = *peer;
796 priv->pending_bindings = g_list_append (priv->pending_bindings, pending);
797 return FALSE;
800 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
801 uint16_t channel = 0x4000;
802 GList *i = priv->channels;
803 for (; i; i = i->next) {
804 ChannelBinding *b = i->data;
805 if (channel == b->channel) {
806 i = priv->channels;
807 channel++;
808 continue;
812 if (channel >= 0x4000 && channel < 0xffff) {
813 gboolean ret = priv_send_channel_bind (priv, NULL, channel, peer);
814 if (ret) {
815 priv->current_binding = g_new0 (ChannelBinding, 1);
816 priv->current_binding->channel = channel;
817 priv->current_binding->peer = *peer;
819 return ret;
821 return FALSE;
822 } else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
823 TURNMessage *msg = g_new0 (TURNMessage, 1);
824 if (!stun_agent_init_request (&priv->agent, &msg->message,
825 msg->buffer, sizeof(msg->buffer), STUN_OLD_SET_ACTIVE_DST)) {
826 g_free (msg);
827 return FALSE;
830 if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_MAGIC_COOKIE,
831 TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS) {
832 g_free (msg);
833 return FALSE;
836 if (priv->username != NULL && priv->username_len > 0) {
837 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
838 priv->username, priv->username_len) != STUN_MESSAGE_RETURN_SUCCESS) {
839 g_free (msg);
840 return FALSE;
844 if (stun_message_append_addr (&msg->message,
845 STUN_ATTRIBUTE_DESTINATION_ADDRESS,
846 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS) {
847 g_free (msg);
848 return FALSE;
851 stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
852 priv->password, priv->password_len);
854 if (stun_len > 0) {
855 priv->current_binding = g_new0 (ChannelBinding, 1);
856 priv->current_binding->channel = 0;
857 priv->current_binding->peer = *peer;
858 priv_send_turn_message (priv, msg);
859 return TRUE;
861 g_free (msg);
862 return FALSE;
863 } else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
864 priv->current_binding = g_new0 (ChannelBinding, 1);
865 priv->current_binding->channel = 0;
866 priv->current_binding->peer = *peer;
867 return TRUE;
868 } else {
869 return FALSE;
872 return FALSE;