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