libnice/stun/usages/turn.c

455 lines
15 KiB
C

/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2008-2009 Collabora Ltd.
* Contact: Youness Alaoui
* (C) 2007-2009 Nokia Corporation. All rights reserved.
* 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:
* Youness Alaoui, Collabora Ltd.
* 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
#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#endif
#include "stun/stunagent.h"
#include "turn.h"
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#define REQUESTED_PROPS_E 0x80000000
#define REQUESTED_PROPS_R 0x40000000
#define REQUESTED_PROPS_P 0x20000000
#define STUN_ATTRIBUTE_MSN_MAPPED_ADDRESS 0x8000
#define TURN_REQUESTED_TRANSPORT_UDP 0x11000000
/** Non-blocking mode STUN TURN usage */
size_t stun_usage_turn_create (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len,
StunMessage *previous_response,
StunUsageTurnRequestPorts request_props,
int32_t bandwidth, int32_t lifetime,
uint8_t *username, size_t username_len,
uint8_t *password, size_t password_len,
StunUsageTurnCompatibility compatibility)
{
stun_agent_init_request (agent, msg, buffer, buffer_len, STUN_ALLOCATE);
if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 ||
compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) {
if (stun_message_append32 (msg, STUN_ATTRIBUTE_REQUESTED_TRANSPORT,
TURN_REQUESTED_TRANSPORT_UDP) != STUN_MESSAGE_RETURN_SUCCESS)
return 0;
if (bandwidth >= 0) {
if (stun_message_append32 (msg, STUN_ATTRIBUTE_BANDWIDTH, bandwidth) !=
STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
} else {
if (stun_message_append32 (msg, STUN_ATTRIBUTE_MAGIC_COOKIE,
TURN_MAGIC_COOKIE) != STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
if (stun_message_append32(msg, STUN_ATTRIBUTE_MS_VERSION, 1) !=
STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
if (lifetime >= 0) {
if (stun_message_append32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime) !=
STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
if ((compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 ||
compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) &&
request_props != STUN_USAGE_TURN_REQUEST_PORT_NORMAL) {
uint32_t req = 0;
if (request_props & STUN_USAGE_TURN_REQUEST_PORT_EVEN_AND_RESERVE) {
req |= REQUESTED_PROPS_R;
req |= REQUESTED_PROPS_E;
} else if (request_props & STUN_USAGE_TURN_REQUEST_PORT_EVEN) {
req |= REQUESTED_PROPS_E;
}
if (stun_message_append32 (msg, STUN_ATTRIBUTE_REQUESTED_PORT_PROPS,
req) != STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
if (previous_response) {
uint8_t *realm;
uint8_t *nonce;
uint64_t reservation;
uint16_t len;
realm = (uint8_t *) stun_message_find (previous_response,
STUN_ATTRIBUTE_REALM, &len);
if (realm != NULL) {
if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_REALM, realm, len) !=
STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
nonce = (uint8_t *) stun_message_find (previous_response,
STUN_ATTRIBUTE_NONCE, &len);
if (nonce != NULL) {
if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_NONCE, nonce, len) !=
STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
if (stun_message_find64 (previous_response,
STUN_ATTRIBUTE_RESERVATION_TOKEN,
&reservation) == STUN_MESSAGE_RETURN_SUCCESS) {
if (stun_message_append64 (msg, STUN_ATTRIBUTE_RESERVATION_TOKEN,
reservation) != STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
}
if (username != NULL && username_len > 0 &&
(agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS ||
previous_response)) {
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);
}
size_t stun_usage_turn_create_refresh (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len,
StunMessage *previous_response, int32_t lifetime,
uint8_t *username, size_t username_len,
uint8_t *password, size_t password_len,
StunUsageTurnCompatibility compatibility)
{
if (compatibility != STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 &&
compatibility != STUN_USAGE_TURN_COMPATIBILITY_RFC5766) {
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);
}
stun_agent_init_request (agent, msg, buffer, buffer_len, STUN_REFRESH);
if (lifetime >= 0) {
if (stun_message_append32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime) !=
STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
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) {
if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_REALM, realm, len) !=
STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
nonce = (uint8_t *) stun_message_find (previous_response,
STUN_ATTRIBUTE_NONCE, &len);
if (nonce != NULL) {
if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_NONCE, nonce, len) !=
STUN_MESSAGE_RETURN_SUCCESS)
return 0;
}
}
if (username != NULL && username_len > 0 &&
(agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS ||
previous_response)) {
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);
}
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,
struct sockaddr_storage *peer,
StunUsageTurnCompatibility compatibility)
{
if (!peer)
return 0;
stun_agent_init_request (agent, msg, buffer, buffer_len,
STUN_CREATEPERMISSION);
/* PEER address */
if (stun_message_append_xor_addr (msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS,
peer, sizeof(*peer)) != STUN_MESSAGE_RETURN_SUCCESS) {
return 0;
}
/* 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 */
if (username != NULL &&
(agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS ||
(nonce != NULL && realm != NULL))) {
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);
}
StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg,
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,
uint32_t *bandwidth, uint32_t *lifetime,
StunUsageTurnCompatibility compatibility)
{
int val, code = -1;
StunUsageTurnReturn ret = STUN_USAGE_TURN_RETURN_RELAY_SUCCESS;
if (stun_message_get_method (msg) != STUN_ALLOCATE)
return STUN_USAGE_TURN_RETURN_INVALID;
switch (stun_message_get_class (msg))
{
case STUN_REQUEST:
case STUN_INDICATION:
return STUN_USAGE_TURN_RETURN_INVALID;
case STUN_RESPONSE:
break;
case STUN_ERROR:
if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
/* missing ERROR-CODE: ignore message */
return STUN_USAGE_TURN_RETURN_INVALID;
}
/* NOTE: currently we ignore unauthenticated messages if the context
* is authenticated, for security reasons. */
stun_debug (" STUN error message received (code: %d)", code);
/* ALTERNATE-SERVER mechanism */
if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007 &&
alternate_server && alternate_server_len &&
stun_message_find_addr (msg, STUN_ATTRIBUTE_MS_ALTERNATE_SERVER,
alternate_server,
alternate_server_len) == STUN_MESSAGE_RETURN_SUCCESS) {
stun_debug ("Found alternate server");
/* The ALTERNATE_SERVER will always be returned by the MS turn server.
* We need to check if the ALTERNATE_SERVER is the same as the current
* server to decide whether we need to switch servers or not.
*/
}
if ((code / 100) == 3) {
if (alternate_server && alternate_server_len) {
if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER,
alternate_server, alternate_server_len) !=
STUN_MESSAGE_RETURN_SUCCESS) {
stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute");
return STUN_USAGE_TURN_RETURN_ERROR;
}
} else {
if (!stun_message_has_attribute (msg,
STUN_ATTRIBUTE_ALTERNATE_SERVER)) {
stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute");
return STUN_USAGE_TURN_RETURN_ERROR;
}
}
stun_debug ("Found alternate server");
return STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER;
}
return STUN_USAGE_TURN_RETURN_ERROR;
default:
/* Fall through. */
break;
}
stun_debug ("Received %u-bytes STUN message", stun_message_length (msg));
if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 ||
compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) {
val = stun_message_find_xor_addr (msg,
STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen);
if (val == STUN_MESSAGE_RETURN_SUCCESS)
ret = STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS;
val = stun_message_find_xor_addr (msg,
STUN_ATTRIBUTE_RELAY_ADDRESS, relay_addr, relay_addrlen);
if (val != STUN_MESSAGE_RETURN_SUCCESS) {
stun_debug (" No RELAYED-ADDRESS: %d", val);
return STUN_USAGE_TURN_RETURN_ERROR;
}
} else if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_GOOGLE) {
val = stun_message_find_addr (msg,
STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen);
if (val != STUN_MESSAGE_RETURN_SUCCESS) {
stun_debug (" No MAPPED-ADDRESS: %d", val);
return STUN_USAGE_TURN_RETURN_ERROR;
}
} else if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_MSN) {
val = stun_message_find_addr (msg,
STUN_ATTRIBUTE_MSN_MAPPED_ADDRESS, addr, addrlen);
if (val == STUN_MESSAGE_RETURN_SUCCESS)
ret = STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS;
val = stun_message_find_addr (msg,
STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen);
if (val != STUN_MESSAGE_RETURN_SUCCESS) {
stun_debug (" No MAPPED-ADDRESS: %d", val);
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;
val = stun_message_find_addr (msg,
STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen);
if (val != STUN_MESSAGE_RETURN_SUCCESS) {
stun_debug (" No MAPPED-ADDRESS: %d", val);
return STUN_USAGE_TURN_RETURN_ERROR;
}
}
stun_message_find32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime);
stun_message_find32 (msg, STUN_ATTRIBUTE_BANDWIDTH, bandwidth);
stun_debug (" Mapped address found!");
return ret;
}
StunUsageTurnReturn stun_usage_turn_refresh_process (StunMessage *msg,
uint32_t *lifetime, StunUsageTurnCompatibility compatibility)
{
int code = -1;
StunUsageTurnReturn ret = STUN_USAGE_TURN_RETURN_RELAY_SUCCESS;
if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 ||
compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) {
if (stun_message_get_method (msg) != STUN_REFRESH)
return STUN_USAGE_TURN_RETURN_INVALID;
} else {
if (stun_message_get_method (msg) != STUN_ALLOCATE)
return STUN_USAGE_TURN_RETURN_INVALID;
}
switch (stun_message_get_class (msg))
{
case STUN_REQUEST:
case STUN_INDICATION:
return STUN_USAGE_TURN_RETURN_INVALID;
case STUN_RESPONSE:
break;
case STUN_ERROR:
if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
/* missing ERROR-CODE: ignore message */
return STUN_USAGE_TURN_RETURN_INVALID;
}
return STUN_USAGE_TURN_RETURN_ERROR;
default:
/* Fall through. */
break;
}
stun_message_find32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime);
stun_debug ("TURN Refresh successful!");
return ret;
}