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/. */
7 #include "nsSocketTransport2.h"
9 #include "mozilla/Attributes.h"
10 #include "mozilla/Telemetry.h"
11 #include "nsIOService.h"
12 #include "nsStreamUtils.h"
13 #include "nsNetSegmentUtils.h"
14 #include "nsNetAddr.h"
15 #include "nsTransportUtils.h"
16 #include "nsProxyInfo.h"
18 #include "nsNetUtil.h"
19 #include "nsAutoPtr.h"
23 #include "IOActivityMonitor.h"
24 #include "NSSErrorsService.h"
25 #include "mozilla/dom/ToJSValue.h"
26 #include "mozilla/net/NeckoChild.h"
27 #include "nsThreadUtils.h"
28 #include "nsSocketProviderService.h"
29 #include "nsISocketProvider.h"
30 #include "nsISSLSocketControl.h"
32 #include "nsIClassInfoImpl.h"
33 #include "nsURLHelper.h"
34 #include "nsIDNSService.h"
35 #include "nsIDNSRecord.h"
36 #include "nsIDNSByTypeRecord.h"
37 #include "nsICancelable.h"
38 #include "TCPFastOpenLayer.h"
41 #include "nsPrintfCString.h"
42 #include "xpcpublic.h"
45 # include "ShutdownLayer.h"
48 /* Following inclusions required for keepalive config not supported by NSPR. */
49 #include "private/pprio.h"
51 # include <winsock2.h>
53 #elif defined(XP_UNIX)
55 # include <netinet/tcp.h>
57 /* End keepalive config inclusions. */
59 #define SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 0
60 #define UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 1
61 #define SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 2
62 #define UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 3
64 //-----------------------------------------------------------------------------
66 static NS_DEFINE_CID(kDNSServiceCID
, NS_DNSSERVICE_CID
);
68 //-----------------------------------------------------------------------------
73 class nsSocketEvent
: public Runnable
{
75 nsSocketEvent(nsSocketTransport
*transport
, uint32_t type
,
76 nsresult status
= NS_OK
, nsISupports
*param
= nullptr)
77 : Runnable("net::nsSocketEvent"),
78 mTransport(transport
),
83 NS_IMETHOD
Run() override
{
84 mTransport
->OnSocketEvent(mType
, mStatus
, mParam
);
89 RefPtr
<nsSocketTransport
> mTransport
;
93 nsCOMPtr
<nsISupports
> mParam
;
96 //-----------------------------------------------------------------------------
98 //#define TEST_CONNECT_ERRORS
99 #ifdef TEST_CONNECT_ERRORS
101 static PRErrorCode
RandomizeConnectError(PRErrorCode code
) {
103 // To test out these errors, load http://www.yahoo.com/. It should load
104 // correctly despite the random occurrence of these errors.
107 if (n
> RAND_MAX
/ 2) {
109 PRErrorCode err_code
;
110 const char *err_name
;
113 // These errors should be recoverable provided there is another
114 // IP address in mDNSRecord.
116 {PR_CONNECT_REFUSED_ERROR
, "PR_CONNECT_REFUSED_ERROR"},
117 {PR_CONNECT_TIMEOUT_ERROR
, "PR_CONNECT_TIMEOUT_ERROR"},
119 // This error will cause this socket transport to error out;
120 // however, if the consumer is HTTP, then the HTTP transaction
121 // should be restarted when this error occurs.
123 {PR_CONNECT_RESET_ERROR
, "PR_CONNECT_RESET_ERROR"},
125 n
= n
% (sizeof(errors
) / sizeof(errors
[0]));
126 code
= errors
[n
].err_code
;
127 SOCKET_LOG(("simulating NSPR error %d [%s]\n", code
, errors
[n
].err_name
));
133 //-----------------------------------------------------------------------------
135 nsresult
ErrorAccordingToNSPR(PRErrorCode errorCode
) {
136 nsresult rv
= NS_ERROR_FAILURE
;
138 case PR_WOULD_BLOCK_ERROR
:
139 rv
= NS_BASE_STREAM_WOULD_BLOCK
;
141 case PR_CONNECT_ABORTED_ERROR
:
142 case PR_CONNECT_RESET_ERROR
:
143 rv
= NS_ERROR_NET_RESET
;
145 case PR_END_OF_FILE_ERROR
: // XXX document this correlation
146 rv
= NS_ERROR_NET_INTERRUPT
;
148 case PR_CONNECT_REFUSED_ERROR
:
149 // We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
150 // could get better diagnostics by adding distinct XPCOM error codes for
151 // each of these, but there are a lot of places in Gecko that check
152 // specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
154 case PR_NETWORK_UNREACHABLE_ERROR
:
155 case PR_HOST_UNREACHABLE_ERROR
:
156 case PR_ADDRESS_NOT_AVAILABLE_ERROR
:
157 // Treat EACCES as a soft error since (at least on Linux) connect() returns
158 // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
159 case PR_NO_ACCESS_RIGHTS_ERROR
:
160 rv
= NS_ERROR_CONNECTION_REFUSED
;
162 case PR_ADDRESS_NOT_SUPPORTED_ERROR
:
163 rv
= NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
165 case PR_IO_TIMEOUT_ERROR
:
166 case PR_CONNECT_TIMEOUT_ERROR
:
167 rv
= NS_ERROR_NET_TIMEOUT
;
169 case PR_OUT_OF_MEMORY_ERROR
:
170 // These really indicate that the descriptor table filled up, or that the
171 // kernel ran out of network buffers - but nobody really cares which part of
172 // the system ran out of memory.
173 case PR_PROC_DESC_TABLE_FULL_ERROR
:
174 case PR_SYS_DESC_TABLE_FULL_ERROR
:
175 case PR_INSUFFICIENT_RESOURCES_ERROR
:
176 rv
= NS_ERROR_OUT_OF_MEMORY
;
178 case PR_ADDRESS_IN_USE_ERROR
:
179 rv
= NS_ERROR_SOCKET_ADDRESS_IN_USE
;
181 // These filename-related errors can arise when using Unix-domain sockets.
182 case PR_FILE_NOT_FOUND_ERROR
:
183 rv
= NS_ERROR_FILE_NOT_FOUND
;
185 case PR_IS_DIRECTORY_ERROR
:
186 rv
= NS_ERROR_FILE_IS_DIRECTORY
;
189 rv
= NS_ERROR_FILE_UNRESOLVABLE_SYMLINK
;
191 case PR_NAME_TOO_LONG_ERROR
:
192 rv
= NS_ERROR_FILE_NAME_TOO_LONG
;
194 case PR_NO_DEVICE_SPACE_ERROR
:
195 rv
= NS_ERROR_FILE_NO_DEVICE_SPACE
;
197 case PR_NOT_DIRECTORY_ERROR
:
198 rv
= NS_ERROR_FILE_NOT_DIRECTORY
;
200 case PR_READ_ONLY_FILESYSTEM_ERROR
:
201 rv
= NS_ERROR_FILE_READ_ONLY
;
203 case PR_BAD_ADDRESS_ERROR
:
204 rv
= NS_ERROR_UNKNOWN_HOST
;
207 if (psm::IsNSSErrorCode(errorCode
)) {
208 rv
= psm::GetXPCOMFromNSSError(errorCode
);
212 // NSPR's socket code can return these, but they're not worth breaking out
213 // into their own error codes, distinct from NS_ERROR_FAILURE:
215 // PR_BAD_DESCRIPTOR_ERROR
216 // PR_INVALID_ARGUMENT_ERROR
217 // PR_NOT_SOCKET_ERROR
218 // PR_NOT_TCP_SOCKET_ERROR
219 // These would indicate a bug internal to the component.
221 // PR_PROTOCOL_NOT_SUPPORTED_ERROR
222 // This means that we can't use the given "protocol" (like
223 // IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
224 // above, this indicates an internal bug.
226 // PR_IS_CONNECTED_ERROR
227 // This indicates that we've applied a system call like 'bind' or
228 // 'connect' to a socket that is already connected. The socket
229 // components manage each file descriptor's state, and in some cases
230 // handle this error result internally. We shouldn't be returning
231 // this to our callers.
234 // This is so vague that NS_ERROR_FAILURE is just as good.
236 SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%" PRIx32
"]\n", errorCode
,
237 static_cast<uint32_t>(rv
)));
241 //-----------------------------------------------------------------------------
242 // socket input stream impl
243 //-----------------------------------------------------------------------------
245 nsSocketInputStream::nsSocketInputStream(nsSocketTransport
*trans
)
252 // called on the socket transport thread...
254 // condition : failure code if socket has been closed
256 void nsSocketInputStream::OnSocketReady(nsresult condition
) {
257 SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%" PRIx32
"]\n",
258 this, static_cast<uint32_t>(condition
)));
260 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
262 nsCOMPtr
<nsIInputStreamCallback
> callback
;
264 MutexAutoLock
lock(mTransport
->mLock
);
266 // update condition, but be careful not to erase an already
267 // existing error condition.
268 if (NS_SUCCEEDED(mCondition
)) mCondition
= condition
;
270 // ignore event if only waiting for closure and not closed.
271 if (NS_FAILED(mCondition
) || !(mCallbackFlags
& WAIT_CLOSURE_ONLY
)) {
272 callback
= mCallback
.forget();
277 if (callback
) callback
->OnInputStreamReady(this);
280 NS_IMPL_QUERY_INTERFACE(nsSocketInputStream
, nsIInputStream
,
283 NS_IMETHODIMP_(MozExternalRefCountType
)
284 nsSocketInputStream::AddRef() {
286 return mTransport
->AddRef();
289 NS_IMETHODIMP_(MozExternalRefCountType
)
290 nsSocketInputStream::Release() {
291 if (--mReaderRefCnt
== 0) Close();
292 return mTransport
->Release();
296 nsSocketInputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED
); }
299 nsSocketInputStream::Available(uint64_t *avail
) {
300 SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
306 MutexAutoLock
lock(mTransport
->mLock
);
308 if (NS_FAILED(mCondition
)) return mCondition
;
310 fd
= mTransport
->GetFD_Locked();
311 if (!fd
) return NS_OK
;
314 // cannot hold lock while calling NSPR. (worried about the fact that PSM
315 // synchronously proxies notifications over to the UI thread, which could
316 // mistakenly try to re-enter this code.)
317 int32_t n
= PR_Available(fd
);
319 // PSM does not implement PR_Available() so do a best approximation of it
321 if ((n
== -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR
)) {
324 n
= PR_Recv(fd
, &c
, 1, PR_MSG_PEEK
, 0);
326 ("nsSocketInputStream::Available [this=%p] "
327 "using PEEK backup n=%d]\n",
333 MutexAutoLock
lock(mTransport
->mLock
);
335 mTransport
->ReleaseFD_Locked(fd
);
340 PRErrorCode code
= PR_GetError();
341 if (code
== PR_WOULD_BLOCK_ERROR
) return NS_OK
;
342 mCondition
= ErrorAccordingToNSPR(code
);
346 if (NS_FAILED(rv
)) mTransport
->OnInputClosed(rv
);
351 nsSocketInputStream::Read(char *buf
, uint32_t count
, uint32_t *countRead
) {
352 SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count
));
356 PRFileDesc
*fd
= nullptr;
358 MutexAutoLock
lock(mTransport
->mLock
);
360 if (NS_FAILED(mCondition
))
361 return (mCondition
== NS_BASE_STREAM_CLOSED
) ? NS_OK
: mCondition
;
363 fd
= mTransport
->GetFD_Locked();
364 if (!fd
) return NS_BASE_STREAM_WOULD_BLOCK
;
367 SOCKET_LOG((" calling PR_Read [count=%u]\n", count
));
369 // cannot hold lock while calling NSPR. (worried about the fact that PSM
370 // synchronously proxies notifications over to the UI thread, which could
371 // mistakenly try to re-enter this code.)
372 int32_t n
= PR_Read(fd
, buf
, count
);
374 SOCKET_LOG((" PR_Read returned [n=%d]\n", n
));
378 MutexAutoLock
lock(mTransport
->mLock
);
380 #ifdef ENABLE_SOCKET_TRACING
381 if (n
> 0) mTransport
->TraceInBuf(buf
, n
);
384 mTransport
->ReleaseFD_Locked(fd
);
387 mByteCount
+= (*countRead
= n
);
389 PRErrorCode code
= PR_GetError();
390 if (code
== PR_WOULD_BLOCK_ERROR
) return NS_BASE_STREAM_WOULD_BLOCK
;
391 mCondition
= ErrorAccordingToNSPR(code
);
395 if (NS_FAILED(rv
)) mTransport
->OnInputClosed(rv
);
397 // only send this notification if we have indeed read some data.
398 // see bug 196827 for an example of why this is important.
399 if (n
> 0) mTransport
->SendStatus(NS_NET_STATUS_RECEIVING_FROM
);
404 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer
, void *closure
,
405 uint32_t count
, uint32_t *countRead
) {
406 // socket stream is unbuffered
407 return NS_ERROR_NOT_IMPLEMENTED
;
411 nsSocketInputStream::IsNonBlocking(bool *nonblocking
) {
417 nsSocketInputStream::CloseWithStatus(nsresult reason
) {
418 SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%" PRIx32
420 this, static_cast<uint32_t>(reason
)));
422 // may be called from any thread
426 MutexAutoLock
lock(mTransport
->mLock
);
428 if (NS_SUCCEEDED(mCondition
))
429 rv
= mCondition
= reason
;
433 if (NS_FAILED(rv
)) mTransport
->OnInputClosed(rv
);
438 nsSocketInputStream::AsyncWait(nsIInputStreamCallback
*callback
, uint32_t flags
,
439 uint32_t amount
, nsIEventTarget
*target
) {
440 SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
442 bool hasError
= false;
444 MutexAutoLock
lock(mTransport
->mLock
);
446 if (callback
&& target
) {
450 mCallback
= NS_NewInputStreamReadyEvent("nsSocketInputStream::AsyncWait",
453 mCallback
= callback
;
454 mCallbackFlags
= flags
;
456 hasError
= NS_FAILED(mCondition
);
457 } // unlock mTransport->mLock
460 // OnSocketEvent will call OnInputStreamReady with an error code after
461 // going through the event loop. We do this because most socket callers
462 // do not expect AsyncWait() to synchronously execute the OnInputStreamReady
464 mTransport
->PostEvent(nsSocketTransport::MSG_INPUT_PENDING
);
466 mTransport
->OnInputPending();
472 //-----------------------------------------------------------------------------
473 // socket output stream impl
474 //-----------------------------------------------------------------------------
476 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport
*trans
)
483 // called on the socket transport thread...
485 // condition : failure code if socket has been closed
487 void nsSocketOutputStream::OnSocketReady(nsresult condition
) {
488 SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%" PRIx32
490 this, static_cast<uint32_t>(condition
)));
492 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
494 nsCOMPtr
<nsIOutputStreamCallback
> callback
;
496 MutexAutoLock
lock(mTransport
->mLock
);
498 // update condition, but be careful not to erase an already
499 // existing error condition.
500 if (NS_SUCCEEDED(mCondition
)) mCondition
= condition
;
502 // ignore event if only waiting for closure and not closed.
503 if (NS_FAILED(mCondition
) || !(mCallbackFlags
& WAIT_CLOSURE_ONLY
)) {
504 callback
= mCallback
.forget();
509 if (callback
) callback
->OnOutputStreamReady(this);
512 NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream
, nsIOutputStream
,
513 nsIAsyncOutputStream
)
515 NS_IMETHODIMP_(MozExternalRefCountType
)
516 nsSocketOutputStream::AddRef() {
518 return mTransport
->AddRef();
521 NS_IMETHODIMP_(MozExternalRefCountType
)
522 nsSocketOutputStream::Release() {
523 if (--mWriterRefCnt
== 0) Close();
524 return mTransport
->Release();
528 nsSocketOutputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED
); }
531 nsSocketOutputStream::Flush() { return NS_OK
; }
534 nsSocketOutputStream::Write(const char *buf
, uint32_t count
,
535 uint32_t *countWritten
) {
536 SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n", this, count
));
540 // A write of 0 bytes can be used to force the initial SSL handshake, so do
543 PRFileDesc
*fd
= nullptr;
544 bool fastOpenInProgress
;
546 MutexAutoLock
lock(mTransport
->mLock
);
548 if (NS_FAILED(mCondition
)) return mCondition
;
550 fd
= mTransport
->GetFD_LockedAlsoDuringFastOpen();
551 if (!fd
) return NS_BASE_STREAM_WOULD_BLOCK
;
553 fastOpenInProgress
= mTransport
->FastOpenInProgress();
556 if (fastOpenInProgress
) {
557 // If we are in the fast open phase, we should not write more data
558 // than TCPFastOpenLayer can accept. If we write more data, this data
559 // will be buffered in tls and we want to avoid that.
560 uint32_t availableSpace
= TCPFastOpenGetBufferSizeLeft(fd
);
561 count
= (count
> availableSpace
) ? availableSpace
: count
;
564 MutexAutoLock
lock(mTransport
->mLock
);
565 mTransport
->ReleaseFD_Locked(fd
);
567 return NS_BASE_STREAM_WOULD_BLOCK
;
571 SOCKET_LOG((" calling PR_Write [count=%u]\n", count
));
573 // cannot hold lock while calling NSPR. (worried about the fact that PSM
574 // synchronously proxies notifications over to the UI thread, which could
575 // mistakenly try to re-enter this code.)
576 int32_t n
= PR_Write(fd
, buf
, count
);
578 SOCKET_LOG((" PR_Write returned [n=%d]\n", n
));
582 MutexAutoLock
lock(mTransport
->mLock
);
584 #ifdef ENABLE_SOCKET_TRACING
585 if (n
> 0) mTransport
->TraceOutBuf(buf
, n
);
588 mTransport
->ReleaseFD_Locked(fd
);
591 mByteCount
+= (*countWritten
= n
);
593 PRErrorCode code
= PR_GetError();
594 if (code
== PR_WOULD_BLOCK_ERROR
) return NS_BASE_STREAM_WOULD_BLOCK
;
595 mCondition
= ErrorAccordingToNSPR(code
);
599 if (NS_FAILED(rv
)) mTransport
->OnOutputClosed(rv
);
601 // only send this notification if we have indeed written some data.
602 // see bug 196827 for an example of why this is important.
603 // During a fast open we are actually not sending data, the data will be
604 // only buffered in the TCPFastOpenLayer. Therefore we will call
605 // SendStatus(NS_NET_STATUS_SENDING_TO) when we really send data (i.e. when
606 // TCPFastOpenFinish is called.
607 if ((n
> 0) && !fastOpenInProgress
) {
608 mTransport
->SendStatus(NS_NET_STATUS_SENDING_TO
);
615 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader
, void *closure
,
616 uint32_t count
, uint32_t *countRead
) {
617 // socket stream is unbuffered
618 return NS_ERROR_NOT_IMPLEMENTED
;
621 nsresult
nsSocketOutputStream::WriteFromSegments(
622 nsIInputStream
*input
, void *closure
, const char *fromSegment
,
623 uint32_t offset
, uint32_t count
, uint32_t *countRead
) {
624 nsSocketOutputStream
*self
= (nsSocketOutputStream
*)closure
;
625 return self
->Write(fromSegment
, count
, countRead
);
629 nsSocketOutputStream::WriteFrom(nsIInputStream
*stream
, uint32_t count
,
630 uint32_t *countRead
) {
631 return stream
->ReadSegments(WriteFromSegments
, this, count
, countRead
);
635 nsSocketOutputStream::IsNonBlocking(bool *nonblocking
) {
641 nsSocketOutputStream::CloseWithStatus(nsresult reason
) {
642 SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%" PRIx32
644 this, static_cast<uint32_t>(reason
)));
646 // may be called from any thread
650 MutexAutoLock
lock(mTransport
->mLock
);
652 if (NS_SUCCEEDED(mCondition
))
653 rv
= mCondition
= reason
;
657 if (NS_FAILED(rv
)) mTransport
->OnOutputClosed(rv
);
662 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback
*callback
,
663 uint32_t flags
, uint32_t amount
,
664 nsIEventTarget
*target
) {
665 SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n", this));
668 MutexAutoLock
lock(mTransport
->mLock
);
670 if (callback
&& target
) {
674 mCallback
= NS_NewOutputStreamReadyEvent(callback
, target
);
676 mCallback
= callback
;
678 mCallbackFlags
= flags
;
680 mTransport
->OnOutputPending();
684 //-----------------------------------------------------------------------------
685 // socket transport impl
686 //-----------------------------------------------------------------------------
688 nsSocketTransport::nsSocketTransport()
694 mProxyTransparent(false),
695 mProxyTransparentResolvesHost(false),
698 mResetFamilyPreference(false),
700 mReuseAddrPort(false),
701 mState(STATE_CLOSED
),
706 mDNSLookupStatus(NS_OK
),
707 mDNSARequestFinished(0),
710 mNetAddrIsSet(false),
711 mSelfAddrIsSet(false),
712 mLock("nsSocketTransport.mLock"),
716 mFDFastOpenInProgress(false),
717 mSocketTransportService(gSocketTransportService
),
720 mLingerPolarity(false),
723 mKeepaliveEnabled(false),
724 mKeepaliveIdleTimeS(-1),
725 mKeepaliveRetryIntervalS(-1),
726 mKeepaliveProbeCount(-1),
727 mFastOpenCallback(nullptr),
728 mFastOpenLayerHasBufferedData(false),
729 mFastOpenStatus(TFO_NOT_SET
),
730 mFirstRetryError(NS_OK
),
731 mDoNotRetryToConnect(false) {
732 this->mNetAddr
.raw
.family
= 0;
733 this->mNetAddr
.inet
= {};
734 this->mSelfAddr
.raw
.family
= 0;
735 this->mSelfAddr
.inet
= {};
736 SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
738 mTimeouts
[TIMEOUT_CONNECT
] = UINT16_MAX
; // no timeout
739 mTimeouts
[TIMEOUT_READ_WRITE
] = UINT16_MAX
; // no timeout
742 nsSocketTransport::~nsSocketTransport() {
743 SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
748 void nsSocketTransport::CleanupTypes() {
749 // cleanup socket type info
751 for (uint32_t i
= 0; i
< mTypeCount
; ++i
) {
752 PL_strfree(mTypes
[i
]);
760 nsresult
nsSocketTransport::Init(const char **types
, uint32_t typeCount
,
761 const nsACString
&host
, uint16_t port
,
762 const nsACString
&hostRoute
,
764 nsIProxyInfo
*givenProxyInfo
) {
765 nsCOMPtr
<nsProxyInfo
> proxyInfo
;
766 if (givenProxyInfo
) {
767 proxyInfo
= do_QueryInterface(givenProxyInfo
);
768 NS_ENSURE_ARG(proxyInfo
);
771 // init socket type info
775 if (!hostRoute
.IsEmpty()) {
784 mHttpsProxy
= proxyInfo
->IsHTTPS();
787 const char *proxyType
= nullptr;
788 mProxyInfo
= proxyInfo
;
790 mProxyPort
= proxyInfo
->Port();
791 mProxyHost
= proxyInfo
->Host();
792 // grab proxy type (looking for "socks" for example)
793 proxyType
= proxyInfo
->Type();
794 if (proxyType
&& (proxyInfo
->IsHTTP() || proxyInfo
->IsHTTPS() ||
795 proxyInfo
->IsDirect() || !strcmp(proxyType
, "unknown"))) {
801 ("nsSocketTransport::Init [this=%p host=%s:%hu origin=%s:%d "
803 this, mHost
.get(), mPort
, mOriginHost
.get(), mOriginPort
,
804 mProxyHost
.get(), mProxyPort
));
806 // include proxy type as a socket type if proxy type is not "http"
807 mTypeCount
= typeCount
+ (proxyType
!= nullptr);
808 if (!mTypeCount
) return NS_OK
;
810 // if we have socket types, then the socket provider service had
813 nsCOMPtr
<nsISocketProviderService
> spserv
=
814 nsSocketProviderService::GetOrCreate();
816 mTypes
= (char **)malloc(mTypeCount
* sizeof(char *));
817 if (!mTypes
) return NS_ERROR_OUT_OF_MEMORY
;
819 // now verify that each socket type has a registered socket provider.
820 for (uint32_t i
= 0, type
= 0; i
< mTypeCount
; ++i
) {
821 // store socket types
822 if (i
== 0 && proxyType
)
823 mTypes
[i
] = PL_strdup(proxyType
);
825 mTypes
[i
] = PL_strdup(types
[type
++]);
829 return NS_ERROR_OUT_OF_MEMORY
;
831 nsCOMPtr
<nsISocketProvider
> provider
;
832 rv
= spserv
->GetSocketProvider(mTypes
[i
], getter_AddRefs(provider
));
834 NS_WARNING("no registered socket provider");
838 // note if socket type corresponds to a transparent proxy
839 // XXX don't hardcode SOCKS here (use proxy info's flags instead).
840 if ((strcmp(mTypes
[i
], "socks") == 0) ||
841 (strcmp(mTypes
[i
], "socks4") == 0)) {
842 mProxyTransparent
= true;
844 if (proxyInfo
->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
) {
845 // we want the SOCKS layer to send the hostname
846 // and port to the proxy and let it do the DNS.
847 mProxyTransparentResolvesHost
= true;
856 nsresult
nsSocketTransport::InitWithFilename(const char *filename
) {
857 return InitWithName(filename
, strlen(filename
));
860 nsresult
nsSocketTransport::InitWithName(const char *name
, size_t length
) {
861 if (length
> sizeof(mNetAddr
.local
.path
) - 1) {
862 return NS_ERROR_FILE_NAME_TOO_LONG
;
865 if (!name
[0] && length
> 1) {
866 // name is abstract address name that is supported on Linux only
867 # if defined(XP_LINUX)
868 mHost
.Assign(name
+ 1, length
- 1);
870 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
873 // The name isn't abstract socket address. So this is Unix domain
874 // socket that has file path.
875 mHost
.Assign(name
, length
);
880 mNetAddr
.local
.family
= AF_LOCAL
;
881 memcpy(mNetAddr
.local
.path
, name
, length
);
882 mNetAddr
.local
.path
[length
] = '\0';
883 mNetAddrIsSet
= true;
889 nsresult
nsSocketTransport::InitWithConnectedSocket(PRFileDesc
*fd
,
890 const NetAddr
*addr
) {
891 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
892 NS_ASSERTION(!mFD
.IsInitialized(), "already initialized");
894 char buf
[kNetAddrMaxCStrBufSize
];
895 NetAddrToString(addr
, buf
, sizeof(buf
));
899 if (addr
->raw
.family
== AF_INET
)
900 port
= addr
->inet
.port
;
901 else if (addr
->raw
.family
== AF_INET6
)
902 port
= addr
->inet6
.port
;
907 memcpy(&mNetAddr
, addr
, sizeof(NetAddr
));
909 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
910 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
911 mState
= STATE_TRANSFERRING
;
913 mNetAddrIsSet
= true;
916 MutexAutoLock
lock(mLock
);
923 // make sure new socket is non-blocking
924 PRSocketOptionData opt
;
925 opt
.option
= PR_SockOpt_Nonblocking
;
926 opt
.value
.non_blocking
= true;
927 PR_SetSocketOption(fd
, &opt
);
930 ("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
931 this, mHost
.get(), mPort
));
933 // jump to InitiateSocket to get ourselves attached to the STS poll list.
934 return PostEvent(MSG_RETRY_INIT_SOCKET
);
937 nsresult
nsSocketTransport::InitWithConnectedSocket(PRFileDesc
*aFD
,
938 const NetAddr
*aAddr
,
939 nsISupports
*aSecInfo
) {
941 return InitWithConnectedSocket(aFD
, aAddr
);
944 nsresult
nsSocketTransport::PostEvent(uint32_t type
, nsresult status
,
945 nsISupports
*param
) {
946 SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%" PRIx32
948 this, type
, static_cast<uint32_t>(status
), param
));
950 nsCOMPtr
<nsIRunnable
> event
= new nsSocketEvent(this, type
, status
, param
);
951 if (!event
) return NS_ERROR_OUT_OF_MEMORY
;
953 return mSocketTransportService
->Dispatch(event
, NS_DISPATCH_NORMAL
);
956 void nsSocketTransport::SendStatus(nsresult status
) {
957 SOCKET_LOG1(("nsSocketTransport::SendStatus [this=%p status=%" PRIx32
"]\n",
958 this, static_cast<uint32_t>(status
)));
960 nsCOMPtr
<nsITransportEventSink
> sink
;
963 MutexAutoLock
lock(mLock
);
966 case NS_NET_STATUS_SENDING_TO
:
967 progress
= mOutput
.ByteCount();
968 // If Fast Open is used, we buffer some data in TCPFastOpenLayer,
969 // This data can be only tls data or application data as well.
970 // socketTransport should send status only if it really has sent
971 // application data. socketTransport cannot query transaction for
972 // that info but it can know if transaction has send data if
973 // mOutput.ByteCount() is > 0.
978 case NS_NET_STATUS_RECEIVING_FROM
:
979 progress
= mInput
.ByteCount();
987 sink
->OnTransportStatus(this, status
, progress
, -1);
991 nsresult
nsSocketTransport::ResolveHost() {
992 SOCKET_LOG(("nsSocketTransport::ResolveHost [this=%p %s:%d%s]\n", this,
993 SocketHost().get(), SocketPort(),
994 mConnectionFlags
& nsSocketTransport::BYPASS_CACHE
1000 if (!mProxyHost
.IsEmpty()) {
1001 if (!mProxyTransparent
|| mProxyTransparentResolvesHost
) {
1002 #if defined(XP_UNIX)
1003 MOZ_ASSERT(!mNetAddrIsSet
|| mNetAddr
.raw
.family
!= AF_LOCAL
,
1004 "Unix domain sockets can't be used with proxies");
1006 // When not resolving mHost locally, we still want to ensure that
1007 // it only contains valid characters. See bug 304904 for details.
1008 // Sometimes the end host is not yet known and mHost is *
1009 if (!net_IsValidHostName(mHost
) && !mHost
.EqualsLiteral("*")) {
1010 SOCKET_LOG((" invalid hostname %s\n", mHost
.get()));
1011 return NS_ERROR_UNKNOWN_HOST
;
1014 if (mProxyTransparentResolvesHost
) {
1015 // Name resolution is done on the server side. Just pretend
1016 // client resolution is complete, this will get picked up later.
1017 // since we don't need to do DNS now, we bypass the resolving
1018 // step by initializing mNetAddr to an empty address, but we
1019 // must keep the port. The SOCKS IO layer will use the hostname
1020 // we send it when it's created, rather than the empty address
1021 // we send with the connect call.
1022 mState
= STATE_RESOLVING
;
1023 mNetAddr
.raw
.family
= AF_INET
;
1024 mNetAddr
.inet
.port
= htons(SocketPort());
1025 mNetAddr
.inet
.ip
= htonl(INADDR_ANY
);
1026 return PostEvent(MSG_DNS_LOOKUP_COMPLETE
, NS_OK
, nullptr);
1030 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(kDNSServiceCID
, &rv
);
1031 if (NS_FAILED(rv
)) return rv
;
1035 uint32_t dnsFlags
= 0;
1036 if (mConnectionFlags
& nsSocketTransport::BYPASS_CACHE
)
1037 dnsFlags
= nsIDNSService::RESOLVE_BYPASS_CACHE
;
1038 if (mConnectionFlags
& nsSocketTransport::REFRESH_CACHE
)
1039 dnsFlags
= nsIDNSService::RESOLVE_REFRESH_CACHE
;
1040 if (mConnectionFlags
& nsSocketTransport::DISABLE_IPV6
)
1041 dnsFlags
|= nsIDNSService::RESOLVE_DISABLE_IPV6
;
1042 if (mConnectionFlags
& nsSocketTransport::DISABLE_IPV4
)
1043 dnsFlags
|= nsIDNSService::RESOLVE_DISABLE_IPV4
;
1044 if (mConnectionFlags
& nsSocketTransport::DISABLE_TRR
)
1045 dnsFlags
|= nsIDNSService::RESOLVE_DISABLE_TRR
;
1047 NS_ASSERTION(!(dnsFlags
& nsIDNSService::RESOLVE_DISABLE_IPV6
) ||
1048 !(dnsFlags
& nsIDNSService::RESOLVE_DISABLE_IPV4
),
1049 "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
1051 SendStatus(NS_NET_STATUS_RESOLVING_HOST
);
1053 if (!SocketHost().Equals(mOriginHost
)) {
1054 SOCKET_LOG(("nsSocketTransport %p origin %s doing dns for %s\n", this,
1055 mOriginHost
.get(), SocketHost().get()));
1057 rv
= dns
->AsyncResolveNative(SocketHost(), dnsFlags
, this,
1058 mSocketTransportService
, mOriginAttributes
,
1059 getter_AddRefs(mDNSRequest
));
1060 mEsniQueried
= false;
1061 if (mSocketTransportService
->IsEsniEnabled() && NS_SUCCEEDED(rv
) &&
1062 !(mConnectionFlags
& (DONT_TRY_ESNI
| BE_CONSERVATIVE
))) {
1064 for (unsigned int i
= 0; i
< mTypeCount
; ++i
) {
1065 if (!strcmp(mTypes
[i
], "ssl")) {
1071 SOCKET_LOG((" look for esni txt record"));
1072 nsAutoCString esniHost
;
1073 esniHost
.Append("_esni.");
1074 // This might end up being the SocketHost
1075 // see https://github.com/ekr/draft-rescorla-tls-esni/issues/61
1076 esniHost
.Append(mOriginHost
);
1077 rv
= dns
->AsyncResolveByTypeNative(
1078 esniHost
, nsIDNSService::RESOLVE_TYPE_TXT
, dnsFlags
, this,
1079 mSocketTransportService
, mOriginAttributes
,
1080 getter_AddRefs(mDNSTxtRequest
));
1081 if (NS_FAILED(rv
)) {
1082 SOCKET_LOG((" dns request by type failed."));
1083 mDNSTxtRequest
= nullptr;
1086 mEsniQueried
= true;
1091 if (NS_SUCCEEDED(rv
)) {
1092 SOCKET_LOG((" advancing to STATE_RESOLVING\n"));
1093 mState
= STATE_RESOLVING
;
1098 nsresult
nsSocketTransport::BuildSocket(PRFileDesc
*&fd
, bool &proxyTransparent
,
1100 SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
1104 proxyTransparent
= false;
1107 if (mTypeCount
== 0) {
1108 fd
= PR_OpenTCPSocket(mNetAddr
.raw
.family
);
1109 rv
= fd
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
1111 #if defined(XP_UNIX)
1112 MOZ_ASSERT(!mNetAddrIsSet
|| mNetAddr
.raw
.family
!= AF_LOCAL
,
1113 "Unix domain sockets can't be used with socket types");
1118 nsCOMPtr
<nsISocketProviderService
> spserv
=
1119 nsSocketProviderService::GetOrCreate();
1121 // by setting host to mOriginHost, instead of mHost we send the
1122 // SocketProvider (e.g. PSM) the origin hostname but can still do DNS
1123 // on an explicit alternate service host name
1124 const char *host
= mOriginHost
.get();
1125 int32_t port
= (int32_t)mOriginPort
;
1126 nsCOMPtr
<nsIProxyInfo
> proxyInfo
= mProxyInfo
;
1127 uint32_t controlFlags
= 0;
1130 for (i
= 0; i
< mTypeCount
; ++i
) {
1131 nsCOMPtr
<nsISocketProvider
> provider
;
1133 SOCKET_LOG((" pushing io layer [%u:%s]\n", i
, mTypes
[i
]));
1135 rv
= spserv
->GetSocketProvider(mTypes
[i
], getter_AddRefs(provider
));
1136 if (NS_FAILED(rv
)) break;
1138 if (mProxyTransparentResolvesHost
)
1139 controlFlags
|= nsISocketProvider::PROXY_RESOLVES_HOST
;
1141 if (mConnectionFlags
& nsISocketTransport::ANONYMOUS_CONNECT
)
1142 controlFlags
|= nsISocketProvider::ANONYMOUS_CONNECT
;
1144 if (mConnectionFlags
& nsISocketTransport::NO_PERMANENT_STORAGE
)
1145 controlFlags
|= nsISocketProvider::NO_PERMANENT_STORAGE
;
1147 if (mConnectionFlags
& nsISocketTransport::MITM_OK
)
1148 controlFlags
|= nsISocketProvider::MITM_OK
;
1150 if (mConnectionFlags
& nsISocketTransport::BE_CONSERVATIVE
)
1151 controlFlags
|= nsISocketProvider::BE_CONSERVATIVE
;
1153 nsCOMPtr
<nsISupports
> secinfo
;
1155 // if this is the first type, we'll want the
1156 // service to allocate a new socket
1158 // Most layers _ESPECIALLY_ PSM want the origin name here as they
1159 // will use it for secure checks, etc.. and any connection management
1160 // differences between the origin name and the routed name can be
1161 // taken care of via DNS. However, SOCKS is a special case as there is
1162 // no DNS. in the case of SOCKS and PSM the PSM is a separate layer
1163 // and receives the origin name.
1164 const char *socketProviderHost
= host
;
1165 int32_t socketProviderPort
= port
;
1166 if (mProxyTransparentResolvesHost
&&
1167 (!strcmp(mTypes
[0], "socks") || !strcmp(mTypes
[0], "socks4"))) {
1168 SOCKET_LOG(("SOCKS %d Host/Route override: %s:%d -> %s:%d\n",
1169 mHttpsProxy
, socketProviderHost
, socketProviderPort
,
1170 mHost
.get(), mPort
));
1171 socketProviderHost
= mHost
.get();
1172 socketProviderPort
= mPort
;
1175 // when https proxying we want to just connect to the proxy as if
1176 // it were the end host (i.e. expect the proxy's cert)
1178 rv
= provider
->NewSocket(
1179 mNetAddr
.raw
.family
,
1180 mHttpsProxy
? mProxyHost
.get() : socketProviderHost
,
1181 mHttpsProxy
? mProxyPort
: socketProviderPort
, proxyInfo
,
1182 mOriginAttributes
, controlFlags
, mTlsFlags
, &fd
,
1183 getter_AddRefs(secinfo
));
1185 if (NS_SUCCEEDED(rv
) && !fd
) {
1186 MOZ_ASSERT_UNREACHABLE(
1187 "NewSocket succeeded but failed to "
1188 "create a PRFileDesc");
1189 rv
= NS_ERROR_UNEXPECTED
;
1192 // the socket has already been allocated,
1193 // so we just want the service to add itself
1194 // to the stack (such as pushing an io layer)
1195 rv
= provider
->AddToSocket(mNetAddr
.raw
.family
, host
, port
, proxyInfo
,
1196 mOriginAttributes
, controlFlags
, mTlsFlags
,
1197 fd
, getter_AddRefs(secinfo
));
1200 // controlFlags = 0; not used below this point...
1201 if (NS_FAILED(rv
)) break;
1203 // if the service was ssl or starttls, we want to hold onto the socket
1205 bool isSSL
= (strcmp(mTypes
[i
], "ssl") == 0);
1206 if (isSSL
|| (strcmp(mTypes
[i
], "starttls") == 0)) {
1207 // remember security info and give notification callbacks to PSM...
1208 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
1210 MutexAutoLock
lock(mLock
);
1212 callbacks
= mCallbacks
;
1213 SOCKET_LOG((" [secinfo=%p callbacks=%p]\n", mSecInfo
.get(),
1216 // don't call into PSM while holding mLock!!
1217 nsCOMPtr
<nsISSLSocketControl
> secCtrl(do_QueryInterface(secinfo
));
1218 if (secCtrl
) secCtrl
->SetNotificationCallbacks(callbacks
);
1219 // remember if socket type is SSL so we can ProxyStartSSL if need be.
1221 } else if ((strcmp(mTypes
[i
], "socks") == 0) ||
1222 (strcmp(mTypes
[i
], "socks4") == 0)) {
1223 // since socks is transparent, any layers above
1224 // it do not have to worry about proxy stuff
1225 proxyInfo
= nullptr;
1226 proxyTransparent
= true;
1230 if (NS_FAILED(rv
)) {
1231 SOCKET_LOG((" error pushing io layer [%u:%s rv=%" PRIx32
"]\n", i
,
1232 mTypes
[i
], static_cast<uint32_t>(rv
)));
1235 fd
, mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1243 nsresult
nsSocketTransport::InitiateSocket() {
1244 SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
1250 if (gIOService
->IsNetTearingDown()) {
1251 return NS_ERROR_ABORT
;
1253 if (gIOService
->IsOffline()) {
1254 if (!isLocal
) return NS_ERROR_OFFLINE
;
1255 } else if (!isLocal
) {
1257 // all IP networking has to be done from the parent
1258 if (NS_SUCCEEDED(mCondition
) && ((mNetAddr
.raw
.family
== AF_INET
) ||
1259 (mNetAddr
.raw
.family
== AF_INET6
))) {
1260 MOZ_ASSERT(!IsNeckoChild());
1264 if (NS_SUCCEEDED(mCondition
) && xpc::AreNonLocalConnectionsDisabled() &&
1265 !(IsIPAddrAny(&mNetAddr
) || IsIPAddrLocal(&mNetAddr
))) {
1266 nsAutoCString ipaddr
;
1267 RefPtr
<nsNetAddr
> netaddr
= new nsNetAddr(&mNetAddr
);
1268 netaddr
->GetAddress(ipaddr
);
1271 "FATAL ERROR: Non-local network connections are disabled and a "
1273 "attempt to %s (%s) was made.\nYou should only access hostnames "
1274 "available via the test networking proxy (if running mochitests) "
1275 "or from a test-specific httpd.js server (if running xpcshell "
1277 "Browser services should be disabled or redirected to a local "
1279 mHost
.get(), ipaddr
.get());
1280 MOZ_CRASH("Attempting to connect to non-local address!");
1284 // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
1285 // connected - Bug 853423.
1286 if (mConnectionFlags
& nsISocketTransport::DISABLE_RFC1918
&&
1287 IsIPAddrLocal(&mNetAddr
)) {
1288 if (SOCKET_LOG_ENABLED()) {
1289 nsAutoCString netAddrCString
;
1290 netAddrCString
.SetLength(kIPv6CStrBufSize
);
1291 if (!NetAddrToString(&mNetAddr
, netAddrCString
.BeginWriting(),
1293 netAddrCString
= NS_LITERAL_CSTRING("<IP-to-string failed>");
1295 ("nsSocketTransport::InitiateSocket skipping "
1296 "speculative connection for host [%s:%d] proxy "
1297 "[%s:%d] with Local IP address [%s]",
1298 mHost
.get(), mPort
, mProxyHost
.get(), mProxyPort
,
1299 netAddrCString
.get()));
1301 mCondition
= NS_ERROR_CONNECTION_REFUSED
;
1302 OnSocketDetached(nullptr);
1307 // find out if it is going to be ok to attach another socket to the STS.
1308 // if not then we have to wait for the STS to tell us that it is ok.
1309 // the notification is asynchronous, which means that when we could be
1310 // in a race to call AttachSocket once notified. for this reason, when
1311 // we get notified, we just re-enter this function. as a result, we are
1312 // sure to ask again before calling AttachSocket. in this way we deal
1313 // with the race condition. though it isn't the most elegant solution,
1314 // it is far simpler than trying to build a system that would guarantee
1315 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
1316 // 194402 for more info.
1318 if (!mSocketTransportService
->CanAttachSocket()) {
1319 nsCOMPtr
<nsIRunnable
> event
=
1320 new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET
);
1321 if (!event
) return NS_ERROR_OUT_OF_MEMORY
;
1322 return mSocketTransportService
->NotifyWhenCanAttachSocket(event
);
1326 // if we already have a connected socket, then just attach and return.
1328 if (mFD
.IsInitialized()) {
1329 rv
= mSocketTransportService
->AttachSocket(mFD
, this);
1330 if (NS_SUCCEEDED(rv
)) mAttached
= true;
1335 // create new socket fd, push io layers, etc.
1338 bool proxyTransparent
;
1341 rv
= BuildSocket(fd
, proxyTransparent
, usingSSL
);
1342 if (NS_FAILED(rv
)) {
1344 (" BuildSocket failed [rv=%" PRIx32
"]\n", static_cast<uint32_t>(rv
)));
1348 // create proxy via IOActivityMonitor
1349 IOActivityMonitor::MonitorSocket(fd
);
1353 // Make the socket non-blocking...
1354 PRSocketOptionData opt
;
1355 opt
.option
= PR_SockOpt_Nonblocking
;
1356 opt
.value
.non_blocking
= true;
1357 status
= PR_SetSocketOption(fd
, &opt
);
1358 NS_ASSERTION(status
== PR_SUCCESS
, "unable to make socket non-blocking");
1360 if (mReuseAddrPort
) {
1361 SOCKET_LOG((" Setting port/addr reuse socket options\n"));
1363 // Set ReuseAddr for TCP sockets to enable having several
1364 // sockets bound to same local IP and port
1365 PRSocketOptionData opt_reuseaddr
;
1366 opt_reuseaddr
.option
= PR_SockOpt_Reuseaddr
;
1367 opt_reuseaddr
.value
.reuse_addr
= PR_TRUE
;
1368 status
= PR_SetSocketOption(fd
, &opt_reuseaddr
);
1369 if (status
!= PR_SUCCESS
) {
1370 SOCKET_LOG((" Couldn't set reuse addr socket option: %d\n", status
));
1373 // And also set ReusePort for platforms supporting this socket option
1374 PRSocketOptionData opt_reuseport
;
1375 opt_reuseport
.option
= PR_SockOpt_Reuseport
;
1376 opt_reuseport
.value
.reuse_port
= PR_TRUE
;
1377 status
= PR_SetSocketOption(fd
, &opt_reuseport
);
1378 if (status
!= PR_SUCCESS
&&
1379 PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR
) {
1380 SOCKET_LOG((" Couldn't set reuse port socket option: %d\n", status
));
1384 // disable the nagle algorithm - if we rely on it to coalesce writes into
1385 // full packets the final packet of a multi segment POST/PUT or pipeline
1386 // sequence is delayed a full rtt
1387 opt
.option
= PR_SockOpt_NoDelay
;
1388 opt
.value
.no_delay
= true;
1389 PR_SetSocketOption(fd
, &opt
);
1391 // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
1392 // The Windows default of 8KB is too small and as of vista sp1, autotuning
1393 // only applies to receive window
1394 int32_t sndBufferSize
;
1395 mSocketTransportService
->GetSendBufferSize(&sndBufferSize
);
1396 if (sndBufferSize
> 0) {
1397 opt
.option
= PR_SockOpt_SendBufferSize
;
1398 opt
.value
.send_buffer_size
= sndBufferSize
;
1399 PR_SetSocketOption(fd
, &opt
);
1403 opt
.option
= PR_SockOpt_IpTypeOfService
;
1404 opt
.value
.tos
= mQoSBits
;
1405 PR_SetSocketOption(fd
, &opt
);
1409 // The linger is turned off by default. This is not a hard close, but
1410 // closesocket should return immediately and operating system tries to send
1411 // remaining data for certain, implementation specific, amount of time.
1412 // https://msdn.microsoft.com/en-us/library/ms739165.aspx
1414 // Turn the linger option on an set the interval to 0. This will cause hard
1415 // close of the socket.
1416 opt
.option
= PR_SockOpt_Linger
;
1417 opt
.value
.linger
.polarity
= 1;
1418 opt
.value
.linger
.linger
= 0;
1419 PR_SetSocketOption(fd
, &opt
);
1422 // inform socket transport about this newly created socket...
1423 rv
= mSocketTransportService
->AttachSocket(fd
, this);
1424 if (NS_FAILED(rv
)) {
1426 mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1431 // assign mFD so that we can properly handle OnSocketDetached before we've
1432 // established a connection.
1434 MutexAutoLock
lock(mLock
);
1437 mFDconnected
= false;
1440 SOCKET_LOG((" advancing to STATE_CONNECTING\n"));
1441 mState
= STATE_CONNECTING
;
1442 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
1443 SendStatus(NS_NET_STATUS_CONNECTING_TO
);
1445 if (SOCKET_LOG_ENABLED()) {
1446 char buf
[kNetAddrMaxCStrBufSize
];
1447 NetAddrToString(&mNetAddr
, buf
, sizeof(buf
));
1448 SOCKET_LOG((" trying address: %s\n", buf
));
1452 // Initiate the connect() to the host...
1457 MutexAutoLock
lock(mLock
);
1458 NetAddrToPRNetAddr(mBindAddr
.get(), &prAddr
);
1459 status
= PR_Bind(fd
, &prAddr
);
1460 if (status
!= PR_SUCCESS
) {
1461 return NS_ERROR_FAILURE
;
1463 mBindAddr
= nullptr;
1467 NetAddrToPRNetAddr(&mNetAddr
, &prAddr
);
1470 // Find the real tcp socket and set non-blocking once again!
1472 PRFileDesc
*bottom
= PR_GetIdentitiesLayer(fd
, PR_NSPR_IO_LAYER
);
1474 PROsfd osfd
= PR_FileDesc2NativeHandle(bottom
);
1475 u_long nonblocking
= 1;
1476 if (ioctlsocket(osfd
, FIONBIO
, &nonblocking
) != 0) {
1477 NS_WARNING("Socket could not be set non-blocking!");
1478 return NS_ERROR_FAILURE
;
1483 if (!mDNSRecordTxt
.IsEmpty() && mSecInfo
) {
1484 nsCOMPtr
<nsISSLSocketControl
> secCtrl
= do_QueryInterface(mSecInfo
);
1486 SOCKET_LOG(("nsSocketTransport::InitiateSocket set esni keys."));
1487 rv
= secCtrl
->SetEsniTxt(mDNSRecordTxt
);
1488 if (NS_FAILED(rv
)) {
1495 // We use PRIntervalTime here because we need
1496 // nsIOService::LastOfflineStateChange time and
1497 // nsIOService::LastConectivityChange time to be atomic.
1498 PRIntervalTime connectStarted
= 0;
1499 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
1500 connectStarted
= PR_IntervalNow();
1504 if (!mProxyTransparent
&& mFastOpenCallback
&&
1505 mFastOpenCallback
->FastOpenEnabled()) {
1506 if (NS_SUCCEEDED(AttachTCPFastOpenIOLayer(fd
))) {
1509 ("nsSocketTransport::InitiateSocket TCP Fast Open "
1510 "started [this=%p]\n",
1515 bool connectCalled
= true; // This is only needed for telemetry.
1516 status
= PR_Connect(fd
, &prAddr
, NS_SOCKET_CONNECT_TIMEOUT
);
1517 PRErrorCode code
= PR_GetError();
1518 if (status
== PR_SUCCESS
) {
1519 PR_SetFDInheritable(fd
, false);
1521 if ((status
== PR_SUCCESS
) && tfo
) {
1523 MutexAutoLock
lock(mLock
);
1524 mFDFastOpenInProgress
= true;
1526 SOCKET_LOG(("Using TCP Fast Open."));
1527 rv
= mFastOpenCallback
->StartFastOpen();
1528 if (NS_FAILED(rv
)) {
1529 if (NS_SUCCEEDED(mCondition
)) {
1532 mFastOpenCallback
= nullptr;
1533 MutexAutoLock
lock(mLock
);
1534 mFDFastOpenInProgress
= false;
1537 status
= PR_FAILURE
;
1538 connectCalled
= false;
1539 bool fastOpenNotSupported
= false;
1540 TCPFastOpenFinish(fd
, code
, fastOpenNotSupported
, mFastOpenStatus
);
1542 // If we have sent data, trigger a socket status event.
1543 if (mFastOpenStatus
== TFO_DATA_SENT
) {
1544 SendStatus(NS_NET_STATUS_SENDING_TO
);
1547 // If we have still some data buffered this data must be flush before
1548 // mOutput.OnSocketReady(NS_OK) is called in
1549 // nsSocketTransport::OnSocketReady, partially to keep socket status
1551 mFastOpenLayerHasBufferedData
= TCPFastOpenGetCurrentBufferSize(fd
);
1553 MOZ_ASSERT((mFastOpenStatus
== TFO_NOT_TRIED
) ||
1554 (mFastOpenStatus
== TFO_DISABLED
) ||
1555 (mFastOpenStatus
== TFO_DATA_SENT
) ||
1556 (mFastOpenStatus
== TFO_TRIED
));
1557 mFastOpenCallback
->SetFastOpenStatus(mFastOpenStatus
);
1559 ("called StartFastOpen - code=%d; fastOpen is %s "
1561 code
, fastOpenNotSupported
? "not" : ""));
1562 SOCKET_LOG(("TFO status %d\n", mFastOpenStatus
));
1564 if (fastOpenNotSupported
) {
1565 // When TCP_FastOpen is turned off on the local host
1566 // SendTo will return PR_NOT_TCP_SOCKET_ERROR. This is only
1568 // If a windows version does not support Fast Open, the return value
1569 // will be PR_NOT_IMPLEMENTED_ERROR. This is only for windows 10
1570 // versions older than version 1607, because we do not have subverion
1571 // to check, we need to call PR_SendTo to check if it is supported.
1572 mFastOpenCallback
->FastOpenNotSupported();
1573 // FastOpenNotSupported will set Fast Open as not supported globally.
1574 // For this connection we will pretend that we still use fast open,
1575 // because of the fallback mechanism in case we need to restart the
1576 // attached transaction.
1577 connectCalled
= true;
1580 mFastOpenCallback
= nullptr;
1583 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase() &&
1584 connectStarted
&& connectCalled
) {
1585 SendPRBlockingTelemetry(
1586 connectStarted
, Telemetry::PRCONNECT_BLOCKING_TIME_NORMAL
,
1587 Telemetry::PRCONNECT_BLOCKING_TIME_SHUTDOWN
,
1588 Telemetry::PRCONNECT_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
1589 Telemetry::PRCONNECT_BLOCKING_TIME_LINK_CHANGE
,
1590 Telemetry::PRCONNECT_BLOCKING_TIME_OFFLINE
);
1593 if (status
== PR_SUCCESS
) {
1595 // we are connected!
1597 OnSocketConnected();
1599 #if defined(TEST_CONNECT_ERRORS)
1600 code
= RandomizeConnectError(code
);
1603 // If the PR_Connect(...) would block, then poll for a connection.
1605 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
))
1606 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
1608 // If the socket is already connected, then return success...
1610 else if (PR_IS_CONNECTED_ERROR
== code
) {
1612 // we are connected!
1614 OnSocketConnected();
1616 if (mSecInfo
&& !mProxyHost
.IsEmpty() && proxyTransparent
&& usingSSL
) {
1617 // if the connection phase is finished, and the ssl layer has
1618 // been pushed, and we were proxying (transparently; ie. nothing
1619 // has to happen in the protocol layer above us), it's time for
1620 // the ssl to start doing it's thing.
1621 nsCOMPtr
<nsISSLSocketControl
> secCtrl
= do_QueryInterface(mSecInfo
);
1623 SOCKET_LOG((" calling ProxyStartSSL()\n"));
1624 secCtrl
->ProxyStartSSL();
1626 // XXX what if we were forced to poll on the socket for a successful
1627 // connection... wouldn't we need to call ProxyStartSSL after a call
1628 // to PR_ConnectContinue indicates that we are connected?
1630 // XXX this appears to be what the old socket transport did. why
1631 // isn't this broken?
1635 // A SOCKS request was rejected; get the actual error code from
1638 else if (PR_UNKNOWN_ERROR
== code
&& mProxyTransparent
&&
1639 !mProxyHost
.IsEmpty()) {
1640 code
= PR_GetOSError();
1641 rv
= ErrorAccordingToNSPR(code
);
1644 // The connection was refused...
1647 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase() &&
1648 connectStarted
&& connectStarted
) {
1649 SendPRBlockingTelemetry(
1650 connectStarted
, Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_NORMAL
,
1651 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_SHUTDOWN
,
1652 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
1653 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_LINK_CHANGE
,
1654 Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_OFFLINE
);
1657 rv
= ErrorAccordingToNSPR(code
);
1658 if ((rv
== NS_ERROR_CONNECTION_REFUSED
) && !mProxyHost
.IsEmpty())
1659 rv
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
1665 bool nsSocketTransport::RecoverFromError() {
1666 NS_ASSERTION(NS_FAILED(mCondition
), "there should be something wrong");
1669 ("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%" PRIx32
1671 this, mState
, static_cast<uint32_t>(mCondition
)));
1673 if (mDoNotRetryToConnect
) {
1675 ("nsSocketTransport::RecoverFromError do not retry because "
1676 "mDoNotRetryToConnect is set [this=%p]\n",
1681 #if defined(XP_UNIX)
1682 // Unix domain connections don't have multiple addresses to try,
1683 // so the recovery techniques here don't apply.
1684 if (mNetAddrIsSet
&& mNetAddr
.raw
.family
== AF_LOCAL
) return false;
1687 // can only recover from errors in these states
1688 if (mState
!= STATE_RESOLVING
&& mState
!= STATE_CONNECTING
) {
1689 SOCKET_LOG((" not in a recoverable state"));
1695 // OK to check this outside mLock
1696 NS_ASSERTION(!mFDconnected
, "socket should not be connected");
1698 // all connection failures need to be reported to DNS so that the next
1699 // time we will use a different address if available.
1700 // Skip conditions that can be cause by TCP Fast Open.
1701 if ((!mFDFastOpenInProgress
||
1702 ((mCondition
!= NS_ERROR_CONNECTION_REFUSED
) &&
1703 (mCondition
!= NS_ERROR_NET_TIMEOUT
) &&
1704 (mCondition
!= NS_ERROR_PROXY_CONNECTION_REFUSED
))) &&
1705 mState
== STATE_CONNECTING
&& mDNSRecord
) {
1706 mDNSRecord
->ReportUnusable(SocketPort());
1709 #if defined(_WIN64) && defined(WIN95)
1710 // can only recover from these errors
1711 if (mCondition
!= NS_ERROR_CONNECTION_REFUSED
&&
1712 mCondition
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
1713 mCondition
!= NS_ERROR_NET_TIMEOUT
&&
1714 mCondition
!= NS_ERROR_UNKNOWN_HOST
&&
1715 mCondition
!= NS_ERROR_UNKNOWN_PROXY_HOST
&&
1716 !(mFDFastOpenInProgress
&& (mCondition
== NS_ERROR_FAILURE
))) {
1717 SOCKET_LOG((" not a recoverable error %" PRIx32
,
1718 static_cast<uint32_t>(mCondition
)));
1722 if (mCondition
!= NS_ERROR_CONNECTION_REFUSED
&&
1723 mCondition
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
1724 mCondition
!= NS_ERROR_NET_TIMEOUT
&&
1725 mCondition
!= NS_ERROR_UNKNOWN_HOST
&&
1726 mCondition
!= NS_ERROR_UNKNOWN_PROXY_HOST
) {
1727 SOCKET_LOG((" not a recoverable error %" PRIx32
,
1728 static_cast<uint32_t>(mCondition
)));
1733 bool tryAgain
= false;
1734 if (mFDFastOpenInProgress
&&
1735 ((mCondition
== NS_ERROR_CONNECTION_REFUSED
) ||
1736 (mCondition
== NS_ERROR_NET_TIMEOUT
) ||
1737 #if defined(_WIN64) && defined(WIN95)
1738 // On Windows PR_ContinueConnect can return NS_ERROR_FAILURE.
1739 // This will be fixed in bug 1386719 and this is just a temporary
1741 (mCondition
== NS_ERROR_FAILURE
) ||
1743 (mCondition
== NS_ERROR_PROXY_CONNECTION_REFUSED
))) {
1744 // TCP Fast Open can be blocked by middle boxes so we will retry
1747 // If we cancel the connection because backup socket was successfully
1748 // connected, mFDFastOpenInProgress will be true but mFastOpenCallback
1750 if (mFastOpenCallback
) {
1751 mFastOpenCallback
->SetFastOpenConnected(mCondition
, true);
1753 mFastOpenCallback
= nullptr;
1756 // This is only needed for telemetry.
1757 if (NS_SUCCEEDED(mFirstRetryError
)) {
1758 mFirstRetryError
= mCondition
;
1760 if ((mState
== STATE_CONNECTING
) && mDNSRecord
) {
1761 if (mNetAddr
.raw
.family
== AF_INET
) {
1762 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
1763 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
1764 UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS
);
1766 } else if (mNetAddr
.raw
.family
== AF_INET6
) {
1767 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
1768 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
1769 UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS
);
1774 if (mConnectionFlags
& RETRY_WITH_DIFFERENT_IP_FAMILY
&&
1775 mCondition
== NS_ERROR_UNKNOWN_HOST
&& mState
== STATE_RESOLVING
&&
1776 !mProxyTransparentResolvesHost
) {
1777 SOCKET_LOG((" trying lookup again with opposite ip family\n"));
1778 mConnectionFlags
^= (DISABLE_IPV6
| DISABLE_IPV4
);
1779 mConnectionFlags
&= ~RETRY_WITH_DIFFERENT_IP_FAMILY
;
1780 // This will tell the consuming half-open to reset preference on the
1782 mResetFamilyPreference
= true;
1786 // try next ip address only if past the resolver stage...
1787 if (mState
== STATE_CONNECTING
&& mDNSRecord
) {
1788 nsresult rv
= mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
1789 if (NS_SUCCEEDED(rv
)) {
1790 SOCKET_LOG((" trying again with next ip address\n"));
1792 } else if (mConnectionFlags
& RETRY_WITH_DIFFERENT_IP_FAMILY
) {
1793 SOCKET_LOG((" failed to connect, trying with opposite ip family\n"));
1794 // Drop state to closed. This will trigger new round of DNS
1795 // resolving bellow.
1796 mState
= STATE_CLOSED
;
1797 mConnectionFlags
^= (DISABLE_IPV6
| DISABLE_IPV4
);
1798 mConnectionFlags
&= ~RETRY_WITH_DIFFERENT_IP_FAMILY
;
1799 // This will tell the consuming half-open to reset preference on the
1801 mResetFamilyPreference
= true;
1803 } else if (!(mConnectionFlags
& DISABLE_TRR
)) {
1805 mDNSRecord
->IsTRR(&trrEnabled
);
1807 // Drop state to closed. This will trigger a new round of
1808 // DNS resolving. Bypass the cache this time since the
1809 // cached data came from TRR and failed already!
1810 SOCKET_LOG((" failed to connect with TRR enabled, try w/o\n"));
1811 mState
= STATE_CLOSED
;
1812 mConnectionFlags
|= DISABLE_TRR
| BYPASS_CACHE
| REFRESH_CACHE
;
1819 // prepare to try again.
1823 if (mState
== STATE_CONNECTING
) {
1824 mState
= STATE_RESOLVING
;
1825 msg
= MSG_DNS_LOOKUP_COMPLETE
;
1827 mState
= STATE_CLOSED
;
1828 msg
= MSG_ENSURE_CONNECT
;
1831 rv
= PostEvent(msg
, NS_OK
);
1832 if (NS_FAILED(rv
)) tryAgain
= false;
1838 // called on the socket thread only
1839 void nsSocketTransport::OnMsgInputClosed(nsresult reason
) {
1840 SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%" PRIx32
1842 this, static_cast<uint32_t>(reason
)));
1844 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1846 mInputClosed
= true;
1847 // check if event should affect entire transport
1848 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
))
1849 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1850 else if (mOutputClosed
)
1852 NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1854 if (mState
== STATE_TRANSFERRING
) mPollFlags
&= ~PR_POLL_READ
;
1855 mInput
.OnSocketReady(reason
);
1859 // called on the socket thread only
1860 void nsSocketTransport::OnMsgOutputClosed(nsresult reason
) {
1861 SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%p reason=%" PRIx32
1863 this, static_cast<uint32_t>(reason
)));
1865 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1867 mOutputClosed
= true;
1868 // check if event should affect entire transport
1869 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
))
1870 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1871 else if (mInputClosed
)
1873 NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1875 if (mState
== STATE_TRANSFERRING
) mPollFlags
&= ~PR_POLL_WRITE
;
1876 mOutput
.OnSocketReady(reason
);
1880 void nsSocketTransport::OnSocketConnected() {
1881 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1882 SOCKET_LOG((" advancing to STATE_TRANSFERRING\n"));
1884 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
1885 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
1886 mState
= STATE_TRANSFERRING
;
1888 // Set the m*AddrIsSet flags only when state has reached TRANSFERRING
1889 // because we need to make sure its value does not change due to failover
1890 mNetAddrIsSet
= true;
1892 if (mFDFastOpenInProgress
&& mFastOpenCallback
) {
1893 // mFastOpenCallback can be null when for example h2 is negotiated on
1894 // another connection to the same host and all connections are
1896 mFastOpenCallback
->SetFastOpenConnected(NS_OK
, false);
1898 mFastOpenCallback
= nullptr;
1900 // assign mFD (must do this within the transport lock), but take care not
1901 // to trample over mFDref if mFD is already set.
1903 MutexAutoLock
lock(mLock
);
1904 NS_ASSERTION(mFD
.IsInitialized(), "no socket");
1905 NS_ASSERTION(mFDref
== 1, "wrong socket ref count");
1907 mFDconnected
= true;
1908 mFDFastOpenInProgress
= false;
1911 // Ensure keepalive is configured correctly if previously enabled.
1912 if (mKeepaliveEnabled
) {
1913 nsresult rv
= SetKeepaliveEnabledInternal(true);
1914 if (NS_WARN_IF(NS_FAILED(rv
))) {
1915 SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%" PRIx32
"]",
1916 static_cast<uint32_t>(rv
)));
1920 SendStatus(NS_NET_STATUS_CONNECTED_TO
);
1923 void nsSocketTransport::SetSocketName(PRFileDesc
*fd
) {
1924 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1925 if (mSelfAddrIsSet
) {
1930 memset(&prAddr
, 0, sizeof(prAddr
));
1931 if (PR_GetSockName(fd
, &prAddr
) == PR_SUCCESS
) {
1932 PRNetAddrToNetAddr(&prAddr
, &mSelfAddr
);
1933 mSelfAddrIsSet
= true;
1937 PRFileDesc
*nsSocketTransport::GetFD_Locked() {
1938 mLock
.AssertCurrentThreadOwns();
1940 // mFD is not available to the streams while disconnected.
1941 if (!mFDconnected
) return nullptr;
1943 if (mFD
.IsInitialized()) mFDref
++;
1948 PRFileDesc
*nsSocketTransport::GetFD_LockedAlsoDuringFastOpen() {
1949 mLock
.AssertCurrentThreadOwns();
1951 // mFD is not available to the streams while disconnected.
1952 if (!mFDconnected
&& !mFDFastOpenInProgress
) {
1956 if (mFD
.IsInitialized()) {
1963 bool nsSocketTransport::FastOpenInProgress() {
1964 mLock
.AssertCurrentThreadOwns();
1965 return mFDFastOpenInProgress
;
1968 class ThunkPRClose
: public Runnable
{
1970 explicit ThunkPRClose(PRFileDesc
*fd
)
1971 : Runnable("net::ThunkPRClose"), mFD(fd
) {}
1973 NS_IMETHOD
Run() override
{
1974 nsSocketTransport::CloseSocket(
1975 mFD
, gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
1983 void STS_PRCloseOnSocketTransport(PRFileDesc
*fd
, bool lingerPolarity
,
1984 int16_t lingerTimeout
) {
1985 if (gSocketTransportService
) {
1986 // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
1987 gSocketTransportService
->Dispatch(new ThunkPRClose(fd
), NS_DISPATCH_NORMAL
);
1989 // something horrible has happened
1990 NS_ASSERTION(gSocketTransportService
, "No STS service");
1994 void nsSocketTransport::ReleaseFD_Locked(PRFileDesc
*fd
) {
1995 mLock
.AssertCurrentThreadOwns();
1997 NS_ASSERTION(mFD
== fd
, "wrong fd");
1999 if (--mFDref
== 0) {
2000 if (gIOService
->IsNetTearingDown() &&
2001 ((PR_IntervalNow() - gIOService
->NetTearingDownStarted()) >
2002 gSocketTransportService
->MaxTimeForPrClosePref())) {
2003 // If shutdown last to long, let the socket leak and do not close it.
2004 SOCKET_LOG(("Intentional leak"));
2006 if (mLingerPolarity
|| mLingerTimeout
) {
2007 PRSocketOptionData socket_linger
;
2008 socket_linger
.option
= PR_SockOpt_Linger
;
2009 socket_linger
.value
.linger
.polarity
= mLingerPolarity
;
2010 socket_linger
.value
.linger
.linger
= mLingerTimeout
;
2011 PR_SetSocketOption(mFD
, &socket_linger
);
2013 if (OnSocketThread()) {
2014 SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
2016 mFD
, mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase());
2018 // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
2019 STS_PRCloseOnSocketTransport(mFD
, mLingerPolarity
, mLingerTimeout
);
2026 //-----------------------------------------------------------------------------
2027 // socket event handler impl
2029 void nsSocketTransport::OnSocketEvent(uint32_t type
, nsresult status
,
2030 nsISupports
*param
) {
2032 ("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%" PRIx32
2034 this, type
, static_cast<uint32_t>(status
), param
));
2036 if (NS_FAILED(mCondition
)) {
2037 // block event since we're apparently already dead.
2038 SOCKET_LOG((" blocking event [condition=%" PRIx32
"]\n",
2039 static_cast<uint32_t>(mCondition
)));
2041 // notify input/output streams in case either has a pending notify.
2043 mInput
.OnSocketReady(mCondition
);
2044 mOutput
.OnSocketReady(mCondition
);
2049 case MSG_ENSURE_CONNECT
:
2050 SOCKET_LOG((" MSG_ENSURE_CONNECT\n"));
2052 // ensure that we have created a socket, attached it, and have a
2055 if (mState
== STATE_CLOSED
) {
2056 // Unix domain sockets are ready to connect; mNetAddr is all we
2057 // need. Internet address families require a DNS lookup (or possibly
2058 // several) before we can connect.
2059 #if defined(XP_UNIX)
2060 if (mNetAddrIsSet
&& mNetAddr
.raw
.family
== AF_LOCAL
)
2061 mCondition
= InitiateSocket();
2064 mCondition
= ResolveHost();
2067 SOCKET_LOG((" ignoring redundant event\n"));
2071 case MSG_DNS_LOOKUP_COMPLETE
:
2073 mDNSTxtRequest
) { // only send this if we actually resolved anything
2074 SendStatus(NS_NET_STATUS_RESOLVED_HOST
);
2077 SOCKET_LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
2078 mDNSRequest
= nullptr;
2079 mDNSTxtRequest
= nullptr;
2081 mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
2083 // status contains DNS lookup status
2084 if (NS_FAILED(status
)) {
2085 // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
2086 // proxy host is not found, so we fixup the error code.
2087 // For SOCKS proxies (mProxyTransparent == true), the socket
2088 // transport resolves the real host here, so there's no fixup
2089 // (see bug 226943).
2090 if ((status
== NS_ERROR_UNKNOWN_HOST
) && !mProxyTransparent
&&
2091 !mProxyHost
.IsEmpty())
2092 mCondition
= NS_ERROR_UNKNOWN_PROXY_HOST
;
2094 mCondition
= status
;
2095 } else if (mState
== STATE_RESOLVING
) {
2096 mCondition
= InitiateSocket();
2100 case MSG_RETRY_INIT_SOCKET
:
2101 mCondition
= InitiateSocket();
2104 case MSG_INPUT_CLOSED
:
2105 SOCKET_LOG((" MSG_INPUT_CLOSED\n"));
2106 OnMsgInputClosed(status
);
2109 case MSG_INPUT_PENDING
:
2110 SOCKET_LOG((" MSG_INPUT_PENDING\n"));
2111 OnMsgInputPending();
2114 case MSG_OUTPUT_CLOSED
:
2115 SOCKET_LOG((" MSG_OUTPUT_CLOSED\n"));
2116 OnMsgOutputClosed(status
);
2119 case MSG_OUTPUT_PENDING
:
2120 SOCKET_LOG((" MSG_OUTPUT_PENDING\n"));
2121 OnMsgOutputPending();
2123 case MSG_TIMEOUT_CHANGED
:
2124 SOCKET_LOG((" MSG_TIMEOUT_CHANGED\n"));
2126 mTimeouts
[(mState
== STATE_TRANSFERRING
) ? TIMEOUT_READ_WRITE
2130 SOCKET_LOG((" unhandled event!\n"));
2133 if (NS_FAILED(mCondition
)) {
2134 SOCKET_LOG((" after event [this=%p cond=%" PRIx32
"]\n", this,
2135 static_cast<uint32_t>(mCondition
)));
2136 if (!mAttached
) // need to process this error ourselves...
2137 OnSocketDetached(nullptr);
2138 } else if (mPollFlags
== PR_POLL_EXCEPT
)
2139 mPollFlags
= 0; // make idle
2142 //-----------------------------------------------------------------------------
2143 // socket handler impl
2145 void nsSocketTransport::OnSocketReady(PRFileDesc
*fd
, int16_t outFlags
) {
2146 SOCKET_LOG1(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",
2149 if (outFlags
== -1) {
2150 SOCKET_LOG(("socket timeout expired\n"));
2151 mCondition
= NS_ERROR_NET_TIMEOUT
;
2155 if ((mState
== STATE_TRANSFERRING
) && mFastOpenLayerHasBufferedData
) {
2156 // We have some data buffered in TCPFastOpenLayer. We will flush them
2157 // first. We need to do this first before calling OnSocketReady below
2158 // so that the socket status events are kept in the correct order.
2159 mFastOpenLayerHasBufferedData
= TCPFastOpenFlushBuffer(fd
);
2160 if (mFastOpenLayerHasBufferedData
) {
2163 SendStatus(NS_NET_STATUS_SENDING_TO
);
2165 // If we are done sending the buffered data continue with the normal
2167 // In case of an error, TCPFastOpenFlushBuffer will return false and
2168 // the normal code path will pick up the error.
2169 mFastOpenLayerHasBufferedData
= false;
2172 if (mState
== STATE_TRANSFERRING
) {
2173 // if waiting to write and socket is writable or hit an exception.
2174 if ((mPollFlags
& PR_POLL_WRITE
) && (outFlags
& ~PR_POLL_READ
)) {
2175 // assume that we won't need to poll any longer (the stream will
2176 // request that we poll again if it is still pending).
2177 mPollFlags
&= ~PR_POLL_WRITE
;
2178 mOutput
.OnSocketReady(NS_OK
);
2180 // if waiting to read and socket is readable or hit an exception.
2181 if ((mPollFlags
& PR_POLL_READ
) && (outFlags
& ~PR_POLL_WRITE
)) {
2182 // assume that we won't need to poll any longer (the stream will
2183 // request that we poll again if it is still pending).
2184 mPollFlags
&= ~PR_POLL_READ
;
2185 mInput
.OnSocketReady(NS_OK
);
2187 // Update poll timeout in case it was changed
2188 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
2189 } else if ((mState
== STATE_CONNECTING
) && !gIOService
->IsNetTearingDown()) {
2190 // We do not need to do PR_ConnectContinue when we are already
2193 // We use PRIntervalTime here because we need
2194 // nsIOService::LastOfflineStateChange time and
2195 // nsIOService::LastConectivityChange time to be atomic.
2196 PRIntervalTime connectStarted
= 0;
2197 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
2198 connectStarted
= PR_IntervalNow();
2201 PRStatus status
= PR_ConnectContinue(fd
, outFlags
);
2203 #if defined(_WIN64) && defined(WIN95)
2204 # ifndef TCP_FASTOPEN
2205 # define TCP_FASTOPEN 15
2208 if (mFDFastOpenInProgress
&& mFastOpenCallback
&&
2209 (mFastOpenStatus
== TFO_DATA_SENT
)) {
2210 PROsfd osfd
= PR_FileDesc2NativeHandle(fd
);
2212 int len
= sizeof(option
);
2213 PRInt32 rv
= getsockopt((SOCKET
)osfd
, IPPROTO_TCP
, TCP_FASTOPEN
,
2214 (char *)&option
, &len
);
2215 if (!rv
&& !option
) {
2216 // On error, I will let the normal necko paths pickup the error.
2217 mFastOpenCallback
->SetFastOpenStatus(TFO_DATA_COOKIE_NOT_ACCEPTED
);
2222 if (gSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase() &&
2224 SendPRBlockingTelemetry(
2225 connectStarted
, Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_NORMAL
,
2226 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_SHUTDOWN
,
2227 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
2228 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_LINK_CHANGE
,
2229 Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_OFFLINE
);
2232 if (status
== PR_SUCCESS
) {
2234 // we are connected!
2236 OnSocketConnected();
2238 if (mNetAddr
.raw
.family
== AF_INET
) {
2239 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
2240 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
2241 SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS
);
2243 } else if (mNetAddr
.raw
.family
== AF_INET6
) {
2244 if (mSocketTransportService
->IsTelemetryEnabledAndNotSleepPhase()) {
2245 Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY
,
2246 SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS
);
2250 PRErrorCode code
= PR_GetError();
2251 #if defined(TEST_CONNECT_ERRORS)
2252 code
= RandomizeConnectError(code
);
2255 // If the connect is still not ready, then continue polling...
2257 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
)) {
2258 // Set up the select flags for connect...
2259 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
2260 // Update poll timeout in case it was changed
2261 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
2264 // The SOCKS proxy rejected our request. Find out why.
2266 else if (PR_UNKNOWN_ERROR
== code
&& mProxyTransparent
&&
2267 !mProxyHost
.IsEmpty()) {
2268 code
= PR_GetOSError();
2269 mCondition
= ErrorAccordingToNSPR(code
);
2272 // else, the connection failed...
2274 mCondition
= ErrorAccordingToNSPR(code
);
2275 if ((mCondition
== NS_ERROR_CONNECTION_REFUSED
) &&
2276 !mProxyHost
.IsEmpty())
2277 mCondition
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
2278 SOCKET_LOG((" connection failed! [reason=%" PRIx32
"]\n",
2279 static_cast<uint32_t>(mCondition
)));
2282 } else if ((mState
== STATE_CONNECTING
) && gIOService
->IsNetTearingDown()) {
2283 // We do not need to do PR_ConnectContinue when we are already
2286 ("We are in shutdown so skip PR_ConnectContinue and set "
2288 mCondition
= NS_ERROR_ABORT
;
2290 NS_ERROR("unexpected socket state");
2291 mCondition
= NS_ERROR_UNEXPECTED
;
2294 if (mPollFlags
== PR_POLL_EXCEPT
) mPollFlags
= 0; // make idle
2297 // called on the socket thread only
2298 void nsSocketTransport::OnSocketDetached(PRFileDesc
*fd
) {
2299 SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%" PRIx32
2301 this, static_cast<uint32_t>(mCondition
)));
2303 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2307 // if we didn't initiate this detach, then be sure to pass an error
2308 // condition up to our consumers. (e.g., STS is shutting down.)
2309 if (NS_SUCCEEDED(mCondition
)) {
2310 if (gIOService
->IsOffline()) {
2311 mCondition
= NS_ERROR_OFFLINE
;
2313 mCondition
= NS_ERROR_ABORT
;
2317 mFastOpenLayerHasBufferedData
= false;
2319 // If we are not shutting down try again.
2320 if (!gIOService
->IsNetTearingDown() && RecoverFromError())
2323 mState
= STATE_CLOSED
;
2325 // The error can happened before we start fast open. In that case do not
2326 // call mFastOpenCallback->SetFastOpenConnected; If error happends during
2327 // fast open, inform the halfOpenSocket.
2328 // If we cancel the connection because backup socket was successfully
2329 // connected, mFDFastOpenInProgress will be true but mFastOpenCallback
2331 if (mFDFastOpenInProgress
&& mFastOpenCallback
) {
2332 mFastOpenCallback
->SetFastOpenConnected(mCondition
, false);
2334 mFastOpenCallback
= nullptr;
2336 // make sure there isn't any pending DNS request
2338 mDNSRequest
->Cancel(NS_ERROR_ABORT
);
2339 mDNSRequest
= nullptr;
2342 if (mDNSTxtRequest
) {
2343 mDNSTxtRequest
->Cancel(NS_ERROR_ABORT
);
2344 mDNSTxtRequest
= nullptr;
2348 // notify input/output streams
2350 mInput
.OnSocketReady(mCondition
);
2351 mOutput
.OnSocketReady(mCondition
);
2354 // If FastOpen has been used (mFDFastOpenInProgress==true),
2355 // mFastOpenCallback must be nullptr now. We decided to recover from
2356 // error like NET_TIMEOUT, CONNECTION_REFUSED or we have called
2357 // SetFastOpenConnected(mCondition) in this function a couple of lines
2359 // If FastOpen has not been used (mFDFastOpenInProgress==false) it can be
2360 // that mFastOpenCallback is no null, this is the case when we recover from
2361 // errors like UKNOWN_HOST in which case socket was not been connected yet
2362 // and mFastOpenCallback-StartFastOpen was not be called yet (but we can
2363 // still call it in the next try).
2364 MOZ_ASSERT(!(mFDFastOpenInProgress
&& mFastOpenCallback
));
2366 // break any potential reference cycle between the security info object
2367 // and ourselves by resetting its notification callbacks object. see
2368 // bug 285991 for details.
2369 nsCOMPtr
<nsISSLSocketControl
> secCtrl
= do_QueryInterface(mSecInfo
);
2370 if (secCtrl
) secCtrl
->SetNotificationCallbacks(nullptr);
2372 // finally, release our reference to the socket (must do this within
2373 // the transport lock) possibly closing the socket. Also release our
2374 // listeners to break potential refcount cycles.
2376 // We should be careful not to release mEventSink and mCallbacks while
2377 // we're locked, because releasing it might require acquiring the lock
2378 // again, so we just null out mEventSink and mCallbacks while we're
2379 // holding the lock, and let the stack based objects' destuctors take
2380 // care of destroying it if needed.
2381 nsCOMPtr
<nsIInterfaceRequestor
> ourCallbacks
;
2382 nsCOMPtr
<nsITransportEventSink
> ourEventSink
;
2384 MutexAutoLock
lock(mLock
);
2385 if (mFD
.IsInitialized()) {
2386 ReleaseFD_Locked(mFD
);
2387 // flag mFD as unusable; this prevents other consumers from
2388 // acquiring a reference to mFD.
2389 mFDconnected
= false;
2390 mFDFastOpenInProgress
= false;
2393 // We must release mCallbacks and mEventSink to avoid memory leak
2394 // but only when RecoverFromError() above failed. Otherwise we lose
2395 // link with UI and security callbacks on next connection attempt
2396 // round. That would lead e.g. to a broken certificate exception page.
2397 if (NS_FAILED(mCondition
)) {
2398 mCallbacks
.swap(ourCallbacks
);
2399 mEventSink
.swap(ourEventSink
);
2404 void nsSocketTransport::IsLocal(bool *aIsLocal
) {
2406 MutexAutoLock
lock(mLock
);
2408 #if defined(XP_UNIX)
2409 // Unix-domain sockets are always local.
2410 if (mNetAddr
.raw
.family
== PR_AF_LOCAL
) {
2416 *aIsLocal
= IsLoopBackAddress(&mNetAddr
);
2420 //-----------------------------------------------------------------------------
2423 NS_IMPL_ISUPPORTS(nsSocketTransport
, nsISocketTransport
, nsITransport
,
2424 nsIDNSListener
, nsIClassInfo
, nsIInterfaceRequestor
)
2425 NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport
, nsISocketTransport
, nsITransport
,
2426 nsIDNSListener
, nsIInterfaceRequestor
)
2429 nsSocketTransport::OpenInputStream(uint32_t flags
, uint32_t segsize
,
2430 uint32_t segcount
, nsIInputStream
**result
) {
2432 ("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n", this, flags
));
2434 NS_ENSURE_TRUE(!mInput
.IsReferenced(), NS_ERROR_UNEXPECTED
);
2437 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
2439 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
2440 // XXX if the caller wants blocking, then the caller also gets buffered!
2441 // bool openBuffered = !(flags & OPEN_UNBUFFERED);
2442 bool openBlocking
= (flags
& OPEN_BLOCKING
);
2444 net_ResolveSegmentParams(segsize
, segcount
);
2447 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
2448 rv
= NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
),
2449 !openBlocking
, true, segsize
, segcount
);
2450 if (NS_FAILED(rv
)) return rv
;
2452 // async copy from socket to pipe
2453 rv
= NS_AsyncCopy(&mInput
, pipeOut
, mSocketTransportService
,
2454 NS_ASYNCCOPY_VIA_WRITESEGMENTS
, segsize
);
2455 if (NS_FAILED(rv
)) return rv
;
2461 // flag input stream as open
2462 mInputClosed
= false;
2464 rv
= PostEvent(MSG_ENSURE_CONNECT
);
2465 if (NS_FAILED(rv
)) return rv
;
2472 nsSocketTransport::OpenOutputStream(uint32_t flags
, uint32_t segsize
,
2474 nsIOutputStream
**result
) {
2475 SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n", this,
2478 NS_ENSURE_TRUE(!mOutput
.IsReferenced(), NS_ERROR_UNEXPECTED
);
2481 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
2482 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
2483 // XXX if the caller wants blocking, then the caller also gets buffered!
2484 // bool openBuffered = !(flags & OPEN_UNBUFFERED);
2485 bool openBlocking
= (flags
& OPEN_BLOCKING
);
2487 net_ResolveSegmentParams(segsize
, segcount
);
2490 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
2491 rv
= NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
), true,
2492 !openBlocking
, segsize
, segcount
);
2493 if (NS_FAILED(rv
)) return rv
;
2495 // async copy from socket to pipe
2496 rv
= NS_AsyncCopy(pipeIn
, &mOutput
, mSocketTransportService
,
2497 NS_ASYNCCOPY_VIA_READSEGMENTS
, segsize
);
2498 if (NS_FAILED(rv
)) return rv
;
2504 // flag output stream as open
2505 mOutputClosed
= false;
2507 rv
= PostEvent(MSG_ENSURE_CONNECT
);
2508 if (NS_FAILED(rv
)) return rv
;
2515 nsSocketTransport::Close(nsresult reason
) {
2516 SOCKET_LOG(("nsSocketTransport::Close %p reason=%" PRIx32
, this,
2517 static_cast<uint32_t>(reason
)));
2519 if (NS_SUCCEEDED(reason
)) reason
= NS_BASE_STREAM_CLOSED
;
2521 mDoNotRetryToConnect
= true;
2523 if (mFDFastOpenInProgress
&& mFastOpenCallback
) {
2524 mFastOpenCallback
->SetFastOpenConnected(reason
, false);
2526 mFastOpenCallback
= nullptr;
2528 mInput
.CloseWithStatus(reason
);
2529 mOutput
.CloseWithStatus(reason
);
2534 nsSocketTransport::GetSecurityInfo(nsISupports
**secinfo
) {
2535 MutexAutoLock
lock(mLock
);
2536 NS_IF_ADDREF(*secinfo
= mSecInfo
);
2541 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor
**callbacks
) {
2542 MutexAutoLock
lock(mLock
);
2543 NS_IF_ADDREF(*callbacks
= mCallbacks
);
2548 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor
*callbacks
) {
2549 nsCOMPtr
<nsIInterfaceRequestor
> threadsafeCallbacks
;
2550 NS_NewNotificationCallbacksAggregation(callbacks
, nullptr,
2551 GetCurrentThreadEventTarget(),
2552 getter_AddRefs(threadsafeCallbacks
));
2554 nsCOMPtr
<nsISupports
> secinfo
;
2556 MutexAutoLock
lock(mLock
);
2557 mCallbacks
= threadsafeCallbacks
;
2558 SOCKET_LOG(("Reset callbacks for secinfo=%p callbacks=%p\n", mSecInfo
.get(),
2564 // don't call into PSM while holding mLock!!
2565 nsCOMPtr
<nsISSLSocketControl
> secCtrl(do_QueryInterface(secinfo
));
2566 if (secCtrl
) secCtrl
->SetNotificationCallbacks(threadsafeCallbacks
);
2572 nsSocketTransport::SetEventSink(nsITransportEventSink
*sink
,
2573 nsIEventTarget
*target
) {
2574 nsCOMPtr
<nsITransportEventSink
> temp
;
2577 net_NewTransportEventSinkProxy(getter_AddRefs(temp
), sink
, target
);
2578 if (NS_FAILED(rv
)) return rv
;
2582 MutexAutoLock
lock(mLock
);
2588 nsSocketTransport::IsAlive(bool *result
) {
2591 // During Fast Open we need to return true here.
2592 if (mFDFastOpenInProgress
) {
2597 nsresult conditionWhileLocked
= NS_OK
;
2598 PRFileDescAutoLock
fd(this, false, &conditionWhileLocked
);
2599 if (NS_FAILED(conditionWhileLocked
) || !fd
.IsInitialized()) {
2603 // XXX do some idle-time based checks??
2606 int32_t rval
= PR_Recv(fd
, &c
, 1, PR_MSG_PEEK
, 0);
2608 if ((rval
> 0) || (rval
< 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR
))
2615 nsSocketTransport::GetHost(nsACString
&host
) {
2616 host
= SocketHost();
2621 nsSocketTransport::GetPort(int32_t *port
) {
2622 *port
= (int32_t)SocketPort();
2627 nsSocketTransport::GetScriptableOriginAttributes(
2628 JSContext
*aCx
, JS::MutableHandle
<JS::Value
> aOriginAttributes
) {
2629 if (NS_WARN_IF(!ToJSValue(aCx
, mOriginAttributes
, aOriginAttributes
))) {
2630 return NS_ERROR_FAILURE
;
2636 nsSocketTransport::SetScriptableOriginAttributes(
2637 JSContext
*aCx
, JS::Handle
<JS::Value
> aOriginAttributes
) {
2638 MutexAutoLock
lock(mLock
);
2639 NS_ENSURE_FALSE(mFD
.IsInitialized(), NS_ERROR_FAILURE
);
2641 OriginAttributes attrs
;
2642 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
2643 return NS_ERROR_INVALID_ARG
;
2646 mOriginAttributes
= attrs
;
2650 nsresult
nsSocketTransport::GetOriginAttributes(
2651 OriginAttributes
*aOriginAttributes
) {
2652 NS_ENSURE_ARG(aOriginAttributes
);
2653 *aOriginAttributes
= mOriginAttributes
;
2657 nsresult
nsSocketTransport::SetOriginAttributes(
2658 const OriginAttributes
&aOriginAttributes
) {
2659 MutexAutoLock
lock(mLock
);
2660 NS_ENSURE_FALSE(mFD
.IsInitialized(), NS_ERROR_FAILURE
);
2662 mOriginAttributes
= aOriginAttributes
;
2667 nsSocketTransport::GetPeerAddr(NetAddr
*addr
) {
2668 // once we are in the connected state, mNetAddr will not change.
2669 // so if we can verify that we are in the connected state, then
2670 // we can freely access mNetAddr from any thread without being
2671 // inside a critical section.
2673 if (!mNetAddrIsSet
) {
2675 ("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
2676 "NOT_AVAILABLE because not yet connected.",
2678 return NS_ERROR_NOT_AVAILABLE
;
2681 memcpy(addr
, &mNetAddr
, sizeof(NetAddr
));
2686 nsSocketTransport::GetSelfAddr(NetAddr
*addr
) {
2687 // once we are in the connected state, mSelfAddr will not change.
2688 // so if we can verify that we are in the connected state, then
2689 // we can freely access mSelfAddr from any thread without being
2690 // inside a critical section.
2692 if (!mSelfAddrIsSet
) {
2694 ("nsSocketTransport::GetSelfAddr [this=%p state=%d] "
2695 "NOT_AVAILABLE because not yet connected.",
2697 return NS_ERROR_NOT_AVAILABLE
;
2700 memcpy(addr
, &mSelfAddr
, sizeof(NetAddr
));
2705 nsSocketTransport::Bind(NetAddr
*aLocalAddr
) {
2706 NS_ENSURE_ARG(aLocalAddr
);
2708 MutexAutoLock
lock(mLock
);
2710 return NS_ERROR_FAILURE
;
2713 mBindAddr
= new NetAddr();
2714 memcpy(mBindAddr
.get(), aLocalAddr
, sizeof(NetAddr
));
2720 nsSocketTransport::GetScriptablePeerAddr(nsINetAddr
**addr
) {
2724 rv
= GetPeerAddr(&rawAddr
);
2725 if (NS_FAILED(rv
)) return rv
;
2727 NS_ADDREF(*addr
= new nsNetAddr(&rawAddr
));
2733 nsSocketTransport::GetScriptableSelfAddr(nsINetAddr
**addr
) {
2737 rv
= GetSelfAddr(&rawAddr
);
2738 if (NS_FAILED(rv
)) return rv
;
2740 NS_ADDREF(*addr
= new nsNetAddr(&rawAddr
));
2746 nsSocketTransport::GetTimeout(uint32_t type
, uint32_t *value
) {
2747 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
2748 *value
= (uint32_t)mTimeouts
[type
];
2753 nsSocketTransport::SetTimeout(uint32_t type
, uint32_t value
) {
2754 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
2756 SOCKET_LOG(("nsSocketTransport::SetTimeout %p type=%u, value=%u", this, type
,
2759 // truncate overly large timeout values.
2760 mTimeouts
[type
] = (uint16_t)std::min
<uint32_t>(value
, UINT16_MAX
);
2761 PostEvent(MSG_TIMEOUT_CHANGED
);
2766 nsSocketTransport::SetReuseAddrPort(bool reuseAddrPort
) {
2767 mReuseAddrPort
= reuseAddrPort
;
2772 nsSocketTransport::SetLinger(bool aPolarity
, int16_t aTimeout
) {
2773 MutexAutoLock
lock(mLock
);
2775 mLingerPolarity
= aPolarity
;
2776 mLingerTimeout
= aTimeout
;
2782 nsSocketTransport::SetQoSBits(uint8_t aQoSBits
) {
2783 // Don't do any checking here of bits. Why? Because as of RFC-4594
2784 // several different Class Selector and Assured Forwarding values
2785 // have been defined, but that isn't to say more won't be added later.
2786 // In that case, any checking would be an impediment to interoperating
2787 // with newer QoS definitions.
2789 mQoSBits
= aQoSBits
;
2794 nsSocketTransport::GetQoSBits(uint8_t *aQoSBits
) {
2795 *aQoSBits
= mQoSBits
;
2800 nsSocketTransport::GetRecvBufferSize(uint32_t *aSize
) {
2801 PRFileDescAutoLock
fd(this, false);
2802 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2804 nsresult rv
= NS_OK
;
2805 PRSocketOptionData opt
;
2806 opt
.option
= PR_SockOpt_RecvBufferSize
;
2807 if (PR_GetSocketOption(fd
, &opt
) == PR_SUCCESS
)
2808 *aSize
= opt
.value
.recv_buffer_size
;
2810 rv
= NS_ERROR_FAILURE
;
2816 nsSocketTransport::GetSendBufferSize(uint32_t *aSize
) {
2817 PRFileDescAutoLock
fd(this, false);
2818 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2820 nsresult rv
= NS_OK
;
2821 PRSocketOptionData opt
;
2822 opt
.option
= PR_SockOpt_SendBufferSize
;
2823 if (PR_GetSocketOption(fd
, &opt
) == PR_SUCCESS
)
2824 *aSize
= opt
.value
.send_buffer_size
;
2826 rv
= NS_ERROR_FAILURE
;
2832 nsSocketTransport::SetRecvBufferSize(uint32_t aSize
) {
2833 PRFileDescAutoLock
fd(this, false);
2834 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2836 nsresult rv
= NS_OK
;
2837 PRSocketOptionData opt
;
2838 opt
.option
= PR_SockOpt_RecvBufferSize
;
2839 opt
.value
.recv_buffer_size
= aSize
;
2840 if (PR_SetSocketOption(fd
, &opt
) != PR_SUCCESS
) rv
= NS_ERROR_FAILURE
;
2846 nsSocketTransport::SetSendBufferSize(uint32_t aSize
) {
2847 PRFileDescAutoLock
fd(this, false);
2848 if (!fd
.IsInitialized()) return NS_ERROR_NOT_CONNECTED
;
2850 nsresult rv
= NS_OK
;
2851 PRSocketOptionData opt
;
2852 opt
.option
= PR_SockOpt_SendBufferSize
;
2853 opt
.value
.send_buffer_size
= aSize
;
2854 if (PR_SetSocketOption(fd
, &opt
) != PR_SUCCESS
) rv
= NS_ERROR_FAILURE
;
2860 nsSocketTransport::OnLookupComplete(nsICancelable
*request
, nsIDNSRecord
*rec
,
2862 SOCKET_LOG(("nsSocketTransport::OnLookupComplete: this=%p status %" PRIx32
2864 this, static_cast<uint32_t>(status
)));
2865 if (NS_FAILED(status
) && mDNSTxtRequest
) {
2866 mDNSTxtRequest
->Cancel(NS_ERROR_ABORT
);
2867 } else if (NS_SUCCEEDED(status
)) {
2868 mDNSRecord
= static_cast<nsIDNSRecord
*>(rec
);
2871 // flag host lookup complete for the benefit of the ResolveHost method.
2872 if (!mDNSTxtRequest
) {
2874 Telemetry::Accumulate(Telemetry::ESNI_KEYS_RECORD_FETCH_DELAYS
, 0);
2877 nsresult rv
= PostEvent(MSG_DNS_LOOKUP_COMPLETE
, status
, nullptr);
2879 // if posting a message fails, then we should assume that the socket
2880 // transport has been shutdown. this should never happen! if it does
2881 // it means that the socket transport service was shutdown before the
2883 if (NS_FAILED(rv
)) {
2884 NS_WARNING("unable to post DNS lookup complete message");
2888 status
; // remember the status to send it when esni lookup is ready.
2889 mDNSRequest
= nullptr;
2890 mDNSARequestFinished
= PR_IntervalNow();
2897 nsSocketTransport::OnLookupByTypeComplete(nsICancelable
*request
,
2898 nsIDNSByTypeRecord
*txtResponse
,
2901 ("nsSocketTransport::OnLookupByTypeComplete: "
2902 "this=%p status %" PRIx32
".",
2903 this, static_cast<uint32_t>(status
)));
2904 MOZ_ASSERT(mDNSTxtRequest
== request
);
2906 if (NS_SUCCEEDED(status
)) {
2907 txtResponse
->GetRecordsAsOneString(mDNSRecordTxt
);
2908 mDNSRecordTxt
.Trim(" ");
2910 Telemetry::Accumulate(Telemetry::ESNI_KEYS_RECORDS_FOUND
,
2911 NS_SUCCEEDED(status
));
2912 // flag host lookup complete for the benefit of the ResolveHost method.
2915 MOZ_ASSERT(mDNSARequestFinished
);
2916 Telemetry::Accumulate(
2917 Telemetry::ESNI_KEYS_RECORD_FETCH_DELAYS
,
2918 PR_IntervalToMilliseconds(PR_IntervalNow() - mDNSARequestFinished
));
2920 nsresult rv
= PostEvent(MSG_DNS_LOOKUP_COMPLETE
, mDNSLookupStatus
, nullptr);
2922 // if posting a message fails, then we should assume that the socket
2923 // transport has been shutdown. this should never happen! if it does
2924 // it means that the socket transport service was shutdown before the
2926 if (NS_FAILED(rv
)) {
2927 NS_WARNING("unable to post DNS lookup complete message");
2930 mDNSTxtRequest
= nullptr;
2936 // nsIInterfaceRequestor
2938 nsSocketTransport::GetInterface(const nsIID
&iid
, void **result
) {
2939 if (iid
.Equals(NS_GET_IID(nsIDNSRecord
))) {
2940 return mDNSRecord
? mDNSRecord
->QueryInterface(iid
, result
)
2941 : NS_ERROR_NO_INTERFACE
;
2943 return this->QueryInterface(iid
, result
);
2947 nsSocketTransport::GetInterfaces(uint32_t *count
, nsIID
***array
) {
2948 return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport
)(count
, array
);
2952 nsSocketTransport::GetScriptableHelper(nsIXPCScriptable
**_retval
) {
2958 nsSocketTransport::GetContractID(nsACString
&aContractID
) {
2959 aContractID
.SetIsVoid(true);
2964 nsSocketTransport::GetClassDescription(nsACString
&aClassDescription
) {
2965 aClassDescription
.SetIsVoid(true);
2970 nsSocketTransport::GetClassID(nsCID
**aClassID
) {
2971 *aClassID
= nullptr;
2976 nsSocketTransport::GetFlags(uint32_t *aFlags
) {
2977 *aFlags
= nsIClassInfo::THREADSAFE
;
2982 nsSocketTransport::GetClassIDNoAlloc(nsCID
*aClassIDNoAlloc
) {
2983 return NS_ERROR_NOT_AVAILABLE
;
2987 nsSocketTransport::GetConnectionFlags(uint32_t *value
) {
2988 *value
= mConnectionFlags
;
2993 nsSocketTransport::SetConnectionFlags(uint32_t value
) {
2995 ("nsSocketTransport::SetConnectionFlags %p flags=%u", this, value
));
2997 mConnectionFlags
= value
;
2998 mIsPrivate
= value
& nsISocketTransport::NO_PERMANENT_STORAGE
;
3004 nsSocketTransport::GetTlsFlags(uint32_t *value
) {
3010 nsSocketTransport::SetTlsFlags(uint32_t value
) {
3015 void nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled
) {
3016 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3018 // The global pref toggles keepalive as a system feature; it only affects
3019 // an individual socket if keepalive has been specifically enabled for it.
3020 // So, ensure keepalive is configured correctly if previously enabled.
3021 if (mKeepaliveEnabled
) {
3022 nsresult rv
= SetKeepaliveEnabledInternal(aEnabled
);
3023 if (NS_WARN_IF(NS_FAILED(rv
))) {
3024 SOCKET_LOG((" SetKeepaliveEnabledInternal [%s] failed rv[0x%" PRIx32
"]",
3025 aEnabled
? "enable" : "disable", static_cast<uint32_t>(rv
)));
3030 nsresult
nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable
) {
3031 MOZ_ASSERT(mKeepaliveIdleTimeS
> 0 && mKeepaliveIdleTimeS
<= kMaxTCPKeepIdle
);
3032 MOZ_ASSERT(mKeepaliveRetryIntervalS
> 0 &&
3033 mKeepaliveRetryIntervalS
<= kMaxTCPKeepIntvl
);
3034 MOZ_ASSERT(mKeepaliveProbeCount
> 0 &&
3035 mKeepaliveProbeCount
<= kMaxTCPKeepCount
);
3037 PRFileDescAutoLock
fd(this, true);
3038 if (NS_WARN_IF(!fd
.IsInitialized())) {
3039 return NS_ERROR_NOT_INITIALIZED
;
3042 // Only enable if keepalives are globally enabled, but ensure other
3043 // options are set correctly on the fd.
3044 bool enable
= aEnable
&& mSocketTransportService
->IsKeepaliveEnabled();
3046 fd
.SetKeepaliveVals(enable
, mKeepaliveIdleTimeS
, mKeepaliveRetryIntervalS
,
3047 mKeepaliveProbeCount
);
3048 if (NS_WARN_IF(NS_FAILED(rv
))) {
3049 SOCKET_LOG((" SetKeepaliveVals failed rv[0x%" PRIx32
"]",
3050 static_cast<uint32_t>(rv
)));
3053 rv
= fd
.SetKeepaliveEnabled(enable
);
3054 if (NS_WARN_IF(NS_FAILED(rv
))) {
3055 SOCKET_LOG((" SetKeepaliveEnabled failed rv[0x%" PRIx32
"]",
3056 static_cast<uint32_t>(rv
)));
3063 nsSocketTransport::GetKeepaliveEnabled(bool *aResult
) {
3064 MOZ_ASSERT(aResult
);
3066 *aResult
= mKeepaliveEnabled
;
3070 nsresult
nsSocketTransport::EnsureKeepaliveValsAreInitialized() {
3071 nsresult rv
= NS_OK
;
3073 if (mKeepaliveIdleTimeS
== -1) {
3074 rv
= mSocketTransportService
->GetKeepaliveIdleTime(&val
);
3075 if (NS_WARN_IF(NS_FAILED(rv
))) {
3078 mKeepaliveIdleTimeS
= val
;
3080 if (mKeepaliveRetryIntervalS
== -1) {
3081 rv
= mSocketTransportService
->GetKeepaliveRetryInterval(&val
);
3082 if (NS_WARN_IF(NS_FAILED(rv
))) {
3085 mKeepaliveRetryIntervalS
= val
;
3087 if (mKeepaliveProbeCount
== -1) {
3088 rv
= mSocketTransportService
->GetKeepaliveProbeCount(&val
);
3089 if (NS_WARN_IF(NS_FAILED(rv
))) {
3092 mKeepaliveProbeCount
= val
;
3098 nsSocketTransport::SetKeepaliveEnabled(bool aEnable
) {
3099 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
3100 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3102 if (aEnable
== mKeepaliveEnabled
) {
3103 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.", this,
3104 aEnable
? "enabled" : "disabled"));
3108 nsresult rv
= NS_OK
;
3110 rv
= EnsureKeepaliveValsAreInitialized();
3111 if (NS_WARN_IF(NS_FAILED(rv
))) {
3113 (" SetKeepaliveEnabled [%p] "
3114 "error [0x%" PRIx32
"] initializing keepalive vals",
3115 this, static_cast<uint32_t>(rv
)));
3120 ("nsSocketTransport::SetKeepaliveEnabled [%p] "
3121 "%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
3123 this, aEnable
? "enabled" : "disabled", mKeepaliveIdleTimeS
,
3124 mKeepaliveRetryIntervalS
, mKeepaliveProbeCount
,
3125 mSocketTransportService
->IsKeepaliveEnabled() ? "enabled" : "disabled"));
3127 // Set mKeepaliveEnabled here so that state is maintained; it is possible
3128 // that we're in between fds, e.g. the 1st IP address failed, so we're about
3129 // to retry on a 2nd from the DNS record.
3130 mKeepaliveEnabled
= aEnable
;
3132 rv
= SetKeepaliveEnabledInternal(aEnable
);
3133 if (NS_WARN_IF(NS_FAILED(rv
))) {
3134 SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%" PRIx32
"]",
3135 static_cast<uint32_t>(rv
)));
3140 #else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
3141 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
3142 return NS_ERROR_NOT_IMPLEMENTED
;
3147 nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime
, int32_t aRetryInterval
) {
3148 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
3149 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3150 if (NS_WARN_IF(aIdleTime
<= 0 || kMaxTCPKeepIdle
< aIdleTime
)) {
3151 return NS_ERROR_INVALID_ARG
;
3153 if (NS_WARN_IF(aRetryInterval
<= 0 || kMaxTCPKeepIntvl
< aRetryInterval
)) {
3154 return NS_ERROR_INVALID_ARG
;
3157 if (aIdleTime
== mKeepaliveIdleTimeS
&&
3158 aRetryInterval
== mKeepaliveRetryIntervalS
) {
3160 ("nsSocketTransport::SetKeepaliveVals [%p] idle time "
3161 "already %ds and retry interval already %ds.",
3162 this, mKeepaliveIdleTimeS
, mKeepaliveRetryIntervalS
));
3165 mKeepaliveIdleTimeS
= aIdleTime
;
3166 mKeepaliveRetryIntervalS
= aRetryInterval
;
3168 nsresult rv
= NS_OK
;
3169 if (mKeepaliveProbeCount
== -1) {
3171 nsresult rv
= mSocketTransportService
->GetKeepaliveProbeCount(&val
);
3172 if (NS_WARN_IF(NS_FAILED(rv
))) {
3175 mKeepaliveProbeCount
= val
;
3179 ("nsSocketTransport::SetKeepaliveVals [%p] "
3180 "keepalive %s, idle time[%ds] retry interval[%ds] "
3182 this, mKeepaliveEnabled
? "enabled" : "disabled", mKeepaliveIdleTimeS
,
3183 mKeepaliveRetryIntervalS
, mKeepaliveProbeCount
));
3185 PRFileDescAutoLock
fd(this, true);
3186 if (NS_WARN_IF(!fd
.IsInitialized())) {
3187 return NS_ERROR_NULL_POINTER
;
3190 rv
= fd
.SetKeepaliveVals(mKeepaliveEnabled
, mKeepaliveIdleTimeS
,
3191 mKeepaliveRetryIntervalS
, mKeepaliveProbeCount
);
3192 if (NS_WARN_IF(NS_FAILED(rv
))) {
3197 SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
3198 return NS_ERROR_NOT_IMPLEMENTED
;
3202 #ifdef ENABLE_SOCKET_TRACING
3208 static void DumpBytesToFile(const char *path
, const char *header
,
3209 const char *buf
, int32_t n
) {
3210 FILE *fp
= fopen(path
, "a");
3212 fprintf(fp
, "\n%s [%d bytes]\n", header
, n
);
3214 const unsigned char *p
;
3216 p
= (const unsigned char *)buf
;
3218 int32_t i
, row_max
= std::min(16, n
);
3220 for (i
= 0; i
< row_max
; ++i
) fprintf(fp
, "%02x ", *p
++);
3221 for (i
= row_max
; i
< 16; ++i
) fprintf(fp
, " ");
3223 p
= (const unsigned char *)buf
;
3224 for (i
= 0; i
< row_max
; ++i
, ++p
) {
3226 fprintf(fp
, "%c", *p
);
3240 void nsSocketTransport::TraceInBuf(const char *buf
, int32_t n
) {
3241 char *val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
3242 if (!val
|| !*val
) return;
3244 nsAutoCString header
;
3245 header
.AssignLiteral("Reading from: ");
3246 header
.Append(mHost
);
3248 header
.AppendInt(mPort
);
3250 DumpBytesToFile(val
, header
.get(), buf
, n
);
3253 void nsSocketTransport::TraceOutBuf(const char *buf
, int32_t n
) {
3254 char *val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
3255 if (!val
|| !*val
) return;
3257 nsAutoCString header
;
3258 header
.AssignLiteral("Writing to: ");
3259 header
.Append(mHost
);
3261 header
.AppendInt(mPort
);
3263 DumpBytesToFile(val
, header
.get(), buf
, n
);
3268 static void LogNSPRError(const char *aPrefix
, const void *aObjPtr
) {
3270 PRErrorCode errCode
= PR_GetError();
3271 int errLen
= PR_GetErrorTextLength();
3272 nsAutoCString errStr
;
3274 errStr
.SetLength(errLen
);
3275 PR_GetErrorText(errStr
.BeginWriting());
3278 nsPrintfCString("%s [%p] NSPR error[0x%x] %s.",
3279 aPrefix
? aPrefix
: "nsSocketTransport", aObjPtr
, errCode
,
3280 errLen
> 0 ? errStr
.BeginReading() : "<no error text>")
3285 nsresult
nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(
3287 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3288 MOZ_ASSERT(!(aEnable
&& !gSocketTransportService
->IsKeepaliveEnabled()),
3289 "Cannot enable keepalive if global pref is disabled!");
3290 if (aEnable
&& !gSocketTransportService
->IsKeepaliveEnabled()) {
3291 return NS_ERROR_ILLEGAL_VALUE
;
3294 PRSocketOptionData opt
;
3296 opt
.option
= PR_SockOpt_Keepalive
;
3297 opt
.value
.keep_alive
= aEnable
;
3298 PRStatus status
= PR_SetSocketOption(mFd
, &opt
);
3299 if (NS_WARN_IF(status
!= PR_SUCCESS
)) {
3300 LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
3302 return ErrorAccordingToNSPR(PR_GetError());
3307 static void LogOSError(const char *aPrefix
, const void *aObjPtr
) {
3309 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3312 DWORD errCode
= WSAGetLastError();
3314 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
|
3315 FORMAT_MESSAGE_IGNORE_INSERTS
,
3316 NULL
, errCode
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
3317 (LPTSTR
)&errMessage
, 0, NULL
);
3319 int errCode
= errno
;
3320 char *errMessage
= strerror(errno
);
3322 NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%x] %s",
3323 aPrefix
? aPrefix
: "nsSocketTransport", aObjPtr
,
3325 errMessage
? errMessage
: "<no error text>")
3328 LocalFree(errMessage
);
3333 /* XXX PR_SetSockOpt does not support setting keepalive values, so native
3334 * handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
3335 * file. Requires inclusion of NSPR private/pprio.h, and platform headers.
3338 nsresult
nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(
3339 bool aEnabled
, int aIdleTime
, int aRetryInterval
, int aProbeCount
) {
3340 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
3341 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3342 if (NS_WARN_IF(aIdleTime
<= 0 || kMaxTCPKeepIdle
< aIdleTime
)) {
3343 return NS_ERROR_INVALID_ARG
;
3345 if (NS_WARN_IF(aRetryInterval
<= 0 || kMaxTCPKeepIntvl
< aRetryInterval
)) {
3346 return NS_ERROR_INVALID_ARG
;
3348 if (NS_WARN_IF(aProbeCount
<= 0 || kMaxTCPKeepCount
< aProbeCount
)) {
3349 return NS_ERROR_INVALID_ARG
;
3352 PROsfd sock
= PR_FileDesc2NativeHandle(mFd
);
3353 if (NS_WARN_IF(sock
== -1)) {
3354 LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
3356 return ErrorAccordingToNSPR(PR_GetError());
3361 // Windows allows idle time and retry interval to be set; NOT ping count.
3362 struct tcp_keepalive keepalive_vals
= {(u_long
)aEnabled
,
3363 // Windows uses msec.
3364 (u_long
)(aIdleTime
* 1000UL),
3365 (u_long
)(aRetryInterval
* 1000UL)};
3366 DWORD bytes_returned
;
3368 WSAIoctl(sock
, SIO_KEEPALIVE_VALS
, &keepalive_vals
,
3369 sizeof(keepalive_vals
), NULL
, 0, &bytes_returned
, NULL
, NULL
);
3370 if (NS_WARN_IF(err
)) {
3371 LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport
);
3372 return NS_ERROR_UNEXPECTED
;
3376 #elif defined(XP_DARWIN)
3377 // Darwin uses sec; only supports idle time being set.
3378 int err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPALIVE
, &aIdleTime
,
3380 if (NS_WARN_IF(err
)) {
3381 LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
3383 return NS_ERROR_UNEXPECTED
;
3387 #elif defined(XP_UNIX)
3388 // Not all *nix OSes support the following setsockopt() options
3389 // ... but we assume they are supported in the Android kernel;
3390 // build errors will tell us if they are not.
3391 # if defined(ANDROID) || defined(TCP_KEEPIDLE)
3392 // Idle time until first keepalive probe; interval between ack'd probes;
3394 int err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPIDLE
, &aIdleTime
,
3396 if (NS_WARN_IF(err
)) {
3397 LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
3399 return NS_ERROR_UNEXPECTED
;
3403 # if defined(ANDROID) || defined(TCP_KEEPINTVL)
3404 // Interval between unack'd keepalive probes; seconds.
3405 err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPINTVL
, &aRetryInterval
,
3406 sizeof(aRetryInterval
));
3407 if (NS_WARN_IF(err
)) {
3408 LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
3410 return NS_ERROR_UNEXPECTED
;
3414 # if defined(ANDROID) || defined(TCP_KEEPCNT)
3415 // Number of unack'd keepalive probes before connection times out.
3416 err
= setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPCNT
, &aProbeCount
,
3417 sizeof(aProbeCount
));
3418 if (NS_WARN_IF(err
)) {
3419 LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
3421 return NS_ERROR_UNEXPECTED
;
3428 "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
3429 "called on unsupported platform!");
3430 return NS_ERROR_UNEXPECTED
;
3434 void nsSocketTransport::CloseSocket(PRFileDesc
*aFd
, bool aTelemetryEnabled
) {
3436 AttachShutdownLayer(aFd
);
3439 // We use PRIntervalTime here because we need
3440 // nsIOService::LastOfflineStateChange time and
3441 // nsIOService::LastConectivityChange time to be atomic.
3442 PRIntervalTime closeStarted
;
3443 if (aTelemetryEnabled
) {
3444 closeStarted
= PR_IntervalNow();
3449 if (aTelemetryEnabled
) {
3450 SendPRBlockingTelemetry(
3451 closeStarted
, Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL
,
3452 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN
,
3453 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE
,
3454 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE
,
3455 Telemetry::PRCLOSE_TCP_BLOCKING_TIME_OFFLINE
);
3459 void nsSocketTransport::SendPRBlockingTelemetry(
3460 PRIntervalTime aStart
, Telemetry::HistogramID aIDNormal
,
3461 Telemetry::HistogramID aIDShutdown
,
3462 Telemetry::HistogramID aIDConnectivityChange
,
3463 Telemetry::HistogramID aIDLinkChange
, Telemetry::HistogramID aIDOffline
) {
3464 PRIntervalTime now
= PR_IntervalNow();
3465 if (gIOService
->IsNetTearingDown()) {
3466 Telemetry::Accumulate(aIDShutdown
, PR_IntervalToMilliseconds(now
- aStart
));
3468 } else if (PR_IntervalToSeconds(now
- gIOService
->LastConnectivityChange()) <
3470 Telemetry::Accumulate(aIDConnectivityChange
,
3471 PR_IntervalToMilliseconds(now
- aStart
));
3472 } else if (PR_IntervalToSeconds(now
- gIOService
->LastNetworkLinkChange()) <
3474 Telemetry::Accumulate(aIDLinkChange
,
3475 PR_IntervalToMilliseconds(now
- aStart
));
3477 } else if (PR_IntervalToSeconds(now
- gIOService
->LastOfflineStateChange()) <
3479 Telemetry::Accumulate(aIDOffline
, PR_IntervalToMilliseconds(now
- aStart
));
3481 Telemetry::Accumulate(aIDNormal
, PR_IntervalToMilliseconds(now
- aStart
));
3486 nsSocketTransport::SetFastOpenCallback(TCPFastOpen
*aFastOpen
) {
3487 mFastOpenCallback
= aFastOpen
;
3492 nsSocketTransport::GetFirstRetryError(nsresult
*aFirstRetryError
) {
3493 *aFirstRetryError
= mFirstRetryError
;
3498 nsSocketTransport::GetResetIPFamilyPreference(bool *aReset
) {
3499 *aReset
= mResetFamilyPreference
;
3504 nsSocketTransport::GetEsniUsed(bool *aEsniUsed
) {
3505 *aEsniUsed
= mEsniUsed
;
3510 } // namespace mozilla