1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set ts=4 sw=4 cindent et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/DebugOnly.h"
9 
10 #include "nsIOService.h"
11 #include "nsIDOMNode.h"
12 #include "nsIProtocolHandler.h"
13 #include "nsIFileProtocolHandler.h"
14 #include "nscore.h"
15 #include "nsIURI.h"
16 #include "prprf.h"
17 #include "nsIErrorService.h"
18 #include "netCore.h"
19 #include "nsIObserverService.h"
20 #include "nsIPrefService.h"
21 #include "nsXPCOM.h"
22 #include "nsIProxiedProtocolHandler.h"
23 #include "nsIProxyInfo.h"
24 #include "nsEscape.h"
25 #include "nsNetUtil.h"
26 #include "nsNetCID.h"
27 #include "nsCRT.h"
28 #include "nsSecCheckWrapChannel.h"
29 #include "nsSimpleNestedURI.h"
30 #include "nsTArray.h"
31 #include "nsIConsoleService.h"
32 #include "nsIUploadChannel2.h"
33 #include "nsXULAppAPI.h"
34 #include "nsIScriptError.h"
35 #include "nsIScriptSecurityManager.h"
36 #include "nsIProtocolProxyCallback.h"
37 #include "nsICancelable.h"
38 #include "nsINetworkLinkService.h"
39 #include "nsPISocketTransportService.h"
40 #include "nsAsyncRedirectVerifyHelper.h"
41 #include "nsURLHelper.h"
42 #include "nsPIDNSService.h"
43 #include "nsIProtocolProxyService2.h"
44 #include "MainThreadUtils.h"
45 #include "nsINode.h"
46 #include "nsIWidget.h"
47 #include "nsThreadUtils.h"
48 #include "mozilla/LoadInfo.h"
49 #include "mozilla/net/NeckoCommon.h"
50 #include "mozilla/Services.h"
51 #include "mozilla/Telemetry.h"
52 #include "mozilla/net/DNS.h"
53 #include "mozilla/ipc/URIUtils.h"
54 #include "mozilla/net/NeckoChild.h"
55 #include "mozilla/dom/ClientInfo.h"
56 #include "mozilla/dom/ContentParent.h"
57 #include "mozilla/dom/ServiceWorkerDescriptor.h"
58 #include "mozilla/net/CaptivePortalService.h"
59 #include "mozilla/Unused.h"
60 #include "ReferrerPolicy.h"
61 #include "nsContentSecurityManager.h"
62 #include "nsContentUtils.h"
63 #include "xpcpublic.h"
64 
65 namespace mozilla {
66 namespace net {
67 
68 using mozilla::Maybe;
69 using mozilla::dom::ClientInfo;
70 using mozilla::dom::ServiceWorkerDescriptor;
71 
72 #define PORT_PREF_PREFIX "network.security.ports."
73 #define PORT_PREF(x) PORT_PREF_PREFIX x
74 #define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status"
75 #define OFFLINE_MIRRORS_CONNECTIVITY "network.offline-mirrors-connectivity"
76 
77 // Nb: these have been misnomers since bug 715770 removed the buffer cache.
78 // "network.segment.count" and "network.segment.size" would be better names,
79 // but the old names are still used to preserve backward compatibility.
80 #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count"
81 #define NECKO_BUFFER_CACHE_SIZE_PREF "network.buffer.cache.size"
82 #define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
83 #define NETWORK_CAPTIVE_PORTAL_PREF "network.captive-portal-service.enabled"
84 
85 #define MAX_RECURSION_COUNT 50
86 
87 nsIOService *gIOService;
88 static bool gHasWarnedUploadChannel2;
89 static bool gCaptivePortalEnabled = false;
90 static LazyLogModule gIOServiceLog("nsIOService");
91 #undef LOG
92 #define LOG(args) MOZ_LOG(gIOServiceLog, LogLevel::Debug, args)
93 
94 // A general port blacklist.  Connections to these ports will not be allowed
95 // unless the protocol overrides.
96 //
97 // TODO: I am sure that there are more ports to be added.
98 //       This cut is based on the classic mozilla codebase
99 
100 int16_t gBadPortList[] = {
101     1,     // tcpmux
102     7,     // echo
103     9,     // discard
104     11,    // systat
105     13,    // daytime
106     15,    // netstat
107     17,    // qotd
108     19,    // chargen
109     20,    // ftp-data
110     21,    // ftp-cntl
111     22,    // ssh
112     23,    // telnet
113     25,    // smtp
114     37,    // time
115     42,    // name
116     43,    // nicname
117     53,    // domain
118     77,    // priv-rjs
119     79,    // finger
120     87,    // ttylink
121     95,    // supdup
122     101,   // hostriame
123     102,   // iso-tsap
124     103,   // gppitnp
125     104,   // acr-nema
126     109,   // pop2
127     110,   // pop3
128     111,   // sunrpc
129     113,   // auth
130     115,   // sftp
131     117,   // uucp-path
132     119,   // nntp
133     123,   // NTP
134     135,   // loc-srv / epmap
135     139,   // netbios
136     143,   // imap2
137     179,   // BGP
138     389,   // ldap
139     465,   // smtp+ssl
140     512,   // print / exec
141     513,   // login
142     514,   // shell
143     515,   // printer
144     526,   // tempo
145     530,   // courier
146     531,   // Chat
147     532,   // netnews
148     540,   // uucp
149     556,   // remotefs
150     563,   // nntp+ssl
151     587,   //
152     601,   //
153     636,   // ldap+ssl
154     993,   // imap+ssl
155     995,   // pop3+ssl
156     2049,  // nfs
157     3659,  // apple-sasl / PasswordServer
158     4045,  // lockd
159     6000,  // x11
160     6665,  // Alternate IRC [Apple addition]
161     6666,  // Alternate IRC [Apple addition]
162     6667,  // Standard IRC [Apple addition]
163     6668,  // Alternate IRC [Apple addition]
164     6669,  // Alternate IRC [Apple addition]
165     0,     // This MUST be zero so that we can populating the array
166 };
167 
168 static const char kProfileChangeNetTeardownTopic[] =
169     "profile-change-net-teardown";
170 static const char kProfileChangeNetRestoreTopic[] =
171     "profile-change-net-restore";
172 static const char kProfileDoChange[] = "profile-do-change";
173 
174 // Necko buffer defaults
175 uint32_t nsIOService::gDefaultSegmentSize = 4096;
176 uint32_t nsIOService::gDefaultSegmentCount = 24;
177 
178 bool nsIOService::sIsDataURIUniqueOpaqueOrigin = false;
179 bool nsIOService::sBlockToplevelDataUriNavigations = false;
180 
181 ////////////////////////////////////////////////////////////////////////////////
182 
nsIOService()183 nsIOService::nsIOService()
184     : mOffline(true),
185       mOfflineForProfileChange(false),
186       mManageLinkStatus(false),
187       mConnectivity(true),
188       mOfflineMirrorsConnectivity(true),
189       mSettingOffline(false),
190       mSetOfflineValue(false),
191       mShutdown(false),
192       mHttpHandlerAlreadyShutingDown(false),
193       mNetworkLinkServiceInitialized(false),
194       mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY),
195       mNetworkNotifyChanged(true),
196       mTotalRequests(0),
197       mCacheWon(0),
198       mNetWon(0),
199       mLastOfflineStateChange(PR_IntervalNow()),
200       mLastConnectivityChange(PR_IntervalNow()),
201       mLastNetworkLinkChange(PR_IntervalNow()),
202       mNetTearingDownStarted(0) {}
203 
Init()204 nsresult nsIOService::Init() {
205   nsresult rv;
206 
207   // We need to get references to the DNS service so that we can shut it
208   // down later. If we wait until the nsIOService is being shut down,
209   // GetService will fail at that point.
210 
211   mDNSService = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
212   if (NS_FAILED(rv)) {
213     NS_WARNING("failed to get DNS service");
214     return rv;
215   }
216 
217   // XXX hack until xpidl supports error info directly (bug 13423)
218   nsCOMPtr<nsIErrorService> errorService =
219       do_GetService(NS_ERRORSERVICE_CONTRACTID);
220   if (errorService) {
221     errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_NETWORK,
222                                             NECKO_MSGS_URL);
223   } else
224     NS_WARNING("failed to get error service");
225 
226   InitializeCaptivePortalService();
227 
228   // setup our bad port list stuff
229   for (int i = 0; gBadPortList[i]; i++)
230     mRestrictedPortList.AppendElement(gBadPortList[i]);
231 
232   // Further modifications to the port list come from prefs
233   nsCOMPtr<nsIPrefBranch> prefBranch;
234   GetPrefBranch(getter_AddRefs(prefBranch));
235   if (prefBranch) {
236     prefBranch->AddObserver(PORT_PREF_PREFIX, this, true);
237     prefBranch->AddObserver(MANAGE_OFFLINE_STATUS_PREF, this, true);
238     prefBranch->AddObserver(NECKO_BUFFER_CACHE_COUNT_PREF, this, true);
239     prefBranch->AddObserver(NECKO_BUFFER_CACHE_SIZE_PREF, this, true);
240     prefBranch->AddObserver(NETWORK_NOTIFY_CHANGED_PREF, this, true);
241     prefBranch->AddObserver(NETWORK_CAPTIVE_PORTAL_PREF, this, true);
242     PrefsChanged(prefBranch);
243   }
244 
245   // Register for profile change notifications
246   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
247   if (observerService) {
248     observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
249     observerService->AddObserver(this, kProfileChangeNetRestoreTopic, true);
250     observerService->AddObserver(this, kProfileDoChange, true);
251     observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
252     observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
253     observerService->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
254   } else
255     NS_WARNING("failed to get observer service");
256 
257   Preferences::AddBoolVarCache(&sIsDataURIUniqueOpaqueOrigin,
258                                "security.data_uri.unique_opaque_origin", false);
259   Preferences::AddBoolVarCache(
260       &sBlockToplevelDataUriNavigations,
261       "security.data_uri.block_toplevel_data_uri_navigations", false);
262   Preferences::AddBoolVarCache(&mOfflineMirrorsConnectivity,
263                                OFFLINE_MIRRORS_CONNECTIVITY, true);
264 
265   gIOService = this;
266 
267   InitializeNetworkLinkService();
268   InitializeProtocolProxyService();
269 
270   SetOffline(false);
271 
272   return NS_OK;
273 }
274 
~nsIOService()275 nsIOService::~nsIOService() {
276   if (gIOService) {
277     MOZ_ASSERT(gIOService == this);
278     gIOService = nullptr;
279   }
280 }
281 
InitializeCaptivePortalService()282 nsresult nsIOService::InitializeCaptivePortalService() {
283   if (XRE_GetProcessType() != GeckoProcessType_Default) {
284     // We only initalize a captive portal service in the main process
285     return NS_OK;
286   }
287 
288   mCaptivePortalService = do_GetService(NS_CAPTIVEPORTAL_CID);
289   if (mCaptivePortalService) {
290     return static_cast<CaptivePortalService *>(mCaptivePortalService.get())
291         ->Initialize();
292   }
293 
294   return NS_OK;
295 }
296 
InitializeSocketTransportService()297 nsresult nsIOService::InitializeSocketTransportService() {
298   nsresult rv = NS_OK;
299 
300   if (!mSocketTransportService) {
301     mSocketTransportService =
302         do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
303     if (NS_FAILED(rv)) {
304       NS_WARNING("failed to get socket transport service");
305     }
306   }
307 
308   if (mSocketTransportService) {
309     rv = mSocketTransportService->Init();
310     NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
311     mSocketTransportService->SetOffline(false);
312   }
313 
314   return rv;
315 }
316 
InitializeNetworkLinkService()317 nsresult nsIOService::InitializeNetworkLinkService() {
318   nsresult rv = NS_OK;
319 
320   if (mNetworkLinkServiceInitialized) return rv;
321 
322   if (!NS_IsMainThread()) {
323     NS_WARNING("Network link service should be created on main thread");
324     return NS_ERROR_FAILURE;
325   }
326 
327   // go into managed mode if we can, and chrome process
328   if (XRE_IsParentProcess()) {
329     mNetworkLinkService =
330         do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
331   }
332 
333   if (mNetworkLinkService) {
334     mNetworkLinkServiceInitialized = true;
335   }
336 
337   // After initializing the networkLinkService, query the connectivity state
338   OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN);
339 
340   return rv;
341 }
342 
InitializeProtocolProxyService()343 nsresult nsIOService::InitializeProtocolProxyService() {
344   nsresult rv = NS_OK;
345 
346   if (XRE_IsParentProcess()) {
347     // for early-initialization
348     Unused << do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
349   }
350 
351   return rv;
352 }
353 
GetInstance()354 already_AddRefed<nsIOService> nsIOService::GetInstance() {
355   if (!gIOService) {
356     RefPtr<nsIOService> ios = new nsIOService();
357     if (NS_SUCCEEDED(ios->Init())) {
358       MOZ_ASSERT(gIOService == ios.get());
359       return ios.forget();
360     }
361   }
362   return do_AddRef(gIOService);
363 }
364 
NS_IMPL_ISUPPORTS(nsIOService,nsIIOService,nsINetUtil,nsISpeculativeConnect,nsIObserver,nsIIOServiceInternal,nsISupportsWeakReference)365 NS_IMPL_ISUPPORTS(nsIOService, nsIIOService, nsINetUtil, nsISpeculativeConnect,
366                   nsIObserver, nsIIOServiceInternal, nsISupportsWeakReference)
367 
368 ////////////////////////////////////////////////////////////////////////////////
369 
370 nsresult nsIOService::RecheckCaptivePortal() {
371   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
372   if (!mCaptivePortalService) {
373     return NS_OK;
374   }
375   nsCOMPtr<nsIRunnable> task = NewRunnableMethod(
376       "nsIOService::RecheckCaptivePortal", mCaptivePortalService,
377       &nsICaptivePortalService::RecheckCaptivePortal);
378   return NS_DispatchToMainThread(task);
379 }
380 
RecheckCaptivePortalIfLocalRedirect(nsIChannel * newChan)381 nsresult nsIOService::RecheckCaptivePortalIfLocalRedirect(nsIChannel *newChan) {
382   nsresult rv;
383 
384   if (!mCaptivePortalService) {
385     return NS_OK;
386   }
387 
388   nsCOMPtr<nsIURI> uri;
389   rv = newChan->GetURI(getter_AddRefs(uri));
390   if (NS_FAILED(rv)) {
391     return rv;
392   }
393 
394   nsCString host;
395   rv = uri->GetHost(host);
396   if (NS_FAILED(rv)) {
397     return rv;
398   }
399 
400   PRNetAddr prAddr;
401   if (PR_StringToNetAddr(host.BeginReading(), &prAddr) != PR_SUCCESS) {
402     // The redirect wasn't to an IP literal, so there's probably no need
403     // to trigger the captive portal detection right now. It can wait.
404     return NS_OK;
405   }
406 
407   NetAddr netAddr;
408   PRNetAddrToNetAddr(&prAddr, &netAddr);
409   if (IsIPAddrLocal(&netAddr)) {
410     // Redirects to local IP addresses are probably captive portals
411     RecheckCaptivePortal();
412   }
413 
414   return NS_OK;
415 }
416 
AsyncOnChannelRedirect(nsIChannel * oldChan,nsIChannel * newChan,uint32_t flags,nsAsyncRedirectVerifyHelper * helper)417 nsresult nsIOService::AsyncOnChannelRedirect(
418     nsIChannel *oldChan, nsIChannel *newChan, uint32_t flags,
419     nsAsyncRedirectVerifyHelper *helper) {
420   // If a redirect to a local network address occurs, then chances are we
421   // are in a captive portal, so we trigger a recheck.
422   RecheckCaptivePortalIfLocalRedirect(newChan);
423 
424   // This is silly. I wish there was a simpler way to get at the global
425   // reference of the contentSecurityManager. But it lives in the XPCOM
426   // service registry.
427   nsCOMPtr<nsIChannelEventSink> sink =
428       do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
429   if (sink) {
430     nsresult rv =
431         helper->DelegateOnChannelRedirect(sink, oldChan, newChan, flags);
432     if (NS_FAILED(rv)) return rv;
433   }
434 
435   // Finally, our category
436   nsCOMArray<nsIChannelEventSink> entries;
437   mChannelEventSinks.GetEntries(entries);
438   int32_t len = entries.Count();
439   for (int32_t i = 0; i < len; ++i) {
440     nsresult rv =
441         helper->DelegateOnChannelRedirect(entries[i], oldChan, newChan, flags);
442     if (NS_FAILED(rv)) return rv;
443   }
444   return NS_OK;
445 }
446 
CacheProtocolHandler(const char * scheme,nsIProtocolHandler * handler)447 nsresult nsIOService::CacheProtocolHandler(const char *scheme,
448                                            nsIProtocolHandler *handler) {
449   MOZ_ASSERT(NS_IsMainThread());
450 
451   for (unsigned int i = 0; i < NS_N(gScheme); i++) {
452     if (!nsCRT::strcasecmp(scheme, gScheme[i])) {
453       nsresult rv;
454       NS_ASSERTION(!mWeakHandler[i], "Protocol handler already cached");
455       // Make sure the handler supports weak references.
456       nsCOMPtr<nsISupportsWeakReference> factoryPtr =
457           do_QueryInterface(handler, &rv);
458       if (!factoryPtr) {
459       // Don't cache handlers that don't support weak reference as
460       // there is real danger of a circular reference.
461 #ifdef DEBUG_dp
462         printf(
463             "DEBUG: %s protcol handler doesn't support weak ref. Not cached.\n",
464             scheme);
465 #endif /* DEBUG_dp */
466         return NS_ERROR_FAILURE;
467       }
468       mWeakHandler[i] = do_GetWeakReference(handler);
469       return NS_OK;
470     }
471   }
472   return NS_ERROR_FAILURE;
473 }
474 
GetCachedProtocolHandler(const char * scheme,nsIProtocolHandler ** result,uint32_t start,uint32_t end)475 nsresult nsIOService::GetCachedProtocolHandler(const char *scheme,
476                                                nsIProtocolHandler **result,
477                                                uint32_t start, uint32_t end) {
478   MOZ_ASSERT(NS_IsMainThread());
479 
480   uint32_t len = end - start - 1;
481   for (unsigned int i = 0; i < NS_N(gScheme); i++) {
482     if (!mWeakHandler[i]) continue;
483 
484     // handle unterminated strings
485     // start is inclusive, end is exclusive, len = end - start - 1
486     if (end ? (!nsCRT::strncasecmp(scheme + start, gScheme[i], len) &&
487                gScheme[i][len] == '\0')
488             : (!nsCRT::strcasecmp(scheme, gScheme[i]))) {
489       return CallQueryReferent(mWeakHandler[i].get(), result);
490     }
491   }
492   return NS_ERROR_FAILURE;
493 }
494 
UsesExternalProtocolHandler(const char * aScheme)495 static bool UsesExternalProtocolHandler(const char *aScheme) {
496   if (NS_LITERAL_CSTRING("file").Equals(aScheme) ||
497       NS_LITERAL_CSTRING("chrome").Equals(aScheme) ||
498       NS_LITERAL_CSTRING("resource").Equals(aScheme)) {
499     // Don't allow file:, chrome: or resource: URIs to be handled with
500     // nsExternalProtocolHandler, since internally we rely on being able to
501     // use and read from these URIs.
502     return false;
503   }
504 
505   nsAutoCString pref("network.protocol-handler.external.");
506   pref += aScheme;
507 
508   return Preferences::GetBool(pref.get(), false);
509 }
510 
511 NS_IMETHODIMP
GetProtocolHandler(const char * scheme,nsIProtocolHandler ** result)512 nsIOService::GetProtocolHandler(const char *scheme,
513                                 nsIProtocolHandler **result) {
514   nsresult rv;
515 
516   NS_ENSURE_ARG_POINTER(scheme);
517   // XXX we may want to speed this up by introducing our own protocol
518   // scheme -> protocol handler mapping, avoiding the string manipulation
519   // and service manager stuff
520 
521   rv = GetCachedProtocolHandler(scheme, result);
522   if (NS_SUCCEEDED(rv)) return rv;
523 
524   if (!UsesExternalProtocolHandler(scheme)) {
525     nsAutoCString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
526     contractID += scheme;
527     ToLowerCase(contractID);
528 
529     rv = CallGetService(contractID.get(), result);
530     if (NS_SUCCEEDED(rv)) {
531       CacheProtocolHandler(scheme, *result);
532       return rv;
533     }
534 
535 #ifdef MOZ_WIDGET_GTK
536     // check to see whether GVFS can handle this URI scheme.  if it can
537     // create a nsIURI for the "scheme:", then we assume it has support for
538     // the requested protocol.  otherwise, we failover to using the default
539     // protocol handler.
540 
541     rv =
542         CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-gio", result);
543     if (NS_SUCCEEDED(rv)) {
544       nsAutoCString spec(scheme);
545       spec.Append(':');
546 
547       nsIURI *uri;
548       rv = (*result)->NewURI(spec, nullptr, nullptr, &uri);
549       if (NS_SUCCEEDED(rv)) {
550         NS_RELEASE(uri);
551         return rv;
552       }
553 
554       NS_RELEASE(*result);
555     }
556 #endif
557   }
558 
559   // Okay we don't have a protocol handler to handle this url type, so use
560   // the default protocol handler.  This will cause urls to get dispatched
561   // out to the OS ('cause we can't do anything with them) when we try to
562   // read from a channel created by the default protocol handler.
563 
564   rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "default", result);
565   if (NS_FAILED(rv)) return NS_ERROR_UNKNOWN_PROTOCOL;
566 
567   return rv;
568 }
569 
570 NS_IMETHODIMP
ExtractScheme(const nsACString & inURI,nsACString & scheme)571 nsIOService::ExtractScheme(const nsACString &inURI, nsACString &scheme) {
572   return net_ExtractURLScheme(inURI, scheme);
573 }
574 
575 NS_IMETHODIMP
HostnameIsLocalIPAddress(nsIURI * aURI,bool * aResult)576 nsIOService::HostnameIsLocalIPAddress(nsIURI *aURI, bool *aResult) {
577   NS_ENSURE_ARG_POINTER(aURI);
578 
579   nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
580   NS_ENSURE_ARG_POINTER(innerURI);
581 
582   nsAutoCString host;
583   nsresult rv = innerURI->GetAsciiHost(host);
584   if (NS_FAILED(rv)) {
585     return rv;
586   }
587 
588   *aResult = false;
589 
590   PRNetAddr addr;
591   PRStatus result = PR_StringToNetAddr(host.get(), &addr);
592   if (result == PR_SUCCESS) {
593     NetAddr netAddr;
594     PRNetAddrToNetAddr(&addr, &netAddr);
595     if (IsIPAddrLocal(&netAddr)) {
596       *aResult = true;
597     }
598   }
599 
600   return NS_OK;
601 }
602 
603 NS_IMETHODIMP
GetProtocolFlags(const char * scheme,uint32_t * flags)604 nsIOService::GetProtocolFlags(const char *scheme, uint32_t *flags) {
605   nsCOMPtr<nsIProtocolHandler> handler;
606   nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
607   if (NS_FAILED(rv)) return rv;
608 
609   // We can't call DoGetProtocolFlags here because we don't have a URI. This
610   // API is used by (and only used by) extensions, which is why it's still
611   // around. Calling this on a scheme with dynamic flags will throw.
612   rv = handler->GetProtocolFlags(flags);
613 #if !IS_ORIGIN_IS_FULL_SPEC_DEFINED
614   MOZ_RELEASE_ASSERT(!(*flags & nsIProtocolHandler::ORIGIN_IS_FULL_SPEC),
615                      "ORIGIN_IS_FULL_SPEC is unsupported but used");
616 #endif
617   return rv;
618 }
619 
620 class AutoIncrement {
621  public:
AutoIncrement(uint32_t * var)622   explicit AutoIncrement(uint32_t *var) : mVar(var) { ++*var; }
~AutoIncrement()623   ~AutoIncrement() { --*mVar; }
624 
625  private:
626   uint32_t *mVar;
627 };
628 
NewURI(const nsACString & aSpec,const char * aCharset,nsIURI * aBaseURI,nsIURI ** result)629 nsresult nsIOService::NewURI(const nsACString &aSpec, const char *aCharset,
630                              nsIURI *aBaseURI, nsIURI **result) {
631   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
632 
633   static uint32_t recursionCount = 0;
634   if (recursionCount >= MAX_RECURSION_COUNT) return NS_ERROR_MALFORMED_URI;
635   AutoIncrement inc(&recursionCount);
636 
637   nsAutoCString scheme;
638   nsresult rv = ExtractScheme(aSpec, scheme);
639   if (NS_FAILED(rv)) {
640     // then aSpec is relative
641     if (!aBaseURI) return NS_ERROR_MALFORMED_URI;
642 
643     if (!aSpec.IsEmpty() && aSpec[0] == '#') {
644       // Looks like a reference instead of a fully-specified URI.
645       // --> initialize |uri| as a clone of |aBaseURI|, with ref appended.
646       return aBaseURI->CloneWithNewRef(aSpec, result);
647     }
648 
649     rv = aBaseURI->GetScheme(scheme);
650     if (NS_FAILED(rv)) return rv;
651   }
652 
653   // now get the handler for this scheme
654   nsCOMPtr<nsIProtocolHandler> handler;
655   rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
656   if (NS_FAILED(rv)) return rv;
657 
658   return handler->NewURI(aSpec, aCharset, aBaseURI, result);
659 }
660 
661 NS_IMETHODIMP
NewFileURI(nsIFile * file,nsIURI ** result)662 nsIOService::NewFileURI(nsIFile *file, nsIURI **result) {
663   nsresult rv;
664   NS_ENSURE_ARG_POINTER(file);
665 
666   nsCOMPtr<nsIProtocolHandler> handler;
667 
668   rv = GetProtocolHandler("file", getter_AddRefs(handler));
669   if (NS_FAILED(rv)) return rv;
670 
671   nsCOMPtr<nsIFileProtocolHandler> fileHandler(do_QueryInterface(handler, &rv));
672   if (NS_FAILED(rv)) return rv;
673 
674   return fileHandler->NewFileURI(file, result);
675 }
676 
677 NS_IMETHODIMP
NewChannelFromURI2(nsIURI * aURI,nsIDOMNode * aLoadingNode,nsIPrincipal * aLoadingPrincipal,nsIPrincipal * aTriggeringPrincipal,uint32_t aSecurityFlags,uint32_t aContentPolicyType,nsIChannel ** result)678 nsIOService::NewChannelFromURI2(nsIURI *aURI, nsIDOMNode *aLoadingNode,
679                                 nsIPrincipal *aLoadingPrincipal,
680                                 nsIPrincipal *aTriggeringPrincipal,
681                                 uint32_t aSecurityFlags,
682                                 uint32_t aContentPolicyType,
683                                 nsIChannel **result) {
684   return NewChannelFromURIWithProxyFlags2(aURI,
685                                           nullptr,  // aProxyURI
686                                           0,        // aProxyFlags
687                                           aLoadingNode, aLoadingPrincipal,
688                                           aTriggeringPrincipal, aSecurityFlags,
689                                           aContentPolicyType, result);
690 }
NewChannelFromURIWithClientAndController(nsIURI * aURI,nsIDOMNode * aLoadingNode,nsIPrincipal * aLoadingPrincipal,nsIPrincipal * aTriggeringPrincipal,const Maybe<ClientInfo> & aLoadingClientInfo,const Maybe<ServiceWorkerDescriptor> & aController,uint32_t aSecurityFlags,uint32_t aContentPolicyType,nsIChannel ** aResult)691 nsresult nsIOService::NewChannelFromURIWithClientAndController(
692     nsIURI *aURI, nsIDOMNode *aLoadingNode, nsIPrincipal *aLoadingPrincipal,
693     nsIPrincipal *aTriggeringPrincipal,
694     const Maybe<ClientInfo> &aLoadingClientInfo,
695     const Maybe<ServiceWorkerDescriptor> &aController, uint32_t aSecurityFlags,
696     uint32_t aContentPolicyType, nsIChannel **aResult) {
697   return NewChannelFromURIWithProxyFlagsInternal(
698       aURI,
699       nullptr,  // aProxyURI
700       0,        // aProxyFlags
701       aLoadingNode, aLoadingPrincipal, aTriggeringPrincipal, aLoadingClientInfo,
702       aController, aSecurityFlags, aContentPolicyType, aResult);
703 }
704 
705 /*  ***** DEPRECATED *****
706  * please use NewChannelFromURI2 providing the right arguments for:
707  *        * aLoadingNode
708  *        * aLoadingPrincipal
709  *        * aTriggeringPrincipal
710  *        * aSecurityFlags
711  *        * aContentPolicyType
712  *
713  * See nsIIoService.idl for a detailed description of those arguments
714  */
715 NS_IMETHODIMP
NewChannelFromURI(nsIURI * aURI,nsIChannel ** result)716 nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result) {
717   NS_ASSERTION(
718       false,
719       "Deprecated, use NewChannelFromURI2 providing loadInfo arguments!");
720 
721   const char16_t *params[] = {u"nsIOService::NewChannelFromURI()",
722                               u"nsIOService::NewChannelFromURI2()"};
723   nsContentUtils::ReportToConsole(
724       nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Security by Default"),
725       nullptr,  // aDocument
726       nsContentUtils::eNECKO_PROPERTIES, "APIDeprecationWarning", params,
727       ArrayLength(params));
728 
729   return NewChannelFromURI2(aURI,
730                             nullptr,  // aLoadingNode
731                             nsContentUtils::GetSystemPrincipal(),
732                             nullptr,  // aTriggeringPrincipal
733                             nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
734                             nsIContentPolicy::TYPE_OTHER, result);
735 }
736 
737 NS_IMETHODIMP
NewChannelFromURIWithLoadInfo(nsIURI * aURI,nsILoadInfo * aLoadInfo,nsIChannel ** result)738 nsIOService::NewChannelFromURIWithLoadInfo(nsIURI *aURI, nsILoadInfo *aLoadInfo,
739                                            nsIChannel **result) {
740   return NewChannelFromURIWithProxyFlagsInternal(aURI,
741                                                  nullptr,  // aProxyURI
742                                                  0,        // aProxyFlags
743                                                  aLoadInfo, result);
744 }
745 
NewChannelFromURIWithProxyFlagsInternal(nsIURI * aURI,nsIURI * aProxyURI,uint32_t aProxyFlags,nsIDOMNode * aLoadingNode,nsIPrincipal * aLoadingPrincipal,nsIPrincipal * aTriggeringPrincipal,const Maybe<ClientInfo> & aLoadingClientInfo,const Maybe<ServiceWorkerDescriptor> & aController,uint32_t aSecurityFlags,uint32_t aContentPolicyType,nsIChannel ** result)746 nsresult nsIOService::NewChannelFromURIWithProxyFlagsInternal(
747     nsIURI *aURI, nsIURI *aProxyURI, uint32_t aProxyFlags,
748     nsIDOMNode *aLoadingNode, nsIPrincipal *aLoadingPrincipal,
749     nsIPrincipal *aTriggeringPrincipal,
750     const Maybe<ClientInfo> &aLoadingClientInfo,
751     const Maybe<ServiceWorkerDescriptor> &aController, uint32_t aSecurityFlags,
752     uint32_t aContentPolicyType, nsIChannel **result) {
753   // Ideally all callers of NewChannelFromURIWithProxyFlagsInternal provide
754   // the necessary arguments to create a loadinfo.
755   //
756   // Note, historically this could be called with nullptr aLoadingNode,
757   // aLoadingPrincipal, and aTriggeringPrincipal from addons using
758   // newChannelFromURIWithProxyFlags().  This code tried to accomodate
759   // by not creating a LoadInfo in such cases.  Now that both the legacy
760   // addons and that API are gone we could possibly require always creating a
761   // LoadInfo here.  See bug 1432205.
762   nsCOMPtr<nsILoadInfo> loadInfo;
763 
764   // TYPE_DOCUMENT loads don't require a loadingNode or principal, but other
765   // types do.
766   if (aLoadingNode || aLoadingPrincipal ||
767       aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
768     nsCOMPtr<nsINode> loadingNode(do_QueryInterface(aLoadingNode));
769     loadInfo = new LoadInfo(aLoadingPrincipal, aTriggeringPrincipal,
770                             loadingNode, aSecurityFlags, aContentPolicyType,
771                             aLoadingClientInfo, aController);
772   }
773   NS_ASSERTION(loadInfo, "Please pass security info when creating a channel");
774   return NewChannelFromURIWithProxyFlagsInternal(aURI, aProxyURI, aProxyFlags,
775                                                  loadInfo, result);
776 }
777 
NewChannelFromURIWithProxyFlagsInternal(nsIURI * aURI,nsIURI * aProxyURI,uint32_t aProxyFlags,nsILoadInfo * aLoadInfo,nsIChannel ** result)778 nsresult nsIOService::NewChannelFromURIWithProxyFlagsInternal(
779     nsIURI *aURI, nsIURI *aProxyURI, uint32_t aProxyFlags,
780     nsILoadInfo *aLoadInfo, nsIChannel **result) {
781   nsresult rv;
782   NS_ENSURE_ARG_POINTER(aURI);
783 
784   nsAutoCString scheme;
785   rv = aURI->GetScheme(scheme);
786   if (NS_FAILED(rv)) return rv;
787 
788   nsCOMPtr<nsIProtocolHandler> handler;
789   rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
790   if (NS_FAILED(rv)) return rv;
791 
792   uint32_t protoFlags;
793   rv = handler->DoGetProtocolFlags(aURI, &protoFlags);
794   if (NS_FAILED(rv)) return rv;
795 
796   // Ideally we are creating new channels by calling NewChannel2
797   // (NewProxiedChannel2). Keep in mind that Addons can implement their own
798   // Protocolhandlers, hence NewChannel2() might *not* be implemented. We do not
799   // want to break those addons, therefore we first try to create a channel
800   // calling NewChannel2(); if that fails:
801   // * we fall back to creating a channel by calling NewChannel()
802   // * wrap the addon channel
803   // * and attach the loadInfo to the channel wrapper
804   nsCOMPtr<nsIChannel> channel;
805   nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler);
806   if (pph) {
807     rv = pph->NewProxiedChannel2(aURI, nullptr, aProxyFlags, aProxyURI,
808                                  aLoadInfo, getter_AddRefs(channel));
809     // if calling NewProxiedChannel2() fails we try to fall back to
810     // creating a new proxied channel by calling NewProxiedChannel().
811     if (NS_FAILED(rv)) {
812       rv = pph->NewProxiedChannel(aURI, nullptr, aProxyFlags, aProxyURI,
813                                   getter_AddRefs(channel));
814       NS_ENSURE_SUCCESS(rv, rv);
815 
816       // The protocol handler does not implement NewProxiedChannel2, so
817       // maybe we need to wrap the channel (see comment in MaybeWrap
818       // function).
819       channel = nsSecCheckWrapChannel::MaybeWrap(channel, aLoadInfo);
820     }
821   } else {
822     rv = handler->NewChannel2(aURI, aLoadInfo, getter_AddRefs(channel));
823     // if an implementation of NewChannel2() is missing we try to fall back to
824     // creating a new channel by calling NewChannel().
825     if (rv == NS_ERROR_NOT_IMPLEMENTED ||
826         rv == NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED) {
827       LOG(("NewChannel2 not implemented rv=%" PRIx32
828            ". Falling back to NewChannel\n",
829            static_cast<uint32_t>(rv)));
830       rv = handler->NewChannel(aURI, getter_AddRefs(channel));
831       if (NS_FAILED(rv)) {
832         return rv;
833       }
834       // The protocol handler does not implement NewChannel2, so
835       // maybe we need to wrap the channel (see comment in MaybeWrap
836       // function).
837       channel = nsSecCheckWrapChannel::MaybeWrap(channel, aLoadInfo);
838     } else if (NS_FAILED(rv)) {
839       return rv;
840     }
841   }
842 
843   // Make sure that all the individual protocolhandlers attach a loadInfo.
844   if (aLoadInfo) {
845     // make sure we have the same instance of loadInfo on the newly created
846     // channel
847     nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
848     if (aLoadInfo != loadInfo) {
849       MOZ_ASSERT(false, "newly created channel must have a loadinfo attached");
850       return NS_ERROR_UNEXPECTED;
851     }
852 
853     // If we're sandboxed, make sure to clear any owner the channel
854     // might already have.
855     if (loadInfo->GetLoadingSandboxed()) {
856       channel->SetOwner(nullptr);
857     }
858   }
859 
860   // Some extensions override the http protocol handler and provide their own
861   // implementation. The channels returned from that implementation doesn't
862   // seem to always implement the nsIUploadChannel2 interface, presumably
863   // because it's a new interface.
864   // Eventually we should remove this and simply require that http channels
865   // implement the new interface.
866   // See bug 529041
867   if (!gHasWarnedUploadChannel2 && scheme.EqualsLiteral("http")) {
868     nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(channel);
869     if (!uploadChannel2) {
870       nsCOMPtr<nsIConsoleService> consoleService =
871           do_GetService(NS_CONSOLESERVICE_CONTRACTID);
872       if (consoleService) {
873         consoleService->LogStringMessage(
874             u"Http channel implementation "
875             "doesn't support nsIUploadChannel2. An extension has "
876             "supplied a non-functional http protocol handler. This will "
877             "break behavior and in future releases not work at all.");
878       }
879       gHasWarnedUploadChannel2 = true;
880     }
881   }
882 
883   channel.forget(result);
884   return NS_OK;
885 }
886 
887 NS_IMETHODIMP
NewChannelFromURIWithProxyFlags2(nsIURI * aURI,nsIURI * aProxyURI,uint32_t aProxyFlags,nsIDOMNode * aLoadingNode,nsIPrincipal * aLoadingPrincipal,nsIPrincipal * aTriggeringPrincipal,uint32_t aSecurityFlags,uint32_t aContentPolicyType,nsIChannel ** result)888 nsIOService::NewChannelFromURIWithProxyFlags2(
889     nsIURI *aURI, nsIURI *aProxyURI, uint32_t aProxyFlags,
890     nsIDOMNode *aLoadingNode, nsIPrincipal *aLoadingPrincipal,
891     nsIPrincipal *aTriggeringPrincipal, uint32_t aSecurityFlags,
892     uint32_t aContentPolicyType, nsIChannel **result) {
893   return NewChannelFromURIWithProxyFlagsInternal(
894       aURI, aProxyURI, aProxyFlags, aLoadingNode, aLoadingPrincipal,
895       aTriggeringPrincipal, Maybe<ClientInfo>(),
896       Maybe<ServiceWorkerDescriptor>(), aSecurityFlags, aContentPolicyType,
897       result);
898 }
899 
900 NS_IMETHODIMP
NewChannel2(const nsACString & aSpec,const char * aCharset,nsIURI * aBaseURI,nsIDOMNode * aLoadingNode,nsIPrincipal * aLoadingPrincipal,nsIPrincipal * aTriggeringPrincipal,uint32_t aSecurityFlags,uint32_t aContentPolicyType,nsIChannel ** result)901 nsIOService::NewChannel2(const nsACString &aSpec, const char *aCharset,
902                          nsIURI *aBaseURI, nsIDOMNode *aLoadingNode,
903                          nsIPrincipal *aLoadingPrincipal,
904                          nsIPrincipal *aTriggeringPrincipal,
905                          uint32_t aSecurityFlags, uint32_t aContentPolicyType,
906                          nsIChannel **result) {
907   nsresult rv;
908   nsCOMPtr<nsIURI> uri;
909   rv = NewURI(aSpec, aCharset, aBaseURI, getter_AddRefs(uri));
910   if (NS_FAILED(rv)) return rv;
911 
912   return NewChannelFromURI2(uri, aLoadingNode, aLoadingPrincipal,
913                             aTriggeringPrincipal, aSecurityFlags,
914                             aContentPolicyType, result);
915 }
916 
917 /*  ***** DEPRECATED *****
918  * please use NewChannel2 providing the right arguments for:
919  *        * aLoadingNode
920  *        * aLoadingPrincipal
921  *        * aTriggeringPrincipal
922  *        * aSecurityFlags
923  *        * aContentPolicyType
924  *
925  * See nsIIoService.idl for a detailed description of those arguments
926  */
927 NS_IMETHODIMP
NewChannel(const nsACString & aSpec,const char * aCharset,nsIURI * aBaseURI,nsIChannel ** result)928 nsIOService::NewChannel(const nsACString &aSpec, const char *aCharset,
929                         nsIURI *aBaseURI, nsIChannel **result) {
930   NS_ASSERTION(false,
931                "Deprecated, use NewChannel2 providing loadInfo arguments!");
932 
933   const char16_t *params[] = {u"nsIOService::NewChannel()",
934                               u"nsIOService::NewChannel2()"};
935   nsContentUtils::ReportToConsole(
936       nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Security by Default"),
937       nullptr,  // aDocument
938       nsContentUtils::eNECKO_PROPERTIES, "APIDeprecationWarning", params,
939       ArrayLength(params));
940 
941   // Call NewChannel2 providing default arguments for the loadInfo.
942   return NewChannel2(aSpec, aCharset, aBaseURI,
943                      nullptr,                               // aLoadingNode
944                      nsContentUtils::GetSystemPrincipal(),  // aLoadingPrincipal
945                      nullptr,  // aTriggeringPrincipal
946                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
947                      nsIContentPolicy::TYPE_OTHER, result);
948 }
949 
IsLinkUp()950 bool nsIOService::IsLinkUp() {
951   InitializeNetworkLinkService();
952 
953   if (!mNetworkLinkService) {
954     // We cannot decide, assume the link is up
955     return true;
956   }
957 
958   bool isLinkUp;
959   nsresult rv;
960   rv = mNetworkLinkService->GetIsLinkUp(&isLinkUp);
961   if (NS_FAILED(rv)) {
962     return true;
963   }
964 
965   return isLinkUp;
966 }
967 
968 NS_IMETHODIMP
GetOffline(bool * offline)969 nsIOService::GetOffline(bool *offline) {
970   if (mOfflineMirrorsConnectivity) {
971     *offline = mOffline || !mConnectivity;
972   } else {
973     *offline = mOffline;
974   }
975   return NS_OK;
976 }
977 
978 NS_IMETHODIMP
SetOffline(bool offline)979 nsIOService::SetOffline(bool offline) {
980   LOG(("nsIOService::SetOffline offline=%d\n", offline));
981   // When someone wants to go online (!offline) after we got XPCOM shutdown
982   // throw ERROR_NOT_AVAILABLE to prevent return to online state.
983   if ((mShutdown || mOfflineForProfileChange) && !offline)
984     return NS_ERROR_NOT_AVAILABLE;
985 
986   // SetOffline() may re-enter while it's shutting down services.
987   // If that happens, save the most recent value and it will be
988   // processed when the first SetOffline() call is done bringing
989   // down the service.
990   mSetOfflineValue = offline;
991   if (mSettingOffline) {
992     return NS_OK;
993   }
994 
995   mSettingOffline = true;
996 
997   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
998 
999   NS_ASSERTION(observerService, "The observer service should not be null");
1000 
1001   if (XRE_IsParentProcess()) {
1002     if (observerService) {
1003       (void)observerService->NotifyObservers(nullptr,
1004                                              NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
1005                                              offline ? u"true" : u"false");
1006     }
1007   }
1008 
1009   nsIIOService *subject = static_cast<nsIIOService *>(this);
1010   while (mSetOfflineValue != mOffline) {
1011     offline = mSetOfflineValue;
1012 
1013     if (offline && !mOffline) {
1014       mOffline = true;  // indicate we're trying to shutdown
1015 
1016       // don't care if notifications fail
1017       if (observerService)
1018         observerService->NotifyObservers(subject,
1019                                          NS_IOSERVICE_GOING_OFFLINE_TOPIC,
1020                                          u"" NS_IOSERVICE_OFFLINE);
1021 
1022       if (mSocketTransportService) mSocketTransportService->SetOffline(true);
1023 
1024       mLastOfflineStateChange = PR_IntervalNow();
1025       if (observerService)
1026         observerService->NotifyObservers(subject,
1027                                          NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1028                                          u"" NS_IOSERVICE_OFFLINE);
1029     } else if (!offline && mOffline) {
1030       // go online
1031       if (mDNSService) {
1032         DebugOnly<nsresult> rv = mDNSService->Init();
1033         NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed");
1034       }
1035       InitializeSocketTransportService();
1036       mOffline = false;  // indicate success only AFTER we've
1037                          // brought up the services
1038 
1039       mLastOfflineStateChange = PR_IntervalNow();
1040       // don't care if notification fails
1041       // Only send the ONLINE notification if there is connectivity
1042       if (observerService && mConnectivity) {
1043         observerService->NotifyObservers(subject,
1044                                          NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1045                                          (u"" NS_IOSERVICE_ONLINE));
1046       }
1047     }
1048   }
1049 
1050   // Don't notify here, as the above notifications (if used) suffice.
1051   if ((mShutdown || mOfflineForProfileChange) && mOffline) {
1052     // be sure to try and shutdown both (even if the first fails)...
1053     // shutdown dns service first, because it has callbacks for socket transport
1054     if (mDNSService) {
1055       DebugOnly<nsresult> rv = mDNSService->Shutdown();
1056       NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service shutdown failed");
1057     }
1058     if (mSocketTransportService) {
1059       DebugOnly<nsresult> rv = mSocketTransportService->Shutdown(mShutdown);
1060       NS_ASSERTION(NS_SUCCEEDED(rv),
1061                    "socket transport service shutdown failed");
1062     }
1063   }
1064 
1065   mSettingOffline = false;
1066 
1067   return NS_OK;
1068 }
1069 
1070 NS_IMETHODIMP
GetConnectivity(bool * aConnectivity)1071 nsIOService::GetConnectivity(bool *aConnectivity) {
1072   *aConnectivity = mConnectivity;
1073   return NS_OK;
1074 }
1075 
1076 NS_IMETHODIMP
SetConnectivity(bool aConnectivity)1077 nsIOService::SetConnectivity(bool aConnectivity) {
1078   LOG(("nsIOService::SetConnectivity aConnectivity=%d\n", aConnectivity));
1079   // This should only be called from ContentChild to pass the connectivity
1080   // value from the chrome process to the content process.
1081   if (XRE_IsParentProcess()) {
1082     return NS_ERROR_NOT_AVAILABLE;
1083   }
1084   return SetConnectivityInternal(aConnectivity);
1085 }
1086 
SetConnectivityInternal(bool aConnectivity)1087 nsresult nsIOService::SetConnectivityInternal(bool aConnectivity) {
1088   LOG(("nsIOService::SetConnectivityInternal aConnectivity=%d\n",
1089        aConnectivity));
1090   if (mConnectivity == aConnectivity) {
1091     // Nothing to do here.
1092     return NS_OK;
1093   }
1094   mConnectivity = aConnectivity;
1095 
1096   // This is used for PR_Connect PR_Close telemetry so it is important that
1097   // we have statistic about network change event even if we are offline.
1098   mLastConnectivityChange = PR_IntervalNow();
1099 
1100   if (mCaptivePortalService) {
1101     if (aConnectivity && !xpc::AreNonLocalConnectionsDisabled() &&
1102         gCaptivePortalEnabled) {
1103       // This will also trigger a captive portal check for the new network
1104       static_cast<CaptivePortalService *>(mCaptivePortalService.get())->Start();
1105     } else {
1106       static_cast<CaptivePortalService *>(mCaptivePortalService.get())->Stop();
1107     }
1108   }
1109 
1110   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
1111   if (!observerService) {
1112     return NS_OK;
1113   }
1114   // This notification sends the connectivity to the child processes
1115   if (XRE_IsParentProcess()) {
1116     observerService->NotifyObservers(nullptr,
1117                                      NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
1118                                      aConnectivity ? u"true" : u"false");
1119   }
1120 
1121   if (mOffline) {
1122     // We don't need to send any notifications if we're offline
1123     return NS_OK;
1124   }
1125 
1126   if (aConnectivity) {
1127     // If we were previously offline due to connectivity=false,
1128     // send the ONLINE notification
1129     observerService->NotifyObservers(static_cast<nsIIOService *>(this),
1130                                      NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1131                                      (u"" NS_IOSERVICE_ONLINE));
1132   } else {
1133     // If we were previously online and lost connectivity
1134     // send the OFFLINE notification
1135     observerService->NotifyObservers(static_cast<nsIIOService *>(this),
1136                                      NS_IOSERVICE_GOING_OFFLINE_TOPIC,
1137                                      u"" NS_IOSERVICE_OFFLINE);
1138     observerService->NotifyObservers(static_cast<nsIIOService *>(this),
1139                                      NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1140                                      u"" NS_IOSERVICE_OFFLINE);
1141   }
1142   return NS_OK;
1143 }
1144 
1145 NS_IMETHODIMP
AllowPort(int32_t inPort,const char * scheme,bool * _retval)1146 nsIOService::AllowPort(int32_t inPort, const char *scheme, bool *_retval) {
1147   int16_t port = inPort;
1148   if (port == -1) {
1149     *_retval = true;
1150     return NS_OK;
1151   }
1152 
1153   if (port == 0) {
1154     *_retval = false;
1155     return NS_OK;
1156   }
1157 
1158   // first check to see if the port is in our blacklist:
1159   int32_t badPortListCnt = mRestrictedPortList.Length();
1160   for (int i = 0; i < badPortListCnt; i++) {
1161     if (port == mRestrictedPortList[i]) {
1162       *_retval = false;
1163 
1164       // check to see if the protocol wants to override
1165       if (!scheme) return NS_OK;
1166 
1167       nsCOMPtr<nsIProtocolHandler> handler;
1168       nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
1169       if (NS_FAILED(rv)) return rv;
1170 
1171       // let the protocol handler decide
1172       return handler->AllowPort(port, scheme, _retval);
1173     }
1174   }
1175 
1176   *_retval = true;
1177   return NS_OK;
1178 }
1179 
1180 ////////////////////////////////////////////////////////////////////////////////
1181 
PrefsChanged(nsIPrefBranch * prefs,const char * pref)1182 void nsIOService::PrefsChanged(nsIPrefBranch *prefs, const char *pref) {
1183   if (!prefs) return;
1184 
1185   // Look for extra ports to block
1186   if (!pref || strcmp(pref, PORT_PREF("banned")) == 0)
1187     ParsePortList(prefs, PORT_PREF("banned"), false);
1188 
1189   // ...as well as previous blocks to remove.
1190   if (!pref || strcmp(pref, PORT_PREF("banned.override")) == 0)
1191     ParsePortList(prefs, PORT_PREF("banned.override"), true);
1192 
1193   if (!pref || strcmp(pref, MANAGE_OFFLINE_STATUS_PREF) == 0) {
1194     bool manage;
1195     if (mNetworkLinkServiceInitialized &&
1196         NS_SUCCEEDED(prefs->GetBoolPref(MANAGE_OFFLINE_STATUS_PREF, &manage))) {
1197       LOG(("nsIOService::PrefsChanged ManageOfflineStatus manage=%d\n",
1198            manage));
1199       SetManageOfflineStatus(manage);
1200     }
1201   }
1202 
1203   if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_COUNT_PREF) == 0) {
1204     int32_t count;
1205     if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_COUNT_PREF, &count)))
1206       /* check for bogus values and default if we find such a value */
1207       if (count > 0) gDefaultSegmentCount = count;
1208   }
1209 
1210   if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_SIZE_PREF) == 0) {
1211     int32_t size;
1212     if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_SIZE_PREF, &size)))
1213       /* check for bogus values and default if we find such a value
1214        * the upper limit here is arbitrary. having a 1mb segment size
1215        * is pretty crazy.  if you remove this, consider adding some
1216        * integer rollover test.
1217        */
1218       if (size > 0 && size < 1024 * 1024) gDefaultSegmentSize = size;
1219     NS_WARNING_ASSERTION(!(size & (size - 1)),
1220                          "network segment size is not a power of 2!");
1221   }
1222 
1223   if (!pref || strcmp(pref, NETWORK_NOTIFY_CHANGED_PREF) == 0) {
1224     bool allow;
1225     nsresult rv = prefs->GetBoolPref(NETWORK_NOTIFY_CHANGED_PREF, &allow);
1226     if (NS_SUCCEEDED(rv)) {
1227       mNetworkNotifyChanged = allow;
1228     }
1229   }
1230 
1231   if (!pref || strcmp(pref, NETWORK_CAPTIVE_PORTAL_PREF) == 0) {
1232     nsresult rv =
1233         prefs->GetBoolPref(NETWORK_CAPTIVE_PORTAL_PREF, &gCaptivePortalEnabled);
1234     if (NS_SUCCEEDED(rv) && mCaptivePortalService) {
1235       if (gCaptivePortalEnabled && !xpc::AreNonLocalConnectionsDisabled()) {
1236         static_cast<CaptivePortalService *>(mCaptivePortalService.get())
1237             ->Start();
1238       } else {
1239         static_cast<CaptivePortalService *>(mCaptivePortalService.get())
1240             ->Stop();
1241       }
1242     }
1243   }
1244 }
1245 
ParsePortList(nsIPrefBranch * prefBranch,const char * pref,bool remove)1246 void nsIOService::ParsePortList(nsIPrefBranch *prefBranch, const char *pref,
1247                                 bool remove) {
1248   nsAutoCString portList;
1249 
1250   // Get a pref string and chop it up into a list of ports.
1251   prefBranch->GetCharPref(pref, portList);
1252   if (!portList.IsVoid()) {
1253     nsTArray<nsCString> portListArray;
1254     ParseString(portList, ',', portListArray);
1255     uint32_t index;
1256     for (index = 0; index < portListArray.Length(); index++) {
1257       portListArray[index].StripWhitespace();
1258       int32_t portBegin, portEnd;
1259 
1260       if (PR_sscanf(portListArray[index].get(), "%d-%d", &portBegin,
1261                     &portEnd) == 2) {
1262         if ((portBegin < 65536) && (portEnd < 65536)) {
1263           int32_t curPort;
1264           if (remove) {
1265             for (curPort = portBegin; curPort <= portEnd; curPort++)
1266               mRestrictedPortList.RemoveElement(curPort);
1267           } else {
1268             for (curPort = portBegin; curPort <= portEnd; curPort++)
1269               mRestrictedPortList.AppendElement(curPort);
1270           }
1271         }
1272       } else {
1273         nsresult aErrorCode;
1274         int32_t port = portListArray[index].ToInteger(&aErrorCode);
1275         if (NS_SUCCEEDED(aErrorCode) && port < 65536) {
1276           if (remove)
1277             mRestrictedPortList.RemoveElement(port);
1278           else
1279             mRestrictedPortList.AppendElement(port);
1280         }
1281       }
1282     }
1283   }
1284 }
1285 
GetPrefBranch(nsIPrefBranch ** result)1286 void nsIOService::GetPrefBranch(nsIPrefBranch **result) {
1287   *result = nullptr;
1288   CallGetService(NS_PREFSERVICE_CONTRACTID, result);
1289 }
1290 
1291 class nsWakeupNotifier : public Runnable {
1292  public:
nsWakeupNotifier(nsIIOServiceInternal * ioService)1293   explicit nsWakeupNotifier(nsIIOServiceInternal *ioService)
1294       : Runnable("net::nsWakeupNotifier"), mIOService(ioService) {}
1295 
Run()1296   NS_IMETHOD Run() override { return mIOService->NotifyWakeup(); }
1297 
1298  private:
~nsWakeupNotifier()1299   virtual ~nsWakeupNotifier() {}
1300   nsCOMPtr<nsIIOServiceInternal> mIOService;
1301 };
1302 
1303 NS_IMETHODIMP
NotifyWakeup()1304 nsIOService::NotifyWakeup() {
1305   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
1306 
1307   NS_ASSERTION(observerService, "The observer service should not be null");
1308 
1309   if (observerService && mNetworkNotifyChanged) {
1310     (void)observerService->NotifyObservers(nullptr, NS_NETWORK_LINK_TOPIC,
1311                                            (u"" NS_NETWORK_LINK_DATA_CHANGED));
1312   }
1313 
1314   RecheckCaptivePortal();
1315 
1316   return NS_OK;
1317 }
1318 
SetHttpHandlerAlreadyShutingDown()1319 void nsIOService::SetHttpHandlerAlreadyShutingDown() {
1320   if (!mShutdown && !mOfflineForProfileChange) {
1321     mNetTearingDownStarted = PR_IntervalNow();
1322     mHttpHandlerAlreadyShutingDown = true;
1323   }
1324 }
1325 
1326 // nsIObserver interface
1327 NS_IMETHODIMP
Observe(nsISupports * subject,const char * topic,const char16_t * data)1328 nsIOService::Observe(nsISupports *subject, const char *topic,
1329                      const char16_t *data) {
1330   if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
1331     nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
1332     if (prefBranch) PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
1333   } else if (!strcmp(topic, kProfileChangeNetTeardownTopic)) {
1334     if (!mHttpHandlerAlreadyShutingDown) {
1335       mNetTearingDownStarted = PR_IntervalNow();
1336     }
1337     mHttpHandlerAlreadyShutingDown = false;
1338     if (!mOffline) {
1339       mOfflineForProfileChange = true;
1340       SetOffline(true);
1341     }
1342   } else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) {
1343     if (mOfflineForProfileChange) {
1344       mOfflineForProfileChange = false;
1345       SetOffline(false);
1346     }
1347   } else if (!strcmp(topic, kProfileDoChange)) {
1348     if (data && NS_LITERAL_STRING("startup").Equals(data)) {
1349       // Lazy initialization of network link service (see bug 620472)
1350       InitializeNetworkLinkService();
1351       // Set up the initilization flag regardless the actuall result.
1352       // If we fail here, we will fail always on.
1353       mNetworkLinkServiceInitialized = true;
1354 
1355       // And now reflect the preference setting
1356       nsCOMPtr<nsIPrefBranch> prefBranch;
1357       GetPrefBranch(getter_AddRefs(prefBranch));
1358       PrefsChanged(prefBranch, MANAGE_OFFLINE_STATUS_PREF);
1359 
1360       // Bug 870460 - Read cookie database at an early-as-possible time
1361       // off main thread. Hence, we have more chance to finish db query
1362       // before something calls into the cookie service.
1363       nsCOMPtr<nsISupports> cookieServ =
1364           do_GetService(NS_COOKIESERVICE_CONTRACTID);
1365     }
1366   } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
1367     // Remember we passed XPCOM shutdown notification to prevent any
1368     // changes of the offline status from now. We must not allow going
1369     // online after this point.
1370     mShutdown = true;
1371 
1372     if (!mHttpHandlerAlreadyShutingDown && !mOfflineForProfileChange) {
1373       mNetTearingDownStarted = PR_IntervalNow();
1374     }
1375     mHttpHandlerAlreadyShutingDown = false;
1376 
1377     SetOffline(true);
1378 
1379     if (mCaptivePortalService) {
1380       static_cast<CaptivePortalService *>(mCaptivePortalService.get())->Stop();
1381       mCaptivePortalService = nullptr;
1382     }
1383 
1384   } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
1385     OnNetworkLinkEvent(NS_ConvertUTF16toUTF8(data).get());
1386   } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
1387     // coming back alive from sleep
1388     // this indirection brought to you by:
1389     // https://bugzilla.mozilla.org/show_bug.cgi?id=1152048#c19
1390     nsCOMPtr<nsIRunnable> wakeupNotifier = new nsWakeupNotifier(this);
1391     NS_DispatchToMainThread(wakeupNotifier);
1392   }
1393 
1394   return NS_OK;
1395 }
1396 
1397 // nsINetUtil interface
1398 NS_IMETHODIMP
ParseRequestContentType(const nsACString & aTypeHeader,nsACString & aCharset,bool * aHadCharset,nsACString & aContentType)1399 nsIOService::ParseRequestContentType(const nsACString &aTypeHeader,
1400                                      nsACString &aCharset, bool *aHadCharset,
1401                                      nsACString &aContentType) {
1402   net_ParseRequestContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
1403   return NS_OK;
1404 }
1405 
1406 // nsINetUtil interface
1407 NS_IMETHODIMP
ParseResponseContentType(const nsACString & aTypeHeader,nsACString & aCharset,bool * aHadCharset,nsACString & aContentType)1408 nsIOService::ParseResponseContentType(const nsACString &aTypeHeader,
1409                                       nsACString &aCharset, bool *aHadCharset,
1410                                       nsACString &aContentType) {
1411   net_ParseContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
1412   return NS_OK;
1413 }
1414 
1415 NS_IMETHODIMP
ProtocolHasFlags(nsIURI * uri,uint32_t flags,bool * result)1416 nsIOService::ProtocolHasFlags(nsIURI *uri, uint32_t flags, bool *result) {
1417   NS_ENSURE_ARG(uri);
1418 
1419   *result = false;
1420   nsAutoCString scheme;
1421   nsresult rv = uri->GetScheme(scheme);
1422   NS_ENSURE_SUCCESS(rv, rv);
1423 
1424   // Grab the protocol flags from the URI.
1425   uint32_t protocolFlags;
1426   nsCOMPtr<nsIProtocolHandler> handler;
1427   rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
1428   NS_ENSURE_SUCCESS(rv, rv);
1429   rv = handler->DoGetProtocolFlags(uri, &protocolFlags);
1430   NS_ENSURE_SUCCESS(rv, rv);
1431 
1432   *result = (protocolFlags & flags) == flags;
1433   return NS_OK;
1434 }
1435 
1436 NS_IMETHODIMP
URIChainHasFlags(nsIURI * uri,uint32_t flags,bool * result)1437 nsIOService::URIChainHasFlags(nsIURI *uri, uint32_t flags, bool *result) {
1438   nsresult rv = ProtocolHasFlags(uri, flags, result);
1439   NS_ENSURE_SUCCESS(rv, rv);
1440 
1441   if (*result) {
1442     return rv;
1443   }
1444 
1445   // Dig deeper into the chain.  Note that this is not a do/while loop to
1446   // avoid the extra addref/release on |uri| in the common (non-nested) case.
1447   nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
1448   while (nestedURI) {
1449     nsCOMPtr<nsIURI> innerURI;
1450     rv = nestedURI->GetInnerURI(getter_AddRefs(innerURI));
1451     NS_ENSURE_SUCCESS(rv, rv);
1452 
1453     rv = ProtocolHasFlags(innerURI, flags, result);
1454 
1455     if (*result) {
1456       return rv;
1457     }
1458 
1459     nestedURI = do_QueryInterface(innerURI);
1460   }
1461 
1462   return rv;
1463 }
1464 
1465 NS_IMETHODIMP
ToImmutableURI(nsIURI * uri,nsIURI ** result)1466 nsIOService::ToImmutableURI(nsIURI *uri, nsIURI **result) {
1467   if (!uri) {
1468     *result = nullptr;
1469     return NS_OK;
1470   }
1471 
1472   nsresult rv = NS_EnsureSafeToReturn(uri, result);
1473   NS_ENSURE_SUCCESS(rv, rv);
1474 
1475   NS_TryToSetImmutable(*result);
1476   return NS_OK;
1477 }
1478 
1479 NS_IMETHODIMP
SetManageOfflineStatus(bool aManage)1480 nsIOService::SetManageOfflineStatus(bool aManage) {
1481   LOG(("nsIOService::SetManageOfflineStatus aManage=%d\n", aManage));
1482   mManageLinkStatus = aManage;
1483 
1484   // When detection is not activated, the default connectivity state is true.
1485   if (!mManageLinkStatus) {
1486     SetConnectivityInternal(true);
1487     return NS_OK;
1488   }
1489 
1490   InitializeNetworkLinkService();
1491   // If the NetworkLinkService is already initialized, it does not call
1492   // OnNetworkLinkEvent. This is needed, when mManageLinkStatus goes from
1493   // false to true.
1494   OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN);
1495   return NS_OK;
1496 }
1497 
1498 NS_IMETHODIMP
GetManageOfflineStatus(bool * aManage)1499 nsIOService::GetManageOfflineStatus(bool *aManage) {
1500   *aManage = mManageLinkStatus;
1501   return NS_OK;
1502 }
1503 
1504 // input argument 'data' is already UTF8'ed
OnNetworkLinkEvent(const char * data)1505 nsresult nsIOService::OnNetworkLinkEvent(const char *data) {
1506   LOG(("nsIOService::OnNetworkLinkEvent data:%s\n", data));
1507   if (!mNetworkLinkService) return NS_ERROR_FAILURE;
1508 
1509   if (mShutdown) return NS_ERROR_NOT_AVAILABLE;
1510 
1511   if (!mManageLinkStatus) {
1512     LOG(("nsIOService::OnNetworkLinkEvent mManageLinkStatus=false\n"));
1513     return NS_OK;
1514   }
1515 
1516   bool isUp = true;
1517   if (!strcmp(data, NS_NETWORK_LINK_DATA_CHANGED)) {
1518     mLastNetworkLinkChange = PR_IntervalNow();
1519     // CHANGED means UP/DOWN didn't change
1520     // but the status of the captive portal may have changed.
1521     RecheckCaptivePortal();
1522     return NS_OK;
1523   } else if (!strcmp(data, NS_NETWORK_LINK_DATA_DOWN)) {
1524     isUp = false;
1525   } else if (!strcmp(data, NS_NETWORK_LINK_DATA_UP)) {
1526     isUp = true;
1527   } else if (!strcmp(data, NS_NETWORK_LINK_DATA_UNKNOWN)) {
1528     nsresult rv = mNetworkLinkService->GetIsLinkUp(&isUp);
1529     NS_ENSURE_SUCCESS(rv, rv);
1530   } else {
1531     NS_WARNING("Unhandled network event!");
1532     return NS_OK;
1533   }
1534 
1535   return SetConnectivityInternal(isUp);
1536 }
1537 
1538 NS_IMETHODIMP
EscapeString(const nsACString & aString,uint32_t aEscapeType,nsACString & aResult)1539 nsIOService::EscapeString(const nsACString &aString, uint32_t aEscapeType,
1540                           nsACString &aResult) {
1541   NS_ENSURE_ARG_MAX(aEscapeType, 4);
1542 
1543   nsAutoCString stringCopy(aString);
1544   nsCString result;
1545 
1546   if (!NS_Escape(stringCopy, result, (nsEscapeMask)aEscapeType))
1547     return NS_ERROR_OUT_OF_MEMORY;
1548 
1549   aResult.Assign(result);
1550 
1551   return NS_OK;
1552 }
1553 
1554 NS_IMETHODIMP
EscapeURL(const nsACString & aStr,uint32_t aFlags,nsACString & aResult)1555 nsIOService::EscapeURL(const nsACString &aStr, uint32_t aFlags,
1556                        nsACString &aResult) {
1557   aResult.Truncate();
1558   NS_EscapeURL(aStr.BeginReading(), aStr.Length(), aFlags | esc_AlwaysCopy,
1559                aResult);
1560   return NS_OK;
1561 }
1562 
1563 NS_IMETHODIMP
UnescapeString(const nsACString & aStr,uint32_t aFlags,nsACString & aResult)1564 nsIOService::UnescapeString(const nsACString &aStr, uint32_t aFlags,
1565                             nsACString &aResult) {
1566   aResult.Truncate();
1567   NS_UnescapeURL(aStr.BeginReading(), aStr.Length(), aFlags | esc_AlwaysCopy,
1568                  aResult);
1569   return NS_OK;
1570 }
1571 
1572 NS_IMETHODIMP
ExtractCharsetFromContentType(const nsACString & aTypeHeader,nsACString & aCharset,int32_t * aCharsetStart,int32_t * aCharsetEnd,bool * aHadCharset)1573 nsIOService::ExtractCharsetFromContentType(const nsACString &aTypeHeader,
1574                                            nsACString &aCharset,
1575                                            int32_t *aCharsetStart,
1576                                            int32_t *aCharsetEnd,
1577                                            bool *aHadCharset) {
1578   nsAutoCString ignored;
1579   net_ParseContentType(aTypeHeader, ignored, aCharset, aHadCharset,
1580                        aCharsetStart, aCharsetEnd);
1581   if (*aHadCharset && *aCharsetStart == *aCharsetEnd) {
1582     *aHadCharset = false;
1583   }
1584   return NS_OK;
1585 }
1586 
1587 // parse policyString to policy enum value (see ReferrerPolicy.h)
1588 NS_IMETHODIMP
ParseAttributePolicyString(const nsAString & policyString,uint32_t * outPolicyEnum)1589 nsIOService::ParseAttributePolicyString(const nsAString &policyString,
1590                                         uint32_t *outPolicyEnum) {
1591   NS_ENSURE_ARG(outPolicyEnum);
1592   *outPolicyEnum = (uint32_t)AttributeReferrerPolicyFromString(policyString);
1593   return NS_OK;
1594 }
1595 
1596 // nsISpeculativeConnect
1597 class IOServiceProxyCallback final : public nsIProtocolProxyCallback {
~IOServiceProxyCallback()1598   ~IOServiceProxyCallback() {}
1599 
1600  public:
1601   NS_DECL_ISUPPORTS
1602   NS_DECL_NSIPROTOCOLPROXYCALLBACK
1603 
IOServiceProxyCallback(nsIInterfaceRequestor * aCallbacks,nsIOService * aIOService)1604   IOServiceProxyCallback(nsIInterfaceRequestor *aCallbacks,
1605                          nsIOService *aIOService)
1606       : mCallbacks(aCallbacks), mIOService(aIOService) {}
1607 
1608  private:
1609   RefPtr<nsIInterfaceRequestor> mCallbacks;
1610   RefPtr<nsIOService> mIOService;
1611 };
1612 
NS_IMPL_ISUPPORTS(IOServiceProxyCallback,nsIProtocolProxyCallback)1613 NS_IMPL_ISUPPORTS(IOServiceProxyCallback, nsIProtocolProxyCallback)
1614 
1615 NS_IMETHODIMP
1616 IOServiceProxyCallback::OnProxyAvailable(nsICancelable *request,
1617                                          nsIChannel *channel, nsIProxyInfo *pi,
1618                                          nsresult status) {
1619   // Checking proxy status for speculative connect
1620   nsAutoCString type;
1621   if (NS_SUCCEEDED(status) && pi && NS_SUCCEEDED(pi->GetType(type)) &&
1622       !type.EqualsLiteral("direct")) {
1623     // proxies dont do speculative connect
1624     return NS_OK;
1625   }
1626 
1627   nsCOMPtr<nsIURI> uri;
1628   nsresult rv = channel->GetURI(getter_AddRefs(uri));
1629   if (NS_FAILED(rv)) {
1630     return NS_OK;
1631   }
1632 
1633   nsAutoCString scheme;
1634   rv = uri->GetScheme(scheme);
1635   if (NS_FAILED(rv)) return NS_OK;
1636 
1637   nsCOMPtr<nsIProtocolHandler> handler;
1638   rv = mIOService->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
1639   if (NS_FAILED(rv)) return NS_OK;
1640 
1641   nsCOMPtr<nsISpeculativeConnect> speculativeHandler =
1642       do_QueryInterface(handler);
1643   if (!speculativeHandler) return NS_OK;
1644 
1645   nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
1646   nsCOMPtr<nsIPrincipal> principal;
1647   if (loadInfo) {
1648     principal = loadInfo->LoadingPrincipal();
1649   }
1650 
1651   nsLoadFlags loadFlags = 0;
1652   channel->GetLoadFlags(&loadFlags);
1653   if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
1654     speculativeHandler->SpeculativeAnonymousConnect2(uri, principal,
1655                                                      mCallbacks);
1656   } else {
1657     speculativeHandler->SpeculativeConnect2(uri, principal, mCallbacks);
1658   }
1659 
1660   return NS_OK;
1661 }
1662 
SpeculativeConnectInternal(nsIURI * aURI,nsIPrincipal * aPrincipal,nsIInterfaceRequestor * aCallbacks,bool aAnonymous)1663 nsresult nsIOService::SpeculativeConnectInternal(
1664     nsIURI *aURI, nsIPrincipal *aPrincipal, nsIInterfaceRequestor *aCallbacks,
1665     bool aAnonymous) {
1666   NS_ENSURE_ARG(aURI);
1667 
1668   bool isHTTP, isHTTPS;
1669   if (!(NS_SUCCEEDED(aURI->SchemeIs("http", &isHTTP)) && isHTTP) &&
1670       !(NS_SUCCEEDED(aURI->SchemeIs("https", &isHTTPS)) && isHTTPS)) {
1671     // We don't speculatively connect to non-HTTP[S] URIs.
1672     return NS_OK;
1673   }
1674 
1675   if (IsNeckoChild()) {
1676     ipc::URIParams params;
1677     SerializeURI(aURI, params);
1678     gNeckoChild->SendSpeculativeConnect(params, IPC::Principal(aPrincipal),
1679                                         aAnonymous);
1680     return NS_OK;
1681   }
1682 
1683   // Check for proxy information. If there is a proxy configured then a
1684   // speculative connect should not be performed because the potential
1685   // reward is slim with tcp peers closely located to the browser.
1686   nsresult rv;
1687   nsCOMPtr<nsIProtocolProxyService> pps =
1688       do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
1689   NS_ENSURE_SUCCESS(rv, rv);
1690 
1691   nsCOMPtr<nsIPrincipal> loadingPrincipal = aPrincipal;
1692 
1693   NS_ASSERTION(aPrincipal, "We expect passing a principal here.");
1694 
1695   // If the principal is given, we use this principal directly. Otherwise,
1696   // we fallback to use the system principal.
1697   if (!aPrincipal) {
1698     loadingPrincipal = nsContentUtils::GetSystemPrincipal();
1699   }
1700 
1701   // dummy channel used to create a TCP connection.
1702   // we perform security checks on the *real* channel, responsible
1703   // for any network loads. this real channel just checks the TCP
1704   // pool if there is an available connection created by the
1705   // channel we create underneath - hence it's safe to use
1706   // the systemPrincipal as the loadingPrincipal for this channel.
1707   nsCOMPtr<nsIChannel> channel;
1708   rv =
1709       NewChannelFromURI2(aURI,
1710                          nullptr,  // aLoadingNode,
1711                          loadingPrincipal,
1712                          nullptr,  // aTriggeringPrincipal,
1713                          nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1714                          nsIContentPolicy::TYPE_OTHER, getter_AddRefs(channel));
1715   NS_ENSURE_SUCCESS(rv, rv);
1716 
1717   if (aAnonymous) {
1718     nsLoadFlags loadFlags = 0;
1719     channel->GetLoadFlags(&loadFlags);
1720     loadFlags |= nsIRequest::LOAD_ANONYMOUS;
1721     channel->SetLoadFlags(loadFlags);
1722   }
1723 
1724   nsCOMPtr<nsICancelable> cancelable;
1725   RefPtr<IOServiceProxyCallback> callback =
1726       new IOServiceProxyCallback(aCallbacks, this);
1727   nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
1728   if (pps2) {
1729     return pps2->AsyncResolve2(channel, 0, callback, nullptr,
1730                                getter_AddRefs(cancelable));
1731   }
1732   return pps->AsyncResolve(channel, 0, callback, nullptr,
1733                            getter_AddRefs(cancelable));
1734 }
1735 
1736 NS_IMETHODIMP
SpeculativeConnect(nsIURI * aURI,nsIInterfaceRequestor * aCallbacks)1737 nsIOService::SpeculativeConnect(nsIURI *aURI,
1738                                 nsIInterfaceRequestor *aCallbacks) {
1739   return SpeculativeConnectInternal(aURI, nullptr, aCallbacks, false);
1740 }
1741 
1742 NS_IMETHODIMP
SpeculativeConnect2(nsIURI * aURI,nsIPrincipal * aPrincipal,nsIInterfaceRequestor * aCallbacks)1743 nsIOService::SpeculativeConnect2(nsIURI *aURI, nsIPrincipal *aPrincipal,
1744                                  nsIInterfaceRequestor *aCallbacks) {
1745   return SpeculativeConnectInternal(aURI, aPrincipal, aCallbacks, false);
1746 }
1747 
1748 NS_IMETHODIMP
SpeculativeAnonymousConnect(nsIURI * aURI,nsIInterfaceRequestor * aCallbacks)1749 nsIOService::SpeculativeAnonymousConnect(nsIURI *aURI,
1750                                          nsIInterfaceRequestor *aCallbacks) {
1751   return SpeculativeConnectInternal(aURI, nullptr, aCallbacks, true);
1752 }
1753 
1754 NS_IMETHODIMP
SpeculativeAnonymousConnect2(nsIURI * aURI,nsIPrincipal * aPrincipal,nsIInterfaceRequestor * aCallbacks)1755 nsIOService::SpeculativeAnonymousConnect2(nsIURI *aURI,
1756                                           nsIPrincipal *aPrincipal,
1757                                           nsIInterfaceRequestor *aCallbacks) {
1758   return SpeculativeConnectInternal(aURI, aPrincipal, aCallbacks, true);
1759 }
1760 
IsDataURIUniqueOpaqueOrigin()1761 /*static*/ bool nsIOService::IsDataURIUniqueOpaqueOrigin() {
1762   return sIsDataURIUniqueOpaqueOrigin;
1763 }
1764 
BlockToplevelDataUriNavigations()1765 /*static*/ bool nsIOService::BlockToplevelDataUriNavigations() {
1766   return sBlockToplevelDataUriNavigations;
1767 }
1768 
1769 NS_IMETHODIMP
NotImplemented()1770 nsIOService::NotImplemented() { return NS_ERROR_NOT_IMPLEMENTED; }
1771 
1772 }  // namespace net
1773 }  // namespace mozilla
1774