tor-browser

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

WebSocketChannel.h (13811B)


      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 #ifndef mozilla_net_WebSocketChannel_h
      8 #define mozilla_net_WebSocketChannel_h
      9 
     10 #include "nsISupports.h"
     11 #include "nsIInterfaceRequestor.h"
     12 #include "nsIStreamListener.h"
     13 #include "nsIAsyncInputStream.h"
     14 #include "nsIAsyncOutputStream.h"
     15 #include "nsITimer.h"
     16 #include "nsIDNSListener.h"
     17 #include "nsINamed.h"
     18 #include "nsIObserver.h"
     19 #include "nsIProtocolProxyCallback.h"
     20 #include "nsIChannelEventSink.h"
     21 #include "nsIHttpChannelInternal.h"
     22 #include "mozilla/net/WebSocketConnectionListener.h"
     23 #include "mozilla/Mutex.h"
     24 #include "BaseWebSocketChannel.h"
     25 
     26 #include "nsCOMPtr.h"
     27 #include "nsString.h"
     28 #include "nsDeque.h"
     29 #include "mozilla/Atomics.h"
     30 
     31 class nsIAsyncVerifyRedirectCallback;
     32 class nsIDashboardEventNotifier;
     33 class nsIEventTarget;
     34 class nsIHttpChannel;
     35 class nsIRandomGenerator;
     36 class nsISocketTransport;
     37 class nsIURI;
     38 
     39 namespace mozilla {
     40 namespace net {
     41 
     42 class OutboundMessage;
     43 class OutboundEnqueuer;
     44 class nsWSAdmissionManager;
     45 class PMCECompression;
     46 class CallOnMessageAvailable;
     47 class CallOnStop;
     48 class CallOnServerClose;
     49 class CallAcknowledge;
     50 class WebSocketEventService;
     51 class WebSocketConnectionBase;
     52 
     53 [[nodiscard]] extern nsresult CalculateWebSocketHashedSecret(
     54    const nsACString& aKey, nsACString& aHash);
     55 extern void ProcessServerWebSocketExtensions(const nsACString& aExtensions,
     56                                             nsACString& aNegotiatedExtensions);
     57 
     58 // Used to enforce "1 connecting websocket per host" rule, and reconnect delays
     59 enum wsConnectingState {
     60  NOT_CONNECTING = 0,     // Not yet (or no longer) trying to open connection
     61  CONNECTING_QUEUED,      // Waiting for other ws to same host to finish opening
     62  CONNECTING_DELAYED,     // Delayed by "reconnect after failure" algorithm
     63  CONNECTING_IN_PROGRESS  // Started connection: waiting for result
     64 };
     65 
     66 class WebSocketChannel : public BaseWebSocketChannel,
     67                         public nsIHttpUpgradeListener,
     68                         public nsIStreamListener,
     69                         public nsIInputStreamCallback,
     70                         public nsIOutputStreamCallback,
     71                         public nsITimerCallback,
     72                         public nsIDNSListener,
     73                         public nsIObserver,
     74                         public nsIProtocolProxyCallback,
     75                         public nsIInterfaceRequestor,
     76                         public nsIChannelEventSink,
     77                         public nsINamed,
     78                         public WebSocketConnectionListener {
     79  friend class WebSocketFrame;
     80 
     81 public:
     82  NS_DECL_THREADSAFE_ISUPPORTS
     83  NS_DECL_NSIHTTPUPGRADELISTENER
     84  NS_DECL_NSIREQUESTOBSERVER
     85  NS_DECL_NSISTREAMLISTENER
     86  NS_DECL_NSIINPUTSTREAMCALLBACK
     87  NS_DECL_NSIOUTPUTSTREAMCALLBACK
     88  NS_DECL_NSITIMERCALLBACK
     89  NS_DECL_NSIDNSLISTENER
     90  NS_DECL_NSIPROTOCOLPROXYCALLBACK
     91  NS_DECL_NSIINTERFACEREQUESTOR
     92  NS_DECL_NSICHANNELEVENTSINK
     93  NS_DECL_NSIOBSERVER
     94  NS_DECL_NSINAMED
     95 
     96  // nsIWebSocketChannel methods BaseWebSocketChannel didn't implement for us
     97  //
     98  NS_IMETHOD AsyncOpen(nsIURI* aURI, const nsACString& aOrigin,
     99                       JS::Handle<JS::Value> aOriginAttributes,
    100                       uint64_t aWindowID, nsIWebSocketListener* aListener,
    101                       nsISupports* aContext, JSContext* aCx) override;
    102  NS_IMETHOD AsyncOpenNative(nsIURI* aURI, const nsACString& aOrigin,
    103                             const OriginAttributes& aOriginAttributes,
    104                             uint64_t aWindowID,
    105                             nsIWebSocketListener* aListener,
    106                             nsISupports* aContext) override;
    107  NS_IMETHOD Close(uint16_t aCode, const nsACString& aReason) override;
    108  NS_IMETHOD SendMsg(const nsACString& aMsg) override;
    109  NS_IMETHOD SendBinaryMsg(const nsACString& aMsg) override;
    110  NS_IMETHOD SendBinaryStream(nsIInputStream* aStream,
    111                              uint32_t length) override;
    112  NS_IMETHOD GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) override;
    113 
    114  WebSocketChannel();
    115  static void Shutdown();
    116 
    117  // Off main thread URI access.
    118  void GetEffectiveURL(nsAString& aEffectiveURL) const override;
    119  bool IsEncrypted() const override;
    120 
    121  nsresult OnTransportAvailableInternal();
    122  void OnError(nsresult aStatus) override;
    123  void OnTCPClosed() override;
    124  nsresult OnDataReceived(uint8_t* aData, uint32_t aCount) override;
    125 
    126  const static uint32_t kControlFrameMask = 0x8;
    127 
    128  // First byte of the header
    129  const static uint8_t kFinalFragBit = 0x80;
    130  const static uint8_t kRsvBitsMask = 0x70;
    131  const static uint8_t kRsv1Bit = 0x40;
    132  const static uint8_t kRsv2Bit = 0x20;
    133  const static uint8_t kRsv3Bit = 0x10;
    134  const static uint8_t kOpcodeBitsMask = 0x0F;
    135 
    136  // Second byte of the header
    137  const static uint8_t kMaskBit = 0x80;
    138  const static uint8_t kPayloadLengthBitsMask = 0x7F;
    139 
    140 protected:
    141  ~WebSocketChannel() override;
    142 
    143 private:
    144  friend class OutboundEnqueuer;
    145  friend class nsWSAdmissionManager;
    146  friend class FailDelayManager;
    147  friend class CallOnMessageAvailable;
    148  friend class CallOnStop;
    149  friend class CallOnServerClose;
    150  friend class CallAcknowledge;
    151 
    152  // Common send code for binary + text msgs
    153  [[nodiscard]] nsresult SendMsgCommon(const nsACString& aMsg, bool isBinary,
    154                                       uint32_t length,
    155                                       nsIInputStream* aStream = nullptr);
    156 
    157  void EnqueueOutgoingMessage(nsDeque<OutboundMessage>& aQueue,
    158                              OutboundMessage* aMsg);
    159  void DoEnqueueOutgoingMessage();
    160 
    161  void PrimeNewOutgoingMessage();
    162  void DeleteCurrentOutGoingMessage();
    163  void GeneratePong(uint8_t* payload, uint32_t len);
    164  void GeneratePing();
    165 
    166  [[nodiscard]] nsresult OnNetworkChanged();
    167  [[nodiscard]] nsresult StartPinging();
    168 
    169  void BeginOpen(bool aCalledFromAdmissionManager);
    170  void BeginOpenInternal();
    171  [[nodiscard]] nsresult HandleExtensions();
    172  [[nodiscard]] nsresult SetupRequest();
    173  [[nodiscard]] nsresult ApplyForAdmission();
    174  [[nodiscard]] nsresult DoAdmissionDNS();
    175  [[nodiscard]] nsresult CallStartWebsocketData();
    176  [[nodiscard]] nsresult StartWebsocketData();
    177  uint16_t ResultToCloseCode(nsresult resultCode);
    178  void ReportConnectionTelemetry(nsresult aStatusCode);
    179 
    180  void StopSession(nsresult reason);
    181  void DoStopSession(nsresult reason);
    182  void AbortSession(nsresult reason);
    183  void ReleaseSession();
    184  void CleanupConnection();
    185  void IncrementSessionCount();
    186  void DecrementSessionCount();
    187 
    188  void EnsureHdrOut(uint32_t size);
    189 
    190  static void ApplyMask(uint32_t mask, uint8_t* data, uint64_t len);
    191 
    192  bool IsPersistentFramePtr();
    193  [[nodiscard]] nsresult ProcessInput(uint8_t* buffer, uint32_t count);
    194  [[nodiscard]] bool UpdateReadBuffer(uint8_t* buffer, uint32_t count,
    195                                      uint32_t accumulatedFragments,
    196                                      uint32_t* available);
    197 
    198  inline void ResetPingTimer() {
    199    mPingOutstanding = 0;
    200    if (mPingTimer) {
    201      if (!mPingInterval) {
    202        // The timer was created by forced ping and regular pinging is disabled,
    203        // so cancel and null out mPingTimer.
    204        mPingTimer->Cancel();
    205        mPingTimer = nullptr;
    206      } else {
    207        mPingTimer->SetDelay(mPingInterval);
    208      }
    209    }
    210  }
    211 
    212  void NotifyOnStart();
    213 
    214  nsCOMPtr<nsIEventTarget> mIOThread;
    215  // Set in AsyncOpenNative and AsyncOnChannelRedirect, modified in
    216  // DoStopSession on IO thread (.forget()).   Probably ok...
    217  nsCOMPtr<nsIHttpChannelInternal> mChannel;
    218  nsCOMPtr<nsIHttpChannel> mHttpChannel;
    219 
    220  nsCOMPtr<nsICancelable> mCancelable MOZ_GUARDED_BY(mMutex);
    221  // Mainthread only
    222  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
    223  // Set on Mainthread during AsyncOpen, used on IO thread and Mainthread
    224  nsCOMPtr<nsIRandomGenerator> mRandomGenerator;
    225 
    226  nsCString mHashedSecret;  // MainThread only
    227 
    228  // Used as key for connection managment: Initially set to hostname from URI,
    229  // then to IP address (unless we're leaving DNS resolution to a proxy server)
    230  // MainThread only
    231  nsCString mAddress;
    232  nsCString mPath;
    233  int32_t mPort;  // WS server port
    234  // Secondary key for the connection queue. Used by nsWSAdmissionManager.
    235  nsCString mOriginSuffix;  // MainThread only
    236 
    237  // Used for off main thread access to the URI string.
    238  // Set on MainThread in AsyncOpenNative, used on TargetThread and IO thread
    239  nsCString mHost;
    240  nsString mEffectiveURL;
    241 
    242  // Set on MainThread before multithread use, used on IO thread, cleared on
    243  // IOThread
    244  nsCOMPtr<nsISocketTransport> mTransport;
    245  nsCOMPtr<nsIAsyncInputStream> mSocketIn;
    246  nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
    247  RefPtr<WebSocketConnectionBase> mConnection;
    248 
    249  // Only used on IO Thread (accessed when known-to-be-null in DoStopSession
    250  // on MainThread before mDataStarted)
    251  nsCOMPtr<nsITimer> mCloseTimer;
    252  // set in AsyncOpenInternal on MainThread, used on IO thread.
    253  // No multithread use before it's set, no changes after that.
    254  uint32_t mCloseTimeout; /* milliseconds */
    255 
    256  nsCOMPtr<nsITimer> mOpenTimer; /* Mainthread only */
    257  uint32_t mOpenTimeout;         /* milliseconds, MainThread only */
    258  wsConnectingState mConnecting; /* 0 if not connecting, MainThread only */
    259  // Set on MainThread, deleted on MainThread, used on MainThread or
    260  // IO Thread (in DoStopSession). Mutex required to access off-main-thread.
    261  nsCOMPtr<nsITimer> mReconnectDelayTimer MOZ_GUARDED_BY(mMutex);
    262 
    263  // Only touched on IOThread (DoStopSession reads it on MainThread if
    264  // we haven't connected yet (mDataStarted==false), and it's always null
    265  // until mDataStarted=true)
    266  nsCOMPtr<nsITimer> mPingTimer;
    267 
    268  // Created in DoStopSession on IO thread (mDataStarted=true), accessed
    269  // only from IO Thread
    270  nsCOMPtr<nsITimer> mLingeringCloseTimer;
    271  const static int32_t kLingeringCloseTimeout = 1000;
    272  const static int32_t kLingeringCloseThreshold = 50;
    273 
    274  RefPtr<WebSocketEventService> mService;  // effectively const
    275 
    276  int32_t
    277      mMaxConcurrentConnections;  // only used in AsyncOpenNative on MainThread
    278 
    279  // Set on MainThread in AsyncOpenNative; then used on IO thread
    280  uint64_t mInnerWindowID;
    281 
    282  // following members are accessed only on the main thread
    283  uint32_t mGotUpgradeOK : 1;
    284  uint32_t mRecvdHttpUpgradeTransport : 1;
    285  uint32_t : 0;  // ensure these aren't mixed with the next set
    286 
    287  // following members are accessed only on the IO thread
    288  uint32_t mPingOutstanding : 1;
    289  uint32_t mReleaseOnTransmit : 1;
    290  uint32_t : 0;
    291 
    292  Atomic<bool> mDataStarted;
    293  // All changes to mRequestedClose happen under mutex, but since it's atomic,
    294  // it can be read anywhere without a lock
    295  Atomic<bool> mRequestedClose;
    296  // mServer/ClientClosed are only modified on IOThread
    297  Atomic<bool> mClientClosed;
    298  Atomic<bool> mServerClosed;
    299  // All changes to mStopped happen under mutex, but since it's atomic, it
    300  // can be read anywhere without a lock
    301  Atomic<bool> mStopped;
    302  Atomic<bool> mCalledOnStop;
    303  Atomic<bool> mTCPClosed;
    304  Atomic<bool> mOpenedHttpChannel;
    305  Atomic<bool> mIncrementedSessionCount;
    306  Atomic<bool> mDecrementedSessionCount;
    307 
    308  int32_t mMaxMessageSize;  // set on MainThread in AsyncOpenNative, read on IO
    309                            // thread
    310  // Set on IOThread, or on MainThread before mDataStarted.  Used on IO Thread
    311  // (after mDataStarted)
    312  nsresult mStopOnClose;
    313  uint16_t mServerCloseCode;     // only used on IO thread
    314  nsCString mServerCloseReason;  // only used on IO thread
    315  uint16_t mScriptCloseCode MOZ_GUARDED_BY(mMutex);
    316  nsCString mScriptCloseReason MOZ_GUARDED_BY(mMutex);
    317 
    318  // These are for the read buffers
    319  const static uint32_t kIncomingBufferInitialSize = 16 * 1024;
    320  // We're ok with keeping a buffer this size or smaller around for the life of
    321  // the websocket.  If a particular message needs bigger than this we'll
    322  // increase the buffer temporarily, then drop back down to this size.
    323  const static uint32_t kIncomingBufferStableSize = 128 * 1024;
    324 
    325  // Set at creation, used/modified only on IO thread
    326  uint8_t* mFramePtr;
    327  uint8_t* mBuffer;
    328  uint8_t mFragmentOpcode;
    329  uint32_t mFragmentAccumulator;
    330  uint32_t mBuffered;
    331  uint32_t mBufferSize;
    332 
    333  // These are for the send buffers
    334  const static int32_t kCopyBreak = 1000;
    335 
    336  // Only used on IO thread
    337  OutboundMessage* mCurrentOut;
    338  uint32_t mCurrentOutSent;
    339  nsDeque<OutboundMessage> mOutgoingMessages;
    340  nsDeque<OutboundMessage> mOutgoingPingMessages;
    341  nsDeque<OutboundMessage> mOutgoingPongMessages;
    342  uint32_t mHdrOutToSend;
    343  uint8_t* mHdrOut;
    344  uint8_t mOutHeader[kCopyBreak + 16]{0};
    345 
    346  // Set on MainThread in OnStartRequest (before mDataStarted), or in
    347  // HandleExtensions() or OnTransportAvailableInternal(),used on IO Thread
    348  // (after mDataStarted), cleared in DoStopSession on IOThread or on
    349  // MainThread (if mDataStarted == false).
    350  Mutex mCompressorMutex;
    351  UniquePtr<PMCECompression> mPMCECompressor MOZ_GUARDED_BY(mCompressorMutex);
    352 
    353  // Used by EnsureHdrOut, which isn't called anywhere
    354  uint32_t mDynamicOutputSize;
    355  uint8_t* mDynamicOutput;
    356  // Set on creation and AsyncOpen, read on both threads
    357  Atomic<bool> mPrivateBrowsing;
    358 
    359  nsCOMPtr<nsIDashboardEventNotifier>
    360      mConnectionLogService;  // effectively const
    361 
    362  mozilla::Mutex mMutex;
    363 };
    364 
    365 class WebSocketSSLChannel : public WebSocketChannel {
    366 public:
    367  WebSocketSSLChannel() { BaseWebSocketChannel::mEncrypted = true; }
    368 
    369 protected:
    370  virtual ~WebSocketSSLChannel() = default;
    371 };
    372 
    373 }  // namespace net
    374 }  // namespace mozilla
    375 
    376 #endif  // mozilla_net_WebSocketChannel_h