blob: 4b2b4c35b2929e94b2c78bfabe34af488c5accf0 [file] [log] [blame]
Luke Huang00b15f32019-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 Huang7466ac8d2019-05-23 06:14:28 -070019import static android.net.NetworkUtils.getDnsNetId;
Luke Huangc09f2d62019-03-08 14:48:59 +080020import static android.net.NetworkUtils.resNetworkCancel;
Luke Huang00b15f32019-01-04 19:56:29 +080021import static android.net.NetworkUtils.resNetworkQuery;
22import static android.net.NetworkUtils.resNetworkResult;
23import static android.net.NetworkUtils.resNetworkSend;
Luke Huang7466ac8d2019-05-23 06:14:28 -070024import static android.net.util.DnsUtils.haveIpv4;
25import static android.net.util.DnsUtils.haveIpv6;
26import static android.net.util.DnsUtils.rfc6724Sort;
Luke Huang00b15f32019-01-04 19:56:29 +080027import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
28import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
Luke Huang7466ac8d2019-05-23 06:14:28 -070029import static android.system.OsConstants.ENONET;
Luke Huang00b15f32019-01-04 19:56:29 +080030
Luke Huange4c79132019-03-07 19:01:26 +080031import android.annotation.CallbackExecutor;
Luke Huang00b15f32019-01-04 19:56:29 +080032import android.annotation.IntDef;
33import android.annotation.NonNull;
34import android.annotation.Nullable;
Luke Huangc09f2d62019-03-08 14:48:59 +080035import android.os.CancellationSignal;
Luke Huange4c79132019-03-07 19:01:26 +080036import android.os.Looper;
Luke Huang00b15f32019-01-04 19:56:29 +080037import android.system.ErrnoException;
38import android.util.Log;
39
40import java.io.FileDescriptor;
41import java.lang.annotation.Retention;
42import java.lang.annotation.RetentionPolicy;
43import java.net.InetAddress;
44import java.net.UnknownHostException;
45import java.util.ArrayList;
46import java.util.List;
Luke Huange4c79132019-03-07 19:01:26 +080047import java.util.concurrent.Executor;
Luke Huang00b15f32019-01-04 19:56:29 +080048
49/**
50 * Dns resolver class for asynchronous dns querying
51 *
Luke Huang33bfef32019-01-23 21:53:13 +080052 * Note that if a client sends a query with more than 1 record in the question section but
53 * the remote dns server does not support this, it may not respond at all, leading to a timeout.
54 *
Luke Huang00b15f32019-01-04 19:56:29 +080055 */
56public final class DnsResolver {
57 private static final String TAG = "DnsResolver";
58 private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
59 private static final int MAXPACKET = 8 * 1024;
Luke Huang5bf7aa22019-03-29 18:01:11 +080060 private static final int SLEEP_TIME_MS = 2;
Luke Huang00b15f32019-01-04 19:56:29 +080061
62 @IntDef(prefix = { "CLASS_" }, value = {
63 CLASS_IN
64 })
65 @Retention(RetentionPolicy.SOURCE)
66 @interface QueryClass {}
67 public static final int CLASS_IN = 1;
68
69 @IntDef(prefix = { "TYPE_" }, value = {
70 TYPE_A,
71 TYPE_AAAA
72 })
73 @Retention(RetentionPolicy.SOURCE)
74 @interface QueryType {}
75 public static final int TYPE_A = 1;
76 public static final int TYPE_AAAA = 28;
77
78 @IntDef(prefix = { "FLAG_" }, value = {
79 FLAG_EMPTY,
80 FLAG_NO_RETRY,
81 FLAG_NO_CACHE_STORE,
82 FLAG_NO_CACHE_LOOKUP
83 })
84 @Retention(RetentionPolicy.SOURCE)
85 @interface QueryFlag {}
86 public static final int FLAG_EMPTY = 0;
87 public static final int FLAG_NO_RETRY = 1 << 0;
88 public static final int FLAG_NO_CACHE_STORE = 1 << 1;
89 public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2;
90
Luke Huangec214472019-04-11 17:54:41 -070091 @IntDef(prefix = { "ERROR_" }, value = {
92 ERROR_PARSE,
93 ERROR_SYSTEM
94 })
95 @Retention(RetentionPolicy.SOURCE)
96 @interface DnsError {}
97 /**
98 * Indicates that there was an error parsing the response the query.
99 * The cause of this error is available via getCause() and is a ParseException.
100 */
101 public static final int ERROR_PARSE = 0;
102 /**
103 * Indicates that there was an error sending the query.
104 * The cause of this error is available via getCause() and is an ErrnoException.
105 */
106 public static final int ERROR_SYSTEM = 1;
107
Luke Huang00b15f32019-01-04 19:56:29 +0800108 private static final int NETID_UNSET = 0;
109
110 private static final DnsResolver sInstance = new DnsResolver();
111
112 /**
Luke Huang00b15f32019-01-04 19:56:29 +0800113 * Get instance for DnsResolver
114 */
Luke Huang304491d2019-03-04 17:08:03 +0800115 public static @NonNull DnsResolver getInstance() {
Luke Huang00b15f32019-01-04 19:56:29 +0800116 return sInstance;
117 }
118
119 private DnsResolver() {}
120
121 /**
Luke Huangec214472019-04-11 17:54:41 -0700122 * Base interface for answer callbacks
Luke Huang304491d2019-03-04 17:08:03 +0800123 *
Luke Huangec214472019-04-11 17:54:41 -0700124 * @param <T> The type of the answer
Luke Huang304491d2019-03-04 17:08:03 +0800125 */
Luke Huangec214472019-04-11 17:54:41 -0700126 public interface Callback<T> {
Luke Huang304491d2019-03-04 17:08:03 +0800127 /**
128 * Success response to
Luke Huangec214472019-04-11 17:54:41 -0700129 * {@link android.net.DnsResolver#query query()} or
130 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang304491d2019-03-04 17:08:03 +0800131 *
132 * Invoked when the answer to a query was successfully parsed.
133 *
Luke Huangec214472019-04-11 17:54:41 -0700134 * @param answer <T> answer to the query.
135 * @param rcode The response code in the DNS response.
Luke Huang304491d2019-03-04 17:08:03 +0800136 *
137 * {@see android.net.DnsResolver#query query()}
138 */
Luke Huangec214472019-04-11 17:54:41 -0700139 void onAnswer(@NonNull T answer, int rcode);
Luke Huang304491d2019-03-04 17:08:03 +0800140 /**
141 * Error response to
Luke Huangec214472019-04-11 17:54:41 -0700142 * {@link android.net.DnsResolver#query query()} or
143 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang304491d2019-03-04 17:08:03 +0800144 *
145 * Invoked when there is no valid answer to
146 * {@link android.net.DnsResolver#query query()}
Luke Huangec214472019-04-11 17:54:41 -0700147 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang304491d2019-03-04 17:08:03 +0800148 *
Luke Huangec214472019-04-11 17:54:41 -0700149 * @param error a {@link DnsException} object with additional
Luke Huang304491d2019-03-04 17:08:03 +0800150 * detail regarding the failure
151 */
Luke Huangec214472019-04-11 17:54:41 -0700152 void onError(@NonNull DnsException error);
Luke Huang304491d2019-03-04 17:08:03 +0800153 }
154
155 /**
Luke Huangec214472019-04-11 17:54:41 -0700156 * Class to represent DNS error
Luke Huang304491d2019-03-04 17:08:03 +0800157 */
Luke Huangec214472019-04-11 17:54:41 -0700158 public static class DnsException extends Exception {
159 /**
160 * DNS error code as one of the ERROR_* constants
161 */
162 @DnsError public final int code;
Luke Huang304491d2019-03-04 17:08:03 +0800163
Luke Huangec214472019-04-11 17:54:41 -0700164 DnsException(@DnsError int code, @Nullable Throwable cause) {
165 super(cause);
166 this.code = code;
Luke Huang304491d2019-03-04 17:08:03 +0800167 }
168 }
169
170 /**
Luke Huange4c79132019-03-07 19:01:26 +0800171 * Send a raw DNS query.
Luke Huangec214472019-04-11 17:54:41 -0700172 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang00b15f32019-01-04 19:56:29 +0800173 *
Luke Huange9b2bba2019-03-28 13:56:31 +0800174 * @param network {@link Network} specifying which network to query on.
Luke Huang00b15f32019-01-04 19:56:29 +0800175 * {@code null} for query on default network.
Luke Huange9b2bba2019-03-28 13:56:31 +0800176 * @param query blob message to query
Luke Huang00b15f32019-01-04 19:56:29 +0800177 * @param flags flags as a combination of the FLAGS_* constants
Luke Huange4c79132019-03-07 19:01:26 +0800178 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangc09f2d62019-03-08 14:48:59 +0800179 * @param cancellationSignal used by the caller to signal if the query should be
180 * cancelled. May be {@code null}.
Luke Huangec214472019-04-11 17:54:41 -0700181 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangc09f2d62019-03-08 14:48:59 +0800182 * of the result of dns query.
Luke Huang00b15f32019-01-04 19:56:29 +0800183 */
Luke Huangec214472019-04-11 17:54:41 -0700184 public void rawQuery(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags,
Luke Huangc09f2d62019-03-08 14:48:59 +0800185 @NonNull @CallbackExecutor Executor executor,
186 @Nullable CancellationSignal cancellationSignal,
Luke Huangec214472019-04-11 17:54:41 -0700187 @NonNull Callback<? super byte[]> callback) {
Luke Huangc09f2d62019-03-08 14:48:59 +0800188 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
189 return;
190 }
Luke Huang5386f492019-03-26 15:50:10 +0800191 final Object lock = new Object();
Luke Huang304491d2019-03-04 17:08:03 +0800192 final FileDescriptor queryfd;
193 try {
Luke Huang7466ac8d2019-05-23 06:14:28 -0700194 queryfd = resNetworkSend((network != null)
195 ? network.getNetIdForResolv() : NETID_UNSET, query, query.length, flags);
Luke Huang304491d2019-03-04 17:08:03 +0800196 } catch (ErrnoException e) {
Luke Huangec214472019-04-11 17:54:41 -0700197 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang304491d2019-03-04 17:08:03 +0800198 return;
199 }
200
Luke Huang5bf7aa22019-03-29 18:01:11 +0800201 synchronized (lock) {
202 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
203 if (cancellationSignal == null) return;
204 addCancellationSignal(cancellationSignal, queryfd, lock);
205 }
Luke Huang00b15f32019-01-04 19:56:29 +0800206 }
207
208 /**
Luke Huange4c79132019-03-07 19:01:26 +0800209 * Send a DNS query with the specified name, class and query type.
Luke Huangec214472019-04-11 17:54:41 -0700210 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang00b15f32019-01-04 19:56:29 +0800211 *
Luke Huange9b2bba2019-03-28 13:56:31 +0800212 * @param network {@link Network} specifying which network to query on.
Luke Huang00b15f32019-01-04 19:56:29 +0800213 * {@code null} for query on default network.
Luke Huange9b2bba2019-03-28 13:56:31 +0800214 * @param domain domain name to query
Luke Huang00b15f32019-01-04 19:56:29 +0800215 * @param nsClass dns class as one of the CLASS_* constants
216 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
217 * @param flags flags as a combination of the FLAGS_* constants
Luke Huange4c79132019-03-07 19:01:26 +0800218 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangc09f2d62019-03-08 14:48:59 +0800219 * @param cancellationSignal used by the caller to signal if the query should be
220 * cancelled. May be {@code null}.
Luke Huangec214472019-04-11 17:54:41 -0700221 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangc09f2d62019-03-08 14:48:59 +0800222 * of the result of dns query.
Luke Huang00b15f32019-01-04 19:56:29 +0800223 */
Luke Huangec214472019-04-11 17:54:41 -0700224 public void rawQuery(@Nullable Network network, @NonNull String domain,
Luke Huang304491d2019-03-04 17:08:03 +0800225 @QueryClass int nsClass, @QueryType int nsType, @QueryFlag int flags,
Luke Huangc09f2d62019-03-08 14:48:59 +0800226 @NonNull @CallbackExecutor Executor executor,
227 @Nullable CancellationSignal cancellationSignal,
Luke Huangec214472019-04-11 17:54:41 -0700228 @NonNull Callback<? super byte[]> callback) {
Luke Huangc09f2d62019-03-08 14:48:59 +0800229 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
230 return;
231 }
Luke Huang5386f492019-03-26 15:50:10 +0800232 final Object lock = new Object();
Luke Huang304491d2019-03-04 17:08:03 +0800233 final FileDescriptor queryfd;
234 try {
Luke Huang7466ac8d2019-05-23 06:14:28 -0700235 queryfd = resNetworkQuery((network != null)
236 ? network.getNetIdForResolv() : NETID_UNSET, domain, nsClass, nsType, flags);
Luke Huang304491d2019-03-04 17:08:03 +0800237 } catch (ErrnoException e) {
Luke Huangec214472019-04-11 17:54:41 -0700238 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang304491d2019-03-04 17:08:03 +0800239 return;
240 }
Luke Huang5bf7aa22019-03-29 18:01:11 +0800241 synchronized (lock) {
242 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
243 if (cancellationSignal == null) return;
244 addCancellationSignal(cancellationSignal, queryfd, lock);
245 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800246 }
247
Luke Huangec214472019-04-11 17:54:41 -0700248 private class InetAddressAnswerAccumulator implements Callback<byte[]> {
Luke Huange9b2bba2019-03-28 13:56:31 +0800249 private final List<InetAddress> mAllAnswers;
Luke Huang7466ac8d2019-05-23 06:14:28 -0700250 private final Network mNetwork;
Luke Huangec214472019-04-11 17:54:41 -0700251 private int mRcode;
252 private DnsException mDnsException;
253 private final Callback<? super List<InetAddress>> mUserCallback;
Luke Huange9b2bba2019-03-28 13:56:31 +0800254 private final int mTargetAnswerCount;
255 private int mReceivedAnswerCount = 0;
256
Luke Huang7466ac8d2019-05-23 06:14:28 -0700257 InetAddressAnswerAccumulator(@NonNull Network network, int size,
Luke Huangec214472019-04-11 17:54:41 -0700258 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huang7466ac8d2019-05-23 06:14:28 -0700259 mNetwork = network;
Luke Huange9b2bba2019-03-28 13:56:31 +0800260 mTargetAnswerCount = size;
261 mAllAnswers = new ArrayList<>();
262 mUserCallback = callback;
263 }
264
Luke Huangec214472019-04-11 17:54:41 -0700265 private boolean maybeReportError() {
266 if (mRcode != 0) {
267 mUserCallback.onAnswer(mAllAnswers, mRcode);
Luke Huange9b2bba2019-03-28 13:56:31 +0800268 return true;
269 }
Luke Huangec214472019-04-11 17:54:41 -0700270 if (mDnsException != null) {
271 mUserCallback.onError(mDnsException);
Luke Huange9b2bba2019-03-28 13:56:31 +0800272 return true;
273 }
274 return false;
275 }
276
277 private void maybeReportAnswer() {
278 if (++mReceivedAnswerCount != mTargetAnswerCount) return;
Luke Huangec214472019-04-11 17:54:41 -0700279 if (mAllAnswers.isEmpty() && maybeReportError()) return;
Luke Huang7466ac8d2019-05-23 06:14:28 -0700280 mUserCallback.onAnswer(rfc6724Sort(mNetwork, mAllAnswers), mRcode);
Luke Huange9b2bba2019-03-28 13:56:31 +0800281 }
282
283 @Override
Luke Huangec214472019-04-11 17:54:41 -0700284 public void onAnswer(@NonNull byte[] answer, int rcode) {
285 // If at least one query succeeded, return an rcode of 0.
286 // Otherwise, arbitrarily return the first rcode received.
287 if (mReceivedAnswerCount == 0 || rcode == 0) {
288 mRcode = rcode;
289 }
290 try {
291 mAllAnswers.addAll(new DnsAddressAnswer(answer).getAddresses());
292 } catch (ParseException e) {
293 mDnsException = new DnsException(ERROR_PARSE, e);
294 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800295 maybeReportAnswer();
296 }
297
298 @Override
Luke Huangec214472019-04-11 17:54:41 -0700299 public void onError(@NonNull DnsException error) {
300 mDnsException = error;
Luke Huange9b2bba2019-03-28 13:56:31 +0800301 maybeReportAnswer();
302 }
303 }
304
305 /**
Luke Huangec214472019-04-11 17:54:41 -0700306 * Send a DNS query with the specified name on a network with both IPv4 and IPv6,
Luke Huang7466ac8d2019-05-23 06:14:28 -0700307 * get back a set of InetAddresses with rfc6724 sorting style asynchronously.
Luke Huangec214472019-04-11 17:54:41 -0700308 *
309 * This method will examine the connection ability on given network, and query IPv4
310 * and IPv6 if connection is available.
311 *
312 * If at least one query succeeded with valid answer, rcode will be 0
313 *
314 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huange9b2bba2019-03-28 13:56:31 +0800315 *
316 * @param network {@link Network} specifying which network to query on.
317 * {@code null} for query on default network.
318 * @param domain domain name to query
319 * @param flags flags as a combination of the FLAGS_* constants
320 * @param executor The {@link Executor} that the callback should be executed on.
321 * @param cancellationSignal used by the caller to signal if the query should be
322 * cancelled. May be {@code null}.
Luke Huangec214472019-04-11 17:54:41 -0700323 * @param callback a {@link Callback} which will be called to notify the
Luke Huange9b2bba2019-03-28 13:56:31 +0800324 * caller of the result of dns query.
325 */
326 public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
327 @NonNull @CallbackExecutor Executor executor,
328 @Nullable CancellationSignal cancellationSignal,
Luke Huangec214472019-04-11 17:54:41 -0700329 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huange9b2bba2019-03-28 13:56:31 +0800330 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
331 return;
332 }
333 final Object lock = new Object();
Luke Huang7466ac8d2019-05-23 06:14:28 -0700334 final Network queryNetwork;
335 try {
336 queryNetwork = (network != null) ? network : new Network(getDnsNetId());
337 } catch (ErrnoException e) {
338 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
339 return;
340 }
341 final boolean queryIpv6 = haveIpv6(queryNetwork);
342 final boolean queryIpv4 = haveIpv4(queryNetwork);
343
344 // This can only happen if queryIpv4 and queryIpv6 are both false.
345 // This almost certainly means that queryNetwork does not exist or no longer exists.
346 if (!queryIpv6 && !queryIpv4) {
347 executor.execute(() -> callback.onError(
348 new DnsException(ERROR_SYSTEM, new ErrnoException("resNetworkQuery", ENONET))));
349 return;
350 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800351
352 final FileDescriptor v4fd;
353 final FileDescriptor v6fd;
354
355 int queryCount = 0;
356
357 if (queryIpv6) {
358 try {
Luke Huang7466ac8d2019-05-23 06:14:28 -0700359 v6fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN,
360 TYPE_AAAA, flags);
Luke Huange9b2bba2019-03-28 13:56:31 +0800361 } catch (ErrnoException e) {
Luke Huangec214472019-04-11 17:54:41 -0700362 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huange9b2bba2019-03-28 13:56:31 +0800363 return;
364 }
365 queryCount++;
366 } else v6fd = null;
367
Luke Huange9b2bba2019-03-28 13:56:31 +0800368 // Avoiding gateways drop packets if queries are sent too close together
369 try {
Luke Huang5bf7aa22019-03-29 18:01:11 +0800370 Thread.sleep(SLEEP_TIME_MS);
Luke Huangec214472019-04-11 17:54:41 -0700371 } catch (InterruptedException ex) {
372 Thread.currentThread().interrupt();
373 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800374
375 if (queryIpv4) {
376 try {
Luke Huang7466ac8d2019-05-23 06:14:28 -0700377 v4fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, TYPE_A,
378 flags);
Luke Huange9b2bba2019-03-28 13:56:31 +0800379 } catch (ErrnoException e) {
380 if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
Luke Huangec214472019-04-11 17:54:41 -0700381 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huange9b2bba2019-03-28 13:56:31 +0800382 return;
383 }
384 queryCount++;
385 } else v4fd = null;
386
387 final InetAddressAnswerAccumulator accumulator =
Luke Huang7466ac8d2019-05-23 06:14:28 -0700388 new InetAddressAnswerAccumulator(queryNetwork, queryCount, callback);
Luke Huange9b2bba2019-03-28 13:56:31 +0800389
Luke Huang5bf7aa22019-03-29 18:01:11 +0800390 synchronized (lock) {
391 if (queryIpv6) {
392 registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
Luke Huange9b2bba2019-03-28 13:56:31 +0800393 }
Luke Huang5bf7aa22019-03-29 18:01:11 +0800394 if (queryIpv4) {
395 registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
396 }
397 if (cancellationSignal == null) return;
398 cancellationSignal.setOnCancelListener(() -> {
399 synchronized (lock) {
400 if (queryIpv4) cancelQuery(v4fd);
401 if (queryIpv6) cancelQuery(v6fd);
402 }
403 });
404 }
Luke Huang00b15f32019-01-04 19:56:29 +0800405 }
406
Luke Huangec214472019-04-11 17:54:41 -0700407 /**
408 * Send a DNS query with the specified name and query type, get back a set of
Luke Huang7466ac8d2019-05-23 06:14:28 -0700409 * InetAddresses with rfc6724 sorting style asynchronously.
Luke Huangec214472019-04-11 17:54:41 -0700410 *
411 * The answer will be provided asynchronously through the provided {@link Callback}.
412 *
413 * @param network {@link Network} specifying which network to query on.
414 * {@code null} for query on default network.
415 * @param domain domain name to query
416 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
417 * @param flags flags as a combination of the FLAGS_* constants
418 * @param executor The {@link Executor} that the callback should be executed on.
419 * @param cancellationSignal used by the caller to signal if the query should be
420 * cancelled. May be {@code null}.
421 * @param callback a {@link Callback} which will be called to notify the caller
422 * of the result of dns query.
423 */
424 public void query(@Nullable Network network, @NonNull String domain,
425 @QueryType int nsType, @QueryFlag int flags,
426 @NonNull @CallbackExecutor Executor executor,
427 @Nullable CancellationSignal cancellationSignal,
428 @NonNull Callback<? super List<InetAddress>> callback) {
429 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
430 return;
431 }
432 final Object lock = new Object();
433 final FileDescriptor queryfd;
Luke Huang7466ac8d2019-05-23 06:14:28 -0700434 final Network queryNetwork;
Luke Huangec214472019-04-11 17:54:41 -0700435 try {
Luke Huang7466ac8d2019-05-23 06:14:28 -0700436 queryNetwork = (network != null) ? network : new Network(getDnsNetId());
437 queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType,
438 flags);
Luke Huangec214472019-04-11 17:54:41 -0700439 } catch (ErrnoException e) {
440 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
441 return;
442 }
443 final InetAddressAnswerAccumulator accumulator =
Luke Huang7466ac8d2019-05-23 06:14:28 -0700444 new InetAddressAnswerAccumulator(queryNetwork, 1, callback);
Luke Huangec214472019-04-11 17:54:41 -0700445 synchronized (lock) {
446 registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock);
447 if (cancellationSignal == null) return;
448 addCancellationSignal(cancellationSignal, queryfd, lock);
449 }
450 }
451
452 /**
453 * Class to retrieve DNS response
454 *
455 * @hide
456 */
457 public static final class DnsResponse {
458 public final @NonNull byte[] answerbuf;
459 public final int rcode;
460 public DnsResponse(@NonNull byte[] answerbuf, int rcode) {
461 this.answerbuf = answerbuf;
462 this.rcode = rcode;
463 }
464 }
465
466 private void registerFDListener(@NonNull Executor executor,
467 @NonNull FileDescriptor queryfd, @NonNull Callback<? super byte[]> answerCallback,
Luke Huang5386f492019-03-26 15:50:10 +0800468 @Nullable CancellationSignal cancellationSignal, @NonNull Object lock) {
Luke Huange4c79132019-03-07 19:01:26 +0800469 Looper.getMainLooper().getQueue().addOnFileDescriptorEventListener(
Luke Huang00b15f32019-01-04 19:56:29 +0800470 queryfd,
471 FD_EVENTS,
472 (fd, events) -> {
Luke Huange4c79132019-03-07 19:01:26 +0800473 executor.execute(() -> {
Luke Huangec214472019-04-11 17:54:41 -0700474 DnsResponse resp = null;
475 ErrnoException exception = null;
Luke Huang5386f492019-03-26 15:50:10 +0800476 synchronized (lock) {
477 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
478 return;
479 }
Luke Huang5386f492019-03-26 15:50:10 +0800480 try {
Luke Huangec214472019-04-11 17:54:41 -0700481 resp = resNetworkResult(fd); // Closes fd, marks it invalid.
Luke Huang5386f492019-03-26 15:50:10 +0800482 } catch (ErrnoException e) {
483 Log.e(TAG, "resNetworkResult:" + e.toString());
Luke Huangec214472019-04-11 17:54:41 -0700484 exception = e;
Luke Huang5386f492019-03-26 15:50:10 +0800485 }
Luke Huange4c79132019-03-07 19:01:26 +0800486 }
Luke Huangec214472019-04-11 17:54:41 -0700487 if (exception != null) {
488 answerCallback.onError(new DnsException(ERROR_SYSTEM, exception));
489 return;
490 }
491 answerCallback.onAnswer(resp.answerbuf, resp.rcode);
Luke Huange4c79132019-03-07 19:01:26 +0800492 });
Luke Huang00b15f32019-01-04 19:56:29 +0800493 // Unregister this fd listener
494 return 0;
495 });
496 }
497
Luke Huange9b2bba2019-03-28 13:56:31 +0800498 private void cancelQuery(@NonNull FileDescriptor queryfd) {
499 if (!queryfd.valid()) return;
500 Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd);
501 resNetworkCancel(queryfd); // Closes fd, marks it invalid.
502 }
503
Luke Huang5bf7aa22019-03-29 18:01:11 +0800504 private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
Luke Huang5386f492019-03-26 15:50:10 +0800505 @NonNull FileDescriptor queryfd, @NonNull Object lock) {
Luke Huang5386f492019-03-26 15:50:10 +0800506 cancellationSignal.setOnCancelListener(() -> {
507 synchronized (lock) {
Luke Huange9b2bba2019-03-28 13:56:31 +0800508 cancelQuery(queryfd);
Luke Huang5386f492019-03-26 15:50:10 +0800509 }
510 });
Luke Huangc09f2d62019-03-08 14:48:59 +0800511 }
512
Luke Huang304491d2019-03-04 17:08:03 +0800513 private static class DnsAddressAnswer extends DnsPacket {
Luke Huang00b15f32019-01-04 19:56:29 +0800514 private static final String TAG = "DnsResolver.DnsAddressAnswer";
515 private static final boolean DBG = false;
516
517 private final int mQueryType;
518
519 DnsAddressAnswer(@NonNull byte[] data) throws ParseException {
520 super(data);
521 if ((mHeader.flags & (1 << 15)) == 0) {
522 throw new ParseException("Not an answer packet");
523 }
Luke Huang33bfef32019-01-23 21:53:13 +0800524 if (mHeader.getRecordCount(QDSECTION) == 0) {
Luke Huang00b15f32019-01-04 19:56:29 +0800525 throw new ParseException("No question found");
526 }
Luke Huang33bfef32019-01-23 21:53:13 +0800527 // Expect only one question in question section.
528 mQueryType = mRecords[QDSECTION].get(0).nsType;
Luke Huang00b15f32019-01-04 19:56:29 +0800529 }
530
531 public @NonNull List<InetAddress> getAddresses() {
532 final List<InetAddress> results = new ArrayList<InetAddress>();
Luke Huang304491d2019-03-04 17:08:03 +0800533 if (mHeader.getRecordCount(ANSECTION) == 0) return results;
534
Luke Huang33bfef32019-01-23 21:53:13 +0800535 for (final DnsRecord ansSec : mRecords[ANSECTION]) {
Luke Huang00b15f32019-01-04 19:56:29 +0800536 // Only support A and AAAA, also ignore answers if query type != answer type.
537 int nsType = ansSec.nsType;
538 if (nsType != mQueryType || (nsType != TYPE_A && nsType != TYPE_AAAA)) {
539 continue;
540 }
541 try {
542 results.add(InetAddress.getByAddress(ansSec.getRR()));
543 } catch (UnknownHostException e) {
544 if (DBG) {
545 Log.w(TAG, "rr to address fail");
546 }
547 }
548 }
549 return results;
550 }
551 }
552
Luke Huang00b15f32019-01-04 19:56:29 +0800553}