tor-browser

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

InternalResponse.cpp (15614B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 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 "InternalResponse.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/RandomNum.h"
     11 #include "mozilla/RefPtr.h"
     12 #include "mozilla/RemoteLazyInputStreamStorage.h"
     13 #include "mozilla/dom/FetchStreamUtils.h"
     14 #include "mozilla/dom/FetchTypes.h"
     15 #include "mozilla/dom/InternalHeaders.h"
     16 #include "mozilla/dom/cache/CacheTypes.h"
     17 #include "mozilla/ipc/IPCStreamUtils.h"
     18 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     19 #include "nsIRandomGenerator.h"
     20 #include "nsStreamUtils.h"
     21 
     22 namespace mozilla::dom {
     23 
     24 namespace {
     25 
     26 // Const variable for generate padding size
     27 // XXX This will be tweaked to something more meaningful in Bug 1383656.
     28 const uint32_t kMaxRandomNumber = 102400;
     29 
     30 }  // namespace
     31 
     32 InternalResponse::InternalResponse(uint16_t aStatus,
     33                                   const nsACString& aStatusText,
     34                                   RequestCredentials aCredentialsMode)
     35    : mType(ResponseType::Default),
     36      mStatus(aStatus),
     37      mStatusText(aStatusText),
     38      mHeaders(new InternalHeaders(HeadersGuardEnum::Response)),
     39      mBodySize(UNKNOWN_BODY_SIZE),
     40      mPaddingSize(UNKNOWN_PADDING_SIZE),
     41      mErrorCode(NS_OK),
     42      mCredentialsMode(aCredentialsMode),
     43      mCloned(false) {}
     44 
     45 /* static */ SafeRefPtr<InternalResponse> InternalResponse::FromIPC(
     46    const ParentToParentInternalResponse& aIPCResponse) {
     47  MOZ_ASSERT(XRE_IsParentProcess());
     48  return FromIPCTemplate(aIPCResponse);
     49 }
     50 
     51 /* static */ SafeRefPtr<InternalResponse> InternalResponse::FromIPC(
     52    const ParentToChildInternalResponse& aIPCResponse) {
     53  MOZ_ASSERT(XRE_IsContentProcess());
     54  return FromIPCTemplate(aIPCResponse);
     55 }
     56 
     57 template <typename T>
     58 /* static */ SafeRefPtr<InternalResponse> InternalResponse::FromIPCTemplate(
     59    const T& aIPCResponse) {
     60  if (aIPCResponse.metadata().type() == ResponseType::Error) {
     61    return InternalResponse::NetworkError(aIPCResponse.metadata().errorCode());
     62  }
     63 
     64  SafeRefPtr<InternalResponse> response = MakeSafeRefPtr<InternalResponse>(
     65      aIPCResponse.metadata().status(), aIPCResponse.metadata().statusText());
     66 
     67  response->SetURLList(aIPCResponse.metadata().urlList());
     68  response->mHeaders =
     69      new InternalHeaders(aIPCResponse.metadata().headers(),
     70                          aIPCResponse.metadata().headersGuard());
     71 
     72  if (aIPCResponse.body()) {
     73    auto bodySize = aIPCResponse.bodySize();
     74    auto body = ToInputStream(*aIPCResponse.body());
     75    response->SetBody(body.get(), bodySize);
     76  }
     77 
     78  response->SetAlternativeDataType(
     79      aIPCResponse.metadata().alternativeDataType());
     80 
     81  if (aIPCResponse.alternativeBody()) {
     82    auto alternativeBody = ToInputStream(*aIPCResponse.alternativeBody());
     83    response->SetAlternativeBody(alternativeBody.get());
     84  }
     85 
     86  response->InitChannelInfo(aIPCResponse.metadata().securityInfo());
     87 
     88  if (aIPCResponse.metadata().principalInfo()) {
     89    response->SetPrincipalInfo(MakeUnique<mozilla::ipc::PrincipalInfo>(
     90        aIPCResponse.metadata().principalInfo().ref()));
     91  }
     92 
     93  nsAutoCString bodyBlobURISpec(aIPCResponse.metadata().bodyBlobURISpec());
     94  response->SetBodyBlobURISpec(bodyBlobURISpec);
     95  nsAutoString bodyLocalPath(aIPCResponse.metadata().bodyLocalPath());
     96  response->SetBodyLocalPath(bodyLocalPath);
     97 
     98  response->mCredentialsMode = aIPCResponse.metadata().credentialsMode();
     99 
    100  switch (aIPCResponse.metadata().type()) {
    101    case ResponseType::Basic:
    102      response = response->BasicResponse();
    103      break;
    104    case ResponseType::Cors:
    105      response = response->CORSResponse();
    106      break;
    107    case ResponseType::Default:
    108      break;
    109    case ResponseType::Opaque:
    110      response = response->OpaqueResponse();
    111      break;
    112    case ResponseType::Opaqueredirect:
    113      response = response->OpaqueRedirectResponse();
    114      break;
    115    default:
    116      MOZ_CRASH("Unexpected ResponseType!");
    117  }
    118 
    119  MOZ_ASSERT(response);
    120 
    121  return response;
    122 }
    123 
    124 InternalResponse::~InternalResponse() = default;
    125 
    126 InternalResponseMetadata InternalResponse::GetMetadata() {
    127  nsTArray<HeadersEntry> headers;
    128  HeadersGuardEnum headersGuard;
    129  UnfilteredHeaders()->ToIPC(headers, headersGuard);
    130 
    131  Maybe<mozilla::ipc::PrincipalInfo> principalInfo =
    132      mPrincipalInfo ? Some(*mPrincipalInfo) : Nothing();
    133 
    134  nsAutoCString bodyBlobURISpec(BodyBlobURISpec());
    135  nsAutoString bodyLocalPath(BodyLocalPath());
    136 
    137  // Note: all the arguments are copied rather than moved, which would be more
    138  // efficient, because there's no move-friendly constructor generated.
    139  nsCOMPtr<nsITransportSecurityInfo> securityInfo(mChannelInfo.SecurityInfo());
    140  return InternalResponseMetadata(
    141      mType, GetUnfilteredURLList(), GetUnfilteredStatus(),
    142      GetUnfilteredStatusText(), headersGuard, headers, mErrorCode,
    143      GetAlternativeDataType(), securityInfo, principalInfo, bodyBlobURISpec,
    144      bodyLocalPath, GetCredentialsMode());
    145 }
    146 
    147 void InternalResponse::ToChildToParentInternalResponse(
    148    ChildToParentInternalResponse* aIPCResponse,
    149    mozilla::ipc::PBackgroundChild* aManager) {
    150  *aIPCResponse = ChildToParentInternalResponse(GetMetadata(), Nothing(),
    151                                                UNKNOWN_BODY_SIZE, Nothing());
    152 
    153  nsCOMPtr<nsIInputStream> body;
    154  int64_t bodySize;
    155  GetUnfilteredBody(getter_AddRefs(body), &bodySize);
    156 
    157  if (body) {
    158    aIPCResponse->body().emplace(ChildToParentStream());
    159    aIPCResponse->bodySize() = bodySize;
    160 
    161    DebugOnly<bool> ok = mozilla::ipc::SerializeIPCStream(
    162        body.forget(), aIPCResponse->body()->stream(), /* aAllowLazy */ false);
    163    MOZ_ASSERT(ok);
    164  }
    165 
    166  nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody();
    167  if (alternativeBody) {
    168    aIPCResponse->alternativeBody().emplace(ChildToParentStream());
    169 
    170    DebugOnly<bool> ok = mozilla::ipc::SerializeIPCStream(
    171        alternativeBody.forget(), aIPCResponse->alternativeBody()->stream(),
    172        /* aAllowLazy */ false);
    173    MOZ_ASSERT(ok);
    174  }
    175 }
    176 
    177 ParentToParentInternalResponse
    178 InternalResponse::ToParentToParentInternalResponse() {
    179  ParentToParentInternalResponse result(GetMetadata(), Nothing(),
    180                                        UNKNOWN_BODY_SIZE, Nothing());
    181 
    182  nsCOMPtr<nsIInputStream> body;
    183  int64_t bodySize;
    184  GetUnfilteredBody(getter_AddRefs(body), &bodySize);
    185 
    186  if (body) {
    187    result.body() = Some(ToParentToParentStream(WrapNotNull(body), bodySize));
    188    result.bodySize() = bodySize;
    189  }
    190 
    191  nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody();
    192  if (alternativeBody) {
    193    result.alternativeBody() = Some(ToParentToParentStream(
    194        WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE));
    195  }
    196 
    197  return result;
    198 }
    199 
    200 ParentToChildInternalResponse
    201 InternalResponse::ToParentToChildInternalResponse() {
    202  ParentToChildInternalResponse result(GetMetadata(), Nothing(),
    203                                       UNKNOWN_BODY_SIZE, Nothing());
    204 
    205  nsCOMPtr<nsIInputStream> body;
    206  int64_t bodySize;
    207  GetUnfilteredBody(getter_AddRefs(body), &bodySize);
    208 
    209  if (body) {
    210    ParentToChildStream bodyStream =
    211        ToParentToChildStream(WrapNotNull(body), bodySize, mSerializeAsLazy);
    212    // The body stream can fail to serialize as an IPCStream. In the case, the
    213    // IPCStream's type would be T__None. Don't set up IPCInternalResponse's
    214    // body with the failed IPCStream.
    215    if (mSerializeAsLazy || bodyStream.get_IPCStream().stream().type() !=
    216                                mozilla::ipc::InputStreamParams::T__None) {
    217      result.body() = Some(bodyStream);
    218      result.bodySize() = bodySize;
    219    }
    220  }
    221 
    222  nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody();
    223  if (alternativeBody) {
    224    ParentToChildStream alterBodyStream = ToParentToChildStream(
    225        WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE, mSerializeAsLazy);
    226    // The body stream can fail to serialize as an IPCStream. In the case, the
    227    // IPCStream's type would be T__None. Don't set up IPCInternalResponse's
    228    // body with the failed IPCStream.
    229    if (mSerializeAsLazy || alterBodyStream.get_IPCStream().stream().type() !=
    230                                mozilla::ipc::InputStreamParams::T__None) {
    231      result.alternativeBody() = Some(alterBodyStream);
    232    }
    233  }
    234 
    235  return result;
    236 }
    237 
    238 SafeRefPtr<InternalResponse> InternalResponse::Clone(CloneType aCloneType) {
    239  SafeRefPtr<InternalResponse> clone = CreateIncompleteCopy();
    240  clone->mCloned = (mCloned = true);
    241 
    242  clone->mHeaders = new InternalHeaders(*mHeaders);
    243 
    244  // Make sure the clone response will have the same padding size.
    245  clone->mPaddingInfo = mPaddingInfo;
    246  clone->mPaddingSize = mPaddingSize;
    247 
    248  clone->mCacheInfoChannel = mCacheInfoChannel;
    249  clone->mCredentialsMode = mCredentialsMode;
    250 
    251  if (mWrappedResponse) {
    252    clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType);
    253    MOZ_ASSERT(!mBody);
    254    return clone;
    255  }
    256 
    257  if (!mBody || aCloneType == eDontCloneInputStream) {
    258    return clone;
    259  }
    260 
    261  nsCOMPtr<nsIInputStream> clonedBody;
    262  nsCOMPtr<nsIInputStream> replacementBody;
    263 
    264  nsresult rv = NS_CloneInputStream(mBody, getter_AddRefs(clonedBody),
    265                                    getter_AddRefs(replacementBody));
    266  if (NS_WARN_IF(NS_FAILED(rv))) {
    267    return nullptr;
    268  }
    269 
    270  clone->mBody.swap(clonedBody);
    271  if (replacementBody) {
    272    mBody.swap(replacementBody);
    273  }
    274 
    275  return clone;
    276 }
    277 
    278 SafeRefPtr<InternalResponse> InternalResponse::BasicResponse() {
    279  MOZ_ASSERT(!mWrappedResponse,
    280             "Can't BasicResponse a already wrapped response");
    281  SafeRefPtr<InternalResponse> basic = CreateIncompleteCopy();
    282  basic->mType = ResponseType::Basic;
    283  basic->mHeaders = InternalHeaders::BasicHeaders(Headers());
    284  basic->mWrappedResponse = SafeRefPtrFromThis();
    285  return basic;
    286 }
    287 
    288 SafeRefPtr<InternalResponse> InternalResponse::CORSResponse() {
    289  MOZ_ASSERT(!mWrappedResponse,
    290             "Can't CORSResponse a already wrapped response");
    291  SafeRefPtr<InternalResponse> cors = CreateIncompleteCopy();
    292  cors->mType = ResponseType::Cors;
    293  cors->mHeaders = InternalHeaders::CORSHeaders(Headers(), mCredentialsMode);
    294  cors->mWrappedResponse = SafeRefPtrFromThis();
    295  return cors;
    296 }
    297 
    298 uint32_t InternalResponse::GetPaddingInfo() {
    299  // If it's an opaque response, the paddingInfo should be generated only when
    300  // paddingSize is unknown size.
    301  // If it's not, the paddingInfo should be nothing and the paddingSize should
    302  // be unknown size.
    303  MOZ_DIAGNOSTIC_ASSERT(
    304      (mType == ResponseType::Opaque && mPaddingSize == UNKNOWN_PADDING_SIZE &&
    305       mPaddingInfo.isSome()) ||
    306      (mType == ResponseType::Opaque && mPaddingSize != UNKNOWN_PADDING_SIZE &&
    307       mPaddingInfo.isNothing()) ||
    308      (mType != ResponseType::Opaque && mPaddingSize == UNKNOWN_PADDING_SIZE &&
    309       mPaddingInfo.isNothing()));
    310  return mPaddingInfo.isSome() ? mPaddingInfo.ref() : 0;
    311 }
    312 
    313 nsresult InternalResponse::GeneratePaddingInfo() {
    314  MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque);
    315  MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE);
    316 
    317  // Utilize random generator to generator a random number
    318  nsresult rv;
    319  uint32_t randomNumber = 0;
    320  nsCOMPtr<nsIRandomGenerator> randomGenerator =
    321      do_GetService("@mozilla.org/security/random-generator;1", &rv);
    322  if (NS_WARN_IF(NS_FAILED(rv))) {
    323    Maybe<uint64_t> maybeRandomNum = RandomUint64();
    324    if (maybeRandomNum.isSome()) {
    325      mPaddingInfo.emplace(uint32_t(maybeRandomNum.value() % kMaxRandomNumber));
    326      return NS_OK;
    327    }
    328    return rv;
    329  }
    330 
    331  MOZ_DIAGNOSTIC_ASSERT(randomGenerator);
    332 
    333  rv = randomGenerator->GenerateRandomBytesInto(randomNumber);
    334  if (NS_WARN_IF(NS_FAILED(rv))) {
    335    Maybe<uint64_t> maybeRandomNum = RandomUint64();
    336    if (maybeRandomNum.isSome()) {
    337      mPaddingInfo.emplace(uint32_t(maybeRandomNum.value() % kMaxRandomNumber));
    338      return NS_OK;
    339    }
    340    return rv;
    341  }
    342 
    343  mPaddingInfo.emplace(randomNumber % kMaxRandomNumber);
    344 
    345  return rv;
    346 }
    347 
    348 int64_t InternalResponse::GetPaddingSize() {
    349  // We initialize padding size to an unknown size (-1). After cached, we only
    350  // pad opaque response. Opaque response's padding size might be unknown before
    351  // cached.
    352  MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque ||
    353                        mPaddingSize == UNKNOWN_PADDING_SIZE);
    354  MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE ||
    355                        mPaddingSize >= 0);
    356 
    357  return mPaddingSize;
    358 }
    359 
    360 void InternalResponse::SetPaddingSize(int64_t aPaddingSize) {
    361  // We should only pad the opaque response.
    362  MOZ_DIAGNOSTIC_ASSERT(
    363      (mType == ResponseType::Opaque) !=
    364      (aPaddingSize == InternalResponse::UNKNOWN_PADDING_SIZE));
    365  MOZ_DIAGNOSTIC_ASSERT(aPaddingSize == UNKNOWN_PADDING_SIZE ||
    366                        aPaddingSize >= 0);
    367 
    368  mPaddingSize = aPaddingSize;
    369 }
    370 
    371 void InternalResponse::SetPrincipalInfo(
    372    UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo) {
    373  mPrincipalInfo = std::move(aPrincipalInfo);
    374 }
    375 
    376 LoadTainting InternalResponse::GetTainting() const {
    377  switch (mType) {
    378    case ResponseType::Cors:
    379      return LoadTainting::CORS;
    380    case ResponseType::Opaque:
    381      return LoadTainting::Opaque;
    382    default:
    383      return LoadTainting::Basic;
    384  }
    385 }
    386 
    387 SafeRefPtr<InternalResponse> InternalResponse::Unfiltered() {
    388  SafeRefPtr<InternalResponse> ref = mWrappedResponse.clonePtr();
    389  if (!ref) {
    390    ref = SafeRefPtrFromThis();
    391  }
    392  return ref;
    393 }
    394 
    395 SafeRefPtr<InternalResponse> InternalResponse::OpaqueResponse() {
    396  MOZ_ASSERT(!mWrappedResponse,
    397             "Can't OpaqueResponse a already wrapped response");
    398  SafeRefPtr<InternalResponse> response =
    399      MakeSafeRefPtr<InternalResponse>(0, ""_ns);
    400  response->mType = ResponseType::Opaque;
    401  response->mChannelInfo = mChannelInfo;
    402  if (mPrincipalInfo) {
    403    response->mPrincipalInfo =
    404        MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
    405  }
    406  response->mWrappedResponse = SafeRefPtrFromThis();
    407  return response;
    408 }
    409 
    410 SafeRefPtr<InternalResponse> InternalResponse::OpaqueRedirectResponse() {
    411  MOZ_ASSERT(!mWrappedResponse,
    412             "Can't OpaqueRedirectResponse a already wrapped response");
    413  MOZ_ASSERT(!mURLList.IsEmpty(),
    414             "URLList should not be emtpy for internalResponse");
    415  SafeRefPtr<InternalResponse> response = OpaqueResponse();
    416  response->mType = ResponseType::Opaqueredirect;
    417  response->mURLList = mURLList.Clone();
    418  return response;
    419 }
    420 
    421 SafeRefPtr<InternalResponse> InternalResponse::CreateIncompleteCopy() {
    422  SafeRefPtr<InternalResponse> copy =
    423      MakeSafeRefPtr<InternalResponse>(mStatus, mStatusText);
    424  copy->mType = mType;
    425  copy->mURLList = mURLList.Clone();
    426  copy->mChannelInfo = mChannelInfo;
    427  if (mPrincipalInfo) {
    428    copy->mPrincipalInfo =
    429        MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
    430  }
    431  return copy;
    432 }
    433 
    434 ParentToChildInternalResponse ToParentToChild(
    435    const ParentToParentInternalResponse& aResponse) {
    436  ParentToChildInternalResponse result(aResponse.metadata(), Nothing(),
    437                                       aResponse.bodySize(), Nothing());
    438 
    439  if (aResponse.body().isSome()) {
    440    result.body() = Some(
    441        ToParentToChildStream(aResponse.body().ref(), aResponse.bodySize()));
    442  }
    443  if (aResponse.alternativeBody().isSome()) {
    444    result.alternativeBody() =
    445        Some(ToParentToChildStream(aResponse.alternativeBody().ref(),
    446                                   InternalResponse::UNKNOWN_BODY_SIZE));
    447  }
    448 
    449  return result;
    450 }
    451 
    452 }  // namespace mozilla::dom