blob: 34b6059604a96d8b05cbb169a31123dbc25d77ee [file] [log] [blame]
/*
* libjingle
* Copyright 2004 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 <string>
#include "talk/base/bytebuffer.h"
#include "talk/base/gunit.h"
#include "talk/base/logging.h"
#include "talk/p2p/base/stun.h"
using namespace cricket;
static const unsigned char INPUT_STUN[] = {
0x00, 0x01, 0x00, 135, // message header
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7',
'8', '9', 'a', 'b',
0x00, 0x01, 0x00, 8, // mapped address
0x00, 0x01, 0x00, 13,
0x00, 0x00, 0x00, 17,
0x00, 0x06, 0x00, 12, // username
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
0x00, 0x08, 0x00, 20, // message integrity
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
0x00, 0x09, 0x00, 12, // error code
0x00, 0x00, 2, 10,
'f', 'o', 'o', ' ', 'b', 'a', 'r', '!',
0x00, 0x0a, 0x00, 4, // unknown attributes
0x00, 0x01, 0x00, 0x02,
0x00, 0x0d, 0x00, 4, // lifetime
0x00, 0x00, 0x00, 11,
0x00, 0x0f, 0x00, 4, // magic cookie
0x72, 0xc6, 0x4b, 0xc6,
0x00, 0x10, 0x00, 4, // bandwidth
0x00, 0x00, 0x00, 6,
0x00, 0x11, 0x00, 8, // destination address
0x00, 0x01, 0x00, 13,
0x00, 0x00, 0x00, 17,
0x00, 0x12, 0x00, 8, // source address 2
0x00, 0x01, 0x00, 13,
0x00, 0x00, 0x00, 17,
0x00, 0x13, 0x00, 7, // data
'a', 'b', 'c', 'd', 'e', 'f', 'g'
};
// RTCP packet, for testing we correctly ignore non stun packet types.
// V=2, P=false, RC=0, Type=200, Len=6, Sender-SSRC=85, etc
static const unsigned char INPUT_RTCP[] = {
0x80, 0xc8, 0x00, 0x06, 0x00, 0x00, 0x00, 0x55,
0xce, 0xa5, 0x18, 0x3a, 0x39, 0xcc, 0x7d, 0x09,
0x23, 0xed, 0x19, 0x07, 0x00, 0x00, 0x01, 0x56,
0x00, 0x03, 0x73, 0x50,
};
// STUN packet with a legacy header.
static const unsigned char INPUT_LEGACY[] = {
0x00, 0x01, 0x00, 135, // message header
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f',
0x00, 0x01, 0x00, 8, // mapped address
0x00, 0x01, 0x00, 13,
0x00, 0x00, 0x00, 17,
0x00, 0x06, 0x00, 12, // username
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
0x00, 0x08, 0x00, 20, // message integrity
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
0x00, 0x09, 0x00, 12, // error code
0x00, 0x00, 2, 10,
'f', 'o', 'o', ' ', 'b', 'a', 'r', '!',
0x00, 0x0a, 0x00, 4, // unknown attributes
0x00, 0x01, 0x00, 0x02,
0x00, 0x0d, 0x00, 4, // lifetime
0x00, 0x00, 0x00, 11,
0x00, 0x0f, 0x00, 4, // magic cookie
0x72, 0xc6, 0x4b, 0xc6,
0x00, 0x10, 0x00, 4, // bandwidth
0x00, 0x00, 0x00, 6,
0x00, 0x11, 0x00, 8, // destination address
0x00, 0x01, 0x00, 13,
0x00, 0x00, 0x00, 17,
0x00, 0x12, 0x00, 8, // source address 2
0x00, 0x01, 0x00, 13,
0x00, 0x00, 0x00, 17,
0x00, 0x13, 0x00, 7, // data
'a', 'b', 'c', 'd', 'e', 'f', 'g'
};
static const unsigned char INPUT_STUN_UNKNOWN_ATTR[] = {
0x00, 0x01, 0x00, 0x1F, // message header
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7',
'8', '9', 'a', 'b',
0x00, 0x01, 0x00, 8, // mapped address
0x00, 0x01, 0x00, 13,
0x00, 0x00, 0x00, 17,
0x00, 0xaa, 0x00, 7, // unknown attribute
'a', 'b', 'c', 'd', 'e', 'f', 'g',
0x00, 0x0d, 0x00, 4, // lifetime
0x00, 0x00, 0x00, 11,
};
static const unsigned char INPUT_STUN_XOR_MAPPED_ADDRESS[] = {
0x00, 0x01, 0x00, 0x0c, // message header
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7',
'8', '9', 'a', 'b',
0x00, 0x20, 0x00, 0x08, // xor mapped address
0x00, 0x01, 0x21, 0x1F,
0x21, 0x12, 0xA4, 0x53,
};
static void DoTest(const char* input, size_t size, const char* transaction_id) {
StunMessage msg, msg2;
talk_base::ByteBuffer buf(input, size);
EXPECT_TRUE(msg.Read(&buf));
EXPECT_EQ(STUN_BINDING_REQUEST, msg.type());
EXPECT_EQ(size - 20, msg.length());
EXPECT_EQ(transaction_id, msg.transaction_id());
msg2.SetType(STUN_BINDING_REQUEST);
msg2.SetTransactionID(transaction_id);
const StunAddressAttribute* addr = msg.GetAddress(STUN_ATTR_MAPPED_ADDRESS);
EXPECT_TRUE(addr != NULL);
EXPECT_EQ(1, addr->family());
EXPECT_EQ(13, addr->port());
EXPECT_EQ(talk_base::IPAddress(17U), addr->ipaddr());
StunAddressAttribute* addr2 =
StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
addr2->SetPort(13);
addr2->SetIP(talk_base::IPAddress(17U));
msg2.AddAttribute(addr2);
const StunByteStringAttribute* bytes = msg.GetByteString(STUN_ATTR_USERNAME);
EXPECT_TRUE(bytes != NULL);
EXPECT_EQ(12, bytes->length());
EXPECT_EQ(0, memcmp(bytes->bytes(), "abcdefghijkl", bytes->length()));
StunByteStringAttribute* bytes2 =
StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
bytes2->CopyBytes("abcdefghijkl");
msg2.AddAttribute(bytes2);
bytes = msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY);
EXPECT_TRUE(bytes != NULL);
EXPECT_EQ(20, bytes->length());
EXPECT_EQ(0, memcmp(bytes->bytes(), "abcdefghijklmnopqrst", bytes->length()));
bytes2 = StunAttribute::CreateByteString(STUN_ATTR_MESSAGE_INTEGRITY);
bytes2->CopyBytes("abcdefghijklmnopqrst");
msg2.AddAttribute(bytes2);
const StunErrorCodeAttribute* ecode = msg.GetErrorCode();
EXPECT_TRUE(ecode != NULL);
EXPECT_EQ(2, ecode->error_class());
EXPECT_EQ(10, ecode->number());
EXPECT_EQ("foo bar!", ecode->reason());
StunErrorCodeAttribute* ecode2 = StunAttribute::CreateErrorCode();
ecode2->SetErrorClass(2);
ecode2->SetNumber(10);
ecode2->SetReason("foo bar!");
msg2.AddAttribute(ecode2);
const StunUInt16ListAttribute* unknown = msg.GetUnknownAttributes();
EXPECT_TRUE(unknown != NULL);
EXPECT_EQ(2U, unknown->Size());
EXPECT_EQ(1U, unknown->GetType(0));
EXPECT_EQ(2U, unknown->GetType(1));
StunUInt16ListAttribute* unknown2 = StunAttribute::CreateUnknownAttributes();
unknown2->AddType(1);
unknown2->AddType(2);
msg2.AddAttribute(unknown2);
const StunUInt32Attribute* uval = msg.GetUInt32(STUN_ATTR_LIFETIME);
EXPECT_TRUE(uval != NULL);
EXPECT_EQ(11U, uval->value());
StunUInt32Attribute* uval2 = StunAttribute::CreateUInt32(STUN_ATTR_LIFETIME);
uval2->SetValue(11);
msg2.AddAttribute(uval2);
bytes = msg.GetByteString(STUN_ATTR_MAGIC_COOKIE);
EXPECT_TRUE(bytes != NULL);
EXPECT_EQ(4, bytes->length());
EXPECT_EQ(0, memcmp(bytes->bytes(), TURN_MAGIC_COOKIE_VALUE,
sizeof(TURN_MAGIC_COOKIE_VALUE)));
bytes2 = StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
bytes2->CopyBytes(reinterpret_cast<const char*>(TURN_MAGIC_COOKIE_VALUE),
sizeof(TURN_MAGIC_COOKIE_VALUE));
msg2.AddAttribute(bytes2);
uval = msg.GetUInt32(STUN_ATTR_BANDWIDTH);
EXPECT_TRUE(uval != NULL);
EXPECT_EQ(6U, uval->value());
uval2 = StunAttribute::CreateUInt32(STUN_ATTR_BANDWIDTH);
uval2->SetValue(6);
msg2.AddAttribute(uval2);
addr = msg.GetAddress(STUN_ATTR_DESTINATION_ADDRESS);
EXPECT_TRUE(addr != NULL);
EXPECT_EQ(1, addr->family());
EXPECT_EQ(13, addr->port());
EXPECT_EQ(talk_base::IPAddress(17U), addr->ipaddr());
addr2 = StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
addr2->SetPort(13);
addr2->SetIP(talk_base::IPAddress(17U));
msg2.AddAttribute(addr2);
addr = msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2);
EXPECT_TRUE(addr != NULL);
EXPECT_EQ(1, addr->family());
EXPECT_EQ(13, addr->port());
EXPECT_EQ(talk_base::IPAddress(17U), addr->ipaddr());
addr2 = StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2);
addr2->SetPort(13);
addr2->SetIP(talk_base::IPAddress(17U));
msg2.AddAttribute(addr2);
bytes = msg.GetByteString(STUN_ATTR_DATA);
EXPECT_TRUE(bytes != NULL);
EXPECT_EQ(7, bytes->length());
EXPECT_EQ(0, memcmp(bytes->bytes(), "abcdefg", bytes->length()));
bytes2 = StunAttribute::CreateByteString(STUN_ATTR_DATA);
bytes2->CopyBytes("abcdefg");
msg2.AddAttribute(bytes2);
talk_base::ByteBuffer out;
msg.Write(&out);
EXPECT_EQ(size, out.Length());
EXPECT_EQ(0, memcmp(out.Data(), input, out.Length()));
talk_base::ByteBuffer out2;
msg2.Write(&out2);
EXPECT_EQ(size, out2.Length());
EXPECT_EQ(0, memcmp(out2.Data(), input, out2.Length()));
}
TEST(StunTest, TestStunPacket) {
DoTest(reinterpret_cast<const char*>(INPUT_STUN), sizeof(INPUT_STUN),
"0123456789ab");
}
TEST(StunTest, TestRejectsRtcpPacket) {
StunMessage msg;
talk_base::ByteBuffer buf(
reinterpret_cast<const char*>(INPUT_RTCP), sizeof(INPUT_RTCP));
EXPECT_FALSE(msg.Read(&buf));
}
TEST(StunTest, TestLegacyPacket) {
DoTest(reinterpret_cast<const char*>(INPUT_LEGACY),
sizeof(INPUT_LEGACY), "0123456789abcdef");
}
TEST(StunTest, TestIgnoreUnknownAttr) {
StunMessage msg;
talk_base::ByteBuffer buf(
reinterpret_cast<const char*>(INPUT_STUN_UNKNOWN_ATTR),
sizeof(INPUT_STUN_UNKNOWN_ATTR));
EXPECT_TRUE(msg.Read(&buf));
const StunAddressAttribute* addr = msg.GetAddress(STUN_ATTR_MAPPED_ADDRESS);
EXPECT_TRUE(addr != NULL);
EXPECT_EQ(1, addr->family());
EXPECT_EQ(13, addr->port());
EXPECT_EQ(talk_base::IPAddress(17U), addr->ipaddr());
const StunUInt32Attribute* uval = msg.GetUInt32(STUN_ATTR_LIFETIME);
EXPECT_TRUE(uval != NULL);
EXPECT_EQ(11U, uval->value());
}
TEST(StunTest, TestXorMappedAddress) {
StunMessage msg;
talk_base::ByteBuffer buf(
reinterpret_cast<const char*>(INPUT_STUN_XOR_MAPPED_ADDRESS),
sizeof(INPUT_STUN_XOR_MAPPED_ADDRESS));
EXPECT_TRUE(msg.Read(&buf));
const StunAddressAttribute* addr = msg.GetAddress(STUN_ATTR_MAPPED_ADDRESS);
EXPECT_TRUE(addr != NULL);
EXPECT_EQ(1, addr->family());
EXPECT_EQ(13, addr->port());
EXPECT_EQ(talk_base::IPAddress(17U), addr->ipaddr());
addr = msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
EXPECT_TRUE(addr != NULL);
EXPECT_EQ(1, addr->family());
EXPECT_EQ(13, addr->port());
EXPECT_EQ(talk_base::IPAddress(17U), addr->ipaddr());
}