| // Copyright 2018 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <utility> | 
 |  | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | namespace { | 
 |  | 
 | // Maximum length of header name and header value.  This limits the amount of | 
 | // memory the peer can make the decoder allocate when sending string literals. | 
 | const size_t kStringLiteralLengthLimit = 1024 * 1024; | 
 |  | 
 | }  // namespace | 
 |  | 
 | QpackInstructionDecoder::QpackInstructionDecoder(const QpackLanguage* language, | 
 |                                                  Delegate* delegate) | 
 |     : language_(language), | 
 |       delegate_(delegate), | 
 |       s_bit_(false), | 
 |       varint_(0), | 
 |       varint2_(0), | 
 |       is_huffman_encoded_(false), | 
 |       string_length_(0), | 
 |       error_detected_(false), | 
 |       state_(State::kStartInstruction) {} | 
 |  | 
 | void QpackInstructionDecoder::Decode(QuicStringPiece data) { | 
 |   DCHECK(!data.empty()); | 
 |   DCHECK(!error_detected_); | 
 |  | 
 |   while (true) { | 
 |     size_t bytes_consumed = 0; | 
 |  | 
 |     switch (state_) { | 
 |       case State::kStartInstruction: | 
 |         DoStartInstruction(data); | 
 |         break; | 
 |       case State::kStartField: | 
 |         DoStartField(); | 
 |         break; | 
 |       case State::kReadBit: | 
 |         DoReadBit(data); | 
 |         break; | 
 |       case State::kVarintStart: | 
 |         bytes_consumed = DoVarintStart(data); | 
 |         break; | 
 |       case State::kVarintResume: | 
 |         bytes_consumed = DoVarintResume(data); | 
 |         break; | 
 |       case State::kVarintDone: | 
 |         DoVarintDone(); | 
 |         break; | 
 |       case State::kReadString: | 
 |         bytes_consumed = DoReadString(data); | 
 |         break; | 
 |       case State::kReadStringDone: | 
 |         DoReadStringDone(); | 
 |         break; | 
 |     } | 
 |  | 
 |     if (error_detected_) { | 
 |       return; | 
 |     } | 
 |  | 
 |     DCHECK_LE(bytes_consumed, data.size()); | 
 |  | 
 |     data = QuicStringPiece(data.data() + bytes_consumed, | 
 |                            data.size() - bytes_consumed); | 
 |  | 
 |     // Stop processing if no more data but next state would require it. | 
 |     if (data.empty() && (state_ != State::kStartField) && | 
 |         (state_ != State::kVarintDone) && (state_ != State::kReadStringDone)) { | 
 |       return; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | bool QpackInstructionDecoder::AtInstructionBoundary() const { | 
 |   return state_ == State::kStartInstruction; | 
 | } | 
 |  | 
 | void QpackInstructionDecoder::DoStartInstruction(QuicStringPiece data) { | 
 |   DCHECK(!data.empty()); | 
 |  | 
 |   instruction_ = LookupOpcode(data[0]); | 
 |   field_ = instruction_->fields.begin(); | 
 |  | 
 |   state_ = State::kStartField; | 
 | } | 
 |  | 
 | void QpackInstructionDecoder::DoStartField() { | 
 |   if (field_ == instruction_->fields.end()) { | 
 |     // Completed decoding this instruction. | 
 |  | 
 |     if (!delegate_->OnInstructionDecoded(instruction_)) { | 
 |       error_detected_ = true; | 
 |       return; | 
 |     } | 
 |  | 
 |     state_ = State::kStartInstruction; | 
 |     return; | 
 |   } | 
 |  | 
 |   switch (field_->type) { | 
 |     case QpackInstructionFieldType::kSbit: | 
 |     case QpackInstructionFieldType::kName: | 
 |     case QpackInstructionFieldType::kValue: | 
 |       state_ = State::kReadBit; | 
 |       return; | 
 |     case QpackInstructionFieldType::kVarint: | 
 |     case QpackInstructionFieldType::kVarint2: | 
 |       state_ = State::kVarintStart; | 
 |       return; | 
 |   } | 
 | } | 
 |  | 
 | void QpackInstructionDecoder::DoReadBit(QuicStringPiece data) { | 
 |   DCHECK(!data.empty()); | 
 |  | 
 |   switch (field_->type) { | 
 |     case QpackInstructionFieldType::kSbit: { | 
 |       const uint8_t bitmask = field_->param; | 
 |       s_bit_ = (data[0] & bitmask) == bitmask; | 
 |  | 
 |       ++field_; | 
 |       state_ = State::kStartField; | 
 |  | 
 |       return; | 
 |     } | 
 |     case QpackInstructionFieldType::kName: | 
 |     case QpackInstructionFieldType::kValue: { | 
 |       const uint8_t prefix_length = field_->param; | 
 |       DCHECK_GE(7, prefix_length); | 
 |       const uint8_t bitmask = 1 << prefix_length; | 
 |       is_huffman_encoded_ = (data[0] & bitmask) == bitmask; | 
 |  | 
 |       state_ = State::kVarintStart; | 
 |  | 
 |       return; | 
 |     } | 
 |     default: | 
 |       DCHECK(false); | 
 |   } | 
 | } | 
 |  | 
 | size_t QpackInstructionDecoder::DoVarintStart(QuicStringPiece data) { | 
 |   DCHECK(!data.empty()); | 
 |   DCHECK(field_->type == QpackInstructionFieldType::kVarint || | 
 |          field_->type == QpackInstructionFieldType::kVarint2 || | 
 |          field_->type == QpackInstructionFieldType::kName || | 
 |          field_->type == QpackInstructionFieldType::kValue); | 
 |  | 
 |   http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1); | 
 |   http2::DecodeStatus status = | 
 |       varint_decoder_.Start(data[0], field_->param, &buffer); | 
 |  | 
 |   size_t bytes_consumed = 1 + buffer.Offset(); | 
 |   switch (status) { | 
 |     case http2::DecodeStatus::kDecodeDone: | 
 |       state_ = State::kVarintDone; | 
 |       return bytes_consumed; | 
 |     case http2::DecodeStatus::kDecodeInProgress: | 
 |       state_ = State::kVarintResume; | 
 |       return bytes_consumed; | 
 |     case http2::DecodeStatus::kDecodeError: | 
 |       OnError("Encoded integer too large."); | 
 |       return bytes_consumed; | 
 |     default: | 
 |       QUIC_BUG << "Unknown decode status " << status; | 
 |       return bytes_consumed; | 
 |   } | 
 | } | 
 |  | 
 | size_t QpackInstructionDecoder::DoVarintResume(QuicStringPiece data) { | 
 |   DCHECK(!data.empty()); | 
 |   DCHECK(field_->type == QpackInstructionFieldType::kVarint || | 
 |          field_->type == QpackInstructionFieldType::kVarint2 || | 
 |          field_->type == QpackInstructionFieldType::kName || | 
 |          field_->type == QpackInstructionFieldType::kValue); | 
 |  | 
 |   http2::DecodeBuffer buffer(data); | 
 |   http2::DecodeStatus status = varint_decoder_.Resume(&buffer); | 
 |  | 
 |   size_t bytes_consumed = buffer.Offset(); | 
 |   switch (status) { | 
 |     case http2::DecodeStatus::kDecodeDone: | 
 |       state_ = State::kVarintDone; | 
 |       return bytes_consumed; | 
 |     case http2::DecodeStatus::kDecodeInProgress: | 
 |       DCHECK_EQ(bytes_consumed, data.size()); | 
 |       DCHECK(buffer.Empty()); | 
 |       return bytes_consumed; | 
 |     case http2::DecodeStatus::kDecodeError: | 
 |       OnError("Encoded integer too large."); | 
 |       return bytes_consumed; | 
 |     default: | 
 |       QUIC_BUG << "Unknown decode status " << status; | 
 |       return bytes_consumed; | 
 |   } | 
 | } | 
 |  | 
 | void QpackInstructionDecoder::DoVarintDone() { | 
 |   DCHECK(field_->type == QpackInstructionFieldType::kVarint || | 
 |          field_->type == QpackInstructionFieldType::kVarint2 || | 
 |          field_->type == QpackInstructionFieldType::kName || | 
 |          field_->type == QpackInstructionFieldType::kValue); | 
 |  | 
 |   if (field_->type == QpackInstructionFieldType::kVarint) { | 
 |     varint_ = varint_decoder_.value(); | 
 |  | 
 |     ++field_; | 
 |     state_ = State::kStartField; | 
 |     return; | 
 |   } | 
 |  | 
 |   if (field_->type == QpackInstructionFieldType::kVarint2) { | 
 |     varint2_ = varint_decoder_.value(); | 
 |  | 
 |     ++field_; | 
 |     state_ = State::kStartField; | 
 |     return; | 
 |   } | 
 |  | 
 |   string_length_ = varint_decoder_.value(); | 
 |   if (string_length_ > kStringLiteralLengthLimit) { | 
 |     OnError("String literal too long."); | 
 |     return; | 
 |   } | 
 |  | 
 |   std::string* const string = | 
 |       (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; | 
 |   string->clear(); | 
 |  | 
 |   if (string_length_ == 0) { | 
 |     ++field_; | 
 |     state_ = State::kStartField; | 
 |     return; | 
 |   } | 
 |  | 
 |   string->reserve(string_length_); | 
 |  | 
 |   state_ = State::kReadString; | 
 | } | 
 |  | 
 | size_t QpackInstructionDecoder::DoReadString(QuicStringPiece data) { | 
 |   DCHECK(!data.empty()); | 
 |   DCHECK(field_->type == QpackInstructionFieldType::kName || | 
 |          field_->type == QpackInstructionFieldType::kValue); | 
 |  | 
 |   std::string* const string = | 
 |       (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; | 
 |   DCHECK_LT(string->size(), string_length_); | 
 |  | 
 |   size_t bytes_consumed = | 
 |       std::min(string_length_ - string->size(), data.size()); | 
 |   string->append(data.data(), bytes_consumed); | 
 |  | 
 |   DCHECK_LE(string->size(), string_length_); | 
 |   if (string->size() == string_length_) { | 
 |     state_ = State::kReadStringDone; | 
 |   } | 
 |   return bytes_consumed; | 
 | } | 
 |  | 
 | void QpackInstructionDecoder::DoReadStringDone() { | 
 |   DCHECK(field_->type == QpackInstructionFieldType::kName || | 
 |          field_->type == QpackInstructionFieldType::kValue); | 
 |  | 
 |   std::string* const string = | 
 |       (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; | 
 |   DCHECK_EQ(string->size(), string_length_); | 
 |  | 
 |   if (is_huffman_encoded_) { | 
 |     huffman_decoder_.Reset(); | 
 |     // HpackHuffmanDecoder::Decode() cannot perform in-place decoding. | 
 |     std::string decoded_value; | 
 |     huffman_decoder_.Decode(*string, &decoded_value); | 
 |     if (!huffman_decoder_.InputProperlyTerminated()) { | 
 |       OnError("Error in Huffman-encoded string."); | 
 |       return; | 
 |     } | 
 |     *string = std::move(decoded_value); | 
 |   } | 
 |  | 
 |   ++field_; | 
 |   state_ = State::kStartField; | 
 | } | 
 |  | 
 | const QpackInstruction* QpackInstructionDecoder::LookupOpcode( | 
 |     uint8_t byte) const { | 
 |   for (const auto* instruction : *language_) { | 
 |     if ((byte & instruction->opcode.mask) == instruction->opcode.value) { | 
 |       return instruction; | 
 |     } | 
 |   } | 
 |   // |language_| should be defined such that instruction opcodes cover every | 
 |   // possible input. | 
 |   DCHECK(false); | 
 |   return nullptr; | 
 | } | 
 |  | 
 | void QpackInstructionDecoder::OnError(QuicStringPiece error_message) { | 
 |   DCHECK(!error_detected_); | 
 |  | 
 |   error_detected_ = true; | 
 |   delegate_->OnError(error_message); | 
 | } | 
 |  | 
 | }  // namespace quic |