// 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_packets.h"

#include <algorithm>
#include <memory>
#include <utility>

#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/platform/api/quic_flags.h"

namespace quic {

QuicConnectionId GetServerConnectionIdAsRecipient(
    const QuicPacketHeader& header, Perspective perspective) {
  if (perspective == Perspective::IS_SERVER) {
    return header.destination_connection_id;
  }
  return header.source_connection_id;
}

QuicConnectionId GetClientConnectionIdAsRecipient(
    const QuicPacketHeader& header, Perspective perspective) {
  if (perspective == Perspective::IS_CLIENT) {
    return header.destination_connection_id;
  }
  return header.source_connection_id;
}

QuicConnectionId GetServerConnectionIdAsSender(const QuicPacketHeader& header,
                                               Perspective perspective) {
  if (perspective == Perspective::IS_CLIENT) {
    return header.destination_connection_id;
  }
  return header.source_connection_id;
}

QuicConnectionIdIncluded GetServerConnectionIdIncludedAsSender(
    const QuicPacketHeader& header, Perspective perspective) {
  if (perspective == Perspective::IS_CLIENT) {
    return header.destination_connection_id_included;
  }
  return header.source_connection_id_included;
}

QuicConnectionId GetClientConnectionIdAsSender(const QuicPacketHeader& header,
                                               Perspective perspective) {
  if (perspective == Perspective::IS_CLIENT) {
    return header.source_connection_id;
  }
  return header.destination_connection_id;
}

QuicConnectionIdIncluded GetClientConnectionIdIncludedAsSender(
    const QuicPacketHeader& header, Perspective perspective) {
  if (perspective == Perspective::IS_CLIENT) {
    return header.source_connection_id_included;
  }
  return header.destination_connection_id_included;
}

uint8_t GetIncludedConnectionIdLength(
    QuicConnectionId connection_id,
    QuicConnectionIdIncluded connection_id_included) {
  QUICHE_DCHECK(connection_id_included == CONNECTION_ID_PRESENT ||
                connection_id_included == CONNECTION_ID_ABSENT);
  return connection_id_included == CONNECTION_ID_PRESENT
             ? connection_id.length()
             : 0;
}

uint8_t GetIncludedDestinationConnectionIdLength(
    const QuicPacketHeader& header) {
  return GetIncludedConnectionIdLength(
      header.destination_connection_id,
      header.destination_connection_id_included);
}

uint8_t GetIncludedSourceConnectionIdLength(const QuicPacketHeader& header) {
  return GetIncludedConnectionIdLength(header.source_connection_id,
                                       header.source_connection_id_included);
}

size_t GetPacketHeaderSize(QuicTransportVersion version,
                           const QuicPacketHeader& header) {
  return GetPacketHeaderSize(
      version, GetIncludedDestinationConnectionIdLength(header),
      GetIncludedSourceConnectionIdLength(header), header.version_flag,
      header.nonce != nullptr, header.packet_number_length,
      header.retry_token_length_length, header.retry_token.length(),
      header.length_length);
}

size_t GetPacketHeaderSize(
    QuicTransportVersion version, uint8_t destination_connection_id_length,
    uint8_t source_connection_id_length, bool include_version,
    bool include_diversification_nonce,
    QuicPacketNumberLength packet_number_length,
    quiche::QuicheVariableLengthIntegerLength retry_token_length_length,
    QuicByteCount retry_token_length,
    quiche::QuicheVariableLengthIntegerLength length_length) {
  if (include_version) {
    // Long header.
    size_t size = kPacketHeaderTypeSize + kConnectionIdLengthSize +
                  destination_connection_id_length +
                  source_connection_id_length + packet_number_length +
                  kQuicVersionSize;
    if (include_diversification_nonce) {
      size += kDiversificationNonceSize;
    }
    if (VersionHasLengthPrefixedConnectionIds(version)) {
      size += kConnectionIdLengthSize;
    }
    QUICHE_DCHECK(
        QuicVersionHasLongHeaderLengths(version) ||
        retry_token_length_length + retry_token_length + length_length == 0);
    if (QuicVersionHasLongHeaderLengths(version)) {
      size += retry_token_length_length + retry_token_length + length_length;
    }
    return size;
  }
  // Short header.
  return kPacketHeaderTypeSize + destination_connection_id_length +
         packet_number_length;
}

size_t GetStartOfEncryptedData(QuicTransportVersion version,
                               const QuicPacketHeader& header) {
  return GetPacketHeaderSize(version, header);
}

size_t GetStartOfEncryptedData(
    QuicTransportVersion version, uint8_t destination_connection_id_length,
    uint8_t source_connection_id_length, bool include_version,
    bool include_diversification_nonce,
    QuicPacketNumberLength packet_number_length,
    quiche::QuicheVariableLengthIntegerLength retry_token_length_length,
    QuicByteCount retry_token_length,
    quiche::QuicheVariableLengthIntegerLength length_length) {
  // Encryption starts before private flags.
  return GetPacketHeaderSize(
      version, destination_connection_id_length, source_connection_id_length,
      include_version, include_diversification_nonce, packet_number_length,
      retry_token_length_length, retry_token_length, length_length);
}

QuicPacketHeader::QuicPacketHeader()
    : destination_connection_id(EmptyQuicConnectionId()),
      destination_connection_id_included(CONNECTION_ID_PRESENT),
      source_connection_id(EmptyQuicConnectionId()),
      source_connection_id_included(CONNECTION_ID_ABSENT),
      reset_flag(false),
      version_flag(false),
      has_possible_stateless_reset_token(false),
      packet_number_length(PACKET_4BYTE_PACKET_NUMBER),
      type_byte(0),
      version(UnsupportedQuicVersion()),
      nonce(nullptr),
      form(GOOGLE_QUIC_PACKET),
      long_packet_type(INITIAL),
      possible_stateless_reset_token({}),
      retry_token_length_length(quiche::VARIABLE_LENGTH_INTEGER_LENGTH_0),
      retry_token(absl::string_view()),
      length_length(quiche::VARIABLE_LENGTH_INTEGER_LENGTH_0),
      remaining_packet_length(0) {}

QuicPacketHeader::QuicPacketHeader(const QuicPacketHeader& other) = default;

QuicPacketHeader::~QuicPacketHeader() {}

QuicPacketHeader& QuicPacketHeader::operator=(const QuicPacketHeader& other) =
    default;

QuicPublicResetPacket::QuicPublicResetPacket()
    : connection_id(EmptyQuicConnectionId()), nonce_proof(0) {}

QuicPublicResetPacket::QuicPublicResetPacket(QuicConnectionId connection_id)
    : connection_id(connection_id), nonce_proof(0) {}

QuicVersionNegotiationPacket::QuicVersionNegotiationPacket()
    : connection_id(EmptyQuicConnectionId()) {}

QuicVersionNegotiationPacket::QuicVersionNegotiationPacket(
    QuicConnectionId connection_id)
    : connection_id(connection_id) {}

QuicVersionNegotiationPacket::QuicVersionNegotiationPacket(
    const QuicVersionNegotiationPacket& other) = default;

QuicVersionNegotiationPacket::~QuicVersionNegotiationPacket() {}

QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket()
    : stateless_reset_token({}) {}

QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket(
    const QuicPacketHeader& header, StatelessResetToken token)
    : header(header), stateless_reset_token(token) {}

QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket(
    const QuicIetfStatelessResetPacket& other) = default;

QuicIetfStatelessResetPacket::~QuicIetfStatelessResetPacket() {}

std::ostream& operator<<(std::ostream& os, const QuicPacketHeader& header) {
  os << "{ destination_connection_id: " << header.destination_connection_id
     << " ("
     << (header.destination_connection_id_included == CONNECTION_ID_PRESENT
             ? "present"
             : "absent")
     << "), source_connection_id: " << header.source_connection_id << " ("
     << (header.source_connection_id_included == CONNECTION_ID_PRESENT
             ? "present"
             : "absent")
     << "), packet_number_length: "
     << static_cast<int>(header.packet_number_length)
     << ", reset_flag: " << header.reset_flag
     << ", version_flag: " << header.version_flag;
  if (header.version_flag) {
    os << ", version: " << ParsedQuicVersionToString(header.version);
    if (header.long_packet_type != INVALID_PACKET_TYPE) {
      os << ", long_packet_type: "
         << QuicUtils::QuicLongHeaderTypetoString(header.long_packet_type);
    }
    if (header.retry_token_length_length !=
        quiche::VARIABLE_LENGTH_INTEGER_LENGTH_0) {
      os << ", retry_token_length_length: "
         << static_cast<int>(header.retry_token_length_length);
    }
    if (header.retry_token.length() != 0) {
      os << ", retry_token_length: " << header.retry_token.length();
    }
    if (header.length_length != quiche::VARIABLE_LENGTH_INTEGER_LENGTH_0) {
      os << ", length_length: " << static_cast<int>(header.length_length);
    }
    if (header.remaining_packet_length != 0) {
      os << ", remaining_packet_length: " << header.remaining_packet_length;
    }
  }
  if (header.nonce != nullptr) {
    os << ", diversification_nonce: "
       << absl::BytesToHexString(
              absl::string_view(header.nonce->data(), header.nonce->size()));
  }
  os << ", packet_number: " << header.packet_number << " }\n";
  return os;
}

QuicData::QuicData(const char* buffer, size_t length)
    : buffer_(buffer), length_(length), owns_buffer_(false) {}

QuicData::QuicData(const char* buffer, size_t length, bool owns_buffer)
    : buffer_(buffer), length_(length), owns_buffer_(owns_buffer) {}

QuicData::QuicData(absl::string_view packet_data)
    : buffer_(packet_data.data()),
      length_(packet_data.length()),
      owns_buffer_(false) {}

QuicData::~QuicData() {
  if (owns_buffer_) {
    delete[] const_cast<char*>(buffer_);
  }
}

QuicPacket::QuicPacket(
    char* buffer, size_t length, bool owns_buffer,
    uint8_t destination_connection_id_length,
    uint8_t source_connection_id_length, bool includes_version,
    bool includes_diversification_nonce,
    QuicPacketNumberLength packet_number_length,
    quiche::QuicheVariableLengthIntegerLength retry_token_length_length,
    QuicByteCount retry_token_length,
    quiche::QuicheVariableLengthIntegerLength length_length)
    : QuicData(buffer, length, owns_buffer),
      buffer_(buffer),
      destination_connection_id_length_(destination_connection_id_length),
      source_connection_id_length_(source_connection_id_length),
      includes_version_(includes_version),
      includes_diversification_nonce_(includes_diversification_nonce),
      packet_number_length_(packet_number_length),
      retry_token_length_length_(retry_token_length_length),
      retry_token_length_(retry_token_length),
      length_length_(length_length) {}

QuicPacket::QuicPacket(QuicTransportVersion /*version*/, char* buffer,
                       size_t length, bool owns_buffer,
                       const QuicPacketHeader& header)
    : QuicPacket(buffer, length, owns_buffer,
                 GetIncludedDestinationConnectionIdLength(header),
                 GetIncludedSourceConnectionIdLength(header),
                 header.version_flag, header.nonce != nullptr,
                 header.packet_number_length, header.retry_token_length_length,
                 header.retry_token.length(), header.length_length) {}

QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer, size_t length)
    : QuicData(buffer, length) {}

QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer, size_t length,
                                         bool owns_buffer)
    : QuicData(buffer, length, owns_buffer) {}

QuicEncryptedPacket::QuicEncryptedPacket(absl::string_view data)
    : QuicData(data) {}

std::unique_ptr<QuicEncryptedPacket> QuicEncryptedPacket::Clone() const {
  char* buffer = new char[this->length()];
  std::copy(this->data(), this->data() + this->length(), buffer);
  return std::make_unique<QuicEncryptedPacket>(buffer, this->length(), true);
}

std::ostream& operator<<(std::ostream& os, const QuicEncryptedPacket& s) {
  os << s.length() << "-byte data";
  return os;
}

QuicReceivedPacket::QuicReceivedPacket(const char* buffer, size_t length,
                                       QuicTime receipt_time)
    : QuicReceivedPacket(buffer, length, receipt_time,
                         false /* owns_buffer */) {}

QuicReceivedPacket::QuicReceivedPacket(const char* buffer, size_t length,
                                       QuicTime receipt_time, bool owns_buffer)
    : QuicReceivedPacket(buffer, length, receipt_time, owns_buffer, 0 /* ttl */,
                         true /* ttl_valid */) {}

QuicReceivedPacket::QuicReceivedPacket(const char* buffer, size_t length,
                                       QuicTime receipt_time, bool owns_buffer,
                                       int ttl, bool ttl_valid)
    : quic::QuicReceivedPacket(buffer, length, receipt_time, owns_buffer, ttl,
                               ttl_valid, nullptr /* packet_headers */,
                               0 /* headers_length */,
                               false /* owns_header_buffer */, ECN_NOT_ECT) {}

QuicReceivedPacket::QuicReceivedPacket(const char* buffer, size_t length,
                                       QuicTime receipt_time, bool owns_buffer,
                                       int ttl, bool ttl_valid,
                                       char* packet_headers,
                                       size_t headers_length,
                                       bool owns_header_buffer)
    : quic::QuicReceivedPacket(buffer, length, receipt_time, owns_buffer, ttl,
                               ttl_valid, packet_headers, headers_length,
                               owns_header_buffer, ECN_NOT_ECT) {}

QuicReceivedPacket::QuicReceivedPacket(
    const char* buffer, size_t length, QuicTime receipt_time, bool owns_buffer,
    int ttl, bool ttl_valid, char* packet_headers, size_t headers_length,
    bool owns_header_buffer, QuicEcnCodepoint ecn_codepoint)
    : QuicEncryptedPacket(buffer, length, owns_buffer),
      receipt_time_(receipt_time),
      ttl_(ttl_valid ? ttl : -1),
      packet_headers_(packet_headers),
      headers_length_(headers_length),
      owns_header_buffer_(owns_header_buffer),
      ecn_codepoint_(ecn_codepoint) {}

QuicReceivedPacket::~QuicReceivedPacket() {
  if (owns_header_buffer_) {
    delete[] static_cast<char*>(packet_headers_);
  }
}

std::unique_ptr<QuicReceivedPacket> QuicReceivedPacket::Clone() const {
  char* buffer = new char[this->length()];
  memcpy(buffer, this->data(), this->length());
  if (this->packet_headers()) {
    char* headers_buffer = new char[this->headers_length()];
    memcpy(headers_buffer, this->packet_headers(), this->headers_length());
    if (GetQuicReloadableFlag(quic_clone_ecn)) {
      QUIC_RELOADABLE_FLAG_COUNT_N(quic_clone_ecn, 1, 2);
      return std::make_unique<QuicReceivedPacket>(
          buffer, this->length(), receipt_time(), true, ttl(), ttl() >= 0,
          headers_buffer, this->headers_length(), true, this->ecn_codepoint());
    } else {
      return std::make_unique<QuicReceivedPacket>(
          buffer, this->length(), receipt_time(), true, ttl(), ttl() >= 0,
          headers_buffer, this->headers_length(), true);
    }
  }

  if (GetQuicReloadableFlag(quic_clone_ecn)) {
    QUIC_RELOADABLE_FLAG_COUNT_N(quic_clone_ecn, 2, 2);
    return std::make_unique<QuicReceivedPacket>(
        buffer, this->length(), receipt_time(), true, ttl(), ttl() >= 0,
        nullptr, 0, false, this->ecn_codepoint());
  } else {
    return std::make_unique<QuicReceivedPacket>(
        buffer, this->length(), receipt_time(), true, ttl(), ttl() >= 0);
  }
}

std::ostream& operator<<(std::ostream& os, const QuicReceivedPacket& s) {
  os << s.length() << "-byte data";
  return os;
}

absl::string_view QuicPacket::AssociatedData(
    QuicTransportVersion version) const {
  return absl::string_view(
      data(),
      GetStartOfEncryptedData(version, destination_connection_id_length_,
                              source_connection_id_length_, includes_version_,
                              includes_diversification_nonce_,
                              packet_number_length_, retry_token_length_length_,
                              retry_token_length_, length_length_));
}

absl::string_view QuicPacket::Plaintext(QuicTransportVersion version) const {
  const size_t start_of_encrypted_data = GetStartOfEncryptedData(
      version, destination_connection_id_length_, source_connection_id_length_,
      includes_version_, includes_diversification_nonce_, packet_number_length_,
      retry_token_length_length_, retry_token_length_, length_length_);
  return absl::string_view(data() + start_of_encrypted_data,
                           length() - start_of_encrypted_data);
}

SerializedPacket::SerializedPacket(QuicPacketNumber packet_number,
                                   QuicPacketNumberLength packet_number_length,
                                   const char* encrypted_buffer,
                                   QuicPacketLength encrypted_length,
                                   bool has_ack, bool has_stop_waiting)
    : encrypted_buffer(encrypted_buffer),
      encrypted_length(encrypted_length),
      has_crypto_handshake(NOT_HANDSHAKE),
      packet_number(packet_number),
      packet_number_length(packet_number_length),
      encryption_level(ENCRYPTION_INITIAL),
      has_ack(has_ack),
      has_stop_waiting(has_stop_waiting),
      transmission_type(NOT_RETRANSMISSION),
      has_ack_frame_copy(false),
      has_ack_frequency(false),
      has_message(false),
      fate(SEND_TO_WRITER) {}

SerializedPacket::SerializedPacket(SerializedPacket&& other)
    : has_crypto_handshake(other.has_crypto_handshake),
      packet_number(other.packet_number),
      packet_number_length(other.packet_number_length),
      encryption_level(other.encryption_level),
      has_ack(other.has_ack),
      has_stop_waiting(other.has_stop_waiting),
      has_ack_ecn(other.has_ack_ecn),
      transmission_type(other.transmission_type),
      largest_acked(other.largest_acked),
      has_ack_frame_copy(other.has_ack_frame_copy),
      has_ack_frequency(other.has_ack_frequency),
      has_message(other.has_message),
      fate(other.fate),
      peer_address(other.peer_address),
      bytes_not_retransmitted(other.bytes_not_retransmitted),
      initial_header(other.initial_header) {
  if (this != &other) {
    if (release_encrypted_buffer && encrypted_buffer != nullptr) {
      release_encrypted_buffer(encrypted_buffer);
    }
    encrypted_buffer = other.encrypted_buffer;
    encrypted_length = other.encrypted_length;
    release_encrypted_buffer = std::move(other.release_encrypted_buffer);
    other.release_encrypted_buffer = nullptr;

    retransmittable_frames.swap(other.retransmittable_frames);
    nonretransmittable_frames.swap(other.nonretransmittable_frames);
  }
}

SerializedPacket::~SerializedPacket() {
  if (release_encrypted_buffer && encrypted_buffer != nullptr) {
    release_encrypted_buffer(encrypted_buffer);
  }

  if (!retransmittable_frames.empty()) {
    DeleteFrames(&retransmittable_frames);
  }
  for (auto& frame : nonretransmittable_frames) {
    if (!has_ack_frame_copy && frame.type == ACK_FRAME) {
      // Do not delete ack frame if the packet does not own a copy of it.
      continue;
    }
    DeleteFrame(&frame);
  }
}

SerializedPacket* CopySerializedPacket(const SerializedPacket& serialized,
                                       quiche::QuicheBufferAllocator* allocator,
                                       bool copy_buffer) {
  SerializedPacket* copy = new SerializedPacket(
      serialized.packet_number, serialized.packet_number_length,
      serialized.encrypted_buffer, serialized.encrypted_length,
      serialized.has_ack, serialized.has_stop_waiting);
  copy->has_crypto_handshake = serialized.has_crypto_handshake;
  copy->encryption_level = serialized.encryption_level;
  copy->transmission_type = serialized.transmission_type;
  copy->largest_acked = serialized.largest_acked;
  copy->has_ack_frequency = serialized.has_ack_frequency;
  copy->has_message = serialized.has_message;
  copy->fate = serialized.fate;
  copy->peer_address = serialized.peer_address;
  copy->bytes_not_retransmitted = serialized.bytes_not_retransmitted;
  copy->initial_header = serialized.initial_header;
  copy->has_ack_ecn = serialized.has_ack_ecn;

  if (copy_buffer) {
    copy->encrypted_buffer = CopyBuffer(serialized);
    copy->release_encrypted_buffer = [](const char* p) { delete[] p; };
  }
  // Copy underlying frames.
  copy->retransmittable_frames =
      CopyQuicFrames(allocator, serialized.retransmittable_frames);
  QUICHE_DCHECK(copy->nonretransmittable_frames.empty());
  for (const auto& frame : serialized.nonretransmittable_frames) {
    if (frame.type == ACK_FRAME) {
      copy->has_ack_frame_copy = true;
    }
    copy->nonretransmittable_frames.push_back(CopyQuicFrame(allocator, frame));
  }
  return copy;
}

char* CopyBuffer(const SerializedPacket& packet) {
  return CopyBuffer(packet.encrypted_buffer, packet.encrypted_length);
}

char* CopyBuffer(const char* encrypted_buffer,
                 QuicPacketLength encrypted_length) {
  char* dst_buffer = new char[encrypted_length];
  memcpy(dst_buffer, encrypted_buffer, encrypted_length);
  return dst_buffer;
}

ReceivedPacketInfo::ReceivedPacketInfo(const QuicSocketAddress& self_address,
                                       const QuicSocketAddress& peer_address,
                                       const QuicReceivedPacket& packet)
    : self_address(self_address),
      peer_address(peer_address),
      packet(packet),
      form(GOOGLE_QUIC_PACKET),
      long_packet_type(INVALID_PACKET_TYPE),
      version_flag(false),
      use_length_prefix(false),
      version_label(0),
      version(ParsedQuicVersion::Unsupported()),
      destination_connection_id(EmptyQuicConnectionId()),
      source_connection_id(EmptyQuicConnectionId()) {}

ReceivedPacketInfo::~ReceivedPacketInfo() {}

std::string ReceivedPacketInfo::ToString() const {
  std::string output =
      absl::StrCat("{ self_address: ", self_address.ToString(),
                   ", peer_address: ", peer_address.ToString(),
                   ", packet_length: ", packet.length(),
                   ", header_format: ", form, ", version_flag: ", version_flag);
  if (version_flag) {
    absl::StrAppend(&output, ", version: ", ParsedQuicVersionToString(version));
  }
  absl::StrAppend(
      &output,
      ", destination_connection_id: ", destination_connection_id.ToString(),
      ", source_connection_id: ", source_connection_id.ToString(), " }\n");
  return output;
}

std::ostream& operator<<(std::ostream& os,
                         const ReceivedPacketInfo& packet_info) {
  os << packet_info.ToString();
  return os;
}

bool QuicPacketHeader::operator==(const QuicPacketHeader& other) const {
  return destination_connection_id == other.destination_connection_id &&
         destination_connection_id_included ==
             other.destination_connection_id_included &&
         source_connection_id == other.source_connection_id &&
         source_connection_id_included == other.source_connection_id_included &&
         reset_flag == other.reset_flag && version_flag == other.version_flag &&
         has_possible_stateless_reset_token ==
             other.has_possible_stateless_reset_token &&
         packet_number_length == other.packet_number_length &&
         type_byte == other.type_byte && version == other.version &&
         nonce == other.nonce &&
         ((!packet_number.IsInitialized() &&
           !other.packet_number.IsInitialized()) ||
          (packet_number.IsInitialized() &&
           other.packet_number.IsInitialized() &&
           packet_number == other.packet_number)) &&
         form == other.form && long_packet_type == other.long_packet_type &&
         possible_stateless_reset_token ==
             other.possible_stateless_reset_token &&
         retry_token_length_length == other.retry_token_length_length &&
         retry_token == other.retry_token &&
         length_length == other.length_length &&
         remaining_packet_length == other.remaining_packet_length;
}

bool QuicPacketHeader::operator!=(const QuicPacketHeader& other) const {
  return !operator==(other);
}

}  // namespace quic
