Backed out 10 changesets (bug 1803810) for xpcshell failures on test_import_global...
[gecko.git] / netwerk / dns / GetAddrInfo.cpp
blob0dd1f0099a04817cf25bc236f0e53336a4fa2483
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
7 #include "GetAddrInfo.h"
9 #ifdef DNSQUERY_AVAILABLE
10 // There is a bug in windns.h where the type of parameter ppQueryResultsSet for
11 // DnsQuery_A is dependent on UNICODE being set. It should *always* be
12 // PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this
13 // we make sure that UNICODE is unset.
14 # undef UNICODE
15 # include <ws2tcpip.h>
16 # undef GetAddrInfo
17 # include <windns.h>
18 #endif // DNSQUERY_AVAILABLE
20 #include "mozilla/ClearOnShutdown.h"
21 #include "mozilla/net/DNS.h"
22 #include "NativeDNSResolverOverrideParent.h"
23 #include "prnetdb.h"
24 #include "nsIOService.h"
25 #include "nsHostResolver.h"
26 #include "nsError.h"
27 #include "mozilla/net/DNS.h"
28 #include <algorithm>
29 #include "prerror.h"
31 #include "mozilla/Logging.h"
32 #include "mozilla/StaticPrefs_network.h"
33 #include "mozilla/net/DNSPacket.h"
34 #include "nsIDNSService.h"
36 namespace mozilla::net {
38 static StaticRefPtr<NativeDNSResolverOverride> gOverrideService;
40 LazyLogModule gGetAddrInfoLog("GetAddrInfo");
41 #define LOG(msg, ...) \
42 MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
43 #define LOG_WARNING(msg, ...) \
44 MOZ_LOG(gGetAddrInfoLog, LogLevel::Warning, ("[DNS]: " msg, ##__VA_ARGS__))
46 #ifdef DNSQUERY_AVAILABLE
48 # define COMPUTER_NAME_BUFFER_SIZE 100
49 static char sDNSComputerName[COMPUTER_NAME_BUFFER_SIZE];
50 static char sNETBIOSComputerName[MAX_COMPUTERNAME_LENGTH + 1];
52 ////////////////////////////
53 // WINDOWS IMPLEMENTATION //
54 ////////////////////////////
56 // Ensure consistency of PR_* and AF_* constants to allow for legacy usage of
57 // PR_* constants with this API.
58 static_assert(PR_AF_INET == AF_INET && PR_AF_INET6 == AF_INET6 &&
59 PR_AF_UNSPEC == AF_UNSPEC,
60 "PR_AF_* must match AF_*");
62 // If successful, returns in aResult a TTL value that is smaller or
63 // equal with the one already there. Gets the TTL value by calling
64 // to DnsQuery_A and iterating through the returned
65 // records to find the one with the smallest TTL value.
66 static MOZ_ALWAYS_INLINE nsresult _CallDnsQuery_A_Windows(
67 const nsACString& aHost, uint16_t aAddressFamily, DWORD aFlags,
68 std::function<void(PDNS_RECORDA)> aCallback) {
69 NS_ConvertASCIItoUTF16 name(aHost);
71 auto callDnsQuery_A = [&](uint16_t reqFamily) {
72 PDNS_RECORDA dnsData = nullptr;
73 DNS_STATUS status = DnsQuery_A(aHost.BeginReading(), reqFamily, aFlags,
74 nullptr, &dnsData, nullptr);
75 if (status == DNS_INFO_NO_RECORDS || status == DNS_ERROR_RCODE_NAME_ERROR ||
76 !dnsData) {
77 LOG("No DNS records found for %s. status=%lX. reqFamily = %X\n",
78 aHost.BeginReading(), status, reqFamily);
79 return NS_ERROR_FAILURE;
80 } else if (status != NOERROR) {
81 LOG_WARNING("DnsQuery_A failed with status %lX.\n", status);
82 return NS_ERROR_UNEXPECTED;
85 for (PDNS_RECORDA curRecord = dnsData; curRecord;
86 curRecord = curRecord->pNext) {
87 // Only records in the answer section are important
88 if (curRecord->Flags.S.Section != DnsSectionAnswer) {
89 continue;
91 if (curRecord->wType != reqFamily) {
92 continue;
95 aCallback(curRecord);
98 DnsFree(dnsData, DNS_FREE_TYPE::DnsFreeRecordList);
99 return NS_OK;
102 if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET) {
103 callDnsQuery_A(DNS_TYPE_A);
106 if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET6) {
107 callDnsQuery_A(DNS_TYPE_AAAA);
109 return NS_OK;
112 bool recordTypeMatchesRequest(uint16_t wType, uint16_t aAddressFamily) {
113 if (aAddressFamily == PR_AF_UNSPEC) {
114 return wType == DNS_TYPE_A || wType == DNS_TYPE_AAAA;
116 if (aAddressFamily == PR_AF_INET) {
117 return wType == DNS_TYPE_A;
119 if (aAddressFamily == PR_AF_INET6) {
120 return wType == DNS_TYPE_AAAA;
122 return false;
125 static MOZ_ALWAYS_INLINE nsresult _GetTTLData_Windows(const nsACString& aHost,
126 uint32_t* aResult,
127 uint16_t aAddressFamily) {
128 MOZ_ASSERT(!aHost.IsEmpty());
129 MOZ_ASSERT(aResult);
130 if (aAddressFamily != PR_AF_UNSPEC && aAddressFamily != PR_AF_INET &&
131 aAddressFamily != PR_AF_INET6) {
132 return NS_ERROR_UNEXPECTED;
135 // In order to avoid using ANY records which are not always implemented as a
136 // "Gimme what you have" request in hostname resolvers, we should send A
137 // and/or AAAA requests, based on the address family requested.
138 const DWORD ttlFlags =
139 (DNS_QUERY_STANDARD | DNS_QUERY_NO_NETBT | DNS_QUERY_NO_HOSTS_FILE |
140 DNS_QUERY_NO_MULTICAST | DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE |
141 DNS_QUERY_DONT_RESET_TTL_VALUES);
142 unsigned int ttl = (unsigned int)-1;
143 _CallDnsQuery_A_Windows(
144 aHost, aAddressFamily, ttlFlags,
145 [&ttl, &aHost, aAddressFamily](PDNS_RECORDA curRecord) {
146 if (recordTypeMatchesRequest(curRecord->wType, aAddressFamily)) {
147 ttl = std::min<unsigned int>(ttl, curRecord->dwTtl);
148 } else {
149 LOG("Received unexpected record type %u in response for %s.\n",
150 curRecord->wType, aHost.BeginReading());
154 if (ttl == (unsigned int)-1) {
155 LOG("No useable TTL found.");
156 return NS_ERROR_FAILURE;
159 *aResult = ttl;
160 return NS_OK;
163 static MOZ_ALWAYS_INLINE nsresult
164 _DNSQuery_A_SingleLabel(const nsACString& aCanonHost, uint16_t aAddressFamily,
165 uint16_t aFlags, AddrInfo** aAddrInfo) {
166 bool setCanonName = aFlags & nsHostResolver::RES_CANON_NAME;
167 nsAutoCString canonName;
168 const DWORD flags = (DNS_QUERY_STANDARD | DNS_QUERY_NO_MULTICAST |
169 DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE);
170 nsTArray<NetAddr> addresses;
172 _CallDnsQuery_A_Windows(
173 aCanonHost, aAddressFamily, flags, [&](PDNS_RECORDA curRecord) {
174 MOZ_DIAGNOSTIC_ASSERT(curRecord->wType == DNS_TYPE_A ||
175 curRecord->wType == DNS_TYPE_AAAA);
176 if (setCanonName) {
177 canonName.Assign(curRecord->pName);
179 NetAddr addr{};
180 addr.inet.family = AF_INET;
181 addr.inet.ip = curRecord->Data.A.IpAddress;
182 addresses.AppendElement(addr);
185 LOG("Query for: %s has %zu results", aCanonHost.BeginReading(),
186 addresses.Length());
187 if (addresses.IsEmpty()) {
188 return NS_ERROR_UNKNOWN_HOST;
190 RefPtr<AddrInfo> ai(new AddrInfo(
191 aCanonHost, canonName, DNSResolverType::Native, 0, std::move(addresses)));
192 ai.forget(aAddrInfo);
194 return NS_OK;
197 #endif
199 ////////////////////////////////////
200 // PORTABLE RUNTIME IMPLEMENTATION//
201 ////////////////////////////////////
203 static MOZ_ALWAYS_INLINE nsresult
204 _GetAddrInfo_Portable(const nsACString& aCanonHost, uint16_t aAddressFamily,
205 uint16_t aFlags, AddrInfo** aAddrInfo) {
206 MOZ_ASSERT(!aCanonHost.IsEmpty());
207 MOZ_ASSERT(aAddrInfo);
209 // We accept the same aFlags that nsHostResolver::ResolveHost accepts, but we
210 // need to translate the aFlags into a form that PR_GetAddrInfoByName
211 // accepts.
212 int prFlags = PR_AI_ADDRCONFIG;
213 if (!(aFlags & nsHostResolver::RES_CANON_NAME)) {
214 prFlags |= PR_AI_NOCANONNAME;
217 // We need to remove IPv4 records manually because PR_GetAddrInfoByName
218 // doesn't support PR_AF_INET6.
219 bool disableIPv4 = aAddressFamily == PR_AF_INET6;
220 if (disableIPv4) {
221 aAddressFamily = PR_AF_UNSPEC;
224 #if defined(DNSQUERY_AVAILABLE)
225 if (StaticPrefs::network_dns_dns_query_single_label() &&
226 !aCanonHost.Contains('.') && aCanonHost != "localhost"_ns) {
227 // For some reason we can't use DnsQuery_A to get the computer's IP.
228 if (!aCanonHost.Equals(nsDependentCString(sDNSComputerName),
229 nsCaseInsensitiveCStringComparator) &&
230 !aCanonHost.Equals(nsDependentCString(sNETBIOSComputerName),
231 nsCaseInsensitiveCStringComparator)) {
232 // This is a single label name resolve without a dot.
233 // We use DNSQuery_A for these.
234 LOG("Resolving %s using DnsQuery_A (computername: %s)\n",
235 aCanonHost.BeginReading(), sDNSComputerName);
236 return _DNSQuery_A_SingleLabel(aCanonHost, aAddressFamily, aFlags,
237 aAddrInfo);
240 #endif
242 LOG("Resolving %s using PR_GetAddrInfoByName", aCanonHost.BeginReading());
243 PRAddrInfo* prai =
244 PR_GetAddrInfoByName(aCanonHost.BeginReading(), aAddressFamily, prFlags);
246 if (!prai) {
247 LOG("PR_GetAddrInfoByName returned null PR_GetError:%d PR_GetOSErrpr:%d",
248 PR_GetError(), PR_GetOSError());
249 return NS_ERROR_UNKNOWN_HOST;
252 nsAutoCString canonName;
253 if (aFlags & nsHostResolver::RES_CANON_NAME) {
254 canonName.Assign(PR_GetCanonNameFromAddrInfo(prai));
257 bool filterNameCollision =
258 !(aFlags & nsHostResolver::RES_ALLOW_NAME_COLLISION);
259 RefPtr<AddrInfo> ai(new AddrInfo(aCanonHost, prai, disableIPv4,
260 filterNameCollision, canonName));
261 PR_FreeAddrInfo(prai);
262 if (ai->Addresses().IsEmpty()) {
263 LOG("PR_GetAddrInfoByName returned empty address list");
264 return NS_ERROR_UNKNOWN_HOST;
267 ai.forget(aAddrInfo);
269 LOG("PR_GetAddrInfoByName resolved successfully");
270 return NS_OK;
273 //////////////////////////////////////
274 // COMMON/PLATFORM INDEPENDENT CODE //
275 //////////////////////////////////////
276 nsresult GetAddrInfoInit() {
277 LOG("Initializing GetAddrInfo.\n");
279 #ifdef DNSQUERY_AVAILABLE
280 DWORD namesize = COMPUTER_NAME_BUFFER_SIZE;
281 if (!GetComputerNameExA(ComputerNameDnsHostname, sDNSComputerName,
282 &namesize)) {
283 sDNSComputerName[0] = 0;
285 namesize = MAX_COMPUTERNAME_LENGTH + 1;
286 if (!GetComputerNameExA(ComputerNameNetBIOS, sNETBIOSComputerName,
287 &namesize)) {
288 sNETBIOSComputerName[0] = 0;
290 #endif
291 return NS_OK;
294 nsresult GetAddrInfoShutdown() {
295 LOG("Shutting down GetAddrInfo.\n");
296 return NS_OK;
299 bool FindAddrOverride(const nsACString& aHost, uint16_t aAddressFamily,
300 uint16_t aFlags, AddrInfo** aAddrInfo) {
301 RefPtr<NativeDNSResolverOverride> overrideService = gOverrideService;
302 if (!overrideService) {
303 return false;
305 AutoReadLock lock(overrideService->mLock);
306 auto overrides = overrideService->mOverrides.Lookup(aHost);
307 if (!overrides) {
308 return false;
310 nsCString* cname = nullptr;
311 if (aFlags & nsHostResolver::RES_CANON_NAME) {
312 cname = overrideService->mCnames.Lookup(aHost).DataPtrOrNull();
315 RefPtr<AddrInfo> ai;
317 nsTArray<NetAddr> addresses;
318 for (const auto& ip : *overrides) {
319 if (aAddressFamily != AF_UNSPEC && ip.raw.family != aAddressFamily) {
320 continue;
322 addresses.AppendElement(ip);
325 if (!cname) {
326 ai = new AddrInfo(aHost, DNSResolverType::Native, 0, std::move(addresses));
327 } else {
328 ai = new AddrInfo(aHost, *cname, DNSResolverType::Native, 0,
329 std::move(addresses));
332 ai.forget(aAddrInfo);
333 return true;
336 nsresult GetAddrInfo(const nsACString& aHost, uint16_t aAddressFamily,
337 uint16_t aFlags, AddrInfo** aAddrInfo, bool aGetTtl) {
338 if (NS_WARN_IF(aHost.IsEmpty()) || NS_WARN_IF(!aAddrInfo)) {
339 return NS_ERROR_NULL_POINTER;
341 *aAddrInfo = nullptr;
343 if (StaticPrefs::network_dns_disabled()) {
344 return NS_ERROR_UNKNOWN_HOST;
347 #ifdef DNSQUERY_AVAILABLE
348 // The GetTTLData needs the canonical name to function properly
349 if (aGetTtl) {
350 aFlags |= nsHostResolver::RES_CANON_NAME;
352 #endif
354 // If there is an override for this host, then we synthetize a result.
355 if (gOverrideService &&
356 FindAddrOverride(aHost, aAddressFamily, aFlags, aAddrInfo)) {
357 LOG("Returning IP address from NativeDNSResolverOverride");
358 return (*aAddrInfo)->Addresses().Length() ? NS_OK : NS_ERROR_UNKNOWN_HOST;
361 nsAutoCString host;
362 if (StaticPrefs::network_dns_copy_string_before_call()) {
363 host = Substring(aHost.BeginReading(), aHost.Length());
364 MOZ_ASSERT(aHost.BeginReading() != host.BeginReading());
365 } else {
366 host = aHost;
369 if (gNativeIsLocalhost) {
370 // pretend we use the given host but use IPv4 localhost instead!
371 host = "localhost"_ns;
372 aAddressFamily = PR_AF_INET;
375 RefPtr<AddrInfo> info;
376 nsresult rv =
377 _GetAddrInfo_Portable(host, aAddressFamily, aFlags, getter_AddRefs(info));
379 #ifdef DNSQUERY_AVAILABLE
380 if (aGetTtl && NS_SUCCEEDED(rv)) {
381 // Figure out the canonical name, or if that fails, just use the host name
382 // we have.
383 nsAutoCString name;
384 if (info && !info->CanonicalHostname().IsEmpty()) {
385 name = info->CanonicalHostname();
386 } else {
387 name = host;
390 LOG("Getting TTL for %s (cname = %s).", host.get(), name.get());
391 uint32_t ttl = 0;
392 nsresult ttlRv = _GetTTLData_Windows(name, &ttl, aAddressFamily);
393 if (NS_SUCCEEDED(ttlRv)) {
394 auto builder = info->Build();
395 builder.SetTTL(ttl);
396 info = builder.Finish();
397 LOG("Got TTL %u for %s (name = %s).", ttl, host.get(), name.get());
398 } else {
399 LOG_WARNING("Could not get TTL for %s (cname = %s).", host.get(),
400 name.get());
403 #endif
405 info.forget(aAddrInfo);
406 return rv;
409 bool FindHTTPSRecordOverride(const nsACString& aHost,
410 TypeRecordResultType& aResult) {
411 LOG("FindHTTPSRecordOverride aHost=%s", nsCString(aHost).get());
412 RefPtr<NativeDNSResolverOverride> overrideService = gOverrideService;
413 if (!overrideService) {
414 return false;
417 AutoReadLock lock(overrideService->mLock);
418 auto overrides = overrideService->mHTTPSRecordOverrides.Lookup(aHost);
419 if (!overrides) {
420 return false;
423 DNSPacket packet;
424 nsAutoCString host(aHost);
425 nsAutoCString cname;
427 LOG("resolving %s\n", host.get());
428 // Perform the query
429 nsresult rv = packet.FillBuffer(
430 [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int {
431 if (overrides->Length() > DNSPacket::MAX_SIZE) {
432 return -1;
434 memcpy(response, overrides->Elements(), overrides->Length());
435 return overrides->Length();
437 if (NS_FAILED(rv)) {
438 return false;
441 uint32_t ttl = 0;
442 rv = ParseHTTPSRecord(host, packet, aResult, ttl);
444 return NS_SUCCEEDED(rv);
447 nsresult ParseHTTPSRecord(nsCString& aHost, DNSPacket& aDNSPacket,
448 TypeRecordResultType& aResult, uint32_t& aTTL) {
449 nsAutoCString cname;
450 nsresult rv;
452 aDNSPacket.SetNativePacket(true);
454 int32_t loopCount = 64;
455 while (loopCount > 0 && aResult.is<Nothing>()) {
456 loopCount--;
457 DOHresp resp;
458 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
459 rv = aDNSPacket.Decode(aHost, TRRTYPE_HTTPSSVC, cname, true, resp, aResult,
460 additionalRecords, aTTL);
461 if (NS_FAILED(rv)) {
462 LOG("Decode failed %x", static_cast<uint32_t>(rv));
463 return rv;
465 if (!cname.IsEmpty() && aResult.is<Nothing>()) {
466 aHost = cname;
467 cname.Truncate();
468 continue;
472 if (aResult.is<Nothing>()) {
473 LOG("Result is nothing");
474 // The call succeeded, but no HTTPS records were found.
475 return NS_ERROR_UNKNOWN_HOST;
478 return NS_OK;
481 nsresult ResolveHTTPSRecord(const nsACString& aHost, uint16_t aFlags,
482 TypeRecordResultType& aResult, uint32_t& aTTL) {
483 if (gOverrideService) {
484 return FindHTTPSRecordOverride(aHost, aResult) ? NS_OK
485 : NS_ERROR_UNKNOWN_HOST;
488 return ResolveHTTPSRecordImpl(aHost, aFlags, aResult, aTTL);
491 // static
492 already_AddRefed<nsINativeDNSResolverOverride>
493 NativeDNSResolverOverride::GetSingleton() {
494 if (nsIOService::UseSocketProcess() && XRE_IsParentProcess()) {
495 return NativeDNSResolverOverrideParent::GetSingleton();
498 if (gOverrideService) {
499 return do_AddRef(gOverrideService);
502 gOverrideService = new NativeDNSResolverOverride();
503 ClearOnShutdown(&gOverrideService);
504 return do_AddRef(gOverrideService);
507 NS_IMPL_ISUPPORTS(NativeDNSResolverOverride, nsINativeDNSResolverOverride)
509 NS_IMETHODIMP NativeDNSResolverOverride::AddIPOverride(
510 const nsACString& aHost, const nsACString& aIPLiteral) {
511 NetAddr tempAddr;
513 if (aIPLiteral.Equals("N/A"_ns)) {
514 AutoWriteLock lock(mLock);
515 auto& overrides = mOverrides.LookupOrInsert(aHost);
516 overrides.Clear();
517 return NS_OK;
520 if (NS_FAILED(tempAddr.InitFromString(aIPLiteral))) {
521 return NS_ERROR_UNEXPECTED;
524 AutoWriteLock lock(mLock);
525 auto& overrides = mOverrides.LookupOrInsert(aHost);
526 overrides.AppendElement(tempAddr);
528 return NS_OK;
531 NS_IMETHODIMP NativeDNSResolverOverride::AddHTTPSRecordOverride(
532 const nsACString& aHost, const uint8_t* aData, uint32_t aLength) {
533 AutoWriteLock lock(mLock);
534 nsTArray<uint8_t> data(aData, aLength);
535 mHTTPSRecordOverrides.InsertOrUpdate(aHost, std::move(data));
537 return NS_OK;
540 NS_IMETHODIMP NativeDNSResolverOverride::SetCnameOverride(
541 const nsACString& aHost, const nsACString& aCNAME) {
542 if (aCNAME.IsEmpty()) {
543 return NS_ERROR_UNEXPECTED;
546 AutoWriteLock lock(mLock);
547 mCnames.InsertOrUpdate(aHost, nsCString(aCNAME));
549 return NS_OK;
552 NS_IMETHODIMP NativeDNSResolverOverride::ClearHostOverride(
553 const nsACString& aHost) {
554 AutoWriteLock lock(mLock);
555 mCnames.Remove(aHost);
556 auto overrides = mOverrides.Extract(aHost);
557 if (!overrides) {
558 return NS_OK;
561 overrides->Clear();
562 return NS_OK;
565 NS_IMETHODIMP NativeDNSResolverOverride::ClearOverrides() {
566 AutoWriteLock lock(mLock);
567 mOverrides.Clear();
568 mCnames.Clear();
569 return NS_OK;
572 #ifdef MOZ_NO_HTTPS_IMPL
574 // If there is no platform specific implementation of ResolveHTTPSRecordImpl
575 // we link a dummy implementation here.
576 // Otherwise this is implemented in GetAddrInfoWin/Linux/etc
577 nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags,
578 TypeRecordResultType& aResult, uint32_t& aTTL) {
579 return NS_ERROR_NOT_IMPLEMENTED;
582 #endif // MOZ_NO_HTTPS_IMPL
584 } // namespace mozilla::net