diff --git a/netwerk/base/Dashboard.cpp b/netwerk/base/Dashboard.cpp index 398758aef..cf180dc1e 100644 --- a/netwerk/base/Dashboard.cpp +++ b/netwerk/base/Dashboard.cpp @@ -893,13 +893,15 @@ typedef struct #define ERROR(key, val) {key, #key} ErrorEntry socketTransportStatuses[] = { - ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)), - ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)), - ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)), - ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)), - ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)), - ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)), - ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)), + ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)), + ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)), + ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)), + ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)), + ERROR(NS_NET_STATUS_TLS_HANDSHAKE_STARTING, FAILURE(12)), + ERROR(NS_NET_STATUS_TLS_HANDSHAKE_ENDED, FAILURE(13)), + ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)), + ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)), + ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)), }; #undef ERROR diff --git a/netwerk/base/security-prefs.js b/netwerk/base/security-prefs.js index 17b851b7e..704c6ba3d 100644 --- a/netwerk/base/security-prefs.js +++ b/netwerk/base/security-prefs.js @@ -7,6 +7,7 @@ pref("security.tls.version.max", 3); pref("security.tls.version.fallback-limit", 3); pref("security.tls.insecure_fallback_hosts", ""); pref("security.tls.unrestricted_rc4_fallback", false); +pref("security.tls.enable_0rtt_data", false); pref("security.ssl.treat_unsafe_negotiation_as_broken", false); pref("security.ssl.require_safe_negotiation", false); diff --git a/netwerk/protocol/http/nsAHttpTransaction.h b/netwerk/protocol/http/nsAHttpTransaction.h index ff0695190..9b1e33ecc 100644 --- a/netwerk/protocol/http/nsAHttpTransaction.h +++ b/netwerk/protocol/http/nsAHttpTransaction.h @@ -204,6 +204,22 @@ public: virtual void DisableSpdy() { } virtual void ReuseConnectionOnRestartOK(bool) { } + + // Returns true if early-data is possible. + virtual bool Do0RTT() { + return false; + } + // This function will be called when a tls handshake has been finished and + // we know whether early-data that was sent has been accepted or not, e.g. + // do we need to restart a transaction. This will be called only if Do0RTT + // returns true. + // If aRestart parameter is true we need to restart the transaction, + // otherwise the erly-data has been accepted and we can continue the + // transaction. + // The function will return success or failure of the transaction restart. + virtual nsresult Finish0RTT(bool aRestart) { + return NS_ERROR_NOT_IMPLEMENTED; + } }; NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpTransaction, NS_AHTTPTRANSACTION_IID) diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index c6b9f4f1a..7ba3cdb04 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -79,6 +79,9 @@ nsHttpConnection::nsHttpConnection() , mResponseTimeoutEnabled(false) , mTCPKeepaliveConfig(kTCPKeepaliveDisabled) , mForceSendPending(false) + , m0RTTChecked(false) + , mWaitingFor0RTTResponse(false) + , mContentBytesWritten0RTT(0) { LOG(("Creating nsHttpConnection @%p\n", this)); @@ -268,8 +271,12 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion) } bool -nsHttpConnection::EnsureNPNComplete() +nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, + uint32_t &aOut0RTTBytesWritten) { + aOut0RTTWriteHandshakeValue = NS_OK; + aOut0RTTBytesWritten = 0; + // If for some reason the components to check on NPN aren't available, // this function will just return true to continue on and disable SPDY @@ -294,16 +301,81 @@ nsHttpConnection::EnsureNPNComplete() goto npnComplete; } + if (!m0RTTChecked) { + // We reuse m0RTTChecked. We want to send this status only once. + mTransaction->OnTransportStatus(mSocketTransport, + NS_NET_STATUS_TLS_HANDSHAKE_STARTING, + 0); + } + ssl = do_QueryInterface(securityInfo, &rv); if (NS_FAILED(rv)) goto npnComplete; rv = ssl->GetNegotiatedNPN(negotiatedNPN); + if (!m0RTTChecked && (rv == NS_ERROR_NOT_CONNECTED) && + !mConnInfo->UsingProxy()) { + // 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. + m0RTTChecked = true; + nsAutoCString earlyNegotiatedNPN; + nsresult rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN); + if (NS_FAILED(rvEarlyAlpn)) { + // if ssl->DriveHandshake() has never been called the value + // for AlpnEarlySelection is still not set. So call it here and + // check again. + LOG(("nsHttpConnection::EnsureNPNComplete %p - " + "early selected alpn not available, we will try one more time.", + this)); + // Let's do DriveHandshake again. + rv = ssl->DriveHandshake(); + if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { + goto npnComplete; + } + + // Check NegotiatedNPN first. + rv = ssl->GetNegotiatedNPN(negotiatedNPN); + if (rv == NS_ERROR_NOT_CONNECTED) { + rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN); + } + } + + if (NS_FAILED(rvEarlyAlpn)) { + LOG(("nsHttpConnection::EnsureNPNComplete %p - " + "early selected alpn not available", this)); + } else { + LOG(("nsHttpConnection::EnsureNPNComplete %p -" + "early selected alpn: %s", this, earlyNegotiatedNPN.get())); + uint32_t infoIndex; + const SpdyInformation *info = gHttpHandler->SpdyInfo(); + // We are doing 0RTT only with Http/1 right now! + if (NS_FAILED(info->GetNPNIndex(earlyNegotiatedNPN, &infoIndex))) { + // Check if early-data is allowed for this transaction. + if (mTransaction->Do0RTT()) { + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - We " + "can do 0RTT!", this)); + mWaitingFor0RTTResponse = true; + } + } + } + } + if (rv == NS_ERROR_NOT_CONNECTED) { - // By writing 0 bytes to the socket the SSL handshake machine is - // pushed forward. - uint32_t count = 0; - rv = mSocketOut->Write("", 0, &count); + if (mWaitingFor0RTTResponse) { + aOut0RTTWriteHandshakeValue = mTransaction->ReadSegments(this, + nsIOService::gDefaultSegmentSize, &aOut0RTTBytesWritten); + if (NS_FAILED(aOut0RTTWriteHandshakeValue) && + aOut0RTTWriteHandshakeValue != NS_BASE_STREAM_WOULD_BLOCK) { + goto npnComplete; + } + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - written %d " + "bytes during 0RTT", this, aOut0RTTBytesWritten)); + mContentBytesWritten0RTT += aOut0RTTBytesWritten; + } + + rv = ssl->DriveHandshake(); if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { goto npnComplete; } @@ -316,10 +388,30 @@ nsHttpConnection::EnsureNPNComplete() this, mConnInfo->HashKey().get(), negotiatedNPN.get(), mTLSFilter ? " [Double Tunnel]" : "")); - uint32_t infoIndex; - const SpdyInformation *info = gHttpHandler->SpdyInfo(); - if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) { - StartSpdy(info->Version[infoIndex]); + bool ealyDataAccepted = false; + if (mWaitingFor0RTTResponse) { + mWaitingFor0RTTResponse = false; + // Check if early data has been accepted. + rv = ssl->GetEarlyDataAccepted(&ealyDataAccepted); + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - early data " + "that was sent during 0RTT %s been accepted.", + this, ealyDataAccepted ? "has" : "has not")); + if (NS_FAILED(rv) || + NS_FAILED(mTransaction->Finish0RTT(!ealyDataAccepted))) { + mTransaction->Close(NS_ERROR_NET_RESET); + goto npnComplete; + } + } + if (!ealyDataAccepted) { + uint32_t infoIndex; + const SpdyInformation *info = gHttpHandler->SpdyInfo(); + if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) { + StartSpdy(info->Version[infoIndex]); + } + } else { + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %d bytes " + "has been sent during 0RTT.", this, mContentBytesWritten0RTT)); + mContentBytesWritten = mContentBytesWritten0RTT; } Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy()); @@ -328,6 +420,17 @@ nsHttpConnection::EnsureNPNComplete() npnComplete: LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true")); mNPNComplete = true; + + mTransaction->OnTransportStatus(mSocketTransport, + NS_NET_STATUS_TLS_HANDSHAKE_ENDED, + 0); + if (mWaitingFor0RTTResponse) { + mWaitingFor0RTTResponse = false; + if (NS_FAILED(mTransaction->Finish0RTT(true))) { + mTransaction->Close(NS_ERROR_NET_RESET); + } + mContentBytesWritten0RTT = 0; + } return true; } @@ -1572,7 +1675,9 @@ nsHttpConnection::OnSocketWritable() // request differently for http/1, http/2, spdy, etc.. and that is // negotiated with NPN/ALPN in the SSL handshake. - if (mConnInfo->UsingHttpsProxy() && !EnsureNPNComplete()) { + if (mConnInfo->UsingHttpsProxy() && + !EnsureNPNComplete(rv, transactionBytes)) { + MOZ_ASSERT(!transactionBytes); mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; } else if (mProxyConnectStream) { // If we're need an HTTP/1 CONNECT tunnel through a proxy @@ -1581,8 +1686,11 @@ nsHttpConnection::OnSocketWritable() rv = mProxyConnectStream->ReadSegments(ReadFromStream, this, nsIOService::gDefaultSegmentSize, &transactionBytes); - } else if (!EnsureNPNComplete()) { - mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; + } else if (!EnsureNPNComplete(rv, transactionBytes)) { + if (NS_SUCCEEDED(rv) && !transactionBytes && + NS_SUCCEEDED(mSocketOutCondition)) { + mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; + } } else { // for non spdy sessions let the connection manager know @@ -1630,7 +1738,7 @@ nsHttpConnection::OnSocketWritable() } else if (!transactionBytes) { rv = NS_OK; - if (mTransaction) { // in case the ReadSegments stack called CloseTransaction() + if (mTransaction && !mWaitingFor0RTTResponse) { // in case the ReadSegments stack called CloseTransaction() // // at this point we've written out the entire transaction, and now we // must wait for the server's response. we manufacture a status message diff --git a/netwerk/protocol/http/nsHttpConnection.h b/netwerk/protocol/http/nsHttpConnection.h index f4fef480a..f7ddde320 100644 --- a/netwerk/protocol/http/nsHttpConnection.h +++ b/netwerk/protocol/http/nsHttpConnection.h @@ -241,7 +241,8 @@ private: // Makes certain the SSL handshake is complete and NPN negotiation // has had a chance to happen - bool EnsureNPNComplete(); + bool EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, + uint32_t &aOut0RTTBytesWritten); void SetupSSL(); // Start the Spdy transaction handler when NPN indicates spdy/* @@ -350,6 +351,17 @@ private: uint32_t mTCPKeepaliveConfig; nsCOMPtr mTCPKeepaliveTransitionTimer; + // Helper variable for 0RTT handshake; + bool m0RTTChecked; // Possible 0RTT has been + // checked. + bool mWaitingFor0RTTResponse; // We have are + // sending 0RTT + // data and we + // are waiting + // for the end of + // the handsake. + int64_t mContentBytesWritten0RTT; + private: // For ForceSend() static void ForceSendIO(nsITimer *aTimer, void *aClosure); diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index ec5aaaa34..a806000b0 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -139,6 +139,8 @@ nsHttpTransaction::nsHttpTransaction() , mAppId(NECKO_NO_APP_ID) , mIsInBrowser(false) , mClassOfService(0) + , m0RTTInProgress(false) + , mTransportStatus(NS_OK) { LOG(("Creating nsHttpTransaction @%p\n", this)); gHttpHandler->GetMaxPipelineObjectSize(&mMaxPipelineObjectSize); @@ -529,6 +531,50 @@ nsHttpTransaction::OnTransportStatus(nsITransport* transport, } } + // A transaction can given to multiple HalfOpen sockets (this is a bug in + // nsHttpConnectionMgr). We are going to fix it here as a work around to be + // able to uplift it. + switch(status) { + case NS_NET_STATUS_RESOLVING_HOST: + if (mTransportStatus != NS_OK) { + LOG(("nsHttpTransaction::OnSocketStatus - ignore socket events " + "from backup transport")); + return; + } + break; + case NS_NET_STATUS_RESOLVED_HOST: + if (mTransportStatus != NS_NET_STATUS_RESOLVING_HOST && + mTransportStatus != NS_OK) { + LOG(("nsHttpTransaction::OnSocketStatus - ignore socket events " + "from backup transport")); + return; + } + break; + case NS_NET_STATUS_CONNECTING_TO: + if (mTransportStatus != NS_NET_STATUS_RESOLVING_HOST && + mTransportStatus != NS_NET_STATUS_RESOLVED_HOST && + mTransportStatus != NS_OK) { + LOG(("nsHttpTransaction::OnSocketStatus - ignore socket events " + "from backup transport")); + return; + } + break; + case NS_NET_STATUS_CONNECTED_TO: + if (mTransportStatus != NS_NET_STATUS_RESOLVING_HOST && + mTransportStatus != NS_NET_STATUS_RESOLVED_HOST && + mTransportStatus != NS_NET_STATUS_CONNECTING_TO && + mTransportStatus != NS_OK) { + LOG(("nsHttpTransaction::OnSocketStatus - ignore socket events " + "from backup transport")); + return; + } + break; + default: + LOG(("nsHttpTransaction::OnSocketStatus - a new event")); + } + + mTransportStatus = status; + // If the timing is enabled, and we are not using a persistent connection // then the requestStart timestamp will be null, so we mark the timestamps // for domainLookupStart/End and connectStart/End @@ -542,7 +588,9 @@ nsHttpTransaction::OnTransportStatus(nsITransport* transport, } else if (status == NS_NET_STATUS_CONNECTING_TO) { SetConnectStart(TimeStamp::Now()); } else if (status == NS_NET_STATUS_CONNECTED_TO) { - SetConnectEnd(TimeStamp::Now()); + SetConnectEnd(TimeStamp::Now(), true); + } else if (status == NS_NET_STATUS_TLS_HANDSHAKE_ENDED) { + SetConnectEnd(TimeStamp::Now(), false); } } @@ -687,7 +735,7 @@ nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader, return mStatus; } - if (!mConnected) { + if (!mConnected && !m0RTTInProgress) { mConnected = true; mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo)); } @@ -1271,6 +1319,8 @@ nsHttpTransaction::Restart() } } + mTransportStatus = NS_OK; + return gHttpHandler->InitiateTransaction(this, mPriority); } @@ -2304,5 +2354,33 @@ nsHttpTransaction::GetNetworkAddresses(NetAddr &self, NetAddr &peer) peer = mPeerAddr; } +bool +nsHttpTransaction::Do0RTT() +{ + if (mRequestHead->IsSafeMethod() && + !mConnection->IsProxyConnectInProgress()) { + m0RTTInProgress = true; + } + return m0RTTInProgress; +} + +nsresult +nsHttpTransaction::Finish0RTT(bool aRestart) +{ + MOZ_ASSERT(m0RTTInProgress); + m0RTTInProgress = false; + if (aRestart) { + // Reset request headers to be sent again. + nsCOMPtr seekable = + do_QueryInterface(mRequestStream); + if (seekable) { + seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + } else { + return NS_ERROR_FAILURE; + } + } + return NS_OK; +} + } // namespace net } // namespace mozilla diff --git a/netwerk/protocol/http/nsHttpTransaction.h b/netwerk/protocol/http/nsHttpTransaction.h index b7021dc13..085975aaf 100644 --- a/netwerk/protocol/http/nsHttpTransaction.h +++ b/netwerk/protocol/http/nsHttpTransaction.h @@ -164,6 +164,8 @@ public: int64_t GetTransferSize() { return mTransferSize; } + bool Do0RTT() override; + nsresult Finish0RTT(bool aRestart) override; private: friend class DeleteHttpTransaction; virtual ~nsHttpTransaction(); @@ -451,6 +453,10 @@ public: private: RefPtr mTunnelProvider; + bool m0RTTInProgress; + + nsresult mTransportStatus; + public: void GetNetworkAddresses(NetAddr &self, NetAddr &peer); diff --git a/netwerk/socket/nsISSLSocketControl.idl b/netwerk/socket/nsISSLSocketControl.idl index b99bd2e2c..ea1ec0729 100644 --- a/netwerk/socket/nsISSLSocketControl.idl +++ b/netwerk/socket/nsISSLSocketControl.idl @@ -44,6 +44,22 @@ interface nsISSLSocketControl : nsISupports { */ readonly attribute ACString negotiatedNPN; + /* For 0RTT we need to know the alpn protocol selected for the last tls + * session. This function will return a value if applicable or an error + * NS_ERROR_NOT_AVAILABLE. + */ + ACString getAlpnEarlySelection(); + + /* If 0RTT handshake was applied and some data has been sent, as soon as + * the handshake finishes this attribute will be set to appropriate value. + */ + readonly attribute bool earlyDataAccepted; + + /* When 0RTT is performed, PR_Write will not drive the handshake forward. + * It must be forced by calling this function. + */ + void driveHandshake(); + /* Determine if a potential SSL connection to hostname:port with * a desired NPN negotiated protocol of npnProtocol can use the socket * associated with this object instead of making a new one. diff --git a/security/manager/ssl/nsNSSCallbacks.cpp b/security/manager/ssl/nsNSSCallbacks.cpp index 3d684e613..65fbe4d66 100644 --- a/security/manager/ssl/nsNSSCallbacks.cpp +++ b/security/manager/ssl/nsNSSCallbacks.cpp @@ -887,6 +887,7 @@ PreliminaryHandshakeDone(PRFileDesc* fd) SSLChannelInfo channelInfo; if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) == SECSuccess) { infoObject->SetSSLVersionUsed(channelInfo.protocolVersion); + infoObject->SetEarlyDataAccepted(channelInfo.earlyDataAccepted); SSLCipherSuiteInfo cipherInfo; if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp index e66cdcfb8..0821c6513 100644 --- a/security/manager/ssl/nsNSSComponent.cpp +++ b/security/manager/ssl/nsNSSComponent.cpp @@ -726,6 +726,7 @@ static const bool REQUIRE_SAFE_NEGOTIATION_DEFAULT = false; static const bool FALSE_START_ENABLED_DEFAULT = true; static const bool NPN_ENABLED_DEFAULT = true; static const bool ALPN_ENABLED_DEFAULT = false; +static const bool ENABLED_0RTT_DATA_DEFAULT = false; static void ConfigureTLSSessionIdentifiers() @@ -1086,6 +1087,10 @@ nsNSSComponent::InitializeNSS() Preferences::GetBool("security.ssl.enable_alpn", ALPN_ENABLED_DEFAULT)); + SSL_OptionSetDefault(SSL_ENABLE_0RTT_DATA, + Preferences::GetBool("security.tls.enable_0rtt_data", + ENABLED_0RTT_DATA_DEFAULT)); + if (NS_FAILED(InitializeCipherSuite())) { MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("Unable to initialize cipher suite settings\n")); return NS_ERROR_FAILURE; @@ -1300,6 +1305,10 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, SSL_OptionSetDefault(SSL_ENABLE_ALPN, Preferences::GetBool("security.ssl.enable_alpn", ALPN_ENABLED_DEFAULT)); + } else if (prefName.EqualsLiteral("security.tls.enable_0rtt_data")) { + SSL_OptionSetDefault(SSL_ENABLE_0RTT_DATA, + Preferences::GetBool("security.tls.enable_0rtt_data", + ENABLED_0RTT_DATA_DEFAULT)); } else if (prefName.Equals("security.ssl.disable_session_identifiers")) { ConfigureTLSSessionIdentifiers(); } else if (prefName.EqualsLiteral("security.OCSP.enabled") || diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp index c203267d3..bce66f183 100644 --- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -61,6 +61,8 @@ using namespace mozilla::psm; namespace { +#define MAX_ALPN_LENGTH 255 + void getSiteKey(const nsACString& hostName, uint16_t port, /*out*/ nsCSubstring& key) @@ -93,6 +95,7 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags) mRememberClientAuthCertificate(false), mPreliminaryHandshakeDone(false), mNPNCompleted(false), + mEarlyDataAccepted(false), mFalseStartCallbackCalled(false), mFalseStarted(false), mIsFullHandshake(false), @@ -313,6 +316,71 @@ nsNSSSocketInfo::GetNegotiatedNPN(nsACString& aNegotiatedNPN) return NS_OK; } +NS_IMETHODIMP +nsNSSSocketInfo::GetAlpnEarlySelection(nsACString& aAlpnSelected) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown() || isPK11LoggedOut()) { + return NS_ERROR_NOT_AVAILABLE; + } + SSLNextProtoState alpnState; + unsigned char chosenAlpn[MAX_ALPN_LENGTH]; + unsigned int chosenAlpnLen; + SECStatus rv = SSL_GetNextProto(mFd, &alpnState, chosenAlpn, &chosenAlpnLen, + AssertedCast(ArrayLength(chosenAlpn))); + + if (rv != SECSuccess || alpnState != SSL_NEXT_PROTO_EARLY_VALUE || + chosenAlpnLen == 0) { + return NS_ERROR_NOT_AVAILABLE; + } + + aAlpnSelected.Assign(BitwiseCast(chosenAlpn), + chosenAlpnLen); + return NS_OK; +} + +NS_IMETHODIMP +nsNSSSocketInfo::GetEarlyDataAccepted(bool* aAccepted) +{ + *aAccepted = mEarlyDataAccepted; + return NS_OK; +} + +void +nsNSSSocketInfo::SetEarlyDataAccepted(bool aAccepted) +{ + mEarlyDataAccepted = aAccepted; +} + +NS_IMETHODIMP +nsNSSSocketInfo::DriveHandshake() +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown() || isPK11LoggedOut()) { + return NS_ERROR_NOT_AVAILABLE; + } + if (!mFd) { + return NS_ERROR_FAILURE; + } + PRErrorCode errorCode = GetErrorCode(); + if (errorCode) { + return GetXPCOMFromNSSError(errorCode); + } + + SECStatus rv = SSL_ForceHandshake(mFd); + + if (rv != SECSuccess) { + errorCode = PR_GetError(); + if (errorCode == PR_WOULD_BLOCK_ERROR) { + return NS_BASE_STREAM_WOULD_BLOCK; + } + + SetCanceled(errorCode, PlainErrorMessage); + return GetXPCOMFromNSSError(errorCode); + } + return NS_OK; +} + NS_IMETHODIMP nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval) { diff --git a/security/manager/ssl/nsNSSIOLayer.h b/security/manager/ssl/nsNSSIOLayer.h index 7a84849df..ba966c420 100644 --- a/security/manager/ssl/nsNSSIOLayer.h +++ b/security/manager/ssl/nsNSSIOLayer.h @@ -52,6 +52,7 @@ public: const nsNSSShutDownPreventionLock& proofOfLock); void SetNegotiatedNPN(const char* value, uint32_t length); + void SetEarlyDataAccepted(bool aAccepted); void SetHandshakeCompleted(); void NoteTimeUntilReady(); @@ -139,6 +140,7 @@ private: nsCString mNegotiatedNPN; bool mNPNCompleted; + bool mEarlyDataAccepted; bool mFalseStartCallbackCalled; bool mFalseStarted; bool mIsFullHandshake; diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h index 1610caf9e..63586f59e 100644 --- a/xpcom/base/ErrorList.h +++ b/xpcom/base/ErrorList.h @@ -313,13 +313,15 @@ ERROR(NS_NET_STATUS_WRITING, FAILURE(9)), /* nsISocketTransport */ - ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)), - ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)), - ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)), - ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)), - ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)), - ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)), - ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)), + ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)), + ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)), + ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)), + ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)), + ERROR(NS_NET_STATUS_TLS_HANDSHAKE_STARTING, FAILURE(12)), + ERROR(NS_NET_STATUS_TLS_HANDSHAKE_ENDED, FAILURE(13)), + ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)), + ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)), + ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)), /* nsIInterceptedChannel */ /* Generic error for non-specific failures during service worker interception */