Merge mozilla-central to autoland. CLOSED TREE
[gecko.git] / netwerk / base / NetworkConnectivityService.cpp
blob1e126742ce6958c7c1323176d95befa1ff3c311f
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/. */
5 #include "DNSUtils.h"
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"
11 #include "nsCOMPtr.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");
22 #undef LOG
23 #define LOG(args) MOZ_LOG(gNCSLog, mozilla::LogLevel::Debug, args)
25 namespace mozilla {
26 namespace net {
28 NS_IMPL_ISUPPORTS(NetworkConnectivityService, nsIDNSListener, nsIObserver,
29 nsINetworkConnectivityService, nsIStreamListener)
31 static StaticRefPtr<NetworkConnectivityService> gConnService;
33 NetworkConnectivityService::NetworkConnectivityService()
34 : mDNSv4(UNKNOWN),
35 mDNSv6(UNKNOWN),
36 mIPv4(UNKNOWN),
37 mIPv6(UNKNOWN),
38 mNAT64(UNKNOWN),
39 mLock("nat64prefixes") {}
41 // static
42 already_AddRefed<NetworkConnectivityService>
43 NetworkConnectivityService::GetSingleton() {
44 if (gConnService) {
45 return do_AddRef(gConnService);
48 RefPtr<NetworkConnectivityService> service = new NetworkConnectivityService();
49 service->Init();
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",
62 false);
64 return NS_OK;
67 NS_IMETHODIMP
68 NetworkConnectivityService::GetDNSv4(ConnectivityState* aState) {
69 NS_ENSURE_ARG(aState);
70 *aState = mDNSv4;
71 return NS_OK;
74 NS_IMETHODIMP
75 NetworkConnectivityService::GetDNSv6(ConnectivityState* aState) {
76 NS_ENSURE_ARG(aState);
77 *aState = mDNSv6;
78 return NS_OK;
81 NS_IMETHODIMP
82 NetworkConnectivityService::GetIPv4(ConnectivityState* aState) {
83 NS_ENSURE_ARG(aState);
84 *aState = mIPv4;
85 return NS_OK;
88 NS_IMETHODIMP
89 NetworkConnectivityService::GetIPv6(ConnectivityState* aState) {
90 NS_ENSURE_ARG(aState);
91 *aState = mIPv6;
92 return NS_OK;
95 NS_IMETHODIMP
96 NetworkConnectivityService::GetNAT64(ConnectivityState* aState) {
97 NS_ENSURE_ARG(aState);
98 *aState = mNAT64;
99 return NS_OK;
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() {
155 mDNSv4 = UNKNOWN;
156 mDNSv6 = UNKNOWN;
158 mIPv4 = UNKNOWN;
159 mIPv6 = UNKNOWN;
161 mNAT64 = UNKNOWN;
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);
173 if (havePrefix) {
174 mNAT64Prefixes.AppendElement(priorityPrefix);
175 mNAT64 = OK;
179 RecheckDNS();
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);
195 if (havePrefix) {
196 mNAT64 = OK;
197 mNAT64Prefixes.AppendElement(priorityPrefix);
200 if (!rec) {
201 if (!havePrefix) {
202 mNAT64 = NOT_AVAILABLE;
204 return;
207 mNAT64 = UNKNOWN;
208 NetAddr addr{};
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.
214 continue;
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)) {
226 continue;
229 mNAT64Prefixes.AppendElement(addr);
232 size_t length = mNAT64Prefixes.Length();
233 if (length == 0) {
234 mNAT64 = NOT_AVAILABLE;
235 return;
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);
250 i--;
251 length--;
252 } else {
253 prev = mNAT64Prefixes[i];
257 // The prioritized address might also appear in the record we received.
259 if (havePrefix) {
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.
264 break;
269 mNAT64 = OK;
272 NS_IMETHODIMP
273 NetworkConnectivityService::OnLookupComplete(nsICancelable* aRequest,
274 nsIDNSRecord* aRecord,
275 nsresult aStatus) {
276 ConnectivityState state = NS_SUCCEEDED(aStatus) ? OK : NOT_AVAILABLE;
278 if (aRequest == mDNSv4Request) {
279 mDNSv4 = state;
280 mDNSv4Request = nullptr;
281 } else if (aRequest == mDNSv6Request) {
282 mDNSv6 = state;
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");
292 return NS_OK;
295 NS_IMETHODIMP
296 NetworkConnectivityService::RecheckDNS() {
297 bool enabled =
298 Preferences::GetBool("network.connectivity-service.enabled", false);
299 if (!enabled) {
300 return NS_OK;
303 if (nsIOService::UseSocketProcess()) {
304 SocketProcessParent* parent = SocketProcessParent::GetSingleton();
305 if (parent) {
306 Unused << parent->SendRecheckDNS();
310 nsresult rv;
311 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
312 OriginAttributes attrs;
313 nsAutoCString host;
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);
340 return rv;
343 NS_IMETHODIMP
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.
348 PerformChecks();
349 } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
350 if (mDNSv4Request) {
351 mDNSv4Request->Cancel(NS_ERROR_ABORT);
352 mDNSv4Request = nullptr;
354 if (mDNSv6Request) {
355 mDNSv6Request->Cancel(NS_ERROR_ABORT);
356 mDNSv6Request = nullptr;
358 if (mNAT64Request) {
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)
371 .Equals(aData)) {
372 PerformChecks();
375 return NS_OK;
378 already_AddRefed<nsIChannel> NetworkConnectivityService::SetupIPCheckChannel(
379 bool ipv4) {
380 nsresult rv;
381 nsAutoCString url;
383 if (ipv4) {
384 rv = Preferences::GetCString("network.connectivity-service.IPv4.url", url);
385 } else {
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));
397 if (NS_FAILED(rv)) {
398 return nullptr;
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);
404 } else {
405 rv = NS_NewChannel(
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
412 nullptr,
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);
436 if (ipv4) {
437 internalChan->SetIPv6Disabled();
438 } else {
439 internalChan->SetIPv4Disabled();
442 return channel.forget();
445 NS_IMETHODIMP
446 NetworkConnectivityService::RecheckIPConnectivity() {
447 bool enabled =
448 Preferences::GetBool("network.connectivity-service.enabled", false);
449 if (!enabled) {
450 return NS_OK;
453 if (nsIOService::UseSocketProcess()) {
454 SocketProcessParent* parent = SocketProcessParent::GetSingleton();
455 if (parent) {
456 Unused << parent->SendRecheckIPConnectivity();
460 if (xpc::AreNonLocalConnectionsDisabled() &&
461 !Preferences::GetBool("network.captive-portal-service.testMode", false)) {
462 return NS_OK;
465 if (mIPv4Channel) {
466 mIPv4Channel->Cancel(NS_ERROR_ABORT);
467 mIPv4Channel = nullptr;
469 if (mIPv6Channel) {
470 mIPv6Channel->Cancel(NS_ERROR_ABORT);
471 mIPv6Channel = nullptr;
474 nsresult rv;
475 mHasNetworkId = false;
476 mCheckedNetworkId = false;
477 mIPv4Channel = SetupIPCheckChannel(/* ipv4 = */ true);
478 if (mIPv4Channel) {
479 rv = mIPv4Channel->AsyncOpen(this);
480 NS_ENSURE_SUCCESS(rv, rv);
483 mIPv6Channel = SetupIPCheckChannel(/* ipv4 = */ false);
484 if (mIPv6Channel) {
485 rv = mIPv6Channel->AsyncOpen(this);
486 NS_ENSURE_SUCCESS(rv, rv);
489 return NS_OK;
492 NS_IMETHODIMP
493 NetworkConnectivityService::OnStartRequest(nsIRequest* aRequest) {
494 return NS_OK;
497 NS_IMETHODIMP
498 NetworkConnectivityService::OnStopRequest(nsIRequest* aRequest,
499 nsresult aStatusCode) {
500 if (aStatusCode == NS_ERROR_ABORT) {
501 return NS_OK;
504 ConnectivityState status = NS_FAILED(aStatusCode) ? NOT_AVAILABLE : OK;
506 if (aRequest == mIPv4Channel) {
507 mIPv4 = status;
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) {
517 mIPv6 = status;
518 mIPv6Channel = nullptr;
521 if (!mIPv6Channel && !mIPv4Channel) {
522 NotifyObservers("network:connectivity-service:ip-checks-complete");
525 return NS_OK;
528 NS_IMETHODIMP
529 NetworkConnectivityService::OnDataAvailable(nsIRequest* aRequest,
530 nsIInputStream* aInputStream,
531 uint64_t aOffset, uint32_t aCount) {
532 nsAutoCString data;
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;
542 if (nls) {
543 nls->GetNetworkID(networkId);
545 mHasNetworkId = !networkId.IsEmpty();
546 mCheckedNetworkId = true;
549 Unused << NS_ReadInputStreamToString(aInputStream, data, aCount);
550 return NS_OK;
553 } // namespace net
554 } // namespace mozilla