Proper error handling was missing, using VERIFY to crash in debug builds, while release builds would ignore the error and leak the attribute memory. The check of attribute type consistency was changed to a RTC_DCHECK. Also removes a large number of uses of the deprecated VERIFY macro. BUG=webrtc:6424 Review-Url: https://codereview.webrtc.org/2665343002 Cr-Commit-Position: refs/heads/master@{#16413}
1488 lines
56 KiB
C++
1488 lines
56 KiB
C++
/*
|
|
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include <string>
|
|
|
|
#include "webrtc/p2p/base/stun.h"
|
|
#include "webrtc/base/arraysize.h"
|
|
#include "webrtc/base/bytebuffer.h"
|
|
#include "webrtc/base/gunit.h"
|
|
#include "webrtc/base/logging.h"
|
|
#include "webrtc/base/messagedigest.h"
|
|
#include "webrtc/base/socketaddress.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(length, msg.transaction_id().size());
|
|
ASSERT_EQ(length == kStunTransactionIdLength + 4, msg.IsLegacy());
|
|
ASSERT_EQ(length == kStunTransactionIdLength, !msg.IsLegacy());
|
|
ASSERT_EQ(0, memcmp(msg.transaction_id().c_str(), expectedID, length));
|
|
}
|
|
|
|
void CheckStunAddressAttribute(const StunAddressAttribute* addr,
|
|
StunAddressFamily expected_family,
|
|
int expected_port,
|
|
rtc::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, 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, 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);
|
|
rtc::ByteBufferReader 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, 0x06, 0x00, 0x03, // Followed by a known attribute we can
|
|
0x61, 0x62, 0x63, 0x00 // check for (username of length 3)
|
|
};
|
|
|
|
// 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_t 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, 0x11, 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
|
|
};
|
|
|
|
static const unsigned char kStunMessageWithOriginAttribute[] = {
|
|
0x00, 0x01, 0x00, 0x18, // message header (binding request), length 24
|
|
0x21, 0x12, 0xA4, 0x42, // magic cookie
|
|
0x29, 0x1f, 0xcd, 0x7c, // transaction id
|
|
0xba, 0x58, 0xab, 0xd7,
|
|
0xf2, 0x41, 0x01, 0x00,
|
|
0x80, 0x2f, 0x00, 0x12, // origin attribute (length 18)
|
|
0x68, 0x74, 0x74, 0x70, // http://example.com
|
|
0x3A, 0x2F, 0x2F, 0x65,
|
|
0x78, 0x61, 0x6d, 0x70,
|
|
0x6c, 0x65, 0x2e, 0x63,
|
|
0x6f, 0x6d, 0x00, 0x00,
|
|
};
|
|
|
|
// 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,
|
|
};
|
|
|
|
static const unsigned char kStunMessageWithBadHmacAtEnd[] = {
|
|
0x00, 0x01, 0x00, 0x14, // message length exactly 20
|
|
0x21, 0x12, 0xA4, 0x42, // magic cookie
|
|
'0', '1', '2', '3', // transaction ID
|
|
'4', '5', '6', '7',
|
|
'8', '9', 'a', 'b',
|
|
0x00, 0x08, 0x00, 0x14, // type=STUN_ATTR_MESSAGE_INTEGRITY, length=20
|
|
'0', '0', '0', '0', // We lied, there are only 16 bytes of HMAC.
|
|
'0', '0', '0', '0',
|
|
'0', '0', '0', '0',
|
|
'0', '0', '0', '0',
|
|
};
|
|
|
|
// 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,
|
|
};
|
|
|
|
// RFC5769 Test Vectors
|
|
// Software name (request): "STUN test client" (without quotes)
|
|
// Software name (response): "test vector" (without quotes)
|
|
// Username: "evtj:h6vY" (without quotes)
|
|
// Password: "VOkJxbRl1RmTxUk/WvJxBt" (without quotes)
|
|
static const unsigned char kRfc5769SampleMsgTransactionId[] = {
|
|
0xb7, 0xe7, 0xa7, 0x01, 0xbc, 0x34, 0xd6, 0x86, 0xfa, 0x87, 0xdf, 0xae
|
|
};
|
|
static const char kRfc5769SampleMsgClientSoftware[] = "STUN test client";
|
|
static const char kRfc5769SampleMsgServerSoftware[] = "test vector";
|
|
static const char kRfc5769SampleMsgUsername[] = "evtj:h6vY";
|
|
static const char kRfc5769SampleMsgPassword[] = "VOkJxbRl1RmTxUk/WvJxBt";
|
|
static const rtc::SocketAddress kRfc5769SampleMsgMappedAddress(
|
|
"192.0.2.1", 32853);
|
|
static const rtc::SocketAddress kRfc5769SampleMsgIPv6MappedAddress(
|
|
"2001:db8:1234:5678:11:2233:4455:6677", 32853);
|
|
|
|
static const unsigned char kRfc5769SampleMsgWithAuthTransactionId[] = {
|
|
0x78, 0xad, 0x34, 0x33, 0xc6, 0xad, 0x72, 0xc0, 0x29, 0xda, 0x41, 0x2e
|
|
};
|
|
static const char kRfc5769SampleMsgWithAuthUsername[] =
|
|
"\xe3\x83\x9e\xe3\x83\x88\xe3\x83\xaa\xe3\x83\x83\xe3\x82\xaf\xe3\x82\xb9";
|
|
static const char kRfc5769SampleMsgWithAuthPassword[] = "TheMatrIX";
|
|
static const char kRfc5769SampleMsgWithAuthNonce[] =
|
|
"f//499k954d6OL34oL9FSTvy64sA";
|
|
static const char kRfc5769SampleMsgWithAuthRealm[] = "example.org";
|
|
|
|
// 2.1. Sample Request
|
|
static const unsigned char kRfc5769SampleRequest[] = {
|
|
0x00, 0x01, 0x00, 0x58, // Request type and message length
|
|
0x21, 0x12, 0xa4, 0x42, // Magic cookie
|
|
0xb7, 0xe7, 0xa7, 0x01, // }
|
|
0xbc, 0x34, 0xd6, 0x86, // } Transaction ID
|
|
0xfa, 0x87, 0xdf, 0xae, // }
|
|
0x80, 0x22, 0x00, 0x10, // SOFTWARE attribute header
|
|
0x53, 0x54, 0x55, 0x4e, // }
|
|
0x20, 0x74, 0x65, 0x73, // } User-agent...
|
|
0x74, 0x20, 0x63, 0x6c, // } ...name
|
|
0x69, 0x65, 0x6e, 0x74, // }
|
|
0x00, 0x24, 0x00, 0x04, // PRIORITY attribute header
|
|
0x6e, 0x00, 0x01, 0xff, // ICE priority value
|
|
0x80, 0x29, 0x00, 0x08, // ICE-CONTROLLED attribute header
|
|
0x93, 0x2f, 0xf9, 0xb1, // } Pseudo-random tie breaker...
|
|
0x51, 0x26, 0x3b, 0x36, // } ...for ICE control
|
|
0x00, 0x06, 0x00, 0x09, // USERNAME attribute header
|
|
0x65, 0x76, 0x74, 0x6a, // }
|
|
0x3a, 0x68, 0x36, 0x76, // } Username (9 bytes) and padding (3 bytes)
|
|
0x59, 0x20, 0x20, 0x20, // }
|
|
0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY attribute header
|
|
0x9a, 0xea, 0xa7, 0x0c, // }
|
|
0xbf, 0xd8, 0xcb, 0x56, // }
|
|
0x78, 0x1e, 0xf2, 0xb5, // } HMAC-SHA1 fingerprint
|
|
0xb2, 0xd3, 0xf2, 0x49, // }
|
|
0xc1, 0xb5, 0x71, 0xa2, // }
|
|
0x80, 0x28, 0x00, 0x04, // FINGERPRINT attribute header
|
|
0xe5, 0x7a, 0x3b, 0xcf // CRC32 fingerprint
|
|
};
|
|
|
|
// 2.2. Sample IPv4 Response
|
|
static const unsigned char kRfc5769SampleResponse[] = {
|
|
0x01, 0x01, 0x00, 0x3c, // Response type and message length
|
|
0x21, 0x12, 0xa4, 0x42, // Magic cookie
|
|
0xb7, 0xe7, 0xa7, 0x01, // }
|
|
0xbc, 0x34, 0xd6, 0x86, // } Transaction ID
|
|
0xfa, 0x87, 0xdf, 0xae, // }
|
|
0x80, 0x22, 0x00, 0x0b, // SOFTWARE attribute header
|
|
0x74, 0x65, 0x73, 0x74, // }
|
|
0x20, 0x76, 0x65, 0x63, // } UTF-8 server name
|
|
0x74, 0x6f, 0x72, 0x20, // }
|
|
0x00, 0x20, 0x00, 0x08, // XOR-MAPPED-ADDRESS attribute header
|
|
0x00, 0x01, 0xa1, 0x47, // Address family (IPv4) and xor'd mapped port
|
|
0xe1, 0x12, 0xa6, 0x43, // Xor'd mapped IPv4 address
|
|
0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY attribute header
|
|
0x2b, 0x91, 0xf5, 0x99, // }
|
|
0xfd, 0x9e, 0x90, 0xc3, // }
|
|
0x8c, 0x74, 0x89, 0xf9, // } HMAC-SHA1 fingerprint
|
|
0x2a, 0xf9, 0xba, 0x53, // }
|
|
0xf0, 0x6b, 0xe7, 0xd7, // }
|
|
0x80, 0x28, 0x00, 0x04, // FINGERPRINT attribute header
|
|
0xc0, 0x7d, 0x4c, 0x96 // CRC32 fingerprint
|
|
};
|
|
|
|
// 2.3. Sample IPv6 Response
|
|
static const unsigned char kRfc5769SampleResponseIPv6[] = {
|
|
0x01, 0x01, 0x00, 0x48, // Response type and message length
|
|
0x21, 0x12, 0xa4, 0x42, // Magic cookie
|
|
0xb7, 0xe7, 0xa7, 0x01, // }
|
|
0xbc, 0x34, 0xd6, 0x86, // } Transaction ID
|
|
0xfa, 0x87, 0xdf, 0xae, // }
|
|
0x80, 0x22, 0x00, 0x0b, // SOFTWARE attribute header
|
|
0x74, 0x65, 0x73, 0x74, // }
|
|
0x20, 0x76, 0x65, 0x63, // } UTF-8 server name
|
|
0x74, 0x6f, 0x72, 0x20, // }
|
|
0x00, 0x20, 0x00, 0x14, // XOR-MAPPED-ADDRESS attribute header
|
|
0x00, 0x02, 0xa1, 0x47, // Address family (IPv6) and xor'd mapped port.
|
|
0x01, 0x13, 0xa9, 0xfa, // }
|
|
0xa5, 0xd3, 0xf1, 0x79, // } Xor'd mapped IPv6 address
|
|
0xbc, 0x25, 0xf4, 0xb5, // }
|
|
0xbe, 0xd2, 0xb9, 0xd9, // }
|
|
0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY attribute header
|
|
0xa3, 0x82, 0x95, 0x4e, // }
|
|
0x4b, 0xe6, 0x7b, 0xf1, // }
|
|
0x17, 0x84, 0xc9, 0x7c, // } HMAC-SHA1 fingerprint
|
|
0x82, 0x92, 0xc2, 0x75, // }
|
|
0xbf, 0xe3, 0xed, 0x41, // }
|
|
0x80, 0x28, 0x00, 0x04, // FINGERPRINT attribute header
|
|
0xc8, 0xfb, 0x0b, 0x4c // CRC32 fingerprint
|
|
};
|
|
|
|
// 2.4. Sample Request with Long-Term Authentication
|
|
static const unsigned char kRfc5769SampleRequestLongTermAuth[] = {
|
|
0x00, 0x01, 0x00, 0x60, // Request type and message length
|
|
0x21, 0x12, 0xa4, 0x42, // Magic cookie
|
|
0x78, 0xad, 0x34, 0x33, // }
|
|
0xc6, 0xad, 0x72, 0xc0, // } Transaction ID
|
|
0x29, 0xda, 0x41, 0x2e, // }
|
|
0x00, 0x06, 0x00, 0x12, // USERNAME attribute header
|
|
0xe3, 0x83, 0x9e, 0xe3, // }
|
|
0x83, 0x88, 0xe3, 0x83, // }
|
|
0xaa, 0xe3, 0x83, 0x83, // } Username value (18 bytes) and padding (2 bytes)
|
|
0xe3, 0x82, 0xaf, 0xe3, // }
|
|
0x82, 0xb9, 0x00, 0x00, // }
|
|
0x00, 0x15, 0x00, 0x1c, // NONCE attribute header
|
|
0x66, 0x2f, 0x2f, 0x34, // }
|
|
0x39, 0x39, 0x6b, 0x39, // }
|
|
0x35, 0x34, 0x64, 0x36, // }
|
|
0x4f, 0x4c, 0x33, 0x34, // } Nonce value
|
|
0x6f, 0x4c, 0x39, 0x46, // }
|
|
0x53, 0x54, 0x76, 0x79, // }
|
|
0x36, 0x34, 0x73, 0x41, // }
|
|
0x00, 0x14, 0x00, 0x0b, // REALM attribute header
|
|
0x65, 0x78, 0x61, 0x6d, // }
|
|
0x70, 0x6c, 0x65, 0x2e, // } Realm value (11 bytes) and padding (1 byte)
|
|
0x6f, 0x72, 0x67, 0x00, // }
|
|
0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY attribute header
|
|
0xf6, 0x70, 0x24, 0x65, // }
|
|
0x6d, 0xd6, 0x4a, 0x3e, // }
|
|
0x02, 0xb8, 0xe0, 0x71, // } HMAC-SHA1 fingerprint
|
|
0x2e, 0x85, 0xc9, 0xa2, // }
|
|
0x8c, 0xa8, 0x96, 0x66 // }
|
|
};
|
|
|
|
// Length parameter is changed to 0x38 from 0x58.
|
|
// AddMessageIntegrity will add MI information and update the length param
|
|
// accordingly.
|
|
static const unsigned char kRfc5769SampleRequestWithoutMI[] = {
|
|
0x00, 0x01, 0x00, 0x38, // Request type and message length
|
|
0x21, 0x12, 0xa4, 0x42, // Magic cookie
|
|
0xb7, 0xe7, 0xa7, 0x01, // }
|
|
0xbc, 0x34, 0xd6, 0x86, // } Transaction ID
|
|
0xfa, 0x87, 0xdf, 0xae, // }
|
|
0x80, 0x22, 0x00, 0x10, // SOFTWARE attribute header
|
|
0x53, 0x54, 0x55, 0x4e, // }
|
|
0x20, 0x74, 0x65, 0x73, // } User-agent...
|
|
0x74, 0x20, 0x63, 0x6c, // } ...name
|
|
0x69, 0x65, 0x6e, 0x74, // }
|
|
0x00, 0x24, 0x00, 0x04, // PRIORITY attribute header
|
|
0x6e, 0x00, 0x01, 0xff, // ICE priority value
|
|
0x80, 0x29, 0x00, 0x08, // ICE-CONTROLLED attribute header
|
|
0x93, 0x2f, 0xf9, 0xb1, // } Pseudo-random tie breaker...
|
|
0x51, 0x26, 0x3b, 0x36, // } ...for ICE control
|
|
0x00, 0x06, 0x00, 0x09, // USERNAME attribute header
|
|
0x65, 0x76, 0x74, 0x6a, // }
|
|
0x3a, 0x68, 0x36, 0x76, // } Username (9 bytes) and padding (3 bytes)
|
|
0x59, 0x20, 0x20, 0x20 // }
|
|
};
|
|
|
|
// This HMAC differs from the RFC 5769 SampleRequest message. This differs
|
|
// because spec uses 0x20 for the padding where as our implementation uses 0.
|
|
static const unsigned char kCalculatedHmac1[] = {
|
|
0x79, 0x07, 0xc2, 0xd2, // }
|
|
0xed, 0xbf, 0xea, 0x48, // }
|
|
0x0e, 0x4c, 0x76, 0xd8, // } HMAC-SHA1 fingerprint
|
|
0x29, 0x62, 0xd5, 0xc3, // }
|
|
0x74, 0x2a, 0xf9, 0xe3 // }
|
|
};
|
|
|
|
// Length parameter is changed to 0x1c from 0x3c.
|
|
// AddMessageIntegrity will add MI information and update the length param
|
|
// accordingly.
|
|
static const unsigned char kRfc5769SampleResponseWithoutMI[] = {
|
|
0x01, 0x01, 0x00, 0x1c, // Response type and message length
|
|
0x21, 0x12, 0xa4, 0x42, // Magic cookie
|
|
0xb7, 0xe7, 0xa7, 0x01, // }
|
|
0xbc, 0x34, 0xd6, 0x86, // } Transaction ID
|
|
0xfa, 0x87, 0xdf, 0xae, // }
|
|
0x80, 0x22, 0x00, 0x0b, // SOFTWARE attribute header
|
|
0x74, 0x65, 0x73, 0x74, // }
|
|
0x20, 0x76, 0x65, 0x63, // } UTF-8 server name
|
|
0x74, 0x6f, 0x72, 0x20, // }
|
|
0x00, 0x20, 0x00, 0x08, // XOR-MAPPED-ADDRESS attribute header
|
|
0x00, 0x01, 0xa1, 0x47, // Address family (IPv4) and xor'd mapped port
|
|
0xe1, 0x12, 0xa6, 0x43 // Xor'd mapped IPv4 address
|
|
};
|
|
|
|
// This HMAC differs from the RFC 5769 SampleResponse message. This differs
|
|
// because spec uses 0x20 for the padding where as our implementation uses 0.
|
|
static const unsigned char kCalculatedHmac2[] = {
|
|
0x5d, 0x6b, 0x58, 0xbe, // }
|
|
0xad, 0x94, 0xe0, 0x7e, // }
|
|
0xef, 0x0d, 0xfc, 0x12, // } HMAC-SHA1 fingerprint
|
|
0x82, 0xa2, 0xbd, 0x08, // }
|
|
0x43, 0x14, 0x10, 0x28 // }
|
|
};
|
|
|
|
// 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 } } };
|
|
|
|
#ifdef WEBRTC_POSIX
|
|
const in_addr kIPv4TestAddress1 = { 0xe64417ac };
|
|
#elif defined WEBRTC_WIN
|
|
// Windows in_addr has a union with a uchar[] array first.
|
|
const in_addr kIPv4TestAddress1 = { { { 0x0ac, 0x017, 0x044, 0x0e6 } } };
|
|
#endif
|
|
const char kTestUserName1[] = "abcdefgh";
|
|
const char kTestUserName2[] = "abc";
|
|
const char kTestErrorReason[] = "Unauthorized";
|
|
const char kTestOrigin[] = "http://example.com";
|
|
const int kTestErrorClass = 4;
|
|
const int kTestErrorNumber = 1;
|
|
const int kTestErrorCode = 401;
|
|
|
|
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 that the GetStun*Type and IsStun*Type methods work as expected.
|
|
TEST_F(StunTest, MessageTypes) {
|
|
EXPECT_EQ(STUN_BINDING_RESPONSE,
|
|
GetStunSuccessResponseType(STUN_BINDING_REQUEST));
|
|
EXPECT_EQ(STUN_BINDING_ERROR_RESPONSE,
|
|
GetStunErrorResponseType(STUN_BINDING_REQUEST));
|
|
EXPECT_EQ(-1, GetStunSuccessResponseType(STUN_BINDING_INDICATION));
|
|
EXPECT_EQ(-1, GetStunSuccessResponseType(STUN_BINDING_RESPONSE));
|
|
EXPECT_EQ(-1, GetStunSuccessResponseType(STUN_BINDING_ERROR_RESPONSE));
|
|
EXPECT_EQ(-1, GetStunErrorResponseType(STUN_BINDING_INDICATION));
|
|
EXPECT_EQ(-1, GetStunErrorResponseType(STUN_BINDING_RESPONSE));
|
|
EXPECT_EQ(-1, GetStunErrorResponseType(STUN_BINDING_ERROR_RESPONSE));
|
|
|
|
int types[] = {
|
|
STUN_BINDING_REQUEST, STUN_BINDING_INDICATION,
|
|
STUN_BINDING_RESPONSE, STUN_BINDING_ERROR_RESPONSE
|
|
};
|
|
for (size_t i = 0; i < arraysize(types); ++i) {
|
|
EXPECT_EQ(i == 0U, IsStunRequestType(types[i]));
|
|
EXPECT_EQ(i == 1U, IsStunIndicationType(types[i]));
|
|
EXPECT_EQ(i == 2U, IsStunSuccessResponseType(types[i]));
|
|
EXPECT_EQ(i == 3U, IsStunErrorResponseType(types[i]));
|
|
EXPECT_EQ(1, types[i] & 0xFEEF);
|
|
}
|
|
}
|
|
|
|
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);
|
|
rtc::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);
|
|
rtc::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);
|
|
|
|
rtc::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);
|
|
|
|
rtc::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);
|
|
|
|
rtc::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);
|
|
}
|
|
|
|
// Read the RFC5389 fields from the RFC5769 sample STUN request.
|
|
TEST_F(StunTest, ReadRfc5769RequestMessage) {
|
|
StunMessage msg;
|
|
size_t size = ReadStunMessage(&msg, kRfc5769SampleRequest);
|
|
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
|
|
CheckStunTransactionID(msg, kRfc5769SampleMsgTransactionId,
|
|
kStunTransactionIdLength);
|
|
|
|
const StunByteStringAttribute* software =
|
|
msg.GetByteString(STUN_ATTR_SOFTWARE);
|
|
ASSERT_TRUE(software != NULL);
|
|
EXPECT_EQ(kRfc5769SampleMsgClientSoftware, software->GetString());
|
|
|
|
const StunByteStringAttribute* username =
|
|
msg.GetByteString(STUN_ATTR_USERNAME);
|
|
ASSERT_TRUE(username != NULL);
|
|
EXPECT_EQ(kRfc5769SampleMsgUsername, username->GetString());
|
|
|
|
// Actual M-I value checked in a later test.
|
|
ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
|
|
|
|
// Fingerprint checked in a later test, but double-check the value here.
|
|
const StunUInt32Attribute* fingerprint =
|
|
msg.GetUInt32(STUN_ATTR_FINGERPRINT);
|
|
ASSERT_TRUE(fingerprint != NULL);
|
|
EXPECT_EQ(0xe57a3bcf, fingerprint->value());
|
|
}
|
|
|
|
// Read the RFC5389 fields from the RFC5769 sample STUN response.
|
|
TEST_F(StunTest, ReadRfc5769ResponseMessage) {
|
|
StunMessage msg;
|
|
size_t size = ReadStunMessage(&msg, kRfc5769SampleResponse);
|
|
CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
|
|
CheckStunTransactionID(msg, kRfc5769SampleMsgTransactionId,
|
|
kStunTransactionIdLength);
|
|
|
|
const StunByteStringAttribute* software =
|
|
msg.GetByteString(STUN_ATTR_SOFTWARE);
|
|
ASSERT_TRUE(software != NULL);
|
|
EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->GetString());
|
|
|
|
const StunAddressAttribute* mapped_address =
|
|
msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
|
|
ASSERT_TRUE(mapped_address != NULL);
|
|
EXPECT_EQ(kRfc5769SampleMsgMappedAddress, mapped_address->GetAddress());
|
|
|
|
// Actual M-I and fingerprint checked in later tests.
|
|
ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
|
|
ASSERT_TRUE(msg.GetUInt32(STUN_ATTR_FINGERPRINT) != NULL);
|
|
}
|
|
|
|
// Read the RFC5389 fields from the RFC5769 sample STUN response for IPv6.
|
|
TEST_F(StunTest, ReadRfc5769ResponseMessageIPv6) {
|
|
StunMessage msg;
|
|
size_t size = ReadStunMessage(&msg, kRfc5769SampleResponseIPv6);
|
|
CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
|
|
CheckStunTransactionID(msg, kRfc5769SampleMsgTransactionId,
|
|
kStunTransactionIdLength);
|
|
|
|
const StunByteStringAttribute* software =
|
|
msg.GetByteString(STUN_ATTR_SOFTWARE);
|
|
ASSERT_TRUE(software != NULL);
|
|
EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->GetString());
|
|
|
|
const StunAddressAttribute* mapped_address =
|
|
msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
|
|
ASSERT_TRUE(mapped_address != NULL);
|
|
EXPECT_EQ(kRfc5769SampleMsgIPv6MappedAddress, mapped_address->GetAddress());
|
|
|
|
// Actual M-I and fingerprint checked in later tests.
|
|
ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
|
|
ASSERT_TRUE(msg.GetUInt32(STUN_ATTR_FINGERPRINT) != NULL);
|
|
}
|
|
|
|
// Read the RFC5389 fields from the RFC5769 sample STUN response with auth.
|
|
TEST_F(StunTest, ReadRfc5769RequestMessageLongTermAuth) {
|
|
StunMessage msg;
|
|
size_t size = ReadStunMessage(&msg, kRfc5769SampleRequestLongTermAuth);
|
|
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
|
|
CheckStunTransactionID(msg, kRfc5769SampleMsgWithAuthTransactionId,
|
|
kStunTransactionIdLength);
|
|
|
|
const StunByteStringAttribute* username =
|
|
msg.GetByteString(STUN_ATTR_USERNAME);
|
|
ASSERT_TRUE(username != NULL);
|
|
EXPECT_EQ(kRfc5769SampleMsgWithAuthUsername, username->GetString());
|
|
|
|
const StunByteStringAttribute* nonce =
|
|
msg.GetByteString(STUN_ATTR_NONCE);
|
|
ASSERT_TRUE(nonce != NULL);
|
|
EXPECT_EQ(kRfc5769SampleMsgWithAuthNonce, nonce->GetString());
|
|
|
|
const StunByteStringAttribute* realm =
|
|
msg.GetByteString(STUN_ATTR_REALM);
|
|
ASSERT_TRUE(realm != NULL);
|
|
EXPECT_EQ(kRfc5769SampleMsgWithAuthRealm, realm->GetString());
|
|
|
|
// No fingerprint, actual M-I checked in later tests.
|
|
ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
|
|
ASSERT_TRUE(msg.GetUInt32(STUN_ATTR_FINGERPRINT) == NULL);
|
|
}
|
|
|
|
// The RFC3489 packet in this test is the same as
|
|
// kStunMessageWithIPv4MappedAddress, but with a different value where the
|
|
// magic cookie was.
|
|
TEST_F(StunTest, ReadLegacyMessage) {
|
|
unsigned char rfc3489_packet[sizeof(kStunMessageWithIPv4MappedAddress)];
|
|
memcpy(rfc3489_packet, kStunMessageWithIPv4MappedAddress,
|
|
sizeof(kStunMessageWithIPv4MappedAddress));
|
|
// Overwrite the magic cookie here.
|
|
memcpy(&rfc3489_packet[4], "ABCD", 4);
|
|
|
|
StunMessage msg;
|
|
size_t size = ReadStunMessage(&msg, rfc3489_packet);
|
|
CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
|
|
CheckStunTransactionID(msg, &rfc3489_packet[4], kStunTransactionIdLength + 4);
|
|
|
|
const StunAddressAttribute* addr = msg.GetAddress(STUN_ATTR_MAPPED_ADDRESS);
|
|
rtc::IPAddress test_address(kIPv4TestAddress1);
|
|
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV4,
|
|
kTestMessagePort4, test_address);
|
|
}
|
|
|
|
TEST_F(StunTest, SetIPv6XorAddressAttributeOwner) {
|
|
StunMessage msg;
|
|
StunMessage msg2;
|
|
size_t size = ReadStunMessage(&msg, kStunMessageWithIPv6XorMappedAddress);
|
|
|
|
rtc::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, NULL);
|
|
addr2.SetIP(addr->ipaddr());
|
|
addr2.SetPort(addr->port());
|
|
addr2.SetOwner(&msg2);
|
|
// The internal IP address shouldn't change.
|
|
ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());
|
|
|
|
rtc::ByteBufferWriter correct_buf;
|
|
rtc::ByteBufferWriter wrong_buf;
|
|
EXPECT_TRUE(addr->Write(&correct_buf));
|
|
EXPECT_TRUE(addr2.Write(&wrong_buf));
|
|
// But when written out, the buffers should look different.
|
|
ASSERT_NE(0,
|
|
memcmp(correct_buf.Data(), wrong_buf.Data(), wrong_buf.Length()));
|
|
// And when reading a known good value, the address should be wrong.
|
|
rtc::ByteBufferReader read_buf(correct_buf);
|
|
addr2.Read(&read_buf);
|
|
ASSERT_NE(addr->ipaddr(), addr2.ipaddr());
|
|
addr2.SetIP(addr->ipaddr());
|
|
addr2.SetPort(addr->port());
|
|
// Try writing with no owner at all, should fail and write nothing.
|
|
addr2.SetOwner(NULL);
|
|
ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());
|
|
wrong_buf.Clear();
|
|
EXPECT_FALSE(addr2.Write(&wrong_buf));
|
|
ASSERT_EQ(0U, 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);
|
|
|
|
rtc::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, NULL);
|
|
addr2.SetIP(addr->ipaddr());
|
|
addr2.SetPort(addr->port());
|
|
addr2.SetOwner(&msg2);
|
|
// The internal IP address shouldn't change.
|
|
ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());
|
|
|
|
rtc::ByteBufferWriter correct_buf;
|
|
rtc::ByteBufferWriter wrong_buf;
|
|
EXPECT_TRUE(addr->Write(&correct_buf));
|
|
EXPECT_TRUE(addr2.Write(&wrong_buf));
|
|
// The same address data should be written.
|
|
ASSERT_EQ(0,
|
|
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.
|
|
rtc::ByteBufferReader read_buf(correct_buf);
|
|
EXPECT_TRUE(addr2.Read(&read_buf));
|
|
ASSERT_EQ(addr->ipaddr(), addr2.ipaddr());
|
|
|
|
// However, no owner is still an error, should fail and write nothing.
|
|
addr2.SetOwner(NULL);
|
|
ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());
|
|
wrong_buf.Clear();
|
|
EXPECT_FALSE(addr2.Write(&wrong_buf));
|
|
}
|
|
|
|
TEST_F(StunTest, CreateIPv6AddressAttribute) {
|
|
rtc::IPAddress test_ip(kIPv6TestAddress2);
|
|
|
|
StunAddressAttribute* addr =
|
|
StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
|
|
rtc::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;
|
|
rtc::IPAddress test_ip(test_in_addr);
|
|
|
|
StunAddressAttribute* addr =
|
|
StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
|
|
rtc::SocketAddress test_addr(test_ip, kTestMessagePort2);
|
|
addr->SetAddress(test_addr);
|
|
|
|
CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV4,
|
|
kTestMessagePort2, test_ip);
|
|
delete addr;
|
|
}
|
|
|
|
// 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(rtc::IPAddress(kIPv4TestAddress1));
|
|
ASSERT_EQ(kTestMessagePort1, addr->port());
|
|
ASSERT_EQ(rtc::IPAddress(kIPv4TestAddress1), addr->ipaddr());
|
|
|
|
StunAddressAttribute* addr2 =
|
|
StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
|
|
// IP first
|
|
addr2->SetIP(rtc::IPAddress(kIPv4TestAddress1));
|
|
addr2->SetPort(kTestMessagePort2);
|
|
ASSERT_EQ(kTestMessagePort2, addr2->port());
|
|
ASSERT_EQ(rtc::IPAddress(kIPv4TestAddress1), addr2->ipaddr());
|
|
|
|
delete addr;
|
|
delete addr2;
|
|
}
|
|
|
|
TEST_F(StunTest, WriteMessageWithIPv6AddressAttribute) {
|
|
StunMessage msg;
|
|
size_t size = sizeof(kStunMessageWithIPv6MappedAddress);
|
|
|
|
rtc::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);
|
|
rtc::SocketAddress test_addr(test_ip, kTestMessagePort2);
|
|
addr->SetAddress(test_addr);
|
|
msg.AddAttribute(addr);
|
|
|
|
CheckStunHeader(msg, STUN_BINDING_REQUEST, (size - 20));
|
|
|
|
rtc::ByteBufferWriter out;
|
|
EXPECT_TRUE(msg.Write(&out));
|
|
ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv6MappedAddress));
|
|
int len1 = static_cast<int>(out.Length());
|
|
rtc::ByteBufferReader read_buf(out);
|
|
std::string bytes;
|
|
read_buf.ReadString(&bytes, len1);
|
|
ASSERT_EQ(0, memcmp(bytes.c_str(), kStunMessageWithIPv6MappedAddress, len1));
|
|
}
|
|
|
|
TEST_F(StunTest, WriteMessageWithIPv4AddressAttribute) {
|
|
StunMessage msg;
|
|
size_t size = sizeof(kStunMessageWithIPv4MappedAddress);
|
|
|
|
rtc::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);
|
|
rtc::SocketAddress test_addr(test_ip, kTestMessagePort4);
|
|
addr->SetAddress(test_addr);
|
|
msg.AddAttribute(addr);
|
|
|
|
CheckStunHeader(msg, STUN_BINDING_RESPONSE, (size - 20));
|
|
|
|
rtc::ByteBufferWriter out;
|
|
EXPECT_TRUE(msg.Write(&out));
|
|
ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv4MappedAddress));
|
|
int len1 = static_cast<int>(out.Length());
|
|
rtc::ByteBufferReader read_buf(out);
|
|
std::string bytes;
|
|
read_buf.ReadString(&bytes, len1);
|
|
ASSERT_EQ(0, memcmp(bytes.c_str(), kStunMessageWithIPv4MappedAddress, len1));
|
|
}
|
|
|
|
TEST_F(StunTest, WriteMessageWithIPv6XorAddressAttribute) {
|
|
StunMessage msg;
|
|
size_t size = sizeof(kStunMessageWithIPv6XorMappedAddress);
|
|
|
|
rtc::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::CreateXorAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
|
|
rtc::SocketAddress test_addr(test_ip, kTestMessagePort1);
|
|
addr->SetAddress(test_addr);
|
|
msg.AddAttribute(addr);
|
|
|
|
CheckStunHeader(msg, STUN_BINDING_RESPONSE, (size - 20));
|
|
|
|
rtc::ByteBufferWriter out;
|
|
EXPECT_TRUE(msg.Write(&out));
|
|
ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv6XorMappedAddress));
|
|
int len1 = static_cast<int>(out.Length());
|
|
rtc::ByteBufferReader read_buf(out);
|
|
std::string bytes;
|
|
read_buf.ReadString(&bytes, len1);
|
|
ASSERT_EQ(0,
|
|
memcmp(bytes.c_str(), kStunMessageWithIPv6XorMappedAddress, len1));
|
|
}
|
|
|
|
TEST_F(StunTest, WriteMessageWithIPv4XoreAddressAttribute) {
|
|
StunMessage msg;
|
|
size_t size = sizeof(kStunMessageWithIPv4XorMappedAddress);
|
|
|
|
rtc::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::CreateXorAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
|
|
rtc::SocketAddress test_addr(test_ip, kTestMessagePort3);
|
|
addr->SetAddress(test_addr);
|
|
msg.AddAttribute(addr);
|
|
|
|
CheckStunHeader(msg, STUN_BINDING_RESPONSE, (size - 20));
|
|
|
|
rtc::ByteBufferWriter out;
|
|
EXPECT_TRUE(msg.Write(&out));
|
|
ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv4XorMappedAddress));
|
|
int len1 = static_cast<int>(out.Length());
|
|
rtc::ByteBufferReader read_buf(out);
|
|
std::string bytes;
|
|
read_buf.ReadString(&bytes, len1);
|
|
ASSERT_EQ(0,
|
|
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_TRUE(username != NULL);
|
|
EXPECT_EQ(kTestUserName1, username->GetString());
|
|
}
|
|
|
|
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_TRUE(username != NULL);
|
|
EXPECT_EQ(kTestUserName2, username->GetString());
|
|
}
|
|
|
|
TEST_F(StunTest, ReadErrorCodeAttribute) {
|
|
StunMessage msg;
|
|
size_t size = ReadStunMessage(&msg, kStunMessageWithErrorAttribute);
|
|
|
|
CheckStunHeader(msg, STUN_BINDING_ERROR_RESPONSE, size);
|
|
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
|
|
const StunErrorCodeAttribute* errorcode = msg.GetErrorCode();
|
|
ASSERT_TRUE(errorcode != NULL);
|
|
EXPECT_EQ(kTestErrorClass, errorcode->eclass());
|
|
EXPECT_EQ(kTestErrorNumber, errorcode->number());
|
|
EXPECT_EQ(kTestErrorReason, errorcode->reason());
|
|
EXPECT_EQ(kTestErrorCode, errorcode->code());
|
|
}
|
|
|
|
TEST_F(StunTest, ReadMessageWithAUInt16ListAttribute) {
|
|
StunMessage msg;
|
|
size_t size = ReadStunMessage(&msg, kStunMessageWithUInt16ListAttribute);
|
|
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
|
|
const StunUInt16ListAttribute* types = msg.GetUnknownAttributes();
|
|
ASSERT_TRUE(types != NULL);
|
|
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, ReadMessageWithAnUnknownAttribute) {
|
|
StunMessage msg;
|
|
size_t size = ReadStunMessage(&msg, kStunMessageWithUnknownAttribute);
|
|
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
|
|
|
|
// Parsing should have succeeded and there should be a USERNAME attribute
|
|
const StunByteStringAttribute* username =
|
|
msg.GetByteString(STUN_ATTR_USERNAME);
|
|
ASSERT_TRUE(username != NULL);
|
|
EXPECT_EQ(kTestUserName2, username->GetString());
|
|
}
|
|
|
|
TEST_F(StunTest, ReadMessageWithOriginAttribute) {
|
|
StunMessage msg;
|
|
size_t size = ReadStunMessage(&msg, kStunMessageWithOriginAttribute);
|
|
CheckStunHeader(msg, STUN_BINDING_REQUEST, size);
|
|
const StunByteStringAttribute* origin =
|
|
msg.GetByteString(STUN_ATTR_ORIGIN);
|
|
ASSERT_TRUE(origin != NULL);
|
|
EXPECT_EQ(kTestOrigin, origin->GetString());
|
|
}
|
|
|
|
TEST_F(StunTest, WriteMessageWithAnErrorCodeAttribute) {
|
|
StunMessage msg;
|
|
size_t size = sizeof(kStunMessageWithErrorAttribute);
|
|
|
|
msg.SetType(STUN_BINDING_ERROR_RESPONSE);
|
|
msg.SetTransactionID(
|
|
std::string(reinterpret_cast<const char*>(kTestTransactionId1),
|
|
kStunTransactionIdLength));
|
|
CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength);
|
|
StunErrorCodeAttribute* errorcode = StunAttribute::CreateErrorCode();
|
|
errorcode->SetCode(kTestErrorCode);
|
|
errorcode->SetReason(kTestErrorReason);
|
|
msg.AddAttribute(errorcode);
|
|
CheckStunHeader(msg, STUN_BINDING_ERROR_RESPONSE, (size - 20));
|
|
|
|
rtc::ByteBufferWriter out;
|
|
EXPECT_TRUE(msg.Write(&out));
|
|
ASSERT_EQ(size, out.Length());
|
|
// No padding.
|
|
ASSERT_EQ(0, memcmp(out.Data(), kStunMessageWithErrorAttribute, size));
|
|
}
|
|
|
|
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));
|
|
|
|
rtc::ByteBufferWriter out;
|
|
EXPECT_TRUE(msg.Write(&out));
|
|
ASSERT_EQ(size, out.Length());
|
|
// Check everything up to the padding.
|
|
ASSERT_EQ(0,
|
|
memcmp(out.Data(), kStunMessageWithUInt16ListAttribute, size - 2));
|
|
}
|
|
|
|
TEST_F(StunTest, WriteMessageWithOriginAttribute) {
|
|
StunMessage msg;
|
|
size_t size = sizeof(kStunMessageWithOriginAttribute);
|
|
|
|
msg.SetType(STUN_BINDING_REQUEST);
|
|
msg.SetTransactionID(
|
|
std::string(reinterpret_cast<const char*>(kTestTransactionId1),
|
|
kStunTransactionIdLength));
|
|
StunByteStringAttribute* origin =
|
|
new StunByteStringAttribute(STUN_ATTR_ORIGIN, kTestOrigin);
|
|
msg.AddAttribute(origin);
|
|
|
|
rtc::ByteBufferWriter out;
|
|
EXPECT_TRUE(msg.Write(&out));
|
|
ASSERT_EQ(size, out.Length());
|
|
// Check everything up to the padding
|
|
ASSERT_EQ(0, memcmp(out.Data(), kStunMessageWithOriginAttribute, size - 2));
|
|
}
|
|
|
|
// Test that we fail to read messages with invalid lengths.
|
|
void CheckFailureToRead(const unsigned char* testcase, size_t length) {
|
|
StunMessage msg;
|
|
const char* input = reinterpret_cast<const char*>(testcase);
|
|
rtc::ByteBufferReader buf(input, length);
|
|
ASSERT_FALSE(msg.Read(&buf));
|
|
}
|
|
|
|
TEST_F(StunTest, FailToReadInvalidMessages) {
|
|
CheckFailureToRead(kStunMessageWithZeroLength,
|
|
kRealLengthOfInvalidLengthTestCases);
|
|
CheckFailureToRead(kStunMessageWithSmallLength,
|
|
kRealLengthOfInvalidLengthTestCases);
|
|
CheckFailureToRead(kStunMessageWithExcessLength,
|
|
kRealLengthOfInvalidLengthTestCases);
|
|
}
|
|
|
|
// Test that we properly fail to read a non-STUN message.
|
|
TEST_F(StunTest, FailToReadRtcpPacket) {
|
|
CheckFailureToRead(kRtcpPacket, sizeof(kRtcpPacket));
|
|
}
|
|
|
|
// Check our STUN message validation code against the RFC5769 test messages.
|
|
TEST_F(StunTest, ValidateMessageIntegrity) {
|
|
// Try the messages from RFC 5769.
|
|
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kRfc5769SampleRequest),
|
|
sizeof(kRfc5769SampleRequest),
|
|
kRfc5769SampleMsgPassword));
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kRfc5769SampleRequest),
|
|
sizeof(kRfc5769SampleRequest),
|
|
"InvalidPassword"));
|
|
|
|
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kRfc5769SampleResponse),
|
|
sizeof(kRfc5769SampleResponse),
|
|
kRfc5769SampleMsgPassword));
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kRfc5769SampleResponse),
|
|
sizeof(kRfc5769SampleResponse),
|
|
"InvalidPassword"));
|
|
|
|
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kRfc5769SampleResponseIPv6),
|
|
sizeof(kRfc5769SampleResponseIPv6),
|
|
kRfc5769SampleMsgPassword));
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kRfc5769SampleResponseIPv6),
|
|
sizeof(kRfc5769SampleResponseIPv6),
|
|
"InvalidPassword"));
|
|
|
|
// We first need to compute the key for the long-term authentication HMAC.
|
|
std::string key;
|
|
ComputeStunCredentialHash(kRfc5769SampleMsgWithAuthUsername,
|
|
kRfc5769SampleMsgWithAuthRealm, kRfc5769SampleMsgWithAuthPassword, &key);
|
|
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kRfc5769SampleRequestLongTermAuth),
|
|
sizeof(kRfc5769SampleRequestLongTermAuth), key));
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kRfc5769SampleRequestLongTermAuth),
|
|
sizeof(kRfc5769SampleRequestLongTermAuth),
|
|
"InvalidPassword"));
|
|
|
|
// Try some edge cases.
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
|
|
sizeof(kStunMessageWithZeroLength),
|
|
kRfc5769SampleMsgPassword));
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
|
|
sizeof(kStunMessageWithExcessLength),
|
|
kRfc5769SampleMsgPassword));
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
|
|
sizeof(kStunMessageWithSmallLength),
|
|
kRfc5769SampleMsgPassword));
|
|
|
|
// Again, but with the lengths matching what is claimed in the headers.
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
|
|
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithZeroLength[2]),
|
|
kRfc5769SampleMsgPassword));
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
|
|
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithExcessLength[2]),
|
|
kRfc5769SampleMsgPassword));
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
|
|
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithSmallLength[2]),
|
|
kRfc5769SampleMsgPassword));
|
|
|
|
// Check that a too-short HMAC doesn't cause buffer overflow.
|
|
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(kStunMessageWithBadHmacAtEnd),
|
|
sizeof(kStunMessageWithBadHmacAtEnd),
|
|
kRfc5769SampleMsgPassword));
|
|
|
|
// Test that munging a single bit anywhere in the message causes the
|
|
// message-integrity check to fail, unless it is after the M-I attribute.
|
|
char buf[sizeof(kRfc5769SampleRequest)];
|
|
memcpy(buf, kRfc5769SampleRequest, sizeof(kRfc5769SampleRequest));
|
|
for (size_t i = 0; i < sizeof(buf); ++i) {
|
|
buf[i] ^= 0x01;
|
|
if (i > 0)
|
|
buf[i - 1] ^= 0x01;
|
|
EXPECT_EQ(i >= sizeof(buf) - 8, StunMessage::ValidateMessageIntegrity(
|
|
buf, sizeof(buf), kRfc5769SampleMsgPassword));
|
|
}
|
|
}
|
|
|
|
// Validate that we generate correct MESSAGE-INTEGRITY attributes.
|
|
// Note the use of IceMessage instead of StunMessage; this is necessary because
|
|
// the RFC5769 test messages used include attributes not found in basic STUN.
|
|
TEST_F(StunTest, AddMessageIntegrity) {
|
|
IceMessage msg;
|
|
rtc::ByteBufferReader buf(
|
|
reinterpret_cast<const char*>(kRfc5769SampleRequestWithoutMI),
|
|
sizeof(kRfc5769SampleRequestWithoutMI));
|
|
EXPECT_TRUE(msg.Read(&buf));
|
|
EXPECT_TRUE(msg.AddMessageIntegrity(kRfc5769SampleMsgPassword));
|
|
const StunByteStringAttribute* mi_attr =
|
|
msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY);
|
|
EXPECT_EQ(20U, mi_attr->length());
|
|
EXPECT_EQ(0, memcmp(
|
|
mi_attr->bytes(), kCalculatedHmac1, sizeof(kCalculatedHmac1)));
|
|
|
|
rtc::ByteBufferWriter buf1;
|
|
EXPECT_TRUE(msg.Write(&buf1));
|
|
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(),
|
|
kRfc5769SampleMsgPassword));
|
|
|
|
IceMessage msg2;
|
|
rtc::ByteBufferReader buf2(
|
|
reinterpret_cast<const char*>(kRfc5769SampleResponseWithoutMI),
|
|
sizeof(kRfc5769SampleResponseWithoutMI));
|
|
EXPECT_TRUE(msg2.Read(&buf2));
|
|
EXPECT_TRUE(msg2.AddMessageIntegrity(kRfc5769SampleMsgPassword));
|
|
const StunByteStringAttribute* mi_attr2 =
|
|
msg2.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY);
|
|
EXPECT_EQ(20U, mi_attr2->length());
|
|
EXPECT_EQ(
|
|
0, memcmp(mi_attr2->bytes(), kCalculatedHmac2, sizeof(kCalculatedHmac2)));
|
|
|
|
rtc::ByteBufferWriter buf3;
|
|
EXPECT_TRUE(msg2.Write(&buf3));
|
|
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
|
|
reinterpret_cast<const char*>(buf3.Data()), buf3.Length(),
|
|
kRfc5769SampleMsgPassword));
|
|
}
|
|
|
|
// Check our STUN message validation code against the RFC5769 test messages.
|
|
TEST_F(StunTest, ValidateFingerprint) {
|
|
EXPECT_TRUE(StunMessage::ValidateFingerprint(
|
|
reinterpret_cast<const char*>(kRfc5769SampleRequest),
|
|
sizeof(kRfc5769SampleRequest)));
|
|
EXPECT_TRUE(StunMessage::ValidateFingerprint(
|
|
reinterpret_cast<const char*>(kRfc5769SampleResponse),
|
|
sizeof(kRfc5769SampleResponse)));
|
|
EXPECT_TRUE(StunMessage::ValidateFingerprint(
|
|
reinterpret_cast<const char*>(kRfc5769SampleResponseIPv6),
|
|
sizeof(kRfc5769SampleResponseIPv6)));
|
|
|
|
EXPECT_FALSE(StunMessage::ValidateFingerprint(
|
|
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
|
|
sizeof(kStunMessageWithZeroLength)));
|
|
EXPECT_FALSE(StunMessage::ValidateFingerprint(
|
|
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
|
|
sizeof(kStunMessageWithExcessLength)));
|
|
EXPECT_FALSE(StunMessage::ValidateFingerprint(
|
|
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
|
|
sizeof(kStunMessageWithSmallLength)));
|
|
|
|
// Test that munging a single bit anywhere in the message causes the
|
|
// fingerprint check to fail.
|
|
char buf[sizeof(kRfc5769SampleRequest)];
|
|
memcpy(buf, kRfc5769SampleRequest, sizeof(kRfc5769SampleRequest));
|
|
for (size_t i = 0; i < sizeof(buf); ++i) {
|
|
buf[i] ^= 0x01;
|
|
if (i > 0)
|
|
buf[i - 1] ^= 0x01;
|
|
EXPECT_FALSE(StunMessage::ValidateFingerprint(buf, sizeof(buf)));
|
|
}
|
|
// Put them all back to normal and the check should pass again.
|
|
buf[sizeof(buf) - 1] ^= 0x01;
|
|
EXPECT_TRUE(StunMessage::ValidateFingerprint(buf, sizeof(buf)));
|
|
}
|
|
|
|
TEST_F(StunTest, AddFingerprint) {
|
|
IceMessage msg;
|
|
rtc::ByteBufferReader buf(
|
|
reinterpret_cast<const char*>(kRfc5769SampleRequestWithoutMI),
|
|
sizeof(kRfc5769SampleRequestWithoutMI));
|
|
EXPECT_TRUE(msg.Read(&buf));
|
|
EXPECT_TRUE(msg.AddFingerprint());
|
|
|
|
rtc::ByteBufferWriter buf1;
|
|
EXPECT_TRUE(msg.Write(&buf1));
|
|
EXPECT_TRUE(StunMessage::ValidateFingerprint(
|
|
reinterpret_cast<const char*>(buf1.Data()), buf1.Length()));
|
|
}
|
|
|
|
// Sample "GTURN" relay message.
|
|
static const unsigned char kRelayMessage[] = {
|
|
0x00, 0x01, 0x00, 88, // 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, 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.
|
|
};
|
|
|
|
// Test that we can read the GTURN-specific fields.
|
|
TEST_F(StunTest, ReadRelayMessage) {
|
|
RelayMessage msg, msg2;
|
|
|
|
const char* input = reinterpret_cast<const char*>(kRelayMessage);
|
|
size_t size = sizeof(kRelayMessage);
|
|
rtc::ByteBufferReader buf(input, size);
|
|
EXPECT_TRUE(msg.Read(&buf));
|
|
|
|
EXPECT_EQ(STUN_BINDING_REQUEST, msg.type());
|
|
EXPECT_EQ(size - 20, msg.length());
|
|
EXPECT_EQ("0123456789ab", msg.transaction_id());
|
|
|
|
msg2.SetType(STUN_BINDING_REQUEST);
|
|
msg2.SetTransactionID("0123456789ab");
|
|
|
|
in_addr legacy_in_addr;
|
|
legacy_in_addr.s_addr = htonl(17U);
|
|
rtc::IPAddress legacy_ip(legacy_in_addr);
|
|
|
|
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(12U, bytes->length());
|
|
EXPECT_EQ("abcdefghijkl", bytes->GetString());
|
|
|
|
StunByteStringAttribute* bytes2 =
|
|
StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
|
|
bytes2->CopyBytes("abcdefghijkl");
|
|
msg2.AddAttribute(bytes2);
|
|
|
|
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(4U, 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);
|
|
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(7U, bytes->length());
|
|
EXPECT_EQ("abcdefg", bytes->GetString());
|
|
|
|
bytes2 = StunAttribute::CreateByteString(STUN_ATTR_DATA);
|
|
bytes2->CopyBytes("abcdefg");
|
|
msg2.AddAttribute(bytes2);
|
|
|
|
rtc::ByteBufferWriter out;
|
|
EXPECT_TRUE(msg.Write(&out));
|
|
EXPECT_EQ(size, out.Length());
|
|
size_t len1 = out.Length();
|
|
rtc::ByteBufferReader read_buf(out);
|
|
std::string outstring;
|
|
read_buf.ReadString(&outstring, len1);
|
|
EXPECT_EQ(0, memcmp(outstring.c_str(), input, len1));
|
|
|
|
rtc::ByteBufferWriter out2;
|
|
EXPECT_TRUE(msg2.Write(&out2));
|
|
EXPECT_EQ(size, out2.Length());
|
|
size_t len2 = out2.Length();
|
|
rtc::ByteBufferReader read_buf2(out2);
|
|
std::string outstring2;
|
|
read_buf2.ReadString(&outstring2, len2);
|
|
EXPECT_EQ(0, memcmp(outstring2.c_str(), input, len2));
|
|
}
|
|
|
|
} // namespace cricket
|