1 // Copyright 2023 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/core/ext/transport/chttp2/transport/frame.h"
16
17 #include <grpc/support/port_platform.h>
18 #include <stddef.h>
19
20 #include <cstdint>
21 #include <utility>
22
23 #include "absl/log/check.h"
24 #include "absl/status/status.h"
25 #include "absl/strings/str_cat.h"
26 #include "src/core/util/crash.h"
27
28 namespace grpc_core {
29
30 namespace {
31
32 constexpr uint8_t kFrameTypeData = 0;
33 constexpr uint8_t kFrameTypeHeader = 1;
34 constexpr uint8_t kFrameTypeContinuation = 9;
35 constexpr uint8_t kFrameTypeRstStream = 3;
36 constexpr uint8_t kFrameTypeSettings = 4;
37 constexpr uint8_t kFrameTypePing = 6;
38 constexpr uint8_t kFrameTypeGoaway = 7;
39 constexpr uint8_t kFrameTypeWindowUpdate = 8;
40 constexpr uint8_t kFrameTypePushPromise = 5;
41 constexpr uint8_t kFrameTypeSecurity = 200;
42
43 constexpr uint8_t kFlagEndStream = 1;
44 constexpr uint8_t kFlagAck = 1;
45 constexpr uint8_t kFlagEndHeaders = 4;
46 constexpr uint8_t kFlagPadded = 8;
47 constexpr uint8_t kFlagPriority = 0x20;
48
49 constexpr size_t kFrameHeaderSize = 9;
50
Write2b(uint16_t x,uint8_t * output)51 void Write2b(uint16_t x, uint8_t* output) {
52 output[0] = static_cast<uint8_t>(x >> 8);
53 output[1] = static_cast<uint8_t>(x);
54 }
55
Read2b(const uint8_t * input)56 uint16_t Read2b(const uint8_t* input) {
57 return static_cast<uint16_t>(input[0]) << 8 | static_cast<uint16_t>(input[1]);
58 }
59
Write3b(uint32_t x,uint8_t * output)60 void Write3b(uint32_t x, uint8_t* output) {
61 CHECK_LT(x, 16777216u);
62 output[0] = static_cast<uint8_t>(x >> 16);
63 output[1] = static_cast<uint8_t>(x >> 8);
64 output[2] = static_cast<uint8_t>(x);
65 }
66
Read3b(const uint8_t * input)67 uint32_t Read3b(const uint8_t* input) {
68 return static_cast<uint32_t>(input[0]) << 16 |
69 static_cast<uint32_t>(input[1]) << 8 | static_cast<uint32_t>(input[2]);
70 }
71
Write4b(uint32_t x,uint8_t * output)72 void Write4b(uint32_t x, uint8_t* output) {
73 output[0] = static_cast<uint8_t>(x >> 24);
74 output[1] = static_cast<uint8_t>(x >> 16);
75 output[2] = static_cast<uint8_t>(x >> 8);
76 output[3] = static_cast<uint8_t>(x);
77 }
78
Read4b(const uint8_t * input)79 uint32_t Read4b(const uint8_t* input) {
80 return static_cast<uint32_t>(input[0]) << 24 |
81 static_cast<uint32_t>(input[1]) << 16 |
82 static_cast<uint32_t>(input[2]) << 8 | static_cast<uint32_t>(input[3]);
83 }
84
Write8b(uint64_t x,uint8_t * output)85 void Write8b(uint64_t x, uint8_t* output) {
86 output[0] = static_cast<uint8_t>(x >> 56);
87 output[1] = static_cast<uint8_t>(x >> 48);
88 output[2] = static_cast<uint8_t>(x >> 40);
89 output[3] = static_cast<uint8_t>(x >> 32);
90 output[4] = static_cast<uint8_t>(x >> 24);
91 output[5] = static_cast<uint8_t>(x >> 16);
92 output[6] = static_cast<uint8_t>(x >> 8);
93 output[7] = static_cast<uint8_t>(x);
94 }
95
Read8b(const uint8_t * input)96 uint64_t Read8b(const uint8_t* input) {
97 return static_cast<uint64_t>(input[0]) << 56 |
98 static_cast<uint64_t>(input[1]) << 48 |
99 static_cast<uint64_t>(input[2]) << 40 |
100 static_cast<uint64_t>(input[3]) << 32 |
101 static_cast<uint64_t>(input[4]) << 24 |
102 static_cast<uint64_t>(input[5]) << 16 |
103 static_cast<uint64_t>(input[6]) << 8 | static_cast<uint64_t>(input[7]);
104 }
105
MaybeFlag(bool condition,uint8_t flag_mask)106 uint8_t MaybeFlag(bool condition, uint8_t flag_mask) {
107 return condition ? flag_mask : 0;
108 }
109
ExtractFlag(uint8_t flags,uint8_t flag_mask)110 bool ExtractFlag(uint8_t flags, uint8_t flag_mask) {
111 return (flags & flag_mask) != 0;
112 }
113
114 class SerializeExtraBytesRequired {
115 public:
operator ()(const Http2DataFrame &)116 size_t operator()(const Http2DataFrame&) { return 0; }
operator ()(const Http2HeaderFrame &)117 size_t operator()(const Http2HeaderFrame&) { return 0; }
operator ()(const Http2ContinuationFrame &)118 size_t operator()(const Http2ContinuationFrame&) { return 0; }
operator ()(const Http2RstStreamFrame &)119 size_t operator()(const Http2RstStreamFrame&) { return 4; }
operator ()(const Http2SettingsFrame & f)120 size_t operator()(const Http2SettingsFrame& f) {
121 return 6 * f.settings.size();
122 }
operator ()(const Http2PingFrame &)123 size_t operator()(const Http2PingFrame&) { return 8; }
operator ()(const Http2GoawayFrame &)124 size_t operator()(const Http2GoawayFrame&) { return 8; }
operator ()(const Http2WindowUpdateFrame &)125 size_t operator()(const Http2WindowUpdateFrame&) { return 4; }
operator ()(const Http2SecurityFrame &)126 size_t operator()(const Http2SecurityFrame&) { return 0; }
operator ()(const Http2UnknownFrame &)127 size_t operator()(const Http2UnknownFrame&) { Crash("unreachable"); }
128 };
129
130 class SerializeHeaderAndPayload {
131 public:
SerializeHeaderAndPayload(size_t extra_bytes,SliceBuffer & out)132 SerializeHeaderAndPayload(size_t extra_bytes, SliceBuffer& out)
133 : out_(out),
134 extra_bytes_(MutableSlice::CreateUninitialized(extra_bytes)) {}
135
operator ()(Http2DataFrame & frame)136 void operator()(Http2DataFrame& frame) {
137 auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
138 Http2FrameHeader{
139 static_cast<uint32_t>(frame.payload.Length()), kFrameTypeData,
140 MaybeFlag(frame.end_stream, kFlagEndStream), frame.stream_id}
141 .Serialize(hdr.begin());
142 out_.AppendIndexed(Slice(std::move(hdr)));
143 out_.TakeAndAppend(frame.payload);
144 }
145
operator ()(Http2HeaderFrame & frame)146 void operator()(Http2HeaderFrame& frame) {
147 auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
148 Http2FrameHeader{
149 static_cast<uint32_t>(frame.payload.Length()), kFrameTypeHeader,
150 static_cast<uint8_t>(MaybeFlag(frame.end_headers, kFlagEndHeaders) |
151 MaybeFlag(frame.end_stream, kFlagEndStream)),
152 frame.stream_id}
153 .Serialize(hdr.begin());
154 out_.AppendIndexed(Slice(std::move(hdr)));
155 out_.TakeAndAppend(frame.payload);
156 }
157
operator ()(Http2ContinuationFrame & frame)158 void operator()(Http2ContinuationFrame& frame) {
159 auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
160 Http2FrameHeader{
161 static_cast<uint32_t>(frame.payload.Length()), kFrameTypeContinuation,
162 static_cast<uint8_t>(MaybeFlag(frame.end_headers, kFlagEndHeaders)),
163 frame.stream_id}
164 .Serialize(hdr.begin());
165 out_.AppendIndexed(Slice(std::move(hdr)));
166 out_.TakeAndAppend(frame.payload);
167 }
168
operator ()(Http2RstStreamFrame & frame)169 void operator()(Http2RstStreamFrame& frame) {
170 auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 4);
171 Http2FrameHeader{4, kFrameTypeRstStream, 0, frame.stream_id}.Serialize(
172 hdr_and_payload.begin());
173 Write4b(frame.error_code, hdr_and_payload.begin() + kFrameHeaderSize);
174 out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
175 }
176
operator ()(Http2SettingsFrame & frame)177 void operator()(Http2SettingsFrame& frame) {
178 // Six bytes per setting (u16 id, u32 value)
179 const size_t payload_size = 6 * frame.settings.size();
180 auto hdr_and_payload =
181 extra_bytes_.TakeFirst(kFrameHeaderSize + payload_size);
182 Http2FrameHeader{static_cast<uint32_t>(payload_size), kFrameTypeSettings,
183 MaybeFlag(frame.ack, kFlagAck), 0}
184 .Serialize(hdr_and_payload.begin());
185 size_t offset = kFrameHeaderSize;
186 for (auto& setting : frame.settings) {
187 Write2b(setting.id, hdr_and_payload.begin() + offset);
188 Write4b(setting.value, hdr_and_payload.begin() + offset + 2);
189 offset += 6;
190 }
191 out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
192 }
193
operator ()(Http2PingFrame & frame)194 void operator()(Http2PingFrame& frame) {
195 auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 8);
196 Http2FrameHeader{8, kFrameTypePing, MaybeFlag(frame.ack, kFlagAck), 0}
197 .Serialize(hdr_and_payload.begin());
198 Write8b(frame.opaque, hdr_and_payload.begin() + kFrameHeaderSize);
199 out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
200 }
201
operator ()(Http2GoawayFrame & frame)202 void operator()(Http2GoawayFrame& frame) {
203 auto hdr_and_fixed_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 8);
204 Http2FrameHeader{static_cast<uint32_t>(8 + frame.debug_data.length()),
205 kFrameTypeGoaway, 0, 0}
206 .Serialize(hdr_and_fixed_payload.begin());
207 Write4b(frame.last_stream_id,
208 hdr_and_fixed_payload.begin() + kFrameHeaderSize);
209 Write4b(frame.error_code,
210 hdr_and_fixed_payload.begin() + kFrameHeaderSize + 4);
211 out_.AppendIndexed(Slice(std::move(hdr_and_fixed_payload)));
212 out_.AppendIndexed(std::move(frame.debug_data));
213 }
214
operator ()(Http2WindowUpdateFrame & frame)215 void operator()(Http2WindowUpdateFrame& frame) {
216 auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 4);
217 Http2FrameHeader{4, kFrameTypeWindowUpdate, 0, frame.stream_id}.Serialize(
218 hdr_and_payload.begin());
219 Write4b(frame.increment, hdr_and_payload.begin() + kFrameHeaderSize);
220 out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
221 }
222
operator ()(Http2SecurityFrame & frame)223 void operator()(Http2SecurityFrame& frame) {
224 auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
225 Http2FrameHeader{static_cast<uint32_t>(frame.payload.Length()),
226 kFrameTypeSecurity, 0, 0}
227 .Serialize(hdr.begin());
228 out_.AppendIndexed(Slice(std::move(hdr)));
229 out_.TakeAndAppend(frame.payload);
230 }
231
operator ()(Http2UnknownFrame &)232 void operator()(Http2UnknownFrame&) { Crash("unreachable"); }
233
234 private:
235 SliceBuffer& out_;
236 MutableSlice extra_bytes_;
237 };
238
StripPadding(SliceBuffer & payload)239 absl::Status StripPadding(SliceBuffer& payload) {
240 if (payload.Length() < 1) {
241 return absl::InternalError("padding flag set but no padding byte");
242 }
243 uint8_t padding_bytes;
244 payload.MoveFirstNBytesIntoBuffer(1, &padding_bytes);
245 if (payload.Length() < padding_bytes) {
246 return absl::InternalError("padding flag set but not enough padding bytes");
247 }
248 payload.RemoveLastNBytes(padding_bytes);
249 return absl::OkStatus();
250 }
251
ParseDataFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)252 absl::StatusOr<Http2DataFrame> ParseDataFrame(const Http2FrameHeader& hdr,
253 SliceBuffer& payload) {
254 if (hdr.stream_id == 0) {
255 return absl::InternalError(
256 absl::StrCat("invalid stream id: ", hdr.ToString()));
257 }
258
259 if (hdr.flags & kFlagPadded) {
260 auto s = StripPadding(payload);
261 if (!s.ok()) return s;
262 }
263
264 return Http2DataFrame{hdr.stream_id, ExtractFlag(hdr.flags, kFlagEndStream),
265 std::move(payload)};
266 }
267
ParseHeaderFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)268 absl::StatusOr<Http2HeaderFrame> ParseHeaderFrame(const Http2FrameHeader& hdr,
269 SliceBuffer& payload) {
270 if (hdr.stream_id == 0) {
271 return absl::InternalError(
272 absl::StrCat("invalid stream id: ", hdr.ToString()));
273 }
274
275 if (hdr.flags & kFlagPadded) {
276 auto s = StripPadding(payload);
277 if (!s.ok()) return s;
278 }
279
280 if (hdr.flags & kFlagPriority) {
281 if (payload.Length() < 5) {
282 return absl::InternalError(
283 absl::StrCat("invalid priority payload: ", hdr.ToString()));
284 }
285 uint8_t trash[5];
286 payload.MoveFirstNBytesIntoBuffer(5, trash);
287 }
288
289 return Http2HeaderFrame{
290 hdr.stream_id, ExtractFlag(hdr.flags, kFlagEndHeaders),
291 ExtractFlag(hdr.flags, kFlagEndStream), std::move(payload)};
292 }
293
ParseContinuationFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)294 absl::StatusOr<Http2ContinuationFrame> ParseContinuationFrame(
295 const Http2FrameHeader& hdr, SliceBuffer& payload) {
296 if (hdr.stream_id == 0) {
297 return absl::InternalError(
298 absl::StrCat("invalid stream id: ", hdr.ToString()));
299 }
300
301 return Http2ContinuationFrame{hdr.stream_id,
302 ExtractFlag(hdr.flags, kFlagEndHeaders),
303 std::move(payload)};
304 }
305
ParseRstStreamFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)306 absl::StatusOr<Http2RstStreamFrame> ParseRstStreamFrame(
307 const Http2FrameHeader& hdr, SliceBuffer& payload) {
308 if (payload.Length() != 4) {
309 return absl::InternalError(
310 absl::StrCat("invalid rst stream payload: ", hdr.ToString()));
311 }
312
313 if (hdr.stream_id == 0) {
314 return absl::InternalError(
315 absl::StrCat("invalid stream id: ", hdr.ToString()));
316 }
317
318 uint8_t buffer[4];
319 payload.CopyToBuffer(buffer);
320
321 return Http2RstStreamFrame{hdr.stream_id, Read4b(buffer)};
322 }
323
ParseSettingsFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)324 absl::StatusOr<Http2SettingsFrame> ParseSettingsFrame(
325 const Http2FrameHeader& hdr, SliceBuffer& payload) {
326 if (hdr.stream_id != 0) {
327 return absl::InternalError(
328 absl::StrCat("invalid stream id: ", hdr.ToString()));
329 }
330 if (hdr.flags == kFlagAck) {
331 if (payload.Length() != 0) {
332 return absl::InternalError(
333 absl::StrCat("invalid settings ack length: ", hdr.ToString()));
334 }
335 return Http2SettingsFrame{true, {}};
336 }
337
338 if (payload.Length() % 6 != 0) {
339 return absl::InternalError(
340 absl::StrCat("invalid settings payload: ", hdr.ToString(),
341 " -- settings must be multiples of 6 bytes long"));
342 }
343
344 Http2SettingsFrame frame{false, {}};
345 while (payload.Length() != 0) {
346 uint8_t buffer[6];
347 payload.MoveFirstNBytesIntoBuffer(6, buffer);
348 frame.settings.push_back({
349 Read2b(buffer),
350 Read4b(buffer + 2),
351 });
352 }
353 return std::move(frame);
354 }
355
ParsePingFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)356 absl::StatusOr<Http2PingFrame> ParsePingFrame(const Http2FrameHeader& hdr,
357 SliceBuffer& payload) {
358 if (payload.Length() != 8) {
359 return absl::InternalError(
360 absl::StrCat("invalid ping payload: ", hdr.ToString()));
361 }
362
363 if (hdr.stream_id != 0) {
364 return absl::InternalError(
365 absl::StrCat("invalid ping stream id: ", hdr.ToString()));
366 }
367
368 bool ack;
369 switch (hdr.flags) {
370 case 0:
371 ack = false;
372 break;
373 case kFlagAck:
374 ack = true;
375 break;
376 default:
377 return absl::InternalError(
378 absl::StrCat("invalid ping flags: ", hdr.ToString()));
379 }
380
381 uint8_t buffer[8];
382 payload.CopyToBuffer(buffer);
383
384 return Http2PingFrame{ack, Read8b(buffer)};
385 }
386
ParseGoawayFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)387 absl::StatusOr<Http2GoawayFrame> ParseGoawayFrame(const Http2FrameHeader& hdr,
388 SliceBuffer& payload) {
389 if (payload.Length() < 8) {
390 return absl::InternalError(
391 absl::StrCat("invalid goaway payload: ", hdr.ToString(),
392 " -- must be at least 8 bytes"));
393 }
394
395 if (hdr.stream_id != 0) {
396 return absl::InternalError(
397 absl::StrCat("invalid goaway stream id: ", hdr.ToString()));
398 }
399
400 if (hdr.flags != 0) {
401 return absl::InternalError(
402 absl::StrCat("invalid goaway flags: ", hdr.ToString()));
403 }
404
405 uint8_t buffer[8];
406 payload.MoveFirstNBytesIntoBuffer(8, buffer);
407 return Http2GoawayFrame{Read4b(buffer), Read4b(buffer + 4),
408 payload.JoinIntoSlice()};
409 }
410
ParseWindowUpdateFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)411 absl::StatusOr<Http2WindowUpdateFrame> ParseWindowUpdateFrame(
412 const Http2FrameHeader& hdr, SliceBuffer& payload) {
413 if (payload.Length() != 4) {
414 return absl::InternalError(
415 absl::StrCat("invalid window update payload: ", hdr.ToString(),
416 " -- must be 4 bytes"));
417 }
418
419 if (hdr.flags != 0) {
420 return absl::InternalError(
421 absl::StrCat("invalid window update flags: ", hdr.ToString()));
422 }
423
424 uint8_t buffer[4];
425 payload.CopyToBuffer(buffer);
426 return Http2WindowUpdateFrame{hdr.stream_id, Read4b(buffer)};
427 }
428
ParseSecurityFrame(const Http2FrameHeader &,SliceBuffer & payload)429 absl::StatusOr<Http2SecurityFrame> ParseSecurityFrame(
430 const Http2FrameHeader& /*hdr*/, SliceBuffer& payload) {
431 return Http2SecurityFrame{std::move(payload)};
432 }
433
434 } // namespace
435
Serialize(uint8_t * output) const436 void Http2FrameHeader::Serialize(uint8_t* output) const {
437 Write3b(length, output);
438 output[3] = type;
439 output[4] = flags;
440 Write4b(stream_id, output + 5);
441 }
442
Parse(const uint8_t * input)443 Http2FrameHeader Http2FrameHeader::Parse(const uint8_t* input) {
444 return Http2FrameHeader{Read3b(input), input[3], input[4], Read4b(input + 5)};
445 }
446
447 namespace {
Http2FrameTypeString(uint8_t frame_type)448 std::string Http2FrameTypeString(uint8_t frame_type) {
449 switch (frame_type) {
450 case kFrameTypeData:
451 return "DATA";
452 case kFrameTypeHeader:
453 return "HEADER";
454 case kFrameTypeContinuation:
455 return "CONTINUATION";
456 case kFrameTypeRstStream:
457 return "RST_STREAM";
458 case kFrameTypeSettings:
459 return "SETTINGS";
460 case kFrameTypeGoaway:
461 return "GOAWAY";
462 case kFrameTypeWindowUpdate:
463 return "WINDOW_UPDATE";
464 case kFrameTypePing:
465 return "PING";
466 case kFrameTypeSecurity:
467 return "SECURITY";
468 }
469 return absl::StrCat("UNKNOWN(", frame_type, ")");
470 }
471 } // namespace
472
ToString() const473 std::string Http2FrameHeader::ToString() const {
474 return absl::StrCat("{", Http2FrameTypeString(type), ": flags=", flags,
475 ", stream_id=", stream_id, ", length=", length, "}");
476 }
477
Serialize(absl::Span<Http2Frame> frames,SliceBuffer & out)478 void Serialize(absl::Span<Http2Frame> frames, SliceBuffer& out) {
479 size_t buffer_needed = 0;
480 for (auto& frame : frames) {
481 // Bytes needed for framing
482 buffer_needed += kFrameHeaderSize;
483 // Bytes needed for frame payload
484 buffer_needed += absl::visit(SerializeExtraBytesRequired(), frame);
485 }
486 SerializeHeaderAndPayload serialize(buffer_needed, out);
487 for (auto& frame : frames) {
488 absl::visit(serialize, frame);
489 }
490 }
491
ParseFramePayload(const Http2FrameHeader & hdr,SliceBuffer payload)492 absl::StatusOr<Http2Frame> ParseFramePayload(const Http2FrameHeader& hdr,
493 SliceBuffer payload) {
494 CHECK(payload.Length() == hdr.length);
495 switch (hdr.type) {
496 case kFrameTypeData:
497 return ParseDataFrame(hdr, payload);
498 case kFrameTypeHeader:
499 return ParseHeaderFrame(hdr, payload);
500 case kFrameTypeContinuation:
501 return ParseContinuationFrame(hdr, payload);
502 case kFrameTypeRstStream:
503 return ParseRstStreamFrame(hdr, payload);
504 case kFrameTypeSettings:
505 return ParseSettingsFrame(hdr, payload);
506 case kFrameTypePing:
507 return ParsePingFrame(hdr, payload);
508 case kFrameTypeGoaway:
509 return ParseGoawayFrame(hdr, payload);
510 case kFrameTypeWindowUpdate:
511 return ParseWindowUpdateFrame(hdr, payload);
512 case kFrameTypePushPromise:
513 return absl::InternalError(
514 "push promise not supported (and SETTINGS_ENABLE_PUSH explicitly "
515 "disabled).");
516 case kFrameTypeSecurity:
517 return ParseSecurityFrame(hdr, payload);
518 default:
519 return Http2UnknownFrame{};
520 }
521 }
522
523 } // namespace grpc_core
524