1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
/* -*- 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/NetwerkMetrics.h"
#include "mozilla/net/DNSPacket.h"
#include "nsIDNSService.h"
#include "mozilla/StaticPrefs_network.h"
#include <dns_sd.h>
#include <unistd.h>
#include <arpa/inet.h>
namespace mozilla::net {
#define LOG(msg, ...) \
MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
struct DNSContext {
nsresult mRv = NS_OK;
TypeRecordResultType* mResult;
nsCString mHost;
uint32_t* mTTL;
};
// Callback for DNSServiceQueryRecord
void QueryCallback(DNSServiceRef aSDRef, DNSServiceFlags aFlags,
uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
const char* aFullname, uint16_t aRRType, uint16_t aRRClass,
uint16_t aRDLen, const void* aRdata, uint32_t aTtl,
void* aContext) {
struct DNSContext* context = (struct DNSContext*)aContext;
LOG("DNS response name: %s type: %u rdlen %u class %u ttl %u", aFullname,
aRRType, aRDLen, aRRClass, aTtl);
if (aErrorCode != kDNSServiceErr_NoError) {
LOG("Error resolving record: %d\n", aErrorCode);
context->mRv = NS_ERROR_UNKNOWN_HOST;
return;
}
if (NS_FAILED(context->mRv)) {
LOG("Parsing already failed for a previous record");
return;
}
// Process the rdata for HTTPS records (type 65)
if (aRRType != TRRTYPE_HTTPSSVC || aRDLen == 0) {
context->mRv = NS_ERROR_UNKNOWN_HOST;
return;
}
nsDependentCString fullname(aFullname);
if (fullname.Length() && fullname.Last() == '.') {
// The fullname argument is always FQDN
fullname.Rebind(aFullname, fullname.Length() - 1);
}
struct SVCB parsed;
nsresult rv = DNSPacket::ParseHTTPS(
aRDLen, parsed, 0, (const unsigned char*)aRdata, aRDLen, fullname);
if (NS_FAILED(rv)) {
LOG("ParseHTTPS failed\n");
context->mRv = rv;
return;
}
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.
return;
}
if (parsed.mSvcFieldPriority == 0) {
// Alias form SvcDomainName must not have the "." value (empty)
if (parsed.mSvcDomainName.IsEmpty()) {
context->mRv = NS_ERROR_UNEXPECTED;
return;
}
LOG("alias mode %s -> %s", context->mHost.get(),
parsed.mSvcDomainName.get());
context->mHost = parsed.mSvcDomainName;
ToLowerCase(context->mHost);
return;
}
if (!context->mResult->is<TypeRecordHTTPSSVC>()) {
*context->mResult = mozilla::AsVariant(CopyableTArray<SVCB>());
}
auto& results = context->mResult->as<TypeRecordHTTPSSVC>();
results.AppendElement(parsed);
*context->mTTL = std::min<uint32_t>(*context->mTTL, aTtl);
}
nsresult ResolveHTTPSRecordImpl(const nsACString& aHost,
nsIDNSService::DNSFlags aFlags,
TypeRecordResultType& aResult, uint32_t& aTTL) {
nsAutoCString host(aHost);
nsAutoCString cname;
if (xpc::IsInAutomation() &&
!StaticPrefs::network_dns_native_https_query_in_automation()) {
return NS_ERROR_UNKNOWN_HOST;
}
LOG("resolving %s\n", host.get());
TimeStamp startTime = TimeStamp::Now();
struct DNSContext context{
.mResult = &aResult,
.mHost = host,
.mTTL = &aTTL,
};
DNSServiceRef sdRef;
DNSServiceErrorType err;
err = DNSServiceQueryRecord(&sdRef,
0, // No flags
0, // All interfaces
host.get(), TRRTYPE_HTTPSSVC, kDNSServiceClass_IN,
QueryCallback, &context);
if (err != kDNSServiceErr_NoError) {
LOG("DNSServiceQueryRecord failed: %d\n", err);
return NS_ERROR_UNKNOWN_HOST;
}
int fd = DNSServiceRefSockFD(sdRef);
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
// If the domain queried results in NXDOMAIN, then QueryCallback will
// never get called, and select will hang forever. We need to use a
// timeout so that select() eventually returns.
struct timeval timeout;
timeout.tv_sec =
StaticPrefs::network_dns_native_https_timeout_mac_msec() / 1000;
timeout.tv_usec =
(StaticPrefs::network_dns_native_https_timeout_mac_msec() % 1000) * 1000;
int result = select(fd + 1, &readfds, NULL, NULL, &timeout);
if (result > 0 && FD_ISSET(fd, &readfds)) {
// Process the result
DNSServiceProcessResult(sdRef);
} else if (result < 0) {
LOG("select() failed");
} else if (result == 0) {
LOG("select timed out");
}
// Cleanup
DNSServiceRefDeallocate(sdRef);
mozilla::glean::networking::dns_native_https_call_time.AccumulateRawDuration(
TimeStamp::Now() - startTime);
LOG("resolving %s done %x ttl=%u", host.get(), context.mRv, aTTL);
if (NS_FAILED(context.mRv)) {
return context.mRv;
}
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
|