Improve test-turn to test for turnserver locally too
[sipe-libnice.git] / socket / turn.c
blob997b7029d77ab428a941b0c5b2f3b6068d00b9fd
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 gint ref;
94 } SendRequest;
96 static void socket_close (NiceSocket *sock);
97 static gint socket_recv (NiceSocket *sock, NiceAddress *from,
98 guint len, gchar *buf);
99 static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
100 guint len, const gchar *buf);
101 static gboolean socket_is_reliable (NiceSocket *sock);
103 static void priv_process_pending_bindings (TurnPriv *priv);
104 static gboolean priv_retransmissions_tick_unlocked (TurnPriv *priv);
105 static gboolean priv_retransmissions_tick (gpointer pointer);
106 static void priv_schedule_tick (TurnPriv *priv);
107 static void priv_send_turn_message (TurnPriv *priv, TURNMessage *msg);
108 static gboolean priv_send_channel_bind (TurnPriv *priv, StunMessage *resp,
109 uint16_t channel, NiceAddress *peer);
110 static gboolean priv_add_channel_binding (TurnPriv *priv, NiceAddress *peer);
111 static gboolean priv_forget_send_request (gpointer pointer);
114 NiceSocket *
115 nice_turn_socket_new (NiceAgent *agent, NiceAddress *addr,
116 NiceSocket *base_socket, NiceAddress *server_addr,
117 gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility)
119 TurnPriv *priv = g_new0 (TurnPriv, 1);
120 NiceSocket *sock = g_slice_new0 (NiceSocket);
122 if (!sock) {
123 return NULL;
126 if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
127 stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
128 STUN_COMPATIBILITY_RFC5389,
129 STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS);
130 } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
131 stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
132 STUN_COMPATIBILITY_RFC3489,
133 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
134 STUN_AGENT_USAGE_NO_INDICATION_AUTH);
135 } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
136 stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
137 STUN_COMPATIBILITY_RFC3489,
138 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
139 STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
142 priv->nice = agent;
143 priv->channels = NULL;
144 priv->current_binding = NULL;
145 priv->base_socket = base_socket;
147 if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
148 priv->username = g_base64_decode (username, &priv->username_len);
149 priv->password = g_base64_decode (password, &priv->password_len);
150 } else {
151 priv->username = (uint8_t *)g_strdup (username);
152 priv->username_len = (size_t) strlen (username);
153 if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
154 priv->password = NULL;
155 priv->password_len = 0;
156 } else {
157 priv->password = (uint8_t *)g_strdup (password);
158 priv->password_len = (size_t) strlen (password);
161 priv->server_addr = *server_addr;
162 priv->compatibility = compatibility;
163 priv->send_requests = g_queue_new ();
164 sock->addr = *addr;
165 sock->fileno = base_socket->fileno;
166 sock->send = socket_send;
167 sock->recv = socket_recv;
168 sock->is_reliable = socket_is_reliable;
169 sock->close = socket_close;
170 sock->priv = (void *) priv;
171 return sock;
175 static void
176 socket_close (NiceSocket *sock)
178 TurnPriv *priv = (TurnPriv *) sock->priv;
179 GList *i = NULL;
181 for (i = priv->channels; i; i = i->next) {
182 ChannelBinding *b = i->data;
183 g_free (b);
185 g_list_free (priv->channels);
187 for (i = priv->pending_bindings; i; i = i->next) {
188 ChannelBinding *b = i->data;
189 g_free (b);
191 g_list_free (priv->pending_bindings);
193 if (priv->tick_source != NULL) {
194 g_source_destroy (priv->tick_source);
195 g_source_unref (priv->tick_source);
196 priv->tick_source = NULL;
199 for (i = g_queue_peek_head_link (priv->send_requests); i; i = i->next) {
200 SendRequest *r = i->data;
201 g_source_destroy (r->source);
202 g_source_unref (r->source);
203 g_slice_free (SendRequest, r);
205 g_queue_free (priv->send_requests);
208 g_free (priv->current_binding);
209 g_free (priv->current_binding_msg);
210 g_free (priv->username);
211 g_free (priv->password);
212 g_free (priv);
215 static gint
216 socket_recv (NiceSocket *sock, NiceAddress *from, guint len, gchar *buf)
218 TurnPriv *priv = (TurnPriv *) sock->priv;
219 uint8_t recv_buf[STUN_MAX_MESSAGE_SIZE];
220 gint recv_len;
221 NiceAddress recv_from;
222 NiceSocket *dummy;;
224 recv_len = nice_socket_recv (priv->base_socket, &recv_from,
225 sizeof(recv_buf), (gchar *) recv_buf);
227 if (recv_len > 0)
228 return nice_turn_socket_parse_recv (sock, &dummy, from, len, buf,
229 &recv_from, (gchar *) recv_buf, (guint) recv_len);
230 else
231 return recv_len;
234 static gboolean
235 socket_send (NiceSocket *sock, const NiceAddress *to,
236 guint len, const gchar *buf)
238 TurnPriv *priv = (TurnPriv *) sock->priv;
239 StunMessage msg;
240 uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
241 size_t msg_len;
242 struct sockaddr_storage sa;
243 GList *i = priv->channels;
244 ChannelBinding *binding = NULL;
246 for (; i; i = i->next) {
247 ChannelBinding *b = i->data;
248 if (nice_address_equal (&b->peer, to)) {
249 binding = b;
250 break;
254 nice_address_copy_to_sockaddr (to, (struct sockaddr *)&sa);
256 if (binding) {
257 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
258 if (len + sizeof(uint32_t) <= sizeof(buffer)) {
259 uint16_t len16 = htons ((uint16_t) len);
260 uint16_t channel16 = htons (binding->channel);
261 memcpy (buffer, &channel16, sizeof(uint16_t));
262 memcpy (buffer + sizeof(uint16_t), &len16,sizeof(uint16_t));
263 memcpy (buffer + sizeof(uint32_t), buf, len);
264 msg_len = len + sizeof(uint32_t);
265 } else {
266 return 0;
268 } else {
269 return nice_socket_send (priv->base_socket, &priv->server_addr, len, buf);
271 } else {
272 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
273 if (!stun_agent_init_indication (&priv->agent, &msg,
274 buffer, sizeof(buffer), STUN_IND_SEND))
275 goto send;
276 if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_PEER_ADDRESS,
277 (struct sockaddr *)&sa, sizeof(sa)) !=
278 STUN_MESSAGE_RETURN_SUCCESS)
279 goto send;
280 } else {
281 if (!stun_agent_init_request (&priv->agent, &msg,
282 buffer, sizeof(buffer), STUN_SEND))
283 goto send;
285 if (stun_message_append32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
286 TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS)
287 goto send;
288 if (priv->username != NULL && priv->username_len > 0) {
289 if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_USERNAME,
290 priv->username, priv->username_len) !=
291 STUN_MESSAGE_RETURN_SUCCESS)
292 goto send;
294 if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_DESTINATION_ADDRESS,
295 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS)
296 goto send;
298 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE &&
299 priv->current_binding &&
300 nice_address_equal (&priv->current_binding->peer, to)) {
301 stun_message_append32 (&msg, STUN_ATTRIBUTE_OPTIONS, 1);
305 if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_DATA,
306 buf, len) != STUN_MESSAGE_RETURN_SUCCESS)
307 goto send;
309 msg_len = stun_agent_finish_message (&priv->agent, &msg,
310 priv->password, priv->password_len);
311 if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST) {
312 SendRequest *req = g_slice_new0 (SendRequest);
314 req->priv = priv;
315 stun_message_id (&msg, req->id);
316 req->source = agent_timeout_add_with_context (priv->nice, STUN_END_TIMEOUT,
317 priv_forget_send_request, req);
318 g_queue_push_tail (priv->send_requests, req);
319 g_atomic_int_inc (&req->ref);
323 if (msg_len > 0) {
324 return nice_socket_send (priv->base_socket, &priv->server_addr,
325 msg_len, (gchar *)buffer);
327 send:
328 return nice_socket_send (priv->base_socket, to, len, buf);
331 static gboolean
332 socket_is_reliable (NiceSocket *sock)
334 TurnPriv *priv = (TurnPriv *) sock->priv;
335 return nice_socket_is_reliable (priv->base_socket);
338 static gboolean
339 priv_forget_send_request (gpointer pointer)
341 SendRequest *req = pointer;
343 g_atomic_int_inc (&req->ref);
345 g_static_rec_mutex_lock (&req->priv->nice->mutex);
347 stun_agent_forget_transaction (&req->priv->agent, req->id);
349 g_source_destroy (req->source);
350 g_source_unref (req->source);
352 if (g_queue_index (req->priv->send_requests, req) != -1) {
353 g_queue_remove (req->priv->send_requests, req);
354 (void)g_atomic_int_dec_and_test (&req->ref);
357 g_static_rec_mutex_unlock (&req->priv->nice->mutex);
359 if (g_atomic_int_dec_and_test (&req->ref))
360 g_slice_free (SendRequest, req);
362 return FALSE;
366 gint
367 nice_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
368 NiceAddress *from, guint len, gchar *buf,
369 NiceAddress *recv_from, gchar *recv_buf, guint recv_len)
372 TurnPriv *priv = (TurnPriv *) sock->priv;
373 StunValidationStatus valid;
374 StunMessage msg;
375 struct sockaddr_storage sa;
376 socklen_t from_len = sizeof (sa);
377 GList *i = priv->channels;
378 ChannelBinding *binding = NULL;
380 if (nice_address_equal (&priv->server_addr, recv_from)) {
381 valid = stun_agent_validate (&priv->agent, &msg,
382 (uint8_t *) recv_buf, (size_t) recv_len, NULL, NULL);
384 if (valid == STUN_VALIDATION_SUCCESS) {
385 if (priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
386 uint32_t cookie;
387 if (stun_message_find32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
388 &cookie) != STUN_MESSAGE_RETURN_SUCCESS)
389 goto recv;
390 if (cookie != TURN_MAGIC_COOKIE)
391 goto recv;
394 if (stun_message_get_method (&msg) == STUN_SEND) {
395 if (stun_message_get_class (&msg) == STUN_RESPONSE) {
396 SendRequest *req = NULL;
397 GList *i = g_queue_peek_head_link (priv->send_requests);
398 StunTransactionId msg_id;
400 stun_message_id (&msg, msg_id);
402 for (; i; i = i->next) {
403 SendRequest *r = i->data;
404 if (memcmp (&r->id, msg_id, sizeof(StunTransactionId)) == 0) {
405 req = r;
406 break;
410 if (req) {
411 g_source_destroy (req->source);
412 g_source_unref (req->source);
414 g_queue_remove (priv->send_requests, req);
416 if (g_atomic_int_dec_and_test (&req->ref))
417 g_slice_free (SendRequest, req);
420 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
421 uint32_t opts = 0;
422 if (stun_message_find32 (&msg, STUN_ATTRIBUTE_OPTIONS, &opts) ==
423 STUN_MESSAGE_RETURN_SUCCESS && opts & 0x1)
424 goto msn_google_lock;
427 return 0;
428 } else if (stun_message_get_method (&msg) == STUN_OLD_SET_ACTIVE_DST) {
429 StunTransactionId request_id;
430 StunTransactionId response_id;
431 if (priv->current_binding && priv->current_binding_msg) {
432 stun_message_id (&msg, response_id);
433 stun_message_id (&priv->current_binding_msg->message, request_id);
434 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
435 g_free (priv->current_binding_msg);
436 priv->current_binding_msg = NULL;
438 if (stun_message_get_class (&msg) == STUN_RESPONSE &&
439 priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
440 goto msn_google_lock;
441 } else {
442 g_free (priv->current_binding);
443 priv->current_binding = NULL;
448 return 0;
449 } else if (stun_message_get_method (&msg) == STUN_CHANNELBIND) {
450 StunTransactionId request_id;
451 StunTransactionId response_id;
452 if (priv->current_binding && priv->current_binding_msg) {
453 stun_message_id (&msg, response_id);
454 stun_message_id (&priv->current_binding_msg->message, request_id);
455 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
456 if (stun_message_get_class (&msg) == STUN_ERROR) {
457 int code = -1;
458 uint8_t *sent_realm = NULL;
459 uint8_t *recv_realm = NULL;
460 uint16_t sent_realm_len = 0;
461 uint16_t recv_realm_len = 0;
463 sent_realm = (uint8_t *) stun_message_find (
464 &priv->current_binding_msg->message,
465 STUN_ATTRIBUTE_REALM, &sent_realm_len);
466 recv_realm = (uint8_t *) stun_message_find (&msg,
467 STUN_ATTRIBUTE_REALM, &recv_realm_len);
469 /* check for unauthorized error response */
470 if (stun_message_find_error (&msg, &code) ==
471 STUN_MESSAGE_RETURN_SUCCESS &&
472 (code == 438 || (code == 401 &&
473 !(recv_realm != NULL &&
474 recv_realm_len > 0 &&
475 recv_realm_len == sent_realm_len &&
476 sent_realm != NULL &&
477 memcmp (sent_realm, recv_realm, sent_realm_len) == 0)))) {
478 g_free (priv->current_binding_msg);
479 priv->current_binding_msg = NULL;
480 if (priv->current_binding) {
481 priv_send_channel_bind (priv, &msg,
482 priv->current_binding->channel,
483 &priv->current_binding->peer);
485 } else {
486 g_free (priv->current_binding);
487 priv->current_binding = NULL;
488 g_free (priv->current_binding_msg);
489 priv->current_binding_msg = NULL;
490 priv_process_pending_bindings (priv);
492 } else if (stun_message_get_class (&msg) == STUN_RESPONSE) {
493 g_free (priv->current_binding_msg);
494 priv->current_binding_msg = NULL;
495 if (priv->current_binding) {
496 priv->channels = g_list_append (priv->channels,
497 priv->current_binding);
498 priv->current_binding = NULL;
500 priv_process_pending_bindings (priv);
504 return 0;
505 } else if (stun_message_get_class (&msg) == STUN_INDICATION &&
506 stun_message_get_method (&msg) == STUN_IND_DATA) {
507 uint16_t data_len;
508 uint8_t *data;
510 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
511 if (stun_message_find_xor_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
512 (struct sockaddr *)&sa, &from_len) !=
513 STUN_MESSAGE_RETURN_SUCCESS)
514 goto recv;
515 } else {
516 if (stun_message_find_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
517 (struct sockaddr *)&sa, &from_len) !=
518 STUN_MESSAGE_RETURN_SUCCESS)
519 goto recv;
522 data = (uint8_t *) stun_message_find (&msg, STUN_ATTRIBUTE_DATA,
523 &data_len);
525 if (data == NULL)
526 goto recv;
528 nice_address_set_from_sockaddr (from, (struct sockaddr *)&sa);
530 *from_sock = sock;
531 memmove (buf, data, len > data_len ? data_len : len);
532 return len > data_len ? data_len : len;
533 } else {
534 goto recv;
539 recv:
540 for (i = priv->channels; i; i = i->next) {
541 ChannelBinding *b = i->data;
542 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
543 if (b->channel == ntohs(((uint16_t *)recv_buf)[0])) {
544 recv_len = ntohs (((uint16_t *)recv_buf)[1]);
545 recv_buf += sizeof(uint32_t);
546 binding = b;
547 break;
549 } else {
550 binding = b;
551 break;
555 if (binding) {
556 *from = binding->peer;
557 *from_sock = sock;
558 } else {
559 *from = *recv_from;
562 memmove (buf, recv_buf, len > recv_len ? recv_len : len);
563 return len > recv_len ? recv_len : len;
565 msn_google_lock:
567 if (priv->current_binding) {
568 GList *i = priv->channels;
569 for (; i; i = i->next) {
570 ChannelBinding *b = i->data;
571 g_free (b);
573 g_list_free (priv->channels);
574 priv->channels = g_list_append (NULL, priv->current_binding);
575 priv->current_binding = NULL;
576 priv_process_pending_bindings (priv);
579 return 0;
582 gboolean
583 nice_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer)
585 TurnPriv *priv = (TurnPriv *) sock->priv;
586 return priv_add_channel_binding (priv, peer);
589 static void
590 priv_process_pending_bindings (TurnPriv *priv)
592 gboolean ret = FALSE;
593 while (priv->pending_bindings != NULL && ret == FALSE) {
594 NiceAddress *peer = priv->pending_bindings->data;
595 ret = priv_add_channel_binding (priv, peer);
596 priv->pending_bindings = g_list_remove (priv->pending_bindings, peer);
597 nice_address_free (peer);
601 static gboolean
602 priv_retransmissions_tick_unlocked (TurnPriv *priv)
604 if (priv->current_binding_msg) {
605 switch (stun_timer_refresh (&priv->current_binding_msg->timer)) {
606 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
608 /* Time out */
609 StunTransactionId id;
611 g_free (priv->current_binding);
612 priv->current_binding = NULL;
613 g_free (priv->current_binding_msg);
614 priv->current_binding_msg = NULL;
616 stun_message_id (&priv->current_binding_msg->message, id);
617 stun_agent_forget_transaction (&priv->agent, id);
619 priv_process_pending_bindings (priv);
620 break;
622 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
623 /* Retransmit */
624 nice_socket_send (priv->base_socket, &priv->server_addr,
625 stun_message_length (&priv->current_binding_msg->message),
626 (gchar *)priv->current_binding_msg->buffer);
627 break;
628 case STUN_USAGE_TIMER_RETURN_SUCCESS:
629 break;
633 priv_schedule_tick (priv);
634 return FALSE;
638 static gboolean
639 priv_retransmissions_tick (gpointer pointer)
641 TurnPriv *priv = pointer;
642 gboolean ret;
644 g_static_rec_mutex_lock (&priv->nice->mutex);
645 ret = priv_retransmissions_tick_unlocked (priv);
646 g_static_rec_mutex_unlock (&priv->nice->mutex);
648 return ret;
651 static void
652 priv_schedule_tick (TurnPriv *priv)
654 if (priv->tick_source != NULL) {
655 g_source_destroy (priv->tick_source);
656 g_source_unref (priv->tick_source);
657 priv->tick_source = NULL;
660 if (priv->current_binding_msg) {
661 guint timeout = stun_timer_remainder (&priv->current_binding_msg->timer);
662 if (timeout > 0) {
663 priv->tick_source = agent_timeout_add_with_context (priv->nice, timeout,
664 priv_retransmissions_tick, priv);
665 } else {
666 priv_retransmissions_tick_unlocked (priv);
671 static void
672 priv_send_turn_message (TurnPriv *priv, TURNMessage *msg)
674 size_t stun_len = stun_message_length (&msg->message);
676 if (priv->current_binding_msg) {
677 g_free (priv->current_binding_msg);
678 priv->current_binding_msg = NULL;
681 nice_socket_send (priv->base_socket, &priv->server_addr,
682 stun_len, (gchar *)msg->buffer);
684 if (nice_socket_is_reliable (priv->base_socket)) {
685 stun_timer_start_reliable (&msg->timer);
686 } else {
687 stun_timer_start (&msg->timer);
690 priv->current_binding_msg = msg;
691 priv_schedule_tick (priv);
694 static gboolean
695 priv_send_channel_bind (TurnPriv *priv, StunMessage *resp,
696 uint16_t channel, NiceAddress *peer)
698 uint32_t channel_attr = channel << 16;
699 size_t stun_len;
700 struct sockaddr_storage sa;
701 TURNMessage *msg = g_new0 (TURNMessage, 1);
703 nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);
705 if (!stun_agent_init_request (&priv->agent, &msg->message,
706 msg->buffer, sizeof(msg->buffer), STUN_CHANNELBIND)) {
707 g_free (msg);
708 return FALSE;
711 if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_CHANNEL_NUMBER,
712 channel_attr) != STUN_MESSAGE_RETURN_SUCCESS) {
713 g_free (msg);
714 return FALSE;
717 if (stun_message_append_xor_addr (&msg->message, STUN_ATTRIBUTE_PEER_ADDRESS,
718 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS) {
719 g_free (msg);
720 return FALSE;
723 if (priv->username != NULL && priv->username_len > 0) {
724 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
725 priv->username, priv->username_len) != STUN_MESSAGE_RETURN_SUCCESS) {
726 g_free (msg);
727 return FALSE;
731 if (resp) {
732 uint8_t *realm;
733 uint8_t *nonce;
734 uint16_t len;
736 realm = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_REALM, &len);
737 if (realm != NULL) {
738 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM,
739 realm, len) != STUN_MESSAGE_RETURN_SUCCESS) {
740 g_free (msg);
741 return 0;
744 nonce = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_NONCE, &len);
745 if (nonce != NULL) {
746 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE,
747 nonce, len) != STUN_MESSAGE_RETURN_SUCCESS) {
748 g_free (msg);
749 return 0;
754 stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
755 priv->password, priv->password_len);
757 if (stun_len > 0) {
758 priv_send_turn_message (priv, msg);
759 return TRUE;
762 g_free (msg);
763 return FALSE;
766 static gboolean
767 priv_add_channel_binding (TurnPriv *priv, NiceAddress *peer)
769 size_t stun_len;
770 struct sockaddr_storage sa;
772 nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);
774 if (priv->current_binding) {
775 NiceAddress * pending= nice_address_new ();
776 *pending = *peer;
777 priv->pending_bindings = g_list_append (priv->pending_bindings, pending);
778 return FALSE;
781 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
782 uint16_t channel = 0x4000;
783 GList *i = priv->channels;
784 for (; i; i = i->next) {
785 ChannelBinding *b = i->data;
786 if (channel == b->channel) {
787 i = priv->channels;
788 channel++;
789 continue;
793 if (channel >= 0x4000 && channel < 0xffff) {
794 gboolean ret = priv_send_channel_bind (priv, NULL, channel, peer);
795 if (ret) {
796 priv->current_binding = g_new0 (ChannelBinding, 1);
797 priv->current_binding->channel = channel;
798 priv->current_binding->peer = *peer;
800 return ret;
802 return FALSE;
803 } else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
804 TURNMessage *msg = g_new0 (TURNMessage, 1);
805 if (!stun_agent_init_request (&priv->agent, &msg->message,
806 msg->buffer, sizeof(msg->buffer), STUN_OLD_SET_ACTIVE_DST)) {
807 g_free (msg);
808 return FALSE;
811 if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_MAGIC_COOKIE,
812 TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS) {
813 g_free (msg);
814 return FALSE;
817 if (priv->username != NULL && priv->username_len > 0) {
818 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
819 priv->username, priv->username_len) != STUN_MESSAGE_RETURN_SUCCESS) {
820 g_free (msg);
821 return FALSE;
825 if (stun_message_append_addr (&msg->message,
826 STUN_ATTRIBUTE_DESTINATION_ADDRESS,
827 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS) {
828 g_free (msg);
829 return FALSE;
832 stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
833 priv->password, priv->password_len);
835 if (stun_len > 0) {
836 priv->current_binding = g_new0 (ChannelBinding, 1);
837 priv->current_binding->channel = 0;
838 priv->current_binding->peer = *peer;
839 priv_send_turn_message (priv, msg);
840 return TRUE;
842 g_free (msg);
843 return FALSE;
844 } else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
845 priv->current_binding = g_new0 (ChannelBinding, 1);
846 priv->current_binding->channel = 0;
847 priv->current_binding->peer = *peer;
848 return TRUE;
849 } else {
850 return FALSE;
853 return FALSE;