xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/tools/devious_baton.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2023 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 #include "quiche/quic/tools/devious_baton.h"
6 
7 #include <cstdint>
8 #include <functional>
9 #include <memory>
10 
11 #include "absl/functional/bind_front.h"
12 #include "absl/status/status.h"
13 #include "absl/status/statusor.h"
14 #include "absl/strings/string_view.h"
15 #include "quiche/quic/core/crypto/quic_random.h"
16 #include "quiche/quic/core/quic_types.h"
17 #include "quiche/common/quiche_data_reader.h"
18 #include "quiche/common/wire_serialization.h"
19 #include "quiche/web_transport/complete_buffer_visitor.h"
20 #include "quiche/web_transport/web_transport.h"
21 
22 namespace quic {
23 
24 namespace {
25 
26 constexpr QuicByteCount kMaxPaddingSize = 64;
27 constexpr char kPaddingData[kMaxPaddingSize] = {0};
28 
Parse(absl::string_view message)29 absl::StatusOr<DeviousBatonValue> Parse(absl::string_view message) {
30   quiche::QuicheDataReader reader(message);
31   uint64_t padding_size;
32   if (!reader.ReadVarInt62(&padding_size)) {
33     return absl::InvalidArgumentError("Failed to read the padding size");
34   }
35   if (!reader.Seek(padding_size)) {
36     return absl::InvalidArgumentError("Failed to skip padding");
37   }
38   DeviousBatonValue value;
39   if (!reader.ReadUInt8(&value)) {
40     return absl::InvalidArgumentError("Failed to read the baton");
41   }
42   if (!reader.IsDoneReading()) {
43     return absl::InvalidArgumentError("Trailing data after the baton");
44   }
45   return value;
46 }
47 
Serialize(DeviousBatonValue value)48 std::string Serialize(DeviousBatonValue value) {
49   // Randomize padding size for extra deviousness.
50   QuicByteCount padding_size =
51       QuicRandom::GetInstance()->InsecureRandUint64() % kMaxPaddingSize;
52   absl::string_view padding(kPaddingData, padding_size);
53 
54   absl::StatusOr<std::string> result = quiche::SerializeIntoString(
55       quiche::WireStringWithLengthPrefix<quiche::WireVarInt62>(padding),
56       quiche::WireUint8(value));
57   QUICHE_DCHECK(result.ok());
58   return *std::move(result);
59 }
60 
61 class IncomingBidiBatonVisitor : public webtransport::CompleteBufferVisitor {
62  public:
IncomingBidiBatonVisitor(webtransport::Session & session,webtransport::Stream & stream)63   IncomingBidiBatonVisitor(webtransport::Session& session,
64                            webtransport::Stream& stream)
65       : CompleteBufferVisitor(
66             &stream, absl::bind_front(
67                          &IncomingBidiBatonVisitor::OnAllDataReceived, this)),
68         session_(&session) {}
69 
70  private:
OnAllDataReceived(std::string data)71   void OnAllDataReceived(std::string data) {
72     absl::StatusOr<DeviousBatonValue> value = Parse(data);
73     if (!value.ok()) {
74       session_->CloseSession(kDeviousBatonErrorBruh,
75                              absl::StrCat("Failed to parse incoming baton: ",
76                                           value.status().message()));
77       return;
78     }
79     DeviousBatonValue next_value = 1 + *value;
80     if (next_value != 0) {
81       SetOutgoingData(Serialize(*value + 1));
82     }
83   }
84 
85   webtransport::Session* session_;
86 };
87 
88 }  // namespace
89 
OnSessionReady()90 void DeviousBatonSessionVisitor::OnSessionReady() {
91   if (!is_server_) {
92     return;
93   }
94   for (int i = 0; i < count_; ++i) {
95     webtransport::Stream* stream = session_->OpenOutgoingUnidirectionalStream();
96     if (stream == nullptr) {
97       session_->CloseSession(
98           kDeviousBatonErrorDaYamn,
99           "Insufficient flow control when opening initial baton streams");
100       return;
101     }
102     stream->SetVisitor(std::make_unique<webtransport::CompleteBufferVisitor>(
103         stream, Serialize(initial_value_)));
104     stream->visitor()->OnCanWrite();
105   }
106 }
107 
OnSessionClosed(webtransport::SessionErrorCode error_code,const std::string & error_message)108 void DeviousBatonSessionVisitor::OnSessionClosed(
109     webtransport::SessionErrorCode error_code,
110     const std::string& error_message) {
111   QUICHE_LOG(INFO) << "Devious Baton session closed with error " << error_code
112                    << " (message: " << error_message << ")";
113 }
114 
OnIncomingBidirectionalStreamAvailable()115 void DeviousBatonSessionVisitor::OnIncomingBidirectionalStreamAvailable() {
116   while (true) {
117     webtransport::Stream* stream =
118         session_->AcceptIncomingBidirectionalStream();
119     if (stream == nullptr) {
120       return;
121     }
122     stream->SetVisitor(
123         std::make_unique<IncomingBidiBatonVisitor>(*session_, *stream));
124     stream->visitor()->OnCanRead();
125   }
126 }
127 
OnIncomingUnidirectionalStreamAvailable()128 void DeviousBatonSessionVisitor::OnIncomingUnidirectionalStreamAvailable() {
129   while (true) {
130     webtransport::Stream* stream =
131         session_->AcceptIncomingUnidirectionalStream();
132     if (stream == nullptr) {
133       return;
134     }
135     stream->SetVisitor(std::make_unique<webtransport::CompleteBufferVisitor>(
136         stream, CreateResponseCallback(
137                     &DeviousBatonSessionVisitor::SendBidirectionalBaton)));
138     stream->visitor()->OnCanRead();
139   }
140 }
141 
OnDatagramReceived(absl::string_view datagram)142 void DeviousBatonSessionVisitor::OnDatagramReceived(
143     absl::string_view datagram) {
144   // TODO(vasilvv): implement datagram behavior.
145 }
146 
OnCanCreateNewOutgoingBidirectionalStream()147 void DeviousBatonSessionVisitor::OnCanCreateNewOutgoingBidirectionalStream() {
148   while (!outgoing_bidi_batons_.empty()) {
149     webtransport::Stream* stream = session_->OpenOutgoingBidirectionalStream();
150     if (stream == nullptr) {
151       return;
152     }
153     stream->SetVisitor(std::make_unique<webtransport::CompleteBufferVisitor>(
154         stream, Serialize(outgoing_bidi_batons_.front()),
155         CreateResponseCallback(
156             &DeviousBatonSessionVisitor::SendUnidirectionalBaton)));
157     outgoing_bidi_batons_.pop_front();
158     stream->visitor()->OnCanWrite();
159   }
160 }
161 
OnCanCreateNewOutgoingUnidirectionalStream()162 void DeviousBatonSessionVisitor::OnCanCreateNewOutgoingUnidirectionalStream() {
163   while (!outgoing_unidi_batons_.empty()) {
164     webtransport::Stream* stream = session_->OpenOutgoingUnidirectionalStream();
165     if (stream == nullptr) {
166       return;
167     }
168     stream->SetVisitor(std::make_unique<webtransport::CompleteBufferVisitor>(
169         stream, Serialize(outgoing_unidi_batons_.front())));
170     outgoing_unidi_batons_.pop_front();
171     stream->visitor()->OnCanWrite();
172   }
173 }
174 
175 quiche::SingleUseCallback<void(std::string)>
CreateResponseCallback(SendFunction send_function)176 DeviousBatonSessionVisitor::CreateResponseCallback(SendFunction send_function) {
177   return [this, send_function](std::string data) {
178     absl::StatusOr<DeviousBatonValue> value = Parse(data);
179     if (!value.ok()) {
180       session_->CloseSession(kDeviousBatonErrorBruh,
181                              absl::StrCat("Failed to parse incoming baton: ",
182                                           value.status().message()));
183       return;
184     }
185     DeviousBatonValue new_value = 1 + *value;
186     if (new_value != 0) {
187       std::invoke(send_function, this, *value);
188     }
189   };
190 }
191 
192 }  // namespace quic
193