PlatformDNSAndroid.cpp (4651B)
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/. */ 6 7 #include "GetAddrInfo.h" 8 #include "mozilla/glean/NetwerkMetrics.h" 9 #include "mozilla/net/DNSPacket.h" 10 #include "nsIDNSService.h" 11 #include "mozilla/Maybe.h" 12 #include "mozilla/Atomics.h" 13 #include "mozilla/StaticPrefs_network.h" 14 15 #include <string.h> 16 #include <netinet/in.h> 17 #include <resolv.h> 18 #include <poll.h> 19 #include <android/multinetwork.h> 20 21 namespace mozilla::net { 22 23 // The first call to ResolveHTTPSRecordImpl will load the library 24 // and function pointers. 25 static Atomic<bool> sLibLoading{false}; 26 27 // https://developer.android.com/ndk/reference/group/networking#android_res_nquery 28 // The function android_res_nquery is defined in <android/multinetwork.h> 29 typedef int (*android_res_nquery_ptr)(net_handle_t network, const char* dname, 30 int ns_class, int ns_type, 31 uint32_t flags); 32 static Atomic<android_res_nquery_ptr> sAndroidResNQuery; 33 34 #define LOG(msg, ...) \ 35 MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__)) 36 37 nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, 38 nsIDNSService::DNSFlags aFlags, 39 TypeRecordResultType& aResult, uint32_t& aTTL) { 40 DNSPacket packet; 41 nsAutoCString host(aHost); 42 nsAutoCString cname; 43 nsresult rv; 44 45 if (xpc::IsInAutomation() && 46 !StaticPrefs::network_dns_native_https_query_in_automation()) { 47 return NS_ERROR_UNKNOWN_HOST; 48 } 49 50 if (!sLibLoading.exchange(true)) { 51 // We're the first call here, load the library and symbols. 52 if (__builtin_available(android 29, *)) { 53 sAndroidResNQuery = android_res_nquery; // API 29 54 } else { 55 LOG("No android_res_nquery symbol"); 56 } 57 } 58 59 if (!sAndroidResNQuery) { 60 LOG("nquery not loaded"); 61 // The library hasn't been loaded yet. 62 return NS_ERROR_UNKNOWN_HOST; 63 } 64 65 LOG("resolving %s\n", host.get()); 66 TimeStamp startTime = TimeStamp::Now(); 67 // Perform the query 68 rv = packet.FillBuffer( 69 [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int { 70 int fd = 0; 71 auto closeSocket = MakeScopeExit([&] { 72 if (fd > 0) { 73 close(fd); 74 } 75 }); 76 uint32_t flags = 0; 77 if (aFlags & nsIDNSService::RESOLVE_BYPASS_CACHE) { 78 flags = ANDROID_RESOLV_NO_CACHE_LOOKUP; 79 } 80 fd = sAndroidResNQuery(0, host.get(), ns_c_in, 81 nsIDNSService::RESOLVE_TYPE_HTTPSSVC, flags); 82 83 if (fd < 0) { 84 LOG("DNS query failed"); 85 return fd; 86 } 87 88 struct pollfd fds; 89 fds.fd = fd; 90 fds.events = POLLIN; // Wait for read events 91 92 // Wait for an event on the file descriptor 93 int ret = poll(&fds, 1, 94 StaticPrefs::network_dns_native_https_timeout_android()); 95 if (ret <= 0) { 96 LOG("poll failed %d", ret); 97 return -1; 98 } 99 100 ssize_t len = recv(fd, response, DNSPacket::MAX_SIZE - 1, 0); 101 if (len <= 8) { 102 LOG("size too small %zd", len); 103 return len < 0 ? len : -1; 104 } 105 106 // The first 8 bytes are UDP header. 107 // XXX: we should consider avoiding this move somehow. 108 for (int i = 0; i < len - 8; i++) { 109 response[i] = response[i + 8]; 110 } 111 112 return len - 8; 113 }); 114 mozilla::glean::networking::dns_native_https_call_time.AccumulateRawDuration( 115 TimeStamp::Now() - startTime); 116 if (NS_FAILED(rv)) { 117 LOG("failed rv"); 118 return rv; 119 } 120 packet.SetNativePacket(true); 121 122 int32_t loopCount = 64; 123 while (loopCount > 0 && aResult.is<Nothing>()) { 124 loopCount--; 125 DOHresp resp; 126 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords; 127 rv = packet.Decode(host, TRRTYPE_HTTPSSVC, cname, true, resp, aResult, 128 additionalRecords, aTTL); 129 if (NS_FAILED(rv)) { 130 LOG("Decode failed %x", static_cast<uint32_t>(rv)); 131 return rv; 132 } 133 if (!cname.IsEmpty() && aResult.is<Nothing>()) { 134 host = cname; 135 cname.Truncate(); 136 continue; 137 } 138 } 139 140 if (aResult.is<Nothing>()) { 141 LOG("Result is nothing"); 142 // The call succeeded, but no HTTPS records were found. 143 return NS_ERROR_UNKNOWN_HOST; 144 } 145 146 return NS_OK; 147 } 148 149 void DNSThreadShutdown() {} 150 151 } // namespace mozilla::net