1 // Copyright (c) 2021 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef QUICHE_COMMON_CAPSULE_H_ 6 #define QUICHE_COMMON_CAPSULE_H_ 7 8 #include <cstdint> 9 #include <optional> 10 #include <string> 11 #include <vector> 12 13 #include "absl/status/statusor.h" 14 #include "absl/strings/string_view.h" 15 #include "absl/types/variant.h" 16 #include "quiche/common/platform/api/quiche_export.h" 17 #include "quiche/common/platform/api/quiche_logging.h" 18 #include "quiche/common/quiche_buffer_allocator.h" 19 #include "quiche/common/quiche_ip_address.h" 20 #include "quiche/web_transport/web_transport.h" 21 22 namespace quiche { 23 24 enum class CapsuleType : uint64_t { 25 // Casing in this enum matches the IETF specifications. 26 DATAGRAM = 0x00, // RFC 9297. 27 LEGACY_DATAGRAM = 0xff37a0, // draft-ietf-masque-h3-datagram-04. 28 LEGACY_DATAGRAM_WITHOUT_CONTEXT = 29 0xff37a5, // draft-ietf-masque-h3-datagram-05 to -08. 30 31 // <https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/> 32 CLOSE_WEBTRANSPORT_SESSION = 0x2843, 33 DRAIN_WEBTRANSPORT_SESSION = 0x78ae, 34 35 // draft-ietf-masque-connect-ip-03. 36 ADDRESS_ASSIGN = 0x1ECA6A00, 37 ADDRESS_REQUEST = 0x1ECA6A01, 38 ROUTE_ADVERTISEMENT = 0x1ECA6A02, 39 40 // <https://ietf-wg-webtrans.github.io/draft-webtransport-http2/draft-ietf-webtrans-http2.html#name-webtransport-capsules> 41 WT_RESET_STREAM = 0x190b4d39, 42 WT_STOP_SENDING = 0x190b4d3a, 43 WT_STREAM = 0x190b4d3b, 44 WT_STREAM_WITH_FIN = 0x190b4d3c, 45 // Should be removed as a result of 46 // <https://github.com/ietf-wg-webtrans/draft-webtransport-http2/issues/27>. 47 // WT_MAX_DATA = 0x190b4d3d, 48 WT_MAX_STREAM_DATA = 0x190b4d3e, 49 WT_MAX_STREAMS_BIDI = 0x190b4d3f, 50 WT_MAX_STREAMS_UNIDI = 0x190b4d40, 51 52 // TODO(b/264263113): implement those. 53 // PADDING = 0x190b4d38, 54 // WT_DATA_BLOCKED = 0x190b4d41, 55 // WT_STREAM_DATA_BLOCKED = 0x190b4d42, 56 // WT_STREAMS_BLOCKED_BIDI = 0x190b4d43, 57 // WT_STREAMS_BLOCKED_UNIDI = 0x190b4d44, 58 }; 59 60 QUICHE_EXPORT std::string CapsuleTypeToString(CapsuleType capsule_type); 61 QUICHE_EXPORT std::ostream& operator<<(std::ostream& os, 62 const CapsuleType& capsule_type); 63 64 // General. 65 struct QUICHE_EXPORT DatagramCapsule { 66 absl::string_view http_datagram_payload; 67 68 std::string ToString() const; capsule_typeDatagramCapsule69 CapsuleType capsule_type() const { return CapsuleType::DATAGRAM; } 70 bool operator==(const DatagramCapsule& other) const { 71 return http_datagram_payload == other.http_datagram_payload; 72 } 73 }; 74 75 struct QUICHE_EXPORT LegacyDatagramCapsule { 76 absl::string_view http_datagram_payload; 77 78 std::string ToString() const; capsule_typeLegacyDatagramCapsule79 CapsuleType capsule_type() const { return CapsuleType::LEGACY_DATAGRAM; } 80 bool operator==(const LegacyDatagramCapsule& other) const { 81 return http_datagram_payload == other.http_datagram_payload; 82 } 83 }; 84 85 struct QUICHE_EXPORT LegacyDatagramWithoutContextCapsule { 86 absl::string_view http_datagram_payload; 87 88 std::string ToString() const; capsule_typeLegacyDatagramWithoutContextCapsule89 CapsuleType capsule_type() const { 90 return CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT; 91 } 92 bool operator==(const LegacyDatagramWithoutContextCapsule& other) const { 93 return http_datagram_payload == other.http_datagram_payload; 94 } 95 }; 96 97 // WebTransport over HTTP/3. 98 struct QUICHE_EXPORT CloseWebTransportSessionCapsule { 99 webtransport::SessionErrorCode error_code; 100 absl::string_view error_message; 101 102 std::string ToString() const; capsule_typeCloseWebTransportSessionCapsule103 CapsuleType capsule_type() const { 104 return CapsuleType::CLOSE_WEBTRANSPORT_SESSION; 105 } 106 bool operator==(const CloseWebTransportSessionCapsule& other) const { 107 return error_code == other.error_code && 108 error_message == other.error_message; 109 } 110 }; 111 struct QUICHE_EXPORT DrainWebTransportSessionCapsule { 112 std::string ToString() const; capsule_typeDrainWebTransportSessionCapsule113 CapsuleType capsule_type() const { 114 return CapsuleType::DRAIN_WEBTRANSPORT_SESSION; 115 } 116 bool operator==(const DrainWebTransportSessionCapsule&) const { return true; } 117 }; 118 119 // MASQUE CONNECT-IP. 120 struct QUICHE_EXPORT PrefixWithId { 121 uint64_t request_id; 122 quiche::QuicheIpPrefix ip_prefix; 123 bool operator==(const PrefixWithId& other) const; 124 }; 125 struct QUICHE_EXPORT IpAddressRange { 126 quiche::QuicheIpAddress start_ip_address; 127 quiche::QuicheIpAddress end_ip_address; 128 uint8_t ip_protocol; 129 bool operator==(const IpAddressRange& other) const; 130 }; 131 132 struct QUICHE_EXPORT AddressAssignCapsule { 133 std::vector<PrefixWithId> assigned_addresses; 134 bool operator==(const AddressAssignCapsule& other) const; 135 std::string ToString() const; capsule_typeAddressAssignCapsule136 CapsuleType capsule_type() const { return CapsuleType::ADDRESS_ASSIGN; } 137 }; 138 struct QUICHE_EXPORT AddressRequestCapsule { 139 std::vector<PrefixWithId> requested_addresses; 140 bool operator==(const AddressRequestCapsule& other) const; 141 std::string ToString() const; capsule_typeAddressRequestCapsule142 CapsuleType capsule_type() const { return CapsuleType::ADDRESS_REQUEST; } 143 }; 144 struct QUICHE_EXPORT RouteAdvertisementCapsule { 145 std::vector<IpAddressRange> ip_address_ranges; 146 bool operator==(const RouteAdvertisementCapsule& other) const; 147 std::string ToString() const; capsule_typeRouteAdvertisementCapsule148 CapsuleType capsule_type() const { return CapsuleType::ROUTE_ADVERTISEMENT; } 149 }; 150 struct QUICHE_EXPORT UnknownCapsule { 151 uint64_t type; 152 absl::string_view payload; 153 154 std::string ToString() const; capsule_typeUnknownCapsule155 CapsuleType capsule_type() const { return static_cast<CapsuleType>(type); } 156 bool operator==(const UnknownCapsule& other) const { 157 return type == other.type && payload == other.payload; 158 } 159 }; 160 161 // WebTransport over HTTP/2. 162 struct QUICHE_EXPORT WebTransportStreamDataCapsule { 163 webtransport::StreamId stream_id; 164 absl::string_view data; 165 bool fin; 166 167 bool operator==(const WebTransportStreamDataCapsule& other) const; 168 std::string ToString() const; capsule_typeWebTransportStreamDataCapsule169 CapsuleType capsule_type() const { 170 return fin ? CapsuleType::WT_STREAM_WITH_FIN : CapsuleType::WT_STREAM; 171 } 172 }; 173 struct QUICHE_EXPORT WebTransportResetStreamCapsule { 174 webtransport::StreamId stream_id; 175 uint64_t error_code; 176 177 bool operator==(const WebTransportResetStreamCapsule& other) const; 178 std::string ToString() const; capsule_typeWebTransportResetStreamCapsule179 CapsuleType capsule_type() const { return CapsuleType::WT_RESET_STREAM; } 180 }; 181 struct QUICHE_EXPORT WebTransportStopSendingCapsule { 182 webtransport::StreamId stream_id; 183 uint64_t error_code; 184 185 bool operator==(const WebTransportStopSendingCapsule& other) const; 186 std::string ToString() const; capsule_typeWebTransportStopSendingCapsule187 CapsuleType capsule_type() const { return CapsuleType::WT_STOP_SENDING; } 188 }; 189 struct QUICHE_EXPORT WebTransportMaxStreamDataCapsule { 190 webtransport::StreamId stream_id; 191 uint64_t max_stream_data; 192 193 bool operator==(const WebTransportMaxStreamDataCapsule& other) const; 194 std::string ToString() const; capsule_typeWebTransportMaxStreamDataCapsule195 CapsuleType capsule_type() const { return CapsuleType::WT_MAX_STREAM_DATA; } 196 }; 197 struct QUICHE_EXPORT WebTransportMaxStreamsCapsule { 198 webtransport::StreamType stream_type; 199 uint64_t max_stream_count; 200 201 bool operator==(const WebTransportMaxStreamsCapsule& other) const; 202 std::string ToString() const; capsule_typeWebTransportMaxStreamsCapsule203 CapsuleType capsule_type() const { 204 return stream_type == webtransport::StreamType::kBidirectional 205 ? CapsuleType::WT_MAX_STREAMS_BIDI 206 : CapsuleType::WT_MAX_STREAMS_UNIDI; 207 } 208 }; 209 210 // Capsule from RFC 9297. 211 // IMPORTANT NOTE: Capsule does not own any of the absl::string_view memory it 212 // points to. Strings saved into a capsule must outlive the capsule object. Any 213 // code that sees a capsule in a callback needs to either process it immediately 214 // or perform its own deep copy. 215 class QUICHE_EXPORT Capsule { 216 public: 217 static Capsule Datagram( 218 absl::string_view http_datagram_payload = absl::string_view()); 219 static Capsule LegacyDatagram( 220 absl::string_view http_datagram_payload = absl::string_view()); 221 static Capsule LegacyDatagramWithoutContext( 222 absl::string_view http_datagram_payload = absl::string_view()); 223 static Capsule CloseWebTransportSession( 224 webtransport::SessionErrorCode error_code = 0, 225 absl::string_view error_message = ""); 226 static Capsule AddressRequest(); 227 static Capsule AddressAssign(); 228 static Capsule RouteAdvertisement(); 229 static Capsule Unknown( 230 uint64_t capsule_type, 231 absl::string_view unknown_capsule_data = absl::string_view()); 232 233 template <typename CapsuleStruct> Capsule(CapsuleStruct capsule)234 explicit Capsule(CapsuleStruct capsule) : capsule_(std::move(capsule)) {} 235 bool operator==(const Capsule& other) const; 236 237 // Human-readable information string for debugging purposes. 238 std::string ToString() const; 239 friend QUICHE_EXPORT std::ostream& operator<<(std::ostream& os, 240 const Capsule& capsule); 241 capsule_type()242 CapsuleType capsule_type() const { 243 return absl::visit( 244 [](const auto& capsule) { return capsule.capsule_type(); }, capsule_); 245 } datagram_capsule()246 DatagramCapsule& datagram_capsule() { 247 return absl::get<DatagramCapsule>(capsule_); 248 } datagram_capsule()249 const DatagramCapsule& datagram_capsule() const { 250 return absl::get<DatagramCapsule>(capsule_); 251 } legacy_datagram_capsule()252 LegacyDatagramCapsule& legacy_datagram_capsule() { 253 return absl::get<LegacyDatagramCapsule>(capsule_); 254 } legacy_datagram_capsule()255 const LegacyDatagramCapsule& legacy_datagram_capsule() const { 256 return absl::get<LegacyDatagramCapsule>(capsule_); 257 } 258 LegacyDatagramWithoutContextCapsule& legacy_datagram_without_context_capsule()259 legacy_datagram_without_context_capsule() { 260 return absl::get<LegacyDatagramWithoutContextCapsule>(capsule_); 261 } 262 const LegacyDatagramWithoutContextCapsule& legacy_datagram_without_context_capsule()263 legacy_datagram_without_context_capsule() const { 264 return absl::get<LegacyDatagramWithoutContextCapsule>(capsule_); 265 } close_web_transport_session_capsule()266 CloseWebTransportSessionCapsule& close_web_transport_session_capsule() { 267 return absl::get<CloseWebTransportSessionCapsule>(capsule_); 268 } close_web_transport_session_capsule()269 const CloseWebTransportSessionCapsule& close_web_transport_session_capsule() 270 const { 271 return absl::get<CloseWebTransportSessionCapsule>(capsule_); 272 } address_request_capsule()273 AddressRequestCapsule& address_request_capsule() { 274 return absl::get<AddressRequestCapsule>(capsule_); 275 } address_request_capsule()276 const AddressRequestCapsule& address_request_capsule() const { 277 return absl::get<AddressRequestCapsule>(capsule_); 278 } address_assign_capsule()279 AddressAssignCapsule& address_assign_capsule() { 280 return absl::get<AddressAssignCapsule>(capsule_); 281 } address_assign_capsule()282 const AddressAssignCapsule& address_assign_capsule() const { 283 return absl::get<AddressAssignCapsule>(capsule_); 284 } route_advertisement_capsule()285 RouteAdvertisementCapsule& route_advertisement_capsule() { 286 return absl::get<RouteAdvertisementCapsule>(capsule_); 287 } route_advertisement_capsule()288 const RouteAdvertisementCapsule& route_advertisement_capsule() const { 289 return absl::get<RouteAdvertisementCapsule>(capsule_); 290 } web_transport_stream_data()291 WebTransportStreamDataCapsule& web_transport_stream_data() { 292 return absl::get<WebTransportStreamDataCapsule>(capsule_); 293 } web_transport_stream_data()294 const WebTransportStreamDataCapsule& web_transport_stream_data() const { 295 return absl::get<WebTransportStreamDataCapsule>(capsule_); 296 } web_transport_reset_stream()297 WebTransportResetStreamCapsule& web_transport_reset_stream() { 298 return absl::get<WebTransportResetStreamCapsule>(capsule_); 299 } web_transport_reset_stream()300 const WebTransportResetStreamCapsule& web_transport_reset_stream() const { 301 return absl::get<WebTransportResetStreamCapsule>(capsule_); 302 } web_transport_stop_sending()303 WebTransportStopSendingCapsule& web_transport_stop_sending() { 304 return absl::get<WebTransportStopSendingCapsule>(capsule_); 305 } web_transport_stop_sending()306 const WebTransportStopSendingCapsule& web_transport_stop_sending() const { 307 return absl::get<WebTransportStopSendingCapsule>(capsule_); 308 } web_transport_max_stream_data()309 WebTransportMaxStreamDataCapsule& web_transport_max_stream_data() { 310 return absl::get<WebTransportMaxStreamDataCapsule>(capsule_); 311 } web_transport_max_stream_data()312 const WebTransportMaxStreamDataCapsule& web_transport_max_stream_data() 313 const { 314 return absl::get<WebTransportMaxStreamDataCapsule>(capsule_); 315 } web_transport_max_streams()316 WebTransportMaxStreamsCapsule& web_transport_max_streams() { 317 return absl::get<WebTransportMaxStreamsCapsule>(capsule_); 318 } web_transport_max_streams()319 const WebTransportMaxStreamsCapsule& web_transport_max_streams() const { 320 return absl::get<WebTransportMaxStreamsCapsule>(capsule_); 321 } unknown_capsule()322 UnknownCapsule& unknown_capsule() { 323 return absl::get<UnknownCapsule>(capsule_); 324 } unknown_capsule()325 const UnknownCapsule& unknown_capsule() const { 326 return absl::get<UnknownCapsule>(capsule_); 327 } 328 329 private: 330 absl::variant<DatagramCapsule, LegacyDatagramCapsule, 331 LegacyDatagramWithoutContextCapsule, 332 CloseWebTransportSessionCapsule, 333 DrainWebTransportSessionCapsule, AddressRequestCapsule, 334 AddressAssignCapsule, RouteAdvertisementCapsule, 335 WebTransportStreamDataCapsule, WebTransportResetStreamCapsule, 336 WebTransportStopSendingCapsule, WebTransportMaxStreamsCapsule, 337 WebTransportMaxStreamDataCapsule, UnknownCapsule> 338 capsule_; 339 }; 340 341 namespace test { 342 class CapsuleParserPeer; 343 } // namespace test 344 345 class QUICHE_EXPORT CapsuleParser { 346 public: 347 class QUICHE_EXPORT Visitor { 348 public: ~Visitor()349 virtual ~Visitor() {} 350 351 // Called when a capsule has been successfully parsed. The return value 352 // indicates whether the contents of the capsule are valid: if false is 353 // returned, the parse operation will be considered failed and 354 // OnCapsuleParseFailure will be called. Note that since Capsule does not 355 // own the memory backing its string_views, that memory is only valid until 356 // this callback returns. Visitors that wish to access the capsule later 357 // MUST make a deep copy before this returns. 358 virtual bool OnCapsule(const Capsule& capsule) = 0; 359 360 virtual void OnCapsuleParseFailure(absl::string_view error_message) = 0; 361 }; 362 363 // |visitor| must be non-null, and must outlive CapsuleParser. 364 explicit CapsuleParser(Visitor* visitor); 365 366 // Ingests a capsule fragment (any fragment of bytes from the capsule data 367 // stream) and parses and complete capsules it encounters. Returns false if a 368 // parsing error occurred. 369 bool IngestCapsuleFragment(absl::string_view capsule_fragment); 370 371 void ErrorIfThereIsRemainingBufferedData(); 372 373 friend class test::CapsuleParserPeer; 374 375 private: 376 // Attempts to parse a single capsule from |buffered_data_|. If a full capsule 377 // is not available, returns 0. If a parsing error occurs, returns an error. 378 // Otherwise, returns the number of bytes in the parsed capsule. 379 absl::StatusOr<size_t> AttemptParseCapsule(); 380 void ReportParseFailure(absl::string_view error_message); 381 382 // Whether a parsing error has occurred. 383 bool parsing_error_occurred_ = false; 384 // Visitor which will receive callbacks, unowned. 385 Visitor* visitor_; 386 387 std::string buffered_data_; 388 }; 389 390 // Serializes |capsule| into a newly allocated buffer. 391 QUICHE_EXPORT quiche::QuicheBuffer SerializeCapsule( 392 const Capsule& capsule, quiche::QuicheBufferAllocator* allocator); 393 394 // Serializes the header for a datagram of size |datagram_size|. 395 QUICHE_EXPORT QuicheBuffer SerializeDatagramCapsuleHeader( 396 uint64_t datagram_size, QuicheBufferAllocator* allocator); 397 398 // Serializes the header for a WT_STREAM or a WT_STREAM_WITH_FIN capsule. 399 QUICHE_EXPORT QuicheBuffer SerializeWebTransportStreamCapsuleHeader( 400 webtransport::StreamId stream_id, bool fin, uint64_t write_size, 401 QuicheBufferAllocator* allocator); 402 403 } // namespace quiche 404 405 #endif // QUICHE_COMMON_CAPSULE_H_ 406