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
|
/* -*- 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/Maybe.h"
#include "mozilla/Atomics.h"
#include "mozilla/StaticPrefs_network.h"
#include <string.h>
#include <netinet/in.h>
#include <resolv.h>
#include <poll.h>
#include <android/multinetwork.h>
namespace mozilla::net {
// The first call to ResolveHTTPSRecordImpl will load the library
// and function pointers.
static Atomic<bool> sLibLoading{false};
// https://developer.android.com/ndk/reference/group/networking#android_res_nquery
// The function android_res_nquery is defined in <android/multinetwork.h>
typedef int (*android_res_nquery_ptr)(net_handle_t network, const char* dname,
int ns_class, int ns_type,
uint32_t flags);
static Atomic<android_res_nquery_ptr> sAndroidResNQuery;
#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) {
DNSPacket packet;
nsAutoCString host(aHost);
nsAutoCString cname;
nsresult rv;
if (xpc::IsInAutomation() &&
!StaticPrefs::network_dns_native_https_query_in_automation()) {
return NS_ERROR_UNKNOWN_HOST;
}
if (!sLibLoading.exchange(true)) {
// We're the first call here, load the library and symbols.
if (__builtin_available(android 29, *)) {
sAndroidResNQuery = android_res_nquery; // API 29
} else {
LOG("No android_res_nquery symbol");
}
}
if (!sAndroidResNQuery) {
LOG("nquery not loaded");
// The library hasn't been loaded yet.
return NS_ERROR_UNKNOWN_HOST;
}
LOG("resolving %s\n", host.get());
TimeStamp startTime = TimeStamp::Now();
// Perform the query
rv = packet.FillBuffer(
[&](unsigned char response[DNSPacket::MAX_SIZE]) -> int {
int fd = 0;
auto closeSocket = MakeScopeExit([&] {
if (fd > 0) {
close(fd);
}
});
uint32_t flags = 0;
if (aFlags & nsIDNSService::RESOLVE_BYPASS_CACHE) {
flags = ANDROID_RESOLV_NO_CACHE_LOOKUP;
}
fd = sAndroidResNQuery(0, host.get(), ns_c_in,
nsIDNSService::RESOLVE_TYPE_HTTPSSVC, flags);
if (fd < 0) {
LOG("DNS query failed");
return fd;
}
struct pollfd fds;
fds.fd = fd;
fds.events = POLLIN; // Wait for read events
// Wait for an event on the file descriptor
int ret = poll(&fds, 1,
StaticPrefs::network_dns_native_https_timeout_android());
if (ret <= 0) {
LOG("poll failed %d", ret);
return -1;
}
ssize_t len = recv(fd, response, DNSPacket::MAX_SIZE - 1, 0);
if (len <= 8) {
LOG("size too small %zd", len);
return len < 0 ? len : -1;
}
// The first 8 bytes are UDP header.
// XXX: we should consider avoiding this move somehow.
for (int i = 0; i < len - 8; i++) {
response[i] = response[i + 8];
}
return len - 8;
});
mozilla::glean::networking::dns_native_https_call_time.AccumulateRawDuration(
TimeStamp::Now() - startTime);
if (NS_FAILED(rv)) {
LOG("failed rv");
return rv;
}
packet.SetNativePacket(true);
int32_t loopCount = 64;
while (loopCount > 0 && aResult.is<Nothing>()) {
loopCount--;
DOHresp resp;
nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
rv = packet.Decode(host, TRRTYPE_HTTPSSVC, cname, true, resp, aResult,
additionalRecords, aTTL);
if (NS_FAILED(rv)) {
LOG("Decode failed %x", static_cast<uint32_t>(rv));
return rv;
}
if (!cname.IsEmpty() && aResult.is<Nothing>()) {
host = cname;
cname.Truncate();
continue;
}
}
if (aResult.is<Nothing>()) {
LOG("Result is nothing");
// The call succeeded, but no HTTPS records were found.
return NS_ERROR_UNKNOWN_HOST;
}
return NS_OK;
}
void DNSThreadShutdown() {}
} // namespace mozilla::net
|