• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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