xref: /aosp_15_r20/external/cronet/net/spdy/alps_decoder.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 The Chromium Authors
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 "net/spdy/alps_decoder.h"
6 
7 #include <string_view>
8 
9 #include "base/feature_list.h"
10 #include "base/metrics/histogram_functions.h"
11 #include "net/base/features.h"
12 
13 namespace net {
14 namespace {
15 
ReadUint16PrefixedStringPiece(std::string_view * payload,std::string_view * output)16 bool ReadUint16PrefixedStringPiece(std::string_view* payload,
17                                    std::string_view* output) {
18   if (payload->size() < 2) {
19     return false;
20   }
21   const uint16_t length = (static_cast<uint16_t>((*payload)[0]) << 8) +
22                           (static_cast<uint8_t>((*payload)[1]));
23   payload->remove_prefix(2);
24 
25   if (payload->size() < length) {
26     return false;
27   }
28   *output = payload->substr(0, length);
29   payload->remove_prefix(length);
30 
31   return true;
32 }
33 
34 }  // anonymous namespace
35 
AlpsDecoder()36 AlpsDecoder::AlpsDecoder() {
37   decoder_adapter_.set_visitor(&settings_parser_);
38   decoder_adapter_.set_extension_visitor(&accept_ch_parser_);
39 }
40 
41 AlpsDecoder::~AlpsDecoder() = default;
42 
Decode(base::span<const char> data)43 AlpsDecoder::Error AlpsDecoder::Decode(base::span<const char> data) {
44   decoder_adapter_.ProcessInput(data.data(), data.size());
45 
46   // Log if any errors were bypassed.
47   base::UmaHistogramEnumeration(
48       "Net.SpdySession.AlpsDecoderStatus.Bypassed",
49       accept_ch_parser_.error_bypass());
50 
51   if (decoder_adapter_.HasError()) {
52     return Error::kFramingError;
53   }
54 
55   if (settings_parser_.forbidden_frame_received()) {
56     return Error::kForbiddenFrame;
57   }
58 
59   if (settings_parser_.settings_ack_received()) {
60     return Error::kSettingsWithAck;
61   }
62 
63   if (decoder_adapter_.state() !=
64       http2::Http2DecoderAdapter::SPDY_READY_FOR_FRAME) {
65     return Error::kNotOnFrameBoundary;
66   }
67 
68   return accept_ch_parser_.error();
69 }
70 
settings_frame_count() const71 int AlpsDecoder::settings_frame_count() const {
72   return settings_parser_.settings_frame_count();
73 }
74 
75 AlpsDecoder::SettingsParser::SettingsParser() = default;
76 AlpsDecoder::SettingsParser::~SettingsParser() = default;
77 
OnCommonHeader(spdy::SpdyStreamId,size_t,uint8_t type,uint8_t)78 void AlpsDecoder::SettingsParser::OnCommonHeader(
79     spdy::SpdyStreamId /*stream_id*/,
80     size_t /*length*/,
81     uint8_t type,
82     uint8_t /*flags*/) {
83   if (type == static_cast<uint8_t>(http2::Http2FrameType::DATA) ||
84       type == static_cast<uint8_t>(http2::Http2FrameType::HEADERS) ||
85       type == static_cast<uint8_t>(http2::Http2FrameType::PRIORITY) ||
86       type == static_cast<uint8_t>(http2::Http2FrameType::RST_STREAM) ||
87       type == static_cast<uint8_t>(http2::Http2FrameType::PUSH_PROMISE) ||
88       type == static_cast<uint8_t>(http2::Http2FrameType::PING) ||
89       type == static_cast<uint8_t>(http2::Http2FrameType::GOAWAY) ||
90       type == static_cast<uint8_t>(http2::Http2FrameType::WINDOW_UPDATE) ||
91       type == static_cast<uint8_t>(http2::Http2FrameType::CONTINUATION)) {
92     forbidden_frame_received_ = true;
93   }
94 }
95 
OnSettings()96 void AlpsDecoder::SettingsParser::OnSettings() {
97   settings_frame_count_++;
98 }
OnSetting(spdy::SpdySettingsId id,uint32_t value)99 void AlpsDecoder::SettingsParser::OnSetting(spdy::SpdySettingsId id,
100                                             uint32_t value) {
101   settings_[id] = value;
102 }
103 
OnSettingsAck()104 void AlpsDecoder::SettingsParser::OnSettingsAck() {
105   settings_ack_received_ = true;
106 }
107 
108 AlpsDecoder::AcceptChParser::AcceptChParser() = default;
109 AlpsDecoder::AcceptChParser::~AcceptChParser() = default;
110 
OnFrameHeader(spdy::SpdyStreamId stream_id,size_t length,uint8_t type,uint8_t flags)111 bool AlpsDecoder::AcceptChParser::OnFrameHeader(spdy::SpdyStreamId stream_id,
112                                                 size_t length,
113                                                 uint8_t type,
114                                                 uint8_t flags) {
115   // Ignore data after an error has occurred.
116   if (error_ != Error::kNoError)
117     return false;
118   // Stop all alps parsing if it's disabled.
119   if (!base::FeatureList::IsEnabled(features::kAlpsParsing))
120     return false;
121   // Handle per-type parsing.
122   switch (type) {
123     case static_cast<uint8_t>(spdy::SpdyFrameType::ACCEPT_CH): {
124       // Stop alps client hint parsing if it's disabled.
125       if (!base::FeatureList::IsEnabled(features::kAlpsClientHintParsing))
126         return false;
127       // Check for issues with the frame.
128       if (stream_id != 0) {
129         error_ = Error::kAcceptChInvalidStream;
130         return false;
131       }
132       if (flags != 0) {
133         error_ = Error::kAcceptChWithFlags;
134         return false;
135       }
136       // This frame can be parsed in OnFramePayload.
137       return true;
138     }
139     default:
140       // Ignore all other types.
141       return false;
142   }
143 }
144 
OnFramePayload(const char * data,size_t len)145 void AlpsDecoder::AcceptChParser::OnFramePayload(const char* data, size_t len) {
146   DCHECK_EQ(Error::kNoError, error_);
147 
148   std::string_view payload(data, len);
149 
150   while (!payload.empty()) {
151     std::string_view origin;
152     std::string_view value;
153     if (!ReadUint16PrefixedStringPiece(&payload, &origin) ||
154         !ReadUint16PrefixedStringPiece(&payload, &value)) {
155       if (base::FeatureList::IsEnabled(
156               features::kShouldKillSessionOnAcceptChMalformed)) {
157         // This causes a session termination.
158         error_ = Error::kAcceptChMalformed;
159         return;
160       } else {
161         // This logs that a session termination was bypassed.
162         error_bypass_ = Error::kAcceptChMalformed;
163         return;
164       }
165     }
166     accept_ch_.push_back(
167         spdy::AcceptChOriginValuePair{std::string(origin), std::string(value)});
168   }
169 }
170 
171 }  // namespace net
172