1 // vim:set sw=2 sts=2 et cin:
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsSocketTransportService2.h"
7
8 #include "GeckoProfiler.h"
9 #include "IOActivityMonitor.h"
10 #include "mozilla/Atomics.h"
11 #include "mozilla/ChaosMode.h"
12 #include "mozilla/IntegerPrintfMacros.h"
13 #include "mozilla/Likely.h"
14 #include "mozilla/PodOperations.h"
15 #include "mozilla/Preferences.h"
16 #include "mozilla/PublicSSL.h"
17 #include "mozilla/ReverseIterator.h"
18 #include "mozilla/Services.h"
19 #include "mozilla/Telemetry.h"
20 #include "nsASocketHandler.h"
21 #include "nsError.h"
22 #include "nsIFile.h"
23 #include "nsIOService.h"
24 #include "nsIObserverService.h"
25 #include "nsIWidget.h"
26 #include "nsServiceManagerUtils.h"
27 #include "nsSocketTransport2.h"
28 #include "nsThreadUtils.h"
29 #include "prerror.h"
30 #include "prnetdb.h"
31
32 namespace mozilla {
33 namespace net {
34
35 LazyLogModule gSocketTransportLog("nsSocketTransport");
36 LazyLogModule gUDPSocketLog("UDPSocket");
37 LazyLogModule gTCPSocketLog("TCPSocket");
38
39 nsSocketTransportService* gSocketTransportService = nullptr;
40 static Atomic<PRThread*, Relaxed> gSocketThread(nullptr);
41
42 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
43 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
44 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
45 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
46 #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
47 #define SOCKET_LIMIT_TARGET 1000U
48 #define MAX_TIME_BETWEEN_TWO_POLLS \
49 "network.sts.max_time_for_events_between_two_polls"
50 #define POLL_BUSY_WAIT_PERIOD "network.sts.poll_busy_wait_period"
51 #define POLL_BUSY_WAIT_PERIOD_TIMEOUT \
52 "network.sts.poll_busy_wait_period_timeout"
53 #define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN \
54 "network.sts.max_time_for_pr_close_during_shutdown"
55 #define POLLABLE_EVENT_TIMEOUT "network.sts.pollable_event_timeout"
56
57 #define REPAIR_POLLABLE_EVENT_TIME 10
58
59 uint32_t nsSocketTransportService::gMaxCount;
60 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
61
62 // Utility functions
OnSocketThread()63 bool OnSocketThread() { return PR_GetCurrentThread() == gSocketThread; }
64
65 //-----------------------------------------------------------------------------
66
IsTimedOut(PRIntervalTime now) const67 bool nsSocketTransportService::SocketContext::IsTimedOut(
68 PRIntervalTime now) const {
69 return TimeoutIn(now) == 0;
70 }
71
EnsureTimeout(PRIntervalTime now)72 void nsSocketTransportService::SocketContext::EnsureTimeout(
73 PRIntervalTime now) {
74 SOCKET_LOG(("SocketContext::EnsureTimeout socket=%p", mHandler));
75 if (!mPollStartEpoch) {
76 SOCKET_LOG((" engaging"));
77 mPollStartEpoch = now;
78 }
79 }
80
DisengageTimeout()81 void nsSocketTransportService::SocketContext::DisengageTimeout() {
82 SOCKET_LOG(("SocketContext::DisengageTimeout socket=%p", mHandler));
83 mPollStartEpoch = 0;
84 }
85
TimeoutIn(PRIntervalTime now) const86 PRIntervalTime nsSocketTransportService::SocketContext::TimeoutIn(
87 PRIntervalTime now) const {
88 SOCKET_LOG(("SocketContext::TimeoutIn socket=%p, timeout=%us", mHandler,
89 mHandler->mPollTimeout));
90
91 if (mHandler->mPollTimeout == UINT16_MAX || !mPollStartEpoch) {
92 SOCKET_LOG((" not engaged"));
93 return NS_SOCKET_POLL_TIMEOUT;
94 }
95
96 PRIntervalTime elapsed = (now - mPollStartEpoch);
97 PRIntervalTime timeout = PR_SecondsToInterval(mHandler->mPollTimeout);
98
99 if (elapsed >= timeout) {
100 SOCKET_LOG((" timed out!"));
101 return 0;
102 }
103 SOCKET_LOG((" remains %us", PR_IntervalToSeconds(timeout - elapsed)));
104 return timeout - elapsed;
105 }
106
MaybeResetEpoch()107 void nsSocketTransportService::SocketContext::MaybeResetEpoch() {
108 if (mPollStartEpoch && mHandler->mPollTimeout == UINT16_MAX) {
109 mPollStartEpoch = 0;
110 }
111 }
112
113 //-----------------------------------------------------------------------------
114 // ctor/dtor (called on the main/UI thread by the service manager)
115
nsSocketTransportService()116 nsSocketTransportService::nsSocketTransportService()
117 : mPollableEventTimeout(TimeDuration::FromSeconds(6)),
118 mMaxTimeForPrClosePref(PR_SecondsToInterval(5)),
119 mNetworkLinkChangeBusyWaitPeriod(PR_SecondsToInterval(50)),
120 mNetworkLinkChangeBusyWaitTimeout(PR_SecondsToInterval(7)) {
121 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
122
123 PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
124 mActiveList =
125 (SocketContext*)moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
126 mIdleList =
127 (SocketContext*)moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
128 mPollList =
129 (PRPollDesc*)moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1));
130
131 NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
132 gSocketTransportService = this;
133 }
134
ApplyPortRemap(uint16_t * aPort)135 void nsSocketTransportService::ApplyPortRemap(uint16_t* aPort) {
136 MOZ_ASSERT(IsOnCurrentThreadInfallible());
137
138 if (!mPortRemapping) {
139 return;
140 }
141
142 // Reverse the array to make later rules override earlier rules.
143 for (auto const& portMapping : Reversed(*mPortRemapping)) {
144 if (*aPort < Get<0>(portMapping)) {
145 continue;
146 }
147 if (*aPort > Get<1>(portMapping)) {
148 continue;
149 }
150
151 *aPort = Get<2>(portMapping);
152 return;
153 }
154 }
155
UpdatePortRemapPreference(nsACString const & aPortMappingPref)156 bool nsSocketTransportService::UpdatePortRemapPreference(
157 nsACString const& aPortMappingPref) {
158 TPortRemapping portRemapping;
159
160 auto consumePreference = [&]() -> bool {
161 Tokenizer tokenizer(aPortMappingPref);
162
163 tokenizer.SkipWhites();
164 if (tokenizer.CheckEOF()) {
165 return true;
166 }
167
168 nsTArray<Tuple<uint16_t, uint16_t>> ranges(2);
169 while (true) {
170 uint16_t loPort;
171 tokenizer.SkipWhites();
172 if (!tokenizer.ReadInteger(&loPort)) {
173 break;
174 }
175
176 uint16_t hiPort;
177 tokenizer.SkipWhites();
178 if (tokenizer.CheckChar('-')) {
179 tokenizer.SkipWhites();
180 if (!tokenizer.ReadInteger(&hiPort)) {
181 break;
182 }
183 } else {
184 hiPort = loPort;
185 }
186
187 ranges.AppendElement(MakeTuple(loPort, hiPort));
188
189 tokenizer.SkipWhites();
190 if (tokenizer.CheckChar(',')) {
191 continue; // another port or port range is expected
192 }
193
194 if (tokenizer.CheckChar('=')) {
195 uint16_t targetPort;
196 tokenizer.SkipWhites();
197 if (!tokenizer.ReadInteger(&targetPort)) {
198 break;
199 }
200
201 // Storing reversed, because the most common cases (like 443) will very
202 // likely be listed as first, less common cases will be added to the end
203 // of the list mapping to the same port. As we iterate the whole
204 // remapping array from the end, this may have a small perf win by
205 // hitting the most common cases earlier.
206 for (auto const& range : Reversed(ranges)) {
207 portRemapping.AppendElement(
208 MakeTuple(Get<0>(range), Get<1>(range), targetPort));
209 }
210 ranges.Clear();
211
212 tokenizer.SkipWhites();
213 if (tokenizer.CheckChar(';')) {
214 continue; // more mappings (or EOF) expected
215 }
216 if (tokenizer.CheckEOF()) {
217 return true;
218 }
219 }
220
221 // Anything else is unexpected.
222 break;
223 }
224
225 // 'break' from the parsing loop means ill-formed preference
226 portRemapping.Clear();
227 return false;
228 };
229
230 bool rv = consumePreference();
231
232 if (!IsOnCurrentThread()) {
233 nsCOMPtr<nsIThread> thread = GetThreadSafely();
234 if (!thread) {
235 // Init hasn't been called yet. Could probably just assert.
236 // If shutdown, the dispatch below will just silently fail.
237 NS_ASSERTION(false, "ApplyPortRemapPreference before STS::Init");
238 return false;
239 }
240 thread->Dispatch(NewRunnableMethod<TPortRemapping>(
241 "net::ApplyPortRemapping", this,
242 &nsSocketTransportService::ApplyPortRemapPreference, portRemapping));
243 } else {
244 ApplyPortRemapPreference(portRemapping);
245 }
246
247 return rv;
248 }
249
~nsSocketTransportService()250 nsSocketTransportService::~nsSocketTransportService() {
251 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
252 NS_ASSERTION(!mInitialized, "not shutdown properly");
253
254 free(mActiveList);
255 free(mIdleList);
256 free(mPollList);
257 gSocketTransportService = nullptr;
258 }
259
260 //-----------------------------------------------------------------------------
261 // event queue (any thread)
262
GetThreadSafely()263 already_AddRefed<nsIThread> nsSocketTransportService::GetThreadSafely() {
264 MutexAutoLock lock(mLock);
265 nsCOMPtr<nsIThread> result = mThread;
266 return result.forget();
267 }
268
269 NS_IMETHODIMP
DispatchFromScript(nsIRunnable * event,uint32_t flags)270 nsSocketTransportService::DispatchFromScript(nsIRunnable* event,
271 uint32_t flags) {
272 nsCOMPtr<nsIRunnable> event_ref(event);
273 return Dispatch(event_ref.forget(), flags);
274 }
275
276 NS_IMETHODIMP
Dispatch(already_AddRefed<nsIRunnable> event,uint32_t flags)277 nsSocketTransportService::Dispatch(already_AddRefed<nsIRunnable> event,
278 uint32_t flags) {
279 nsCOMPtr<nsIRunnable> event_ref(event);
280 SOCKET_LOG(("STS dispatch [%p]\n", event_ref.get()));
281
282 nsCOMPtr<nsIThread> thread = GetThreadSafely();
283 nsresult rv;
284 rv = thread ? thread->Dispatch(event_ref.forget(), flags)
285 : NS_ERROR_NOT_INITIALIZED;
286 if (rv == NS_ERROR_UNEXPECTED) {
287 // Thread is no longer accepting events. We must have just shut it
288 // down on the main thread. Pretend we never saw it.
289 rv = NS_ERROR_NOT_INITIALIZED;
290 }
291 return rv;
292 }
293
294 NS_IMETHODIMP
DelayedDispatch(already_AddRefed<nsIRunnable>,uint32_t)295 nsSocketTransportService::DelayedDispatch(already_AddRefed<nsIRunnable>,
296 uint32_t) {
297 return NS_ERROR_NOT_IMPLEMENTED;
298 }
299
300 NS_IMETHODIMP
IsOnCurrentThread(bool * result)301 nsSocketTransportService::IsOnCurrentThread(bool* result) {
302 *result = OnSocketThread();
303 return NS_OK;
304 }
305
NS_IMETHODIMP_(bool)306 NS_IMETHODIMP_(bool)
307 nsSocketTransportService::IsOnCurrentThreadInfallible() {
308 return OnSocketThread();
309 }
310
311 //-----------------------------------------------------------------------------
312 // nsIDirectTaskDispatcher
313
314 already_AddRefed<nsIDirectTaskDispatcher>
GetDirectTaskDispatcherSafely()315 nsSocketTransportService::GetDirectTaskDispatcherSafely() {
316 MutexAutoLock lock(mLock);
317 nsCOMPtr<nsIDirectTaskDispatcher> result = mDirectTaskDispatcher;
318 return result.forget();
319 }
320
321 NS_IMETHODIMP
DispatchDirectTask(already_AddRefed<nsIRunnable> aEvent)322 nsSocketTransportService::DispatchDirectTask(
323 already_AddRefed<nsIRunnable> aEvent) {
324 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
325 GetDirectTaskDispatcherSafely();
326 NS_ENSURE_TRUE(dispatcher, NS_ERROR_NOT_INITIALIZED);
327 return dispatcher->DispatchDirectTask(std::move(aEvent));
328 }
329
DrainDirectTasks()330 NS_IMETHODIMP nsSocketTransportService::DrainDirectTasks() {
331 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
332 GetDirectTaskDispatcherSafely();
333 if (!dispatcher) {
334 // nothing to drain.
335 return NS_OK;
336 }
337 return dispatcher->DrainDirectTasks();
338 }
339
HaveDirectTasks(bool * aValue)340 NS_IMETHODIMP nsSocketTransportService::HaveDirectTasks(bool* aValue) {
341 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
342 GetDirectTaskDispatcherSafely();
343 if (!dispatcher) {
344 *aValue = false;
345 return NS_OK;
346 }
347 return dispatcher->HaveDirectTasks(aValue);
348 }
349
350 //-----------------------------------------------------------------------------
351 // socket api (socket thread only)
352
353 NS_IMETHODIMP
NotifyWhenCanAttachSocket(nsIRunnable * event)354 nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable* event) {
355 SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
356
357 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
358
359 if (CanAttachSocket()) {
360 return Dispatch(event, NS_DISPATCH_NORMAL);
361 }
362
363 auto* runnable = new LinkedRunnableEvent(event);
364 mPendingSocketQueue.insertBack(runnable);
365 return NS_OK;
366 }
367
368 NS_IMETHODIMP
AttachSocket(PRFileDesc * fd,nsASocketHandler * handler)369 nsSocketTransportService::AttachSocket(PRFileDesc* fd,
370 nsASocketHandler* handler) {
371 SOCKET_LOG(
372 ("nsSocketTransportService::AttachSocket [handler=%p]\n", handler));
373
374 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
375
376 if (!CanAttachSocket()) {
377 return NS_ERROR_NOT_AVAILABLE;
378 }
379
380 SocketContext sock{};
381 sock.mFD = fd;
382 sock.mHandler = handler;
383 sock.mPollStartEpoch = 0;
384
385 nsresult rv = AddToIdleList(&sock);
386 if (NS_SUCCEEDED(rv)) NS_ADDREF(handler);
387 return rv;
388 }
389
390 // the number of sockets that can be attached at any given time is
391 // limited. this is done because some operating systems (e.g., Win9x)
392 // limit the number of sockets that can be created by an application.
393 // AttachSocket will fail if the limit is exceeded. consumers should
394 // call CanAttachSocket and check the result before creating a socket.
395
CanAttachSocket()396 bool nsSocketTransportService::CanAttachSocket() {
397 static bool reported900FDLimit = false;
398
399 uint32_t total = mActiveCount + mIdleCount;
400 bool rv = total < gMaxCount;
401
402 if (Telemetry::CanRecordPrereleaseData() &&
403 (((total >= 900) || !rv) && !reported900FDLimit)) {
404 reported900FDLimit = true;
405 Telemetry::Accumulate(Telemetry::NETWORK_SESSION_AT_900FD, true);
406 }
407
408 return rv;
409 }
410
DetachSocket(SocketContext * listHead,SocketContext * sock)411 nsresult nsSocketTransportService::DetachSocket(SocketContext* listHead,
412 SocketContext* sock) {
413 SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n",
414 sock->mHandler));
415 MOZ_ASSERT((listHead == mActiveList) || (listHead == mIdleList),
416 "DetachSocket invalid head");
417
418 {
419 // inform the handler that this socket is going away
420 sock->mHandler->OnSocketDetached(sock->mFD);
421 }
422 mSentBytesCount += sock->mHandler->ByteCountSent();
423 mReceivedBytesCount += sock->mHandler->ByteCountReceived();
424
425 // cleanup
426 sock->mFD = nullptr;
427 NS_RELEASE(sock->mHandler);
428
429 if (listHead == mActiveList) {
430 RemoveFromPollList(sock);
431 } else {
432 RemoveFromIdleList(sock);
433 }
434
435 // NOTE: sock is now an invalid pointer
436
437 //
438 // notify the first element on the pending socket queue...
439 //
440 nsCOMPtr<nsIRunnable> event;
441 LinkedRunnableEvent* runnable = mPendingSocketQueue.getFirst();
442 if (runnable) {
443 event = runnable->TakeEvent();
444 runnable->remove();
445 delete runnable;
446 }
447 if (event) {
448 // move event from pending queue to dispatch queue
449 return Dispatch(event, NS_DISPATCH_NORMAL);
450 }
451 return NS_OK;
452 }
453
AddToPollList(SocketContext * sock)454 nsresult nsSocketTransportService::AddToPollList(SocketContext* sock) {
455 MOZ_ASSERT(!(static_cast<uint32_t>(sock - mActiveList) < mActiveListSize),
456 "AddToPollList Socket Already Active");
457
458 SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%p]\n",
459 sock->mHandler));
460 if (mActiveCount == mActiveListSize) {
461 SOCKET_LOG((" Active List size of %d met\n", mActiveCount));
462 if (!GrowActiveList()) {
463 NS_ERROR("too many active sockets");
464 return NS_ERROR_OUT_OF_MEMORY;
465 }
466 }
467
468 uint32_t newSocketIndex = mActiveCount;
469 if (ChaosMode::isActive(ChaosFeature::NetworkScheduling)) {
470 newSocketIndex = ChaosMode::randomUint32LessThan(mActiveCount + 1);
471 PodMove(mActiveList + newSocketIndex + 1, mActiveList + newSocketIndex,
472 mActiveCount - newSocketIndex);
473 PodMove(mPollList + newSocketIndex + 2, mPollList + newSocketIndex + 1,
474 mActiveCount - newSocketIndex);
475 }
476
477 sock->EnsureTimeout(PR_IntervalNow());
478 mActiveList[newSocketIndex] = *sock;
479 mActiveCount++;
480
481 mPollList[newSocketIndex + 1].fd = sock->mFD;
482 mPollList[newSocketIndex + 1].in_flags = sock->mHandler->mPollFlags;
483 mPollList[newSocketIndex + 1].out_flags = 0;
484
485 SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
486 return NS_OK;
487 }
488
RemoveFromPollList(SocketContext * sock)489 void nsSocketTransportService::RemoveFromPollList(SocketContext* sock) {
490 SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%p]\n",
491 sock->mHandler));
492
493 uint32_t index = sock - mActiveList;
494 MOZ_ASSERT(index < mActiveListSize, "invalid index");
495
496 SOCKET_LOG((" index=%u mActiveCount=%u\n", index, mActiveCount));
497
498 if (index != mActiveCount - 1) {
499 mActiveList[index] = mActiveList[mActiveCount - 1];
500 mPollList[index + 1] = mPollList[mActiveCount];
501 }
502 mActiveCount--;
503
504 SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
505 }
506
AddToIdleList(SocketContext * sock)507 nsresult nsSocketTransportService::AddToIdleList(SocketContext* sock) {
508 MOZ_ASSERT(!(static_cast<uint32_t>(sock - mIdleList) < mIdleListSize),
509 "AddToIdlelList Socket Already Idle");
510
511 SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%p]\n",
512 sock->mHandler));
513 if (mIdleCount == mIdleListSize) {
514 SOCKET_LOG((" Idle List size of %d met\n", mIdleCount));
515 if (!GrowIdleList()) {
516 NS_ERROR("too many idle sockets");
517 return NS_ERROR_OUT_OF_MEMORY;
518 }
519 }
520
521 mIdleList[mIdleCount] = *sock;
522 mIdleCount++;
523
524 SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
525 return NS_OK;
526 }
527
RemoveFromIdleList(SocketContext * sock)528 void nsSocketTransportService::RemoveFromIdleList(SocketContext* sock) {
529 SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n",
530 sock->mHandler));
531
532 uint32_t index = sock - mIdleList;
533 NS_ASSERTION(index < mIdleListSize, "invalid index in idle list");
534
535 if (index != mIdleCount - 1) mIdleList[index] = mIdleList[mIdleCount - 1];
536 mIdleCount--;
537
538 SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
539 }
540
MoveToIdleList(SocketContext * sock)541 void nsSocketTransportService::MoveToIdleList(SocketContext* sock) {
542 nsresult rv = AddToIdleList(sock);
543 if (NS_FAILED(rv)) {
544 DetachSocket(mActiveList, sock);
545 } else {
546 RemoveFromPollList(sock);
547 }
548 }
549
MoveToPollList(SocketContext * sock)550 void nsSocketTransportService::MoveToPollList(SocketContext* sock) {
551 nsresult rv = AddToPollList(sock);
552 if (NS_FAILED(rv)) {
553 DetachSocket(mIdleList, sock);
554 } else {
555 RemoveFromIdleList(sock);
556 }
557 }
558
GrowActiveList()559 bool nsSocketTransportService::GrowActiveList() {
560 int32_t toAdd = gMaxCount - mActiveListSize;
561 if (toAdd > 100) {
562 toAdd = 100;
563 } else if (toAdd < 1) {
564 MOZ_ASSERT(false, "CanAttachSocket() should prevent this");
565 return false;
566 }
567
568 mActiveListSize += toAdd;
569 mActiveList = (SocketContext*)moz_xrealloc(
570 mActiveList, sizeof(SocketContext) * mActiveListSize);
571 mPollList = (PRPollDesc*)moz_xrealloc(
572 mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1));
573 return true;
574 }
575
GrowIdleList()576 bool nsSocketTransportService::GrowIdleList() {
577 int32_t toAdd = gMaxCount - mIdleListSize;
578 if (toAdd > 100) {
579 toAdd = 100;
580 } else if (toAdd < 1) {
581 MOZ_ASSERT(false, "CanAttachSocket() should prevent this");
582 return false;
583 }
584
585 mIdleListSize += toAdd;
586 mIdleList = (SocketContext*)moz_xrealloc(
587 mIdleList, sizeof(SocketContext) * mIdleListSize);
588 return true;
589 }
590
ApplyPortRemapPreference(TPortRemapping const & portRemapping)591 void nsSocketTransportService::ApplyPortRemapPreference(
592 TPortRemapping const& portRemapping) {
593 MOZ_ASSERT(IsOnCurrentThreadInfallible());
594
595 mPortRemapping.reset();
596 if (!portRemapping.IsEmpty()) {
597 mPortRemapping.emplace(portRemapping);
598 }
599 }
600
PollTimeout(PRIntervalTime now)601 PRIntervalTime nsSocketTransportService::PollTimeout(PRIntervalTime now) {
602 if (mActiveCount == 0) {
603 return NS_SOCKET_POLL_TIMEOUT;
604 }
605
606 // compute minimum time before any socket timeout expires.
607 PRIntervalTime minR = NS_SOCKET_POLL_TIMEOUT;
608 for (uint32_t i = 0; i < mActiveCount; ++i) {
609 const SocketContext& s = mActiveList[i];
610 PRIntervalTime r = s.TimeoutIn(now);
611 if (r < minR) {
612 minR = r;
613 }
614 }
615 if (minR == NS_SOCKET_POLL_TIMEOUT) {
616 SOCKET_LOG(("poll timeout: none\n"));
617 return NS_SOCKET_POLL_TIMEOUT;
618 }
619 SOCKET_LOG(("poll timeout: %" PRIu32 "\n", PR_IntervalToSeconds(minR)));
620 return minR;
621 }
622
Poll(TimeDuration * pollDuration,PRIntervalTime ts)623 int32_t nsSocketTransportService::Poll(TimeDuration* pollDuration,
624 PRIntervalTime ts) {
625 MOZ_ASSERT(IsOnCurrentThread());
626 PRPollDesc* pollList;
627 uint32_t pollCount;
628 PRIntervalTime pollTimeout;
629 *pollDuration = nullptr;
630
631 // If there are pending events for this thread then
632 // DoPollIteration() should service the network without blocking.
633 bool pendingEvents = false;
634 mRawThread->HasPendingEvents(&pendingEvents);
635
636 if (mPollList[0].fd) {
637 mPollList[0].out_flags = 0;
638 pollList = mPollList;
639 pollCount = mActiveCount + 1;
640 pollTimeout = pendingEvents ? PR_INTERVAL_NO_WAIT : PollTimeout(ts);
641 } else {
642 // no pollable event, so busy wait...
643 pollCount = mActiveCount;
644 if (pollCount) {
645 pollList = &mPollList[1];
646 } else {
647 pollList = nullptr;
648 }
649 pollTimeout =
650 pendingEvents ? PR_INTERVAL_NO_WAIT : PR_MillisecondsToInterval(25);
651 }
652
653 if ((ts - mLastNetworkLinkChangeTime) < mNetworkLinkChangeBusyWaitPeriod) {
654 // Being here means we are few seconds after a network change has
655 // been detected.
656 PRIntervalTime to = mNetworkLinkChangeBusyWaitTimeout;
657 if (to) {
658 pollTimeout = std::min(to, pollTimeout);
659 SOCKET_LOG((" timeout shorthened after network change event"));
660 }
661 }
662
663 TimeStamp pollStart;
664 if (Telemetry::CanRecordPrereleaseData()) {
665 pollStart = TimeStamp::NowLoRes();
666 }
667
668 SOCKET_LOG((" timeout = %i milliseconds\n",
669 PR_IntervalToMilliseconds(pollTimeout)));
670
671 int32_t rv = [&]() {
672 if (pollTimeout != PR_INTERVAL_NO_WAIT) {
673 // There will be an actual non-zero wait, let the profiler record
674 // idle time and mark thread as sleeping around the polling call.
675 AUTO_PROFILER_LABEL("nsSocketTransportService::Poll", IDLE);
676 AUTO_PROFILER_THREAD_SLEEP;
677 return PR_Poll(pollList, pollCount, pollTimeout);
678 }
679 return PR_Poll(pollList, pollCount, pollTimeout);
680 }();
681
682 if (Telemetry::CanRecordPrereleaseData() && !pollStart.IsNull()) {
683 *pollDuration = TimeStamp::NowLoRes() - pollStart;
684 }
685
686 SOCKET_LOG((" ...returned after %i milliseconds\n",
687 PR_IntervalToMilliseconds(PR_IntervalNow() - ts)));
688
689 return rv;
690 }
691
692 //-----------------------------------------------------------------------------
693 // xpcom api
694
695 NS_IMPL_ISUPPORTS(nsSocketTransportService, nsISocketTransportService,
696 nsIRoutedSocketTransportService, nsIEventTarget,
697 nsISerialEventTarget, nsIThreadObserver, nsIRunnable,
698 nsPISocketTransportService, nsIObserver,
699 nsIDirectTaskDispatcher)
700
701 static const char* gCallbackPrefs[] = {
702 SEND_BUFFER_PREF,
703 KEEPALIVE_ENABLED_PREF,
704 KEEPALIVE_IDLE_TIME_PREF,
705 KEEPALIVE_RETRY_INTERVAL_PREF,
706 KEEPALIVE_PROBE_COUNT_PREF,
707 MAX_TIME_BETWEEN_TWO_POLLS,
708 MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN,
709 POLLABLE_EVENT_TIMEOUT,
710 "network.socket.forcePort",
711 nullptr,
712 };
713
714 /* static */
UpdatePrefs(const char * aPref,void * aSelf)715 void nsSocketTransportService::UpdatePrefs(const char* aPref, void* aSelf) {
716 static_cast<nsSocketTransportService*>(aSelf)->UpdatePrefs();
717 }
718
719 // called from main thread only
720 NS_IMETHODIMP
Init()721 nsSocketTransportService::Init() {
722 if (!NS_IsMainThread()) {
723 NS_ERROR("wrong thread");
724 return NS_ERROR_UNEXPECTED;
725 }
726
727 if (mInitialized) {
728 return NS_OK;
729 }
730
731 if (mShuttingDown) {
732 return NS_ERROR_UNEXPECTED;
733 }
734
735 nsCOMPtr<nsIThread> thread;
736 nsresult rv =
737 NS_NewNamedThread("Socket Thread", getter_AddRefs(thread), this);
738 NS_ENSURE_SUCCESS(rv, rv);
739
740 {
741 MutexAutoLock lock(mLock);
742 // Install our mThread, protecting against concurrent readers
743 thread.swap(mThread);
744 mDirectTaskDispatcher = do_QueryInterface(mThread);
745 }
746
747 MOZ_DIAGNOSTIC_ASSERT(
748 mDirectTaskDispatcher,
749 "Underlying thread must support direct task dispatching");
750
751 Preferences::RegisterCallbacks(UpdatePrefs, gCallbackPrefs, this);
752 UpdatePrefs();
753
754 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
755 // Note that the observr notifications are forwarded from parent process to
756 // socket process. We have to make sure the topics registered below are also
757 // registered in nsIObserver::Init().
758 if (obsSvc) {
759 obsSvc->AddObserver(this, "profile-initial-state", false);
760 obsSvc->AddObserver(this, "last-pb-context-exited", false);
761 obsSvc->AddObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC, true);
762 obsSvc->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
763 obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
764 obsSvc->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
765 }
766
767 // We can now dispatch tasks to the socket thread.
768 mInitialized = true;
769 return NS_OK;
770 }
771
772 // called from main thread only
773 NS_IMETHODIMP
Shutdown(bool aXpcomShutdown)774 nsSocketTransportService::Shutdown(bool aXpcomShutdown) {
775 SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
776
777 NS_ENSURE_STATE(NS_IsMainThread());
778
779 if (!mInitialized) {
780 return NS_OK;
781 }
782
783 if (mShuttingDown) {
784 return NS_ERROR_UNEXPECTED;
785 }
786
787 {
788 auto observersCopy = mShutdownObservers;
789 for (auto& observer : observersCopy) {
790 observer->Observe();
791 }
792 }
793
794 // signal the socket thread to shutdown
795 mShuttingDown = true;
796
797 {
798 MutexAutoLock lock(mLock);
799
800 if (mPollableEvent) {
801 mPollableEvent->Signal();
802 }
803 }
804
805 if (!aXpcomShutdown) {
806 return ShutdownThread();
807 }
808
809 return NS_OK;
810 }
811
ShutdownThread()812 nsresult nsSocketTransportService::ShutdownThread() {
813 SOCKET_LOG(("nsSocketTransportService::ShutdownThread\n"));
814
815 NS_ENSURE_STATE(NS_IsMainThread());
816
817 if (!mInitialized || !mShuttingDown) {
818 return NS_OK;
819 }
820
821 // join with thread
822 mThread->Shutdown();
823 {
824 MutexAutoLock lock(mLock);
825 // Drop our reference to mThread and make sure that any concurrent readers
826 // are excluded
827 mThread = nullptr;
828 mDirectTaskDispatcher = nullptr;
829 }
830
831 Preferences::UnregisterCallbacks(UpdatePrefs, gCallbackPrefs, this);
832
833 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
834 if (obsSvc) {
835 obsSvc->RemoveObserver(this, "profile-initial-state");
836 obsSvc->RemoveObserver(this, "last-pb-context-exited");
837 obsSvc->RemoveObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC);
838 obsSvc->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
839 obsSvc->RemoveObserver(this, "xpcom-shutdown-threads");
840 obsSvc->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
841 }
842
843 if (mAfterWakeUpTimer) {
844 mAfterWakeUpTimer->Cancel();
845 mAfterWakeUpTimer = nullptr;
846 }
847
848 IOActivityMonitor::Shutdown();
849
850 mInitialized = false;
851 mShuttingDown = false;
852
853 return NS_OK;
854 }
855
856 NS_IMETHODIMP
GetOffline(bool * offline)857 nsSocketTransportService::GetOffline(bool* offline) {
858 *offline = mOffline;
859 return NS_OK;
860 }
861
862 NS_IMETHODIMP
SetOffline(bool offline)863 nsSocketTransportService::SetOffline(bool offline) {
864 MutexAutoLock lock(mLock);
865 if (!mOffline && offline) {
866 // signal the socket thread to go offline, so it will detach sockets
867 mGoingOffline = true;
868 mOffline = true;
869 } else if (mOffline && !offline) {
870 mOffline = false;
871 }
872 if (mPollableEvent) {
873 mPollableEvent->Signal();
874 }
875
876 return NS_OK;
877 }
878
879 NS_IMETHODIMP
GetKeepaliveIdleTime(int32_t * aKeepaliveIdleTimeS)880 nsSocketTransportService::GetKeepaliveIdleTime(int32_t* aKeepaliveIdleTimeS) {
881 MOZ_ASSERT(aKeepaliveIdleTimeS);
882 if (NS_WARN_IF(!aKeepaliveIdleTimeS)) {
883 return NS_ERROR_NULL_POINTER;
884 }
885 *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS;
886 return NS_OK;
887 }
888
889 NS_IMETHODIMP
GetKeepaliveRetryInterval(int32_t * aKeepaliveRetryIntervalS)890 nsSocketTransportService::GetKeepaliveRetryInterval(
891 int32_t* aKeepaliveRetryIntervalS) {
892 MOZ_ASSERT(aKeepaliveRetryIntervalS);
893 if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) {
894 return NS_ERROR_NULL_POINTER;
895 }
896 *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS;
897 return NS_OK;
898 }
899
900 NS_IMETHODIMP
GetKeepaliveProbeCount(int32_t * aKeepaliveProbeCount)901 nsSocketTransportService::GetKeepaliveProbeCount(
902 int32_t* aKeepaliveProbeCount) {
903 MOZ_ASSERT(aKeepaliveProbeCount);
904 if (NS_WARN_IF(!aKeepaliveProbeCount)) {
905 return NS_ERROR_NULL_POINTER;
906 }
907 *aKeepaliveProbeCount = mKeepaliveProbeCount;
908 return NS_OK;
909 }
910
911 NS_IMETHODIMP
CreateTransport(const nsTArray<nsCString> & types,const nsACString & host,int32_t port,nsIProxyInfo * proxyInfo,nsIDNSRecord * dnsRecord,nsISocketTransport ** result)912 nsSocketTransportService::CreateTransport(const nsTArray<nsCString>& types,
913 const nsACString& host, int32_t port,
914 nsIProxyInfo* proxyInfo,
915 nsIDNSRecord* dnsRecord,
916 nsISocketTransport** result) {
917 return CreateRoutedTransport(types, host, port, ""_ns, 0, proxyInfo,
918 dnsRecord, result);
919 }
920
921 NS_IMETHODIMP
CreateRoutedTransport(const nsTArray<nsCString> & types,const nsACString & host,int32_t port,const nsACString & hostRoute,int32_t portRoute,nsIProxyInfo * proxyInfo,nsIDNSRecord * dnsRecord,nsISocketTransport ** result)922 nsSocketTransportService::CreateRoutedTransport(
923 const nsTArray<nsCString>& types, const nsACString& host, int32_t port,
924 const nsACString& hostRoute, int32_t portRoute, nsIProxyInfo* proxyInfo,
925 nsIDNSRecord* dnsRecord, nsISocketTransport** result) {
926 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
927 NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
928
929 RefPtr<nsSocketTransport> trans = new nsSocketTransport();
930 nsresult rv = trans->Init(types, host, port, hostRoute, portRoute, proxyInfo,
931 dnsRecord);
932 if (NS_FAILED(rv)) {
933 return rv;
934 }
935
936 trans.forget(result);
937 return NS_OK;
938 }
939
940 NS_IMETHODIMP
CreateUnixDomainTransport(nsIFile * aPath,nsISocketTransport ** result)941 nsSocketTransportService::CreateUnixDomainTransport(
942 nsIFile* aPath, nsISocketTransport** result) {
943 #ifdef XP_UNIX
944 nsresult rv;
945
946 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
947
948 nsAutoCString path;
949 rv = aPath->GetNativePath(path);
950 NS_ENSURE_SUCCESS(rv, rv);
951
952 RefPtr<nsSocketTransport> trans = new nsSocketTransport();
953
954 rv = trans->InitWithFilename(path.get());
955 NS_ENSURE_SUCCESS(rv, rv);
956
957 trans.forget(result);
958 return NS_OK;
959 #else
960 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
961 #endif
962 }
963
964 NS_IMETHODIMP
CreateUnixDomainAbstractAddressTransport(const nsACString & aName,nsISocketTransport ** result)965 nsSocketTransportService::CreateUnixDomainAbstractAddressTransport(
966 const nsACString& aName, nsISocketTransport** result) {
967 // Abstract socket address is supported on Linux only
968 #ifdef XP_LINUX
969 RefPtr<nsSocketTransport> trans = new nsSocketTransport();
970 // First character of Abstract socket address is null
971 UniquePtr<char[]> name(new char[aName.Length() + 1]);
972 *(name.get()) = 0;
973 memcpy(name.get() + 1, aName.BeginReading(), aName.Length());
974 nsresult rv = trans->InitWithName(name.get(), aName.Length() + 1);
975 if (NS_FAILED(rv)) {
976 return rv;
977 }
978
979 trans.forget(result);
980 return NS_OK;
981 #else
982 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
983 #endif
984 }
985
986 NS_IMETHODIMP
OnDispatchedEvent()987 nsSocketTransportService::OnDispatchedEvent() {
988 #ifndef XP_WIN
989 // On windows poll can hang and this became worse when we introduced the
990 // patch for bug 698882 (see also bug 1292181), therefore we reverted the
991 // behavior on windows to be as before bug 698882, e.g. write to the socket
992 // also if an event dispatch is on the socket thread and writing to the
993 // socket for each event.
994 if (OnSocketThread()) {
995 // this check is redundant to one done inside ::Signal(), but
996 // we can do it here and skip obtaining the lock - given that
997 // this is a relatively common occurance its worth the
998 // redundant code
999 SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
1000 return NS_OK;
1001 }
1002 #else
1003 if (gIOService->IsNetTearingDown()) {
1004 // Poll can hang sometimes. If we are in shutdown, we are going to
1005 // start a watchdog. If we do not exit poll within
1006 // REPAIR_POLLABLE_EVENT_TIME signal a pollable event again.
1007 StartPollWatchdog();
1008 }
1009 #endif
1010
1011 MutexAutoLock lock(mLock);
1012 if (mPollableEvent) {
1013 mPollableEvent->Signal();
1014 }
1015 return NS_OK;
1016 }
1017
1018 NS_IMETHODIMP
OnProcessNextEvent(nsIThreadInternal * thread,bool mayWait)1019 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal* thread,
1020 bool mayWait) {
1021 return NS_OK;
1022 }
1023
1024 NS_IMETHODIMP
AfterProcessNextEvent(nsIThreadInternal * thread,bool eventWasProcessed)1025 nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
1026 bool eventWasProcessed) {
1027 return NS_OK;
1028 }
1029
MarkTheLastElementOfPendingQueue()1030 void nsSocketTransportService::MarkTheLastElementOfPendingQueue() {
1031 mServingPendingQueue = false;
1032 }
1033
1034 NS_IMETHODIMP
Run()1035 nsSocketTransportService::Run() {
1036 SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount));
1037
1038 #if defined(XP_WIN)
1039 // see bug 1361495, gethostname() triggers winsock initialization.
1040 // so do it here (on parent and child) to protect against it being done first
1041 // accidentally on the main thread.. especially via PR_GetSystemInfo(). This
1042 // will also improve latency of first real winsock operation
1043 // ..
1044 // If STS-thread is no longer needed this should still be run before exiting
1045
1046 char ignoredStackBuffer[255];
1047 Unused << gethostname(ignoredStackBuffer, 255);
1048 #endif
1049
1050 psm::InitializeSSLServerCertVerificationThreads();
1051
1052 gSocketThread = PR_GetCurrentThread();
1053
1054 {
1055 MutexAutoLock lock(mLock);
1056 mPollableEvent.reset(new PollableEvent());
1057 //
1058 // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
1059 // or similar software.
1060 //
1061 // NOTE: per bug 191739, this failure could also be caused by lack
1062 // of a loopback device on Windows and OS/2 platforms (it creates
1063 // a loopback socket pair on these platforms to implement a pollable
1064 // event object). if we can't create a pollable event, then we'll
1065 // have to "busy wait" to implement the socket event queue :-(
1066 //
1067 if (!mPollableEvent->Valid()) {
1068 mPollableEvent = nullptr;
1069 NS_WARNING("running socket transport thread without a pollable event");
1070 SOCKET_LOG(("running socket transport thread without a pollable event"));
1071 }
1072
1073 mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
1074 mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
1075 mPollList[0].out_flags = 0;
1076 }
1077
1078 mRawThread = NS_GetCurrentThread();
1079
1080 // Ensure a call to GetCurrentSerialEventTarget() returns this event target.
1081 SerialEventTargetGuard guard(this);
1082
1083 // hook ourselves up to observe event processing for this thread
1084 nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(mRawThread);
1085 threadInt->SetObserver(this);
1086
1087 // make sure the pseudo random number generator is seeded on this thread
1088 srand(static_cast<unsigned>(PR_Now()));
1089
1090 // For the calculation of the duration of the last cycle (i.e. the last
1091 // for-loop iteration before shutdown).
1092 TimeStamp startOfCycleForLastCycleCalc;
1093
1094 // For measuring of the poll iteration duration without time spent blocked
1095 // in poll().
1096 TimeStamp pollCycleStart;
1097 // Time blocked in poll().
1098 TimeDuration singlePollDuration;
1099
1100 // For calculating the time needed for a new element to run.
1101 TimeStamp startOfIteration;
1102 TimeStamp startOfNextIteration;
1103
1104 // If there is too many pending events queued, we will run some poll()
1105 // between them and the following variable is cumulative time spent
1106 // blocking in poll().
1107 TimeDuration pollDuration;
1108
1109 for (;;) {
1110 bool pendingEvents = false;
1111 if (Telemetry::CanRecordPrereleaseData()) {
1112 startOfCycleForLastCycleCalc = TimeStamp::NowLoRes();
1113 startOfNextIteration = TimeStamp::NowLoRes();
1114 }
1115 pollDuration = nullptr;
1116 // We pop out to this loop when there are no pending events.
1117 // If we don't reset these, we may not re-enter ProcessNextEvent()
1118 // until we have events to process, and it may seem like we have
1119 // an event running for a very long time.
1120 mRawThread->SetRunningEventDelay(TimeDuration(), TimeStamp());
1121
1122 do {
1123 if (Telemetry::CanRecordPrereleaseData()) {
1124 pollCycleStart = TimeStamp::NowLoRes();
1125 }
1126
1127 DoPollIteration(&singlePollDuration);
1128
1129 if (Telemetry::CanRecordPrereleaseData() && !pollCycleStart.IsNull()) {
1130 Telemetry::Accumulate(Telemetry::STS_POLL_BLOCK_TIME,
1131 singlePollDuration.ToMilliseconds());
1132 Telemetry::AccumulateTimeDelta(Telemetry::STS_POLL_CYCLE,
1133 pollCycleStart + singlePollDuration,
1134 TimeStamp::NowLoRes());
1135 pollDuration += singlePollDuration;
1136 }
1137
1138 mRawThread->HasPendingEvents(&pendingEvents);
1139 if (pendingEvents) {
1140 if (!mServingPendingQueue) {
1141 nsresult rv = Dispatch(
1142 NewRunnableMethod(
1143 "net::nsSocketTransportService::"
1144 "MarkTheLastElementOfPendingQueue",
1145 this,
1146 &nsSocketTransportService::MarkTheLastElementOfPendingQueue),
1147 nsIEventTarget::DISPATCH_NORMAL);
1148 if (NS_FAILED(rv)) {
1149 NS_WARNING(
1150 "Could not dispatch a new event on the "
1151 "socket thread.");
1152 } else {
1153 mServingPendingQueue = true;
1154 }
1155
1156 if (Telemetry::CanRecordPrereleaseData()) {
1157 startOfIteration = startOfNextIteration;
1158 // Everything that comes after this point will
1159 // be served in the next iteration. If no even
1160 // arrives, startOfNextIteration will be reset at the
1161 // beginning of each for-loop.
1162 startOfNextIteration = TimeStamp::NowLoRes();
1163 }
1164 }
1165 TimeStamp eventQueueStart = TimeStamp::NowLoRes();
1166 do {
1167 NS_ProcessNextEvent(mRawThread);
1168 pendingEvents = false;
1169 mRawThread->HasPendingEvents(&pendingEvents);
1170 } while (pendingEvents && mServingPendingQueue &&
1171 ((TimeStamp::NowLoRes() - eventQueueStart).ToMilliseconds() <
1172 mMaxTimePerPollIter));
1173
1174 if (Telemetry::CanRecordPrereleaseData() && !mServingPendingQueue &&
1175 !startOfIteration.IsNull()) {
1176 Telemetry::AccumulateTimeDelta(Telemetry::STS_POLL_AND_EVENTS_CYCLE,
1177 startOfIteration + pollDuration,
1178 TimeStamp::NowLoRes());
1179 pollDuration = nullptr;
1180 }
1181 }
1182 } while (pendingEvents);
1183
1184 bool goingOffline = false;
1185 // now that our event queue is empty, check to see if we should exit
1186 if (mShuttingDown) {
1187 if (Telemetry::CanRecordPrereleaseData() &&
1188 !startOfCycleForLastCycleCalc.IsNull()) {
1189 Telemetry::AccumulateTimeDelta(
1190 Telemetry::STS_POLL_AND_EVENT_THE_LAST_CYCLE,
1191 startOfCycleForLastCycleCalc, TimeStamp::NowLoRes());
1192 }
1193 break;
1194 }
1195 {
1196 MutexAutoLock lock(mLock);
1197 if (mGoingOffline) {
1198 mGoingOffline = false;
1199 goingOffline = true;
1200 }
1201 }
1202 // Avoid potential deadlock
1203 if (goingOffline) {
1204 Reset(true);
1205 }
1206 }
1207
1208 SOCKET_LOG(("STS shutting down thread\n"));
1209
1210 // detach all sockets, including locals
1211 Reset(false);
1212
1213 // We don't clear gSocketThread so that OnSocketThread() won't be a false
1214 // alarm for events generated by stopping the SLL threads during shutdown.
1215 psm::StopSSLServerCertVerificationThreads();
1216
1217 // Final pass over the event queue. This makes sure that events posted by
1218 // socket detach handlers get processed.
1219 NS_ProcessPendingEvents(mRawThread);
1220
1221 SOCKET_LOG(("STS thread exit\n"));
1222
1223 return NS_OK;
1224 }
1225
DetachSocketWithGuard(bool aGuardLocals,SocketContext * socketList,int32_t index)1226 void nsSocketTransportService::DetachSocketWithGuard(bool aGuardLocals,
1227 SocketContext* socketList,
1228 int32_t index) {
1229 bool isGuarded = false;
1230 if (aGuardLocals) {
1231 socketList[index].mHandler->IsLocal(&isGuarded);
1232 if (!isGuarded) socketList[index].mHandler->KeepWhenOffline(&isGuarded);
1233 }
1234 if (!isGuarded) DetachSocket(socketList, &socketList[index]);
1235 }
1236
Reset(bool aGuardLocals)1237 void nsSocketTransportService::Reset(bool aGuardLocals) {
1238 // detach any sockets
1239 int32_t i;
1240 for (i = mActiveCount - 1; i >= 0; --i) {
1241 DetachSocketWithGuard(aGuardLocals, mActiveList, i);
1242 }
1243 for (i = mIdleCount - 1; i >= 0; --i) {
1244 DetachSocketWithGuard(aGuardLocals, mIdleList, i);
1245 }
1246 }
1247
DoPollIteration(TimeDuration * pollDuration)1248 nsresult nsSocketTransportService::DoPollIteration(TimeDuration* pollDuration) {
1249 SOCKET_LOG(("STS poll iter\n"));
1250
1251 PRIntervalTime now = PR_IntervalNow();
1252
1253 int32_t i, count;
1254 //
1255 // poll loop
1256 //
1257 // walk active list backwards to see if any sockets should actually be
1258 // idle, then walk the idle list backwards to see if any idle sockets
1259 // should become active. take care to check only idle sockets that
1260 // were idle to begin with ;-)
1261 //
1262 count = mIdleCount;
1263 for (i = mActiveCount - 1; i >= 0; --i) {
1264 //---
1265 SOCKET_LOG((" active [%u] { handler=%p condition=%" PRIx32
1266 " pollflags=%hu }\n",
1267 i, mActiveList[i].mHandler,
1268 static_cast<uint32_t>(mActiveList[i].mHandler->mCondition),
1269 mActiveList[i].mHandler->mPollFlags));
1270 //---
1271 if (NS_FAILED(mActiveList[i].mHandler->mCondition)) {
1272 DetachSocket(mActiveList, &mActiveList[i]);
1273 } else {
1274 uint16_t in_flags = mActiveList[i].mHandler->mPollFlags;
1275 if (in_flags == 0) {
1276 MoveToIdleList(&mActiveList[i]);
1277 } else {
1278 // update poll flags
1279 mPollList[i + 1].in_flags = in_flags;
1280 mPollList[i + 1].out_flags = 0;
1281 mActiveList[i].EnsureTimeout(now);
1282 }
1283 }
1284 }
1285 for (i = count - 1; i >= 0; --i) {
1286 //---
1287 SOCKET_LOG((" idle [%u] { handler=%p condition=%" PRIx32
1288 " pollflags=%hu }\n",
1289 i, mIdleList[i].mHandler,
1290 static_cast<uint32_t>(mIdleList[i].mHandler->mCondition),
1291 mIdleList[i].mHandler->mPollFlags));
1292 //---
1293 if (NS_FAILED(mIdleList[i].mHandler->mCondition)) {
1294 DetachSocket(mIdleList, &mIdleList[i]);
1295 } else if (mIdleList[i].mHandler->mPollFlags != 0) {
1296 MoveToPollList(&mIdleList[i]);
1297 }
1298 }
1299
1300 {
1301 MutexAutoLock lock(mLock);
1302 if (mPollableEvent) {
1303 // we want to make sure the timeout is measured from the time
1304 // we enter poll(). This method resets the timestamp to 'now',
1305 // if we were first signalled between leaving poll() and here.
1306 // If we didn't do this and processing events took longer than
1307 // the allowed signal timeout, we would detect it as a
1308 // false-positive. AdjustFirstSignalTimestamp is then a no-op
1309 // until mPollableEvent->Clear() is called.
1310 mPollableEvent->AdjustFirstSignalTimestamp();
1311 }
1312 }
1313
1314 SOCKET_LOG(
1315 (" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
1316
1317 #if defined(XP_WIN)
1318 // 30 active connections is the historic limit before firefox 7's 256. A few
1319 // windows systems have troubles with the higher limit, so actively probe a
1320 // limit the first time we exceed 30.
1321 if ((mActiveCount > 30) && !mProbedMaxCount) ProbeMaxCount();
1322 #endif
1323
1324 // Measures seconds spent while blocked on PR_Poll
1325 int32_t n = 0;
1326 *pollDuration = nullptr;
1327
1328 if (!gIOService->IsNetTearingDown()) {
1329 // Let's not do polling during shutdown.
1330 #if defined(XP_WIN)
1331 StartPolling();
1332 #endif
1333 n = Poll(pollDuration, now);
1334 #if defined(XP_WIN)
1335 EndPolling();
1336 #endif
1337 }
1338
1339 now = PR_IntervalNow();
1340
1341 if (n < 0) {
1342 SOCKET_LOG((" PR_Poll error [%d] os error [%d]\n", PR_GetError(),
1343 PR_GetOSError()));
1344 } else {
1345 //
1346 // service "active" sockets...
1347 //
1348 for (i = 0; i < int32_t(mActiveCount); ++i) {
1349 PRPollDesc& desc = mPollList[i + 1];
1350 SocketContext& s = mActiveList[i];
1351 if (n > 0 && desc.out_flags != 0) {
1352 s.DisengageTimeout();
1353 s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
1354 } else if (s.IsTimedOut(now)) {
1355 SOCKET_LOG(("socket %p timed out", s.mHandler));
1356 s.DisengageTimeout();
1357 s.mHandler->OnSocketReady(desc.fd, -1);
1358 } else {
1359 s.MaybeResetEpoch();
1360 }
1361 }
1362 //
1363 // check for "dead" sockets and remove them (need to do this in
1364 // reverse order obviously).
1365 //
1366 for (i = mActiveCount - 1; i >= 0; --i) {
1367 if (NS_FAILED(mActiveList[i].mHandler->mCondition)) {
1368 DetachSocket(mActiveList, &mActiveList[i]);
1369 }
1370 }
1371
1372 {
1373 MutexAutoLock lock(mLock);
1374 // acknowledge pollable event (should not block)
1375 if (n != 0 &&
1376 (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT)) &&
1377 mPollableEvent &&
1378 ((mPollList[0].out_flags & PR_POLL_EXCEPT) ||
1379 !mPollableEvent->Clear())) {
1380 // On Windows, the TCP loopback connection in the
1381 // pollable event may become broken when a laptop
1382 // switches between wired and wireless networks or
1383 // wakes up from hibernation. We try to create a
1384 // new pollable event. If that fails, we fall back
1385 // on "busy wait".
1386 TryRepairPollableEvent();
1387 }
1388
1389 if (mPollableEvent &&
1390 !mPollableEvent->IsSignallingAlive(mPollableEventTimeout)) {
1391 SOCKET_LOG(("Pollable event signalling failed/timed out"));
1392 TryRepairPollableEvent();
1393 }
1394 }
1395 }
1396
1397 return NS_OK;
1398 }
1399
UpdateSendBufferPref()1400 void nsSocketTransportService::UpdateSendBufferPref() {
1401 int32_t bufferSize;
1402
1403 // If the pref is set, honor it. 0 means use OS defaults.
1404 nsresult rv = Preferences::GetInt(SEND_BUFFER_PREF, &bufferSize);
1405 if (NS_SUCCEEDED(rv)) {
1406 mSendBufferSize = bufferSize;
1407 return;
1408 }
1409
1410 #if defined(XP_WIN)
1411 mSendBufferSize = 131072 * 4;
1412 #endif
1413 }
1414
UpdatePrefs()1415 nsresult nsSocketTransportService::UpdatePrefs() {
1416 mSendBufferSize = 0;
1417
1418 UpdateSendBufferPref();
1419
1420 // Default TCP Keepalive Values.
1421 int32_t keepaliveIdleTimeS;
1422 nsresult rv =
1423 Preferences::GetInt(KEEPALIVE_IDLE_TIME_PREF, &keepaliveIdleTimeS);
1424 if (NS_SUCCEEDED(rv)) {
1425 mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS, 1, kMaxTCPKeepIdle);
1426 }
1427
1428 int32_t keepaliveRetryIntervalS;
1429 rv = Preferences::GetInt(KEEPALIVE_RETRY_INTERVAL_PREF,
1430 &keepaliveRetryIntervalS);
1431 if (NS_SUCCEEDED(rv)) {
1432 mKeepaliveRetryIntervalS =
1433 clamped(keepaliveRetryIntervalS, 1, kMaxTCPKeepIntvl);
1434 }
1435
1436 int32_t keepaliveProbeCount;
1437 rv = Preferences::GetInt(KEEPALIVE_PROBE_COUNT_PREF, &keepaliveProbeCount);
1438 if (NS_SUCCEEDED(rv)) {
1439 mKeepaliveProbeCount = clamped(keepaliveProbeCount, 1, kMaxTCPKeepCount);
1440 }
1441 bool keepaliveEnabled = false;
1442 rv = Preferences::GetBool(KEEPALIVE_ENABLED_PREF, &keepaliveEnabled);
1443 if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
1444 mKeepaliveEnabledPref = keepaliveEnabled;
1445 OnKeepaliveEnabledPrefChange();
1446 }
1447
1448 int32_t maxTimePref;
1449 rv = Preferences::GetInt(MAX_TIME_BETWEEN_TWO_POLLS, &maxTimePref);
1450 if (NS_SUCCEEDED(rv) && maxTimePref >= 0) {
1451 mMaxTimePerPollIter = maxTimePref;
1452 }
1453
1454 int32_t pollBusyWaitPeriod;
1455 rv = Preferences::GetInt(POLL_BUSY_WAIT_PERIOD, &pollBusyWaitPeriod);
1456 if (NS_SUCCEEDED(rv) && pollBusyWaitPeriod > 0) {
1457 mNetworkLinkChangeBusyWaitPeriod = PR_SecondsToInterval(pollBusyWaitPeriod);
1458 }
1459
1460 int32_t pollBusyWaitPeriodTimeout;
1461 rv = Preferences::GetInt(POLL_BUSY_WAIT_PERIOD_TIMEOUT,
1462 &pollBusyWaitPeriodTimeout);
1463 if (NS_SUCCEEDED(rv) && pollBusyWaitPeriodTimeout > 0) {
1464 mNetworkLinkChangeBusyWaitTimeout =
1465 PR_SecondsToInterval(pollBusyWaitPeriodTimeout);
1466 }
1467
1468 int32_t maxTimeForPrClosePref;
1469 rv = Preferences::GetInt(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN,
1470 &maxTimeForPrClosePref);
1471 if (NS_SUCCEEDED(rv) && maxTimeForPrClosePref >= 0) {
1472 mMaxTimeForPrClosePref = PR_MillisecondsToInterval(maxTimeForPrClosePref);
1473 }
1474
1475 int32_t pollableEventTimeout;
1476 rv = Preferences::GetInt(POLLABLE_EVENT_TIMEOUT, &pollableEventTimeout);
1477 if (NS_SUCCEEDED(rv) && pollableEventTimeout >= 0) {
1478 MutexAutoLock lock(mLock);
1479 mPollableEventTimeout = TimeDuration::FromSeconds(pollableEventTimeout);
1480 }
1481
1482 nsAutoCString portMappingPref;
1483 rv = Preferences::GetCString("network.socket.forcePort", portMappingPref);
1484 if (NS_SUCCEEDED(rv)) {
1485 bool rv = UpdatePortRemapPreference(portMappingPref);
1486 if (!rv) {
1487 NS_ERROR(
1488 "network.socket.forcePort preference is ill-formed, this will likely "
1489 "make everything unexpectedly fail!");
1490 }
1491 }
1492
1493 return NS_OK;
1494 }
1495
OnKeepaliveEnabledPrefChange()1496 void nsSocketTransportService::OnKeepaliveEnabledPrefChange() {
1497 // Dispatch to socket thread if we're not executing there.
1498 if (!OnSocketThread()) {
1499 gSocketTransportService->Dispatch(
1500 NewRunnableMethod(
1501 "net::nsSocketTransportService::OnKeepaliveEnabledPrefChange", this,
1502 &nsSocketTransportService::OnKeepaliveEnabledPrefChange),
1503 NS_DISPATCH_NORMAL);
1504 return;
1505 }
1506
1507 SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
1508 mKeepaliveEnabledPref ? "enabled" : "disabled"));
1509
1510 // Notify each socket that keepalive has been en/disabled globally.
1511 for (int32_t i = mActiveCount - 1; i >= 0; --i) {
1512 NotifyKeepaliveEnabledPrefChange(&mActiveList[i]);
1513 }
1514 for (int32_t i = mIdleCount - 1; i >= 0; --i) {
1515 NotifyKeepaliveEnabledPrefChange(&mIdleList[i]);
1516 }
1517 }
1518
NotifyKeepaliveEnabledPrefChange(SocketContext * sock)1519 void nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(
1520 SocketContext* sock) {
1521 MOZ_ASSERT(sock, "SocketContext cannot be null!");
1522 MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!");
1523
1524 if (!sock || !sock->mHandler) {
1525 return;
1526 }
1527
1528 sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
1529 }
1530
1531 NS_IMETHODIMP
Observe(nsISupports * subject,const char * topic,const char16_t * data)1532 nsSocketTransportService::Observe(nsISupports* subject, const char* topic,
1533 const char16_t* data) {
1534 SOCKET_LOG(("nsSocketTransportService::Observe topic=%s", topic));
1535
1536 if (!strcmp(topic, "profile-initial-state")) {
1537 if (!Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false)) {
1538 return NS_OK;
1539 }
1540 return net::IOActivityMonitor::Init();
1541 }
1542
1543 if (!strcmp(topic, "last-pb-context-exited")) {
1544 nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(
1545 "net::nsSocketTransportService::ClosePrivateConnections", this,
1546 &nsSocketTransportService::ClosePrivateConnections);
1547 nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
1548 NS_ENSURE_SUCCESS(rv, rv);
1549 }
1550
1551 if (!strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
1552 nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
1553 if (timer == mAfterWakeUpTimer) {
1554 mAfterWakeUpTimer = nullptr;
1555 mSleepPhase = false;
1556 }
1557
1558 #if defined(XP_WIN)
1559 if (timer == mPollRepairTimer) {
1560 DoPollRepair();
1561 }
1562 #endif
1563
1564 } else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
1565 mSleepPhase = true;
1566 if (mAfterWakeUpTimer) {
1567 mAfterWakeUpTimer->Cancel();
1568 mAfterWakeUpTimer = nullptr;
1569 }
1570 } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
1571 if (mSleepPhase && !mAfterWakeUpTimer) {
1572 NS_NewTimerWithObserver(getter_AddRefs(mAfterWakeUpTimer), this, 2000,
1573 nsITimer::TYPE_ONE_SHOT);
1574 }
1575 } else if (!strcmp(topic, "xpcom-shutdown-threads")) {
1576 ShutdownThread();
1577 } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
1578 mLastNetworkLinkChangeTime = PR_IntervalNow();
1579 mNotTrustedMitmDetected = false;
1580 }
1581
1582 return NS_OK;
1583 }
1584
ClosePrivateConnections()1585 void nsSocketTransportService::ClosePrivateConnections() {
1586 MOZ_ASSERT(IsOnCurrentThread(), "Must be called on the socket thread");
1587
1588 for (int32_t i = mActiveCount - 1; i >= 0; --i) {
1589 if (mActiveList[i].mHandler->mIsPrivate) {
1590 DetachSocket(mActiveList, &mActiveList[i]);
1591 }
1592 }
1593 for (int32_t i = mIdleCount - 1; i >= 0; --i) {
1594 if (mIdleList[i].mHandler->mIsPrivate) {
1595 DetachSocket(mIdleList, &mIdleList[i]);
1596 }
1597 }
1598
1599 ClearPrivateSSLState();
1600 }
1601
1602 NS_IMETHODIMP
GetSendBufferSize(int32_t * value)1603 nsSocketTransportService::GetSendBufferSize(int32_t* value) {
1604 *value = mSendBufferSize;
1605 return NS_OK;
1606 }
1607
1608 /// ugly OS specific includes are placed at the bottom of the src for clarity
1609
1610 #if defined(XP_WIN)
1611 # include <windows.h>
1612 #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
1613 # include <sys/resource.h>
1614 #endif
1615
1616 // Right now the only need to do this is on windows.
1617 #if defined(XP_WIN)
ProbeMaxCount()1618 void nsSocketTransportService::ProbeMaxCount() {
1619 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1620
1621 if (mProbedMaxCount) {
1622 return;
1623 }
1624 mProbedMaxCount = true;
1625
1626 // Allocate and test a PR_Poll up to the gMaxCount number of unconnected
1627 // sockets. See bug 692260 - windows should be able to handle 1000 sockets
1628 // in select() without a problem, but LSPs have been known to balk at lower
1629 // numbers. (64 in the bug).
1630
1631 // Allocate
1632 struct PRPollDesc pfd[SOCKET_LIMIT_TARGET];
1633 uint32_t numAllocated = 0;
1634
1635 for (uint32_t index = 0; index < gMaxCount; ++index) {
1636 pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
1637 pfd[index].out_flags = 0;
1638 pfd[index].fd = PR_OpenTCPSocket(PR_AF_INET);
1639 if (!pfd[index].fd) {
1640 SOCKET_LOG(("Socket Limit Test index %d failed\n", index));
1641 if (index < SOCKET_LIMIT_MIN)
1642 gMaxCount = SOCKET_LIMIT_MIN;
1643 else
1644 gMaxCount = index;
1645 break;
1646 }
1647 ++numAllocated;
1648 }
1649
1650 // Test
1651 static_assert(SOCKET_LIMIT_MIN >= 32U, "Minimum Socket Limit is >= 32");
1652 while (gMaxCount <= numAllocated) {
1653 int32_t rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0));
1654
1655 SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n", gMaxCount, rv));
1656
1657 if (rv >= 0) break;
1658
1659 SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
1660 gMaxCount, rv, PR_GetError()));
1661
1662 gMaxCount -= 32;
1663 if (gMaxCount <= SOCKET_LIMIT_MIN) {
1664 gMaxCount = SOCKET_LIMIT_MIN;
1665 break;
1666 }
1667 }
1668
1669 // Free
1670 for (uint32_t index = 0; index < numAllocated; ++index)
1671 if (pfd[index].fd) PR_Close(pfd[index].fd);
1672
1673 Telemetry::Accumulate(Telemetry::NETWORK_PROBE_MAXCOUNT, gMaxCount);
1674 SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount));
1675 }
1676 #endif // windows
1677
DiscoverMaxCount()1678 PRStatus nsSocketTransportService::DiscoverMaxCount() {
1679 gMaxCount = SOCKET_LIMIT_MIN;
1680
1681 #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
1682 // On unix and os x network sockets and file
1683 // descriptors are the same. OS X comes defaulted at 256,
1684 // most linux at 1000. We can reliably use [sg]rlimit to
1685 // query that and raise it if needed.
1686
1687 struct rlimit rlimitData {};
1688 if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1) { // rlimit broken - use min
1689 return PR_SUCCESS;
1690 }
1691
1692 if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET) { // larger than target!
1693 gMaxCount = SOCKET_LIMIT_TARGET;
1694 return PR_SUCCESS;
1695 }
1696
1697 int32_t maxallowed = rlimitData.rlim_max;
1698 if ((uint32_t)maxallowed <= SOCKET_LIMIT_MIN) {
1699 return PR_SUCCESS; // so small treat as if rlimit is broken
1700 }
1701
1702 if ((maxallowed == -1) || // no hard cap - ok to set target
1703 ((uint32_t)maxallowed >= SOCKET_LIMIT_TARGET)) {
1704 maxallowed = SOCKET_LIMIT_TARGET;
1705 }
1706
1707 rlimitData.rlim_cur = maxallowed;
1708 setrlimit(RLIMIT_NOFILE, &rlimitData);
1709 if ((getrlimit(RLIMIT_NOFILE, &rlimitData) != -1) &&
1710 (rlimitData.rlim_cur > SOCKET_LIMIT_MIN)) {
1711 gMaxCount = rlimitData.rlim_cur;
1712 }
1713
1714 #elif defined(XP_WIN) && !defined(WIN_CE)
1715 // >= XP is confirmed to have at least 1000
1716 static_assert(SOCKET_LIMIT_TARGET <= 1000,
1717 "SOCKET_LIMIT_TARGET max value is 1000");
1718 gMaxCount = SOCKET_LIMIT_TARGET;
1719 #else
1720 // other platforms are harder to test - so leave at safe legacy value
1721 #endif
1722
1723 return PR_SUCCESS;
1724 }
1725
1726 // Used to return connection info to Dashboard.cpp
AnalyzeConnection(nsTArray<SocketInfo> * data,struct SocketContext * context,bool aActive)1727 void nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo>* data,
1728 struct SocketContext* context,
1729 bool aActive) {
1730 if (context->mHandler->mIsPrivate) {
1731 return;
1732 }
1733 PRFileDesc* aFD = context->mFD;
1734
1735 PRFileDesc* idLayer = PR_GetIdentitiesLayer(aFD, PR_NSPR_IO_LAYER);
1736
1737 NS_ENSURE_TRUE_VOID(idLayer);
1738
1739 bool tcp = PR_GetDescType(idLayer) == PR_DESC_SOCKET_TCP;
1740
1741 PRNetAddr peer_addr;
1742 PodZero(&peer_addr);
1743 PRStatus rv = PR_GetPeerName(aFD, &peer_addr);
1744 if (rv != PR_SUCCESS) {
1745 return;
1746 }
1747
1748 char host[64] = {0};
1749 rv = PR_NetAddrToString(&peer_addr, host, sizeof(host));
1750 if (rv != PR_SUCCESS) {
1751 return;
1752 }
1753
1754 uint16_t port;
1755 if (peer_addr.raw.family == PR_AF_INET) {
1756 port = peer_addr.inet.port;
1757 } else {
1758 port = peer_addr.ipv6.port;
1759 }
1760 port = PR_ntohs(port);
1761 uint64_t sent = context->mHandler->ByteCountSent();
1762 uint64_t received = context->mHandler->ByteCountReceived();
1763 SocketInfo info = {nsCString(host), sent, received, port, aActive, tcp};
1764
1765 data->AppendElement(info);
1766 }
1767
GetSocketConnections(nsTArray<SocketInfo> * data)1768 void nsSocketTransportService::GetSocketConnections(
1769 nsTArray<SocketInfo>* data) {
1770 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1771 for (uint32_t i = 0; i < mActiveCount; i++) {
1772 AnalyzeConnection(data, &mActiveList[i], true);
1773 }
1774 for (uint32_t i = 0; i < mIdleCount; i++) {
1775 AnalyzeConnection(data, &mIdleList[i], false);
1776 }
1777 }
1778
IsTelemetryEnabledAndNotSleepPhase()1779 bool nsSocketTransportService::IsTelemetryEnabledAndNotSleepPhase() {
1780 return Telemetry::CanRecordPrereleaseData() && !mSleepPhase;
1781 }
1782
1783 #if defined(XP_WIN)
StartPollWatchdog()1784 void nsSocketTransportService::StartPollWatchdog() {
1785 // Start off the timer from a runnable off of the main thread in order to
1786 // avoid a deadlock, see bug 1370448.
1787 RefPtr<nsSocketTransportService> self(this);
1788 NS_DispatchToMainThread(NS_NewRunnableFunction(
1789 "nsSocketTransportService::StartPollWatchdog", [self] {
1790 MutexAutoLock lock(self->mLock);
1791
1792 // Poll can hang sometimes. If we are in shutdown, we are going to start
1793 // a watchdog. If we do not exit poll within REPAIR_POLLABLE_EVENT_TIME
1794 // signal a pollable event again.
1795 MOZ_ASSERT(gIOService->IsNetTearingDown());
1796 if (self->mPolling && !self->mPollRepairTimer) {
1797 NS_NewTimerWithObserver(getter_AddRefs(self->mPollRepairTimer), self,
1798 REPAIR_POLLABLE_EVENT_TIME,
1799 nsITimer::TYPE_REPEATING_SLACK);
1800 }
1801 }));
1802 }
1803
DoPollRepair()1804 void nsSocketTransportService::DoPollRepair() {
1805 MutexAutoLock lock(mLock);
1806 if (mPolling && mPollableEvent) {
1807 mPollableEvent->Signal();
1808 } else if (mPollRepairTimer) {
1809 mPollRepairTimer->Cancel();
1810 }
1811 }
1812
StartPolling()1813 void nsSocketTransportService::StartPolling() {
1814 MutexAutoLock lock(mLock);
1815 mPolling = true;
1816 }
1817
EndPolling()1818 void nsSocketTransportService::EndPolling() {
1819 MutexAutoLock lock(mLock);
1820 mPolling = false;
1821 if (mPollRepairTimer) {
1822 mPollRepairTimer->Cancel();
1823 }
1824 }
1825
1826 #endif
1827
TryRepairPollableEvent()1828 void nsSocketTransportService::TryRepairPollableEvent() {
1829 mLock.AssertCurrentThreadOwns();
1830
1831 NS_WARNING("Trying to repair mPollableEvent");
1832 mPollableEvent.reset(new PollableEvent());
1833 if (!mPollableEvent->Valid()) {
1834 mPollableEvent = nullptr;
1835 }
1836 SOCKET_LOG(
1837 ("running socket transport thread without "
1838 "a pollable event now valid=%d",
1839 !!mPollableEvent));
1840 mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
1841 mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
1842 mPollList[0].out_flags = 0;
1843 }
1844
1845 NS_IMETHODIMP
AddShutdownObserver(nsISTSShutdownObserver * aObserver)1846 nsSocketTransportService::AddShutdownObserver(
1847 nsISTSShutdownObserver* aObserver) {
1848 mShutdownObservers.AppendElement(aObserver);
1849 return NS_OK;
1850 }
1851
1852 NS_IMETHODIMP
RemoveShutdownObserver(nsISTSShutdownObserver * aObserver)1853 nsSocketTransportService::RemoveShutdownObserver(
1854 nsISTSShutdownObserver* aObserver) {
1855 mShutdownObservers.RemoveElement(aObserver);
1856 return NS_OK;
1857 }
1858
1859 } // namespace net
1860 } // namespace mozilla
1861