/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=4 sw=2 et cindent: */ /* 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 "Http2StreamTunnel.h" #include "TLSTransportLayer.h" #include "nsISocketProvider.h" #include "nsITLSSocketControl.h" #include "nsQueryObject.h" #include "nsSocketProviderService.h" #include "nsSocketTransport2.h" namespace mozilla::net { //----------------------------------------------------------------------------- // TLSTransportLayerInputStream impl //----------------------------------------------------------------------------- NS_IMPL_QUERY_INTERFACE(TLSTransportLayer::InputStreamWrapper, nsIInputStream, nsIAsyncInputStream) NS_IMETHODIMP_(MozExternalRefCountType) TLSTransportLayer::InputStreamWrapper::AddRef() { return mTransport->AddRef(); } NS_IMETHODIMP_(MozExternalRefCountType) TLSTransportLayer::InputStreamWrapper::Release() { return mTransport->Release(); } TLSTransportLayer::InputStreamWrapper::InputStreamWrapper( nsIAsyncInputStream* aInputStream, TLSTransportLayer* aTransport) : mSocketIn(aInputStream), mTransport(aTransport) {} NS_IMETHODIMP TLSTransportLayer::InputStreamWrapper::Close() { LOG(("TLSTransportLayer::InputStreamWrapper::Close [this=%p]\n", this)); return mSocketIn->Close(); } NS_IMETHODIMP TLSTransportLayer::InputStreamWrapper::Available( uint64_t* avail) { LOG(("TLSTransportLayer::InputStreamWrapper::Available [this=%p]\n", this)); return mSocketIn->Available(avail); } nsresult TLSTransportLayer::InputStreamWrapper::ReadDirectly( char* buf, uint32_t count, uint32_t* countRead) { LOG(("TLSTransportLayer::InputStreamWrapper::ReadDirectly [this=%p]\n", this)); return mSocketIn->Read(buf, count, countRead); } NS_IMETHODIMP TLSTransportLayer::InputStreamWrapper::Read(char* buf, uint32_t count, uint32_t* countRead) { LOG(("TLSTransportLayer::InputStreamWrapper::Read [this=%p]\n", this)); *countRead = 0; if (NS_FAILED(mStatus)) { return (mStatus == NS_BASE_STREAM_CLOSED) ? NS_OK : mStatus; } int32_t bytesRead = PR_Read(mTransport->mFD, buf, count); if (bytesRead > 0) { *countRead = bytesRead; } else if (bytesRead < 0) { PRErrorCode code = PR_GetError(); if (code == PR_WOULD_BLOCK_ERROR) { LOG(( "TLSTransportLayer::InputStreamWrapper::Read %p PR_Read would block ", this)); return NS_BASE_STREAM_WOULD_BLOCK; } // If reading from the socket succeeded (NS_SUCCEEDED(mStatus)), // but the nss layer encountered an error remember the error. if (NS_SUCCEEDED(mStatus)) { mStatus = ErrorAccordingToNSPR(code); LOG(("TLSTransportLayer::InputStreamWrapper::Read %p nss error %" PRIx32 ".\n", this, static_cast(mStatus))); } } if (NS_SUCCEEDED(mStatus) && !bytesRead) { LOG( ("TLSTransportLayer::InputStreamWrapper::Read %p " "Second layer of TLS stripping results in STREAM_CLOSED\n", this)); mStatus = NS_BASE_STREAM_CLOSED; } LOG(("TLSTransportLayer::InputStreamWrapper::Read %p rv=%" PRIx32 " didread=%d " "2 layers of ssl stripped to plaintext\n", this, static_cast(mStatus), bytesRead)); return mStatus; } NS_IMETHODIMP TLSTransportLayer::InputStreamWrapper::ReadSegments(nsWriteSegmentFun writer, void* closure, uint32_t count, uint32_t* countRead) { LOG(("TLSTransportLayer::InputStreamWrapper::ReadSegments [this=%p]\n", this)); return mSocketIn->ReadSegments(writer, closure, count, countRead); } NS_IMETHODIMP TLSTransportLayer::InputStreamWrapper::IsNonBlocking(bool* nonblocking) { return mSocketIn->IsNonBlocking(nonblocking); } NS_IMETHODIMP TLSTransportLayer::InputStreamWrapper::CloseWithStatus(nsresult reason) { LOG( ("TLSTransportLayer::InputStreamWrapper::CloseWithStatus [this=%p " "reason=%" PRIx32 "]\n", this, static_cast(reason))); return mSocketIn->CloseWithStatus(reason); } NS_IMETHODIMP TLSTransportLayer::InputStreamWrapper::AsyncWait( nsIInputStreamCallback* callback, uint32_t flags, uint32_t amount, nsIEventTarget* target) { LOG( ("TLSTransportLayer::InputStreamWrapper::AsyncWait [this=%p, " "callback=%p]\n", this, callback)); mTransport->mInputCallback = callback; // Don't bother to call PR_POLL when |callback| is NULL. We call |AsyncWait| // directly to null out the underlying callback. if (!callback) { return mSocketIn->AsyncWait(nullptr, 0, 0, nullptr); } PRPollDesc pd; pd.fd = mTransport->mFD; pd.in_flags = PR_POLL_READ | PR_POLL_EXCEPT; // Only run PR_Poll on the socket thread. Also, make sure this lives at least // as long as that operation. auto DoPoll = [self = RefPtr{this}, pd(pd)]() mutable { int32_t rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); LOG(("TLSTransportLayer::InputStreamWrapper::AsyncWait rv=%d", rv)); }; if (OnSocketThread()) { DoPoll(); } else { gSocketTransportService->Dispatch(NS_NewRunnableFunction( "TLSTransportLayer::InputStreamWrapper::AsyncWait", DoPoll)); } return NS_OK; } //----------------------------------------------------------------------------- // TLSTransportLayerOutputStream impl //----------------------------------------------------------------------------- NS_IMPL_QUERY_INTERFACE(TLSTransportLayer::OutputStreamWrapper, nsIOutputStream, nsIAsyncOutputStream) NS_IMETHODIMP_(MozExternalRefCountType) TLSTransportLayer::OutputStreamWrapper::AddRef() { return mTransport->AddRef(); } NS_IMETHODIMP_(MozExternalRefCountType) TLSTransportLayer::OutputStreamWrapper::Release() { return mTransport->Release(); } TLSTransportLayer::OutputStreamWrapper::OutputStreamWrapper( nsIAsyncOutputStream* aOutputStream, TLSTransportLayer* aTransport) : mSocketOut(aOutputStream), mTransport(aTransport) {} NS_IMETHODIMP TLSTransportLayer::OutputStreamWrapper::Close() { LOG(("TLSTransportLayer::OutputStreamWrapper::Close [this=%p]\n", this)); return mSocketOut->Close(); } NS_IMETHODIMP TLSTransportLayer::OutputStreamWrapper::Flush() { LOG(("TLSTransportLayerOutputStream::Flush [this=%p]\n", this)); return mSocketOut->Flush(); } nsresult TLSTransportLayer::OutputStreamWrapper::WriteDirectly( const char* buf, uint32_t count, uint32_t* countWritten) { LOG( ("TLSTransportLayer::OutputStreamWrapper::WriteDirectly [this=%p " "count=%u]\n", this, count)); return mSocketOut->Write(buf, count, countWritten); } NS_IMETHODIMP TLSTransportLayer::OutputStreamWrapper::Write(const char* buf, uint32_t count, uint32_t* countWritten) { LOG(("TLSTransportLayer::OutputStreamWrapper::Write [this=%p count=%u]\n", this, count)); *countWritten = 0; if (NS_FAILED(mStatus)) { return (mStatus == NS_BASE_STREAM_CLOSED) ? NS_OK : mStatus; } int32_t written = PR_Write(mTransport->mFD, buf, count); LOG( ("TLSTransportLayer::OutputStreamWrapper::Write %p PRWrite(%d) = %d " "%d\n", this, count, written, PR_GetError() == PR_WOULD_BLOCK_ERROR)); if (written > 0) { *countWritten = written; } else if (written < 0) { PRErrorCode code = PR_GetError(); if (code == PR_WOULD_BLOCK_ERROR) { LOG( ("TLSTransportLayer::OutputStreamWrapper::Write %p PRWrite would " "block ", this)); return NS_BASE_STREAM_WOULD_BLOCK; } // Writing to the socket succeeded, but failed in nss layer. if (NS_SUCCEEDED(mStatus)) { mStatus = ErrorAccordingToNSPR(code); } } return mStatus; } NS_IMETHODIMP TLSTransportLayer::OutputStreamWrapper::WriteSegments(nsReadSegmentFun reader, void* closure, uint32_t count, uint32_t* countRead) { return mSocketOut->WriteSegments(reader, closure, count, countRead); } // static nsresult TLSTransportLayer::OutputStreamWrapper::WriteFromSegments( nsIInputStream* input, void* closure, const char* fromSegment, uint32_t offset, uint32_t count, uint32_t* countRead) { OutputStreamWrapper* self = (OutputStreamWrapper*)closure; return self->Write(fromSegment, count, countRead); } NS_IMETHODIMP TLSTransportLayer::OutputStreamWrapper::WriteFrom(nsIInputStream* stream, uint32_t count, uint32_t* countRead) { return stream->ReadSegments(WriteFromSegments, this, count, countRead); } NS_IMETHODIMP TLSTransportLayer::OutputStreamWrapper::IsNonBlocking(bool* nonblocking) { return mSocketOut->IsNonBlocking(nonblocking); } NS_IMETHODIMP TLSTransportLayer::OutputStreamWrapper::CloseWithStatus(nsresult reason) { LOG(("OutputStreamWrapper::CloseWithStatus [this=%p reason=%" PRIx32 "]\n", this, static_cast(reason))); return mSocketOut->CloseWithStatus(reason); } NS_IMETHODIMP TLSTransportLayer::OutputStreamWrapper::AsyncWait( nsIOutputStreamCallback* callback, uint32_t flags, uint32_t amount, nsIEventTarget* target) { LOG( ("TLSTransportLayer::OutputStreamWrapper::AsyncWait [this=%p, " "mOutputCallback=%p " "callback=%p]\n", this, mTransport->mOutputCallback.get(), callback)); mTransport->mOutputCallback = callback; // Don't bother to call PR_POLL when |callback| is NULL. We call |AsyncWait| // directly to null out the underlying callback. if (!callback) { return mSocketOut->AsyncWait(nullptr, 0, 0, nullptr); } PRPollDesc pd; pd.fd = mTransport->mFD; pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; int32_t rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); LOG(("TLSTransportLayer::OutputStreamWrapper::AsyncWait rv=%d", rv)); return NS_OK; } //----------------------------------------------------------------------------- // TLSTransportLayer impl //----------------------------------------------------------------------------- static PRDescIdentity sTLSTransportLayerIdentity; static PRIOMethods sTLSTransportLayerMethods; static PRIOMethods* sTLSTransportLayerMethodsPtr = nullptr; NS_IMPL_ADDREF(TLSTransportLayer) NS_IMPL_RELEASE(TLSTransportLayer) NS_INTERFACE_MAP_BEGIN(TLSTransportLayer) NS_INTERFACE_MAP_ENTRY(nsISocketTransport) NS_INTERFACE_MAP_ENTRY(nsITransport) NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback) NS_INTERFACE_MAP_ENTRY(nsIOutputStreamCallback) NS_INTERFACE_MAP_ENTRY_CONCRETE(TLSTransportLayer) NS_INTERFACE_MAP_END TLSTransportLayer::TLSTransportLayer(nsISocketTransport* aTransport, nsIAsyncInputStream* aInputStream, nsIAsyncOutputStream* aOutputStream, nsIInputStreamCallback* aOwner) : mSocketTransport(aTransport), mSocketInWrapper(aInputStream, this), mSocketOutWrapper(aOutputStream, this), mOwner(aOwner) { MOZ_ASSERT(OnSocketThread(), "not on socket thread"); LOG(("TLSTransportLayer ctor this=[%p]", this)); } TLSTransportLayer::~TLSTransportLayer() { MOZ_ASSERT(OnSocketThread(), "not on socket thread"); LOG(("TLSTransportLayer dtor this=[%p]", this)); if (mFD) { PR_Close(mFD); mFD = nullptr; } mTLSSocketControl = nullptr; } bool TLSTransportLayer::Init(const char* aTLSHost, int32_t aTLSPort) { LOG(("TLSTransportLayer::Init this=[%p]", this)); nsCOMPtr provider; nsCOMPtr spserv = nsSocketProviderService::GetOrCreate(); if (!spserv) { return false; } spserv->GetSocketProvider("ssl", getter_AddRefs(provider)); if (!provider) { return false; } // Install an NSPR layer to handle getpeername() with a failure. This is kind // of silly, but the default one used by the pipe asserts when called and the // nss code calls it to see if we are connected to a real socket or not. if (!sTLSTransportLayerMethodsPtr) { // one time initialization sTLSTransportLayerIdentity = PR_GetUniqueIdentity("TLSTransportLayer"); sTLSTransportLayerMethods = *PR_GetDefaultIOMethods(); sTLSTransportLayerMethods.getpeername = GetPeerName; sTLSTransportLayerMethods.getsocketoption = GetSocketOption; sTLSTransportLayerMethods.setsocketoption = SetSocketOption; sTLSTransportLayerMethods.read = Read; sTLSTransportLayerMethods.write = Write; sTLSTransportLayerMethods.send = Send; sTLSTransportLayerMethods.recv = Recv; sTLSTransportLayerMethods.close = Close; sTLSTransportLayerMethods.poll = Poll; sTLSTransportLayerMethodsPtr = &sTLSTransportLayerMethods; } mFD = PR_CreateIOLayerStub(sTLSTransportLayerIdentity, &sTLSTransportLayerMethods); if (!mFD) { return false; } mFD->secret = reinterpret_cast(this); return NS_SUCCEEDED(provider->AddToSocket( PR_AF_INET, aTLSHost, aTLSPort, nullptr, OriginAttributes(), 0, 0, mFD, getter_AddRefs(mTLSSocketControl))); } NS_IMETHODIMP TLSTransportLayer::OnInputStreamReady(nsIAsyncInputStream* in) { nsCOMPtr callback = std::move(mInputCallback); if (callback) { return callback->OnInputStreamReady(&mSocketInWrapper); } return NS_OK; } NS_IMETHODIMP TLSTransportLayer::OnOutputStreamReady(nsIAsyncOutputStream* out) { nsCOMPtr callback = std::move(mOutputCallback); nsresult rv = NS_OK; if (callback) { rv = callback->OnOutputStreamReady(&mSocketOutWrapper); RefPtr tunnel = do_QueryObject(out); if (tunnel) { tunnel->MaybeSetRequestDone(callback); } } return rv; } NS_IMETHODIMP TLSTransportLayer::SetKeepaliveEnabled(bool aKeepaliveEnabled) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetKeepaliveEnabled(aKeepaliveEnabled); } NS_IMETHODIMP TLSTransportLayer::SetKeepaliveVals(int32_t keepaliveIdleTime, int32_t keepaliveRetryInterval) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetKeepaliveVals(keepaliveIdleTime, keepaliveRetryInterval); } NS_IMETHODIMP TLSTransportLayer::GetSecurityCallbacks( nsIInterfaceRequestor** aSecurityCallbacks) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->GetSecurityCallbacks(aSecurityCallbacks); } NS_IMETHODIMP TLSTransportLayer::SetSecurityCallbacks( nsIInterfaceRequestor* aSecurityCallbacks) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetSecurityCallbacks(aSecurityCallbacks); } NS_IMETHODIMP TLSTransportLayer::OpenInputStream(uint32_t aFlags, uint32_t aSegmentSize, uint32_t aSegmentCount, nsIInputStream** _retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP TLSTransportLayer::OpenOutputStream(uint32_t aFlags, uint32_t aSegmentSize, uint32_t aSegmentCount, nsIOutputStream** _retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP TLSTransportLayer::Close(nsresult aReason) { LOG(("TLSTransportLayer::Close [this=%p reason=%" PRIx32 "]\n", this, static_cast(aReason))); mInputCallback = nullptr; mOutputCallback = nullptr; if (mSocketTransport) { mSocketTransport->Close(aReason); mSocketTransport = nullptr; } mSocketInWrapper.AsyncWait(nullptr, 0, 0, nullptr); mSocketOutWrapper.AsyncWait(nullptr, 0, 0, nullptr); if (mOwner) { RefPtr self = this; Unused << NS_DispatchToCurrentThread(NS_NewRunnableFunction( "TLSTransportLayer::Close", [self{std::move(self)}]() { nsCOMPtr inputCallback = std::move(self->mOwner); if (inputCallback) { // This is hack. We need to make // nsHttpConnection::OnInputStreamReady be called, so // nsHttpConnection::CloseTransaction can be called to release the // transaction. Unused << inputCallback->OnInputStreamReady( &self->mSocketInWrapper); } })); } return NS_OK; } NS_IMETHODIMP TLSTransportLayer::SetEventSink(nsITransportEventSink* aSink, nsIEventTarget* aEventTarget) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetEventSink(aSink, aEventTarget); } NS_IMETHODIMP TLSTransportLayer::Bind(NetAddr* aLocalAddr) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->Bind(aLocalAddr); } NS_IMETHODIMP TLSTransportLayer::GetEchConfigUsed(bool* aEchConfigUsed) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->GetEchConfigUsed(aEchConfigUsed); } NS_IMETHODIMP TLSTransportLayer::SetEchConfig(const nsACString& aEchConfig) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetEchConfig(aEchConfig); } NS_IMETHODIMP TLSTransportLayer::ResolvedByTRR(bool* aResolvedByTRR) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->ResolvedByTRR(aResolvedByTRR); } #define FWD_TS_PTR(fx, ts) \ NS_IMETHODIMP \ TLSTransportLayer::fx(ts* arg) { \ if (!mSocketTransport) return NS_ERROR_FAILURE; \ return mSocketTransport->fx(arg); \ } #define FWD_TS_ADDREF(fx, ts) \ NS_IMETHODIMP \ TLSTransportLayer::fx(ts** arg) { \ if (!mSocketTransport) return NS_ERROR_FAILURE; \ return mSocketTransport->fx(arg); \ } #define FWD_TS(fx, ts) \ NS_IMETHODIMP \ TLSTransportLayer::fx(ts arg) { \ if (!mSocketTransport) return NS_ERROR_FAILURE; \ return mSocketTransport->fx(arg); \ } FWD_TS_PTR(GetKeepaliveEnabled, bool); FWD_TS_PTR(GetSendBufferSize, uint32_t); FWD_TS(SetSendBufferSize, uint32_t); FWD_TS_PTR(GetPort, int32_t); FWD_TS_PTR(GetPeerAddr, mozilla::net::NetAddr); FWD_TS_PTR(GetSelfAddr, mozilla::net::NetAddr); FWD_TS_ADDREF(GetScriptablePeerAddr, nsINetAddr); FWD_TS_ADDREF(GetScriptableSelfAddr, nsINetAddr); FWD_TS_PTR(IsAlive, bool); FWD_TS_PTR(GetConnectionFlags, uint32_t); FWD_TS(SetConnectionFlags, uint32_t); FWD_TS(SetIsPrivate, bool); FWD_TS_PTR(GetTlsFlags, uint32_t); FWD_TS(SetTlsFlags, uint32_t); FWD_TS_PTR(GetRecvBufferSize, uint32_t); FWD_TS(SetRecvBufferSize, uint32_t); FWD_TS_PTR(GetResetIPFamilyPreference, bool); nsresult TLSTransportLayer::GetTlsSocketControl( nsITLSSocketControl** tlsSocketControl) { if (!mTLSSocketControl) { return NS_ERROR_ABORT; } *tlsSocketControl = do_AddRef(mTLSSocketControl).take(); return NS_OK; } nsresult TLSTransportLayer::GetOriginAttributes( mozilla::OriginAttributes* aOriginAttributes) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->GetOriginAttributes(aOriginAttributes); } nsresult TLSTransportLayer::SetOriginAttributes( const mozilla::OriginAttributes& aOriginAttributes) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetOriginAttributes(aOriginAttributes); } NS_IMETHODIMP TLSTransportLayer::GetScriptableOriginAttributes( JSContext* aCx, JS::MutableHandle aOriginAttributes) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->GetScriptableOriginAttributes(aCx, aOriginAttributes); } NS_IMETHODIMP TLSTransportLayer::SetScriptableOriginAttributes( JSContext* aCx, JS::Handle aOriginAttributes) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetScriptableOriginAttributes(aCx, aOriginAttributes); } NS_IMETHODIMP TLSTransportLayer::GetHost(nsACString& aHost) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->GetHost(aHost); } NS_IMETHODIMP TLSTransportLayer::GetTimeout(uint32_t aType, uint32_t* _retval) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->GetTimeout(aType, _retval); } NS_IMETHODIMP TLSTransportLayer::SetTimeout(uint32_t aType, uint32_t aValue) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetTimeout(aType, aValue); } NS_IMETHODIMP TLSTransportLayer::SetReuseAddrPort(bool aReuseAddrPort) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetReuseAddrPort(aReuseAddrPort); } NS_IMETHODIMP TLSTransportLayer::SetLinger(bool aPolarity, int16_t aTimeout) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetLinger(aPolarity, aTimeout); } NS_IMETHODIMP TLSTransportLayer::GetQoSBits(uint8_t* aQoSBits) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->GetQoSBits(aQoSBits); } NS_IMETHODIMP TLSTransportLayer::SetQoSBits(uint8_t aQoSBits) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->SetQoSBits(aQoSBits); } NS_IMETHODIMP TLSTransportLayer::GetRetryDnsIfPossible(bool* aRetry) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->GetRetryDnsIfPossible(aRetry); } NS_IMETHODIMP TLSTransportLayer::GetStatus(nsresult* aStatus) { if (!mSocketTransport) { return NS_ERROR_FAILURE; } return mSocketTransport->GetStatus(aStatus); } int32_t TLSTransportLayer::OutputInternal(const char* aBuf, int32_t aAmount) { LOG(("TLSTransportLayer::OutputInternal %p %d", this, aAmount)); uint32_t outCountWrite = 0; nsresult rv = mSocketOutWrapper.WriteDirectly(aBuf, aAmount, &outCountWrite); if (NS_FAILED(rv)) { if (rv == NS_BASE_STREAM_WOULD_BLOCK) { PR_SetError(PR_WOULD_BLOCK_ERROR, 0); } else { PR_SetError(PR_UNKNOWN_ERROR, 0); } return -1; } return outCountWrite; } int32_t TLSTransportLayer::InputInternal(char* aBuf, int32_t aAmount) { LOG(("TLSTransportLayer::InputInternal aAmount=%d\n", aAmount)); uint32_t outCountRead = 0; nsresult rv = mSocketInWrapper.ReadDirectly(aBuf, aAmount, &outCountRead); if (NS_FAILED(rv)) { if (rv == NS_BASE_STREAM_WOULD_BLOCK) { PR_SetError(PR_WOULD_BLOCK_ERROR, 0); } else { PR_SetError(PR_UNKNOWN_ERROR, 0); } return -1; } return outCountRead; } PRStatus TLSTransportLayer::GetPeerName(PRFileDesc* aFD, PRNetAddr* addr) { TLSTransportLayer* self = reinterpret_cast(aFD->secret); NetAddr peeraddr; if (NS_FAILED(self->Transport()->GetPeerAddr(&peeraddr))) { return PR_FAILURE; } NetAddrToPRNetAddr(&peeraddr, addr); return PR_SUCCESS; } PRStatus TLSTransportLayer::GetSocketOption(PRFileDesc* aFD, PRSocketOptionData* aOpt) { if (aOpt->option == PR_SockOpt_Nonblocking) { aOpt->value.non_blocking = PR_TRUE; return PR_SUCCESS; } return PR_FAILURE; } PRStatus TLSTransportLayer::SetSocketOption(PRFileDesc* aFD, const PRSocketOptionData* aOpt) { return PR_FAILURE; } PRStatus TLSTransportLayer::Close(PRFileDesc* aFD) { return PR_SUCCESS; } int32_t TLSTransportLayer::Write(PRFileDesc* aFD, const void* aBuf, int32_t aAmount) { TLSTransportLayer* self = reinterpret_cast(aFD->secret); return self->OutputInternal(static_cast(aBuf), aAmount); } int32_t TLSTransportLayer::Send(PRFileDesc* aFD, const void* aBuf, int32_t aAmount, int, PRIntervalTime) { return Write(aFD, aBuf, aAmount); } int32_t TLSTransportLayer::Read(PRFileDesc* aFD, void* aBuf, int32_t aAmount) { TLSTransportLayer* self = reinterpret_cast(aFD->secret); return self->InputInternal(static_cast(aBuf), aAmount); } int32_t TLSTransportLayer::Recv(PRFileDesc* aFD, void* aBuf, int32_t aAmount, int, PRIntervalTime) { return Read(aFD, aBuf, aAmount); } int16_t TLSTransportLayer::Poll(PRFileDesc* fd, int16_t in_flags, int16_t* out_flags) { LOG(("TLSTransportLayer::Poll fd=%p inf_flags=%d\n", fd, (int)in_flags)); *out_flags = in_flags; TLSTransportLayer* self = reinterpret_cast(fd->secret); if (!self) { return 0; } if (in_flags & PR_POLL_READ) { self->mSocketInWrapper.mSocketIn->AsyncWait(self, 0, 0, nullptr); } else if (in_flags & PR_POLL_WRITE) { self->mSocketOutWrapper.mSocketOut->AsyncWait(self, 0, 0, nullptr); } return in_flags; } bool TLSTransportLayer::HasDataToRecv() { MOZ_ASSERT(OnSocketThread(), "not on socket thread"); if (!mFD) { return false; } int32_t n = 0; char c; n = PR_Recv(mFD, &c, 1, PR_MSG_PEEK, 0); return n > 0; } } // namespace mozilla::net