Commit 48469478 authored by Butkovits Atila's avatar Butkovits Atila
Browse files

Backed out changeset 36943e34018f (bug 1754744) for causing bustages at...

Backed out changeset 36943e34018f (bug 1754744) for causing bustages at TlsHandshaker.cpp. CLOSED TREE
parent 404b3257
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -139,9 +139,8 @@ void DnsAndConnectSocket::PrintDiagnostics(nsCString& log) {
void nsHttpConnection::PrintDiagnostics(nsCString& log) {
  log.AppendPrintf("    CanDirectlyActivate = %d\n", CanDirectlyActivate());

  log.AppendPrintf("    npncomplete = %d  setupSSLCalled = %d\n",
                   mTlsHandshaker->NPNComplete(),
                   mTlsHandshaker->SetupSSLCalled());
  log.AppendPrintf("    npncomplete = %d  setupSSLCalled = %d\n", mNPNComplete,
                   mSetupSSLCalled);

  log.AppendPrintf("    spdyVersion = %d  reportedSpdy = %d everspdy = %d\n",
                   static_cast<int32_t>(mUsingSpdyVersion), mReportedSpdy,
+0 −329
Original line number Diff line number Diff line
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=4 sw=2 sts=2 et cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// HttpLog.h should generally be included first
#include "HttpLog.h"

#include "TlsHandshaker.h"
#include "nsHttpConnectionInfo.h"
#include "mozilla/StaticPrefs_network.h"

#define TLS_EARLY_DATA_NOT_AVAILABLE 0
#define TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED 1
#define TLS_EARLY_DATA_AVAILABLE_AND_USED 2

namespace mozilla::net {

NS_IMPL_ISUPPORTS(TlsHandshaker, nsITlsHandshakeCallbackListener)

TlsHandshaker::TlsHandshaker(nsHttpConnectionInfo* aInfo,
                             nsHttpConnection* aOwner)
    : mConnInfo(aInfo), mOwner(aOwner) {
  LOG(("TlsHandshaker ctor %p", this));
}

TlsHandshaker::~TlsHandshaker() { LOG(("TlsHandshaker dtor %p", this)); }

NS_IMETHODIMP
TlsHandshaker::HandshakeDone() {
  LOG(("TlsHandshaker::HandshakeDone mOwner=%p", mOwner.get()));
  if (mOwner) {
    mTlsHandshakeComplitionPending = true;

    // HandshakeDone needs to be dispatched so that it is not called inside
    // nss locks.
    RefPtr<TlsHandshaker> self(this);
    NS_DispatchToCurrentThread(NS_NewRunnableFunction(
        "TlsHandshaker::HandshakeDoneInternal", [self{std::move(self)}]() {
          if (self->mTlsHandshakeComplitionPending && self->mOwner) {
            self->mOwner->HandshakeDoneInternal();
            self->mTlsHandshakeComplitionPending = false;
          }
        }));
  }
  return NS_OK;
}

void TlsHandshaker::SetupSSL(bool aInSpdyTunnel, bool aForcePlainText) {
  if (!mOwner) {
    return;
  }

  LOG1(("TlsHandshaker::SetupSSL %p caps=0x%X %s\n", mOwner.get(),
        mOwner->TransactionCaps(), mConnInfo->HashKey().get()));

  if (mSetupSSLCalled) {  // do only once
    return;
  }
  mSetupSSLCalled = true;

  if (mNPNComplete) {
    return;
  }

  // we flip this back to false if SetNPNList succeeds at the end
  // of this function
  mNPNComplete = true;

  if (!mConnInfo->FirstHopSSL() || aForcePlainText) {
    return;
  }

  // if we are connected to the proxy with TLS, start the TLS
  // flow immediately without waiting for a CONNECT sequence.
  DebugOnly<nsresult> rv{};
  if (aInSpdyTunnel) {
    rv = InitSSLParams(false, true);
  } else {
    bool usingHttpsProxy = mConnInfo->UsingHttpsProxy();
    rv = InitSSLParams(usingHttpsProxy, usingHttpsProxy);
  }
  MOZ_ASSERT(NS_SUCCEEDED(rv));
}

nsresult TlsHandshaker::InitSSLParams(bool connectingToProxy,
                                      bool proxyStartSSL) {
  LOG(("TlsHandshaker::InitSSLParams [mOwner=%p] connectingToProxy=%d\n",
       mOwner.get(), connectingToProxy));
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  if (!mOwner) {
    return NS_ERROR_ABORT;
  }

  nsresult rv;
  nsCOMPtr<nsISupports> securityInfo;
  mOwner->GetSecurityInfo(getter_AddRefs(securityInfo));
  if (!securityInfo) {
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // If proxy is use or 0RTT is excluded for a origin, don't use early-data.
  if (mConnInfo->UsingProxy() || gHttpHandler->Is0RttTcpExcluded(mConnInfo)) {
    ssl->DisableEarlyData();
  }

  if (proxyStartSSL) {
    rv = ssl->ProxyStartSSL();
    if (NS_FAILED(rv)) {
      return rv;
    }
  }

  if (NS_SUCCEEDED(SetupNPNList(ssl, mOwner->TransactionCaps())) &&
      NS_SUCCEEDED(ssl->SetHandshakeCallbackListener(this))) {
    LOG(("InitSSLParams Setting up SPDY Negotiation OK mOwner=%p",
         mOwner.get()));
    mNPNComplete = false;
  }

  return NS_OK;
}

// The naming of NPN is historical - this function creates the basic
// offer list for both NPN and ALPN. ALPN validation callbacks are made
// now before the handshake is complete, and NPN validation callbacks
// are made during the handshake.
nsresult TlsHandshaker::SetupNPNList(nsISSLSocketControl* ssl, uint32_t caps) {
  nsTArray<nsCString> protocolArray;

  nsCString npnToken = mConnInfo->GetNPNToken();
  if (npnToken.IsEmpty()) {
    // The first protocol is used as the fallback if none of the
    // protocols supported overlap with the server's list.
    // When using ALPN the advertised preferences are protocolArray indicies
    // {1, .., N, 0} in decreasing order.
    // For NPN, In the case of overlap, matching priority is driven by
    // the order of the server's advertisement - with index 0 used when
    // there is no match.
    protocolArray.AppendElement("http/1.1"_ns);

    if (StaticPrefs::network_http_http2_enabled() &&
        !(caps & NS_HTTP_DISALLOW_SPDY)) {
      LOG(("nsHttpConnection::SetupSSL Allow SPDY NPN selection"));
      const SpdyInformation* info = gHttpHandler->SpdyInfo();
      if (info->ALPNCallbacks(ssl)) {
        protocolArray.AppendElement(info->VersionString);
      }
    }
  } else {
    LOG(("nsHttpConnection::SetupSSL limiting NPN selection to %s",
         npnToken.get()));
    protocolArray.AppendElement(npnToken);
  }

  nsresult rv = ssl->SetNPNList(protocolArray);
  LOG(("TlsHandshaker::SetupNPNList %p %" PRIx32 "\n", mOwner.get(),
       static_cast<uint32_t>(rv)));
  return rv;
}

// Checks if TLS handshake is needed and it is responsible to move it forward.
bool TlsHandshaker::EnsureNPNComplete() {
  if (!mOwner) {
    mNPNComplete = true;
    return true;
  }

  nsCOMPtr<nsISocketTransport> transport = mOwner->Transport();
  MOZ_ASSERT(transport);
  if (!transport) {
    // this cannot happen
    mNPNComplete = true;
    return true;
  }

  if (mNPNComplete) {
    return true;
  }

  if (mTlsHandshakeComplitionPending) {
    return false;
  }

  nsresult rv = NS_OK;
  nsCOMPtr<nsISupports> securityInfo;
  mOwner->GetSecurityInfo(getter_AddRefs(securityInfo));
  if (!securityInfo) {
    FinishNPNSetup(false, false);
    return true;
  }

  nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
  if (NS_FAILED(rv)) {
    FinishNPNSetup(false, false);
    return true;
  }

  if (!m0RTTChecked) {
    // We reuse m0RTTChecked. We want to send this status only once.
    RefPtr<nsAHttpTransaction> transaction = mOwner->Transaction();
    nsCOMPtr<nsISocketTransport> transport = mOwner->Transport();
    if (transaction && transport) {
      transaction->OnTransportStatus(transport,
                                     NS_NET_STATUS_TLS_HANDSHAKE_STARTING, 0);
    }
  }

  LOG(("TlsHandshaker::EnsureNPNComplete [mOwner=%p] drive TLS handshake",
       mOwner.get()));
  rv = ssl->DriveHandshake();
  if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
    FinishNPNSetup(false, true);
    return true;
  }

  Check0RttEnabled(ssl);
  return false;
}

void TlsHandshaker::EarlyDataDone() {
  if (mEarlyDataState == EarlyData::USED) {
    mEarlyDataState = EarlyData::DONE_USED;
  } else if (mEarlyDataState == EarlyData::CANNOT_BE_USED) {
    mEarlyDataState = EarlyData::DONE_CANNOT_BE_USED;
  } else if (mEarlyDataState == EarlyData::NOT_AVAILABLE) {
    mEarlyDataState = EarlyData::DONE_NOT_AVAILABLE;
  }
}

void TlsHandshaker::FinishNPNSetup(bool handshakeSucceeded,
                                   bool hasSecurityInfo) {
  LOG(("TlsHandshaker::FinishNPNSetup mOwner=%p", mOwner.get()));
  mNPNComplete = true;

  mOwner->PostProcessNPNSetup(handshakeSucceeded, hasSecurityInfo,
                              EarlyDataUsed());
  EarlyDataDone();
}

void TlsHandshaker::Check0RttEnabled(nsISSLSocketControl* ssl) {
  if (!mOwner) {
    return;
  }

  if (m0RTTChecked) {
    return;
  }

  m0RTTChecked = true;

  if (mConnInfo->UsingProxy()) {
    return;
  }

  // There is no ALPN info (yet!). We need to consider doing 0RTT. We
  // will do so if there is ALPN information from a previous session
  // (AlpnEarlySelection), we are using HTTP/1, and the request data can
  // be safely retried.
  if (NS_FAILED(ssl->GetAlpnEarlySelection(mEarlyNegotiatedALPN))) {
    LOG1(
        ("TlsHandshaker::Check0RttEnabled %p - "
         "early selected alpn not available",
         mOwner.get()));
  } else {
    LOG1(
        ("TlsHandshaker::Check0RttEnabled %p -"
         "early selected alpn: %s",
         mOwner.get(), mEarlyNegotiatedALPN.get()));
    const SpdyInformation* info = gHttpHandler->SpdyInfo();
    if (!mEarlyNegotiatedALPN.Equals(info->VersionString)) {
      // This is the HTTP/1 case.
      // Check if early-data is allowed for this transaction.
      RefPtr<nsAHttpTransaction> transaction = mOwner->Transaction();
      if (transaction && transaction->Do0RTT()) {
        LOG(
            ("TlsHandshaker::Check0RttEnabled [mOwner=%p] - We "
             "can do 0RTT (http/1)!",
             mOwner.get()));
        mEarlyDataState = EarlyData::USED;
      } else {
        mEarlyDataState = EarlyData::CANNOT_BE_USED;
        // Poll for read now. Polling for write will cause us to busy wait.
        // When the handshake is done the polling flags will be set correctly.
        Unused << mOwner->ResumeRecv();
      }
    } else {
      // We have h2, we can at least 0-RTT the preamble and opening
      // SETTINGS, etc, and maybe some of the first request
      LOG(
          ("TlsHandshaker::Check0RttEnabled [mOwner=%p] - Starting "
           "0RTT for h2!",
           mOwner.get()));
      mEarlyDataState = EarlyData::USED;
      mOwner->Start0RTTSpdy(info->Version);
    }
  }
}

void TlsHandshaker::EarlyDataTelemetry(int16_t tlsVersion,
                                       bool earlyDataAccepted,
                                       int64_t aContentBytesWritten0RTT) {
  // Send the 0RTT telemetry only for tls1.3
  if (tlsVersion > nsISSLSocketControl::TLS_VERSION_1_2) {
    Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_NEGOTIATED,
                          (mEarlyDataState == EarlyData::NOT_AVAILABLE)
                              ? TLS_EARLY_DATA_NOT_AVAILABLE
                              : ((mEarlyDataState == EarlyData::USED)
                                     ? TLS_EARLY_DATA_AVAILABLE_AND_USED
                                     : TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED));
    if (EarlyDataUsed()) {
      Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_ACCEPTED,
                            earlyDataAccepted);
    }
    if (earlyDataAccepted) {
      Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_BYTES_WRITTEN,
                            aContentBytesWritten0RTT);
    }
  }
}

}  // namespace mozilla::net
+0 −94
Original line number Diff line number Diff line
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef TlsHandshaker_h__
#define TlsHandshaker_h__

#include "nsITlsHandshakeListener.h"

class nsISocketTransport;
class nsISSLSocketControl;

namespace mozilla::net {

class nsHttpConnection;
class nsHttpConnectionInfo;

class TlsHandshaker : public nsITlsHandshakeCallbackListener {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSITLSHANDSHAKECALLBACKLISTENER

  TlsHandshaker(nsHttpConnectionInfo* aInfo, nsHttpConnection* aOwner);

  void SetupSSL(bool aInSpdyTunnel, bool aForcePlainText);
  [[nodiscard]] nsresult InitSSLParams(bool connectingToProxy,
                                       bool ProxyStartSSL);
  [[nodiscard]] nsresult SetupNPNList(nsISSLSocketControl* ssl, uint32_t caps);
  // Makes certain the SSL handshake is complete and NPN negotiation
  // has had a chance to happen
  [[nodiscard]] bool EnsureNPNComplete();
  void FinishNPNSetup(bool handshakeSucceeded, bool hasSecurityInfo);
  bool EarlyDataAvailable() const {
    return mEarlyDataState == EarlyData::USED ||
           mEarlyDataState == EarlyData::CANNOT_BE_USED;
  }
  bool EarlyDataWasAvailable() const {
    return mEarlyDataState != EarlyData::NOT_AVAILABLE &&
           mEarlyDataState != EarlyData::DONE_NOT_AVAILABLE;
  }
  bool EarlyDataUsed() const { return mEarlyDataState == EarlyData::USED; }
  bool EarlyDataCanNotBeUsed() const {
    return mEarlyDataState == EarlyData::CANNOT_BE_USED;
  }
  void EarlyDataDone();
  void EarlyDataTelemetry(int16_t tlsVersion, bool earlyDataAccepted,
                          int64_t aContentBytesWritten0RTT);

  bool NPNComplete() const { return mNPNComplete; }
  bool SetupSSLCalled() const { return mSetupSSLCalled; }
  bool TlsHandshakeComplitionPending() const {
    return mTlsHandshakeComplitionPending;
  }
  const nsCString& EarlyNegotiatedALPN() const { return mEarlyNegotiatedALPN; }
  void SetNPNComplete() { mNPNComplete = true; }
  void NotifyClose() {
    mTlsHandshakeComplitionPending = false;
    mOwner = nullptr;
  }

 private:
  virtual ~TlsHandshaker();

  void Check0RttEnabled(nsISSLSocketControl* ssl);

  // SPDY related
  bool mSetupSSLCalled{false};
  bool mNPNComplete{false};

  bool mTlsHandshakeComplitionPending{false};
  // Helper variable for 0RTT handshake;
  // Possible 0RTT has been checked.
  bool m0RTTChecked{false};
  // 0RTT data state.
  enum EarlyData {
    NOT_AVAILABLE,
    USED,
    CANNOT_BE_USED,
    DONE_NOT_AVAILABLE,
    DONE_USED,
    DONE_CANNOT_BE_USED,
  };
  EarlyData mEarlyDataState{EarlyData::NOT_AVAILABLE};
  nsCString mEarlyNegotiatedALPN;
  RefPtr<nsHttpConnectionInfo> mConnInfo;
  // nsHttpConnection and TlsHandshaker create a reference cycle. To break this
  // cycle, NotifyClose() needs to be called in nsHttpConnection::Close().
  RefPtr<nsHttpConnection> mOwner;
};

}  // namespace mozilla::net

#endif  // TlsHandshaker_h__
+0 −1
Original line number Diff line number Diff line
@@ -153,7 +153,6 @@ UNIFIED_SOURCES += [
    "SocketWrapper.cpp",
    "SpeculativeTransaction.cpp",
    "TLSFilterTransaction.cpp",
    "TlsHandshaker.cpp",
    "TRRServiceChannel.cpp",
]

+312 −46

File changed.

Preview size limit exceeded, changes collapsed.

Loading