udp-turn.c 22.5 KB
Newer Older
1 2 3 4
/*
 * This file is part of the Nice GLib ICE library.
 *
 * (C) 2008 Collabora Ltd.
Olivier Crête's avatar
Olivier Crête committed
5
 * (C) 2008 Nokia Corporation
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *  Contact: Youness Alaoui
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Nice GLib ICE library.
 *
 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
 * Corporation. All Rights Reserved.
 *
 * Contributors:
 *   Dafydd Harries, Collabora Ltd.
25
 *   Youness Alaoui, Collabora Ltd.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
 *   Rémi Denis-Courmont, Nokia
 *   Kai Vehmanen
 *
 * Alternatively, the contents of this file may be used under the terms of the
 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
 * case the provisions of LGPL are applicable instead of those above. If you
 * wish to allow use of your version of this file only under the terms of the
 * LGPL and not to allow others to use your version of this file under the
 * MPL, indicate your decision by deleting the provisions above and replace
 * them with the notice and other provisions required by the LGPL. If you do
 * not delete the provisions above, a recipient may use your version of this
 * file under either the MPL or the LGPL.
 */

/*
 * Implementation of UDP socket interface using Berkeley sockets. (See
 * http://en.wikipedia.org/wiki/Berkeley_sockets.)
 */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <arpa/inet.h>

#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

#include "udp-turn.h"
#include "udp-bsd.h"
57 58
#include "stun/stunagent.h"
#include "stun/usages/timer.h"
59
#include "agent-priv.h"
60 61

typedef struct {
62
  StunMessage message;
63 64
  uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
  stun_timer_t timer;
65
} TURNMessage;
66 67


68 69 70 71 72
typedef struct {
  NiceAddress peer;
  uint16_t channel;
} ChannelBinding;

73
typedef struct {
74
  NiceAgent *nice;
75
  StunAgent agent;
76
  GList *channels;
77
  GList *pending_bindings;
78
  ChannelBinding *current_binding;
79 80
  TURNMessage *current_binding_msg;
  GSource *tick_source;
81
  NiceSocket *base_socket;
82
  NiceAddress server_addr;
83 84 85 86
  uint8_t *username;
  size_t username_len;
  uint8_t *password;
  size_t password_len;
87
  NiceUdpTurnSocketCompatibility compatibility;
88 89
} turn_priv;

90 91 92
static void
priv_schedule_tick (turn_priv *priv);

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
static gboolean
priv_add_channel_binding (turn_priv *priv, NiceAddress *peer);


static void
priv_process_pending_bindings (turn_priv *priv)
{
  gboolean ret = FALSE;
  while (priv->pending_bindings != NULL && ret == FALSE) {
    NiceAddress *peer = priv->pending_bindings->data;
    ret = priv_add_channel_binding (priv, peer);
    priv->pending_bindings = g_list_remove (priv->pending_bindings, peer);
    g_free (peer);
  }
}
108 109 110 111 112 113 114 115 116 117 118 119 120

static gboolean
priv_retransmissions_tick_unlocked (turn_priv *priv)
{
  if (priv->current_binding_msg) {
    guint timeout = stun_timer_refresh (&priv->current_binding_msg->timer);
    switch (timeout) {
      case -1:
        /* Time out */
        g_free (priv->current_binding);
        priv->current_binding = NULL;
        g_free (priv->current_binding_msg);
        priv->current_binding_msg = NULL;
121
        priv_process_pending_bindings (priv);
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
        break;
      case 0:
        /* Retransmit */
        nice_socket_send (priv->base_socket, &priv->server_addr,
            stun_message_length (&priv->current_binding_msg->message),
            (gchar *)priv->current_binding_msg->buffer);
        break;
      default:
        break;
    }
  }

  priv_schedule_tick (priv);
  return FALSE;
}


static gboolean
priv_retransmissions_tick (gpointer pointer)
141
{
142
  turn_priv *priv = pointer;
143 144
  gboolean ret;

145 146 147
  g_static_rec_mutex_lock (&priv->nice->mutex);
  ret = priv_retransmissions_tick_unlocked (priv);
  g_static_rec_mutex_unlock (&priv->nice->mutex);
148 149 150

  return ret;
}
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189

static void
priv_schedule_tick (turn_priv *priv)
{
  if (priv->tick_source != NULL) {
    g_source_destroy (priv->tick_source);
    g_source_unref (priv->tick_source);
    priv->tick_source = NULL;
  }

  if (priv->current_binding_msg) {
    guint timeout = stun_timer_remainder (&priv->current_binding_msg->timer);
    if (timeout > 0) {
      priv->tick_source = agent_timeout_add_with_context (priv->nice, timeout,
          priv_retransmissions_tick, priv);
    } else {
      priv_retransmissions_tick_unlocked (priv);
    }
  }
}

static void
priv_send_turn_message (turn_priv *priv, TURNMessage *msg)
{
  size_t stun_len = stun_message_length (&msg->message);

  if (priv->current_binding_msg) {
    g_free (priv->current_binding_msg);
    priv->current_binding_msg = NULL;
  }

  nice_socket_send (priv->base_socket, &priv->server_addr,
      stun_len, (gchar *)msg->buffer);

  if (nice_socket_is_reliable (priv->base_socket)) {
    stun_timer_start_reliable (&msg->timer);
  } else {
    stun_timer_start (&msg->timer);
  }
190 191 192

  priv->current_binding_msg = msg;
  priv_schedule_tick (priv);
193
}
194

195 196 197 198 199 200
static gboolean
priv_send_channel_bind (turn_priv *priv,  StunMessage *resp,
    uint16_t channel, NiceAddress *peer) {
  uint32_t channel_attr = channel << 16;
  size_t stun_len;
  struct sockaddr_storage sa;
201
  TURNMessage *msg = g_new0 (TURNMessage, 1);
202 203 204

  nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);

205 206 207
  if (!stun_agent_init_request (&priv->agent, &msg->message,
          msg->buffer, sizeof(msg->buffer), STUN_CHANNELBIND)) {
    g_free (msg);
208
    return FALSE;
209
  }
210

211 212 213
  if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_CHANNEL_NUMBER,
          channel_attr) != 0) {
    g_free (msg);
214
    return FALSE;
215
  }
216

217 218 219
  if (stun_message_append_xor_addr (&msg->message, STUN_ATTRIBUTE_PEER_ADDRESS,
          (struct sockaddr *)&sa, sizeof(sa)) != 0) {
    g_free (msg);
220
    return FALSE;
221
  }
222 223

  if (priv->username != NULL && priv->username_len > 0) {
224 225 226
    if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
            priv->username, priv->username_len) != 0) {
      g_free (msg);
227
      return FALSE;
228
    }
229 230 231 232 233 234 235 236 237
  }

  if (resp) {
    uint8_t *realm;
    uint8_t *nonce;
    uint16_t len;

    realm = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_REALM, &len);
    if (realm != NULL) {
238 239 240
      if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM,
              realm, len) != 0) {
        g_free (msg);
241
        return 0;
242
      }
243 244 245
    }
    nonce = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_NONCE, &len);
    if (nonce != NULL) {
246 247 248
      if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE,
              nonce, len) != 0) {
        g_free (msg);
249
        return 0;
250
      }
251 252 253
    }
  }

254
  stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
255 256 257
      priv->password, priv->password_len);

  if (stun_len > 0) {
258
    priv_send_turn_message (priv, msg);
259 260 261
    return TRUE;
  }

262
  g_free (msg);
263 264
  return FALSE;
}
265 266 267

static gboolean
priv_add_channel_binding (turn_priv *priv, NiceAddress *peer)
268 269 270 271 272 273
{
  size_t stun_len;
  struct sockaddr_storage sa;

  nice_address_copy_to_sockaddr (peer, (struct sockaddr *)&sa);

274 275 276 277
  if (priv->current_binding) {
    NiceAddress * pending= g_new0 (NiceAddress, 1);
    *pending = *peer;
    priv->pending_bindings = g_list_append (priv->pending_bindings, pending);
278
    return FALSE;
279
  }
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303

  if (priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
    uint16_t channel = 0x4000;
    GList *i = priv->channels;
    for (; i; i = i->next) {
      ChannelBinding *b = i->data;
      if (channel == b->channel) {
        i = priv->channels;
        channel++;
        continue;
      }
    }

    if (channel >= 0x4000 && channel < 0xffff) {
      gboolean ret = priv_send_channel_bind (priv, NULL, channel, peer);
      if (ret) {
        priv->current_binding = g_new0 (ChannelBinding, 1);
        priv->current_binding->channel = channel;
        priv->current_binding->peer = *peer;
      }
      return ret;
    }
    return FALSE;
  } else if (priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_MSN) {
304 305 306 307
    TURNMessage *msg = g_new0 (TURNMessage, 1);
    if (!stun_agent_init_request (&priv->agent, &msg->message,
            msg->buffer, sizeof(msg->buffer), STUN_OLD_SET_ACTIVE_DST)) {
      g_free (msg);
308
      return FALSE;
309
    }
310

311 312 313
    if (stun_message_append32 (&msg->message, STUN_ATTRIBUTE_MAGIC_COOKIE,
            TURN_MAGIC_COOKIE) != 0) {
      g_free (msg);
314
      return FALSE;
315
    }
316 317

    if (priv->username != NULL && priv->username_len > 0) {
318 319 320
      if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
              priv->username, priv->username_len) != 0) {
        g_free (msg);
321
        return FALSE;
322
      }
323 324
    }

325 326 327 328
    if (stun_message_append_addr (&msg->message,
            STUN_ATTRIBUTE_DESTINATION_ADDRESS,
            (struct sockaddr *)&sa, sizeof(sa)) != 0) {
      g_free (msg);
329
      return FALSE;
330
    }
331

332
    stun_len = stun_agent_finish_message (&priv->agent, &msg->message,
333 334 335 336 337 338
        priv->password, priv->password_len);

    if (stun_len > 0) {
      priv->current_binding = g_new0 (ChannelBinding, 1);
      priv->current_binding->channel = 0;
      priv->current_binding->peer = *peer;
339 340
      priv_send_turn_message (priv, msg);
      return TRUE;
341
    }
342 343
    g_free (msg);
    return FALSE;
344 345 346 347 348 349 350 351 352 353 354 355
  } else if (priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
    priv->current_binding = g_new0 (ChannelBinding, 1);
    priv->current_binding->channel = 0;
    priv->current_binding->peer = *peer;
    return TRUE;
  } else {
    return FALSE;
  }

  return FALSE;
}

356 357 358 359 360 361 362
NICEAPI_EXPORT gboolean
nice_udp_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer)
{
  turn_priv *priv = (turn_priv *) sock->priv;
  return priv_add_channel_binding (priv, peer);
}

363

364 365
gint
nice_udp_turn_socket_parse_recv (
366
  NiceSocket *sock,
367
  NiceSocket **from_sock,
368 369
  NiceAddress *from,
  guint len,
370 371 372 373
  gchar *buf,
  NiceAddress *recv_from,
  gchar *recv_buf,
  guint recv_len)
374
{
375

376
  turn_priv *priv = (turn_priv *) sock->priv;
377 378 379 380
  StunValidationStatus valid;
  StunMessage msg;
  struct sockaddr_storage sa;
  guint from_len = sizeof (sa);
381 382
  GList *i = priv->channels;
  ChannelBinding *binding = NULL;
383

384 385 386
  if (nice_address_equal (&priv->server_addr, recv_from)) {
    valid = stun_agent_validate (&priv->agent, &msg,
        (uint8_t *) recv_buf, (size_t) recv_len, NULL, NULL);
387

388
    if (valid == STUN_VALIDATION_SUCCESS) {
389 390
      if (priv->compatibility != NICE_UDP_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
        uint32_t cookie;
391 392
        if (stun_message_find32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
                &cookie) != 0)
393 394 395 396
          goto recv;
        if (cookie != TURN_MAGIC_COOKIE)
          goto recv;
      }
397

398 399 400
      if (stun_message_get_method (&msg) == STUN_SEND) {
        if (stun_message_get_class (&msg) == STUN_RESPONSE &&
            priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
401 402 403
          uint32_t opts = 0;
          if (stun_message_find32 (&msg, STUN_ATTRIBUTE_OPTIONS, &opts) == 0 &&
              opts & 0x1)
404 405 406
            goto msn_google_lock;
        }
        return 0;
407
      } else if (stun_message_get_method (&msg) == STUN_OLD_SET_ACTIVE_DST) {
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
        stun_transid_t request_id;
        stun_transid_t response_id;
        if (priv->current_binding && priv->current_binding_msg) {
          stun_message_id (&msg, response_id);
          stun_message_id (&priv->current_binding_msg->message, request_id);
          if (memcmp (request_id, response_id, sizeof(stun_transid_t)) == 0) {
            g_free (priv->current_binding_msg);
            priv->current_binding_msg = NULL;

            if (stun_message_get_class (&msg) == STUN_RESPONSE &&
                priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_MSN) {
              goto msn_google_lock;
            } else {
              g_free (priv->current_binding);
              priv->current_binding = NULL;
            }
          }
425
        }
426

427
        return 0;
428 429 430 431 432 433 434 435 436
      } else if (stun_message_get_method (&msg) == STUN_CHANNELBIND) {
        stun_transid_t request_id;
        stun_transid_t response_id;
        if (priv->current_binding && priv->current_binding_msg) {
          stun_message_id (&msg, response_id);
          stun_message_id (&priv->current_binding_msg->message, request_id);
          if (memcmp (request_id, response_id, sizeof(stun_transid_t)) == 0) {
            if (stun_message_get_class (&msg) == STUN_ERROR) {
              int code = -1;
437 438 439 440
              uint8_t *sent_realm = NULL;
              uint8_t *recv_realm = NULL;
              uint16_t sent_realm_len = 0;
              uint16_t recv_realm_len = 0;
441

442
              sent_realm = (uint8_t *) stun_message_find (
443
                  &priv->current_binding_msg->message,
444 445 446
                  STUN_ATTRIBUTE_REALM, &sent_realm_len);
              recv_realm = (uint8_t *) stun_message_find (&msg,
                  STUN_ATTRIBUTE_REALM, &recv_realm_len);
447 448 449

              /* check for unauthorized error response */
              if (stun_message_find_error (&msg, &code) == 0 &&
450 451 452 453 454 455
                  (code == 438 || (code == 401 &&
                   !(recv_realm != NULL &&
                       recv_realm_len > 0 &&
                       recv_realm_len == sent_realm_len &&
                       sent_realm != NULL &&
                       memcmp (sent_realm, recv_realm, sent_realm_len) == 0)))) {
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
                g_free (priv->current_binding_msg);
                priv->current_binding_msg = NULL;
                if (priv->current_binding) {
                  priv_send_channel_bind (priv, &msg,
                      priv->current_binding->channel,
                      &priv->current_binding->peer);
                }
              } else {
                g_free (priv->current_binding);
                priv->current_binding = NULL;
                g_free (priv->current_binding_msg);
                priv->current_binding_msg = NULL;
                priv_process_pending_bindings (priv);
              }
            } else if (stun_message_get_class (&msg) == STUN_RESPONSE) {
              g_free (priv->current_binding_msg);
              priv->current_binding_msg = NULL;
              if (priv->current_binding) {
                priv->channels = g_list_append (priv->channels,
                    priv->current_binding);
                priv->current_binding = NULL;
              }
              priv_process_pending_bindings (priv);
            }
          }
481
        }
482 483 484 485 486
        return 0;
      } else if (stun_message_get_class (&msg) == STUN_INDICATION &&
          stun_message_get_method (&msg) == STUN_IND_DATA) {
        uint16_t data_len;
        uint8_t *data;
Youness Alaoui's avatar
Youness Alaoui committed
487

488 489 490 491 492 493 494 495 496
        if (priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
          if (stun_message_find_xor_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
                  (struct sockaddr *)&sa, &from_len) != 0)
            goto recv;
        } else {
          if (stun_message_find_addr (&msg, STUN_ATTRIBUTE_REMOTE_ADDRESS,
                  (struct sockaddr *)&sa, &from_len) != 0)
            goto recv;
        }
497

498 499 500
        data = (uint8_t *) stun_message_find (&msg, STUN_ATTRIBUTE_DATA,
            &data_len);

501 502 503 504 505
        if (data == NULL)
          goto recv;

        nice_address_set_from_sockaddr (from, (struct sockaddr *)&sa);

506
        *from_sock = sock;
507 508 509 510 511
        memmove (buf, data, len > data_len ? data_len : len);
        return len > data_len ? data_len : len;
      } else {
        goto recv;
      }
512 513
    }
  }
514

515
 recv:
516
  for (i = priv->channels; i; i = i->next) {
517 518
    ChannelBinding *b = i->data;
    if (priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
519 520
      if (b->channel == ntohs(((uint16_t *)recv_buf)[0])) {
        recv_len = ntohs (((uint16_t *)recv_buf)[1]);
521 522 523 524 525 526 527 528 529 530 531 532
        recv_buf += sizeof(uint32_t);
        binding = b;
        break;
      }
    } else {
      binding = b;
      break;
    }
  }

  if (binding) {
    *from = binding->peer;
533
    *from_sock = sock;
534 535 536
  } else {
    *from = *recv_from;
  }
537

538 539
  memmove (buf, recv_buf, len > recv_len ? recv_len : len);
  return len > recv_len ? recv_len : len;
540 541 542 543 544 545 546 547 548 549 550 551

 msn_google_lock:

  if (priv->current_binding) {
    GList *i = priv->channels;
    for (; i; i = i->next) {
      ChannelBinding *b = i->data;
      g_free (b);
    }
    g_list_free (priv->channels);
    priv->channels = g_list_append (NULL, priv->current_binding);
    priv->current_binding = NULL;
552
    priv_process_pending_bindings (priv);
553 554 555
  }

  return 0;
556 557 558 559
}

static gint
socket_recv (
560
  NiceSocket *sock,
561 562 563 564 565 566 567 568
  NiceAddress *from,
  guint len,
  gchar *buf)
{
  turn_priv *priv = (turn_priv *) sock->priv;
  uint8_t recv_buf[STUN_MAX_MESSAGE_SIZE];
  gint recv_len;
  NiceAddress recv_from;
569
  NiceSocket *dummy;;
570

571
  recv_len = nice_socket_recv (priv->base_socket, &recv_from,
572 573
      sizeof(recv_buf), (gchar *) recv_buf);

574 575
  return nice_udp_turn_socket_parse_recv (sock, &dummy, from, len, buf,
      &recv_from, (gchar *) recv_buf, (guint) recv_len);
576 577 578 579
}

static gboolean
socket_send (
580
  NiceSocket *sock,
581 582 583 584 585
  const NiceAddress *to,
  guint len,
  const gchar *buf)
{
  turn_priv *priv = (turn_priv *) sock->priv;
586 587
  StunMessage msg;
  uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
588
  size_t msg_len;
589
  struct sockaddr_storage sa;
590 591 592 593 594 595 596 597 598 599
  GList *i = priv->channels;
  ChannelBinding *binding = NULL;

  for (; i; i = i->next) {
    ChannelBinding *b = i->data;
    if (nice_address_equal (&b->peer, to)) {
      binding = b;
      break;
    }
  }
600 601 602

  nice_address_copy_to_sockaddr (to, (struct sockaddr *)&sa);

603 604 605
  if (binding) {
    if (priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_DRAFT9 &&
        len + sizeof(uint32_t) <= sizeof(buffer)) {
606 607 608
      uint16_t len16 = htons ((uint16_t) len);
      uint16_t channel16 = htons (binding->channel);
      memcpy (buffer, &channel16, sizeof(uint16_t));
609 610 611 612
      memcpy (buffer + sizeof(uint16_t), &len16,sizeof(uint16_t));
      memcpy (buffer + sizeof(uint32_t), buf, len);
      msg_len = len + sizeof(uint32_t);
    } else {
613 614
      nice_socket_send (priv->base_socket, &priv->server_addr, len, buf);
      return TRUE;
615
    }
616
  } else {
617 618 619 620 621 622 623 624 625 626 627
    if (priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
      if (!stun_agent_init_indication (&priv->agent, &msg,
              buffer, sizeof(buffer), STUN_IND_SEND))
        goto send;
      if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_PEER_ADDRESS,
              (struct sockaddr *)&sa, sizeof(sa)) != 0)
        goto send;
    } else {
      if (!stun_agent_init_request (&priv->agent, &msg,
              buffer, sizeof(buffer), STUN_SEND))
        goto send;
628

629 630 631 632 633 634 635 636 637 638
      if (stun_message_append32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
              TURN_MAGIC_COOKIE) != 0)
        goto send;
      if (priv->username != NULL && priv->username_len > 0) {
        if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_USERNAME,
                priv->username, priv->username_len) != 0)
          goto send;
      }
      if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_DESTINATION_ADDRESS,
              (struct sockaddr *)&sa, sizeof(sa)) != 0)
639
        goto send;
640 641 642 643 644 645

      if (priv->compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_GOOGLE &&
          priv->current_binding &&
          nice_address_equal (&priv->current_binding->peer, to)) {
        stun_message_append32 (&msg, STUN_ATTRIBUTE_OPTIONS, 1);
      }
646 647
    }

648
    if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_DATA, buf, len) != 0)
649
      goto send;
650

651 652 653
    msg_len = stun_agent_finish_message (&priv->agent, &msg,
        priv->password, priv->password_len);
  }
654

655
  if (msg_len > 0) {
656
    nice_socket_send (priv->base_socket, &priv->server_addr,
657
        msg_len, (gchar *)buffer);
658 659 660
    return TRUE;
  }
 send:
661
  nice_socket_send (priv->base_socket, to, len, buf);
662 663 664 665

  return TRUE;
}

666 667 668 669 670 671 672
static gboolean
socket_is_reliable (NiceSocket *sock)
{
  turn_priv *priv = (turn_priv *) sock->priv;
  return nice_socket_is_reliable (priv->base_socket);
}

673
static void
674
socket_close (NiceSocket *sock)
675 676
{
  turn_priv *priv = (turn_priv *) sock->priv;
677 678
  GList *i = NULL;
  for (i = priv->channels; i; i = i->next) {
679 680 681 682
    ChannelBinding *b = i->data;
    g_free (b);
  }
  g_list_free (priv->channels);
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697

  for (i = priv->pending_bindings; i; i = i->next) {
    ChannelBinding *b = i->data;
    g_free (b);
  }
  g_list_free (priv->pending_bindings);

  if (priv->tick_source != NULL) {
    g_source_destroy (priv->tick_source);
    g_source_unref (priv->tick_source);
    priv->tick_source = NULL;
  }

  g_free (priv->current_binding);
  g_free (priv->current_binding_msg);
698 699
  g_free (priv->username);
  g_free (priv->password);
700 701 702 703
  g_free (priv);
}


704 705
NICEAPI_EXPORT NiceSocket *
nice_udp_turn_socket_new (
706
    NiceAgent *agent,
707 708 709 710 711
    NiceAddress *addr,
    NiceSocket *base_socket,
    NiceAddress *server_addr,
    gchar *username,
    gchar *password,
712
    NiceUdpTurnSocketCompatibility compatibility)
713 714
{
  turn_priv *priv = g_new0 (turn_priv, 1);
715 716 717 718 719
  NiceSocket *sock = g_slice_new0 (NiceSocket);

  if (!sock) {
    return NULL;
  }
720

721 722 723
  if (compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_DRAFT9) {
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
        STUN_COMPATIBILITY_3489BIS,
724
        STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS);
725
  } else if (compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_MSN) {
726 727
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
        STUN_COMPATIBILITY_RFC3489,
728 729 730 731 732 733 734
        STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
        STUN_AGENT_USAGE_NO_INDICATION_AUTH);
  } else if (compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
        STUN_COMPATIBILITY_RFC3489,
        STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
        STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
735
  }
736

737
  priv->nice = agent;
738 739
  priv->channels = NULL;
  priv->current_binding = NULL;
740
  priv->base_socket = base_socket;
741 742 743 744 745 746 747

  if (compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_MSN) {
    priv->username = g_base64_decode (username, &priv->username_len);
    priv->password = g_base64_decode (password, &priv->password_len);
  } else {
    priv->username = (uint8_t *)g_strdup (username);
    priv->username_len = (size_t) strlen (username);
748 749 750 751 752 753 754
    if (compatibility == NICE_UDP_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
      priv->password = NULL;
      priv->password_len = 0;
    } else {
      priv->password = (uint8_t *)g_strdup (password);
      priv->password_len = (size_t) strlen (password);
    }
755
  }
756
  priv->server_addr = *server_addr;
757
  priv->compatibility = compatibility;
758
  sock->addr = *addr;
759
  sock->fileno = base_socket->fileno;
760 761
  sock->send = socket_send;
  sock->recv = socket_recv;
762
  sock->is_reliable = socket_is_reliable;
763 764
  sock->close = socket_close;
  sock->priv = (void *) priv;
765
  return sock;
766
}