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