blob: 1152beea2d785ef9d403c2bd8e9fd97cd9f77b90 [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/base/scoped_ptr.h"
#include "talk/base/socketaddress.h"
#include "talk/p2p/base/stun.h"
namespace cricket {
class StunTest : public ::testing::Test {
protected:
void CheckStunHeader(const StunMessage& msg, StunMessageType expected_type,
size_t expected_length) {
ASSERT_EQ(expected_type, msg.type());
ASSERT_EQ(expected_length, msg.length());
}
void CheckStunTransactionID(const StunMessage& msg,
const unsigned char* expectedID, size_t length) {
ASSERT_EQ(0, std::memcmp(msg.transaction_id().c_str(),
expectedID, length));
}
void CheckStunAddressAttribute(const StunAddressAttribute* addr,
StunAddressFamily expected_family,
int expected_port,
talk_base::IPAddress expected_address) {
ASSERT_EQ(expected_family, addr->family());
ASSERT_EQ(expected_port, addr->port());
if (addr->family() == STUN_ADDRESS_IPV4) {
in_addr v4_address = expected_address.ipv4_address();
in_addr stun_address = addr->ipaddr().ipv4_address();
ASSERT_EQ(0, std::memcmp(&v4_address, &stun_address,
sizeof(stun_address)));
} else if (addr->family() == STUN_ADDRESS_IPV6) {
in6_addr v6_address = expected_address.ipv6_address();
in6_addr stun_address = addr->ipaddr().ipv6_address();
ASSERT_EQ(0, std::memcmp(&v6_address, &stun_address,
sizeof(stun_address)));
} else {
ASSERT_TRUE(addr->family() == STUN_ADDRESS_IPV6 ||
addr->family() == STUN_ADDRESS_IPV4);
}
}
size_t ReadStunMessageTestCase(StunMessage* msg,
const unsigned char* testcase,
size_t size) {
const char* input = reinterpret_cast<const char*>(testcase);
talk_base::ByteBuffer buf(input, size);
if (msg->Read(&buf)) {
// Returns the size the stun message should report itself as being
return (size - 20);
} else {
return 0;
}
}
};
// Sample STUN packets with various attributes
// Gathered by wiresharking pjproject's pjnath test programs
// pjproject available at www.pjsip.org
static const unsigned char kStunMessageWithIPv6MappedAddress[] = {
0x00, 0x01, 0x00, 0x18, // message header
0x21, 0x12, 0xa4, 0x42, // transaction id
0x29, 0x1f, 0xcd, 0x7c,
0xba, 0x58, 0xab, 0xd7,
0xf2, 0x41, 0x01, 0x00,
0x00, 0x01, 0x00, 0x14, // Address type (mapped), length
0x00, 0x02, 0xb8, 0x81, // family (IPv6), port
0x24, 0x01, 0xfa, 0x00, // an IPv6 address
0x00, 0x04, 0x10, 0x00,
0xbe, 0x30, 0x5b, 0xff,
0xfe, 0xe5, 0x00, 0xc3
};
static const unsigned char kStunMessageWithIPv4MappedAddress[] = {
0x01, 0x01, 0x00, 0x0c, // binding response, length 12
0x21, 0x12, 0xa4, 0x42, // magic cookie
0x29, 0x1f, 0xcd, 0x7c, // transaction ID
0xba, 0x58, 0xab, 0xd7,
0xf2, 0x41, 0x01, 0x00,
0x00, 0x01, 0x00, 0x08, // Mapped, 8 byte length
0x00, 0x01, 0x9d, 0xfc, // AF_INET, unxor-ed port
0xac, 0x17, 0x44, 0xe6 // IPv4 address
};
// Test XOR-mapped IP addresses:
static const unsigned char kStunMessageWithIPv6XorMappedAddress[] = {
0x01, 0x01, 0x00, 0x18, // message header (binding response)
0x21, 0x12, 0xa4, 0x42, // magic cookie (rfc5389)
0xe3, 0xa9, 0x46, 0xe1, // transaction ID
0x7c, 0x00, 0xc2, 0x62,
0x54, 0x08, 0x01, 0x00,
0x00, 0x20, 0x00, 0x14, // Address Type (XOR), length
0x00, 0x02, 0xcb, 0x5b, // family, XOR-ed port
0x05, 0x13, 0x5e, 0x42, // XOR-ed IPv6 address
0xe3, 0xad, 0x56, 0xe1,
0xc2, 0x30, 0x99, 0x9d,
0xaa, 0xed, 0x01, 0xc3
};
static const unsigned char kStunMessageWithIPv4XorMappedAddress[] = {
0x01, 0x01, 0x00, 0x0c, // message header (binding response)
0x21, 0x12, 0xa4, 0x42, // magic cookie
0x29, 0x1f, 0xcd, 0x7c, // transaction ID
0xba, 0x58, 0xab, 0xd7,
0xf2, 0x41, 0x01, 0x00,
0x00, 0x20, 0x00, 0x08, // address type (xor), length
0x00, 0x01, 0xfc, 0xb5, // family (AF_INET), XOR-ed port
0x8d, 0x05, 0xe0, 0xa4 // IPv4 address
};
// ByteString Attribute (username)
static const unsigned char kStunMessageWithByteStringAttribute[] = {
0x00, 0x01, 0x00, 0x0c,
0x21, 0x12, 0xa4, 0x42,
0xe3, 0xa9, 0x46, 0xe1,
0x7c, 0x00, 0xc2, 0x62,
0x54, 0x08, 0x01, 0x00,
0x00, 0x06, 0x00, 0x08, // username attribute (length 8)
0x61, 0x62, 0x63, 0x64, // abcdefgh
0x65, 0x66, 0x67, 0x68
};
// Message with an unknown but comprehensible optional attribute.
// Parsing should succeed despite this unknown attribute.
static const unsigned char kStunMessageWithUnknownAttribute[] = {
0x00, 0x01, 0x00, 0x14,
0x21, 0x12, 0xa4, 0x42,
0xe3, 0xa9, 0x46, 0xe1,
0x7c, 0x00, 0xc2, 0x62,
0x54, 0x08, 0x01, 0x00,
0x00, 0xaa, 0x00, 0x07, // Unknown attribute, length 7 (needs padding!)
0x61, 0x62, 0x63, 0x64, // abcdefg + padding
0x65, 0x66, 0x67, 0x00,
0x00, 0x0d, 0x00, 0x04, // Followed by a known attribute we can
0x00, 0x00, 0x00, 0x0b // check for.
};
// ByteString Attribute (username) with padding byte
static const unsigned char kStunMessageWithPaddedByteStringAttribute[] = {
0x00, 0x01, 0x00, 0x08,
0x21, 0x12, 0xa4, 0x42,
0xe3, 0xa9, 0x46, 0xe1,
0x7c, 0x00, 0xc2, 0x62,
0x54, 0x08, 0x01, 0x00,
0x00, 0x06, 0x00, 0x03, // username attribute (length 3)
0x61, 0x62, 0x63, 0xcc // abc
};
// Message with an Unknown Attributes (uint16 list) attribute.
static const unsigned char kStunMessageWithUInt16ListAttribute[] = {
0x00, 0x01, 0x00, 0x0c,
0x21, 0x12, 0xa4, 0x42,
0xe3, 0xa9, 0x46, 0xe1,
0x7c, 0x00, 0xc2, 0x62,
0x54, 0x08, 0x01, 0x00,
0x00, 0x0a, 0x00, 0x06, // username attribute (length 6)
0x00, 0x01, 0x10, 0x00, // three attributes plus padding
0xAB, 0xCU, 0xBE, 0xEF
};
// Error response message (unauthorized)
static const unsigned char kStunMessageWithErrorAttribute[] = {
0x01, 0x13, 0x00, 0x14,
0x21, 0x12, 0xa4, 0x42,
0x29, 0x1f, 0xcd, 0x7c,
0xba, 0x58, 0xab, 0xd7,
0xf2, 0x41, 0x01, 0x00,
0x00, 0x09, 0x00, 0x10,
0x00, 0x00, 0x04, 0x01,
0x55, 0x6e, 0x61, 0x75,
0x74, 0x68, 0x6f, 0x72,
0x69, 0x7a, 0x65, 0x64
};
// Message with an address attribute with an unknown address family,
// and a byte string attribute. Check that we quit reading after the
// bogus address family and don't read the username attribute.
static const unsigned char kStunMessageWithInvalidAddressFamily[] = {
0x01, 0x01, 0x00, 0x18, // binding response, length 24
0x21, 0x12, 0xa4, 0x42, // magic cookie
0x29, 0x1f, 0xcd, 0x7c, // transaction ID
0xba, 0x58, 0xab, 0xd7,
0xf2, 0x41, 0x01, 0x00,
0x00, 0x01, 0x00, 0x08, // Mapped address, 4 byte length
0x00, 0x09, 0xfe, 0xed, // Bogus address family (port unimportant).
0xac, 0x17, 0x44, 0xe6, // Should be skipped.
0x00, 0x06, 0x00, 0x08, // Username attribute (length 8)
0x61, 0x62, 0x63, 0x64, // abcdefgh
0x65, 0x66, 0x67, 0x68
};
// Message with an address attribute with an invalid address length.
// Should fail to be read.
static const unsigned char kStunMessageWithInvalidAddressLength[] = {
0x01, 0x01, 0x00, 0x18, // binding response, length 24
0x21, 0x12, 0xa4, 0x42, // magic cookie
0x29, 0x1f, 0xcd, 0x7c, // transaction ID
0xba, 0x58, 0xab, 0xd7,
0xf2, 0x41, 0x01, 0x00,
0x00, 0x01, 0x00, 0x0c, // Mapped address, 12 byte length
0x00, 0x01, 0xfe, 0xed, // Claims to be AF_INET.
0xac, 0x17, 0x44, 0xe6,
0x00, 0x06, 0x00, 0x08
};
// Sample messages with an invalid length Field
// The actual length in bytes of the invalid messages (including STUN header)
static const int kRealLengthOfInvalidLengthTestCases = 32;
static const unsigned char kStunMessageWithZeroLength[] = {
0x00, 0x01, 0x00, 0x00, // length of 0 (last 2 bytes)
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 const unsigned char kStunMessageWithExcessLength[] = {
0x00, 0x01, 0x00, 0x55, // length of 85
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 const unsigned char kStunMessageWithSmallLength[] = {
0x00, 0x01, 0x00, 0x03, // length of 3
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,
};
// Legacy STUN tests.
// Included for completeness, but it's not recommended to change these.
static const unsigned char kStunMessageWithManyAttributes[] = {
0x00, 0x01, 0x00, 136, // 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', 0 // DATA must be padded per rfc5766.
};
// 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 kRtcpPacket[] = {
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,
};
// A transaction ID without the 'magic cookie' portion
// pjnat's test programs use this transaction ID a lot.
const unsigned char kTestTransactionId1[] = { 0x029, 0x01f, 0x0cd, 0x07c,
0x0ba, 0x058, 0x0ab, 0x0d7,
0x0f2, 0x041, 0x001, 0x000 };
// They use this one sometimes too.
const unsigned char kTestTransactionId2[] = { 0x0e3, 0x0a9, 0x046, 0x0e1,
0x07c, 0x000, 0x0c2, 0x062,
0x054, 0x008, 0x001, 0x000 };
const in6_addr kIPv6TestAddress1 = { { { 0x24, 0x01, 0xfa, 0x00,
0x00, 0x04, 0x10, 0x00,
0xbe, 0x30, 0x5b, 0xff,
0xfe, 0xe5, 0x00, 0xc3 } } };
const in6_addr kIPv6TestAddress2 = { { { 0x24, 0x01, 0xfa, 0x00,
0x00, 0x04, 0x10, 0x12,
0x06, 0x0c, 0xce, 0xff,
0xfe, 0x1f, 0x61, 0xa4 } } };
// This is kIPv6TestAddress1 xor-ed with kTestTransactionID2.
const in6_addr kIPv6XoredTestAddress = { { { 0x05, 0x13, 0x5e, 0x42,
0xe3, 0xad, 0x56, 0xe1,
0xc2, 0x30, 0x99, 0x9d,
0xaa, 0xed, 0x01, 0xc3 } } };
#ifdef POSIX
const in_addr kIPv4TestAddress1 = { 0xe64417ac };
// This is kIPv4TestAddress xored with the STUN magic cookie.
const in_addr kIPv4XoredTestAddress = { 0x8d05e0a4 };
#elif defined WIN32
// Windows in_addr has a union with a uchar[] array first.
const in_addr kIPv4XoredTestAddress = { { 0x8d, 0x05, 0xe0, 0xa4 } };
const in_addr kIPv4TestAddress1 = { { 0x0ac, 0x017, 0x044, 0x0e6 } };
#endif
const char kTestUserName1[] = "abcdefgh";
const char kTestUserName2[] = "abc";
const char kTestErrorReason[] = "Unauthorized";
const int kTestErrorClass = 4;
const int kTestErrorNumber = 1;
const int kTestMessagePort1 = 59977;
const int kTestMessagePort2 = 47233;
const int kTestMessagePort3 = 56743;
const int kTestMessagePort4 = 40444;
#define ReadStunMessage(X, Y) ReadStunMessageTestCase(X, Y, sizeof(Y));
TEST_F(StunTest, ReadMessageWithIPv4AddressAttribute) {
StunMessage msg;
size_t size = ReadStunMessage(&msg, kStunMessageWithIPv4MappedAddress);
CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
const StunAddressAttribute* addr = msg.GetAddress(STUN_ATTR_MAPPED_ADDRESS);
talk_base::IPAddress test_address(kIPv4TestAddress1);
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV4,
kTestMessagePort4, test_address);
}
TEST_F(StunTest, ReadMessageWithIPv4XorAddressAttribute) {
StunMessage msg;
StunMessage msg2;
size_t size = ReadStunMessage(&msg, kStunMessageWithIPv4XorMappedAddress);
CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
const StunAddressAttribute* addr =
msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
talk_base::IPAddress test_address(kIPv4TestAddress1);
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV4,
kTestMessagePort3, test_address);
}
TEST_F(StunTest, ReadMessageWithIPv6AddressAttribute) {
StunMessage msg;
size_t size = ReadStunMessage(&msg, kStunMessageWithIPv6MappedAddress);
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
talk_base::IPAddress test_address(kIPv6TestAddress1);
const StunAddressAttribute* addr = msg.GetAddress(STUN_ATTR_MAPPED_ADDRESS);
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV6,
kTestMessagePort2, test_address);
}
TEST_F(StunTest, ReadMessageWithInvalidAddressAttribute) {
StunMessage msg;
size_t size = ReadStunMessage(&msg, kStunMessageWithIPv6MappedAddress);
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
talk_base::IPAddress test_address(kIPv6TestAddress1);
const StunAddressAttribute* addr = msg.GetAddress(STUN_ATTR_MAPPED_ADDRESS);
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV6,
kTestMessagePort2, test_address);
}
TEST_F(StunTest, ReadMessageWithIPv6XorAddressAttribute) {
StunMessage msg;
size_t size = ReadStunMessage(&msg, kStunMessageWithIPv6XorMappedAddress);
talk_base::IPAddress test_address(kIPv6TestAddress1);
CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
CheckStunTransactionID(msg, kTestTransactionId2, kStunTransactionIdLength);
const StunAddressAttribute* addr =
msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV6,
kTestMessagePort1, test_address);
}
TEST_F(StunTest, SetIPv6XorAddressAttributeOwner) {
StunMessage msg;
StunMessage msg2;
size_t size = ReadStunMessage(&msg, kStunMessageWithIPv6XorMappedAddress);
talk_base::IPAddress test_address(kIPv6TestAddress1);
CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
CheckStunTransactionID(msg, kTestTransactionId2, kStunTransactionIdLength);
const StunAddressAttribute* addr =
msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV6,
kTestMessagePort1, test_address);
// Owner with a different transaction ID.
msg2.SetTransactionID("ABCDABCDABCD");
StunXorAddressAttribute addr2(STUN_ATTR_XOR_MAPPED_ADDRESS, 20);
addr2.SetIP(addr->ipaddr());
addr2.SetPort(addr->port());
addr2.SetOwner(&msg2);
// The internal IP address shouldn't change.
ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());
talk_base::ByteBuffer correct_buf;
talk_base::ByteBuffer wrong_buf;
addr->Write(&correct_buf);
addr2.Write(&wrong_buf);
// But when written out, the buffers should look different.
ASSERT_NE(0, std::memcmp(correct_buf.Data(),
wrong_buf.Data(),
wrong_buf.Length()));
// And when reading a known good value, the address should be wrong
addr2.Read(&correct_buf);
ASSERT_NE(addr->ipaddr(), addr2.ipaddr());
addr2.SetIP(addr->ipaddr());
addr2.SetPort(addr->port());
// Try writing with no owner at all. Should write 4 bytes (1 byte reserved,
// 2 bytes port, 1 byte address family, 0 bytes address). This is an invalid
// but well-formed address attribute.
addr2.SetOwner(NULL);
ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());
wrong_buf.Shift(wrong_buf.Length());
addr2.Write(&wrong_buf);
ASSERT_EQ(4U, wrong_buf.Length());
ASSERT_NE(wrong_buf.Length(), correct_buf.Length());
ASSERT_NE(0, std::memcmp(correct_buf.Data(),
wrong_buf.Data(),
wrong_buf.Length()));
}
TEST_F(StunTest, SetIPv4XorAddressAttributeOwner) {
// Unlike the IPv6XorAddressAttributeOwner test, IPv4 XOR address attributes
// should _not_ be affected by a change in owner. IPv4 XOR address uses the
// magic cookie value which is fixed.
StunMessage msg;
StunMessage msg2;
size_t size = ReadStunMessage(&msg, kStunMessageWithIPv4XorMappedAddress);
talk_base::IPAddress test_address(kIPv4TestAddress1);
CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
const StunAddressAttribute* addr =
msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV4,
kTestMessagePort3, test_address);
// Owner with a different transaction ID.
msg2.SetTransactionID("ABCDABCDABCD");
StunXorAddressAttribute addr2(STUN_ATTR_XOR_MAPPED_ADDRESS, 20);
addr2.SetIP(addr->ipaddr());
addr2.SetPort(addr->port());
addr2.SetOwner(&msg2);
// The internal IP address shouldn't change.
ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());
talk_base::ByteBuffer correct_buf;
talk_base::ByteBuffer wrong_buf;
addr->Write(&correct_buf);
addr2.Write(&wrong_buf);
// The same address data should be written.
ASSERT_EQ(0, std::memcmp(correct_buf.Data(),
wrong_buf.Data(),
wrong_buf.Length()));
// And an attribute should be able to un-XOR an address belonging to a message
// with a different transaction ID.
addr2.Read(&correct_buf);
ASSERT_EQ(addr->ipaddr(), addr2.ipaddr());
// However, no owner is still an error. Write 4 bytes and a 0 length address.
addr2.SetOwner(NULL);
ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());
wrong_buf.Shift(wrong_buf.Length());
addr2.Write(&wrong_buf);
ASSERT_NE(wrong_buf.Length(), correct_buf.Length());
ASSERT_NE(0, std::memcmp(correct_buf.Data(),
wrong_buf.Data(),
wrong_buf.Length()));
}
TEST_F(StunTest, CreateIPv6AddressAttribute) {
talk_base::IPAddress test_ip(kIPv6TestAddress2);
StunAddressAttribute* addr =
StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
talk_base::SocketAddress test_addr(test_ip, kTestMessagePort2);
addr->SetAddress(test_addr);
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV6,
kTestMessagePort2, test_ip);
delete addr;
}
TEST_F(StunTest, CreateIPv4AddressAttribute) {
struct in_addr test_in_addr;
test_in_addr.s_addr = 0xBEB0B0BE;
talk_base::IPAddress test_ip(test_in_addr);
StunAddressAttribute* addr =
StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
talk_base::SocketAddress test_addr(test_ip, kTestMessagePort2);
addr->SetAddress(test_addr);
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV4,
kTestMessagePort2, test_ip);
delete addr;
}
TEST_F(StunTest, WriteMessageWithIPv6AddressAttribute) {
StunMessage msg;
size_t size = sizeof(kStunMessageWithIPv6MappedAddress);
talk_base::IPAddress test_ip(kIPv6TestAddress1);
msg.SetType(STUN_BINDING_REQUEST);
msg.SetTransactionID(
std::string(reinterpret_cast<const char*>(kTestTransactionId1),
kStunTransactionIdLength));
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
StunAddressAttribute* addr =
StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
talk_base::SocketAddress test_addr(test_ip, kTestMessagePort2);
addr->SetAddress(test_addr);
msg.AddAttribute(addr);
CheckStunHeader(msg, STUN_BINDING_REQUEST, (size - 20));
talk_base::ByteBuffer out;
msg.Write(&out);
ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv6MappedAddress));
int len1 = out.Length();
std::string bytes;
out.ReadString(&bytes, len1);
ASSERT_EQ(0, std::memcmp(bytes.c_str(),
kStunMessageWithIPv6MappedAddress,
len1));
}
TEST_F(StunTest, WriteMessageWithIPv4AddressAttribute) {
StunMessage msg;
size_t size = sizeof(kStunMessageWithIPv4MappedAddress);
talk_base::IPAddress test_ip(kIPv4TestAddress1);
msg.SetType(STUN_BINDING_RESPONSE);
msg.SetTransactionID(
std::string(reinterpret_cast<const char*>(kTestTransactionId1),
kStunTransactionIdLength));
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
StunAddressAttribute* addr =
StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
talk_base::SocketAddress test_addr(test_ip, kTestMessagePort4);
addr->SetAddress(test_addr);
msg.AddAttribute(addr);
CheckStunHeader(msg, STUN_BINDING_RESPONSE, (size - 20));
talk_base::ByteBuffer out;
msg.Write(&out);
ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv4MappedAddress));
int len1 = out.Length();
std::string bytes;
out.ReadString(&bytes, len1);
ASSERT_EQ(0, std::memcmp(bytes.c_str(),
kStunMessageWithIPv4MappedAddress,
len1));
}
TEST_F(StunTest, WriteMessageWithIPv6XorAddressAttribute) {
StunMessage msg;
size_t size = sizeof(kStunMessageWithIPv6XorMappedAddress);
talk_base::IPAddress test_ip(kIPv6TestAddress1);
msg.SetType(STUN_BINDING_RESPONSE);
msg.SetTransactionID(
std::string(reinterpret_cast<const char*>(kTestTransactionId2),
kStunTransactionIdLength));
CheckStunTransactionID(msg, kTestTransactionId2, kStunTransactionIdLength);
StunAddressAttribute* addr =
StunAttribute::CreateAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
talk_base::SocketAddress test_addr(test_ip, kTestMessagePort1);
addr->SetAddress(test_addr);
msg.AddAttribute(addr);
CheckStunHeader(msg, STUN_BINDING_RESPONSE, (size - 20));
talk_base::ByteBuffer out;
msg.Write(&out);
ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv6XorMappedAddress));
int len1 = out.Length();
std::string bytes;
out.ReadString(&bytes, len1);
ASSERT_EQ(0, std::memcmp(bytes.c_str(),
kStunMessageWithIPv6XorMappedAddress,
len1));
}
TEST_F(StunTest, WriteMessageWithIPv4XoreAddressAttribute) {
StunMessage msg;
size_t size = sizeof(kStunMessageWithIPv4XorMappedAddress);
talk_base::IPAddress test_ip(kIPv4TestAddress1);
msg.SetType(STUN_BINDING_RESPONSE);
msg.SetTransactionID(
std::string(reinterpret_cast<const char*>(kTestTransactionId1),
kStunTransactionIdLength));
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
StunAddressAttribute* addr =
StunAttribute::CreateAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
talk_base::SocketAddress test_addr(test_ip, kTestMessagePort3);
addr->SetAddress(test_addr);
msg.AddAttribute(addr);
CheckStunHeader(msg, STUN_BINDING_RESPONSE, (size - 20));
talk_base::ByteBuffer out;
msg.Write(&out);
ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv4XorMappedAddress));
int len1 = out.Length();
std::string bytes;
out.ReadString(&bytes, len1);
ASSERT_EQ(0, std::memcmp(bytes.c_str(),
kStunMessageWithIPv4XorMappedAddress,
len1));
}
TEST_F(StunTest, ReadByteStringAttribute) {
StunMessage msg;
size_t size = ReadStunMessage(&msg, kStunMessageWithByteStringAttribute);
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
CheckStunTransactionID(msg, kTestTransactionId2, kStunTransactionIdLength);
const StunByteStringAttribute* username =
msg.GetByteString(STUN_ATTR_USERNAME);
ASSERT_EQ(0, std::memcmp(kTestUserName1, username->bytes(),
username->length()));
}
TEST_F(StunTest, ReadPaddedByteStringAttribute) {
StunMessage msg;
size_t size = ReadStunMessage(&msg,
kStunMessageWithPaddedByteStringAttribute);
ASSERT_NE(0U, size);
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
CheckStunTransactionID(msg, kTestTransactionId2, kStunTransactionIdLength);
const StunByteStringAttribute* username =
msg.GetByteString(STUN_ATTR_USERNAME);
ASSERT_EQ(0, std::memcmp(kTestUserName2, username->bytes(),
username->length()));
}
TEST_F(StunTest, ReadErrorCodeAttribute) {
StunMessage msg;
size_t size = ReadStunMessage(&msg, kStunMessageWithErrorAttribute);
CheckStunHeader(msg, STUN_ALLOCATE_ERROR_RESPONSE, size);
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
const StunErrorCodeAttribute* errorcode = msg.GetErrorCode();
ASSERT_EQ(kTestErrorClass, errorcode->error_class());
ASSERT_EQ(kTestErrorNumber, errorcode->number());
std::string reason = errorcode->reason();
ASSERT_EQ(0, strcmp(reason.c_str(), kTestErrorReason));
}
TEST_F(StunTest, ReadMessageWithAnUnknownAttribute) {
StunMessage msg;
size_t size = ReadStunMessage(&msg, kStunMessageWithUnknownAttribute);
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
// Parsing should have succeeded and there should be a lifetime attribute
const StunUInt32Attribute* uval = msg.GetUInt32(STUN_ATTR_LIFETIME);
EXPECT_TRUE(uval != NULL);
if (uval != NULL) {
EXPECT_EQ(11U, uval->value());
}
}
TEST_F(StunTest, ReadMessageWithAUInt16ListAttribute) {
StunMessage msg;
size_t size = ReadStunMessage(&msg, kStunMessageWithUInt16ListAttribute);
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
const StunUInt16ListAttribute* types = msg.GetUnknownAttributes();
EXPECT_EQ(3U, types->Size());
EXPECT_EQ(0x1U, types->GetType(0));
EXPECT_EQ(0x1000U, types->GetType(1));
EXPECT_EQ(0xAB0CU, types->GetType(2));
}
TEST_F(StunTest, WriteMessageWithAUInt16ListAttribute) {
StunMessage msg;
size_t size = sizeof(kStunMessageWithUInt16ListAttribute);
msg.SetType(STUN_BINDING_REQUEST);
msg.SetTransactionID(
std::string(reinterpret_cast<const char*>(kTestTransactionId2),
kStunTransactionIdLength));
CheckStunTransactionID(msg, kTestTransactionId2, kStunTransactionIdLength);
StunUInt16ListAttribute* list = StunAttribute::CreateUnknownAttributes();
list->AddType(0x1U);
list->AddType(0x1000U);
list->AddType(0xAB0CU);
msg.AddAttribute(list);
CheckStunHeader(msg, STUN_BINDING_REQUEST, (size - 20));
talk_base::ByteBuffer out;
msg.Write(&out);
ASSERT_EQ(size, out.Length());
// Check everything up to the padding.
ASSERT_EQ(0, std::memcmp(out.Data(), kStunMessageWithUInt16ListAttribute,
size - 2));
}
void CheckFailureToRead(const unsigned char* testcase, size_t length) {
StunMessage msg;
const char* input = reinterpret_cast<const char*>(testcase);
talk_base::ByteBuffer buf(input, length);
ASSERT_FALSE(msg.Read(&buf));
}
TEST_F(StunTest, FailToReadInvalidMessages) {
CheckFailureToRead(kStunMessageWithZeroLength,
kRealLengthOfInvalidLengthTestCases);
CheckFailureToRead(kStunMessageWithSmallLength,
kRealLengthOfInvalidLengthTestCases);
CheckFailureToRead(kStunMessageWithExcessLength,
kRealLengthOfInvalidLengthTestCases);
}
#undef ReadStunMessage
// Test that we don't care what order we set the parts of an address
TEST_F(StunTest, CreateAddressInArbitraryOrder) {
StunAddressAttribute* addr =
StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
// Port first
addr->SetPort(kTestMessagePort1);
addr->SetIP(talk_base::IPAddress(kIPv4TestAddress1));
ASSERT_EQ(kTestMessagePort1, addr->port());
ASSERT_EQ(talk_base::IPAddress(kIPv4TestAddress1), addr->ipaddr());
StunAddressAttribute* addr2 =
StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
// IP first
addr2->SetIP(talk_base::IPAddress(kIPv4TestAddress1));
addr2->SetPort(kTestMessagePort2);
ASSERT_EQ(kTestMessagePort2, addr2->port());
ASSERT_EQ(talk_base::IPAddress(kIPv4TestAddress1), addr2->ipaddr());
delete addr;
delete addr2;
}
// Legacy test bodies
static void DoTest(const char* input, size_t size, const char* transaction_id) {
StunMessage msg, msg2;
in_addr legacy_in_addr;
legacy_in_addr.s_addr = htonl(17U);
talk_base::IPAddress legacy_ip(legacy_in_addr);
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);
ASSERT_TRUE(addr != NULL);
EXPECT_EQ(1, addr->family());
EXPECT_EQ(13, addr->port());
EXPECT_EQ(legacy_ip, addr->ipaddr());
StunAddressAttribute* addr2 =
StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
addr2->SetPort(13);
addr2->SetIP(legacy_ip);
msg2.AddAttribute(addr2);
const StunByteStringAttribute* bytes = msg.GetByteString(STUN_ATTR_USERNAME);
ASSERT_TRUE(bytes != NULL);
EXPECT_EQ(12, bytes->length());
EXPECT_EQ(0, std::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);
ASSERT_TRUE(bytes != NULL);
EXPECT_EQ(20, bytes->length());
EXPECT_EQ(0, std::memcmp(bytes->bytes(),
"abcdefghijklmnopqrst",
bytes->length()));
bytes2 = StunAttribute::CreateByteString(STUN_ATTR_MESSAGE_INTEGRITY);
bytes2->CopyBytes("abcdefghijklmnopqrst");
msg2.AddAttribute(bytes2);
const StunErrorCodeAttribute* ecode = msg.GetErrorCode();
ASSERT_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();
ASSERT_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);
ASSERT_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);
ASSERT_TRUE(bytes != NULL);
EXPECT_EQ(4, bytes->length());
EXPECT_EQ(0, std::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);
ASSERT_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);
ASSERT_TRUE(addr != NULL);
EXPECT_EQ(1, addr->family());
EXPECT_EQ(13, addr->port());
EXPECT_EQ(legacy_ip, addr->ipaddr());
addr2 = StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
addr2->SetPort(13);
addr2->SetIP(legacy_ip);
msg2.AddAttribute(addr2);
addr = msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2);
ASSERT_TRUE(addr != NULL);
EXPECT_EQ(1, addr->family());
EXPECT_EQ(13, addr->port());
EXPECT_EQ(legacy_ip, addr->ipaddr());
addr2 = StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2);
addr2->SetPort(13);
addr2->SetIP(legacy_ip);
msg2.AddAttribute(addr2);
bytes = msg.GetByteString(STUN_ATTR_DATA);
ASSERT_TRUE(bytes != NULL);
EXPECT_EQ(7, bytes->length());
EXPECT_EQ(0, std::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());
size_t len1 = out.Length();
std::string outstring;
out.ReadString(&outstring, len1);
EXPECT_EQ(0, std::memcmp(outstring.c_str(), input, len1));
talk_base::ByteBuffer out2;
msg2.Write(&out2);
EXPECT_EQ(size, out2.Length());
size_t len2 = out2.Length();
std::string outstring2;
out2.ReadString(&outstring2, len2);
EXPECT_EQ(0, std::memcmp(outstring2.c_str(), input, len2));
}
TEST_F(StunTest, TestStunPacket) {
DoTest(reinterpret_cast<const char*>(kStunMessageWithManyAttributes),
sizeof(kStunMessageWithManyAttributes),
"0123456789ab");
}
TEST_F(StunTest, TestRejectsRtcpPacket) {
StunMessage msg;
talk_base::ByteBuffer buf(
reinterpret_cast<const char*>(kRtcpPacket), sizeof(kRtcpPacket));
EXPECT_FALSE(msg.Read(&buf));
}
TEST_F(StunTest, TestLegacyPacket) {
// The RFC3489 packet in this test is the same as
// kStunMessageWithManyAttributes, but with a different value where the
// magic cookie was.
talk_base::scoped_array<char>
rfc3489_packet(new char[sizeof(kStunMessageWithManyAttributes)]);
memcpy(rfc3489_packet.get(), kStunMessageWithManyAttributes,
sizeof(kStunMessageWithManyAttributes));
// Overwrites the magic cookie here.
memcpy(&rfc3489_packet[4], &kStunMessageWithManyAttributes[8], 4);
DoTest(reinterpret_cast<const char*>(rfc3489_packet.get()),
sizeof(kStunMessageWithManyAttributes), "01230123456789ab");
}
} // namespace cricket