PlatformDNSWin.cpp (4705B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=4 sw=2 sts=2 et cin: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "GetAddrInfo.h" 8 #include "mozilla/glean/NetwerkMetrics.h" 9 #include "mozilla/net/DNSPacket.h" 10 #include "nsIDNSService.h" 11 #include "mozilla/Maybe.h" 12 #include "mozilla/ScopeExit.h" 13 #include "mozilla/StaticPrefs_network.h" 14 15 #ifdef DNSQUERY_AVAILABLE 16 // There is a bug in windns.h where the type of parameter ppQueryResultsSet for 17 // DnsQuery_A is dependent on UNICODE being set. It should *always* be 18 // PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this 19 // we make sure that UNICODE is unset. 20 # undef UNICODE 21 # include <ws2tcpip.h> 22 # undef GetAddrInfo 23 # include <windns.h> 24 #endif // DNSQUERY_AVAILABLE 25 26 namespace mozilla::net { 27 28 #define LOG(msg, ...) \ 29 MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__)) 30 31 nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, 32 nsIDNSService::DNSFlags aFlags, 33 TypeRecordResultType& aResult, uint32_t& aTTL) { 34 nsAutoCString host(aHost); 35 PDNS_RECORD result = nullptr; 36 nsAutoCString cname; 37 aTTL = UINT32_MAX; 38 39 if (xpc::IsInAutomation() && 40 !StaticPrefs::network_dns_native_https_query_in_automation()) { 41 return NS_ERROR_UNKNOWN_HOST; 42 } 43 44 TimeStamp startTime = TimeStamp::Now(); 45 46 DNS_STATUS status = 47 DnsQuery_A(host.get(), nsIDNSService::RESOLVE_TYPE_HTTPSSVC, 48 DNS_QUERY_STANDARD, nullptr, &result, nullptr); 49 50 mozilla::glean::networking::dns_native_https_call_time.AccumulateRawDuration( 51 TimeStamp::Now() - startTime); 52 53 if (status != ERROR_SUCCESS) { 54 LOG("DnsQuery_A failed with error: %ld\n", status); 55 return NS_ERROR_UNKNOWN_HOST; 56 } 57 58 // This will free the record if we exit early from this function. 59 auto freeDnsRecord = 60 MakeScopeExit([&]() { DnsRecordListFree(result, DnsFreeRecordList); }); 61 62 auto CheckRecords = [&aResult, &cname, &aTTL]( 63 PDNS_RECORD result, 64 const nsCString& aHost) -> nsresult { 65 PDNS_RECORD current = result; 66 67 for (current = result; current; current = current->pNext) { 68 if (strcmp(current->pName, aHost.get()) != 0) { 69 continue; 70 } 71 if (current->wType == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) { 72 const unsigned char* ptr = (const unsigned char*)&(current->Data); 73 struct SVCB parsed; 74 nsresult rv = DNSPacket::ParseHTTPS(current->wDataLength, parsed, 0, 75 ptr, current->wDataLength, aHost); 76 if (NS_FAILED(rv)) { 77 return rv; 78 } 79 80 if (parsed.mSvcDomainName.IsEmpty() && parsed.mSvcFieldPriority == 0) { 81 // For AliasMode SVCB RRs, a TargetName of "." indicates that the 82 // service is not available or does not exist. 83 continue; 84 } 85 86 if (parsed.mSvcFieldPriority == 0) { 87 // Alias form SvcDomainName must not have the "." value (empty) 88 if (parsed.mSvcDomainName.IsEmpty()) { 89 return NS_ERROR_UNEXPECTED; 90 } 91 cname = parsed.mSvcDomainName; 92 ToLowerCase(cname); 93 break; 94 } 95 96 if (!aResult.is<TypeRecordHTTPSSVC>()) { 97 aResult = mozilla::AsVariant(CopyableTArray<SVCB>()); 98 } 99 auto& results = aResult.as<TypeRecordHTTPSSVC>(); 100 results.AppendElement(parsed); 101 aTTL = std::min<uint32_t>(aTTL, current->dwTtl); 102 } else if (current->wType == DNS_TYPE_CNAME) { 103 cname = current->Data.Cname.pNameHost; 104 ToLowerCase(cname); 105 aTTL = std::min<uint32_t>(aTTL, current->dwTtl); 106 break; 107 } 108 } 109 return NS_OK; 110 }; 111 112 int32_t loopCount = 64; 113 while (loopCount > 0 && aResult.is<Nothing>()) { 114 loopCount--; 115 116 nsresult rv = CheckRecords(result, host); 117 if (NS_FAILED(rv)) { 118 return rv; 119 } 120 121 if (aResult.is<Nothing>() && !cname.IsEmpty()) { 122 host = cname; 123 cname.Truncate(); 124 continue; 125 } 126 127 if (aResult.is<Nothing>()) { 128 return NS_ERROR_UNKNOWN_HOST; 129 } 130 } 131 132 // CNAME loop 133 if (loopCount == 0) { 134 return NS_ERROR_UNKNOWN_HOST; 135 } 136 137 if (aResult.is<Nothing>()) { 138 // The call succeeded, but no HTTPS records were found. 139 return NS_ERROR_UNKNOWN_HOST; 140 } 141 142 if (aTTL == UINT32_MAX) { 143 aTTL = 60; // Defaults to 60 seconds 144 } 145 return NS_OK; 146 } 147 148 void DNSThreadShutdown() {} 149 150 } // namespace mozilla::net