blob: 3f7660f5709ae91a99ea3653adc4bc38defa176d [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
Luke Huangc1a52b82020-06-16 08:13:28 +080041import com.android.net.module.util.DnsPacket;
42
Luke Huang00b15f32019-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 Huange4c79132019-03-07 19:01:26 +080050import java.util.concurrent.Executor;
Luke Huang00b15f32019-01-04 19:56:29 +080051
52/**
53 * Dns resolver class for asynchronous dns querying
54 *
Luke Huang33bfef32019-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 Huang00b15f32019-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 Huang5bf7aa22019-03-29 18:01:11 +080063 private static final int SLEEP_TIME_MS = 2;
Luke Huang00b15f32019-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,
74 TYPE_AAAA
75 })
76 @Retention(RetentionPolicy.SOURCE)
77 @interface QueryType {}
78 public static final int TYPE_A = 1;
79 public static final int TYPE_AAAA = 28;
80
81 @IntDef(prefix = { "FLAG_" }, value = {
82 FLAG_EMPTY,
83 FLAG_NO_RETRY,
84 FLAG_NO_CACHE_STORE,
85 FLAG_NO_CACHE_LOOKUP
86 })
87 @Retention(RetentionPolicy.SOURCE)
88 @interface QueryFlag {}
89 public static final int FLAG_EMPTY = 0;
90 public static final int FLAG_NO_RETRY = 1 << 0;
91 public static final int FLAG_NO_CACHE_STORE = 1 << 1;
92 public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2;
93
Luke Huang4219ab62019-04-08 15:16:04 +080094 @IntDef(prefix = { "ERROR_" }, value = {
95 ERROR_PARSE,
96 ERROR_SYSTEM
97 })
98 @Retention(RetentionPolicy.SOURCE)
99 @interface DnsError {}
100 /**
101 * Indicates that there was an error parsing the response the query.
Luke Huangc1a52b82020-06-16 08:13:28 +0800102 * The cause of this error is available via getCause() and is a {@link ParseException}.
Luke Huang4219ab62019-04-08 15:16:04 +0800103 */
104 public static final int ERROR_PARSE = 0;
105 /**
106 * Indicates that there was an error sending the query.
107 * The cause of this error is available via getCause() and is an ErrnoException.
108 */
109 public static final int ERROR_SYSTEM = 1;
110
Luke Huang00b15f32019-01-04 19:56:29 +0800111 private static final int NETID_UNSET = 0;
112
113 private static final DnsResolver sInstance = new DnsResolver();
114
115 /**
Luke Huang00b15f32019-01-04 19:56:29 +0800116 * Get instance for DnsResolver
117 */
Luke Huang304491d2019-03-04 17:08:03 +0800118 public static @NonNull DnsResolver getInstance() {
Luke Huang00b15f32019-01-04 19:56:29 +0800119 return sInstance;
120 }
121
122 private DnsResolver() {}
123
124 /**
Luke Huang4219ab62019-04-08 15:16:04 +0800125 * Base interface for answer callbacks
Luke Huang304491d2019-03-04 17:08:03 +0800126 *
Luke Huang4219ab62019-04-08 15:16:04 +0800127 * @param <T> The type of the answer
Luke Huang304491d2019-03-04 17:08:03 +0800128 */
Luke Huang4219ab62019-04-08 15:16:04 +0800129 public interface Callback<T> {
Luke Huang304491d2019-03-04 17:08:03 +0800130 /**
131 * Success response to
Luke Huang4219ab62019-04-08 15:16:04 +0800132 * {@link android.net.DnsResolver#query query()} or
133 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang304491d2019-03-04 17:08:03 +0800134 *
135 * Invoked when the answer to a query was successfully parsed.
136 *
Luke Huang4219ab62019-04-08 15:16:04 +0800137 * @param answer <T> answer to the query.
138 * @param rcode The response code in the DNS response.
Luke Huang304491d2019-03-04 17:08:03 +0800139 *
140 * {@see android.net.DnsResolver#query query()}
141 */
Luke Huang4219ab62019-04-08 15:16:04 +0800142 void onAnswer(@NonNull T answer, int rcode);
Luke Huang304491d2019-03-04 17:08:03 +0800143 /**
144 * Error response to
Luke Huang4219ab62019-04-08 15:16:04 +0800145 * {@link android.net.DnsResolver#query query()} or
146 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang304491d2019-03-04 17:08:03 +0800147 *
148 * Invoked when there is no valid answer to
149 * {@link android.net.DnsResolver#query query()}
Luke Huang4219ab62019-04-08 15:16:04 +0800150 * {@link android.net.DnsResolver#rawQuery rawQuery()}.
Luke Huang304491d2019-03-04 17:08:03 +0800151 *
Luke Huang4219ab62019-04-08 15:16:04 +0800152 * @param error a {@link DnsException} object with additional
Luke Huang304491d2019-03-04 17:08:03 +0800153 * detail regarding the failure
154 */
Luke Huang4219ab62019-04-08 15:16:04 +0800155 void onError(@NonNull DnsException error);
Luke Huang304491d2019-03-04 17:08:03 +0800156 }
157
158 /**
Luke Huang4219ab62019-04-08 15:16:04 +0800159 * Class to represent DNS error
Luke Huang304491d2019-03-04 17:08:03 +0800160 */
Luke Huang4219ab62019-04-08 15:16:04 +0800161 public static class DnsException extends Exception {
162 /**
163 * DNS error code as one of the ERROR_* constants
164 */
165 @DnsError public final int code;
Luke Huang304491d2019-03-04 17:08:03 +0800166
Luke Huang4219ab62019-04-08 15:16:04 +0800167 DnsException(@DnsError int code, @Nullable Throwable cause) {
168 super(cause);
169 this.code = code;
Luke Huang304491d2019-03-04 17:08:03 +0800170 }
171 }
172
173 /**
Luke Huange4c79132019-03-07 19:01:26 +0800174 * Send a raw DNS query.
Luke Huang4219ab62019-04-08 15:16:04 +0800175 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang00b15f32019-01-04 19:56:29 +0800176 *
Luke Huange9b2bba2019-03-28 13:56:31 +0800177 * @param network {@link Network} specifying which network to query on.
Luke Huang00b15f32019-01-04 19:56:29 +0800178 * {@code null} for query on default network.
Luke Huange9b2bba2019-03-28 13:56:31 +0800179 * @param query blob message to query
Luke Huang00b15f32019-01-04 19:56:29 +0800180 * @param flags flags as a combination of the FLAGS_* constants
Luke Huange4c79132019-03-07 19:01:26 +0800181 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangc09f2d62019-03-08 14:48:59 +0800182 * @param cancellationSignal used by the caller to signal if the query should be
183 * cancelled. May be {@code null}.
Luke Huang4219ab62019-04-08 15:16:04 +0800184 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangc09f2d62019-03-08 14:48:59 +0800185 * of the result of dns query.
Luke Huang00b15f32019-01-04 19:56:29 +0800186 */
Luke Huang4219ab62019-04-08 15:16:04 +0800187 public void rawQuery(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags,
Luke Huangc09f2d62019-03-08 14:48:59 +0800188 @NonNull @CallbackExecutor Executor executor,
189 @Nullable CancellationSignal cancellationSignal,
Luke Huang4219ab62019-04-08 15:16:04 +0800190 @NonNull Callback<? super byte[]> callback) {
Luke Huangc09f2d62019-03-08 14:48:59 +0800191 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
192 return;
193 }
Luke Huang5386f492019-03-26 15:50:10 +0800194 final Object lock = new Object();
Luke Huang304491d2019-03-04 17:08:03 +0800195 final FileDescriptor queryfd;
196 try {
Luke Huang247d83a2019-04-17 22:27:56 +0800197 queryfd = resNetworkSend((network != null)
198 ? network.getNetIdForResolv() : NETID_UNSET, query, query.length, flags);
Luke Huang304491d2019-03-04 17:08:03 +0800199 } catch (ErrnoException e) {
Luke Huang4219ab62019-04-08 15:16:04 +0800200 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang304491d2019-03-04 17:08:03 +0800201 return;
202 }
203
Luke Huang5bf7aa22019-03-29 18:01:11 +0800204 synchronized (lock) {
205 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
206 if (cancellationSignal == null) return;
207 addCancellationSignal(cancellationSignal, queryfd, lock);
208 }
Luke Huang00b15f32019-01-04 19:56:29 +0800209 }
210
211 /**
Luke Huange4c79132019-03-07 19:01:26 +0800212 * Send a DNS query with the specified name, class and query type.
Luke Huang4219ab62019-04-08 15:16:04 +0800213 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huang00b15f32019-01-04 19:56:29 +0800214 *
Luke Huange9b2bba2019-03-28 13:56:31 +0800215 * @param network {@link Network} specifying which network to query on.
Luke Huang00b15f32019-01-04 19:56:29 +0800216 * {@code null} for query on default network.
Luke Huange9b2bba2019-03-28 13:56:31 +0800217 * @param domain domain name to query
Luke Huang00b15f32019-01-04 19:56:29 +0800218 * @param nsClass dns class as one of the CLASS_* constants
219 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
220 * @param flags flags as a combination of the FLAGS_* constants
Luke Huange4c79132019-03-07 19:01:26 +0800221 * @param executor The {@link Executor} that the callback should be executed on.
Luke Huangc09f2d62019-03-08 14:48:59 +0800222 * @param cancellationSignal used by the caller to signal if the query should be
223 * cancelled. May be {@code null}.
Luke Huang4219ab62019-04-08 15:16:04 +0800224 * @param callback a {@link Callback} which will be called to notify the caller
Luke Huangc09f2d62019-03-08 14:48:59 +0800225 * of the result of dns query.
Luke Huang00b15f32019-01-04 19:56:29 +0800226 */
Luke Huang4219ab62019-04-08 15:16:04 +0800227 public void rawQuery(@Nullable Network network, @NonNull String domain,
Luke Huang304491d2019-03-04 17:08:03 +0800228 @QueryClass int nsClass, @QueryType int nsType, @QueryFlag int flags,
Luke Huangc09f2d62019-03-08 14:48:59 +0800229 @NonNull @CallbackExecutor Executor executor,
230 @Nullable CancellationSignal cancellationSignal,
Luke Huang4219ab62019-04-08 15:16:04 +0800231 @NonNull Callback<? super byte[]> callback) {
Luke Huangc09f2d62019-03-08 14:48:59 +0800232 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
233 return;
234 }
Luke Huang5386f492019-03-26 15:50:10 +0800235 final Object lock = new Object();
Luke Huang304491d2019-03-04 17:08:03 +0800236 final FileDescriptor queryfd;
237 try {
Luke Huang247d83a2019-04-17 22:27:56 +0800238 queryfd = resNetworkQuery((network != null)
239 ? network.getNetIdForResolv() : NETID_UNSET, domain, nsClass, nsType, flags);
Luke Huang304491d2019-03-04 17:08:03 +0800240 } catch (ErrnoException e) {
Luke Huang4219ab62019-04-08 15:16:04 +0800241 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huang304491d2019-03-04 17:08:03 +0800242 return;
243 }
Luke Huang5bf7aa22019-03-29 18:01:11 +0800244 synchronized (lock) {
245 registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
246 if (cancellationSignal == null) return;
247 addCancellationSignal(cancellationSignal, queryfd, lock);
248 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800249 }
250
Luke Huang4219ab62019-04-08 15:16:04 +0800251 private class InetAddressAnswerAccumulator implements Callback<byte[]> {
Luke Huange9b2bba2019-03-28 13:56:31 +0800252 private final List<InetAddress> mAllAnswers;
Luke Huang247d83a2019-04-17 22:27:56 +0800253 private final Network mNetwork;
Luke Huang4219ab62019-04-08 15:16:04 +0800254 private int mRcode;
255 private DnsException mDnsException;
256 private final Callback<? super List<InetAddress>> mUserCallback;
Luke Huange9b2bba2019-03-28 13:56:31 +0800257 private final int mTargetAnswerCount;
258 private int mReceivedAnswerCount = 0;
259
Luke Huang247d83a2019-04-17 22:27:56 +0800260 InetAddressAnswerAccumulator(@NonNull Network network, int size,
Luke Huang4219ab62019-04-08 15:16:04 +0800261 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huang247d83a2019-04-17 22:27:56 +0800262 mNetwork = network;
Luke Huange9b2bba2019-03-28 13:56:31 +0800263 mTargetAnswerCount = size;
264 mAllAnswers = new ArrayList<>();
265 mUserCallback = callback;
266 }
267
Luke Huang4219ab62019-04-08 15:16:04 +0800268 private boolean maybeReportError() {
269 if (mRcode != 0) {
270 mUserCallback.onAnswer(mAllAnswers, mRcode);
Luke Huange9b2bba2019-03-28 13:56:31 +0800271 return true;
272 }
Luke Huang4219ab62019-04-08 15:16:04 +0800273 if (mDnsException != null) {
274 mUserCallback.onError(mDnsException);
Luke Huange9b2bba2019-03-28 13:56:31 +0800275 return true;
276 }
277 return false;
278 }
279
280 private void maybeReportAnswer() {
281 if (++mReceivedAnswerCount != mTargetAnswerCount) return;
Luke Huang4219ab62019-04-08 15:16:04 +0800282 if (mAllAnswers.isEmpty() && maybeReportError()) return;
Luke Huang247d83a2019-04-17 22:27:56 +0800283 mUserCallback.onAnswer(rfc6724Sort(mNetwork, mAllAnswers), mRcode);
Luke Huange9b2bba2019-03-28 13:56:31 +0800284 }
285
286 @Override
Luke Huang4219ab62019-04-08 15:16:04 +0800287 public void onAnswer(@NonNull byte[] answer, int rcode) {
288 // If at least one query succeeded, return an rcode of 0.
289 // Otherwise, arbitrarily return the first rcode received.
290 if (mReceivedAnswerCount == 0 || rcode == 0) {
291 mRcode = rcode;
292 }
293 try {
294 mAllAnswers.addAll(new DnsAddressAnswer(answer).getAddresses());
Luke Huangc1a52b82020-06-16 08:13:28 +0800295 } catch (DnsPacket.ParseException e) {
296 // Convert the com.android.net.module.util.DnsPacket.ParseException to an
297 // android.net.ParseException. This is the type that was used in Q and is implied
298 // by the public documentation of ERROR_PARSE.
299 //
300 // DnsPacket cannot throw android.net.ParseException directly because it's @hide.
301 ParseException pe = new ParseException(e.reason, e.getCause());
302 pe.setStackTrace(e.getStackTrace());
303 mDnsException = new DnsException(ERROR_PARSE, pe);
Luke Huang4219ab62019-04-08 15:16:04 +0800304 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800305 maybeReportAnswer();
306 }
307
308 @Override
Luke Huang4219ab62019-04-08 15:16:04 +0800309 public void onError(@NonNull DnsException error) {
310 mDnsException = error;
Luke Huange9b2bba2019-03-28 13:56:31 +0800311 maybeReportAnswer();
312 }
313 }
314
315 /**
Luke Huang4219ab62019-04-08 15:16:04 +0800316 * Send a DNS query with the specified name on a network with both IPv4 and IPv6,
Luke Huang247d83a2019-04-17 22:27:56 +0800317 * get back a set of InetAddresses with rfc6724 sorting style asynchronously.
Luke Huang4219ab62019-04-08 15:16:04 +0800318 *
319 * This method will examine the connection ability on given network, and query IPv4
320 * and IPv6 if connection is available.
321 *
322 * If at least one query succeeded with valid answer, rcode will be 0
323 *
324 * The answer will be provided asynchronously through the provided {@link Callback}.
Luke Huange9b2bba2019-03-28 13:56:31 +0800325 *
326 * @param network {@link Network} specifying which network to query on.
327 * {@code null} for query on default network.
328 * @param domain domain name to query
329 * @param flags flags as a combination of the FLAGS_* constants
330 * @param executor The {@link Executor} that the callback should be executed on.
331 * @param cancellationSignal used by the caller to signal if the query should be
332 * cancelled. May be {@code null}.
Luke Huang4219ab62019-04-08 15:16:04 +0800333 * @param callback a {@link Callback} which will be called to notify the
Luke Huange9b2bba2019-03-28 13:56:31 +0800334 * caller of the result of dns query.
335 */
336 public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
337 @NonNull @CallbackExecutor Executor executor,
338 @Nullable CancellationSignal cancellationSignal,
Luke Huang4219ab62019-04-08 15:16:04 +0800339 @NonNull Callback<? super List<InetAddress>> callback) {
Luke Huange9b2bba2019-03-28 13:56:31 +0800340 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
341 return;
342 }
343 final Object lock = new Object();
Luke Huang247d83a2019-04-17 22:27:56 +0800344 final Network queryNetwork;
345 try {
Luke Huangc211b272019-05-29 01:30:07 +0800346 queryNetwork = (network != null) ? network : getDnsNetwork();
Luke Huang247d83a2019-04-17 22:27:56 +0800347 } catch (ErrnoException e) {
348 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
349 return;
350 }
351 final boolean queryIpv6 = haveIpv6(queryNetwork);
352 final boolean queryIpv4 = haveIpv4(queryNetwork);
353
354 // This can only happen if queryIpv4 and queryIpv6 are both false.
355 // This almost certainly means that queryNetwork does not exist or no longer exists.
356 if (!queryIpv6 && !queryIpv4) {
357 executor.execute(() -> callback.onError(
358 new DnsException(ERROR_SYSTEM, new ErrnoException("resNetworkQuery", ENONET))));
359 return;
360 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800361
362 final FileDescriptor v4fd;
363 final FileDescriptor v6fd;
364
365 int queryCount = 0;
366
367 if (queryIpv6) {
368 try {
Luke Huang247d83a2019-04-17 22:27:56 +0800369 v6fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN,
370 TYPE_AAAA, flags);
Luke Huange9b2bba2019-03-28 13:56:31 +0800371 } catch (ErrnoException e) {
Luke Huang4219ab62019-04-08 15:16:04 +0800372 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huange9b2bba2019-03-28 13:56:31 +0800373 return;
374 }
375 queryCount++;
376 } else v6fd = null;
377
Luke Huange9b2bba2019-03-28 13:56:31 +0800378 // Avoiding gateways drop packets if queries are sent too close together
379 try {
Luke Huang5bf7aa22019-03-29 18:01:11 +0800380 Thread.sleep(SLEEP_TIME_MS);
Luke Huang4219ab62019-04-08 15:16:04 +0800381 } catch (InterruptedException ex) {
382 Thread.currentThread().interrupt();
383 }
Luke Huange9b2bba2019-03-28 13:56:31 +0800384
385 if (queryIpv4) {
386 try {
Luke Huang247d83a2019-04-17 22:27:56 +0800387 v4fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, TYPE_A,
388 flags);
Luke Huange9b2bba2019-03-28 13:56:31 +0800389 } catch (ErrnoException e) {
390 if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
Luke Huang4219ab62019-04-08 15:16:04 +0800391 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
Luke Huange9b2bba2019-03-28 13:56:31 +0800392 return;
393 }
394 queryCount++;
395 } else v4fd = null;
396
397 final InetAddressAnswerAccumulator accumulator =
Luke Huang247d83a2019-04-17 22:27:56 +0800398 new InetAddressAnswerAccumulator(queryNetwork, queryCount, callback);
Luke Huange9b2bba2019-03-28 13:56:31 +0800399
Luke Huang5bf7aa22019-03-29 18:01:11 +0800400 synchronized (lock) {
401 if (queryIpv6) {
402 registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
Luke Huange9b2bba2019-03-28 13:56:31 +0800403 }
Luke Huang5bf7aa22019-03-29 18:01:11 +0800404 if (queryIpv4) {
405 registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
406 }
407 if (cancellationSignal == null) return;
408 cancellationSignal.setOnCancelListener(() -> {
409 synchronized (lock) {
410 if (queryIpv4) cancelQuery(v4fd);
411 if (queryIpv6) cancelQuery(v6fd);
412 }
413 });
414 }
Luke Huang00b15f32019-01-04 19:56:29 +0800415 }
416
Luke Huang4219ab62019-04-08 15:16:04 +0800417 /**
418 * Send a DNS query with the specified name and query type, get back a set of
Luke Huang247d83a2019-04-17 22:27:56 +0800419 * InetAddresses with rfc6724 sorting style asynchronously.
Luke Huang4219ab62019-04-08 15:16:04 +0800420 *
421 * The answer will be provided asynchronously through the provided {@link Callback}.
422 *
423 * @param network {@link Network} specifying which network to query on.
424 * {@code null} for query on default network.
425 * @param domain domain name to query
426 * @param nsType dns resource record (RR) type as one of the TYPE_* constants
427 * @param flags flags as a combination of the FLAGS_* constants
428 * @param executor The {@link Executor} that the callback should be executed on.
429 * @param cancellationSignal used by the caller to signal if the query should be
430 * cancelled. May be {@code null}.
431 * @param callback a {@link Callback} which will be called to notify the caller
432 * of the result of dns query.
433 */
434 public void query(@Nullable Network network, @NonNull String domain,
435 @QueryType int nsType, @QueryFlag int flags,
436 @NonNull @CallbackExecutor Executor executor,
437 @Nullable CancellationSignal cancellationSignal,
438 @NonNull Callback<? super List<InetAddress>> callback) {
439 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
440 return;
441 }
442 final Object lock = new Object();
443 final FileDescriptor queryfd;
Luke Huang247d83a2019-04-17 22:27:56 +0800444 final Network queryNetwork;
Luke Huang4219ab62019-04-08 15:16:04 +0800445 try {
Luke Huangc211b272019-05-29 01:30:07 +0800446 queryNetwork = (network != null) ? network : getDnsNetwork();
Luke Huang247d83a2019-04-17 22:27:56 +0800447 queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType,
448 flags);
Luke Huang4219ab62019-04-08 15:16:04 +0800449 } catch (ErrnoException e) {
450 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
451 return;
452 }
453 final InetAddressAnswerAccumulator accumulator =
Luke Huang247d83a2019-04-17 22:27:56 +0800454 new InetAddressAnswerAccumulator(queryNetwork, 1, callback);
Luke Huang4219ab62019-04-08 15:16:04 +0800455 synchronized (lock) {
456 registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock);
457 if (cancellationSignal == null) return;
458 addCancellationSignal(cancellationSignal, queryfd, lock);
459 }
460 }
461
462 /**
463 * Class to retrieve DNS response
464 *
465 * @hide
466 */
467 public static final class DnsResponse {
468 public final @NonNull byte[] answerbuf;
469 public final int rcode;
470 public DnsResponse(@NonNull byte[] answerbuf, int rcode) {
471 this.answerbuf = answerbuf;
472 this.rcode = rcode;
473 }
474 }
475
476 private void registerFDListener(@NonNull Executor executor,
477 @NonNull FileDescriptor queryfd, @NonNull Callback<? super byte[]> answerCallback,
Luke Huang5386f492019-03-26 15:50:10 +0800478 @Nullable CancellationSignal cancellationSignal, @NonNull Object lock) {
Luke Huang5b311262019-06-11 14:25:45 +0800479 final MessageQueue mainThreadMessageQueue = Looper.getMainLooper().getQueue();
480 mainThreadMessageQueue.addOnFileDescriptorEventListener(
Luke Huang00b15f32019-01-04 19:56:29 +0800481 queryfd,
482 FD_EVENTS,
483 (fd, events) -> {
Luke Huang5b311262019-06-11 14:25:45 +0800484 // b/134310704
485 // Unregister fd event listener before resNetworkResult is called to prevent
486 // race condition caused by fd reused.
487 // For example when querying v4 and v6, it's possible that the first query ends
488 // and the fd is closed before the second request starts, which might return
489 // the same fd for the second request. By that time, the looper must have
490 // unregistered the fd, otherwise another event listener can't be registered.
491 mainThreadMessageQueue.removeOnFileDescriptorEventListener(fd);
492
Luke Huange4c79132019-03-07 19:01:26 +0800493 executor.execute(() -> {
Luke Huang4219ab62019-04-08 15:16:04 +0800494 DnsResponse resp = null;
495 ErrnoException exception = null;
Luke Huang5386f492019-03-26 15:50:10 +0800496 synchronized (lock) {
497 if (cancellationSignal != null && cancellationSignal.isCanceled()) {
498 return;
499 }
Luke Huang5386f492019-03-26 15:50:10 +0800500 try {
Luke Huang4219ab62019-04-08 15:16:04 +0800501 resp = resNetworkResult(fd); // Closes fd, marks it invalid.
Luke Huang5386f492019-03-26 15:50:10 +0800502 } catch (ErrnoException e) {
503 Log.e(TAG, "resNetworkResult:" + e.toString());
Luke Huang4219ab62019-04-08 15:16:04 +0800504 exception = e;
Luke Huang5386f492019-03-26 15:50:10 +0800505 }
Luke Huange4c79132019-03-07 19:01:26 +0800506 }
Luke Huang4219ab62019-04-08 15:16:04 +0800507 if (exception != null) {
508 answerCallback.onError(new DnsException(ERROR_SYSTEM, exception));
509 return;
510 }
511 answerCallback.onAnswer(resp.answerbuf, resp.rcode);
Luke Huange4c79132019-03-07 19:01:26 +0800512 });
Luke Huang5b311262019-06-11 14:25:45 +0800513
514 // The file descriptor has already been unregistered, so it does not really
515 // matter what is returned here. In spirit 0 (meaning "unregister this FD")
516 // is still the closest to what the looper needs to do. When returning 0,
517 // Looper knows to ignore the fd if it has already been unregistered.
Luke Huang00b15f32019-01-04 19:56:29 +0800518 return 0;
519 });
520 }
521
Luke Huange9b2bba2019-03-28 13:56:31 +0800522 private void cancelQuery(@NonNull FileDescriptor queryfd) {
523 if (!queryfd.valid()) return;
524 Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd);
525 resNetworkCancel(queryfd); // Closes fd, marks it invalid.
526 }
527
Luke Huang5bf7aa22019-03-29 18:01:11 +0800528 private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
Luke Huang5386f492019-03-26 15:50:10 +0800529 @NonNull FileDescriptor queryfd, @NonNull Object lock) {
Luke Huang5386f492019-03-26 15:50:10 +0800530 cancellationSignal.setOnCancelListener(() -> {
531 synchronized (lock) {
Luke Huange9b2bba2019-03-28 13:56:31 +0800532 cancelQuery(queryfd);
Luke Huang5386f492019-03-26 15:50:10 +0800533 }
534 });
Luke Huangc09f2d62019-03-08 14:48:59 +0800535 }
536
Luke Huang304491d2019-03-04 17:08:03 +0800537 private static class DnsAddressAnswer extends DnsPacket {
Luke Huang00b15f32019-01-04 19:56:29 +0800538 private static final String TAG = "DnsResolver.DnsAddressAnswer";
539 private static final boolean DBG = false;
540
541 private final int mQueryType;
542
543 DnsAddressAnswer(@NonNull byte[] data) throws ParseException {
544 super(data);
545 if ((mHeader.flags & (1 << 15)) == 0) {
546 throw new ParseException("Not an answer packet");
547 }
Luke Huang33bfef32019-01-23 21:53:13 +0800548 if (mHeader.getRecordCount(QDSECTION) == 0) {
Luke Huang00b15f32019-01-04 19:56:29 +0800549 throw new ParseException("No question found");
550 }
Luke Huang33bfef32019-01-23 21:53:13 +0800551 // Expect only one question in question section.
552 mQueryType = mRecords[QDSECTION].get(0).nsType;
Luke Huang00b15f32019-01-04 19:56:29 +0800553 }
554
555 public @NonNull List<InetAddress> getAddresses() {
556 final List<InetAddress> results = new ArrayList<InetAddress>();
Luke Huang304491d2019-03-04 17:08:03 +0800557 if (mHeader.getRecordCount(ANSECTION) == 0) return results;
558
Luke Huang33bfef32019-01-23 21:53:13 +0800559 for (final DnsRecord ansSec : mRecords[ANSECTION]) {
Luke Huang00b15f32019-01-04 19:56:29 +0800560 // Only support A and AAAA, also ignore answers if query type != answer type.
561 int nsType = ansSec.nsType;
562 if (nsType != mQueryType || (nsType != TYPE_A && nsType != TYPE_AAAA)) {
563 continue;
564 }
565 try {
566 results.add(InetAddress.getByAddress(ansSec.getRR()));
567 } catch (UnknownHostException e) {
568 if (DBG) {
569 Log.w(TAG, "rr to address fail");
570 }
571 }
572 }
573 return results;
574 }
575 }
576
Luke Huang00b15f32019-01-04 19:56:29 +0800577}