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/. */
4
5 #include "DNSUtils.h"
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"
16
17 static LazyLogModule gNCSLog("NetworkConnectivityService");
18 #undef LOG
19 #define LOG(args) MOZ_LOG(gNCSLog, mozilla::LogLevel::Debug, args)
20
21 namespace mozilla {
22 namespace net {
23
24 NS_IMPL_ISUPPORTS(NetworkConnectivityService, nsIDNSListener, nsIObserver,
25 nsINetworkConnectivityService, nsIStreamListener)
26
27 static StaticRefPtr<NetworkConnectivityService> gConnService;
28
NetworkConnectivityService()29 NetworkConnectivityService::NetworkConnectivityService()
30 : mDNSv4(UNKNOWN),
31 mDNSv6(UNKNOWN),
32 mIPv4(UNKNOWN),
33 mIPv6(UNKNOWN),
34 mNAT64(UNKNOWN),
35 mLock("nat64prefixes") {}
36
37 // static
38 already_AddRefed<NetworkConnectivityService>
GetSingleton()39 NetworkConnectivityService::GetSingleton() {
40 if (gConnService) {
41 return do_AddRef(gConnService);
42 }
43
44 RefPtr<NetworkConnectivityService> service = new NetworkConnectivityService();
45 service->Init();
46
47 gConnService = std::move(service);
48 ClearOnShutdown(&gConnService);
49 return do_AddRef(gConnService);
50 }
51
Init()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",
58 false);
59
60 return NS_OK;
61 }
62
63 NS_IMETHODIMP
GetDNSv4(ConnectivityState * aState)64 NetworkConnectivityService::GetDNSv4(ConnectivityState* aState) {
65 NS_ENSURE_ARG(aState);
66 *aState = mDNSv4;
67 return NS_OK;
68 }
69
70 NS_IMETHODIMP
GetDNSv6(ConnectivityState * aState)71 NetworkConnectivityService::GetDNSv6(ConnectivityState* aState) {
72 NS_ENSURE_ARG(aState);
73 *aState = mDNSv6;
74 return NS_OK;
75 }
76
77 NS_IMETHODIMP
GetIPv4(ConnectivityState * aState)78 NetworkConnectivityService::GetIPv4(ConnectivityState* aState) {
79 NS_ENSURE_ARG(aState);
80 *aState = mIPv4;
81 return NS_OK;
82 }
83
84 NS_IMETHODIMP
GetIPv6(ConnectivityState * aState)85 NetworkConnectivityService::GetIPv6(ConnectivityState* aState) {
86 NS_ENSURE_ARG(aState);
87 *aState = mIPv6;
88 return NS_OK;
89 }
90
91 NS_IMETHODIMP
GetNAT64(ConnectivityState * aState)92 NetworkConnectivityService::GetNAT64(ConnectivityState* aState) {
93 NS_ENSURE_ARG(aState);
94 *aState = mNAT64;
95 return NS_OK;
96 }
97
MapNAT64IPs(AddrInfo * aNewRRSet)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);
106 }
107
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();
111
112 {
113 MutexAutoLock lock(mLock);
114 for (const auto& prefix : mNAT64Prefixes) {
115 NetAddr addr = NetAddr(prefix);
116
117 // Copy the IPv4 address to the end
118 addr.inet6.ip.u32[3] = ip;
119
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);
124 }
125 }
126
127 auto builder = aNewRRSet->Build();
128 builder.SetAddresses(std::move(addresses));
129 return builder.Finish();
130 }
131
132 // Returns true if a prefix was read and saved to the argument
NAT64PrefixFromPref(NetAddr * prefix)133 static inline bool NAT64PrefixFromPref(NetAddr* prefix) {
134 nsAutoCString nat64PrefixPref;
135
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);
141 }
142
NAT64PrefixCompare(const NetAddr & prefix1,const NetAddr & prefix2)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];
148 }
149
PerformChecks()150 void NetworkConnectivityService::PerformChecks() {
151 mDNSv4 = UNKNOWN;
152 mDNSv6 = UNKNOWN;
153
154 mIPv4 = UNKNOWN;
155 mIPv6 = UNKNOWN;
156
157 mNAT64 = UNKNOWN;
158
159 {
160 MutexAutoLock lock(mLock);
161 mNAT64Prefixes.Clear();
162
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.
166
167 NetAddr priorityPrefix{};
168 bool havePrefix = NAT64PrefixFromPref(&priorityPrefix);
169 if (havePrefix) {
170 mNAT64Prefixes.AppendElement(priorityPrefix);
171 mNAT64 = OK;
172 }
173 }
174
175 RecheckDNS();
176 RecheckIPConnectivity();
177 }
178
NotifyObservers(const char * aTopic)179 static inline void NotifyObservers(const char* aTopic) {
180 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
181 obs->NotifyObservers(nullptr, aTopic, nullptr);
182 }
183
SaveNAT64Prefixes(nsIDNSRecord * aRecord)184 void NetworkConnectivityService::SaveNAT64Prefixes(nsIDNSRecord* aRecord) {
185 nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord);
186 MutexAutoLock lock(mLock);
187 mNAT64Prefixes.Clear();
188
189 NetAddr priorityPrefix{};
190 bool havePrefix = NAT64PrefixFromPref(&priorityPrefix);
191 if (havePrefix) {
192 mNAT64 = OK;
193 mNAT64Prefixes.AppendElement(priorityPrefix);
194 }
195
196 if (!rec) {
197 if (!havePrefix) {
198 mNAT64 = NOT_AVAILABLE;
199 }
200 return;
201 }
202
203 mNAT64 = UNKNOWN;
204 NetAddr addr{};
205
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.
210 continue;
211 }
212
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
217
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)) {
222 continue;
223 }
224
225 mNAT64Prefixes.AppendElement(addr);
226 }
227
228 size_t length = mNAT64Prefixes.Length();
229 if (length == 0) {
230 mNAT64 = NOT_AVAILABLE;
231 return;
232 }
233
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.
240
241 NetAddr prev = mNAT64Prefixes[0];
242
243 for (size_t i = 1; i < length; i++) {
244 if (NAT64PrefixCompare(prev, mNAT64Prefixes[i])) {
245 mNAT64Prefixes.RemoveElementAt(i);
246 i--;
247 length--;
248 } else {
249 prev = mNAT64Prefixes[i];
250 }
251 }
252
253 // The prioritized address might also appear in the record we received.
254
255 if (havePrefix) {
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.
260 break;
261 }
262 }
263 }
264
265 mNAT64 = OK;
266 }
267
268 NS_IMETHODIMP
OnLookupComplete(nsICancelable * aRequest,nsIDNSRecord * aRecord,nsresult aStatus)269 NetworkConnectivityService::OnLookupComplete(nsICancelable* aRequest,
270 nsIDNSRecord* aRecord,
271 nsresult aStatus) {
272 ConnectivityState state = aRecord ? OK : NOT_AVAILABLE;
273
274 if (aRequest == mDNSv4Request) {
275 mDNSv4 = state;
276 mDNSv4Request = nullptr;
277 } else if (aRequest == mDNSv6Request) {
278 mDNSv6 = state;
279 mDNSv6Request = nullptr;
280 } else if (aRequest == mNAT64Request) {
281 mNAT64Request = nullptr;
282 SaveNAT64Prefixes(aRecord);
283 }
284
285 if (!mDNSv4Request && !mDNSv6Request && !mNAT64Request) {
286 NotifyObservers("network:connectivity-service:dns-checks-complete");
287 }
288 return NS_OK;
289 }
290
291 NS_IMETHODIMP
RecheckDNS()292 NetworkConnectivityService::RecheckDNS() {
293 bool enabled =
294 Preferences::GetBool("network.connectivity-service.enabled", false);
295 if (!enabled) {
296 return NS_OK;
297 }
298
299 if (nsIOService::UseSocketProcess()) {
300 SocketProcessParent* parent = SocketProcessParent::GetSingleton();
301 if (parent) {
302 Unused << parent->SendRecheckDNS();
303 }
304 }
305
306 nsresult rv;
307 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
308 OriginAttributes attrs;
309 nsAutoCString host;
310 Preferences::GetCString("network.connectivity-service.DNSv4.domain", host);
311
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);
318
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);
326
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);
335 }
336 return rv;
337 }
338
339 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)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.
344 PerformChecks();
345 } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
346 if (mDNSv4Request) {
347 mDNSv4Request->Cancel(NS_ERROR_ABORT);
348 mDNSv4Request = nullptr;
349 }
350 if (mDNSv6Request) {
351 mDNSv6Request->Cancel(NS_ERROR_ABORT);
352 mDNSv6Request = nullptr;
353 }
354 if (mNAT64Request) {
355 mNAT64Request->Cancel(NS_ERROR_ABORT);
356 mNAT64Request = nullptr;
357 }
358
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)
367 .Equals(aData)) {
368 PerformChecks();
369 }
370
371 return NS_OK;
372 }
373
SetupIPCheckChannel(bool ipv4)374 already_AddRefed<nsIChannel> NetworkConnectivityService::SetupIPCheckChannel(
375 bool ipv4) {
376 nsresult rv;
377 nsAutoCString url;
378
379 if (ipv4) {
380 rv = Preferences::GetCString("network.connectivity-service.IPv4.url", url);
381 } else {
382 rv = Preferences::GetCString("network.connectivity-service.IPv6.url", url);
383 }
384 NS_ENSURE_SUCCESS(rv, nullptr);
385
386 nsCOMPtr<nsIURI> uri;
387 rv = NS_NewURI(getter_AddRefs(uri), url);
388 NS_ENSURE_SUCCESS(rv, nullptr);
389
390 nsCOMPtr<nsIChannel> channel;
391 if (XRE_IsSocketProcess()) {
392 rv = DNSUtils::CreateChannelHelper(uri, getter_AddRefs(channel));
393 if (NS_FAILED(rv)) {
394 return nullptr;
395 }
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);
400 } else {
401 rv = NS_NewChannel(
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
408 nullptr,
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);
413
414 {
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);
420
421 // allow deprecated HTTP request from SystemPrincipal
422 loadInfo->SetAllowDeprecatedSystemRequests(true);
423 }
424 }
425
426 rv = channel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
427 NS_ENSURE_SUCCESS(rv, nullptr);
428
429 nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(channel);
430 NS_ENSURE_TRUE(internalChan, nullptr);
431
432 if (ipv4) {
433 internalChan->SetIPv6Disabled();
434 } else {
435 internalChan->SetIPv4Disabled();
436 }
437
438 return channel.forget();
439 }
440
441 NS_IMETHODIMP
RecheckIPConnectivity()442 NetworkConnectivityService::RecheckIPConnectivity() {
443 bool enabled =
444 Preferences::GetBool("network.connectivity-service.enabled", false);
445 if (!enabled) {
446 return NS_OK;
447 }
448
449 if (nsIOService::UseSocketProcess()) {
450 SocketProcessParent* parent = SocketProcessParent::GetSingleton();
451 if (parent) {
452 Unused << parent->SendRecheckIPConnectivity();
453 }
454 }
455
456 if (xpc::AreNonLocalConnectionsDisabled() &&
457 !Preferences::GetBool("network.captive-portal-service.testMode", false)) {
458 return NS_OK;
459 }
460
461 if (mIPv4Channel) {
462 mIPv4Channel->Cancel(NS_ERROR_ABORT);
463 mIPv4Channel = nullptr;
464 }
465 if (mIPv6Channel) {
466 mIPv6Channel->Cancel(NS_ERROR_ABORT);
467 mIPv6Channel = nullptr;
468 }
469
470 nsresult rv;
471 mHasNetworkId = false;
472 mCheckedNetworkId = false;
473 mIPv4Channel = SetupIPCheckChannel(/* ipv4 = */ true);
474 if (mIPv4Channel) {
475 rv = mIPv4Channel->AsyncOpen(this);
476 NS_ENSURE_SUCCESS(rv, rv);
477 }
478
479 mIPv6Channel = SetupIPCheckChannel(/* ipv4 = */ false);
480 if (mIPv6Channel) {
481 rv = mIPv6Channel->AsyncOpen(this);
482 NS_ENSURE_SUCCESS(rv, rv);
483 }
484
485 return NS_OK;
486 }
487
488 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest)489 NetworkConnectivityService::OnStartRequest(nsIRequest* aRequest) {
490 return NS_OK;
491 }
492
493 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)494 NetworkConnectivityService::OnStopRequest(nsIRequest* aRequest,
495 nsresult aStatusCode) {
496 if (aStatusCode == NS_ERROR_ABORT) {
497 return NS_OK;
498 }
499
500 ConnectivityState status = NS_FAILED(aStatusCode) ? NOT_AVAILABLE : OK;
501
502 if (aRequest == mIPv4Channel) {
503 mIPv4 = status;
504 mIPv4Channel = nullptr;
505
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));
511 }
512 } else if (aRequest == mIPv6Channel) {
513 mIPv6 = status;
514 mIPv6Channel = nullptr;
515 }
516
517 if (!mIPv6Channel && !mIPv4Channel) {
518 NotifyObservers("network:connectivity-service:ip-checks-complete");
519 }
520
521 return NS_OK;
522 }
523
524 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)525 NetworkConnectivityService::OnDataAvailable(nsIRequest* aRequest,
526 nsIInputStream* aInputStream,
527 uint64_t aOffset, uint32_t aCount) {
528 nsAutoCString data;
529
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;
538 if (nls) {
539 nls->GetNetworkID(networkId);
540 }
541 mHasNetworkId = !networkId.IsEmpty();
542 mCheckedNetworkId = true;
543 }
544
545 Unused << NS_ReadInputStreamToString(aInputStream, data, aCount);
546 return NS_OK;
547 }
548
549 } // namespace net
550 } // namespace mozilla
551