nsNetworkLinkService.mm (31073B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 #include <vector> 6 #include <algorithm> 7 8 #include <sys/socket.h> 9 #include <sys/sysctl.h> 10 11 #include <net/if.h> 12 #include <net/if_dl.h> 13 #include <net/if_types.h> 14 #include <net/route.h> 15 #include <netinet/in.h> 16 #include <netinet/if_ether.h> 17 #include <arpa/inet.h> 18 #include <ifaddrs.h> 19 #include <resolv.h> 20 21 #include "nsCOMPtr.h" 22 #include "nsIObserverService.h" 23 #include "nsServiceManagerUtils.h" 24 #include "nsString.h" 25 #include "nsCRT.h" 26 #include "nsNetCID.h" 27 #include "nsThreadUtils.h" 28 #include "mozilla/AppShutdown.h" 29 #include "mozilla/Components.h" 30 #include "mozilla/Logging.h" 31 #include "mozilla/StaticPrefs_network.h" 32 #include "mozilla/SHA1.h" 33 #include "mozilla/Base64.h" 34 #include "mozilla/ScopeExit.h" 35 #include "mozilla/Services.h" 36 #include "mozilla/glean/NetwerkMetrics.h" 37 #include "nsNetworkLinkService.h" 38 #include "../../base/IPv6Utils.h" 39 #include "../LinkServiceCommon.h" 40 #include "../NetworkLinkServiceDefines.h" 41 42 #import <Cocoa/Cocoa.h> 43 #import <netinet/in.h> 44 45 #define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed" 46 47 using namespace mozilla; 48 49 static LazyLogModule gNotifyAddrLog("nsNotifyAddr"); 50 #define LOG(args) MOZ_LOG(gNotifyAddrLog, mozilla::LogLevel::Debug, args) 51 52 // See bug 1584165. Sometimes the ARP table is empty or doesn't have 53 // the entry of gateway after the network change, so we'd like to delay 54 // the calaulation of network id. 55 static const uint32_t kNetworkIdDelayAfterChange = 3000; 56 57 // When you remove search domains from the settings page and hit Apply a 58 // network change event is generated, but res.dnsrch is not updated to the 59 // correct values. Thus, after a network change, we add a small delay to 60 // the runnable so the OS has the chance to update the values. 61 static const uint32_t kDNSSuffixDelayAfterChange = 50; 62 63 // If non-successful, extract the error code and return it. This 64 // error code dance is inspired by 65 // http://developer.apple.com/technotes/tn/tn1145.html 66 static OSStatus getErrorCodeBool(Boolean success) { 67 OSStatus err = noErr; 68 if (!success) { 69 int scErr = ::SCError(); 70 if (scErr == kSCStatusOK) { 71 scErr = kSCStatusFailed; 72 } 73 err = scErr; 74 } 75 return err; 76 } 77 78 // If given a NULL pointer, return the error code. 79 static OSStatus getErrorCodePtr(const void* value) { 80 return getErrorCodeBool(value != nullptr); 81 } 82 83 // Convenience function to allow NULL input. 84 static void CFReleaseSafe(CFTypeRef cf) { 85 if (cf) { 86 // "If cf is NULL, this will cause a runtime error and your 87 // application will crash." / Apple docs 88 ::CFRelease(cf); 89 } 90 } 91 92 mozilla::Atomic<bool, mozilla::MemoryOrdering::Relaxed> sHasNonLocalIPv6{true}; 93 94 NS_IMPL_ISUPPORTS(nsNetworkLinkService, nsINetworkLinkService, nsIObserver, 95 nsITimerCallback, nsINamed) 96 97 nsNetworkLinkService::nsNetworkLinkService() 98 : mLinkUp(true), 99 mStatusKnown(false), 100 mReachability(nullptr), 101 mCFRunLoop(nullptr), 102 mRunLoopSource(nullptr), 103 mStoreRef(nullptr), 104 mMutex("nsNetworkLinkService::mMutex") {} 105 106 nsNetworkLinkService::~nsNetworkLinkService() = default; 107 108 NS_IMETHODIMP 109 nsNetworkLinkService::GetIsLinkUp(bool* aIsUp) { 110 *aIsUp = mLinkUp; 111 return NS_OK; 112 } 113 114 NS_IMETHODIMP 115 nsNetworkLinkService::GetLinkStatusKnown(bool* aIsUp) { 116 *aIsUp = mStatusKnown; 117 return NS_OK; 118 } 119 120 NS_IMETHODIMP 121 nsNetworkLinkService::GetLinkType(uint32_t* aLinkType) { 122 NS_ENSURE_ARG_POINTER(aLinkType); 123 124 // XXX This function has not yet been implemented for this platform 125 *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN; 126 return NS_OK; 127 } 128 129 NS_IMETHODIMP 130 nsNetworkLinkService::GetNetworkID(nsACString& aNetworkID) { 131 #ifdef BASE_BROWSER_VERSION 132 aNetworkID.Truncate(); 133 #else 134 MutexAutoLock lock(mMutex); 135 aNetworkID = mNetworkId; 136 #endif 137 return NS_OK; 138 } 139 140 NS_IMETHODIMP 141 nsNetworkLinkService::GetPlatformDNSIndications( 142 uint32_t* aPlatformDNSIndications) { 143 return NS_ERROR_NOT_IMPLEMENTED; 144 } 145 146 void nsNetworkLinkService::GetDnsSuffixListInternal() { 147 MOZ_ASSERT(!NS_IsMainThread()); 148 LOG(("GetDnsSuffixListInternal")); 149 150 auto sendNotification = mozilla::MakeScopeExit([self = RefPtr{this}] { 151 NS_DispatchToMainThread(NS_NewRunnableFunction( 152 "nsNetworkLinkService::GetDnsSuffixListInternal", [self]() { 153 self->NotifyObservers(NS_DNS_SUFFIX_LIST_UPDATED_TOPIC, nullptr); 154 })); 155 }); 156 157 nsTArray<nsCString> result; 158 159 struct __res_state res; 160 if (res_ninit(&res) == 0) { 161 for (int i = 0; i < MAXDNSRCH; i++) { 162 if (!res.dnsrch[i]) { 163 break; 164 } 165 LOG(("DNS search domain from [%s]\n", res.dnsrch[i])); 166 result.AppendElement(nsCString(res.dnsrch[i])); 167 } 168 res_nclose(&res); 169 } 170 171 MutexAutoLock lock(mMutex); 172 mDNSSuffixList = std::move(result); 173 } 174 175 NS_IMETHODIMP 176 nsNetworkLinkService::GetDnsSuffixList(nsTArray<nsCString>& aDnsSuffixList) { 177 aDnsSuffixList.Clear(); 178 179 MutexAutoLock lock(mMutex); 180 aDnsSuffixList.AppendElements(mDNSSuffixList); 181 return NS_OK; 182 } 183 184 NS_IMETHODIMP 185 nsNetworkLinkService::GetResolvers(nsTArray<RefPtr<nsINetAddr>>& aResolvers) { 186 return NS_ERROR_NOT_IMPLEMENTED; 187 } 188 189 NS_IMETHODIMP 190 nsNetworkLinkService::GetNativeResolvers( 191 nsTArray<mozilla::net::NetAddr>& aResolvers) { 192 return NS_ERROR_NOT_IMPLEMENTED; 193 } 194 195 #ifndef SA_SIZE 196 # define SA_SIZE(sa) \ 197 ((!(sa) || ((struct sockaddr*)(sa))->sa_len == 0) \ 198 ? sizeof(uint32_t) \ 199 : 1 + ((((struct sockaddr*)(sa))->sa_len - 1) | \ 200 (sizeof(uint32_t) - 1))) 201 #endif 202 203 static bool getMac(struct sockaddr_dl* sdl, char* buf, size_t bufsize) { 204 unsigned char* mac; 205 mac = (unsigned char*)LLADDR(sdl); 206 207 if (sdl->sdl_alen != 6) { 208 LOG(("networkid: unexpected MAC size %u", sdl->sdl_alen)); 209 return false; 210 } 211 212 snprintf(buf, bufsize, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], 213 mac[2], mac[3], mac[4], mac[5]); 214 return true; 215 } 216 217 /* If the IP matches, get the MAC and return true */ 218 static bool matchIp(struct sockaddr_dl* sdl, struct sockaddr_inarp* addr, 219 char* ip, char* buf, size_t bufsize) { 220 if (sdl->sdl_alen) { 221 if (!strcmp(inet_ntoa(addr->sin_addr), ip)) { 222 if (getMac(sdl, buf, bufsize)) { 223 return true; /* done! */ 224 } 225 } 226 } 227 return false; /* continue */ 228 } 229 230 /* 231 * Scan for the 'IP' address in the ARP table and store the corresponding MAC 232 * address in 'mac'. The output buffer is 'maclen' bytes big. 233 * 234 * Returns 'true' if it found the IP and returns a MAC. 235 */ 236 static bool scanArp(char* ip, char* mac, size_t maclen) { 237 int mib[6]; 238 char *lim, *next; 239 int st; 240 241 mib[0] = CTL_NET; 242 mib[1] = PF_ROUTE; 243 mib[2] = 0; 244 mib[3] = AF_INET; 245 mib[4] = NET_RT_FLAGS; 246 mib[5] = RTF_LLINFO; 247 248 size_t needed; 249 auto allocateBuf = [&]() -> UniquePtr<char[]> { 250 // calling sysctl with a null buffer to get the minimum buffer size 251 if (sysctl(mib, 6, nullptr, &needed, nullptr, 0) < 0) { 252 return nullptr; 253 } 254 255 if (needed == 0) { 256 LOG(("scanArp: empty table")); 257 return nullptr; 258 } 259 260 return MakeUnique<char[]>(needed); 261 }; 262 263 UniquePtr<char[]> buf = allocateBuf(); 264 if (!buf) { 265 return false; 266 } 267 268 st = sysctl(mib, 6, &buf[0], &needed, nullptr, 0); 269 // If errno is ENOMEM, try to allocate a new buffer and try again. 270 if (st != 0) { 271 if (errno != ENOMEM) { 272 return false; 273 } 274 275 buf = allocateBuf(); 276 if (!buf) { 277 return false; 278 } 279 280 st = sysctl(mib, 6, &buf[0], &needed, nullptr, 0); 281 if (st == -1) { 282 return false; 283 } 284 } 285 286 lim = &buf[needed]; 287 288 struct rt_msghdr* rtm; 289 for (next = &buf[0]; next < lim; next += rtm->rtm_msglen) { 290 rtm = reinterpret_cast<struct rt_msghdr*>(next); 291 struct sockaddr_inarp* sin2 = 292 reinterpret_cast<struct sockaddr_inarp*>(rtm + 1); 293 struct sockaddr_dl* sdl = 294 reinterpret_cast<struct sockaddr_dl*>((char*)sin2 + SA_SIZE(sin2)); 295 if (matchIp(sdl, sin2, ip, mac, maclen)) { 296 return true; 297 } 298 } 299 300 return false; 301 } 302 303 // Append the mac address of rtm to `stringsToHash`. If it's not in arp table, 304 // append ifname and IP address. 305 static bool parseHashKey(struct rt_msghdr* rtm, 306 nsTArray<nsCString>& stringsToHash, 307 bool skipDstCheck) { 308 struct sockaddr* sa; 309 struct sockaddr_in* sockin; 310 char ip[INET_ADDRSTRLEN]; 311 312 // Ignore the routing table message without destination/gateway sockaddr. 313 // Destination address is needed to check if the gateway is default or 314 // overwritten by VPN. If yes, append the mac address or IP/interface name to 315 // `stringsToHash`. 316 if ((rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY)) != (RTA_DST | RTA_GATEWAY)) { 317 return false; 318 } 319 320 sa = reinterpret_cast<struct sockaddr*>(rtm + 1); 321 322 struct sockaddr* destination = 323 reinterpret_cast<struct sockaddr*>((char*)sa + RTAX_DST * SA_SIZE(sa)); 324 if (!destination || destination->sa_family != AF_INET) { 325 return false; 326 } 327 328 sockin = reinterpret_cast<struct sockaddr_in*>(destination); 329 330 inet_ntop(AF_INET, &sockin->sin_addr.s_addr, ip, sizeof(ip) - 1); 331 332 if (!skipDstCheck && strcmp("0.0.0.0", ip)) { 333 return false; 334 } 335 336 struct sockaddr* gateway = reinterpret_cast<struct sockaddr*>( 337 (char*)sa + RTAX_GATEWAY * SA_SIZE(sa)); 338 339 if (!gateway) { 340 return false; 341 } 342 if (gateway->sa_family == AF_INET) { 343 sockin = reinterpret_cast<struct sockaddr_in*>(gateway); 344 inet_ntop(AF_INET, &sockin->sin_addr.s_addr, ip, sizeof(ip) - 1); 345 char mac[18]; 346 347 // TODO: cache the arp table instead of multiple system call. 348 if (scanArp(ip, mac, sizeof(mac))) { 349 stringsToHash.AppendElement(nsCString(mac)); 350 } else { 351 // Can't find a real MAC address. This might be a VPN gateway. 352 char buf[IFNAMSIZ] = {0}; 353 char* ifName = if_indextoname(rtm->rtm_index, buf); 354 if (!ifName) { 355 LOG(("parseHashKey: AF_INET if_indextoname failed")); 356 return false; 357 } 358 359 stringsToHash.AppendElement(nsCString(ifName)); 360 stringsToHash.AppendElement(nsCString(ip)); 361 } 362 } else if (gateway->sa_family == AF_LINK) { 363 char buf[64]; 364 struct sockaddr_dl* sockdl = reinterpret_cast<struct sockaddr_dl*>(gateway); 365 if (getMac(sockdl, buf, sizeof(buf))) { 366 stringsToHash.AppendElement(nsCString(buf)); 367 } else { 368 char buf[IFNAMSIZ] = {0}; 369 char* ifName = if_indextoname(rtm->rtm_index, buf); 370 if (!ifName) { 371 LOG(("parseHashKey: AF_LINK if_indextoname failed")); 372 return false; 373 } 374 375 stringsToHash.AppendElement(nsCString(ifName)); 376 } 377 } 378 return true; 379 } 380 381 // It detects the IP of the default gateways in the routing table, then the MAC 382 // address of that IP in the ARP table before it hashes that string (to avoid 383 // information leakage). 384 bool nsNetworkLinkService::RoutingTable(nsTArray<nsCString>& aHash) { 385 size_t needed; 386 int mib[6]; 387 struct rt_msghdr* rtm; 388 389 mib[0] = CTL_NET; 390 mib[1] = PF_ROUTE; 391 mib[2] = 0; 392 mib[3] = 0; 393 mib[4] = NET_RT_DUMP; 394 mib[5] = 0; 395 396 if (sysctl(mib, 6, nullptr, &needed, nullptr, 0) < 0) { 397 return false; 398 } 399 400 UniquePtr<char[]> buf(new char[needed]); 401 402 if (sysctl(mib, 6, &buf[0], &needed, nullptr, 0) < 0) { 403 return false; 404 } 405 406 char* lim = &buf[0] + needed; 407 bool rv = false; 408 409 // `next + 1 < lim` ensures we have valid `rtm->rtm_msglen` which is an 410 // unsigned short at the beginning of `rt_msghdr`. 411 for (char* next = &buf[0]; next + 1 < lim; next += rtm->rtm_msglen) { 412 rtm = reinterpret_cast<struct rt_msghdr*>(next); 413 414 if (next + rtm->rtm_msglen > lim) { 415 LOG(("Rt msg is truncated...")); 416 break; 417 } 418 419 if (parseHashKey(rtm, aHash, false)) { 420 rv = true; 421 } 422 } 423 return rv; 424 } 425 426 // Detect the routing of network.netlink.route.check.IPv4 427 bool nsNetworkLinkService::RoutingFromKernel(nsTArray<nsCString>& aHash) { 428 int sockfd; 429 if ((sockfd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { 430 LOG(("RoutingFromKernel: Can create a socket for network id")); 431 return false; 432 } 433 auto sockfd_guard = mozilla::MakeScopeExit([sockfd] { close(sockfd); }); 434 435 MOZ_ASSERT(!NS_IsMainThread()); 436 437 size_t needed = 1024; 438 struct rt_msghdr* rtm; 439 struct sockaddr_in* sin; 440 UniquePtr<char[]> buf(new char[needed]); 441 pid_t pid; 442 int seq; 443 444 rtm = reinterpret_cast<struct rt_msghdr*>(&buf[0]); 445 memset(rtm, 0, sizeof(struct rt_msghdr)); 446 rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in); 447 rtm->rtm_version = RTM_VERSION; 448 rtm->rtm_type = RTM_GET; 449 rtm->rtm_addrs = RTA_DST; 450 rtm->rtm_pid = (pid = getpid()); 451 rtm->rtm_seq = (seq = random()); 452 453 sin = reinterpret_cast<struct sockaddr_in*>(rtm + 1); 454 memset(sin, 0, sizeof(struct sockaddr_in)); 455 sin->sin_len = sizeof(struct sockaddr_in); 456 sin->sin_family = AF_INET; 457 sin->sin_addr = mRouteCheckIPv4; 458 459 if (write(sockfd, rtm, rtm->rtm_msglen) == -1) { 460 LOG(("RoutingFromKernel: write() failed. No route to the predefine " 461 "destincation")); 462 return false; 463 } 464 465 do { 466 ssize_t r; 467 if ((r = read(sockfd, rtm, needed)) < 0) { 468 LOG(("RoutingFromKernel: read() failed.")); 469 return false; 470 } 471 472 LOG(("RoutingFromKernel: read() rtm_type: %d (%d), rtm_pid: %d (%d), " 473 "rtm_seq: %d (%d)\n", 474 rtm->rtm_type, RTM_GET, rtm->rtm_pid, pid, rtm->rtm_seq, seq)); 475 } while (rtm->rtm_type != RTM_GET || rtm->rtm_pid != pid || 476 rtm->rtm_seq != seq); 477 478 return parseHashKey(rtm, aHash, true); 479 } 480 481 // Figure out the current IPv4 "network identification" string. 482 bool nsNetworkLinkService::IPv4NetworkId(SHA1Sum* aSHA1) { 483 nsTArray<nsCString> hash; 484 if (!RoutingTable(hash)) { 485 NS_WARNING("IPv4NetworkId: No default gateways"); 486 } 487 488 if (!RoutingFromKernel(hash)) { 489 NS_WARNING("IPv4NetworkId: No route to the predefined destination"); 490 } 491 492 // We didn't get any valid hash key to generate network ID. 493 if (hash.IsEmpty()) { 494 LOG(("IPv4NetworkId: No valid hash key")); 495 return false; 496 } 497 498 hash.Sort(); 499 for (uint32_t i = 0; i < hash.Length(); ++i) { 500 LOG(("IPv4NetworkId: Hashing string for network id: %s", hash[i].get())); 501 aSHA1->update(hash[i].get(), hash[i].Length()); 502 } 503 504 return true; 505 } 506 507 // 508 // Sort and hash the prefixes and netmasks 509 // 510 void nsNetworkLinkService::HashSortedPrefixesAndNetmasks( 511 std::vector<prefix_and_netmask> prefixAndNetmaskStore, SHA1Sum* sha1) { 512 // getifaddrs does not guarantee the interfaces will always be in the same 513 // order. We want to make sure the hash remains consistent Regardless of the 514 // interface order. 515 std::sort(prefixAndNetmaskStore.begin(), prefixAndNetmaskStore.end(), 516 [](prefix_and_netmask a, prefix_and_netmask b) { 517 // compare prefixStore 518 int comparedPrefix = memcmp(&a.first, &b.first, sizeof(in6_addr)); 519 if (comparedPrefix == 0) { 520 // compare netmaskStore 521 return memcmp(&a.second, &b.second, sizeof(in6_addr)) < 0; 522 } 523 return comparedPrefix < 0; 524 }); 525 526 for (const auto& prefixAndNetmask : prefixAndNetmaskStore) { 527 sha1->update(&prefixAndNetmask.first, sizeof(in6_addr)); 528 sha1->update(&prefixAndNetmask.second, sizeof(in6_addr)); 529 } 530 } 531 532 bool nsNetworkLinkService::IPv6NetworkId(SHA1Sum* sha1) { 533 struct ifaddrs* ifap; 534 std::vector<prefix_and_netmask> prefixAndNetmaskStore; 535 536 if (!getifaddrs(&ifap)) { 537 bool hasNonLocalIPv6 = false; 538 struct ifaddrs* ifa; 539 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 540 if (ifa->ifa_addr == NULL) { 541 continue; 542 } 543 if ((AF_INET6 == ifa->ifa_addr->sa_family) && 544 !(ifa->ifa_flags & (IFF_POINTOPOINT | IFF_LOOPBACK))) { 545 // only IPv6 interfaces that aren't pointtopoint or loopback 546 hasNonLocalIPv6 = true; 547 struct sockaddr_in6* sin_netmask = 548 (struct sockaddr_in6*)ifa->ifa_netmask; 549 if (sin_netmask) { 550 struct sockaddr_in6* sin_addr = (struct sockaddr_in6*)ifa->ifa_addr; 551 int scope = net::utils::ipv6_scope(sin_addr->sin6_addr.s6_addr); 552 if (scope == IPV6_SCOPE_GLOBAL) { 553 struct in6_addr prefix; 554 memset(&prefix, 0, sizeof(prefix)); 555 // Get the prefix by combining the address and netmask. 556 for (size_t i = 0; i < sizeof(prefix); ++i) { 557 prefix.s6_addr[i] = sin_addr->sin6_addr.s6_addr[i] & 558 sin_netmask->sin6_addr.s6_addr[i]; 559 } 560 561 // check if prefix and netmask was already found 562 auto prefixAndNetmask = 563 std::make_pair(prefix, sin_netmask->sin6_addr); 564 auto foundPosition = std::find_if( 565 prefixAndNetmaskStore.begin(), prefixAndNetmaskStore.end(), 566 [&prefixAndNetmask](prefix_and_netmask current) { 567 return memcmp(&prefixAndNetmask.first, ¤t.first, 568 sizeof(in6_addr)) == 0 && 569 memcmp(&prefixAndNetmask.second, ¤t.second, 570 sizeof(in6_addr)) == 0; 571 }); 572 if (foundPosition != prefixAndNetmaskStore.end()) { 573 continue; 574 } 575 prefixAndNetmaskStore.push_back(prefixAndNetmask); 576 } 577 } 578 } 579 } 580 sHasNonLocalIPv6 = hasNonLocalIPv6; 581 freeifaddrs(ifap); 582 } 583 if (prefixAndNetmaskStore.empty()) { 584 LOG(("IPv6NetworkId failed")); 585 return false; 586 } 587 588 nsNetworkLinkService::HashSortedPrefixesAndNetmasks(prefixAndNetmaskStore, 589 sha1); 590 591 return true; 592 } 593 594 void nsNetworkLinkService::calculateNetworkIdWithDelay(uint32_t aDelay) { 595 MOZ_ASSERT(NS_IsMainThread()); 596 597 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownNetTeardown)) { 598 return; 599 } 600 601 if (aDelay) { 602 if (mNetworkIdTimer) { 603 LOG(("Restart the network id timer.")); 604 mNetworkIdTimer->Cancel(); 605 } else { 606 LOG(("Create the network id timer.")); 607 mNetworkIdTimer = NS_NewTimer(); 608 } 609 mNetworkIdTimer->InitWithCallback(this, aDelay, nsITimer::TYPE_ONE_SHOT); 610 return; 611 } 612 613 nsCOMPtr<nsIEventTarget> target; 614 target = mozilla::components::StreamTransport::Service(); 615 if (!target) { 616 return; 617 } 618 619 MOZ_ALWAYS_SUCCEEDS(target->Dispatch( 620 NewRunnableMethod("nsNetworkLinkService::calculateNetworkIdInternal", 621 this, 622 &nsNetworkLinkService::calculateNetworkIdInternal), 623 NS_DISPATCH_NORMAL)); 624 } 625 626 NS_IMETHODIMP 627 nsNetworkLinkService::Notify(nsITimer* aTimer) { 628 MOZ_ASSERT(aTimer == mNetworkIdTimer); 629 630 mNetworkIdTimer = nullptr; 631 calculateNetworkIdWithDelay(0); 632 return NS_OK; 633 } 634 635 NS_IMETHODIMP 636 nsNetworkLinkService::GetName(nsACString& aName) { 637 aName.AssignLiteral("nsNetworkLinkService"); 638 return NS_OK; 639 } 640 641 void nsNetworkLinkService::calculateNetworkIdInternal(void) { 642 MOZ_ASSERT(!NS_IsMainThread(), "Should not be called on the main thread"); 643 SHA1Sum sha1; 644 bool idChanged = false; 645 bool found4 = IPv4NetworkId(&sha1); 646 bool found6 = IPv6NetworkId(&sha1); 647 648 if (found4 || found6) { 649 nsAutoCString output; 650 SeedNetworkId(sha1); 651 uint8_t digest[SHA1Sum::kHashSize]; 652 sha1.finish(digest); 653 nsAutoCString newString(reinterpret_cast<char*>(digest), 654 SHA1Sum::kHashSize); 655 nsresult rv = Base64Encode(newString, output); 656 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); 657 LOG(("networkid: id %s\n", output.get())); 658 MutexAutoLock lock(mMutex); 659 if (mNetworkId != output) { 660 // new id 661 if (found4 && !found6) { 662 glean::network::id.AccumulateSingleSample(1); // IPv4 only 663 } else if (!found4 && found6) { 664 glean::network::id.AccumulateSingleSample(3); // IPv6 only 665 } else { 666 glean::network::id.AccumulateSingleSample(4); // Both! 667 } 668 mNetworkId = output; 669 idChanged = true; 670 } else { 671 // same id 672 LOG(("Same network id")); 673 glean::network::id.AccumulateSingleSample(2); 674 } 675 } else { 676 // no id 677 LOG(("No network id")); 678 MutexAutoLock lock(mMutex); 679 if (!mNetworkId.IsEmpty()) { 680 mNetworkId.Truncate(); 681 idChanged = true; 682 glean::network::id.AccumulateSingleSample(0); 683 } 684 } 685 686 // Don't report network change if this is the first time we calculate the id. 687 static bool initialIDCalculation = true; 688 if (idChanged && !initialIDCalculation) { 689 RefPtr<nsNetworkLinkService> self = this; 690 691 NS_DispatchToMainThread(NS_NewRunnableFunction( 692 "nsNetworkLinkService::calculateNetworkIdInternal", 693 [self]() { self->OnNetworkIdChanged(); })); 694 } 695 696 initialIDCalculation = false; 697 } 698 699 NS_IMETHODIMP 700 nsNetworkLinkService::Observe(nsISupports* subject, const char* topic, 701 const char16_t* data) { 702 if (!strcmp(topic, "xpcom-shutdown")) { 703 Shutdown(); 704 } 705 706 return NS_OK; 707 } 708 709 /* static */ 710 void nsNetworkLinkService::NetworkConfigChanged(SCDynamicStoreRef aStoreREf, 711 CFArrayRef aChangedKeys, 712 void* aInfo) { 713 LOG(("nsNetworkLinkService::NetworkConfigChanged")); 714 715 bool ipConfigChanged = false; 716 bool dnsConfigChanged = false; 717 for (CFIndex i = 0; i < CFArrayGetCount(aChangedKeys); ++i) { 718 CFStringRef key = 719 static_cast<CFStringRef>(CFArrayGetValueAtIndex(aChangedKeys, i)); 720 if (CFStringHasSuffix(key, kSCEntNetIPv4) || 721 CFStringHasSuffix(key, kSCEntNetIPv6)) { 722 ipConfigChanged = true; 723 } 724 if (CFStringHasSuffix(key, kSCEntNetDNS)) { 725 dnsConfigChanged = true; 726 } 727 } 728 729 nsNetworkLinkService* service = static_cast<nsNetworkLinkService*>(aInfo); 730 if (ipConfigChanged) { 731 service->OnIPConfigChanged(); 732 } 733 734 if (dnsConfigChanged) { 735 service->DNSConfigChanged(kDNSSuffixDelayAfterChange); 736 } 737 } 738 739 void nsNetworkLinkService::DNSConfigChanged(uint32_t aDelayMs) { 740 LOG(("nsNetworkLinkService::DNSConfigChanged")); 741 nsCOMPtr<nsIEventTarget> target; 742 target = mozilla::components::StreamTransport::Service(); 743 if (!target) { 744 return; 745 } 746 if (aDelayMs) { 747 MutexAutoLock lock(mMutex); 748 nsCOMPtr<nsITimer> timer; 749 MOZ_ALWAYS_SUCCEEDS(NS_NewTimerWithCallback( 750 getter_AddRefs(timer), 751 [self = RefPtr{this}](nsITimer* aTimer) { 752 self->GetDnsSuffixListInternal(); 753 754 MutexAutoLock lock(self->mMutex); 755 self->mDNSConfigChangedTimers.RemoveElement(aTimer); 756 }, 757 TimeDuration::FromMilliseconds(aDelayMs), nsITimer::TYPE_ONE_SHOT, 758 "nsNetworkLinkService::GetDnsSuffixListInternal"_ns, target)); 759 mDNSConfigChangedTimers.AppendElement(timer); 760 } else { 761 MOZ_ALWAYS_SUCCEEDS(target->Dispatch(NS_NewRunnableFunction( 762 "nsNetworkLinkService::GetDnsSuffixListInternal", 763 [self = RefPtr{this}]() { self->GetDnsSuffixListInternal(); }))); 764 } 765 } 766 767 nsresult nsNetworkLinkService::Init(void) { 768 nsresult rv; 769 770 nsCOMPtr<nsIObserverService> observerService; 771 observerService = mozilla::components::Observer::Service(&rv); 772 NS_ENSURE_SUCCESS(rv, rv); 773 774 rv = observerService->AddObserver(this, "xpcom-shutdown", false); 775 NS_ENSURE_SUCCESS(rv, rv); 776 777 if (inet_pton(AF_INET, ROUTE_CHECK_IPV4, &mRouteCheckIPv4) != 1) { 778 LOG(("Cannot parse address " ROUTE_CHECK_IPV4)); 779 MOZ_DIAGNOSTIC_CRASH("Cannot parse address " ROUTE_CHECK_IPV4); 780 return NS_ERROR_UNEXPECTED; 781 } 782 783 // If the network reachability API can reach 0.0.0.0 without 784 // requiring a connection, there is a network interface available. 785 struct sockaddr_in addr; 786 bzero(&addr, sizeof(addr)); 787 addr.sin_len = sizeof(addr); 788 addr.sin_family = AF_INET; 789 mReachability = ::SCNetworkReachabilityCreateWithAddress( 790 nullptr, (struct sockaddr*)&addr); 791 if (!mReachability) { 792 return NS_ERROR_NOT_AVAILABLE; 793 } 794 795 SCNetworkReachabilityContext context = {0, this, nullptr, nullptr, nullptr}; 796 if (!::SCNetworkReachabilitySetCallback(mReachability, ReachabilityChanged, 797 &context)) { 798 NS_WARNING("SCNetworkReachabilitySetCallback failed."); 799 ::CFRelease(mReachability); 800 mReachability = nullptr; 801 return NS_ERROR_NOT_AVAILABLE; 802 } 803 804 SCDynamicStoreContext storeContext = {0, this, nullptr, nullptr, nullptr}; 805 mStoreRef = 806 ::SCDynamicStoreCreate(nullptr, CFSTR("IPAndDNSChangeCallbackSCF"), 807 NetworkConfigChanged, &storeContext); 808 809 CFStringRef patterns[4] = {nullptr, nullptr, nullptr, nullptr}; 810 OSStatus err = getErrorCodePtr(mStoreRef); 811 if (err == noErr) { 812 // This pattern is "State:/Network/Service/[^/]+/IPv4". 813 patterns[0] = ::SCDynamicStoreKeyCreateNetworkServiceEntity( 814 nullptr, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); 815 // This pattern is "State:/Network/Service/[^/]+/IPv6". 816 patterns[1] = ::SCDynamicStoreKeyCreateNetworkServiceEntity( 817 nullptr, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); 818 // This pattern is "State:/Network/Service/[^/]+/DNS". 819 patterns[2] = ::SCDynamicStoreKeyCreateNetworkServiceEntity( 820 nullptr, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetDNS); 821 // This pattern is "Setup:/Network/Service/[^/]+/DNS". 822 patterns[3] = ::SCDynamicStoreKeyCreateNetworkServiceEntity( 823 nullptr, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetDNS); 824 if (!patterns[0] || !patterns[1] || !patterns[2] || !patterns[3]) { 825 err = -1; 826 } 827 } 828 829 CFArrayRef patternList = nullptr; 830 // Create a pattern list containing just one pattern, 831 // then tell SCF that we want to watch changes in keys 832 // that match that pattern list, then create our run loop 833 // source. 834 if (err == noErr) { 835 patternList = ::CFArrayCreate(nullptr, (const void**)patterns, 4, 836 &kCFTypeArrayCallBacks); 837 if (!patternList) { 838 err = -1; 839 } 840 } 841 if (err == noErr) { 842 err = getErrorCodeBool( 843 ::SCDynamicStoreSetNotificationKeys(mStoreRef, nullptr, patternList)); 844 } 845 846 if (err == noErr) { 847 mRunLoopSource = ::SCDynamicStoreCreateRunLoopSource(nullptr, mStoreRef, 0); 848 err = getErrorCodePtr(mRunLoopSource); 849 } 850 851 CFReleaseSafe(patterns[0]); 852 CFReleaseSafe(patterns[1]); 853 CFReleaseSafe(patterns[2]); 854 CFReleaseSafe(patterns[3]); 855 CFReleaseSafe(patternList); 856 857 if (err != noErr) { 858 CFReleaseSafe(mStoreRef); 859 return NS_ERROR_NOT_AVAILABLE; 860 } 861 862 // Get the current run loop. This service is initialized at startup, 863 // so we shouldn't run in to any problems with modal dialog run loops. 864 mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop]; 865 if (!mCFRunLoop) { 866 NS_WARNING("Could not get current run loop."); 867 ::CFRelease(mReachability); 868 mReachability = nullptr; 869 return NS_ERROR_NOT_AVAILABLE; 870 } 871 ::CFRetain(mCFRunLoop); 872 873 ::CFRunLoopAddSource(mCFRunLoop, mRunLoopSource, kCFRunLoopDefaultMode); 874 875 if (!::SCNetworkReachabilityScheduleWithRunLoop(mReachability, mCFRunLoop, 876 kCFRunLoopDefaultMode)) { 877 NS_WARNING("SCNetworkReachabilityScheduleWIthRunLoop failed."); 878 ::CFRelease(mReachability); 879 mReachability = nullptr; 880 ::CFRelease(mCFRunLoop); 881 mCFRunLoop = nullptr; 882 return NS_ERROR_NOT_AVAILABLE; 883 } 884 UpdateReachability(); 885 886 calculateNetworkIdWithDelay(0); 887 888 DNSConfigChanged(0); 889 890 return NS_OK; 891 } 892 893 nsresult nsNetworkLinkService::Shutdown() { 894 if (!::SCNetworkReachabilityUnscheduleFromRunLoop(mReachability, mCFRunLoop, 895 kCFRunLoopDefaultMode)) { 896 NS_WARNING("SCNetworkReachabilityUnscheduleFromRunLoop failed."); 897 } 898 899 CFRunLoopRemoveSource(mCFRunLoop, mRunLoopSource, kCFRunLoopDefaultMode); 900 901 ::CFRelease(mReachability); 902 mReachability = nullptr; 903 904 ::CFRelease(mCFRunLoop); 905 mCFRunLoop = nullptr; 906 907 ::CFRelease(mStoreRef); 908 mStoreRef = nullptr; 909 910 ::CFRelease(mRunLoopSource); 911 mRunLoopSource = nullptr; 912 913 if (mNetworkIdTimer) { 914 mNetworkIdTimer->Cancel(); 915 mNetworkIdTimer = nullptr; 916 } 917 918 nsTArray<nsCOMPtr<nsITimer>> dnsConfigChangedTimers; 919 { 920 MutexAutoLock lock(mMutex); 921 dnsConfigChangedTimers = std::move(mDNSConfigChangedTimers); 922 mDNSConfigChangedTimers.Clear(); 923 } 924 for (const auto& timer : dnsConfigChangedTimers) { 925 timer->Cancel(); 926 } 927 928 return NS_OK; 929 } 930 931 void nsNetworkLinkService::UpdateReachability() { 932 if (!mReachability) { 933 return; 934 } 935 936 SCNetworkConnectionFlags flags; 937 if (!::SCNetworkReachabilityGetFlags(mReachability, &flags)) { 938 mStatusKnown = false; 939 return; 940 } 941 942 bool reachable = (flags & kSCNetworkFlagsReachable) != 0; 943 bool needsConnection = (flags & kSCNetworkFlagsConnectionRequired) != 0; 944 945 mLinkUp = (reachable && !needsConnection); 946 mStatusKnown = true; 947 } 948 949 void nsNetworkLinkService::OnIPConfigChanged() { 950 MOZ_ASSERT(NS_IsMainThread()); 951 952 calculateNetworkIdWithDelay(kNetworkIdDelayAfterChange); 953 if (!StaticPrefs::network_notify_changed()) { 954 return; 955 } 956 957 NotifyObservers(NS_NETWORK_LINK_TOPIC, NS_NETWORK_LINK_DATA_CHANGED); 958 } 959 960 void nsNetworkLinkService::OnNetworkIdChanged() { 961 MOZ_ASSERT(NS_IsMainThread()); 962 963 NotifyObservers(NS_NETWORK_ID_CHANGED_TOPIC, nullptr); 964 } 965 966 void nsNetworkLinkService::OnReachabilityChanged() { 967 MOZ_ASSERT(NS_IsMainThread()); 968 969 if (!mStatusKnown) { 970 NotifyObservers(NS_NETWORK_LINK_TOPIC, NS_NETWORK_LINK_DATA_UNKNOWN); 971 return; 972 } 973 974 NotifyObservers(NS_NETWORK_LINK_TOPIC, mLinkUp ? NS_NETWORK_LINK_DATA_UP 975 : NS_NETWORK_LINK_DATA_DOWN); 976 } 977 978 void nsNetworkLinkService::NotifyObservers(const char* aTopic, 979 const char* aData) { 980 MOZ_ASSERT(NS_IsMainThread()); 981 982 LOG(("nsNetworkLinkService::NotifyObservers: topic:%s data:%s\n", aTopic, 983 aData ? aData : "")); 984 985 nsCOMPtr<nsIObserverService> observerService = 986 mozilla::services::GetObserverService(); 987 988 if (observerService) { 989 observerService->NotifyObservers( 990 static_cast<nsINetworkLinkService*>(this), aTopic, 991 aData ? NS_ConvertASCIItoUTF16(aData).get() : nullptr); 992 } 993 } 994 995 /* static */ 996 void nsNetworkLinkService::ReachabilityChanged(SCNetworkReachabilityRef target, 997 SCNetworkConnectionFlags flags, 998 void* info) { 999 LOG(("nsNetworkLinkService::ReachabilityChanged")); 1000 nsNetworkLinkService* service = static_cast<nsNetworkLinkService*>(info); 1001 1002 service->UpdateReachability(); 1003 service->OnReachabilityChanged(); 1004 service->calculateNetworkIdWithDelay(kNetworkIdDelayAfterChange); 1005 // If a new interface is up or the order of interfaces is changed, we should 1006 // update the DNS suffix list. 1007 service->DNSConfigChanged(0); 1008 } 1009 1010 // static 1011 bool nsINetworkLinkService::HasNonLocalIPv6Address() { 1012 return sHasNonLocalIPv6; 1013 }