udp-turn.c 62.5 KB
Newer Older
1 2 3 4 5
/*
 * This file is part of the Nice GLib ICE library.
 *
 * (C) 2008 Collabora Ltd.
 *  Contact: Youness Alaoui
6
 * (C) 2008 Nokia Corporation
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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:
Olivier Crête's avatar
Olivier Crête committed
24
 *   Youness Alaoui, Collabora Ltd.
25 26 27 28 29 30 31 32 33 34 35 36 37
 *
 * 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.
 */

/*
38
 * Implementation of TURN
39 40 41 42 43 44 45 46
 */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <string.h>
#include <errno.h>
#include <fcntl.h>
47
#include <stdlib.h>
48

49
#include "udp-turn.h"
50 51
#include "stun/stunagent.h"
#include "stun/usages/timer.h"
52
#include "agent-priv.h"
53

54
#define STUN_END_TIMEOUT 8000
55
#define STUN_MAX_MS_REALM_LEN 128 // as defined in [MS-TURN]
56 57 58
#define STUN_EXPIRE_TIMEOUT 60 /* Time we refresh before expiration  */
#define STUN_PERMISSION_TIMEOUT (300 - STUN_EXPIRE_TIMEOUT) /* 240 s */
#define STUN_BINDING_TIMEOUT (600 - STUN_EXPIRE_TIMEOUT) /* 540 s */
59

60
typedef struct {
61
  StunMessage message;
62
  uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
63
  StunTimer timer;
64
} TURNMessage;
65

66 67 68
typedef struct {
  NiceAddress peer;
  uint16_t channel;
69
  gboolean renew;
70
  GSource *timeout_source;
71 72
} ChannelBinding;

73
typedef struct {
74
  GMainContext *ctx;
75
  StunAgent agent;
76
  GList *channels;
77
  GList *pending_bindings;
78
  ChannelBinding *current_binding;
79
  TURNMessage *current_binding_msg;
80
  GList *pending_permissions;
81
  GSource *tick_source_channel_bind;
82
  GSource *tick_source_create_permission;
83
  NiceSocket *base_socket;
84
  NiceAddress server_addr;
85
  uint8_t *username;
86
  gsize username_len;
87
  uint8_t *password;
88
  gsize password_len;
89
  NiceTurnSocketCompatibility compatibility;
90
  GQueue *send_requests;
91 92 93 94
  uint8_t ms_realm[STUN_MAX_MS_REALM_LEN + 1];
  uint8_t ms_connection_id[20];
  uint32_t ms_sequence_num;
  bool ms_connection_id_valid;
95
  GList *permissions;           /* the peers (NiceAddress) for which
Marcus Lundblad's avatar
Marcus Lundblad committed
96
                                   there is an installed permission */
97
  GList *sent_permissions; /* ongoing permission installed */
98
  GHashTable *send_data_queues; /* stores a send data queue for per peer */
99
  GSource *permission_timeout_source;      /* timer used to invalidate
Marcus Lundblad's avatar
Marcus Lundblad committed
100
                                           permissions */
101
} UdpTurnPriv;
102

103

104 105 106
typedef struct {
  StunTransactionId id;
  GSource *source;
107
  UdpTurnPriv *priv;
108
} SendRequest;
109

110
/* used to store data sent while obtaining a permission */
111 112 113
typedef struct {
  gchar *data;
  guint data_len;
114
  gboolean reliable;
115 116
} SendData;

117
static void socket_close (NiceSocket *sock);
118 119
static gint socket_recv_messages (NiceSocket *sock,
    NiceInputMessage *recv_messages, guint n_recv_messages);
120
static gint socket_send_messages (NiceSocket *sock, const NiceAddress *to,
121
    const NiceOutputMessage *messages, guint n_messages);
122 123
static gint socket_send_messages_reliable (NiceSocket *sock,
    const NiceAddress *to, const NiceOutputMessage *messages, guint n_messages);
124
static gboolean socket_is_reliable (NiceSocket *sock);
125 126 127
static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr);
static void socket_set_writable_callback (NiceSocket *sock,
    NiceSocketWritableCb callback, gpointer user_data);
128

129 130
static void priv_process_pending_bindings (UdpTurnPriv *priv);
static gboolean priv_retransmissions_tick_unlocked (UdpTurnPriv *priv);
131
static gboolean priv_retransmissions_tick (gpointer pointer);
132 133 134
static void priv_schedule_tick (UdpTurnPriv *priv);
static void priv_send_turn_message (UdpTurnPriv *priv, TURNMessage *msg);
static gboolean priv_send_create_permission (UdpTurnPriv *priv,  StunMessage *resp,
Youness Alaoui's avatar
Youness Alaoui committed
135
    const NiceAddress *peer);
136
static gboolean priv_send_channel_bind (UdpTurnPriv *priv, StunMessage *resp,
Youness Alaoui's avatar
Youness Alaoui committed
137 138
    uint16_t channel,
    const NiceAddress *peer);
139
static gboolean priv_add_channel_binding (UdpTurnPriv *priv,
Youness Alaoui's avatar
Youness Alaoui committed
140
    const NiceAddress *peer);
141
static gboolean priv_forget_send_request (gpointer pointer);
142
static void priv_clear_permissions (UdpTurnPriv *priv);
143

144 145 146
static guint
priv_nice_address_hash (gconstpointer data)
{
147 148 149 150
  gchar address[NICE_ADDRESS_STRING_LEN];

  nice_address_to_string ((NiceAddress *) data, address);

151
  return g_str_hash(address);
152 153 154
}

static void
Philip Withnall's avatar
Philip Withnall committed
155
priv_send_data_queue_destroy (gpointer user_data)
156
{
Philip Withnall's avatar
Philip Withnall committed
157
  GQueue *send_queue = (GQueue *) user_data;
158
  GList *i;
159

160 161 162 163 164 165 166
  for (i = g_queue_peek_head_link (send_queue); i; i = i->next) {
    SendData *data = (SendData *) i->data;

    g_free (data->data);
    g_slice_free (SendData, data);
  }
  g_queue_free (send_queue);
167
}
168 169

NiceSocket *
170
nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr,
Youness Alaoui's avatar
Youness Alaoui committed
171 172 173
    NiceSocket *base_socket, NiceAddress *server_addr,
    gchar *username, gchar *password,
    NiceTurnSocketCompatibility compatibility)
174
{
175
  UdpTurnPriv *priv;
176 177 178 179
  NiceSocket *sock = g_slice_new0 (NiceSocket);

  if (!sock) {
    return NULL;
180
  }
181

182
  priv = g_new0 (UdpTurnPriv, 1);
183

184 185
  if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
      compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766) {
186
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
Youness Alaoui's avatar
Youness Alaoui committed
187 188
        STUN_COMPATIBILITY_RFC5389,
        STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS);
189 190
  } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
Youness Alaoui's avatar
Youness Alaoui committed
191 192 193
        STUN_COMPATIBILITY_RFC3489,
        STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
        STUN_AGENT_USAGE_NO_INDICATION_AUTH);
194 195
  } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
Youness Alaoui's avatar
Youness Alaoui committed
196 197 198
        STUN_COMPATIBILITY_RFC3489,
        STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
        STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
199
  } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_OC2007) {
Youness Alaoui's avatar
Youness Alaoui committed
200
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
201 202 203
        STUN_COMPATIBILITY_OC2007,
        STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS |
        STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES);
204 205
  }

206 207 208
  priv->channels = NULL;
  priv->current_binding = NULL;
  priv->base_socket = base_socket;
209 210
  if (ctx)
    priv->ctx = g_main_context_ref (ctx);
211

212 213
  if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN ||
      compatibility == NICE_TURN_SOCKET_COMPATIBILITY_OC2007) {
214 215 216 217
    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);
218
    priv->username_len = (gsize) strlen (username);
219 220 221 222 223
    if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
      priv->password = NULL;
      priv->password_len = 0;
    } else {
      priv->password = (uint8_t *)g_strdup (password);
224
      priv->password_len = (gsize) strlen (password);
225 226 227 228
    }
  }
  priv->server_addr = *server_addr;
  priv->compatibility = compatibility;
229
  priv->send_requests = g_queue_new ();
230

231
  priv->send_data_queues =
Youness Alaoui's avatar
Youness Alaoui committed
232 233 234 235
      g_hash_table_new_full (priv_nice_address_hash,
          (GEqualFunc) nice_address_equal,
          (GDestroyNotify) nice_address_free,
          priv_send_data_queue_destroy);
236

237
  sock->type = NICE_SOCKET_TYPE_UDP_TURN;
238
  sock->fileno = base_socket->fileno;
239
  sock->addr = *addr;
240
  sock->send_messages = socket_send_messages;
241
  sock->send_messages_reliable = socket_send_messages_reliable;
242
  sock->recv_messages = socket_recv_messages;
243
  sock->is_reliable = socket_is_reliable;
244 245
  sock->can_send = socket_can_send;
  sock->set_writable_callback = socket_set_writable_callback;
246 247
  sock->close = socket_close;
  sock->priv = (void *) priv;
248

249
  return sock;
250 251 252
}


253

254 255
static void
socket_close (NiceSocket *sock)
256
{
257
  UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
258
  GList *i = NULL;
259

260 261
  for (i = priv->channels; i; i = i->next) {
    ChannelBinding *b = i->data;
262 263 264 265
    if (b->timeout_source) {
      g_source_destroy (b->timeout_source);
      g_source_unref (b->timeout_source);
    }
266 267 268
    g_free (b);
  }
  g_list_free (priv->channels);
269

270
  g_list_foreach (priv->pending_bindings, (GFunc) nice_address_free,
Youness Alaoui's avatar
Youness Alaoui committed
271
      NULL);
272
  g_list_free (priv->pending_bindings);
273

274 275 276 277
  if (priv->tick_source_channel_bind != NULL) {
    g_source_destroy (priv->tick_source_channel_bind);
    g_source_unref (priv->tick_source_channel_bind);
    priv->tick_source_channel_bind = NULL;
278 279
  }

280 281 282 283 284 285 286
  if (priv->tick_source_create_permission != NULL) {
    g_source_destroy (priv->tick_source_create_permission);
    g_source_unref (priv->tick_source_create_permission);
    priv->tick_source_create_permission = NULL;
  }


287
  for (i = g_queue_peek_head_link (priv->send_requests); i; i = i->next) {
288 289 290
    SendRequest *r = i->data;
    g_source_destroy (r->source);
    g_source_unref (r->source);
291 292 293 294
    r->source = NULL;

    stun_agent_forget_transaction (&priv->agent, r->id);

295
    g_slice_free (SendRequest, r);
296

297
  }
298
  g_queue_free (priv->send_requests);
299

300
  priv_clear_permissions (priv);
301 302
  g_list_foreach (priv->sent_permissions, (GFunc) nice_address_free, NULL);
  g_list_free (priv->sent_permissions);
303
  g_hash_table_destroy (priv->send_data_queues);
304

305 306 307 308 309
  if (priv->permission_timeout_source) {
    g_source_destroy (priv->permission_timeout_source);
    g_source_unref (priv->permission_timeout_source);
    priv->permission_timeout_source = NULL;
  }
310

311 312 313
  if (priv->ctx)
    g_main_context_unref (priv->ctx);

314 315
  g_free (priv->current_binding);
  g_free (priv->current_binding_msg);
316 317
  g_list_foreach (priv->pending_permissions, (GFunc) g_free, NULL);
  g_list_free(priv->pending_permissions);
318 319 320
  g_free (priv->username);
  g_free (priv->password);
  g_free (priv);
321 322
}

323
static gint
324 325
socket_recv_messages (NiceSocket *sock,
    NiceInputMessage *recv_messages, guint n_recv_messages)
326
{
327
  UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
328 329 330 331
  gint n_messages;
  guint i;
  gboolean error = FALSE;
  guint n_valid_messages;
332

333
  nice_debug ("received message on TURN socket");
334

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
  n_messages = nice_socket_recv_messages (priv->base_socket,
      recv_messages, n_recv_messages);

  if (n_messages < 0)
    return n_messages;

  /* Process all the messages. Those which fail parsing are re-used for the next
   * message.
   *
   * FIXME: This needs a fast path which avoids allocations or memcpy()s.
   * Implementing such a path means rewriting the TURN parser (and hence the
   * STUN message code) to operate on vectors of buffers, rather than a
   * monolithic buffer. */
  for (i = 0; i < (guint) n_messages; i += n_valid_messages) {
    NiceInputMessage *message = &recv_messages[i];
    NiceSocket *dummy;
    NiceAddress from;
    guint8 *buffer;
    gsize buffer_length;
    gint parsed_buffer_length;
    gboolean allocated_buffer = FALSE;

    n_valid_messages = 1;

359 360 361
    if (message->length == 0)
      continue;

362 363 364 365 366 367 368
    /* Compact the message’s buffers into a single one for parsing. Avoid this
     * in the (hopefully) common case of a single-element buffer vector. */
    if (message->n_buffers == 1 ||
        (message->n_buffers == -1 &&
         message->buffers[0].buffer != NULL &&
         message->buffers[1].buffer == NULL)) {
      buffer = message->buffers[0].buffer;
369
      buffer_length = message->length;
370 371 372 373 374 375 376 377
    } else {
      nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC);

      buffer = compact_input_message (message, &buffer_length);
      allocated_buffer = TRUE;
    }

    /* Parse in-place. */
378
    parsed_buffer_length = nice_udp_turn_socket_parse_recv (sock, &dummy,
379 380
        &from, buffer_length, buffer,
        message->from, buffer, buffer_length);
381 382 383 384 385 386 387 388
    message->length = MAX (parsed_buffer_length, 0);

    if (parsed_buffer_length < 0) {
      error = TRUE;
    } else if (parsed_buffer_length == 0) {
      /* A TURN control message which needs ignoring. Re-use this
       * NiceInputMessage in the next loop iteration. */
      n_valid_messages = 0;
389 390
    } else {
      *message->from = from;
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
    }

    /* Split up the monolithic buffer again into the caller-provided buffers. */
    if (parsed_buffer_length > 0 && allocated_buffer) {
      parsed_buffer_length =
          memcpy_buffer_to_input_message (message, buffer,
              parsed_buffer_length);
    }

    if (allocated_buffer)
      g_free (buffer);

    if (error)
      break;
  }

  /* Was there an error processing the first message? */
  if (error && i == 0)
    return -1;
410

411
  return i;
412
}
413

414
static GSource *
415
priv_timeout_add_with_context (UdpTurnPriv *priv, guint interval,
416
    gboolean seconds, GSourceFunc function, gpointer data)
417 418 419 420 421
{
  GSource *source;

  g_return_val_if_fail (function != NULL, NULL);

422 423 424 425
  if (seconds)
    source = g_timeout_source_new_seconds (interval);
  else
    source = g_timeout_source_new (interval);
426 427 428 429 430 431 432

  g_source_set_callback (source, function, data, NULL);
  g_source_attach (source, priv->ctx);

  return source;
}

433 434 435 436 437 438 439 440 441
static StunMessageReturn
stun_message_append_ms_connection_id(StunMessage *msg,
    uint8_t *ms_connection_id, uint32_t ms_sequence_num)
{
  uint8_t buf[24];

  memcpy(buf, ms_connection_id, 20);
  *(uint32_t*)(buf + 20) = htonl(ms_sequence_num);
  return stun_message_append_bytes (msg, STUN_ATTRIBUTE_MS_SEQUENCE_NUMBER,
Youness Alaoui's avatar
Youness Alaoui committed
442
      buf, 24);
443 444 445 446 447 448 449 450 451 452 453 454 455
}

static void
stun_message_ensure_ms_realm(StunMessage *msg, uint8_t *realm)
{
  /* With MS-TURN, original clients do not send REALM attribute in Send and Set
   * Active Destination requests, but use it to compute MESSAGE-INTEGRITY. We
   * simply append cached realm value to the message and use it in subsequent
   * stun_agent_finish_message() call. Messages with this additional attribute
   * are handled correctly on OCS Access Edge working as TURN server. */
  if (stun_message_get_method(msg) == STUN_SEND ||
      stun_message_get_method(msg) == STUN_OLD_SET_ACTIVE_DST) {
    stun_message_append_bytes (msg, STUN_ATTRIBUTE_REALM, realm,
Youness Alaoui's avatar
Youness Alaoui committed
456
        strlen((char *)realm));
457 458 459
  }
}

460
static gboolean
461
priv_is_peer_in_list (const GList *list, const NiceAddress *peer)
462
{
463
  const GList *iter;
464

465
  for (iter = list ; iter ; iter = g_list_next (iter)) {
466 467
    NiceAddress *address = (NiceAddress *) iter->data;

468
    if (nice_address_equal (address, peer))
469 470 471 472
      return TRUE;
  }

  return FALSE;
473 474 475
}

static gboolean
476
priv_has_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
477
{
478 479
  return priv_is_peer_in_list (priv->permissions, peer);
}
480

481
static gboolean
482
priv_has_sent_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
483 484 485
{
  return priv_is_peer_in_list (priv->sent_permissions, peer);
}
486

487
static void
488
priv_add_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
489
{
490 491
  priv->permissions =
      g_list_append (priv->permissions, nice_address_dup (peer));
492 493 494
}

static void
495
priv_add_sent_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
496 497
{
  priv->sent_permissions =
Youness Alaoui's avatar
Youness Alaoui committed
498
      g_list_append (priv->sent_permissions, nice_address_dup (peer));
499 500 501 502 503 504
}

static GList *
priv_remove_peer_from_list (GList *list, const NiceAddress *peer)
{
  GList *iter;
505

506 507
  for (iter = list ; iter ; iter = g_list_next (iter)) {
    NiceAddress *address = (NiceAddress *) iter->data;
508

509
    if (nice_address_equal (address, peer)) {
510 511
      GList *prev = iter->prev;

512
      nice_address_free (address);
513
      list = g_list_delete_link (list, iter);
514 515 516
      iter = prev;
      if (iter)
        iter = list;
517
    }
518 519
  }

520
  return list;
521 522
}

523
static void
524
priv_remove_sent_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
525 526
{
  priv->sent_permissions =
Youness Alaoui's avatar
Youness Alaoui committed
527
      priv_remove_peer_from_list (priv->sent_permissions, peer);
528 529 530
}

static void
531
priv_clear_permissions (UdpTurnPriv *priv)
532 533 534 535 536
{
  g_list_foreach (priv->permissions, (GFunc) nice_address_free, NULL);
  g_list_free (priv->permissions);
  priv->permissions = NULL;
}
537

538 539 540 541
static gint
_socket_send_messages_wrapped (NiceSocket *sock, const NiceAddress *to,
    const NiceOutputMessage *messages, guint n_messages, gboolean reliable)
{
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
  if (!nice_socket_is_reliable (sock)) {
    if (reliable)
      return nice_socket_send_messages_reliable (sock, to, messages, n_messages);
    else
      return nice_socket_send_messages (sock, to, messages, n_messages);
  } else {
    GOutputVector *local_bufs;
    NiceOutputMessage local_message;
    const NiceOutputMessage *message;
    gsize message_len;
    guint n_bufs = 0;
    guint16 rfc4571_frame;
    guint i;
    gint ret;

    g_assert (n_messages == 1);
    message = &messages[0];
    message_len = output_message_get_size (message);
    g_assert (message_len <= G_MAXUINT16);

    /* ICE-TCP requires that all packets be framed with RFC4571 */

    /* Count the number of buffers. */
    if (message->n_buffers == -1) {
      for (i = 0; message->buffers[i].buffer != NULL; i++)
        n_bufs++;
    } else {
      n_bufs = message->n_buffers;
    }

    local_bufs = g_malloc_n (n_bufs + 1, sizeof (GOutputVector));
    local_message.buffers = local_bufs;
    local_message.n_buffers = n_bufs + 1;

    rfc4571_frame = htons (message_len);
    local_bufs[0].buffer = &rfc4571_frame;
    local_bufs[0].size = sizeof (guint16);

    for (i = 0; i < n_bufs; i++) {
      local_bufs[i + 1].buffer = message->buffers[i].buffer;
      local_bufs[i + 1].size = message->buffers[i].size;
    }


    if (reliable)
      ret = nice_socket_send_messages_reliable (sock, to,
          &local_message, 1);
    else
      ret = nice_socket_send_messages (sock, to, &local_message, 1);

    if (ret == 1)
      ret = message_len;

    g_free (local_bufs);

    return ret;
  }
599 600 601 602 603 604 605 606
}

static gssize
_socket_send_wrapped (NiceSocket *sock, const NiceAddress *to,
    guint len, const gchar *buf, gboolean reliable)
{
  gint ret;

607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
  if (!nice_socket_is_reliable (sock)) {
    GOutputVector local_buf = { buf, len };
    NiceOutputMessage local_message = { &local_buf, 1};

    ret = _socket_send_messages_wrapped (sock, to, &local_message, 1, reliable);
    if (ret == 1)
      return len;
    return ret;
  } else {
    guint16 rfc4571_frame = htons (len);
    GOutputVector local_buf[2] = {{&rfc4571_frame, 2}, { buf, len }};
    NiceOutputMessage local_message = { local_buf, 2};

    if (reliable)
      ret = nice_socket_send_messages_reliable (sock, to, &local_message, 1);
    else
      ret = nice_socket_send_messages (sock, to, &local_message, 1);

    if (ret == 1)
      return len;
    return ret;
  }
629 630
}

631
static void
632
socket_enqueue_data(UdpTurnPriv *priv, const NiceAddress *to,
633
    guint len, const gchar *buf, gboolean reliable)
634
{
635 636
  SendData *data = g_slice_new0 (SendData);
  GQueue *queue = g_hash_table_lookup (priv->send_data_queues, to);
637

638 639 640
  if (queue == NULL) {
    queue = g_queue_new ();
    g_hash_table_insert (priv->send_data_queues, nice_address_dup (to),
Youness Alaoui's avatar
Youness Alaoui committed
641
        queue);
642 643 644 645
  }

  data->data = g_memdup(buf, len);
  data->data_len = len;
646
  data->reliable = reliable;
647
  g_queue_push_tail (queue, data);
648 649 650
}

static void
651
socket_dequeue_all_data (UdpTurnPriv *priv, const NiceAddress *to)
652
{
653
  GQueue *send_queue = g_hash_table_lookup (priv->send_data_queues, to);
654

655 656 657
  if (send_queue) {
    while (!g_queue_is_empty (send_queue)) {
      SendData *data =
Youness Alaoui's avatar
Youness Alaoui committed
658
          (SendData *) g_queue_pop_head(send_queue);
659

660
      nice_debug ("dequeuing data");
661 662
      _socket_send_wrapped (priv->base_socket, &priv->server_addr,
          data->data_len, data->data, data->reliable);
663 664 665 666 667 668 669 670

      g_free (data->data);
      g_slice_free (SendData, data);
    }

    /* remove queue from table */
    g_hash_table_remove (priv->send_data_queues, to);
  }
671 672 673
}


674
static gssize
675
socket_send_message (NiceSocket *sock, const NiceAddress *to,
676
    const NiceOutputMessage *message, gboolean reliable)
677
{
678
  UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
679 680 681
  StunMessage msg;
  uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
  size_t msg_len;
682 683 684 685
  union {
    struct sockaddr_storage storage;
    struct sockaddr addr;
  } sa;
686 687
  GList *i = priv->channels;
  ChannelBinding *binding = NULL;
688
  gint ret;
689

690 691
  for (; i; i = i->next) {
    ChannelBinding *b = i->data;
692
    if (nice_address_equal (&b->peer, to)) {
693 694 695
      binding = b;
      break;
    }
696
  }
697

698
  nice_address_copy_to_sockaddr (to, &sa.addr);
699

700
  if (binding) {
701 702
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
        priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766) {
703 704 705
      gsize message_len = output_message_get_size (message);

      if (message_len + sizeof(uint32_t) <= sizeof(buffer)) {
706 707 708 709
        guint j;
        uint16_t len16, channel16;
        gsize message_offset = 0;

710
        len16 = htons ((uint16_t) message_len);
711 712
        channel16 = htons (binding->channel);

Youness Alaoui's avatar
Youness Alaoui committed
713
        memcpy (buffer, &channel16, sizeof(uint16_t));
714
        memcpy (buffer + sizeof(uint16_t), &len16, sizeof(uint16_t));
715 716 717 718 719 720 721 722 723 724 725

        /* FIXME: Slow path! This should be replaced by code which manipulates
         * the GOutputVector array, rather than the buffer contents
         * themselves. */
        for (j = 0;
             (message->n_buffers >= 0 && j < (guint) message->n_buffers) ||
             (message->n_buffers < 0 && message->buffers[j].buffer != NULL);
             j++) {
          const GOutputVector *out_buf = &message->buffers[j];
          gsize out_len;

726
          out_len = MIN (message_len - message_offset, out_buf->size);
727 728 729 730 731
          memcpy (buffer + sizeof (uint32_t) + message_offset,
              out_buf->buffer, out_len);
          message_offset += out_len;
        }

732
        msg_len = message_len + sizeof(uint32_t);
Youness Alaoui's avatar
Youness Alaoui committed
733
      } else {
734
        goto error;
Youness Alaoui's avatar
Youness Alaoui committed
735
      }
736
    } else {
737 738
      ret = _socket_send_messages_wrapped (priv->base_socket,
          &priv->server_addr, message, 1, reliable);
739

740
      if (ret == 1)
741
        return output_message_get_size (message);
742
      return ret;
743
    }
744
  } else {
745 746 747
    guint8 *compacted_buf;
    gsize compacted_buf_len;

748 749
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
        priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766) {
750
      if (!stun_agent_init_indication (&priv->agent, &msg,
Youness Alaoui's avatar
Youness Alaoui committed
751
              buffer, sizeof(buffer), STUN_IND_SEND))
752
        goto error;
753
      if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_PEER_ADDRESS,
754
              &sa.storage, sizeof(sa)) !=
755
          STUN_MESSAGE_RETURN_SUCCESS)
756
        goto error;
757 758
    } else {
      if (!stun_agent_init_request (&priv->agent, &msg,
Youness Alaoui's avatar
Youness Alaoui committed
759
              buffer, sizeof(buffer), STUN_SEND))
760
        goto error;
761

762
      if (stun_message_append32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
763
              TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS)
764
        goto error;
765 766
      if (priv->username != NULL && priv->username_len > 0) {
        if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_USERNAME,
Youness Alaoui's avatar
Youness Alaoui committed
767
                priv->username, priv->username_len) !=
768
            STUN_MESSAGE_RETURN_SUCCESS)
769
          goto error;
770
      }
771
      if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_DESTINATION_ADDRESS,
772
              &sa.addr, sizeof(sa)) !=
773
          STUN_MESSAGE_RETURN_SUCCESS)
774
        goto error;
775 776 777

      if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE &&
          priv->current_binding &&
778
          nice_address_equal (&priv->current_binding->peer, to)) {
779
        stun_message_append32 (&msg, STUN_ATTRIBUTE_OPTIONS, 1);
780
      }
781 782
    }

783 784 785 786 787 788 789 790 791 792
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_OC2007) {
      stun_message_append32(&msg, STUN_ATTRIBUTE_MS_VERSION, 1);

      if (priv->ms_connection_id_valid)
        stun_message_append_ms_connection_id(&msg, priv->ms_connection_id,
            ++priv->ms_sequence_num);

      stun_message_ensure_ms_realm(&msg, priv->ms_realm);
    }

793 794 795 796 797
    /* Slow path! We have to compact the buffers to append them to the message.
     * FIXME: This could be improved by adding vectored I/O support to
      * stun_message_append_bytes(). */
    compacted_buf = compact_output_message (message, &compacted_buf_len);

798
    if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_DATA,
799 800
            compacted_buf, compacted_buf_len) != STUN_MESSAGE_RETURN_SUCCESS) {
      g_free (compacted_buf);
801
      goto error;
802 803 804
    }

    g_free (compacted_buf);
805

806
    /* Finish the message. */
807
    msg_len = stun_agent_finish_message (&priv->agent, &msg,
Youness Alaoui's avatar
Youness Alaoui committed
808
        priv->password, priv->password_len);
809 810 811 812 813
    if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST) {
      SendRequest *req = g_slice_new0 (SendRequest);

      req->priv = priv;
      stun_message_id (&msg, req->id);
814
      req->source = priv_timeout_add_with_context (priv,
815
          STUN_END_TIMEOUT, FALSE, priv_forget_send_request, req);
816
      g_queue_push_tail (priv->send_requests, req);
817
    }
818 819
  }

820
  if (msg_len > 0) {
821
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766 &&
822 823 824
        !priv_has_permission_for_peer (priv, to)) {
      if (!priv_has_sent_permission_for_peer (priv, to)) {
        priv_send_create_permission (priv, NULL, to);
825 826
      }

827
      /* enque data */
828
      nice_debug ("enqueuing data");
829
      socket_enqueue_data(priv, to, msg_len, (gchar *)buffer, reliable);
830 831

      return msg_len;
832
    } else {
833
      GOutputVector local_buf = { buffer, msg_len };
834
      NiceOutputMessage local_message = {&local_buf, 1};
835

836 837
      ret = _socket_send_messages_wrapped (priv->base_socket,
          &priv->server_addr, &local_message, 1, reliable);
838

839 840 841
      if (ret == 1)
        return msg_len;
      return ret;
842
    }
843
  }
844 845

  /* Error condition pass through to the base socket. */
846 847
  ret = _socket_send_messages_wrapped (priv->base_socket, to, message, 1,
      reliable);
848
  if (ret == 1)
849
    return output_message_get_size (message);
850
  return ret;
851 852
error:
  return -1;
853 854 855
}

static gint
856 857
socket_send_messages (NiceSocket *sock, const NiceAddress *to,
    const NiceOutputMessage *messages, guint n_messages)
858 859 860 861 862 863 864
{
  guint i;

  for (i = 0; i < n_messages; i++) {
    const NiceOutputMessage *message = &messages[i];
    gssize len;

865
    len = socket_send_message (sock, to, message, FALSE);
866 867 868

    if (len < 0) {
      /* Error. */
869 870
      if (i > 0)
        break;
871 872 873 874 875 876 877 878
      return len;
    } else if (len == 0) {
      /* EWOULDBLOCK. */
      break;
    }
  }

  return i;
879
}
880

881 882 883 884
static gint
socket_send_messages_reliable (NiceSocket *sock, const NiceAddress *to,
    const NiceOutputMessage *messages, guint n_messages)
{
885
  UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
  guint i;

  /* TURN can depend either on tcp-turn or udp-bsd as a base socket
   * if we allow reliable send and need to create permissions and we queue the
   * data, then we must be sure that the reliable send will succeed later, so
   * we check for udp-bsd here as the base socket and don't allow it.
   */
  if (priv->base_socket->type == NICE_SOCKET_TYPE_UDP_BSD)
    return -1;

  for (i = 0; i < n_messages; i++) {
    const NiceOutputMessage *message = &messages[i];
    gssize len;

    len = socket_send_message (sock, to, message, TRUE);

    if (len < 0) {
      /* Error. */
      return len;
    } else if (len == 0) {
      /* EWOULDBLOCK. */
      break;
    }
  }

  return i;
}

914
static gboolean
915
socket_is_reliable (NiceSocket *sock)
916
{
917
  UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
918

919
  return nice_socket_is_reliable (priv->base_socket);
920 921
}

922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938
static gboolean
socket_can_send (NiceSocket *sock, NiceAddress *addr)
{
  UdpTurnPriv *priv = sock->priv;

  return nice_socket_can_send (priv->base_socket, addr);
}

static void
socket_set_writable_callback (NiceSocket *sock,
    NiceSocketWritableCb callback, gpointer user_data)
{
  UdpTurnPriv *priv = sock->priv;

  nice_socket_set_writable_callback (priv->base_socket, callback, user_data);
}

939 940 941 942
static gboolean
priv_forget_send_request (gpointer pointer)
{
  SendRequest *req = pointer;
943

944
  agent_lock ();
945

946 947
  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
Youness Alaoui's avatar
Youness Alaoui committed
948
        "Avoided race condition in turn.c:priv_forget_send_request");
949 950 951
    agent_unlock ();
    return FALSE;
  }
952

953
  stun_agent_forget_transaction (&req->priv->agent, req->id);
954

955
  g_queue_remove (req->priv->send_requests, req);
956

957 958 959
  g_source_destroy (req->source);
  g_source_unref (req->source);
  req->source = NULL;
960

961
  agent_unlock ();
962

963
  g_slice_free (SendRequest, req);
964

965 966
  return FALSE;
}
967

968 969 970
static gboolean
priv_permission_timeout (gpointer data)
{
971
  UdpTurnPriv *priv = (UdpTurnPriv *) data;
972 973

  nice_debug ("Permission is about to timeout, schedule renewal");
974

975 976 977
  agent_lock ();
  /* remove all permissions for this agent (the permission for the peer
     we are sending to will be renewed) */
978
  priv_clear_permissions (priv);
979
  agent_unlock ();
Youness Alaoui's avatar
Youness Alaoui committed
980

981
  return TRUE;
982 983
}

984 985 986
static gboolean
priv_binding_expired_timeout (gpointer data)
{
987
  UdpTurnPriv *priv = (UdpTurnPriv *) data;
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
  GList *i;
  GSource *source = NULL;

  nice_debug ("Permission expired, refresh failed");

  agent_lock ();

  source = g_main_current_source ();
  if (g_source_is_destroyed (source)) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in turn.c:priv_binding_expired_timeout");
    agent_unlock ();
    return FALSE;
  }

  /* find current binding and destroy it */
  for (i = priv->channels ; i; i = i->next) {
    ChannelBinding *b = i->data;
1006
    if (b->timeout_source == source) {
1007
      priv->channels = g_list_remove (priv->channels, b);
1008 1009
      /* Make sure we don't free a currently being-refreshed binding */
      if (priv->current_binding_msg && !priv->current_binding) {
1010 1011 1012 1013
        union {
          struct sockaddr_storage storage;
          struct sockaddr addr;
        } sa;
1014 1015 1016 1017 1018 1019
        socklen_t sa_len = sizeof(sa);
        NiceAddress to;

        /* look up binding associated with peer */
        stun_message_find_xor_addr (
            &priv->current_binding_msg->message,
1020
            STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &sa.storage, &sa_len);
1021
        nice_address_set_from_sockaddr (&to, &sa.addr);
1022 1023 1024 1025 1026 1027 1028 1029 1030

        /* If the binding is being refreshed, then move it to
           priv->current_binding so it counts as a 'new' binding and
           will get readded to the list if it succeeds */
        if (nice_address_equal (&b->peer, &to)) {
          priv->current_binding = b;
          break;
        }
      }
1031 1032 1033
      /* In case the binding timed out before it could be processed, add it to
         the pending list */
      priv_add_channel_binding (priv, &b->peer);
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
      g_free (b);
      break;
    }
  }

  agent_unlock ();

  return FALSE;
}

1044 1045 1046
static gboolean
priv_binding_timeout (gpointer data)
{
1047
  UdpTurnPriv *priv = (UdpTurnPriv *) data;
1048
  GList *i;
1049
  GSource *source = NULL;
Youness Alaoui's avatar
Youness Alaoui committed
1050