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/net/SocketProcessParent.h"
8 #include "mozilla/Preferences.h"
9 #include "mozilla/Services.h"
10 #include "nsIOService.h"
11 #include "xpcpublic.h"
12 #include "nsSocketTransport2.h"
13 #include "nsIHttpChannelInternal.h"
14 #include "nsINetworkLinkService.h"
15 #include "mozilla/StaticPrefs_network.h"
17 static LazyLogModule
gNCSLog("NetworkConnectivityService");
19 #define LOG(args) MOZ_LOG(gNCSLog, mozilla::LogLevel::Debug, args)
24 NS_IMPL_ISUPPORTS(NetworkConnectivityService
, nsIDNSListener
, nsIObserver
,
25 nsINetworkConnectivityService
, nsIStreamListener
)
27 static StaticRefPtr
<NetworkConnectivityService
> gConnService
;
29 NetworkConnectivityService::NetworkConnectivityService()
35 mLock("nat64prefixes") {}
38 already_AddRefed
<NetworkConnectivityService
>
39 NetworkConnectivityService::GetSingleton() {
41 return do_AddRef(gConnService
);
44 RefPtr
<NetworkConnectivityService
> service
= new NetworkConnectivityService();
47 gConnService
= std::move(service
);
48 ClearOnShutdown(&gConnService
);
49 return do_AddRef(gConnService
);
52 nsresult
NetworkConnectivityService::Init() {
53 nsCOMPtr
<nsIObserverService
> observerService
=
54 mozilla::services::GetObserverService();
55 observerService
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
56 observerService
->AddObserver(this, NS_NETWORK_LINK_TOPIC
, false);
57 observerService
->AddObserver(this, "network:captive-portal-connectivity",
64 NetworkConnectivityService::GetDNSv4(ConnectivityState
* aState
) {
65 NS_ENSURE_ARG(aState
);
71 NetworkConnectivityService::GetDNSv6(ConnectivityState
* aState
) {
72 NS_ENSURE_ARG(aState
);
78 NetworkConnectivityService::GetIPv4(ConnectivityState
* aState
) {
79 NS_ENSURE_ARG(aState
);
85 NetworkConnectivityService::GetIPv6(ConnectivityState
* aState
) {
86 NS_ENSURE_ARG(aState
);
92 NetworkConnectivityService::GetNAT64(ConnectivityState
* aState
) {
93 NS_ENSURE_ARG(aState
);
98 already_AddRefed
<AddrInfo
> NetworkConnectivityService::MapNAT64IPs(
99 AddrInfo
* aNewRRSet
) {
100 // Add prefixes only if there are no IPv6 addresses.
101 // Expect that if aNewRRSet has IPv6 addresses, they must come
102 // before IPv4 addresses.
103 if (aNewRRSet
->Addresses().IsEmpty() ||
104 aNewRRSet
->Addresses()[0].raw
.family
== PR_AF_INET6
) {
105 return do_AddRef(aNewRRSet
);
108 // Currently we only add prefixes to the first IP's clones.
109 uint32_t ip
= aNewRRSet
->Addresses()[0].inet
.ip
;
110 nsTArray
<NetAddr
> addresses
= aNewRRSet
->Addresses().Clone();
113 MutexAutoLock
lock(mLock
);
114 for (const auto& prefix
: mNAT64Prefixes
) {
115 NetAddr addr
= NetAddr(prefix
);
117 // Copy the IPv4 address to the end
118 addr
.inet6
.ip
.u32
[3] = ip
;
120 // If we have both IPv4 and NAT64, we be could insourcing NAT64
121 // to avoid double NAT and improve performance. However, this
122 // breaks WebRTC, so we push it to the back.
123 addresses
.AppendElement(addr
);
127 auto builder
= aNewRRSet
->Build();
128 builder
.SetAddresses(std::move(addresses
));
129 return builder
.Finish();
132 // Returns true if a prefix was read and saved to the argument
133 static inline bool NAT64PrefixFromPref(NetAddr
* prefix
) {
134 nsAutoCString nat64PrefixPref
;
136 nsresult rv
= Preferences::GetCString(
137 "network.connectivity-service.nat64-prefix", nat64PrefixPref
);
138 return !(NS_FAILED(rv
) || nat64PrefixPref
.IsEmpty() ||
139 NS_FAILED(prefix
->InitFromString(nat64PrefixPref
)) ||
140 prefix
->raw
.family
!= PR_AF_INET6
);
143 static inline bool NAT64PrefixCompare(const NetAddr
& prefix1
,
144 const NetAddr
& prefix2
) {
145 // Compare the first 96 bits as 64 + 32
146 return prefix1
.inet6
.ip
.u64
[0] == prefix2
.inet6
.ip
.u64
[0] &&
147 prefix1
.inet6
.ip
.u32
[2] == prefix2
.inet6
.ip
.u32
[2];
150 void NetworkConnectivityService::PerformChecks() {
160 MutexAutoLock
lock(mLock
);
161 mNAT64Prefixes
.Clear();
163 // NAT64 checks might be disabled.
164 // Since We can't guarantee a DNS response, we should set up
165 // NAT64 manually now if needed.
167 NetAddr priorityPrefix
{};
168 bool havePrefix
= NAT64PrefixFromPref(&priorityPrefix
);
170 mNAT64Prefixes
.AppendElement(priorityPrefix
);
176 RecheckIPConnectivity();
179 static inline void NotifyObservers(const char* aTopic
) {
180 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
181 obs
->NotifyObservers(nullptr, aTopic
, nullptr);
184 void NetworkConnectivityService::SaveNAT64Prefixes(nsIDNSRecord
* aRecord
) {
185 nsCOMPtr
<nsIDNSAddrRecord
> rec
= do_QueryInterface(aRecord
);
186 MutexAutoLock
lock(mLock
);
187 mNAT64Prefixes
.Clear();
189 NetAddr priorityPrefix
{};
190 bool havePrefix
= NAT64PrefixFromPref(&priorityPrefix
);
193 mNAT64Prefixes
.AppendElement(priorityPrefix
);
198 mNAT64
= NOT_AVAILABLE
;
206 // use port 80 as dummy value for NetAddr
207 while (NS_SUCCEEDED(rec
->GetNextAddr(80, &addr
))) {
208 if (addr
.raw
.family
!= AF_INET6
|| addr
.IsIPAddrV4Mapped()) {
209 // These are not the kind of addresses we are looking for.
213 // RFC 7050 does not require the embedded IPv4 to be
214 // at the end of IPv6. In practice, and as we assume,
215 // it is always at the end.
216 // The embedded IP must be 192.0.0.170 or 192.0.0.171
218 // Clear the last bit to compare with the next one.
219 addr
.inet6
.ip
.u8
[15] &= ~(uint32_t)1;
220 if ((addr
.inet6
.ip
.u8
[12] != 192) || (addr
.inet6
.ip
.u8
[13] != 0) ||
221 (addr
.inet6
.ip
.u8
[14] != 0) || (addr
.inet6
.ip
.u8
[15] != 170)) {
225 mNAT64Prefixes
.AppendElement(addr
);
228 size_t length
= mNAT64Prefixes
.Length();
230 mNAT64
= NOT_AVAILABLE
;
234 // Remove duplicates. Typically a DNS64 resolver sends every
235 // prefix twice with address with different last bits. We want
236 // a list of unique prefixes while reordering is not allowed.
237 // We must not handle the case with an element in-between
238 // two identical ones, which is never the case for a properly
239 // configured DNS64 resolver.
241 NetAddr prev
= mNAT64Prefixes
[0];
243 for (size_t i
= 1; i
< length
; i
++) {
244 if (NAT64PrefixCompare(prev
, mNAT64Prefixes
[i
])) {
245 mNAT64Prefixes
.RemoveElementAt(i
);
249 prev
= mNAT64Prefixes
[i
];
253 // The prioritized address might also appear in the record we received.
256 for (size_t i
= 1; i
< length
; i
++) {
257 if (NAT64PrefixCompare(priorityPrefix
, mNAT64Prefixes
[i
])) {
258 mNAT64Prefixes
.RemoveElementAt(i
);
259 // It wouldn't appear more than once.
269 NetworkConnectivityService::OnLookupComplete(nsICancelable
* aRequest
,
270 nsIDNSRecord
* aRecord
,
272 ConnectivityState state
= aRecord
? OK
: NOT_AVAILABLE
;
274 if (aRequest
== mDNSv4Request
) {
276 mDNSv4Request
= nullptr;
277 } else if (aRequest
== mDNSv6Request
) {
279 mDNSv6Request
= nullptr;
280 } else if (aRequest
== mNAT64Request
) {
281 mNAT64Request
= nullptr;
282 SaveNAT64Prefixes(aRecord
);
285 if (!mDNSv4Request
&& !mDNSv6Request
&& !mNAT64Request
) {
286 NotifyObservers("network:connectivity-service:dns-checks-complete");
292 NetworkConnectivityService::RecheckDNS() {
294 Preferences::GetBool("network.connectivity-service.enabled", false);
299 if (nsIOService::UseSocketProcess()) {
300 SocketProcessParent
* parent
= SocketProcessParent::GetSingleton();
302 Unused
<< parent
->SendRecheckDNS();
307 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
);
308 OriginAttributes attrs
;
310 Preferences::GetCString("network.connectivity-service.DNSv4.domain", host
);
312 rv
= dns
->AsyncResolveNative(host
, nsIDNSService::RESOLVE_TYPE_DEFAULT
,
313 nsIDNSService::RESOLVE_DISABLE_IPV6
|
314 nsIDNSService::RESOLVE_TRR_DISABLED_MODE
,
315 nullptr, this, NS_GetCurrentThread(), attrs
,
316 getter_AddRefs(mDNSv4Request
));
317 NS_ENSURE_SUCCESS(rv
, rv
);
319 Preferences::GetCString("network.connectivity-service.DNSv6.domain", host
);
320 rv
= dns
->AsyncResolveNative(host
, nsIDNSService::RESOLVE_TYPE_DEFAULT
,
321 nsIDNSService::RESOLVE_DISABLE_IPV4
|
322 nsIDNSService::RESOLVE_TRR_DISABLED_MODE
,
323 nullptr, this, NS_GetCurrentThread(), attrs
,
324 getter_AddRefs(mDNSv6Request
));
325 NS_ENSURE_SUCCESS(rv
, rv
);
327 if (StaticPrefs::network_connectivity_service_nat64_check()) {
328 rv
= dns
->AsyncResolveNative("ipv4only.arpa"_ns
,
329 nsIDNSService::RESOLVE_TYPE_DEFAULT
,
330 nsIDNSService::RESOLVE_DISABLE_IPV4
|
331 nsIDNSService::RESOLVE_TRR_DISABLED_MODE
,
332 nullptr, this, NS_GetCurrentThread(), attrs
,
333 getter_AddRefs(mNAT64Request
));
334 NS_ENSURE_SUCCESS(rv
, rv
);
340 NetworkConnectivityService::Observe(nsISupports
* aSubject
, const char* aTopic
,
341 const char16_t
* aData
) {
342 if (!strcmp(aTopic
, "network:captive-portal-connectivity")) {
343 // Captive portal is cleared, so we redo the checks.
345 } else if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
347 mDNSv4Request
->Cancel(NS_ERROR_ABORT
);
348 mDNSv4Request
= nullptr;
351 mDNSv6Request
->Cancel(NS_ERROR_ABORT
);
352 mDNSv6Request
= nullptr;
355 mNAT64Request
->Cancel(NS_ERROR_ABORT
);
356 mNAT64Request
= nullptr;
359 nsCOMPtr
<nsIObserverService
> observerService
=
360 mozilla::services::GetObserverService();
361 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
362 observerService
->RemoveObserver(this,
363 "network:captive-portal-connectivity");
364 observerService
->RemoveObserver(this, NS_NETWORK_LINK_TOPIC
);
365 } else if (!strcmp(aTopic
, NS_NETWORK_LINK_TOPIC
) &&
366 !NS_LITERAL_STRING_FROM_CSTRING(NS_NETWORK_LINK_DATA_UNKNOWN
)
374 already_AddRefed
<nsIChannel
> NetworkConnectivityService::SetupIPCheckChannel(
380 rv
= Preferences::GetCString("network.connectivity-service.IPv4.url", url
);
382 rv
= Preferences::GetCString("network.connectivity-service.IPv6.url", url
);
384 NS_ENSURE_SUCCESS(rv
, nullptr);
386 nsCOMPtr
<nsIURI
> uri
;
387 rv
= NS_NewURI(getter_AddRefs(uri
), url
);
388 NS_ENSURE_SUCCESS(rv
, nullptr);
390 nsCOMPtr
<nsIChannel
> channel
;
391 if (XRE_IsSocketProcess()) {
392 rv
= DNSUtils::CreateChannelHelper(uri
, getter_AddRefs(channel
));
396 channel
->SetLoadFlags(
397 nsIRequest::LOAD_BYPASS_CACHE
| // don't read from the cache
398 nsIRequest::INHIBIT_CACHING
| // don't write the response to cache
399 nsIRequest::LOAD_ANONYMOUS
);
402 getter_AddRefs(channel
), uri
, nsContentUtils::GetSystemPrincipal(),
403 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
404 nsIContentPolicy::TYPE_OTHER
,
405 nullptr, // nsICookieJarSettings
406 nullptr, // aPerformanceStorage
407 nullptr, // aLoadGroup
409 nsIRequest::LOAD_BYPASS_CACHE
| // don't read from the cache
410 nsIRequest::INHIBIT_CACHING
| // don't write the response to cache
411 nsIRequest::LOAD_ANONYMOUS
); // prevent privacy leaks
412 NS_ENSURE_SUCCESS(rv
, nullptr);
415 // Prevent HTTPS-Only Mode from upgrading the OCSP request.
416 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
417 uint32_t httpsOnlyStatus
= loadInfo
->GetHttpsOnlyStatus();
418 httpsOnlyStatus
|= nsILoadInfo::HTTPS_ONLY_EXEMPT
;
419 loadInfo
->SetHttpsOnlyStatus(httpsOnlyStatus
);
421 // allow deprecated HTTP request from SystemPrincipal
422 loadInfo
->SetAllowDeprecatedSystemRequests(true);
426 rv
= channel
->SetTRRMode(nsIRequest::TRR_DISABLED_MODE
);
427 NS_ENSURE_SUCCESS(rv
, nullptr);
429 nsCOMPtr
<nsIHttpChannelInternal
> internalChan
= do_QueryInterface(channel
);
430 NS_ENSURE_TRUE(internalChan
, nullptr);
433 internalChan
->SetIPv6Disabled();
435 internalChan
->SetIPv4Disabled();
438 return channel
.forget();
442 NetworkConnectivityService::RecheckIPConnectivity() {
444 Preferences::GetBool("network.connectivity-service.enabled", false);
449 if (nsIOService::UseSocketProcess()) {
450 SocketProcessParent
* parent
= SocketProcessParent::GetSingleton();
452 Unused
<< parent
->SendRecheckIPConnectivity();
456 if (xpc::AreNonLocalConnectionsDisabled() &&
457 !Preferences::GetBool("network.captive-portal-service.testMode", false)) {
462 mIPv4Channel
->Cancel(NS_ERROR_ABORT
);
463 mIPv4Channel
= nullptr;
466 mIPv6Channel
->Cancel(NS_ERROR_ABORT
);
467 mIPv6Channel
= nullptr;
471 mHasNetworkId
= false;
472 mCheckedNetworkId
= false;
473 mIPv4Channel
= SetupIPCheckChannel(/* ipv4 = */ true);
475 rv
= mIPv4Channel
->AsyncOpen(this);
476 NS_ENSURE_SUCCESS(rv
, rv
);
479 mIPv6Channel
= SetupIPCheckChannel(/* ipv4 = */ false);
481 rv
= mIPv6Channel
->AsyncOpen(this);
482 NS_ENSURE_SUCCESS(rv
, rv
);
489 NetworkConnectivityService::OnStartRequest(nsIRequest
* aRequest
) {
494 NetworkConnectivityService::OnStopRequest(nsIRequest
* aRequest
,
495 nsresult aStatusCode
) {
496 if (aStatusCode
== NS_ERROR_ABORT
) {
500 ConnectivityState status
= NS_FAILED(aStatusCode
) ? NOT_AVAILABLE
: OK
;
502 if (aRequest
== mIPv4Channel
) {
504 mIPv4Channel
= nullptr;
506 if (mIPv4
== nsINetworkConnectivityService::OK
) {
507 Telemetry::AccumulateCategorical(
508 mHasNetworkId
? Telemetry::LABELS_NETWORK_ID_ONLINE::present
509 : Telemetry::LABELS_NETWORK_ID_ONLINE::absent
);
510 LOG(("mHasNetworkId : %d\n", mHasNetworkId
));
512 } else if (aRequest
== mIPv6Channel
) {
514 mIPv6Channel
= nullptr;
517 if (!mIPv6Channel
&& !mIPv4Channel
) {
518 NotifyObservers("network:connectivity-service:ip-checks-complete");
525 NetworkConnectivityService::OnDataAvailable(nsIRequest
* aRequest
,
526 nsIInputStream
* aInputStream
,
527 uint64_t aOffset
, uint32_t aCount
) {
530 // We perform this check here, instead of doing it in OnStopRequest in case
531 // a network down event occurs after the data has arrived but before we fire
532 // OnStopRequest. That would cause us to report a missing networkID, even
533 // though it was not empty while receiving data.
534 if (aRequest
== mIPv4Channel
&& !mCheckedNetworkId
) {
535 nsCOMPtr
<nsINetworkLinkService
> nls
=
536 do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID
);
537 nsAutoCString networkId
;
539 nls
->GetNetworkID(networkId
);
541 mHasNetworkId
= !networkId
.IsEmpty();
542 mCheckedNetworkId
= true;
545 Unused
<< NS_ReadInputStreamToString(aInputStream
, data
, aCount
);
550 } // namespace mozilla