Bug 1852900 - Use android_res_nquery from libandroid.so to resolve HTTPS records on Android (API level >= 29) r=necko-reviewers,jesup

Depends on D194515

Differential Revision: https://phabricator.services.mozilla.com/D196068
This commit is contained in:
Valentin Gosu 2023-12-19 00:06:53 +00:00
parent 30640603d5
commit eb73825495
5 changed files with 168 additions and 3 deletions

View file

@ -11912,6 +11912,13 @@
value: false
mirror: always
# When resolving a native HTTPS query with native APIs
# the Android implementation has a max timeout
- name: network.dns.native_https_timeout_android
type: RelaxedAtomicInt32
value: 20000
mirror: always
# When this pref is true, we copy the host name to a fresh string before
# calling into getaddrinfo.
- name: network.dns.copy_string_before_call

View file

@ -46,6 +46,7 @@ nsresult DNSPacket::FillBuffer(
std::function<int(unsigned char response[MAX_SIZE])>&& aPredicate) {
int response_length = aPredicate(mResponse);
if (response_length < 0) {
LOG(("FillBuffer response len < 0"));
mBodySize = 0;
mStatus = NS_ERROR_UNEXPECTED;
return mStatus;

View file

@ -0,0 +1,149 @@
/* -*- 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/net/DNSPacket.h"
#include "nsIDNSService.h"
#include "mozilla/Maybe.h"
#include "mozilla/Atomics.h"
#include "mozilla/StaticPrefs_network.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <resolv.h>
#include <poll.h>
#include <dlfcn.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, uint16_t aFlags,
TypeRecordResultType& aResult, uint32_t& aTTL) {
DNSPacket packet;
nsAutoCString host(aHost);
nsAutoCString cname;
nsresult rv;
if (!sLibLoading.exchange(true)) {
// We're the first call here, load the library and symbols.
void* handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
if (!handle) {
LOG("Error loading libandroid_net %s", dlerror());
return NS_ERROR_UNKNOWN_HOST;
}
auto x = dlsym(handle, "android_res_nquery");
if (!x) {
LOG("No android_res_nquery symbol");
}
sAndroidResNQuery = (android_res_nquery_ptr)x;
}
if (!sAndroidResNQuery) {
LOG("nquery not loaded");
// The library hasn't been loaded yet.
return NS_ERROR_UNKNOWN_HOST;
}
LOG("resolving %s\n", host.get());
// 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;
});
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;
}
} // namespace mozilla::net

View file

@ -64,6 +64,8 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
elif CONFIG["OS_TARGET"] == "Linux" or CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
SOURCES += ["PlatformDNSUnix.cpp"]
OS_LIBS += ["resolv"]
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
SOURCES += ["PlatformDNSAndroid.cpp"]
else:
DEFINES["MOZ_NO_HTTPS_IMPL"] = 1

View file

@ -52,6 +52,10 @@
# include "mozilla/WindowsVersion.h"
#endif // XP_WIN
#ifdef MOZ_WIDGET_ANDROID
# include "mozilla/jni/Utils.h"
#endif
#define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
#define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
@ -232,11 +236,13 @@ nsresult nsHostResolver::Init() MOZ_NO_THREAD_SAFETY_ANALYSIS {
// It returns a success code, but no records. We only allow
// native HTTPS records on Win 11 for now.
sNativeHTTPSSupported = mozilla::IsWin11OrLater();
#elif defined(XP_LINUX) && !defined(ANDROID)
sNativeHTTPSSupported = true;
#elif defined(XP_MACOSX)
#elif defined(MOZ_WIDGET_ANDROID)
// android_res_nquery only got added in API level 29
sNativeHTTPSSupported = jni::GetAPIVersion() >= 29;
#elif defined(XP_LINUX) || defined(XP_MACOSX)
sNativeHTTPSSupported = true;
#endif
LOG(("Native HTTPS records supported=%d", bool(sNativeHTTPSSupported)));
nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MaxResolverThreads()));