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"
23 #include "nsICancelable.h"
24 #include "nsIClassInfoImpl.h"
25 #include "nsIDNSByTypeRecord.h"
26 #include "nsIDNSRecord.h"
27 #include "nsIDNSService.h"
28 #include "nsIOService.h"
30 #include "nsISocketProvider.h"
31 #include "nsITLSSocketControl.h"
32 #include "nsNetAddr.h"
34 #include "nsNetSegmentUtils.h"
35 #include "nsNetUtil.h"
36 #include "nsPrintfCString.h"
37 #include "nsProxyInfo.h"
38 #include "nsSocketProviderService.h"
39 #include "nsStreamUtils.h"
40 #include "nsThreadUtils.h"
41 #include "nsTransportUtils.h"
42 #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 : Runnable("net::nsSocketEvent"),
88 mTransport(transport
),
93 NS_IMETHOD
Run() override
{
94 mTransport
->OnSocketEvent(mType
, mStatus
, mParam
);
99 RefPtr
<nsSocketTransport
> mTransport
;
103 nsCOMPtr
<nsISupports
> mParam
;
106 //-----------------------------------------------------------------------------
108 // #define TEST_CONNECT_ERRORS
109 #ifdef TEST_CONNECT_ERRORS
111 static PRErrorCode
RandomizeConnectError(PRErrorCode code
) {
113 // To test out these errors, load http://www.yahoo.com/. It should load
114 // correctly despite the random occurrence of these errors.
117 if (n
> RAND_MAX
/ 2) {
119 PRErrorCode err_code
;
120 const char* err_name
;
123 // These errors should be recoverable provided there is another
124 // IP address in mDNSRecord.
126 {PR_CONNECT_REFUSED_ERROR
, "PR_CONNECT_REFUSED_ERROR"},
127 {PR_CONNECT_TIMEOUT_ERROR
, "PR_CONNECT_TIMEOUT_ERROR"},
129 // This error will cause this socket transport to error out;
130 // however, if the consumer is HTTP, then the HTTP transaction
131 // should be restarted when this error occurs.
133 {PR_CONNECT_RESET_ERROR
, "PR_CONNECT_RESET_ERROR"},
135 n
= n
% (sizeof(errors
) / sizeof(errors
[0]));
136 code
= errors
[n
].err_code
;
137 SOCKET_LOG(("simulating NSPR error %d [%s]\n", code
, errors
[n
].err_name
));
143 //-----------------------------------------------------------------------------
145 nsresult
ErrorAccordingToNSPR(PRErrorCode errorCode
) {
146 nsresult rv
= NS_ERROR_FAILURE
;
148 case PR_WOULD_BLOCK_ERROR
:
149 rv
= NS_BASE_STREAM_WOULD_BLOCK
;
151 case PR_CONNECT_ABORTED_ERROR
:
152 case PR_CONNECT_RESET_ERROR
:
153 rv
= NS_ERROR_NET_RESET
;
155 case PR_END_OF_FILE_ERROR
: // XXX document this correlation
156 rv
= NS_ERROR_NET_INTERRUPT
;
158 case PR_CONNECT_REFUSED_ERROR
:
159 // We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
160 // could get better diagnostics by adding distinct XPCOM error codes for
161 // each of these, but there are a lot of places in Gecko that check
162 // specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
164 case PR_NETWORK_UNREACHABLE_ERROR
:
165 case PR_HOST_UNREACHABLE_ERROR
:
166 case PR_ADDRESS_NOT_AVAILABLE_ERROR
:
167 // Treat EACCES as a soft error since (at least on Linux) connect() returns
168 // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
169 case PR_NO_ACCESS_RIGHTS_ERROR
:
170 rv
= NS_ERROR_CONNECTION_REFUSED
;
172 case PR_ADDRESS_NOT_SUPPORTED_ERROR
:
173 rv
= NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
175 case PR_IO_TIMEOUT_ERROR
:
176 case PR_CONNECT_TIMEOUT_ERROR
:
177 rv
= NS_ERROR_NET_TIMEOUT
;
179 case PR_OUT_OF_MEMORY_ERROR
:
180 // These really indicate that the descriptor table filled up, or that the
181 // kernel ran out of network buffers - but nobody really cares which part of
182 // the system ran out of memory.
183 case PR_PROC_DESC_TABLE_FULL_ERROR
:
184 case PR_SYS_DESC_TABLE_FULL_ERROR
:
185 case PR_INSUFFICIENT_RESOURCES_ERROR
:
186 rv
= NS_ERROR_OUT_OF_MEMORY
;
188 case PR_ADDRESS_IN_USE_ERROR
:
189 rv
= NS_ERROR_SOCKET_ADDRESS_IN_USE
;
191 // These filename-related errors can arise when using Unix-domain sockets.
192 case PR_FILE_NOT_FOUND_ERROR
:
193 rv
= NS_ERROR_FILE_NOT_FOUND
;
195 case PR_IS_DIRECTORY_ERROR
:
196 rv
= NS_ERROR_FILE_IS_DIRECTORY
;
199 rv
= NS_ERROR_FILE_UNRESOLVABLE_SYMLINK
;
201 case PR_NAME_TOO_LONG_ERROR
:
202 rv
= NS_ERROR_FILE_NAME_TOO_LONG
;
204 case PR_NO_DEVICE_SPACE_ERROR
:
205 rv
= NS_ERROR_FILE_NO_DEVICE_SPACE
;
207 case PR_NOT_DIRECTORY_ERROR
:
208 rv
= NS_ERROR_FILE_NOT_DIRECTORY
;
210 case PR_READ_ONLY_FILESYSTEM_ERROR
:
211 rv
= NS_ERROR_FILE_READ_ONLY
;
213 case PR_BAD_ADDRESS_ERROR
:
214 rv
= NS_ERROR_UNKNOWN_HOST
;
217 if (psm::IsNSSErrorCode(errorCode
)) {
218 rv
= psm::GetXPCOMFromNSSError(errorCode
);
222 // NSPR's socket code can return these, but they're not worth breaking out
223 // into their own error codes, distinct from NS_ERROR_FAILURE:
225 // PR_BAD_DESCRIPTOR_ERROR
226 // PR_INVALID_ARGUMENT_ERROR
227 // PR_NOT_SOCKET_ERROR
228 // PR_NOT_TCP_SOCKET_ERROR
229 // These would indicate a bug internal to the component.
231 // PR_PROTOCOL_NOT_SUPPORTED_ERROR
232 // This means that we can't use the given "protocol" (like
233 // IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
234 // above, this indicates an internal bug.
236 // PR_IS_CONNECTED_ERROR
237 // This indicates that we've applied a system call like 'bind' or
238 // 'connect' to a socket that is already connected. The socket
239 // components manage each file descriptor's state, and in some cases
240 // handle this error result internally. We shouldn't be returning
241 // this to our callers.
244 // This is so vague that NS_ERROR_FAILURE is just as good.
246 SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%" PRIx32
"]\n", errorCode
,
247 static_cast<uint32_t>(rv
)));
251 //-----------------------------------------------------------------------------
252 // socket input stream impl
253 //-----------------------------------------------------------------------------
255 nsSocketInputStream::nsSocketInputStream(nsSocketTransport
* trans
)
256 : mTransport(trans
) {}
258 // called on the socket transport thread...
260 // condition : failure code if socket has been closed
262 void nsSocketInputStream::OnSocketReady(nsresult condition
) {
263 SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%" PRIx32
"]\n",
264 this, static_cast<uint32_t>(condition
)));
266 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
268 nsCOMPtr
<nsIInputStreamCallback
> callback
;
270 MutexAutoLock
lock(mTransport
->mLock
);
272 // update condition, but be careful not to erase an already
273 // existing error condition.
274 if (NS_SUCCEEDED(mCondition
)) mCondition
= condition
;
276 // ignore event if only waiting for closure and not closed.
277 if (NS_FAILED(mCondition
) || !(mCallbackFlags
& WAIT_CLOSURE_ONLY
)) {
278 callback
= std::move(mCallback
);
283 if (callback
) callback
->OnInputStreamReady(this);
286 NS_IMPL_QUERY_INTERFACE(nsSocketInputStream
, nsIInputStream
,
289 NS_IMETHODIMP_(MozExternalRefCountType
)
290 nsSocketInputStream::AddRef() {
292 return mTransport
->AddRef();
295 NS_IMETHODIMP_(MozExternalRefCountType
)
296 nsSocketInputStream::Release() {
297 if (--mReaderRefCnt
== 0) Close();
298 return mTransport
->Release();
302 nsSocketInputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED
); }
305 nsSocketInputStream::Available(uint64_t* avail
) {
306 SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
312 MutexAutoLock
lock(mTransport
->mLock
);
314 if (NS_FAILED(mCondition
)) return mCondition
;
316 fd
= mTransport
->GetFD_Locked();
317 if (!fd
) return NS_OK
;
320 // cannot hold lock while calling NSPR. (worried about the fact that PSM
321 // synchronously proxies notifications over to the UI thread, which could
322 // mistakenly try to re-enter this code.)
323 int32_t n
= PR_Available(fd
);
325 // PSM does not implement PR_Available() so do a best approximation of it
327 if ((n
== -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR
)) {
330 n
= PR_Recv(fd
, &c
, 1, PR_MSG_PEEK
, 0);
332 ("nsSocketInputStream::Available [this=%p] "
333 "using PEEK backup n=%d]\n",
339 MutexAutoLock
lock(mTransport
->mLock
);
341 mTransport
->ReleaseFD_Locked(fd
);
346 PRErrorCode code
= PR_GetError();
347 if (code
== PR_WOULD_BLOCK_ERROR
) return NS_OK
;
348 mCondition
= ErrorAccordingToNSPR(code
);
352 if (NS_FAILED(rv
)) mTransport
->OnInputClosed(rv
);
357 nsSocketInputStream::Read(char* buf
, uint32_t count
, uint32_t* countRead
) {
358 SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count
));
362 PRFileDesc
* fd
= nullptr;
364 MutexAutoLock
lock(mTransport
->mLock
);
366 if (NS_FAILED(mCondition
)) {
367 return (mCondition
== NS_BASE_STREAM_CLOSED
) ? NS_OK
: mCondition
;
370 fd
= mTransport
->GetFD_Locked();
371 if (!fd
) return NS_BASE_STREAM_WOULD_BLOCK
;
374 SOCKET_LOG((" calling PR_Read [count=%u]\n", count
));
376 // cannot hold lock while calling NSPR. (worried about the fact that PSM
377 // synchronously proxies notifications over to the UI thread, which could
378 // mistakenly try to re-enter this code.)
379 int32_t n
= PR_Read(fd
, buf
, count
);
381 SOCKET_LOG((" PR_Read returned [n=%d]\n", n
));
385 MutexAutoLock
lock(mTransport
->mLock
);
387 #ifdef ENABLE_SOCKET_TRACING
388 if (n
> 0) mTransport
->TraceInBuf(buf
, n
);
391 mTransport
->ReleaseFD_Locked(fd
);
394 mByteCount
+= (*countRead
= n
);
396 PRErrorCode code
= PR_GetError();
397 if (code
== PR_WOULD_BLOCK_ERROR
) return NS_BASE_STREAM_WOULD_BLOCK
;
398 mCondition
= ErrorAccordingToNSPR(code
);
402 if (NS_FAILED(rv
)) mTransport
->OnInputClosed(rv
);
404 // only send this notification if we have indeed read some data.
405 // see bug 196827 for an example of why this is important.
406 if (n
> 0) mTransport
->SendStatus(NS_NET_STATUS_RECEIVING_FROM
);
411 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer
, void* closure
,
412 uint32_t count
, uint32_t* countRead
) {
413 // socket stream is unbuffered
414 return NS_ERROR_NOT_IMPLEMENTED
;
418 nsSocketInputStream::IsNonBlocking(bool* nonblocking
) {
424 nsSocketInputStream::CloseWithStatus(nsresult reason
) {
425 SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%" PRIx32
427 this, static_cast<uint32_t>(reason
)));
429 // may be called from any thread
433 MutexAutoLock
lock(mTransport
->mLock
);
435 if (NS_SUCCEEDED(mCondition
)) {
436 rv
= mCondition
= reason
;
441 if (NS_FAILED(rv
)) mTransport
->OnInputClosed(rv
);
446 nsSocketInputStream::AsyncWait(nsIInputStreamCallback
* callback
, uint32_t flags
,
447 uint32_t amount
, nsIEventTarget
* target
) {
448 SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
450 bool hasError
= false;
452 MutexAutoLock
lock(mTransport
->mLock
);
454 if (callback
&& target
) {
458 mCallback
= NS_NewInputStreamReadyEvent("nsSocketInputStream::AsyncWait",
461 mCallback
= callback
;
463 mCallbackFlags
= flags
;
465 hasError
= NS_FAILED(mCondition
);
466 } // unlock mTransport->mLock
469 // OnSocketEvent will call OnInputStreamReady with an error code after
470 // going through the event loop. We do this because most socket callers
471 // do not expect AsyncWait() to synchronously execute the OnInputStreamReady
473 mTransport
->PostEvent(nsSocketTransport::MSG_INPUT_PENDING
);
475 mTransport
->OnInputPending();
481 //-----------------------------------------------------------------------------
482 // socket output stream impl
483 //-----------------------------------------------------------------------------
485 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport
* trans
)
486 : mTransport(trans
) {}
488 // called on the socket transport thread...
490 // condition : failure code if socket has been closed
492 void nsSocketOutputStream::OnSocketReady(nsresult condition
) {
493 SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%" PRIx32
495 this, static_cast<uint32_t>(condition
)));
497 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
499 nsCOMPtr
<nsIOutputStreamCallback
> callback
;
501 MutexAutoLock
lock(mTransport
->mLock
);
503 // update condition, but be careful not to erase an already
504 // existing error condition.
505 if (NS_SUCCEEDED(mCondition
)) mCondition
= condition
;
507 // ignore event if only waiting for closure and not closed.
508 if (NS_FAILED(mCondition
) || !(mCallbackFlags
& WAIT_CLOSURE_ONLY
)) {
509 callback
= std::move(mCallback
);
514 if (callback
) callback
->OnOutputStreamReady(this);
517 NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream
, nsIOutputStream
,
518 nsIAsyncOutputStream
)
520 NS_IMETHODIMP_(MozExternalRefCountType
)
521 nsSocketOutputStream::AddRef() {
523 return mTransport
->AddRef();
526 NS_IMETHODIMP_(MozExternalRefCountType
)
527 nsSocketOutputStream::Release() {
528 if (--mWriterRefCnt
== 0) Close();
529 return mTransport
->Release();
533 nsSocketOutputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED
); }
536 nsSocketOutputStream::Flush() { return NS_OK
; }
539 nsSocketOutputStream::Write(const char* buf
, uint32_t count
,
540 uint32_t* countWritten
) {
541 SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n", this, count
));
545 // A write of 0 bytes can be used to force the initial SSL handshake, so do
548 PRFileDesc
* fd
= nullptr;
550 MutexAutoLock
lock(mTransport
->mLock
);
552 if (NS_FAILED(mCondition
)) return mCondition
;
554 fd
= mTransport
->GetFD_Locked();
555 if (!fd
) return NS_BASE_STREAM_WOULD_BLOCK
;
558 SOCKET_LOG((" calling PR_Write [count=%u]\n", count
));
560 // cannot hold lock while calling NSPR. (worried about the fact that PSM
561 // synchronously proxies notifications over to the UI thread, which could
562 // mistakenly try to re-enter this code.)
563 int32_t n
= PR_Write(fd
, buf
, count
);
565 SOCKET_LOG((" PR_Write returned [n=%d]\n", n
));
569 MutexAutoLock
lock(mTransport
->mLock
);
571 #ifdef ENABLE_SOCKET_TRACING
572 if (n
> 0) mTransport
->TraceOutBuf(buf
, n
);
575 mTransport
->ReleaseFD_Locked(fd
);
578 mByteCount
+= (*countWritten
= n
);
580 PRErrorCode code
= PR_GetError();
581 if (code
== PR_WOULD_BLOCK_ERROR
) return NS_BASE_STREAM_WOULD_BLOCK
;
582 mCondition
= ErrorAccordingToNSPR(code
);
586 if (NS_FAILED(rv
)) mTransport
->OnOutputClosed(rv
);
588 // only send this notification if we have indeed written some data.
589 // see bug 196827 for an example of why this is important.
591 mTransport
->SendStatus(NS_NET_STATUS_SENDING_TO
);
598 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader
, void* closure
,
599 uint32_t count
, uint32_t* countRead
) {
600 // socket stream is unbuffered
601 return NS_ERROR_NOT_IMPLEMENTED
;
604 nsresult
nsSocketOutputStream::WriteFromSegments(
605 nsIInputStream
* input
, void* closure
, const char* fromSegment
,
606 uint32_t offset
, uint32_t count
, uint32_t* countRead
) {
607 nsSocketOutputStream
* self
= (nsSocketOutputStream
*)closure
;
608 return self
->Write(fromSegment
, count
, countRead
);
612 nsSocketOutputStream::WriteFrom(nsIInputStream
* stream
, uint32_t count
,
613 uint32_t* countRead
) {
614 return stream
->ReadSegments(WriteFromSegments
, this, count
, countRead
);
618 nsSocketOutputStream::IsNonBlocking(bool* nonblocking
) {
624 nsSocketOutputStream::CloseWithStatus(nsresult reason
) {
625 SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%" PRIx32
627 this, static_cast<uint32_t>(reason
)));
629 // may be called from any thread
633 MutexAutoLock
lock(mTransport
->mLock
);
635 if (NS_SUCCEEDED(mCondition
)) {
636 rv
= mCondition
= reason
;
641 if (NS_FAILED(rv
)) mTransport
->OnOutputClosed(rv
);
646 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback
* callback
,
647 uint32_t flags
, uint32_t amount
,
648 nsIEventTarget
* target
) {
649 SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n", this));
652 MutexAutoLock
lock(mTransport
->mLock
);
654 if (callback
&& target
) {
658 mCallback
= NS_NewOutputStreamReadyEvent(callback
, target
);
660 mCallback
= callback
;
663 mCallbackFlags
= flags
;
665 mTransport
->OnOutputPending();
669 //-----------------------------------------------------------------------------
670 // socket transport impl
671 //-----------------------------------------------------------------------------
673 nsSocketTransport::nsSocketTransport()
675 mSocketTransportService(gSocketTransportService
),
678 SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
680 mTimeouts
[TIMEOUT_CONNECT
] = UINT16_MAX
; // no timeout
681 mTimeouts
[TIMEOUT_READ_WRITE
] = UINT16_MAX
; // no timeout
684 nsSocketTransport::~nsSocketTransport() {
685 SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
688 nsresult
nsSocketTransport::Init(const nsTArray
<nsCString
>& types
,
689 const nsACString
& host
, uint16_t port
,
690 const nsACString
& hostRoute
,
692 nsIProxyInfo
* givenProxyInfo
,
693 nsIDNSRecord
* dnsRecord
) {
694 nsCOMPtr
<nsProxyInfo
> proxyInfo
;
695 if (givenProxyInfo
) {
696 proxyInfo
= do_QueryInterface(givenProxyInfo
);
697 NS_ENSURE_ARG(proxyInfo
);
701 mExternalDNSResolution
= true;
702 mDNSRecord
= do_QueryInterface(dnsRecord
);
705 // init socket type info
709 if (!hostRoute
.IsEmpty()) {
717 // A subtle check we don't enter this method more than once for the socket
718 // transport lifetime. Disable on TSan builds to prevent race checking, we
719 // don't want an atomic here for perf reasons!
721 MOZ_ASSERT(!mPortRemappingApplied
);
725 mHttpsProxy
= proxyInfo
->IsHTTPS();
728 const char* proxyType
= nullptr;
729 mProxyInfo
= proxyInfo
;
731 mProxyPort
= proxyInfo
->Port();
732 mProxyHost
= proxyInfo
->Host();
733 // grab proxy type (looking for "socks" for example)
734 proxyType
= proxyInfo
->Type();
735 if (proxyType
&& (proxyInfo
->IsHTTP() || proxyInfo
->IsHTTPS() ||
736 proxyInfo
->IsDirect() || !strcmp(proxyType
, "unknown"))) {
742 ("nsSocketTransport::Init [this=%p host=%s:%hu origin=%s:%d "
744 this, mHost
.get(), mPort
, mOriginHost
.get(), mOriginPort
,
745 mProxyHost
.get(), mProxyPort
));
747 // include proxy type as a socket type if proxy type is not "http"
748 uint32_t typeCount
= types
.Length() + (proxyType
!= nullptr);
749 if (!typeCount
) return NS_OK
;
751 // if we have socket types, then the socket provider service had
754 nsCOMPtr
<nsISocketProviderService
> spserv
=
755 nsSocketProviderService::GetOrCreate();
757 if (!mTypes
.SetCapacity(typeCount
, fallible
)) {
758 return NS_ERROR_OUT_OF_MEMORY
;
761 // now verify that each socket type has a registered socket provider.
762 for (uint32_t i
= 0, type
= 0; i
< typeCount
; ++i
) {
763 // store socket types
764 if (i
== 0 && proxyType
) {
765 mTypes
.AppendElement(proxyType
);
767 mTypes
.AppendElement(types
[type
++]);
770 nsCOMPtr
<nsISocketProvider
> provider
;
771 rv
= spserv
->GetSocketProvider(mTypes
[i
].get(), getter_AddRefs(provider
));
773 NS_WARNING("no registered socket provider");
777 // note if socket type corresponds to a transparent proxy
778 // XXX don't hardcode SOCKS here (use proxy info's flags instead).
779 if (mTypes
[i
].EqualsLiteral("socks") || mTypes
[i
].EqualsLiteral("socks4")) {
780 mProxyTransparent
= true;
782 if (proxyInfo
->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
) {
783 // we want the SOCKS layer to send the hostname
784 // and port to the proxy and let it do the DNS.
785 mProxyTransparentResolvesHost
= true;
794 nsresult
nsSocketTransport::InitWithFilename(const char* filename
) {
795 return InitWithName(filename
, strlen(filename
));
798 nsresult
nsSocketTransport::InitWithName(const char* name
, size_t length
) {
799 if (length
> sizeof(mNetAddr
.local
.path
) - 1) {
800 return NS_ERROR_FILE_NAME_TOO_LONG
;
803 if (!name
[0] && length
> 1) {
804 // name is abstract address name that is supported on Linux only
805 # if defined(XP_LINUX)
806 mHost
.Assign(name
+ 1, length
- 1);
808 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
811 // The name isn't abstract socket address. So this is Unix domain
812 // socket that has file path.
813 mHost
.Assign(name
, length
);
817 mNetAddr
.local
.family
= AF_LOCAL
;
818 memcpy(mNetAddr
.local
.path
, name
, length
);
819 mNetAddr
.local
.path
[length
] = '\0';
820 mNetAddrIsSet
= true;
826 nsresult
nsSocketTransport::InitWithConnectedSocket(PRFileDesc
* fd
,
827 const NetAddr
* addr
) {
828 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
830 char buf
[kNetAddrMaxCStrBufSize
];
831 addr
->ToStringBuffer(buf
, sizeof(buf
));
835 if (addr
->raw
.family
== AF_INET
) {
836 port
= addr
->inet
.port
;
837 } else if (addr
->raw
.family
== AF_INET6
) {
838 port
= addr
->inet6
.port
;
844 memcpy(&mNetAddr
, addr
, sizeof(NetAddr
));
846 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
847 mState
= STATE_TRANSFERRING
;
849 mNetAddrIsSet
= true;
852 MutexAutoLock
lock(mLock
);
853 NS_ASSERTION(!mFD
.IsInitialized(), "already initialized");
857 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
860 // make sure new socket is non-blocking
861 PRSocketOptionData opt
;
862 opt
.option
= PR_SockOpt_Nonblocking
;
863 opt
.value
.non_blocking
= true;
864 PR_SetSocketOption(fd
, &opt
);
867 ("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
868 this, mHost
.get(), mPort
));
870 // jump to InitiateSocket to get ourselves attached to the STS poll list.
871 return PostEvent(MSG_RETRY_INIT_SOCKET
);
874 nsresult
nsSocketTransport::InitWithConnectedSocket(
875 PRFileDesc
* aFD
, const NetAddr
* aAddr
, nsIInterfaceRequestor
* aCallbacks
) {
877 MutexAutoLock
lock(mLock
);
878 mCallbacks
= aCallbacks
;
880 return InitWithConnectedSocket(aFD
, aAddr
);
883 nsresult
nsSocketTransport::PostEvent(uint32_t type
, nsresult status
,
884 nsISupports
* param
) {
885 SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%" PRIx32
887 this, type
, static_cast<uint32_t>(status
), param
));
889 nsCOMPtr
<nsIRunnable
> event
= new nsSocketEvent(this, type
, status
, param
);
890 if (!event
) return NS_ERROR_OUT_OF_MEMORY
;
892 return mSocketTransportService
->Dispatch(event
, NS_DISPATCH_NORMAL
);
895 void nsSocketTransport::SendStatus(nsresult status
) {
896 SOCKET_LOG1(("nsSocketTransport::SendStatus [this=%p status=%" PRIx32
"]\n",
897 this, static_cast<uint32_t>(status
)));
899 nsCOMPtr
<nsITransportEventSink
> sink
;
902 MutexAutoLock
lock(mLock
);
905 case NS_NET_STATUS_SENDING_TO
:
906 progress
= mOutput
.ByteCount();
908 case NS_NET_STATUS_RECEIVING_FROM
:
909 progress
= mInput
.ByteCount();
917 sink
->OnTransportStatus(this, status
, progress
, -1);
921 nsresult
nsSocketTransport::ResolveHost() {
923 "nsSocketTransport::ResolveHost [this=%p %s:%d%s] "
924 "mProxyTransparentResolvesHost=%d\n",
925 this, SocketHost().get(), SocketPort(),
926 mConnectionFlags
& nsSocketTransport::BYPASS_CACHE
? " bypass cache" : "",
927 mProxyTransparentResolvesHost
));
928 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
932 if (!mProxyHost
.IsEmpty()) {
933 if (!mProxyTransparent
|| mProxyTransparentResolvesHost
) {
935 MOZ_ASSERT(!mNetAddrIsSet
|| mNetAddr
.raw
.family
!= AF_LOCAL
,
936 "Unix domain sockets can't be used with proxies");
938 // When not resolving mHost locally, we still want to ensure that
939 // it only contains valid characters. See bug 304904 for details.
940 // Sometimes the end host is not yet known and mHost is *
941 if (!net_IsValidHostName(mHost
) && !mHost
.EqualsLiteral("*")) {
942 SOCKET_LOG((" invalid hostname %s\n", mHost
.get()));
943 return NS_ERROR_UNKNOWN_HOST
;
946 if (mProxyTransparentResolvesHost
) {
947 // Name resolution is done on the server side. Just pretend
948 // client resolution is complete, this will get picked up later.
949 // since we don't need to do DNS now, we bypass the resolving
950 // step by initializing mNetAddr to an empty address, but we
951 // must keep the port. The SOCKS IO layer will use the hostname
952 // we send it when it's created, rather than the empty address
953 // we send with the connect call.
954 mState
= STATE_RESOLVING
;
955 mNetAddr
.raw
.family
= AF_INET
;
956 mNetAddr
.inet
.port
= htons(SocketPort());
957 mNetAddr
.inet
.ip
= htonl(INADDR_ANY
);
958 return PostEvent(MSG_DNS_LOOKUP_COMPLETE
, NS_OK
, nullptr);
962 if (mExternalDNSResolution
) {
963 MOZ_ASSERT(mDNSRecord
);
964 mState
= STATE_RESOLVING
;
965 return PostEvent(MSG_DNS_LOOKUP_COMPLETE
, NS_OK
, nullptr);
968 nsCOMPtr
<nsIDNSService
> dns
= nullptr;
969 auto initTask
= [&dns
]() { dns
= do_GetService(kDNSServiceCID
); };
970 if (!NS_IsMainThread()) {
971 // Forward to the main thread synchronously.
972 RefPtr
<nsIThread
> mainThread
= do_GetMainThread();
974 return NS_ERROR_FAILURE
;
977 SyncRunnable::DispatchToThread(
979 NS_NewRunnableFunction("nsSocketTransport::ResolveHost->GetDNSService",
985 return NS_ERROR_FAILURE
;
990 uint32_t dnsFlags
= 0;
991 if (mConnectionFlags
& nsSocketTransport::BYPASS_CACHE
) {
992 dnsFlags
= nsIDNSService::RESOLVE_BYPASS_CACHE
;
994 if (mConnectionFlags
& nsSocketTransport::REFRESH_CACHE
) {
995 dnsFlags
= nsIDNSService::RESOLVE_REFRESH_CACHE
;
997 if (mConnectionFlags
& nsSocketTransport::DISABLE_IPV6
) {
998 dnsFlags
|= nsIDNSService::RESOLVE_DISABLE_IPV6
;
1000 if (mConnectionFlags
& nsSocketTransport::DISABLE_IPV4
) {
1001 dnsFlags
|= nsIDNSService::RESOLVE_DISABLE_IPV4
;
1003 if (mConnectionFlags
& nsSocketTransport::DISABLE_TRR
) {
1004 dnsFlags
|= nsIDNSService::RESOLVE_DISABLE_TRR
;
1007 if (mConnectionFlags
& nsSocketTransport::USE_IP_HINT_ADDRESS
) {
1008 dnsFlags
|= nsIDNSService::RESOLVE_IP_HINT
;
1011 dnsFlags
|= nsIDNSService::GetFlagsFromTRRMode(
1012 nsISocketTransport::GetTRRModeFromFlags(mConnectionFlags
));
1014 // When we get here, we are not resolving using any configured proxy likely
1015 // because of individual proxy setting on the request or because the host is
1016 // excluded from proxying. Hence, force resolution despite global proxy-DNS
1018 dnsFlags
|= nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS
;
1020 NS_ASSERTION(!(dnsFlags
& nsIDNSService::RESOLVE_DISABLE_IPV6
) ||
1021 !(dnsFlags
& nsIDNSService::RESOLVE_DISABLE_IPV4
),
1022 "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
1024 SendStatus(NS_NET_STATUS_RESOLVING_HOST
);
1026 if (!SocketHost().Equals(mOriginHost
)) {
1027 SOCKET_LOG(("nsSocketTransport %p origin %s doing dns for %s\n", this,
1028 mOriginHost
.get(), SocketHost().get()));
1031 dns
->AsyncResolveNative(SocketHost(), nsIDNSService::RESOLVE_TYPE_DEFAULT
,
1032 dnsFlags
, nullptr, this, mSocketTransportService
,
1033 mOriginAttributes
, getter_AddRefs(mDNSRequest
));
1035 if (NS_SUCCEEDED(rv
)) {
1036 SOCKET_LOG((" advancing to STATE_RESOLVING\n"));
1037 mState
= STATE_RESOLVING
;
1042 nsresult
nsSocketTransport::BuildSocket(PRFileDesc
*& fd
, bool& proxyTransparent
,
1044 SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
1046 nsresult rv
= NS_OK
;
1048 proxyTransparent
= false;
1051 if (mTypes
.IsEmpty()) {
1052 fd
= PR_OpenTCPSocket(mNetAddr
.raw
.family
);
1054 SOCKET_LOG((" error creating TCP nspr socket [rv=%" PRIx32
"]\n",
1055 static_cast<uint32_t>(rv
)));
1056 return NS_ERROR_OUT_OF_MEMORY
;
1061 #if defined(XP_UNIX)
1062 MOZ_ASSERT(!mNetAddrIsSet
|| mNetAddr
.raw
.family
!= AF_LOCAL
,
1063 "Unix domain sockets can't be used with socket types");
1068 uint32_t controlFlags
= 0;
1069 if (mProxyTransparentResolvesHost
) {
1070 controlFlags
|= nsISocketProvider::PROXY_RESOLVES_HOST
;
1073 if (mConnectionFlags
& nsISocketTransport::ANONYMOUS_CONNECT
) {
1074 controlFlags
|= nsISocketProvider::ANONYMOUS_CONNECT
;
1077 if (mConnectionFlags
& nsISocketTransport::NO_PERMANENT_STORAGE
) {
1078 controlFlags
|= nsISocketProvider::NO_PERMANENT_STORAGE
;
1081 if (mConnectionFlags
& nsISocketTransport::BE_CONSERVATIVE
) {
1082 controlFlags
|= nsISocketProvider::BE_CONSERVATIVE
;
1085 if (mConnectionFlags
& nsISocketTransport::DONT_TRY_ECH
) {
1086 controlFlags
|= nsISocketProvider::DONT_TRY_ECH
;
1089 if (mConnectionFlags
& nsISocketTransport::IS_RETRY
) {
1090 controlFlags
|= nsISocketProvider::IS_RETRY
;
1093 if (mConnectionFlags
&
1094 nsISocketTransport::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT
) {
1095 controlFlags
|= nsISocketProvider::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT
;
1098 if (mResolvedByTRR
) {
1099 controlFlags
|= nsISocketProvider::USED_PRIVATE_DNS
;
1102 // by setting host to mOriginHost, instead of mHost we send the
1103 // SocketProvider (e.g. PSM) the origin hostname but can still do DNS
1104 // on an explicit alternate service host name
1105 const char* host
= mOriginHost
.get();
1106 int32_t port
= (int32_t)mOriginPort
;
1108 nsCOMPtr
<nsISocketProviderService
> spserv
=
1109 nsSocketProviderService::GetOrCreate();
1110 nsCOMPtr
<nsIProxyInfo
> proxyInfo
= mProxyInfo
;
1113 for (i
= 0; i
< mTypes
.Length(); ++i
) {
1114 nsCOMPtr
<nsISocketProvider
> provider
;
1116 SOCKET_LOG((" pushing io layer [%u:%s]\n", i
, mTypes
[i
].get()));
1118 rv
= spserv
->GetSocketProvider(mTypes
[i
].get(), getter_AddRefs(provider
));
1119 if (NS_FAILED(rv
)) break;
1121 nsCOMPtr
<nsITLSSocketControl
> tlsSocketControl
;
1123 // if this is the first type, we'll want the
1124 // service to allocate a new socket
1126 // Most layers _ESPECIALLY_ PSM want the origin name here as they
1127 // will use it for secure checks, etc.. and any connection management
1128 // differences between the origin name and the routed name can be
1129 // taken care of via DNS. However, SOCKS is a special case as there is
1130 // no DNS. in the case of SOCKS and PSM the PSM is a separate layer
1131 // and receives the origin name.
1132 const char* socketProviderHost
= host
;
1133 int32_t socketProviderPort
= port
;
1134 if (mProxyTransparentResolvesHost
&&
1135 (mTypes
[0].EqualsLiteral("socks") ||
1136 mTypes
[0].EqualsLiteral("socks4"))) {
1137 SOCKET_LOG(("SOCKS %d Host/Route override: %s:%d -> %s:%d\n",
1138 mHttpsProxy
, socketProviderHost
, socketProviderPort
,
1139 mHost
.get(), mPort
));
1140 socketProviderHost
= mHost
.get();
1141 socketProviderPort
= mPort
;
1144 // when https proxying we want to just connect to the proxy as if
1145 // it were the end host (i.e. expect the proxy's cert)
1147 rv
= provider
->NewSocket(
1148 mNetAddr
.raw
.family
,
1149 mHttpsProxy
? mProxyHost
.get() : socketProviderHost
,
1150 mHttpsProxy
? mProxyPort
: socketProviderPort
, proxyInfo
,
1151 mOriginAttributes
, controlFlags
, mTlsFlags
, &fd
,
1152 getter_AddRefs(tlsSocketControl
));
1154 if (NS_SUCCEEDED(rv
) && !fd
) {
1155 MOZ_ASSERT_UNREACHABLE(
1156 "NewSocket succeeded but failed to "
1157 "create a PRFileDesc");
1158 rv
= NS_ERROR_UNEXPECTED
;
1161 // the socket has already been allocated,
1162 // so we just want the service to add itself
1163 // to the stack (such as pushing an io layer)
1164 rv
= provider
->AddToSocket(mNetAddr
.raw
.family
, host
, port
, proxyInfo
,
1165 mOriginAttributes
, controlFlags
, mTlsFlags
, fd
,
1166 getter_AddRefs(tlsSocketControl
));
1169 // controlFlags = 0; not used below this point...
1170 if (NS_FAILED(rv
)) break;
1172 // if the service was ssl or starttls, we want to hold onto the socket
1174 bool isSSL
= mTypes
[i
].EqualsLiteral("ssl");
1175 if (isSSL
|| mTypes
[i
].EqualsLiteral("starttls")) {
1176 // remember security info
1178 MutexAutoLock
lock(mLock
);
1179 mTLSSocketControl
= tlsSocketControl
;
1180 SOCKET_LOG((" [tlsSocketControl=%p callbacks=%p]\n",
1181 mTLSSocketControl
.get(), mCallbacks
.get()));
1183 // remember if socket type is SSL so we can ProxyStartSSL if need be.
1185 } else if (mTypes
[i
].EqualsLiteral("socks") ||
1186 mTypes
[i
].EqualsLiteral("socks4")) {
1187 // since socks is transparent, any layers above
1188 // it do not have to worry about proxy stuff
1189 proxyInfo
= nullptr;
1190 proxyTransparent
= true;
1194 if (NS_FAILED(rv
)) {
1195 SOCKET_LOG((" error pushing io layer [%u:%s rv=%" PRIx32
"]\n", i
,
1196 mTypes
[i
].get(), static_cast<uint32_t>(rv
)));
1199 fd
, mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1205 nsresult
nsSocketTransport::InitiateSocket() {
1206 SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
1207 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1213 if (gIOService
->IsNetTearingDown()) {
1214 return NS_ERROR_ABORT
;
1216 if (gIOService
->IsOffline()) {
1217 if (StaticPrefs::network_disable_localhost_when_offline() || !isLocal
) {
1218 return NS_ERROR_OFFLINE
;
1220 } else if (!isLocal
) {
1222 // all IP networking has to be done from the parent
1223 if (NS_SUCCEEDED(mCondition
) && ((mNetAddr
.raw
.family
== AF_INET
) ||
1224 (mNetAddr
.raw
.family
== AF_INET6
))) {
1225 MOZ_ASSERT(!IsNeckoChild());
1229 if (NS_SUCCEEDED(mCondition
) && xpc::AreNonLocalConnectionsDisabled() &&
1230 !(mNetAddr
.IsIPAddrAny() || mNetAddr
.IsIPAddrLocal() ||
1231 mNetAddr
.IsIPAddrShared())) {
1232 nsAutoCString ipaddr
;
1233 RefPtr
<nsNetAddr
> netaddr
= new nsNetAddr(&mNetAddr
);
1234 netaddr
->GetAddress(ipaddr
);
1237 "FATAL ERROR: Non-local network connections are disabled and a "
1239 "attempt to %s (%s) was made.\nYou should only access hostnames "
1240 "available via the test networking proxy (if running mochitests) "
1241 "or from a test-specific httpd.js server (if running xpcshell "
1243 "Browser services should be disabled or redirected to a local "
1245 mHost
.get(), ipaddr
.get());
1246 return NS_ERROR_NON_LOCAL_CONNECTION_REFUSED
;
1250 // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
1251 // connected - Bug 853423.
1252 if (mConnectionFlags
& nsISocketTransport::DISABLE_RFC1918
&&
1253 mNetAddr
.IsIPAddrLocal()) {
1254 if (SOCKET_LOG_ENABLED()) {
1255 nsAutoCString netAddrCString
;
1256 netAddrCString
.SetLength(kIPv6CStrBufSize
);
1257 if (!mNetAddr
.ToStringBuffer(netAddrCString
.BeginWriting(),
1258 kIPv6CStrBufSize
)) {
1259 netAddrCString
= "<IP-to-string failed>"_ns
;
1262 ("nsSocketTransport::InitiateSocket skipping "
1263 "speculative connection for host [%s:%d] proxy "
1264 "[%s:%d] with Local IP address [%s]",
1265 mHost
.get(), mPort
, mProxyHost
.get(), mProxyPort
,
1266 netAddrCString
.get()));
1268 mCondition
= NS_ERROR_CONNECTION_REFUSED
;
1269 OnSocketDetached(nullptr);
1274 // find out if it is going to be ok to attach another socket to the STS.
1275 // if not then we have to wait for the STS to tell us that it is ok.
1276 // the notification is asynchronous, which means that when we could be
1277 // in a race to call AttachSocket once notified. for this reason, when
1278 // we get notified, we just re-enter this function. as a result, we are
1279 // sure to ask again before calling AttachSocket. in this way we deal
1280 // with the race condition. though it isn't the most elegant solution,
1281 // it is far simpler than trying to build a system that would guarantee
1282 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
1283 // 194402 for more info.
1285 if (!mSocketTransportService
->CanAttachSocket()) {
1286 nsCOMPtr
<nsIRunnable
> event
=
1287 new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET
);
1288 if (!event
) return NS_ERROR_OUT_OF_MEMORY
;
1289 return mSocketTransportService
->NotifyWhenCanAttachSocket(event
);
1293 // if we already have a connected socket, then just attach and return.
1296 MutexAutoLock
lock(mLock
);
1297 if (mFD
.IsInitialized()) {
1298 rv
= mSocketTransportService
->AttachSocket(mFD
, this);
1299 if (NS_SUCCEEDED(rv
)) mAttached
= true;
1305 // create new socket fd, push io layers, etc.
1308 bool proxyTransparent
;
1311 rv
= BuildSocket(fd
, proxyTransparent
, usingSSL
);
1312 if (NS_FAILED(rv
)) {
1314 (" BuildSocket failed [rv=%" PRIx32
"]\n", static_cast<uint32_t>(rv
)));
1318 // create proxy via IOActivityMonitor
1319 IOActivityMonitor::MonitorSocket(fd
);
1322 if (StaticPrefs::fuzzing_necko_enabled()) {
1323 rv
= AttachFuzzyIOLayer(fd
);
1324 if (NS_FAILED(rv
)) {
1325 SOCKET_LOG(("Failed to attach fuzzing IOLayer [rv=%" PRIx32
"].\n",
1326 static_cast<uint32_t>(rv
)));
1329 SOCKET_LOG(("Successfully attached fuzzing IOLayer.\n"));
1332 mTLSSocketControl
= new FuzzySocketControl();
1339 // Make the socket non-blocking...
1340 PRSocketOptionData opt
;
1341 opt
.option
= PR_SockOpt_Nonblocking
;
1342 opt
.value
.non_blocking
= true;
1343 status
= PR_SetSocketOption(fd
, &opt
);
1344 NS_ASSERTION(status
== PR_SUCCESS
, "unable to make socket non-blocking");
1346 if (mReuseAddrPort
) {
1347 SOCKET_LOG((" Setting port/addr reuse socket options\n"));
1349 // Set ReuseAddr for TCP sockets to enable having several
1350 // sockets bound to same local IP and port
1351 PRSocketOptionData opt_reuseaddr
;
1352 opt_reuseaddr
.option
= PR_SockOpt_Reuseaddr
;
1353 opt_reuseaddr
.value
.reuse_addr
= PR_TRUE
;
1354 status
= PR_SetSocketOption(fd
, &opt_reuseaddr
);
1355 if (status
!= PR_SUCCESS
) {
1356 SOCKET_LOG((" Couldn't set reuse addr socket option: %d\n", status
));
1359 // And also set ReusePort for platforms supporting this socket option
1360 PRSocketOptionData opt_reuseport
;
1361 opt_reuseport
.option
= PR_SockOpt_Reuseport
;
1362 opt_reuseport
.value
.reuse_port
= PR_TRUE
;
1363 status
= PR_SetSocketOption(fd
, &opt_reuseport
);
1364 if (status
!= PR_SUCCESS
&&
1365 PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR
) {
1366 SOCKET_LOG((" Couldn't set reuse port socket option: %d\n", status
));
1370 // disable the nagle algorithm - if we rely on it to coalesce writes into
1371 // full packets the final packet of a multi segment POST/PUT or pipeline
1372 // sequence is delayed a full rtt
1373 opt
.option
= PR_SockOpt_NoDelay
;
1374 opt
.value
.no_delay
= true;
1375 PR_SetSocketOption(fd
, &opt
);
1377 // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
1378 // The Windows default of 8KB is too small and as of vista sp1, autotuning
1379 // only applies to receive window
1380 int32_t sndBufferSize
;
1381 mSocketTransportService
->GetSendBufferSize(&sndBufferSize
);
1382 if (sndBufferSize
> 0) {
1383 opt
.option
= PR_SockOpt_SendBufferSize
;
1384 opt
.value
.send_buffer_size
= sndBufferSize
;
1385 PR_SetSocketOption(fd
, &opt
);
1389 opt
.option
= PR_SockOpt_IpTypeOfService
;
1390 opt
.value
.tos
= mQoSBits
;
1391 PR_SetSocketOption(fd
, &opt
);
1395 // The linger is turned off by default. This is not a hard close, but
1396 // closesocket should return immediately and operating system tries to send
1397 // remaining data for certain, implementation specific, amount of time.
1398 // https://msdn.microsoft.com/en-us/library/ms739165.aspx
1400 // Turn the linger option on an set the interval to 0. This will cause hard
1401 // close of the socket.
1402 opt
.option
= PR_SockOpt_Linger
;
1403 opt
.value
.linger
.polarity
= 1;
1404 opt
.value
.linger
.linger
= 0;
1405 PR_SetSocketOption(fd
, &opt
);
1408 // up to here, mFD will only be accessed by us
1410 // assign mFD so that we can properly handle OnSocketDetached before we've
1411 // established a connection.
1413 MutexAutoLock
lock(mLock
);
1414 // inform socket transport about this newly created socket...
1415 rv
= mSocketTransportService
->AttachSocket(fd
, this);
1416 if (NS_FAILED(rv
)) {
1418 fd
, mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1425 mFDconnected
= false;
1426 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
1429 SOCKET_LOG((" advancing to STATE_CONNECTING\n"));
1430 mState
= STATE_CONNECTING
;
1431 SendStatus(NS_NET_STATUS_CONNECTING_TO
);
1433 if (SOCKET_LOG_ENABLED()) {
1434 char buf
[kNetAddrMaxCStrBufSize
];
1435 mNetAddr
.ToStringBuffer(buf
, sizeof(buf
));
1436 SOCKET_LOG((" trying address: %s\n", buf
));
1440 // Initiate the connect() to the host...
1443 memset(&prAddr
, 0, sizeof(prAddr
));
1446 MutexAutoLock
lock(mLock
);
1447 NetAddrToPRNetAddr(mBindAddr
.get(), &prAddr
);
1448 status
= PR_Bind(fd
, &prAddr
);
1449 if (status
!= PR_SUCCESS
) {
1450 return NS_ERROR_FAILURE
;
1452 mBindAddr
= nullptr;
1456 NetAddrToPRNetAddr(&mNetAddr
, &prAddr
);
1459 // Find the real tcp socket and set non-blocking once again!
1461 PRFileDesc
* bottom
= PR_GetIdentitiesLayer(fd
, PR_NSPR_IO_LAYER
);
1463 PROsfd osfd
= PR_FileDesc2NativeHandle(bottom
);
1464 u_long nonblocking
= 1;
1465 if (ioctlsocket(osfd
, FIONBIO
, &nonblocking
) != 0) {
1466 NS_WARNING("Socket could not be set non-blocking!");
1467 return NS_ERROR_FAILURE
;
1472 if (mTLSSocketControl
) {
1473 if (!mEchConfig
.IsEmpty() &&
1474 !(mConnectionFlags
& (DONT_TRY_ECH
| BE_CONSERVATIVE
))) {
1475 SOCKET_LOG(("nsSocketTransport::InitiateSocket set echconfig."));
1476 rv
= mTLSSocketControl
->SetEchConfig(mEchConfig
);
1477 if (NS_FAILED(rv
)) {
1480 mEchConfigUsed
= true;
1484 // We use PRIntervalTime here because we need
1485 // nsIOService::LastOfflineStateChange time and
1486 // nsIOService::LastConectivityChange time to be atomic.
1487 PRIntervalTime connectStarted
= 0;
1488 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
1489 connectStarted
= PR_IntervalNow();
1492 if (Telemetry::CanRecordPrereleaseData() ||
1493 Telemetry::CanRecordReleaseData()) {
1494 if (NS_FAILED(AttachNetworkDataCountLayer(fd
))) {
1496 ("nsSocketTransport::InitiateSocket "
1497 "AttachNetworkDataCountLayer failed [this=%p]\n",
1502 bool connectCalled
= true; // This is only needed for telemetry.
1503 status
= PR_Connect(fd
, &prAddr
, NS_SOCKET_CONNECT_TIMEOUT
);
1504 PRErrorCode code
= PR_GetError();
1505 if (status
== PR_SUCCESS
) {
1506 PR_SetFDInheritable(fd
, false);
1509 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase() &&
1510 connectStarted
&& connectCalled
) {
1511 SendPRBlockingTelemetry(
1512 connectStarted
, Telemetry::PRCONNECT_BLOCKING_TIME_NORMAL
,
1513 Telemetry::PRCONNECT_BLOCKING_TIME_SHUTDOWN
,
1514 Telemetry::PRCONNECT_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
1515 Telemetry::PRCONNECT_BLOCKING_TIME_LINK_CHANGE
,
1516 Telemetry::PRCONNECT_BLOCKING_TIME_OFFLINE
);
1519 if (status
== PR_SUCCESS
) {
1521 // we are connected!
1523 OnSocketConnected();
1525 #if defined(TEST_CONNECT_ERRORS)
1526 code
= RandomizeConnectError(code
);
1529 // If the PR_Connect(...) would block, then poll for a connection.
1531 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
)) {
1532 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
1534 // If the socket is already connected, then return success...
1536 } else if (PR_IS_CONNECTED_ERROR
== code
) {
1538 // we are connected!
1540 OnSocketConnected();
1542 if (mTLSSocketControl
&& !mProxyHost
.IsEmpty() && proxyTransparent
&&
1544 // if the connection phase is finished, and the ssl layer has
1545 // been pushed, and we were proxying (transparently; ie. nothing
1546 // has to happen in the protocol layer above us), it's time for
1547 // the ssl to start doing it's thing.
1548 SOCKET_LOG((" calling ProxyStartSSL()\n"));
1549 mTLSSocketControl
->ProxyStartSSL();
1550 // XXX what if we were forced to poll on the socket for a successful
1551 // connection... wouldn't we need to call ProxyStartSSL after a call
1552 // to PR_ConnectContinue indicates that we are connected?
1554 // XXX this appears to be what the old socket transport did. why
1555 // isn't this broken?
1559 // A SOCKS request was rejected; get the actual error code from
1562 else if (PR_UNKNOWN_ERROR
== code
&& mProxyTransparent
&&
1563 !mProxyHost
.IsEmpty()) {
1564 code
= PR_GetOSError();
1565 rv
= ErrorAccordingToNSPR(code
);
1568 // The connection was refused...
1571 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase() &&
1572 connectStarted
&& connectCalled
) {
1573 SendPRBlockingTelemetry(
1574 connectStarted
, Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_NORMAL
,
1575 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_SHUTDOWN
,
1576 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
1577 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_LINK_CHANGE
,
1578 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_OFFLINE
);
1581 rv
= ErrorAccordingToNSPR(code
);
1582 if ((rv
== NS_ERROR_CONNECTION_REFUSED
) && !mProxyHost
.IsEmpty()) {
1583 rv
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
1590 bool nsSocketTransport::RecoverFromError() {
1591 NS_ASSERTION(NS_FAILED(mCondition
), "there should be something wrong");
1594 ("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%" PRIx32
1596 this, mState
, static_cast<uint32_t>(mCondition
)));
1598 if (mDoNotRetryToConnect
) {
1600 ("nsSocketTransport::RecoverFromError do not retry because "
1601 "mDoNotRetryToConnect is set [this=%p]\n",
1606 #if defined(XP_UNIX)
1607 // Unix domain connections don't have multiple addresses to try,
1608 // so the recovery techniques here don't apply.
1609 if (mNetAddrIsSet
&& mNetAddr
.raw
.family
== AF_LOCAL
) return false;
1612 if ((mConnectionFlags
& nsSocketTransport::USE_IP_HINT_ADDRESS
) &&
1613 mCondition
== NS_ERROR_UNKNOWN_HOST
&&
1614 (mState
== MSG_DNS_LOOKUP_COMPLETE
|| mState
== MSG_ENSURE_CONNECT
)) {
1615 SOCKET_LOG((" try again without USE_IP_HINT_ADDRESS"));
1616 mConnectionFlags
&= ~nsSocketTransport::USE_IP_HINT_ADDRESS
;
1617 mState
= STATE_CLOSED
;
1618 return NS_SUCCEEDED(PostEvent(MSG_ENSURE_CONNECT
, NS_OK
));
1621 // can only recover from errors in these states
1622 if (mState
!= STATE_RESOLVING
&& mState
!= STATE_CONNECTING
) {
1623 SOCKET_LOG((" not in a recoverable state"));
1631 MutexAutoLock
lock(mLock
);
1632 NS_ASSERTION(!mFDconnected
, "socket should not be connected");
1636 // all connection failures need to be reported to DNS so that the next
1637 // time we will use a different address if available.
1638 // NS_BASE_STREAM_CLOSED is not an actual connection failure, so don't report
1640 if (mState
== STATE_CONNECTING
&& mDNSRecord
&&
1641 mCondition
!= NS_BASE_STREAM_CLOSED
) {
1642 mDNSRecord
->ReportUnusable(SocketPort());
1645 if (mCondition
!= NS_ERROR_CONNECTION_REFUSED
&&
1646 mCondition
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
1647 mCondition
!= NS_ERROR_NET_TIMEOUT
&&
1648 mCondition
!= NS_ERROR_UNKNOWN_HOST
&&
1649 mCondition
!= NS_ERROR_UNKNOWN_PROXY_HOST
) {
1650 SOCKET_LOG((" not a recoverable error %" PRIx32
,
1651 static_cast<uint32_t>(mCondition
)));
1655 bool tryAgain
= false;
1657 if ((mState
== STATE_CONNECTING
) && mDNSRecord
) {
1658 if (mNetAddr
.raw
.family
== AF_INET
) {
1659 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
1660 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
1661 UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS
);
1663 } else if (mNetAddr
.raw
.family
== AF_INET6
) {
1664 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
1665 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
1666 UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS
);
1671 if (mConnectionFlags
& RETRY_WITH_DIFFERENT_IP_FAMILY
&&
1672 mCondition
== NS_ERROR_UNKNOWN_HOST
&& mState
== STATE_RESOLVING
&&
1673 !mProxyTransparentResolvesHost
) {
1674 SOCKET_LOG((" trying lookup again with opposite ip family\n"));
1675 mConnectionFlags
^= (DISABLE_IPV6
| DISABLE_IPV4
);
1676 mConnectionFlags
&= ~RETRY_WITH_DIFFERENT_IP_FAMILY
;
1677 // This will tell the consuming half-open to reset preference on the
1679 mResetFamilyPreference
= true;
1683 // try next ip address only if past the resolver stage...
1684 if (mState
== STATE_CONNECTING
&& mDNSRecord
) {
1685 nsresult rv
= mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
1686 mDNSRecord
->IsTRR(&mResolvedByTRR
);
1687 if (NS_SUCCEEDED(rv
)) {
1688 SOCKET_LOG((" trying again with next ip address\n"));
1690 } else if (mExternalDNSResolution
) {
1691 mRetryDnsIfPossible
= true;
1693 mDNSRecord
->IsTRR(&trrEnabled
);
1694 // Bug 1648147 - If the server responded with `0.0.0.0` or `::` then we
1695 // should intentionally not fallback to regular DNS.
1696 if (trrEnabled
&& !StaticPrefs::network_trr_fallback_on_zero_response() &&
1697 ((mNetAddr
.raw
.family
== AF_INET
&& mNetAddr
.inet
.ip
== 0) ||
1698 (mNetAddr
.raw
.family
== AF_INET6
&& mNetAddr
.inet6
.ip
.u64
[0] == 0 &&
1699 mNetAddr
.inet6
.ip
.u64
[1] == 0))) {
1700 SOCKET_LOG((" TRR returned 0.0.0.0 and there are no other IPs"));
1701 mRetryDnsIfPossible
= false;
1703 } else if (mConnectionFlags
& RETRY_WITH_DIFFERENT_IP_FAMILY
) {
1704 SOCKET_LOG((" failed to connect, trying with opposite ip family\n"));
1705 // Drop state to closed. This will trigger new round of DNS
1706 // resolving bellow.
1707 mState
= STATE_CLOSED
;
1708 mConnectionFlags
^= (DISABLE_IPV6
| DISABLE_IPV4
);
1709 mConnectionFlags
&= ~RETRY_WITH_DIFFERENT_IP_FAMILY
;
1710 // This will tell the consuming half-open to reset preference on the
1712 mResetFamilyPreference
= true;
1714 } else if (!(mConnectionFlags
& DISABLE_TRR
)) {
1716 mDNSRecord
->IsTRR(&trrEnabled
);
1718 // Bug 1648147 - If the server responded with `0.0.0.0` or `::` then we
1719 // should intentionally not fallback to regular DNS.
1720 if (!StaticPrefs::network_trr_fallback_on_zero_response() &&
1721 ((mNetAddr
.raw
.family
== AF_INET
&& mNetAddr
.inet
.ip
== 0) ||
1722 (mNetAddr
.raw
.family
== AF_INET6
&& mNetAddr
.inet6
.ip
.u64
[0] == 0 &&
1723 mNetAddr
.inet6
.ip
.u64
[1] == 0))) {
1724 SOCKET_LOG((" TRR returned 0.0.0.0 and there are no other IPs"));
1725 } else if (trrEnabled
) {
1726 uint32_t trrMode
= 0;
1727 mDNSRecord
->GetEffectiveTRRMode(&trrMode
);
1728 // If current trr mode is trr only, we should not retry.
1730 // Drop state to closed. This will trigger a new round of
1731 // DNS resolving. Bypass the cache this time since the
1732 // cached data came from TRR and failed already!
1733 SOCKET_LOG((" failed to connect with TRR enabled, try w/o\n"));
1734 mState
= STATE_CLOSED
;
1735 mConnectionFlags
|= DISABLE_TRR
| BYPASS_CACHE
| REFRESH_CACHE
;
1742 // prepare to try again.
1746 if (mState
== STATE_CONNECTING
) {
1747 mState
= STATE_RESOLVING
;
1748 msg
= MSG_DNS_LOOKUP_COMPLETE
;
1750 mState
= STATE_CLOSED
;
1751 msg
= MSG_ENSURE_CONNECT
;
1754 rv
= PostEvent(msg
, NS_OK
);
1755 if (NS_FAILED(rv
)) tryAgain
= false;
1761 // called on the socket thread only
1762 void nsSocketTransport::OnMsgInputClosed(nsresult reason
) {
1763 SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%" PRIx32
1765 this, static_cast<uint32_t>(reason
)));
1767 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1769 mInputClosed
= true;
1770 // check if event should affect entire transport
1771 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
)) {
1772 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1773 } else if (mOutputClosed
) {
1775 NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1777 if (mState
== STATE_TRANSFERRING
) mPollFlags
&= ~PR_POLL_READ
;
1778 mInput
.OnSocketReady(reason
);
1782 // called on the socket thread only
1783 void nsSocketTransport::OnMsgOutputClosed(nsresult reason
) {
1784 SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%p reason=%" PRIx32
1786 this, static_cast<uint32_t>(reason
)));
1788 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1790 mOutputClosed
= true;
1791 // check if event should affect entire transport
1792 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
)) {
1793 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1794 } else if (mInputClosed
) {
1796 NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1798 if (mState
== STATE_TRANSFERRING
) mPollFlags
&= ~PR_POLL_WRITE
;
1799 mOutput
.OnSocketReady(reason
);
1803 void nsSocketTransport::OnSocketConnected() {
1804 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1805 SOCKET_LOG((" advancing to STATE_TRANSFERRING\n"));
1807 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
1808 mState
= STATE_TRANSFERRING
;
1810 // Set the m*AddrIsSet flags only when state has reached TRANSFERRING
1811 // because we need to make sure its value does not change due to failover
1812 mNetAddrIsSet
= true;
1814 // assign mFD (must do this within the transport lock), but take care not
1815 // to trample over mFDref if mFD is already set.
1817 MutexAutoLock
lock(mLock
);
1818 NS_ASSERTION(mFD
.IsInitialized(), "no socket");
1819 NS_ASSERTION(mFDref
== 1, "wrong socket ref count");
1821 mFDconnected
= true;
1822 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
1825 // Ensure keepalive is configured correctly if previously enabled.
1826 if (mKeepaliveEnabled
) {
1827 nsresult rv
= SetKeepaliveEnabledInternal(true);
1828 if (NS_WARN_IF(NS_FAILED(rv
))) {
1829 SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%" PRIx32
"]",
1830 static_cast<uint32_t>(rv
)));
1834 SendStatus(NS_NET_STATUS_CONNECTED_TO
);
1837 void nsSocketTransport::SetSocketName(PRFileDesc
* fd
) {
1838 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1839 if (mSelfAddrIsSet
) {
1844 memset(&prAddr
, 0, sizeof(prAddr
));
1845 if (PR_GetSockName(fd
, &prAddr
) == PR_SUCCESS
) {
1846 PRNetAddrToNetAddr(&prAddr
, &mSelfAddr
);
1847 mSelfAddrIsSet
= true;
1851 PRFileDesc
* nsSocketTransport::GetFD_Locked() {
1852 mLock
.AssertCurrentThreadOwns();
1854 // mFD is not available to the streams while disconnected.
1855 if (!mFDconnected
) return nullptr;
1857 if (mFD
.IsInitialized()) mFDref
++;
1862 class ThunkPRClose
: public Runnable
{
1864 explicit ThunkPRClose(PRFileDesc
* fd
)
1865 : Runnable("net::ThunkPRClose"), mFD(fd
) {}
1867 NS_IMETHOD
Run() override
{
1868 nsSocketTransport::CloseSocket(
1869 mFD
, gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1877 void STS_PRCloseOnSocketTransport(PRFileDesc
* fd
, bool lingerPolarity
,
1878 int16_t lingerTimeout
) {
1879 if (gSocketTransportService
) {
1880 // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
1881 gSocketTransportService
->Dispatch(new ThunkPRClose(fd
), NS_DISPATCH_NORMAL
);
1883 // something horrible has happened
1884 NS_ASSERTION(gSocketTransportService
, "No STS service");
1888 void nsSocketTransport::ReleaseFD_Locked(PRFileDesc
* fd
) {
1889 mLock
.AssertCurrentThreadOwns();
1891 NS_ASSERTION(mFD
== fd
, "wrong fd");
1893 if (--mFDref
== 0) {
1894 if (gIOService
->IsNetTearingDown() &&
1895 ((PR_IntervalNow() - gIOService
->NetTearingDownStarted()) >
1896 gSocketTransportService
->MaxTimeForPrClosePref())) {
1897 // If shutdown last to long, let the socket leak and do not close it.
1898 SOCKET_LOG(("Intentional leak"));
1900 if (mLingerPolarity
|| mLingerTimeout
) {
1901 PRSocketOptionData socket_linger
;
1902 socket_linger
.option
= PR_SockOpt_Linger
;
1903 socket_linger
.value
.linger
.polarity
= mLingerPolarity
;
1904 socket_linger
.value
.linger
.linger
= mLingerTimeout
;
1905 PR_SetSocketOption(mFD
, &socket_linger
);
1907 if (OnSocketThread()) {
1908 SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
1910 mFD
, mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1912 // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
1913 STS_PRCloseOnSocketTransport(mFD
, mLingerPolarity
, mLingerTimeout
);
1920 //-----------------------------------------------------------------------------
1921 // socket event handler impl
1923 void nsSocketTransport::OnSocketEvent(uint32_t type
, nsresult status
,
1924 nsISupports
* param
) {
1925 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1927 ("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%" PRIx32
1929 this, type
, static_cast<uint32_t>(status
), param
));
1931 if (NS_FAILED(mCondition
)) {
1932 // block event since we're apparently already dead.
1933 SOCKET_LOG((" blocking event [condition=%" PRIx32
"]\n",
1934 static_cast<uint32_t>(mCondition
)));
1936 // notify input/output streams in case either has a pending notify.
1938 mInput
.OnSocketReady(mCondition
);
1939 mOutput
.OnSocketReady(mCondition
);
1944 case MSG_ENSURE_CONNECT
:
1945 SOCKET_LOG((" MSG_ENSURE_CONNECT\n"));
1947 // Apply port remapping here so that we do it on the socket thread and
1948 // before we process the resolved DNS name or create the socket the first
1950 if (!mPortRemappingApplied
) {
1951 mPortRemappingApplied
= true;
1953 mSocketTransportService
->ApplyPortRemap(&mPort
);
1954 mSocketTransportService
->ApplyPortRemap(&mOriginPort
);
1958 // ensure that we have created a socket, attached it, and have a
1961 if (mState
== STATE_CLOSED
) {
1962 // Unix domain sockets are ready to connect; mNetAddr is all we
1963 // need. Internet address families require a DNS lookup (or possibly
1964 // several) before we can connect.
1965 #if defined(XP_UNIX)
1966 if (mNetAddrIsSet
&& mNetAddr
.raw
.family
== AF_LOCAL
) {
1967 mCondition
= InitiateSocket();
1972 mCondition
= ResolveHost();
1976 SOCKET_LOG((" ignoring redundant event\n"));
1980 case MSG_DNS_LOOKUP_COMPLETE
:
1981 if (mDNSRequest
) { // only send this if we actually resolved anything
1982 SendStatus(NS_NET_STATUS_RESOLVED_HOST
);
1985 SOCKET_LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
1986 mDNSRequest
= nullptr;
1989 mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
1990 mDNSRecord
->IsTRR(&mResolvedByTRR
);
1992 // status contains DNS lookup status
1993 if (NS_FAILED(status
)) {
1994 // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
1995 // proxy host is not found, so we fixup the error code.
1996 // For SOCKS proxies (mProxyTransparent == true), the socket
1997 // transport resolves the real host here, so there's no fixup
1998 // (see bug 226943).
1999 if ((status
== NS_ERROR_UNKNOWN_HOST
) && !mProxyTransparent
&&
2000 !mProxyHost
.IsEmpty()) {
2001 mCondition
= NS_ERROR_UNKNOWN_PROXY_HOST
;
2003 mCondition
= status
;
2005 } else if (mState
== STATE_RESOLVING
) {
2006 mCondition
= InitiateSocket();
2010 case MSG_RETRY_INIT_SOCKET
:
2011 mCondition
= InitiateSocket();
2014 case MSG_INPUT_CLOSED
:
2015 SOCKET_LOG((" MSG_INPUT_CLOSED\n"));
2016 OnMsgInputClosed(status
);
2019 case MSG_INPUT_PENDING
:
2020 SOCKET_LOG((" MSG_INPUT_PENDING\n"));
2021 OnMsgInputPending();
2024 case MSG_OUTPUT_CLOSED
:
2025 SOCKET_LOG((" MSG_OUTPUT_CLOSED\n"));
2026 OnMsgOutputClosed(status
);
2029 case MSG_OUTPUT_PENDING
:
2030 SOCKET_LOG((" MSG_OUTPUT_PENDING\n"));
2031 OnMsgOutputPending();
2033 case MSG_TIMEOUT_CHANGED
:
2034 SOCKET_LOG((" MSG_TIMEOUT_CHANGED\n"));
2036 MutexAutoLock
lock(mLock
);
2038 mTimeouts
[(mState
== STATE_TRANSFERRING
) ? TIMEOUT_READ_WRITE
2043 SOCKET_LOG((" unhandled event!\n"));
2046 if (NS_FAILED(mCondition
)) {
2047 SOCKET_LOG((" after event [this=%p cond=%" PRIx32
"]\n", this,
2048 static_cast<uint32_t>(mCondition
)));
2049 if (!mAttached
) { // need to process this error ourselves...
2050 OnSocketDetached(nullptr);
2052 } else if (mPollFlags
== PR_POLL_EXCEPT
) {
2053 mPollFlags
= 0; // make idle
2057 //-----------------------------------------------------------------------------
2058 // socket handler impl
2060 void nsSocketTransport::OnSocketReady(PRFileDesc
* fd
, int16_t outFlags
) {
2061 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2062 SOCKET_LOG1(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",
2065 if (outFlags
== -1) {
2066 SOCKET_LOG(("socket timeout expired\n"));
2067 mCondition
= NS_ERROR_NET_TIMEOUT
;
2071 if (mState
== STATE_TRANSFERRING
) {
2072 // if waiting to write and socket is writable or hit an exception.
2073 if ((mPollFlags
& PR_POLL_WRITE
) && (outFlags
& ~PR_POLL_READ
)) {
2074 // assume that we won't need to poll any longer (the stream will
2075 // request that we poll again if it is still pending).
2076 mPollFlags
&= ~PR_POLL_WRITE
;
2077 mOutput
.OnSocketReady(NS_OK
);
2079 // if waiting to read and socket is readable or hit an exception.
2080 if ((mPollFlags
& PR_POLL_READ
) && (outFlags
& ~PR_POLL_WRITE
)) {
2081 // assume that we won't need to poll any longer (the stream will
2082 // request that we poll again if it is still pending).
2083 mPollFlags
&= ~PR_POLL_READ
;
2084 mInput
.OnSocketReady(NS_OK
);
2086 // Update poll timeout in case it was changed
2088 MutexAutoLock
lock(mLock
);
2089 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
2091 } else if ((mState
== STATE_CONNECTING
) && !gIOService
->IsNetTearingDown()) {
2092 // We do not need to do PR_ConnectContinue when we are already
2095 // We use PRIntervalTime here because we need
2096 // nsIOService::LastOfflineStateChange time and
2097 // nsIOService::LastConectivityChange time to be atomic.
2098 PRIntervalTime connectStarted
= 0;
2099 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
2100 connectStarted
= PR_IntervalNow();
2103 PRStatus status
= PR_ConnectContinue(fd
, outFlags
);
2105 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase() &&
2107 SendPRBlockingTelemetry(
2108 connectStarted
, Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_NORMAL
,
2109 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_SHUTDOWN
,
2110 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
2111 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_LINK_CHANGE
,
2112 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_OFFLINE
);
2115 if (status
== PR_SUCCESS
) {
2117 // we are connected!
2119 OnSocketConnected();
2121 if (mNetAddr
.raw
.family
== AF_INET
) {
2122 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
2123 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
2124 SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS
);
2126 } else if (mNetAddr
.raw
.family
== AF_INET6
) {
2127 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
2128 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
2129 SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS
);
2133 PRErrorCode code
= PR_GetError();
2134 #if defined(TEST_CONNECT_ERRORS)
2135 code
= RandomizeConnectError(code
);
2138 // If the connect is still not ready, then continue polling...
2140 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
)) {
2141 // Set up the select flags for connect...
2142 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
2143 // Update poll timeout in case it was changed
2145 MutexAutoLock
lock(mLock
);
2146 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
2150 // The SOCKS proxy rejected our request. Find out why.
2152 else if (PR_UNKNOWN_ERROR
== code
&& mProxyTransparent
&&
2153 !mProxyHost
.IsEmpty()) {
2154 code
= PR_GetOSError();
2155 mCondition
= ErrorAccordingToNSPR(code
);
2158 // else, the connection failed...
2160 mCondition
= ErrorAccordingToNSPR(code
);
2161 if ((mCondition
== NS_ERROR_CONNECTION_REFUSED
) &&
2162 !mProxyHost
.IsEmpty()) {
2163 mCondition
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
2165 SOCKET_LOG((" connection failed! [reason=%" PRIx32
"]\n",
2166 static_cast<uint32_t>(mCondition
)));
2169 } else if ((mState
== STATE_CONNECTING
) && gIOService
->IsNetTearingDown()) {
2170 // We do not need to do PR_ConnectContinue when we are already
2173 ("We are in shutdown so skip PR_ConnectContinue and set "
2175 mCondition
= NS_ERROR_ABORT
;
2177 NS_ERROR("unexpected socket state");
2178 mCondition
= NS_ERROR_UNEXPECTED
;
2181 if (mPollFlags
== PR_POLL_EXCEPT
) mPollFlags
= 0; // make idle
2184 // called on the socket thread only
2185 void nsSocketTransport::OnSocketDetached(PRFileDesc
* fd
) {
2186 SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%" PRIx32
2188 this, static_cast<uint32_t>(mCondition
)));
2190 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2194 // if we didn't initiate this detach, then be sure to pass an error
2195 // condition up to our consumers. (e.g., STS is shutting down.)
2196 if (NS_SUCCEEDED(mCondition
)) {
2197 if (gIOService
->IsOffline()) {
2198 mCondition
= NS_ERROR_OFFLINE
;
2200 mCondition
= NS_ERROR_ABORT
;
2204 // If we are not shutting down try again.
2205 if (!gIOService
->IsNetTearingDown() && RecoverFromError()) {
2208 mState
= STATE_CLOSED
;
2210 // make sure there isn't any pending DNS request
2212 mDNSRequest
->Cancel(NS_ERROR_ABORT
);
2213 mDNSRequest
= nullptr;
2217 // notify input/output streams
2219 mInput
.OnSocketReady(mCondition
);
2220 mOutput
.OnSocketReady(mCondition
);
2223 if (mCondition
== NS_ERROR_NET_RESET
&& mDNSRecord
&&
2224 mOutput
.ByteCount() == 0) {
2225 // If we are here, it's likely that we are retrying a transaction. Blocking
2226 // the already used address could increase the successful rate of the retry.
2227 mDNSRecord
->ReportUnusable(SocketPort());
2230 // finally, release our reference to the socket (must do this within
2231 // the transport lock) possibly closing the socket. Also release our
2232 // listeners to break potential refcount cycles.
2234 // We should be careful not to release mEventSink and mCallbacks while
2235 // we're locked, because releasing it might require acquiring the lock
2236 // again, so we just null out mEventSink and mCallbacks while we're
2237 // holding the lock, and let the stack based objects' destuctors take
2238 // care of destroying it if needed.
2239 nsCOMPtr
<nsIInterfaceRequestor
> ourCallbacks
;
2240 nsCOMPtr
<nsITransportEventSink
> ourEventSink
;
2242 MutexAutoLock
lock(mLock
);
2243 if (mFD
.IsInitialized()) {
2244 ReleaseFD_Locked(mFD
);
2245 // flag mFD as unusable; this prevents other consumers from
2246 // acquiring a reference to mFD.
2247 mFDconnected
= false;
2250 // We must release mCallbacks and mEventSink to avoid memory leak
2251 // but only when RecoverFromError() above failed. Otherwise we lose
2252 // link with UI and security callbacks on next connection attempt
2253 // round. That would lead e.g. to a broken certificate exception page.
2254 if (NS_FAILED(mCondition
)) {
2255 mCallbacks
.swap(ourCallbacks
);
2256 mEventSink
.swap(ourEventSink
);
2261 void nsSocketTransport::IsLocal(bool* aIsLocal
) {
2263 MutexAutoLock
lock(mLock
);
2265 #if defined(XP_UNIX)
2266 // Unix-domain sockets are always local.
2267 if (mNetAddr
.raw
.family
== PR_AF_LOCAL
) {
2273 *aIsLocal
= mNetAddr
.IsLoopbackAddr();
2277 //-----------------------------------------------------------------------------
2280 NS_IMPL_ISUPPORTS(nsSocketTransport
, nsISocketTransport
, nsITransport
,
2281 nsIDNSListener
, nsIClassInfo
, nsIInterfaceRequestor
)
2282 NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport
, nsISocketTransport
, nsITransport
,
2283 nsIDNSListener
, nsIInterfaceRequestor
)
2286 nsSocketTransport::OpenInputStream(uint32_t flags
, uint32_t segsize
,
2288 nsIInputStream
** aResult
) {
2290 ("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n", this, flags
));
2292 NS_ENSURE_TRUE(!mInput
.IsReferenced(), NS_ERROR_UNEXPECTED
);
2295 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
2296 nsCOMPtr
<nsIInputStream
> result
;
2298 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
2299 // XXX if the caller wants blocking, then the caller also gets buffered!
2300 // bool openBuffered = !(flags & OPEN_UNBUFFERED);
2301 bool openBlocking
= (flags
& OPEN_BLOCKING
);
2303 net_ResolveSegmentParams(segsize
, segcount
);
2306 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
2307 NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
), !openBlocking
,
2308 true, segsize
, segcount
);
2310 // async copy from socket to pipe
2311 rv
= NS_AsyncCopy(&mInput
, pipeOut
, mSocketTransportService
,
2312 NS_ASYNCCOPY_VIA_WRITESEGMENTS
, segsize
);
2313 if (NS_FAILED(rv
)) return rv
;
2320 // flag input stream as open
2321 mInputClosed
= false;
2323 rv
= PostEvent(MSG_ENSURE_CONNECT
);
2324 if (NS_FAILED(rv
)) {
2328 result
.forget(aResult
);
2333 nsSocketTransport::OpenOutputStream(uint32_t flags
, uint32_t segsize
,
2335 nsIOutputStream
** aResult
) {
2336 SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n", this,
2339 NS_ENSURE_TRUE(!mOutput
.IsReferenced(), NS_ERROR_UNEXPECTED
);
2342 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
2343 nsCOMPtr
<nsIOutputStream
> result
;
2344 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
2345 // XXX if the caller wants blocking, then the caller also gets buffered!
2346 // bool openBuffered = !(flags & OPEN_UNBUFFERED);
2347 bool openBlocking
= (flags
& OPEN_BLOCKING
);
2349 net_ResolveSegmentParams(segsize
, segcount
);
2352 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
2353 NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
), true,
2354 !openBlocking
, segsize
, segcount
);
2356 // async copy from socket to pipe
2357 rv
= NS_AsyncCopy(pipeIn
, &mOutput
, mSocketTransportService
,
2358 NS_ASYNCCOPY_VIA_READSEGMENTS
, segsize
);
2359 if (NS_FAILED(rv
)) return rv
;
2366 // flag output stream as open
2367 mOutputClosed
= false;
2369 rv
= PostEvent(MSG_ENSURE_CONNECT
);
2370 if (NS_FAILED(rv
)) return rv
;
2372 result
.forget(aResult
);
2377 nsSocketTransport::Close(nsresult reason
) {
2378 SOCKET_LOG(("nsSocketTransport::Close %p reason=%" PRIx32
, this,
2379 static_cast<uint32_t>(reason
)));
2381 if (NS_SUCCEEDED(reason
)) reason
= NS_BASE_STREAM_CLOSED
;
2383 mDoNotRetryToConnect
= true;
2385 mInput
.CloseWithStatus(reason
);
2386 mOutput
.CloseWithStatus(reason
);
2391 nsSocketTransport::GetTlsSocketControl(nsITLSSocketControl
** tlsSocketControl
) {
2392 MutexAutoLock
lock(mLock
);
2393 *tlsSocketControl
= do_AddRef(mTLSSocketControl
).take();
2398 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor
** callbacks
) {
2399 MutexAutoLock
lock(mLock
);
2400 *callbacks
= do_AddRef(mCallbacks
).take();
2405 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor
* callbacks
) {
2406 nsCOMPtr
<nsIInterfaceRequestor
> threadsafeCallbacks
;
2407 NS_NewNotificationCallbacksAggregation(callbacks
, nullptr,
2408 GetCurrentEventTarget(),
2409 getter_AddRefs(threadsafeCallbacks
));
2410 MutexAutoLock
lock(mLock
);
2411 mCallbacks
= threadsafeCallbacks
;
2412 SOCKET_LOG(("Reset callbacks for tlsSocketInfo=%p callbacks=%p\n",
2413 mTLSSocketControl
.get(), mCallbacks
.get()));
2418 nsSocketTransport::SetEventSink(nsITransportEventSink
* sink
,
2419 nsIEventTarget
* target
) {
2420 nsCOMPtr
<nsITransportEventSink
> temp
;
2423 net_NewTransportEventSinkProxy(getter_AddRefs(temp
), sink
, target
);
2424 if (NS_FAILED(rv
)) return rv
;
2428 MutexAutoLock
lock(mLock
);
2434 nsSocketTransport::IsAlive(bool* result
) {
2437 nsresult conditionWhileLocked
= NS_OK
;
2438 PRFileDescAutoLock
fd(this, &conditionWhileLocked
);
2439 if (NS_FAILED(conditionWhileLocked
) || !fd
.IsInitialized()) {
2443 // XXX do some idle-time based checks??
2446 int32_t rval
= PR_Recv(fd
, &c
, 1, PR_MSG_PEEK
, 0);
2448 if ((rval
> 0) || (rval
< 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR
)) {
2456 nsSocketTransport::GetHost(nsACString
& host
) {
2457 host
= SocketHost();
2462 nsSocketTransport::GetPort(int32_t* port
) {
2463 *port
= (int32_t)SocketPort();
2468 nsSocketTransport::GetScriptableOriginAttributes(
2469 JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aOriginAttributes
) {
2470 if (NS_WARN_IF(!ToJSValue(aCx
, mOriginAttributes
, aOriginAttributes
))) {
2471 return NS_ERROR_FAILURE
;
2477 nsSocketTransport::SetScriptableOriginAttributes(
2478 JSContext
* aCx
, JS::Handle
<JS::Value
> aOriginAttributes
) {
2479 MutexAutoLock
lock(mLock
);
2480 NS_ENSURE_FALSE(mFD
.IsInitialized(), NS_ERROR_FAILURE
);
2482 OriginAttributes attrs
;
2483 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
2484 return NS_ERROR_INVALID_ARG
;
2487 mOriginAttributes
= attrs
;
2491 nsresult
nsSocketTransport::GetOriginAttributes(
2492 OriginAttributes
* aOriginAttributes
) {
2493 NS_ENSURE_ARG(aOriginAttributes
);
2494 *aOriginAttributes
= mOriginAttributes
;
2498 nsresult
nsSocketTransport::SetOriginAttributes(
2499 const OriginAttributes
& aOriginAttributes
) {
2500 MutexAutoLock
lock(mLock
);
2501 NS_ENSURE_FALSE(mFD
.IsInitialized(), NS_ERROR_FAILURE
);
2503 mOriginAttributes
= aOriginAttributes
;
2508 nsSocketTransport::GetPeerAddr(NetAddr
* addr
) {
2509 // once we are in the connected state, mNetAddr will not change.
2510 // so if we can verify that we are in the connected state, then
2511 // we can freely access mNetAddr from any thread without being
2512 // inside a critical section.
2514 if (!mNetAddrIsSet
) {
2516 ("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
2517 "NOT_AVAILABLE because not yet connected.",
2519 return NS_ERROR_NOT_AVAILABLE
;
2522 memcpy(addr
, &mNetAddr
, sizeof(NetAddr
));
2527 nsSocketTransport::GetSelfAddr(NetAddr
* addr
) {
2528 // once we are in the connected state, mSelfAddr will not change.
2529 // so if we can verify that we are in the connected state, then
2530 // we can freely access mSelfAddr from any thread without being
2531 // inside a critical section.
2533 if (!mSelfAddrIsSet
) {
2535 ("nsSocketTransport::GetSelfAddr [this=%p state=%d] "
2536 "NOT_AVAILABLE because not yet connected.",
2538 return NS_ERROR_NOT_AVAILABLE
;
2541 memcpy(addr
, &mSelfAddr
, sizeof(NetAddr
));
2546 nsSocketTransport::Bind(NetAddr
* aLocalAddr
) {
2547 NS_ENSURE_ARG(aLocalAddr
);
2549 MutexAutoLock
lock(mLock
);
2550 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2552 return NS_ERROR_FAILURE
;
2555 mBindAddr
= MakeUnique
<NetAddr
>();
2556 memcpy(mBindAddr
.get(), aLocalAddr
, sizeof(NetAddr
));
2562 nsSocketTransport::GetScriptablePeerAddr(nsINetAddr
** addr
) {
2566 rv
= GetPeerAddr(&rawAddr
);
2567 if (NS_FAILED(rv
)) return rv
;
2569 RefPtr
<nsNetAddr
> netaddr
= new nsNetAddr(&rawAddr
);
2570 netaddr
.forget(addr
);
2575 nsSocketTransport::GetScriptableSelfAddr(nsINetAddr
** addr
) {
2579 rv
= GetSelfAddr(&rawAddr
);
2580 if (NS_FAILED(rv
)) return rv
;
2582 RefPtr
<nsNetAddr
> netaddr
= new nsNetAddr(&rawAddr
);
2583 netaddr
.forget(addr
);
2589 nsSocketTransport::GetTimeout(uint32_t type
, uint32_t* value
) {
2590 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
2591 MutexAutoLock
lock(mLock
);
2592 *value
= (uint32_t)mTimeouts
[type
];
2597 nsSocketTransport::SetTimeout(uint32_t type
, uint32_t value
) {
2598 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
2600 SOCKET_LOG(("nsSocketTransport::SetTimeout %p type=%u, value=%u", this, type
,
2603 // truncate overly large timeout values.
2605 MutexAutoLock
lock(mLock
);
2606 mTimeouts
[type
] = (uint16_t)std::min
<uint32_t>(value
, UINT16_MAX
);
2608 PostEvent(MSG_TIMEOUT_CHANGED
);
2613 nsSocketTransport::SetReuseAddrPort(bool reuseAddrPort
) {
2614 mReuseAddrPort
= reuseAddrPort
;
2619 nsSocketTransport::SetLinger(bool aPolarity
, int16_t aTimeout
) {
2620 MutexAutoLock
lock(mLock
);
2622 mLingerPolarity
= aPolarity
;
2623 mLingerTimeout
= aTimeout
;
2629 nsSocketTransport::SetQoSBits(uint8_t aQoSBits
) {
2630 // Don't do any checking here of bits. Why? Because as of RFC-4594
2631 // several different Class Selector and Assured Forwarding values
2632 // have been defined, but that isn't to say more won't be added later.
2633 // In that case, any checking would be an impediment to interoperating
2634 // with newer QoS definitions.
2636 mQoSBits
= aQoSBits
;
2641 nsSocketTransport::GetQoSBits(uint8_t* aQoSBits
) {
2642 *aQoSBits
= mQoSBits
;
2647 nsSocketTransport::GetRecvBufferSize(uint32_t* aSize
) {
2648 PRFileDescAutoLock
fd(this);
2649 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2651 nsresult rv
= NS_OK
;
2652 PRSocketOptionData opt
;
2653 opt
.option
= PR_SockOpt_RecvBufferSize
;
2654 if (PR_GetSocketOption(fd
, &opt
) == PR_SUCCESS
) {
2655 *aSize
= opt
.value
.recv_buffer_size
;
2657 rv
= NS_ERROR_FAILURE
;
2664 nsSocketTransport::GetSendBufferSize(uint32_t* aSize
) {
2665 PRFileDescAutoLock
fd(this);
2666 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2668 nsresult rv
= NS_OK
;
2669 PRSocketOptionData opt
;
2670 opt
.option
= PR_SockOpt_SendBufferSize
;
2671 if (PR_GetSocketOption(fd
, &opt
) == PR_SUCCESS
) {
2672 *aSize
= opt
.value
.send_buffer_size
;
2674 rv
= NS_ERROR_FAILURE
;
2681 nsSocketTransport::SetRecvBufferSize(uint32_t aSize
) {
2682 PRFileDescAutoLock
fd(this);
2683 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2685 nsresult rv
= NS_OK
;
2686 PRSocketOptionData opt
;
2687 opt
.option
= PR_SockOpt_RecvBufferSize
;
2688 opt
.value
.recv_buffer_size
= aSize
;
2689 if (PR_SetSocketOption(fd
, &opt
) != PR_SUCCESS
) rv
= NS_ERROR_FAILURE
;
2695 nsSocketTransport::SetSendBufferSize(uint32_t aSize
) {
2696 PRFileDescAutoLock
fd(this);
2697 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2699 nsresult rv
= NS_OK
;
2700 PRSocketOptionData opt
;
2701 opt
.option
= PR_SockOpt_SendBufferSize
;
2702 opt
.value
.send_buffer_size
= aSize
;
2703 if (PR_SetSocketOption(fd
, &opt
) != PR_SUCCESS
) rv
= NS_ERROR_FAILURE
;
2709 nsSocketTransport::OnLookupComplete(nsICancelable
* request
, nsIDNSRecord
* rec
,
2711 SOCKET_LOG(("nsSocketTransport::OnLookupComplete: this=%p status %" PRIx32
2713 this, static_cast<uint32_t>(status
)));
2715 if (NS_SUCCEEDED(status
)) {
2716 mDNSRecord
= do_QueryInterface(rec
);
2717 MOZ_ASSERT(mDNSRecord
);
2720 // flag host lookup complete for the benefit of the ResolveHost method.
2722 nsresult rv
= PostEvent(MSG_DNS_LOOKUP_COMPLETE
, status
, nullptr);
2724 // if posting a message fails, then we should assume that the socket
2725 // transport has been shutdown. this should never happen! if it does
2726 // it means that the socket transport service was shutdown before the
2728 if (NS_FAILED(rv
)) {
2729 NS_WARNING("unable to post DNS lookup complete message");
2735 // nsIInterfaceRequestor
2737 nsSocketTransport::GetInterface(const nsIID
& iid
, void** result
) {
2738 if (iid
.Equals(NS_GET_IID(nsIDNSRecord
)) ||
2739 iid
.Equals(NS_GET_IID(nsIDNSAddrRecord
))) {
2740 return mDNSRecord
? mDNSRecord
->QueryInterface(iid
, result
)
2741 : NS_ERROR_NO_INTERFACE
;
2743 return this->QueryInterface(iid
, result
);
2747 nsSocketTransport::GetInterfaces(nsTArray
<nsIID
>& array
) {
2748 return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport
)(array
);
2752 nsSocketTransport::GetScriptableHelper(nsIXPCScriptable
** _retval
) {
2758 nsSocketTransport::GetContractID(nsACString
& aContractID
) {
2759 aContractID
.SetIsVoid(true);
2764 nsSocketTransport::GetClassDescription(nsACString
& aClassDescription
) {
2765 aClassDescription
.SetIsVoid(true);
2770 nsSocketTransport::GetClassID(nsCID
** aClassID
) {
2771 *aClassID
= nullptr;
2776 nsSocketTransport::GetFlags(uint32_t* aFlags
) {
2777 *aFlags
= nsIClassInfo::THREADSAFE
;
2782 nsSocketTransport::GetClassIDNoAlloc(nsCID
* aClassIDNoAlloc
) {
2783 return NS_ERROR_NOT_AVAILABLE
;
2787 nsSocketTransport::GetConnectionFlags(uint32_t* value
) {
2788 *value
= mConnectionFlags
;
2793 nsSocketTransport::SetConnectionFlags(uint32_t value
) {
2795 ("nsSocketTransport::SetConnectionFlags %p flags=%u", this, value
));
2797 mConnectionFlags
= value
;
2802 nsSocketTransport::SetIsPrivate(bool aIsPrivate
) {
2803 mIsPrivate
= aIsPrivate
;
2808 nsSocketTransport::GetTlsFlags(uint32_t* value
) {
2814 nsSocketTransport::SetTlsFlags(uint32_t value
) {
2819 void nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled
) {
2820 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2822 // The global pref toggles keepalive as a system feature; it only affects
2823 // an individual socket if keepalive has been specifically enabled for it.
2824 // So, ensure keepalive is configured correctly if previously enabled.
2825 if (mKeepaliveEnabled
) {
2826 nsresult rv
= SetKeepaliveEnabledInternal(aEnabled
);
2827 if (NS_WARN_IF(NS_FAILED(rv
))) {
2828 SOCKET_LOG((" SetKeepaliveEnabledInternal [%s] failed rv[0x%" PRIx32
"]",
2829 aEnabled
? "enable" : "disable", static_cast<uint32_t>(rv
)));
2834 nsresult
nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable
) {
2835 MOZ_ASSERT(mKeepaliveIdleTimeS
> 0 && mKeepaliveIdleTimeS
<= kMaxTCPKeepIdle
);
2836 MOZ_ASSERT(mKeepaliveRetryIntervalS
> 0 &&
2837 mKeepaliveRetryIntervalS
<= kMaxTCPKeepIntvl
);
2838 MOZ_ASSERT(mKeepaliveProbeCount
> 0 &&
2839 mKeepaliveProbeCount
<= kMaxTCPKeepCount
);
2841 PRFileDescAutoLock
fd(this);
2842 if (NS_WARN_IF(!fd
.IsInitialized())) {
2843 return NS_ERROR_NOT_INITIALIZED
;
2846 // Only enable if keepalives are globally enabled, but ensure other
2847 // options are set correctly on the fd.
2848 bool enable
= aEnable
&& mSocketTransportService
->IsKeepaliveEnabled();
2850 fd
.SetKeepaliveVals(enable
, mKeepaliveIdleTimeS
, mKeepaliveRetryIntervalS
,
2851 mKeepaliveProbeCount
);
2852 if (NS_WARN_IF(NS_FAILED(rv
))) {
2853 SOCKET_LOG((" SetKeepaliveVals failed rv[0x%" PRIx32
"]",
2854 static_cast<uint32_t>(rv
)));
2857 rv
= fd
.SetKeepaliveEnabled(enable
);
2858 if (NS_WARN_IF(NS_FAILED(rv
))) {
2859 SOCKET_LOG((" SetKeepaliveEnabled failed rv[0x%" PRIx32
"]",
2860 static_cast<uint32_t>(rv
)));
2867 nsSocketTransport::GetKeepaliveEnabled(bool* aResult
) {
2868 MOZ_ASSERT(aResult
);
2870 *aResult
= mKeepaliveEnabled
;
2874 nsresult
nsSocketTransport::EnsureKeepaliveValsAreInitialized() {
2875 nsresult rv
= NS_OK
;
2877 if (mKeepaliveIdleTimeS
== -1) {
2878 rv
= mSocketTransportService
->GetKeepaliveIdleTime(&val
);
2879 if (NS_WARN_IF(NS_FAILED(rv
))) {
2882 mKeepaliveIdleTimeS
= val
;
2884 if (mKeepaliveRetryIntervalS
== -1) {
2885 rv
= mSocketTransportService
->GetKeepaliveRetryInterval(&val
);
2886 if (NS_WARN_IF(NS_FAILED(rv
))) {
2889 mKeepaliveRetryIntervalS
= val
;
2891 if (mKeepaliveProbeCount
== -1) {
2892 rv
= mSocketTransportService
->GetKeepaliveProbeCount(&val
);
2893 if (NS_WARN_IF(NS_FAILED(rv
))) {
2896 mKeepaliveProbeCount
= val
;
2902 nsSocketTransport::SetKeepaliveEnabled(bool aEnable
) {
2903 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
2904 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2906 if (aEnable
== mKeepaliveEnabled
) {
2907 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.", this,
2908 aEnable
? "enabled" : "disabled"));
2912 nsresult rv
= NS_OK
;
2914 rv
= EnsureKeepaliveValsAreInitialized();
2915 if (NS_WARN_IF(NS_FAILED(rv
))) {
2917 (" SetKeepaliveEnabled [%p] "
2918 "error [0x%" PRIx32
"] initializing keepalive vals",
2919 this, static_cast<uint32_t>(rv
)));
2924 ("nsSocketTransport::SetKeepaliveEnabled [%p] "
2925 "%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
2927 this, aEnable
? "enabled" : "disabled", mKeepaliveIdleTimeS
,
2928 mKeepaliveRetryIntervalS
, mKeepaliveProbeCount
,
2929 mSocketTransportService
->IsKeepaliveEnabled() ? "enabled" : "disabled"));
2931 // Set mKeepaliveEnabled here so that state is maintained; it is possible
2932 // that we're in between fds, e.g. the 1st IP address failed, so we're about
2933 // to retry on a 2nd from the DNS record.
2934 mKeepaliveEnabled
= aEnable
;
2936 rv
= SetKeepaliveEnabledInternal(aEnable
);
2937 if (NS_WARN_IF(NS_FAILED(rv
))) {
2938 SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%" PRIx32
"]",
2939 static_cast<uint32_t>(rv
)));
2944 #else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
2945 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
2946 return NS_ERROR_NOT_IMPLEMENTED
;
2951 nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime
, int32_t aRetryInterval
) {
2952 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
2953 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2954 if (NS_WARN_IF(aIdleTime
<= 0 || kMaxTCPKeepIdle
< aIdleTime
)) {
2955 return NS_ERROR_INVALID_ARG
;
2957 if (NS_WARN_IF(aRetryInterval
<= 0 || kMaxTCPKeepIntvl
< aRetryInterval
)) {
2958 return NS_ERROR_INVALID_ARG
;
2961 if (aIdleTime
== mKeepaliveIdleTimeS
&&
2962 aRetryInterval
== mKeepaliveRetryIntervalS
) {
2964 ("nsSocketTransport::SetKeepaliveVals [%p] idle time "
2965 "already %ds and retry interval already %ds.",
2966 this, mKeepaliveIdleTimeS
, mKeepaliveRetryIntervalS
));
2969 mKeepaliveIdleTimeS
= aIdleTime
;
2970 mKeepaliveRetryIntervalS
= aRetryInterval
;
2972 nsresult rv
= NS_OK
;
2973 if (mKeepaliveProbeCount
== -1) {
2975 nsresult rv
= mSocketTransportService
->GetKeepaliveProbeCount(&val
);
2976 if (NS_WARN_IF(NS_FAILED(rv
))) {
2979 mKeepaliveProbeCount
= val
;
2983 ("nsSocketTransport::SetKeepaliveVals [%p] "
2984 "keepalive %s, idle time[%ds] retry interval[%ds] "
2986 this, mKeepaliveEnabled
? "enabled" : "disabled", mKeepaliveIdleTimeS
,
2987 mKeepaliveRetryIntervalS
, mKeepaliveProbeCount
));
2989 PRFileDescAutoLock
fd(this);
2990 if (NS_WARN_IF(!fd
.IsInitialized())) {
2991 return NS_ERROR_NULL_POINTER
;
2994 rv
= fd
.SetKeepaliveVals(mKeepaliveEnabled
, mKeepaliveIdleTimeS
,
2995 mKeepaliveRetryIntervalS
, mKeepaliveProbeCount
);
2996 if (NS_WARN_IF(NS_FAILED(rv
))) {
3001 SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
3002 return NS_ERROR_NOT_IMPLEMENTED
;
3006 #ifdef ENABLE_SOCKET_TRACING
3012 static void DumpBytesToFile(const char* path
, const char* header
,
3013 const char* buf
, int32_t n
) {
3014 FILE* fp
= fopen(path
, "a");
3016 fprintf(fp
, "\n%s [%d bytes]\n", header
, n
);
3018 const unsigned char* p
;
3020 p
= (const unsigned char*)buf
;
3022 int32_t i
, row_max
= std::min(16, n
);
3024 for (i
= 0; i
< row_max
; ++i
) fprintf(fp
, "%02x ", *p
++);
3025 for (i
= row_max
; i
< 16; ++i
) fprintf(fp
, " ");
3027 p
= (const unsigned char*)buf
;
3028 for (i
= 0; i
< row_max
; ++i
, ++p
) {
3030 fprintf(fp
, "%c", *p
);
3044 void nsSocketTransport::TraceInBuf(const char* buf
, int32_t n
) {
3045 char* val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
3046 if (!val
|| !*val
) return;
3048 nsAutoCString header
;
3049 header
.AssignLiteral("Reading from: ");
3050 header
.Append(mHost
);
3052 header
.AppendInt(mPort
);
3054 DumpBytesToFile(val
, header
.get(), buf
, n
);
3057 void nsSocketTransport::TraceOutBuf(const char* buf
, int32_t n
) {
3058 char* val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
3059 if (!val
|| !*val
) return;
3061 nsAutoCString header
;
3062 header
.AssignLiteral("Writing to: ");
3063 header
.Append(mHost
);
3065 header
.AppendInt(mPort
);
3067 DumpBytesToFile(val
, header
.get(), buf
, n
);
3072 static void LogNSPRError(const char* aPrefix
, const void* aObjPtr
) {
3074 PRErrorCode errCode
= PR_GetError();
3075 int errLen
= PR_GetErrorTextLength();
3076 nsAutoCString errStr
;
3078 errStr
.SetLength(errLen
);
3079 PR_GetErrorText(errStr
.BeginWriting());
3082 nsPrintfCString("%s [%p] NSPR error[0x%x] %s.",
3083 aPrefix
? aPrefix
: "nsSocketTransport", aObjPtr
, errCode
,
3084 errLen
> 0 ? errStr
.BeginReading() : "<no error text>")
3089 nsresult
nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(
3091 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3092 MOZ_ASSERT(!(aEnable
&& !gSocketTransportService
->IsKeepaliveEnabled()),
3093 "Cannot enable keepalive if global pref is disabled!");
3094 if (aEnable
&& !gSocketTransportService
->IsKeepaliveEnabled()) {
3095 return NS_ERROR_ILLEGAL_VALUE
;
3098 PRSocketOptionData opt
;
3100 opt
.option
= PR_SockOpt_Keepalive
;
3101 opt
.value
.keep_alive
= aEnable
;
3102 PRStatus status
= PR_SetSocketOption(mFd
, &opt
);
3103 if (NS_WARN_IF(status
!= PR_SUCCESS
)) {
3104 LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
3106 return ErrorAccordingToNSPR(PR_GetError());
3111 static void LogOSError(const char* aPrefix
, const void* aObjPtr
) {
3113 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3116 DWORD errCode
= WSAGetLastError();
3118 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
|
3119 FORMAT_MESSAGE_IGNORE_INSERTS
,
3120 NULL
, errCode
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
3121 (LPSTR
)&errMessage
, 0, NULL
);
3122 NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%lx] %s",
3123 aPrefix
? aPrefix
: "nsSocketTransport", aObjPtr
,
3125 errMessage
? errMessage
: "<no error text>")
3127 LocalFree(errMessage
);
3129 int errCode
= errno
;
3130 char* errMessage
= strerror(errno
);
3131 NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%x] %s",
3132 aPrefix
? aPrefix
: "nsSocketTransport", aObjPtr
,
3134 errMessage
? errMessage
: "<no error text>")
3140 /* XXX PR_SetSockOpt does not support setting keepalive values, so native
3141 * handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
3142 * file. Requires inclusion of NSPR private/pprio.h, and platform headers.
3145 nsresult
nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(
3146 bool aEnabled
, int aIdleTime
, int aRetryInterval
, int aProbeCount
) {
3147 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
3148 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3149 if (NS_WARN_IF(aIdleTime
<= 0 || kMaxTCPKeepIdle
< aIdleTime
)) {
3150 return NS_ERROR_INVALID_ARG
;
3152 if (NS_WARN_IF(aRetryInterval
<= 0 || kMaxTCPKeepIntvl
< aRetryInterval
)) {
3153 return NS_ERROR_INVALID_ARG
;
3155 if (NS_WARN_IF(aProbeCount
<= 0 || kMaxTCPKeepCount
< aProbeCount
)) {
3156 return NS_ERROR_INVALID_ARG
;
3159 PROsfd sock
= PR_FileDesc2NativeHandle(mFd
);
3160 if (NS_WARN_IF(sock
== -1)) {
3161 LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
3163 return ErrorAccordingToNSPR(PR_GetError());
3168 // Windows allows idle time and retry interval to be set; NOT ping count.
3169 struct tcp_keepalive keepalive_vals
= {(u_long
)aEnabled
,
3170 // Windows uses msec.
3171 (u_long
)(aIdleTime
* 1000UL),
3172 (u_long
)(aRetryInterval
* 1000UL)};
3173 DWORD bytes_returned
;
3175 WSAIoctl(sock
, SIO_KEEPALIVE_VALS
, &keepalive_vals
,
3176 sizeof(keepalive_vals
), NULL
, 0, &bytes_returned
, NULL
, NULL
);
3177 if (NS_WARN_IF(err
)) {
3178 LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport
);
3179 return NS_ERROR_UNEXPECTED
;
3183 #elif defined(XP_DARWIN)
3184 // Darwin uses sec; only supports idle time being set.
3185 int err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPALIVE
, &aIdleTime
,
3187 if (NS_WARN_IF(err
)) {
3188 LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
3190 return NS_ERROR_UNEXPECTED
;
3194 #elif defined(XP_UNIX)
3195 // Not all *nix OSes support the following setsockopt() options
3196 // ... but we assume they are supported in the Android kernel;
3197 // build errors will tell us if they are not.
3198 # if defined(ANDROID) || defined(TCP_KEEPIDLE)
3199 // Idle time until first keepalive probe; interval between ack'd probes;
3201 int err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPIDLE
, &aIdleTime
,
3203 if (NS_WARN_IF(err
)) {
3204 LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
3206 return NS_ERROR_UNEXPECTED
;
3210 # if defined(ANDROID) || defined(TCP_KEEPINTVL)
3211 // Interval between unack'd keepalive probes; seconds.
3212 err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPINTVL
, &aRetryInterval
,
3213 sizeof(aRetryInterval
));
3214 if (NS_WARN_IF(err
)) {
3215 LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
3217 return NS_ERROR_UNEXPECTED
;
3221 # if defined(ANDROID) || defined(TCP_KEEPCNT)
3222 // Number of unack'd keepalive probes before connection times out.
3223 err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPCNT
, &aProbeCount
,
3224 sizeof(aProbeCount
));
3225 if (NS_WARN_IF(err
)) {
3226 LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
3228 return NS_ERROR_UNEXPECTED
;
3235 "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
3236 "called on unsupported platform!");
3237 return NS_ERROR_UNEXPECTED
;
3241 void nsSocketTransport::CloseSocket(PRFileDesc
* aFd
, bool aTelemetryEnabled
) {
3243 AttachShutdownLayer(aFd
);
3246 // We use PRIntervalTime here because we need
3247 // nsIOService::LastOfflineStateChange time and
3248 // nsIOService::LastConectivityChange time to be atomic.
3249 PRIntervalTime closeStarted
;
3250 if (aTelemetryEnabled
) {
3251 closeStarted
= PR_IntervalNow();
3256 if (aTelemetryEnabled
) {
3257 SendPRBlockingTelemetry(
3258 closeStarted
, Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL
,
3259 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN
,
3260 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
3261 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE
,
3262 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_OFFLINE
);
3266 void nsSocketTransport::SendPRBlockingTelemetry(
3267 PRIntervalTime aStart
, Telemetry::HistogramID aIDNormal
,
3268 Telemetry::HistogramID aIDShutdown
,
3269 Telemetry::HistogramID aIDConnectivityChange
,
3270 Telemetry::HistogramID aIDLinkChange
, Telemetry::HistogramID aIDOffline
) {
3271 PRIntervalTime now
= PR_IntervalNow();
3272 if (gIOService
->IsNetTearingDown()) {
3273 Telemetry::Accumulate(aIDShutdown
, PR_IntervalToMilliseconds(now
- aStart
));
3275 } else if (PR_IntervalToSeconds(now
- gIOService
->LastConnectivityChange()) <
3277 Telemetry::Accumulate(aIDConnectivityChange
,
3278 PR_IntervalToMilliseconds(now
- aStart
));
3279 } else if (PR_IntervalToSeconds(now
- gIOService
->LastNetworkLinkChange()) <
3281 Telemetry::Accumulate(aIDLinkChange
,
3282 PR_IntervalToMilliseconds(now
- aStart
));
3284 } else if (PR_IntervalToSeconds(now
- gIOService
->LastOfflineStateChange()) <
3286 Telemetry::Accumulate(aIDOffline
, PR_IntervalToMilliseconds(now
- aStart
));
3288 Telemetry::Accumulate(aIDNormal
, PR_IntervalToMilliseconds(now
- aStart
));
3293 nsSocketTransport::GetResetIPFamilyPreference(bool* aReset
) {
3294 *aReset
= mResetFamilyPreference
;
3299 nsSocketTransport::GetEchConfigUsed(bool* aEchConfigUsed
) {
3300 *aEchConfigUsed
= mEchConfigUsed
;
3305 nsSocketTransport::SetEchConfig(const nsACString
& aEchConfig
) {
3306 mEchConfig
= aEchConfig
;
3311 nsSocketTransport::ResolvedByTRR(bool* aResolvedByTRR
) {
3312 *aResolvedByTRR
= mResolvedByTRR
;
3317 nsSocketTransport::GetRetryDnsIfPossible(bool* aRetryDns
) {
3318 *aRetryDns
= mRetryDnsIfPossible
;
3323 nsSocketTransport::GetStatus(nsresult
* aStatus
) {
3324 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3326 *aStatus
= mCondition
;
3331 } // namespace mozilla