// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "quiche/quic/core/quic_utils.h"

#include <algorithm>
#include <cstdint>
#include <cstring>
#include <limits>
#include <string>

#include "absl/base/macros.h"
#include "absl/base/optimization.h"
#include "absl/numeric/int128.h"
#include "absl/strings/string_view.h"
#include "openssl/sha.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/quic/platform/api/quic_flag_utils.h"
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_endian.h"
#include "quiche/common/quiche_mem_slice.h"

namespace quic {
namespace {

// We know that >= GCC 4.8 and Clang have a __uint128_t intrinsic. Other
// compilers don't necessarily, notably MSVC.
#if defined(__x86_64__) &&                                         \
    ((defined(__GNUC__) &&                                         \
      (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \
     defined(__clang__))
#define QUIC_UTIL_HAS_UINT128 1
#endif

#ifdef QUIC_UTIL_HAS_UINT128
absl::uint128 IncrementalHashFast(absl::uint128 uhash, absl::string_view data) {
  // This code ends up faster than the naive implementation for 2 reasons:
  // 1. absl::uint128 is sufficiently complicated that the compiler
  //    cannot transform the multiplication by kPrime into a shift-multiply-add;
  //    it has go through all of the instructions for a 128-bit multiply.
  // 2. Because there are so fewer instructions (around 13), the hot loop fits
  //    nicely in the instruction queue of many Intel CPUs.
  // kPrime = 309485009821345068724781371
  static const absl::uint128 kPrime =
      (static_cast<absl::uint128>(16777216) << 64) + 315;
  auto hi = absl::Uint128High64(uhash);
  auto lo = absl::Uint128Low64(uhash);
  absl::uint128 xhash = (static_cast<absl::uint128>(hi) << 64) + lo;
  const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data());
  for (size_t i = 0; i < data.length(); ++i) {
    xhash = (xhash ^ static_cast<uint32_t>(octets[i])) * kPrime;
  }
  return absl::MakeUint128(absl::Uint128High64(xhash),
                           absl::Uint128Low64(xhash));
}
#endif

#ifndef QUIC_UTIL_HAS_UINT128
// Slow implementation of IncrementalHash. In practice, only used by Chromium.
absl::uint128 IncrementalHashSlow(absl::uint128 hash, absl::string_view data) {
  // kPrime = 309485009821345068724781371
  static const absl::uint128 kPrime = absl::MakeUint128(16777216, 315);
  const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data());
  for (size_t i = 0; i < data.length(); ++i) {
    hash = hash ^ absl::MakeUint128(0, octets[i]);
    hash = hash * kPrime;
  }
  return hash;
}
#endif

absl::uint128 IncrementalHash(absl::uint128 hash, absl::string_view data) {
#ifdef QUIC_UTIL_HAS_UINT128
  return IncrementalHashFast(hash, data);
#else
  return IncrementalHashSlow(hash, data);
#endif
}

}  // namespace

// static
uint64_t QuicUtils::FNV1a_64_Hash(absl::string_view data) {
  static const uint64_t kOffset = UINT64_C(14695981039346656037);
  static const uint64_t kPrime = UINT64_C(1099511628211);

  const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data());

  uint64_t hash = kOffset;

  for (size_t i = 0; i < data.length(); ++i) {
    hash = hash ^ octets[i];
    hash = hash * kPrime;
  }

  return hash;
}

// static
absl::uint128 QuicUtils::FNV1a_128_Hash(absl::string_view data) {
  return FNV1a_128_Hash_Three(data, absl::string_view(), absl::string_view());
}

// static
absl::uint128 QuicUtils::FNV1a_128_Hash_Two(absl::string_view data1,
                                            absl::string_view data2) {
  return FNV1a_128_Hash_Three(data1, data2, absl::string_view());
}

// static
absl::uint128 QuicUtils::FNV1a_128_Hash_Three(absl::string_view data1,
                                              absl::string_view data2,
                                              absl::string_view data3) {
  // The two constants are defined as part of the hash algorithm.
  // see http://www.isthe.com/chongo/tech/comp/fnv/
  // kOffset = 144066263297769815596495629667062367629
  const absl::uint128 kOffset = absl::MakeUint128(
      UINT64_C(7809847782465536322), UINT64_C(7113472399480571277));

  absl::uint128 hash = IncrementalHash(kOffset, data1);
  if (data2.empty()) {
    return hash;
  }

  hash = IncrementalHash(hash, data2);
  if (data3.empty()) {
    return hash;
  }
  return IncrementalHash(hash, data3);
}

// static
void QuicUtils::SerializeUint128Short(absl::uint128 v, uint8_t* out) {
  const uint64_t lo = absl::Uint128Low64(v);
  const uint64_t hi = absl::Uint128High64(v);
  // This assumes that the system is little-endian.
  memcpy(out, &lo, sizeof(lo));
  memcpy(out + sizeof(lo), &hi, sizeof(hi) / 2);
}

#define RETURN_STRING_LITERAL(x) \
  case x:                        \
    return #x;

std::string QuicUtils::AddressChangeTypeToString(AddressChangeType type) {
  switch (type) {
    RETURN_STRING_LITERAL(NO_CHANGE);
    RETURN_STRING_LITERAL(PORT_CHANGE);
    RETURN_STRING_LITERAL(IPV4_SUBNET_CHANGE);
    RETURN_STRING_LITERAL(IPV4_TO_IPV6_CHANGE);
    RETURN_STRING_LITERAL(IPV6_TO_IPV4_CHANGE);
    RETURN_STRING_LITERAL(IPV6_TO_IPV6_CHANGE);
    RETURN_STRING_LITERAL(IPV4_TO_IPV4_CHANGE);
  }
  return "INVALID_ADDRESS_CHANGE_TYPE";
}

const char* QuicUtils::SentPacketStateToString(SentPacketState state) {
  switch (state) {
    RETURN_STRING_LITERAL(OUTSTANDING);
    RETURN_STRING_LITERAL(NEVER_SENT);
    RETURN_STRING_LITERAL(ACKED);
    RETURN_STRING_LITERAL(UNACKABLE);
    RETURN_STRING_LITERAL(NEUTERED);
    RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMITTED);
    RETURN_STRING_LITERAL(LOST);
    RETURN_STRING_LITERAL(PTO_RETRANSMITTED);
    RETURN_STRING_LITERAL(NOT_CONTRIBUTING_RTT);
  }
  return "INVALID_SENT_PACKET_STATE";
}

// static
const char* QuicUtils::QuicLongHeaderTypetoString(QuicLongHeaderType type) {
  switch (type) {
    RETURN_STRING_LITERAL(VERSION_NEGOTIATION);
    RETURN_STRING_LITERAL(INITIAL);
    RETURN_STRING_LITERAL(RETRY);
    RETURN_STRING_LITERAL(HANDSHAKE);
    RETURN_STRING_LITERAL(ZERO_RTT_PROTECTED);
    default:
      return "INVALID_PACKET_TYPE";
  }
}

// static
const char* QuicUtils::AckResultToString(AckResult result) {
  switch (result) {
    RETURN_STRING_LITERAL(PACKETS_NEWLY_ACKED);
    RETURN_STRING_LITERAL(NO_PACKETS_NEWLY_ACKED);
    RETURN_STRING_LITERAL(UNSENT_PACKETS_ACKED);
    RETURN_STRING_LITERAL(UNACKABLE_PACKETS_ACKED);
    RETURN_STRING_LITERAL(PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE);
  }
  return "INVALID_ACK_RESULT";
}

// static
AddressChangeType QuicUtils::DetermineAddressChangeType(
    const QuicSocketAddress& old_address,
    const QuicSocketAddress& new_address) {
  if (!old_address.IsInitialized() || !new_address.IsInitialized() ||
      old_address == new_address) {
    return NO_CHANGE;
  }

  if (old_address.host() == new_address.host()) {
    return PORT_CHANGE;
  }

  bool old_ip_is_ipv4 = old_address.host().IsIPv4() ? true : false;
  bool migrating_ip_is_ipv4 = new_address.host().IsIPv4() ? true : false;
  if (old_ip_is_ipv4 && !migrating_ip_is_ipv4) {
    return IPV4_TO_IPV6_CHANGE;
  }

  if (!old_ip_is_ipv4) {
    return migrating_ip_is_ipv4 ? IPV6_TO_IPV4_CHANGE : IPV6_TO_IPV6_CHANGE;
  }

  const int kSubnetMaskLength = 24;
  if (old_address.host().InSameSubnet(new_address.host(), kSubnetMaskLength)) {
    // Subnet part does not change (here, we use /24), which is considered to be
    // caused by NATs.
    return IPV4_SUBNET_CHANGE;
  }

  return IPV4_TO_IPV4_CHANGE;
}

// static
bool QuicUtils::IsAckable(SentPacketState state) {
  return state != NEVER_SENT && state != ACKED && state != UNACKABLE;
}

// static
bool QuicUtils::IsRetransmittableFrame(QuicFrameType type) {
  switch (type) {
    case ACK_FRAME:
    case PADDING_FRAME:
    case STOP_WAITING_FRAME:
    case MTU_DISCOVERY_FRAME:
    case PATH_CHALLENGE_FRAME:
    case PATH_RESPONSE_FRAME:
    case IMMEDIATE_ACK_FRAME:
      return false;
    default:
      return true;
  }
}

// static
bool QuicUtils::IsHandshakeFrame(const QuicFrame& frame,
                                 QuicTransportVersion transport_version) {
  if (!QuicVersionUsesCryptoFrames(transport_version)) {
    return frame.type == STREAM_FRAME &&
           frame.stream_frame.stream_id == GetCryptoStreamId(transport_version);
  } else {
    return frame.type == CRYPTO_FRAME;
  }
}

// static
bool QuicUtils::ContainsFrameType(const QuicFrames& frames,
                                  QuicFrameType type) {
  for (const QuicFrame& frame : frames) {
    if (frame.type == type) {
      return true;
    }
  }
  return false;
}

// static
SentPacketState QuicUtils::RetransmissionTypeToPacketState(
    TransmissionType retransmission_type) {
  switch (retransmission_type) {
    case ALL_ZERO_RTT_RETRANSMISSION:
      return UNACKABLE;
    case HANDSHAKE_RETRANSMISSION:
      return HANDSHAKE_RETRANSMITTED;
    case LOSS_RETRANSMISSION:
      return LOST;
    case PTO_RETRANSMISSION:
      return PTO_RETRANSMITTED;
    case PATH_RETRANSMISSION:
      return NOT_CONTRIBUTING_RTT;
    case ALL_INITIAL_RETRANSMISSION:
      return UNACKABLE;
    default:
      QUIC_BUG(quic_bug_10839_2)
          << retransmission_type << " is not a retransmission_type";
      return UNACKABLE;
  }
}

// static
bool QuicUtils::IsIetfPacketHeader(uint8_t first_byte) {
  return (first_byte & FLAGS_LONG_HEADER) || (first_byte & FLAGS_FIXED_BIT) ||
         !(first_byte & FLAGS_DEMULTIPLEXING_BIT);
}

// static
bool QuicUtils::IsIetfPacketShortHeader(uint8_t first_byte) {
  return IsIetfPacketHeader(first_byte) && !(first_byte & FLAGS_LONG_HEADER);
}

// static
QuicStreamId QuicUtils::GetInvalidStreamId(QuicTransportVersion version) {
  return VersionHasIetfQuicFrames(version)
             ? std::numeric_limits<QuicStreamId>::max()
             : 0;
}

// static
QuicStreamId QuicUtils::GetCryptoStreamId(QuicTransportVersion version) {
  QUIC_BUG_IF(quic_bug_12982_1, QuicVersionUsesCryptoFrames(version))
      << "CRYPTO data aren't in stream frames; they have no stream ID.";
  return QuicVersionUsesCryptoFrames(version) ? GetInvalidStreamId(version) : 1;
}

// static
bool QuicUtils::IsCryptoStreamId(QuicTransportVersion version,
                                 QuicStreamId stream_id) {
  if (QuicVersionUsesCryptoFrames(version)) {
    return false;
  }
  return stream_id == GetCryptoStreamId(version);
}

// static
QuicStreamId QuicUtils::GetHeadersStreamId(QuicTransportVersion version) {
  QUICHE_DCHECK(!VersionUsesHttp3(version));
  return GetFirstBidirectionalStreamId(version, Perspective::IS_CLIENT);
}

// static
bool QuicUtils::IsClientInitiatedStreamId(QuicTransportVersion version,
                                          QuicStreamId id) {
  if (id == GetInvalidStreamId(version)) {
    return false;
  }
  return VersionHasIetfQuicFrames(version) ? id % 2 == 0 : id % 2 != 0;
}

// static
bool QuicUtils::IsServerInitiatedStreamId(QuicTransportVersion version,
                                          QuicStreamId id) {
  if (id == GetInvalidStreamId(version)) {
    return false;
  }
  return VersionHasIetfQuicFrames(version) ? id % 2 != 0 : id % 2 == 0;
}

// static
bool QuicUtils::IsOutgoingStreamId(ParsedQuicVersion version, QuicStreamId id,
                                   Perspective perspective) {
  // Streams are outgoing streams, iff:
  // - we are the server and the stream is server-initiated
  // - we are the client and the stream is client-initiated.
  const bool perspective_is_server = perspective == Perspective::IS_SERVER;
  const bool stream_is_server =
      QuicUtils::IsServerInitiatedStreamId(version.transport_version, id);
  return perspective_is_server == stream_is_server;
}

// static
bool QuicUtils::IsBidirectionalStreamId(QuicStreamId id,
                                        ParsedQuicVersion version) {
  QUICHE_DCHECK(version.HasIetfQuicFrames());
  return id % 4 < 2;
}

// static
StreamType QuicUtils::GetStreamType(QuicStreamId id, Perspective perspective,
                                    bool peer_initiated,
                                    ParsedQuicVersion version) {
  QUICHE_DCHECK(version.HasIetfQuicFrames());
  if (IsBidirectionalStreamId(id, version)) {
    return BIDIRECTIONAL;
  }

  if (peer_initiated) {
    if (perspective == Perspective::IS_SERVER) {
      QUICHE_DCHECK_EQ(2u, id % 4);
    } else {
      QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective);
      QUICHE_DCHECK_EQ(3u, id % 4);
    }
    return READ_UNIDIRECTIONAL;
  }

  if (perspective == Perspective::IS_SERVER) {
    QUICHE_DCHECK_EQ(3u, id % 4);
  } else {
    QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective);
    QUICHE_DCHECK_EQ(2u, id % 4);
  }
  return WRITE_UNIDIRECTIONAL;
}

// static
QuicStreamId QuicUtils::StreamIdDelta(QuicTransportVersion version) {
  return VersionHasIetfQuicFrames(version) ? 4 : 2;
}

// static
QuicStreamId QuicUtils::GetFirstBidirectionalStreamId(
    QuicTransportVersion version, Perspective perspective) {
  if (VersionHasIetfQuicFrames(version)) {
    return perspective == Perspective::IS_CLIENT ? 0 : 1;
  } else if (QuicVersionUsesCryptoFrames(version)) {
    return perspective == Perspective::IS_CLIENT ? 1 : 2;
  }
  return perspective == Perspective::IS_CLIENT ? 3 : 2;
}

// static
QuicStreamId QuicUtils::GetFirstUnidirectionalStreamId(
    QuicTransportVersion version, Perspective perspective) {
  if (VersionHasIetfQuicFrames(version)) {
    return perspective == Perspective::IS_CLIENT ? 2 : 3;
  } else if (QuicVersionUsesCryptoFrames(version)) {
    return perspective == Perspective::IS_CLIENT ? 1 : 2;
  }
  return perspective == Perspective::IS_CLIENT ? 3 : 2;
}

// static
QuicStreamId QuicUtils::GetMaxClientInitiatedBidirectionalStreamId(
    QuicTransportVersion version) {
  if (VersionHasIetfQuicFrames(version)) {
    // Client initiated bidirectional streams have stream IDs divisible by 4.
    return std::numeric_limits<QuicStreamId>::max() - 3;
  }

  // Client initiated bidirectional streams have odd stream IDs.
  return std::numeric_limits<QuicStreamId>::max();
}

// static
QuicConnectionId QuicUtils::CreateRandomConnectionId() {
  return CreateRandomConnectionId(kQuicDefaultConnectionIdLength,
                                  QuicRandom::GetInstance());
}

// static
QuicConnectionId QuicUtils::CreateRandomConnectionId(QuicRandom* random) {
  return CreateRandomConnectionId(kQuicDefaultConnectionIdLength, random);
}
// static
QuicConnectionId QuicUtils::CreateRandomConnectionId(
    uint8_t connection_id_length) {
  return CreateRandomConnectionId(connection_id_length,
                                  QuicRandom::GetInstance());
}

// static
QuicConnectionId QuicUtils::CreateRandomConnectionId(
    uint8_t connection_id_length, QuicRandom* random) {
  QuicConnectionId connection_id;
  connection_id.set_length(connection_id_length);
  if (connection_id.length() > 0) {
    random->RandBytes(connection_id.mutable_data(), connection_id.length());
  }
  return connection_id;
}

// static
QuicConnectionId QuicUtils::CreateZeroConnectionId(
    QuicTransportVersion version) {
  if (!VersionAllowsVariableLengthConnectionIds(version)) {
    char connection_id_bytes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    return QuicConnectionId(static_cast<char*>(connection_id_bytes),
                            ABSL_ARRAYSIZE(connection_id_bytes));
  }
  return EmptyQuicConnectionId();
}

// static
bool QuicUtils::IsConnectionIdLengthValidForVersion(
    size_t connection_id_length, QuicTransportVersion transport_version) {
  // No version of QUIC can support lengths that do not fit in an uint8_t.
  if (connection_id_length >
      static_cast<size_t>(std::numeric_limits<uint8_t>::max())) {
    return false;
  }

  if (transport_version == QUIC_VERSION_UNSUPPORTED ||
      transport_version == QUIC_VERSION_RESERVED_FOR_NEGOTIATION) {
    // Unknown versions could allow connection ID lengths up to 255.
    return true;
  }

  const uint8_t connection_id_length8 =
      static_cast<uint8_t>(connection_id_length);
  // Versions that do not support variable lengths only support length 8.
  if (!VersionAllowsVariableLengthConnectionIds(transport_version)) {
    return connection_id_length8 == kQuicDefaultConnectionIdLength;
  }
  return connection_id_length8 <= kQuicMaxConnectionIdWithLengthPrefixLength;
}

// static
bool QuicUtils::IsConnectionIdValidForVersion(
    const QuicConnectionId& connection_id,
    QuicTransportVersion transport_version) {
  return IsConnectionIdLengthValidForVersion(connection_id.length(),
                                             transport_version);
}

StatelessResetToken QuicUtils::GenerateStatelessResetToken(
    const QuicConnectionId& connection_id) {
  static_assert(sizeof(absl::uint128) == sizeof(StatelessResetToken),
                "bad size");
  static_assert(alignof(absl::uint128) >= alignof(StatelessResetToken),
                "bad alignment");
  absl::uint128 hash = FNV1a_128_Hash(
      absl::string_view(connection_id.data(), connection_id.length()));
  return *reinterpret_cast<StatelessResetToken*>(&hash);
}

// static
QuicStreamCount QuicUtils::GetMaxStreamCount() {
  return (kMaxQuicStreamCount >> 2) + 1;
}

// static
PacketNumberSpace QuicUtils::GetPacketNumberSpace(
    EncryptionLevel encryption_level) {
  switch (encryption_level) {
    case ENCRYPTION_INITIAL:
      return INITIAL_DATA;
    case ENCRYPTION_HANDSHAKE:
      return HANDSHAKE_DATA;
    case ENCRYPTION_ZERO_RTT:
    case ENCRYPTION_FORWARD_SECURE:
      return APPLICATION_DATA;
    default:
      QUIC_BUG(quic_bug_10839_3)
          << "Try to get packet number space of encryption level: "
          << encryption_level;
      return NUM_PACKET_NUMBER_SPACES;
  }
}

// static
EncryptionLevel QuicUtils::GetEncryptionLevelToSendAckofSpace(
    PacketNumberSpace packet_number_space) {
  switch (packet_number_space) {
    case INITIAL_DATA:
      return ENCRYPTION_INITIAL;
    case HANDSHAKE_DATA:
      return ENCRYPTION_HANDSHAKE;
    case APPLICATION_DATA:
      return ENCRYPTION_FORWARD_SECURE;
    default:
      QUICHE_DCHECK(false);
      return NUM_ENCRYPTION_LEVELS;
  }
}

// static
bool QuicUtils::IsProbingFrame(QuicFrameType type) {
  switch (type) {
    case PATH_CHALLENGE_FRAME:
    case PATH_RESPONSE_FRAME:
    case NEW_CONNECTION_ID_FRAME:
    case PADDING_FRAME:
      return true;
    default:
      return false;
  }
}

// static
bool QuicUtils::IsAckElicitingFrame(QuicFrameType type) {
  switch (type) {
    case PADDING_FRAME:
    case STOP_WAITING_FRAME:
    case ACK_FRAME:
    case CONNECTION_CLOSE_FRAME:
      return false;
    default:
      return true;
  }
}

// static
bool QuicUtils::AreStatelessResetTokensEqual(
    const StatelessResetToken& token1, const StatelessResetToken& token2) {
  char byte = 0;
  for (size_t i = 0; i < kStatelessResetTokenLength; i++) {
    // This avoids compiler optimizations that could make us stop comparing
    // after we find a byte that doesn't match.
    byte |= (token1[i] ^ token2[i]);
  }
  return byte == 0;
}

bool IsValidWebTransportSessionId(WebTransportSessionId id,
                                  ParsedQuicVersion version) {
  QUICHE_DCHECK(version.UsesHttp3());
  return (id <= std::numeric_limits<QuicStreamId>::max()) &&
         QuicUtils::IsBidirectionalStreamId(id, version) &&
         QuicUtils::IsClientInitiatedStreamId(version.transport_version, id);
}

QuicByteCount MemSliceSpanTotalSize(absl::Span<quiche::QuicheMemSlice> span) {
  QuicByteCount total = 0;
  for (const quiche::QuicheMemSlice& slice : span) {
    total += slice.length();
  }
  return total;
}

absl::string_view PosixBasename(absl::string_view path) {
  constexpr char kPathSeparator = '/';
  size_t pos = path.find_last_of(kPathSeparator);

  // Handle the case with no `kPathSeparator` in `path`.
  if (pos == absl::string_view::npos) {
    return path;
  }

  // Handle the case with a single leading `kPathSeparator` in `path`.
  if (pos == 0) {
    return absl::ClippedSubstr(path, 1);
  }

  return absl::ClippedSubstr(path, pos + 1);
}

std::string RawSha256(absl::string_view input) {
  std::string raw_hash;
  raw_hash.resize(SHA256_DIGEST_LENGTH);
  SHA256(reinterpret_cast<const uint8_t*>(input.data()), input.size(),
         reinterpret_cast<uint8_t*>(&raw_hash[0]));
  return raw_hash;
}

#undef RETURN_STRING_LITERAL  // undef for jumbo builds
}  // namespace quic
