Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "mozilla/DebugOnly.h"
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 "nsIErrorService.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 "nsNetCID.h"
24 #include "nsCRT.h"
25 #include "nsSimpleNestedURI.h"
26 #include "nsNetUtil.h"
27 #include "nsTArray.h"
28 #include "nsIConsoleService.h"
29 #include "nsIUploadChannel2.h"
30 #include "nsXULAppAPI.h"
31 #include "nsIProtocolProxyCallback.h"
32 #include "nsICancelable.h"
33 #include "nsINetworkLinkService.h"
34 #include "nsPISocketTransportService.h"
35 #include "nsAsyncRedirectVerifyHelper.h"
36 #include "nsURLHelper.h"
37 #include "nsPIDNSService.h"
38 #include "nsIProtocolProxyService2.h"
39 #include "MainThreadUtils.h"
41 #if defined(XP_WIN)
42 #include "nsNativeConnectionHelper.h"
43 #endif
45 using namespace mozilla;
47 #define PORT_PREF_PREFIX "network.security.ports."
48 #define PORT_PREF(x) PORT_PREF_PREFIX x
49 #define AUTODIAL_PREF "network.autodial-helper.enabled"
50 #define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status"
52 // Nb: these have been misnomers since bug 715770 removed the buffer cache.
53 // "network.segment.count" and "network.segment.size" would be better names,
54 // but the old names are still used to preserve backward compatibility.
55 #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count"
56 #define NECKO_BUFFER_CACHE_SIZE_PREF "network.buffer.cache.size"
58 #define MAX_RECURSION_COUNT 50
60 nsIOService* gIOService = nullptr;
61 static bool gHasWarnedUploadChannel2;
63 // A general port blacklist. Connections to these ports will not be allowed unless
64 // the protocol overrides.
65 //
66 // TODO: I am sure that there are more ports to be added.
67 // This cut is based on the classic mozilla codebase
69 int16_t gBadPortList[] = {
70 1, // tcpmux
71 7, // echo
72 9, // discard
73 11, // systat
74 13, // daytime
75 15, // netstat
76 17, // qotd
77 19, // chargen
78 20, // ftp-data
79 21, // ftp-cntl
80 22, // ssh
81 23, // telnet
82 25, // smtp
83 37, // time
84 42, // name
85 43, // nicname
86 53, // domain
87 77, // priv-rjs
88 79, // finger
89 87, // ttylink
90 95, // supdup
91 101, // hostriame
92 102, // iso-tsap
93 103, // gppitnp
94 104, // acr-nema
95 109, // pop2
96 110, // pop3
97 111, // sunrpc
98 113, // auth
99 115, // sftp
100 117, // uucp-path
101 119, // nntp
102 123, // NTP
103 135, // loc-srv / epmap
104 139, // netbios
105 143, // imap2
106 179, // BGP
107 389, // ldap
108 465, // smtp+ssl
109 512, // print / exec
110 513, // login
111 514, // shell
112 515, // printer
113 526, // tempo
114 530, // courier
115 531, // Chat
116 532, // netnews
117 540, // uucp
118 556, // remotefs
119 563, // nntp+ssl
120 587, //
121 601, //
122 636, // ldap+ssl
123 993, // imap+ssl
124 995, // pop3+ssl
125 2049, // nfs
126 4045, // lockd
127 6000, // x11
128 0, // This MUST be zero so that we can populating the array
129 };
131 static const char kProfileChangeNetTeardownTopic[] = "profile-change-net-teardown";
132 static const char kProfileChangeNetRestoreTopic[] = "profile-change-net-restore";
133 static const char kProfileDoChange[] = "profile-do-change";
135 // Necko buffer defaults
136 uint32_t nsIOService::gDefaultSegmentSize = 4096;
137 uint32_t nsIOService::gDefaultSegmentCount = 24;
139 ////////////////////////////////////////////////////////////////////////////////
141 nsIOService::nsIOService()
142 : mOffline(true)
143 , mOfflineForProfileChange(false)
144 , mManageOfflineStatus(false)
145 , mSettingOffline(false)
146 , mSetOfflineValue(false)
147 , mShutdown(false)
148 , mNetworkLinkServiceInitialized(false)
149 , mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
150 , mAutoDialEnabled(false)
151 {
152 }
154 nsresult
155 nsIOService::Init()
156 {
157 nsresult rv;
159 // We need to get references to the DNS service so that we can shut it
160 // down later. If we wait until the nsIOService is being shut down,
161 // GetService will fail at that point.
163 mDNSService = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
164 if (NS_FAILED(rv)) {
165 NS_WARNING("failed to get DNS service");
166 return rv;
167 }
169 // XXX hack until xpidl supports error info directly (bug 13423)
170 nsCOMPtr<nsIErrorService> errorService = do_GetService(NS_ERRORSERVICE_CONTRACTID);
171 if (errorService) {
172 errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_NETWORK, NECKO_MSGS_URL);
173 }
174 else
175 NS_WARNING("failed to get error service");
177 // setup our bad port list stuff
178 for(int i=0; gBadPortList[i]; i++)
179 mRestrictedPortList.AppendElement(gBadPortList[i]);
181 // Further modifications to the port list come from prefs
182 nsCOMPtr<nsIPrefBranch> prefBranch;
183 GetPrefBranch(getter_AddRefs(prefBranch));
184 if (prefBranch) {
185 prefBranch->AddObserver(PORT_PREF_PREFIX, this, true);
186 prefBranch->AddObserver(AUTODIAL_PREF, this, true);
187 prefBranch->AddObserver(MANAGE_OFFLINE_STATUS_PREF, this, true);
188 prefBranch->AddObserver(NECKO_BUFFER_CACHE_COUNT_PREF, this, true);
189 prefBranch->AddObserver(NECKO_BUFFER_CACHE_SIZE_PREF, this, true);
190 PrefsChanged(prefBranch);
191 }
193 // Register for profile change notifications
194 nsCOMPtr<nsIObserverService> observerService =
195 mozilla::services::GetObserverService();
196 if (observerService) {
197 observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
198 observerService->AddObserver(this, kProfileChangeNetRestoreTopic, true);
199 observerService->AddObserver(this, kProfileDoChange, true);
200 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
201 observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
202 }
203 else
204 NS_WARNING("failed to get observer service");
206 gIOService = this;
208 InitializeNetworkLinkService();
210 return NS_OK;
211 }
214 nsIOService::~nsIOService()
215 {
216 gIOService = nullptr;
217 }
219 nsresult
220 nsIOService::InitializeSocketTransportService()
221 {
222 nsresult rv = NS_OK;
224 if (!mSocketTransportService) {
225 mSocketTransportService = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
226 if (NS_FAILED(rv)) {
227 NS_WARNING("failed to get socket transport service");
228 }
229 }
231 if (mSocketTransportService) {
232 rv = mSocketTransportService->Init();
233 NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
234 mSocketTransportService->SetAutodialEnabled(mAutoDialEnabled);
235 mSocketTransportService->SetOffline(false);
236 }
238 return rv;
239 }
241 nsresult
242 nsIOService::InitializeNetworkLinkService()
243 {
244 nsresult rv = NS_OK;
246 if (mNetworkLinkServiceInitialized)
247 return rv;
249 if (!NS_IsMainThread()) {
250 NS_WARNING("Network link service should be created on main thread");
251 return NS_ERROR_FAILURE;
252 }
254 // go into managed mode if we can, and chrome process
255 if (XRE_GetProcessType() == GeckoProcessType_Default)
256 {
257 mNetworkLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
258 }
260 if (mNetworkLinkService) {
261 mNetworkLinkServiceInitialized = true;
262 }
263 else {
264 // We can't really determine if the machine has a usable network connection,
265 // so let's cross our fingers!
266 mManageOfflineStatus = false;
267 }
270 if (mManageOfflineStatus)
271 TrackNetworkLinkStatusForOffline();
272 else
273 SetOffline(false);
275 return rv;
276 }
278 nsIOService*
279 nsIOService::GetInstance() {
280 if (!gIOService) {
281 gIOService = new nsIOService();
282 if (!gIOService)
283 return nullptr;
284 NS_ADDREF(gIOService);
286 nsresult rv = gIOService->Init();
287 if (NS_FAILED(rv)) {
288 NS_RELEASE(gIOService);
289 return nullptr;
290 }
291 return gIOService;
292 }
293 NS_ADDREF(gIOService);
294 return gIOService;
295 }
297 NS_IMPL_ISUPPORTS(nsIOService,
298 nsIIOService,
299 nsIIOService2,
300 nsINetUtil,
301 nsISpeculativeConnect,
302 nsIObserver,
303 nsISupportsWeakReference)
305 ////////////////////////////////////////////////////////////////////////////////
307 nsresult
308 nsIOService::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
309 uint32_t flags,
310 nsAsyncRedirectVerifyHelper *helper)
311 {
312 nsCOMPtr<nsIChannelEventSink> sink =
313 do_GetService(NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID);
314 if (sink) {
315 nsresult rv = helper->DelegateOnChannelRedirect(sink, oldChan,
316 newChan, flags);
317 if (NS_FAILED(rv))
318 return rv;
319 }
321 // Finally, our category
322 nsCOMArray<nsIChannelEventSink> entries;
323 mChannelEventSinks.GetEntries(entries);
324 int32_t len = entries.Count();
325 for (int32_t i = 0; i < len; ++i) {
326 nsresult rv = helper->DelegateOnChannelRedirect(entries[i], oldChan,
327 newChan, flags);
328 if (NS_FAILED(rv))
329 return rv;
330 }
331 return NS_OK;
332 }
334 nsresult
335 nsIOService::CacheProtocolHandler(const char *scheme, nsIProtocolHandler *handler)
336 {
337 for (unsigned int i=0; i<NS_N(gScheme); i++)
338 {
339 if (!nsCRT::strcasecmp(scheme, gScheme[i]))
340 {
341 nsresult rv;
342 NS_ASSERTION(!mWeakHandler[i], "Protocol handler already cached");
343 // Make sure the handler supports weak references.
344 nsCOMPtr<nsISupportsWeakReference> factoryPtr = do_QueryInterface(handler, &rv);
345 if (!factoryPtr)
346 {
347 // Don't cache handlers that don't support weak reference as
348 // there is real danger of a circular reference.
349 #ifdef DEBUG_dp
350 printf("DEBUG: %s protcol handler doesn't support weak ref. Not cached.\n", scheme);
351 #endif /* DEBUG_dp */
352 return NS_ERROR_FAILURE;
353 }
354 mWeakHandler[i] = do_GetWeakReference(handler);
355 return NS_OK;
356 }
357 }
358 return NS_ERROR_FAILURE;
359 }
361 nsresult
362 nsIOService::GetCachedProtocolHandler(const char *scheme, nsIProtocolHandler **result, uint32_t start, uint32_t end)
363 {
364 uint32_t len = end - start - 1;
365 for (unsigned int i=0; i<NS_N(gScheme); i++)
366 {
367 if (!mWeakHandler[i])
368 continue;
370 // handle unterminated strings
371 // start is inclusive, end is exclusive, len = end - start - 1
372 if (end ? (!nsCRT::strncasecmp(scheme + start, gScheme[i], len)
373 && gScheme[i][len] == '\0')
374 : (!nsCRT::strcasecmp(scheme, gScheme[i])))
375 {
376 return CallQueryReferent(mWeakHandler[i].get(), result);
377 }
378 }
379 return NS_ERROR_FAILURE;
380 }
382 NS_IMETHODIMP
383 nsIOService::GetProtocolHandler(const char* scheme, nsIProtocolHandler* *result)
384 {
385 nsresult rv;
387 NS_ENSURE_ARG_POINTER(scheme);
388 // XXX we may want to speed this up by introducing our own protocol
389 // scheme -> protocol handler mapping, avoiding the string manipulation
390 // and service manager stuff
392 rv = GetCachedProtocolHandler(scheme, result);
393 if (NS_SUCCEEDED(rv))
394 return rv;
396 bool externalProtocol = false;
397 nsCOMPtr<nsIPrefBranch> prefBranch;
398 GetPrefBranch(getter_AddRefs(prefBranch));
399 if (prefBranch) {
400 nsAutoCString externalProtocolPref("network.protocol-handler.external.");
401 externalProtocolPref += scheme;
402 rv = prefBranch->GetBoolPref(externalProtocolPref.get(), &externalProtocol);
403 if (NS_FAILED(rv)) {
404 externalProtocol = false;
405 }
406 }
408 if (!externalProtocol) {
409 nsAutoCString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
410 contractID += scheme;
411 ToLowerCase(contractID);
413 rv = CallGetService(contractID.get(), result);
414 if (NS_SUCCEEDED(rv)) {
415 CacheProtocolHandler(scheme, *result);
416 return rv;
417 }
419 #ifdef MOZ_X11
420 // check to see whether GVFS can handle this URI scheme. if it can
421 // create a nsIURI for the "scheme:", then we assume it has support for
422 // the requested protocol. otherwise, we failover to using the default
423 // protocol handler.
425 rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"moz-gio",
426 result);
427 if (NS_SUCCEEDED(rv)) {
428 nsAutoCString spec(scheme);
429 spec.Append(':');
431 nsIURI *uri;
432 rv = (*result)->NewURI(spec, nullptr, nullptr, &uri);
433 if (NS_SUCCEEDED(rv)) {
434 NS_RELEASE(uri);
435 return rv;
436 }
438 NS_RELEASE(*result);
439 }
441 // check to see whether GnomeVFS can handle this URI scheme. if it can
442 // create a nsIURI for the "scheme:", then we assume it has support for
443 // the requested protocol. otherwise, we failover to using the default
444 // protocol handler.
446 // XXX should this be generalized into something that searches a
447 // category? (see bug 234714)
449 rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"moz-gnomevfs",
450 result);
451 if (NS_SUCCEEDED(rv)) {
452 nsAutoCString spec(scheme);
453 spec.Append(':');
455 nsIURI *uri;
456 rv = (*result)->NewURI(spec, nullptr, nullptr, &uri);
457 if (NS_SUCCEEDED(rv)) {
458 NS_RELEASE(uri);
459 return rv;
460 }
462 NS_RELEASE(*result);
463 }
464 #endif
465 }
467 // Okay we don't have a protocol handler to handle this url type, so use
468 // the default protocol handler. This will cause urls to get dispatched
469 // out to the OS ('cause we can't do anything with them) when we try to
470 // read from a channel created by the default protocol handler.
472 rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default",
473 result);
474 if (NS_FAILED(rv))
475 return NS_ERROR_UNKNOWN_PROTOCOL;
477 return rv;
478 }
480 NS_IMETHODIMP
481 nsIOService::ExtractScheme(const nsACString &inURI, nsACString &scheme)
482 {
483 return net_ExtractURLScheme(inURI, nullptr, nullptr, &scheme);
484 }
486 NS_IMETHODIMP
487 nsIOService::GetProtocolFlags(const char* scheme, uint32_t *flags)
488 {
489 nsCOMPtr<nsIProtocolHandler> handler;
490 nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
491 if (NS_FAILED(rv)) return rv;
493 rv = handler->GetProtocolFlags(flags);
494 return rv;
495 }
497 class AutoIncrement
498 {
499 public:
500 AutoIncrement(uint32_t *var) : mVar(var)
501 {
502 ++*var;
503 }
504 ~AutoIncrement()
505 {
506 --*mVar;
507 }
508 private:
509 uint32_t *mVar;
510 };
512 nsresult
513 nsIOService::NewURI(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIURI **result)
514 {
515 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
517 static uint32_t recursionCount = 0;
518 if (recursionCount >= MAX_RECURSION_COUNT)
519 return NS_ERROR_MALFORMED_URI;
520 AutoIncrement inc(&recursionCount);
522 nsAutoCString scheme;
523 nsresult rv = ExtractScheme(aSpec, scheme);
524 if (NS_FAILED(rv)) {
525 // then aSpec is relative
526 if (!aBaseURI)
527 return NS_ERROR_MALFORMED_URI;
529 rv = aBaseURI->GetScheme(scheme);
530 if (NS_FAILED(rv)) return rv;
531 }
533 // now get the handler for this scheme
534 nsCOMPtr<nsIProtocolHandler> handler;
535 rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
536 if (NS_FAILED(rv)) return rv;
538 return handler->NewURI(aSpec, aCharset, aBaseURI, result);
539 }
542 NS_IMETHODIMP
543 nsIOService::NewFileURI(nsIFile *file, nsIURI **result)
544 {
545 nsresult rv;
546 NS_ENSURE_ARG_POINTER(file);
548 nsCOMPtr<nsIProtocolHandler> handler;
550 rv = GetProtocolHandler("file", getter_AddRefs(handler));
551 if (NS_FAILED(rv)) return rv;
553 nsCOMPtr<nsIFileProtocolHandler> fileHandler( do_QueryInterface(handler, &rv) );
554 if (NS_FAILED(rv)) return rv;
556 return fileHandler->NewFileURI(file, result);
557 }
559 NS_IMETHODIMP
560 nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result)
561 {
562 return NewChannelFromURIWithProxyFlags(aURI, nullptr, 0, result);
563 }
565 NS_IMETHODIMP
566 nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI,
567 nsIURI *aProxyURI,
568 uint32_t aProxyFlags,
569 nsIChannel **result)
570 {
571 nsresult rv;
572 NS_ENSURE_ARG_POINTER(aURI);
574 nsAutoCString scheme;
575 rv = aURI->GetScheme(scheme);
576 if (NS_FAILED(rv))
577 return rv;
579 nsCOMPtr<nsIProtocolHandler> handler;
580 rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
581 if (NS_FAILED(rv))
582 return rv;
584 uint32_t protoFlags;
585 rv = handler->GetProtocolFlags(&protoFlags);
586 if (NS_FAILED(rv))
587 return rv;
589 nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler);
590 if (pph)
591 rv = pph->NewProxiedChannel(aURI, nullptr, aProxyFlags, aProxyURI, result);
592 else
593 rv = handler->NewChannel(aURI, result);
594 NS_ENSURE_SUCCESS(rv, rv);
596 // Some extensions override the http protocol handler and provide their own
597 // implementation. The channels returned from that implementation doesn't
598 // seem to always implement the nsIUploadChannel2 interface, presumably
599 // because it's a new interface.
600 // Eventually we should remove this and simply require that http channels
601 // implement the new interface.
602 // See bug 529041
603 if (!gHasWarnedUploadChannel2 && scheme.EqualsLiteral("http")) {
604 nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(*result);
605 if (!uploadChannel2) {
606 nsCOMPtr<nsIConsoleService> consoleService =
607 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
608 if (consoleService) {
609 consoleService->LogStringMessage(NS_LITERAL_STRING(
610 "Http channel implementation doesn't support nsIUploadChannel2. An extension has supplied a non-functional http protocol handler. This will break behavior and in future releases not work at all."
611 ).get());
612 }
613 gHasWarnedUploadChannel2 = true;
614 }
615 }
617 return NS_OK;
618 }
620 NS_IMETHODIMP
621 nsIOService::NewChannel(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIChannel **result)
622 {
623 nsresult rv;
624 nsCOMPtr<nsIURI> uri;
625 rv = NewURI(aSpec, aCharset, aBaseURI, getter_AddRefs(uri));
626 if (NS_FAILED(rv)) return rv;
628 return NewChannelFromURI(uri, result);
629 }
631 bool
632 nsIOService::IsLinkUp()
633 {
634 InitializeNetworkLinkService();
636 if (!mNetworkLinkService) {
637 // We cannot decide, assume the link is up
638 return true;
639 }
641 bool isLinkUp;
642 nsresult rv;
643 rv = mNetworkLinkService->GetIsLinkUp(&isLinkUp);
644 if (NS_FAILED(rv)) {
645 return true;
646 }
648 return isLinkUp;
649 }
651 NS_IMETHODIMP
652 nsIOService::GetOffline(bool *offline)
653 {
654 *offline = mOffline;
655 return NS_OK;
656 }
658 NS_IMETHODIMP
659 nsIOService::SetOffline(bool offline)
660 {
661 // When someone wants to go online (!offline) after we got XPCOM shutdown
662 // throw ERROR_NOT_AVAILABLE to prevent return to online state.
663 if ((mShutdown || mOfflineForProfileChange) && !offline)
664 return NS_ERROR_NOT_AVAILABLE;
666 // SetOffline() may re-enter while it's shutting down services.
667 // If that happens, save the most recent value and it will be
668 // processed when the first SetOffline() call is done bringing
669 // down the service.
670 mSetOfflineValue = offline;
671 if (mSettingOffline) {
672 return NS_OK;
673 }
675 mSettingOffline = true;
677 nsCOMPtr<nsIObserverService> observerService =
678 mozilla::services::GetObserverService();
680 NS_ASSERTION(observerService, "The observer service should not be null");
682 if (XRE_GetProcessType() == GeckoProcessType_Default) {
683 if (observerService) {
684 (void)observerService->NotifyObservers(nullptr,
685 NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, offline ?
686 MOZ_UTF16("true") :
687 MOZ_UTF16("false"));
688 }
689 }
691 nsIIOService *subject = static_cast<nsIIOService *>(this);
692 while (mSetOfflineValue != mOffline) {
693 offline = mSetOfflineValue;
695 if (offline && !mOffline) {
696 NS_NAMED_LITERAL_STRING(offlineString, NS_IOSERVICE_OFFLINE);
697 mOffline = true; // indicate we're trying to shutdown
699 // don't care if notifications fail
700 if (observerService)
701 observerService->NotifyObservers(subject,
702 NS_IOSERVICE_GOING_OFFLINE_TOPIC,
703 offlineString.get());
705 if (mDNSService)
706 mDNSService->SetOffline(true);
708 if (mSocketTransportService)
709 mSocketTransportService->SetOffline(true);
711 if (observerService)
712 observerService->NotifyObservers(subject,
713 NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
714 offlineString.get());
715 }
716 else if (!offline && mOffline) {
717 // go online
718 if (mDNSService) {
719 mDNSService->SetOffline(false);
720 DebugOnly<nsresult> rv = mDNSService->Init();
721 NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed");
722 }
723 InitializeSocketTransportService();
724 mOffline = false; // indicate success only AFTER we've
725 // brought up the services
727 // trigger a PAC reload when we come back online
728 if (mProxyService)
729 mProxyService->ReloadPAC();
731 // don't care if notification fails
732 if (observerService)
733 observerService->NotifyObservers(subject,
734 NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
735 NS_LITERAL_STRING(NS_IOSERVICE_ONLINE).get());
736 }
737 }
739 // Don't notify here, as the above notifications (if used) suffice.
740 if ((mShutdown || mOfflineForProfileChange) && mOffline) {
741 // be sure to try and shutdown both (even if the first fails)...
742 // shutdown dns service first, because it has callbacks for socket transport
743 if (mDNSService) {
744 DebugOnly<nsresult> rv = mDNSService->Shutdown();
745 NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service shutdown failed");
746 }
747 if (mSocketTransportService) {
748 DebugOnly<nsresult> rv = mSocketTransportService->Shutdown();
749 NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed");
750 }
751 }
753 mSettingOffline = false;
755 return NS_OK;
756 }
759 NS_IMETHODIMP
760 nsIOService::AllowPort(int32_t inPort, const char *scheme, bool *_retval)
761 {
762 int16_t port = inPort;
763 if (port == -1) {
764 *_retval = true;
765 return NS_OK;
766 }
768 if (port == 0) {
769 *_retval = false;
770 return NS_OK;
771 }
773 // first check to see if the port is in our blacklist:
774 int32_t badPortListCnt = mRestrictedPortList.Length();
775 for (int i=0; i<badPortListCnt; i++)
776 {
777 if (port == mRestrictedPortList[i])
778 {
779 *_retval = false;
781 // check to see if the protocol wants to override
782 if (!scheme)
783 return NS_OK;
785 nsCOMPtr<nsIProtocolHandler> handler;
786 nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
787 if (NS_FAILED(rv)) return rv;
789 // let the protocol handler decide
790 return handler->AllowPort(port, scheme, _retval);
791 }
792 }
794 *_retval = true;
795 return NS_OK;
796 }
798 ////////////////////////////////////////////////////////////////////////////////
800 void
801 nsIOService::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
802 {
803 if (!prefs) return;
805 // Look for extra ports to block
806 if (!pref || strcmp(pref, PORT_PREF("banned")) == 0)
807 ParsePortList(prefs, PORT_PREF("banned"), false);
809 // ...as well as previous blocks to remove.
810 if (!pref || strcmp(pref, PORT_PREF("banned.override")) == 0)
811 ParsePortList(prefs, PORT_PREF("banned.override"), true);
813 if (!pref || strcmp(pref, AUTODIAL_PREF) == 0) {
814 bool enableAutodial = false;
815 nsresult rv = prefs->GetBoolPref(AUTODIAL_PREF, &enableAutodial);
816 // If pref not found, default to disabled.
817 mAutoDialEnabled = enableAutodial;
818 if (NS_SUCCEEDED(rv)) {
819 if (mSocketTransportService)
820 mSocketTransportService->SetAutodialEnabled(enableAutodial);
821 }
822 }
824 if (!pref || strcmp(pref, MANAGE_OFFLINE_STATUS_PREF) == 0) {
825 bool manage;
826 if (mNetworkLinkServiceInitialized &&
827 NS_SUCCEEDED(prefs->GetBoolPref(MANAGE_OFFLINE_STATUS_PREF,
828 &manage)))
829 SetManageOfflineStatus(manage);
830 }
832 if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_COUNT_PREF) == 0) {
833 int32_t count;
834 if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_COUNT_PREF,
835 &count)))
836 /* check for bogus values and default if we find such a value */
837 if (count > 0)
838 gDefaultSegmentCount = count;
839 }
841 if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_SIZE_PREF) == 0) {
842 int32_t size;
843 if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_SIZE_PREF,
844 &size)))
845 /* check for bogus values and default if we find such a value
846 * the upper limit here is arbitrary. having a 1mb segment size
847 * is pretty crazy. if you remove this, consider adding some
848 * integer rollover test.
849 */
850 if (size > 0 && size < 1024*1024)
851 gDefaultSegmentSize = size;
852 NS_WARN_IF_FALSE( (!(size & (size - 1))) , "network segment size is not a power of 2!");
853 }
854 }
856 void
857 nsIOService::ParsePortList(nsIPrefBranch *prefBranch, const char *pref, bool remove)
858 {
859 nsXPIDLCString portList;
861 // Get a pref string and chop it up into a list of ports.
862 prefBranch->GetCharPref(pref, getter_Copies(portList));
863 if (portList) {
864 nsTArray<nsCString> portListArray;
865 ParseString(portList, ',', portListArray);
866 uint32_t index;
867 for (index=0; index < portListArray.Length(); index++) {
868 portListArray[index].StripWhitespace();
869 int32_t portBegin, portEnd;
871 if (PR_sscanf(portListArray[index].get(), "%d-%d", &portBegin, &portEnd) == 2) {
872 if ((portBegin < 65536) && (portEnd < 65536)) {
873 int32_t curPort;
874 if (remove) {
875 for (curPort=portBegin; curPort <= portEnd; curPort++)
876 mRestrictedPortList.RemoveElement(curPort);
877 } else {
878 for (curPort=portBegin; curPort <= portEnd; curPort++)
879 mRestrictedPortList.AppendElement(curPort);
880 }
881 }
882 } else {
883 nsresult aErrorCode;
884 int32_t port = portListArray[index].ToInteger(&aErrorCode);
885 if (NS_SUCCEEDED(aErrorCode) && port < 65536) {
886 if (remove)
887 mRestrictedPortList.RemoveElement(port);
888 else
889 mRestrictedPortList.AppendElement(port);
890 }
891 }
893 }
894 }
895 }
897 void
898 nsIOService::GetPrefBranch(nsIPrefBranch **result)
899 {
900 *result = nullptr;
901 CallGetService(NS_PREFSERVICE_CONTRACTID, result);
902 }
904 // nsIObserver interface
905 NS_IMETHODIMP
906 nsIOService::Observe(nsISupports *subject,
907 const char *topic,
908 const char16_t *data)
909 {
910 if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
911 nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
912 if (prefBranch)
913 PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
914 }
915 else if (!strcmp(topic, kProfileChangeNetTeardownTopic)) {
916 if (!mOffline) {
917 mOfflineForProfileChange = true;
918 SetOffline(true);
919 }
920 }
921 else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) {
922 if (mOfflineForProfileChange) {
923 mOfflineForProfileChange = false;
924 if (!mManageOfflineStatus ||
925 NS_FAILED(TrackNetworkLinkStatusForOffline())) {
926 SetOffline(false);
927 }
928 }
929 }
930 else if (!strcmp(topic, kProfileDoChange)) {
931 if (data && NS_LITERAL_STRING("startup").Equals(data)) {
932 // Lazy initialization of network link service (see bug 620472)
933 InitializeNetworkLinkService();
934 // Set up the initilization flag regardless the actuall result.
935 // If we fail here, we will fail always on.
936 mNetworkLinkServiceInitialized = true;
937 // And now reflect the preference setting
938 nsCOMPtr<nsIPrefBranch> prefBranch;
939 GetPrefBranch(getter_AddRefs(prefBranch));
940 PrefsChanged(prefBranch, MANAGE_OFFLINE_STATUS_PREF);
941 }
942 }
943 else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
944 // Remember we passed XPCOM shutdown notification to prevent any
945 // changes of the offline status from now. We must not allow going
946 // online after this point.
947 mShutdown = true;
949 SetOffline(true);
951 // Break circular reference.
952 mProxyService = nullptr;
953 }
954 else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
955 if (!mOfflineForProfileChange && mManageOfflineStatus) {
956 TrackNetworkLinkStatusForOffline();
957 }
958 }
960 return NS_OK;
961 }
963 // nsINetUtil interface
964 NS_IMETHODIMP
965 nsIOService::ParseContentType(const nsACString &aTypeHeader,
966 nsACString &aCharset,
967 bool *aHadCharset,
968 nsACString &aContentType)
969 {
970 net_ParseContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
971 return NS_OK;
972 }
974 NS_IMETHODIMP
975 nsIOService::ProtocolHasFlags(nsIURI *uri,
976 uint32_t flags,
977 bool *result)
978 {
979 NS_ENSURE_ARG(uri);
981 *result = false;
982 nsAutoCString scheme;
983 nsresult rv = uri->GetScheme(scheme);
984 NS_ENSURE_SUCCESS(rv, rv);
986 uint32_t protocolFlags;
987 rv = GetProtocolFlags(scheme.get(), &protocolFlags);
989 if (NS_SUCCEEDED(rv)) {
990 *result = (protocolFlags & flags) == flags;
991 }
993 return rv;
994 }
996 NS_IMETHODIMP
997 nsIOService::URIChainHasFlags(nsIURI *uri,
998 uint32_t flags,
999 bool *result)
1000 {
1001 nsresult rv = ProtocolHasFlags(uri, flags, result);
1002 NS_ENSURE_SUCCESS(rv, rv);
1004 if (*result) {
1005 return rv;
1006 }
1008 // Dig deeper into the chain. Note that this is not a do/while loop to
1009 // avoid the extra addref/release on |uri| in the common (non-nested) case.
1010 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
1011 while (nestedURI) {
1012 nsCOMPtr<nsIURI> innerURI;
1013 rv = nestedURI->GetInnerURI(getter_AddRefs(innerURI));
1014 NS_ENSURE_SUCCESS(rv, rv);
1016 rv = ProtocolHasFlags(innerURI, flags, result);
1018 if (*result) {
1019 return rv;
1020 }
1022 nestedURI = do_QueryInterface(innerURI);
1023 }
1025 return rv;
1026 }
1028 NS_IMETHODIMP
1029 nsIOService::ToImmutableURI(nsIURI* uri, nsIURI** result)
1030 {
1031 if (!uri) {
1032 *result = nullptr;
1033 return NS_OK;
1034 }
1036 nsresult rv = NS_EnsureSafeToReturn(uri, result);
1037 NS_ENSURE_SUCCESS(rv, rv);
1039 NS_TryToSetImmutable(*result);
1040 return NS_OK;
1041 }
1043 NS_IMETHODIMP
1044 nsIOService::NewSimpleNestedURI(nsIURI* aURI, nsIURI** aResult)
1045 {
1046 NS_ENSURE_ARG(aURI);
1048 nsCOMPtr<nsIURI> safeURI;
1049 nsresult rv = NS_EnsureSafeToReturn(aURI, getter_AddRefs(safeURI));
1050 NS_ENSURE_SUCCESS(rv, rv);
1052 NS_IF_ADDREF(*aResult = new nsSimpleNestedURI(safeURI));
1053 return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1054 }
1056 NS_IMETHODIMP
1057 nsIOService::SetManageOfflineStatus(bool aManage) {
1058 nsresult rv = NS_OK;
1060 // SetManageOfflineStatus must throw when we fail to go from non-managed
1061 // to managed. Usually because there is no link monitoring service
1062 // available. Failure to do this switch is detected by a failure of
1063 // TrackNetworkLinkStatusForOffline(). When there is no network link
1064 // available during call to InitializeNetworkLinkService(), application is
1065 // put to offline mode. And when we change mMangeOfflineStatus to false
1066 // on the next line we get stuck on being offline even though the link
1067 // becomes later available.
1068 bool wasManaged = mManageOfflineStatus;
1069 mManageOfflineStatus = aManage;
1071 InitializeNetworkLinkService();
1073 if (mManageOfflineStatus && !wasManaged) {
1074 rv = TrackNetworkLinkStatusForOffline();
1075 if (NS_FAILED(rv))
1076 mManageOfflineStatus = false;
1077 }
1078 return rv;
1079 }
1081 NS_IMETHODIMP
1082 nsIOService::GetManageOfflineStatus(bool* aManage) {
1083 *aManage = mManageOfflineStatus;
1084 return NS_OK;
1085 }
1087 nsresult
1088 nsIOService::TrackNetworkLinkStatusForOffline()
1089 {
1090 NS_ASSERTION(mManageOfflineStatus,
1091 "Don't call this unless we're managing the offline status");
1092 if (!mNetworkLinkService)
1093 return NS_ERROR_FAILURE;
1095 if (mShutdown)
1096 return NS_ERROR_NOT_AVAILABLE;
1098 // check to make sure this won't collide with Autodial
1099 if (mSocketTransportService) {
1100 bool autodialEnabled = false;
1101 mSocketTransportService->GetAutodialEnabled(&autodialEnabled);
1102 // If autodialing-on-link-down is enabled, check if the OS auto dial
1103 // option is set to always autodial. If so, then we are
1104 // always up for the purposes of offline management.
1105 if (autodialEnabled) {
1106 #if defined(XP_WIN)
1107 // On Windows, we should first check with the OS
1108 // to see if autodial is enabled. If it is
1109 // enabled then we are allowed to manage the
1110 // offline state.
1111 if(nsNativeConnectionHelper::IsAutodialEnabled())
1112 return SetOffline(false);
1113 #else
1114 return SetOffline(false);
1115 #endif
1116 }
1117 }
1119 bool isUp;
1120 nsresult rv = mNetworkLinkService->GetIsLinkUp(&isUp);
1121 NS_ENSURE_SUCCESS(rv, rv);
1122 return SetOffline(!isUp);
1123 }
1125 NS_IMETHODIMP
1126 nsIOService::EscapeString(const nsACString& aString,
1127 uint32_t aEscapeType,
1128 nsACString& aResult)
1129 {
1130 NS_ENSURE_ARG_MAX(aEscapeType, 4);
1132 nsAutoCString stringCopy(aString);
1133 nsCString result;
1135 if (!NS_Escape(stringCopy, result, (nsEscapeMask) aEscapeType))
1136 return NS_ERROR_OUT_OF_MEMORY;
1138 aResult.Assign(result);
1140 return NS_OK;
1141 }
1143 NS_IMETHODIMP
1144 nsIOService::EscapeURL(const nsACString &aStr,
1145 uint32_t aFlags, nsACString &aResult)
1146 {
1147 aResult.Truncate();
1148 NS_EscapeURL(aStr.BeginReading(), aStr.Length(),
1149 aFlags | esc_AlwaysCopy, aResult);
1150 return NS_OK;
1151 }
1153 NS_IMETHODIMP
1154 nsIOService::UnescapeString(const nsACString &aStr,
1155 uint32_t aFlags, nsACString &aResult)
1156 {
1157 aResult.Truncate();
1158 NS_UnescapeURL(aStr.BeginReading(), aStr.Length(),
1159 aFlags | esc_AlwaysCopy, aResult);
1160 return NS_OK;
1161 }
1163 NS_IMETHODIMP
1164 nsIOService::ExtractCharsetFromContentType(const nsACString &aTypeHeader,
1165 nsACString &aCharset,
1166 int32_t *aCharsetStart,
1167 int32_t *aCharsetEnd,
1168 bool *aHadCharset)
1169 {
1170 nsAutoCString ignored;
1171 net_ParseContentType(aTypeHeader, ignored, aCharset, aHadCharset,
1172 aCharsetStart, aCharsetEnd);
1173 if (*aHadCharset && *aCharsetStart == *aCharsetEnd) {
1174 *aHadCharset = false;
1175 }
1176 return NS_OK;
1177 }
1179 // nsISpeculativeConnect
1180 class IOServiceProxyCallback MOZ_FINAL : public nsIProtocolProxyCallback
1181 {
1182 public:
1183 NS_DECL_ISUPPORTS
1184 NS_DECL_NSIPROTOCOLPROXYCALLBACK
1186 IOServiceProxyCallback(nsIInterfaceRequestor *aCallbacks,
1187 nsIOService *aIOService)
1188 : mCallbacks(aCallbacks)
1189 , mIOService(aIOService)
1190 { }
1192 private:
1193 nsRefPtr<nsIInterfaceRequestor> mCallbacks;
1194 nsRefPtr<nsIOService> mIOService;
1195 };
1197 NS_IMPL_ISUPPORTS(IOServiceProxyCallback, nsIProtocolProxyCallback)
1199 NS_IMETHODIMP
1200 IOServiceProxyCallback::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
1201 nsIProxyInfo *pi, nsresult status)
1202 {
1203 // Checking proxy status for speculative connect
1204 nsAutoCString type;
1205 if (NS_SUCCEEDED(status) && pi &&
1206 NS_SUCCEEDED(pi->GetType(type)) &&
1207 !type.EqualsLiteral("direct")) {
1208 // proxies dont do speculative connect
1209 return NS_OK;
1210 }
1212 nsCOMPtr<nsIURI> uri;
1213 nsresult rv = channel->GetURI(getter_AddRefs(uri));
1214 if (NS_FAILED(rv))
1215 return NS_OK;
1217 nsAutoCString scheme;
1218 rv = uri->GetScheme(scheme);
1219 if (NS_FAILED(rv))
1220 return NS_OK;
1222 nsCOMPtr<nsIProtocolHandler> handler;
1223 rv = mIOService->GetProtocolHandler(scheme.get(),
1224 getter_AddRefs(handler));
1225 if (NS_FAILED(rv))
1226 return NS_OK;
1228 nsCOMPtr<nsISpeculativeConnect> speculativeHandler =
1229 do_QueryInterface(handler);
1230 if (!speculativeHandler)
1231 return NS_OK;
1233 speculativeHandler->SpeculativeConnect(uri,
1234 mCallbacks);
1235 return NS_OK;
1236 }
1238 NS_IMETHODIMP
1239 nsIOService::SpeculativeConnect(nsIURI *aURI,
1240 nsIInterfaceRequestor *aCallbacks)
1241 {
1242 // Check for proxy information. If there is a proxy configured then a
1243 // speculative connect should not be performed because the potential
1244 // reward is slim with tcp peers closely located to the browser.
1245 nsresult rv;
1246 nsCOMPtr<nsIProtocolProxyService> pps =
1247 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
1248 if (NS_FAILED(rv))
1249 return rv;
1251 nsCOMPtr<nsIChannel> channel;
1252 rv = NewChannelFromURI(aURI, getter_AddRefs(channel));
1253 if (NS_FAILED(rv))
1254 return rv;
1256 nsCOMPtr<nsICancelable> cancelable;
1257 nsRefPtr<IOServiceProxyCallback> callback =
1258 new IOServiceProxyCallback(aCallbacks, this);
1259 return pps->AsyncResolve(channel, 0, callback, getter_AddRefs(cancelable));
1260 }