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 "NetworkConnectivityService.h"
6 #include "mozilla/Preferences.h"
7 #include "mozilla/Services.h"
8 #include "xpcpublic.h"
9 #include "nsSocketTransport2.h"
10 #include "nsIHttpChannelInternal.h"
11 #include "nsINetworkLinkService.h"
12 #include "mozilla/StaticPrefs_network.h"
13 
14 static LazyLogModule gNCSLog("NetworkConnectivityService");
15 #undef LOG
16 #define LOG(args) MOZ_LOG(gNCSLog, mozilla::LogLevel::Debug, args)
17 
18 namespace mozilla {
19 namespace net {
20 
21 NS_IMPL_ISUPPORTS(NetworkConnectivityService, nsIDNSListener, nsIObserver,
22                   nsINetworkConnectivityService, nsIStreamListener)
23 
24 static StaticRefPtr<NetworkConnectivityService> gConnService;
25 
NetworkConnectivityService()26 NetworkConnectivityService::NetworkConnectivityService()
27     : mDNSv4(UNKNOWN),
28       mDNSv6(UNKNOWN),
29       mIPv4(UNKNOWN),
30       mIPv6(UNKNOWN),
31       mNAT64(UNKNOWN),
32       mLock("nat64prefixes") {}
33 
34 // static
35 already_AddRefed<NetworkConnectivityService>
GetSingleton()36 NetworkConnectivityService::GetSingleton() {
37   if (!XRE_IsParentProcess()) {
38     return nullptr;
39   }
40 
41   if (gConnService) {
42     return do_AddRef(gConnService);
43   }
44 
45   RefPtr<NetworkConnectivityService> service = new NetworkConnectivityService();
46   service->Init();
47 
48   gConnService = std::move(service);
49   ClearOnShutdown(&gConnService);
50   return do_AddRef(gConnService);
51 }
52 
Init()53 nsresult NetworkConnectivityService::Init() {
54   nsCOMPtr<nsIObserverService> observerService =
55       mozilla::services::GetObserverService();
56   observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
57   observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
58   observerService->AddObserver(this, "network:captive-portal-connectivity",
59                                false);
60 
61   return NS_OK;
62 }
63 
64 NS_IMETHODIMP
GetDNSv4(ConnectivityState * aState)65 NetworkConnectivityService::GetDNSv4(ConnectivityState* aState) {
66   NS_ENSURE_ARG(aState);
67   *aState = mDNSv4;
68   return NS_OK;
69 }
70 
71 NS_IMETHODIMP
GetDNSv6(ConnectivityState * aState)72 NetworkConnectivityService::GetDNSv6(ConnectivityState* aState) {
73   NS_ENSURE_ARG(aState);
74   *aState = mDNSv6;
75   return NS_OK;
76 }
77 
78 NS_IMETHODIMP
GetIPv4(ConnectivityState * aState)79 NetworkConnectivityService::GetIPv4(ConnectivityState* aState) {
80   NS_ENSURE_ARG(aState);
81   *aState = mIPv4;
82   return NS_OK;
83 }
84 
85 NS_IMETHODIMP
GetIPv6(ConnectivityState * aState)86 NetworkConnectivityService::GetIPv6(ConnectivityState* aState) {
87   NS_ENSURE_ARG(aState);
88   *aState = mIPv6;
89   return NS_OK;
90 }
91 
92 NS_IMETHODIMP
GetNAT64(ConnectivityState * aState)93 NetworkConnectivityService::GetNAT64(ConnectivityState* aState) {
94   NS_ENSURE_ARG(aState);
95   *aState = mNAT64;
96   return NS_OK;
97 }
98 
MapNAT64IPs(AddrInfo * aNewRRSet)99 already_AddRefed<AddrInfo> NetworkConnectivityService::MapNAT64IPs(
100     AddrInfo* aNewRRSet) {
101   // Add prefixes only if there are no IPv6 addresses.
102   // Expect that if aNewRRSet has IPv6 addresses, they must come
103   // before IPv4 addresses.
104   if (aNewRRSet->Addresses().IsEmpty() ||
105       aNewRRSet->Addresses()[0].raw.family == PR_AF_INET6) {
106     return do_AddRef(aNewRRSet);
107   }
108 
109   // Currently we only add prefixes to the first IP's clones.
110   uint32_t ip = aNewRRSet->Addresses()[0].inet.ip;
111   nsTArray<NetAddr> addresses = aNewRRSet->Addresses().Clone();
112 
113   {
114     MutexAutoLock lock(mLock);
115     for (const auto& prefix : mNAT64Prefixes) {
116       NetAddr addr = NetAddr(prefix);
117 
118       // Copy the IPv4 address to the end
119       addr.inet6.ip.u32[3] = ip;
120 
121       // If we have both IPv4 and NAT64, we be could insourcing NAT64
122       // to avoid double NAT and improve performance. However, this
123       // breaks WebRTC, so we push it to the back.
124       addresses.AppendElement(addr);
125     }
126   }
127 
128   auto builder = aNewRRSet->Build();
129   builder.SetAddresses(std::move(addresses));
130   return builder.Finish();
131 }
132 
133 // Returns true if a prefix was read and saved to the argument
NAT64PrefixFromPref(NetAddr * prefix)134 static inline bool NAT64PrefixFromPref(NetAddr* prefix) {
135   nsAutoCString nat64PrefixPref;
136 
137   nsresult rv = Preferences::GetCString(
138       "network.connectivity-service.nat64-prefix", nat64PrefixPref);
139   return !(NS_FAILED(rv) || nat64PrefixPref.IsEmpty() ||
140            NS_FAILED(prefix->InitFromString(nat64PrefixPref)) ||
141            prefix->raw.family != PR_AF_INET6);
142 }
143 
NAT64PrefixCompare(const NetAddr & prefix1,const NetAddr & prefix2)144 static inline bool NAT64PrefixCompare(const NetAddr& prefix1,
145                                       const NetAddr& prefix2) {
146   // Compare the first 96 bits as 64 + 32
147   return prefix1.inet6.ip.u64[0] == prefix2.inet6.ip.u64[0] &&
148          prefix1.inet6.ip.u32[2] == prefix2.inet6.ip.u32[2];
149 }
150 
PerformChecks()151 void NetworkConnectivityService::PerformChecks() {
152   mDNSv4 = UNKNOWN;
153   mDNSv6 = UNKNOWN;
154 
155   mIPv4 = UNKNOWN;
156   mIPv6 = UNKNOWN;
157 
158   mNAT64 = UNKNOWN;
159 
160   {
161     MutexAutoLock lock(mLock);
162     mNAT64Prefixes.Clear();
163 
164     // NAT64 checks might be disabled.
165     // Since We can't guarantee a DNS response, we should set up
166     // NAT64 manually now if needed.
167 
168     NetAddr priorityPrefix{};
169     bool havePrefix = NAT64PrefixFromPref(&priorityPrefix);
170     if (havePrefix) {
171       mNAT64Prefixes.AppendElement(priorityPrefix);
172       mNAT64 = OK;
173     }
174   }
175 
176   RecheckDNS();
177   RecheckIPConnectivity();
178 }
179 
NotifyObservers(const char * aTopic)180 static inline void NotifyObservers(const char* aTopic) {
181   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
182   obs->NotifyObservers(nullptr, aTopic, nullptr);
183 }
184 
SaveNAT64Prefixes(nsIDNSRecord * aRecord)185 void NetworkConnectivityService::SaveNAT64Prefixes(nsIDNSRecord* aRecord) {
186   nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord);
187   MutexAutoLock lock(mLock);
188   mNAT64Prefixes.Clear();
189 
190   NetAddr priorityPrefix{};
191   bool havePrefix = NAT64PrefixFromPref(&priorityPrefix);
192   if (havePrefix) {
193     mNAT64 = OK;
194     mNAT64Prefixes.AppendElement(priorityPrefix);
195   }
196 
197   if (!rec) {
198     if (!havePrefix) {
199       mNAT64 = NOT_AVAILABLE;
200     }
201     return;
202   }
203 
204   mNAT64 = UNKNOWN;
205   NetAddr addr{};
206 
207   // use port 80 as dummy value for NetAddr
208   while (NS_SUCCEEDED(rec->GetNextAddr(80, &addr))) {
209     if (addr.raw.family != AF_INET6 || addr.IsIPAddrV4Mapped()) {
210       // These are not the kind of addresses we are looking for.
211       continue;
212     }
213 
214     // RFC 7050 does not require the embedded IPv4 to be
215     // at the end of IPv6. In practice, and as we assume,
216     // it is always at the end.
217     // The embedded IP must be 192.0.0.170 or 192.0.0.171
218 
219     // Clear the last bit to compare with the next one.
220     addr.inet6.ip.u8[15] &= ~(uint32_t)1;
221     if ((addr.inet6.ip.u8[12] != 192) || (addr.inet6.ip.u8[13] != 0) ||
222         (addr.inet6.ip.u8[14] != 0) || (addr.inet6.ip.u8[15] != 170)) {
223       continue;
224     }
225 
226     mNAT64Prefixes.AppendElement(addr);
227   }
228 
229   size_t length = mNAT64Prefixes.Length();
230   if (length == 0) {
231     mNAT64 = NOT_AVAILABLE;
232     return;
233   }
234 
235   // Remove duplicates. Typically a DNS64 resolver sends every
236   // prefix twice with address with different last bits. We want
237   // a list of unique prefixes while reordering is not allowed.
238   // We must not handle the case with an element in-between
239   // two identical ones, which is never the case for a properly
240   // configured DNS64 resolver.
241 
242   NetAddr prev = mNAT64Prefixes[0];
243 
244   for (size_t i = 1; i < length; i++) {
245     if (NAT64PrefixCompare(prev, mNAT64Prefixes[i])) {
246       mNAT64Prefixes.RemoveElementAt(i);
247       i--;
248       length--;
249     } else {
250       prev = mNAT64Prefixes[i];
251     }
252   }
253 
254   // The prioritized address might also appear in the record we received.
255 
256   if (havePrefix) {
257     for (size_t i = 1; i < length; i++) {
258       if (NAT64PrefixCompare(priorityPrefix, mNAT64Prefixes[i])) {
259         mNAT64Prefixes.RemoveElementAt(i);
260         // It wouldn't appear more than once.
261         break;
262       }
263     }
264   }
265 
266   mNAT64 = OK;
267 }
268 
269 NS_IMETHODIMP
OnLookupComplete(nsICancelable * aRequest,nsIDNSRecord * aRecord,nsresult aStatus)270 NetworkConnectivityService::OnLookupComplete(nsICancelable* aRequest,
271                                              nsIDNSRecord* aRecord,
272                                              nsresult aStatus) {
273   ConnectivityState state = aRecord ? OK : NOT_AVAILABLE;
274 
275   if (aRequest == mDNSv4Request) {
276     mDNSv4 = state;
277     mDNSv4Request = nullptr;
278   } else if (aRequest == mDNSv6Request) {
279     mDNSv6 = state;
280     mDNSv6Request = nullptr;
281   } else if (aRequest == mNAT64Request) {
282     mNAT64Request = nullptr;
283     SaveNAT64Prefixes(aRecord);
284   }
285 
286   if (!mDNSv4Request && !mDNSv6Request && !mNAT64Request) {
287     NotifyObservers("network:connectivity-service:dns-checks-complete");
288   }
289   return NS_OK;
290 }
291 
292 NS_IMETHODIMP
RecheckDNS()293 NetworkConnectivityService::RecheckDNS() {
294   bool enabled =
295       Preferences::GetBool("network.connectivity-service.enabled", false);
296   if (!enabled) {
297     return NS_OK;
298   }
299 
300   nsresult rv;
301   nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
302   OriginAttributes attrs;
303   nsAutoCString host;
304   Preferences::GetCString("network.connectivity-service.DNSv4.domain", host);
305 
306   rv = dns->AsyncResolveNative(host, nsIDNSService::RESOLVE_TYPE_DEFAULT,
307                                nsIDNSService::RESOLVE_DISABLE_IPV6 |
308                                    nsIDNSService::RESOLVE_TRR_DISABLED_MODE,
309                                nullptr, this, NS_GetCurrentThread(), attrs,
310                                getter_AddRefs(mDNSv4Request));
311   NS_ENSURE_SUCCESS(rv, rv);
312 
313   Preferences::GetCString("network.connectivity-service.DNSv6.domain", host);
314   rv = dns->AsyncResolveNative(host, nsIDNSService::RESOLVE_TYPE_DEFAULT,
315                                nsIDNSService::RESOLVE_DISABLE_IPV4 |
316                                    nsIDNSService::RESOLVE_TRR_DISABLED_MODE,
317                                nullptr, this, NS_GetCurrentThread(), attrs,
318                                getter_AddRefs(mDNSv6Request));
319   NS_ENSURE_SUCCESS(rv, rv);
320 
321   if (StaticPrefs::network_connectivity_service_nat64_check()) {
322     rv = dns->AsyncResolveNative("ipv4only.arpa"_ns,
323                                  nsIDNSService::RESOLVE_TYPE_DEFAULT,
324                                  nsIDNSService::RESOLVE_DISABLE_IPV4 |
325                                      nsIDNSService::RESOLVE_TRR_DISABLED_MODE,
326                                  nullptr, this, NS_GetCurrentThread(), attrs,
327                                  getter_AddRefs(mNAT64Request));
328     NS_ENSURE_SUCCESS(rv, rv);
329   }
330   return rv;
331 }
332 
333 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)334 NetworkConnectivityService::Observe(nsISupports* aSubject, const char* aTopic,
335                                     const char16_t* aData) {
336   if (!strcmp(aTopic, "network:captive-portal-connectivity")) {
337     // Captive portal is cleared, so we redo the checks.
338     PerformChecks();
339   } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
340     if (mDNSv4Request) {
341       mDNSv4Request->Cancel(NS_ERROR_ABORT);
342       mDNSv4Request = nullptr;
343     }
344     if (mDNSv6Request) {
345       mDNSv6Request->Cancel(NS_ERROR_ABORT);
346       mDNSv6Request = nullptr;
347     }
348     if (mNAT64Request) {
349       mNAT64Request->Cancel(NS_ERROR_ABORT);
350       mNAT64Request = nullptr;
351     }
352 
353     nsCOMPtr<nsIObserverService> observerService =
354         mozilla::services::GetObserverService();
355     observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
356     observerService->RemoveObserver(this,
357                                     "network:captive-portal-connectivity");
358     observerService->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
359   } else if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC) &&
360              !NS_LITERAL_STRING_FROM_CSTRING(NS_NETWORK_LINK_DATA_UNKNOWN)
361                   .Equals(aData)) {
362     PerformChecks();
363   }
364 
365   return NS_OK;
366 }
367 
SetupIPCheckChannel(bool ipv4)368 static inline already_AddRefed<nsIChannel> SetupIPCheckChannel(bool ipv4) {
369   nsresult rv;
370   nsAutoCString url;
371 
372   if (ipv4) {
373     rv = Preferences::GetCString("network.connectivity-service.IPv4.url", url);
374   } else {
375     rv = Preferences::GetCString("network.connectivity-service.IPv6.url", url);
376   }
377   NS_ENSURE_SUCCESS(rv, nullptr);
378 
379   nsCOMPtr<nsIURI> uri;
380   rv = NS_NewURI(getter_AddRefs(uri), url);
381   NS_ENSURE_SUCCESS(rv, nullptr);
382 
383   nsCOMPtr<nsIChannel> channel;
384   rv = NS_NewChannel(
385       getter_AddRefs(channel), uri, nsContentUtils::GetSystemPrincipal(),
386       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
387       nsIContentPolicy::TYPE_OTHER,
388       nullptr,  // nsICookieJarSettings
389       nullptr,  // aPerformanceStorage
390       nullptr,  // aLoadGroup
391       nullptr,
392       nsIRequest::LOAD_BYPASS_CACHE |    // don't read from the cache
393           nsIRequest::INHIBIT_CACHING |  // don't write the response to cache
394           nsIRequest::LOAD_ANONYMOUS);   // prevent privacy leaks
395   NS_ENSURE_SUCCESS(rv, nullptr);
396 
397   rv = channel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
398   NS_ENSURE_SUCCESS(rv, nullptr);
399 
400   {
401     // Prevent HTTPS-Only Mode from upgrading the OCSP request.
402     nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
403     uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
404     httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT;
405     loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
406 
407     // allow deprecated HTTP request from SystemPrincipal
408     loadInfo->SetAllowDeprecatedSystemRequests(true);
409   }
410 
411   nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(channel);
412   NS_ENSURE_TRUE(internalChan, nullptr);
413 
414   if (ipv4) {
415     internalChan->SetIPv6Disabled();
416   } else {
417     internalChan->SetIPv4Disabled();
418   }
419 
420   return channel.forget();
421 }
422 
423 NS_IMETHODIMP
RecheckIPConnectivity()424 NetworkConnectivityService::RecheckIPConnectivity() {
425   bool enabled =
426       Preferences::GetBool("network.connectivity-service.enabled", false);
427   if (!enabled) {
428     return NS_OK;
429   }
430 
431   if (xpc::AreNonLocalConnectionsDisabled() &&
432       !Preferences::GetBool("network.captive-portal-service.testMode", false)) {
433     return NS_OK;
434   }
435 
436   if (mIPv4Channel) {
437     mIPv4Channel->Cancel(NS_ERROR_ABORT);
438     mIPv4Channel = nullptr;
439   }
440   if (mIPv6Channel) {
441     mIPv6Channel->Cancel(NS_ERROR_ABORT);
442     mIPv6Channel = nullptr;
443   }
444 
445   nsresult rv;
446   mHasNetworkId = false;
447   mCheckedNetworkId = false;
448   mIPv4Channel = SetupIPCheckChannel(/* ipv4 = */ true);
449   if (mIPv4Channel) {
450     rv = mIPv4Channel->AsyncOpen(this);
451     NS_ENSURE_SUCCESS(rv, rv);
452   }
453 
454   mIPv6Channel = SetupIPCheckChannel(/* ipv4 = */ false);
455   if (mIPv6Channel) {
456     rv = mIPv6Channel->AsyncOpen(this);
457     NS_ENSURE_SUCCESS(rv, rv);
458   }
459 
460   return NS_OK;
461 }
462 
463 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest)464 NetworkConnectivityService::OnStartRequest(nsIRequest* aRequest) {
465   return NS_OK;
466 }
467 
468 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)469 NetworkConnectivityService::OnStopRequest(nsIRequest* aRequest,
470                                           nsresult aStatusCode) {
471   if (aStatusCode == NS_ERROR_ABORT) {
472     return NS_OK;
473   }
474 
475   ConnectivityState status = NS_FAILED(aStatusCode) ? NOT_AVAILABLE : OK;
476 
477   if (aRequest == mIPv4Channel) {
478     mIPv4 = status;
479     mIPv4Channel = nullptr;
480 
481     if (mIPv4 == nsINetworkConnectivityService::OK) {
482       Telemetry::AccumulateCategorical(
483           mHasNetworkId ? Telemetry::LABELS_NETWORK_ID_ONLINE::present
484                         : Telemetry::LABELS_NETWORK_ID_ONLINE::absent);
485       LOG(("mHasNetworkId : %d\n", mHasNetworkId));
486     }
487   } else if (aRequest == mIPv6Channel) {
488     mIPv6 = status;
489     mIPv6Channel = nullptr;
490   }
491 
492   if (!mIPv6Channel && !mIPv4Channel) {
493     NotifyObservers("network:connectivity-service:ip-checks-complete");
494   }
495 
496   return NS_OK;
497 }
498 
499 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)500 NetworkConnectivityService::OnDataAvailable(nsIRequest* aRequest,
501                                             nsIInputStream* aInputStream,
502                                             uint64_t aOffset, uint32_t aCount) {
503   nsAutoCString data;
504 
505   // We perform this check here, instead of doing it in OnStopRequest in case
506   // a network down event occurs after the data has arrived but before we fire
507   // OnStopRequest. That would cause us to report a missing networkID, even
508   // though it was not empty while receiving data.
509   if (aRequest == mIPv4Channel && !mCheckedNetworkId) {
510     nsCOMPtr<nsINetworkLinkService> nls =
511         do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID);
512     nsAutoCString networkId;
513     if (nls) {
514       nls->GetNetworkID(networkId);
515     }
516     mHasNetworkId = !networkId.IsEmpty();
517     mCheckedNetworkId = true;
518   }
519 
520   Unused << NS_ReadInputStreamToString(aInputStream, data, aCount);
521   return NS_OK;
522 }
523 
524 }  // namespace net
525 }  // namespace mozilla
526