tor-browser

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

OpaqueResponseUtils.h (6465B)


      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 
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #ifndef mozilla_net_OpaqueResponseUtils_h
      9 #define mozilla_net_OpaqueResponseUtils_h
     10 
     11 #include "ipc/EnumSerializer.h"
     12 #include "mozilla/TimeStamp.h"
     13 #include "nsIContentPolicy.h"
     14 #include "nsIEncodedChannel.h"
     15 #include "nsIStreamListener.h"
     16 #include "nsUnknownDecoder.h"
     17 #include "nsMimeTypes.h"
     18 #include "nsIHttpChannel.h"
     19 
     20 #include "mozilla/Logging.h"
     21 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
     22 
     23 #include "nsCOMPtr.h"
     24 #include "nsString.h"
     25 #include "nsTArray.h"
     26 
     27 class nsIContentSniffer;
     28 
     29 namespace mozilla::dom {
     30 class JSValidatorParent;
     31 }
     32 
     33 namespace mozilla::ipc {
     34 class Shmem;
     35 }
     36 
     37 namespace mozilla::net {
     38 
     39 class HttpBaseChannel;
     40 class nsHttpResponseHead;
     41 
     42 enum class OpaqueResponseBlockedReason : uint32_t {
     43  ALLOWED_SAFE_LISTED,
     44  ALLOWED_SAFE_LISTED_SPEC_BREAKING,
     45  BLOCKED_BLOCKLISTED_NEVER_SNIFFED,
     46  BLOCKED_206_AND_BLOCKLISTED,
     47  BLOCKED_NOSNIFF_AND_EITHER_BLOCKLISTED_OR_TEXTPLAIN,
     48  BLOCKED_SHOULD_SNIFF
     49 };
     50 
     51 using OpaqueResponseBlockedTelemetryReason = glean::orb::BlockReasonLabel;
     52 
     53 enum class OpaqueResponse { Block, Allow, SniffCompressed, Sniff };
     54 
     55 OpaqueResponseBlockedReason GetOpaqueResponseBlockedReason(
     56    const nsACString& aContentType, uint16_t aStatus, bool aNoSniff);
     57 
     58 OpaqueResponseBlockedReason GetOpaqueResponseBlockedReason(
     59    nsHttpResponseHead& aResponseHead);
     60 
     61 // Returns a tuple of (rangeStart, rangeEnd, rangeTotal) from the input range
     62 // header string if succeed.
     63 Result<std::tuple<int64_t, int64_t, int64_t>, nsresult>
     64 ParseContentRangeHeaderString(const nsAutoCString& aRangeStr);
     65 
     66 bool IsFirstPartialResponse(nsHttpResponseHead& aResponseHead);
     67 
     68 LogModule* GetORBLog();
     69 
     70 // Helper class to filter data for opaque responses destined for `Window.fetch`.
     71 // See https://fetch.spec.whatwg.org/#concept-filtered-response-opaque.
     72 class OpaqueResponseFilter final : public nsIStreamListener {
     73 public:
     74  NS_DECL_THREADSAFE_ISUPPORTS
     75  NS_DECL_NSIREQUESTOBSERVER
     76  NS_DECL_NSISTREAMLISTENER;
     77 
     78  explicit OpaqueResponseFilter(nsIStreamListener* aNext);
     79 
     80 private:
     81  virtual ~OpaqueResponseFilter() = default;
     82 
     83  nsCOMPtr<nsIStreamListener> mNext;
     84 };
     85 
     86 class OpaqueResponseBlocker final : public nsIStreamListener {
     87  enum class State { Sniffing, Allowed, Blocked };
     88 
     89 public:
     90  NS_DECL_THREADSAFE_ISUPPORTS
     91  NS_DECL_NSIREQUESTOBSERVER
     92  NS_DECL_NSISTREAMLISTENER;
     93 
     94  OpaqueResponseBlocker(nsIStreamListener* aNext, HttpBaseChannel* aChannel,
     95                        const nsCString& aContentType, bool aNoSniff);
     96 
     97  bool IsSniffing() const;
     98  void AllowResponse();
     99  void BlockResponse(HttpBaseChannel* aChannel, nsresult aStatus);
    100  void FilterResponse();
    101 
    102  nsresult EnsureOpaqueResponseIsAllowedAfterSniff(nsIRequest* aRequest);
    103 
    104  OpaqueResponse EnsureOpaqueResponseIsAllowedAfterJavaScriptValidation(
    105      HttpBaseChannel* aChannel, bool aAllow);
    106 
    107  // The four possible results for validation. `JavaScript` and `JSON` are
    108  // self-explanatory. `JavaScript` is the only successful result, in the sense
    109  // that it will allow the opaque response, whereas `JSON` will block. `Other`
    110  // is the case where validation fails, because the response is neither
    111  // `JavaScript` nor `JSON`, but the framework itself works as intended.
    112  // `Failure` implies that something has gone wrong, such as allocation, etc.
    113  enum class ValidatorResult : uint32_t { JavaScript, JSON, Other, Failure };
    114 
    115 private:
    116  virtual ~OpaqueResponseBlocker() = default;
    117 
    118  nsresult ValidateJavaScript(HttpBaseChannel* aChannel, nsIURI* aURI,
    119                              nsILoadInfo* aLoadInfo);
    120 
    121  void ResolveAndProcessData(HttpBaseChannel* aChannel, bool aAllowed,
    122                             Maybe<mozilla::ipc::Shmem>& aSharedData);
    123 
    124  void MaybeRunOnStopRequest(HttpBaseChannel* aChannel);
    125 
    126  nsCOMPtr<nsIStreamListener> mNext;
    127 
    128  const nsCString mContentType;
    129  const bool mNoSniff;
    130  bool mShouldFilter = false;
    131 
    132  State mState = State::Sniffing;
    133  nsresult mStatus = NS_OK;
    134 
    135  TimeStamp mStartOfJavaScriptValidation;
    136 
    137  RefPtr<dom::JSValidatorParent> mJSValidator;
    138 
    139  Maybe<nsresult> mPendingOnStopRequestStatus{Nothing()};
    140 };
    141 
    142 class nsCompressedAudioVideoImageDetector : public nsUnknownDecoder {
    143  const std::function<void(void*, const uint8_t*, uint32_t)> mCallback;
    144 
    145 public:
    146  nsCompressedAudioVideoImageDetector(
    147      nsIStreamListener* aListener,
    148      std::function<void(void*, const uint8_t*, uint32_t)>&& aCallback)
    149      : nsUnknownDecoder(aListener), mCallback(aCallback) {}
    150 
    151 protected:
    152  virtual void DetermineContentType(nsIRequest* aRequest) override {
    153    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
    154    if (!httpChannel) {
    155      return;
    156    }
    157 
    158    const char* testData = mBuffer;
    159    uint32_t testDataLen = mBufferLen;
    160    // Check if data are compressed.
    161    nsAutoCString decodedData;
    162 
    163    // ConvertEncodedData is always called only on a single thread for each
    164    // instance of an object.
    165    nsresult rv = ConvertEncodedData(aRequest, mBuffer, mBufferLen);
    166    if (NS_SUCCEEDED(rv)) {
    167      MutexAutoLock lock(mMutex);
    168      decodedData = mDecodedData;
    169    }
    170    if (!decodedData.IsEmpty()) {
    171      testData = decodedData.get();
    172      testDataLen = std::min<uint32_t>(decodedData.Length(), 512u);
    173    }
    174 
    175    mCallback(httpChannel, (const uint8_t*)testData, testDataLen);
    176 
    177    nsAutoCString contentType;
    178    rv = httpChannel->GetContentType(contentType);
    179 
    180    MutexAutoLock lock(mMutex);
    181    if (!contentType.IsEmpty()) {
    182      mContentType = contentType;
    183    } else {
    184      mContentType = UNKNOWN_CONTENT_TYPE;
    185    }
    186 
    187    nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
    188    if (encodedChannel) {
    189      encodedChannel->SetHasContentDecompressed(true);
    190    }
    191  }
    192 };
    193 }  // namespace mozilla::net
    194 
    195 namespace IPC {
    196 template <>
    197 struct ParamTraits<mozilla::net::OpaqueResponseBlocker::ValidatorResult>
    198    : public ContiguousEnumSerializerInclusive<
    199          mozilla::net::OpaqueResponseBlocker::ValidatorResult,
    200          mozilla::net::OpaqueResponseBlocker::ValidatorResult::JavaScript,
    201          mozilla::net::OpaqueResponseBlocker::ValidatorResult::Failure> {};
    202 }  // namespace IPC
    203 
    204 #endif  // mozilla_net_OpaqueResponseUtils_h