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

#include <algorithm>
#include <cstdio>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "quiche/quic/core/quic_clock.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_utils.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"

namespace quic {

QuicConnectionIdData::QuicConnectionIdData(
    const QuicConnectionId& connection_id, uint64_t sequence_number,
    const StatelessResetToken& stateless_reset_token)
    : connection_id(connection_id),
      sequence_number(sequence_number),
      stateless_reset_token(stateless_reset_token) {}

namespace {

class RetirePeerIssuedConnectionIdAlarm
    : public QuicAlarm::DelegateWithContext {
 public:
  explicit RetirePeerIssuedConnectionIdAlarm(
      QuicConnectionIdManagerVisitorInterface* visitor,
      QuicConnectionContext* context)
      : QuicAlarm::DelegateWithContext(context), visitor_(visitor) {}
  RetirePeerIssuedConnectionIdAlarm(const RetirePeerIssuedConnectionIdAlarm&) =
      delete;
  RetirePeerIssuedConnectionIdAlarm& operator=(
      const RetirePeerIssuedConnectionIdAlarm&) = delete;

  void OnAlarm() override { visitor_->OnPeerIssuedConnectionIdRetired(); }

 private:
  QuicConnectionIdManagerVisitorInterface* visitor_;
};

std::vector<QuicConnectionIdData>::const_iterator FindConnectionIdData(
    const std::vector<QuicConnectionIdData>& cid_data_vector,
    const QuicConnectionId& cid) {
  return std::find_if(cid_data_vector.begin(), cid_data_vector.end(),
                      [&cid](const QuicConnectionIdData& cid_data) {
                        return cid == cid_data.connection_id;
                      });
}

std::vector<QuicConnectionIdData>::iterator FindConnectionIdData(
    std::vector<QuicConnectionIdData>* cid_data_vector,
    const QuicConnectionId& cid) {
  return std::find_if(cid_data_vector->begin(), cid_data_vector->end(),
                      [&cid](const QuicConnectionIdData& cid_data) {
                        return cid == cid_data.connection_id;
                      });
}

}  // namespace

QuicPeerIssuedConnectionIdManager::QuicPeerIssuedConnectionIdManager(
    size_t active_connection_id_limit,
    const QuicConnectionId& initial_peer_issued_connection_id,
    const QuicClock* clock, QuicAlarmFactory* alarm_factory,
    QuicConnectionIdManagerVisitorInterface* visitor,
    QuicConnectionContext* context)
    : active_connection_id_limit_(active_connection_id_limit),
      clock_(clock),
      retire_connection_id_alarm_(alarm_factory->CreateAlarm(
          new RetirePeerIssuedConnectionIdAlarm(visitor, context))) {
  QUICHE_DCHECK_GE(active_connection_id_limit_, 2u);
  QUICHE_DCHECK(!initial_peer_issued_connection_id.IsEmpty());
  active_connection_id_data_.emplace_back<const QuicConnectionId&, uint64_t,
                                          const StatelessResetToken&>(
      initial_peer_issued_connection_id,
      /*sequence_number=*/0u, {});
  recent_new_connection_id_sequence_numbers_.Add(0u, 1u);
}

QuicPeerIssuedConnectionIdManager::~QuicPeerIssuedConnectionIdManager() {
  retire_connection_id_alarm_->Cancel();
}

bool QuicPeerIssuedConnectionIdManager::IsConnectionIdNew(
    const QuicNewConnectionIdFrame& frame) {
  auto is_old_connection_id = [&frame](const QuicConnectionIdData& cid_data) {
    return cid_data.connection_id == frame.connection_id;
  };
  if (std::any_of(active_connection_id_data_.begin(),
                  active_connection_id_data_.end(), is_old_connection_id)) {
    return false;
  }
  if (std::any_of(unused_connection_id_data_.begin(),
                  unused_connection_id_data_.end(), is_old_connection_id)) {
    return false;
  }
  if (std::any_of(to_be_retired_connection_id_data_.begin(),
                  to_be_retired_connection_id_data_.end(),
                  is_old_connection_id)) {
    return false;
  }
  return true;
}

void QuicPeerIssuedConnectionIdManager::PrepareToRetireConnectionIdPriorTo(
    uint64_t retire_prior_to,
    std::vector<QuicConnectionIdData>* cid_data_vector) {
  auto it2 = cid_data_vector->begin();
  for (auto it = cid_data_vector->begin(); it != cid_data_vector->end(); ++it) {
    if (it->sequence_number >= retire_prior_to) {
      *it2++ = *it;
    } else {
      to_be_retired_connection_id_data_.push_back(*it);
      if (!retire_connection_id_alarm_->IsSet()) {
        retire_connection_id_alarm_->Set(clock_->ApproximateNow());
      }
    }
  }
  cid_data_vector->erase(it2, cid_data_vector->end());
}

QuicErrorCode QuicPeerIssuedConnectionIdManager::OnNewConnectionIdFrame(
    const QuicNewConnectionIdFrame& frame, std::string* error_detail,
    bool* is_duplicate_frame) {
  if (recent_new_connection_id_sequence_numbers_.Contains(
          frame.sequence_number)) {
    // This frame has a recently seen sequence number. Ignore.
    *is_duplicate_frame = true;
    return QUIC_NO_ERROR;
  }
  if (!IsConnectionIdNew(frame)) {
    *error_detail =
        "Received a NEW_CONNECTION_ID frame that reuses a previously seen Id.";
    return IETF_QUIC_PROTOCOL_VIOLATION;
  }

  recent_new_connection_id_sequence_numbers_.AddOptimizedForAppend(
      frame.sequence_number, frame.sequence_number + 1);

  if (recent_new_connection_id_sequence_numbers_.Size() >
      kMaxNumConnectionIdSequenceNumberIntervals) {
    *error_detail =
        "Too many disjoint connection Id sequence number intervals.";
    return IETF_QUIC_PROTOCOL_VIOLATION;
  }

  // QuicFramer::ProcessNewConnectionIdFrame guarantees that
  // frame.sequence_number >= frame.retire_prior_to, and hence there is no need
  // to check that.
  if (frame.sequence_number < max_new_connection_id_frame_retire_prior_to_) {
    // Later frames have asked for retirement of the current frame.
    to_be_retired_connection_id_data_.emplace_back(frame.connection_id,
                                                   frame.sequence_number,
                                                   frame.stateless_reset_token);
    if (!retire_connection_id_alarm_->IsSet()) {
      retire_connection_id_alarm_->Set(clock_->ApproximateNow());
    }
    return QUIC_NO_ERROR;
  }
  if (frame.retire_prior_to > max_new_connection_id_frame_retire_prior_to_) {
    max_new_connection_id_frame_retire_prior_to_ = frame.retire_prior_to;
    PrepareToRetireConnectionIdPriorTo(frame.retire_prior_to,
                                       &active_connection_id_data_);
    PrepareToRetireConnectionIdPriorTo(frame.retire_prior_to,
                                       &unused_connection_id_data_);
  }

  if (active_connection_id_data_.size() + unused_connection_id_data_.size() >=
      active_connection_id_limit_) {
    *error_detail = "Peer provides more connection IDs than the limit.";
    return QUIC_CONNECTION_ID_LIMIT_ERROR;
  }

  unused_connection_id_data_.emplace_back(
      frame.connection_id, frame.sequence_number, frame.stateless_reset_token);
  return QUIC_NO_ERROR;
}

const QuicConnectionIdData*
QuicPeerIssuedConnectionIdManager::ConsumeOneUnusedConnectionId() {
  if (unused_connection_id_data_.empty()) {
    return nullptr;
  }
  active_connection_id_data_.push_back(unused_connection_id_data_.back());
  unused_connection_id_data_.pop_back();
  return &active_connection_id_data_.back();
}

void QuicPeerIssuedConnectionIdManager::PrepareToRetireActiveConnectionId(
    const QuicConnectionId& cid) {
  auto it = FindConnectionIdData(active_connection_id_data_, cid);
  if (it == active_connection_id_data_.end()) {
    // The cid has already been retired.
    return;
  }
  to_be_retired_connection_id_data_.push_back(*it);
  active_connection_id_data_.erase(it);
  if (!retire_connection_id_alarm_->IsSet()) {
    retire_connection_id_alarm_->Set(clock_->ApproximateNow());
  }
}

void QuicPeerIssuedConnectionIdManager::MaybeRetireUnusedConnectionIds(
    const std::vector<QuicConnectionId>& active_connection_ids_on_path) {
  std::vector<QuicConnectionId> cids_to_retire;
  for (const auto& cid_data : active_connection_id_data_) {
    if (std::find(active_connection_ids_on_path.begin(),
                  active_connection_ids_on_path.end(),
                  cid_data.connection_id) ==
        active_connection_ids_on_path.end()) {
      cids_to_retire.push_back(cid_data.connection_id);
    }
  }
  for (const auto& cid : cids_to_retire) {
    PrepareToRetireActiveConnectionId(cid);
  }
}

bool QuicPeerIssuedConnectionIdManager::IsConnectionIdActive(
    const QuicConnectionId& cid) const {
  return FindConnectionIdData(active_connection_id_data_, cid) !=
         active_connection_id_data_.end();
}

std::vector<uint64_t> QuicPeerIssuedConnectionIdManager::
    ConsumeToBeRetiredConnectionIdSequenceNumbers() {
  std::vector<uint64_t> result;
  for (auto const& cid_data : to_be_retired_connection_id_data_) {
    result.push_back(cid_data.sequence_number);
  }
  to_be_retired_connection_id_data_.clear();
  return result;
}

void QuicPeerIssuedConnectionIdManager::ReplaceConnectionId(
    const QuicConnectionId& old_connection_id,
    const QuicConnectionId& new_connection_id) {
  auto it1 =
      FindConnectionIdData(&active_connection_id_data_, old_connection_id);
  if (it1 != active_connection_id_data_.end()) {
    it1->connection_id = new_connection_id;
    return;
  }
  auto it2 = FindConnectionIdData(&to_be_retired_connection_id_data_,
                                  old_connection_id);
  if (it2 != to_be_retired_connection_id_data_.end()) {
    it2->connection_id = new_connection_id;
  }
}

namespace {

class RetireSelfIssuedConnectionIdAlarmDelegate
    : public QuicAlarm::DelegateWithContext {
 public:
  explicit RetireSelfIssuedConnectionIdAlarmDelegate(
      QuicSelfIssuedConnectionIdManager* connection_id_manager,
      QuicConnectionContext* context)
      : QuicAlarm::DelegateWithContext(context),
        connection_id_manager_(connection_id_manager) {}
  RetireSelfIssuedConnectionIdAlarmDelegate(
      const RetireSelfIssuedConnectionIdAlarmDelegate&) = delete;
  RetireSelfIssuedConnectionIdAlarmDelegate& operator=(
      const RetireSelfIssuedConnectionIdAlarmDelegate&) = delete;

  void OnAlarm() override { connection_id_manager_->RetireConnectionId(); }

 private:
  QuicSelfIssuedConnectionIdManager* connection_id_manager_;
};

}  // namespace

QuicSelfIssuedConnectionIdManager::QuicSelfIssuedConnectionIdManager(
    size_t active_connection_id_limit,
    const QuicConnectionId& initial_connection_id, const QuicClock* clock,
    QuicAlarmFactory* alarm_factory,
    QuicConnectionIdManagerVisitorInterface* visitor,
    QuicConnectionContext* context, ConnectionIdGeneratorInterface& generator)
    : active_connection_id_limit_(active_connection_id_limit),
      clock_(clock),
      visitor_(visitor),
      retire_connection_id_alarm_(alarm_factory->CreateAlarm(
          new RetireSelfIssuedConnectionIdAlarmDelegate(this, context))),
      last_connection_id_(initial_connection_id),
      next_connection_id_sequence_number_(1u),
      last_connection_id_consumed_by_self_sequence_number_(0u),
      connection_id_generator_(generator) {
  active_connection_ids_.emplace_back(initial_connection_id, 0u);
}

QuicSelfIssuedConnectionIdManager::~QuicSelfIssuedConnectionIdManager() {
  retire_connection_id_alarm_->Cancel();
}

std::optional<QuicNewConnectionIdFrame>
QuicSelfIssuedConnectionIdManager::MaybeIssueNewConnectionId() {
  std::optional<QuicConnectionId> new_cid =
      connection_id_generator_.GenerateNextConnectionId(last_connection_id_);
  if (!new_cid.has_value()) {
    return {};
  }
  if (!visitor_->MaybeReserveConnectionId(*new_cid)) {
    return {};
  }
  QuicNewConnectionIdFrame frame;
  frame.connection_id = *new_cid;
  frame.sequence_number = next_connection_id_sequence_number_++;
  frame.stateless_reset_token =
      QuicUtils::GenerateStatelessResetToken(frame.connection_id);
  active_connection_ids_.emplace_back(frame.connection_id,
                                      frame.sequence_number);
  frame.retire_prior_to = active_connection_ids_.front().second;
  last_connection_id_ = frame.connection_id;
  return frame;
}

std::optional<QuicNewConnectionIdFrame> QuicSelfIssuedConnectionIdManager::
    MaybeIssueNewConnectionIdForPreferredAddress() {
  std::optional<QuicNewConnectionIdFrame> frame = MaybeIssueNewConnectionId();
  QUICHE_DCHECK(!frame.has_value() || (frame->sequence_number == 1u));
  return frame;
}

QuicErrorCode QuicSelfIssuedConnectionIdManager::OnRetireConnectionIdFrame(
    const QuicRetireConnectionIdFrame& frame, QuicTime::Delta pto_delay,
    std::string* error_detail) {
  QUICHE_DCHECK(!active_connection_ids_.empty());
  if (frame.sequence_number >= next_connection_id_sequence_number_) {
    *error_detail = "To be retired connecton ID is never issued.";
    return IETF_QUIC_PROTOCOL_VIOLATION;
  }

  auto it =
      std::find_if(active_connection_ids_.begin(), active_connection_ids_.end(),
                   [&frame](const std::pair<QuicConnectionId, uint64_t>& p) {
                     return p.second == frame.sequence_number;
                   });
  // The corresponding connection ID has been retired. Ignore.
  if (it == active_connection_ids_.end()) {
    return QUIC_NO_ERROR;
  }

  if (to_be_retired_connection_ids_.size() + active_connection_ids_.size() >=
      kMaxNumConnectonIdsInUse) {
    // Close connection if the number of connection IDs in use will exeed the
    // limit, i.e., peer retires connection ID too fast.
    *error_detail = "There are too many connection IDs in use.";
    return QUIC_TOO_MANY_CONNECTION_ID_WAITING_TO_RETIRE;
  }

  QuicTime retirement_time = clock_->ApproximateNow() + 3 * pto_delay;
  if (!to_be_retired_connection_ids_.empty()) {
    retirement_time =
        std::max(retirement_time, to_be_retired_connection_ids_.back().second);
  }

  to_be_retired_connection_ids_.emplace_back(it->first, retirement_time);
  if (!retire_connection_id_alarm_->IsSet()) {
    retire_connection_id_alarm_->Set(retirement_time);
  }

  active_connection_ids_.erase(it);
  MaybeSendNewConnectionIds();

  return QUIC_NO_ERROR;
}

std::vector<QuicConnectionId>
QuicSelfIssuedConnectionIdManager::GetUnretiredConnectionIds() const {
  std::vector<QuicConnectionId> unretired_ids;
  unretired_ids.reserve(to_be_retired_connection_ids_.size() +
                        active_connection_ids_.size());
  for (const auto& cid_pair : to_be_retired_connection_ids_) {
    unretired_ids.push_back(cid_pair.first);
  }
  for (const auto& cid_pair : active_connection_ids_) {
    unretired_ids.push_back(cid_pair.first);
  }
  return unretired_ids;
}

QuicConnectionId QuicSelfIssuedConnectionIdManager::GetOneActiveConnectionId()
    const {
  QUICHE_DCHECK(!active_connection_ids_.empty());
  return active_connection_ids_.front().first;
}

void QuicSelfIssuedConnectionIdManager::RetireConnectionId() {
  if (to_be_retired_connection_ids_.empty()) {
    QUIC_BUG(quic_bug_12420_1)
        << "retire_connection_id_alarm fired but there is no connection ID "
           "to be retired.";
    return;
  }
  QuicTime now = clock_->ApproximateNow();
  auto it = to_be_retired_connection_ids_.begin();
  do {
    visitor_->OnSelfIssuedConnectionIdRetired(it->first);
    ++it;
  } while (it != to_be_retired_connection_ids_.end() && it->second <= now);
  to_be_retired_connection_ids_.erase(to_be_retired_connection_ids_.begin(),
                                      it);
  // Set the alarm again if there is another connection ID to be removed.
  if (!to_be_retired_connection_ids_.empty()) {
    retire_connection_id_alarm_->Set(
        to_be_retired_connection_ids_.front().second);
  }
}

void QuicSelfIssuedConnectionIdManager::MaybeSendNewConnectionIds() {
  while (active_connection_ids_.size() < active_connection_id_limit_) {
    std::optional<QuicNewConnectionIdFrame> frame = MaybeIssueNewConnectionId();
    if (!frame.has_value()) {
      break;
    }
    if (!visitor_->SendNewConnectionId(*frame)) {
      break;
    }
  }
}

bool QuicSelfIssuedConnectionIdManager::HasConnectionIdToConsume() const {
  for (const auto& active_cid_data : active_connection_ids_) {
    if (active_cid_data.second >
        last_connection_id_consumed_by_self_sequence_number_) {
      return true;
    }
  }
  return false;
}

std::optional<QuicConnectionId>
QuicSelfIssuedConnectionIdManager::ConsumeOneConnectionId() {
  for (const auto& active_cid_data : active_connection_ids_) {
    if (active_cid_data.second >
        last_connection_id_consumed_by_self_sequence_number_) {
      // Since connection IDs in active_connection_ids_ has monotonically
      // increasing sequence numbers, the returned connection ID has the
      // smallest sequence number among all unconsumed active connection IDs.
      last_connection_id_consumed_by_self_sequence_number_ =
          active_cid_data.second;
      return active_cid_data.first;
    }
  }
  return std::nullopt;
}

bool QuicSelfIssuedConnectionIdManager::IsConnectionIdInUse(
    const QuicConnectionId& cid) const {
  for (const auto& active_cid_data : active_connection_ids_) {
    if (active_cid_data.first == cid) {
      return true;
    }
  }
  for (const auto& to_be_retired_cid_data : to_be_retired_connection_ids_) {
    if (to_be_retired_cid_data.first == cid) {
      return true;
    }
  }
  return false;
}

}  // namespace quic
