uint16_t cached_realm_len;
guint8 *cached_nonce;
uint16_t cached_nonce_len;
+
+ GByteArray *fragment_buffer;
+ NiceAddress from;
} UdpTurnPriv;
g_free (priv->password);
g_free (priv->cached_realm);
g_free (priv->cached_nonce);
+
+ if (priv->fragment_buffer) {
+ g_byte_array_free(priv->fragment_buffer, TRUE);
+ }
+
g_free (priv);
sock->priv = NULL;
{
UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
gint n_messages;
+ gint n_output_messages = 0;
guint i;
gboolean error = FALSE;
nice_debug_verbose ("received message on TURN socket");
+ 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) {
+ guint16 msg_len = ntohs (*(guint16 *)f_buffer) + sizeof (guint16);
+
+ 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;
+ }
+ }
+
n_messages = nice_socket_recv_messages (priv->base_socket,
recv_messages, n_recv_messages);
/* parsed_buffer_length == 0 means this is a TURN control message which
* needs ignoring. */
+ if (nice_socket_is_reliable (sock) && parsed_buffer_length > 0) {
+ /* Determine the portion of the current NiceInputMessage we can already
+ * return. */
+ guint16 msg_len = 0;
+ if (!priv->fragment_buffer) {
+ msg_len = ntohs (*(guint16 *)buffer) + sizeof (guint16);
+ 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;
+ }
+ }
+
/* Split up the monolithic buffer again into the caller-provided buffers. */
if (parsed_buffer_length > 0 && allocated_buffer) {
memcpy_buffer_to_input_message (message, buffer,
if (error)
break;
+
+ ++n_output_messages;
}
/* Was there an error processing the first message? */
if (error && i == 0)
return -1;
- return i;
+ return n_output_messages;
}
/* interval is given in milliseconds */
--- /dev/null
+/*
+ * This file is part of the Nice GLib ICE library.
+ *
+ * (C) 2018 Jakub Adam <jakub.adam@ktknet.cz>
+ *
+ * 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.
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "agent-priv.h"
+#include "socket.h"
+
+static GRand *rand;
+
+static GSList *
+generate_test_messages(void)
+{
+ guint i;
+ GSList *result = NULL;
+
+ for (i = 0; i != 100; ++i) {
+ GInputVector *msg_data = g_new (GInputVector, 1);
+ gsize msg_size = g_rand_int_range (rand, 0, G_MAXUINT16);
+ gsize j;
+
+ msg_data->size = msg_size + sizeof (guint16);
+ msg_data->buffer = g_malloc (msg_data->size);
+ *(guint16 *)(msg_data->buffer) = htons (msg_size);
+
+ for (j = 2; j != msg_data->size; ++j) {
+ ((guint8 *)msg_data->buffer)[j] = g_rand_int(rand);
+ }
+
+ result = g_slist_append(result, msg_data);
+ }
+
+ return result;
+}
+
+typedef struct {
+ GSList *msg_data;
+ GSList *current_msg;
+ gsize offset;
+ guint8 send_buffer[G_MAXUINT16 + sizeof (guint16)];
+} TestSocketPriv;
+
+static gint
+test_socket_recv_messages (NiceSocket *sock, NiceInputMessage *recv_messages,
+ guint n_recv_messages) {
+ TestSocketPriv *priv = sock->priv;
+ guint i;
+
+ for (i = 0; priv->current_msg && i != n_recv_messages; ++i) {
+ gsize msg_size = g_rand_int_range (rand, 0, G_MAXUINT16) + sizeof (guint16);
+ gsize j;
+
+ j = sizeof (guint16);
+ while (priv->current_msg && j < msg_size) {
+ GInputVector *msg = priv->current_msg->data;
+ gsize cpylen = MIN (msg->size - priv->offset, msg_size - j);
+ memcpy (priv->send_buffer + j, (guint8 *)msg->buffer + priv->offset,
+ cpylen);
+ priv->offset += cpylen;
+ j += cpylen;
+
+ if (priv->offset == msg->size) {
+ priv->current_msg = priv->current_msg->next;
+ priv->offset = 0;
+ }
+ }
+
+ msg_size = j;
+ *(guint16 *)(priv->send_buffer) = htons (msg_size - sizeof (guint16));
+
+ memcpy_buffer_to_input_message (&recv_messages[i], priv->send_buffer, msg_size);
+ nice_address_set_from_string (recv_messages[i].from, "127.0.0.1");
+ }
+
+ return i;
+}
+
+static gboolean
+test_socket_is_reliable (NiceSocket *sock) {
+ return TRUE;
+}
+
+static void
+test_socket_close (NiceSocket *sock) {
+ g_free(sock->priv);
+}
+
+static NiceSocket *
+test_socket_new (GSList *msg_data)
+{
+ NiceSocket *sock = g_new0 (NiceSocket, 1);
+ TestSocketPriv *priv = g_new0 (TestSocketPriv, 1);
+ priv->msg_data = msg_data;
+ priv->current_msg = msg_data;
+ priv->offset = 0;
+
+ sock->type = NICE_SOCKET_TYPE_UDP_TURN_OVER_TCP;
+ sock->recv_messages = test_socket_recv_messages;
+ sock->is_reliable = test_socket_is_reliable;
+ sock->close = test_socket_close;
+ sock->priv = (void *) priv;
+
+ return sock;
+}
+
+static void
+tcp_turn_fragmentation (void)
+{
+ /* Generate some RFC4571-framed test messages. A dummy base socket will split
+ * them randomly into TCP-TURN messages. Test that tcp-turn socket can
+ * correctly extract and reassemble the original test data out of the TURN
+ * messages. */
+ GSList *test_messages = generate_test_messages ();
+ NiceAddress addr;
+ NiceSocket *turnsock;
+
+ guint n_recv_messages = 7;
+ NiceInputMessage recv_messages[n_recv_messages];
+ GInputVector recv_vectors[n_recv_messages];
+ NiceAddress recv_addr[n_recv_messages];
+ guint8 recv_buffers[n_recv_messages][G_MAXUINT16 + sizeof (guint16)];
+
+ gint n_messages;
+ guint i;
+ GSList *li;
+
+ for (i = 0; i != n_recv_messages; ++i) {
+ recv_messages[i].buffers = &recv_vectors[i];
+ recv_messages[i].from = &recv_addr[i];
+ recv_messages[i].n_buffers = 1;
+ recv_messages[i].length = 0;
+ recv_vectors[i].buffer = &recv_buffers[i];
+ recv_vectors[i].size = sizeof (recv_buffers[i]);
+ }
+
+ nice_address_set_from_string (&addr, "127.0.0.1");
+
+ turnsock = nice_udp_turn_socket_new (NULL, &addr,
+ test_socket_new (test_messages), &addr, "", "",
+ NICE_TURN_SOCKET_COMPATIBILITY_OC2007);
+
+ li = test_messages;
+ while (li) {
+ n_messages = nice_socket_recv_messages (turnsock, recv_messages,
+ n_recv_messages);
+
+ for (i = 0; i != (guint)n_messages; ++i) {
+ NiceInputMessage *message = &recv_messages[i];
+ GInputVector *vec = li->data;
+ if (message->length == 0) {
+ continue;
+ }
+ g_assert (message->length == vec->size);
+ g_assert (!memcmp (message->buffers->buffer, vec->buffer,
+ message->length));
+
+ li = li->next;
+ }
+ }
+
+ for (li = test_messages; li; li = li->next) {
+ GInputVector *v = li->data;
+ g_free (v->buffer);
+ g_free (v);
+ }
+ g_slist_free (test_messages);
+
+ nice_socket_free(turnsock);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GMainLoop *mainloop;
+
+ rand = g_rand_new();
+ g_test_init (&argc, &argv, NULL);
+
+ mainloop = g_main_loop_new (NULL, TRUE);
+
+ g_test_add_func ("/udp-turn/tcp-fragmentation", tcp_turn_fragmentation);
+
+ g_test_run ();
+
+ g_rand_free(rand);
+
+ g_main_loop_unref (mainloop);
+
+ return 0;
+}