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/. */
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"
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.
21 # include <ws2tcpip.h>
24 #endif // DNSQUERY_AVAILABLE
26 namespace mozilla::net
{
28 #define LOG(msg, ...) \
29 MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
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;
39 if (xpc::IsInAutomation() &&
40 !StaticPrefs::network_dns_native_https_query_in_automation()) {
41 return NS_ERROR_UNKNOWN_HOST
;
44 TimeStamp startTime
= TimeStamp::Now();
47 DnsQuery_A(host
.get(), nsIDNSService::RESOLVE_TYPE_HTTPSSVC
,
48 DNS_QUERY_STANDARD
, nullptr, &result
, nullptr);
50 mozilla::glean::networking::dns_native_https_call_time
.AccumulateRawDuration(
51 TimeStamp::Now() - startTime
);
53 if (status
!= ERROR_SUCCESS
) {
54 LOG("DnsQuery_A failed with error: %ld\n", status
);
55 return NS_ERROR_UNKNOWN_HOST
;
58 // This will free the record if we exit early from this function.
60 MakeScopeExit([&]() { DnsRecordListFree(result
, DnsFreeRecordList
); });
62 auto CheckRecords
= [&aResult
, &cname
, &aTTL
](
64 const nsCString
& aHost
) -> nsresult
{
65 PDNS_RECORD current
= result
;
67 for (current
= result
; current
; current
= current
->pNext
) {
68 if (strcmp(current
->pName
, aHost
.get()) != 0) {
71 if (current
->wType
== nsIDNSService::RESOLVE_TYPE_HTTPSSVC
) {
72 const unsigned char* ptr
= (const unsigned char*)&(current
->Data
);
74 nsresult rv
= DNSPacket::ParseHTTPS(current
->wDataLength
, parsed
, 0,
75 ptr
, current
->wDataLength
, aHost
);
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.
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
;
91 cname
= parsed
.mSvcDomainName
;
96 if (!aResult
.is
<TypeRecordHTTPSSVC
>()) {
97 aResult
= mozilla::AsVariant(CopyableTArray
<SVCB
>());
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
;
105 aTTL
= std::min
<uint32_t>(aTTL
, current
->dwTtl
);
112 int32_t loopCount
= 64;
113 while (loopCount
> 0 && aResult
.is
<Nothing
>()) {
116 nsresult rv
= CheckRecords(result
, host
);
121 if (aResult
.is
<Nothing
>() && !cname
.IsEmpty()) {
127 if (aResult
.is
<Nothing
>()) {
128 return NS_ERROR_UNKNOWN_HOST
;
133 if (loopCount
== 0) {
134 return NS_ERROR_UNKNOWN_HOST
;
137 if (aResult
.is
<Nothing
>()) {
138 // The call succeeded, but no HTTPS records were found.
139 return NS_ERROR_UNKNOWN_HOST
;
142 if (aTTL
== UINT32_MAX
) {
143 aTTL
= 60; // Defaults to 60 seconds
148 void DNSThreadShutdown() {}
150 } // namespace mozilla::net