Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=4 sw=2 sts=2 et cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GetAddrInfo.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/net/DNSPacket.h"
#include "nsIDNSService.h"
#include "mozilla/Maybe.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_network.h"
#ifdef DNSQUERY_AVAILABLE
// There is a bug in windns.h where the type of parameter ppQueryResultsSet for
// DnsQuery_A is dependent on UNICODE being set. It should *always* be
// PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this
// we make sure that UNICODE is unset.
# undef UNICODE
# include <ws2tcpip.h>
# undef GetAddrInfo
# include <windns.h>
#endif // DNSQUERY_AVAILABLE
namespace mozilla::net {
#define LOG(msg, ...) \
MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
nsresult ResolveHTTPSRecordImpl(const nsACString& aHost,
nsIDNSService::DNSFlags aFlags,
TypeRecordResultType& aResult, uint32_t& aTTL) {
nsAutoCString host(aHost);
PDNS_RECORD result = nullptr;
nsAutoCString cname;
aTTL = UINT32_MAX;
if (xpc::IsInAutomation() &&
!StaticPrefs::network_dns_native_https_query_in_automation()) {
return NS_ERROR_UNKNOWN_HOST;
}
TimeStamp startTime = TimeStamp::Now();
DNS_STATUS status =
DnsQuery_A(host.get(), nsIDNSService::RESOLVE_TYPE_HTTPSSVC,
DNS_QUERY_STANDARD, nullptr, &result, nullptr);
mozilla::glean::networking::dns_native_https_call_time.AccumulateRawDuration(
TimeStamp::Now() - startTime);
if (status != ERROR_SUCCESS) {
LOG("DnsQuery_A failed with error: %ld\n", status);
return NS_ERROR_UNKNOWN_HOST;
}
// This will free the record if we exit early from this function.
auto freeDnsRecord =
MakeScopeExit([&]() { DnsRecordListFree(result, DnsFreeRecordList); });
auto CheckRecords = [&aResult, &cname, &aTTL](
PDNS_RECORD result,
const nsCString& aHost) -> nsresult {
PDNS_RECORD current = result;
for (current = result; current; current = current->pNext) {
if (strcmp(current->pName, aHost.get()) != 0) {
continue;
}
if (current->wType == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) {
const unsigned char* ptr = (const unsigned char*)&(current->Data);
struct SVCB parsed;
nsresult rv = DNSPacket::ParseHTTPS(current->wDataLength, parsed, 0,
ptr, current->wDataLength, aHost);
if (NS_FAILED(rv)) {
return rv;
}
if (parsed.mSvcDomainName.IsEmpty() && parsed.mSvcFieldPriority == 0) {
// For AliasMode SVCB RRs, a TargetName of "." indicates that the
// service is not available or does not exist.
continue;
}
if (parsed.mSvcFieldPriority == 0) {
// Alias form SvcDomainName must not have the "." value (empty)
if (parsed.mSvcDomainName.IsEmpty()) {
return NS_ERROR_UNEXPECTED;
}
cname = parsed.mSvcDomainName;
ToLowerCase(cname);
break;
}
if (!aResult.is<TypeRecordHTTPSSVC>()) {
aResult = mozilla::AsVariant(CopyableTArray<SVCB>());
}
auto& results = aResult.as<TypeRecordHTTPSSVC>();
results.AppendElement(parsed);
aTTL = std::min<uint32_t>(aTTL, current->dwTtl);
} else if (current->wType == DNS_TYPE_CNAME) {
cname = current->Data.Cname.pNameHost;
ToLowerCase(cname);
aTTL = std::min<uint32_t>(aTTL, current->dwTtl);
break;
}
}
return NS_OK;
};
int32_t loopCount = 64;
while (loopCount > 0 && aResult.is<Nothing>()) {
loopCount--;
nsresult rv = CheckRecords(result, host);
if (NS_FAILED(rv)) {
return rv;
}
if (aResult.is<Nothing>() && !cname.IsEmpty()) {
host = cname;
cname.Truncate();
continue;
}
if (aResult.is<Nothing>()) {
return NS_ERROR_UNKNOWN_HOST;
}
}
// CNAME loop
if (loopCount == 0) {
return NS_ERROR_UNKNOWN_HOST;
}
if (aResult.is<Nothing>()) {
// The call succeeded, but no HTTPS records were found.
return NS_ERROR_UNKNOWN_HOST;
}
if (aTTL == UINT32_MAX) {
aTTL = 60; // Defaults to 60 seconds
}
return NS_OK;
}
void DNSThreadShutdown() {}
} // namespace mozilla::net