| // Copyright (c) 2021 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/http/capsule.h" | 
 |  | 
 | #include <type_traits> | 
 |  | 
 | #include "absl/strings/escaping.h" | 
 | #include "absl/strings/str_cat.h" | 
 | #include "absl/strings/string_view.h" | 
 | #include "quiche/quic/core/http/http_frames.h" | 
 | #include "quiche/quic/core/quic_data_reader.h" | 
 | #include "quiche/quic/core/quic_data_writer.h" | 
 | #include "quiche/quic/core/quic_types.h" | 
 | #include "quiche/quic/platform/api/quic_bug_tracker.h" | 
 | #include "quiche/quic/platform/api/quic_ip_address.h" | 
 | #include "quiche/common/platform/api/quiche_logging.h" | 
 | #include "quiche/common/quiche_ip_address.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | std::string CapsuleTypeToString(CapsuleType capsule_type) { | 
 |   switch (capsule_type) { | 
 |     case CapsuleType::LEGACY_DATAGRAM: | 
 |       return "LEGACY_DATAGRAM"; | 
 |     case CapsuleType::DATAGRAM_WITHOUT_CONTEXT: | 
 |       return "DATAGRAM_WITHOUT_CONTEXT"; | 
 |     case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: | 
 |       return "CLOSE_WEBTRANSPORT_SESSION"; | 
 |     case CapsuleType::ADDRESS_REQUEST: | 
 |       return "ADDRESS_REQUEST"; | 
 |     case CapsuleType::ADDRESS_ASSIGN: | 
 |       return "ADDRESS_ASSIGN"; | 
 |     case CapsuleType::ROUTE_ADVERTISEMENT: | 
 |       return "ROUTE_ADVERTISEMENT"; | 
 |   } | 
 |   return absl::StrCat("Unknown(", static_cast<uint64_t>(capsule_type), ")"); | 
 | } | 
 |  | 
 | std::ostream& operator<<(std::ostream& os, const CapsuleType& capsule_type) { | 
 |   os << CapsuleTypeToString(capsule_type); | 
 |   return os; | 
 | } | 
 |  | 
 | Capsule::Capsule(CapsuleType capsule_type) : capsule_type_(capsule_type) { | 
 |   switch (capsule_type) { | 
 |     case CapsuleType::LEGACY_DATAGRAM: | 
 |       static_assert( | 
 |           std::is_standard_layout<LegacyDatagramCapsule>::value && | 
 |               std::is_trivially_destructible<LegacyDatagramCapsule>::value, | 
 |           "All inline capsule structs must have these properties"); | 
 |       legacy_datagram_capsule_ = LegacyDatagramCapsule(); | 
 |       break; | 
 |     case CapsuleType::DATAGRAM_WITHOUT_CONTEXT: | 
 |       static_assert( | 
 |           std::is_standard_layout<DatagramWithoutContextCapsule>::value && | 
 |               std::is_trivially_destructible< | 
 |                   DatagramWithoutContextCapsule>::value, | 
 |           "All inline capsule structs must have these properties"); | 
 |       datagram_without_context_capsule_ = DatagramWithoutContextCapsule(); | 
 |       break; | 
 |     case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: | 
 |       static_assert( | 
 |           std::is_standard_layout<CloseWebTransportSessionCapsule>::value && | 
 |               std::is_trivially_destructible< | 
 |                   CloseWebTransportSessionCapsule>::value, | 
 |           "All inline capsule structs must have these properties"); | 
 |       close_web_transport_session_capsule_ = CloseWebTransportSessionCapsule(); | 
 |       break; | 
 |     case CapsuleType::ADDRESS_REQUEST: | 
 |       address_request_capsule_ = new AddressRequestCapsule(); | 
 |       break; | 
 |     case CapsuleType::ADDRESS_ASSIGN: | 
 |       address_assign_capsule_ = new AddressAssignCapsule(); | 
 |       break; | 
 |     case CapsuleType::ROUTE_ADVERTISEMENT: | 
 |       route_advertisement_capsule_ = new RouteAdvertisementCapsule(); | 
 |       break; | 
 |     default: | 
 |       unknown_capsule_data_ = absl::string_view(); | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | void Capsule::Free() { | 
 |   switch (capsule_type_) { | 
 |     // Inlined capsule types. | 
 |     case CapsuleType::LEGACY_DATAGRAM: | 
 |     case CapsuleType::DATAGRAM_WITHOUT_CONTEXT: | 
 |     case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: | 
 |       // Do nothing, these are guaranteed to be trivially destructible. | 
 |       break; | 
 |     // Out-of-line capsule types. | 
 |     case CapsuleType::ADDRESS_REQUEST: | 
 |       delete address_request_capsule_; | 
 |       break; | 
 |     case CapsuleType::ADDRESS_ASSIGN: | 
 |       delete address_assign_capsule_; | 
 |       break; | 
 |     case CapsuleType::ROUTE_ADVERTISEMENT: | 
 |       delete route_advertisement_capsule_; | 
 |       break; | 
 |   } | 
 |   capsule_type_ = static_cast<CapsuleType>(0x17);  // Reserved unknown value. | 
 |   unknown_capsule_data_ = absl::string_view(); | 
 | } | 
 | Capsule::~Capsule() { Free(); } | 
 |  | 
 | // static | 
 | Capsule Capsule::LegacyDatagram(absl::string_view http_datagram_payload) { | 
 |   Capsule capsule(CapsuleType::LEGACY_DATAGRAM); | 
 |   capsule.legacy_datagram_capsule().http_datagram_payload = | 
 |       http_datagram_payload; | 
 |   return capsule; | 
 | } | 
 |  | 
 | // static | 
 | Capsule Capsule::DatagramWithoutContext( | 
 |     absl::string_view http_datagram_payload) { | 
 |   Capsule capsule(CapsuleType::DATAGRAM_WITHOUT_CONTEXT); | 
 |   capsule.datagram_without_context_capsule().http_datagram_payload = | 
 |       http_datagram_payload; | 
 |   return capsule; | 
 | } | 
 |  | 
 | // static | 
 | Capsule Capsule::CloseWebTransportSession(WebTransportSessionError error_code, | 
 |                                           absl::string_view error_message) { | 
 |   Capsule capsule(CapsuleType::CLOSE_WEBTRANSPORT_SESSION); | 
 |   capsule.close_web_transport_session_capsule().error_code = error_code; | 
 |   capsule.close_web_transport_session_capsule().error_message = error_message; | 
 |   return capsule; | 
 | } | 
 |  | 
 | // static | 
 | Capsule Capsule::AddressRequest() { | 
 |   return Capsule(CapsuleType::ADDRESS_REQUEST); | 
 | } | 
 |  | 
 | // static | 
 | Capsule Capsule::AddressAssign() { | 
 |   return Capsule(CapsuleType::ADDRESS_ASSIGN); | 
 | } | 
 |  | 
 | // static | 
 | Capsule Capsule::RouteAdvertisement() { | 
 |   return Capsule(CapsuleType::ROUTE_ADVERTISEMENT); | 
 | } | 
 |  | 
 | // static | 
 | Capsule Capsule::Unknown(uint64_t capsule_type, | 
 |                          absl::string_view unknown_capsule_data) { | 
 |   Capsule capsule(static_cast<CapsuleType>(capsule_type)); | 
 |   capsule.unknown_capsule_data() = unknown_capsule_data; | 
 |   return capsule; | 
 | } | 
 |  | 
 | Capsule& Capsule::operator=(const Capsule& other) { | 
 |   Free(); | 
 |   capsule_type_ = other.capsule_type_; | 
 |   switch (capsule_type_) { | 
 |     case CapsuleType::LEGACY_DATAGRAM: | 
 |       legacy_datagram_capsule_ = other.legacy_datagram_capsule_; | 
 |       break; | 
 |     case CapsuleType::DATAGRAM_WITHOUT_CONTEXT: | 
 |       datagram_without_context_capsule_ = | 
 |           other.datagram_without_context_capsule_; | 
 |       break; | 
 |     case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: | 
 |       close_web_transport_session_capsule_ = | 
 |           other.close_web_transport_session_capsule_; | 
 |       break; | 
 |     case CapsuleType::ADDRESS_ASSIGN: | 
 |       address_assign_capsule_ = new AddressAssignCapsule(); | 
 |       *address_assign_capsule_ = *other.address_assign_capsule_; | 
 |       break; | 
 |     case CapsuleType::ADDRESS_REQUEST: | 
 |       address_request_capsule_ = new AddressRequestCapsule(); | 
 |       *address_request_capsule_ = *other.address_request_capsule_; | 
 |       break; | 
 |     case CapsuleType::ROUTE_ADVERTISEMENT: | 
 |       route_advertisement_capsule_ = new RouteAdvertisementCapsule(); | 
 |       *route_advertisement_capsule_ = *other.route_advertisement_capsule_; | 
 |       break; | 
 |     default: | 
 |       unknown_capsule_data_ = other.unknown_capsule_data_; | 
 |       break; | 
 |   } | 
 |   return *this; | 
 | } | 
 |  | 
 | Capsule::Capsule(const Capsule& other) : Capsule(other.capsule_type_) { | 
 |   *this = other; | 
 | } | 
 |  | 
 | bool Capsule::operator==(const Capsule& other) const { | 
 |   if (capsule_type_ != other.capsule_type_) { | 
 |     return false; | 
 |   } | 
 |   switch (capsule_type_) { | 
 |     case CapsuleType::LEGACY_DATAGRAM: | 
 |       return legacy_datagram_capsule_.http_datagram_payload == | 
 |              other.legacy_datagram_capsule_.http_datagram_payload; | 
 |     case CapsuleType::DATAGRAM_WITHOUT_CONTEXT: | 
 |       return datagram_without_context_capsule_.http_datagram_payload == | 
 |              other.datagram_without_context_capsule_.http_datagram_payload; | 
 |     case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: | 
 |       return close_web_transport_session_capsule_.error_code == | 
 |                  other.close_web_transport_session_capsule_.error_code && | 
 |              close_web_transport_session_capsule_.error_message == | 
 |                  other.close_web_transport_session_capsule_.error_message; | 
 |     case CapsuleType::ADDRESS_REQUEST: | 
 |       return address_request_capsule_->requested_addresses == | 
 |              other.address_request_capsule_->requested_addresses; | 
 |     case CapsuleType::ADDRESS_ASSIGN: | 
 |       return address_assign_capsule_->assigned_addresses == | 
 |              other.address_assign_capsule_->assigned_addresses; | 
 |     case CapsuleType::ROUTE_ADVERTISEMENT: | 
 |       return route_advertisement_capsule_->ip_address_ranges == | 
 |              other.route_advertisement_capsule_->ip_address_ranges; | 
 |     default: | 
 |       return unknown_capsule_data_ == other.unknown_capsule_data_; | 
 |   } | 
 | } | 
 |  | 
 | std::string Capsule::ToString() const { | 
 |   std::string rv = CapsuleTypeToString(capsule_type_); | 
 |   switch (capsule_type_) { | 
 |     case CapsuleType::LEGACY_DATAGRAM: | 
 |       absl::StrAppend(&rv, "[", | 
 |                       absl::BytesToHexString( | 
 |                           legacy_datagram_capsule_.http_datagram_payload), | 
 |                       "]"); | 
 |       break; | 
 |     case CapsuleType::DATAGRAM_WITHOUT_CONTEXT: | 
 |       absl::StrAppend( | 
 |           &rv, "[", | 
 |           absl::BytesToHexString( | 
 |               datagram_without_context_capsule_.http_datagram_payload), | 
 |           "]"); | 
 |       break; | 
 |     case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: | 
 |       absl::StrAppend( | 
 |           &rv, "(error_code=", close_web_transport_session_capsule_.error_code, | 
 |           ",error_message=\"", | 
 |           close_web_transport_session_capsule_.error_message, "\")"); | 
 |       break; | 
 |     case CapsuleType::ADDRESS_REQUEST: { | 
 |       absl::StrAppend(&rv, "["); | 
 |       for (auto requested_address : | 
 |            address_request_capsule_->requested_addresses) { | 
 |         absl::StrAppend(&rv, "(", requested_address.request_id, "-", | 
 |                         requested_address.ip_prefix.ToString(), ")"); | 
 |       } | 
 |       absl::StrAppend(&rv, "]"); | 
 |     } break; | 
 |     case CapsuleType::ADDRESS_ASSIGN: { | 
 |       absl::StrAppend(&rv, "["); | 
 |       for (auto assigned_address : | 
 |            address_assign_capsule_->assigned_addresses) { | 
 |         absl::StrAppend(&rv, "(", assigned_address.request_id, "-", | 
 |                         assigned_address.ip_prefix.ToString(), ")"); | 
 |       } | 
 |       absl::StrAppend(&rv, "]"); | 
 |     } break; | 
 |     case CapsuleType::ROUTE_ADVERTISEMENT: { | 
 |       absl::StrAppend(&rv, "["); | 
 |       for (auto ip_address_range : | 
 |            route_advertisement_capsule_->ip_address_ranges) { | 
 |         absl::StrAppend(&rv, "(", ip_address_range.start_ip_address.ToString(), | 
 |                         "-", ip_address_range.end_ip_address.ToString(), "-", | 
 |                         static_cast<int>(ip_address_range.ip_protocol), ")"); | 
 |       } | 
 |       absl::StrAppend(&rv, "]"); | 
 |     } break; | 
 |     default: | 
 |       absl::StrAppend(&rv, "[", absl::BytesToHexString(unknown_capsule_data_), | 
 |                       "]"); | 
 |       break; | 
 |   } | 
 |   return rv; | 
 | } | 
 |  | 
 | std::ostream& operator<<(std::ostream& os, const Capsule& capsule) { | 
 |   os << capsule.ToString(); | 
 |   return os; | 
 | } | 
 |  | 
 | CapsuleParser::CapsuleParser(Visitor* visitor) : visitor_(visitor) { | 
 |   QUICHE_DCHECK_NE(visitor_, nullptr); | 
 | } | 
 |  | 
 | quiche::QuicheBuffer SerializeCapsule( | 
 |     const Capsule& capsule, quiche::QuicheBufferAllocator* allocator) { | 
 |   QuicByteCount capsule_type_length = QuicDataWriter::GetVarInt62Len( | 
 |       static_cast<uint64_t>(capsule.capsule_type())); | 
 |   QuicByteCount capsule_data_length; | 
 |   switch (capsule.capsule_type()) { | 
 |     case CapsuleType::LEGACY_DATAGRAM: | 
 |       capsule_data_length = | 
 |           capsule.legacy_datagram_capsule().http_datagram_payload.length(); | 
 |       break; | 
 |     case CapsuleType::DATAGRAM_WITHOUT_CONTEXT: | 
 |       capsule_data_length = capsule.datagram_without_context_capsule() | 
 |                                 .http_datagram_payload.length(); | 
 |       break; | 
 |     case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: | 
 |       capsule_data_length = | 
 |           sizeof(WebTransportSessionError) + | 
 |           capsule.close_web_transport_session_capsule().error_message.size(); | 
 |       break; | 
 |     case CapsuleType::ADDRESS_REQUEST: | 
 |       capsule_data_length = 0; | 
 |       for (auto requested_address : | 
 |            capsule.address_request_capsule().requested_addresses) { | 
 |         capsule_data_length += | 
 |             QuicDataWriter::GetVarInt62Len(requested_address.request_id) + 1 + | 
 |             (requested_address.ip_prefix.address().IsIPv4() | 
 |                  ? QuicIpAddress::kIPv4AddressSize | 
 |                  : QuicIpAddress::kIPv6AddressSize) + | 
 |             1; | 
 |       } | 
 |       break; | 
 |     case CapsuleType::ADDRESS_ASSIGN: | 
 |       capsule_data_length = 0; | 
 |       for (auto assigned_address : | 
 |            capsule.address_assign_capsule().assigned_addresses) { | 
 |         capsule_data_length += | 
 |             QuicDataWriter::GetVarInt62Len(assigned_address.request_id) + 1 + | 
 |             (assigned_address.ip_prefix.address().IsIPv4() | 
 |                  ? QuicIpAddress::kIPv4AddressSize | 
 |                  : QuicIpAddress::kIPv6AddressSize) + | 
 |             1; | 
 |       } | 
 |       break; | 
 |     case CapsuleType::ROUTE_ADVERTISEMENT: | 
 |       capsule_data_length = 0; | 
 |       for (auto ip_address_range : | 
 |            capsule.route_advertisement_capsule().ip_address_ranges) { | 
 |         capsule_data_length += 1 + | 
 |                                (ip_address_range.start_ip_address.IsIPv4() | 
 |                                     ? QuicIpAddress::kIPv4AddressSize | 
 |                                     : QuicIpAddress::kIPv6AddressSize) * | 
 |                                    2 + | 
 |                                1; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       capsule_data_length = capsule.unknown_capsule_data().length(); | 
 |       break; | 
 |   } | 
 |   QuicByteCount capsule_length_length = | 
 |       QuicDataWriter::GetVarInt62Len(capsule_data_length); | 
 |   QuicByteCount total_capsule_length = | 
 |       capsule_type_length + capsule_length_length + capsule_data_length; | 
 |   quiche::QuicheBuffer buffer(allocator, total_capsule_length); | 
 |   QuicDataWriter writer(buffer.size(), buffer.data()); | 
 |   if (!writer.WriteVarInt62(static_cast<uint64_t>(capsule.capsule_type()))) { | 
 |     QUIC_BUG(capsule type write fail) << "Failed to write CAPSULE type"; | 
 |     return {}; | 
 |   } | 
 |   if (!writer.WriteVarInt62(capsule_data_length)) { | 
 |     QUIC_BUG(capsule length write fail) << "Failed to write CAPSULE length"; | 
 |     return {}; | 
 |   } | 
 |   switch (capsule.capsule_type()) { | 
 |     case CapsuleType::LEGACY_DATAGRAM: | 
 |       if (!writer.WriteStringPiece( | 
 |               capsule.legacy_datagram_capsule().http_datagram_payload)) { | 
 |         QUIC_BUG(datagram capsule payload write fail) | 
 |             << "Failed to write LEGACY_DATAGRAM CAPSULE payload"; | 
 |         return {}; | 
 |       } | 
 |       break; | 
 |     case CapsuleType::DATAGRAM_WITHOUT_CONTEXT: | 
 |       if (!writer.WriteStringPiece(capsule.datagram_without_context_capsule() | 
 |                                        .http_datagram_payload)) { | 
 |         QUIC_BUG(datagram capsule payload write fail) | 
 |             << "Failed to write DATAGRAM_WITHOUT_CONTEXT CAPSULE payload"; | 
 |         return {}; | 
 |       } | 
 |       break; | 
 |     case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: | 
 |       if (!writer.WriteUInt32( | 
 |               capsule.close_web_transport_session_capsule().error_code)) { | 
 |         QUIC_BUG(close webtransport session capsule error code write fail) | 
 |             << "Failed to write CLOSE_WEBTRANSPORT_SESSION error code"; | 
 |         return {}; | 
 |       } | 
 |       if (!writer.WriteStringPiece( | 
 |               capsule.close_web_transport_session_capsule().error_message)) { | 
 |         QUIC_BUG(close webtransport session capsule error message write fail) | 
 |             << "Failed to write CLOSE_WEBTRANSPORT_SESSION error message"; | 
 |         return {}; | 
 |       } | 
 |       break; | 
 |     case CapsuleType::ADDRESS_REQUEST: | 
 |       for (auto requested_address : | 
 |            capsule.address_request_capsule().requested_addresses) { | 
 |         if (!writer.WriteVarInt62(requested_address.request_id)) { | 
 |           QUIC_BUG(address request capsule id write fail) | 
 |               << "Failed to write ADDRESS_REQUEST ID"; | 
 |           return {}; | 
 |         } | 
 |         if (!writer.WriteUInt8( | 
 |                 requested_address.ip_prefix.address().IsIPv4() ? 4 : 6)) { | 
 |           QUIC_BUG(address request capsule family write fail) | 
 |               << "Failed to write ADDRESS_REQUEST family"; | 
 |           return {}; | 
 |         } | 
 |         if (!writer.WriteStringPiece( | 
 |                 requested_address.ip_prefix.address().ToPackedString())) { | 
 |           QUIC_BUG(address request capsule address write fail) | 
 |               << "Failed to write ADDRESS_REQUEST address"; | 
 |           return {}; | 
 |         } | 
 |         if (!writer.WriteUInt8(requested_address.ip_prefix.prefix_length())) { | 
 |           QUIC_BUG(address request capsule prefix length write fail) | 
 |               << "Failed to write ADDRESS_REQUEST prefix length"; | 
 |           return {}; | 
 |         } | 
 |       } | 
 |       break; | 
 |     case CapsuleType::ADDRESS_ASSIGN: | 
 |       for (auto assigned_address : | 
 |            capsule.address_assign_capsule().assigned_addresses) { | 
 |         if (!writer.WriteVarInt62(assigned_address.request_id)) { | 
 |           QUIC_BUG(address request capsule id write fail) | 
 |               << "Failed to write ADDRESS_ASSIGN ID"; | 
 |           return {}; | 
 |         } | 
 |         if (!writer.WriteUInt8( | 
 |                 assigned_address.ip_prefix.address().IsIPv4() ? 4 : 6)) { | 
 |           QUIC_BUG(address request capsule family write fail) | 
 |               << "Failed to write ADDRESS_ASSIGN family"; | 
 |           return {}; | 
 |         } | 
 |         if (!writer.WriteStringPiece( | 
 |                 assigned_address.ip_prefix.address().ToPackedString())) { | 
 |           QUIC_BUG(address request capsule address write fail) | 
 |               << "Failed to write ADDRESS_ASSIGN address"; | 
 |           return {}; | 
 |         } | 
 |         if (!writer.WriteUInt8(assigned_address.ip_prefix.prefix_length())) { | 
 |           QUIC_BUG(address request capsule prefix length write fail) | 
 |               << "Failed to write ADDRESS_ASSIGN prefix length"; | 
 |           return {}; | 
 |         } | 
 |       } | 
 |       break; | 
 |     case CapsuleType::ROUTE_ADVERTISEMENT: | 
 |       for (auto ip_address_range : | 
 |            capsule.route_advertisement_capsule().ip_address_ranges) { | 
 |         if (!writer.WriteUInt8( | 
 |                 ip_address_range.start_ip_address.IsIPv4() ? 4 : 6)) { | 
 |           QUIC_BUG(route advertisement capsule family write fail) | 
 |               << "Failed to write ROUTE_ADVERTISEMENT family"; | 
 |           return {}; | 
 |         } | 
 |         if (!writer.WriteStringPiece( | 
 |                 ip_address_range.start_ip_address.ToPackedString())) { | 
 |           QUIC_BUG(route advertisement capsule start address write fail) | 
 |               << "Failed to write ROUTE_ADVERTISEMENT start address"; | 
 |           return {}; | 
 |         } | 
 |         if (!writer.WriteStringPiece( | 
 |                 ip_address_range.end_ip_address.ToPackedString())) { | 
 |           QUIC_BUG(route advertisement capsule end address write fail) | 
 |               << "Failed to write ROUTE_ADVERTISEMENT end address"; | 
 |           return {}; | 
 |         } | 
 |         if (!writer.WriteUInt8(ip_address_range.ip_protocol)) { | 
 |           QUIC_BUG(route advertisement capsule IP protocol write fail) | 
 |               << "Failed to write ROUTE_ADVERTISEMENT IP protocol"; | 
 |           return {}; | 
 |         } | 
 |       } | 
 |       break; | 
 |     default: | 
 |       if (!writer.WriteStringPiece(capsule.unknown_capsule_data())) { | 
 |         QUIC_BUG(capsule data write fail) << "Failed to write CAPSULE data"; | 
 |         return {}; | 
 |       } | 
 |       break; | 
 |   } | 
 |   if (writer.remaining() != 0) { | 
 |     QUIC_BUG(capsule write length mismatch) | 
 |         << "CAPSULE serialization wrote " << writer.length() << " instead of " | 
 |         << writer.capacity(); | 
 |     return {}; | 
 |   } | 
 |   return buffer; | 
 | } | 
 |  | 
 | bool CapsuleParser::IngestCapsuleFragment(absl::string_view capsule_fragment) { | 
 |   if (parsing_error_occurred_) { | 
 |     return false; | 
 |   } | 
 |   absl::StrAppend(&buffered_data_, capsule_fragment); | 
 |   while (true) { | 
 |     const size_t buffered_data_read = AttemptParseCapsule(); | 
 |     if (parsing_error_occurred_) { | 
 |       QUICHE_DCHECK_EQ(buffered_data_read, 0u); | 
 |       buffered_data_.clear(); | 
 |       return false; | 
 |     } | 
 |     if (buffered_data_read == 0) { | 
 |       break; | 
 |     } | 
 |     buffered_data_.erase(0, buffered_data_read); | 
 |   } | 
 |   static constexpr size_t kMaxCapsuleBufferSize = 1024 * 1024; | 
 |   if (buffered_data_.size() > kMaxCapsuleBufferSize) { | 
 |     buffered_data_.clear(); | 
 |     ReportParseFailure("Refusing to buffer too much capsule data"); | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | size_t CapsuleParser::AttemptParseCapsule() { | 
 |   QUICHE_DCHECK(!parsing_error_occurred_); | 
 |   if (buffered_data_.empty()) { | 
 |     return 0; | 
 |   } | 
 |   QuicDataReader capsule_fragment_reader(buffered_data_); | 
 |   uint64_t capsule_type64; | 
 |   if (!capsule_fragment_reader.ReadVarInt62(&capsule_type64)) { | 
 |     QUIC_DVLOG(2) << "Partial read: not enough data to read capsule type"; | 
 |     return 0; | 
 |   } | 
 |   absl::string_view capsule_data; | 
 |   if (!capsule_fragment_reader.ReadStringPieceVarInt62(&capsule_data)) { | 
 |     QUIC_DVLOG(2) << "Partial read: not enough data to read capsule length or " | 
 |                      "full capsule data"; | 
 |     return 0; | 
 |   } | 
 |   QuicDataReader capsule_data_reader(capsule_data); | 
 |   Capsule capsule(static_cast<CapsuleType>(capsule_type64)); | 
 |   switch (capsule.capsule_type()) { | 
 |     case CapsuleType::LEGACY_DATAGRAM: | 
 |       capsule.legacy_datagram_capsule().http_datagram_payload = | 
 |           capsule_data_reader.ReadRemainingPayload(); | 
 |       break; | 
 |     case CapsuleType::DATAGRAM_WITHOUT_CONTEXT: | 
 |       capsule.datagram_without_context_capsule().http_datagram_payload = | 
 |           capsule_data_reader.ReadRemainingPayload(); | 
 |       break; | 
 |     case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: | 
 |       if (!capsule_data_reader.ReadUInt32( | 
 |               &capsule.close_web_transport_session_capsule().error_code)) { | 
 |         ReportParseFailure( | 
 |             "Unable to parse capsule CLOSE_WEBTRANSPORT_SESSION error code"); | 
 |         return 0; | 
 |       } | 
 |       capsule.close_web_transport_session_capsule().error_message = | 
 |           capsule_data_reader.ReadRemainingPayload(); | 
 |       break; | 
 |     case CapsuleType::ADDRESS_REQUEST: { | 
 |       while (!capsule_data_reader.IsDoneReading()) { | 
 |         PrefixWithId requested_address; | 
 |         if (!capsule_data_reader.ReadVarInt62(&requested_address.request_id)) { | 
 |           ReportParseFailure( | 
 |               "Unable to parse capsule ADDRESS_REQUEST request ID"); | 
 |           return 0; | 
 |         } | 
 |         uint8_t address_family; | 
 |         if (!capsule_data_reader.ReadUInt8(&address_family)) { | 
 |           ReportParseFailure("Unable to parse capsule ADDRESS_REQUEST family"); | 
 |           return 0; | 
 |         } | 
 |         if (address_family != 4 && address_family != 6) { | 
 |           ReportParseFailure("Bad ADDRESS_REQUEST family"); | 
 |           return 0; | 
 |         } | 
 |         absl::string_view ip_address_bytes; | 
 |         if (!capsule_data_reader.ReadStringPiece( | 
 |                 &ip_address_bytes, address_family == 4 | 
 |                                        ? QuicIpAddress::kIPv4AddressSize | 
 |                                        : QuicIpAddress::kIPv6AddressSize)) { | 
 |           ReportParseFailure("Unable to read capsule ADDRESS_REQUEST address"); | 
 |           return 0; | 
 |         } | 
 |         quiche::QuicheIpAddress ip_address; | 
 |         if (!ip_address.FromPackedString(ip_address_bytes.data(), | 
 |                                          ip_address_bytes.size())) { | 
 |           ReportParseFailure("Unable to parse capsule ADDRESS_REQUEST address"); | 
 |           return 0; | 
 |         } | 
 |         uint8_t ip_prefix_length; | 
 |         if (!capsule_data_reader.ReadUInt8(&ip_prefix_length)) { | 
 |           ReportParseFailure( | 
 |               "Unable to parse capsule ADDRESS_REQUEST IP prefix length"); | 
 |           return 0; | 
 |         } | 
 |         if (ip_prefix_length > | 
 |             quiche::QuicheIpPrefix(ip_address).prefix_length()) { | 
 |           ReportParseFailure("Invalid IP prefix length"); | 
 |           return 0; | 
 |         } | 
 |         requested_address.ip_prefix = | 
 |             quiche::QuicheIpPrefix(ip_address, ip_prefix_length); | 
 |         capsule.address_request_capsule().requested_addresses.push_back( | 
 |             requested_address); | 
 |       } | 
 |     } break; | 
 |     case CapsuleType::ADDRESS_ASSIGN: { | 
 |       while (!capsule_data_reader.IsDoneReading()) { | 
 |         PrefixWithId assigned_address; | 
 |         if (!capsule_data_reader.ReadVarInt62(&assigned_address.request_id)) { | 
 |           ReportParseFailure( | 
 |               "Unable to parse capsule ADDRESS_ASSIGN request ID"); | 
 |           return 0; | 
 |         } | 
 |         uint8_t address_family; | 
 |         if (!capsule_data_reader.ReadUInt8(&address_family)) { | 
 |           ReportParseFailure("Unable to parse capsule ADDRESS_ASSIGN family"); | 
 |           return 0; | 
 |         } | 
 |         if (address_family != 4 && address_family != 6) { | 
 |           ReportParseFailure("Bad ADDRESS_ASSIGN family"); | 
 |           return 0; | 
 |         } | 
 |         absl::string_view ip_address_bytes; | 
 |         if (!capsule_data_reader.ReadStringPiece( | 
 |                 &ip_address_bytes, address_family == 4 | 
 |                                        ? QuicIpAddress::kIPv4AddressSize | 
 |                                        : QuicIpAddress::kIPv6AddressSize)) { | 
 |           ReportParseFailure("Unable to read capsule ADDRESS_ASSIGN address"); | 
 |           return 0; | 
 |         } | 
 |         quiche::QuicheIpAddress ip_address; | 
 |         if (!ip_address.FromPackedString(ip_address_bytes.data(), | 
 |                                          ip_address_bytes.size())) { | 
 |           ReportParseFailure("Unable to parse capsule ADDRESS_ASSIGN address"); | 
 |           return 0; | 
 |         } | 
 |         uint8_t ip_prefix_length; | 
 |         if (!capsule_data_reader.ReadUInt8(&ip_prefix_length)) { | 
 |           ReportParseFailure( | 
 |               "Unable to parse capsule ADDRESS_ASSIGN IP prefix length"); | 
 |           return 0; | 
 |         } | 
 |         if (ip_prefix_length > | 
 |             quiche::QuicheIpPrefix(ip_address).prefix_length()) { | 
 |           ReportParseFailure("Invalid IP prefix length"); | 
 |           return 0; | 
 |         } | 
 |         assigned_address.ip_prefix = | 
 |             quiche::QuicheIpPrefix(ip_address, ip_prefix_length); | 
 |         capsule.address_assign_capsule().assigned_addresses.push_back( | 
 |             assigned_address); | 
 |       } | 
 |     } break; | 
 |     case CapsuleType::ROUTE_ADVERTISEMENT: { | 
 |       while (!capsule_data_reader.IsDoneReading()) { | 
 |         uint8_t address_family; | 
 |         if (!capsule_data_reader.ReadUInt8(&address_family)) { | 
 |           ReportParseFailure( | 
 |               "Unable to parse capsule ROUTE_ADVERTISEMENT family"); | 
 |           return 0; | 
 |         } | 
 |         if (address_family != 4 && address_family != 6) { | 
 |           ReportParseFailure("Bad ROUTE_ADVERTISEMENT family"); | 
 |           return 0; | 
 |         } | 
 |         IpAddressRange ip_address_range; | 
 |         absl::string_view start_ip_address_bytes; | 
 |         if (!capsule_data_reader.ReadStringPiece( | 
 |                 &start_ip_address_bytes, | 
 |                 address_family == 4 ? QuicIpAddress::kIPv4AddressSize | 
 |                                     : QuicIpAddress::kIPv6AddressSize)) { | 
 |           ReportParseFailure( | 
 |               "Unable to read capsule ROUTE_ADVERTISEMENT start address"); | 
 |           return 0; | 
 |         } | 
 |         if (!ip_address_range.start_ip_address.FromPackedString( | 
 |                 start_ip_address_bytes.data(), start_ip_address_bytes.size())) { | 
 |           ReportParseFailure( | 
 |               "Unable to parse capsule ROUTE_ADVERTISEMENT start address"); | 
 |           return 0; | 
 |         } | 
 |         absl::string_view end_ip_address_bytes; | 
 |         if (!capsule_data_reader.ReadStringPiece( | 
 |                 &end_ip_address_bytes, address_family == 4 | 
 |                                            ? QuicIpAddress::kIPv4AddressSize | 
 |                                            : QuicIpAddress::kIPv6AddressSize)) { | 
 |           ReportParseFailure( | 
 |               "Unable to read capsule ROUTE_ADVERTISEMENT end address"); | 
 |           return 0; | 
 |         } | 
 |         if (!ip_address_range.end_ip_address.FromPackedString( | 
 |                 end_ip_address_bytes.data(), end_ip_address_bytes.size())) { | 
 |           ReportParseFailure( | 
 |               "Unable to parse capsule ROUTE_ADVERTISEMENT end address"); | 
 |           return 0; | 
 |         } | 
 |         if (!capsule_data_reader.ReadUInt8(&ip_address_range.ip_protocol)) { | 
 |           ReportParseFailure( | 
 |               "Unable to parse capsule ROUTE_ADVERTISEMENT IP protocol"); | 
 |           return 0; | 
 |         } | 
 |         capsule.route_advertisement_capsule().ip_address_ranges.push_back( | 
 |             ip_address_range); | 
 |       } | 
 |     } break; | 
 |     default: | 
 |       capsule.unknown_capsule_data() = | 
 |           capsule_data_reader.ReadRemainingPayload(); | 
 |   } | 
 |   if (!visitor_->OnCapsule(capsule)) { | 
 |     ReportParseFailure("Visitor failed to process capsule"); | 
 |     return 0; | 
 |   } | 
 |   return capsule_fragment_reader.PreviouslyReadPayload().length(); | 
 | } | 
 |  | 
 | void CapsuleParser::ReportParseFailure(const std::string& error_message) { | 
 |   if (parsing_error_occurred_) { | 
 |     QUIC_BUG(multiple parse errors) << "Experienced multiple parse failures"; | 
 |     return; | 
 |   } | 
 |   parsing_error_occurred_ = true; | 
 |   visitor_->OnCapsuleParseFailure(error_message); | 
 | } | 
 |  | 
 | void CapsuleParser::ErrorIfThereIsRemainingBufferedData() { | 
 |   if (parsing_error_occurred_) { | 
 |     return; | 
 |   } | 
 |   if (!buffered_data_.empty()) { | 
 |     ReportParseFailure("Incomplete capsule left at the end of the stream"); | 
 |   } | 
 | } | 
 |  | 
 | bool PrefixWithId::operator==(const PrefixWithId& other) const { | 
 |   return request_id == other.request_id && ip_prefix == other.ip_prefix; | 
 | } | 
 |  | 
 | bool IpAddressRange::operator==(const IpAddressRange& other) const { | 
 |   return start_ip_address == other.start_ip_address && | 
 |          end_ip_address == other.end_ip_address && | 
 |          ip_protocol == other.ip_protocol; | 
 | } | 
 |  | 
 | bool AddressAssignCapsule::operator==(const AddressAssignCapsule& other) const { | 
 |   return assigned_addresses == other.assigned_addresses; | 
 | } | 
 |  | 
 | bool AddressRequestCapsule::operator==( | 
 |     const AddressRequestCapsule& other) const { | 
 |   return requested_addresses == other.requested_addresses; | 
 | } | 
 |  | 
 | bool RouteAdvertisementCapsule::operator==( | 
 |     const RouteAdvertisementCapsule& other) const { | 
 |   return ip_address_ranges == other.ip_address_ranges; | 
 | } | 
 |  | 
 | }  // namespace quic |