udp-turn.c 68.9 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
61
static GMutex mutex;

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

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

75
typedef struct {
76
  GMainContext *ctx;
77
  StunAgent agent;
78
  GList *channels;
79
  GList *pending_bindings;
80
  ChannelBinding *current_binding;
81
  TURNMessage *current_binding_msg;
82
  GList *pending_permissions;
83
  GSource *tick_source_channel_bind;
84
  GSource *tick_source_create_permission;
85
  NiceSocket *base_socket;
86
  NiceAddress server_addr;
87
  uint8_t *username;
88
  gsize username_len;
89
  uint8_t *password;
90
  gsize password_len;
91
  NiceTurnSocketCompatibility compatibility;
92
  GQueue *send_requests;
93
94
95
96
  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;
97
  GList *permissions;           /* the peers (NiceAddress) for which
Marcus Lundblad's avatar
Marcus Lundblad committed
98
                                   there is an installed permission */
99
  GList *sent_permissions; /* ongoing permission installed */
100
  GHashTable *send_data_queues; /* stores a send data queue for per peer */
101
  GSource *permission_timeout_source;      /* timer used to invalidate
Marcus Lundblad's avatar
Marcus Lundblad committed
102
                                           permissions */
103
104
105
106
107

  guint8 *cached_realm;
  uint16_t cached_realm_len;
  guint8 *cached_nonce;
  uint16_t cached_nonce_len;
108
109
110

  GByteArray *fragment_buffer;
  NiceAddress from;
111
} UdpTurnPriv;
112

113

114
115
116
typedef struct {
  StunTransactionId id;
  GSource *source;
117
  UdpTurnPriv *priv;
118
} SendRequest;
119

120
/* used to store data sent while obtaining a permission */
121
122
123
typedef struct {
  gchar *data;
  guint data_len;
124
  gboolean reliable;
125
126
} SendData;

127
static void socket_close (NiceSocket *sock);
128
129
static gint socket_recv_messages (NiceSocket *sock,
    NiceInputMessage *recv_messages, guint n_recv_messages);
130
static gint socket_send_messages (NiceSocket *sock, const NiceAddress *to,
131
    const NiceOutputMessage *messages, guint n_messages);
132
133
static gint socket_send_messages_reliable (NiceSocket *sock,
    const NiceAddress *to, const NiceOutputMessage *messages, guint n_messages);
134
static gboolean socket_is_reliable (NiceSocket *sock);
135
136
137
static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr);
static void socket_set_writable_callback (NiceSocket *sock,
    NiceSocketWritableCb callback, gpointer user_data);
138
static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other);
139

140
141
static void priv_process_pending_bindings (UdpTurnPriv *priv);
static gboolean priv_retransmissions_tick_unlocked (UdpTurnPriv *priv);
142
static gboolean priv_retransmissions_tick (gpointer pointer);
143
144
static void priv_schedule_tick (UdpTurnPriv *priv);
static void priv_send_turn_message (UdpTurnPriv *priv, TURNMessage *msg);
145
static gboolean priv_send_create_permission (UdpTurnPriv *priv,
Youness Alaoui's avatar
Youness Alaoui committed
146
    const NiceAddress *peer);
147
static gboolean priv_send_channel_bind (UdpTurnPriv *priv,
Youness Alaoui's avatar
Youness Alaoui committed
148
149
    uint16_t channel,
    const NiceAddress *peer);
150
static gboolean priv_add_channel_binding (UdpTurnPriv *priv,
Youness Alaoui's avatar
Youness Alaoui committed
151
    const NiceAddress *peer);
152
static gboolean priv_forget_send_request_timeout (gpointer pointer);
153
static void priv_clear_permissions (UdpTurnPriv *priv);
154

155
156
157
158
159
160
161
162
163
164
165
static void
send_request_free (SendRequest *r)
{
    g_source_destroy (r->source);
    g_source_unref (r->source);

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

    g_slice_free (SendRequest, r);
}

166
167
168
static guint
priv_nice_address_hash (gconstpointer data)
{
169
170
171
172
  gchar address[NICE_ADDRESS_STRING_LEN];

  nice_address_to_string ((NiceAddress *) data, address);

173
  return g_str_hash(address);
174
175
176
}

static void
Philip Withnall's avatar
Philip Withnall committed
177
priv_send_data_queue_destroy (gpointer user_data)
178
{
Philip Withnall's avatar
Philip Withnall committed
179
  GQueue *send_queue = (GQueue *) user_data;
180
  GList *i;
181

182
183
184
185
186
187
188
  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);
189
}
190
191

NiceSocket *
192
nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr,
193
194
    NiceSocket *base_socket, const NiceAddress *server_addr,
    const gchar *username, const gchar *password,
Youness Alaoui's avatar
Youness Alaoui committed
195
    NiceTurnSocketCompatibility compatibility)
196
{
197
  UdpTurnPriv *priv;
198
199
200
201
  NiceSocket *sock = g_slice_new0 (NiceSocket);

  if (!sock) {
    return NULL;
202
  }
203

204
  priv = g_new0 (UdpTurnPriv, 1);
205

206
207
  if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
      compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766) {
208
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
Youness Alaoui's avatar
Youness Alaoui committed
209
210
        STUN_COMPATIBILITY_RFC5389,
        STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS);
211
212
  } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN) {
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
Youness Alaoui's avatar
Youness Alaoui committed
213
214
215
        STUN_COMPATIBILITY_RFC3489,
        STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
        STUN_AGENT_USAGE_NO_INDICATION_AUTH);
216
217
  } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
Youness Alaoui's avatar
Youness Alaoui committed
218
219
220
        STUN_COMPATIBILITY_RFC3489,
        STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
        STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
221
  } else if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_OC2007) {
Youness Alaoui's avatar
Youness Alaoui committed
222
    stun_agent_init (&priv->agent, STUN_ALL_KNOWN_ATTRIBUTES,
223
224
225
        STUN_COMPATIBILITY_OC2007,
        STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS |
        STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES);
226
227
  }

228
229
230
  priv->channels = NULL;
  priv->current_binding = NULL;
  priv->base_socket = base_socket;
231
232
  if (ctx)
    priv->ctx = g_main_context_ref (ctx);
233

234
235
  if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_MSN ||
      compatibility == NICE_TURN_SOCKET_COMPATIBILITY_OC2007) {
236
237
238
239
    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);
240
    priv->username_len = (gsize) strlen (username);
241
242
243
244
245
    if (compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
      priv->password = NULL;
      priv->password_len = 0;
    } else {
      priv->password = (uint8_t *)g_strdup (password);
246
      priv->password_len = (gsize) strlen (password);
247
248
249
250
    }
  }
  priv->server_addr = *server_addr;
  priv->compatibility = compatibility;
251
  priv->send_requests = g_queue_new ();
252

253
  priv->send_data_queues =
Youness Alaoui's avatar
Youness Alaoui committed
254
255
256
257
      g_hash_table_new_full (priv_nice_address_hash,
          (GEqualFunc) nice_address_equal,
          (GDestroyNotify) nice_address_free,
          priv_send_data_queue_destroy);
258

259
  sock->type = NICE_SOCKET_TYPE_UDP_TURN;
260
  sock->fileno = NULL;
261
  sock->addr = *addr;
262
  sock->send_messages = socket_send_messages;
263
  sock->send_messages_reliable = socket_send_messages_reliable;
264
  sock->recv_messages = socket_recv_messages;
265
  sock->is_reliable = socket_is_reliable;
266
267
  sock->can_send = socket_can_send;
  sock->set_writable_callback = socket_set_writable_callback;
268
  sock->is_based_on = socket_is_based_on;
269
270
  sock->close = socket_close;
  sock->priv = (void *) priv;
271

272
  return sock;
273
274
275
}


276

277
278
static void
socket_close (NiceSocket *sock)
279
{
280
  UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
281
  GList *i = NULL;
282

283
284
  g_mutex_lock (&mutex);

285
286
  for (i = priv->channels; i; i = i->next) {
    ChannelBinding *b = i->data;
287
288
289
290
    if (b->timeout_source) {
      g_source_destroy (b->timeout_source);
      g_source_unref (b->timeout_source);
    }
291
292
293
    g_free (b);
  }
  g_list_free (priv->channels);
294

295
  g_list_free_full (priv->pending_bindings, (GDestroyNotify) nice_address_free);
296

297
298
299
300
  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;
301
302
  }

303
304
305
306
307
308
  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;
  }

309
  g_queue_free_full (priv->send_requests, (GDestroyNotify) send_request_free);
310

311
  priv_clear_permissions (priv);
312
  g_list_free_full (priv->sent_permissions, (GDestroyNotify) nice_address_free);
313
  g_hash_table_destroy (priv->send_data_queues);
314

315
316
317
318
319
  if (priv->permission_timeout_source) {
    g_source_destroy (priv->permission_timeout_source);
    g_source_unref (priv->permission_timeout_source);
    priv->permission_timeout_source = NULL;
  }
320

321
322
323
  if (priv->ctx)
    g_main_context_unref (priv->ctx);

324
325
  g_free (priv->current_binding);
  g_free (priv->current_binding_msg);
326
  g_list_free_full (priv->pending_permissions, g_free);
327
328
  g_free (priv->username);
  g_free (priv->password);
329
330
  g_free (priv->cached_realm);
  g_free (priv->cached_nonce);
331
332
333
334
335

  if (priv->fragment_buffer) {
    g_byte_array_free(priv->fragment_buffer, TRUE);
  }

336
  g_free (priv);
337
338

  sock->priv = NULL;
339
340

  g_mutex_unlock (&mutex);
341
342
}

343
static gint
344
345
socket_recv_messages (NiceSocket *sock,
    NiceInputMessage *recv_messages, guint n_recv_messages)
346
{
347
  UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
348
  gint n_messages;
349
  gint n_output_messages = 0;
350
351
  guint i;
  gboolean error = FALSE;
352

353
354
  /* Make sure socket has not been freed: */
  g_assert (sock->priv != NULL);
355

356
  nice_debug_verbose ("received message on TURN socket");
357

358
359
360
361
362
363
364
  if (priv->fragment_buffer) {
    /* Fill as many recv_messages as possible with RFC4571-framed data we
     * already hold in our buffer before reading more from the base socket. */
    guint8 *f_buffer = priv->fragment_buffer->data;
    guint f_buffer_len = priv->fragment_buffer->len;

    for (i = 0; i < n_recv_messages && f_buffer_len >= sizeof (guint16); ++i) {
365
      guint32 msg_len = ((f_buffer[0] << 8) | f_buffer[1]) + sizeof (guint16);
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395

      if (msg_len > f_buffer_len) {
        /* The next message in the buffer isn't complete yet. Wait for more
         * data from the base socket. */
        break;
      }

      /* We have a full message in the buffer. Copy it into the user-provided
       * NiceInputMessage. */
      memcpy_buffer_to_input_message (&recv_messages[i], f_buffer, msg_len);
      *recv_messages[i].from = priv->from;

      f_buffer += msg_len;
      f_buffer_len -= msg_len;
      ++n_output_messages;
    }

    /* Adjust recv_messages with the number of messages we've just filled. */
    recv_messages += n_output_messages;
    n_recv_messages -= n_output_messages;

    /* Shrink the fragment buffer, deallocate it if empty. */
    g_byte_array_remove_range (priv->fragment_buffer, 0,
                               priv->fragment_buffer->len - f_buffer_len);
    if (priv->fragment_buffer->len == 0) {
      g_byte_array_free (priv->fragment_buffer, TRUE);
      priv->fragment_buffer = NULL;
    }
  }

396
397
398
399
400
401
402
403
404
405
406
407
408
  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. */
409
  for (i = 0; i < (guint) n_messages; ++i) {
410
411
412
413
414
415
416
417
    NiceInputMessage *message = &recv_messages[i];
    NiceSocket *dummy;
    NiceAddress from;
    guint8 *buffer;
    gsize buffer_length;
    gint parsed_buffer_length;
    gboolean allocated_buffer = FALSE;

418
419
420
    if (message->length == 0)
      continue;

421
422
423
424
425
426
427
    /* 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;
428
      buffer_length = message->length;
429
    } else {
430
      nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
431
432
433
434
435
436

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

    /* Parse in-place. */
437
    parsed_buffer_length = nice_udp_turn_socket_parse_recv (sock, &dummy,
438
439
        &from, buffer_length, buffer,
        message->from, buffer, buffer_length);
440
441
442
443
    message->length = MAX (parsed_buffer_length, 0);

    if (parsed_buffer_length < 0) {
      error = TRUE;
444
    } else if (parsed_buffer_length > 0) {
445
      *message->from = from;
446
    }
447
448
    /* parsed_buffer_length == 0 means this is a TURN control message which
     * needs ignoring. */
449

450
451
452
    if (nice_socket_is_reliable (sock) && parsed_buffer_length > 0) {
      /* Determine the portion of the current NiceInputMessage we can already
       * return. */
453
      gint32 msg_len = 0;
454
      if (!priv->fragment_buffer) {
455
        msg_len = ((buffer[0] << 8) | buffer[1]) + sizeof (guint16);
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
481
482
483
484
485
486
        if (msg_len > parsed_buffer_length) {
          /* The RFC4571 frame is larger than the current TURN message, need to
           * buffer it and wait for more data. */
          msg_len = 0;
        }
      }

      if (msg_len != parsed_buffer_length && !priv->fragment_buffer) {
        /* Start of message fragmenting detected. Allocate fragment buffer
         * large enough for the recv_message's we haven't parsed yet. */
        gint j;
        guint buffer_len = 0;

        for (j = i; j < n_messages; ++j) {
          buffer_len += recv_messages[j].length;
        }
        priv->fragment_buffer = g_byte_array_sized_new (buffer_len);
      }

      if (priv->fragment_buffer) {
        /* The messages are fragmented. Store the excess data (after msg_len
         * bytes) into fragment buffer for reassembly. */
        g_byte_array_append (priv->fragment_buffer, buffer + msg_len,
            parsed_buffer_length - msg_len);

        parsed_buffer_length = msg_len;
        message->length = msg_len;
        priv->from = from;
      }
    }

487
488
    /* Split up the monolithic buffer again into the caller-provided buffers. */
    if (parsed_buffer_length > 0 && allocated_buffer) {
Olivier Crête's avatar
Olivier Crête committed
489
490
      memcpy_buffer_to_input_message (message, buffer,
          parsed_buffer_length);
491
492
493
494
495
496
497
    }

    if (allocated_buffer)
      g_free (buffer);

    if (error)
      break;
498
499

    ++n_output_messages;
500
501
502
503
504
  }

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

506
  return n_output_messages;
507
}
508

509
/* interval is given in milliseconds */
510
static GSource *
511
priv_timeout_add_with_context (UdpTurnPriv *priv, guint interval,
512
    GSourceFunc function, gpointer data)
513
{
514
  GSource *source = NULL;
515
516
517

  g_return_val_if_fail (function != NULL, NULL);

518
  source = g_timeout_source_new (interval);
519
520
521
522
523
524
525

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

  return source;
}

526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
/* interval is given in seconds */
static GSource *
priv_timeout_add_seconds_with_context (UdpTurnPriv *priv, guint interval,
    GSourceFunc function, gpointer data)
{
  GSource *source = NULL;

  g_return_val_if_fail (function != NULL, NULL);

  source = g_timeout_source_new_seconds (interval);

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

  return source;
}

543
544
545
546
static StunMessageReturn
stun_message_append_ms_connection_id(StunMessage *msg,
    uint8_t *ms_connection_id, uint32_t ms_sequence_num)
{
547
548
549
550
  union {
    uint8_t buf8[24];
    uint32_t buf32[24/4];
  } buf;
551

552
553
  memcpy(buf.buf8, ms_connection_id, 20);
  buf.buf32[5] = htonl(ms_sequence_num);
554
  return stun_message_append_bytes (msg, STUN_ATTRIBUTE_MS_SEQUENCE_NUMBER,
555
      buf.buf8, 24);
556
557
558
559
560
561
562
563
564
565
566
567
568
}

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
569
        strlen((char *)realm));
570
571
572
  }
}

573
static gboolean
574
priv_is_peer_in_list (const GList *list, const NiceAddress *peer)
575
{
576
  const GList *iter;
577

578
  for (iter = list ; iter ; iter = g_list_next (iter)) {
579
580
    NiceAddress *address = (NiceAddress *) iter->data;

581
    if (nice_address_equal (address, peer))
582
583
584
585
      return TRUE;
  }

  return FALSE;
586
587
588
}

static gboolean
589
priv_has_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
590
{
591
592
  return priv_is_peer_in_list (priv->permissions, peer);
}
593

594
static gboolean
595
priv_has_sent_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
596
597
598
{
  return priv_is_peer_in_list (priv->sent_permissions, peer);
}
599

600
static void
601
priv_add_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
602
{
Youness Alaoui's avatar
Youness Alaoui committed
603
604
  priv->permissions =
      g_list_append (priv->permissions, nice_address_dup (peer));
605
606
607
}

static void
608
priv_add_sent_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
609
610
{
  priv->sent_permissions =
Youness Alaoui's avatar
Youness Alaoui committed
611
      g_list_append (priv->sent_permissions, nice_address_dup (peer));
612
613
614
615
616
617
}

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

619
620
  for (iter = list ; iter ; iter = g_list_next (iter)) {
    NiceAddress *address = (NiceAddress *) iter->data;
621

622
    if (nice_address_equal (address, peer)) {
623
624
      GList *prev = iter->prev;

625
      nice_address_free (address);
626
      list = g_list_delete_link (list, iter);
627
628
629
      iter = prev;
      if (iter)
        iter = list;
630
    }
631
632
  }

633
  return list;
634
635
}

636
static void
637
priv_remove_sent_permission_for_peer (UdpTurnPriv *priv, const NiceAddress *peer)
638
639
{
  priv->sent_permissions =
Youness Alaoui's avatar
Youness Alaoui committed
640
      priv_remove_peer_from_list (priv->sent_permissions, peer);
641
642
643
}

static void
644
priv_clear_permissions (UdpTurnPriv *priv)
645
{
646
  g_list_free_full (priv->permissions, (GDestroyNotify) nice_address_free);
647
648
  priv->permissions = NULL;
}
649

650
651
652
653
static gint
_socket_send_messages_wrapped (NiceSocket *sock, const NiceAddress *to,
    const NiceOutputMessage *messages, guint n_messages, gboolean reliable)
{
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
  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;

669
    g_assert_cmpuint (n_messages, ==, 1);
670
671
    message = &messages[0];
    message_len = output_message_get_size (message);
672
    g_assert_cmpint (message_len, <=, G_MAXUINT16);
673
674
675
676
677
678
679
680
681
682
683

    /* 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;
    }

684
    local_bufs = g_alloca ((n_bufs + 1) * sizeof (GOutputVector));
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
    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;

    return ret;
  }
709
710
711
712
713
714
715
716
}

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

717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
  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;
  }
739
740
}

741
static void
742
socket_enqueue_data(UdpTurnPriv *priv, const NiceAddress *to,
743
    guint len, const gchar *buf, gboolean reliable)
744
{
745
746
  SendData *data = g_slice_new0 (SendData);
  GQueue *queue = g_hash_table_lookup (priv->send_data_queues, to);
747

748
749
750
  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
751
        queue);
752
753
754
755
  }

  data->data = g_memdup(buf, len);
  data->data_len = len;
756
  data->reliable = reliable;
757
  g_queue_push_tail (queue, data);
758
759
760
}

static void
761
socket_dequeue_all_data (UdpTurnPriv *priv, const NiceAddress *to)
762
{
763
  GQueue *send_queue = g_hash_table_lookup (priv->send_data_queues, to);
764

765
766
767
  if (send_queue) {
    while (!g_queue_is_empty (send_queue)) {
      SendData *data =
Youness Alaoui's avatar
Youness Alaoui committed
768
          (SendData *) g_queue_pop_head(send_queue);
769

770
      nice_debug_verbose ("dequeuing data");
771
772
      _socket_send_wrapped (priv->base_socket, &priv->server_addr,
          data->data_len, data->data, data->reliable);
773
774
775
776
777
778
779
780

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

    /* remove queue from table */
    g_hash_table_remove (priv->send_data_queues, to);
  }
781
782
783
}


784
static gssize
785
socket_send_message (NiceSocket *sock, const NiceAddress *to,
786
    const NiceOutputMessage *message, gboolean reliable)
787
{
788
  UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
789
790
791
  StunMessage msg;
  uint8_t buffer[STUN_MAX_MESSAGE_SIZE];
  size_t msg_len;
792
793
794
795
  union {
    struct sockaddr_storage storage;
    struct sockaddr addr;
  } sa;
796
  GList *i;
797
  ChannelBinding *binding = NULL;
798
  gint ret;
799

800
801
  /* Make sure socket has not been freed: */
  g_assert (sock->priv != NULL);
802

803
  for (i = priv->channels; i; i = i->next) {
804
    ChannelBinding *b = i->data;
805
    if (nice_address_equal (&b->peer, to)) {
806
807
808
      binding = b;
      break;
    }
809
  }
810

811
  nice_address_copy_to_sockaddr (to, &sa.addr);
812

813
  if (binding) {
814
815
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
        priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766) {
816
817
818
      gsize message_len = output_message_get_size (message);

      if (message_len + sizeof(uint32_t) <= sizeof(buffer)) {
819
820
821
822
        guint j;
        uint16_t len16, channel16;
        gsize message_offset = 0;

823
        len16 = htons ((uint16_t) message_len);
824
825
        channel16 = htons (binding->channel);

Youness Alaoui's avatar
Youness Alaoui committed
826
        memcpy (buffer, &channel16, sizeof(uint16_t));
827
        memcpy (buffer + sizeof(uint16_t), &len16, sizeof(uint16_t));
828
829
830
831
832
833
834
835
836
837
838

        /* 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;

839
          out_len = MIN (message_len - message_offset, out_buf->size);
840
841
842
843
844
          memcpy (buffer + sizeof (uint32_t) + message_offset,
              out_buf->buffer, out_len);
          message_offset += out_len;
        }

845
        msg_len = message_len + sizeof(uint32_t);
Youness Alaoui's avatar
Youness Alaoui committed
846
      } else {
847
        goto error;
Youness Alaoui's avatar
Youness Alaoui committed
848
      }
849
    } else {
850
851
      ret = _socket_send_messages_wrapped (priv->base_socket,
          &priv->server_addr, message, 1, reliable);
852

853
      if (ret == 1)
854
        return output_message_get_size (message);
855
      return ret;
856
    }
857
  } else {
858
859
860
    guint8 *compacted_buf;
    gsize compacted_buf_len;

861
862
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
        priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766) {
863
      if (!stun_agent_init_indication (&priv->agent, &msg,
Youness Alaoui's avatar
Youness Alaoui committed
864
              buffer, sizeof(buffer), STUN_IND_SEND))
865
        goto error;
866
      if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_PEER_ADDRESS,
867
              &sa.storage, sizeof(sa)) !=
868
          STUN_MESSAGE_RETURN_SUCCESS)
869
        goto error;
870
871
    } else {
      if (!stun_agent_init_request (&priv->agent, &msg,
Youness Alaoui's avatar
Youness Alaoui committed
872
              buffer, sizeof(buffer), STUN_SEND))
873
        goto error;
874

875
      if (stun_message_append32 (&msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
876
              TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS)
877
        goto error;
878
879
      if (priv->username != NULL && priv->username_len > 0) {
        if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_USERNAME,
Youness Alaoui's avatar
Youness Alaoui committed
880
                priv->username, priv->username_len) !=
881
            STUN_MESSAGE_RETURN_SUCCESS)
882
          goto error;
883
      }
884
      if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_DESTINATION_ADDRESS,
885
              &sa.addr, sizeof(sa)) !=
886
          STUN_MESSAGE_RETURN_SUCCESS)
887
        goto error;
888
889
890

      if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE &&
          priv->current_binding &&
891
          nice_address_equal (&priv->current_binding->peer, to)) {
892
893
894
        if (stun_message_append32 (&msg, STUN_ATTRIBUTE_OPTIONS, 1) !=
            STUN_MESSAGE_RETURN_SUCCESS)
          goto error;
895
      }
896
897
    }

898
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_OC2007) {
899
900
901
      if(stun_message_append32(&msg, STUN_ATTRIBUTE_MS_VERSION, 1) !=
            STUN_MESSAGE_RETURN_SUCCESS)
          goto error;
902
903

      if (priv->ms_connection_id_valid)
904
905
906
907
        if (stun_message_append_ms_connection_id(&msg, priv->ms_connection_id,
            ++priv->ms_sequence_num) !=
            STUN_MESSAGE_RETURN_SUCCESS)
          goto error;
908
909
910
911

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

912
913
914
915
916
    /* 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);

917
    if (stun_message_append_bytes (&msg, STUN_ATTRIBUTE_DATA,
918
919
            compacted_buf, compacted_buf_len) != STUN_MESSAGE_RETURN_SUCCESS) {
      g_free (compacted_buf);
920
      goto error;
921
922
923
    }

    g_free (compacted_buf);
924

925
    /* Finish the message. */
926
    msg_len = stun_agent_finish_message (&priv->agent, &msg,
Youness Alaoui's avatar
Youness Alaoui committed
927
        priv->password, priv->password_len);
928
929
    if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST &&
        priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_OC2007) {
930
931
932
933
      SendRequest *req = g_slice_new0 (SendRequest);

      req->priv = priv;
      stun_message_id (&msg, req->id);
934
      req->source = priv_timeout_add_with_context (priv,
935
          STUN_END_TIMEOUT, priv_forget_send_request_timeout, req);
936
      g_queue_push_tail (priv->send_requests, req);
937
    }
938
939
  }

940
  if (msg_len > 0) {
941
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766 &&
942
943
        !priv_has_permission_for_peer (priv, to)) {
      if (!priv_has_sent_permission_for_peer (priv, to)) {
944
        priv_send_create_permission (priv, to);
945
946
      }

947
      /* enque data */
948
      nice_debug_verbose ("enqueuing data");
949
      socket_enqueue_data(priv, to, msg_len, (gchar *)buffer, reliable);
950
951

      return msg_len;
952
    } else {
953
      GOutputVector local_buf = { buffer, msg_len };
954
      NiceOutputMessage local_message = {&local_buf, 1};
955

956
957
      ret = _socket_send_messages_wrapped (priv->base_socket,
          &priv->server_addr, &local_message, 1, reliable);
958

959
960
961
      if (ret == 1)
        return msg_len;
      return ret;
962
    }
963
  }
964
965

  /* Error condition pass through to the base socket. */
966
967
  ret = _socket_send_messages_wrapped (priv->base_socket, to, message, 1,
      reliable);
968
  if (ret == 1)
969
    return output_message_get_size (message);
970
  return ret;
971
972
error:
  return -1;
973
974
975
}

static gint