1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http:mozilla.org/MPL/2.0/. */
4 
5 #include "mozilla/dom/NetDashboardBinding.h"
6 #include "mozilla/dom/ToJSValue.h"
7 #include "mozilla/ErrorNames.h"
8 #include "mozilla/net/Dashboard.h"
9 #include "mozilla/net/HttpInfo.h"
10 #include "mozilla/net/HTTPSSVC.h"
11 #include "mozilla/net/SocketProcessParent.h"
12 #include "nsHttp.h"
13 #include "nsICancelable.h"
14 #include "nsIDNSListener.h"
15 #include "nsIDNSService.h"
16 #include "nsIDNSRecord.h"
17 #include "nsIDNSByTypeRecord.h"
18 #include "nsIInputStream.h"
19 #include "nsINamed.h"
20 #include "nsINetAddr.h"
21 #include "nsISocketTransport.h"
22 #include "nsProxyRelease.h"
23 #include "nsSocketTransportService2.h"
24 #include "nsThreadUtils.h"
25 #include "nsURLHelper.h"
26 #include "mozilla/Logging.h"
27 #include "nsIOService.h"
28 #include "../cache2/CacheFileUtils.h"
29 
30 using mozilla::AutoSafeJSContext;
31 using mozilla::dom::Sequence;
32 using mozilla::dom::ToJSValue;
33 
34 namespace mozilla {
35 namespace net {
36 
37 class SocketData : public nsISupports {
38  public:
39   NS_DECL_THREADSAFE_ISUPPORTS
40 
41   SocketData() = default;
42 
43   uint64_t mTotalSent{0};
44   uint64_t mTotalRecv{0};
45   nsTArray<SocketInfo> mData;
46   nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
47   nsIEventTarget* mEventTarget{nullptr};
48 
49  private:
50   virtual ~SocketData() = default;
51 };
52 
53 static void GetErrorString(nsresult rv, nsAString& errorString);
54 
55 NS_IMPL_ISUPPORTS0(SocketData)
56 
57 class HttpData : public nsISupports {
58   virtual ~HttpData() = default;
59 
60  public:
61   NS_DECL_THREADSAFE_ISUPPORTS
62 
63   HttpData() = default;
64 
65   nsTArray<HttpRetParams> mData;
66   nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
67   nsIEventTarget* mEventTarget{nullptr};
68 };
69 
70 NS_IMPL_ISUPPORTS0(HttpData)
71 
72 class WebSocketRequest : public nsISupports {
73   virtual ~WebSocketRequest() = default;
74 
75  public:
76   NS_DECL_THREADSAFE_ISUPPORTS
77 
78   WebSocketRequest() = default;
79 
80   nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
81   nsIEventTarget* mEventTarget{nullptr};
82 };
83 
84 NS_IMPL_ISUPPORTS0(WebSocketRequest)
85 
86 class DnsData : public nsISupports {
87   virtual ~DnsData() = default;
88 
89  public:
90   NS_DECL_THREADSAFE_ISUPPORTS
91 
92   DnsData() = default;
93 
94   nsTArray<DNSCacheEntries> mData;
95   nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
96   nsIEventTarget* mEventTarget{nullptr};
97 };
98 
99 NS_IMPL_ISUPPORTS0(DnsData)
100 
101 class ConnectionData : public nsITransportEventSink,
102                        public nsITimerCallback,
103                        public nsINamed {
~ConnectionData()104   virtual ~ConnectionData() {
105     if (mTimer) {
106       mTimer->Cancel();
107     }
108   }
109 
110  public:
111   NS_DECL_THREADSAFE_ISUPPORTS
112   NS_DECL_NSITRANSPORTEVENTSINK
113   NS_DECL_NSITIMERCALLBACK
114 
GetName(nsACString & aName)115   NS_IMETHOD GetName(nsACString& aName) override {
116     aName.AssignLiteral("net::ConnectionData");
117     return NS_OK;
118   }
119 
120   void StartTimer(uint32_t aTimeout);
121   void StopTimer();
122 
ConnectionData(Dashboard * target)123   explicit ConnectionData(Dashboard* target) { mDashboard = target; }
124 
125   nsCOMPtr<nsISocketTransport> mSocket;
126   nsCOMPtr<nsIInputStream> mStreamIn;
127   nsCOMPtr<nsITimer> mTimer;
128   nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
129   nsIEventTarget* mEventTarget{nullptr};
130   Dashboard* mDashboard;
131 
132   nsCString mHost;
133   uint32_t mPort{0};
134   nsCString mProtocol;
135   uint32_t mTimeout{0};
136 
137   nsString mStatus;
138 };
139 
140 NS_IMPL_ISUPPORTS(ConnectionData, nsITransportEventSink, nsITimerCallback,
141                   nsINamed)
142 
143 class RcwnData : public nsISupports {
144   virtual ~RcwnData() = default;
145 
146  public:
147   NS_DECL_THREADSAFE_ISUPPORTS
148 
149   RcwnData() = default;
150 
151   nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
152   nsIEventTarget* mEventTarget{nullptr};
153 };
154 
NS_IMPL_ISUPPORTS0(RcwnData)155 NS_IMPL_ISUPPORTS0(RcwnData)
156 
157 NS_IMETHODIMP
158 ConnectionData::OnTransportStatus(nsITransport* aTransport, nsresult aStatus,
159                                   int64_t aProgress, int64_t aProgressMax) {
160   if (aStatus == NS_NET_STATUS_CONNECTED_TO) {
161     StopTimer();
162   }
163 
164   GetErrorString(aStatus, mStatus);
165   mEventTarget->Dispatch(NewRunnableMethod<RefPtr<ConnectionData>>(
166                              "net::Dashboard::GetConnectionStatus", mDashboard,
167                              &Dashboard::GetConnectionStatus, this),
168                          NS_DISPATCH_NORMAL);
169 
170   return NS_OK;
171 }
172 
173 NS_IMETHODIMP
Notify(nsITimer * aTimer)174 ConnectionData::Notify(nsITimer* aTimer) {
175   MOZ_ASSERT(aTimer == mTimer);
176 
177   if (mSocket) {
178     mSocket->Close(NS_ERROR_ABORT);
179     mSocket = nullptr;
180     mStreamIn = nullptr;
181   }
182 
183   mTimer = nullptr;
184 
185   mStatus.AssignLiteral(u"NS_ERROR_NET_TIMEOUT");
186   mEventTarget->Dispatch(NewRunnableMethod<RefPtr<ConnectionData>>(
187                              "net::Dashboard::GetConnectionStatus", mDashboard,
188                              &Dashboard::GetConnectionStatus, this),
189                          NS_DISPATCH_NORMAL);
190 
191   return NS_OK;
192 }
193 
StartTimer(uint32_t aTimeout)194 void ConnectionData::StartTimer(uint32_t aTimeout) {
195   if (!mTimer) {
196     mTimer = NS_NewTimer();
197   }
198 
199   mTimer->InitWithCallback(this, aTimeout * 1000, nsITimer::TYPE_ONE_SHOT);
200 }
201 
StopTimer()202 void ConnectionData::StopTimer() {
203   if (mTimer) {
204     mTimer->Cancel();
205     mTimer = nullptr;
206   }
207 }
208 
209 class LookupHelper;
210 
211 class LookupArgument : public nsISupports {
212   virtual ~LookupArgument() = default;
213 
214  public:
215   NS_DECL_THREADSAFE_ISUPPORTS
216 
LookupArgument(nsIDNSRecord * aRecord,LookupHelper * aHelper)217   LookupArgument(nsIDNSRecord* aRecord, LookupHelper* aHelper) {
218     mRecord = aRecord;
219     mHelper = aHelper;
220   }
221 
222   nsCOMPtr<nsIDNSRecord> mRecord;
223   RefPtr<LookupHelper> mHelper;
224 };
225 
226 NS_IMPL_ISUPPORTS0(LookupArgument)
227 
228 class LookupHelper final : public nsIDNSListener {
~LookupHelper()229   virtual ~LookupHelper() {
230     if (mCancel) {
231       mCancel->Cancel(NS_ERROR_ABORT);
232     }
233   }
234 
235  public:
236   NS_DECL_THREADSAFE_ISUPPORTS
237   NS_DECL_NSIDNSLISTENER
238 
239   LookupHelper() = default;
240 
241   nsresult ConstructAnswer(LookupArgument* aArgument);
242   nsresult ConstructHTTPSRRAnswer(LookupArgument* aArgument);
243 
244  public:
245   nsCOMPtr<nsICancelable> mCancel;
246   nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
247   nsIEventTarget* mEventTarget{nullptr};
248   nsresult mStatus{NS_ERROR_NOT_INITIALIZED};
249 };
250 
NS_IMPL_ISUPPORTS(LookupHelper,nsIDNSListener)251 NS_IMPL_ISUPPORTS(LookupHelper, nsIDNSListener)
252 
253 NS_IMETHODIMP
254 LookupHelper::OnLookupComplete(nsICancelable* aRequest, nsIDNSRecord* aRecord,
255                                nsresult aStatus) {
256   MOZ_ASSERT(aRequest == mCancel);
257   mCancel = nullptr;
258   mStatus = aStatus;
259 
260   nsCOMPtr<nsIDNSHTTPSSVCRecord> httpsRecord = do_QueryInterface(aRecord);
261   if (httpsRecord) {
262     RefPtr<LookupArgument> arg = new LookupArgument(aRecord, this);
263     mEventTarget->Dispatch(
264         NewRunnableMethod<RefPtr<LookupArgument>>(
265             "net::LookupHelper::ConstructHTTPSRRAnswer", this,
266             &LookupHelper::ConstructHTTPSRRAnswer, arg),
267         NS_DISPATCH_NORMAL);
268     return NS_OK;
269   }
270 
271   RefPtr<LookupArgument> arg = new LookupArgument(aRecord, this);
272   mEventTarget->Dispatch(NewRunnableMethod<RefPtr<LookupArgument>>(
273                              "net::LookupHelper::ConstructAnswer", this,
274                              &LookupHelper::ConstructAnswer, arg),
275                          NS_DISPATCH_NORMAL);
276 
277   return NS_OK;
278 }
279 
ConstructAnswer(LookupArgument * aArgument)280 nsresult LookupHelper::ConstructAnswer(LookupArgument* aArgument) {
281   nsIDNSRecord* aRecord = aArgument->mRecord;
282   AutoSafeJSContext cx;
283 
284   mozilla::dom::DNSLookupDict dict;
285   dict.mAddress.Construct();
286 
287   Sequence<nsString>& addresses = dict.mAddress.Value();
288   nsCOMPtr<nsIDNSAddrRecord> record = do_QueryInterface(aRecord);
289   if (NS_SUCCEEDED(mStatus) && record) {
290     dict.mAnswer = true;
291     bool hasMore;
292     record->HasMore(&hasMore);
293     while (hasMore) {
294       nsString* nextAddress = addresses.AppendElement(fallible);
295       if (!nextAddress) {
296         return NS_ERROR_OUT_OF_MEMORY;
297       }
298 
299       nsCString nextAddressASCII;
300       record->GetNextAddrAsString(nextAddressASCII);
301       CopyASCIItoUTF16(nextAddressASCII, *nextAddress);
302       record->HasMore(&hasMore);
303     }
304   } else {
305     dict.mAnswer = false;
306     GetErrorString(mStatus, dict.mError);
307   }
308 
309   JS::RootedValue val(cx);
310   if (!ToJSValue(cx, dict, &val)) {
311     return NS_ERROR_FAILURE;
312   }
313 
314   this->mCallback->OnDashboardDataAvailable(val);
315 
316   return NS_OK;
317 }
318 
CStringToHexString(const nsACString & aIn,nsAString & aOut)319 static void CStringToHexString(const nsACString& aIn, nsAString& aOut) {
320   static const char* const lut = "0123456789ABCDEF";
321 
322   size_t len = aIn.Length();
323 
324   aOut.SetCapacity(2 * len);
325   for (size_t i = 0; i < aIn.Length(); ++i) {
326     const char c = static_cast<char>(aIn[i]);
327     aOut.Append(lut[(c >> 4) & 0x0F]);
328     aOut.Append(lut[c & 15]);
329   }
330 }
331 
ConstructHTTPSRRAnswer(LookupArgument * aArgument)332 nsresult LookupHelper::ConstructHTTPSRRAnswer(LookupArgument* aArgument) {
333   nsCOMPtr<nsIDNSHTTPSSVCRecord> httpsRecord =
334       do_QueryInterface(aArgument->mRecord);
335 
336   AutoSafeJSContext cx;
337 
338   mozilla::dom::HTTPSRRLookupDict dict;
339   dict.mRecords.Construct();
340 
341   Sequence<dom::HTTPSRecord>& records = dict.mRecords.Value();
342   if (NS_SUCCEEDED(mStatus) && httpsRecord) {
343     dict.mAnswer = true;
344     nsTArray<RefPtr<nsISVCBRecord>> svcbRecords;
345     httpsRecord->GetRecords(svcbRecords);
346 
347     for (const auto& record : svcbRecords) {
348       dom::HTTPSRecord* nextRecord = records.AppendElement(fallible);
349       if (!nextRecord) {
350         return NS_ERROR_OUT_OF_MEMORY;
351       }
352 
353       Unused << record->GetPriority(&nextRecord->mPriority);
354       nsCString name;
355       Unused << record->GetName(name);
356       CopyASCIItoUTF16(name, nextRecord->mTargetName);
357 
358       nsTArray<RefPtr<nsISVCParam>> values;
359       Unused << record->GetValues(values);
360       if (values.IsEmpty()) {
361         continue;
362       }
363 
364       for (const auto& value : values) {
365         uint16_t type;
366         Unused << value->GetType(&type);
367         switch (type) {
368           case SvcParamKeyAlpn: {
369             nextRecord->mAlpn.Construct();
370             nextRecord->mAlpn.Value().mType = type;
371             nsCOMPtr<nsISVCParamAlpn> alpnParam = do_QueryInterface(value);
372             nsTArray<nsCString> alpn;
373             Unused << alpnParam->GetAlpn(alpn);
374             nsAutoCString alpnStr;
375             for (const auto& str : alpn) {
376               alpnStr.Append(str);
377               alpnStr.Append(',');
378             }
379             CopyASCIItoUTF16(Span(alpnStr.BeginReading(), alpnStr.Length() - 1),
380                              nextRecord->mAlpn.Value().mAlpn);
381             break;
382           }
383           case SvcParamKeyNoDefaultAlpn: {
384             nextRecord->mNoDefaultAlpn.Construct();
385             nextRecord->mNoDefaultAlpn.Value().mType = type;
386             break;
387           }
388           case SvcParamKeyPort: {
389             nextRecord->mPort.Construct();
390             nextRecord->mPort.Value().mType = type;
391             nsCOMPtr<nsISVCParamPort> portParam = do_QueryInterface(value);
392             Unused << portParam->GetPort(&nextRecord->mPort.Value().mPort);
393             break;
394           }
395           case SvcParamKeyIpv4Hint: {
396             nextRecord->mIpv4Hint.Construct();
397             nextRecord->mIpv4Hint.Value().mType = type;
398             nsCOMPtr<nsISVCParamIPv4Hint> ipv4Param = do_QueryInterface(value);
399             nsTArray<RefPtr<nsINetAddr>> ipv4Hint;
400             Unused << ipv4Param->GetIpv4Hint(ipv4Hint);
401             if (!ipv4Hint.IsEmpty()) {
402               nextRecord->mIpv4Hint.Value().mAddress.Construct();
403               for (const auto& address : ipv4Hint) {
404                 nsString* nextAddress = nextRecord->mIpv4Hint.Value()
405                                             .mAddress.Value()
406                                             .AppendElement(fallible);
407                 if (!nextAddress) {
408                   return NS_ERROR_OUT_OF_MEMORY;
409                 }
410 
411                 nsCString addressASCII;
412                 Unused << address->GetAddress(addressASCII);
413                 CopyASCIItoUTF16(addressASCII, *nextAddress);
414               }
415             }
416             break;
417           }
418           case SvcParamKeyIpv6Hint: {
419             nextRecord->mIpv6Hint.Construct();
420             nextRecord->mIpv6Hint.Value().mType = type;
421             nsCOMPtr<nsISVCParamIPv6Hint> ipv6Param = do_QueryInterface(value);
422             nsTArray<RefPtr<nsINetAddr>> ipv6Hint;
423             Unused << ipv6Param->GetIpv6Hint(ipv6Hint);
424             if (!ipv6Hint.IsEmpty()) {
425               nextRecord->mIpv6Hint.Value().mAddress.Construct();
426               for (const auto& address : ipv6Hint) {
427                 nsString* nextAddress = nextRecord->mIpv6Hint.Value()
428                                             .mAddress.Value()
429                                             .AppendElement(fallible);
430                 if (!nextAddress) {
431                   return NS_ERROR_OUT_OF_MEMORY;
432                 }
433 
434                 nsCString addressASCII;
435                 Unused << address->GetAddress(addressASCII);
436                 CopyASCIItoUTF16(addressASCII, *nextAddress);
437               }
438             }
439             break;
440           }
441           case SvcParamKeyEchConfig: {
442             nextRecord->mEchConfig.Construct();
443             nextRecord->mEchConfig.Value().mType = type;
444             nsCOMPtr<nsISVCParamEchConfig> echConfigParam =
445                 do_QueryInterface(value);
446             nsCString echConfigStr;
447             Unused << echConfigParam->GetEchconfig(echConfigStr);
448             CStringToHexString(echConfigStr,
449                                nextRecord->mEchConfig.Value().mEchConfig);
450             break;
451           }
452           case SvcParamKeyODoHConfig: {
453             nextRecord->mODoHConfig.Construct();
454             nextRecord->mODoHConfig.Value().mType = type;
455             nsCOMPtr<nsISVCParamODoHConfig> ODoHConfigParam =
456                 do_QueryInterface(value);
457             nsCString ODoHConfigStr;
458             Unused << ODoHConfigParam->GetODoHConfig(ODoHConfigStr);
459             CStringToHexString(ODoHConfigStr,
460                                nextRecord->mODoHConfig.Value().mODoHConfig);
461             break;
462           }
463           default:
464             break;
465         }
466       }
467     }
468   } else {
469     dict.mAnswer = false;
470     GetErrorString(mStatus, dict.mError);
471   }
472 
473   JS::RootedValue val(cx);
474   if (!ToJSValue(cx, dict, &val)) {
475     return NS_ERROR_FAILURE;
476   }
477 
478   this->mCallback->OnDashboardDataAvailable(val);
479 
480   return NS_OK;
481 }
482 
NS_IMPL_ISUPPORTS(Dashboard,nsIDashboard,nsIDashboardEventNotifier)483 NS_IMPL_ISUPPORTS(Dashboard, nsIDashboard, nsIDashboardEventNotifier)
484 
485 Dashboard::Dashboard() { mEnableLogging = false; }
486 
487 NS_IMETHODIMP
RequestSockets(nsINetDashboardCallback * aCallback)488 Dashboard::RequestSockets(nsINetDashboardCallback* aCallback) {
489   RefPtr<SocketData> socketData = new SocketData();
490   socketData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
491       "nsINetDashboardCallback", aCallback, true);
492   socketData->mEventTarget = GetCurrentEventTarget();
493 
494   if (nsIOService::UseSocketProcess()) {
495     if (!gIOService->SocketProcessReady()) {
496       return NS_ERROR_NOT_AVAILABLE;
497     }
498 
499     RefPtr<Dashboard> self(this);
500     SocketProcessParent::GetSingleton()->SendGetSocketData()->Then(
501         GetMainThreadSerialEventTarget(), __func__,
502         [self{std::move(self)},
503          socketData{std::move(socketData)}](SocketDataArgs&& args) {
504           socketData->mData.Assign(args.info());
505           socketData->mTotalSent = args.totalSent();
506           socketData->mTotalRecv = args.totalRecv();
507           socketData->mEventTarget->Dispatch(
508               NewRunnableMethod<RefPtr<SocketData>>(
509                   "net::Dashboard::GetSockets", self, &Dashboard::GetSockets,
510                   socketData),
511               NS_DISPATCH_NORMAL);
512         },
513         [self](const mozilla::ipc::ResponseRejectReason) {});
514     return NS_OK;
515   }
516 
517   gSocketTransportService->Dispatch(
518       NewRunnableMethod<RefPtr<SocketData>>(
519           "net::Dashboard::GetSocketsDispatch", this,
520           &Dashboard::GetSocketsDispatch, socketData),
521       NS_DISPATCH_NORMAL);
522   return NS_OK;
523 }
524 
GetSocketsDispatch(SocketData * aSocketData)525 nsresult Dashboard::GetSocketsDispatch(SocketData* aSocketData) {
526   RefPtr<SocketData> socketData = aSocketData;
527   if (gSocketTransportService) {
528     gSocketTransportService->GetSocketConnections(&socketData->mData);
529     socketData->mTotalSent = gSocketTransportService->GetSentBytes();
530     socketData->mTotalRecv = gSocketTransportService->GetReceivedBytes();
531   }
532   socketData->mEventTarget->Dispatch(
533       NewRunnableMethod<RefPtr<SocketData>>("net::Dashboard::GetSockets", this,
534                                             &Dashboard::GetSockets, socketData),
535       NS_DISPATCH_NORMAL);
536   return NS_OK;
537 }
538 
GetSockets(SocketData * aSocketData)539 nsresult Dashboard::GetSockets(SocketData* aSocketData) {
540   RefPtr<SocketData> socketData = aSocketData;
541   AutoSafeJSContext cx;
542 
543   mozilla::dom::SocketsDict dict;
544   dict.mSockets.Construct();
545   dict.mSent = 0;
546   dict.mReceived = 0;
547 
548   Sequence<mozilla::dom::SocketElement>& sockets = dict.mSockets.Value();
549 
550   uint32_t length = socketData->mData.Length();
551   if (!sockets.SetCapacity(length, fallible)) {
552     JS_ReportOutOfMemory(cx);
553     return NS_ERROR_OUT_OF_MEMORY;
554   }
555 
556   for (uint32_t i = 0; i < socketData->mData.Length(); i++) {
557     dom::SocketElement& mSocket = *sockets.AppendElement(fallible);
558     CopyASCIItoUTF16(socketData->mData[i].host, mSocket.mHost);
559     mSocket.mPort = socketData->mData[i].port;
560     mSocket.mActive = socketData->mData[i].active;
561     mSocket.mTcp = socketData->mData[i].tcp;
562     mSocket.mSent = (double)socketData->mData[i].sent;
563     mSocket.mReceived = (double)socketData->mData[i].received;
564     dict.mSent += socketData->mData[i].sent;
565     dict.mReceived += socketData->mData[i].received;
566   }
567 
568   dict.mSent += socketData->mTotalSent;
569   dict.mReceived += socketData->mTotalRecv;
570   JS::RootedValue val(cx);
571   if (!ToJSValue(cx, dict, &val)) return NS_ERROR_FAILURE;
572   socketData->mCallback->OnDashboardDataAvailable(val);
573 
574   return NS_OK;
575 }
576 
577 NS_IMETHODIMP
RequestHttpConnections(nsINetDashboardCallback * aCallback)578 Dashboard::RequestHttpConnections(nsINetDashboardCallback* aCallback) {
579   RefPtr<HttpData> httpData = new HttpData();
580   httpData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
581       "nsINetDashboardCallback", aCallback, true);
582   httpData->mEventTarget = GetCurrentEventTarget();
583 
584   if (nsIOService::UseSocketProcess()) {
585     if (!gIOService->SocketProcessReady()) {
586       return NS_ERROR_NOT_AVAILABLE;
587     }
588 
589     RefPtr<Dashboard> self(this);
590     SocketProcessParent::GetSingleton()->SendGetHttpConnectionData()->Then(
591         GetMainThreadSerialEventTarget(), __func__,
592         [self{std::move(self)}, httpData](nsTArray<HttpRetParams>&& params) {
593           httpData->mData.Assign(std::move(params));
594           self->GetHttpConnections(httpData);
595           httpData->mEventTarget->Dispatch(
596               NewRunnableMethod<RefPtr<HttpData>>(
597                   "net::Dashboard::GetHttpConnections", self,
598                   &Dashboard::GetHttpConnections, httpData),
599               NS_DISPATCH_NORMAL);
600         },
601         [self](const mozilla::ipc::ResponseRejectReason) {});
602     return NS_OK;
603   }
604 
605   gSocketTransportService->Dispatch(NewRunnableMethod<RefPtr<HttpData>>(
606                                         "net::Dashboard::GetHttpDispatch", this,
607                                         &Dashboard::GetHttpDispatch, httpData),
608                                     NS_DISPATCH_NORMAL);
609   return NS_OK;
610 }
611 
GetHttpDispatch(HttpData * aHttpData)612 nsresult Dashboard::GetHttpDispatch(HttpData* aHttpData) {
613   RefPtr<HttpData> httpData = aHttpData;
614   HttpInfo::GetHttpConnectionData(&httpData->mData);
615   httpData->mEventTarget->Dispatch(
616       NewRunnableMethod<RefPtr<HttpData>>("net::Dashboard::GetHttpConnections",
617                                           this, &Dashboard::GetHttpConnections,
618                                           httpData),
619       NS_DISPATCH_NORMAL);
620   return NS_OK;
621 }
622 
GetHttpConnections(HttpData * aHttpData)623 nsresult Dashboard::GetHttpConnections(HttpData* aHttpData) {
624   RefPtr<HttpData> httpData = aHttpData;
625   AutoSafeJSContext cx;
626 
627   mozilla::dom::HttpConnDict dict;
628   dict.mConnections.Construct();
629 
630   using mozilla::dom::DnsAndSockInfoDict;
631   using mozilla::dom::HttpConnectionElement;
632   using mozilla::dom::HttpConnInfo;
633   Sequence<HttpConnectionElement>& connections = dict.mConnections.Value();
634 
635   uint32_t length = httpData->mData.Length();
636   if (!connections.SetCapacity(length, fallible)) {
637     JS_ReportOutOfMemory(cx);
638     return NS_ERROR_OUT_OF_MEMORY;
639   }
640 
641   for (uint32_t i = 0; i < httpData->mData.Length(); i++) {
642     HttpConnectionElement& connection = *connections.AppendElement(fallible);
643 
644     CopyASCIItoUTF16(httpData->mData[i].host, connection.mHost);
645     connection.mPort = httpData->mData[i].port;
646     CopyASCIItoUTF16(httpData->mData[i].httpVersion, connection.mHttpVersion);
647     connection.mSsl = httpData->mData[i].ssl;
648 
649     connection.mActive.Construct();
650     connection.mIdle.Construct();
651     connection.mDnsAndSocks.Construct();
652 
653     Sequence<HttpConnInfo>& active = connection.mActive.Value();
654     Sequence<HttpConnInfo>& idle = connection.mIdle.Value();
655     Sequence<DnsAndSockInfoDict>& dnsAndSocks = connection.mDnsAndSocks.Value();
656 
657     if (!active.SetCapacity(httpData->mData[i].active.Length(), fallible) ||
658         !idle.SetCapacity(httpData->mData[i].idle.Length(), fallible) ||
659         !dnsAndSocks.SetCapacity(httpData->mData[i].dnsAndSocks.Length(),
660                                  fallible)) {
661       JS_ReportOutOfMemory(cx);
662       return NS_ERROR_OUT_OF_MEMORY;
663     }
664 
665     for (uint32_t j = 0; j < httpData->mData[i].active.Length(); j++) {
666       HttpConnInfo& info = *active.AppendElement(fallible);
667       info.mRtt = httpData->mData[i].active[j].rtt;
668       info.mTtl = httpData->mData[i].active[j].ttl;
669       info.mProtocolVersion = httpData->mData[i].active[j].protocolVersion;
670     }
671 
672     for (uint32_t j = 0; j < httpData->mData[i].idle.Length(); j++) {
673       HttpConnInfo& info = *idle.AppendElement(fallible);
674       info.mRtt = httpData->mData[i].idle[j].rtt;
675       info.mTtl = httpData->mData[i].idle[j].ttl;
676       info.mProtocolVersion = httpData->mData[i].idle[j].protocolVersion;
677     }
678 
679     for (uint32_t j = 0; j < httpData->mData[i].dnsAndSocks.Length(); j++) {
680       DnsAndSockInfoDict& info = *dnsAndSocks.AppendElement(fallible);
681       info.mSpeculative = httpData->mData[i].dnsAndSocks[j].speculative;
682     }
683   }
684 
685   JS::RootedValue val(cx);
686   if (!ToJSValue(cx, dict, &val)) {
687     return NS_ERROR_FAILURE;
688   }
689 
690   httpData->mCallback->OnDashboardDataAvailable(val);
691 
692   return NS_OK;
693 }
694 
695 NS_IMETHODIMP
GetEnableLogging(bool * value)696 Dashboard::GetEnableLogging(bool* value) {
697   *value = mEnableLogging;
698   return NS_OK;
699 }
700 
701 NS_IMETHODIMP
SetEnableLogging(const bool value)702 Dashboard::SetEnableLogging(const bool value) {
703   mEnableLogging = value;
704   return NS_OK;
705 }
706 
707 NS_IMETHODIMP
AddHost(const nsACString & aHost,uint32_t aSerial,bool aEncrypted)708 Dashboard::AddHost(const nsACString& aHost, uint32_t aSerial, bool aEncrypted) {
709   if (mEnableLogging) {
710     mozilla::MutexAutoLock lock(mWs.lock);
711     LogData mData(nsCString(aHost), aSerial, aEncrypted);
712     if (mWs.data.Contains(mData)) {
713       return NS_OK;
714     }
715     // XXX(Bug 1631371) Check if this should use a fallible operation as it
716     // pretended earlier.
717     mWs.data.AppendElement(mData);
718     return NS_OK;
719   }
720   return NS_ERROR_FAILURE;
721 }
722 
723 NS_IMETHODIMP
RemoveHost(const nsACString & aHost,uint32_t aSerial)724 Dashboard::RemoveHost(const nsACString& aHost, uint32_t aSerial) {
725   if (mEnableLogging) {
726     mozilla::MutexAutoLock lock(mWs.lock);
727     int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
728     if (index == -1) return NS_ERROR_FAILURE;
729     mWs.data.RemoveElementAt(index);
730     return NS_OK;
731   }
732   return NS_ERROR_FAILURE;
733 }
734 
735 NS_IMETHODIMP
NewMsgSent(const nsACString & aHost,uint32_t aSerial,uint32_t aLength)736 Dashboard::NewMsgSent(const nsACString& aHost, uint32_t aSerial,
737                       uint32_t aLength) {
738   if (mEnableLogging) {
739     mozilla::MutexAutoLock lock(mWs.lock);
740     int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
741     if (index == -1) return NS_ERROR_FAILURE;
742     mWs.data[index].mMsgSent++;
743     mWs.data[index].mSizeSent += aLength;
744     return NS_OK;
745   }
746   return NS_ERROR_FAILURE;
747 }
748 
749 NS_IMETHODIMP
NewMsgReceived(const nsACString & aHost,uint32_t aSerial,uint32_t aLength)750 Dashboard::NewMsgReceived(const nsACString& aHost, uint32_t aSerial,
751                           uint32_t aLength) {
752   if (mEnableLogging) {
753     mozilla::MutexAutoLock lock(mWs.lock);
754     int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
755     if (index == -1) return NS_ERROR_FAILURE;
756     mWs.data[index].mMsgReceived++;
757     mWs.data[index].mSizeReceived += aLength;
758     return NS_OK;
759   }
760   return NS_ERROR_FAILURE;
761 }
762 
763 NS_IMETHODIMP
RequestWebsocketConnections(nsINetDashboardCallback * aCallback)764 Dashboard::RequestWebsocketConnections(nsINetDashboardCallback* aCallback) {
765   RefPtr<WebSocketRequest> wsRequest = new WebSocketRequest();
766   wsRequest->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
767       "nsINetDashboardCallback", aCallback, true);
768   wsRequest->mEventTarget = GetCurrentEventTarget();
769 
770   wsRequest->mEventTarget->Dispatch(
771       NewRunnableMethod<RefPtr<WebSocketRequest>>(
772           "net::Dashboard::GetWebSocketConnections", this,
773           &Dashboard::GetWebSocketConnections, wsRequest),
774       NS_DISPATCH_NORMAL);
775   return NS_OK;
776 }
777 
GetWebSocketConnections(WebSocketRequest * aWsRequest)778 nsresult Dashboard::GetWebSocketConnections(WebSocketRequest* aWsRequest) {
779   RefPtr<WebSocketRequest> wsRequest = aWsRequest;
780   AutoSafeJSContext cx;
781 
782   mozilla::dom::WebSocketDict dict;
783   dict.mWebsockets.Construct();
784   Sequence<mozilla::dom::WebSocketElement>& websockets =
785       dict.mWebsockets.Value();
786 
787   mozilla::MutexAutoLock lock(mWs.lock);
788   uint32_t length = mWs.data.Length();
789   if (!websockets.SetCapacity(length, fallible)) {
790     JS_ReportOutOfMemory(cx);
791     return NS_ERROR_OUT_OF_MEMORY;
792   }
793 
794   for (uint32_t i = 0; i < mWs.data.Length(); i++) {
795     dom::WebSocketElement& websocket = *websockets.AppendElement(fallible);
796     CopyASCIItoUTF16(mWs.data[i].mHost, websocket.mHostport);
797     websocket.mMsgsent = mWs.data[i].mMsgSent;
798     websocket.mMsgreceived = mWs.data[i].mMsgReceived;
799     websocket.mSentsize = mWs.data[i].mSizeSent;
800     websocket.mReceivedsize = mWs.data[i].mSizeReceived;
801     websocket.mEncrypted = mWs.data[i].mEncrypted;
802   }
803 
804   JS::RootedValue val(cx);
805   if (!ToJSValue(cx, dict, &val)) {
806     return NS_ERROR_FAILURE;
807   }
808   wsRequest->mCallback->OnDashboardDataAvailable(val);
809 
810   return NS_OK;
811 }
812 
813 NS_IMETHODIMP
RequestDNSInfo(nsINetDashboardCallback * aCallback)814 Dashboard::RequestDNSInfo(nsINetDashboardCallback* aCallback) {
815   RefPtr<DnsData> dnsData = new DnsData();
816   dnsData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
817       "nsINetDashboardCallback", aCallback, true);
818 
819   nsresult rv;
820   dnsData->mData.Clear();
821   dnsData->mEventTarget = GetCurrentEventTarget();
822 
823   if (!mDnsService) {
824     mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
825     if (NS_FAILED(rv)) {
826       return rv;
827     }
828   }
829 
830   if (nsIOService::UseSocketProcess()) {
831     if (!gIOService->SocketProcessReady()) {
832       return NS_ERROR_NOT_AVAILABLE;
833     }
834 
835     RefPtr<Dashboard> self(this);
836     SocketProcessParent::GetSingleton()->SendGetDNSCacheEntries()->Then(
837         GetMainThreadSerialEventTarget(), __func__,
838         [self{std::move(self)},
839          dnsData{std::move(dnsData)}](nsTArray<DNSCacheEntries>&& entries) {
840           dnsData->mData.Assign(std::move(entries));
841           dnsData->mEventTarget->Dispatch(
842               NewRunnableMethod<RefPtr<DnsData>>(
843                   "net::Dashboard::GetDNSCacheEntries", self,
844                   &Dashboard::GetDNSCacheEntries, dnsData),
845               NS_DISPATCH_NORMAL);
846         },
847         [self](const mozilla::ipc::ResponseRejectReason) {});
848     return NS_OK;
849   }
850 
851   gSocketTransportService->Dispatch(
852       NewRunnableMethod<RefPtr<DnsData>>("net::Dashboard::GetDnsInfoDispatch",
853                                          this, &Dashboard::GetDnsInfoDispatch,
854                                          dnsData),
855       NS_DISPATCH_NORMAL);
856   return NS_OK;
857 }
858 
GetDnsInfoDispatch(DnsData * aDnsData)859 nsresult Dashboard::GetDnsInfoDispatch(DnsData* aDnsData) {
860   RefPtr<DnsData> dnsData = aDnsData;
861   if (mDnsService) {
862     mDnsService->GetDNSCacheEntries(&dnsData->mData);
863   }
864   dnsData->mEventTarget->Dispatch(
865       NewRunnableMethod<RefPtr<DnsData>>("net::Dashboard::GetDNSCacheEntries",
866                                          this, &Dashboard::GetDNSCacheEntries,
867                                          dnsData),
868       NS_DISPATCH_NORMAL);
869   return NS_OK;
870 }
871 
GetDNSCacheEntries(DnsData * dnsData)872 nsresult Dashboard::GetDNSCacheEntries(DnsData* dnsData) {
873   AutoSafeJSContext cx;
874 
875   mozilla::dom::DNSCacheDict dict;
876   dict.mEntries.Construct();
877   Sequence<mozilla::dom::DnsCacheEntry>& entries = dict.mEntries.Value();
878 
879   uint32_t length = dnsData->mData.Length();
880   if (!entries.SetCapacity(length, fallible)) {
881     JS_ReportOutOfMemory(cx);
882     return NS_ERROR_OUT_OF_MEMORY;
883   }
884 
885   for (uint32_t i = 0; i < dnsData->mData.Length(); i++) {
886     dom::DnsCacheEntry& entry = *entries.AppendElement(fallible);
887     entry.mHostaddr.Construct();
888 
889     Sequence<nsString>& addrs = entry.mHostaddr.Value();
890     if (!addrs.SetCapacity(dnsData->mData[i].hostaddr.Length(), fallible)) {
891       JS_ReportOutOfMemory(cx);
892       return NS_ERROR_OUT_OF_MEMORY;
893     }
894 
895     CopyASCIItoUTF16(dnsData->mData[i].hostname, entry.mHostname);
896     entry.mExpiration = dnsData->mData[i].expiration;
897     entry.mTrr = dnsData->mData[i].TRR;
898 
899     for (uint32_t j = 0; j < dnsData->mData[i].hostaddr.Length(); j++) {
900       nsString* addr = addrs.AppendElement(fallible);
901       if (!addr) {
902         JS_ReportOutOfMemory(cx);
903         return NS_ERROR_OUT_OF_MEMORY;
904       }
905       CopyASCIItoUTF16(dnsData->mData[i].hostaddr[j], *addr);
906     }
907 
908     if (dnsData->mData[i].family == PR_AF_INET6) {
909       entry.mFamily.AssignLiteral(u"ipv6");
910     } else {
911       entry.mFamily.AssignLiteral(u"ipv4");
912     }
913 
914     entry.mOriginAttributesSuffix =
915         NS_ConvertUTF8toUTF16(dnsData->mData[i].originAttributesSuffix);
916   }
917 
918   JS::RootedValue val(cx);
919   if (!ToJSValue(cx, dict, &val)) {
920     return NS_ERROR_FAILURE;
921   }
922   dnsData->mCallback->OnDashboardDataAvailable(val);
923 
924   return NS_OK;
925 }
926 
927 NS_IMETHODIMP
RequestDNSLookup(const nsACString & aHost,nsINetDashboardCallback * aCallback)928 Dashboard::RequestDNSLookup(const nsACString& aHost,
929                             nsINetDashboardCallback* aCallback) {
930   nsresult rv;
931 
932   if (!mDnsService) {
933     mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
934     if (NS_FAILED(rv)) {
935       return rv;
936     }
937   }
938 
939   RefPtr<LookupHelper> helper = new LookupHelper();
940   helper->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
941       "nsINetDashboardCallback", aCallback, true);
942   helper->mEventTarget = GetCurrentEventTarget();
943   OriginAttributes attrs;
944   rv = mDnsService->AsyncResolveNative(
945       aHost, nsIDNSService::RESOLVE_TYPE_DEFAULT, 0, nullptr, helper.get(),
946       NS_GetCurrentThread(), attrs, getter_AddRefs(helper->mCancel));
947   return rv;
948 }
949 
950 NS_IMETHODIMP
RequestDNSHTTPSRRLookup(const nsACString & aHost,nsINetDashboardCallback * aCallback)951 Dashboard::RequestDNSHTTPSRRLookup(const nsACString& aHost,
952                                    nsINetDashboardCallback* aCallback) {
953   nsresult rv;
954 
955   if (!mDnsService) {
956     mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
957     if (NS_FAILED(rv)) {
958       return rv;
959     }
960   }
961 
962   RefPtr<LookupHelper> helper = new LookupHelper();
963   helper->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
964       "nsINetDashboardCallback", aCallback, true);
965   helper->mEventTarget = GetCurrentEventTarget();
966   OriginAttributes attrs;
967   rv = mDnsService->AsyncResolveNative(
968       aHost, nsIDNSService::RESOLVE_TYPE_HTTPSSVC, 0, nullptr, helper.get(),
969       NS_GetCurrentThread(), attrs, getter_AddRefs(helper->mCancel));
970   return rv;
971 }
972 
973 NS_IMETHODIMP
RequestRcwnStats(nsINetDashboardCallback * aCallback)974 Dashboard::RequestRcwnStats(nsINetDashboardCallback* aCallback) {
975   RefPtr<RcwnData> rcwnData = new RcwnData();
976   rcwnData->mEventTarget = GetCurrentEventTarget();
977   rcwnData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
978       "nsINetDashboardCallback", aCallback, true);
979 
980   return rcwnData->mEventTarget->Dispatch(
981       NewRunnableMethod<RefPtr<RcwnData>>("net::Dashboard::GetRcwnData", this,
982                                           &Dashboard::GetRcwnData, rcwnData),
983       NS_DISPATCH_NORMAL);
984 }
985 
GetRcwnData(RcwnData * aData)986 nsresult Dashboard::GetRcwnData(RcwnData* aData) {
987   AutoSafeJSContext cx;
988   mozilla::dom::RcwnStatus dict;
989 
990   dict.mTotalNetworkRequests = gIOService->GetTotalRequestNumber();
991   dict.mRcwnCacheWonCount = gIOService->GetCacheWonRequestNumber();
992   dict.mRcwnNetWonCount = gIOService->GetNetWonRequestNumber();
993 
994   uint32_t cacheSlow, cacheNotSlow;
995   CacheFileUtils::CachePerfStats::GetSlowStats(&cacheSlow, &cacheNotSlow);
996   dict.mCacheSlowCount = cacheSlow;
997   dict.mCacheNotSlowCount = cacheNotSlow;
998 
999   dict.mPerfStats.Construct();
1000   Sequence<mozilla::dom::RcwnPerfStats>& perfStats = dict.mPerfStats.Value();
1001   uint32_t length = CacheFileUtils::CachePerfStats::LAST;
1002   if (!perfStats.SetCapacity(length, fallible)) {
1003     JS_ReportOutOfMemory(cx);
1004     return NS_ERROR_OUT_OF_MEMORY;
1005   }
1006 
1007   for (uint32_t i = 0; i < length; i++) {
1008     CacheFileUtils::CachePerfStats::EDataType perfType =
1009         static_cast<CacheFileUtils::CachePerfStats::EDataType>(i);
1010     dom::RcwnPerfStats& elem = *perfStats.AppendElement(fallible);
1011     elem.mAvgShort =
1012         CacheFileUtils::CachePerfStats::GetAverage(perfType, false);
1013     elem.mAvgLong = CacheFileUtils::CachePerfStats::GetAverage(perfType, true);
1014     elem.mStddevLong =
1015         CacheFileUtils::CachePerfStats::GetStdDev(perfType, true);
1016   }
1017 
1018   JS::RootedValue val(cx);
1019   if (!ToJSValue(cx, dict, &val)) {
1020     return NS_ERROR_FAILURE;
1021   }
1022 
1023   aData->mCallback->OnDashboardDataAvailable(val);
1024 
1025   return NS_OK;
1026 }
1027 
SetHTTPProtocolVersion(HttpVersion pv)1028 void HttpConnInfo::SetHTTPProtocolVersion(HttpVersion pv) {
1029   switch (pv) {
1030     case HttpVersion::v0_9:
1031       protocolVersion.AssignLiteral(u"http/0.9");
1032       break;
1033     case HttpVersion::v1_0:
1034       protocolVersion.AssignLiteral(u"http/1.0");
1035       break;
1036     case HttpVersion::v1_1:
1037       protocolVersion.AssignLiteral(u"http/1.1");
1038       break;
1039     case HttpVersion::v2_0:
1040       protocolVersion.AssignLiteral(u"http/2");
1041       break;
1042     case HttpVersion::v3_0:
1043       protocolVersion.AssignLiteral(u"http/3");
1044       break;
1045     default:
1046       protocolVersion.AssignLiteral(u"unknown protocol version");
1047   }
1048 }
1049 
1050 NS_IMETHODIMP
GetLogPath(nsACString & aLogPath)1051 Dashboard::GetLogPath(nsACString& aLogPath) {
1052   aLogPath.SetLength(2048);
1053   uint32_t len = LogModule::GetLogFile(aLogPath.BeginWriting(), 2048);
1054   aLogPath.SetLength(len);
1055   return NS_OK;
1056 }
1057 
1058 NS_IMETHODIMP
RequestConnection(const nsACString & aHost,uint32_t aPort,const char * aProtocol,uint32_t aTimeout,nsINetDashboardCallback * aCallback)1059 Dashboard::RequestConnection(const nsACString& aHost, uint32_t aPort,
1060                              const char* aProtocol, uint32_t aTimeout,
1061                              nsINetDashboardCallback* aCallback) {
1062   nsresult rv;
1063   RefPtr<ConnectionData> connectionData = new ConnectionData(this);
1064   connectionData->mHost = aHost;
1065   connectionData->mPort = aPort;
1066   connectionData->mProtocol = aProtocol;
1067   connectionData->mTimeout = aTimeout;
1068 
1069   connectionData->mCallback =
1070       new nsMainThreadPtrHolder<nsINetDashboardCallback>(
1071           "nsINetDashboardCallback", aCallback, true);
1072   connectionData->mEventTarget = GetCurrentEventTarget();
1073 
1074   rv = TestNewConnection(connectionData);
1075   if (NS_FAILED(rv)) {
1076     mozilla::net::GetErrorString(rv, connectionData->mStatus);
1077     connectionData->mEventTarget->Dispatch(
1078         NewRunnableMethod<RefPtr<ConnectionData>>(
1079             "net::Dashboard::GetConnectionStatus", this,
1080             &Dashboard::GetConnectionStatus, connectionData),
1081         NS_DISPATCH_NORMAL);
1082     return rv;
1083   }
1084 
1085   return NS_OK;
1086 }
1087 
GetConnectionStatus(ConnectionData * aConnectionData)1088 nsresult Dashboard::GetConnectionStatus(ConnectionData* aConnectionData) {
1089   RefPtr<ConnectionData> connectionData = aConnectionData;
1090   AutoSafeJSContext cx;
1091 
1092   mozilla::dom::ConnStatusDict dict;
1093   dict.mStatus = connectionData->mStatus;
1094 
1095   JS::RootedValue val(cx);
1096   if (!ToJSValue(cx, dict, &val)) return NS_ERROR_FAILURE;
1097 
1098   connectionData->mCallback->OnDashboardDataAvailable(val);
1099 
1100   return NS_OK;
1101 }
1102 
TestNewConnection(ConnectionData * aConnectionData)1103 nsresult Dashboard::TestNewConnection(ConnectionData* aConnectionData) {
1104   RefPtr<ConnectionData> connectionData = aConnectionData;
1105 
1106   nsresult rv;
1107   if (!connectionData->mHost.Length() ||
1108       !net_IsValidHostName(connectionData->mHost)) {
1109     return NS_ERROR_UNKNOWN_HOST;
1110   }
1111 
1112   if (connectionData->mProtocol.EqualsLiteral("ssl")) {
1113     AutoTArray<nsCString, 1> socketTypes = {connectionData->mProtocol};
1114     rv = gSocketTransportService->CreateTransport(
1115         socketTypes, connectionData->mHost, connectionData->mPort, nullptr,
1116         nullptr, getter_AddRefs(connectionData->mSocket));
1117   } else {
1118     rv = gSocketTransportService->CreateTransport(
1119         nsTArray<nsCString>(), connectionData->mHost, connectionData->mPort,
1120         nullptr, nullptr, getter_AddRefs(connectionData->mSocket));
1121   }
1122   if (NS_FAILED(rv)) {
1123     return rv;
1124   }
1125 
1126   rv = connectionData->mSocket->SetEventSink(connectionData,
1127                                              GetCurrentEventTarget());
1128   if (NS_FAILED(rv)) {
1129     return rv;
1130   }
1131 
1132   rv = connectionData->mSocket->OpenInputStream(
1133       nsITransport::OPEN_BLOCKING, 0, 0,
1134       getter_AddRefs(connectionData->mStreamIn));
1135   if (NS_FAILED(rv)) {
1136     return rv;
1137   }
1138 
1139   connectionData->StartTimer(connectionData->mTimeout);
1140 
1141   return rv;
1142 }
1143 
1144 using ErrorEntry = struct {
1145   nsresult key;
1146   const char* error;
1147 };
1148 
1149 #undef ERROR
1150 #define ERROR(key, val) \
1151   { key, #key }
1152 
1153 ErrorEntry socketTransportStatuses[] = {
1154     ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)),
1155     ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)),
1156     ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)),
1157     ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)),
1158     ERROR(NS_NET_STATUS_TLS_HANDSHAKE_STARTING, FAILURE(12)),
1159     ERROR(NS_NET_STATUS_TLS_HANDSHAKE_ENDED, FAILURE(13)),
1160     ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)),
1161     ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)),
1162     ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)),
1163 };
1164 #undef ERROR
1165 
GetErrorString(nsresult rv,nsAString & errorString)1166 static void GetErrorString(nsresult rv, nsAString& errorString) {
1167   for (auto& socketTransportStatus : socketTransportStatuses) {
1168     if (socketTransportStatus.key == rv) {
1169       errorString.AssignASCII(socketTransportStatus.error);
1170       return;
1171     }
1172   }
1173   nsAutoCString errorCString;
1174   mozilla::GetErrorName(rv, errorCString);
1175   CopyUTF8toUTF16(errorCString, errorString);
1176 }
1177 
1178 }  // namespace net
1179 }  // namespace mozilla
1180