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