HTTPSSVC.cpp (19814B)
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 "HTTPSSVC.h" 6 #include "mozilla/net/DNS.h" 7 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 8 #include "mozilla/StaticPrefs_network.h" 9 #include "nsHttp.h" 10 #include "nsHttpHandler.h" 11 #include "nsNetAddr.h" 12 #include "nsNetUtil.h" 13 #include "nsIDNSService.h" 14 15 namespace mozilla { 16 namespace net { 17 18 NS_IMPL_ISUPPORTS(SVCBRecord, nsISVCBRecord) 19 20 class SvcParam : public nsISVCParam, 21 public nsISVCParamAlpn, 22 public nsISVCParamNoDefaultAlpn, 23 public nsISVCParamPort, 24 public nsISVCParamIPv4Hint, 25 public nsISVCParamEchConfig, 26 public nsISVCParamIPv6Hint, 27 public nsISVCParamODoHConfig { 28 NS_DECL_THREADSAFE_ISUPPORTS 29 NS_DECL_NSISVCPARAM 30 NS_DECL_NSISVCPARAMALPN 31 NS_DECL_NSISVCPARAMNODEFAULTALPN 32 NS_DECL_NSISVCPARAMPORT 33 NS_DECL_NSISVCPARAMIPV4HINT 34 NS_DECL_NSISVCPARAMECHCONFIG 35 NS_DECL_NSISVCPARAMIPV6HINT 36 NS_DECL_NSISVCPARAMODOHCONFIG 37 public: 38 explicit SvcParam(const SvcParamType& value) : mValue(value) {}; 39 40 private: 41 virtual ~SvcParam() = default; 42 SvcParamType mValue; 43 }; 44 45 NS_IMPL_ADDREF(SvcParam) 46 NS_IMPL_RELEASE(SvcParam) 47 NS_INTERFACE_MAP_BEGIN(SvcParam) 48 NS_INTERFACE_MAP_ENTRY(nsISVCParam) 49 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVCParam) 50 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamAlpn, mValue.is<SvcParamAlpn>()) 51 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamNoDefaultAlpn, 52 mValue.is<SvcParamNoDefaultAlpn>()) 53 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamPort, mValue.is<SvcParamPort>()) 54 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamIPv4Hint, 55 mValue.is<SvcParamIpv4Hint>()) 56 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamEchConfig, 57 mValue.is<SvcParamEchConfig>()) 58 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamIPv6Hint, 59 mValue.is<SvcParamIpv6Hint>()) 60 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamODoHConfig, 61 mValue.is<SvcParamODoHConfig>()) 62 NS_INTERFACE_MAP_END 63 64 NS_IMETHODIMP 65 SvcParam::GetType(uint16_t* aType) { 66 *aType = mValue.match( 67 [](Nothing&) { return SvcParamKeyMandatory; }, 68 [](SvcParamAlpn&) { return SvcParamKeyAlpn; }, 69 [](SvcParamNoDefaultAlpn&) { return SvcParamKeyNoDefaultAlpn; }, 70 [](SvcParamPort&) { return SvcParamKeyPort; }, 71 [](SvcParamIpv4Hint&) { return SvcParamKeyIpv4Hint; }, 72 [](SvcParamEchConfig&) { return SvcParamKeyEchConfig; }, 73 [](SvcParamIpv6Hint&) { return SvcParamKeyIpv6Hint; }, 74 [](SvcParamODoHConfig&) { return SvcParamKeyODoHConfig; }); 75 return NS_OK; 76 } 77 78 NS_IMETHODIMP 79 SvcParam::GetAlpn(nsTArray<nsCString>& aAlpn) { 80 if (!mValue.is<SvcParamAlpn>()) { 81 MOZ_ASSERT(false, "Unexpected type for variant"); 82 return NS_ERROR_NOT_AVAILABLE; 83 } 84 aAlpn.AppendElements(mValue.as<SvcParamAlpn>().mValue); 85 return NS_OK; 86 } 87 88 NS_IMETHODIMP 89 SvcParam::GetPort(uint16_t* aPort) { 90 if (!mValue.is<SvcParamPort>()) { 91 MOZ_ASSERT(false, "Unexpected type for variant"); 92 return NS_ERROR_NOT_AVAILABLE; 93 } 94 *aPort = mValue.as<SvcParamPort>().mValue; 95 return NS_OK; 96 } 97 98 NS_IMETHODIMP 99 SvcParam::GetEchconfig(nsACString& aEchConfig) { 100 if (!mValue.is<SvcParamEchConfig>()) { 101 MOZ_ASSERT(false, "Unexpected type for variant"); 102 return NS_ERROR_NOT_AVAILABLE; 103 } 104 aEchConfig = mValue.as<SvcParamEchConfig>().mValue; 105 return NS_OK; 106 } 107 108 NS_IMETHODIMP 109 SvcParam::GetIpv4Hint(nsTArray<RefPtr<nsINetAddr>>& aIpv4Hint) { 110 if (!mValue.is<SvcParamIpv4Hint>()) { 111 MOZ_ASSERT(false, "Unexpected type for variant"); 112 return NS_ERROR_NOT_AVAILABLE; 113 } 114 const auto& results = mValue.as<SvcParamIpv4Hint>().mValue; 115 for (const auto& ip : results) { 116 if (ip.raw.family != AF_INET) { 117 return NS_ERROR_UNEXPECTED; 118 } 119 RefPtr<nsINetAddr> hint = new nsNetAddr(&ip); 120 aIpv4Hint.AppendElement(hint); 121 } 122 123 return NS_OK; 124 } 125 126 NS_IMETHODIMP 127 SvcParam::GetIpv6Hint(nsTArray<RefPtr<nsINetAddr>>& aIpv6Hint) { 128 if (!mValue.is<SvcParamIpv6Hint>()) { 129 MOZ_ASSERT(false, "Unexpected type for variant"); 130 return NS_ERROR_NOT_AVAILABLE; 131 } 132 const auto& results = mValue.as<SvcParamIpv6Hint>().mValue; 133 for (const auto& ip : results) { 134 if (ip.raw.family != AF_INET6) { 135 return NS_ERROR_UNEXPECTED; 136 } 137 RefPtr<nsINetAddr> hint = new nsNetAddr(&ip); 138 aIpv6Hint.AppendElement(hint); 139 } 140 return NS_OK; 141 } 142 143 NS_IMETHODIMP 144 SvcParam::GetODoHConfig(nsACString& aODoHConfig) { 145 if (!mValue.is<SvcParamODoHConfig>()) { 146 MOZ_ASSERT(false, "Unexpected type for variant"); 147 return NS_ERROR_NOT_AVAILABLE; 148 } 149 aODoHConfig = mValue.as<SvcParamODoHConfig>().mValue; 150 return NS_OK; 151 } 152 153 bool SVCB::operator<(const SVCB& aOther) const { 154 if (nsHttpHandler::EchConfigEnabled()) { 155 if (mHasEchConfig && !aOther.mHasEchConfig) { 156 return true; 157 } 158 if (!mHasEchConfig && aOther.mHasEchConfig) { 159 return false; 160 } 161 } 162 163 return mSvcFieldPriority < aOther.mSvcFieldPriority; 164 } 165 166 Maybe<uint16_t> SVCB::GetPort() const { 167 Maybe<uint16_t> port; 168 for (const auto& value : mSvcFieldValue) { 169 if (value.mValue.is<SvcParamPort>()) { 170 port.emplace(value.mValue.as<SvcParamPort>().mValue); 171 if (NS_FAILED(NS_CheckPortSafety(*port, "https"))) { 172 *port = 0; 173 } 174 return port; 175 } 176 } 177 178 return Nothing(); 179 } 180 181 bool SVCB::NoDefaultAlpn() const { 182 for (const auto& value : mSvcFieldValue) { 183 if (value.mValue.is<SvcParamKeyNoDefaultAlpn>()) { 184 return true; 185 } 186 } 187 188 return false; 189 } 190 191 void SVCB::GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const { 192 if (mSvcFieldPriority == 0) { 193 return; 194 } 195 196 for (const auto& value : mSvcFieldValue) { 197 if (value.mValue.is<SvcParamIpv4Hint>()) { 198 aAddresses.AppendElements(value.mValue.as<SvcParamIpv4Hint>().mValue); 199 } else if (value.mValue.is<SvcParamIpv6Hint>()) { 200 aAddresses.AppendElements(value.mValue.as<SvcParamIpv6Hint>().mValue); 201 } 202 } 203 } 204 205 class AlpnComparator { 206 public: 207 bool Equals(const std::tuple<nsCString, SupportedAlpnRank>& aA, 208 const std::tuple<nsCString, SupportedAlpnRank>& aB) const { 209 return std::get<1>(aA) == std::get<1>(aB); 210 } 211 bool LessThan(const std::tuple<nsCString, SupportedAlpnRank>& aA, 212 const std::tuple<nsCString, SupportedAlpnRank>& aB) const { 213 return std::get<1>(aA) > std::get<1>(aB); 214 } 215 }; 216 217 nsTArray<std::tuple<nsCString, SupportedAlpnRank>> SVCB::GetAllAlpn( 218 bool& aHasNoDefaultAlpn) const { 219 aHasNoDefaultAlpn = false; 220 nsTArray<std::tuple<nsCString, SupportedAlpnRank>> alpnList; 221 for (const auto& value : mSvcFieldValue) { 222 if (value.mValue.is<SvcParamAlpn>()) { 223 for (const auto& alpn : value.mValue.as<SvcParamAlpn>().mValue) { 224 alpnList.AppendElement(std::make_tuple(alpn, IsAlpnSupported(alpn))); 225 } 226 } else if (value.mValue.is<SvcParamKeyNoDefaultAlpn>()) { 227 // Found "no-default-alpn". 228 aHasNoDefaultAlpn = true; 229 } 230 } 231 alpnList.Sort(AlpnComparator()); 232 return alpnList; 233 } 234 235 SVCBRecord::SVCBRecord(const SVCB& data, 236 Maybe<std::tuple<nsCString, SupportedAlpnRank>> aAlpn) 237 : mData(data), mAlpn(aAlpn) { 238 mPort = mData.GetPort(); 239 } 240 241 NS_IMETHODIMP SVCBRecord::GetPriority(uint16_t* aPriority) { 242 *aPriority = mData.mSvcFieldPriority; 243 return NS_OK; 244 } 245 246 NS_IMETHODIMP SVCBRecord::GetName(nsACString& aName) { 247 aName = mData.mSvcDomainName; 248 return NS_OK; 249 } 250 251 Maybe<uint16_t> SVCBRecord::GetPort() { return mPort; } 252 253 Maybe<std::tuple<nsCString, SupportedAlpnRank>> SVCBRecord::GetAlpn() { 254 return mAlpn; 255 } 256 257 NS_IMETHODIMP SVCBRecord::GetSelectedAlpn(nsACString& aAlpn) { 258 if (!mAlpn) { 259 return NS_ERROR_NOT_AVAILABLE; 260 } 261 262 aAlpn = std::get<0>(*mAlpn); 263 return NS_OK; 264 } 265 266 NS_IMETHODIMP SVCBRecord::GetEchConfig(nsACString& aEchConfig) { 267 aEchConfig = mData.mEchConfig; 268 return NS_OK; 269 } 270 271 NS_IMETHODIMP SVCBRecord::GetODoHConfig(nsACString& aODoHConfig) { 272 aODoHConfig = mData.mODoHConfig; 273 return NS_OK; 274 } 275 276 NS_IMETHODIMP SVCBRecord::GetValues(nsTArray<RefPtr<nsISVCParam>>& aValues) { 277 for (const auto& v : mData.mSvcFieldValue) { 278 RefPtr<nsISVCParam> param = new SvcParam(v.mValue); 279 aValues.AppendElement(param); 280 } 281 return NS_OK; 282 } 283 284 NS_IMETHODIMP SVCBRecord::GetHasIPHintAddress(bool* aHasIPHintAddress) { 285 *aHasIPHintAddress = mData.mHasIPHints; 286 return NS_OK; 287 } 288 289 static bool CheckRecordIsUsableWithCname(const SVCB& aRecord, 290 const nsACString& aCname) { 291 if (StaticPrefs::network_dns_https_rr_check_record_with_cname() && 292 !aCname.IsEmpty() && !aRecord.mSvcDomainName.Equals(aCname)) { 293 return false; 294 } 295 296 return true; 297 } 298 299 static bool CheckRecordIsUsable(const SVCB& aRecord, nsIDNSService* aDNSService, 300 const nsACString& aHost, 301 uint32_t& aExcludedCount) { 302 if (!aHost.IsEmpty()) { 303 bool excluded = false; 304 if (NS_SUCCEEDED(aDNSService->IsSVCDomainNameFailed( 305 aHost, aRecord.mSvcDomainName, &excluded)) && 306 excluded) { 307 // Skip if the domain name of this record was failed to connect before. 308 ++aExcludedCount; 309 return false; 310 } 311 } 312 313 Maybe<uint16_t> port = aRecord.GetPort(); 314 if (port && *port == 0) { 315 // Found an unsafe port, skip this record. 316 return false; 317 } 318 319 return true; 320 } 321 322 static bool CheckAlpnIsUsable(SupportedAlpnRank aAlpnType, bool aNoHttp2, 323 bool aNoHttp3, bool aCheckHttp3ExcludedList, 324 const nsACString& aTargetName, 325 uint32_t& aExcludedCount) { 326 // Skip if this alpn is not supported. 327 if (aAlpnType == SupportedAlpnRank::NOT_SUPPORTED) { 328 return false; 329 } 330 331 // Skip if we don't want to use http2. 332 if (aNoHttp2 && aAlpnType == SupportedAlpnRank::HTTP_2) { 333 return false; 334 } 335 336 if (IsHttp3(aAlpnType)) { 337 if (aCheckHttp3ExcludedList && gHttpHandler->IsHttp3Excluded(aTargetName)) { 338 aExcludedCount++; 339 return false; 340 } 341 342 if (aNoHttp3) { 343 return false; 344 } 345 } 346 347 return true; 348 } 349 350 static nsTArray<SVCBWrapper> FlattenRecords(const nsACString& aHost, 351 const nsTArray<SVCB>& aRecords, 352 uint32_t& aH3RecordCount) { 353 nsTArray<SVCBWrapper> result; 354 aH3RecordCount = 0; 355 for (const auto& record : aRecords) { 356 bool hasNoDefaultAlpn = false; 357 nsTArray<std::tuple<nsCString, SupportedAlpnRank>> alpnList = 358 record.GetAllAlpn(hasNoDefaultAlpn); 359 if (alpnList.IsEmpty()) { 360 result.AppendElement(SVCBWrapper(record)); 361 } else { 362 bool h1AlpnAdded = false; 363 if (!hasNoDefaultAlpn) { 364 // Consider two scenarios when "no-default-alpn" is not found: 365 // 1. If echConfig is present in the record: 366 // - Firefox should always attempt to connect using echConfig without 367 // fallback. 368 // - Therefore, we add an additional record with an empty ALPN to 369 // allow Firefox to retry using HTTP/1.1 or h2 with echConfig. 370 // 371 // 2. If echConfig is not present in the record:: 372 // - We allow fallback to connections that do not use HTTPS RR. 373 // - In this case, adding another record with the same target name as 374 // the host name is unnecessary. 375 if (!aHost.Equals(record.mSvcDomainName) || record.mHasEchConfig) { 376 alpnList.AppendElement( 377 std::make_tuple(""_ns, SupportedAlpnRank::HTTP_1_1)); 378 h1AlpnAdded = true; 379 } 380 } 381 for (const auto& alpn : alpnList) { 382 const auto alpnRank = std::get<1>(alpn); 383 if (IsHttp3(alpnRank)) { 384 aH3RecordCount++; 385 } 386 387 // Skip explicit h2 if h1 is already present. 388 if (alpnRank == SupportedAlpnRank::HTTP_2 && h1AlpnAdded) { 389 continue; 390 } 391 392 // For h2, normalize the tuple to use an empty ALPN. If both h1 and h2 393 // are available, the ALPN negotiation will automatically select h2 when 394 // the server supports it, otherwise it will fall back to h1. 395 // However, if `no-default-alpn` is present, fallback to h1 is not 396 // possible, so we must explicitly set h2. 397 auto chosen = 398 (alpnRank == SupportedAlpnRank::HTTP_2 && !hasNoDefaultAlpn) 399 ? std::make_tuple(""_ns, alpnRank) 400 : alpn; 401 SVCBWrapper wrapper(record); 402 wrapper.mAlpn = Some(chosen); 403 result.AppendElement(std::move(wrapper)); 404 } 405 } 406 } 407 return result; 408 } 409 410 static void TelemetryForServiceModeRecord(const nsACString& aKey) { 411 #ifndef ANDROID 412 glean::networking::https_record_state.Get(aKey).Add(1); 413 #endif 414 } 415 416 already_AddRefed<nsISVCBRecord> 417 DNSHTTPSSVCRecordBase::GetServiceModeRecordInternal( 418 bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords, 419 bool& aRecordsAllExcluded, bool aCheckHttp3ExcludedList, 420 const nsACString& aCname) { 421 RefPtr<SVCBRecord> selectedRecord; 422 RefPtr<SVCBRecord> h3RecordWithEchConfig; 423 uint32_t recordHasNoDefaultAlpnCount = 0; 424 uint32_t recordExcludedCount = 0; 425 uint32_t recordHasUnmatchedCname = 0; 426 aRecordsAllExcluded = false; 427 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); 428 uint32_t h3ExcludedCount = 0; 429 uint32_t h3RecordCount = 0; 430 nsTArray<SVCBWrapper> records = 431 FlattenRecords(mHost, aRecords, h3RecordCount); 432 for (const auto& record : records) { 433 if (record.mRecord.mSvcFieldPriority == 0) { 434 // In ServiceMode, the SvcPriority should never be 0. 435 TelemetryForServiceModeRecord("invalid"_ns); 436 return nullptr; 437 } 438 439 if (record.mRecord.NoDefaultAlpn()) { 440 ++recordHasNoDefaultAlpnCount; 441 } 442 443 if (!CheckRecordIsUsable(record.mRecord, dns, mHost, recordExcludedCount)) { 444 // Skip if this record is not usable. 445 continue; 446 } 447 448 if (!CheckRecordIsUsableWithCname(record.mRecord, aCname)) { 449 recordHasUnmatchedCname++; 450 continue; 451 } 452 453 if (record.mAlpn) { 454 if (!CheckAlpnIsUsable(std::get<1>(*(record.mAlpn)), aNoHttp2, aNoHttp3, 455 aCheckHttp3ExcludedList, 456 record.mRecord.mSvcDomainName, h3ExcludedCount)) { 457 continue; 458 } 459 460 if (IsHttp3(std::get<1>(*(record.mAlpn)))) { 461 // If the selected alpn is h3 and ech for h3 is disabled, we want 462 // to find out if there is another non-h3 record that has 463 // echConfig. If yes, we'll use the non-h3 record with echConfig 464 // to connect. If not, we'll use h3 to connect without echConfig. 465 if (record.mRecord.mHasEchConfig && 466 (nsHttpHandler::EchConfigEnabled() && 467 !nsHttpHandler::EchConfigEnabled(true))) { 468 if (!h3RecordWithEchConfig) { 469 // Save this h3 record for later use. 470 h3RecordWithEchConfig = 471 new SVCBRecord(record.mRecord, record.mAlpn); 472 // Make sure the next record is not h3. 473 aNoHttp3 = true; 474 continue; 475 } 476 } 477 } 478 } 479 480 if (!selectedRecord) { 481 selectedRecord = new SVCBRecord(record.mRecord, record.mAlpn); 482 } 483 } 484 485 if (!selectedRecord && !h3RecordWithEchConfig) { 486 // If all records indicate "no-default-alpn", we should not use this RRSet. 487 if (recordHasNoDefaultAlpnCount == records.Length()) { 488 TelemetryForServiceModeRecord("no_default_alpn"_ns); 489 return nullptr; 490 } 491 492 if (recordExcludedCount == records.Length()) { 493 aRecordsAllExcluded = true; 494 TelemetryForServiceModeRecord("all_excluded"_ns); 495 return nullptr; 496 } 497 498 if (recordHasUnmatchedCname == records.Length()) { 499 TelemetryForServiceModeRecord("unmatched_cname"_ns); 500 return nullptr; 501 } 502 503 // If all records are in http3 excluded list, try again without checking the 504 // excluded list. This is better than returning nothing. 505 if (h3ExcludedCount && h3ExcludedCount == h3RecordCount && 506 aCheckHttp3ExcludedList) { 507 return GetServiceModeRecordInternal(aNoHttp2, aNoHttp3, aRecords, 508 aRecordsAllExcluded, false, aCname); 509 } 510 } 511 512 if (h3RecordWithEchConfig) { 513 TelemetryForServiceModeRecord("succeeded"_ns); 514 if (selectedRecord && selectedRecord->mData.mHasEchConfig) { 515 return selectedRecord.forget(); 516 } 517 518 return h3RecordWithEchConfig.forget(); 519 } 520 521 if (selectedRecord) { 522 TelemetryForServiceModeRecord("succeeded"_ns); 523 } else { 524 TelemetryForServiceModeRecord("others"_ns); 525 } 526 return selectedRecord.forget(); 527 } 528 529 void DNSHTTPSSVCRecordBase::GetAllRecordsInternal( 530 bool aNoHttp2, bool aNoHttp3, const nsACString& aCname, 531 const nsTArray<SVCB>& aRecords, bool aOnlyRecordsWithECH, 532 bool* aAllRecordsHaveEchConfig, bool* aAllRecordsInH3ExcludedList, 533 nsTArray<RefPtr<nsISVCBRecord>>& aResult, bool aCheckHttp3ExcludedList) { 534 if (aRecords.IsEmpty()) { 535 return; 536 } 537 538 *aAllRecordsHaveEchConfig = aRecords[0].mHasEchConfig; 539 *aAllRecordsInH3ExcludedList = false; 540 // The first record should have echConfig. 541 if (aOnlyRecordsWithECH && !(*aAllRecordsHaveEchConfig)) { 542 return; 543 } 544 545 uint32_t h3ExcludedCount = 0; 546 uint32_t h3RecordCount = 0; 547 nsTArray<SVCBWrapper> records = 548 FlattenRecords(mHost, aRecords, h3RecordCount); 549 for (const auto& record : records) { 550 if (record.mRecord.mSvcFieldPriority == 0) { 551 // This should not happen, since GetAllRecordsInternal() 552 // should be called only if GetServiceModeRecordInternal() returns a 553 // non-null record. 554 MOZ_ASSERT(false); 555 return; 556 } 557 558 // Records with echConfig are in front of records without echConfig, so we 559 // don't have to continue. 560 *aAllRecordsHaveEchConfig &= record.mRecord.mHasEchConfig; 561 if (aOnlyRecordsWithECH && !(*aAllRecordsHaveEchConfig)) { 562 aResult.Clear(); 563 return; 564 } 565 566 if (!CheckRecordIsUsableWithCname(record.mRecord, aCname)) { 567 continue; 568 } 569 570 Maybe<uint16_t> port = record.mRecord.GetPort(); 571 if (port && *port == 0) { 572 // Found an unsafe port, skip this record. 573 continue; 574 } 575 576 if (record.mAlpn) { 577 if (!CheckAlpnIsUsable(std::get<1>(*(record.mAlpn)), aNoHttp2, aNoHttp3, 578 aCheckHttp3ExcludedList, 579 record.mRecord.mSvcDomainName, h3ExcludedCount)) { 580 continue; 581 } 582 } 583 584 RefPtr<nsISVCBRecord> svcbRecord = 585 new SVCBRecord(record.mRecord, record.mAlpn); 586 aResult.AppendElement(svcbRecord); 587 } 588 589 // If all records are in http3 excluded list, try again without checking the 590 // excluded list. This is better than returning nothing. 591 if (h3ExcludedCount && h3ExcludedCount == h3RecordCount && 592 aCheckHttp3ExcludedList) { 593 GetAllRecordsInternal(aNoHttp2, aNoHttp3, aCname, aRecords, 594 aOnlyRecordsWithECH, aAllRecordsHaveEchConfig, 595 aAllRecordsInH3ExcludedList, aResult, false); 596 *aAllRecordsInH3ExcludedList = true; 597 } 598 } 599 600 bool DNSHTTPSSVCRecordBase::HasIPAddressesInternal( 601 const nsTArray<SVCB>& aRecords) { 602 for (const SVCB& record : aRecords) { 603 if (record.mSvcFieldPriority != 0) { 604 for (const auto& value : record.mSvcFieldValue) { 605 if (value.mValue.is<SvcParamIpv4Hint>()) { 606 return true; 607 } 608 if (value.mValue.is<SvcParamIpv6Hint>()) { 609 return true; 610 } 611 } 612 } 613 } 614 615 return false; 616 } 617 618 } // namespace net 619 } // namespace mozilla