1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=4 sw=2 et cindent: */
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/. */
9 #include "nsSocketTransport2.h"
11 #include "IOActivityMonitor.h"
12 #include "NSSErrorsService.h"
13 #include "NetworkDataCountLayer.h"
14 #include "QuicSocketControl.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/StaticPrefs_network.h"
17 #include "mozilla/SyncRunnable.h"
18 #include "mozilla/Telemetry.h"
19 #include "mozilla/dom/ToJSValue.h"
20 #include "mozilla/net/NeckoChild.h"
21 #include "mozilla/net/SSLTokensCache.h"
22 #include "mozilla/ProfilerBandwidthCounter.h"
24 #include "nsICancelable.h"
25 #include "nsIClassInfoImpl.h"
26 #include "nsIDNSByTypeRecord.h"
27 #include "nsIDNSRecord.h"
28 #include "nsIDNSService.h"
29 #include "nsIOService.h"
31 #include "nsISocketProvider.h"
32 #include "nsITLSSocketControl.h"
33 #include "nsNetAddr.h"
35 #include "nsNetSegmentUtils.h"
36 #include "nsNetUtil.h"
37 #include "nsPrintfCString.h"
38 #include "nsProxyInfo.h"
39 #include "nsSocketProviderService.h"
40 #include "nsStreamUtils.h"
41 #include "nsThreadUtils.h"
42 #include "nsTransportUtils.h"
43 #include "nsURLHelper.h"
46 #include "xpcpublic.h"
49 # include "FuzzyLayer.h"
50 # include "FuzzySocketControl.h"
51 # include "mozilla/StaticPrefs_fuzzing.h"
55 # include "ShutdownLayer.h"
58 /* Following inclusions required for keepalive config not supported by NSPR. */
59 #include "private/pprio.h"
61 # include <winsock2.h>
63 #elif defined(XP_UNIX)
65 # include <netinet/tcp.h>
67 /* End keepalive config inclusions. */
69 #define SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 0
70 #define UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 1
71 #define SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 2
72 #define UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 3
74 //-----------------------------------------------------------------------------
76 static NS_DEFINE_CID(kDNSServiceCID
, NS_DNSSERVICE_CID
);
78 //-----------------------------------------------------------------------------
83 class nsSocketEvent
: public Runnable
{
85 nsSocketEvent(nsSocketTransport
* transport
, uint32_t type
,
86 nsresult status
= NS_OK
, nsISupports
* param
= nullptr,
87 std::function
<void()>&& task
= nullptr)
88 : Runnable("net::nsSocketEvent"),
89 mTransport(transport
),
93 mTask(std::move(task
)) {}
95 NS_IMETHOD
Run() override
{
96 mTransport
->OnSocketEvent(mType
, mStatus
, mParam
, std::move(mTask
));
101 RefPtr
<nsSocketTransport
> mTransport
;
105 nsCOMPtr
<nsISupports
> mParam
;
106 std::function
<void()> mTask
;
109 //-----------------------------------------------------------------------------
111 // #define TEST_CONNECT_ERRORS
112 #ifdef TEST_CONNECT_ERRORS
114 static PRErrorCode
RandomizeConnectError(PRErrorCode code
) {
116 // To test out these errors, load http://www.yahoo.com/. It should load
117 // correctly despite the random occurrence of these errors.
120 if (n
> RAND_MAX
/ 2) {
122 PRErrorCode err_code
;
123 const char* err_name
;
126 // These errors should be recoverable provided there is another
127 // IP address in mDNSRecord.
129 {PR_CONNECT_REFUSED_ERROR
, "PR_CONNECT_REFUSED_ERROR"},
130 {PR_CONNECT_TIMEOUT_ERROR
, "PR_CONNECT_TIMEOUT_ERROR"},
132 // This error will cause this socket transport to error out;
133 // however, if the consumer is HTTP, then the HTTP transaction
134 // should be restarted when this error occurs.
136 {PR_CONNECT_RESET_ERROR
, "PR_CONNECT_RESET_ERROR"},
138 n
= n
% (sizeof(errors
) / sizeof(errors
[0]));
139 code
= errors
[n
].err_code
;
140 SOCKET_LOG(("simulating NSPR error %d [%s]\n", code
, errors
[n
].err_name
));
146 //-----------------------------------------------------------------------------
148 nsresult
ErrorAccordingToNSPR(PRErrorCode errorCode
) {
149 nsresult rv
= NS_ERROR_FAILURE
;
151 case PR_WOULD_BLOCK_ERROR
:
152 rv
= NS_BASE_STREAM_WOULD_BLOCK
;
154 case PR_CONNECT_ABORTED_ERROR
:
155 case PR_CONNECT_RESET_ERROR
:
156 rv
= NS_ERROR_NET_RESET
;
158 case PR_END_OF_FILE_ERROR
: // XXX document this correlation
159 rv
= NS_ERROR_NET_INTERRUPT
;
161 case PR_CONNECT_REFUSED_ERROR
:
162 // We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
163 // could get better diagnostics by adding distinct XPCOM error codes for
164 // each of these, but there are a lot of places in Gecko that check
165 // specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
167 case PR_NETWORK_UNREACHABLE_ERROR
:
168 case PR_HOST_UNREACHABLE_ERROR
:
169 case PR_ADDRESS_NOT_AVAILABLE_ERROR
:
170 // Treat EACCES as a soft error since (at least on Linux) connect() returns
171 // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
172 case PR_NO_ACCESS_RIGHTS_ERROR
:
173 rv
= NS_ERROR_CONNECTION_REFUSED
;
175 case PR_ADDRESS_NOT_SUPPORTED_ERROR
:
176 rv
= NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
178 case PR_IO_TIMEOUT_ERROR
:
179 case PR_CONNECT_TIMEOUT_ERROR
:
180 rv
= NS_ERROR_NET_TIMEOUT
;
182 case PR_OUT_OF_MEMORY_ERROR
:
183 // These really indicate that the descriptor table filled up, or that the
184 // kernel ran out of network buffers - but nobody really cares which part of
185 // the system ran out of memory.
186 case PR_PROC_DESC_TABLE_FULL_ERROR
:
187 case PR_SYS_DESC_TABLE_FULL_ERROR
:
188 case PR_INSUFFICIENT_RESOURCES_ERROR
:
189 rv
= NS_ERROR_OUT_OF_MEMORY
;
191 case PR_ADDRESS_IN_USE_ERROR
:
192 rv
= NS_ERROR_SOCKET_ADDRESS_IN_USE
;
194 // These filename-related errors can arise when using Unix-domain sockets.
195 case PR_FILE_NOT_FOUND_ERROR
:
196 rv
= NS_ERROR_FILE_NOT_FOUND
;
198 case PR_IS_DIRECTORY_ERROR
:
199 rv
= NS_ERROR_FILE_IS_DIRECTORY
;
202 rv
= NS_ERROR_FILE_UNRESOLVABLE_SYMLINK
;
204 case PR_NAME_TOO_LONG_ERROR
:
205 rv
= NS_ERROR_FILE_NAME_TOO_LONG
;
207 case PR_NO_DEVICE_SPACE_ERROR
:
208 rv
= NS_ERROR_FILE_NO_DEVICE_SPACE
;
210 case PR_NOT_DIRECTORY_ERROR
:
211 rv
= NS_ERROR_FILE_NOT_DIRECTORY
;
213 case PR_READ_ONLY_FILESYSTEM_ERROR
:
214 rv
= NS_ERROR_FILE_READ_ONLY
;
216 case PR_BAD_ADDRESS_ERROR
:
217 rv
= NS_ERROR_UNKNOWN_HOST
;
220 if (psm::IsNSSErrorCode(errorCode
)) {
221 rv
= psm::GetXPCOMFromNSSError(errorCode
);
225 // NSPR's socket code can return these, but they're not worth breaking out
226 // into their own error codes, distinct from NS_ERROR_FAILURE:
228 // PR_BAD_DESCRIPTOR_ERROR
229 // PR_INVALID_ARGUMENT_ERROR
230 // PR_NOT_SOCKET_ERROR
231 // PR_NOT_TCP_SOCKET_ERROR
232 // These would indicate a bug internal to the component.
234 // PR_PROTOCOL_NOT_SUPPORTED_ERROR
235 // This means that we can't use the given "protocol" (like
236 // IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
237 // above, this indicates an internal bug.
239 // PR_IS_CONNECTED_ERROR
240 // This indicates that we've applied a system call like 'bind' or
241 // 'connect' to a socket that is already connected. The socket
242 // components manage each file descriptor's state, and in some cases
243 // handle this error result internally. We shouldn't be returning
244 // this to our callers.
247 // This is so vague that NS_ERROR_FAILURE is just as good.
249 SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%" PRIx32
"]\n", errorCode
,
250 static_cast<uint32_t>(rv
)));
254 //-----------------------------------------------------------------------------
255 // socket input stream impl
256 //-----------------------------------------------------------------------------
258 nsSocketInputStream::nsSocketInputStream(nsSocketTransport
* trans
)
259 : mTransport(trans
) {}
261 // called on the socket transport thread...
263 // condition : failure code if socket has been closed
265 void nsSocketInputStream::OnSocketReady(nsresult condition
) {
266 SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%" PRIx32
"]\n",
267 this, static_cast<uint32_t>(condition
)));
269 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
271 nsCOMPtr
<nsIInputStreamCallback
> callback
;
273 MutexAutoLock
lock(mTransport
->mLock
);
275 // update condition, but be careful not to erase an already
276 // existing error condition.
277 if (NS_SUCCEEDED(mCondition
)) mCondition
= condition
;
279 // ignore event if only waiting for closure and not closed.
280 if (NS_FAILED(mCondition
) || !(mCallbackFlags
& WAIT_CLOSURE_ONLY
)) {
281 callback
= std::move(mCallback
);
286 if (callback
) callback
->OnInputStreamReady(this);
289 NS_IMPL_QUERY_INTERFACE(nsSocketInputStream
, nsIInputStream
,
292 NS_IMETHODIMP_(MozExternalRefCountType
)
293 nsSocketInputStream::AddRef() {
295 return mTransport
->AddRef();
298 NS_IMETHODIMP_(MozExternalRefCountType
)
299 nsSocketInputStream::Release() {
300 if (--mReaderRefCnt
== 0) Close();
301 return mTransport
->Release();
305 nsSocketInputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED
); }
308 nsSocketInputStream::Available(uint64_t* avail
) {
309 SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
315 MutexAutoLock
lock(mTransport
->mLock
);
317 if (NS_FAILED(mCondition
)) return mCondition
;
319 fd
= mTransport
->GetFD_Locked();
320 if (!fd
) return NS_OK
;
323 // cannot hold lock while calling NSPR. (worried about the fact that PSM
324 // synchronously proxies notifications over to the UI thread, which could
325 // mistakenly try to re-enter this code.)
326 int32_t n
= PR_Available(fd
);
328 // PSM does not implement PR_Available() so do a best approximation of it
330 if ((n
== -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR
)) {
333 n
= PR_Recv(fd
, &c
, 1, PR_MSG_PEEK
, 0);
335 ("nsSocketInputStream::Available [this=%p] "
336 "using PEEK backup n=%d]\n",
342 MutexAutoLock
lock(mTransport
->mLock
);
344 mTransport
->ReleaseFD_Locked(fd
);
349 PRErrorCode code
= PR_GetError();
350 if (code
== PR_WOULD_BLOCK_ERROR
) return NS_OK
;
351 mCondition
= ErrorAccordingToNSPR(code
);
355 if (NS_FAILED(rv
)) mTransport
->OnInputClosed(rv
);
360 nsSocketInputStream::StreamStatus() {
361 SOCKET_LOG(("nsSocketInputStream::StreamStatus [this=%p]\n", this));
363 MutexAutoLock
lock(mTransport
->mLock
);
368 nsSocketInputStream::Read(char* buf
, uint32_t count
, uint32_t* countRead
) {
369 SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count
));
373 PRFileDesc
* fd
= nullptr;
375 MutexAutoLock
lock(mTransport
->mLock
);
377 if (NS_FAILED(mCondition
)) {
378 return (mCondition
== NS_BASE_STREAM_CLOSED
) ? NS_OK
: mCondition
;
381 fd
= mTransport
->GetFD_Locked();
382 if (!fd
) return NS_BASE_STREAM_WOULD_BLOCK
;
385 SOCKET_LOG((" calling PR_Read [count=%u]\n", count
));
387 // cannot hold lock while calling NSPR. (worried about the fact that PSM
388 // synchronously proxies notifications over to the UI thread, which could
389 // mistakenly try to re-enter this code.)
390 int32_t n
= PR_Read(fd
, buf
, count
);
392 SOCKET_LOG((" PR_Read returned [n=%d]\n", n
));
396 MutexAutoLock
lock(mTransport
->mLock
);
398 #ifdef ENABLE_SOCKET_TRACING
399 if (n
> 0) mTransport
->TraceInBuf(buf
, n
);
402 mTransport
->ReleaseFD_Locked(fd
);
405 mByteCount
+= (*countRead
= n
);
406 profiler_count_bandwidth_read_bytes(n
);
408 PRErrorCode code
= PR_GetError();
409 if (code
== PR_WOULD_BLOCK_ERROR
) return NS_BASE_STREAM_WOULD_BLOCK
;
410 mCondition
= ErrorAccordingToNSPR(code
);
414 if (NS_FAILED(rv
)) mTransport
->OnInputClosed(rv
);
416 // only send this notification if we have indeed read some data.
417 // see bug 196827 for an example of why this is important.
418 if (n
> 0) mTransport
->SendStatus(NS_NET_STATUS_RECEIVING_FROM
);
423 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer
, void* closure
,
424 uint32_t count
, uint32_t* countRead
) {
425 // socket stream is unbuffered
426 return NS_ERROR_NOT_IMPLEMENTED
;
430 nsSocketInputStream::IsNonBlocking(bool* nonblocking
) {
436 nsSocketInputStream::CloseWithStatus(nsresult reason
) {
437 SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%" PRIx32
439 this, static_cast<uint32_t>(reason
)));
441 // may be called from any thread
445 MutexAutoLock
lock(mTransport
->mLock
);
447 if (NS_SUCCEEDED(mCondition
)) {
448 rv
= mCondition
= reason
;
453 if (NS_FAILED(rv
)) mTransport
->OnInputClosed(rv
);
458 nsSocketInputStream::AsyncWait(nsIInputStreamCallback
* callback
, uint32_t flags
,
459 uint32_t amount
, nsIEventTarget
* target
) {
460 SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
462 bool hasError
= false;
464 MutexAutoLock
lock(mTransport
->mLock
);
466 if (callback
&& target
) {
470 mCallback
= NS_NewInputStreamReadyEvent("nsSocketInputStream::AsyncWait",
473 mCallback
= callback
;
475 mCallbackFlags
= flags
;
477 hasError
= NS_FAILED(mCondition
);
478 } // unlock mTransport->mLock
481 // OnSocketEvent will call OnInputStreamReady with an error code after
482 // going through the event loop. We do this because most socket callers
483 // do not expect AsyncWait() to synchronously execute the OnInputStreamReady
485 mTransport
->PostEvent(nsSocketTransport::MSG_INPUT_PENDING
);
487 mTransport
->OnInputPending();
493 //-----------------------------------------------------------------------------
494 // socket output stream impl
495 //-----------------------------------------------------------------------------
497 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport
* trans
)
498 : mTransport(trans
) {}
500 // called on the socket transport thread...
502 // condition : failure code if socket has been closed
504 void nsSocketOutputStream::OnSocketReady(nsresult condition
) {
505 SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%" PRIx32
507 this, static_cast<uint32_t>(condition
)));
509 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
511 nsCOMPtr
<nsIOutputStreamCallback
> callback
;
513 MutexAutoLock
lock(mTransport
->mLock
);
515 // update condition, but be careful not to erase an already
516 // existing error condition.
517 if (NS_SUCCEEDED(mCondition
)) mCondition
= condition
;
519 // ignore event if only waiting for closure and not closed.
520 if (NS_FAILED(mCondition
) || !(mCallbackFlags
& WAIT_CLOSURE_ONLY
)) {
521 callback
= std::move(mCallback
);
526 if (callback
) callback
->OnOutputStreamReady(this);
529 NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream
, nsIOutputStream
,
530 nsIAsyncOutputStream
)
532 NS_IMETHODIMP_(MozExternalRefCountType
)
533 nsSocketOutputStream::AddRef() {
535 return mTransport
->AddRef();
538 NS_IMETHODIMP_(MozExternalRefCountType
)
539 nsSocketOutputStream::Release() {
540 if (--mWriterRefCnt
== 0) Close();
541 return mTransport
->Release();
545 nsSocketOutputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED
); }
548 nsSocketOutputStream::Flush() { return NS_OK
; }
551 nsSocketOutputStream::StreamStatus() {
552 MutexAutoLock
lock(mTransport
->mLock
);
557 nsSocketOutputStream::Write(const char* buf
, uint32_t count
,
558 uint32_t* countWritten
) {
559 SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n", this, count
));
563 // A write of 0 bytes can be used to force the initial SSL handshake, so do
566 PRFileDesc
* fd
= nullptr;
568 MutexAutoLock
lock(mTransport
->mLock
);
570 if (NS_FAILED(mCondition
)) return mCondition
;
572 fd
= mTransport
->GetFD_Locked();
573 if (!fd
) return NS_BASE_STREAM_WOULD_BLOCK
;
576 SOCKET_LOG((" calling PR_Write [count=%u]\n", count
));
578 // cannot hold lock while calling NSPR. (worried about the fact that PSM
579 // synchronously proxies notifications over to the UI thread, which could
580 // mistakenly try to re-enter this code.)
581 int32_t n
= PR_Write(fd
, buf
, count
);
583 SOCKET_LOG((" PR_Write returned [n=%d]\n", n
));
587 MutexAutoLock
lock(mTransport
->mLock
);
589 #ifdef ENABLE_SOCKET_TRACING
590 if (n
> 0) mTransport
->TraceOutBuf(buf
, n
);
593 mTransport
->ReleaseFD_Locked(fd
);
596 mByteCount
+= (*countWritten
= n
);
597 profiler_count_bandwidth_written_bytes(n
);
599 PRErrorCode code
= PR_GetError();
600 if (code
== PR_WOULD_BLOCK_ERROR
) return NS_BASE_STREAM_WOULD_BLOCK
;
601 mCondition
= ErrorAccordingToNSPR(code
);
605 if (NS_FAILED(rv
)) mTransport
->OnOutputClosed(rv
);
607 // only send this notification if we have indeed written some data.
608 // see bug 196827 for an example of why this is important.
610 mTransport
->SendStatus(NS_NET_STATUS_SENDING_TO
);
617 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader
, void* closure
,
618 uint32_t count
, uint32_t* countRead
) {
619 // socket stream is unbuffered
620 return NS_ERROR_NOT_IMPLEMENTED
;
623 nsresult
nsSocketOutputStream::WriteFromSegments(
624 nsIInputStream
* input
, void* closure
, const char* fromSegment
,
625 uint32_t offset
, uint32_t count
, uint32_t* countRead
) {
626 nsSocketOutputStream
* self
= (nsSocketOutputStream
*)closure
;
627 return self
->Write(fromSegment
, count
, countRead
);
631 nsSocketOutputStream::WriteFrom(nsIInputStream
* stream
, uint32_t count
,
632 uint32_t* countRead
) {
633 return stream
->ReadSegments(WriteFromSegments
, this, count
, countRead
);
637 nsSocketOutputStream::IsNonBlocking(bool* nonblocking
) {
643 nsSocketOutputStream::CloseWithStatus(nsresult reason
) {
644 SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%" PRIx32
646 this, static_cast<uint32_t>(reason
)));
648 // may be called from any thread
652 MutexAutoLock
lock(mTransport
->mLock
);
654 if (NS_SUCCEEDED(mCondition
)) {
655 rv
= mCondition
= reason
;
660 if (NS_FAILED(rv
)) mTransport
->OnOutputClosed(rv
);
665 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback
* callback
,
666 uint32_t flags
, uint32_t amount
,
667 nsIEventTarget
* target
) {
668 SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n", this));
671 MutexAutoLock
lock(mTransport
->mLock
);
673 if (callback
&& target
) {
677 mCallback
= NS_NewOutputStreamReadyEvent(callback
, target
);
679 mCallback
= callback
;
682 mCallbackFlags
= flags
;
684 mTransport
->OnOutputPending();
688 //-----------------------------------------------------------------------------
689 // socket transport impl
690 //-----------------------------------------------------------------------------
692 nsSocketTransport::nsSocketTransport()
694 mSocketTransportService(gSocketTransportService
),
695 mInput(new nsSocketInputStream(this)),
696 mOutput(new nsSocketOutputStream(this)) {
697 SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
699 mTimeouts
[TIMEOUT_CONNECT
] = UINT16_MAX
; // no timeout
700 mTimeouts
[TIMEOUT_READ_WRITE
] = UINT16_MAX
; // no timeout
703 nsSocketTransport::~nsSocketTransport() {
704 MOZ_RELEASE_ASSERT(!mAttached
);
705 SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
708 nsresult
nsSocketTransport::Init(const nsTArray
<nsCString
>& types
,
709 const nsACString
& host
, uint16_t port
,
710 const nsACString
& hostRoute
,
712 nsIProxyInfo
* givenProxyInfo
,
713 nsIDNSRecord
* dnsRecord
) {
714 nsCOMPtr
<nsProxyInfo
> proxyInfo
;
715 if (givenProxyInfo
) {
716 proxyInfo
= do_QueryInterface(givenProxyInfo
);
717 NS_ENSURE_ARG(proxyInfo
);
721 mExternalDNSResolution
= true;
722 mDNSRecord
= do_QueryInterface(dnsRecord
);
723 mDNSRecord
->IsTRR(&mResolvedByTRR
);
724 mDNSRecord
->GetEffectiveTRRMode(&mEffectiveTRRMode
);
725 mDNSRecord
->GetTrrSkipReason(&mTRRSkipReason
);
728 // init socket type info
732 if (!hostRoute
.IsEmpty()) {
740 // A subtle check we don't enter this method more than once for the socket
741 // transport lifetime. Disable on TSan builds to prevent race checking, we
742 // don't want an atomic here for perf reasons!
744 MOZ_ASSERT(!mPortRemappingApplied
);
748 mHttpsProxy
= proxyInfo
->IsHTTPS();
751 const char* proxyType
= nullptr;
752 mProxyInfo
= proxyInfo
;
754 mProxyPort
= proxyInfo
->Port();
755 mProxyHost
= proxyInfo
->Host();
756 // grab proxy type (looking for "socks" for example)
757 proxyType
= proxyInfo
->Type();
758 if (proxyType
&& (proxyInfo
->IsHTTP() || proxyInfo
->IsHTTPS() ||
759 proxyInfo
->IsDirect() || !strcmp(proxyType
, "unknown"))) {
765 ("nsSocketTransport::Init [this=%p host=%s:%hu origin=%s:%d "
767 this, mHost
.get(), mPort
, mOriginHost
.get(), mOriginPort
,
768 mProxyHost
.get(), mProxyPort
));
770 // include proxy type as a socket type if proxy type is not "http"
771 uint32_t typeCount
= types
.Length() + (proxyType
!= nullptr);
772 if (!typeCount
) return NS_OK
;
774 // if we have socket types, then the socket provider service had
777 nsCOMPtr
<nsISocketProviderService
> spserv
=
778 nsSocketProviderService::GetOrCreate();
780 if (!mTypes
.SetCapacity(typeCount
, fallible
)) {
781 return NS_ERROR_OUT_OF_MEMORY
;
784 // now verify that each socket type has a registered socket provider.
785 for (uint32_t i
= 0, type
= 0; i
< typeCount
; ++i
) {
786 // store socket types
787 if (i
== 0 && proxyType
) {
788 mTypes
.AppendElement(proxyType
);
790 mTypes
.AppendElement(types
[type
++]);
793 nsCOMPtr
<nsISocketProvider
> provider
;
794 rv
= spserv
->GetSocketProvider(mTypes
[i
].get(), getter_AddRefs(provider
));
796 NS_WARNING("no registered socket provider");
800 // note if socket type corresponds to a transparent proxy
801 // XXX don't hardcode SOCKS here (use proxy info's flags instead).
802 if (mTypes
[i
].EqualsLiteral("socks") || mTypes
[i
].EqualsLiteral("socks4")) {
803 mProxyTransparent
= true;
805 if (proxyInfo
->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
) {
806 // we want the SOCKS layer to send the hostname
807 // and port to the proxy and let it do the DNS.
808 mProxyTransparentResolvesHost
= true;
817 nsresult
nsSocketTransport::InitWithFilename(const char* filename
) {
818 return InitWithName(filename
, strlen(filename
));
821 nsresult
nsSocketTransport::InitWithName(const char* name
, size_t length
) {
822 if (length
> sizeof(mNetAddr
.local
.path
) - 1) {
823 return NS_ERROR_FILE_NAME_TOO_LONG
;
826 if (!name
[0] && length
> 1) {
827 // name is abstract address name that is supported on Linux only
828 # if defined(XP_LINUX)
829 mHost
.Assign(name
+ 1, length
- 1);
831 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
834 // The name isn't abstract socket address. So this is Unix domain
835 // socket that has file path.
836 mHost
.Assign(name
, length
);
840 mNetAddr
.local
.family
= AF_LOCAL
;
841 memcpy(mNetAddr
.local
.path
, name
, length
);
842 mNetAddr
.local
.path
[length
] = '\0';
843 mNetAddrIsSet
= true;
849 nsresult
nsSocketTransport::InitWithConnectedSocket(PRFileDesc
* fd
,
850 const NetAddr
* addr
) {
851 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
853 char buf
[kNetAddrMaxCStrBufSize
];
854 addr
->ToStringBuffer(buf
, sizeof(buf
));
858 if (addr
->raw
.family
== AF_INET
) {
859 port
= addr
->inet
.port
;
860 } else if (addr
->raw
.family
== AF_INET6
) {
861 port
= addr
->inet6
.port
;
867 memcpy(&mNetAddr
, addr
, sizeof(NetAddr
));
869 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
870 mState
= STATE_TRANSFERRING
;
872 mNetAddrIsSet
= true;
875 MutexAutoLock
lock(mLock
);
876 NS_ASSERTION(!mFD
.IsInitialized(), "already initialized");
880 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
883 // make sure new socket is non-blocking
884 PRSocketOptionData opt
;
885 opt
.option
= PR_SockOpt_Nonblocking
;
886 opt
.value
.non_blocking
= true;
887 PR_SetSocketOption(fd
, &opt
);
890 ("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
891 this, mHost
.get(), mPort
));
893 // jump to InitiateSocket to get ourselves attached to the STS poll list.
894 return PostEvent(MSG_RETRY_INIT_SOCKET
);
897 nsresult
nsSocketTransport::InitWithConnectedSocket(
898 PRFileDesc
* aFD
, const NetAddr
* aAddr
, nsIInterfaceRequestor
* aCallbacks
) {
900 MutexAutoLock
lock(mLock
);
901 mCallbacks
= aCallbacks
;
903 return InitWithConnectedSocket(aFD
, aAddr
);
906 nsresult
nsSocketTransport::PostEvent(uint32_t type
, nsresult status
,
908 std::function
<void()>&& task
) {
909 SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%" PRIx32
911 this, type
, static_cast<uint32_t>(status
), param
));
913 nsCOMPtr
<nsIRunnable
> event
=
914 new nsSocketEvent(this, type
, status
, param
, std::move(task
));
915 if (!event
) return NS_ERROR_OUT_OF_MEMORY
;
917 return mSocketTransportService
->Dispatch(event
, NS_DISPATCH_NORMAL
);
920 void nsSocketTransport::SendStatus(nsresult status
) {
921 SOCKET_LOG1(("nsSocketTransport::SendStatus [this=%p status=%" PRIx32
"]\n",
922 this, static_cast<uint32_t>(status
)));
924 nsCOMPtr
<nsITransportEventSink
> sink
;
927 MutexAutoLock
lock(mLock
);
930 case NS_NET_STATUS_SENDING_TO
:
931 progress
= mOutput
->ByteCount(lock
);
933 case NS_NET_STATUS_RECEIVING_FROM
:
934 progress
= mInput
->ByteCount(lock
);
942 sink
->OnTransportStatus(this, status
, progress
, -1);
946 nsresult
nsSocketTransport::ResolveHost() {
948 "nsSocketTransport::ResolveHost [this=%p %s:%d%s] "
949 "mProxyTransparentResolvesHost=%d\n",
950 this, SocketHost().get(), SocketPort(),
951 mConnectionFlags
& nsSocketTransport::BYPASS_CACHE
? " bypass cache" : "",
952 mProxyTransparentResolvesHost
));
953 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
957 if (!mProxyHost
.IsEmpty()) {
958 if (!mProxyTransparent
|| mProxyTransparentResolvesHost
) {
960 MOZ_ASSERT(!mNetAddrIsSet
|| mNetAddr
.raw
.family
!= AF_LOCAL
,
961 "Unix domain sockets can't be used with proxies");
963 // When not resolving mHost locally, we still want to ensure that
964 // it only contains valid characters. See bug 304904 for details.
965 // Sometimes the end host is not yet known and mHost is *
966 if (!net_IsValidHostName(mHost
) && !mHost
.EqualsLiteral("*")) {
967 SOCKET_LOG((" invalid hostname %s\n", mHost
.get()));
968 return NS_ERROR_UNKNOWN_HOST
;
971 if (mProxyTransparentResolvesHost
) {
972 // Name resolution is done on the server side. Just pretend
973 // client resolution is complete, this will get picked up later.
974 // since we don't need to do DNS now, we bypass the resolving
975 // step by initializing mNetAddr to an empty address, but we
976 // must keep the port. The SOCKS IO layer will use the hostname
977 // we send it when it's created, rather than the empty address
978 // we send with the connect call.
979 mState
= STATE_RESOLVING
;
980 mNetAddr
.raw
.family
= AF_INET
;
981 mNetAddr
.inet
.port
= htons(SocketPort());
982 mNetAddr
.inet
.ip
= htonl(INADDR_ANY
);
983 return PostEvent(MSG_DNS_LOOKUP_COMPLETE
, NS_OK
, nullptr);
987 if (mExternalDNSResolution
) {
988 MOZ_ASSERT(mDNSRecord
);
989 mState
= STATE_RESOLVING
;
990 return PostEvent(MSG_DNS_LOOKUP_COMPLETE
, NS_OK
, nullptr);
993 nsCOMPtr
<nsIDNSService
> dns
= nullptr;
994 auto initTask
= [&dns
]() { dns
= do_GetService(kDNSServiceCID
); };
995 if (!NS_IsMainThread()) {
996 // Forward to the main thread synchronously.
997 RefPtr
<nsIThread
> mainThread
= do_GetMainThread();
999 return NS_ERROR_FAILURE
;
1002 SyncRunnable::DispatchToThread(
1004 NS_NewRunnableFunction("nsSocketTransport::ResolveHost->GetDNSService",
1010 return NS_ERROR_FAILURE
;
1015 nsIDNSService::DNSFlags dnsFlags
= nsIDNSService::RESOLVE_DEFAULT_FLAGS
;
1016 if (mConnectionFlags
& nsSocketTransport::BYPASS_CACHE
) {
1017 dnsFlags
= nsIDNSService::RESOLVE_BYPASS_CACHE
;
1019 if (mConnectionFlags
& nsSocketTransport::REFRESH_CACHE
) {
1020 dnsFlags
= nsIDNSService::RESOLVE_REFRESH_CACHE
;
1022 if (mConnectionFlags
& nsSocketTransport::DISABLE_IPV6
) {
1023 dnsFlags
|= nsIDNSService::RESOLVE_DISABLE_IPV6
;
1025 if (mConnectionFlags
& nsSocketTransport::DISABLE_IPV4
) {
1026 dnsFlags
|= nsIDNSService::RESOLVE_DISABLE_IPV4
;
1028 if (mConnectionFlags
& nsSocketTransport::DISABLE_TRR
) {
1029 dnsFlags
|= nsIDNSService::RESOLVE_DISABLE_TRR
;
1032 if (mConnectionFlags
& nsSocketTransport::USE_IP_HINT_ADDRESS
) {
1033 dnsFlags
|= nsIDNSService::RESOLVE_IP_HINT
;
1036 dnsFlags
|= nsIDNSService::GetFlagsFromTRRMode(
1037 nsISocketTransport::GetTRRModeFromFlags(mConnectionFlags
));
1039 // When we get here, we are not resolving using any configured proxy likely
1040 // because of individual proxy setting on the request or because the host is
1041 // excluded from proxying. Hence, force resolution despite global proxy-DNS
1043 dnsFlags
|= nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS
;
1045 NS_ASSERTION(!(dnsFlags
& nsIDNSService::RESOLVE_DISABLE_IPV6
) ||
1046 !(dnsFlags
& nsIDNSService::RESOLVE_DISABLE_IPV4
),
1047 "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
1049 SendStatus(NS_NET_STATUS_RESOLVING_HOST
);
1051 if (!SocketHost().Equals(mOriginHost
)) {
1052 SOCKET_LOG(("nsSocketTransport %p origin %s doing dns for %s\n", this,
1053 mOriginHost
.get(), SocketHost().get()));
1056 dns
->AsyncResolveNative(SocketHost(), nsIDNSService::RESOLVE_TYPE_DEFAULT
,
1057 dnsFlags
, nullptr, this, mSocketTransportService
,
1058 mOriginAttributes
, getter_AddRefs(mDNSRequest
));
1060 if (NS_SUCCEEDED(rv
)) {
1061 SOCKET_LOG((" advancing to STATE_RESOLVING\n"));
1062 mState
= STATE_RESOLVING
;
1067 nsresult
nsSocketTransport::BuildSocket(PRFileDesc
*& fd
, bool& proxyTransparent
,
1069 SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
1071 nsresult rv
= NS_OK
;
1073 proxyTransparent
= false;
1076 if (mTypes
.IsEmpty()) {
1077 fd
= PR_OpenTCPSocket(mNetAddr
.raw
.family
);
1079 SOCKET_LOG((" error creating TCP nspr socket [rv=%" PRIx32
"]\n",
1080 static_cast<uint32_t>(rv
)));
1081 return NS_ERROR_OUT_OF_MEMORY
;
1086 #if defined(XP_UNIX)
1087 MOZ_ASSERT(!mNetAddrIsSet
|| mNetAddr
.raw
.family
!= AF_LOCAL
,
1088 "Unix domain sockets can't be used with socket types");
1093 uint32_t controlFlags
= 0;
1094 if (mProxyTransparentResolvesHost
) {
1095 controlFlags
|= nsISocketProvider::PROXY_RESOLVES_HOST
;
1098 if (mConnectionFlags
& nsISocketTransport::ANONYMOUS_CONNECT
) {
1099 controlFlags
|= nsISocketProvider::ANONYMOUS_CONNECT
;
1102 if (mConnectionFlags
& nsISocketTransport::NO_PERMANENT_STORAGE
) {
1103 controlFlags
|= nsISocketProvider::NO_PERMANENT_STORAGE
;
1106 if (mConnectionFlags
& nsISocketTransport::BE_CONSERVATIVE
) {
1107 controlFlags
|= nsISocketProvider::BE_CONSERVATIVE
;
1110 if (mConnectionFlags
& nsISocketTransport::DONT_TRY_ECH
) {
1111 controlFlags
|= nsISocketProvider::DONT_TRY_ECH
;
1114 if (mConnectionFlags
& nsISocketTransport::IS_RETRY
) {
1115 controlFlags
|= nsISocketProvider::IS_RETRY
;
1118 if (mConnectionFlags
&
1119 nsISocketTransport::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT
) {
1120 controlFlags
|= nsISocketProvider::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT
;
1123 if (mConnectionFlags
& nsISocketTransport::IS_SPECULATIVE_CONNECTION
) {
1124 controlFlags
|= nsISocketProvider::IS_SPECULATIVE_CONNECTION
;
1127 if (mResolvedByTRR
) {
1128 controlFlags
|= nsISocketProvider::USED_PRIVATE_DNS
;
1131 // by setting host to mOriginHost, instead of mHost we send the
1132 // SocketProvider (e.g. PSM) the origin hostname but can still do DNS
1133 // on an explicit alternate service host name
1134 const char* host
= mOriginHost
.get();
1135 int32_t port
= (int32_t)mOriginPort
;
1137 nsCOMPtr
<nsISocketProviderService
> spserv
=
1138 nsSocketProviderService::GetOrCreate();
1139 nsCOMPtr
<nsIProxyInfo
> proxyInfo
= mProxyInfo
;
1142 for (i
= 0; i
< mTypes
.Length(); ++i
) {
1143 nsCOMPtr
<nsISocketProvider
> provider
;
1145 SOCKET_LOG((" pushing io layer [%u:%s]\n", i
, mTypes
[i
].get()));
1147 rv
= spserv
->GetSocketProvider(mTypes
[i
].get(), getter_AddRefs(provider
));
1148 if (NS_FAILED(rv
)) break;
1150 nsCOMPtr
<nsITLSSocketControl
> tlsSocketControl
;
1152 // if this is the first type, we'll want the
1153 // service to allocate a new socket
1155 // Most layers _ESPECIALLY_ PSM want the origin name here as they
1156 // will use it for secure checks, etc.. and any connection management
1157 // differences between the origin name and the routed name can be
1158 // taken care of via DNS. However, SOCKS is a special case as there is
1159 // no DNS. in the case of SOCKS and PSM the PSM is a separate layer
1160 // and receives the origin name.
1161 const char* socketProviderHost
= host
;
1162 int32_t socketProviderPort
= port
;
1163 if (mProxyTransparentResolvesHost
&&
1164 (mTypes
[0].EqualsLiteral("socks") ||
1165 mTypes
[0].EqualsLiteral("socks4"))) {
1166 SOCKET_LOG(("SOCKS %d Host/Route override: %s:%d -> %s:%d\n",
1167 mHttpsProxy
, socketProviderHost
, socketProviderPort
,
1168 mHost
.get(), mPort
));
1169 socketProviderHost
= mHost
.get();
1170 socketProviderPort
= mPort
;
1173 // when https proxying we want to just connect to the proxy as if
1174 // it were the end host (i.e. expect the proxy's cert)
1176 rv
= provider
->NewSocket(
1177 mNetAddr
.raw
.family
,
1178 mHttpsProxy
? mProxyHost
.get() : socketProviderHost
,
1179 mHttpsProxy
? mProxyPort
: socketProviderPort
, proxyInfo
,
1180 mOriginAttributes
, controlFlags
, mTlsFlags
, &fd
,
1181 getter_AddRefs(tlsSocketControl
));
1183 if (NS_SUCCEEDED(rv
) && !fd
) {
1184 MOZ_ASSERT_UNREACHABLE(
1185 "NewSocket succeeded but failed to "
1186 "create a PRFileDesc");
1187 rv
= NS_ERROR_UNEXPECTED
;
1190 // the socket has already been allocated,
1191 // so we just want the service to add itself
1192 // to the stack (such as pushing an io layer)
1193 rv
= provider
->AddToSocket(mNetAddr
.raw
.family
, host
, port
, proxyInfo
,
1194 mOriginAttributes
, controlFlags
, mTlsFlags
, fd
,
1195 getter_AddRefs(tlsSocketControl
));
1198 // controlFlags = 0; not used below this point...
1199 if (NS_FAILED(rv
)) break;
1201 // if the service was ssl or starttls, we want to hold onto the socket
1203 bool isSSL
= mTypes
[i
].EqualsLiteral("ssl");
1204 if (isSSL
|| mTypes
[i
].EqualsLiteral("starttls")) {
1205 // remember security info
1207 MutexAutoLock
lock(mLock
);
1208 mTLSSocketControl
= tlsSocketControl
;
1209 SOCKET_LOG((" [tlsSocketControl=%p callbacks=%p]\n",
1210 mTLSSocketControl
.get(), mCallbacks
.get()));
1212 // remember if socket type is SSL so we can ProxyStartSSL if need be.
1214 } else if (mTypes
[i
].EqualsLiteral("socks") ||
1215 mTypes
[i
].EqualsLiteral("socks4")) {
1216 // since socks is transparent, any layers above
1217 // it do not have to worry about proxy stuff
1218 proxyInfo
= nullptr;
1219 proxyTransparent
= true;
1223 if (NS_FAILED(rv
)) {
1224 SOCKET_LOG((" error pushing io layer [%u:%s rv=%" PRIx32
"]\n", i
,
1225 mTypes
[i
].get(), static_cast<uint32_t>(rv
)));
1228 fd
, mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1234 nsresult
nsSocketTransport::InitiateSocket() {
1235 SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
1236 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1242 if (gIOService
->IsNetTearingDown()) {
1243 return NS_ERROR_ABORT
;
1245 if (gIOService
->IsOffline()) {
1246 if (StaticPrefs::network_disable_localhost_when_offline() || !isLocal
) {
1247 return NS_ERROR_OFFLINE
;
1249 } else if (!isLocal
) {
1251 // all IP networking has to be done from the parent
1252 if (NS_SUCCEEDED(mCondition
) && ((mNetAddr
.raw
.family
== AF_INET
) ||
1253 (mNetAddr
.raw
.family
== AF_INET6
))) {
1254 MOZ_ASSERT(!IsNeckoChild());
1258 if (NS_SUCCEEDED(mCondition
) && xpc::AreNonLocalConnectionsDisabled() &&
1259 !(mNetAddr
.IsIPAddrAny() || mNetAddr
.IsIPAddrLocal() ||
1260 mNetAddr
.IsIPAddrShared())) {
1261 nsAutoCString ipaddr
;
1262 RefPtr
<nsNetAddr
> netaddr
= new nsNetAddr(&mNetAddr
);
1263 netaddr
->GetAddress(ipaddr
);
1266 "FATAL ERROR: Non-local network connections are disabled and a "
1268 "attempt to %s (%s) was made.\nYou should only access hostnames "
1269 "available via the test networking proxy (if running mochitests) "
1270 "or from a test-specific httpd.js server (if running xpcshell "
1272 "Browser services should be disabled or redirected to a local "
1274 mHost
.get(), ipaddr
.get());
1275 return NS_ERROR_NON_LOCAL_CONNECTION_REFUSED
;
1279 // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
1280 // connected - Bug 853423.
1281 if (mConnectionFlags
& nsISocketTransport::DISABLE_RFC1918
&&
1282 mNetAddr
.IsIPAddrLocal()) {
1283 if (SOCKET_LOG_ENABLED()) {
1284 nsAutoCString netAddrCString
;
1285 netAddrCString
.SetLength(kIPv6CStrBufSize
);
1286 if (!mNetAddr
.ToStringBuffer(netAddrCString
.BeginWriting(),
1287 kIPv6CStrBufSize
)) {
1288 netAddrCString
= "<IP-to-string failed>"_ns
;
1291 ("nsSocketTransport::InitiateSocket skipping "
1292 "speculative connection for host [%s:%d] proxy "
1293 "[%s:%d] with Local IP address [%s]",
1294 mHost
.get(), mPort
, mProxyHost
.get(), mProxyPort
,
1295 netAddrCString
.get()));
1297 mCondition
= NS_ERROR_CONNECTION_REFUSED
;
1298 OnSocketDetached(nullptr);
1303 // find out if it is going to be ok to attach another socket to the STS.
1304 // if not then we have to wait for the STS to tell us that it is ok.
1305 // the notification is asynchronous, which means that when we could be
1306 // in a race to call AttachSocket once notified. for this reason, when
1307 // we get notified, we just re-enter this function. as a result, we are
1308 // sure to ask again before calling AttachSocket. in this way we deal
1309 // with the race condition. though it isn't the most elegant solution,
1310 // it is far simpler than trying to build a system that would guarantee
1311 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
1312 // 194402 for more info.
1314 if (!mSocketTransportService
->CanAttachSocket()) {
1315 nsCOMPtr
<nsIRunnable
> event
=
1316 new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET
);
1317 if (!event
) return NS_ERROR_OUT_OF_MEMORY
;
1318 return mSocketTransportService
->NotifyWhenCanAttachSocket(event
);
1322 // if we already have a connected socket, then just attach and return.
1325 MutexAutoLock
lock(mLock
);
1326 if (mFD
.IsInitialized()) {
1327 rv
= mSocketTransportService
->AttachSocket(mFD
, this);
1328 if (NS_SUCCEEDED(rv
)) mAttached
= true;
1334 // create new socket fd, push io layers, etc.
1337 bool proxyTransparent
;
1340 rv
= BuildSocket(fd
, proxyTransparent
, usingSSL
);
1341 if (NS_FAILED(rv
)) {
1343 (" BuildSocket failed [rv=%" PRIx32
"]\n", static_cast<uint32_t>(rv
)));
1347 // create proxy via IOActivityMonitor
1348 IOActivityMonitor::MonitorSocket(fd
);
1351 if (StaticPrefs::fuzzing_necko_enabled()) {
1352 rv
= AttachFuzzyIOLayer(fd
);
1353 if (NS_FAILED(rv
)) {
1354 SOCKET_LOG(("Failed to attach fuzzing IOLayer [rv=%" PRIx32
"].\n",
1355 static_cast<uint32_t>(rv
)));
1358 SOCKET_LOG(("Successfully attached fuzzing IOLayer.\n"));
1361 mTLSSocketControl
= new FuzzySocketControl();
1368 // Make the socket non-blocking...
1369 PRSocketOptionData opt
;
1370 opt
.option
= PR_SockOpt_Nonblocking
;
1371 opt
.value
.non_blocking
= true;
1372 status
= PR_SetSocketOption(fd
, &opt
);
1373 NS_ASSERTION(status
== PR_SUCCESS
, "unable to make socket non-blocking");
1375 if (mReuseAddrPort
) {
1376 SOCKET_LOG((" Setting port/addr reuse socket options\n"));
1378 // Set ReuseAddr for TCP sockets to enable having several
1379 // sockets bound to same local IP and port
1380 PRSocketOptionData opt_reuseaddr
;
1381 opt_reuseaddr
.option
= PR_SockOpt_Reuseaddr
;
1382 opt_reuseaddr
.value
.reuse_addr
= PR_TRUE
;
1383 status
= PR_SetSocketOption(fd
, &opt_reuseaddr
);
1384 if (status
!= PR_SUCCESS
) {
1385 SOCKET_LOG((" Couldn't set reuse addr socket option: %d\n", status
));
1388 // And also set ReusePort for platforms supporting this socket option
1389 PRSocketOptionData opt_reuseport
;
1390 opt_reuseport
.option
= PR_SockOpt_Reuseport
;
1391 opt_reuseport
.value
.reuse_port
= PR_TRUE
;
1392 status
= PR_SetSocketOption(fd
, &opt_reuseport
);
1393 if (status
!= PR_SUCCESS
&&
1394 PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR
) {
1395 SOCKET_LOG((" Couldn't set reuse port socket option: %d\n", status
));
1399 // disable the nagle algorithm - if we rely on it to coalesce writes into
1400 // full packets the final packet of a multi segment POST/PUT or pipeline
1401 // sequence is delayed a full rtt
1402 opt
.option
= PR_SockOpt_NoDelay
;
1403 opt
.value
.no_delay
= true;
1404 PR_SetSocketOption(fd
, &opt
);
1406 // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
1407 // The Windows default of 8KB is too small and as of vista sp1, autotuning
1408 // only applies to receive window
1409 int32_t sndBufferSize
;
1410 mSocketTransportService
->GetSendBufferSize(&sndBufferSize
);
1411 if (sndBufferSize
> 0) {
1412 opt
.option
= PR_SockOpt_SendBufferSize
;
1413 opt
.value
.send_buffer_size
= sndBufferSize
;
1414 PR_SetSocketOption(fd
, &opt
);
1418 opt
.option
= PR_SockOpt_IpTypeOfService
;
1419 opt
.value
.tos
= mQoSBits
;
1420 PR_SetSocketOption(fd
, &opt
);
1424 // The linger is turned off by default. This is not a hard close, but
1425 // closesocket should return immediately and operating system tries to send
1426 // remaining data for certain, implementation specific, amount of time.
1427 // https://msdn.microsoft.com/en-us/library/ms739165.aspx
1429 // Turn the linger option on an set the interval to 0. This will cause hard
1430 // close of the socket.
1431 opt
.option
= PR_SockOpt_Linger
;
1432 opt
.value
.linger
.polarity
= 1;
1433 opt
.value
.linger
.linger
= 0;
1434 PR_SetSocketOption(fd
, &opt
);
1437 // up to here, mFD will only be accessed by us
1439 // assign mFD so that we can properly handle OnSocketDetached before we've
1440 // established a connection.
1442 MutexAutoLock
lock(mLock
);
1443 // inform socket transport about this newly created socket...
1444 rv
= mSocketTransportService
->AttachSocket(fd
, this);
1445 if (NS_FAILED(rv
)) {
1447 fd
, mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1454 mFDconnected
= false;
1455 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
1458 SOCKET_LOG((" advancing to STATE_CONNECTING\n"));
1459 mState
= STATE_CONNECTING
;
1460 SendStatus(NS_NET_STATUS_CONNECTING_TO
);
1462 if (SOCKET_LOG_ENABLED()) {
1463 char buf
[kNetAddrMaxCStrBufSize
];
1464 mNetAddr
.ToStringBuffer(buf
, sizeof(buf
));
1465 SOCKET_LOG((" trying address: %s\n", buf
));
1469 // Initiate the connect() to the host...
1472 memset(&prAddr
, 0, sizeof(prAddr
));
1475 MutexAutoLock
lock(mLock
);
1476 NetAddrToPRNetAddr(mBindAddr
.get(), &prAddr
);
1477 status
= PR_Bind(fd
, &prAddr
);
1478 if (status
!= PR_SUCCESS
) {
1479 return NS_ERROR_FAILURE
;
1481 mBindAddr
= nullptr;
1485 NetAddrToPRNetAddr(&mNetAddr
, &prAddr
);
1488 // Find the real tcp socket and set non-blocking once again!
1490 PRFileDesc
* bottom
= PR_GetIdentitiesLayer(fd
, PR_NSPR_IO_LAYER
);
1492 PROsfd osfd
= PR_FileDesc2NativeHandle(bottom
);
1493 u_long nonblocking
= 1;
1494 if (ioctlsocket(osfd
, FIONBIO
, &nonblocking
) != 0) {
1495 NS_WARNING("Socket could not be set non-blocking!");
1496 return NS_ERROR_FAILURE
;
1501 if (mTLSSocketControl
) {
1502 if (!mEchConfig
.IsEmpty() &&
1503 !(mConnectionFlags
& (DONT_TRY_ECH
| BE_CONSERVATIVE
))) {
1504 SOCKET_LOG(("nsSocketTransport::InitiateSocket set echconfig."));
1505 rv
= mTLSSocketControl
->SetEchConfig(mEchConfig
);
1506 if (NS_FAILED(rv
)) {
1509 mEchConfigUsed
= true;
1513 // We use PRIntervalTime here because we need
1514 // nsIOService::LastOfflineStateChange time and
1515 // nsIOService::LastConectivityChange time to be atomic.
1516 PRIntervalTime connectStarted
= 0;
1517 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
1518 connectStarted
= PR_IntervalNow();
1521 if (Telemetry::CanRecordPrereleaseData() ||
1522 Telemetry::CanRecordReleaseData()) {
1523 if (NS_FAILED(AttachNetworkDataCountLayer(fd
))) {
1525 ("nsSocketTransport::InitiateSocket "
1526 "AttachNetworkDataCountLayer failed [this=%p]\n",
1531 bool connectCalled
= true; // This is only needed for telemetry.
1532 status
= PR_Connect(fd
, &prAddr
, NS_SOCKET_CONNECT_TIMEOUT
);
1533 PRErrorCode code
= PR_GetError();
1534 if (status
== PR_SUCCESS
) {
1535 PR_SetFDInheritable(fd
, false);
1538 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase() &&
1539 connectStarted
&& connectCalled
) {
1540 SendPRBlockingTelemetry(
1541 connectStarted
, Telemetry::PRCONNECT_BLOCKING_TIME_NORMAL
,
1542 Telemetry::PRCONNECT_BLOCKING_TIME_SHUTDOWN
,
1543 Telemetry::PRCONNECT_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
1544 Telemetry::PRCONNECT_BLOCKING_TIME_LINK_CHANGE
,
1545 Telemetry::PRCONNECT_BLOCKING_TIME_OFFLINE
);
1548 if (status
== PR_SUCCESS
) {
1550 // we are connected!
1552 OnSocketConnected();
1554 #if defined(TEST_CONNECT_ERRORS)
1555 code
= RandomizeConnectError(code
);
1558 // If the PR_Connect(...) would block, then poll for a connection.
1560 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
)) {
1561 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
1563 // If the socket is already connected, then return success...
1565 } else if (PR_IS_CONNECTED_ERROR
== code
) {
1567 // we are connected!
1569 OnSocketConnected();
1571 if (mTLSSocketControl
&& !mProxyHost
.IsEmpty() && proxyTransparent
&&
1573 // if the connection phase is finished, and the ssl layer has
1574 // been pushed, and we were proxying (transparently; ie. nothing
1575 // has to happen in the protocol layer above us), it's time for
1576 // the ssl to start doing it's thing.
1577 SOCKET_LOG((" calling ProxyStartSSL()\n"));
1578 mTLSSocketControl
->ProxyStartSSL();
1579 // XXX what if we were forced to poll on the socket for a successful
1580 // connection... wouldn't we need to call ProxyStartSSL after a call
1581 // to PR_ConnectContinue indicates that we are connected?
1583 // XXX this appears to be what the old socket transport did. why
1584 // isn't this broken?
1588 // A SOCKS request was rejected; get the actual error code from
1591 else if (PR_UNKNOWN_ERROR
== code
&& mProxyTransparent
&&
1592 !mProxyHost
.IsEmpty()) {
1593 code
= PR_GetOSError();
1594 rv
= ErrorAccordingToNSPR(code
);
1597 // The connection was refused...
1600 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase() &&
1601 connectStarted
&& connectCalled
) {
1602 SendPRBlockingTelemetry(
1603 connectStarted
, Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_NORMAL
,
1604 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_SHUTDOWN
,
1605 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
1606 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_LINK_CHANGE
,
1607 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_OFFLINE
);
1610 rv
= ErrorAccordingToNSPR(code
);
1611 if ((rv
== NS_ERROR_CONNECTION_REFUSED
) && !mProxyHost
.IsEmpty()) {
1612 rv
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
1619 bool nsSocketTransport::RecoverFromError() {
1620 NS_ASSERTION(NS_FAILED(mCondition
), "there should be something wrong");
1623 ("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%" PRIx32
1625 this, mState
, static_cast<uint32_t>(mCondition
)));
1627 if (mDoNotRetryToConnect
) {
1629 ("nsSocketTransport::RecoverFromError do not retry because "
1630 "mDoNotRetryToConnect is set [this=%p]\n",
1635 #if defined(XP_UNIX)
1636 // Unix domain connections don't have multiple addresses to try,
1637 // so the recovery techniques here don't apply.
1638 if (mNetAddrIsSet
&& mNetAddr
.raw
.family
== AF_LOCAL
) return false;
1641 if ((mConnectionFlags
& nsSocketTransport::USE_IP_HINT_ADDRESS
) &&
1642 mCondition
== NS_ERROR_UNKNOWN_HOST
&&
1643 (mState
== MSG_DNS_LOOKUP_COMPLETE
|| mState
== MSG_ENSURE_CONNECT
)) {
1644 SOCKET_LOG((" try again without USE_IP_HINT_ADDRESS"));
1645 mConnectionFlags
&= ~nsSocketTransport::USE_IP_HINT_ADDRESS
;
1646 mState
= STATE_CLOSED
;
1647 return NS_SUCCEEDED(PostEvent(MSG_ENSURE_CONNECT
, NS_OK
));
1650 // can only recover from errors in these states
1651 if (mState
!= STATE_RESOLVING
&& mState
!= STATE_CONNECTING
) {
1652 SOCKET_LOG((" not in a recoverable state"));
1660 MutexAutoLock
lock(mLock
);
1661 NS_ASSERTION(!mFDconnected
, "socket should not be connected");
1665 // all connection failures need to be reported to DNS so that the next
1666 // time we will use a different address if available.
1667 // NS_BASE_STREAM_CLOSED is not an actual connection failure, so don't report
1669 if (mState
== STATE_CONNECTING
&& mDNSRecord
&&
1670 mCondition
!= NS_BASE_STREAM_CLOSED
) {
1671 mDNSRecord
->ReportUnusable(SocketPort());
1674 if (mCondition
!= NS_ERROR_CONNECTION_REFUSED
&&
1675 mCondition
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
1676 mCondition
!= NS_ERROR_NET_TIMEOUT
&&
1677 mCondition
!= NS_ERROR_UNKNOWN_HOST
&&
1678 mCondition
!= NS_ERROR_UNKNOWN_PROXY_HOST
) {
1679 SOCKET_LOG((" not a recoverable error %" PRIx32
,
1680 static_cast<uint32_t>(mCondition
)));
1684 bool tryAgain
= false;
1686 if ((mState
== STATE_CONNECTING
) && mDNSRecord
) {
1687 if (mNetAddr
.raw
.family
== AF_INET
) {
1688 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
1689 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
1690 UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS
);
1692 } else if (mNetAddr
.raw
.family
== AF_INET6
) {
1693 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
1694 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
1695 UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS
);
1700 if (mConnectionFlags
& RETRY_WITH_DIFFERENT_IP_FAMILY
&&
1701 mCondition
== NS_ERROR_UNKNOWN_HOST
&& mState
== STATE_RESOLVING
&&
1702 !mProxyTransparentResolvesHost
) {
1703 SOCKET_LOG((" trying lookup again with opposite ip family\n"));
1704 mConnectionFlags
^= (DISABLE_IPV6
| DISABLE_IPV4
);
1705 mConnectionFlags
&= ~RETRY_WITH_DIFFERENT_IP_FAMILY
;
1706 // This will tell the consuming half-open to reset preference on the
1708 mResetFamilyPreference
= true;
1712 // try next ip address only if past the resolver stage...
1713 if (mState
== STATE_CONNECTING
&& mDNSRecord
) {
1714 nsresult rv
= mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
1715 mDNSRecord
->IsTRR(&mResolvedByTRR
);
1716 mDNSRecord
->GetEffectiveTRRMode(&mEffectiveTRRMode
);
1717 mDNSRecord
->GetTrrSkipReason(&mTRRSkipReason
);
1718 if (NS_SUCCEEDED(rv
)) {
1719 SOCKET_LOG((" trying again with next ip address\n"));
1721 } else if (mExternalDNSResolution
) {
1722 mRetryDnsIfPossible
= true;
1724 mDNSRecord
->IsTRR(&trrEnabled
);
1725 // Bug 1648147 - If the server responded with `0.0.0.0` or `::` then we
1726 // should intentionally not fallback to regular DNS.
1727 if (trrEnabled
&& !StaticPrefs::network_trr_fallback_on_zero_response() &&
1728 ((mNetAddr
.raw
.family
== AF_INET
&& mNetAddr
.inet
.ip
== 0) ||
1729 (mNetAddr
.raw
.family
== AF_INET6
&& mNetAddr
.inet6
.ip
.u64
[0] == 0 &&
1730 mNetAddr
.inet6
.ip
.u64
[1] == 0))) {
1731 SOCKET_LOG((" TRR returned 0.0.0.0 and there are no other IPs"));
1732 mRetryDnsIfPossible
= false;
1734 } else if (mConnectionFlags
& RETRY_WITH_DIFFERENT_IP_FAMILY
) {
1735 SOCKET_LOG((" failed to connect, trying with opposite ip family\n"));
1736 // Drop state to closed. This will trigger new round of DNS
1737 // resolving bellow.
1738 mState
= STATE_CLOSED
;
1739 mConnectionFlags
^= (DISABLE_IPV6
| DISABLE_IPV4
);
1740 mConnectionFlags
&= ~RETRY_WITH_DIFFERENT_IP_FAMILY
;
1741 // This will tell the consuming half-open to reset preference on the
1743 mResetFamilyPreference
= true;
1745 } else if (!(mConnectionFlags
& DISABLE_TRR
)) {
1747 mDNSRecord
->IsTRR(&trrEnabled
);
1749 // Bug 1648147 - If the server responded with `0.0.0.0` or `::` then we
1750 // should intentionally not fallback to regular DNS.
1751 if (!StaticPrefs::network_trr_fallback_on_zero_response() &&
1752 ((mNetAddr
.raw
.family
== AF_INET
&& mNetAddr
.inet
.ip
== 0) ||
1753 (mNetAddr
.raw
.family
== AF_INET6
&& mNetAddr
.inet6
.ip
.u64
[0] == 0 &&
1754 mNetAddr
.inet6
.ip
.u64
[1] == 0))) {
1755 SOCKET_LOG((" TRR returned 0.0.0.0 and there are no other IPs"));
1756 } else if (trrEnabled
) {
1757 nsIRequest::TRRMode trrMode
= nsIRequest::TRR_DEFAULT_MODE
;
1758 mDNSRecord
->GetEffectiveTRRMode(&trrMode
);
1759 // If current trr mode is trr only, we should not retry.
1760 if (trrMode
!= nsIRequest::TRR_ONLY_MODE
) {
1761 // Drop state to closed. This will trigger a new round of
1762 // DNS resolving. Bypass the cache this time since the
1763 // cached data came from TRR and failed already!
1764 SOCKET_LOG((" failed to connect with TRR enabled, try w/o\n"));
1765 mState
= STATE_CLOSED
;
1766 mConnectionFlags
|= DISABLE_TRR
| BYPASS_CACHE
| REFRESH_CACHE
;
1773 // prepare to try again.
1777 if (mState
== STATE_CONNECTING
) {
1778 mState
= STATE_RESOLVING
;
1779 msg
= MSG_DNS_LOOKUP_COMPLETE
;
1781 mState
= STATE_CLOSED
;
1782 msg
= MSG_ENSURE_CONNECT
;
1785 rv
= PostEvent(msg
, NS_OK
);
1786 if (NS_FAILED(rv
)) tryAgain
= false;
1792 // called on the socket thread only
1793 void nsSocketTransport::OnMsgInputClosed(nsresult reason
) {
1794 SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%" PRIx32
1796 this, static_cast<uint32_t>(reason
)));
1798 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1800 mInputClosed
= true;
1801 // check if event should affect entire transport
1802 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
)) {
1803 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1804 } else if (mOutputClosed
) {
1806 NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1808 if (mState
== STATE_TRANSFERRING
) mPollFlags
&= ~PR_POLL_READ
;
1809 mInput
->OnSocketReady(reason
);
1813 // called on the socket thread only
1814 void nsSocketTransport::OnMsgOutputClosed(nsresult reason
) {
1815 SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%p reason=%" PRIx32
1817 this, static_cast<uint32_t>(reason
)));
1819 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1821 mOutputClosed
= true;
1822 // check if event should affect entire transport
1823 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
)) {
1824 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1825 } else if (mInputClosed
) {
1827 NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1829 if (mState
== STATE_TRANSFERRING
) mPollFlags
&= ~PR_POLL_WRITE
;
1830 mOutput
->OnSocketReady(reason
);
1834 void nsSocketTransport::OnSocketConnected() {
1835 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1836 SOCKET_LOG((" advancing to STATE_TRANSFERRING\n"));
1838 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
1839 mState
= STATE_TRANSFERRING
;
1841 // Set the m*AddrIsSet flags only when state has reached TRANSFERRING
1842 // because we need to make sure its value does not change due to failover
1843 mNetAddrIsSet
= true;
1845 // assign mFD (must do this within the transport lock), but take care not
1846 // to trample over mFDref if mFD is already set.
1848 MutexAutoLock
lock(mLock
);
1849 NS_ASSERTION(mFD
.IsInitialized(), "no socket");
1850 NS_ASSERTION(mFDref
== 1, "wrong socket ref count");
1852 mFDconnected
= true;
1853 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
1856 // Ensure keepalive is configured correctly if previously enabled.
1857 if (mKeepaliveEnabled
) {
1858 nsresult rv
= SetKeepaliveEnabledInternal(true);
1859 if (NS_WARN_IF(NS_FAILED(rv
))) {
1860 SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%" PRIx32
"]",
1861 static_cast<uint32_t>(rv
)));
1865 SendStatus(NS_NET_STATUS_CONNECTED_TO
);
1868 void nsSocketTransport::SetSocketName(PRFileDesc
* fd
) {
1869 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1870 if (mSelfAddrIsSet
) {
1875 memset(&prAddr
, 0, sizeof(prAddr
));
1876 if (PR_GetSockName(fd
, &prAddr
) == PR_SUCCESS
) {
1877 PRNetAddrToNetAddr(&prAddr
, &mSelfAddr
);
1878 mSelfAddrIsSet
= true;
1882 PRFileDesc
* nsSocketTransport::GetFD_Locked() {
1883 mLock
.AssertCurrentThreadOwns();
1885 // mFD is not available to the streams while disconnected.
1886 if (!mFDconnected
) return nullptr;
1888 if (mFD
.IsInitialized()) mFDref
++;
1893 class ThunkPRClose
: public Runnable
{
1895 explicit ThunkPRClose(PRFileDesc
* fd
)
1896 : Runnable("net::ThunkPRClose"), mFD(fd
) {}
1898 NS_IMETHOD
Run() override
{
1899 nsSocketTransport::CloseSocket(
1900 mFD
, gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1908 void STS_PRCloseOnSocketTransport(PRFileDesc
* fd
, bool lingerPolarity
,
1909 int16_t lingerTimeout
) {
1910 if (gSocketTransportService
) {
1911 // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
1912 gSocketTransportService
->Dispatch(new ThunkPRClose(fd
), NS_DISPATCH_NORMAL
);
1914 // something horrible has happened
1915 NS_ASSERTION(gSocketTransportService
, "No STS service");
1919 void nsSocketTransport::ReleaseFD_Locked(PRFileDesc
* fd
) {
1920 mLock
.AssertCurrentThreadOwns();
1922 NS_ASSERTION(mFD
== fd
, "wrong fd");
1924 if (--mFDref
== 0) {
1925 if (gIOService
->IsNetTearingDown() &&
1926 ((PR_IntervalNow() - gIOService
->NetTearingDownStarted()) >
1927 gSocketTransportService
->MaxTimeForPrClosePref())) {
1928 // If shutdown last to long, let the socket leak and do not close it.
1929 SOCKET_LOG(("Intentional leak"));
1931 if (mLingerPolarity
|| mLingerTimeout
) {
1932 PRSocketOptionData socket_linger
;
1933 socket_linger
.option
= PR_SockOpt_Linger
;
1934 socket_linger
.value
.linger
.polarity
= mLingerPolarity
;
1935 socket_linger
.value
.linger
.linger
= mLingerTimeout
;
1936 PR_SetSocketOption(mFD
, &socket_linger
);
1938 if (OnSocketThread()) {
1939 SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
1941 mFD
, mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1943 // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
1944 STS_PRCloseOnSocketTransport(mFD
, mLingerPolarity
, mLingerTimeout
);
1951 //-----------------------------------------------------------------------------
1952 // socket event handler impl
1954 void nsSocketTransport::OnSocketEvent(uint32_t type
, nsresult status
,
1956 std::function
<void()>&& task
) {
1957 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1959 ("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%" PRIx32
1961 this, type
, static_cast<uint32_t>(status
), param
));
1963 if (NS_FAILED(mCondition
)) {
1964 // block event since we're apparently already dead.
1965 SOCKET_LOG((" blocking event [condition=%" PRIx32
"]\n",
1966 static_cast<uint32_t>(mCondition
)));
1968 // notify input/output streams in case either has a pending notify.
1970 mInput
->OnSocketReady(mCondition
);
1971 mOutput
->OnSocketReady(mCondition
);
1976 case MSG_ENSURE_CONNECT
:
1977 SOCKET_LOG((" MSG_ENSURE_CONNECT\n"));
1982 // Apply port remapping here so that we do it on the socket thread and
1983 // before we process the resolved DNS name or create the socket the first
1985 if (!mPortRemappingApplied
) {
1986 mPortRemappingApplied
= true;
1988 mSocketTransportService
->ApplyPortRemap(&mPort
);
1989 mSocketTransportService
->ApplyPortRemap(&mOriginPort
);
1993 // ensure that we have created a socket, attached it, and have a
1996 if (mState
== STATE_CLOSED
) {
1997 // Unix domain sockets are ready to connect; mNetAddr is all we
1998 // need. Internet address families require a DNS lookup (or possibly
1999 // several) before we can connect.
2000 #if defined(XP_UNIX)
2001 if (mNetAddrIsSet
&& mNetAddr
.raw
.family
== AF_LOCAL
) {
2002 mCondition
= InitiateSocket();
2007 mCondition
= ResolveHost();
2011 SOCKET_LOG((" ignoring redundant event\n"));
2015 case MSG_DNS_LOOKUP_COMPLETE
:
2016 if (mDNSRequest
) { // only send this if we actually resolved anything
2017 SendStatus(NS_NET_STATUS_RESOLVED_HOST
);
2020 SOCKET_LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
2021 mDNSRequest
= nullptr;
2024 mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
2025 mDNSRecord
->IsTRR(&mResolvedByTRR
);
2026 mDNSRecord
->GetEffectiveTRRMode(&mEffectiveTRRMode
);
2027 mDNSRecord
->GetTrrSkipReason(&mTRRSkipReason
);
2029 // status contains DNS lookup status
2030 if (NS_FAILED(status
)) {
2031 // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
2032 // proxy host is not found, so we fixup the error code.
2033 // For SOCKS proxies (mProxyTransparent == true), the socket
2034 // transport resolves the real host here, so there's no fixup
2035 // (see bug 226943).
2036 if ((status
== NS_ERROR_UNKNOWN_HOST
) && !mProxyTransparent
&&
2037 !mProxyHost
.IsEmpty()) {
2038 mCondition
= NS_ERROR_UNKNOWN_PROXY_HOST
;
2040 mCondition
= status
;
2042 } else if (mState
== STATE_RESOLVING
) {
2043 mCondition
= InitiateSocket();
2047 case MSG_RETRY_INIT_SOCKET
:
2048 mCondition
= InitiateSocket();
2051 case MSG_INPUT_CLOSED
:
2052 SOCKET_LOG((" MSG_INPUT_CLOSED\n"));
2053 OnMsgInputClosed(status
);
2056 case MSG_INPUT_PENDING
:
2057 SOCKET_LOG((" MSG_INPUT_PENDING\n"));
2058 OnMsgInputPending();
2061 case MSG_OUTPUT_CLOSED
:
2062 SOCKET_LOG((" MSG_OUTPUT_CLOSED\n"));
2063 OnMsgOutputClosed(status
);
2066 case MSG_OUTPUT_PENDING
:
2067 SOCKET_LOG((" MSG_OUTPUT_PENDING\n"));
2068 OnMsgOutputPending();
2070 case MSG_TIMEOUT_CHANGED
:
2071 SOCKET_LOG((" MSG_TIMEOUT_CHANGED\n"));
2073 MutexAutoLock
lock(mLock
);
2075 mTimeouts
[(mState
== STATE_TRANSFERRING
) ? TIMEOUT_READ_WRITE
2080 SOCKET_LOG((" unhandled event!\n"));
2083 if (NS_FAILED(mCondition
)) {
2084 SOCKET_LOG((" after event [this=%p cond=%" PRIx32
"]\n", this,
2085 static_cast<uint32_t>(mCondition
)));
2086 if (!mAttached
) { // need to process this error ourselves...
2087 OnSocketDetached(nullptr);
2089 } else if (mPollFlags
== PR_POLL_EXCEPT
) {
2090 mPollFlags
= 0; // make idle
2094 uint64_t nsSocketTransport::ByteCountReceived() { return mInput
->ByteCount(); }
2096 uint64_t nsSocketTransport::ByteCountSent() { return mOutput
->ByteCount(); }
2098 //-----------------------------------------------------------------------------
2099 // socket handler impl
2101 void nsSocketTransport::OnSocketReady(PRFileDesc
* fd
, int16_t outFlags
) {
2102 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2103 SOCKET_LOG1(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",
2106 if (outFlags
== -1) {
2107 SOCKET_LOG(("socket timeout expired\n"));
2108 mCondition
= NS_ERROR_NET_TIMEOUT
;
2112 if (mState
== STATE_TRANSFERRING
) {
2113 // if waiting to write and socket is writable or hit an exception.
2114 if ((mPollFlags
& PR_POLL_WRITE
) && (outFlags
& ~PR_POLL_READ
)) {
2115 // assume that we won't need to poll any longer (the stream will
2116 // request that we poll again if it is still pending).
2117 mPollFlags
&= ~PR_POLL_WRITE
;
2118 mOutput
->OnSocketReady(NS_OK
);
2120 // if waiting to read and socket is readable or hit an exception.
2121 if ((mPollFlags
& PR_POLL_READ
) && (outFlags
& ~PR_POLL_WRITE
)) {
2122 // assume that we won't need to poll any longer (the stream will
2123 // request that we poll again if it is still pending).
2124 mPollFlags
&= ~PR_POLL_READ
;
2125 mInput
->OnSocketReady(NS_OK
);
2127 // Update poll timeout in case it was changed
2129 MutexAutoLock
lock(mLock
);
2130 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
2132 } else if ((mState
== STATE_CONNECTING
) && !gIOService
->IsNetTearingDown()) {
2133 // We do not need to do PR_ConnectContinue when we are already
2136 // We use PRIntervalTime here because we need
2137 // nsIOService::LastOfflineStateChange time and
2138 // nsIOService::LastConectivityChange time to be atomic.
2139 PRIntervalTime connectStarted
= 0;
2140 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
2141 connectStarted
= PR_IntervalNow();
2144 PRStatus status
= PR_ConnectContinue(fd
, outFlags
);
2146 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase() &&
2148 SendPRBlockingTelemetry(
2149 connectStarted
, Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_NORMAL
,
2150 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_SHUTDOWN
,
2151 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
2152 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_LINK_CHANGE
,
2153 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_OFFLINE
);
2156 if (status
== PR_SUCCESS
) {
2158 // we are connected!
2160 OnSocketConnected();
2162 if (mNetAddr
.raw
.family
== AF_INET
) {
2163 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
2164 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
2165 SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS
);
2167 } else if (mNetAddr
.raw
.family
== AF_INET6
) {
2168 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
2169 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
2170 SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS
);
2174 PRErrorCode code
= PR_GetError();
2175 #if defined(TEST_CONNECT_ERRORS)
2176 code
= RandomizeConnectError(code
);
2179 // If the connect is still not ready, then continue polling...
2181 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
)) {
2182 // Set up the select flags for connect...
2183 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
2184 // Update poll timeout in case it was changed
2186 MutexAutoLock
lock(mLock
);
2187 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
2191 // The SOCKS proxy rejected our request. Find out why.
2193 else if (PR_UNKNOWN_ERROR
== code
&& mProxyTransparent
&&
2194 !mProxyHost
.IsEmpty()) {
2195 code
= PR_GetOSError();
2196 mCondition
= ErrorAccordingToNSPR(code
);
2199 // else, the connection failed...
2201 mCondition
= ErrorAccordingToNSPR(code
);
2202 if ((mCondition
== NS_ERROR_CONNECTION_REFUSED
) &&
2203 !mProxyHost
.IsEmpty()) {
2204 mCondition
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
2206 SOCKET_LOG((" connection failed! [reason=%" PRIx32
"]\n",
2207 static_cast<uint32_t>(mCondition
)));
2210 } else if ((mState
== STATE_CONNECTING
) && gIOService
->IsNetTearingDown()) {
2211 // We do not need to do PR_ConnectContinue when we are already
2214 ("We are in shutdown so skip PR_ConnectContinue and set "
2216 mCondition
= NS_ERROR_ABORT
;
2218 NS_ERROR("unexpected socket state");
2219 mCondition
= NS_ERROR_UNEXPECTED
;
2222 if (mPollFlags
== PR_POLL_EXCEPT
) mPollFlags
= 0; // make idle
2225 // called on the socket thread only
2226 void nsSocketTransport::OnSocketDetached(PRFileDesc
* fd
) {
2227 SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%" PRIx32
2229 this, static_cast<uint32_t>(mCondition
)));
2231 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2235 // if we didn't initiate this detach, then be sure to pass an error
2236 // condition up to our consumers. (e.g., STS is shutting down.)
2237 if (NS_SUCCEEDED(mCondition
)) {
2238 if (gIOService
->IsOffline()) {
2239 mCondition
= NS_ERROR_OFFLINE
;
2241 mCondition
= NS_ERROR_ABORT
;
2245 // If we are not shutting down try again.
2246 if (!gIOService
->IsNetTearingDown() && RecoverFromError()) {
2249 mState
= STATE_CLOSED
;
2251 // make sure there isn't any pending DNS request
2253 mDNSRequest
->Cancel(NS_ERROR_ABORT
);
2254 mDNSRequest
= nullptr;
2258 // notify input/output streams
2260 mInput
->OnSocketReady(mCondition
);
2261 mOutput
->OnSocketReady(mCondition
);
2262 if (gIOService
->IsNetTearingDown()) {
2263 if (mInputCopyContext
) {
2264 NS_CancelAsyncCopy(mInputCopyContext
, mCondition
);
2266 if (mOutputCopyContext
) {
2267 NS_CancelAsyncCopy(mOutputCopyContext
, mCondition
);
2272 if (mCondition
== NS_ERROR_NET_RESET
&& mDNSRecord
&&
2273 mOutput
->ByteCount() == 0) {
2274 // If we are here, it's likely that we are retrying a transaction. Blocking
2275 // the already used address could increase the successful rate of the retry.
2276 mDNSRecord
->ReportUnusable(SocketPort());
2279 // finally, release our reference to the socket (must do this within
2280 // the transport lock) possibly closing the socket. Also release our
2281 // listeners to break potential refcount cycles.
2283 // We should be careful not to release mEventSink and mCallbacks while
2284 // we're locked, because releasing it might require acquiring the lock
2285 // again, so we just null out mEventSink and mCallbacks while we're
2286 // holding the lock, and let the stack based objects' destuctors take
2287 // care of destroying it if needed.
2288 nsCOMPtr
<nsIInterfaceRequestor
> ourCallbacks
;
2289 nsCOMPtr
<nsITransportEventSink
> ourEventSink
;
2291 MutexAutoLock
lock(mLock
);
2292 if (mFD
.IsInitialized()) {
2293 ReleaseFD_Locked(mFD
);
2294 // flag mFD as unusable; this prevents other consumers from
2295 // acquiring a reference to mFD.
2296 mFDconnected
= false;
2299 // We must release mCallbacks and mEventSink to avoid memory leak
2300 // but only when RecoverFromError() above failed. Otherwise we lose
2301 // link with UI and security callbacks on next connection attempt
2302 // round. That would lead e.g. to a broken certificate exception page.
2303 if (NS_FAILED(mCondition
)) {
2304 mCallbacks
.swap(ourCallbacks
);
2305 mEventSink
.swap(ourEventSink
);
2310 void nsSocketTransport::IsLocal(bool* aIsLocal
) {
2312 MutexAutoLock
lock(mLock
);
2314 #if defined(XP_UNIX)
2315 // Unix-domain sockets are always local.
2316 if (mNetAddr
.raw
.family
== PR_AF_LOCAL
) {
2322 *aIsLocal
= mNetAddr
.IsLoopbackAddr();
2326 //-----------------------------------------------------------------------------
2329 NS_IMPL_ISUPPORTS(nsSocketTransport
, nsISocketTransport
, nsITransport
,
2330 nsIDNSListener
, nsIClassInfo
, nsIInterfaceRequestor
)
2331 NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport
, nsISocketTransport
, nsITransport
,
2332 nsIDNSListener
, nsIInterfaceRequestor
)
2335 nsSocketTransport::OpenInputStream(uint32_t flags
, uint32_t segsize
,
2337 nsIInputStream
** aResult
) {
2339 ("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n", this, flags
));
2341 NS_ENSURE_TRUE(!mInput
->IsReferenced(), NS_ERROR_UNEXPECTED
);
2344 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
2345 nsCOMPtr
<nsIInputStream
> result
;
2346 nsCOMPtr
<nsISupports
> inputCopyContext
;
2348 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
2349 // XXX if the caller wants blocking, then the caller also gets buffered!
2350 // bool openBuffered = !(flags & OPEN_UNBUFFERED);
2351 bool openBlocking
= (flags
& OPEN_BLOCKING
);
2353 net_ResolveSegmentParams(segsize
, segcount
);
2356 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
2357 NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
), !openBlocking
,
2358 true, segsize
, segcount
);
2360 // async copy from socket to pipe
2361 rv
= NS_AsyncCopy(mInput
.get(), pipeOut
, mSocketTransportService
,
2362 NS_ASYNCCOPY_VIA_WRITESEGMENTS
, segsize
, nullptr, nullptr,
2363 true, true, getter_AddRefs(inputCopyContext
));
2364 if (NS_FAILED(rv
)) return rv
;
2368 result
= mInput
.get();
2371 // flag input stream as open
2372 mInputClosed
= false;
2373 // mInputCopyContext can be only touched on socket thread
2374 auto task
= [self
= RefPtr
{this}, inputCopyContext(inputCopyContext
)]() {
2375 MOZ_ASSERT(OnSocketThread());
2376 self
->mInputCopyContext
= inputCopyContext
;
2378 rv
= PostEvent(MSG_ENSURE_CONNECT
, NS_OK
, nullptr, std::move(task
));
2379 if (NS_FAILED(rv
)) {
2383 result
.forget(aResult
);
2388 nsSocketTransport::OpenOutputStream(uint32_t flags
, uint32_t segsize
,
2390 nsIOutputStream
** aResult
) {
2391 SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n", this,
2394 NS_ENSURE_TRUE(!mOutput
->IsReferenced(), NS_ERROR_UNEXPECTED
);
2397 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
2398 nsCOMPtr
<nsIOutputStream
> result
;
2399 nsCOMPtr
<nsISupports
> outputCopyContext
;
2400 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
2401 // XXX if the caller wants blocking, then the caller also gets buffered!
2402 // bool openBuffered = !(flags & OPEN_UNBUFFERED);
2403 bool openBlocking
= (flags
& OPEN_BLOCKING
);
2405 net_ResolveSegmentParams(segsize
, segcount
);
2408 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
2409 NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
), true,
2410 !openBlocking
, segsize
, segcount
);
2412 // async copy from socket to pipe
2413 rv
= NS_AsyncCopy(pipeIn
, mOutput
.get(), mSocketTransportService
,
2414 NS_ASYNCCOPY_VIA_READSEGMENTS
, segsize
, nullptr, nullptr,
2415 true, true, getter_AddRefs(outputCopyContext
));
2416 if (NS_FAILED(rv
)) return rv
;
2420 result
= mOutput
.get();
2423 // flag output stream as open
2424 mOutputClosed
= false;
2426 // mOutputCopyContext can be only touched on socket thread
2427 auto task
= [self
= RefPtr
{this}, outputCopyContext(outputCopyContext
)]() {
2428 MOZ_ASSERT(OnSocketThread());
2429 self
->mOutputCopyContext
= outputCopyContext
;
2431 rv
= PostEvent(MSG_ENSURE_CONNECT
, NS_OK
, nullptr, std::move(task
));
2432 if (NS_FAILED(rv
)) return rv
;
2434 result
.forget(aResult
);
2439 nsSocketTransport::Close(nsresult reason
) {
2440 SOCKET_LOG(("nsSocketTransport::Close %p reason=%" PRIx32
, this,
2441 static_cast<uint32_t>(reason
)));
2443 if (NS_SUCCEEDED(reason
)) reason
= NS_BASE_STREAM_CLOSED
;
2445 mDoNotRetryToConnect
= true;
2447 mInput
->CloseWithStatus(reason
);
2448 mOutput
->CloseWithStatus(reason
);
2453 nsSocketTransport::GetTlsSocketControl(nsITLSSocketControl
** tlsSocketControl
) {
2454 MutexAutoLock
lock(mLock
);
2455 *tlsSocketControl
= do_AddRef(mTLSSocketControl
).take();
2460 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor
** callbacks
) {
2461 MutexAutoLock
lock(mLock
);
2462 *callbacks
= do_AddRef(mCallbacks
).take();
2467 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor
* callbacks
) {
2468 nsCOMPtr
<nsIInterfaceRequestor
> threadsafeCallbacks
;
2469 NS_NewNotificationCallbacksAggregation(callbacks
, nullptr,
2470 GetCurrentSerialEventTarget(),
2471 getter_AddRefs(threadsafeCallbacks
));
2472 MutexAutoLock
lock(mLock
);
2473 mCallbacks
= threadsafeCallbacks
;
2474 SOCKET_LOG(("Reset callbacks for tlsSocketInfo=%p callbacks=%p\n",
2475 mTLSSocketControl
.get(), mCallbacks
.get()));
2480 nsSocketTransport::SetEventSink(nsITransportEventSink
* sink
,
2481 nsIEventTarget
* target
) {
2482 nsCOMPtr
<nsITransportEventSink
> temp
;
2485 net_NewTransportEventSinkProxy(getter_AddRefs(temp
), sink
, target
);
2486 if (NS_FAILED(rv
)) return rv
;
2490 MutexAutoLock
lock(mLock
);
2496 nsSocketTransport::IsAlive(bool* result
) {
2499 nsresult conditionWhileLocked
= NS_OK
;
2500 PRFileDescAutoLock
fd(this, &conditionWhileLocked
);
2501 if (NS_FAILED(conditionWhileLocked
) || !fd
.IsInitialized()) {
2505 // XXX do some idle-time based checks??
2508 int32_t rval
= PR_Recv(fd
, &c
, 1, PR_MSG_PEEK
, 0);
2510 if ((rval
> 0) || (rval
< 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR
)) {
2518 nsSocketTransport::GetHost(nsACString
& host
) {
2519 host
= SocketHost();
2524 nsSocketTransport::GetPort(int32_t* port
) {
2525 *port
= (int32_t)SocketPort();
2530 nsSocketTransport::GetScriptableOriginAttributes(
2531 JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aOriginAttributes
) {
2532 if (NS_WARN_IF(!ToJSValue(aCx
, mOriginAttributes
, aOriginAttributes
))) {
2533 return NS_ERROR_FAILURE
;
2539 nsSocketTransport::SetScriptableOriginAttributes(
2540 JSContext
* aCx
, JS::Handle
<JS::Value
> aOriginAttributes
) {
2541 MutexAutoLock
lock(mLock
);
2542 NS_ENSURE_FALSE(mFD
.IsInitialized(), NS_ERROR_FAILURE
);
2544 OriginAttributes attrs
;
2545 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
2546 return NS_ERROR_INVALID_ARG
;
2549 mOriginAttributes
= attrs
;
2553 nsresult
nsSocketTransport::GetOriginAttributes(
2554 OriginAttributes
* aOriginAttributes
) {
2555 NS_ENSURE_ARG(aOriginAttributes
);
2556 *aOriginAttributes
= mOriginAttributes
;
2560 nsresult
nsSocketTransport::SetOriginAttributes(
2561 const OriginAttributes
& aOriginAttributes
) {
2562 MutexAutoLock
lock(mLock
);
2563 NS_ENSURE_FALSE(mFD
.IsInitialized(), NS_ERROR_FAILURE
);
2565 mOriginAttributes
= aOriginAttributes
;
2570 nsSocketTransport::GetPeerAddr(NetAddr
* addr
) {
2571 // once we are in the connected state, mNetAddr will not change.
2572 // so if we can verify that we are in the connected state, then
2573 // we can freely access mNetAddr from any thread without being
2574 // inside a critical section.
2576 if (!mNetAddrIsSet
) {
2578 ("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
2579 "NOT_AVAILABLE because not yet connected.",
2581 return NS_ERROR_NOT_AVAILABLE
;
2584 memcpy(addr
, &mNetAddr
, sizeof(NetAddr
));
2589 nsSocketTransport::GetSelfAddr(NetAddr
* addr
) {
2590 // once we are in the connected state, mSelfAddr will not change.
2591 // so if we can verify that we are in the connected state, then
2592 // we can freely access mSelfAddr from any thread without being
2593 // inside a critical section.
2595 if (!mSelfAddrIsSet
) {
2597 ("nsSocketTransport::GetSelfAddr [this=%p state=%d] "
2598 "NOT_AVAILABLE because not yet connected.",
2600 return NS_ERROR_NOT_AVAILABLE
;
2603 memcpy(addr
, &mSelfAddr
, sizeof(NetAddr
));
2608 nsSocketTransport::Bind(NetAddr
* aLocalAddr
) {
2609 NS_ENSURE_ARG(aLocalAddr
);
2611 MutexAutoLock
lock(mLock
);
2612 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2614 return NS_ERROR_FAILURE
;
2617 mBindAddr
= MakeUnique
<NetAddr
>();
2618 memcpy(mBindAddr
.get(), aLocalAddr
, sizeof(NetAddr
));
2624 nsSocketTransport::GetScriptablePeerAddr(nsINetAddr
** addr
) {
2628 rv
= GetPeerAddr(&rawAddr
);
2629 if (NS_FAILED(rv
)) return rv
;
2631 RefPtr
<nsNetAddr
> netaddr
= new nsNetAddr(&rawAddr
);
2632 netaddr
.forget(addr
);
2637 nsSocketTransport::GetScriptableSelfAddr(nsINetAddr
** addr
) {
2641 rv
= GetSelfAddr(&rawAddr
);
2642 if (NS_FAILED(rv
)) return rv
;
2644 RefPtr
<nsNetAddr
> netaddr
= new nsNetAddr(&rawAddr
);
2645 netaddr
.forget(addr
);
2651 nsSocketTransport::GetTimeout(uint32_t type
, uint32_t* value
) {
2652 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
2653 MutexAutoLock
lock(mLock
);
2654 *value
= (uint32_t)mTimeouts
[type
];
2659 nsSocketTransport::SetTimeout(uint32_t type
, uint32_t value
) {
2660 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
2662 SOCKET_LOG(("nsSocketTransport::SetTimeout %p type=%u, value=%u", this, type
,
2665 // truncate overly large timeout values.
2667 MutexAutoLock
lock(mLock
);
2668 mTimeouts
[type
] = (uint16_t)std::min
<uint32_t>(value
, UINT16_MAX
);
2670 PostEvent(MSG_TIMEOUT_CHANGED
);
2675 nsSocketTransport::SetReuseAddrPort(bool reuseAddrPort
) {
2676 mReuseAddrPort
= reuseAddrPort
;
2681 nsSocketTransport::SetLinger(bool aPolarity
, int16_t aTimeout
) {
2682 MutexAutoLock
lock(mLock
);
2684 mLingerPolarity
= aPolarity
;
2685 mLingerTimeout
= aTimeout
;
2691 nsSocketTransport::SetQoSBits(uint8_t aQoSBits
) {
2692 // Don't do any checking here of bits. Why? Because as of RFC-4594
2693 // several different Class Selector and Assured Forwarding values
2694 // have been defined, but that isn't to say more won't be added later.
2695 // In that case, any checking would be an impediment to interoperating
2696 // with newer QoS definitions.
2698 mQoSBits
= aQoSBits
;
2703 nsSocketTransport::GetQoSBits(uint8_t* aQoSBits
) {
2704 *aQoSBits
= mQoSBits
;
2709 nsSocketTransport::GetRecvBufferSize(uint32_t* aSize
) {
2710 PRFileDescAutoLock
fd(this);
2711 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2713 nsresult rv
= NS_OK
;
2714 PRSocketOptionData opt
;
2715 opt
.option
= PR_SockOpt_RecvBufferSize
;
2716 if (PR_GetSocketOption(fd
, &opt
) == PR_SUCCESS
) {
2717 *aSize
= opt
.value
.recv_buffer_size
;
2719 rv
= NS_ERROR_FAILURE
;
2726 nsSocketTransport::GetSendBufferSize(uint32_t* aSize
) {
2727 PRFileDescAutoLock
fd(this);
2728 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2730 nsresult rv
= NS_OK
;
2731 PRSocketOptionData opt
;
2732 opt
.option
= PR_SockOpt_SendBufferSize
;
2733 if (PR_GetSocketOption(fd
, &opt
) == PR_SUCCESS
) {
2734 *aSize
= opt
.value
.send_buffer_size
;
2736 rv
= NS_ERROR_FAILURE
;
2743 nsSocketTransport::SetRecvBufferSize(uint32_t aSize
) {
2744 PRFileDescAutoLock
fd(this);
2745 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2747 nsresult rv
= NS_OK
;
2748 PRSocketOptionData opt
;
2749 opt
.option
= PR_SockOpt_RecvBufferSize
;
2750 opt
.value
.recv_buffer_size
= aSize
;
2751 if (PR_SetSocketOption(fd
, &opt
) != PR_SUCCESS
) rv
= NS_ERROR_FAILURE
;
2757 nsSocketTransport::SetSendBufferSize(uint32_t aSize
) {
2758 PRFileDescAutoLock
fd(this);
2759 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2761 nsresult rv
= NS_OK
;
2762 PRSocketOptionData opt
;
2763 opt
.option
= PR_SockOpt_SendBufferSize
;
2764 opt
.value
.send_buffer_size
= aSize
;
2765 if (PR_SetSocketOption(fd
, &opt
) != PR_SUCCESS
) rv
= NS_ERROR_FAILURE
;
2771 nsSocketTransport::OnLookupComplete(nsICancelable
* request
, nsIDNSRecord
* rec
,
2773 SOCKET_LOG(("nsSocketTransport::OnLookupComplete: this=%p status %" PRIx32
2775 this, static_cast<uint32_t>(status
)));
2777 if (NS_SUCCEEDED(status
)) {
2778 mDNSRecord
= do_QueryInterface(rec
);
2779 MOZ_ASSERT(mDNSRecord
);
2782 if (nsCOMPtr
<nsIDNSAddrRecord
> addrRecord
= do_QueryInterface(rec
)) {
2783 addrRecord
->IsTRR(&mResolvedByTRR
);
2784 addrRecord
->GetEffectiveTRRMode(&mEffectiveTRRMode
);
2785 addrRecord
->GetTrrSkipReason(&mTRRSkipReason
);
2788 // flag host lookup complete for the benefit of the ResolveHost method.
2790 nsresult rv
= PostEvent(MSG_DNS_LOOKUP_COMPLETE
, status
, nullptr);
2792 // if posting a message fails, then we should assume that the socket
2793 // transport has been shutdown. this should never happen! if it does
2794 // it means that the socket transport service was shutdown before the
2796 if (NS_FAILED(rv
)) {
2797 NS_WARNING("unable to post DNS lookup complete message");
2803 // nsIInterfaceRequestor
2805 nsSocketTransport::GetInterface(const nsIID
& iid
, void** result
) {
2806 if (iid
.Equals(NS_GET_IID(nsIDNSRecord
)) ||
2807 iid
.Equals(NS_GET_IID(nsIDNSAddrRecord
))) {
2808 return mDNSRecord
? mDNSRecord
->QueryInterface(iid
, result
)
2809 : NS_ERROR_NO_INTERFACE
;
2811 return this->QueryInterface(iid
, result
);
2815 nsSocketTransport::GetInterfaces(nsTArray
<nsIID
>& array
) {
2816 return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport
)(array
);
2820 nsSocketTransport::GetScriptableHelper(nsIXPCScriptable
** _retval
) {
2826 nsSocketTransport::GetContractID(nsACString
& aContractID
) {
2827 aContractID
.SetIsVoid(true);
2832 nsSocketTransport::GetClassDescription(nsACString
& aClassDescription
) {
2833 aClassDescription
.SetIsVoid(true);
2838 nsSocketTransport::GetClassID(nsCID
** aClassID
) {
2839 *aClassID
= nullptr;
2844 nsSocketTransport::GetFlags(uint32_t* aFlags
) {
2845 *aFlags
= nsIClassInfo::THREADSAFE
;
2850 nsSocketTransport::GetClassIDNoAlloc(nsCID
* aClassIDNoAlloc
) {
2851 return NS_ERROR_NOT_AVAILABLE
;
2855 nsSocketTransport::GetConnectionFlags(uint32_t* value
) {
2856 *value
= mConnectionFlags
;
2861 nsSocketTransport::SetConnectionFlags(uint32_t value
) {
2863 ("nsSocketTransport::SetConnectionFlags %p flags=%u", this, value
));
2865 mConnectionFlags
= value
;
2870 nsSocketTransport::SetIsPrivate(bool aIsPrivate
) {
2871 mIsPrivate
= aIsPrivate
;
2876 nsSocketTransport::GetTlsFlags(uint32_t* value
) {
2882 nsSocketTransport::SetTlsFlags(uint32_t value
) {
2887 void nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled
) {
2888 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2890 // The global pref toggles keepalive as a system feature; it only affects
2891 // an individual socket if keepalive has been specifically enabled for it.
2892 // So, ensure keepalive is configured correctly if previously enabled.
2893 if (mKeepaliveEnabled
) {
2894 nsresult rv
= SetKeepaliveEnabledInternal(aEnabled
);
2895 if (NS_WARN_IF(NS_FAILED(rv
))) {
2896 SOCKET_LOG((" SetKeepaliveEnabledInternal [%s] failed rv[0x%" PRIx32
"]",
2897 aEnabled
? "enable" : "disable", static_cast<uint32_t>(rv
)));
2902 nsresult
nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable
) {
2903 MOZ_ASSERT(mKeepaliveIdleTimeS
> 0 && mKeepaliveIdleTimeS
<= kMaxTCPKeepIdle
);
2904 MOZ_ASSERT(mKeepaliveRetryIntervalS
> 0 &&
2905 mKeepaliveRetryIntervalS
<= kMaxTCPKeepIntvl
);
2906 MOZ_ASSERT(mKeepaliveProbeCount
> 0 &&
2907 mKeepaliveProbeCount
<= kMaxTCPKeepCount
);
2909 PRFileDescAutoLock
fd(this);
2910 if (NS_WARN_IF(!fd
.IsInitialized())) {
2911 return NS_ERROR_NOT_INITIALIZED
;
2914 // Only enable if keepalives are globally enabled, but ensure other
2915 // options are set correctly on the fd.
2916 bool enable
= aEnable
&& mSocketTransportService
->IsKeepaliveEnabled();
2918 fd
.SetKeepaliveVals(enable
, mKeepaliveIdleTimeS
, mKeepaliveRetryIntervalS
,
2919 mKeepaliveProbeCount
);
2920 if (NS_WARN_IF(NS_FAILED(rv
))) {
2921 SOCKET_LOG((" SetKeepaliveVals failed rv[0x%" PRIx32
"]",
2922 static_cast<uint32_t>(rv
)));
2925 rv
= fd
.SetKeepaliveEnabled(enable
);
2926 if (NS_WARN_IF(NS_FAILED(rv
))) {
2927 SOCKET_LOG((" SetKeepaliveEnabled failed rv[0x%" PRIx32
"]",
2928 static_cast<uint32_t>(rv
)));
2935 nsSocketTransport::GetKeepaliveEnabled(bool* aResult
) {
2936 MOZ_ASSERT(aResult
);
2938 *aResult
= mKeepaliveEnabled
;
2942 nsresult
nsSocketTransport::EnsureKeepaliveValsAreInitialized() {
2943 nsresult rv
= NS_OK
;
2945 if (mKeepaliveIdleTimeS
== -1) {
2946 rv
= mSocketTransportService
->GetKeepaliveIdleTime(&val
);
2947 if (NS_WARN_IF(NS_FAILED(rv
))) {
2950 mKeepaliveIdleTimeS
= val
;
2952 if (mKeepaliveRetryIntervalS
== -1) {
2953 rv
= mSocketTransportService
->GetKeepaliveRetryInterval(&val
);
2954 if (NS_WARN_IF(NS_FAILED(rv
))) {
2957 mKeepaliveRetryIntervalS
= val
;
2959 if (mKeepaliveProbeCount
== -1) {
2960 rv
= mSocketTransportService
->GetKeepaliveProbeCount(&val
);
2961 if (NS_WARN_IF(NS_FAILED(rv
))) {
2964 mKeepaliveProbeCount
= val
;
2970 nsSocketTransport::SetKeepaliveEnabled(bool aEnable
) {
2971 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
2972 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2974 if (aEnable
== mKeepaliveEnabled
) {
2975 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.", this,
2976 aEnable
? "enabled" : "disabled"));
2980 nsresult rv
= NS_OK
;
2982 rv
= EnsureKeepaliveValsAreInitialized();
2983 if (NS_WARN_IF(NS_FAILED(rv
))) {
2985 (" SetKeepaliveEnabled [%p] "
2986 "error [0x%" PRIx32
"] initializing keepalive vals",
2987 this, static_cast<uint32_t>(rv
)));
2992 ("nsSocketTransport::SetKeepaliveEnabled [%p] "
2993 "%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
2995 this, aEnable
? "enabled" : "disabled", mKeepaliveIdleTimeS
,
2996 mKeepaliveRetryIntervalS
, mKeepaliveProbeCount
,
2997 mSocketTransportService
->IsKeepaliveEnabled() ? "enabled" : "disabled"));
2999 // Set mKeepaliveEnabled here so that state is maintained; it is possible
3000 // that we're in between fds, e.g. the 1st IP address failed, so we're about
3001 // to retry on a 2nd from the DNS record.
3002 mKeepaliveEnabled
= aEnable
;
3004 rv
= SetKeepaliveEnabledInternal(aEnable
);
3005 if (NS_WARN_IF(NS_FAILED(rv
))) {
3006 SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%" PRIx32
"]",
3007 static_cast<uint32_t>(rv
)));
3012 #else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
3013 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
3014 return NS_ERROR_NOT_IMPLEMENTED
;
3019 nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime
, int32_t aRetryInterval
) {
3020 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
3021 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3022 if (NS_WARN_IF(aIdleTime
<= 0 || kMaxTCPKeepIdle
< aIdleTime
)) {
3023 return NS_ERROR_INVALID_ARG
;
3025 if (NS_WARN_IF(aRetryInterval
<= 0 || kMaxTCPKeepIntvl
< aRetryInterval
)) {
3026 return NS_ERROR_INVALID_ARG
;
3029 if (aIdleTime
== mKeepaliveIdleTimeS
&&
3030 aRetryInterval
== mKeepaliveRetryIntervalS
) {
3032 ("nsSocketTransport::SetKeepaliveVals [%p] idle time "
3033 "already %ds and retry interval already %ds.",
3034 this, mKeepaliveIdleTimeS
, mKeepaliveRetryIntervalS
));
3037 mKeepaliveIdleTimeS
= aIdleTime
;
3038 mKeepaliveRetryIntervalS
= aRetryInterval
;
3040 nsresult rv
= NS_OK
;
3041 if (mKeepaliveProbeCount
== -1) {
3043 nsresult rv
= mSocketTransportService
->GetKeepaliveProbeCount(&val
);
3044 if (NS_WARN_IF(NS_FAILED(rv
))) {
3047 mKeepaliveProbeCount
= val
;
3051 ("nsSocketTransport::SetKeepaliveVals [%p] "
3052 "keepalive %s, idle time[%ds] retry interval[%ds] "
3054 this, mKeepaliveEnabled
? "enabled" : "disabled", mKeepaliveIdleTimeS
,
3055 mKeepaliveRetryIntervalS
, mKeepaliveProbeCount
));
3057 PRFileDescAutoLock
fd(this);
3058 if (NS_WARN_IF(!fd
.IsInitialized())) {
3059 return NS_ERROR_NULL_POINTER
;
3062 rv
= fd
.SetKeepaliveVals(mKeepaliveEnabled
, mKeepaliveIdleTimeS
,
3063 mKeepaliveRetryIntervalS
, mKeepaliveProbeCount
);
3064 if (NS_WARN_IF(NS_FAILED(rv
))) {
3069 SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
3070 return NS_ERROR_NOT_IMPLEMENTED
;
3074 #ifdef ENABLE_SOCKET_TRACING
3080 static void DumpBytesToFile(const char* path
, const char* header
,
3081 const char* buf
, int32_t n
) {
3082 FILE* fp
= fopen(path
, "a");
3084 fprintf(fp
, "\n%s [%d bytes]\n", header
, n
);
3086 const unsigned char* p
;
3088 p
= (const unsigned char*)buf
;
3090 int32_t i
, row_max
= std::min(16, n
);
3092 for (i
= 0; i
< row_max
; ++i
) fprintf(fp
, "%02x ", *p
++);
3093 for (i
= row_max
; i
< 16; ++i
) fprintf(fp
, " ");
3095 p
= (const unsigned char*)buf
;
3096 for (i
= 0; i
< row_max
; ++i
, ++p
) {
3098 fprintf(fp
, "%c", *p
);
3112 void nsSocketTransport::TraceInBuf(const char* buf
, int32_t n
) {
3113 char* val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
3114 if (!val
|| !*val
) return;
3116 nsAutoCString header
;
3117 header
.AssignLiteral("Reading from: ");
3118 header
.Append(mHost
);
3120 header
.AppendInt(mPort
);
3122 DumpBytesToFile(val
, header
.get(), buf
, n
);
3125 void nsSocketTransport::TraceOutBuf(const char* buf
, int32_t n
) {
3126 char* val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
3127 if (!val
|| !*val
) return;
3129 nsAutoCString header
;
3130 header
.AssignLiteral("Writing to: ");
3131 header
.Append(mHost
);
3133 header
.AppendInt(mPort
);
3135 DumpBytesToFile(val
, header
.get(), buf
, n
);
3140 static void LogNSPRError(const char* aPrefix
, const void* aObjPtr
) {
3142 PRErrorCode errCode
= PR_GetError();
3143 int errLen
= PR_GetErrorTextLength();
3144 nsAutoCString errStr
;
3146 errStr
.SetLength(errLen
);
3147 PR_GetErrorText(errStr
.BeginWriting());
3150 nsPrintfCString("%s [%p] NSPR error[0x%x] %s.",
3151 aPrefix
? aPrefix
: "nsSocketTransport", aObjPtr
, errCode
,
3152 errLen
> 0 ? errStr
.BeginReading() : "<no error text>")
3157 nsresult
nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(
3159 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3160 MOZ_ASSERT(!(aEnable
&& !gSocketTransportService
->IsKeepaliveEnabled()),
3161 "Cannot enable keepalive if global pref is disabled!");
3162 if (aEnable
&& !gSocketTransportService
->IsKeepaliveEnabled()) {
3163 return NS_ERROR_ILLEGAL_VALUE
;
3166 PRSocketOptionData opt
;
3168 opt
.option
= PR_SockOpt_Keepalive
;
3169 opt
.value
.keep_alive
= aEnable
;
3170 PRStatus status
= PR_SetSocketOption(mFd
, &opt
);
3171 if (NS_WARN_IF(status
!= PR_SUCCESS
)) {
3172 LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
3174 return ErrorAccordingToNSPR(PR_GetError());
3179 static void LogOSError(const char* aPrefix
, const void* aObjPtr
) {
3181 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3184 DWORD errCode
= WSAGetLastError();
3186 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
|
3187 FORMAT_MESSAGE_IGNORE_INSERTS
,
3188 NULL
, errCode
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
3189 (LPSTR
)&errMessage
, 0, NULL
);
3190 NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%lx] %s",
3191 aPrefix
? aPrefix
: "nsSocketTransport", aObjPtr
,
3193 errMessage
? errMessage
: "<no error text>")
3195 LocalFree(errMessage
);
3197 int errCode
= errno
;
3198 char* errMessage
= strerror(errno
);
3199 NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%x] %s",
3200 aPrefix
? aPrefix
: "nsSocketTransport", aObjPtr
,
3202 errMessage
? errMessage
: "<no error text>")
3208 /* XXX PR_SetSockOpt does not support setting keepalive values, so native
3209 * handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
3210 * file. Requires inclusion of NSPR private/pprio.h, and platform headers.
3213 nsresult
nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(
3214 bool aEnabled
, int aIdleTime
, int aRetryInterval
, int aProbeCount
) {
3215 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
3216 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3217 if (NS_WARN_IF(aIdleTime
<= 0 || kMaxTCPKeepIdle
< aIdleTime
)) {
3218 return NS_ERROR_INVALID_ARG
;
3220 if (NS_WARN_IF(aRetryInterval
<= 0 || kMaxTCPKeepIntvl
< aRetryInterval
)) {
3221 return NS_ERROR_INVALID_ARG
;
3223 if (NS_WARN_IF(aProbeCount
<= 0 || kMaxTCPKeepCount
< aProbeCount
)) {
3224 return NS_ERROR_INVALID_ARG
;
3227 PROsfd sock
= PR_FileDesc2NativeHandle(mFd
);
3228 if (NS_WARN_IF(sock
== -1)) {
3229 LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
3231 return ErrorAccordingToNSPR(PR_GetError());
3236 // Windows allows idle time and retry interval to be set; NOT ping count.
3237 struct tcp_keepalive keepalive_vals
= {(u_long
)aEnabled
,
3238 // Windows uses msec.
3239 (u_long
)(aIdleTime
* 1000UL),
3240 (u_long
)(aRetryInterval
* 1000UL)};
3241 DWORD bytes_returned
;
3243 WSAIoctl(sock
, SIO_KEEPALIVE_VALS
, &keepalive_vals
,
3244 sizeof(keepalive_vals
), NULL
, 0, &bytes_returned
, NULL
, NULL
);
3245 if (NS_WARN_IF(err
)) {
3246 LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport
);
3247 return NS_ERROR_UNEXPECTED
;
3251 #elif defined(XP_DARWIN)
3252 // Darwin uses sec; only supports idle time being set.
3253 int err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPALIVE
, &aIdleTime
,
3255 if (NS_WARN_IF(err
)) {
3256 LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
3258 return NS_ERROR_UNEXPECTED
;
3262 #elif defined(XP_UNIX)
3263 // Not all *nix OSes support the following setsockopt() options
3264 // ... but we assume they are supported in the Android kernel;
3265 // build errors will tell us if they are not.
3266 # if defined(ANDROID) || defined(TCP_KEEPIDLE)
3267 // Idle time until first keepalive probe; interval between ack'd probes;
3269 int err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPIDLE
, &aIdleTime
,
3271 if (NS_WARN_IF(err
)) {
3272 LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
3274 return NS_ERROR_UNEXPECTED
;
3278 # if defined(ANDROID) || defined(TCP_KEEPINTVL)
3279 // Interval between unack'd keepalive probes; seconds.
3280 err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPINTVL
, &aRetryInterval
,
3281 sizeof(aRetryInterval
));
3282 if (NS_WARN_IF(err
)) {
3283 LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
3285 return NS_ERROR_UNEXPECTED
;
3289 # if defined(ANDROID) || defined(TCP_KEEPCNT)
3290 // Number of unack'd keepalive probes before connection times out.
3291 err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPCNT
, &aProbeCount
,
3292 sizeof(aProbeCount
));
3293 if (NS_WARN_IF(err
)) {
3294 LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
3296 return NS_ERROR_UNEXPECTED
;
3303 "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
3304 "called on unsupported platform!");
3305 return NS_ERROR_UNEXPECTED
;
3309 void nsSocketTransport::CloseSocket(PRFileDesc
* aFd
, bool aTelemetryEnabled
) {
3311 AttachShutdownLayer(aFd
);
3314 // We use PRIntervalTime here because we need
3315 // nsIOService::LastOfflineStateChange time and
3316 // nsIOService::LastConectivityChange time to be atomic.
3317 PRIntervalTime closeStarted
;
3318 if (aTelemetryEnabled
) {
3319 closeStarted
= PR_IntervalNow();
3324 if (aTelemetryEnabled
) {
3325 SendPRBlockingTelemetry(
3326 closeStarted
, Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL
,
3327 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN
,
3328 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
3329 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE
,
3330 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_OFFLINE
);
3334 void nsSocketTransport::SendPRBlockingTelemetry(
3335 PRIntervalTime aStart
, Telemetry::HistogramID aIDNormal
,
3336 Telemetry::HistogramID aIDShutdown
,
3337 Telemetry::HistogramID aIDConnectivityChange
,
3338 Telemetry::HistogramID aIDLinkChange
, Telemetry::HistogramID aIDOffline
) {
3339 PRIntervalTime now
= PR_IntervalNow();
3340 if (gIOService
->IsNetTearingDown()) {
3341 Telemetry::Accumulate(aIDShutdown
, PR_IntervalToMilliseconds(now
- aStart
));
3343 } else if (PR_IntervalToSeconds(now
- gIOService
->LastConnectivityChange()) <
3345 Telemetry::Accumulate(aIDConnectivityChange
,
3346 PR_IntervalToMilliseconds(now
- aStart
));
3347 } else if (PR_IntervalToSeconds(now
- gIOService
->LastNetworkLinkChange()) <
3349 Telemetry::Accumulate(aIDLinkChange
,
3350 PR_IntervalToMilliseconds(now
- aStart
));
3352 } else if (PR_IntervalToSeconds(now
- gIOService
->LastOfflineStateChange()) <
3354 Telemetry::Accumulate(aIDOffline
, PR_IntervalToMilliseconds(now
- aStart
));
3356 Telemetry::Accumulate(aIDNormal
, PR_IntervalToMilliseconds(now
- aStart
));
3361 nsSocketTransport::GetResetIPFamilyPreference(bool* aReset
) {
3362 *aReset
= mResetFamilyPreference
;
3367 nsSocketTransport::GetEchConfigUsed(bool* aEchConfigUsed
) {
3368 *aEchConfigUsed
= mEchConfigUsed
;
3373 nsSocketTransport::SetEchConfig(const nsACString
& aEchConfig
) {
3374 mEchConfig
= aEchConfig
;
3379 nsSocketTransport::ResolvedByTRR(bool* aResolvedByTRR
) {
3380 *aResolvedByTRR
= mResolvedByTRR
;
3384 NS_IMETHODIMP
nsSocketTransport::GetEffectiveTRRMode(
3385 nsIRequest::TRRMode
* aEffectiveTRRMode
) {
3386 *aEffectiveTRRMode
= mEffectiveTRRMode
;
3390 NS_IMETHODIMP
nsSocketTransport::GetTrrSkipReason(
3391 nsITRRSkipReason::value
* aSkipReason
) {
3392 *aSkipReason
= mTRRSkipReason
;
3397 nsSocketTransport::GetRetryDnsIfPossible(bool* aRetryDns
) {
3398 *aRetryDns
= mRetryDnsIfPossible
;
3403 nsSocketTransport::GetStatus(nsresult
* aStatus
) {
3404 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3406 *aStatus
= mCondition
;
3411 } // namespace mozilla