Source code

Revision control

Other Tools

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