|  | /* | 
|  | *  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); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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"); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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)); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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); | 
|  | EXPECT_TRUE(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"); | 
|  | EXPECT_TRUE(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 |