Hopefully fix a race condition when disposing of the turn socket and the timer to...
[sipe-libnice.git] / socket / turn.c
blob541cde873be79bcf9486ebf1f486816118c921d5
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 r->source = NULL;
205 stun_agent_forget_transaction (&priv->agent, r->id);
207 r->priv = NULL;
208 if (g_atomic_int_dec_and_test (&r->ref))
209 g_slice_free (SendRequest, r);
212 g_queue_free (priv->send_requests);
215 g_free (priv->current_binding);
216 g_free (priv->current_binding_msg);
217 g_free (priv->username);
218 g_free (priv->password);
219 g_free (priv);
222 static gint
223 socket_recv (NiceSocket *sock, NiceAddress *from, guint len, gchar *buf)
225 TurnPriv *priv = (TurnPriv *) sock->priv;
226 uint8_t recv_buf[STUN_MAX_MESSAGE_SIZE];
227 gint recv_len;
228 NiceAddress recv_from;
229 NiceSocket *dummy;;
231 recv_len = nice_socket_recv (priv->base_socket, &recv_from,
232 sizeof(recv_buf), (gchar *) recv_buf);
234 if (recv_len > 0)
235 return nice_turn_socket_parse_recv (sock, &dummy, from, len, buf,
236 &recv_from, (gchar *) recv_buf, (guint) recv_len);
237 else
238 return recv_len;
241 static gboolean
242 socket_send (NiceSocket *sock, const NiceAddress *to,
243 guint len, const gchar *buf)
245 TurnPriv *priv = (TurnPriv *) sock->priv;
246 StunMessage msg;
247 uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
248 size_t msg_len;
249 struct sockaddr_storage sa;
250 GList *i = priv->channels;
251 ChannelBinding *binding = NULL;
253 for (; i; i = i->next) {
254 ChannelBinding *b = i->data;
255 if (nice_address_equal (&b->peer, to)) {
256 binding = b;
257 break;
261 nice_address_copy_to_sockaddr (to, (struct sockaddr *)&sa);
263 if (binding) {
264 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
265 if (len + sizeof(uint32_t) <= sizeof(buffer)) {
266 uint16_t len16 = htons ((uint16_t) len);
267 uint16_t channel16 = htons (binding->channel);
268 memcpy (buffer, &channel16, sizeof(uint16_t));
269 memcpy (buffer + sizeof(uint16_t), &len16,sizeof(uint16_t));
270 memcpy (buffer + sizeof(uint32_t), buf, len);
271 msg_len = len + sizeof(uint32_t);
272 } else {
273 return 0;
275 } else {
276 return nice_socket_send (priv->base_socket, &priv->server_addr, len, buf);
278 } else {
279 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
280 if (!stun_agent_init_indication (&priv->agent, &msg,
281 buffer, sizeof(buffer), STUN_IND_SEND))
282 goto send;
283 if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_PEER_ADDRESS,
284 (struct sockaddr *)&sa, sizeof(sa)) !=
285 STUN_MESSAGE_RETURN_SUCCESS)
286 goto send;
287 } else {
288 if (!stun_agent_init_request (&priv->agent, &msg,
289 buffer, sizeof(buffer), STUN_SEND))
290 goto send;
292 if (stun_message_append32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
293 TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS)
294 goto send;
295 if (priv->username != NULL && priv->username_len > 0) {
296 if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_USERNAME,
297 priv->username, priv->username_len) !=
298 STUN_MESSAGE_RETURN_SUCCESS)
299 goto send;
301 if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_DESTINATION_ADDRESS,
302 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS)
303 goto send;
305 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE &&
306 priv->current_binding &&
307 nice_address_equal (&priv->current_binding->peer, to)) {
308 stun_message_append32 (&msg, STUN_ATTRIBUTE_OPTIONS, 1);
312 if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_DATA,
313 buf, len) != STUN_MESSAGE_RETURN_SUCCESS)
314 goto send;
316 msg_len = stun_agent_finish_message (&priv->agent, &msg,
317 priv->password, priv->password_len);
318 if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST) {
319 SendRequest *req = g_slice_new0 (SendRequest);
321 req->priv = priv;
322 stun_message_id (&msg, req->id);
323 req->source = agent_timeout_add_with_context (priv->nice, STUN_END_TIMEOUT,
324 priv_forget_send_request, req);
325 g_queue_push_tail (priv->send_requests, req);
326 g_atomic_int_inc (&req->ref);
330 if (msg_len > 0) {
331 return nice_socket_send (priv->base_socket, &priv->server_addr,
332 msg_len, (gchar *)buffer);
334 send:
335 return nice_socket_send (priv->base_socket, to, len, buf);
338 static gboolean
339 socket_is_reliable (NiceSocket *sock)
341 TurnPriv *priv = (TurnPriv *) sock->priv;
342 return nice_socket_is_reliable (priv->base_socket);
345 static gboolean
346 priv_forget_send_request (gpointer pointer)
348 SendRequest *req = pointer;
349 GStaticRecMutex *mutex = NULL;
351 if (req->priv == NULL)
352 return FALSE;
354 g_atomic_int_inc (&req->ref);
356 mutex = &req->priv->nice->mutex;
358 g_static_rec_mutex_lock (mutex);
360 if (req->source) {
361 stun_agent_forget_transaction (&req->priv->agent, req->id);
363 if (g_queue_index (req->priv->send_requests, req) != -1) {
364 g_queue_remove (req->priv->send_requests, req);
365 (void)g_atomic_int_dec_and_test (&req->ref);
368 g_source_destroy (req->source);
369 g_source_unref (req->source);
370 req->source = NULL;
373 g_static_rec_mutex_unlock (mutex);
375 if (g_atomic_int_dec_and_test (&req->ref))
376 g_slice_free (SendRequest, req);
378 return FALSE;
382 gint
383 nice_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
384 NiceAddress *from, guint len, gchar *buf,
385 NiceAddress *recv_from, gchar *recv_buf, guint recv_len)
388 TurnPriv *priv = (TurnPriv *) sock->priv;
389 StunValidationStatus valid;
390 StunMessage msg;
391 struct sockaddr_storage sa;
392 socklen_t from_len = sizeof (sa);
393 GList *i = priv->channels;
394 ChannelBinding *binding = NULL;
396 if (nice_address_equal (&priv->server_addr, recv_from)) {
397 valid = stun_agent_validate (&priv->agent, &msg,
398 (uint8_t *) recv_buf, (size_t) recv_len, NULL, NULL);
400 if (valid == STUN_VALIDATION_SUCCESS) {
401 if (priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
402 uint32_t cookie;
403 if (stun_message_find32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
404 &cookie) != STUN_MESSAGE_RETURN_SUCCESS)
405 goto recv;
406 if (cookie != TURN_MAGIC_COOKIE)
407 goto recv;
410 if (stun_message_get_method (&msg) == STUN_SEND) {
411 if (stun_message_get_class (&msg) == STUN_RESPONSE) {
412 SendRequest *req = NULL;
413 GList *i = g_queue_peek_head_link (priv->send_requests);
414 StunTransactionId msg_id;
416 stun_message_id (&msg, msg_id);
418 for (; i; i = i->next) {
419 SendRequest *r = i->data;
420 if (memcmp (&r->id, msg_id, sizeof(StunTransactionId)) == 0) {
421 req = r;
422 break;
426 if (req) {
427 g_source_destroy (req->source);
428 g_source_unref (req->source);
429 req->source = NULL;
431 g_queue_remove (priv->send_requests, req);
433 if (g_atomic_int_dec_and_test (&req->ref))
434 g_slice_free (SendRequest, req);
437 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
438 uint32_t opts = 0;
439 if (stun_message_find32 (&msg, STUN_ATTRIBUTE_OPTIONS, &opts) ==
440 STUN_MESSAGE_RETURN_SUCCESS && opts & 0x1)
441 goto msn_google_lock;
444 return 0;
445 } else if (stun_message_get_method (&msg) == STUN_OLD_SET_ACTIVE_DST) {
446 StunTransactionId request_id;
447 StunTransactionId response_id;
448 if (priv->current_binding && priv->current_binding_msg) {
449 stun_message_id (&msg, response_id);
450 stun_message_id (&priv->current_binding_msg->message, request_id);
451 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
452 g_free (priv->current_binding_msg);
453 priv->current_binding_msg = NULL;
455 if (stun_message_get_class (&msg) == STUN_RESPONSE &&
456 priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
457 goto msn_google_lock;
458 } else {
459 g_free (priv->current_binding);
460 priv->current_binding = NULL;
465 return 0;
466 } else if (stun_message_get_method (&msg) == STUN_CHANNELBIND) {
467 StunTransactionId request_id;
468 StunTransactionId response_id;
469 if (priv->current_binding && priv->current_binding_msg) {
470 stun_message_id (&msg, response_id);
471 stun_message_id (&priv->current_binding_msg->message, request_id);
472 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
473 if (stun_message_get_class (&msg) == STUN_ERROR) {
474 int code = -1;
475 uint8_t *sent_realm = NULL;
476 uint8_t *recv_realm = NULL;
477 uint16_t sent_realm_len = 0;
478 uint16_t recv_realm_len = 0;
480 sent_realm = (uint8_t *) stun_message_find (
481 &priv->current_binding_msg->message,
482 STUN_ATTRIBUTE_REALM, &sent_realm_len);
483 recv_realm = (uint8_t *) stun_message_find (&msg,
484 STUN_ATTRIBUTE_REALM, &recv_realm_len);
486 /* check for unauthorized error response */
487 if (stun_message_find_error (&msg, &code) ==
488 STUN_MESSAGE_RETURN_SUCCESS &&
489 (code == 438 || (code == 401 &&
490 !(recv_realm != NULL &&
491 recv_realm_len > 0 &&
492 recv_realm_len == sent_realm_len &&
493 sent_realm != NULL &&
494 memcmp (sent_realm, recv_realm, sent_realm_len) == 0)))) {
495 g_free (priv->current_binding_msg);
496 priv->current_binding_msg = NULL;
497 if (priv->current_binding) {
498 priv_send_channel_bind (priv, &msg,
499 priv->current_binding->channel,
500 &priv->current_binding->peer);
502 } else {
503 g_free (priv->current_binding);
504 priv->current_binding = NULL;
505 g_free (priv->current_binding_msg);
506 priv->current_binding_msg = NULL;
507 priv_process_pending_bindings (priv);
509 } else if (stun_message_get_class (&msg) == STUN_RESPONSE) {
510 g_free (priv->current_binding_msg);
511 priv->current_binding_msg = NULL;
512 if (priv->current_binding) {
513 priv->channels = g_list_append (priv->channels,
514 priv->current_binding);
515 priv->current_binding = NULL;
517 priv_process_pending_bindings (priv);
521 return 0;
522 } else if (stun_message_get_class (&msg) == STUN_INDICATION &&
523 stun_message_get_method (&msg) == STUN_IND_DATA) {
524 uint16_t data_len;
525 uint8_t *data;
527 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
528 if (stun_message_find_xor_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
529 (struct sockaddr *)&sa, &from_len) !=
530 STUN_MESSAGE_RETURN_SUCCESS)
531 goto recv;
532 } else {
533 if (stun_message_find_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
534 (struct sockaddr *)&sa, &from_len) !=
535 STUN_MESSAGE_RETURN_SUCCESS)
536 goto recv;
539 data = (uint8_t *) stun_message_find (&msg, STUN_ATTRIBUTE_DATA,
540 &data_len);
542 if (data == NULL)
543 goto recv;
545 nice_address_set_from_sockaddr (from, (struct sockaddr *)&sa);
547 *from_sock = sock;
548 memmove (buf, data, len > data_len ? data_len : len);
549 return len > data_len ? data_len : len;
550 } else {
551 goto recv;
556 recv:
557 for (i = priv->channels; i; i = i->next) {
558 ChannelBinding *b = i->data;
559 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
560 if (b->channel == ntohs(((uint16_t *)recv_buf)[0])) {
561 recv_len = ntohs (((uint16_t *)recv_buf)[1]);
562 recv_buf += sizeof(uint32_t);
563 binding = b;
564 break;
566 } else {
567 binding = b;
568 break;
572 if (binding) {
573 *from = binding->peer;
574 *from_sock = sock;
575 } else {
576 *from = *recv_from;
579 memmove (buf, recv_buf, len > recv_len ? recv_len : len);
580 return len > recv_len ? recv_len : len;
582 msn_google_lock:
584 if (priv->current_binding) {
585 GList *i = priv->channels;
586 for (; i; i = i->next) {
587 ChannelBinding *b = i->data;
588 g_free (b);
590 g_list_free (priv->channels);
591 priv->channels = g_list_append (NULL, priv->current_binding);
592 priv->current_binding = NULL;
593 priv_process_pending_bindings (priv);
596 return 0;
599 gboolean
600 nice_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer)
602 TurnPriv *priv = (TurnPriv *) sock->priv;
603 return priv_add_channel_binding (priv, peer);
606 static void
607 priv_process_pending_bindings (TurnPriv *priv)
609 gboolean ret = FALSE;
610 while (priv->pending_bindings != NULL && ret == FALSE) {
611 NiceAddress *peer = priv->pending_bindings->data;
612 ret = priv_add_channel_binding (priv, peer);
613 priv->pending_bindings = g_list_remove (priv->pending_bindings, peer);
614 nice_address_free (peer);
618 static gboolean
619 priv_retransmissions_tick_unlocked (TurnPriv *priv)
621 if (priv->current_binding_msg) {
622 switch (stun_timer_refresh (&priv->current_binding_msg->timer)) {
623 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
625 /* Time out */
626 StunTransactionId id;
628 g_free (priv->current_binding);
629 priv->current_binding = NULL;
630 g_free (priv->current_binding_msg);
631 priv->current_binding_msg = NULL;
633 stun_message_id (&priv->current_binding_msg->message, id);
634 stun_agent_forget_transaction (&priv->agent, id);
636 priv_process_pending_bindings (priv);
637 break;
639 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
640 /* Retransmit */
641 nice_socket_send (priv->base_socket, &priv->server_addr,
642 stun_message_length (&priv->current_binding_msg->message),
643 (gchar *)priv->current_binding_msg->buffer);
644 break;
645 case STUN_USAGE_TIMER_RETURN_SUCCESS:
646 break;
650 priv_schedule_tick (priv);
651 return FALSE;
655 static gboolean
656 priv_retransmissions_tick (gpointer pointer)
658 TurnPriv *priv = pointer;
659 gboolean ret;
661 g_static_rec_mutex_lock (&priv->nice->mutex);
662 ret = priv_retransmissions_tick_unlocked (priv);
663 g_static_rec_mutex_unlock (&priv->nice->mutex);
665 return ret;
668 static void
669 priv_schedule_tick (TurnPriv *priv)
671 if (priv->tick_source != NULL) {
672 g_source_destroy (priv->tick_source);
673 g_source_unref (priv->tick_source);
674 priv->tick_source = NULL;
677 if (priv->current_binding_msg) {
678 guint timeout = stun_timer_remainder (&priv->current_binding_msg->timer);
679 if (timeout > 0) {
680 priv->tick_source = agent_timeout_add_with_context (priv->nice, timeout,
681 priv_retransmissions_tick, priv);
682 } else {
683 priv_retransmissions_tick_unlocked (priv);
688 static void
689 priv_send_turn_message (TurnPriv *priv, TURNMessage *msg)
691 size_t stun_len = stun_message_length (&msg->message);
693 if (priv->current_binding_msg) {
694 g_free (priv->current_binding_msg);
695 priv->current_binding_msg = NULL;
698 nice_socket_send (priv->base_socket, &priv->server_addr,
699 stun_len, (gchar *)msg->buffer);
701 if (nice_socket_is_reliable (priv->base_socket)) {
702 stun_timer_start_reliable (&msg->timer);
703 } else {
704 stun_timer_start (&msg->timer);
707 priv->current_binding_msg = msg;
708 priv_schedule_tick (priv);
711 static gboolean
712 priv_send_channel_bind (TurnPriv *priv, StunMessage *resp,
713 uint16_t channel, NiceAddress *peer)
715 uint32_t channel_attr = channel << 16;
716 size_t stun_len;
717 struct sockaddr_storage sa;
718 TURNMessage *msg = g_new0 (TURNMessage, 1);
720 nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);
722 if (!stun_agent_init_request (&priv->agent, &msg->message,
723 msg->buffer, sizeof(msg->buffer), STUN_CHANNELBIND)) {
724 g_free (msg);
725 return FALSE;
728 if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_CHANNEL_NUMBER,
729 channel_attr) != STUN_MESSAGE_RETURN_SUCCESS) {
730 g_free (msg);
731 return FALSE;
734 if (stun_message_append_xor_addr (&msg->message, STUN_ATTRIBUTE_PEER_ADDRESS,
735 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS) {
736 g_free (msg);
737 return FALSE;
740 if (priv->username != NULL && priv->username_len > 0) {
741 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
742 priv->username, priv->username_len) != STUN_MESSAGE_RETURN_SUCCESS) {
743 g_free (msg);
744 return FALSE;
748 if (resp) {
749 uint8_t *realm;
750 uint8_t *nonce;
751 uint16_t len;
753 realm = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_REALM, &len);
754 if (realm != NULL) {
755 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM,
756 realm, len) != STUN_MESSAGE_RETURN_SUCCESS) {
757 g_free (msg);
758 return 0;
761 nonce = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_NONCE, &len);
762 if (nonce != NULL) {
763 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE,
764 nonce, len) != STUN_MESSAGE_RETURN_SUCCESS) {
765 g_free (msg);
766 return 0;
771 stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
772 priv->password, priv->password_len);
774 if (stun_len > 0) {
775 priv_send_turn_message (priv, msg);
776 return TRUE;
779 g_free (msg);
780 return FALSE;
783 static gboolean
784 priv_add_channel_binding (TurnPriv *priv, NiceAddress *peer)
786 size_t stun_len;
787 struct sockaddr_storage sa;
789 nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);
791 if (priv->current_binding) {
792 NiceAddress * pending= nice_address_new ();
793 *pending = *peer;
794 priv->pending_bindings = g_list_append (priv->pending_bindings, pending);
795 return FALSE;
798 if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
799 uint16_t channel = 0x4000;
800 GList *i = priv->channels;
801 for (; i; i = i->next) {
802 ChannelBinding *b = i->data;
803 if (channel == b->channel) {
804 i = priv->channels;
805 channel++;
806 continue;
810 if (channel >= 0x4000 && channel < 0xffff) {
811 gboolean ret = priv_send_channel_bind (priv, NULL, channel, peer);
812 if (ret) {
813 priv->current_binding = g_new0 (ChannelBinding, 1);
814 priv->current_binding->channel = channel;
815 priv->current_binding->peer = *peer;
817 return ret;
819 return FALSE;
820 } else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
821 TURNMessage *msg = g_new0 (TURNMessage, 1);
822 if (!stun_agent_init_request (&priv->agent, &msg->message,
823 msg->buffer, sizeof(msg->buffer), STUN_OLD_SET_ACTIVE_DST)) {
824 g_free (msg);
825 return FALSE;
828 if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_MAGIC_COOKIE,
829 TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS) {
830 g_free (msg);
831 return FALSE;
834 if (priv->username != NULL && priv->username_len > 0) {
835 if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
836 priv->username, priv->username_len) != STUN_MESSAGE_RETURN_SUCCESS) {
837 g_free (msg);
838 return FALSE;
842 if (stun_message_append_addr (&msg->message,
843 STUN_ATTRIBUTE_DESTINATION_ADDRESS,
844 (struct sockaddr *)&sa, sizeof(sa)) != STUN_MESSAGE_RETURN_SUCCESS) {
845 g_free (msg);
846 return FALSE;
849 stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
850 priv->password, priv->password_len);
852 if (stun_len > 0) {
853 priv->current_binding = g_new0 (ChannelBinding, 1);
854 priv->current_binding->channel = 0;
855 priv->current_binding->peer = *peer;
856 priv_send_turn_message (priv, msg);
857 return TRUE;
859 g_free (msg);
860 return FALSE;
861 } else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
862 priv->current_binding = g_new0 (ChannelBinding, 1);
863 priv->current_binding->channel = 0;
864 priv->current_binding->peer = *peer;
865 return TRUE;
866 } else {
867 return FALSE;
870 return FALSE;