Bug 1526591 - Remove devtools.inspector.shapesHighlighter.enabled pref. r=rcaliman
[gecko.git] / netwerk / base / nsSocketTransport2.cpp
blob24b93310a1b19000398f00a9aa68e672a994f7f3
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"
17 #include "nsNetCID.h"
18 #include "nsNetUtil.h"
19 #include "nsAutoPtr.h"
20 #include "nsCOMPtr.h"
21 #include "plstr.h"
22 #include "prerr.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"
31 #include "nsIPipe.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"
39 #include <algorithm>
41 #include "nsPrintfCString.h"
42 #include "xpcpublic.h"
44 #if defined(XP_WIN)
45 # include "ShutdownLayer.h"
46 #endif
48 /* Following inclusions required for keepalive config not supported by NSPR. */
49 #include "private/pprio.h"
50 #if defined(XP_WIN)
51 # include <winsock2.h>
52 # include <mstcpip.h>
53 #elif defined(XP_UNIX)
54 # include <errno.h>
55 # include <netinet/tcp.h>
56 #endif
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 //-----------------------------------------------------------------------------
70 namespace mozilla {
71 namespace net {
73 class nsSocketEvent : public Runnable {
74 public:
75 nsSocketEvent(nsSocketTransport *transport, uint32_t type,
76 nsresult status = NS_OK, nsISupports *param = nullptr)
77 : Runnable("net::nsSocketEvent"),
78 mTransport(transport),
79 mType(type),
80 mStatus(status),
81 mParam(param) {}
83 NS_IMETHOD Run() override {
84 mTransport->OnSocketEvent(mType, mStatus, mParam);
85 return NS_OK;
88 private:
89 RefPtr<nsSocketTransport> mTransport;
91 uint32_t mType;
92 nsresult mStatus;
93 nsCOMPtr<nsISupports> mParam;
96 //-----------------------------------------------------------------------------
98 //#define TEST_CONNECT_ERRORS
99 #ifdef TEST_CONNECT_ERRORS
100 # include <stdlib.h>
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.
106 int n = rand();
107 if (n > RAND_MAX / 2) {
108 struct {
109 PRErrorCode err_code;
110 const char *err_name;
111 } errors[] = {
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));
129 return code;
131 #endif
133 //-----------------------------------------------------------------------------
135 nsresult ErrorAccordingToNSPR(PRErrorCode errorCode) {
136 nsresult rv = NS_ERROR_FAILURE;
137 switch (errorCode) {
138 case PR_WOULD_BLOCK_ERROR:
139 rv = NS_BASE_STREAM_WOULD_BLOCK;
140 break;
141 case PR_CONNECT_ABORTED_ERROR:
142 case PR_CONNECT_RESET_ERROR:
143 rv = NS_ERROR_NET_RESET;
144 break;
145 case PR_END_OF_FILE_ERROR: // XXX document this correlation
146 rv = NS_ERROR_NET_INTERRUPT;
147 break;
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
153 // be checked.
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;
161 break;
162 case PR_ADDRESS_NOT_SUPPORTED_ERROR:
163 rv = NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
164 break;
165 case PR_IO_TIMEOUT_ERROR:
166 case PR_CONNECT_TIMEOUT_ERROR:
167 rv = NS_ERROR_NET_TIMEOUT;
168 break;
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;
177 break;
178 case PR_ADDRESS_IN_USE_ERROR:
179 rv = NS_ERROR_SOCKET_ADDRESS_IN_USE;
180 break;
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;
184 break;
185 case PR_IS_DIRECTORY_ERROR:
186 rv = NS_ERROR_FILE_IS_DIRECTORY;
187 break;
188 case PR_LOOP_ERROR:
189 rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
190 break;
191 case PR_NAME_TOO_LONG_ERROR:
192 rv = NS_ERROR_FILE_NAME_TOO_LONG;
193 break;
194 case PR_NO_DEVICE_SPACE_ERROR:
195 rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
196 break;
197 case PR_NOT_DIRECTORY_ERROR:
198 rv = NS_ERROR_FILE_NOT_DIRECTORY;
199 break;
200 case PR_READ_ONLY_FILESYSTEM_ERROR:
201 rv = NS_ERROR_FILE_READ_ONLY;
202 break;
203 case PR_BAD_ADDRESS_ERROR:
204 rv = NS_ERROR_UNKNOWN_HOST;
205 break;
206 default:
207 if (psm::IsNSSErrorCode(errorCode)) {
208 rv = psm::GetXPCOMFromNSSError(errorCode);
210 break;
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.
233 // PR_IO_ERROR
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)));
238 return rv;
241 //-----------------------------------------------------------------------------
242 // socket input stream impl
243 //-----------------------------------------------------------------------------
245 nsSocketInputStream::nsSocketInputStream(nsSocketTransport *trans)
246 : mTransport(trans),
247 mReaderRefCnt(0),
248 mCondition(NS_OK),
249 mCallbackFlags(0),
250 mByteCount(0) {}
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();
273 mCallbackFlags = 0;
277 if (callback) callback->OnInputStreamReady(this);
280 NS_IMPL_QUERY_INTERFACE(nsSocketInputStream, nsIInputStream,
281 nsIAsyncInputStream)
283 NS_IMETHODIMP_(MozExternalRefCountType)
284 nsSocketInputStream::AddRef() {
285 ++mReaderRefCnt;
286 return mTransport->AddRef();
289 NS_IMETHODIMP_(MozExternalRefCountType)
290 nsSocketInputStream::Release() {
291 if (--mReaderRefCnt == 0) Close();
292 return mTransport->Release();
295 NS_IMETHODIMP
296 nsSocketInputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
298 NS_IMETHODIMP
299 nsSocketInputStream::Available(uint64_t *avail) {
300 SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
302 *avail = 0;
304 PRFileDesc *fd;
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
320 // with MSG_PEEK
321 if ((n == -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR)) {
322 char c;
324 n = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
325 SOCKET_LOG(
326 ("nsSocketInputStream::Available [this=%p] "
327 "using PEEK backup n=%d]\n",
328 this, n));
331 nsresult rv;
333 MutexAutoLock lock(mTransport->mLock);
335 mTransport->ReleaseFD_Locked(fd);
337 if (n >= 0)
338 *avail = n;
339 else {
340 PRErrorCode code = PR_GetError();
341 if (code == PR_WOULD_BLOCK_ERROR) return NS_OK;
342 mCondition = ErrorAccordingToNSPR(code);
344 rv = mCondition;
346 if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
347 return rv;
350 NS_IMETHODIMP
351 nsSocketInputStream::Read(char *buf, uint32_t count, uint32_t *countRead) {
352 SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count));
354 *countRead = 0;
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));
376 nsresult rv = NS_OK;
378 MutexAutoLock lock(mTransport->mLock);
380 #ifdef ENABLE_SOCKET_TRACING
381 if (n > 0) mTransport->TraceInBuf(buf, n);
382 #endif
384 mTransport->ReleaseFD_Locked(fd);
386 if (n > 0)
387 mByteCount += (*countRead = n);
388 else if (n < 0) {
389 PRErrorCode code = PR_GetError();
390 if (code == PR_WOULD_BLOCK_ERROR) return NS_BASE_STREAM_WOULD_BLOCK;
391 mCondition = ErrorAccordingToNSPR(code);
393 rv = mCondition;
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);
400 return rv;
403 NS_IMETHODIMP
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;
410 NS_IMETHODIMP
411 nsSocketInputStream::IsNonBlocking(bool *nonblocking) {
412 *nonblocking = true;
413 return NS_OK;
416 NS_IMETHODIMP
417 nsSocketInputStream::CloseWithStatus(nsresult reason) {
418 SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%" PRIx32
419 "]\n",
420 this, static_cast<uint32_t>(reason)));
422 // may be called from any thread
424 nsresult rv;
426 MutexAutoLock lock(mTransport->mLock);
428 if (NS_SUCCEEDED(mCondition))
429 rv = mCondition = reason;
430 else
431 rv = NS_OK;
433 if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
434 return NS_OK;
437 NS_IMETHODIMP
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) {
448 // build event proxy
450 mCallback = NS_NewInputStreamReadyEvent("nsSocketInputStream::AsyncWait",
451 callback, target);
452 } else
453 mCallback = callback;
454 mCallbackFlags = flags;
456 hasError = NS_FAILED(mCondition);
457 } // unlock mTransport->mLock
459 if (hasError) {
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
463 // callback.
464 mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING);
465 } else {
466 mTransport->OnInputPending();
469 return NS_OK;
472 //-----------------------------------------------------------------------------
473 // socket output stream impl
474 //-----------------------------------------------------------------------------
476 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport *trans)
477 : mTransport(trans),
478 mWriterRefCnt(0),
479 mCondition(NS_OK),
480 mCallbackFlags(0),
481 mByteCount(0) {}
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
489 "]\n",
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();
505 mCallbackFlags = 0;
509 if (callback) callback->OnOutputStreamReady(this);
512 NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream, nsIOutputStream,
513 nsIAsyncOutputStream)
515 NS_IMETHODIMP_(MozExternalRefCountType)
516 nsSocketOutputStream::AddRef() {
517 ++mWriterRefCnt;
518 return mTransport->AddRef();
521 NS_IMETHODIMP_(MozExternalRefCountType)
522 nsSocketOutputStream::Release() {
523 if (--mWriterRefCnt == 0) Close();
524 return mTransport->Release();
527 NS_IMETHODIMP
528 nsSocketOutputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
530 NS_IMETHODIMP
531 nsSocketOutputStream::Flush() { return NS_OK; }
533 NS_IMETHODIMP
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));
538 *countWritten = 0;
540 // A write of 0 bytes can be used to force the initial SSL handshake, so do
541 // not reject that.
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;
562 if (!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));
580 nsresult rv = NS_OK;
582 MutexAutoLock lock(mTransport->mLock);
584 #ifdef ENABLE_SOCKET_TRACING
585 if (n > 0) mTransport->TraceOutBuf(buf, n);
586 #endif
588 mTransport->ReleaseFD_Locked(fd);
590 if (n > 0)
591 mByteCount += (*countWritten = n);
592 else if (n < 0) {
593 PRErrorCode code = PR_GetError();
594 if (code == PR_WOULD_BLOCK_ERROR) return NS_BASE_STREAM_WOULD_BLOCK;
595 mCondition = ErrorAccordingToNSPR(code);
597 rv = mCondition;
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);
611 return rv;
614 NS_IMETHODIMP
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);
628 NS_IMETHODIMP
629 nsSocketOutputStream::WriteFrom(nsIInputStream *stream, uint32_t count,
630 uint32_t *countRead) {
631 return stream->ReadSegments(WriteFromSegments, this, count, countRead);
634 NS_IMETHODIMP
635 nsSocketOutputStream::IsNonBlocking(bool *nonblocking) {
636 *nonblocking = true;
637 return NS_OK;
640 NS_IMETHODIMP
641 nsSocketOutputStream::CloseWithStatus(nsresult reason) {
642 SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%" PRIx32
643 "]\n",
644 this, static_cast<uint32_t>(reason)));
646 // may be called from any thread
648 nsresult rv;
650 MutexAutoLock lock(mTransport->mLock);
652 if (NS_SUCCEEDED(mCondition))
653 rv = mCondition = reason;
654 else
655 rv = NS_OK;
657 if (NS_FAILED(rv)) mTransport->OnOutputClosed(rv);
658 return NS_OK;
661 NS_IMETHODIMP
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) {
672 // build event proxy
674 mCallback = NS_NewOutputStreamReadyEvent(callback, target);
675 } else
676 mCallback = callback;
678 mCallbackFlags = flags;
680 mTransport->OnOutputPending();
681 return NS_OK;
684 //-----------------------------------------------------------------------------
685 // socket transport impl
686 //-----------------------------------------------------------------------------
688 nsSocketTransport::nsSocketTransport()
689 : mTypes(nullptr),
690 mTypeCount(0),
691 mPort(0),
692 mProxyPort(0),
693 mOriginPort(0),
694 mProxyTransparent(false),
695 mProxyTransparentResolvesHost(false),
696 mHttpsProxy(false),
697 mConnectionFlags(0),
698 mResetFamilyPreference(false),
699 mTlsFlags(0),
700 mReuseAddrPort(false),
701 mState(STATE_CLOSED),
702 mAttached(false),
703 mInputClosed(true),
704 mOutputClosed(true),
705 mResolving(false),
706 mDNSLookupStatus(NS_OK),
707 mDNSARequestFinished(0),
708 mEsniQueried(false),
709 mEsniUsed(false),
710 mNetAddrIsSet(false),
711 mSelfAddrIsSet(false),
712 mLock("nsSocketTransport.mLock"),
713 mFD(this),
714 mFDref(0),
715 mFDconnected(false),
716 mFDFastOpenInProgress(false),
717 mSocketTransportService(gSocketTransportService),
718 mInput(this),
719 mOutput(this),
720 mLingerPolarity(false),
721 mLingerTimeout(0),
722 mQoSBits(0x00),
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));
745 CleanupTypes();
748 void nsSocketTransport::CleanupTypes() {
749 // cleanup socket type info
750 if (mTypes) {
751 for (uint32_t i = 0; i < mTypeCount; ++i) {
752 PL_strfree(mTypes[i]);
754 free(mTypes);
755 mTypes = nullptr;
757 mTypeCount = 0;
760 nsresult nsSocketTransport::Init(const char **types, uint32_t typeCount,
761 const nsACString &host, uint16_t port,
762 const nsACString &hostRoute,
763 uint16_t portRoute,
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
773 mOriginHost = host;
774 mOriginPort = port;
775 if (!hostRoute.IsEmpty()) {
776 mHost = hostRoute;
777 mPort = portRoute;
778 } else {
779 mHost = host;
780 mPort = port;
783 if (proxyInfo) {
784 mHttpsProxy = proxyInfo->IsHTTPS();
787 const char *proxyType = nullptr;
788 mProxyInfo = proxyInfo;
789 if (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"))) {
796 proxyType = nullptr;
800 SOCKET_LOG1(
801 ("nsSocketTransport::Init [this=%p host=%s:%hu origin=%s:%d "
802 "proxy=%s:%hu]\n",
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
811 // better exist!
812 nsresult rv;
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);
824 else
825 mTypes[i] = PL_strdup(types[type++]);
827 if (!mTypes[i]) {
828 mTypeCount = i;
829 return NS_ERROR_OUT_OF_MEMORY;
831 nsCOMPtr<nsISocketProvider> provider;
832 rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
833 if (NS_FAILED(rv)) {
834 NS_WARNING("no registered socket provider");
835 return rv;
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;
852 return NS_OK;
855 #if defined(XP_UNIX)
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);
869 # else
870 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
871 # endif
872 } else {
873 // The name isn't abstract socket address. So this is Unix domain
874 // socket that has file path.
875 mHost.Assign(name, length);
877 mPort = 0;
878 mTypeCount = 0;
880 mNetAddr.local.family = AF_LOCAL;
881 memcpy(mNetAddr.local.path, name, length);
882 mNetAddr.local.path[length] = '\0';
883 mNetAddrIsSet = true;
885 return NS_OK;
887 #endif
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));
896 mHost.Assign(buf);
898 uint16_t port;
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;
903 else
904 port = 0;
905 mPort = ntohs(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;
912 SetSocketName(fd);
913 mNetAddrIsSet = true;
916 MutexAutoLock lock(mLock);
918 mFD = fd;
919 mFDref = 1;
920 mFDconnected = true;
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);
929 SOCKET_LOG(
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) {
940 mSecInfo = 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
947 " param=%p]\n",
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;
961 uint64_t progress;
963 MutexAutoLock lock(mLock);
964 sink = mEventSink;
965 switch (status) {
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.
974 if (progress == 0) {
975 return;
977 break;
978 case NS_NET_STATUS_RECEIVING_FROM:
979 progress = mInput.ByteCount();
980 break;
981 default:
982 progress = 0;
983 break;
986 if (sink) {
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
995 ? " bypass cache"
996 : ""));
998 nsresult rv;
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");
1005 #endif
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;
1033 mResolving = true;
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))) {
1063 bool isSSL = false;
1064 for (unsigned int i = 0; i < mTypeCount; ++i) {
1065 if (!strcmp(mTypes[i], "ssl")) {
1066 isSSL = true;
1067 break;
1070 if (isSSL) {
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;
1084 rv = NS_OK;
1085 } else {
1086 mEsniQueried = true;
1091 if (NS_SUCCEEDED(rv)) {
1092 SOCKET_LOG((" advancing to STATE_RESOLVING\n"));
1093 mState = STATE_RESOLVING;
1095 return rv;
1098 nsresult nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent,
1099 bool &usingSSL) {
1100 SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
1102 nsresult rv;
1104 proxyTransparent = false;
1105 usingSSL = false;
1107 if (mTypeCount == 0) {
1108 fd = PR_OpenTCPSocket(mNetAddr.raw.family);
1109 rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1110 } else {
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");
1114 #endif
1116 fd = nullptr;
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;
1129 uint32_t i;
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;
1154 if (i == 0) {
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;
1191 } else {
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
1204 // info
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);
1211 mSecInfo = secinfo;
1212 callbacks = mCallbacks;
1213 SOCKET_LOG((" [secinfo=%p callbacks=%p]\n", mSecInfo.get(),
1214 mCallbacks.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.
1220 usingSSL = isSSL;
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)));
1233 if (fd) {
1234 CloseSocket(
1235 fd, mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
1240 return rv;
1243 nsresult nsSocketTransport::InitiateSocket() {
1244 SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
1246 nsresult rv;
1247 bool isLocal;
1248 IsLocal(&isLocal);
1250 if (gIOService->IsNetTearingDown()) {
1251 return NS_ERROR_ABORT;
1253 if (gIOService->IsOffline()) {
1254 if (!isLocal) return NS_ERROR_OFFLINE;
1255 } else if (!isLocal) {
1256 #ifdef DEBUG
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());
1262 #endif
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);
1269 fprintf_stderr(
1270 stderr,
1271 "FATAL ERROR: Non-local network connections are disabled and a "
1272 "connection "
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 "
1276 "tests). "
1277 "Browser services should be disabled or redirected to a local "
1278 "server.\n",
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(),
1292 kIPv6CStrBufSize))
1293 netAddrCString = NS_LITERAL_CSTRING("<IP-to-string failed>");
1294 SOCKET_LOG(
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);
1303 return mCondition;
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;
1331 return rv;
1335 // create new socket fd, push io layers, etc.
1337 PRFileDesc *fd;
1338 bool proxyTransparent;
1339 bool usingSSL;
1341 rv = BuildSocket(fd, proxyTransparent, usingSSL);
1342 if (NS_FAILED(rv)) {
1343 SOCKET_LOG(
1344 (" BuildSocket failed [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv)));
1345 return rv;
1348 // create proxy via IOActivityMonitor
1349 IOActivityMonitor::MonitorSocket(fd);
1351 PRStatus status;
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);
1402 if (mQoSBits) {
1403 opt.option = PR_SockOpt_IpTypeOfService;
1404 opt.value.tos = mQoSBits;
1405 PR_SetSocketOption(fd, &opt);
1408 #if defined(XP_WIN)
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);
1420 #endif
1422 // inform socket transport about this newly created socket...
1423 rv = mSocketTransportService->AttachSocket(fd, this);
1424 if (NS_FAILED(rv)) {
1425 CloseSocket(fd,
1426 mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
1427 return rv;
1429 mAttached = true;
1431 // assign mFD so that we can properly handle OnSocketDetached before we've
1432 // established a connection.
1434 MutexAutoLock lock(mLock);
1435 mFD = fd;
1436 mFDref = 1;
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...
1454 PRNetAddr prAddr;
1456 if (mBindAddr) {
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);
1469 #ifdef XP_WIN
1470 // Find the real tcp socket and set non-blocking once again!
1471 // Bug 1158189.
1472 PRFileDesc *bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
1473 if (bottom) {
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;
1481 #endif
1483 if (!mDNSRecordTxt.IsEmpty() && mSecInfo) {
1484 nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
1485 if (secCtrl) {
1486 SOCKET_LOG(("nsSocketTransport::InitiateSocket set esni keys."));
1487 rv = secCtrl->SetEsniTxt(mDNSRecordTxt);
1488 if (NS_FAILED(rv)) {
1489 return rv;
1491 mEsniUsed = true;
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();
1503 bool tfo = false;
1504 if (!mProxyTransparent && mFastOpenCallback &&
1505 mFastOpenCallback->FastOpenEnabled()) {
1506 if (NS_SUCCEEDED(AttachTCPFastOpenIOLayer(fd))) {
1507 tfo = true;
1508 SOCKET_LOG(
1509 ("nsSocketTransport::InitiateSocket TCP Fast Open "
1510 "started [this=%p]\n",
1511 this));
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)) {
1530 mCondition = rv;
1532 mFastOpenCallback = nullptr;
1533 MutexAutoLock lock(mLock);
1534 mFDFastOpenInProgress = false;
1535 return rv;
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
1550 // event in order.
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);
1558 SOCKET_LOG(
1559 ("called StartFastOpen - code=%d; fastOpen is %s "
1560 "supported.\n",
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
1567 // on Linux.
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;
1579 } else {
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();
1598 } else {
1599 #if defined(TEST_CONNECT_ERRORS)
1600 code = RandomizeConnectError(code);
1601 #endif
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);
1622 if (secCtrl) {
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
1636 // the OS error
1638 else if (PR_UNKNOWN_ERROR == code && mProxyTransparent &&
1639 !mProxyHost.IsEmpty()) {
1640 code = PR_GetOSError();
1641 rv = ErrorAccordingToNSPR(code);
1644 // The connection was refused...
1646 else {
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;
1662 return rv;
1665 bool nsSocketTransport::RecoverFromError() {
1666 NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
1668 SOCKET_LOG(
1669 ("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%" PRIx32
1670 "]\n",
1671 this, mState, static_cast<uint32_t>(mCondition)));
1673 if (mDoNotRetryToConnect) {
1674 SOCKET_LOG(
1675 ("nsSocketTransport::RecoverFromError do not retry because "
1676 "mDoNotRetryToConnect is set [this=%p]\n",
1677 this));
1678 return false;
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;
1685 #endif
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"));
1690 return false;
1693 nsresult rv;
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)));
1719 return false;
1721 #else
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)));
1729 return false;
1731 #endif
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
1740 // work around.
1741 (mCondition == NS_ERROR_FAILURE) ||
1742 #endif
1743 (mCondition == NS_ERROR_PROXY_CONNECTION_REFUSED))) {
1744 // TCP Fast Open can be blocked by middle boxes so we will retry
1745 // without it.
1746 tryAgain = true;
1747 // If we cancel the connection because backup socket was successfully
1748 // connected, mFDFastOpenInProgress will be true but mFastOpenCallback
1749 // will be nullptr.
1750 if (mFastOpenCallback) {
1751 mFastOpenCallback->SetFastOpenConnected(mCondition, true);
1753 mFastOpenCallback = nullptr;
1755 } else {
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
1781 // connection entry
1782 mResetFamilyPreference = true;
1783 tryAgain = 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"));
1791 tryAgain = true;
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
1800 // connection entry
1801 mResetFamilyPreference = true;
1802 tryAgain = true;
1803 } else if (!(mConnectionFlags & DISABLE_TRR)) {
1804 bool trrEnabled;
1805 mDNSRecord->IsTRR(&trrEnabled);
1806 if (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;
1813 tryAgain = true;
1819 // prepare to try again.
1820 if (tryAgain) {
1821 uint32_t msg;
1823 if (mState == STATE_CONNECTING) {
1824 mState = STATE_RESOLVING;
1825 msg = MSG_DNS_LOOKUP_COMPLETE;
1826 } else {
1827 mState = STATE_CLOSED;
1828 msg = MSG_ENSURE_CONNECT;
1831 rv = PostEvent(msg, NS_OK);
1832 if (NS_FAILED(rv)) tryAgain = false;
1835 return tryAgain;
1838 // called on the socket thread only
1839 void nsSocketTransport::OnMsgInputClosed(nsresult reason) {
1840 SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%" PRIx32
1841 "]\n",
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)
1851 mCondition =
1852 NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
1853 else {
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
1862 "]\n",
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)
1872 mCondition =
1873 NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
1874 else {
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
1895 // abandoned.
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");
1906 SetSocketName(mFD);
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) {
1926 return;
1929 PRNetAddr prAddr;
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++;
1945 return mFD;
1948 PRFileDesc *nsSocketTransport::GetFD_LockedAlsoDuringFastOpen() {
1949 mLock.AssertCurrentThreadOwns();
1951 // mFD is not available to the streams while disconnected.
1952 if (!mFDconnected && !mFDFastOpenInProgress) {
1953 return nullptr;
1956 if (mFD.IsInitialized()) {
1957 mFDref++;
1960 return mFD;
1963 bool nsSocketTransport::FastOpenInProgress() {
1964 mLock.AssertCurrentThreadOwns();
1965 return mFDFastOpenInProgress;
1968 class ThunkPRClose : public Runnable {
1969 public:
1970 explicit ThunkPRClose(PRFileDesc *fd)
1971 : Runnable("net::ThunkPRClose"), mFD(fd) {}
1973 NS_IMETHOD Run() override {
1974 nsSocketTransport::CloseSocket(
1975 mFD, gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
1976 return NS_OK;
1979 private:
1980 PRFileDesc *mFD;
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);
1988 } else {
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"));
2005 } else {
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));
2015 CloseSocket(
2016 mFD, mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
2017 } else {
2018 // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
2019 STS_PRCloseOnSocketTransport(mFD, mLingerPolarity, mLingerTimeout);
2022 mFD = nullptr;
2026 //-----------------------------------------------------------------------------
2027 // socket event handler impl
2029 void nsSocketTransport::OnSocketEvent(uint32_t type, nsresult status,
2030 nsISupports *param) {
2031 SOCKET_LOG(
2032 ("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%" PRIx32
2033 " param=%p]\n",
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);
2045 return;
2048 switch (type) {
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
2053 // connection.
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();
2062 else
2063 #endif
2064 mCondition = ResolveHost();
2066 } else {
2067 SOCKET_LOG((" ignoring redundant event\n"));
2069 break;
2071 case MSG_DNS_LOOKUP_COMPLETE:
2072 if (mDNSRequest ||
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;
2080 if (mDNSRecord) {
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;
2093 else
2094 mCondition = status;
2095 } else if (mState == STATE_RESOLVING) {
2096 mCondition = InitiateSocket();
2098 break;
2100 case MSG_RETRY_INIT_SOCKET:
2101 mCondition = InitiateSocket();
2102 break;
2104 case MSG_INPUT_CLOSED:
2105 SOCKET_LOG((" MSG_INPUT_CLOSED\n"));
2106 OnMsgInputClosed(status);
2107 break;
2109 case MSG_INPUT_PENDING:
2110 SOCKET_LOG((" MSG_INPUT_PENDING\n"));
2111 OnMsgInputPending();
2112 break;
2114 case MSG_OUTPUT_CLOSED:
2115 SOCKET_LOG((" MSG_OUTPUT_CLOSED\n"));
2116 OnMsgOutputClosed(status);
2117 break;
2119 case MSG_OUTPUT_PENDING:
2120 SOCKET_LOG((" MSG_OUTPUT_PENDING\n"));
2121 OnMsgOutputPending();
2122 break;
2123 case MSG_TIMEOUT_CHANGED:
2124 SOCKET_LOG((" MSG_TIMEOUT_CHANGED\n"));
2125 mPollTimeout =
2126 mTimeouts[(mState == STATE_TRANSFERRING) ? TIMEOUT_READ_WRITE
2127 : TIMEOUT_CONNECT];
2128 break;
2129 default:
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",
2147 this, outFlags));
2149 if (outFlags == -1) {
2150 SOCKET_LOG(("socket timeout expired\n"));
2151 mCondition = NS_ERROR_NET_TIMEOUT;
2152 return;
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) {
2161 return;
2163 SendStatus(NS_NET_STATUS_SENDING_TO);
2165 // If we are done sending the buffered data continue with the normal
2166 // path.
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
2191 // shutting down.
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
2206 # endif
2208 if (mFDFastOpenInProgress && mFastOpenCallback &&
2209 (mFastOpenStatus == TFO_DATA_SENT)) {
2210 PROsfd osfd = PR_FileDesc2NativeHandle(fd);
2211 BOOL option = 0;
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);
2220 #endif
2222 if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
2223 connectStarted) {
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);
2249 } else {
2250 PRErrorCode code = PR_GetError();
2251 #if defined(TEST_CONNECT_ERRORS)
2252 code = RandomizeConnectError(code);
2253 #endif
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);
2270 } else {
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
2284 // shutting down.
2285 SOCKET_LOG(
2286 ("We are in shutdown so skip PR_ConnectContinue and set "
2287 "and error.\n"));
2288 mCondition = NS_ERROR_ABORT;
2289 } else {
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
2300 "]\n",
2301 this, static_cast<uint32_t>(mCondition)));
2303 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2305 mAttached = false;
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;
2312 } else {
2313 mCondition = NS_ERROR_ABORT;
2317 mFastOpenLayerHasBufferedData = false;
2319 // If we are not shutting down try again.
2320 if (!gIOService->IsNetTearingDown() && RecoverFromError())
2321 mCondition = NS_OK;
2322 else {
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
2330 // will be nullptr.
2331 if (mFDFastOpenInProgress && mFastOpenCallback) {
2332 mFastOpenCallback->SetFastOpenConnected(mCondition, false);
2334 mFastOpenCallback = nullptr;
2336 // make sure there isn't any pending DNS request
2337 if (mDNSRequest) {
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
2358 // above.
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) {
2411 *aIsLocal = true;
2412 return;
2414 #endif
2416 *aIsLocal = IsLoopBackAddress(&mNetAddr);
2420 //-----------------------------------------------------------------------------
2421 // xpcom api
2423 NS_IMPL_ISUPPORTS(nsSocketTransport, nsISocketTransport, nsITransport,
2424 nsIDNSListener, nsIClassInfo, nsIInterfaceRequestor)
2425 NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport, nsISocketTransport, nsITransport,
2426 nsIDNSListener, nsIInterfaceRequestor)
2428 NS_IMETHODIMP
2429 nsSocketTransport::OpenInputStream(uint32_t flags, uint32_t segsize,
2430 uint32_t segcount, nsIInputStream **result) {
2431 SOCKET_LOG(
2432 ("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n", this, flags));
2434 NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
2436 nsresult rv;
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);
2446 // create a pipe
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;
2457 *result = pipeIn;
2458 } else
2459 *result = &mInput;
2461 // flag input stream as open
2462 mInputClosed = false;
2464 rv = PostEvent(MSG_ENSURE_CONNECT);
2465 if (NS_FAILED(rv)) return rv;
2467 NS_ADDREF(*result);
2468 return NS_OK;
2471 NS_IMETHODIMP
2472 nsSocketTransport::OpenOutputStream(uint32_t flags, uint32_t segsize,
2473 uint32_t segcount,
2474 nsIOutputStream **result) {
2475 SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n", this,
2476 flags));
2478 NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
2480 nsresult rv;
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);
2489 // create a pipe
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;
2500 *result = pipeOut;
2501 } else
2502 *result = &mOutput;
2504 // flag output stream as open
2505 mOutputClosed = false;
2507 rv = PostEvent(MSG_ENSURE_CONNECT);
2508 if (NS_FAILED(rv)) return rv;
2510 NS_ADDREF(*result);
2511 return NS_OK;
2514 NS_IMETHODIMP
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);
2530 return NS_OK;
2533 NS_IMETHODIMP
2534 nsSocketTransport::GetSecurityInfo(nsISupports **secinfo) {
2535 MutexAutoLock lock(mLock);
2536 NS_IF_ADDREF(*secinfo = mSecInfo);
2537 return NS_OK;
2540 NS_IMETHODIMP
2541 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks) {
2542 MutexAutoLock lock(mLock);
2543 NS_IF_ADDREF(*callbacks = mCallbacks);
2544 return NS_OK;
2547 NS_IMETHODIMP
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(),
2559 mCallbacks.get()));
2561 secinfo = mSecInfo;
2564 // don't call into PSM while holding mLock!!
2565 nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
2566 if (secCtrl) secCtrl->SetNotificationCallbacks(threadsafeCallbacks);
2568 return NS_OK;
2571 NS_IMETHODIMP
2572 nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
2573 nsIEventTarget *target) {
2574 nsCOMPtr<nsITransportEventSink> temp;
2575 if (target) {
2576 nsresult rv =
2577 net_NewTransportEventSinkProxy(getter_AddRefs(temp), sink, target);
2578 if (NS_FAILED(rv)) return rv;
2579 sink = temp.get();
2582 MutexAutoLock lock(mLock);
2583 mEventSink = sink;
2584 return NS_OK;
2587 NS_IMETHODIMP
2588 nsSocketTransport::IsAlive(bool *result) {
2589 *result = false;
2591 // During Fast Open we need to return true here.
2592 if (mFDFastOpenInProgress) {
2593 *result = true;
2594 return NS_OK;
2597 nsresult conditionWhileLocked = NS_OK;
2598 PRFileDescAutoLock fd(this, false, &conditionWhileLocked);
2599 if (NS_FAILED(conditionWhileLocked) || !fd.IsInitialized()) {
2600 return NS_OK;
2603 // XXX do some idle-time based checks??
2605 char c;
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))
2609 *result = true;
2611 return NS_OK;
2614 NS_IMETHODIMP
2615 nsSocketTransport::GetHost(nsACString &host) {
2616 host = SocketHost();
2617 return NS_OK;
2620 NS_IMETHODIMP
2621 nsSocketTransport::GetPort(int32_t *port) {
2622 *port = (int32_t)SocketPort();
2623 return NS_OK;
2626 NS_IMETHODIMP
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;
2632 return NS_OK;
2635 NS_IMETHODIMP
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;
2647 return NS_OK;
2650 nsresult nsSocketTransport::GetOriginAttributes(
2651 OriginAttributes *aOriginAttributes) {
2652 NS_ENSURE_ARG(aOriginAttributes);
2653 *aOriginAttributes = mOriginAttributes;
2654 return NS_OK;
2657 nsresult nsSocketTransport::SetOriginAttributes(
2658 const OriginAttributes &aOriginAttributes) {
2659 MutexAutoLock lock(mLock);
2660 NS_ENSURE_FALSE(mFD.IsInitialized(), NS_ERROR_FAILURE);
2662 mOriginAttributes = aOriginAttributes;
2663 return NS_OK;
2666 NS_IMETHODIMP
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) {
2674 SOCKET_LOG(
2675 ("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
2676 "NOT_AVAILABLE because not yet connected.",
2677 this, mState));
2678 return NS_ERROR_NOT_AVAILABLE;
2681 memcpy(addr, &mNetAddr, sizeof(NetAddr));
2682 return NS_OK;
2685 NS_IMETHODIMP
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) {
2693 SOCKET_LOG(
2694 ("nsSocketTransport::GetSelfAddr [this=%p state=%d] "
2695 "NOT_AVAILABLE because not yet connected.",
2696 this, mState));
2697 return NS_ERROR_NOT_AVAILABLE;
2700 memcpy(addr, &mSelfAddr, sizeof(NetAddr));
2701 return NS_OK;
2704 NS_IMETHODIMP
2705 nsSocketTransport::Bind(NetAddr *aLocalAddr) {
2706 NS_ENSURE_ARG(aLocalAddr);
2708 MutexAutoLock lock(mLock);
2709 if (mAttached) {
2710 return NS_ERROR_FAILURE;
2713 mBindAddr = new NetAddr();
2714 memcpy(mBindAddr.get(), aLocalAddr, sizeof(NetAddr));
2716 return NS_OK;
2719 NS_IMETHODIMP
2720 nsSocketTransport::GetScriptablePeerAddr(nsINetAddr **addr) {
2721 NetAddr rawAddr;
2723 nsresult rv;
2724 rv = GetPeerAddr(&rawAddr);
2725 if (NS_FAILED(rv)) return rv;
2727 NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
2729 return NS_OK;
2732 NS_IMETHODIMP
2733 nsSocketTransport::GetScriptableSelfAddr(nsINetAddr **addr) {
2734 NetAddr rawAddr;
2736 nsresult rv;
2737 rv = GetSelfAddr(&rawAddr);
2738 if (NS_FAILED(rv)) return rv;
2740 NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
2742 return NS_OK;
2745 NS_IMETHODIMP
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];
2749 return NS_OK;
2752 NS_IMETHODIMP
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,
2757 value));
2759 // truncate overly large timeout values.
2760 mTimeouts[type] = (uint16_t)std::min<uint32_t>(value, UINT16_MAX);
2761 PostEvent(MSG_TIMEOUT_CHANGED);
2762 return NS_OK;
2765 NS_IMETHODIMP
2766 nsSocketTransport::SetReuseAddrPort(bool reuseAddrPort) {
2767 mReuseAddrPort = reuseAddrPort;
2768 return NS_OK;
2771 NS_IMETHODIMP
2772 nsSocketTransport::SetLinger(bool aPolarity, int16_t aTimeout) {
2773 MutexAutoLock lock(mLock);
2775 mLingerPolarity = aPolarity;
2776 mLingerTimeout = aTimeout;
2778 return NS_OK;
2781 NS_IMETHODIMP
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;
2790 return NS_OK;
2793 NS_IMETHODIMP
2794 nsSocketTransport::GetQoSBits(uint8_t *aQoSBits) {
2795 *aQoSBits = mQoSBits;
2796 return NS_OK;
2799 NS_IMETHODIMP
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;
2809 else
2810 rv = NS_ERROR_FAILURE;
2812 return rv;
2815 NS_IMETHODIMP
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;
2825 else
2826 rv = NS_ERROR_FAILURE;
2828 return rv;
2831 NS_IMETHODIMP
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;
2842 return rv;
2845 NS_IMETHODIMP
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;
2856 return rv;
2859 NS_IMETHODIMP
2860 nsSocketTransport::OnLookupComplete(nsICancelable *request, nsIDNSRecord *rec,
2861 nsresult status) {
2862 SOCKET_LOG(("nsSocketTransport::OnLookupComplete: this=%p status %" PRIx32
2863 ".",
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) {
2873 if (mEsniQueried) {
2874 Telemetry::Accumulate(Telemetry::ESNI_KEYS_RECORD_FETCH_DELAYS, 0);
2876 mResolving = false;
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
2882 // DNS service.
2883 if (NS_FAILED(rv)) {
2884 NS_WARNING("unable to post DNS lookup complete message");
2886 } else {
2887 mDNSLookupStatus =
2888 status; // remember the status to send it when esni lookup is ready.
2889 mDNSRequest = nullptr;
2890 mDNSARequestFinished = PR_IntervalNow();
2893 return NS_OK;
2896 NS_IMETHODIMP
2897 nsSocketTransport::OnLookupByTypeComplete(nsICancelable *request,
2898 nsIDNSByTypeRecord *txtResponse,
2899 nsresult status) {
2900 SOCKET_LOG(
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.
2913 if (!mDNSRequest) {
2914 mResolving = false;
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
2925 // DNS service.
2926 if (NS_FAILED(rv)) {
2927 NS_WARNING("unable to post DNS lookup complete message");
2929 } else {
2930 mDNSTxtRequest = nullptr;
2933 return NS_OK;
2936 // nsIInterfaceRequestor
2937 NS_IMETHODIMP
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);
2946 NS_IMETHODIMP
2947 nsSocketTransport::GetInterfaces(uint32_t *count, nsIID ***array) {
2948 return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count, array);
2951 NS_IMETHODIMP
2952 nsSocketTransport::GetScriptableHelper(nsIXPCScriptable **_retval) {
2953 *_retval = nullptr;
2954 return NS_OK;
2957 NS_IMETHODIMP
2958 nsSocketTransport::GetContractID(nsACString &aContractID) {
2959 aContractID.SetIsVoid(true);
2960 return NS_OK;
2963 NS_IMETHODIMP
2964 nsSocketTransport::GetClassDescription(nsACString &aClassDescription) {
2965 aClassDescription.SetIsVoid(true);
2966 return NS_OK;
2969 NS_IMETHODIMP
2970 nsSocketTransport::GetClassID(nsCID **aClassID) {
2971 *aClassID = nullptr;
2972 return NS_OK;
2975 NS_IMETHODIMP
2976 nsSocketTransport::GetFlags(uint32_t *aFlags) {
2977 *aFlags = nsIClassInfo::THREADSAFE;
2978 return NS_OK;
2981 NS_IMETHODIMP
2982 nsSocketTransport::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) {
2983 return NS_ERROR_NOT_AVAILABLE;
2986 NS_IMETHODIMP
2987 nsSocketTransport::GetConnectionFlags(uint32_t *value) {
2988 *value = mConnectionFlags;
2989 return NS_OK;
2992 NS_IMETHODIMP
2993 nsSocketTransport::SetConnectionFlags(uint32_t value) {
2994 SOCKET_LOG(
2995 ("nsSocketTransport::SetConnectionFlags %p flags=%u", this, value));
2997 mConnectionFlags = value;
2998 mIsPrivate = value & nsISocketTransport::NO_PERMANENT_STORAGE;
3000 return NS_OK;
3003 NS_IMETHODIMP
3004 nsSocketTransport::GetTlsFlags(uint32_t *value) {
3005 *value = mTlsFlags;
3006 return NS_OK;
3009 NS_IMETHODIMP
3010 nsSocketTransport::SetTlsFlags(uint32_t value) {
3011 mTlsFlags = value;
3012 return NS_OK;
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();
3045 nsresult rv =
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)));
3051 return 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)));
3057 return rv;
3059 return NS_OK;
3062 NS_IMETHODIMP
3063 nsSocketTransport::GetKeepaliveEnabled(bool *aResult) {
3064 MOZ_ASSERT(aResult);
3066 *aResult = mKeepaliveEnabled;
3067 return NS_OK;
3070 nsresult nsSocketTransport::EnsureKeepaliveValsAreInitialized() {
3071 nsresult rv = NS_OK;
3072 int32_t val = -1;
3073 if (mKeepaliveIdleTimeS == -1) {
3074 rv = mSocketTransportService->GetKeepaliveIdleTime(&val);
3075 if (NS_WARN_IF(NS_FAILED(rv))) {
3076 return rv;
3078 mKeepaliveIdleTimeS = val;
3080 if (mKeepaliveRetryIntervalS == -1) {
3081 rv = mSocketTransportService->GetKeepaliveRetryInterval(&val);
3082 if (NS_WARN_IF(NS_FAILED(rv))) {
3083 return rv;
3085 mKeepaliveRetryIntervalS = val;
3087 if (mKeepaliveProbeCount == -1) {
3088 rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
3089 if (NS_WARN_IF(NS_FAILED(rv))) {
3090 return rv;
3092 mKeepaliveProbeCount = val;
3094 return NS_OK;
3097 NS_IMETHODIMP
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"));
3105 return NS_OK;
3108 nsresult rv = NS_OK;
3109 if (aEnable) {
3110 rv = EnsureKeepaliveValsAreInitialized();
3111 if (NS_WARN_IF(NS_FAILED(rv))) {
3112 SOCKET_LOG(
3113 (" SetKeepaliveEnabled [%p] "
3114 "error [0x%" PRIx32 "] initializing keepalive vals",
3115 this, static_cast<uint32_t>(rv)));
3116 return rv;
3119 SOCKET_LOG(
3120 ("nsSocketTransport::SetKeepaliveEnabled [%p] "
3121 "%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
3122 "globally %s.",
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)));
3136 return rv;
3139 return NS_OK;
3140 #else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
3141 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
3142 return NS_ERROR_NOT_IMPLEMENTED;
3143 #endif
3146 NS_IMETHODIMP
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) {
3159 SOCKET_LOG(
3160 ("nsSocketTransport::SetKeepaliveVals [%p] idle time "
3161 "already %ds and retry interval already %ds.",
3162 this, mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS));
3163 return NS_OK;
3165 mKeepaliveIdleTimeS = aIdleTime;
3166 mKeepaliveRetryIntervalS = aRetryInterval;
3168 nsresult rv = NS_OK;
3169 if (mKeepaliveProbeCount == -1) {
3170 int32_t val = -1;
3171 nsresult rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
3172 if (NS_WARN_IF(NS_FAILED(rv))) {
3173 return rv;
3175 mKeepaliveProbeCount = val;
3178 SOCKET_LOG(
3179 ("nsSocketTransport::SetKeepaliveVals [%p] "
3180 "keepalive %s, idle time[%ds] retry interval[%ds] "
3181 "packet count[%d]",
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))) {
3193 return rv;
3195 return NS_OK;
3196 #else
3197 SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
3198 return NS_ERROR_NOT_IMPLEMENTED;
3199 #endif
3202 #ifdef ENABLE_SOCKET_TRACING
3204 # include <stdio.h>
3205 # include <ctype.h>
3206 # include "prenv.h"
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;
3215 while (n) {
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) {
3225 if (isprint(*p))
3226 fprintf(fp, "%c", *p);
3227 else
3228 fprintf(fp, ".");
3231 fprintf(fp, "\n");
3232 buf += row_max;
3233 n -= row_max;
3236 fprintf(fp, "\n");
3237 fclose(fp);
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);
3247 header.Append(':');
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);
3260 header.Append(':');
3261 header.AppendInt(mPort);
3263 DumpBytesToFile(val, header.get(), buf, n);
3266 #endif
3268 static void LogNSPRError(const char *aPrefix, const void *aObjPtr) {
3269 #if defined(DEBUG)
3270 PRErrorCode errCode = PR_GetError();
3271 int errLen = PR_GetErrorTextLength();
3272 nsAutoCString errStr;
3273 if (errLen > 0) {
3274 errStr.SetLength(errLen);
3275 PR_GetErrorText(errStr.BeginWriting());
3277 NS_WARNING(
3278 nsPrintfCString("%s [%p] NSPR error[0x%x] %s.",
3279 aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
3280 errLen > 0 ? errStr.BeginReading() : "<no error text>")
3281 .get());
3282 #endif
3285 nsresult nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(
3286 bool aEnable) {
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",
3301 mSocketTransport);
3302 return ErrorAccordingToNSPR(PR_GetError());
3304 return NS_OK;
3307 static void LogOSError(const char *aPrefix, const void *aObjPtr) {
3308 #if defined(DEBUG)
3309 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3311 # ifdef XP_WIN
3312 DWORD errCode = WSAGetLastError();
3313 LPVOID errMessage;
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);
3318 # else
3319 int errCode = errno;
3320 char *errMessage = strerror(errno);
3321 # endif
3322 NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%x] %s",
3323 aPrefix ? aPrefix : "nsSocketTransport", aObjPtr,
3324 errCode,
3325 errMessage ? errMessage : "<no error text>")
3326 .get());
3327 # ifdef XP_WIN
3328 LocalFree(errMessage);
3329 # endif
3330 #endif
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",
3355 mSocketTransport);
3356 return ErrorAccordingToNSPR(PR_GetError());
3358 #endif
3360 #if defined(XP_WIN)
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;
3367 int err =
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;
3374 return NS_OK;
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,
3379 sizeof(aIdleTime));
3380 if (NS_WARN_IF(err)) {
3381 LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
3382 mSocketTransport);
3383 return NS_ERROR_UNEXPECTED;
3385 return NS_OK;
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;
3393 // seconds.
3394 int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &aIdleTime,
3395 sizeof(aIdleTime));
3396 if (NS_WARN_IF(err)) {
3397 LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
3398 mSocketTransport);
3399 return NS_ERROR_UNEXPECTED;
3402 # endif
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",
3409 mSocketTransport);
3410 return NS_ERROR_UNEXPECTED;
3413 # endif
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",
3420 mSocketTransport);
3421 return NS_ERROR_UNEXPECTED;
3424 # endif
3425 return NS_OK;
3426 #else
3427 MOZ_ASSERT(false,
3428 "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
3429 "called on unsupported platform!");
3430 return NS_ERROR_UNEXPECTED;
3431 #endif
3434 void nsSocketTransport::CloseSocket(PRFileDesc *aFd, bool aTelemetryEnabled) {
3435 #if defined(XP_WIN)
3436 AttachShutdownLayer(aFd);
3437 #endif
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();
3447 PR_Close(aFd);
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()) <
3469 60) {
3470 Telemetry::Accumulate(aIDConnectivityChange,
3471 PR_IntervalToMilliseconds(now - aStart));
3472 } else if (PR_IntervalToSeconds(now - gIOService->LastNetworkLinkChange()) <
3473 60) {
3474 Telemetry::Accumulate(aIDLinkChange,
3475 PR_IntervalToMilliseconds(now - aStart));
3477 } else if (PR_IntervalToSeconds(now - gIOService->LastOfflineStateChange()) <
3478 60) {
3479 Telemetry::Accumulate(aIDOffline, PR_IntervalToMilliseconds(now - aStart));
3480 } else {
3481 Telemetry::Accumulate(aIDNormal, PR_IntervalToMilliseconds(now - aStart));
3485 NS_IMETHODIMP
3486 nsSocketTransport::SetFastOpenCallback(TCPFastOpen *aFastOpen) {
3487 mFastOpenCallback = aFastOpen;
3488 return NS_OK;
3491 NS_IMETHODIMP
3492 nsSocketTransport::GetFirstRetryError(nsresult *aFirstRetryError) {
3493 *aFirstRetryError = mFirstRetryError;
3494 return NS_OK;
3497 NS_IMETHODIMP
3498 nsSocketTransport::GetResetIPFamilyPreference(bool *aReset) {
3499 *aReset = mResetFamilyPreference;
3500 return NS_OK;
3503 NS_IMETHODIMP
3504 nsSocketTransport::GetEsniUsed(bool *aEsniUsed) {
3505 *aEsniUsed = mEsniUsed;
3506 return NS_OK;
3509 } // namespace net
3510 } // namespace mozilla