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