tcp-turn.c 6.3 KB
Newer Older
Youness Alaoui's avatar
Youness Alaoui committed
1 2 3
/*
 * This file is part of the Nice GLib ICE library.
 *
4 5 6
 * (C) 2008-2009 Collabora Ltd.
 *  Contact: Youness Alaoui
 * (C) 2008-2009 Nokia Corporation. All rights reserved.
Youness Alaoui's avatar
Youness Alaoui committed
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:
24
 *   Youness Alaoui, Collabora Ltd.
Youness Alaoui's avatar
Youness Alaoui committed
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 TCP relay socket interface using TCP Berkeley sockets. (See
Youness Alaoui's avatar
Youness Alaoui committed
39 40 41 42 43 44
 * http://en.wikipedia.org/wiki/Berkeley_sockets.)
 */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

45 46
#include "tcp-turn.h"

Youness Alaoui's avatar
Youness Alaoui committed
47 48 49 50
#include <string.h>
#include <errno.h>
#include <fcntl.h>

51 52 53
#ifndef G_OS_WIN32
#include <unistd.h>
#endif
Youness Alaoui's avatar
Youness Alaoui committed
54 55

typedef struct {
56
  NiceTurnSocketCompatibility compatibility;
57 58 59 60 61
  union {
    guint8 u8[65536];
    guint16 u16[32768];
  } recv_buf;
  gsize recv_buf_len;  /* in bytes */
Youness Alaoui's avatar
Youness Alaoui committed
62
  guint expecting_len;
63
  NiceSocket *base_socket;
Youness Alaoui's avatar
Youness Alaoui committed
64 65
} TurnTcpPriv;

66
#define MAX_UDP_MESSAGE_SIZE 65535
Youness Alaoui's avatar
Youness Alaoui committed
67

68 69 70 71 72 73 74 75
static void socket_close (NiceSocket *sock);
static gint socket_recv (NiceSocket *sock, NiceAddress *from,
    guint len, gchar *buf);
static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
    guint len, const gchar *buf);
static gboolean socket_is_reliable (NiceSocket *sock);

NiceSocket *
76
nice_tcp_turn_socket_new (NiceSocket *base_socket,
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
    NiceTurnSocketCompatibility compatibility)
{
  TurnTcpPriv *priv;
  NiceSocket *sock = g_slice_new0 (NiceSocket);
  sock->priv = priv = g_slice_new0 (TurnTcpPriv);

  priv->compatibility = compatibility;
  priv->base_socket = base_socket;

  sock->fileno = priv->base_socket->fileno;
  sock->addr = priv->base_socket->addr;
  sock->send = socket_send;
  sock->recv = socket_recv;
  sock->is_reliable = socket_is_reliable;
  sock->close = socket_close;

  return sock;
}


static void
socket_close (NiceSocket *sock)
{
  TurnTcpPriv *priv = sock->priv;

  if (priv->base_socket)
    nice_socket_free (priv->base_socket);

  g_slice_free(TurnTcpPriv, sock->priv);
}

Youness Alaoui's avatar
Youness Alaoui committed
108 109

static gint
110
socket_recv (NiceSocket *sock, NiceAddress *from, guint len, gchar *buf)
Youness Alaoui's avatar
Youness Alaoui committed
111 112 113
{
  TurnTcpPriv *priv = sock->priv;
  int ret;
114
  guint padlen;
Youness Alaoui's avatar
Youness Alaoui committed
115 116

  if (priv->expecting_len == 0) {
117
    guint headerlen = 0;
Youness Alaoui's avatar
Youness Alaoui committed
118

119 120
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
        priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766)
Youness Alaoui's avatar
Youness Alaoui committed
121
      headerlen = 4;
122
    else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE)
Youness Alaoui's avatar
Youness Alaoui committed
123 124
      headerlen = 2;
    else
125 126 127
      return -1;

    ret = nice_socket_recv (priv->base_socket, from,
128 129
        headerlen - priv->recv_buf_len,
        (gchar *) priv->recv_buf.u8 + priv->recv_buf_len);
130
    if (ret < 0)
Youness Alaoui's avatar
Youness Alaoui committed
131 132 133 134
        return ret;

    priv->recv_buf_len += ret;

135
    if (priv->recv_buf_len < headerlen)
Youness Alaoui's avatar
Youness Alaoui committed
136 137
      return 0;

Youness Alaoui's avatar
Youness Alaoui committed
138 139
    if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
        priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766) {
140 141
      guint16 magic = ntohs (*priv->recv_buf.u16);
      guint16 packetlen = ntohs (*(priv->recv_buf.u16 + 1));
Youness Alaoui's avatar
Youness Alaoui committed
142

143 144
      if (magic < 0x4000) {
        /* Its STUN */
Youness Alaoui's avatar
Youness Alaoui committed
145
        priv->expecting_len = 20 + packetlen;
146 147
      } else {
        /* Channel data */
Youness Alaoui's avatar
Youness Alaoui committed
148 149 150
        priv->expecting_len = 4 + packetlen;
      }
    }
151
    else if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
152 153
      guint compat_len = ntohs (*priv->recv_buf.u16);
      priv->expecting_len = compat_len;
Youness Alaoui's avatar
Youness Alaoui committed
154 155 156 157
      priv->recv_buf_len = 0;
    }
  }

Youness Alaoui's avatar
Youness Alaoui committed
158
  if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
159
      priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766)
160 161 162
    padlen = (priv->expecting_len % 4) ?  4 - (priv->expecting_len % 4) : 0;
  else
    padlen = 0;
Youness Alaoui's avatar
Youness Alaoui committed
163

164 165
  ret = nice_socket_recv (priv->base_socket, from,
      priv->expecting_len + padlen - priv->recv_buf_len,
166
      (gchar *) priv->recv_buf.u8 + priv->recv_buf_len);
167 168

  if (ret < 0)
Youness Alaoui's avatar
Youness Alaoui committed
169 170 171 172 173 174
      return ret;

  priv->recv_buf_len += ret;

  if (priv->recv_buf_len == priv->expecting_len + padlen) {
    guint copy_len = MIN (len, priv->recv_buf_len);
175
    memcpy (buf, priv->recv_buf.u8, copy_len);
Youness Alaoui's avatar
Youness Alaoui committed
176 177
    priv->expecting_len = 0;
    priv->recv_buf_len = 0;
178

Youness Alaoui's avatar
Youness Alaoui committed
179 180 181 182 183 184
    return copy_len;
  }

  return 0;
}

185
static gboolean
186 187
socket_send (NiceSocket *sock, const NiceAddress *to,
    guint len, const gchar *buf)
188
{
Youness Alaoui's avatar
Youness Alaoui committed
189
  TurnTcpPriv *priv = sock->priv;
190 191
  gchar padbuf[3] = {0, 0, 0};
  int padlen = (len%4) ? 4 - (len%4) : 0;
192 193
  gchar buffer[MAX_UDP_MESSAGE_SIZE + sizeof(guint16) + sizeof(padbuf)];
  guint buffer_len = 0;
194

195 196
  if (priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 &&
      priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_RFC5766)
197
    padlen = 0;
Youness Alaoui's avatar
Youness Alaoui committed
198

199 200
  if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE) {
    guint16 tmpbuf = htons (len);
201 202
    memcpy (buffer + buffer_len, (gchar *)&tmpbuf, sizeof(guint16));
    buffer_len += sizeof(guint16);
203
  }
204

205 206
  memcpy (buffer + buffer_len, buf, len);
  buffer_len += len;
Youness Alaoui's avatar
Youness Alaoui committed
207

Youness Alaoui's avatar
Youness Alaoui committed
208
  if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9 ||
209
      priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766) {
210 211 212 213
    memcpy (buffer + buffer_len, padbuf, padlen);
    buffer_len += padlen;
  }
  return nice_socket_send (priv->base_socket, to, buffer_len, buffer);
Youness Alaoui's avatar
Youness Alaoui committed
214 215 216 217

}


218 219 220 221 222 223
static gboolean
socket_is_reliable (NiceSocket *sock)
{
  return TRUE;
}