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