turn.c 14 KB
Newer Older
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) 2007-2009 Nokia Corporation. All rights reserved.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *  Contact: Rémi Denis-Courmont
 *
 * 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:
25
 *   Youness Alaoui, Collabora Ltd.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
 *   Rémi Denis-Courmont, Nokia
 *
 * 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

43 44 45
#ifdef _WIN32
#include <winsock2.h>
#else
46 47
#include <sys/types.h>
#include <sys/socket.h>
48
#endif
49 50 51 52 53 54 55 56

#include "stun/stunagent.h"
#include "turn.h"

#include <string.h>
#include <stdlib.h>
#include <fcntl.h>

57 58 59 60 61 62 63 64 65


#define REQUESTED_PROPS_E 0x80000000
#define REQUESTED_PROPS_R 0x40000000
#define REQUESTED_PROPS_P 0x20000000


#define STUN_ATTRIBUTE_MSN_MAPPED_ADDRESS 0x8000

66 67 68

#define TURN_REQUESTED_TRANSPORT_UDP 0x11000000

69 70 71
/** Non-blocking mode STUN TURN usage */

size_t stun_usage_turn_create (StunAgent *agent, StunMessage *msg,
72
    uint8_t *buffer, size_t buffer_len,
73
    StunMessage *previous_response,
74
    StunUsageTurnRequestPorts request_props,
75
    int32_t bandwidth, int32_t lifetime,
76 77
    uint8_t *username, size_t username_len,
    uint8_t *password, size_t password_len,
78
    StunUsageTurnCompatibility compatibility)
79 80 81
{
  stun_agent_init_request (agent, msg, buffer, buffer_len, STUN_ALLOCATE);

82 83
  if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 ||
      compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) {
84
    if (stun_message_append32 (msg, STUN_ATTRIBUTE_REQUESTED_TRANSPORT,
85
            TURN_REQUESTED_TRANSPORT_UDP) != STUN_MESSAGE_RETURN_SUCCESS)
86
      return 0;
87
    if (bandwidth >= 0) {
88 89
      if (stun_message_append32 (msg, STUN_ATTRIBUTE_BANDWIDTH, bandwidth) !=
          STUN_MESSAGE_RETURN_SUCCESS)
90 91 92 93
        return 0;
    }
  } else {
    if (stun_message_append32 (msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
94
            TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS)
95 96 97
      return 0;
  }

98 99 100 101
  if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
    stun_message_append32(msg, STUN_ATTRIBUTE_MS_VERSION, 1);
  }

102
  if (lifetime >= 0) {
103 104
    if (stun_message_append32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime) !=
        STUN_MESSAGE_RETURN_SUCCESS)
105 106 107
      return 0;
  }

108
  if ((compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 ||
Youness Alaoui's avatar
Youness Alaoui committed
109
          compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) &&
110
      request_props != STUN_USAGE_TURN_REQUEST_PORT_NORMAL) {
111 112 113
    uint32_t req = 0;


114
    if (request_props & STUN_USAGE_TURN_REQUEST_PORT_EVEN_AND_RESERVE) {
115 116 117 118 119 120
      req |= REQUESTED_PROPS_R;
      req |= REQUESTED_PROPS_E;
    } else if (request_props & STUN_USAGE_TURN_REQUEST_PORT_EVEN) {
      req |= REQUESTED_PROPS_E;
    }

121
    if (stun_message_append32 (msg, STUN_ATTRIBUTE_REQUESTED_PORT_PROPS,
122
            req) != STUN_MESSAGE_RETURN_SUCCESS)
123 124 125
      return 0;
  }

126 127 128
  if (previous_response) {
    uint8_t *realm;
    uint8_t *nonce;
129
    uint64_t reservation;
130
    uint16_t len;
131

132 133 134
    realm = (uint8_t *) stun_message_find (previous_response,
        STUN_ATTRIBUTE_REALM, &len);
    if (realm != NULL) {
135 136
      if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_REALM, realm, len) !=
          STUN_MESSAGE_RETURN_SUCCESS)
137 138
        return 0;
    }
139 140 141
    nonce = (uint8_t *) stun_message_find (previous_response,
        STUN_ATTRIBUTE_NONCE, &len);
    if (nonce != NULL) {
142 143
      if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_NONCE, nonce, len) !=
          STUN_MESSAGE_RETURN_SUCCESS)
144 145
        return 0;
    }
Youness Alaoui's avatar
Youness Alaoui committed
146 147
    if (stun_message_find64 (previous_response,
            STUN_ATTRIBUTE_RESERVATION_TOKEN,
148
            &reservation) == STUN_MESSAGE_RETURN_SUCCESS) {
149
      if (stun_message_append64 (msg, STUN_ATTRIBUTE_RESERVATION_TOKEN,
150
              reservation) != STUN_MESSAGE_RETURN_SUCCESS)
151 152 153 154
        return 0;
    }
  }

155 156 157
  if (username != NULL && username_len > 0 &&
      (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS ||
       previous_response)) {
158
    if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
159
            username, username_len) != STUN_MESSAGE_RETURN_SUCCESS)
160 161 162 163 164 165
      return 0;
  }

  return stun_agent_finish_message (agent, msg, password, password_len);
}

166 167
size_t stun_usage_turn_create_refresh (StunAgent *agent, StunMessage *msg,
    uint8_t *buffer, size_t buffer_len,
168 169 170
    StunMessage *previous_response, int32_t lifetime,
    uint8_t *username, size_t username_len,
    uint8_t *password, size_t password_len,
171 172 173
    StunUsageTurnCompatibility compatibility)
{

174 175
  if (compatibility != STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 &&
      compatibility != STUN_USAGE_TURN_COMPATIBILITY_RFC5766) {
176 177 178
    return stun_usage_turn_create (agent, msg, buffer, buffer_len,
        previous_response, STUN_USAGE_TURN_REQUEST_PORT_NORMAL, 0, lifetime,
        username, username_len, password, password_len, compatibility);
179 180
  }

181 182
  stun_agent_init_request (agent, msg, buffer, buffer_len, STUN_REFRESH);
  if (lifetime >= 0) {
183 184
    if (stun_message_append32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime) !=
        STUN_MESSAGE_RETURN_SUCCESS)
185 186
      return 0;
  }
187 188 189 190 191 192 193 194 195

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

    realm = (uint8_t *) stun_message_find (previous_response,
        STUN_ATTRIBUTE_REALM, &len);
    if (realm != NULL) {
196 197
      if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_REALM, realm, len) !=
          STUN_MESSAGE_RETURN_SUCCESS)
198 199 200 201 202
        return 0;
    }
    nonce = (uint8_t *) stun_message_find (previous_response,
        STUN_ATTRIBUTE_NONCE, &len);
    if (nonce != NULL) {
203 204
      if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_NONCE, nonce, len) !=
          STUN_MESSAGE_RETURN_SUCCESS)
205 206
        return 0;
    }
207
  }
208 209


210 211 212
  if (username != NULL && username_len > 0 &&
      (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS ||
       previous_response)) {
213
    if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
214
            username, username_len) != STUN_MESSAGE_RETURN_SUCCESS)
215 216 217 218
      return 0;
  }


219
  return stun_agent_finish_message (agent, msg, password, password_len);
220 221
}

222 223 224 225 226 227
size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg,
    uint8_t *buffer, size_t buffer_len,
    uint8_t *username, size_t username_len,
    uint8_t *password, size_t password_len,
    uint8_t *realm, size_t realm_len,
    uint8_t *nonce, size_t nonce_len,
228
    struct sockaddr_storage *peer,
229 230
    StunUsageTurnCompatibility compatibility)
{
231 232 233
  if (!peer)
    return 0;

Youness Alaoui's avatar
Youness Alaoui committed
234 235
  stun_agent_init_request (agent, msg, buffer, buffer_len,
      STUN_CREATEPERMISSION);
236

Youness Alaoui's avatar
Youness Alaoui committed
237
  /* PEER address */
238 239 240
  if (stun_message_append_xor_addr (msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS,
          peer, sizeof(*peer)) != STUN_MESSAGE_RETURN_SUCCESS) {
    return 0;
Youness Alaoui's avatar
Youness Alaoui committed
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
  }

  /* nonce */
  if (nonce != NULL) {
    if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_NONCE,
            nonce, nonce_len) != STUN_MESSAGE_RETURN_SUCCESS)
      return 0;
  }

  /* realm */
  if (realm != NULL) {
    if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_REALM,
            realm, realm_len) != STUN_MESSAGE_RETURN_SUCCESS)
      return 0;
  }

  /* username */
258 259 260
  if (username != NULL &&
      (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS ||
       (nonce != NULL && realm != NULL))) {
Youness Alaoui's avatar
Youness Alaoui committed
261 262 263 264 265 266
    if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
            username, username_len) != STUN_MESSAGE_RETURN_SUCCESS)
      return 0;
  }

  return stun_agent_finish_message (agent, msg, password, password_len);
267 268 269
}


270
StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg,
271 272 273
    struct sockaddr_storage *relay_addr, socklen_t *relay_addrlen,
    struct sockaddr_storage *addr, socklen_t *addrlen,
    struct sockaddr_storage *alternate_server, socklen_t *alternate_server_len,
274 275
    uint32_t *bandwidth, uint32_t *lifetime,
    StunUsageTurnCompatibility compatibility)
276 277
{
  int val, code = -1;
278
  StunUsageTurnReturn ret = STUN_USAGE_TURN_RETURN_RELAY_SUCCESS;
279

280
  if (stun_message_get_method (msg) != STUN_ALLOCATE)
281
    return STUN_USAGE_TURN_RETURN_INVALID;
282

283 284 285 286
  switch (stun_message_get_class (msg))
  {
    case STUN_REQUEST:
    case STUN_INDICATION:
287
      return STUN_USAGE_TURN_RETURN_INVALID;
288 289 290 291 292

    case STUN_RESPONSE:
      break;

    case STUN_ERROR:
293
      if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
294
        /* missing ERROR-CODE: ignore message */
295
        return STUN_USAGE_TURN_RETURN_INVALID;
296 297 298 299
      }

      /* NOTE: currently we ignore unauthenticated messages if the context
       * is authenticated, for security reasons. */
300
      stun_debug (" STUN error message received (code: %d)", code);
301 302 303 304 305

      /* ALTERNATE-SERVER mechanism */
      if ((code / 100) == 3) {
        if (alternate_server && alternate_server_len) {
          if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER,
306 307
                  alternate_server, alternate_server_len) !=
              STUN_MESSAGE_RETURN_SUCCESS) {
308
            stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute");
309 310 311
            return STUN_USAGE_TURN_RETURN_ERROR;
          }
        } else {
312 313
          if (!stun_message_has_attribute (msg,
                  STUN_ATTRIBUTE_ALTERNATE_SERVER)) {
314
            stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute");
315 316 317 318
            return STUN_USAGE_TURN_RETURN_ERROR;
          }
        }

319
        stun_debug ("Found alternate server");
320 321 322 323
        return STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER;

      }
      return STUN_USAGE_TURN_RETURN_ERROR;
324 325 326 327

    default:
      /* Fall through. */
      break;
328 329
  }

330
  stun_debug ("Received %u-bytes STUN message", stun_message_length (msg));
331

332 333
  if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 ||
      compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) {
334
    val = stun_message_find_xor_addr (msg,
335
        STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen);
336

337
    if (val == STUN_MESSAGE_RETURN_SUCCESS)
338
      ret = STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS;
339
    val = stun_message_find_xor_addr (msg,
340
        STUN_ATTRIBUTE_RELAY_ADDRESS, relay_addr, relay_addrlen);
341
    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
342
      stun_debug (" No RELAYED-ADDRESS: %d", val);
343 344
      return STUN_USAGE_TURN_RETURN_ERROR;
    }
345
  } else if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_GOOGLE) {
346 347
    val = stun_message_find_addr (msg,
        STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen);
348
    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
349
      stun_debug (" No MAPPED-ADDRESS: %d", val);
350 351
      return STUN_USAGE_TURN_RETURN_ERROR;
    }
352
  } else if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_MSN) {
353
    val = stun_message_find_addr (msg,
354
        STUN_ATTRIBUTE_MSN_MAPPED_ADDRESS, addr, addrlen);
355

356
    if (val == STUN_MESSAGE_RETURN_SUCCESS)
357 358
      ret = STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS;

359 360 361
    val = stun_message_find_addr (msg,
        STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen);
    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
362
      stun_debug (" No MAPPED-ADDRESS: %d", val);
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
      return STUN_USAGE_TURN_RETURN_ERROR;
    }
  } else if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
    union {
      StunTransactionId id;
      uint32_t u32[4];
    } transid;
    uint32_t magic_cookie;

    stun_message_id (msg, transid.id);
    magic_cookie = transid.u32[0];

    val = stun_message_find_xor_addr_full (msg,
        STUN_ATTRIBUTE_MS_XOR_MAPPED_ADDRESS, addr, addrlen,
        htonl (magic_cookie));

    if (val == STUN_MESSAGE_RETURN_SUCCESS)
      ret = STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS;

382
    val = stun_message_find_addr (msg,
383
        STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen);
384
    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
385
      stun_debug (" No MAPPED-ADDRESS: %d", val);
386 387 388 389
      return STUN_USAGE_TURN_RETURN_ERROR;
    }
  }

390 391 392
  stun_message_find32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime);
  stun_message_find32 (msg, STUN_ATTRIBUTE_BANDWIDTH, bandwidth);

393
  stun_debug (" Mapped address found!");
394
  return ret;
395 396

}
397 398 399 400 401 402 403 404 405



StunUsageTurnReturn stun_usage_turn_refresh_process (StunMessage *msg,
    uint32_t *lifetime, StunUsageTurnCompatibility compatibility)
{
  int code = -1;
  StunUsageTurnReturn ret = STUN_USAGE_TURN_RETURN_RELAY_SUCCESS;

406 407
  if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 ||
      compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) {
408
    if (stun_message_get_method (msg) != STUN_REFRESH)
409
      return STUN_USAGE_TURN_RETURN_INVALID;
410 411
  } else {
    if (stun_message_get_method (msg) != STUN_ALLOCATE)
412
      return STUN_USAGE_TURN_RETURN_INVALID;
413 414 415 416 417 418
  }

  switch (stun_message_get_class (msg))
  {
    case STUN_REQUEST:
    case STUN_INDICATION:
419
      return STUN_USAGE_TURN_RETURN_INVALID;
420 421 422 423 424

    case STUN_RESPONSE:
      break;

    case STUN_ERROR:
425
      if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
426
        /* missing ERROR-CODE: ignore message */
427
        return STUN_USAGE_TURN_RETURN_INVALID;
428 429 430
      }

      return STUN_USAGE_TURN_RETURN_ERROR;
431 432 433 434

    default:
      /* Fall through. */
      break;
435 436 437 438
  }

  stun_message_find32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime);

439
  stun_debug ("TURN Refresh successful!");
440 441 442
  return ret;

}