blob: 0b1a84534e386930984821faedc8b1dc2ff0ad7e [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 Huangc211b272019-05-29 01:30:07 +080019import static android.net.NetworkUtils.getDnsNetwork;
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 Huang247d83a2019-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 Huang00b15f32019-01-04 19:56:29 +080027import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
28import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
Luke Huang247d83a2019-04-17 22:27:56 +080029import 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 Huang5b311262019-06-11 14:25:45 +080037import android.os.MessageQueue;
Luke Huang00b15f32019-01-04 19:56:29 +080038import android.system.ErrnoException;
39import android.util.Log;
40
41import java.io.FileDescriptor;
42import java.lang.annotation.Retention;
43import java.lang.annotation.RetentionPolicy;
44import java.net.InetAddress;
45import java.net.UnknownHostException;
46import java.util.ArrayList;
47import java.util.List;
Luke Huange4c79132019-03-07 19:01:26 +080048import java.util.concurrent.Executor;
Luke Huang00b15f32019-01-04 19:56:29 +080049
50/**
51 * Dns resolver class for asynchronous dns querying
52 *
Luke Huang33bfef32019-01-23 21:53:13 +080053 * Note that if a client sends a query with more than 1 record in the question section but
54 * the remote dns server does not support this, it may not respond at all, leading to a timeout.
55 *
Luke Huang00b15f32019-01-04 19:56:29 +080056 */
57public final class DnsResolver {
58 private static final String TAG = "DnsResolver";
59 private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
60 private static final int MAXPACKET = 8 * 1024;
Luke Huang5bf7aa22019-03-29 18:01:11 +080061 private static final int SLEEP_TIME_MS = 2;
Luke Huang00b15f32019-01-04 19:56:29 +080062
63 @IntDef(prefix = { "CLASS_" }, value = {
64 CLASS_IN
65 })
66 @Retention(RetentionPolicy.SOURCE)
67 @interface QueryClass {}
68 public static final int CLASS_IN = 1;
69
70 @IntDef(prefix = { "TYPE_" }, value = {
71 TYPE_A,
72 TYPE_AAAA
73 })
74 @Retention(RetentionPolicy.SOURCE)
75 @interface QueryType {}
76 public static final int TYPE_A = 1;
77 public static final int TYPE_AAAA = 28;
78
79 @IntDef(prefix = { "FLAG_" }, value = {
80 FLAG_EMPTY,
81 FLAG_NO_RETRY,
82 FLAG_NO_CACHE_STORE,
83 FLAG_NO_CACHE_LOOKUP
84 })
85 @Retention(RetentionPolicy.SOURCE)
86 @interface QueryFlag {}
87 public static final int FLAG_EMPTY = 0;
88 public static final int FLAG_NO_RETRY = 1 << 0;
89 public static final int FLAG_NO_CACHE_STORE = 1 << 1;
90 public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2;
91
Luke Huang4219ab62019-04-08 15:16:04 +080092 @IntDef(prefix = { "ERROR_" }, value = {
93 ERROR_PARSE,
94 ERROR_SYSTEM
95 })
96 @Retention(RetentionPolicy.SOURCE)
97 @interface DnsError {}
98 /**
99 * Indicates that there was an error parsing the response the query.
100 * The cause of this error is available via getCause() and is a ParseException.
101 */
102 public static final int ERROR_PARSE = 0;
103 /**
104 * Indicates that there was an error sending the query.
105 * The cause of this error is available via getCause() and is an ErrnoException.
106 */
107 public static final int ERROR_SYSTEM = 1;
108
Luke Huang00b15f32019-01-04 19:56:29 +0800109 private static final int NETID_UNSET = 0;
110
111 private static final DnsResolver sInstance = new DnsResolver();
112
113 /**
Luke Huang00b15f32019-01-04 19:56:29 +0800114 * Get instance for DnsResolver
115 */
Luke Huang304491d2019-03-04 17:08:03 +0800116 public static @NonNull DnsResolver getInstance() {
Luke Huang00b15f32019-01-04 19:56:29 +0800117 return sInstance;
118 }
119
120 private DnsResolver() {}
121
122 /**
Luke Huang4219ab62019-04-08 15:16:04 +0800123 * Base interface for answer callbacks
Luke Huang304491d2019-03-04 17:08:03 +0800124 *
Luke Huang4219ab62019-04-08 15:16:04 +0800125 * @param <T> The type of the answer
Luke Huang304491d2019-03-04 17:08:03 +0800126 */
Luke Huang4219ab62019-04-08 15:16:04 +0800127 public interface Callback<T> {
Luke Huang304491d2019-03-04 17:08:03 +0800128 /**
129 * Success response to
Luke Huang4219ab62019-04-08 15:16:04 +0800130 * {@link android.net.DnsResolver#query query()} or
131 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang304491d2019-03-04 17:08:03 +0800132 *
133 * Invoked when the answer to a query was successfully parsed.
134 *
Luke Huang4219ab62019-04-08 15:16:04 +0800135 * @param answer <T> answer to the query.
136 * @param rcode The response code in the DNS response.
Luke Huang304491d2019-03-04 17:08:03 +0800137 *
138 * {@see android.net.DnsResolver#query query()}
139 */
Luke Huang4219ab62019-04-08 15:16:04 +0800140 void onAnswer(@NonNull T answer, int rcode);
Luke Huang304491d2019-03-04 17:08:03 +0800141 /**
142 * Error response to
Luke Huang4219ab62019-04-08 15:16:04 +0800143 * {@link android.net.DnsResolver#query query()} or
144 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang304491d2019-03-04 17:08:03 +0800145 *
146 * Invoked when there is no valid answer to
147 * {@link android.net.DnsResolver#query query()}
Luke Huang4219ab62019-04-08 15:16:04 +0800148 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang304491d2019-03-04 17:08:03 +0800149 *
Luke Huang4219ab62019-04-08 15:16:04 +0800150 * @param error a {@link DnsException} object with additional
Luke Huang304491d2019-03-04 17:08:03 +0800151 * detail regarding the failure
152 */
Luke Huang4219ab62019-04-08 15:16:04 +0800153 void onError(@NonNull DnsException error);
Luke Huang304491d2019-03-04 17:08:03 +0800154 }
155
156 /**
Luke Huang4219ab62019-04-08 15:16:04 +0800157 * Class to represent DNS error
Luke Huang304491d2019-03-04 17:08:03 +0800158 */
Luke Huang4219ab62019-04-08 15:16:04 +0800159 public static class DnsException extends Exception {
160 /**
161 * DNS error code as one of the ERROR_* constants
162 */
163 @DnsError public final int code;
Luke Huang304491d2019-03-04 17:08:03 +0800164
Luke Huang4219ab62019-04-08 15:16:04 +0800165 DnsException(@DnsError int code, @Nullable Throwable cause) {
166 super(cause);
167 this.code = code;
Luke Huang304491d2019-03-04 17:08:03 +0800168 }
169 }
170
171 /**
Luke Huange4c79132019-03-07 19:01:26 +0800172 * Send a raw DNS query.
Luke Huang4219ab62019-04-08 15:16:04 +0800173 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang00b15f32019-01-04 19:56:29 +0800174 *
Luke Huange9b2bba2019-03-28 13:56:31 +0800175 * @param network {@link Network} specifying which network to query on.
Luke Huang00b15f32019-01-04 19:56:29 +0800176 * {@code null} for query on default network.
Luke Huange9b2bba2019-03-28 13:56:31 +0800177 * @param query blob message to query
Luke Huang00b15f32019-01-04 19:56:29 +0800178 * @param flags flags as a combination of the FLAGS_* constants
Luke Huange4c79132019-03-07 19:01:26 +0800179 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangc09f2d62019-03-08 14:48:59 +0800180 * @param cancellationSignal used by the caller to signal if the query should be
181 * cancelled. May be {@code null}.
Luke Huang4219ab62019-04-08 15:16:04 +0800182 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangc09f2d62019-03-08 14:48:59 +0800183 * of the result of dns query.
Luke Huang00b15f32019-01-04 19:56:29 +0800184 */
Luke Huang4219ab62019-04-08 15:16:04 +0800185 public void rawQuery(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags,
Luke Huangc09f2d62019-03-08 14:48:59 +0800186 @NonNull @CallbackExecutor Executor executor,
187 @Nullable CancellationSignal cancellationSignal,
Luke Huang4219ab62019-04-08 15:16:04 +0800188 @NonNull Callback<? super byte[]> callback) {
Luke Huangc09f2d62019-03-08 14:48:59 +0800189 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
190 return;
191 }
Luke Huang5386f492019-03-26 15:50:10 +0800192 final Object lock = new Object();
Luke Huang304491d2019-03-04 17:08:03 +0800193 final FileDescriptor queryfd;
194 try {
Luke Huang247d83a2019-04-17 22:27:56 +0800195 queryfd = resNetworkSend((network != null)
196 ? network.getNetIdForResolv() : NETID_UNSET, query, query.length, flags);
Luke Huang304491d2019-03-04 17:08:03 +0800197 } catch (ErrnoException e) {
Luke Huang4219ab62019-04-08 15:16:04 +0800198 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang304491d2019-03-04 17:08:03 +0800199 return;
200 }
201
Luke Huang5bf7aa22019-03-29 18:01:11 +0800202 synchronized (lock) {
203 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
204 if (cancellationSignal == null) return;
205 addCancellationSignal(cancellationSignal, queryfd, lock);
206 }
Luke Huang00b15f32019-01-04 19:56:29 +0800207 }
208
209 /**
Luke Huange4c79132019-03-07 19:01:26 +0800210 * Send a DNS query with the specified name, class and query type.
Luke Huang4219ab62019-04-08 15:16:04 +0800211 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang00b15f32019-01-04 19:56:29 +0800212 *
Luke Huange9b2bba2019-03-28 13:56:31 +0800213 * @param network {@link Network} specifying which network to query on.
Luke Huang00b15f32019-01-04 19:56:29 +0800214 * {@code null} for query on default network.
Luke Huange9b2bba2019-03-28 13:56:31 +0800215 * @param domain domain name to query
Luke Huang00b15f32019-01-04 19:56:29 +0800216 * @param nsClass dns class as one of the CLASS_* constants
217 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
218 * @param flags flags as a combination of the FLAGS_* constants
Luke Huange4c79132019-03-07 19:01:26 +0800219 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangc09f2d62019-03-08 14:48:59 +0800220 * @param cancellationSignal used by the caller to signal if the query should be
221 * cancelled. May be {@code null}.
Luke Huang4219ab62019-04-08 15:16:04 +0800222 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangc09f2d62019-03-08 14:48:59 +0800223 * of the result of dns query.
Luke Huang00b15f32019-01-04 19:56:29 +0800224 */
Luke Huang4219ab62019-04-08 15:16:04 +0800225 public void rawQuery(@Nullable Network network, @NonNull String domain,
Luke Huang304491d2019-03-04 17:08:03 +0800226 @QueryClass int nsClass, @QueryType int nsType, @QueryFlag int flags,
Luke Huangc09f2d62019-03-08 14:48:59 +0800227 @NonNull @CallbackExecutor Executor executor,
228 @Nullable CancellationSignal cancellationSignal,
Luke Huang4219ab62019-04-08 15:16:04 +0800229 @NonNull Callback<? super byte[]> callback) {
Luke Huangc09f2d62019-03-08 14:48:59 +0800230 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
231 return;
232 }
Luke Huang5386f492019-03-26 15:50:10 +0800233 final Object lock = new Object();
Luke Huang304491d2019-03-04 17:08:03 +0800234 final FileDescriptor queryfd;
235 try {
Luke Huang247d83a2019-04-17 22:27:56 +0800236 queryfd = resNetworkQuery((network != null)
237 ? network.getNetIdForResolv() : NETID_UNSET, domain, nsClass, nsType, flags);
Luke Huang304491d2019-03-04 17:08:03 +0800238 } catch (ErrnoException e) {
Luke Huang4219ab62019-04-08 15:16:04 +0800239 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang304491d2019-03-04 17:08:03 +0800240 return;
241 }
Luke Huang5bf7aa22019-03-29 18:01:11 +0800242 synchronized (lock) {
243 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
244 if (cancellationSignal == null) return;
245 addCancellationSignal(cancellationSignal, queryfd, lock);
246 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800247 }
248
Luke Huang4219ab62019-04-08 15:16:04 +0800249 private class InetAddressAnswerAccumulator implements Callback<byte[]> {
Luke Huange9b2bba2019-03-28 13:56:31 +0800250 private final List<InetAddress> mAllAnswers;
Luke Huang247d83a2019-04-17 22:27:56 +0800251 private final Network mNetwork;
Luke Huang4219ab62019-04-08 15:16:04 +0800252 private int mRcode;
253 private DnsException mDnsException;
254 private final Callback<? super List<InetAddress>> mUserCallback;
Luke Huange9b2bba2019-03-28 13:56:31 +0800255 private final int mTargetAnswerCount;
256 private int mReceivedAnswerCount = 0;
257
Luke Huang247d83a2019-04-17 22:27:56 +0800258 InetAddressAnswerAccumulator(@NonNull Network network, int size,
Luke Huang4219ab62019-04-08 15:16:04 +0800259 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huang247d83a2019-04-17 22:27:56 +0800260 mNetwork = network;
Luke Huange9b2bba2019-03-28 13:56:31 +0800261 mTargetAnswerCount = size;
262 mAllAnswers = new ArrayList<>();
263 mUserCallback = callback;
264 }
265
Luke Huang4219ab62019-04-08 15:16:04 +0800266 private boolean maybeReportError() {
267 if (mRcode != 0) {
268 mUserCallback.onAnswer(mAllAnswers, mRcode);
Luke Huange9b2bba2019-03-28 13:56:31 +0800269 return true;
270 }
Luke Huang4219ab62019-04-08 15:16:04 +0800271 if (mDnsException != null) {
272 mUserCallback.onError(mDnsException);
Luke Huange9b2bba2019-03-28 13:56:31 +0800273 return true;
274 }
275 return false;
276 }
277
278 private void maybeReportAnswer() {
279 if (++mReceivedAnswerCount != mTargetAnswerCount) return;
Luke Huang4219ab62019-04-08 15:16:04 +0800280 if (mAllAnswers.isEmpty() && maybeReportError()) return;
Luke Huang247d83a2019-04-17 22:27:56 +0800281 mUserCallback.onAnswer(rfc6724Sort(mNetwork, mAllAnswers), mRcode);
Luke Huange9b2bba2019-03-28 13:56:31 +0800282 }
283
284 @Override
Luke Huang4219ab62019-04-08 15:16:04 +0800285 public void onAnswer(@NonNull byte[] answer, int rcode) {
286 // If at least one query succeeded, return an rcode of 0.
287 // Otherwise, arbitrarily return the first rcode received.
288 if (mReceivedAnswerCount == 0 || rcode == 0) {
289 mRcode = rcode;
290 }
291 try {
292 mAllAnswers.addAll(new DnsAddressAnswer(answer).getAddresses());
293 } catch (ParseException e) {
294 mDnsException = new DnsException(ERROR_PARSE, e);
295 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800296 maybeReportAnswer();
297 }
298
299 @Override
Luke Huang4219ab62019-04-08 15:16:04 +0800300 public void onError(@NonNull DnsException error) {
301 mDnsException = error;
Luke Huange9b2bba2019-03-28 13:56:31 +0800302 maybeReportAnswer();
303 }
304 }
305
306 /**
Luke Huang4219ab62019-04-08 15:16:04 +0800307 * Send a DNS query with the specified name on a network with both IPv4 and IPv6,
Luke Huang247d83a2019-04-17 22:27:56 +0800308 * get back a set of InetAddresses with rfc6724 sorting style asynchronously.
Luke Huang4219ab62019-04-08 15:16:04 +0800309 *
310 * This method will examine the connection ability on given network, and query IPv4
311 * and IPv6 if connection is available.
312 *
313 * If at least one query succeeded with valid answer, rcode will be 0
314 *
315 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huange9b2bba2019-03-28 13:56:31 +0800316 *
317 * @param network {@link Network} specifying which network to query on.
318 * {@code null} for query on default network.
319 * @param domain domain name to query
320 * @param flags flags as a combination of the FLAGS_* constants
321 * @param executor The {@link Executor} that the callback should be executed on.
322 * @param cancellationSignal used by the caller to signal if the query should be
323 * cancelled. May be {@code null}.
Luke Huang4219ab62019-04-08 15:16:04 +0800324 * @param callback a {@link Callback} which will be called to notify the
Luke Huange9b2bba2019-03-28 13:56:31 +0800325 * caller of the result of dns query.
326 */
327 public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
328 @NonNull @CallbackExecutor Executor executor,
329 @Nullable CancellationSignal cancellationSignal,
Luke Huang4219ab62019-04-08 15:16:04 +0800330 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huange9b2bba2019-03-28 13:56:31 +0800331 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
332 return;
333 }
334 final Object lock = new Object();
Luke Huang247d83a2019-04-17 22:27:56 +0800335 final Network queryNetwork;
336 try {
Luke Huangc211b272019-05-29 01:30:07 +0800337 queryNetwork = (network != null) ? network : getDnsNetwork();
Luke Huang247d83a2019-04-17 22:27:56 +0800338 } catch (ErrnoException e) {
339 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
340 return;
341 }
342 final boolean queryIpv6 = haveIpv6(queryNetwork);
343 final boolean queryIpv4 = haveIpv4(queryNetwork);
344
345 // This can only happen if queryIpv4 and queryIpv6 are both false.
346 // This almost certainly means that queryNetwork does not exist or no longer exists.
347 if (!queryIpv6 && !queryIpv4) {
348 executor.execute(() -> callback.onError(
349 new DnsException(ERROR_SYSTEM, new ErrnoException("resNetworkQuery", ENONET))));
350 return;
351 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800352
353 final FileDescriptor v4fd;
354 final FileDescriptor v6fd;
355
356 int queryCount = 0;
357
358 if (queryIpv6) {
359 try {
Luke Huang247d83a2019-04-17 22:27:56 +0800360 v6fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN,
361 TYPE_AAAA, flags);
Luke Huange9b2bba2019-03-28 13:56:31 +0800362 } catch (ErrnoException e) {
Luke Huang4219ab62019-04-08 15:16:04 +0800363 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huange9b2bba2019-03-28 13:56:31 +0800364 return;
365 }
366 queryCount++;
367 } else v6fd = null;
368
Luke Huange9b2bba2019-03-28 13:56:31 +0800369 // Avoiding gateways drop packets if queries are sent too close together
370 try {
Luke Huang5bf7aa22019-03-29 18:01:11 +0800371 Thread.sleep(SLEEP_TIME_MS);
Luke Huang4219ab62019-04-08 15:16:04 +0800372 } catch (InterruptedException ex) {
373 Thread.currentThread().interrupt();
374 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800375
376 if (queryIpv4) {
377 try {
Luke Huang247d83a2019-04-17 22:27:56 +0800378 v4fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, TYPE_A,
379 flags);
Luke Huange9b2bba2019-03-28 13:56:31 +0800380 } catch (ErrnoException e) {
381 if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
Luke Huang4219ab62019-04-08 15:16:04 +0800382 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huange9b2bba2019-03-28 13:56:31 +0800383 return;
384 }
385 queryCount++;
386 } else v4fd = null;
387
388 final InetAddressAnswerAccumulator accumulator =
Luke Huang247d83a2019-04-17 22:27:56 +0800389 new InetAddressAnswerAccumulator(queryNetwork, queryCount, callback);
Luke Huange9b2bba2019-03-28 13:56:31 +0800390
Luke Huang5bf7aa22019-03-29 18:01:11 +0800391 synchronized (lock) {
392 if (queryIpv6) {
393 registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
Luke Huange9b2bba2019-03-28 13:56:31 +0800394 }
Luke Huang5bf7aa22019-03-29 18:01:11 +0800395 if (queryIpv4) {
396 registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
397 }
398 if (cancellationSignal == null) return;
399 cancellationSignal.setOnCancelListener(() -> {
400 synchronized (lock) {
401 if (queryIpv4) cancelQuery(v4fd);
402 if (queryIpv6) cancelQuery(v6fd);
403 }
404 });
405 }
Luke Huang00b15f32019-01-04 19:56:29 +0800406 }
407
Luke Huang4219ab62019-04-08 15:16:04 +0800408 /**
409 * Send a DNS query with the specified name and query type, get back a set of
Luke Huang247d83a2019-04-17 22:27:56 +0800410 * InetAddresses with rfc6724 sorting style asynchronously.
Luke Huang4219ab62019-04-08 15:16:04 +0800411 *
412 * The answer will be provided asynchronously through the provided {@link Callback}.
413 *
414 * @param network {@link Network} specifying which network to query on.
415 * {@code null} for query on default network.
416 * @param domain domain name to query
417 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
418 * @param flags flags as a combination of the FLAGS_* constants
419 * @param executor The {@link Executor} that the callback should be executed on.
420 * @param cancellationSignal used by the caller to signal if the query should be
421 * cancelled. May be {@code null}.
422 * @param callback a {@link Callback} which will be called to notify the caller
423 * of the result of dns query.
424 */
425 public void query(@Nullable Network network, @NonNull String domain,
426 @QueryType int nsType, @QueryFlag int flags,
427 @NonNull @CallbackExecutor Executor executor,
428 @Nullable CancellationSignal cancellationSignal,
429 @NonNull Callback<? super List<InetAddress>> callback) {
430 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
431 return;
432 }
433 final Object lock = new Object();
434 final FileDescriptor queryfd;
Luke Huang247d83a2019-04-17 22:27:56 +0800435 final Network queryNetwork;
Luke Huang4219ab62019-04-08 15:16:04 +0800436 try {
Luke Huangc211b272019-05-29 01:30:07 +0800437 queryNetwork = (network != null) ? network : getDnsNetwork();
Luke Huang247d83a2019-04-17 22:27:56 +0800438 queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType,
439 flags);
Luke Huang4219ab62019-04-08 15:16:04 +0800440 } catch (ErrnoException e) {
441 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
442 return;
443 }
444 final InetAddressAnswerAccumulator accumulator =
Luke Huang247d83a2019-04-17 22:27:56 +0800445 new InetAddressAnswerAccumulator(queryNetwork, 1, callback);
Luke Huang4219ab62019-04-08 15:16:04 +0800446 synchronized (lock) {
447 registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock);
448 if (cancellationSignal == null) return;
449 addCancellationSignal(cancellationSignal, queryfd, lock);
450 }
451 }
452
453 /**
454 * Class to retrieve DNS response
455 *
456 * @hide
457 */
458 public static final class DnsResponse {
459 public final @NonNull byte[] answerbuf;
460 public final int rcode;
461 public DnsResponse(@NonNull byte[] answerbuf, int rcode) {
462 this.answerbuf = answerbuf;
463 this.rcode = rcode;
464 }
465 }
466
467 private void registerFDListener(@NonNull Executor executor,
468 @NonNull FileDescriptor queryfd, @NonNull Callback<? super byte[]> answerCallback,
Luke Huang5386f492019-03-26 15:50:10 +0800469 @Nullable CancellationSignal cancellationSignal, @NonNull Object lock) {
Luke Huang5b311262019-06-11 14:25:45 +0800470 final MessageQueue mainThreadMessageQueue = Looper.getMainLooper().getQueue();
471 mainThreadMessageQueue.addOnFileDescriptorEventListener(
Luke Huang00b15f32019-01-04 19:56:29 +0800472 queryfd,
473 FD_EVENTS,
474 (fd, events) -> {
Luke Huang5b311262019-06-11 14:25:45 +0800475 // b/134310704
476 // Unregister fd event listener before resNetworkResult is called to prevent
477 // race condition caused by fd reused.
478 // For example when querying v4 and v6, it's possible that the first query ends
479 // and the fd is closed before the second request starts, which might return
480 // the same fd for the second request. By that time, the looper must have
481 // unregistered the fd, otherwise another event listener can't be registered.
482 mainThreadMessageQueue.removeOnFileDescriptorEventListener(fd);
483
Luke Huange4c79132019-03-07 19:01:26 +0800484 executor.execute(() -> {
Luke Huang4219ab62019-04-08 15:16:04 +0800485 DnsResponse resp = null;
486 ErrnoException exception = null;
Luke Huang5386f492019-03-26 15:50:10 +0800487 synchronized (lock) {
488 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
489 return;
490 }
Luke Huang5386f492019-03-26 15:50:10 +0800491 try {
Luke Huang4219ab62019-04-08 15:16:04 +0800492 resp = resNetworkResult(fd); // Closes fd, marks it invalid.
Luke Huang5386f492019-03-26 15:50:10 +0800493 } catch (ErrnoException e) {
494 Log.e(TAG, "resNetworkResult:" + e.toString());
Luke Huang4219ab62019-04-08 15:16:04 +0800495 exception = e;
Luke Huang5386f492019-03-26 15:50:10 +0800496 }
Luke Huange4c79132019-03-07 19:01:26 +0800497 }
Luke Huang4219ab62019-04-08 15:16:04 +0800498 if (exception != null) {
499 answerCallback.onError(new DnsException(ERROR_SYSTEM, exception));
500 return;
501 }
502 answerCallback.onAnswer(resp.answerbuf, resp.rcode);
Luke Huange4c79132019-03-07 19:01:26 +0800503 });
Luke Huang5b311262019-06-11 14:25:45 +0800504
505 // The file descriptor has already been unregistered, so it does not really
506 // matter what is returned here. In spirit 0 (meaning "unregister this FD")
507 // is still the closest to what the looper needs to do. When returning 0,
508 // Looper knows to ignore the fd if it has already been unregistered.
Luke Huang00b15f32019-01-04 19:56:29 +0800509 return 0;
510 });
511 }
512
Luke Huange9b2bba2019-03-28 13:56:31 +0800513 private void cancelQuery(@NonNull FileDescriptor queryfd) {
514 if (!queryfd.valid()) return;
515 Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd);
516 resNetworkCancel(queryfd); // Closes fd, marks it invalid.
517 }
518
Luke Huang5bf7aa22019-03-29 18:01:11 +0800519 private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
Luke Huang5386f492019-03-26 15:50:10 +0800520 @NonNull FileDescriptor queryfd, @NonNull Object lock) {
Luke Huang5386f492019-03-26 15:50:10 +0800521 cancellationSignal.setOnCancelListener(() -> {
522 synchronized (lock) {
Luke Huange9b2bba2019-03-28 13:56:31 +0800523 cancelQuery(queryfd);
Luke Huang5386f492019-03-26 15:50:10 +0800524 }
525 });
Luke Huangc09f2d62019-03-08 14:48:59 +0800526 }
527
Luke Huang304491d2019-03-04 17:08:03 +0800528 private static class DnsAddressAnswer extends DnsPacket {
Luke Huang00b15f32019-01-04 19:56:29 +0800529 private static final String TAG = "DnsResolver.DnsAddressAnswer";
530 private static final boolean DBG = false;
531
532 private final int mQueryType;
533
534 DnsAddressAnswer(@NonNull byte[] data) throws ParseException {
535 super(data);
536 if ((mHeader.flags & (1 << 15)) == 0) {
537 throw new ParseException("Not an answer packet");
538 }
Luke Huang33bfef32019-01-23 21:53:13 +0800539 if (mHeader.getRecordCount(QDSECTION) == 0) {
Luke Huang00b15f32019-01-04 19:56:29 +0800540 throw new ParseException("No question found");
541 }
Luke Huang33bfef32019-01-23 21:53:13 +0800542 // Expect only one question in question section.
543 mQueryType = mRecords[QDSECTION].get(0).nsType;
Luke Huang00b15f32019-01-04 19:56:29 +0800544 }
545
546 public @NonNull List<InetAddress> getAddresses() {
547 final List<InetAddress> results = new ArrayList<InetAddress>();
Luke Huang304491d2019-03-04 17:08:03 +0800548 if (mHeader.getRecordCount(ANSECTION) == 0) return results;
549
Luke Huang33bfef32019-01-23 21:53:13 +0800550 for (final DnsRecord ansSec : mRecords[ANSECTION]) {
Luke Huang00b15f32019-01-04 19:56:29 +0800551 // Only support A and AAAA, also ignore answers if query type != answer type.
552 int nsType = ansSec.nsType;
553 if (nsType != mQueryType || (nsType != TYPE_A && nsType != TYPE_AAAA)) {
554 continue;
555 }
556 try {
557 results.add(InetAddress.getByAddress(ansSec.getRR()));
558 } catch (UnknownHostException e) {
559 if (DBG) {
560 Log.w(TAG, "rr to address fail");
561 }
562 }
563 }
564 return results;
565 }
566 }
567
Luke Huang00b15f32019-01-04 19:56:29 +0800568}