tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CapsuleParser.cpp (7654B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et tw=80 : */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CapsuleParser.h"
      8 
      9 #include "CapsuleDecoder.h"
     10 #include "mozilla/ScopeExit.h"
     11 
     12 namespace mozilla::net {
     13 
     14 bool CapsuleParserListener::OnCapsule(Capsule&& aParsed) {
     15  mParsedCapsules.AppendElement(std::move(aParsed));
     16  return true;
     17 }
     18 
     19 void CapsuleParserListener::OnCapsuleParseFailure(nsresult aError) {
     20  mError = Some(aError);
     21 }
     22 
     23 CapsuleParser::CapsuleParser(Listener* aListener) : mListener(aListener) {}
     24 
     25 bool CapsuleParser::ProcessCapsuleData(const uint8_t* aData, uint32_t aCount) {
     26  // Prevent reentrant calls: if we're already processing, just return.
     27  if (mProcessing) {
     28    return false;
     29  }
     30  mProcessing = true;
     31 
     32  auto resetGuard = MakeScopeExit([&]() { mProcessing = false; });
     33 
     34  Span<const uint8_t> input;
     35  if (!mBuffer.IsEmpty()) {
     36    mBuffer.AppendElements(aData, aCount);
     37    input = Span<const uint8_t>(mBuffer.Elements(), mBuffer.Length());
     38  } else {
     39    input = Span<const uint8_t>(aData, aCount);
     40  }
     41  size_t pos = 0;
     42  size_t length = input.Length();
     43 
     44  while (true) {
     45    Span<const uint8_t> toProcess = input.Subspan(pos, length - pos);
     46    auto result = ParseCapsuleData(toProcess);
     47    if (result.isErr()) {
     48      mBuffer.Clear();
     49      return false;
     50    }
     51 
     52    size_t processed = result.unwrap();
     53    if (processed == 0) {
     54      if (mBuffer.IsEmpty()) {
     55        // Store the remaining data in mBuffer.
     56        mBuffer.AppendElements(toProcess.Elements(), toProcess.Length());
     57      } else {
     58        // Simply remove the already processed data.
     59        mBuffer.RemoveElementsAt(0, pos);
     60      }
     61      break;
     62    }
     63 
     64    pos += processed;
     65  }
     66 
     67  return true;
     68 }
     69 
     70 Result<size_t, nsresult> CapsuleParser::ParseCapsuleData(
     71    Span<const uint8_t> aData) {
     72  if (aData.IsEmpty()) {
     73    return 0;
     74  }
     75 
     76  CapsuleDecoder decoder(aData.Elements(), aData.Length());
     77  auto type = decoder.DecodeVarint();
     78 
     79  if (!type) {
     80    return 0;
     81  }
     82 
     83  CapsuleType capsuleType = static_cast<CapsuleType>(*type);
     84  auto payloadLength = decoder.DecodeVarint();
     85  if (!payloadLength) {
     86    return 0;
     87  }
     88 
     89  auto payload = decoder.Decode(*payloadLength);
     90  if (!payload) {
     91    return 0;
     92  }
     93 
     94  CapsuleDecoder payloadParser(payload->Elements(), payload->Length());
     95  auto result =
     96      ParseCapsulePayload(payloadParser, capsuleType, payload->Length());
     97  if (result.isErr()) {
     98    nsresult error = result.unwrapErr();
     99    mListener->OnCapsuleParseFailure(error);
    100    return Err(error);
    101  }
    102 
    103  Capsule capsule = result.unwrap();
    104  if (!mListener->OnCapsule(std::move(capsule))) {
    105    return Err(NS_ERROR_FAILURE);
    106  }
    107 
    108  return decoder.CurrentPos();
    109 }
    110 
    111 Result<Capsule, nsresult> CapsuleParser::ParseCapsulePayload(
    112    CapsuleDecoder& aDecoder, CapsuleType aType, size_t aPayloadLength) {
    113  switch (aType) {
    114    case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: {
    115      if (aPayloadLength < 4) {
    116        return Err(NS_ERROR_UNEXPECTED);
    117      }
    118      Maybe<uint32_t> status = aDecoder.DecodeUint<uint32_t>();
    119      if (!status) {
    120        return Err(NS_ERROR_UNEXPECTED);
    121      }
    122      // https://www.ietf.org/archive/id/draft-ietf-webtrans-http2-10.html#section-6.12
    123      // The reason MUST not exceed 1024 bytes.
    124      if (aDecoder.BytesRemaining() > 1024) {
    125        return Err(NS_ERROR_UNEXPECTED);
    126      }
    127      auto reason = aDecoder.Decode(aPayloadLength - 4);
    128      if (!reason) {
    129        return Err(NS_ERROR_UNEXPECTED);
    130      }
    131      return Capsule::CloseWebTransportSession(
    132          *status, nsCString(reinterpret_cast<const char*>(reason->Elements()),
    133                             reason->Length()));
    134    }
    135    case CapsuleType::DRAIN_WEBTRANSPORT_SESSION:
    136      break;
    137    case CapsuleType::PADDING:
    138      break;
    139    case CapsuleType::WT_RESET_STREAM: {
    140      auto id = aDecoder.DecodeVarint();
    141      if (!id) {
    142        return Err(NS_ERROR_UNEXPECTED);
    143      }
    144      auto error = aDecoder.DecodeVarint();
    145      if (!error) {
    146        return Err(NS_ERROR_UNEXPECTED);
    147      }
    148      auto size = aDecoder.DecodeVarint();
    149      if (!size) {
    150        return Err(NS_ERROR_UNEXPECTED);
    151      }
    152      return Capsule::WebTransportResetStream(*error, *size, *id);
    153    }
    154    case CapsuleType::WT_STOP_SENDING: {
    155      auto id = aDecoder.DecodeVarint();
    156      if (!id) {
    157        return Err(NS_ERROR_UNEXPECTED);
    158      }
    159      auto error = aDecoder.DecodeVarint();
    160      if (!error) {
    161        return Err(NS_ERROR_UNEXPECTED);
    162      }
    163      return Capsule::WebTransportStopSending(*error, *id);
    164    }
    165    case CapsuleType::WT_STREAM: {
    166      auto id = aDecoder.DecodeVarint();
    167      if (!id) {
    168        return Err(NS_ERROR_UNEXPECTED);
    169      }
    170      nsTArray<uint8_t> data(aDecoder.GetRemaining());
    171      return Capsule::WebTransportStreamData(*id, false, std::move(data));
    172    }
    173    case CapsuleType::WT_STREAM_FIN: {
    174      auto id = aDecoder.DecodeVarint();
    175      if (!id) {
    176        return Err(NS_ERROR_UNEXPECTED);
    177      }
    178      nsTArray<uint8_t> data(aDecoder.GetRemaining());
    179      return Capsule::WebTransportStreamData(*id, true, std::move(data));
    180    }
    181    case CapsuleType::WT_MAX_DATA: {
    182      auto value = aDecoder.DecodeVarint();
    183      if (!value) {
    184        return Err(NS_ERROR_UNEXPECTED);
    185      }
    186      return Capsule::WebTransportMaxData(*value);
    187    }
    188    case CapsuleType::WT_MAX_STREAM_DATA: {
    189      auto id = aDecoder.DecodeVarint();
    190      if (!id) {
    191        return Err(NS_ERROR_UNEXPECTED);
    192      }
    193      auto limit = aDecoder.DecodeVarint();
    194      if (!limit) {
    195        return Err(NS_ERROR_UNEXPECTED);
    196      }
    197      return Capsule::WebTransportMaxStreamData(*limit, *id);
    198    }
    199    case CapsuleType::WT_MAX_STREAMS_BIDI: {
    200      auto value = aDecoder.DecodeVarint();
    201      if (!value) {
    202        return Err(NS_ERROR_UNEXPECTED);
    203      }
    204      return Capsule::WebTransportMaxStreams(*value, true);
    205    }
    206    case CapsuleType::WT_MAX_STREAMS_UNIDI: {
    207      auto value = aDecoder.DecodeVarint();
    208      if (!value) {
    209        return Err(NS_ERROR_UNEXPECTED);
    210      }
    211      return Capsule::WebTransportMaxStreams(*value, false);
    212    }
    213    case CapsuleType::WT_DATA_BLOCKED: {
    214      auto limit = aDecoder.DecodeVarint();
    215      if (!limit) {
    216        return Err(NS_ERROR_UNEXPECTED);
    217      }
    218      return Capsule::WebTransportDataBlocked(*limit);
    219    }
    220    case CapsuleType::WT_STREAM_DATA_BLOCKED: {
    221      auto id = aDecoder.DecodeVarint();
    222      if (!id) {
    223        return Err(NS_ERROR_UNEXPECTED);
    224      }
    225      auto limit = aDecoder.DecodeVarint();
    226      if (!limit) {
    227        return Err(NS_ERROR_UNEXPECTED);
    228      }
    229      return Capsule::WebTransportStreamDataBlocked(*limit, *id);
    230    }
    231    case CapsuleType::WT_STREAMS_BLOCKED_BIDI: {
    232      auto value = aDecoder.DecodeVarint();
    233      if (!value) {
    234        return Err(NS_ERROR_UNEXPECTED);
    235      }
    236      return Capsule::WebTransportStreamsBlocked(*value, true);
    237    }
    238    case CapsuleType::WT_STREAMS_BLOCKED_UNIDI: {
    239      auto value = aDecoder.DecodeVarint();
    240      if (!value) {
    241        return Err(NS_ERROR_UNEXPECTED);
    242      }
    243      return Capsule::WebTransportStreamsBlocked(*value, false);
    244    }
    245    case CapsuleType::DATAGRAM: {
    246      nsTArray<uint8_t> payload(aDecoder.GetRemaining());
    247      return Capsule::WebTransportDatagram(std::move(payload));
    248    }
    249    default:
    250      break;
    251  }
    252 
    253  return Capsule::Unknown(static_cast<uint64_t>(aType),
    254                          nsTArray<uint8_t>(aDecoder.GetRemaining()));
    255 }
    256 
    257 }  // namespace mozilla::net