blob: 5fefcd6770a1d2bd8cfd106ac83c9b4cd95d5981 [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,
Remi NGUYEN VAN110bbda2022-09-08 11:01:05 +000074 TYPE_AAAA
Luke Huang6d23e742019-01-04 19:56:29 +080075 })
76 @Retention(RetentionPolicy.SOURCE)
77 @interface QueryType {}
78 public static final int TYPE_A = 1;
79 public static final int TYPE_AAAA = 28;
Yuyang Huangfca402a2023-05-24 14:45:59 +090080 // TODO: add below constants as part of QueryType and the public API
81 /** @hide */
82 public static final int TYPE_PTR = 12;
83 /** @hide */
84 public static final int TYPE_TXT = 16;
85 /** @hide */
86 public static final int TYPE_SRV = 33;
87 /** @hide */
88 public static final int TYPE_ANY = 255;
Luke Huang6d23e742019-01-04 19:56:29 +080089
90 @IntDef(prefix = { "FLAG_" }, value = {
91 FLAG_EMPTY,
92 FLAG_NO_RETRY,
93 FLAG_NO_CACHE_STORE,
94 FLAG_NO_CACHE_LOOKUP
95 })
96 @Retention(RetentionPolicy.SOURCE)
97 @interface QueryFlag {}
98 public static final int FLAG_EMPTY = 0;
99 public static final int FLAG_NO_RETRY = 1 << 0;
100 public static final int FLAG_NO_CACHE_STORE = 1 << 1;
101 public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2;
102
Luke Huang81cec002019-04-08 15:16:04 +0800103 @IntDef(prefix = { "ERROR_" }, value = {
104 ERROR_PARSE,
105 ERROR_SYSTEM
106 })
107 @Retention(RetentionPolicy.SOURCE)
108 @interface DnsError {}
109 /**
110 * Indicates that there was an error parsing the response the query.
Luke Huangff0dc5c2020-06-16 08:13:28 +0800111 * The cause of this error is available via getCause() and is a {@link ParseException}.
Luke Huang81cec002019-04-08 15:16:04 +0800112 */
113 public static final int ERROR_PARSE = 0;
114 /**
115 * Indicates that there was an error sending the query.
116 * The cause of this error is available via getCause() and is an ErrnoException.
117 */
118 public static final int ERROR_SYSTEM = 1;
119
Luke Huang6d23e742019-01-04 19:56:29 +0800120 private static final int NETID_UNSET = 0;
121
122 private static final DnsResolver sInstance = new DnsResolver();
123
124 /**
Luke Huang6d23e742019-01-04 19:56:29 +0800125 * Get instance for DnsResolver
126 */
Luke Huang6d72b2c2019-03-04 17:08:03 +0800127 public static @NonNull DnsResolver getInstance() {
Luke Huang6d23e742019-01-04 19:56:29 +0800128 return sInstance;
129 }
130
131 private DnsResolver() {}
132
133 /**
Luke Huang81cec002019-04-08 15:16:04 +0800134 * Base interface for answer callbacks
Luke Huang6d72b2c2019-03-04 17:08:03 +0800135 *
Luke Huang81cec002019-04-08 15:16:04 +0800136 * @param <T> The type of the answer
Luke Huang6d72b2c2019-03-04 17:08:03 +0800137 */
Luke Huang81cec002019-04-08 15:16:04 +0800138 public interface Callback<T> {
Luke Huang6d72b2c2019-03-04 17:08:03 +0800139 /**
140 * Success response to
Luke Huang81cec002019-04-08 15:16:04 +0800141 * {@link android.net.DnsResolver#query query()} or
142 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang6d72b2c2019-03-04 17:08:03 +0800143 *
144 * Invoked when the answer to a query was successfully parsed.
145 *
Luke Huang81cec002019-04-08 15:16:04 +0800146 * @param answer <T> answer to the query.
147 * @param rcode The response code in the DNS response.
Luke Huang6d72b2c2019-03-04 17:08:03 +0800148 *
chiachangwang9473c592022-07-15 02:25:52 +0000149 * @see android.net.DnsResolver#query query()
Luke Huang6d72b2c2019-03-04 17:08:03 +0800150 */
Luke Huang81cec002019-04-08 15:16:04 +0800151 void onAnswer(@NonNull T answer, int rcode);
Luke Huang6d72b2c2019-03-04 17:08:03 +0800152 /**
153 * Error response to
Luke Huang81cec002019-04-08 15:16:04 +0800154 * {@link android.net.DnsResolver#query query()} or
155 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang6d72b2c2019-03-04 17:08:03 +0800156 *
157 * Invoked when there is no valid answer to
158 * {@link android.net.DnsResolver#query query()}
Luke Huang81cec002019-04-08 15:16:04 +0800159 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang6d72b2c2019-03-04 17:08:03 +0800160 *
Luke Huang81cec002019-04-08 15:16:04 +0800161 * @param error a {@link DnsException} object with additional
Luke Huang6d72b2c2019-03-04 17:08:03 +0800162 * detail regarding the failure
163 */
Luke Huang81cec002019-04-08 15:16:04 +0800164 void onError(@NonNull DnsException error);
Luke Huang6d72b2c2019-03-04 17:08:03 +0800165 }
166
167 /**
Luke Huang81cec002019-04-08 15:16:04 +0800168 * Class to represent DNS error
Luke Huang6d72b2c2019-03-04 17:08:03 +0800169 */
Luke Huang81cec002019-04-08 15:16:04 +0800170 public static class DnsException extends Exception {
171 /**
172 * DNS error code as one of the ERROR_* constants
173 */
174 @DnsError public final int code;
Luke Huang6d72b2c2019-03-04 17:08:03 +0800175
Aswin Sankar74cbfd52021-12-02 04:25:08 +0000176 public DnsException(@DnsError int code, @Nullable Throwable cause) {
Luke Huang81cec002019-04-08 15:16:04 +0800177 super(cause);
178 this.code = code;
Luke Huang6d72b2c2019-03-04 17:08:03 +0800179 }
180 }
181
182 /**
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800183 * Send a raw DNS query.
Luke Huang81cec002019-04-08 15:16:04 +0800184 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang6d23e742019-01-04 19:56:29 +0800185 *
Luke Huangfb5afab2019-03-28 13:56:31 +0800186 * @param network {@link Network} specifying which network to query on.
Luke Huang6d23e742019-01-04 19:56:29 +0800187 * {@code null} for query on default network.
Luke Huangfb5afab2019-03-28 13:56:31 +0800188 * @param query blob message to query
Luke Huang6d23e742019-01-04 19:56:29 +0800189 * @param flags flags as a combination of the FLAGS_* constants
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800190 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800191 * @param cancellationSignal used by the caller to signal if the query should be
192 * cancelled. May be {@code null}.
Luke Huang81cec002019-04-08 15:16:04 +0800193 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800194 * of the result of dns query.
Luke Huang6d23e742019-01-04 19:56:29 +0800195 */
Luke Huang81cec002019-04-08 15:16:04 +0800196 public void rawQuery(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags,
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800197 @NonNull @CallbackExecutor Executor executor,
198 @Nullable CancellationSignal cancellationSignal,
Luke Huang81cec002019-04-08 15:16:04 +0800199 @NonNull Callback<? super byte[]> callback) {
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800200 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
201 return;
202 }
Luke Huange17bb2d2019-03-26 15:50:10 +0800203 final Object lock = new Object();
Luke Huang6d72b2c2019-03-04 17:08:03 +0800204 final FileDescriptor queryfd;
205 try {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800206 queryfd = resNetworkSend((network != null)
207 ? network.getNetIdForResolv() : NETID_UNSET, query, query.length, flags);
Luke Huang6d72b2c2019-03-04 17:08:03 +0800208 } catch (ErrnoException e) {
Luke Huang81cec002019-04-08 15:16:04 +0800209 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang6d72b2c2019-03-04 17:08:03 +0800210 return;
211 }
212
Luke Huang293ffaa2019-03-29 18:01:11 +0800213 synchronized (lock) {
214 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
215 if (cancellationSignal == null) return;
216 addCancellationSignal(cancellationSignal, queryfd, lock);
217 }
Luke Huang6d23e742019-01-04 19:56:29 +0800218 }
219
220 /**
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800221 * Send a DNS query with the specified name, class and query type.
Luke Huang81cec002019-04-08 15:16:04 +0800222 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang6d23e742019-01-04 19:56:29 +0800223 *
Luke Huangfb5afab2019-03-28 13:56:31 +0800224 * @param network {@link Network} specifying which network to query on.
Luke Huang6d23e742019-01-04 19:56:29 +0800225 * {@code null} for query on default network.
Luke Huangfb5afab2019-03-28 13:56:31 +0800226 * @param domain domain name to query
Luke Huang6d23e742019-01-04 19:56:29 +0800227 * @param nsClass dns class as one of the CLASS_* constants
228 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
229 * @param flags flags as a combination of the FLAGS_* constants
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800230 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800231 * @param cancellationSignal used by the caller to signal if the query should be
232 * cancelled. May be {@code null}.
Luke Huang81cec002019-04-08 15:16:04 +0800233 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800234 * of the result of dns query.
Luke Huang6d23e742019-01-04 19:56:29 +0800235 */
Luke Huang81cec002019-04-08 15:16:04 +0800236 public void rawQuery(@Nullable Network network, @NonNull String domain,
Luke Huang6d72b2c2019-03-04 17:08:03 +0800237 @QueryClass int nsClass, @QueryType int nsType, @QueryFlag int flags,
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800238 @NonNull @CallbackExecutor Executor executor,
239 @Nullable CancellationSignal cancellationSignal,
Luke Huang81cec002019-04-08 15:16:04 +0800240 @NonNull Callback<? super byte[]> callback) {
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800241 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
242 return;
243 }
Luke Huange17bb2d2019-03-26 15:50:10 +0800244 final Object lock = new Object();
Luke Huang6d72b2c2019-03-04 17:08:03 +0800245 final FileDescriptor queryfd;
246 try {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800247 queryfd = resNetworkQuery((network != null)
248 ? network.getNetIdForResolv() : NETID_UNSET, domain, nsClass, nsType, flags);
Luke Huang6d72b2c2019-03-04 17:08:03 +0800249 } catch (ErrnoException e) {
Luke Huang81cec002019-04-08 15:16:04 +0800250 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang6d72b2c2019-03-04 17:08:03 +0800251 return;
252 }
Luke Huang293ffaa2019-03-29 18:01:11 +0800253 synchronized (lock) {
254 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
255 if (cancellationSignal == null) return;
256 addCancellationSignal(cancellationSignal, queryfd, lock);
257 }
Luke Huangfb5afab2019-03-28 13:56:31 +0800258 }
259
Luke Huang81cec002019-04-08 15:16:04 +0800260 private class InetAddressAnswerAccumulator implements Callback<byte[]> {
Luke Huangfb5afab2019-03-28 13:56:31 +0800261 private final List<InetAddress> mAllAnswers;
Luke Huang26c1cdb2019-04-17 22:27:56 +0800262 private final Network mNetwork;
Luke Huang81cec002019-04-08 15:16:04 +0800263 private int mRcode;
264 private DnsException mDnsException;
265 private final Callback<? super List<InetAddress>> mUserCallback;
Luke Huangfb5afab2019-03-28 13:56:31 +0800266 private final int mTargetAnswerCount;
267 private int mReceivedAnswerCount = 0;
268
Luke Huang26c1cdb2019-04-17 22:27:56 +0800269 InetAddressAnswerAccumulator(@NonNull Network network, int size,
Luke Huang81cec002019-04-08 15:16:04 +0800270 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800271 mNetwork = network;
Luke Huangfb5afab2019-03-28 13:56:31 +0800272 mTargetAnswerCount = size;
273 mAllAnswers = new ArrayList<>();
274 mUserCallback = callback;
275 }
276
Luke Huang81cec002019-04-08 15:16:04 +0800277 private boolean maybeReportError() {
278 if (mRcode != 0) {
279 mUserCallback.onAnswer(mAllAnswers, mRcode);
Luke Huangfb5afab2019-03-28 13:56:31 +0800280 return true;
281 }
Luke Huang81cec002019-04-08 15:16:04 +0800282 if (mDnsException != null) {
283 mUserCallback.onError(mDnsException);
Luke Huangfb5afab2019-03-28 13:56:31 +0800284 return true;
285 }
286 return false;
287 }
288
289 private void maybeReportAnswer() {
290 if (++mReceivedAnswerCount != mTargetAnswerCount) return;
Luke Huang81cec002019-04-08 15:16:04 +0800291 if (mAllAnswers.isEmpty() && maybeReportError()) return;
Luke Huang26c1cdb2019-04-17 22:27:56 +0800292 mUserCallback.onAnswer(rfc6724Sort(mNetwork, mAllAnswers), mRcode);
Luke Huangfb5afab2019-03-28 13:56:31 +0800293 }
294
295 @Override
Luke Huang81cec002019-04-08 15:16:04 +0800296 public void onAnswer(@NonNull byte[] answer, int rcode) {
297 // If at least one query succeeded, return an rcode of 0.
298 // Otherwise, arbitrarily return the first rcode received.
299 if (mReceivedAnswerCount == 0 || rcode == 0) {
300 mRcode = rcode;
301 }
302 try {
303 mAllAnswers.addAll(new DnsAddressAnswer(answer).getAddresses());
Luke Huangff0dc5c2020-06-16 08:13:28 +0800304 } catch (DnsPacket.ParseException e) {
305 // Convert the com.android.net.module.util.DnsPacket.ParseException to an
306 // android.net.ParseException. This is the type that was used in Q and is implied
307 // by the public documentation of ERROR_PARSE.
308 //
309 // DnsPacket cannot throw android.net.ParseException directly because it's @hide.
310 ParseException pe = new ParseException(e.reason, e.getCause());
311 pe.setStackTrace(e.getStackTrace());
312 mDnsException = new DnsException(ERROR_PARSE, pe);
Luke Huang81cec002019-04-08 15:16:04 +0800313 }
Luke Huangfb5afab2019-03-28 13:56:31 +0800314 maybeReportAnswer();
315 }
316
317 @Override
Luke Huang81cec002019-04-08 15:16:04 +0800318 public void onError(@NonNull DnsException error) {
319 mDnsException = error;
Luke Huangfb5afab2019-03-28 13:56:31 +0800320 maybeReportAnswer();
321 }
322 }
323
324 /**
Luke Huang81cec002019-04-08 15:16:04 +0800325 * Send a DNS query with the specified name on a network with both IPv4 and IPv6,
Luke Huang26c1cdb2019-04-17 22:27:56 +0800326 * get back a set of InetAddresses with rfc6724 sorting style asynchronously.
Luke Huang81cec002019-04-08 15:16:04 +0800327 *
328 * This method will examine the connection ability on given network, and query IPv4
329 * and IPv6 if connection is available.
330 *
331 * If at least one query succeeded with valid answer, rcode will be 0
332 *
333 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huangfb5afab2019-03-28 13:56:31 +0800334 *
335 * @param network {@link Network} specifying which network to query on.
336 * {@code null} for query on default network.
337 * @param domain domain name to query
338 * @param flags flags as a combination of the FLAGS_* constants
339 * @param executor The {@link Executor} that the callback should be executed on.
340 * @param cancellationSignal used by the caller to signal if the query should be
341 * cancelled. May be {@code null}.
Luke Huang81cec002019-04-08 15:16:04 +0800342 * @param callback a {@link Callback} which will be called to notify the
Luke Huangfb5afab2019-03-28 13:56:31 +0800343 * caller of the result of dns query.
344 */
345 public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
346 @NonNull @CallbackExecutor Executor executor,
347 @Nullable CancellationSignal cancellationSignal,
Luke Huang81cec002019-04-08 15:16:04 +0800348 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huangfb5afab2019-03-28 13:56:31 +0800349 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
350 return;
351 }
352 final Object lock = new Object();
Luke Huang26c1cdb2019-04-17 22:27:56 +0800353 final Network queryNetwork;
354 try {
Luke Huangbe164b22019-05-29 01:30:07 +0800355 queryNetwork = (network != null) ? network : getDnsNetwork();
Luke Huang26c1cdb2019-04-17 22:27:56 +0800356 } catch (ErrnoException e) {
357 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
358 return;
359 }
360 final boolean queryIpv6 = haveIpv6(queryNetwork);
361 final boolean queryIpv4 = haveIpv4(queryNetwork);
362
363 // This can only happen if queryIpv4 and queryIpv6 are both false.
364 // This almost certainly means that queryNetwork does not exist or no longer exists.
365 if (!queryIpv6 && !queryIpv4) {
366 executor.execute(() -> callback.onError(
367 new DnsException(ERROR_SYSTEM, new ErrnoException("resNetworkQuery", ENONET))));
368 return;
369 }
Luke Huangfb5afab2019-03-28 13:56:31 +0800370
371 final FileDescriptor v4fd;
372 final FileDescriptor v6fd;
373
374 int queryCount = 0;
375
376 if (queryIpv6) {
377 try {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800378 v6fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN,
379 TYPE_AAAA, flags);
Luke Huangfb5afab2019-03-28 13:56:31 +0800380 } catch (ErrnoException e) {
Luke Huang81cec002019-04-08 15:16:04 +0800381 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huangfb5afab2019-03-28 13:56:31 +0800382 return;
383 }
384 queryCount++;
385 } else v6fd = null;
386
Luke Huangfb5afab2019-03-28 13:56:31 +0800387 // Avoiding gateways drop packets if queries are sent too close together
388 try {
Luke Huang293ffaa2019-03-29 18:01:11 +0800389 Thread.sleep(SLEEP_TIME_MS);
Luke Huang81cec002019-04-08 15:16:04 +0800390 } catch (InterruptedException ex) {
391 Thread.currentThread().interrupt();
392 }
Luke Huangfb5afab2019-03-28 13:56:31 +0800393
394 if (queryIpv4) {
395 try {
Luke Huang26c1cdb2019-04-17 22:27:56 +0800396 v4fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, TYPE_A,
397 flags);
Luke Huangfb5afab2019-03-28 13:56:31 +0800398 } catch (ErrnoException e) {
399 if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
Luke Huang81cec002019-04-08 15:16:04 +0800400 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huangfb5afab2019-03-28 13:56:31 +0800401 return;
402 }
403 queryCount++;
404 } else v4fd = null;
405
406 final InetAddressAnswerAccumulator accumulator =
Luke Huang26c1cdb2019-04-17 22:27:56 +0800407 new InetAddressAnswerAccumulator(queryNetwork, queryCount, callback);
Luke Huangfb5afab2019-03-28 13:56:31 +0800408
Luke Huang293ffaa2019-03-29 18:01:11 +0800409 synchronized (lock) {
410 if (queryIpv6) {
411 registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
Luke Huangfb5afab2019-03-28 13:56:31 +0800412 }
Luke Huang293ffaa2019-03-29 18:01:11 +0800413 if (queryIpv4) {
414 registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
415 }
416 if (cancellationSignal == null) return;
417 cancellationSignal.setOnCancelListener(() -> {
418 synchronized (lock) {
419 if (queryIpv4) cancelQuery(v4fd);
420 if (queryIpv6) cancelQuery(v6fd);
421 }
422 });
423 }
Luke Huang6d23e742019-01-04 19:56:29 +0800424 }
425
Luke Huang81cec002019-04-08 15:16:04 +0800426 /**
427 * Send a DNS query with the specified name and query type, get back a set of
Luke Huang26c1cdb2019-04-17 22:27:56 +0800428 * InetAddresses with rfc6724 sorting style asynchronously.
Luke Huang81cec002019-04-08 15:16:04 +0800429 *
430 * The answer will be provided asynchronously through the provided {@link Callback}.
431 *
432 * @param network {@link Network} specifying which network to query on.
433 * {@code null} for query on default network.
434 * @param domain domain name to query
435 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
436 * @param flags flags as a combination of the FLAGS_* constants
437 * @param executor The {@link Executor} that the callback should be executed on.
438 * @param cancellationSignal used by the caller to signal if the query should be
439 * cancelled. May be {@code null}.
440 * @param callback a {@link Callback} which will be called to notify the caller
441 * of the result of dns query.
442 */
443 public void query(@Nullable Network network, @NonNull String domain,
444 @QueryType int nsType, @QueryFlag int flags,
445 @NonNull @CallbackExecutor Executor executor,
446 @Nullable CancellationSignal cancellationSignal,
447 @NonNull Callback<? super List<InetAddress>> callback) {
448 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
449 return;
450 }
451 final Object lock = new Object();
452 final FileDescriptor queryfd;
Luke Huang26c1cdb2019-04-17 22:27:56 +0800453 final Network queryNetwork;
Luke Huang81cec002019-04-08 15:16:04 +0800454 try {
Luke Huangbe164b22019-05-29 01:30:07 +0800455 queryNetwork = (network != null) ? network : getDnsNetwork();
Luke Huang26c1cdb2019-04-17 22:27:56 +0800456 queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType,
457 flags);
Luke Huang81cec002019-04-08 15:16:04 +0800458 } catch (ErrnoException e) {
459 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
460 return;
461 }
462 final InetAddressAnswerAccumulator accumulator =
Luke Huang26c1cdb2019-04-17 22:27:56 +0800463 new InetAddressAnswerAccumulator(queryNetwork, 1, callback);
Luke Huang81cec002019-04-08 15:16:04 +0800464 synchronized (lock) {
465 registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock);
466 if (cancellationSignal == null) return;
467 addCancellationSignal(cancellationSignal, queryfd, lock);
468 }
469 }
470
471 /**
472 * Class to retrieve DNS response
473 *
474 * @hide
475 */
476 public static final class DnsResponse {
477 public final @NonNull byte[] answerbuf;
478 public final int rcode;
479 public DnsResponse(@NonNull byte[] answerbuf, int rcode) {
480 this.answerbuf = answerbuf;
481 this.rcode = rcode;
482 }
483 }
484
485 private void registerFDListener(@NonNull Executor executor,
486 @NonNull FileDescriptor queryfd, @NonNull Callback<? super byte[]> answerCallback,
Luke Huange17bb2d2019-03-26 15:50:10 +0800487 @Nullable CancellationSignal cancellationSignal, @NonNull Object lock) {
Luke Huang6c8b7702019-06-11 14:25:45 +0800488 final MessageQueue mainThreadMessageQueue = Looper.getMainLooper().getQueue();
489 mainThreadMessageQueue.addOnFileDescriptorEventListener(
Luke Huang6d23e742019-01-04 19:56:29 +0800490 queryfd,
491 FD_EVENTS,
492 (fd, events) -> {
Luke Huang6c8b7702019-06-11 14:25:45 +0800493 // b/134310704
494 // Unregister fd event listener before resNetworkResult is called to prevent
495 // race condition caused by fd reused.
496 // For example when querying v4 and v6, it's possible that the first query ends
497 // and the fd is closed before the second request starts, which might return
498 // the same fd for the second request. By that time, the looper must have
499 // unregistered the fd, otherwise another event listener can't be registered.
500 mainThreadMessageQueue.removeOnFileDescriptorEventListener(fd);
501
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800502 executor.execute(() -> {
Luke Huang81cec002019-04-08 15:16:04 +0800503 DnsResponse resp = null;
504 ErrnoException exception = null;
Luke Huange17bb2d2019-03-26 15:50:10 +0800505 synchronized (lock) {
506 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
507 return;
508 }
Luke Huange17bb2d2019-03-26 15:50:10 +0800509 try {
Luke Huang81cec002019-04-08 15:16:04 +0800510 resp = resNetworkResult(fd); // Closes fd, marks it invalid.
Luke Huange17bb2d2019-03-26 15:50:10 +0800511 } catch (ErrnoException e) {
Ken Chenf6f39302021-04-02 01:44:27 +0800512 Log.w(TAG, "resNetworkResult:" + e.toString());
Luke Huang81cec002019-04-08 15:16:04 +0800513 exception = e;
Luke Huange17bb2d2019-03-26 15:50:10 +0800514 }
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800515 }
Luke Huang81cec002019-04-08 15:16:04 +0800516 if (exception != null) {
517 answerCallback.onError(new DnsException(ERROR_SYSTEM, exception));
518 return;
519 }
520 answerCallback.onAnswer(resp.answerbuf, resp.rcode);
Luke Huangdfd2e4d2019-03-07 19:01:26 +0800521 });
Luke Huang6c8b7702019-06-11 14:25:45 +0800522
523 // The file descriptor has already been unregistered, so it does not really
524 // matter what is returned here. In spirit 0 (meaning "unregister this FD")
525 // is still the closest to what the looper needs to do. When returning 0,
526 // Looper knows to ignore the fd if it has already been unregistered.
Luke Huang6d23e742019-01-04 19:56:29 +0800527 return 0;
528 });
529 }
530
Luke Huangfb5afab2019-03-28 13:56:31 +0800531 private void cancelQuery(@NonNull FileDescriptor queryfd) {
532 if (!queryfd.valid()) return;
533 Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd);
534 resNetworkCancel(queryfd); // Closes fd, marks it invalid.
535 }
536
Luke Huang293ffaa2019-03-29 18:01:11 +0800537 private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
Luke Huange17bb2d2019-03-26 15:50:10 +0800538 @NonNull FileDescriptor queryfd, @NonNull Object lock) {
Luke Huange17bb2d2019-03-26 15:50:10 +0800539 cancellationSignal.setOnCancelListener(() -> {
540 synchronized (lock) {
Luke Huangfb5afab2019-03-28 13:56:31 +0800541 cancelQuery(queryfd);
Luke Huange17bb2d2019-03-26 15:50:10 +0800542 }
543 });
Luke Huangb1cf5aa2019-03-08 14:48:59 +0800544 }
545
Luke Huang6d72b2c2019-03-04 17:08:03 +0800546 private static class DnsAddressAnswer extends DnsPacket {
Luke Huang6d23e742019-01-04 19:56:29 +0800547 private static final String TAG = "DnsResolver.DnsAddressAnswer";
548 private static final boolean DBG = false;
549
550 private final int mQueryType;
551
552 DnsAddressAnswer(@NonNull byte[] data) throws ParseException {
553 super(data);
Junyu Lai4c93f832022-09-15 07:34:51 +0000554 if ((mHeader.getFlags() & (1 << 15)) == 0) {
Luke Huang6d23e742019-01-04 19:56:29 +0800555 throw new ParseException("Not an answer packet");
556 }
Luke Huang12448352019-01-23 21:53:13 +0800557 if (mHeader.getRecordCount(QDSECTION) == 0) {
Luke Huang6d23e742019-01-04 19:56:29 +0800558 throw new ParseException("No question found");
559 }
Luke Huang12448352019-01-23 21:53:13 +0800560 // Expect only one question in question section.
561 mQueryType = mRecords[QDSECTION].get(0).nsType;
Luke Huang6d23e742019-01-04 19:56:29 +0800562 }
563
564 public @NonNull List<InetAddress> getAddresses() {
565 final List<InetAddress> results = new ArrayList<InetAddress>();
Luke Huang6d72b2c2019-03-04 17:08:03 +0800566 if (mHeader.getRecordCount(ANSECTION) == 0) return results;
567
Luke Huang12448352019-01-23 21:53:13 +0800568 for (final DnsRecord ansSec : mRecords[ANSECTION]) {
Luke Huang6d23e742019-01-04 19:56:29 +0800569 // Only support A and AAAA, also ignore answers if query type != answer type.
570 int nsType = ansSec.nsType;
571 if (nsType != mQueryType || (nsType != TYPE_A && nsType != TYPE_AAAA)) {
572 continue;
573 }
574 try {
575 results.add(InetAddress.getByAddress(ansSec.getRR()));
576 } catch (UnknownHostException e) {
577 if (DBG) {
578 Log.w(TAG, "rr to address fail");
579 }
580 }
581 }
582 return results;
583 }
584 }
585
Luke Huang6d23e742019-01-04 19:56:29 +0800586}