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