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