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/GleanMetrics.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
, uint16_t aFlags
,
32 TypeRecordResultType
& aResult
, uint32_t& aTTL
) {
33 nsAutoCString
host(aHost
);
34 PDNS_RECORD result
= nullptr;
38 if (xpc::IsInAutomation() &&
39 !StaticPrefs::network_dns_native_https_query_in_automation()) {
40 return NS_ERROR_UNKNOWN_HOST
;
43 TimeStamp startTime
= TimeStamp::Now();
46 DnsQuery_A(host
.get(), nsIDNSService::RESOLVE_TYPE_HTTPSSVC
,
47 DNS_QUERY_STANDARD
, nullptr, &result
, nullptr);
49 mozilla::glean::networking::dns_native_https_call_time
.AccumulateRawDuration(
50 TimeStamp::Now() - startTime
);
52 if (status
!= ERROR_SUCCESS
) {
53 LOG("DnsQuery_A failed with error: %ld\n", status
);
54 return NS_ERROR_UNKNOWN_HOST
;
57 // This will free the record if we exit early from this function.
59 MakeScopeExit([&]() { DnsRecordListFree(result
, DnsFreeRecordList
); });
61 auto CheckRecords
= [&aResult
, &cname
, &aTTL
](
63 const nsCString
& aHost
) -> nsresult
{
64 PDNS_RECORD current
= result
;
66 for (current
= result
; current
; current
= current
->pNext
) {
67 if (strcmp(current
->pName
, aHost
.get()) != 0) {
70 if (current
->wType
== nsIDNSService::RESOLVE_TYPE_HTTPSSVC
) {
71 const unsigned char* ptr
= (const unsigned char*)&(current
->Data
);
73 nsresult rv
= DNSPacket::ParseHTTPS(current
->wDataLength
, parsed
, 0,
74 ptr
, current
->wDataLength
, aHost
);
79 if (parsed
.mSvcDomainName
.IsEmpty() && parsed
.mSvcFieldPriority
== 0) {
80 // For AliasMode SVCB RRs, a TargetName of "." indicates that the
81 // service is not available or does not exist.
85 if (parsed
.mSvcFieldPriority
== 0) {
86 // Alias form SvcDomainName must not have the "." value (empty)
87 if (parsed
.mSvcDomainName
.IsEmpty()) {
88 return NS_ERROR_UNEXPECTED
;
90 cname
= parsed
.mSvcDomainName
;
95 if (!aResult
.is
<TypeRecordHTTPSSVC
>()) {
96 aResult
= mozilla::AsVariant(CopyableTArray
<SVCB
>());
98 auto& results
= aResult
.as
<TypeRecordHTTPSSVC
>();
99 results
.AppendElement(parsed
);
100 aTTL
= std::min
<uint32_t>(aTTL
, current
->dwTtl
);
101 } else if (current
->wType
== DNS_TYPE_CNAME
) {
102 cname
= current
->Data
.Cname
.pNameHost
;
104 aTTL
= std::min
<uint32_t>(aTTL
, current
->dwTtl
);
111 int32_t loopCount
= 64;
112 while (loopCount
> 0 && aResult
.is
<Nothing
>()) {
115 nsresult rv
= CheckRecords(result
, host
);
120 if (aResult
.is
<Nothing
>() && !cname
.IsEmpty()) {
126 if (aResult
.is
<Nothing
>()) {
127 return NS_ERROR_UNKNOWN_HOST
;
132 if (loopCount
== 0) {
133 return NS_ERROR_UNKNOWN_HOST
;
136 if (aResult
.is
<Nothing
>()) {
137 // The call succeeded, but no HTTPS records were found.
138 return NS_ERROR_UNKNOWN_HOST
;
141 if (aTTL
== UINT32_MAX
) {
142 aTTL
= 60; // Defaults to 60 seconds
147 void DNSThreadShutdown() {}
149 } // namespace mozilla::net