blob: b7f6b525132b3e0fd34612ea9e495d1eb62c8642 [file] [log] [blame] [edit]
/*
* libjingle
* Copyright 2004--2005, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "talk/p2p/base/stun.h"
#include <cstring>
#include "talk/base/byteorder.h"
#include "talk/base/common.h"
#include "talk/base/logging.h"
using talk_base::ByteBuffer;
namespace cricket {
const char STUN_ERROR_REASON_BAD_REQUEST[] = "BAD REQUEST";
const char STUN_ERROR_REASON_STALE_CREDENTIALS[] = "STALE CREDENTIALS";
const char STUN_ERROR_REASON_SERVER_ERROR[] = "SERVER ERROR";
const char TURN_MAGIC_COOKIE_VALUE[] = { '\x72', '\xC6', '\x4B', '\xC6' };
const char EMPTY_TRANSACTION_ID[] = "0000000000000000";
StunMessage::StunMessage()
: type_(0), length_(0),
transaction_id_(EMPTY_TRANSACTION_ID) {
ASSERT(IsValidTransactionId(transaction_id_));
attrs_ = new std::vector<StunAttribute*>();
}
StunMessage::~StunMessage() {
for (size_t i = 0; i < attrs_->size(); i++)
delete (*attrs_)[i];
delete attrs_;
}
bool StunMessage::IsLegacy() const {
if (transaction_id_.size() == kStunLegacyTransactionIdLength)
return true;
ASSERT(transaction_id_.size() == kStunTransactionIdLength);
return false;
}
bool StunMessage::SetTransactionID(const std::string& str) {
if (!IsValidTransactionId(str)) {
return false;
}
transaction_id_ = str;
return true;
}
void StunMessage::AddAttribute(StunAttribute* attr) {
attrs_->push_back(attr);
attr->SetOwner(this);
size_t attr_length = attr->length();
if (attr_length % 4 != 0) {
attr_length += (4 - (attr_length % 4));
}
length_ += attr_length + 4;
}
const StunAddressAttribute*
StunMessage::GetAddress(StunAttributeType type) const {
switch (type) {
case STUN_ATTR_MAPPED_ADDRESS: {
// Return XOR-MAPPED-ADDRESS when MAPPED-ADDRESS attribute is
// missing.
const StunAttribute* mapped_address =
GetAttribute(STUN_ATTR_MAPPED_ADDRESS);
if (!mapped_address)
mapped_address = GetAttribute(STUN_ATTR_XOR_MAPPED_ADDRESS);
return reinterpret_cast<const StunAddressAttribute*>(mapped_address);
}
case STUN_ATTR_DESTINATION_ADDRESS:
case STUN_ATTR_SOURCE_ADDRESS2:
case STUN_ATTR_XOR_MAPPED_ADDRESS:
return reinterpret_cast<const StunAddressAttribute*>(GetAttribute(type));
default:
ASSERT(0);
return NULL;
}
}
const StunUInt32Attribute*
StunMessage::GetUInt32(StunAttributeType type) const {
switch (type) {
case STUN_ATTR_LIFETIME:
case STUN_ATTR_BANDWIDTH:
case STUN_ATTR_OPTIONS:
return reinterpret_cast<const StunUInt32Attribute*>(GetAttribute(type));
default:
ASSERT(0);
return NULL;
}
}
const StunByteStringAttribute*
StunMessage::GetByteString(StunAttributeType type) const {
switch (type) {
case STUN_ATTR_USERNAME:
case STUN_ATTR_MESSAGE_INTEGRITY:
case STUN_ATTR_DATA:
case STUN_ATTR_MAGIC_COOKIE:
return reinterpret_cast<const StunByteStringAttribute*>(
GetAttribute(type));
default:
ASSERT(0);
return NULL;
}
}
const StunErrorCodeAttribute* StunMessage::GetErrorCode() const {
return reinterpret_cast<const StunErrorCodeAttribute*>(
GetAttribute(STUN_ATTR_ERROR_CODE));
}
const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const {
return reinterpret_cast<const StunUInt16ListAttribute*>(
GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
}
const StunAttribute* StunMessage::GetAttribute(StunAttributeType type) const {
for (size_t i = 0; i < attrs_->size(); i++) {
if ((*attrs_)[i]->type() == type)
return (*attrs_)[i];
}
return NULL;
}
bool StunMessage::Read(ByteBuffer* buf) {
if (!buf->ReadUInt16(&type_))
return false;
if (type_ & 0x8000) {
// rtp and rtcp set MSB of first byte, since first two bits are version,
// and version is always 2 (10). If set, this is not a stun packet.
return false;
}
if (!buf->ReadUInt16(&length_))
return false;
std::string magic_cookie;
if (!buf->ReadString(&magic_cookie, kStunMagicCookieLength))
return false;
std::string transaction_id;
if (!buf->ReadString(&transaction_id, kStunTransactionIdLength))
return false;
uint32 magic_cookie_int =
*reinterpret_cast<const uint32*>(magic_cookie.data());
if (talk_base::NetworkToHost32(magic_cookie_int) != kStunMagicCookie) {
// If magic cookie is invalid it means that the peer implements
// RFC3489 instead of RFC5389.
transaction_id.insert(0, magic_cookie);
}
ASSERT(IsValidTransactionId(transaction_id));
transaction_id_ = transaction_id;
if (length_ != buf->Length())
return false;
attrs_->resize(0);
size_t rest = buf->Length() - length_;
while (buf->Length() > rest) {
uint16 attr_type, attr_length;
if (!buf->ReadUInt16(&attr_type))
return false;
if (!buf->ReadUInt16(&attr_length))
return false;
StunAttribute* attr = StunAttribute::Create(attr_type, attr_length,
this);
if (!attr) {
// Skip an unknown attribute.
if ((attr_length % 4) != 0) {
attr_length += (4 - (attr_length % 4));
}
if (!buf->Consume(attr_length))
return false;
} else {
if (!attr->Read(buf))
return false;
attrs_->push_back(attr);
}
}
ASSERT(buf->Length() == rest);
return true;
}
void StunMessage::Write(ByteBuffer* buf) const {
buf->WriteUInt16(type_);
buf->WriteUInt16(length_);
if (!IsLegacy())
buf->WriteUInt32(kStunMagicCookie);
buf->WriteString(transaction_id_);
for (size_t i = 0; i < attrs_->size(); i++) {
buf->WriteUInt16((*attrs_)[i]->type());
buf->WriteUInt16((*attrs_)[i]->length());
(*attrs_)[i]->Write(buf);
}
}
bool StunMessage::IsValidTransactionId(const std::string& transaction_id) {
return transaction_id.size() == kStunTransactionIdLength ||
transaction_id.size() == kStunLegacyTransactionIdLength;
}
StunAttribute::StunAttribute(uint16 type, uint16 length)
: type_(type), length_(length) {
}
StunAttribute* StunAttribute::Create(uint16 type,
uint16 length,
StunMessage* owner) {
switch (type) {
case STUN_ATTR_MAPPED_ADDRESS:
case STUN_ATTR_DESTINATION_ADDRESS:
case STUN_ATTR_SOURCE_ADDRESS2:
if (length != StunAddressAttribute::SIZE_IP4 &&
length != StunAddressAttribute::SIZE_IP6) {
LOG(LS_WARNING) << "Invalid length specified for address attribute";
return NULL;
}
return new StunAddressAttribute(type, length);
case STUN_ATTR_LIFETIME:
case STUN_ATTR_BANDWIDTH:
case STUN_ATTR_OPTIONS:
if (length != StunUInt32Attribute::SIZE)
return NULL;
return new StunUInt32Attribute(type);
case STUN_ATTR_USERNAME:
case STUN_ATTR_MAGIC_COOKIE:
case STUN_ATTR_DATA:
return new StunByteStringAttribute(type, length);
case STUN_ATTR_MESSAGE_INTEGRITY:
return (length == 20) ? new StunByteStringAttribute(type, length) : 0;
case STUN_ATTR_ERROR_CODE:
if (length < StunErrorCodeAttribute::MIN_SIZE)
return NULL;
return new StunErrorCodeAttribute(type, length);
case STUN_ATTR_UNKNOWN_ATTRIBUTES:
return (length % 2 == 0) ? new StunUInt16ListAttribute(type, length) : 0;
case STUN_ATTR_XOR_MAPPED_ADDRESS:
if (length != StunAddressAttribute::SIZE_IP4 &&
length != StunAddressAttribute::SIZE_IP6) {
LOG(LS_WARNING) << "Invalid length specified for XOR address attribute";
return NULL;
}
return new StunXorAddressAttribute(type, length, owner);
default:
return NULL;
}
}
void StunAttribute::ConsumePadding(talk_base::ByteBuffer* buf) const {
int remainder = length_ % 4;
if (remainder > 0) {
buf->Consume(4 - remainder);
}
}
void StunAttribute::WritePadding(talk_base::ByteBuffer* buf) const {
int remainder = length_ % 4;
if (remainder > 0) {
char zeroes[4] = {0};
buf->WriteBytes(zeroes, 4 - remainder);
}
}
StunAddressAttribute* StunAttribute::CreateAddress(uint16 type) {
switch (type) {
case STUN_ATTR_MAPPED_ADDRESS:
case STUN_ATTR_DESTINATION_ADDRESS:
case STUN_ATTR_SOURCE_ADDRESS2:
return new StunAddressAttribute(type, StunAddressAttribute::SIZE_IP4);
case STUN_ATTR_XOR_MAPPED_ADDRESS:
return new StunXorAddressAttribute(type, StunAddressAttribute::SIZE_IP4);
default:
ASSERT(false);
return NULL;
}
}
StunUInt32Attribute* StunAttribute::CreateUInt32(uint16 type) {
switch (type) {
case STUN_ATTR_LIFETIME:
case STUN_ATTR_BANDWIDTH:
case STUN_ATTR_OPTIONS:
return new StunUInt32Attribute(type);
default:
ASSERT(false);
return NULL;
}
}
StunByteStringAttribute* StunAttribute::CreateByteString(uint16 type) {
switch (type) {
case STUN_ATTR_USERNAME:
case STUN_ATTR_MESSAGE_INTEGRITY:
case STUN_ATTR_DATA:
case STUN_ATTR_MAGIC_COOKIE:
return new StunByteStringAttribute(type, 0);
default:
ASSERT(false);
return NULL;
}
}
StunErrorCodeAttribute* StunAttribute::CreateErrorCode() {
return new StunErrorCodeAttribute(
STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE);
}
StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() {
return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
}
StunAddressAttribute::StunAddressAttribute(uint16 type, uint16 length)
: StunAttribute(type, length) { }
bool StunAddressAttribute::Read(ByteBuffer* buf) {
uint8 dummy;
if (!buf->ReadUInt8(&dummy))
return false;
uint8 stun_family;
if (!buf->ReadUInt8(&stun_family)) {
return false;
}
uint16 port;
if (!buf->ReadUInt16(&port))
return false;
if (stun_family == STUN_ADDRESS_IPV4) {
in_addr v4addr;
if (length() != SIZE_IP4) {
return false;
}
if (!buf->ReadBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr))) {
return false;
}
talk_base::IPAddress ipaddr(v4addr);
SetAddress(talk_base::SocketAddress(ipaddr, port));
} else if (stun_family == STUN_ADDRESS_IPV6) {
in6_addr v6addr;
if (length() != SIZE_IP6) {
return false;
}
if (!buf->ReadBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr))) {
return false;
}
talk_base::IPAddress ipaddr(v6addr);
SetAddress(talk_base::SocketAddress(ipaddr, port));
} else {
return false;
}
return true;
}
void StunAddressAttribute::Write(ByteBuffer* buf) const {
StunAddressFamily address_family = family();
if (address_family == STUN_ADDRESS_UNDEF) {
LOG(LS_ERROR) << "Error writing address attribute: unknown family.";
return;
}
buf->WriteUInt8(0);
buf->WriteUInt8(address_family);
buf->WriteUInt16(address_.port());
switch (address_family) {
case STUN_ADDRESS_IPV4: {
in_addr v4addr = address_.ipaddr().ipv4_address();
buf->WriteBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr));
break;
}
case STUN_ADDRESS_IPV6: {
in6_addr v6addr = address_.ipaddr().ipv6_address();
buf->WriteBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr));
break;
}
}
}
StunXorAddressAttribute::StunXorAddressAttribute(uint16 type, uint16 length)
: StunAddressAttribute(type, length), owner_(NULL) { }
StunXorAddressAttribute::StunXorAddressAttribute(uint16 type,
uint16 length,
StunMessage* owner)
: StunAddressAttribute(type, length), owner_(owner) { }
talk_base::IPAddress StunXorAddressAttribute::GetXoredIP() const {
if (owner_) {
talk_base::IPAddress ip = ipaddr();
switch (ip.family()) {
case AF_INET: {
in_addr v4addr = ip.ipv4_address();
v4addr.s_addr =
(v4addr.s_addr ^ talk_base::HostToNetwork32(kStunMagicCookie));
return talk_base::IPAddress(v4addr);
break;
}
case AF_INET6: {
in6_addr v6addr = ip.ipv6_address();
const std::string& transaction_id = owner_->transaction_id();
if (transaction_id.length() == 12) {
uint32 transactionid_as_ints[3];
memcpy(&transactionid_as_ints[0], transaction_id.c_str(),
transaction_id.length());
uint32* ip_as_ints = reinterpret_cast<uint32*>(&v6addr.s6_addr);
// Transaction ID is in network byte order, but magic cookie
// is stored in host byte order.
ip_as_ints[0] =
(ip_as_ints[0] ^ talk_base::HostToNetwork32(kStunMagicCookie));
ip_as_ints[1] = (ip_as_ints[1] ^ transactionid_as_ints[0]);
ip_as_ints[2] = (ip_as_ints[2] ^ transactionid_as_ints[1]);
ip_as_ints[3] = (ip_as_ints[3] ^ transactionid_as_ints[2]);
return talk_base::IPAddress(v6addr);
}
break;
}
}
}
// Invalid ip family or transaction ID, or missing owner.
// Return an AF_UNSPEC address.
return talk_base::IPAddress();
}
bool StunXorAddressAttribute::Read(ByteBuffer* buf) {
if (!StunAddressAttribute::Read(buf))
return false;
uint16 xoredport = port() ^ (kStunMagicCookie >> 16);
talk_base::IPAddress xored_ip = GetXoredIP();
SetAddress(talk_base::SocketAddress(xored_ip, xoredport));
return true;
}
void StunXorAddressAttribute::Write(ByteBuffer* buf) const {
StunAddressFamily address_family = family();
if (address_family == STUN_ADDRESS_UNDEF) {
LOG(LS_ERROR) << "Error writing xor-address attribute: unknown family.";
return;
}
buf->WriteUInt8(0);
buf->WriteUInt8(family());
buf->WriteUInt16(port() ^ (kStunMagicCookie >> 16));
talk_base::IPAddress xored_ip = GetXoredIP();
switch (xored_ip.family()) {
case AF_INET: {
in_addr v4addr = xored_ip.ipv4_address();
buf->WriteBytes(reinterpret_cast<const char*>(&v4addr), sizeof(v4addr));
break;
}
case AF_INET6: {
in6_addr v6addr = xored_ip.ipv6_address();
buf->WriteBytes(reinterpret_cast<const char*>(&v6addr), sizeof(v6addr));
break;
}
}
}
StunUInt32Attribute::StunUInt32Attribute(uint16 type)
: StunAttribute(type, SIZE), bits_(0) {
}
bool StunUInt32Attribute::GetBit(int index) const {
ASSERT((0 <= index) && (index < 32));
return static_cast<bool>((bits_ >> index) & 0x1);
}
void StunUInt32Attribute::SetBit(int index, bool value) {
ASSERT((0 <= index) && (index < 32));
bits_ &= ~(1 << index);
bits_ |= value ? (1 << index) : 0;
}
bool StunUInt32Attribute::Read(ByteBuffer* buf) {
if (!buf->ReadUInt32(&bits_))
return false;
return true;
}
void StunUInt32Attribute::Write(ByteBuffer* buf) const {
buf->WriteUInt32(bits_);
}
StunByteStringAttribute::StunByteStringAttribute(uint16 type, uint16 length)
: StunAttribute(type, length), bytes_(0) {
}
StunByteStringAttribute::~StunByteStringAttribute() {
delete [] bytes_;
}
void StunByteStringAttribute::SetBytes(char* bytes, uint16 length) {
delete [] bytes_;
bytes_ = bytes;
SetLength(length);
}
void StunByteStringAttribute::CopyBytes(const char* bytes) {
CopyBytes(bytes, static_cast<uint16>(strlen(bytes)));
}
void StunByteStringAttribute::CopyBytes(const void* bytes, uint16 length) {
char* new_bytes = new char[length];
std::memcpy(new_bytes, bytes, length);
SetBytes(new_bytes, length);
}
uint8 StunByteStringAttribute::GetByte(int index) const {
ASSERT(bytes_ != NULL);
ASSERT((0 <= index) && (index < length()));
return static_cast<uint8>(bytes_[index]);
}
void StunByteStringAttribute::SetByte(int index, uint8 value) {
ASSERT(bytes_ != NULL);
ASSERT((0 <= index) && (index < length()));
bytes_[index] = value;
}
bool StunByteStringAttribute::Read(ByteBuffer* buf) {
bytes_ = new char[length()];
if (!buf->ReadBytes(bytes_, length())) {
return false;
}
ConsumePadding(buf);
return true;
}
void StunByteStringAttribute::Write(ByteBuffer* buf) const {
buf->WriteBytes(bytes_, length());
WritePadding(buf);
}
StunErrorCodeAttribute::StunErrorCodeAttribute(uint16 type, uint16 length)
: StunAttribute(type, length), class_(0), number_(0) {
}
StunErrorCodeAttribute::~StunErrorCodeAttribute() {
}
void StunErrorCodeAttribute::SetErrorCode(uint32 code) {
class_ = (uint8)((code >> 8) & 0x7);
number_ = (uint8)(code & 0xff);
}
void StunErrorCodeAttribute::SetReason(const std::string& reason) {
SetLength(MIN_SIZE + static_cast<uint16>(reason.size()));
reason_ = reason;
}
bool StunErrorCodeAttribute::Read(ByteBuffer* buf) {
uint32 val;
if (!buf->ReadUInt32(&val))
return false;
if ((val >> 11) != 0)
LOG(LERROR) << "error-code bits not zero";
SetErrorCode(val);
if (!buf->ReadString(&reason_, length() - 4))
return false;
ConsumePadding(buf);
return true;
}
void StunErrorCodeAttribute::Write(ByteBuffer* buf) const {
buf->WriteUInt32(error_code());
buf->WriteString(reason_);
WritePadding(buf);
}
StunUInt16ListAttribute::StunUInt16ListAttribute(uint16 type, uint16 length)
: StunAttribute(type, length) {
attr_types_ = new std::vector<uint16>();
}
StunUInt16ListAttribute::~StunUInt16ListAttribute() {
delete attr_types_;
}
size_t StunUInt16ListAttribute::Size() const {
return attr_types_->size();
}
uint16 StunUInt16ListAttribute::GetType(int index) const {
return (*attr_types_)[index];
}
void StunUInt16ListAttribute::SetType(int index, uint16 value) {
(*attr_types_)[index] = value;
}
void StunUInt16ListAttribute::AddType(uint16 value) {
attr_types_->push_back(value);
SetLength(static_cast<uint16>(attr_types_->size() * 2));
}
bool StunUInt16ListAttribute::Read(ByteBuffer* buf) {
for (int i = 0; i < length() / 2; i++) {
uint16 attr;
if (!buf->ReadUInt16(&attr))
return false;
attr_types_->push_back(attr);
}
// Padding of these attributes is done in RFC 5389 style. This is
// slightly different from RFC3489, but it shouldn't be important.
// RFC3489 pads out to a 32 bit boundary by duplicating one of the
// entries in the list (not necessarily the last one - it's unspecified).
// RFC5389 pads on the end, and the bytes are always ignored.
ConsumePadding(buf);
return true;
}
void StunUInt16ListAttribute::Write(ByteBuffer* buf) const {
for (size_t i = 0; i < attr_types_->size(); i++) {
buf->WriteUInt16((*attr_types_)[i]);
}
WritePadding(buf);
}
StunMessageType GetStunResponseType(StunMessageType request_type) {
switch (request_type) {
case STUN_SHARED_SECRET_REQUEST:
return STUN_SHARED_SECRET_RESPONSE;
case STUN_ALLOCATE_REQUEST:
return STUN_ALLOCATE_RESPONSE;
case STUN_SEND_REQUEST:
return STUN_SEND_RESPONSE;
default:
return STUN_BINDING_RESPONSE;
}
}
StunMessageType GetStunErrorResponseType(StunMessageType request_type) {
switch (request_type) {
case STUN_SHARED_SECRET_REQUEST:
return STUN_SHARED_SECRET_ERROR_RESPONSE;
case STUN_ALLOCATE_REQUEST:
return STUN_ALLOCATE_ERROR_RESPONSE;
case STUN_SEND_REQUEST:
return STUN_SEND_ERROR_RESPONSE;
default:
return STUN_BINDING_ERROR_RESPONSE;
}
}
} // namespace cricket