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