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