Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; 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 */
#ifndef NSSSocketControl_h
#define NSSSocketControl_h
#include "CommonSocketControl.h"
#include "SharedSSLState.h"
#include "TLSClientAuthCertSelection.h"
#include "nsThreadUtils.h"
extern mozilla::LazyLogModule gPIPNSSLog;
class SelectClientAuthCertificate;
class NSSSocketControl final : public CommonSocketControl {
NSSSocketControl(const nsCString& aHostName, int32_t aPort,
mozilla::psm::SharedSSLState& aState, uint32_t providerFlags,
uint32_t providerTlsFlags);
void SetForSTARTTLS(bool aForSTARTTLS);
bool GetForSTARTTLS();
nsresult GetFileDescPtr(PRFileDesc** aFilePtr);
nsresult SetFileDescPtr(PRFileDesc* aFilePtr);
bool IsHandshakePending() const {
return mHandshakePending;
void SetHandshakeNotPending() {
mHandshakePending = false;
void SetTLSVersionRange(SSLVersionRange range) {
mTLSVersionRange = range;
SSLVersionRange GetTLSVersionRange() const {
return mTLSVersionRange;
// From nsITLSSocketControl.
NS_IMETHOD ProxyStartSSL(void) override;
NS_IMETHOD StartTLS(void) override;
NS_IMETHOD AsyncStartTLS(JSContext* aCx,
mozilla::dom::Promise** aPromise) override;
NS_IMETHOD SetNPNList(nsTArray<nsCString>& aNPNList) override;
NS_IMETHOD GetAlpnEarlySelection(nsACString& _retval) override;
NS_IMETHOD GetEarlyDataAccepted(bool* aEarlyDataAccepted) override;
NS_IMETHOD DriveHandshake(void) override;
NS_IMETHOD GetKEAUsed(int16_t* aKEAUsed) override;
NS_IMETHOD GetKEAKeyBits(uint32_t* aKEAKeyBits) override;
NS_IMETHOD GetSSLVersionOffered(int16_t* aSSLVersionOffered) override;
NS_IMETHOD GetMACAlgorithmUsed(int16_t* aMACAlgorithmUsed) override;
bool GetDenyClientCert() override;
void SetDenyClientCert(bool aDenyClientCert) override;
NS_IMETHOD GetEsniTxt(nsACString& aEsniTxt) override;
NS_IMETHOD SetEsniTxt(const nsACString& aEsniTxt) override;
NS_IMETHOD GetEchConfig(nsACString& aEchConfig) override;
NS_IMETHOD SetEchConfig(const nsACString& aEchConfig) override;
NS_IMETHOD GetPeerId(nsACString& aResult) override;
NS_IMETHOD GetRetryEchConfig(nsACString& aEchConfig) override;
NS_IMETHOD DisableEarlyData(void) override;
NS_IMETHOD SetHandshakeCallbackListener(
nsITlsHandshakeCallbackListener* callback) override;
NS_IMETHOD Claim() override;
NS_IMETHOD SetBrowserId(uint64_t browserId) override;
NS_IMETHOD GetBrowserId(uint64_t* browserId) override;
PRStatus CloseSocketAndDestroy();
void SetNegotiatedNPN(const char* value, uint32_t length);
void SetEarlyDataAccepted(bool aAccepted);
void SetHandshakeCompleted();
bool IsHandshakeCompleted() const {
return mHandshakeCompleted;
void NoteTimeUntilReady();
void SetFalseStartCallbackCalled() {
mFalseStartCallbackCalled = true;
void SetFalseStarted() {
mFalseStarted = true;
// Note that this is only valid *during* a handshake; at the end of the
// handshake, it gets reset back to false.
void SetFullHandshake() {
mIsFullHandshake = true;
bool IsFullHandshake() const {
return mIsFullHandshake;
void UpdateEchExtensionStatus(EchExtensionStatus aEchExtensionStatus) {
mEchExtensionStatus = std::max(aEchExtensionStatus, mEchExtensionStatus);
EchExtensionStatus GetEchExtensionStatus() const {
return mEchExtensionStatus;
void WillSendXyberShare() {
mSentXyberShare = true;
bool SentXyberShare() {
return mSentXyberShare;
void SetHasTls13HandshakeSecrets() {
mHasTls13HandshakeSecrets = true;
bool HasTls13HandshakeSecrets() {
return mHasTls13HandshakeSecrets;
bool GetJoined() {
return mJoined;
uint32_t GetProviderTlsFlags() const {
return mProviderTlsFlags;
mozilla::psm::SharedSSLState& SharedState();
enum CertVerificationState {
void SetCertVerificationWaiting();
// Use errorCode == 0 to indicate success;
void SetCertVerificationResult(PRErrorCode errorCode) override;
void ClientAuthCertificateSelected(
nsTArray<uint8_t>& certBytes,
nsTArray<nsTArray<uint8_t>>& certChainBytes);
bool IsWaitingForCertVerification() const {
return mCertVerificationState == WaitingForCertVerification;
void AddPlaintextBytesRead(uint64_t val) {
mPlaintextBytesRead += val;
bool IsPreliminaryHandshakeDone() const {
return mPreliminaryHandshakeDone;
void SetPreliminaryHandshakeDone() {
mPreliminaryHandshakeDone = true;
void SetKEAUsed(int16_t kea) {
mKEAUsed = kea;
void SetKEAKeyBits(uint32_t keaBits) {
mKEAKeyBits = keaBits;
void SetMACAlgorithmUsed(int16_t mac) {
mMACAlgorithmUsed = mac;
void SetShortWritePending(int32_t amount, unsigned char data) {
mIsShortWritePending = true;
mShortWriteOriginalAmount = amount;
mShortWritePendingByte = data;
bool IsShortWritePending() {
return mIsShortWritePending;
unsigned char const* GetShortWritePendingByteRef() {
return &mShortWritePendingByte;
int32_t ResetShortWritePending() {
mIsShortWritePending = false;
return mShortWriteOriginalAmount;
#ifdef DEBUG
// These helpers assert that the caller does try to send the same data
// as it was previously when we hit the short-write. This is a measure
// to make sure we communicate correctly to the consumer.
void RememberShortWrittenBuffer(const unsigned char* data) {
mShortWriteBufferCheck =
memcpy(mShortWriteBufferCheck.get(), data, mShortWriteOriginalAmount);
void CheckShortWrittenBuffer(const unsigned char* data, int32_t amount) {
if (!mShortWriteBufferCheck) return;
MOZ_ASSERT(amount >= mShortWriteOriginalAmount,
"unexpected amount length after short write");
!memcmp(mShortWriteBufferCheck.get(), data, mShortWriteOriginalAmount),
"unexpected buffer content after short write");
mShortWriteBufferCheck = nullptr;
void SetSharedOwningReference(mozilla::psm::SharedSSLState* ref);
nsresult SetResumptionTokenFromExternalCache(PRFileDesc* fd);
void SetPreliminaryHandshakeInfo(const SSLChannelInfo& channelInfo,
const SSLCipherSuiteInfo& cipherInfo);
void SetPendingSelectClientAuthCertificate(
nsCOMPtr<nsIRunnable>&& selectClientAuthCertificate) {
gPIPNSSLog, mozilla::LogLevel::Debug,
("[%p] setting pending select client auth certificate", (void*)mFd));
// If the connection corresponding to this socket hasn't been claimed, it
// is a speculative connection. The connection will block until the "choose
// a client auth certificate" dialog has been shown. The dialog will only
// be shown when this connection gets claimed. However, necko will never
// claim the connection as long as it is blocking. Thus, this connection
// can't proceed, so it's best to cancel it. Necko will create a new,
// non-speculative connection instead.
if (!mClaimed) {
} else {
mPendingSelectClientAuthCertificate =
void MaybeDispatchSelectClientAuthCertificate() {
if (!IsWaitingForCertVerification() && mClaimed &&
mPendingSelectClientAuthCertificate) {
MOZ_LOG(gPIPNSSLog, mozilla::LogLevel::Debug,
("[%p] dispatching pending select client auth certificate",
mozilla::Unused << NS_DispatchToMainThread(
mPendingSelectClientAuthCertificate = nullptr;
~NSSSocketControl() = default;
PRFileDesc* mFd;
CertVerificationState mCertVerificationState;
mozilla::psm::SharedSSLState& mSharedState;
bool mForSTARTTLS;
SSLVersionRange mTLSVersionRange;
bool mHandshakePending;
bool mPreliminaryHandshakeDone; // after false start items are complete
nsresult ActivateSSL();
nsCString mEsniTxt;
nsCString mEchConfig;
bool mEarlyDataAccepted;
bool mDenyClientCert;
bool mFalseStartCallbackCalled;
bool mFalseStarted;
bool mIsFullHandshake;
bool mNotedTimeUntilReady;
EchExtensionStatus mEchExtensionStatus; // Currently only used for telemetry.
bool mSentXyberShare;
bool mHasTls13HandshakeSecrets;
// True when SSL layer has indicated an "SSL short write", i.e. need
// to call on send one or more times to push all pending data to write.
bool mIsShortWritePending;
// These are only valid if mIsShortWritePending is true.
// Value of the last byte pending from the SSL short write that needs
// to be passed to subsequent calls to send to perform the flush.
unsigned char mShortWritePendingByte;
// Original amount of data the upper layer has requested to write to
// return after the successful flush.
int32_t mShortWriteOriginalAmount;
#ifdef DEBUG
mozilla::UniquePtr<char[]> mShortWriteBufferCheck;
// mKEA* are used in false start and http/2 detetermination
// Values are from nsITLSSocketControl
int16_t mKEAUsed;
uint32_t mKEAKeyBits;
int16_t mMACAlgorithmUsed;
uint32_t mProviderTlsFlags;
mozilla::TimeStamp mSocketCreationTimestamp;
uint64_t mPlaintextBytesRead;
bool mClaimed;
nsCOMPtr<nsIRunnable> mPendingSelectClientAuthCertificate;
// Regarding the client certificate message in the TLS handshake, RFC 5246
// (TLS 1.2) says:
// If the certificate_authorities list in the certificate request
// message was non-empty, one of the certificates in the certificate
// chain SHOULD be issued by one of the listed CAs.
// (RFC 8446 (TLS 1.3) has a similar provision)
// These certificates may be known to gecko but not NSS (e.g. enterprise
// intermediates). In order to make these certificates discoverable to NSS
// so it can include them in the message, we cache them here as temporary
// certificates.
mozilla::UniqueCERTCertList mClientCertChain;
// if non-null this is a reference to the mSharedState (which is
// not an owning reference). If this is used, the info has a private
// state that does not share things like intolerance lists with the
// rest of the session. This is normally used when you have per
// socket tls flags overriding session wide defaults.
RefPtr<mozilla::psm::SharedSSLState> mOwningSharedRef;
nsCOMPtr<nsITlsHandshakeCallbackListener> mTlsHandshakeCallback;
uint64_t mBrowserId;
#endif // NSSSocketControl_h