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