Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

SSL stun_unittest.cc

  Interaktion und
PortierbarkeitC
 

/*
 *  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 "api/transport/stun.h"

#include <algorithm>
#include <array>
#include <cstdint>
#include <cstring>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <utility>
#include <vector>

#include "rtc_base/byte_buffer.h"
#include "rtc_base/byte_order.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/span_helpers.h"
#include "system_wrappers/include/metrics.h"
#include "test/gmock.h"
#include "test/gtest.h"

namespace webrtc {

namespace {

using ::testing::ElementsAreArray;

// Sample STUN packets with various attributes
// Gathered by wiresharking pjproject's pjnath test programs
// pjproject available at www.pjsip.org

// clang-format off
// clang formatting doesn't respect inline comments.

constexpr uint8_t 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
};

constexpr uint8_t 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:
constexpr uint8_t 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
};

constexpr uint8_t 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)
constexpr uint8_t 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.
constexpr uint8_t 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
constexpr uint8_t 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.
constexpr uint8_t 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)
constexpr uint8_t 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
};

// Sample messages with an invalid length Field

// The actual length in bytes of the invalid messages (including STUN header)
constexpr int kRealLengthOfInvalidLengthTestCases = 32;

constexpr uint8_t 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,
};

constexpr uint8_t 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,
};

constexpr uint8_t 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,
};

constexpr uint8_t 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
constexpr uint8_t 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,
};

const std::span<const uint8_t> kZeroLenView(kStunMessageWithZeroLength);
const std::span<const uint8_t> kExcessLenView(kStunMessageWithExcessLength);
const std::span<const uint8_t> kSmallLenView(kStunMessageWithSmallLength);


// 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)
constexpr auto kRfc5769SampleMsgTransactionId = std::to_array<uint8_t>(
    {0xb7, 0xe7, 0xa7, 0x01, 0xbc, 0x34, 0xd6, 0x86, 0xfa, 0x87, 0xdf, 0xae});
constexpr char kRfc5769SampleMsgClientSoftware[] = "STUN test client";
constexpr char kRfc5769SampleMsgServerSoftware[] = "test vector";
constexpr char kRfc5769SampleMsgUsername[] = "evtj:h6vY";
constexpr char kRfc5769SampleMsgPassword[] = "VOkJxbRl1RmTxUk/WvJxBt";
const SocketAddress kRfc5769SampleMsgMappedAddress("192.0.2.1"32853);
const SocketAddress kRfc5769SampleMsgIPv6MappedAddress(
    "2001:db8:1234:5678:11:2233:4455:6677"32853);

constexpr auto kRfc5769SampleMsgWithAuthTransactionId = std::to_array<uint8_t>(
    {0x78, 0xad, 0x34, 0x33, 0xc6, 0xad, 0x72, 0xc0, 0x29, 0xda, 0x41, 0x2e});
constexpr char kRfc5769SampleMsgWithAuthUsername[] =
    "\xe3\x83\x9e\xe3\x83\x88\xe3\x83\xaa\xe3\x83\x83\xe3\x82\xaf\xe3\x82\xb9";
constexpr char kRfc5769SampleMsgWithAuthPassword[] = "TheMatrIX";
constexpr char kRfc5769SampleMsgWithAuthNonce[] =
    "f//499k954d6OL34oL9FSTvy64sA";
constexpr char kRfc5769SampleMsgWithAuthRealm[] = "example.org";

// 2.1.  Sample Request
constexpr uint8_t 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.1.  Sample Request
constexpr uint8_t kSampleRequestMI32[] = {
  0x00, 0x01, 0x00, 0x48,   //    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,   // }
  0xC0, 0x60, 0x00, 0x04,   //    MESSAGE-INTEGRITY-32 attribute header
  0x45, 0x45, 0xce, 0x7c,   // }  HMAC-SHA1 fingerprint (first 32 bit)
  0x80, 0x28, 0x00, 0x04,   //    FINGERPRINT attribute header
  0xe5, 0x7a, 0x3b, 0xcf    //    CRC32 fingerprint
};

// 2.2.  Sample IPv4 Response
constexpr uint8_t 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
constexpr uint8_t 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
constexpr uint8_t 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.
constexpr uint8_t 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.
constexpr uint8_t kCalculatedHmac1[] = {
  0x79, 0x07, 0xc2, 0xd2,  // }
  0xed, 0xbf, 0xea, 0x48,  // }
  0x0e, 0x4c, 0x76, 0xd8,  // }  HMAC-SHA1 fingerprint
  0x29, 0x62, 0xd5, 0xc3,  // }
  0x74, 0x2a, 0xf9, 0xe3   // }
};

// This truncated HMAC differs from kCalculatedHmac1
// above since the sum is computed including header
// and the header is different since the message is shorter
// than when MESSAGE-INTEGRITY is used.
constexpr uint8_t kCalculatedHmac1_32[] = {
  0xda, 0x39, 0xde, 0x5d,  // }
};

// Length parameter is changed to 0x1c from 0x3c.
// AddMessageIntegrity will add MI information and update the length param
// accordingly.
constexpr uint8_t 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.
constexpr uint8_t kCalculatedHmac2[] = {
  0x5d, 0x6b, 0x58, 0xbe,  // }
  0xad, 0x94, 0xe0, 0x7e,  // }
  0xef, 0x0d, 0xfc, 0x12,  // }  HMAC-SHA1 fingerprint
  0x82, 0xa2, 0xbd, 0x08,  // }
  0x43, 0x14, 0x10, 0x28   // }
};

// This truncated HMAC differs from kCalculatedHmac2
// above since the sum is computed including header
// and the header is different since the message is shorter
// than when MESSAGE-INTEGRITY is used.
constexpr uint8_t kCalculatedHmac2_32[] = {
  0xe7, 0x5c, 0xd3, 0x16,  // }
};

// clang-format on

// A transaction ID without the 'magic cookie' portion
// pjnat's test programs use this transaction ID a lot.
constexpr auto kTestTransactionId1 = std::to_array<uint8_t>(
    {0x29, 0x1f, 0xcd, 0x7c, 0xba, 0x58, 0xab, 0xd7, 0xf2, 0x41, 0x01, 0x00});

// They use this one sometimes too.
constexpr auto kTestTransactionId2 = std::to_array<uint8_t>(
    {0xe3, 0xa9, 0x46, 0xe1, 0x7c, 0x00, 0xc2, 0x62, 0x54, 0x08, 0x01, 0x00});

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
constexpr char kTestUserName1[] = "abcdefgh";
constexpr char kTestUserName2[] = "abc";
constexpr char kTestErrorReason[] = "Unauthorized";
constexpr int kTestErrorClass = 4;
constexpr int kTestErrorNumber = 1;
constexpr int kTestErrorCode = 401;

constexpr int kTestMessagePort1 = 59977;
constexpr int kTestMessagePort2 = 47233;
constexpr int kTestMessagePort3 = 56743;
constexpr int kTestMessagePort4 = 40444;

}  // namespace

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,
                              std::span<const uint8_t> expectedID) {
    ASSERT_EQ(expectedID.size(), msg.transaction_id().size());
    ASSERT_EQ(expectedID.size() == kStunTransactionIdLength + 4,
              msg.IsLegacy());
    ASSERT_EQ(expectedID.size() == kStunTransactionIdLength, !msg.IsLegacy());
    EXPECT_THAT(msg.transaction_id(), ElementsAreArray(expectedID));
  }

  void CheckStunAddressAttribute(const StunAddressAttribute* addr,
                                 StunAddressFamily expected_family,
                                 int expected_port,
                                 const 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,
                                 std::span<const uint8_t> data) {
    ByteBufferReader buf(data);
    if (msg->Read(&buf)) {
      // Returns the size the stun message should report itself as being
      return (data.size() - 20);
    } else {
      return 0;
    }
  }
};

#define ReadStunMessage(X, Y) ReadStunMessageTestCase(X, 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 < std::size(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);

  const StunAddressAttribute* addr = msg.GetAddress(STUN_ATTR_MAPPED_ADDRESS);
  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);

  const StunAddressAttribute* addr =
      msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
  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);

  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);

  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);

  IPAddress test_address(kIPv6TestAddress1);

  CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
  CheckStunTransactionID(msg, kTestTransactionId2);

  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);
  const StunByteStringAttribute* software =
      msg.GetByteString(STUN_ATTR_SOFTWARE);
  ASSERT_TRUE(software != nullptr);
  EXPECT_EQ(kRfc5769SampleMsgClientSoftware, software->string_view());

  const StunByteStringAttribute* username =
      msg.GetByteString(STUN_ATTR_USERNAME);
  ASSERT_TRUE(username != nullptr);
  EXPECT_EQ(kRfc5769SampleMsgUsername, username->string_view());

  // Actual M-I value checked in a later test.
  ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != nullptr);

  // Fingerprint checked in a later test, but double-check the value here.
  const StunUInt32Attribute* fingerprint = msg.GetUInt32(STUN_ATTR_FINGERPRINT);
  ASSERT_TRUE(fingerprint != nullptr);
  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);
  const StunByteStringAttribute* software =
      msg.GetByteString(STUN_ATTR_SOFTWARE);
  ASSERT_TRUE(software != nullptr);
  EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->string_view());

  const StunAddressAttribute* mapped_address =
      msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
  ASSERT_TRUE(mapped_address != nullptr);
  EXPECT_EQ(kRfc5769SampleMsgMappedAddress, mapped_address->GetAddress());

  // Actual M-I and fingerprint checked in later tests.
  ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != nullptr);
  ASSERT_TRUE(msg.GetUInt32(STUN_ATTR_FINGERPRINT) != nullptr);
}

// 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);
  const StunByteStringAttribute* software =
      msg.GetByteString(STUN_ATTR_SOFTWARE);
  ASSERT_TRUE(software != nullptr);
  EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->string_view());

  const StunAddressAttribute* mapped_address =
      msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
  ASSERT_TRUE(mapped_address != nullptr);
  EXPECT_EQ(kRfc5769SampleMsgIPv6MappedAddress, mapped_address->GetAddress());

  // Actual M-I and fingerprint checked in later tests.
  ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != nullptr);
  ASSERT_TRUE(msg.GetUInt32(STUN_ATTR_FINGERPRINT) != nullptr);
}

// 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);
  const StunByteStringAttribute* username =
      msg.GetByteString(STUN_ATTR_USERNAME);
  ASSERT_TRUE(username != nullptr);
  EXPECT_EQ(kRfc5769SampleMsgWithAuthUsername, username->string_view());

  const StunByteStringAttribute* nonce = msg.GetByteString(STUN_ATTR_NONCE);
  ASSERT_TRUE(nonce != nullptr);
  EXPECT_EQ(kRfc5769SampleMsgWithAuthNonce, nonce->string_view());

  const StunByteStringAttribute* realm = msg.GetByteString(STUN_ATTR_REALM);
  ASSERT_TRUE(realm != nullptr);
  EXPECT_EQ(kRfc5769SampleMsgWithAuthRealm, realm->string_view());

  // No fingerprint, actual M-I checked in later tests.
  ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != nullptr);
  ASSERT_TRUE(msg.GetUInt32(STUN_ATTR_FINGERPRINT) == nullptr);
}

// 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) {
  auto rfc3489_packet = std::to_array(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, std::span(rfc3489_packet).subspan(4, kStunTransactionIdLength + 4));

  const StunAddressAttribute* addr = msg.GetAddress(STUN_ATTR_MAPPED_ADDRESS);
  IPAddress test_address(kIPv4TestAddress1);
  CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV4, kTestMessagePort4,
                            test_address);
}

TEST_F(StunTest, SetIPv6XorAddressAttributeOwner) {
  StunMessage msg;
  size_t size = ReadStunMessage(&msg, kStunMessageWithIPv6XorMappedAddress);

  IPAddress test_address(kIPv6TestAddress1);

  CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
  CheckStunTransactionID(msg, kTestTransactionId2);

  const StunAddressAttribute* addr =
      msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
  CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV6, kTestMessagePort1,
                            test_address);

  // Owner with a different transaction ID.
  StunMessage msg2(STUN_INVALID_MESSAGE_TYPE, "ABCDABCDABCD");
  StunXorAddressAttribute addr2(STUN_ATTR_XOR_MAPPED_ADDRESS, 20, nullptr);
  addr2.SetIP(addr->ipaddr());
  addr2.SetPort(addr->port());
  addr2.SetOwner(&msg2);
  // The internal IP address shouldn't change.
  ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());

  ByteBufferWriter correct_buf;
  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.
  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(nullptr);
  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;
  size_t size = ReadStunMessage(&msg, kStunMessageWithIPv4XorMappedAddress);

  IPAddress test_address(kIPv4TestAddress1);

  CheckStunHeader(msg, STUN_BINDING_RESPONSE, size);
  CheckStunTransactionID(msg, kTestTransactionId1);

  const StunAddressAttribute* addr =
      msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
  CheckStunAddressAttribute(addr, STUN_ADDRESS_IPV4, kTestMessagePort3,
                            test_address);

  // Owner with a different transaction ID.
  StunMessage msg2(STUN_INVALID_MESSAGE_TYPE, "ABCDABCDABCD");
  StunXorAddressAttribute addr2(STUN_ATTR_XOR_MAPPED_ADDRESS, 20, nullptr);
  addr2.SetIP(addr->ipaddr());
  addr2.SetPort(addr->port());
  addr2.SetOwner(&msg2);
  // The internal IP address shouldn't change.
  ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());

  ByteBufferWriter correct_buf;
  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.
  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(nullptr);
  ASSERT_EQ(addr2.ipaddr(), addr->ipaddr());
  wrong_buf.Clear();
  EXPECT_FALSE(addr2.Write(&wrong_buf));
}

TEST_F(StunTest, CreateIPv6AddressAttribute) {
  IPAddress test_ip(kIPv6TestAddress2);

  auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
  SocketAddress test_addr(test_ip, kTestMessagePort2);
  addr->SetAddress(test_addr);

  CheckStunAddressAttribute(addr.get(), STUN_ADDRESS_IPV6, kTestMessagePort2,
                            test_ip);
}

TEST_F(StunTest, CreateIPv4AddressAttribute) {
  struct in_addr test_in_addr;
  test_in_addr.s_addr = 0xBEB0B0BE;
  IPAddress test_ip(test_in_addr);

  auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
  SocketAddress test_addr(test_ip, kTestMessagePort2);
  addr->SetAddress(test_addr);

  CheckStunAddressAttribute(addr.get(), STUN_ADDRESS_IPV4, kTestMessagePort2,
                            test_ip);
}

// Test that we don't care what order we set the parts of an address
TEST_F(StunTest, CreateAddressInArbitraryOrder) {
  auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
  // Port first
  addr->SetPort(kTestMessagePort1);
  addr->SetIP(IPAddress(kIPv4TestAddress1));
  ASSERT_EQ(kTestMessagePort1, addr->port());
  ASSERT_EQ(IPAddress(kIPv4TestAddress1), addr->ipaddr());

  auto addr2 = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
  // IP first
  addr2->SetIP(IPAddress(kIPv4TestAddress1));
  addr2->SetPort(kTestMessagePort2);
  ASSERT_EQ(kTestMessagePort2, addr2->port());
  ASSERT_EQ(IPAddress(kIPv4TestAddress1), addr2->ipaddr());
}

TEST_F(StunTest, WriteMessageWithIPv6AddressAttribute) {
  size_t size = sizeof(kStunMessageWithIPv6MappedAddress);

  IPAddress test_ip(kIPv6TestAddress1);

  StunMessage msg(STUN_BINDING_REQUEST, AsStringView(kTestTransactionId1));
  CheckStunTransactionID(msg, kTestTransactionId1);

  auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
  SocketAddress test_addr(test_ip, kTestMessagePort2);
  addr->SetAddress(test_addr);
  msg.AddAttribute(std::move(addr));

  CheckStunHeader(msg, STUN_BINDING_REQUEST, (size - 20));

  ByteBufferWriter out;
  EXPECT_TRUE(msg.Write(&out));
  ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv6MappedAddress));
  int len1 = static_cast<int>(out.Length());
  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) {
  size_t size = sizeof(kStunMessageWithIPv4MappedAddress);

  IPAddress test_ip(kIPv4TestAddress1);

  StunMessage msg(STUN_BINDING_RESPONSE, AsStringView(kTestTransactionId1));
  CheckStunTransactionID(msg, kTestTransactionId1);

  auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
  SocketAddress test_addr(test_ip, kTestMessagePort4);
  addr->SetAddress(test_addr);
  msg.AddAttribute(std::move(addr));

  CheckStunHeader(msg, STUN_BINDING_RESPONSE, (size - 20));

  ByteBufferWriter out;
  EXPECT_TRUE(msg.Write(&out));
  ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv4MappedAddress));
  int len1 = static_cast<int>(out.Length());
  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) {
  size_t size = sizeof(kStunMessageWithIPv6XorMappedAddress);

  IPAddress test_ip(kIPv6TestAddress1);

  StunMessage msg(STUN_BINDING_RESPONSE, AsStringView(kTestTransactionId2));
  CheckStunTransactionID(msg, kTestTransactionId2);

  auto addr = StunAttribute::CreateXorAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
  SocketAddress test_addr(test_ip, kTestMessagePort1);
  addr->SetAddress(test_addr);
  msg.AddAttribute(std::move(addr));

  CheckStunHeader(msg, STUN_BINDING_RESPONSE, (size - 20));

  ByteBufferWriter out;
  EXPECT_TRUE(msg.Write(&out));
  ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv6XorMappedAddress));
  int len1 = static_cast<int>(out.Length());
  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) {
  size_t size = sizeof(kStunMessageWithIPv4XorMappedAddress);

  IPAddress test_ip(kIPv4TestAddress1);

  StunMessage msg(STUN_BINDING_RESPONSE, AsStringView(kTestTransactionId1));
  CheckStunTransactionID(msg, kTestTransactionId1);

  auto addr = StunAttribute::CreateXorAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
  SocketAddress test_addr(test_ip, kTestMessagePort3);
  addr->SetAddress(test_addr);
  msg.AddAttribute(std::move(addr));

  CheckStunHeader(msg, STUN_BINDING_RESPONSE, (size - 20));

  ByteBufferWriter out;
  EXPECT_TRUE(msg.Write(&out));
  ASSERT_EQ(out.Length(), sizeof(kStunMessageWithIPv4XorMappedAddress));
  int len1 = static_cast<int>(out.Length());
  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);
  const StunByteStringAttribute* username =
      msg.GetByteString(STUN_ATTR_USERNAME);
  ASSERT_TRUE(username != nullptr);
  EXPECT_EQ(kTestUserName1, username->string_view());
}

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);
  const StunByteStringAttribute* username =
      msg.GetByteString(STUN_ATTR_USERNAME);
  ASSERT_TRUE(username != nullptr);
  EXPECT_EQ(kTestUserName2, username->string_view());
}

TEST_F(StunTest, ReadErrorCodeAttribute) {
  StunMessage msg;
  size_t size = ReadStunMessage(&msg, kStunMessageWithErrorAttribute);

  CheckStunHeader(msg, STUN_BINDING_ERROR_RESPONSE, size);
  CheckStunTransactionID(msg, kTestTransactionId1);
  const StunErrorCodeAttribute* errorcode = msg.GetErrorCode();
  ASSERT_TRUE(errorcode != nullptr);
  EXPECT_EQ(kTestErrorClass, errorcode->eclass());
  EXPECT_EQ(kTestErrorNumber, errorcode->number());
  EXPECT_EQ(kTestErrorReason, errorcode->reason());
  EXPECT_EQ(kTestErrorCode, errorcode->code());
  EXPECT_EQ(kTestErrorCode, msg.GetErrorCodeValue());
}

// Test that GetErrorCodeValue returns STUN_ERROR_GLOBAL_FAILURE if the message
// in question doesn't have an error code attribute, rather than crashing.
TEST_F(StunTest, GetErrorCodeValueWithNoErrorAttribute) {
  StunMessage msg;
  ReadStunMessage(&msg, kStunMessageWithIPv6MappedAddress);
  EXPECT_EQ(STUN_ERROR_GLOBAL_FAILURE, msg.GetErrorCodeValue());
}

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 != nullptr);
  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 != nullptr);
  EXPECT_EQ(kTestUserName2, username->string_view());
}

TEST_F(StunTest, WriteMessageWithAnErrorCodeAttribute) {
  size_t size = sizeof(kStunMessageWithErrorAttribute);

  StunMessage msg(STUN_BINDING_ERROR_RESPONSE,
                  AsStringView(kTestTransactionId1));
  CheckStunTransactionID(msg, kTestTransactionId1);
  auto errorcode = StunAttribute::CreateErrorCode();
  errorcode->SetCode(kTestErrorCode);
  errorcode->SetReason(kTestErrorReason);
  msg.AddAttribute(std::move(errorcode));
  CheckStunHeader(msg, STUN_BINDING_ERROR_RESPONSE, (size - 20));

  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) {
  size_t size = sizeof(kStunMessageWithUInt16ListAttribute);

  StunMessage msg(STUN_BINDING_REQUEST, AsStringView(kTestTransactionId2));
  CheckStunTransactionID(msg, kTestTransactionId2);
  auto list = StunAttribute::CreateUnknownAttributes();
  list->AddType(0x1U);
  list->AddType(0x1000U);
  list->AddType(0xAB0CU);
  msg.AddAttribute(std::move(list));
  CheckStunHeader(msg, STUN_BINDING_REQUEST, (size - 20));

  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 that we fail to read messages with invalid lengths.
void CheckFailureToRead(const uint8_t* testcase, size_t length) {
  StunMessage msg;
  ByteBufferReader buf(std::span(testcase, 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::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, kRfc5769SampleRequest));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      "InvalidPassword", kRfc5769SampleRequest));

  EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, kRfc5769SampleResponse));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      "InvalidPassword", kRfc5769SampleResponse));

  EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, kRfc5769SampleResponseIPv6));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      "InvalidPassword", kRfc5769SampleResponseIPv6));

  // We first need to compute the key for the long-term authentication HMAC.
  std::string key;
  ComputeStunCredentialHash(kRfc5769SampleMsgWithAuthUsername,
                            kRfc5769SampleMsgWithAuthRealm,
                            kRfc5769SampleMsgWithAuthPassword, &key);
  EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
      key, kRfc5769SampleRequestLongTermAuth));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      "InvalidPassword", kRfc5769SampleRequestLongTermAuth));

  // Try some edge cases.
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, kStunMessageWithZeroLength));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, kStunMessageWithExcessLength));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, kStunMessageWithSmallLength));

  // Again, but with the lengths matching what is claimed in the headers.
  auto GetSubspan = [](std::span<const uint8_t> view) {
    size_t claimed_len = kStunHeaderSize + GetBE16(view.subspan(22));
    return view.subspan(0, std::min(claimed_len, view.size()));
  };
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, GetSubspan(kZeroLenView)));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, GetSubspan(kExcessLenView)));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, GetSubspan(kSmallLenView)));

  // Check that a too-short HMAC doesn't cause buffer overflow.
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, kStunMessageWithBadHmacAtEnd));

  // 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.
  auto buf = std::to_array(kRfc5769SampleRequest);
  for (size_t i = 0; i < buf.size(); ++i) {
    buf[i] ^= 0x01;
    if (i > 0)
      buf[i - 1] ^= 0x01;
    EXPECT_EQ(i >= buf.size() - 8,
              StunMessage::ValidateMessageIntegrityForTesting(
                  kRfc5769SampleMsgPassword, buf));
  }
}

// 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;
  ByteBufferReader buf(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->array_view().data(), kCalculatedHmac1,
                      sizeof(kCalculatedHmac1)));

  ByteBufferWriter buf1;
  EXPECT_TRUE(msg.Write(&buf1));
  EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, buf1.DataView()));

  IceMessage msg2;
  ByteBufferReader buf2(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->array_view().data(), kCalculatedHmac2,
                      sizeof(kCalculatedHmac2)));

  ByteBufferWriter buf3;
  EXPECT_TRUE(msg2.Write(&buf3));
  EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
      kRfc5769SampleMsgPassword, buf3.DataView()));
}

// Check our STUN message validation code against the RFC5769 test messages.
TEST_F(StunTest, ValidateMessageIntegrity32) {
  // Try the messages from RFC 5769.
  EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, kSampleRequestMI32));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
      "InvalidPassword", kSampleRequestMI32));

  // Try some edge cases.
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, kStunMessageWithZeroLength));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, kStunMessageWithExcessLength));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, kStunMessageWithSmallLength));

  // Again, but with the lengths matching what is claimed in the headers.
  auto GetSubspan = [](std::span<const uint8_t> view) {
    size_t claimed_len = kStunHeaderSize + GetBE16(view.subspan(22));
    return view.subspan(0, std::min(claimed_len, view.size()));
  };
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, GetSubspan(kZeroLenView)));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, GetSubspan(kExcessLenView)));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, GetSubspan(kSmallLenView)));

  // Check that a too-short HMAC doesn't cause buffer overflow.
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, kStunMessageWithBadHmacAtEnd));

  // 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.
  auto buf = std::to_array(kSampleRequestMI32);
  for (size_t i = 0; i < buf.size(); ++i) {
    buf[i] ^= 0x01;
    if (i > 0)
      buf[i - 1] ^= 0x01;
    EXPECT_EQ(i >= buf.size() - 8,
              StunMessage::ValidateMessageIntegrity32ForTesting(
                  kRfc5769SampleMsgPassword, buf));
  }
}

// Validate that we generate correct MESSAGE-INTEGRITY-32 attributes.
TEST_F(StunTest, AddMessageIntegrity32) {
  IceMessage msg;
  ByteBufferReader buf(kRfc5769SampleRequestWithoutMI);
  EXPECT_TRUE(msg.Read(&buf));
  EXPECT_TRUE(msg.AddMessageIntegrity32(kRfc5769SampleMsgPassword));
  const StunByteStringAttribute* mi_attr =
      msg.GetByteString(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32);
  EXPECT_EQ(4U, mi_attr->length());
  EXPECT_EQ(0, memcmp(mi_attr->array_view().data(), kCalculatedHmac1_32,
                      sizeof(kCalculatedHmac1_32)));

  ByteBufferWriter buf1;
  EXPECT_TRUE(msg.Write(&buf1));
  EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, buf1.DataView()));

  IceMessage msg2;
  ByteBufferReader buf2(kRfc5769SampleResponseWithoutMI);
  EXPECT_TRUE(msg2.Read(&buf2));
  EXPECT_TRUE(msg2.AddMessageIntegrity32(kRfc5769SampleMsgPassword));
  const StunByteStringAttribute* mi_attr2 =
      msg2.GetByteString(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32);
  EXPECT_EQ(4U, mi_attr2->length());
  EXPECT_EQ(0, memcmp(mi_attr2->array_view().data(), kCalculatedHmac2_32,
                      sizeof(kCalculatedHmac2_32)));

  ByteBufferWriter buf3;
  EXPECT_TRUE(msg2.Write(&buf3));
  EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
      kRfc5769SampleMsgPassword, buf3.DataView()));
}

// Validate that the message validates if both MESSAGE-INTEGRITY-32 and
// MESSAGE-INTEGRITY are present in the message.
// This is not expected to be used, but is not forbidden.
TEST_F(StunTest, AddMessageIntegrity32AndMessageIntegrity) {
  IceMessage msg;
  auto attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
  attr->CopyBytes(std::to_array<uint8_t>({'k''e''s''o''\0'}));
  msg.AddAttribute(std::move(attr));
  msg.AddMessageIntegrity32("password1");
  msg.AddMessageIntegrity("password2");

  ByteBufferWriter buf1;
  EXPECT_TRUE(msg.Write(&buf1));
  EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
      "password1", buf1.DataView()));
  EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting("password2",
                                                              buf1.DataView()));

  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
      "password2", buf1.DataView()));
  EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
      "password1", buf1.DataView()));
}

// Check our STUN message validation code against the RFC5769 test messages.
TEST_F(StunTest, ValidateFingerprint) {
  EXPECT_TRUE(StunMessage::ValidateFingerprint(kRfc5769SampleRequest));
  EXPECT_TRUE(StunMessage::ValidateFingerprint(kRfc5769SampleResponse));
  EXPECT_TRUE(StunMessage::ValidateFingerprint(kRfc5769SampleResponseIPv6));

  EXPECT_FALSE(StunMessage::ValidateFingerprint(kStunMessageWithZeroLength));
  EXPECT_FALSE(StunMessage::ValidateFingerprint(kStunMessageWithExcessLength));
  EXPECT_FALSE(StunMessage::ValidateFingerprint(kStunMessageWithSmallLength));

  // Test that munging a single bit anywhere in the message causes the
  // fingerprint check to fail.
  auto buf = std::to_array(kRfc5769SampleRequest);
  for (size_t i = 0; i < buf.size(); ++i) {
    buf[i] ^= 0x01;
    if (i > 0)
      buf[i - 1] ^= 0x01;
    EXPECT_FALSE(StunMessage::ValidateFingerprint(buf));
  }
  // Put them all back to normal and the check should pass again.
  buf[buf.size() - 1] ^= 0x01;
  EXPECT_TRUE(StunMessage::ValidateFingerprint(buf));
}

TEST_F(StunTest, AddFingerprint) {
  IceMessage msg;
  ByteBufferReader buf(kRfc5769SampleRequestWithoutMI);
  EXPECT_TRUE(msg.Read(&buf));
  EXPECT_TRUE(msg.AddFingerprint());

  ByteBufferWriter buf1;
  EXPECT_TRUE(msg.Write(&buf1));
  EXPECT_TRUE(StunMessage::ValidateFingerprint(buf1.DataView()));
}

// Test that we can remove attribute from a message.
TEST_F(StunTest, RemoveAttribute) {
  StunMessage msg;

  // Removing something that does exist should return nullptr.
  EXPECT_EQ(msg.RemoveAttribute(STUN_ATTR_USERNAME), nullptr);

  {
    auto attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
    attr->CopyBytes(std::to_array<uint8_t>({'k''e''s'}));
    msg.AddAttribute(std::move(attr));
  }

  size_t len = msg.length();
  {
    auto attr = msg.RemoveAttribute(STUN_ATTR_USERNAME);
    ASSERT_NE(attr, nullptr);
    EXPECT_EQ(attr->type(), STUN_ATTR_USERNAME);
    EXPECT_EQ("kes",
              static_cast<StunByteStringAttribute*>(attr.get())->string_view());
    EXPECT_LT(msg.length(), len);
  }

  // Now add same attribute type twice.
  {
    auto attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
    attr->CopyBytes(std::to_array<uint8_t>({'k''e''s'}));
    msg.AddAttribute(std::move(attr));
  }

  {
    auto attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
    attr->CopyBytes(std::to_array<uint8_t>({'k''e''n''t''a'}));
    msg.AddAttribute(std::move(attr));
  }

  // Remove should remove the last added occurrence.
  {
    auto attr = msg.RemoveAttribute(STUN_ATTR_USERNAME);
    ASSERT_NE(attr, nullptr);
    EXPECT_EQ(attr->type(), STUN_ATTR_USERNAME);
    EXPECT_EQ("kenta",
              static_cast<StunByteStringAttribute*>(attr.get())->string_view());
  }

  // Remove should remove the last added occurrence.
  {
    auto attr = msg.RemoveAttribute(STUN_ATTR_USERNAME);
    ASSERT_NE(attr, nullptr);
    EXPECT_EQ(attr->type(), STUN_ATTR_USERNAME);
    EXPECT_EQ("kes",
              static_cast<StunByteStringAttribute*>(attr.get())->string_view());
  }

  // Removing something that does exist should return nullptr.
  EXPECT_EQ(msg.RemoveAttribute(STUN_ATTR_USERNAME), nullptr);
}

// Test that we can remove attribute from a message.
TEST_F(StunTest, ClearAttributes) {
  StunMessage msg;

  auto attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
  attr->CopyBytes(std::to_array<uint8_t>({'k''e''s'}));
  msg.AddAttribute(std::move(attr));
  size_t len = msg.length();

  msg.ClearAttributes();
  EXPECT_EQ(msg.length(), len - /* 3 + 1 byte padding + header */ 8);
  EXPECT_EQ(nullptr, msg.GetByteString(STUN_ATTR_USERNAME));
}

// Test CopyStunAttribute
TEST_F(StunTest, CopyAttribute) {
  ByteBufferWriter buf;
  ByteBufferWriter* buffer_ptrs[] = {&buf, nullptr};
  // Test both with and without supplied ByteBufferWriter.
  for (auto buffer_ptr : buffer_ptrs) {
    {  // Test StunByteStringAttribute.
      auto attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
      attr->CopyBytes(std::to_array<uint8_t>({'k''e''s'}));

      auto copy = CopyStunAttribute(*attr, buffer_ptr);
      ASSERT_EQ(copy->value_type(), STUN_VALUE_BYTE_STRING);
      EXPECT_EQ(
          "kes",
          static_cast<StunByteStringAttribute*>(copy.get())->string_view());
    }

    {  // Test StunAddressAttribute.
      IPAddress test_ip(kIPv6TestAddress2);
      auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
      SocketAddress test_addr(test_ip, kTestMessagePort2);
      addr->SetAddress(test_addr);
      CheckStunAddressAttribute(addr.get(), STUN_ADDRESS_IPV6,
                                kTestMessagePort2, test_ip);

      auto copy = CopyStunAttribute(*addr, buffer_ptr);
      ASSERT_EQ(copy->value_type(), STUN_VALUE_ADDRESS);
      CheckStunAddressAttribute(static_cast<StunAddressAttribute*>(copy.get()),
                                STUN_ADDRESS_IPV6, kTestMessagePort2, test_ip);
    }

    {  // Test StunAddressAttribute.
      IPAddress test_ip(kIPv6TestAddress2);
      auto addr = StunAttribute::CreateAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
      SocketAddress test_addr(test_ip, kTestMessagePort2);
      addr->SetAddress(test_addr);
      CheckStunAddressAttribute(addr.get(), STUN_ADDRESS_IPV6,
                                kTestMessagePort2, test_ip);

      auto copy = CopyStunAttribute(*addr, buffer_ptr);
      ASSERT_EQ(copy->value_type(), STUN_VALUE_ADDRESS);
      CheckStunAddressAttribute(static_cast<StunAddressAttribute*>(copy.get()),
                                STUN_ADDRESS_IPV6, kTestMessagePort2, test_ip);
    }
  }
}

// Test Clone
TEST_F(StunTest, Clone) {
  IceMessage msg(0"0123456789ab");
  {
    auto errorcode = StunAttribute::CreateErrorCode();
    errorcode->SetCode(kTestErrorCode);
    errorcode->SetReason(kTestErrorReason);
    msg.AddAttribute(std::move(errorcode));
  }
  {
    auto bytes2 = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
    bytes2->CopyBytes("abcdefghijkl");
    msg.AddAttribute(std::move(bytes2));
  }
  {
    auto uval2 = StunAttribute::CreateUInt32(STUN_ATTR_RETRANSMIT_COUNT);
    uval2->SetValue(11);
    msg.AddAttribute(std::move(uval2));
  }
  {
    auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
    addr->SetIP(IPAddress(kIPv6TestAddress1));
    addr->SetPort(kTestMessagePort1);
    msg.AddAttribute(std::move(addr));
  }
  auto copy = msg.Clone();
  ASSERT_NE(nullptr, copy.get());

  ByteBufferWriter out1;
  EXPECT_TRUE(msg.Write(&out1));
  ByteBufferWriter out2;
  EXPECT_TRUE(copy->Write(&out2));

  ASSERT_EQ(out1.Length(), out2.Length());
  EXPECT_EQ(0, memcmp(out1.Data(), out2.Data(), out1.Length()));
}

// Test EqualAttributes
TEST_F(StunTest, EqualAttributes) {
  IceMessage msg;
  {
    auto errorcode = StunAttribute::CreateErrorCode();
    errorcode->SetCode(kTestErrorCode);
    errorcode->SetReason(kTestErrorReason);
    msg.AddAttribute(std::move(errorcode));
  }
  {
    auto bytes2 = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
    bytes2->CopyBytes("abcdefghijkl");
    msg.AddAttribute(std::move(bytes2));
  }
  {
    auto uval2 = StunAttribute::CreateUInt32(STUN_ATTR_RETRANSMIT_COUNT);
    uval2->SetValue(11);
    msg.AddAttribute(std::move(uval2));
  }
  {
    auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
    addr->SetIP(IPAddress(kIPv6TestAddress1));
    addr->SetPort(kTestMessagePort1);
    msg.AddAttribute(std::move(addr));
  }
  auto copy = msg.Clone();
  ASSERT_NE(nullptr, copy.get());

  EXPECT_TRUE(copy->EqualAttributes(&msg, [](int /* type */) { return true; }));

  {
    auto attr = StunAttribute::CreateByteString(STUN_ATTR_NONCE);
    attr->CopyBytes("keso");
    msg.AddAttribute(std::move(attr));
    EXPECT_FALSE(
        copy->EqualAttributes(&msg, [](int /* type */) { return true; }));
    EXPECT_TRUE(copy->EqualAttributes(
        &msg, [](int type) { return type != STUN_ATTR_NONCE; }));
  }

  {
    auto attr = StunAttribute::CreateByteString(STUN_ATTR_NONCE);
    attr->CopyBytes("keso");
    copy->AddAttribute(std::move(attr));
    EXPECT_TRUE(
        copy->EqualAttributes(&msg, [](int /* type */) { return true; }));
  }
  {
    copy->RemoveAttribute(STUN_ATTR_NONCE);
    auto attr = StunAttribute::CreateByteString(STUN_ATTR_NONCE);
    attr->CopyBytes("kent");
    copy->AddAttribute(std::move(attr));
    EXPECT_FALSE(
        copy->EqualAttributes(&msg, [](int /* type */) { return true; }));
    EXPECT_TRUE(copy->EqualAttributes(
        &msg, [](int type) { return type != STUN_ATTR_NONCE; }));
  }

  {
    msg.RemoveAttribute(STUN_ATTR_NONCE);
    EXPECT_FALSE(
        copy->EqualAttributes(&msg, [](int /* type */) { return true; }));
    EXPECT_TRUE(copy->EqualAttributes(
        &msg, [](int type) { return type != STUN_ATTR_NONCE; }));
  }
}

TEST_F(StunTest, ReduceTransactionIdIsHostOrderIndependent) {
  const std::string transaction_id = "abcdefghijkl";
  StunMessage message(0, transaction_id);
  uint32_t reduced_transaction_id = message.reduced_transaction_id();
  EXPECT_EQ(reduced_transaction_id, 1835954016u);
}

TEST_F(StunTest, GoogMiscInfo) {
  StunMessage msg(STUN_BINDING_REQUEST, "ABCDEFGHIJKL");
  const size_t size =
      /* msg header */ 20 +
      /* attr header */ 4 +
      /* 3 * 2 rounded to multiple of 4 */ 8;
  auto list =
      StunAttribute::CreateUInt16ListAttribute(STUN_ATTR_GOOG_MISC_INFO);
  list->AddTypeAtIndex(00x1U);
  list->AddTypeAtIndex(30x1000U);
  list->AddTypeAtIndex(20xAB0CU);
  msg.AddAttribute(std::move(list));
  CheckStunHeader(msg, STUN_BINDING_REQUEST, (size - 20));

  ByteBufferWriter out;
  EXPECT_TRUE(msg.Write(&out));
  ASSERT_EQ(size, out.Length());

  size_t read_size = ReadStunMessageTestCase(&msg, out.DataView());
  ASSERT_EQ(read_size + 20, size);
  CheckStunHeader(msg, STUN_BINDING_REQUEST, read_size);
  const StunUInt16ListAttribute* types =
      msg.GetUInt16List(STUN_ATTR_GOOG_MISC_INFO);
  ASSERT_TRUE(types != nullptr);
  EXPECT_EQ(4U, types->Size());
  EXPECT_EQ(0x1U, types->GetType(0));
  EXPECT_EQ(0x0U, types->GetType(1));
  EXPECT_EQ(0x1000U, types->GetType(3));
  EXPECT_EQ(0xAB0CU, types->GetType(2));
}

TEST_F(StunTest, IsStunMethod) {
  int methods[] = {STUN_BINDING_REQUEST};
  EXPECT_TRUE(StunMessage::IsStunMethod(methods, kRfc5769SampleRequest));
}

TEST_F(StunTest, SizeRestrictionOnAttributes) {
  StunMessage msg(STUN_BINDING_REQUEST, "ABCDEFGHIJKL");
  auto long_username = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
  std::string long_string(509'x');
  long_username->CopyBytes(long_string);
  msg.AddAttribute(std::move(long_username));
  ByteBufferWriter out;
  ASSERT_FALSE(msg.Write(&out));
}

TEST_F(StunTest, ValidateMessageIntegrityWithParser) {
  metrics::Reset();  // Ensure counters start from zero.
  // Try the messages from RFC 5769.
  StunMessage message;
  ByteBufferReader reader(kRfc5769SampleRequest);
  EXPECT_TRUE(message.Read(&reader));
  EXPECT_EQ(message.ValidateMessageIntegrity(kRfc5769SampleMsgPassword),
            StunMessage::IntegrityStatus::kIntegrityOk);
  EXPECT_EQ(metrics::NumEvents(
                "WebRTC.Stun.Integrity.Request",
                static_cast<int>(StunMessage::IntegrityStatus::kIntegrityOk)),
            1);
  EXPECT_EQ(message.RevalidateMessageIntegrity("Invalid password"),
            StunMessage::IntegrityStatus::kIntegrityBad);
  EXPECT_EQ(metrics::NumEvents(
                "WebRTC.Stun.Integrity.Request",
                static_cast<int>(StunMessage::IntegrityStatus::kIntegrityBad)),
            1);
  EXPECT_EQ(metrics::NumSamples("WebRTC.Stun.Integrity.Request"), 2);
}

TEST_F(StunTest, ByteStringAsVectorOfUint32) {
  StunMessage message;
  ByteBufferReader reader(kRfc5769SampleRequest);
  EXPECT_TRUE(message.Read(&reader));

  // The username attribute has a length which is not a multiple of 4
  // so getting a uint32_t vector from it should fail.
  const StunByteStringAttribute* username =
      message.GetByteString(STUN_ATTR_USERNAME);
  ASSERT_TRUE(username);
  EXPECT_FALSE(username->GetUInt32Vector());

  // message-integrity is 20 bytes which makes it usable as a test case.
  const StunByteStringAttribute* integrity =
      message.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY);
  ASSERT_TRUE(integrity);
  std::optional<std::vector<uint32_t>> integrity_vector =
      integrity->GetUInt32Vector();
  ASSERT_TRUE(integrity_vector);
  EXPECT_EQ(5U, integrity_vector->size());
  // Taken from the RFC 5769 sample request.
  std::vector<uint32_t> expected_integrity_vector = {
      0x9aeaa70c, 0xbfd8cb56, 0x781ef2b5, 0xb2d3f249, 0xc1b571a2};
  EXPECT_THAT(*integrity_vector, ElementsAreArray(expected_integrity_vector));
}

}  // namespace webrtc

Messung V0.5 in Prozent
C=85 H=88 G=86

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.70Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-27) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=655579