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