1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "NetworkConnectivityService.h"
7 #include "mozilla/ClearOnShutdown.h"
8 #include "mozilla/net/SocketProcessParent.h"
9 #include "mozilla/Preferences.h"
10 #include "mozilla/Services.h"
12 #include "nsIChannel.h"
13 #include "nsIOService.h"
14 #include "nsICancelable.h"
15 #include "xpcpublic.h"
16 #include "nsSocketTransport2.h"
17 #include "nsIHttpChannelInternal.h"
18 #include "nsINetworkLinkService.h"
19 #include "mozilla/StaticPrefs_network.h"
21 static mozilla::LazyLogModule
gNCSLog("NetworkConnectivityService");
23 #define LOG(args) MOZ_LOG(gNCSLog, mozilla::LogLevel::Debug, args)
28 NS_IMPL_ISUPPORTS(NetworkConnectivityService
, nsIDNSListener
, nsIObserver
,
29 nsINetworkConnectivityService
, nsIStreamListener
)
31 static StaticRefPtr
<NetworkConnectivityService
> gConnService
;
33 NetworkConnectivityService::NetworkConnectivityService()
39 mLock("nat64prefixes") {}
42 already_AddRefed
<NetworkConnectivityService
>
43 NetworkConnectivityService::GetSingleton() {
45 return do_AddRef(gConnService
);
48 RefPtr
<NetworkConnectivityService
> service
= new NetworkConnectivityService();
51 gConnService
= std::move(service
);
52 ClearOnShutdown(&gConnService
);
53 return do_AddRef(gConnService
);
56 nsresult
NetworkConnectivityService::Init() {
57 nsCOMPtr
<nsIObserverService
> observerService
=
58 mozilla::services::GetObserverService();
59 observerService
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
60 observerService
->AddObserver(this, NS_NETWORK_LINK_TOPIC
, false);
61 observerService
->AddObserver(this, "network:captive-portal-connectivity",
68 NetworkConnectivityService::GetDNSv4(ConnectivityState
* aState
) {
69 NS_ENSURE_ARG(aState
);
75 NetworkConnectivityService::GetDNSv6(ConnectivityState
* aState
) {
76 NS_ENSURE_ARG(aState
);
82 NetworkConnectivityService::GetIPv4(ConnectivityState
* aState
) {
83 NS_ENSURE_ARG(aState
);
89 NetworkConnectivityService::GetIPv6(ConnectivityState
* aState
) {
90 NS_ENSURE_ARG(aState
);
96 NetworkConnectivityService::GetNAT64(ConnectivityState
* aState
) {
97 NS_ENSURE_ARG(aState
);
102 already_AddRefed
<AddrInfo
> NetworkConnectivityService::MapNAT64IPs(
103 AddrInfo
* aNewRRSet
) {
104 // Add prefixes only if there are no IPv6 addresses.
105 // Expect that if aNewRRSet has IPv6 addresses, they must come
106 // before IPv4 addresses.
107 if (aNewRRSet
->Addresses().IsEmpty() ||
108 aNewRRSet
->Addresses()[0].raw
.family
== PR_AF_INET6
) {
109 return do_AddRef(aNewRRSet
);
112 // Currently we only add prefixes to the first IP's clones.
113 uint32_t ip
= aNewRRSet
->Addresses()[0].inet
.ip
;
114 nsTArray
<NetAddr
> addresses
= aNewRRSet
->Addresses().Clone();
117 MutexAutoLock
lock(mLock
);
118 for (const auto& prefix
: mNAT64Prefixes
) {
119 NetAddr addr
= NetAddr(prefix
);
121 // Copy the IPv4 address to the end
122 addr
.inet6
.ip
.u32
[3] = ip
;
124 // If we have both IPv4 and NAT64, we be could insourcing NAT64
125 // to avoid double NAT and improve performance. However, this
126 // breaks WebRTC, so we push it to the back.
127 addresses
.AppendElement(addr
);
131 auto builder
= aNewRRSet
->Build();
132 builder
.SetAddresses(std::move(addresses
));
133 return builder
.Finish();
136 // Returns true if a prefix was read and saved to the argument
137 static inline bool NAT64PrefixFromPref(NetAddr
* prefix
) {
138 nsAutoCString nat64PrefixPref
;
140 nsresult rv
= Preferences::GetCString(
141 "network.connectivity-service.nat64-prefix", nat64PrefixPref
);
142 return !(NS_FAILED(rv
) || nat64PrefixPref
.IsEmpty() ||
143 NS_FAILED(prefix
->InitFromString(nat64PrefixPref
)) ||
144 prefix
->raw
.family
!= PR_AF_INET6
);
147 static inline bool NAT64PrefixCompare(const NetAddr
& prefix1
,
148 const NetAddr
& prefix2
) {
149 // Compare the first 96 bits as 64 + 32
150 return prefix1
.inet6
.ip
.u64
[0] == prefix2
.inet6
.ip
.u64
[0] &&
151 prefix1
.inet6
.ip
.u32
[2] == prefix2
.inet6
.ip
.u32
[2];
154 void NetworkConnectivityService::PerformChecks() {
164 MutexAutoLock
lock(mLock
);
165 mNAT64Prefixes
.Clear();
167 // NAT64 checks might be disabled.
168 // Since We can't guarantee a DNS response, we should set up
169 // NAT64 manually now if needed.
171 NetAddr priorityPrefix
{};
172 bool havePrefix
= NAT64PrefixFromPref(&priorityPrefix
);
174 mNAT64Prefixes
.AppendElement(priorityPrefix
);
180 RecheckIPConnectivity();
183 static inline void NotifyObservers(const char* aTopic
) {
184 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
185 obs
->NotifyObservers(nullptr, aTopic
, nullptr);
188 void NetworkConnectivityService::SaveNAT64Prefixes(nsIDNSRecord
* aRecord
) {
189 nsCOMPtr
<nsIDNSAddrRecord
> rec
= do_QueryInterface(aRecord
);
190 MutexAutoLock
lock(mLock
);
191 mNAT64Prefixes
.Clear();
193 NetAddr priorityPrefix
{};
194 bool havePrefix
= NAT64PrefixFromPref(&priorityPrefix
);
197 mNAT64Prefixes
.AppendElement(priorityPrefix
);
202 mNAT64
= NOT_AVAILABLE
;
210 // use port 80 as dummy value for NetAddr
211 while (NS_SUCCEEDED(rec
->GetNextAddr(80, &addr
))) {
212 if (addr
.raw
.family
!= AF_INET6
|| addr
.IsIPAddrV4Mapped()) {
213 // These are not the kind of addresses we are looking for.
217 // RFC 7050 does not require the embedded IPv4 to be
218 // at the end of IPv6. In practice, and as we assume,
219 // it is always at the end.
220 // The embedded IP must be 192.0.0.170 or 192.0.0.171
222 // Clear the last bit to compare with the next one.
223 addr
.inet6
.ip
.u8
[15] &= ~(uint32_t)1;
224 if ((addr
.inet6
.ip
.u8
[12] != 192) || (addr
.inet6
.ip
.u8
[13] != 0) ||
225 (addr
.inet6
.ip
.u8
[14] != 0) || (addr
.inet6
.ip
.u8
[15] != 170)) {
229 mNAT64Prefixes
.AppendElement(addr
);
232 size_t length
= mNAT64Prefixes
.Length();
234 mNAT64
= NOT_AVAILABLE
;
238 // Remove duplicates. Typically a DNS64 resolver sends every
239 // prefix twice with address with different last bits. We want
240 // a list of unique prefixes while reordering is not allowed.
241 // We must not handle the case with an element in-between
242 // two identical ones, which is never the case for a properly
243 // configured DNS64 resolver.
245 NetAddr prev
= mNAT64Prefixes
[0];
247 for (size_t i
= 1; i
< length
; i
++) {
248 if (NAT64PrefixCompare(prev
, mNAT64Prefixes
[i
])) {
249 mNAT64Prefixes
.RemoveElementAt(i
);
253 prev
= mNAT64Prefixes
[i
];
257 // The prioritized address might also appear in the record we received.
260 for (size_t i
= 1; i
< length
; i
++) {
261 if (NAT64PrefixCompare(priorityPrefix
, mNAT64Prefixes
[i
])) {
262 mNAT64Prefixes
.RemoveElementAt(i
);
263 // It wouldn't appear more than once.
273 NetworkConnectivityService::OnLookupComplete(nsICancelable
* aRequest
,
274 nsIDNSRecord
* aRecord
,
276 ConnectivityState state
= NS_SUCCEEDED(aStatus
) ? OK
: NOT_AVAILABLE
;
278 if (aRequest
== mDNSv4Request
) {
280 mDNSv4Request
= nullptr;
281 } else if (aRequest
== mDNSv6Request
) {
283 mDNSv6Request
= nullptr;
284 } else if (aRequest
== mNAT64Request
) {
285 mNAT64Request
= nullptr;
286 SaveNAT64Prefixes(aRecord
);
289 if (!mDNSv4Request
&& !mDNSv6Request
&& !mNAT64Request
) {
290 NotifyObservers("network:connectivity-service:dns-checks-complete");
296 NetworkConnectivityService::RecheckDNS() {
298 Preferences::GetBool("network.connectivity-service.enabled", false);
303 if (nsIOService::UseSocketProcess()) {
304 SocketProcessParent
* parent
= SocketProcessParent::GetSingleton();
306 Unused
<< parent
->SendRecheckDNS();
311 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
);
312 OriginAttributes attrs
;
314 Preferences::GetCString("network.connectivity-service.DNSv4.domain", host
);
316 rv
= dns
->AsyncResolveNative(host
, nsIDNSService::RESOLVE_TYPE_DEFAULT
,
317 nsIDNSService::RESOLVE_DISABLE_IPV6
|
318 nsIDNSService::RESOLVE_TRR_DISABLED_MODE
,
319 nullptr, this, NS_GetCurrentThread(), attrs
,
320 getter_AddRefs(mDNSv4Request
));
321 NS_ENSURE_SUCCESS(rv
, rv
);
323 Preferences::GetCString("network.connectivity-service.DNSv6.domain", host
);
324 rv
= dns
->AsyncResolveNative(host
, nsIDNSService::RESOLVE_TYPE_DEFAULT
,
325 nsIDNSService::RESOLVE_DISABLE_IPV4
|
326 nsIDNSService::RESOLVE_TRR_DISABLED_MODE
,
327 nullptr, this, NS_GetCurrentThread(), attrs
,
328 getter_AddRefs(mDNSv6Request
));
329 NS_ENSURE_SUCCESS(rv
, rv
);
331 if (StaticPrefs::network_connectivity_service_nat64_check()) {
332 rv
= dns
->AsyncResolveNative("ipv4only.arpa"_ns
,
333 nsIDNSService::RESOLVE_TYPE_DEFAULT
,
334 nsIDNSService::RESOLVE_DISABLE_IPV4
|
335 nsIDNSService::RESOLVE_TRR_DISABLED_MODE
,
336 nullptr, this, NS_GetCurrentThread(), attrs
,
337 getter_AddRefs(mNAT64Request
));
338 NS_ENSURE_SUCCESS(rv
, rv
);
344 NetworkConnectivityService::Observe(nsISupports
* aSubject
, const char* aTopic
,
345 const char16_t
* aData
) {
346 if (!strcmp(aTopic
, "network:captive-portal-connectivity")) {
347 // Captive portal is cleared, so we redo the checks.
349 } else if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
351 mDNSv4Request
->Cancel(NS_ERROR_ABORT
);
352 mDNSv4Request
= nullptr;
355 mDNSv6Request
->Cancel(NS_ERROR_ABORT
);
356 mDNSv6Request
= nullptr;
359 mNAT64Request
->Cancel(NS_ERROR_ABORT
);
360 mNAT64Request
= nullptr;
363 nsCOMPtr
<nsIObserverService
> observerService
=
364 mozilla::services::GetObserverService();
365 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
366 observerService
->RemoveObserver(this,
367 "network:captive-portal-connectivity");
368 observerService
->RemoveObserver(this, NS_NETWORK_LINK_TOPIC
);
369 } else if (!strcmp(aTopic
, NS_NETWORK_LINK_TOPIC
) &&
370 !NS_LITERAL_STRING_FROM_CSTRING(NS_NETWORK_LINK_DATA_UNKNOWN
)
378 already_AddRefed
<nsIChannel
> NetworkConnectivityService::SetupIPCheckChannel(
384 rv
= Preferences::GetCString("network.connectivity-service.IPv4.url", url
);
386 rv
= Preferences::GetCString("network.connectivity-service.IPv6.url", url
);
388 NS_ENSURE_SUCCESS(rv
, nullptr);
390 nsCOMPtr
<nsIURI
> uri
;
391 rv
= NS_NewURI(getter_AddRefs(uri
), url
);
392 NS_ENSURE_SUCCESS(rv
, nullptr);
394 nsCOMPtr
<nsIChannel
> channel
;
395 if (XRE_IsSocketProcess()) {
396 rv
= DNSUtils::CreateChannelHelper(uri
, getter_AddRefs(channel
));
400 channel
->SetLoadFlags(
401 nsIRequest::LOAD_BYPASS_CACHE
| // don't read from the cache
402 nsIRequest::INHIBIT_CACHING
| // don't write the response to cache
403 nsIRequest::LOAD_ANONYMOUS
);
406 getter_AddRefs(channel
), uri
, nsContentUtils::GetSystemPrincipal(),
407 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
408 nsIContentPolicy::TYPE_OTHER
,
409 nullptr, // nsICookieJarSettings
410 nullptr, // aPerformanceStorage
411 nullptr, // aLoadGroup
413 nsIRequest::LOAD_BYPASS_CACHE
| // don't read from the cache
414 nsIRequest::INHIBIT_CACHING
| // don't write the response to cache
415 nsIRequest::LOAD_ANONYMOUS
); // prevent privacy leaks
416 NS_ENSURE_SUCCESS(rv
, nullptr);
419 // Prevent HTTPS-Only Mode from upgrading the OCSP request.
420 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
421 uint32_t httpsOnlyStatus
= loadInfo
->GetHttpsOnlyStatus();
422 httpsOnlyStatus
|= nsILoadInfo::HTTPS_ONLY_EXEMPT
;
423 loadInfo
->SetHttpsOnlyStatus(httpsOnlyStatus
);
425 // allow deprecated HTTP request from SystemPrincipal
426 loadInfo
->SetAllowDeprecatedSystemRequests(true);
430 rv
= channel
->SetTRRMode(nsIRequest::TRR_DISABLED_MODE
);
431 NS_ENSURE_SUCCESS(rv
, nullptr);
433 nsCOMPtr
<nsIHttpChannelInternal
> internalChan
= do_QueryInterface(channel
);
434 NS_ENSURE_TRUE(internalChan
, nullptr);
437 internalChan
->SetIPv6Disabled();
439 internalChan
->SetIPv4Disabled();
442 return channel
.forget();
446 NetworkConnectivityService::RecheckIPConnectivity() {
448 Preferences::GetBool("network.connectivity-service.enabled", false);
453 if (nsIOService::UseSocketProcess()) {
454 SocketProcessParent
* parent
= SocketProcessParent::GetSingleton();
456 Unused
<< parent
->SendRecheckIPConnectivity();
460 if (xpc::AreNonLocalConnectionsDisabled() &&
461 !Preferences::GetBool("network.captive-portal-service.testMode", false)) {
466 mIPv4Channel
->Cancel(NS_ERROR_ABORT
);
467 mIPv4Channel
= nullptr;
470 mIPv6Channel
->Cancel(NS_ERROR_ABORT
);
471 mIPv6Channel
= nullptr;
475 mHasNetworkId
= false;
476 mCheckedNetworkId
= false;
477 mIPv4Channel
= SetupIPCheckChannel(/* ipv4 = */ true);
479 rv
= mIPv4Channel
->AsyncOpen(this);
480 NS_ENSURE_SUCCESS(rv
, rv
);
483 mIPv6Channel
= SetupIPCheckChannel(/* ipv4 = */ false);
485 rv
= mIPv6Channel
->AsyncOpen(this);
486 NS_ENSURE_SUCCESS(rv
, rv
);
493 NetworkConnectivityService::OnStartRequest(nsIRequest
* aRequest
) {
498 NetworkConnectivityService::OnStopRequest(nsIRequest
* aRequest
,
499 nsresult aStatusCode
) {
500 if (aStatusCode
== NS_ERROR_ABORT
) {
504 ConnectivityState status
= NS_FAILED(aStatusCode
) ? NOT_AVAILABLE
: OK
;
506 if (aRequest
== mIPv4Channel
) {
508 mIPv4Channel
= nullptr;
510 if (mIPv4
== nsINetworkConnectivityService::OK
) {
511 Telemetry::AccumulateCategorical(
512 mHasNetworkId
? Telemetry::LABELS_NETWORK_ID_ONLINE::present
513 : Telemetry::LABELS_NETWORK_ID_ONLINE::absent
);
514 LOG(("mHasNetworkId : %d\n", mHasNetworkId
));
516 } else if (aRequest
== mIPv6Channel
) {
518 mIPv6Channel
= nullptr;
521 if (!mIPv6Channel
&& !mIPv4Channel
) {
522 NotifyObservers("network:connectivity-service:ip-checks-complete");
529 NetworkConnectivityService::OnDataAvailable(nsIRequest
* aRequest
,
530 nsIInputStream
* aInputStream
,
531 uint64_t aOffset
, uint32_t aCount
) {
534 // We perform this check here, instead of doing it in OnStopRequest in case
535 // a network down event occurs after the data has arrived but before we fire
536 // OnStopRequest. That would cause us to report a missing networkID, even
537 // though it was not empty while receiving data.
538 if (aRequest
== mIPv4Channel
&& !mCheckedNetworkId
) {
539 nsCOMPtr
<nsINetworkLinkService
> nls
=
540 do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID
);
541 nsAutoCString networkId
;
543 nls
->GetNetworkID(networkId
);
545 mHasNetworkId
= !networkId
.IsEmpty();
546 mCheckedNetworkId
= true;
549 Unused
<< NS_ReadInputStreamToString(aInputStream
, data
, aCount
);
554 } // namespace mozilla