blob: 382a9deb3b455c5c8833864fa32616c6a6fa9653 [file] [log] [blame]
Luke Huang6d23e742019-01-04 19:56:29 +08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
Luke Huangbe164b22019-05-29 01:30:07 +080019import static android.net.NetworkUtils.getDnsNetwork;
Luke Huangb1cf5aa2019-03-08 14:48:59 +080020import static android.net.NetworkUtils.resNetworkCancel;
Luke Huang6d23e742019-01-04 19:56:29 +080021import static android.net.NetworkUtils.resNetworkQuery;
22import static android.net.NetworkUtils.resNetworkResult;
23import static android.net.NetworkUtils.resNetworkSend;
Luke Huang26c1cdb2019-04-17 22:27:56 +080024import static android.net.util.DnsUtils.haveIpv4;
25import static android.net.util.DnsUtils.haveIpv6;
26import static android.net.util.DnsUtils.rfc6724Sort;
Luke Huang6d23e742019-01-04 19:56:29 +080027import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
28import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
Luke Huang26c1cdb2019-04-17 22:27:56 +080029import static android.system.OsConstants.ENONET;
Luke Huang6d23e742019-01-04 19:56:29 +080030
Luke Huangdfd2e4d2019-03-07 19:01:26 +080031import android.annotation.CallbackExecutor;
Luke Huang6d23e742019-01-04 19:56:29 +080032import android.annotation.IntDef;
33import android.annotation.NonNull;
34import android.annotation.Nullable;
Luke Huangb1cf5aa2019-03-08 14:48:59 +080035import android.os.CancellationSignal;
Luke Huangdfd2e4d2019-03-07 19:01:26 +080036import android.os.Looper;
Luke Huang6c8b7702019-06-11 14:25:45 +080037import android.os.MessageQueue;
Luke Huang6d23e742019-01-04 19:56:29 +080038import android.system.ErrnoException;
39import android.util.Log;
40
Luke Huangff0dc5c2020-06-16 08:13:28 +080041import com.android.net.module.util.DnsPacket;
42
Luke Huang6d23e742019-01-04 19:56:29 +080043import java.io.FileDescriptor;
44import java.lang.annotation.Retention;
45import java.lang.annotation.RetentionPolicy;
46import java.net.InetAddress;
47import java.net.UnknownHostException;
48import java.util.ArrayList;
49import java.util.List;
Luke Huangdfd2e4d2019-03-07 19:01:26 +080050import java.util.concurrent.Executor;
Luke Huang6d23e742019-01-04 19:56:29 +080051
52/**
53 * Dns resolver class for asynchronous dns querying
54 *
Luke Huang12448352019-01-23 21:53:13 +080055 * Note that if a client sends a query with more than 1 record in the question section but
56 * the remote dns server does not support this, it may not respond at all, leading to a timeout.
57 *
Luke Huang6d23e742019-01-04 19:56:29 +080058 */
59public final class DnsResolver {
60 private static final String TAG = "DnsResolver";
61 private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
62 private static final int MAXPACKET = 8 * 1024;
Luke Huang293ffaa2019-03-29 18:01:11 +080063 private static final int SLEEP_TIME_MS = 2;
Luke Huang6d23e742019-01-04 19:56:29 +080064
65 @IntDef(prefix = { "CLASS_" }, value = {
66 CLASS_IN
67 })
68 @Retention(RetentionPolicy.SOURCE)
69 @interface QueryClass {}
70 public static final int CLASS_IN = 1;
71
72 @IntDef(prefix = { "TYPE_" }, value = {
73 TYPE_A,
Junyu Lai822ab282022-07-25 17:43:51 +080074 TYPE_AAAA,
75 TYPE_CNAME
Luke Huang6d23e742019-01-04 19:56:29 +080076 })
77 @Retention(RetentionPolicy.SOURCE)
78 @interface QueryType {}
79 public static final int TYPE_A = 1;
80 public static final int TYPE_AAAA = 28;
Junyu Lai822ab282022-07-25 17:43:51 +080081 public static final int TYPE_CNAME = 5;
Luke Huang6d23e742019-01-04 19:56:29 +080082
83 @IntDef(prefix = { "FLAG_" }, value = {
84 FLAG_EMPTY,
85 FLAG_NO_RETRY,
86 FLAG_NO_CACHE_STORE,
87 FLAG_NO_CACHE_LOOKUP
88 })
89 @Retention(RetentionPolicy.SOURCE)
90 @interface QueryFlag {}
91 public static final int FLAG_EMPTY = 0;
92 public static final int FLAG_NO_RETRY = 1 << 0;
93 public static final int FLAG_NO_CACHE_STORE = 1 << 1;
94 public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2;
95
Luke Huang81cec002019-04-08 15:16:04 +080096 @IntDef(prefix = { "ERROR_" }, value = {
97 ERROR_PARSE,
98 ERROR_SYSTEM
99 })
100 @Retention(RetentionPolicy.SOURCE)
101 @interface DnsError {}
102 /**
103 * Indicates that there was an error parsing the response the query.
Luke Huangff0dc5c2020-06-16 08:13:28 +0800104 * The cause of this error is available via getCause() and is a {@link ParseException}.
Luke Huang81cec002019-04-08 15:16:04 +0800105 */
106 public static final int ERROR_PARSE = 0;
107 /**
108 * Indicates that there was an error sending the query.
109 * The cause of this error is available via getCause() and is an ErrnoException.
110 */
111 public static final int ERROR_SYSTEM = 1;
112
Luke Huang6d23e742019-01-04 19:56:29 +0800113 private static final int NETID_UNSET = 0;
114
115 private static final DnsResolver sInstance = new DnsResolver();
116
117 /**
Luke Huang6d23e742019-01-04 19:56:29 +0800118 * Get instance for DnsResolver
119 */
Luke Huang6d72b2c2019-03-04 17:08:03 +0800120 public static @NonNull DnsResolver getInstance() {
Luke Huang6d23e742019-01-04 19:56:29 +0800121 return sInstance;
122 }
123
124 private DnsResolver() {}
125
126 /**
Luke Huang81cec002019-04-08 15:16:04 +0800127 * Base interface for answer callbacks
Luke Huang6d72b2c2019-03-04 17:08:03 +0800128 *
Luke Huang81cec002019-04-08 15:16:04 +0800129 * @param <T> The type of the answer
Luke Huang6d72b2c2019-03-04 17:08:03 +0800130 */
Luke Huang81cec002019-04-08 15:16:04 +0800131 public interface Callback<T> {
Luke Huang6d72b2c2019-03-04 17:08:03 +0800132 /**
133 * Success response to
Luke Huang81cec002019-04-08 15:16:04 +0800134 * {@link android.net.DnsResolver#query query()} or
135 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang6d72b2c2019-03-04 17:08:03 +0800136 *
137 * Invoked when the answer to a query was successfully parsed.
138 *
Luke Huang81cec002019-04-08 15:16:04 +0800139 * @param answer <T> answer to the query.
140 * @param rcode The response code in the DNS response.
Luke Huang6d72b2c2019-03-04 17:08:03 +0800141 *
chiachangwang9473c592022-07-15 02:25:52 +0000142 * @see android.net.DnsResolver#query query()
Luke Huang6d72b2c2019-03-04 17:08:03 +0800143 */
Luke Huang81cec002019-04-08 15:16:04 +0800144 void onAnswer(@NonNull T answer, int rcode);
Luke Huang6d72b2c2019-03-04 17:08:03 +0800145 /**
146 * Error response to
Luke Huang81cec002019-04-08 15:16:04 +0800147 * {@link android.net.DnsResolver#query query()} or
148 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang6d72b2c2019-03-04 17:08:03 +0800149 *
150 * Invoked when there is no valid answer to
151 * {@link android.net.DnsResolver#query query()}
Luke Huang81cec002019-04-08 15:16:04 +0800152 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang6d72b2c2019-03-04 17:08:03 +0800153 *
Luke Huang81cec002019-04-08 15:16:04 +0800154 * @param error a {@link DnsException} object with additional
Luke Huang6d72b2c2019-03-04 17:08:03 +0800155 * detail regarding the failure
156 */
Luke Huang81cec002019-04-08 15:16:04 +0800157 void onError(@NonNull DnsException error);
Luke Huang6d72b2c2019-03-04 17:08:03 +0800158 }
159
160 /**
Luke Huang81cec002019-04-08 15:16:04 +0800161 * Class to represent DNS error
Luke Huang6d72b2c2019-03-04 17:08:03 +0800162 */
Luke Huang81cec002019-04-08 15:16:04 +0800163 public static class DnsException extends Exception {
164 /**
165 * DNS error code as one of the ERROR_* constants
166 */
167 @DnsError public final int code;
Luke Huang6d72b2c2019-03-04 17:08:03 +0800168
Aswin Sankar74cbfd52021-12-02 04:25:08 +0000169 public DnsException(@DnsError int code, @Nullable Throwable cause) {
Luke Huang81cec002019-04-08 15:16:04 +0800170 super(cause);
171 this.code = code;
Luke Huang6d72b2c2019-03-04 17:08:03 +0800172 }
173 }
174
175 /**
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800176 * Send a raw DNS query.
Luke Huang81cec002019-04-08 15:16:04 +0800177 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang6d23e742019-01-04 19:56:29 +0800178 *
Luke Huangfb5afab2019-03-28 13:56:31 +0800179 * @param network {@link Network} specifying which network to query on.
Luke Huang6d23e742019-01-04 19:56:29 +0800180 * {@code null} for query on default network.
Luke Huangfb5afab2019-03-28 13:56:31 +0800181 * @param query blob message to query
Luke Huang6d23e742019-01-04 19:56:29 +0800182 * @param flags flags as a combination of the FLAGS_* constants
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800183 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800184 * @param cancellationSignal used by the caller to signal if the query should be
185 * cancelled. May be {@code null}.
Luke Huang81cec002019-04-08 15:16:04 +0800186 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800187 * of the result of dns query.
Luke Huang6d23e742019-01-04 19:56:29 +0800188 */
Luke Huang81cec002019-04-08 15:16:04 +0800189 public void rawQuery(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags,
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800190 @NonNull @CallbackExecutor Executor executor,
191 @Nullable CancellationSignal cancellationSignal,
Luke Huang81cec002019-04-08 15:16:04 +0800192 @NonNull Callback<? super byte[]> callback) {
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800193 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
194 return;
195 }
Luke Huange17bb2d2019-03-26 15:50:10 +0800196 final Object lock = new Object();
Luke Huang6d72b2c2019-03-04 17:08:03 +0800197 final FileDescriptor queryfd;
198 try {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800199 queryfd = resNetworkSend((network != null)
200 ? network.getNetIdForResolv() : NETID_UNSET, query, query.length, flags);
Luke Huang6d72b2c2019-03-04 17:08:03 +0800201 } catch (ErrnoException e) {
Luke Huang81cec002019-04-08 15:16:04 +0800202 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang6d72b2c2019-03-04 17:08:03 +0800203 return;
204 }
205
Luke Huang293ffaa2019-03-29 18:01:11 +0800206 synchronized (lock) {
207 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
208 if (cancellationSignal == null) return;
209 addCancellationSignal(cancellationSignal, queryfd, lock);
210 }
Luke Huang6d23e742019-01-04 19:56:29 +0800211 }
212
213 /**
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800214 * Send a DNS query with the specified name, class and query type.
Luke Huang81cec002019-04-08 15:16:04 +0800215 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang6d23e742019-01-04 19:56:29 +0800216 *
Luke Huangfb5afab2019-03-28 13:56:31 +0800217 * @param network {@link Network} specifying which network to query on.
Luke Huang6d23e742019-01-04 19:56:29 +0800218 * {@code null} for query on default network.
Luke Huangfb5afab2019-03-28 13:56:31 +0800219 * @param domain domain name to query
Luke Huang6d23e742019-01-04 19:56:29 +0800220 * @param nsClass dns class as one of the CLASS_* constants
221 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
222 * @param flags flags as a combination of the FLAGS_* constants
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800223 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800224 * @param cancellationSignal used by the caller to signal if the query should be
225 * cancelled. May be {@code null}.
Luke Huang81cec002019-04-08 15:16:04 +0800226 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800227 * of the result of dns query.
Luke Huang6d23e742019-01-04 19:56:29 +0800228 */
Luke Huang81cec002019-04-08 15:16:04 +0800229 public void rawQuery(@Nullable Network network, @NonNull String domain,
Luke Huang6d72b2c2019-03-04 17:08:03 +0800230 @QueryClass int nsClass, @QueryType int nsType, @QueryFlag int flags,
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800231 @NonNull @CallbackExecutor Executor executor,
232 @Nullable CancellationSignal cancellationSignal,
Luke Huang81cec002019-04-08 15:16:04 +0800233 @NonNull Callback<? super byte[]> callback) {
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800234 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
235 return;
236 }
Luke Huange17bb2d2019-03-26 15:50:10 +0800237 final Object lock = new Object();
Luke Huang6d72b2c2019-03-04 17:08:03 +0800238 final FileDescriptor queryfd;
239 try {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800240 queryfd = resNetworkQuery((network != null)
241 ? network.getNetIdForResolv() : NETID_UNSET, domain, nsClass, nsType, flags);
Luke Huang6d72b2c2019-03-04 17:08:03 +0800242 } catch (ErrnoException e) {
Luke Huang81cec002019-04-08 15:16:04 +0800243 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang6d72b2c2019-03-04 17:08:03 +0800244 return;
245 }
Luke Huang293ffaa2019-03-29 18:01:11 +0800246 synchronized (lock) {
247 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
248 if (cancellationSignal == null) return;
249 addCancellationSignal(cancellationSignal, queryfd, lock);
250 }
Luke Huangfb5afab2019-03-28 13:56:31 +0800251 }
252
Luke Huang81cec002019-04-08 15:16:04 +0800253 private class InetAddressAnswerAccumulator implements Callback<byte[]> {
Luke Huangfb5afab2019-03-28 13:56:31 +0800254 private final List<InetAddress> mAllAnswers;
Luke Huang26c1cdb2019-04-17 22:27:56 +0800255 private final Network mNetwork;
Luke Huang81cec002019-04-08 15:16:04 +0800256 private int mRcode;
257 private DnsException mDnsException;
258 private final Callback<? super List<InetAddress>> mUserCallback;
Luke Huangfb5afab2019-03-28 13:56:31 +0800259 private final int mTargetAnswerCount;
260 private int mReceivedAnswerCount = 0;
261
Luke Huang26c1cdb2019-04-17 22:27:56 +0800262 InetAddressAnswerAccumulator(@NonNull Network network, int size,
Luke Huang81cec002019-04-08 15:16:04 +0800263 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800264 mNetwork = network;
Luke Huangfb5afab2019-03-28 13:56:31 +0800265 mTargetAnswerCount = size;
266 mAllAnswers = new ArrayList<>();
267 mUserCallback = callback;
268 }
269
Luke Huang81cec002019-04-08 15:16:04 +0800270 private boolean maybeReportError() {
271 if (mRcode != 0) {
272 mUserCallback.onAnswer(mAllAnswers, mRcode);
Luke Huangfb5afab2019-03-28 13:56:31 +0800273 return true;
274 }
Luke Huang81cec002019-04-08 15:16:04 +0800275 if (mDnsException != null) {
276 mUserCallback.onError(mDnsException);
Luke Huangfb5afab2019-03-28 13:56:31 +0800277 return true;
278 }
279 return false;
280 }
281
282 private void maybeReportAnswer() {
283 if (++mReceivedAnswerCount != mTargetAnswerCount) return;
Luke Huang81cec002019-04-08 15:16:04 +0800284 if (mAllAnswers.isEmpty() && maybeReportError()) return;
Luke Huang26c1cdb2019-04-17 22:27:56 +0800285 mUserCallback.onAnswer(rfc6724Sort(mNetwork, mAllAnswers), mRcode);
Luke Huangfb5afab2019-03-28 13:56:31 +0800286 }
287
288 @Override
Luke Huang81cec002019-04-08 15:16:04 +0800289 public void onAnswer(@NonNull byte[] answer, int rcode) {
290 // If at least one query succeeded, return an rcode of 0.
291 // Otherwise, arbitrarily return the first rcode received.
292 if (mReceivedAnswerCount == 0 || rcode == 0) {
293 mRcode = rcode;
294 }
295 try {
296 mAllAnswers.addAll(new DnsAddressAnswer(answer).getAddresses());
Luke Huangff0dc5c2020-06-16 08:13:28 +0800297 } catch (DnsPacket.ParseException e) {
298 // Convert the com.android.net.module.util.DnsPacket.ParseException to an
299 // android.net.ParseException. This is the type that was used in Q and is implied
300 // by the public documentation of ERROR_PARSE.
301 //
302 // DnsPacket cannot throw android.net.ParseException directly because it's @hide.
303 ParseException pe = new ParseException(e.reason, e.getCause());
304 pe.setStackTrace(e.getStackTrace());
305 mDnsException = new DnsException(ERROR_PARSE, pe);
Luke Huang81cec002019-04-08 15:16:04 +0800306 }
Luke Huangfb5afab2019-03-28 13:56:31 +0800307 maybeReportAnswer();
308 }
309
310 @Override
Luke Huang81cec002019-04-08 15:16:04 +0800311 public void onError(@NonNull DnsException error) {
312 mDnsException = error;
Luke Huangfb5afab2019-03-28 13:56:31 +0800313 maybeReportAnswer();
314 }
315 }
316
317 /**
Luke Huang81cec002019-04-08 15:16:04 +0800318 * Send a DNS query with the specified name on a network with both IPv4 and IPv6,
Luke Huang26c1cdb2019-04-17 22:27:56 +0800319 * get back a set of InetAddresses with rfc6724 sorting style asynchronously.
Luke Huang81cec002019-04-08 15:16:04 +0800320 *
321 * This method will examine the connection ability on given network, and query IPv4
322 * and IPv6 if connection is available.
323 *
324 * If at least one query succeeded with valid answer, rcode will be 0
325 *
326 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huangfb5afab2019-03-28 13:56:31 +0800327 *
328 * @param network {@link Network} specifying which network to query on.
329 * {@code null} for query on default network.
330 * @param domain domain name to query
331 * @param flags flags as a combination of the FLAGS_* constants
332 * @param executor The {@link Executor} that the callback should be executed on.
333 * @param cancellationSignal used by the caller to signal if the query should be
334 * cancelled. May be {@code null}.
Luke Huang81cec002019-04-08 15:16:04 +0800335 * @param callback a {@link Callback} which will be called to notify the
Luke Huangfb5afab2019-03-28 13:56:31 +0800336 * caller of the result of dns query.
337 */
338 public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
339 @NonNull @CallbackExecutor Executor executor,
340 @Nullable CancellationSignal cancellationSignal,
Luke Huang81cec002019-04-08 15:16:04 +0800341 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huangfb5afab2019-03-28 13:56:31 +0800342 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
343 return;
344 }
345 final Object lock = new Object();
Luke Huang26c1cdb2019-04-17 22:27:56 +0800346 final Network queryNetwork;
347 try {
Luke Huangbe164b22019-05-29 01:30:07 +0800348 queryNetwork = (network != null) ? network : getDnsNetwork();
Luke Huang26c1cdb2019-04-17 22:27:56 +0800349 } catch (ErrnoException e) {
350 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
351 return;
352 }
353 final boolean queryIpv6 = haveIpv6(queryNetwork);
354 final boolean queryIpv4 = haveIpv4(queryNetwork);
355
356 // This can only happen if queryIpv4 and queryIpv6 are both false.
357 // This almost certainly means that queryNetwork does not exist or no longer exists.
358 if (!queryIpv6 && !queryIpv4) {
359 executor.execute(() -> callback.onError(
360 new DnsException(ERROR_SYSTEM, new ErrnoException("resNetworkQuery", ENONET))));
361 return;
362 }
Luke Huangfb5afab2019-03-28 13:56:31 +0800363
364 final FileDescriptor v4fd;
365 final FileDescriptor v6fd;
366
367 int queryCount = 0;
368
369 if (queryIpv6) {
370 try {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800371 v6fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN,
372 TYPE_AAAA, flags);
Luke Huangfb5afab2019-03-28 13:56:31 +0800373 } catch (ErrnoException e) {
Luke Huang81cec002019-04-08 15:16:04 +0800374 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huangfb5afab2019-03-28 13:56:31 +0800375 return;
376 }
377 queryCount++;
378 } else v6fd = null;
379
Luke Huangfb5afab2019-03-28 13:56:31 +0800380 // Avoiding gateways drop packets if queries are sent too close together
381 try {
Luke Huang293ffaa2019-03-29 18:01:11 +0800382 Thread.sleep(SLEEP_TIME_MS);
Luke Huang81cec002019-04-08 15:16:04 +0800383 } catch (InterruptedException ex) {
384 Thread.currentThread().interrupt();
385 }
Luke Huangfb5afab2019-03-28 13:56:31 +0800386
387 if (queryIpv4) {
388 try {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800389 v4fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, TYPE_A,
390 flags);
Luke Huangfb5afab2019-03-28 13:56:31 +0800391 } catch (ErrnoException e) {
392 if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
Luke Huang81cec002019-04-08 15:16:04 +0800393 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huangfb5afab2019-03-28 13:56:31 +0800394 return;
395 }
396 queryCount++;
397 } else v4fd = null;
398
399 final InetAddressAnswerAccumulator accumulator =
Luke Huang26c1cdb2019-04-17 22:27:56 +0800400 new InetAddressAnswerAccumulator(queryNetwork, queryCount, callback);
Luke Huangfb5afab2019-03-28 13:56:31 +0800401
Luke Huang293ffaa2019-03-29 18:01:11 +0800402 synchronized (lock) {
403 if (queryIpv6) {
404 registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
Luke Huangfb5afab2019-03-28 13:56:31 +0800405 }
Luke Huang293ffaa2019-03-29 18:01:11 +0800406 if (queryIpv4) {
407 registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
408 }
409 if (cancellationSignal == null) return;
410 cancellationSignal.setOnCancelListener(() -> {
411 synchronized (lock) {
412 if (queryIpv4) cancelQuery(v4fd);
413 if (queryIpv6) cancelQuery(v6fd);
414 }
415 });
416 }
Luke Huang6d23e742019-01-04 19:56:29 +0800417 }
418
Luke Huang81cec002019-04-08 15:16:04 +0800419 /**
420 * Send a DNS query with the specified name and query type, get back a set of
Luke Huang26c1cdb2019-04-17 22:27:56 +0800421 * InetAddresses with rfc6724 sorting style asynchronously.
Luke Huang81cec002019-04-08 15:16:04 +0800422 *
423 * The answer will be provided asynchronously through the provided {@link Callback}.
424 *
425 * @param network {@link Network} specifying which network to query on.
426 * {@code null} for query on default network.
427 * @param domain domain name to query
428 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
429 * @param flags flags as a combination of the FLAGS_* constants
430 * @param executor The {@link Executor} that the callback should be executed on.
431 * @param cancellationSignal used by the caller to signal if the query should be
432 * cancelled. May be {@code null}.
433 * @param callback a {@link Callback} which will be called to notify the caller
434 * of the result of dns query.
435 */
436 public void query(@Nullable Network network, @NonNull String domain,
437 @QueryType int nsType, @QueryFlag int flags,
438 @NonNull @CallbackExecutor Executor executor,
439 @Nullable CancellationSignal cancellationSignal,
440 @NonNull Callback<? super List<InetAddress>> callback) {
441 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
442 return;
443 }
444 final Object lock = new Object();
445 final FileDescriptor queryfd;
Luke Huang26c1cdb2019-04-17 22:27:56 +0800446 final Network queryNetwork;
Luke Huang81cec002019-04-08 15:16:04 +0800447 try {
Luke Huangbe164b22019-05-29 01:30:07 +0800448 queryNetwork = (network != null) ? network : getDnsNetwork();
Luke Huang26c1cdb2019-04-17 22:27:56 +0800449 queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType,
450 flags);
Luke Huang81cec002019-04-08 15:16:04 +0800451 } catch (ErrnoException e) {
452 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
453 return;
454 }
455 final InetAddressAnswerAccumulator accumulator =
Luke Huang26c1cdb2019-04-17 22:27:56 +0800456 new InetAddressAnswerAccumulator(queryNetwork, 1, callback);
Luke Huang81cec002019-04-08 15:16:04 +0800457 synchronized (lock) {
458 registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock);
459 if (cancellationSignal == null) return;
460 addCancellationSignal(cancellationSignal, queryfd, lock);
461 }
462 }
463
464 /**
465 * Class to retrieve DNS response
466 *
467 * @hide
468 */
469 public static final class DnsResponse {
470 public final @NonNull byte[] answerbuf;
471 public final int rcode;
472 public DnsResponse(@NonNull byte[] answerbuf, int rcode) {
473 this.answerbuf = answerbuf;
474 this.rcode = rcode;
475 }
476 }
477
478 private void registerFDListener(@NonNull Executor executor,
479 @NonNull FileDescriptor queryfd, @NonNull Callback<? super byte[]> answerCallback,
Luke Huange17bb2d2019-03-26 15:50:10 +0800480 @Nullable CancellationSignal cancellationSignal, @NonNull Object lock) {
Luke Huang6c8b7702019-06-11 14:25:45 +0800481 final MessageQueue mainThreadMessageQueue = Looper.getMainLooper().getQueue();
482 mainThreadMessageQueue.addOnFileDescriptorEventListener(
Luke Huang6d23e742019-01-04 19:56:29 +0800483 queryfd,
484 FD_EVENTS,
485 (fd, events) -> {
Luke Huang6c8b7702019-06-11 14:25:45 +0800486 // b/134310704
487 // Unregister fd event listener before resNetworkResult is called to prevent
488 // race condition caused by fd reused.
489 // For example when querying v4 and v6, it's possible that the first query ends
490 // and the fd is closed before the second request starts, which might return
491 // the same fd for the second request. By that time, the looper must have
492 // unregistered the fd, otherwise another event listener can't be registered.
493 mainThreadMessageQueue.removeOnFileDescriptorEventListener(fd);
494
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800495 executor.execute(() -> {
Luke Huang81cec002019-04-08 15:16:04 +0800496 DnsResponse resp = null;
497 ErrnoException exception = null;
Luke Huange17bb2d2019-03-26 15:50:10 +0800498 synchronized (lock) {
499 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
500 return;
501 }
Luke Huange17bb2d2019-03-26 15:50:10 +0800502 try {
Luke Huang81cec002019-04-08 15:16:04 +0800503 resp = resNetworkResult(fd); // Closes fd, marks it invalid.
Luke Huange17bb2d2019-03-26 15:50:10 +0800504 } catch (ErrnoException e) {
Ken Chenf6f39302021-04-02 01:44:27 +0800505 Log.w(TAG, "resNetworkResult:" + e.toString());
Luke Huang81cec002019-04-08 15:16:04 +0800506 exception = e;
Luke Huange17bb2d2019-03-26 15:50:10 +0800507 }
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800508 }
Luke Huang81cec002019-04-08 15:16:04 +0800509 if (exception != null) {
510 answerCallback.onError(new DnsException(ERROR_SYSTEM, exception));
511 return;
512 }
513 answerCallback.onAnswer(resp.answerbuf, resp.rcode);
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800514 });
Luke Huang6c8b7702019-06-11 14:25:45 +0800515
516 // The file descriptor has already been unregistered, so it does not really
517 // matter what is returned here. In spirit 0 (meaning "unregister this FD")
518 // is still the closest to what the looper needs to do. When returning 0,
519 // Looper knows to ignore the fd if it has already been unregistered.
Luke Huang6d23e742019-01-04 19:56:29 +0800520 return 0;
521 });
522 }
523
Luke Huangfb5afab2019-03-28 13:56:31 +0800524 private void cancelQuery(@NonNull FileDescriptor queryfd) {
525 if (!queryfd.valid()) return;
526 Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd);
527 resNetworkCancel(queryfd); // Closes fd, marks it invalid.
528 }
529
Luke Huang293ffaa2019-03-29 18:01:11 +0800530 private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
Luke Huange17bb2d2019-03-26 15:50:10 +0800531 @NonNull FileDescriptor queryfd, @NonNull Object lock) {
Luke Huange17bb2d2019-03-26 15:50:10 +0800532 cancellationSignal.setOnCancelListener(() -> {
533 synchronized (lock) {
Luke Huangfb5afab2019-03-28 13:56:31 +0800534 cancelQuery(queryfd);
Luke Huange17bb2d2019-03-26 15:50:10 +0800535 }
536 });
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800537 }
538
Luke Huang6d72b2c2019-03-04 17:08:03 +0800539 private static class DnsAddressAnswer extends DnsPacket {
Luke Huang6d23e742019-01-04 19:56:29 +0800540 private static final String TAG = "DnsResolver.DnsAddressAnswer";
541 private static final boolean DBG = false;
542
543 private final int mQueryType;
544
545 DnsAddressAnswer(@NonNull byte[] data) throws ParseException {
546 super(data);
Remi NGUYEN VANc7043882022-09-08 11:01:05 +0000547 if ((mHeader.flags & (1 << 15)) == 0) {
Luke Huang6d23e742019-01-04 19:56:29 +0800548 throw new ParseException("Not an answer packet");
549 }
Luke Huang12448352019-01-23 21:53:13 +0800550 if (mHeader.getRecordCount(QDSECTION) == 0) {
Luke Huang6d23e742019-01-04 19:56:29 +0800551 throw new ParseException("No question found");
552 }
Luke Huang12448352019-01-23 21:53:13 +0800553 // Expect only one question in question section.
554 mQueryType = mRecords[QDSECTION].get(0).nsType;
Luke Huang6d23e742019-01-04 19:56:29 +0800555 }
556
557 public @NonNull List<InetAddress> getAddresses() {
558 final List<InetAddress> results = new ArrayList<InetAddress>();
Luke Huang6d72b2c2019-03-04 17:08:03 +0800559 if (mHeader.getRecordCount(ANSECTION) == 0) return results;
560
Luke Huang12448352019-01-23 21:53:13 +0800561 for (final DnsRecord ansSec : mRecords[ANSECTION]) {
Luke Huang6d23e742019-01-04 19:56:29 +0800562 // Only support A and AAAA, also ignore answers if query type != answer type.
563 int nsType = ansSec.nsType;
564 if (nsType != mQueryType || (nsType != TYPE_A && nsType != TYPE_AAAA)) {
565 continue;
566 }
567 try {
568 results.add(InetAddress.getByAddress(ansSec.getRR()));
569 } catch (UnknownHostException e) {
570 if (DBG) {
571 Log.w(TAG, "rr to address fail");
572 }
573 }
574 }
575 return results;
576 }
577 }
578
Luke Huang6d23e742019-01-04 19:56:29 +0800579}