webrtc_m130/webrtc/p2p/base/stun_unittest.cc
nisse cc99bc25d8 Change StunMessage::AddAttribute return type from bool to void.
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}
2017-02-02 09:31:30 +00:00

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