tor-browser

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

WebSocketConnection.cpp (7471B)


      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 "nsIInterfaceRequestor.h"
      8 #include "WebSocketConnection.h"
      9 
     10 #include "WebSocketLog.h"
     11 #include "mozilla/net/WebSocketConnectionListener.h"
     12 #include "mozilla/Components.h"
     13 #include "nsIOService.h"
     14 #include "nsITLSSocketControl.h"
     15 #include "nsISocketTransport.h"
     16 #include "nsITransportSecurityInfo.h"
     17 #include "nsSocketTransportService2.h"
     18 
     19 namespace mozilla::net {
     20 
     21 NS_IMPL_ISUPPORTS(WebSocketConnection, nsIInputStreamCallback,
     22                  nsIOutputStreamCallback)
     23 
     24 WebSocketConnection::WebSocketConnection(nsISocketTransport* aTransport,
     25                                         nsIAsyncInputStream* aInputStream,
     26                                         nsIAsyncOutputStream* aOutputStream)
     27    : mTransport(aTransport),
     28      mSocketIn(aInputStream),
     29      mSocketOut(aOutputStream) {
     30  LOG(("WebSocketConnection ctor %p\n", this));
     31 }
     32 
     33 WebSocketConnection::~WebSocketConnection() {
     34  LOG(("WebSocketConnection dtor %p\n", this));
     35 }
     36 
     37 nsresult WebSocketConnection::Init(WebSocketConnectionListener* aListener) {
     38  NS_ENSURE_ARG_POINTER(aListener);
     39 
     40  mListener = aListener;
     41  nsresult rv;
     42  mSocketThread = mozilla::components::SocketTransport::Service(&rv);
     43  if (NS_FAILED(rv)) {
     44    return rv;
     45  }
     46 
     47  if (!mTransport) {
     48    return NS_ERROR_FAILURE;
     49  }
     50 
     51  if (XRE_IsParentProcess()) {
     52    nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(mListener);
     53    mTransport->SetSecurityCallbacks(callbacks);
     54  } else {
     55    // NOTE: we don't use security callbacks in socket process.
     56    mTransport->SetSecurityCallbacks(nullptr);
     57  }
     58  return mTransport->SetEventSink(nullptr, nullptr);
     59 }
     60 
     61 void WebSocketConnection::GetIoTarget(nsIEventTarget** aTarget) {
     62  nsCOMPtr<nsIEventTarget> target = mSocketThread;
     63  return target.forget(aTarget);
     64 }
     65 
     66 void WebSocketConnection::Close() {
     67  LOG(("WebSocketConnection::Close %p\n", this));
     68  MOZ_ASSERT(OnSocketThread());
     69 
     70  if (mTransport) {
     71    mTransport->SetSecurityCallbacks(nullptr);
     72    mTransport->SetEventSink(nullptr, nullptr);
     73    mTransport->Close(NS_BASE_STREAM_CLOSED);
     74    mTransport = nullptr;
     75  }
     76 
     77  if (mSocketIn) {
     78    if (mStartReadingCalled) {
     79      mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
     80    }
     81    mSocketIn = nullptr;
     82  }
     83 
     84  if (mSocketOut) {
     85    mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
     86    mSocketOut = nullptr;
     87  }
     88 }
     89 
     90 nsresult WebSocketConnection::WriteOutputData(nsTArray<uint8_t>&& aData) {
     91  MOZ_ASSERT(OnSocketThread());
     92 
     93  if (!mSocketOut) {
     94    return NS_ERROR_NOT_AVAILABLE;
     95  }
     96 
     97  mOutputQueue.emplace_back(std::move(aData));
     98  return OnOutputStreamReady(mSocketOut);
     99 }
    100 
    101 nsresult WebSocketConnection::WriteOutputData(const uint8_t* aHdrBuf,
    102                                              uint32_t aHdrBufLength,
    103                                              const uint8_t* aPayloadBuf,
    104                                              uint32_t aPayloadBufLength) {
    105  MOZ_ASSERT_UNREACHABLE("Should not be called");
    106  return NS_ERROR_NOT_IMPLEMENTED;
    107 }
    108 
    109 nsresult WebSocketConnection::StartReading() {
    110  MOZ_ASSERT(OnSocketThread());
    111 
    112  if (!mSocketIn) {
    113    return NS_ERROR_NOT_AVAILABLE;
    114  }
    115 
    116  MOZ_ASSERT(!mStartReadingCalled, "StartReading twice");
    117  mStartReadingCalled = true;
    118  return mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
    119 }
    120 
    121 void WebSocketConnection::DrainSocketData() {
    122  MOZ_ASSERT(OnSocketThread());
    123 
    124  if (!mSocketIn || !mListener) {
    125    return;
    126  }
    127 
    128  // If we leave any data unconsumed (including the tcp fin) a RST will be
    129  // generated The right thing to do here is shutdown(SHUT_WR) and then wait a
    130  // little while to see if any data comes in.. but there is no reason to delay
    131  // things for that when the websocket handshake is supposed to guarantee a
    132  // quiet connection except for that fin.
    133  char buffer[512];
    134  uint32_t count = 0;
    135  uint32_t total = 0;
    136  nsresult rv;
    137  do {
    138    total += count;
    139    rv = mSocketIn->Read(buffer, 512, &count);
    140    if (rv != NS_BASE_STREAM_WOULD_BLOCK && (NS_FAILED(rv) || count == 0)) {
    141      mListener->OnTCPClosed();
    142    }
    143  } while (NS_SUCCEEDED(rv) && count > 0 && total < 32000);
    144 }
    145 
    146 nsresult WebSocketConnection::GetSecurityInfo(
    147    nsITransportSecurityInfo** aSecurityInfo) {
    148  LOG(("WebSocketConnection::GetSecurityInfo() %p\n", this));
    149  MOZ_ASSERT(OnSocketThread());
    150  *aSecurityInfo = nullptr;
    151 
    152  if (mTransport) {
    153    nsCOMPtr<nsITLSSocketControl> tlsSocketControl;
    154    nsresult rv =
    155        mTransport->GetTlsSocketControl(getter_AddRefs(tlsSocketControl));
    156    if (NS_FAILED(rv)) {
    157      return rv;
    158    }
    159    nsCOMPtr<nsITransportSecurityInfo> securityInfo(
    160        do_QueryInterface(tlsSocketControl));
    161    if (securityInfo) {
    162      securityInfo.forget(aSecurityInfo);
    163    }
    164  }
    165  return NS_OK;
    166 }
    167 
    168 NS_IMETHODIMP
    169 WebSocketConnection::OnInputStreamReady(nsIAsyncInputStream* aStream) {
    170  LOG(("WebSocketConnection::OnInputStreamReady() %p\n", this));
    171  MOZ_ASSERT(OnSocketThread());
    172  MOZ_ASSERT(mListener);
    173 
    174  // did we we clean up the socket after scheduling InputReady?
    175  if (!mSocketIn) {
    176    return NS_OK;
    177  }
    178 
    179  // this is after the http upgrade - so we are speaking websockets
    180  uint8_t buffer[2048];
    181  uint32_t count;
    182  nsresult rv;
    183 
    184  do {
    185    rv = mSocketIn->Read((char*)buffer, 2048, &count);
    186    LOG(("WebSocketConnection::OnInputStreamReady: read %u rv %" PRIx32 "\n",
    187         count, static_cast<uint32_t>(rv)));
    188 
    189    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    190      mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
    191      return NS_OK;
    192    }
    193 
    194    if (NS_FAILED(rv)) {
    195      mListener->OnError(rv);
    196      return rv;
    197    }
    198 
    199    if (count == 0) {
    200      mListener->OnError(NS_BASE_STREAM_CLOSED);
    201      return NS_OK;
    202    }
    203 
    204    rv = mListener->OnDataReceived(buffer, count);
    205    if (NS_FAILED(rv)) {
    206      mListener->OnError(rv);
    207      return rv;
    208    }
    209  } while (NS_SUCCEEDED(rv) && mSocketIn && mListener);
    210 
    211  return NS_OK;
    212 }
    213 
    214 NS_IMETHODIMP
    215 WebSocketConnection::OnOutputStreamReady(nsIAsyncOutputStream* aStream) {
    216  LOG(("WebSocketConnection::OnOutputStreamReady() %p\n", this));
    217  MOZ_ASSERT(OnSocketThread());
    218  MOZ_ASSERT(mListener);
    219 
    220  if (!mSocketOut) {
    221    return NS_OK;
    222  }
    223 
    224  while (!mOutputQueue.empty()) {
    225    const OutputData& data = mOutputQueue.front();
    226 
    227    char* buffer = reinterpret_cast<char*>(
    228                       const_cast<uint8_t*>(data.GetData().Elements())) +
    229                   mWriteOffset;
    230    uint32_t toWrite = data.GetData().Length() - mWriteOffset;
    231 
    232    uint32_t wrote = 0;
    233    nsresult rv = mSocketOut->Write(buffer, toWrite, &wrote);
    234    LOG(("WebSocketConnection::OnOutputStreamReady: write %u rv %" PRIx32,
    235         wrote, static_cast<uint32_t>(rv)));
    236 
    237    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    238      mSocketOut->AsyncWait(this, 0, 0, mSocketThread);
    239      return rv;
    240    }
    241 
    242    if (NS_FAILED(rv)) {
    243      LOG(("WebSocketConnection::OnOutputStreamReady %p failed %u\n", this,
    244           static_cast<uint32_t>(rv)));
    245      mListener->OnError(rv);
    246      return NS_OK;
    247    }
    248 
    249    mWriteOffset += wrote;
    250 
    251    if (toWrite == wrote) {
    252      mWriteOffset = 0;
    253      mOutputQueue.pop_front();
    254    } else {
    255      mSocketOut->AsyncWait(this, 0, 0, mSocketThread);
    256    }
    257  }
    258 
    259  return NS_OK;
    260 }
    261 
    262 }  // namespace mozilla::net