1 /* 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 17 package com.android.networkstack.util; 18 19 import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP; 20 import static android.net.DnsResolver.TYPE_A; 21 import static android.net.DnsResolver.TYPE_AAAA; 22 23 import android.annotation.NonNull; 24 import android.net.DnsResolver; 25 import android.net.Network; 26 import android.net.TrafficStats; 27 import android.util.Log; 28 29 import com.android.internal.util.TrafficStatsConstants; 30 31 import java.net.InetAddress; 32 import java.net.UnknownHostException; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.List; 36 import java.util.concurrent.CountDownLatch; 37 import java.util.concurrent.TimeUnit; 38 import java.util.concurrent.atomic.AtomicReference; 39 40 /** 41 * Collection of utilities for dns query. 42 */ 43 public class DnsUtils { 44 // Decide what queries to make depending on what IP addresses are on the system. 45 public static final int TYPE_ADDRCONFIG = -1; 46 private static final String TAG = DnsUtils.class.getSimpleName(); 47 48 /** 49 * Return both A and AAAA query results regardless the ip address type of the giving network. 50 * Used for probing in NetworkMonitor. 51 */ 52 @NonNull getAllByName(@onNull final DnsResolver dnsResolver, @NonNull final Network network, @NonNull String host, int timeout)53 public static InetAddress[] getAllByName(@NonNull final DnsResolver dnsResolver, 54 @NonNull final Network network, @NonNull String host, int timeout) 55 throws UnknownHostException { 56 final List<InetAddress> result = new ArrayList<InetAddress>(); 57 58 try { 59 result.addAll(Arrays.asList( 60 getAllByName(dnsResolver, network, host, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, 61 timeout))); 62 } catch (UnknownHostException e) { 63 // Might happen if the host is v4-only, still need to query TYPE_A 64 } 65 try { 66 result.addAll(Arrays.asList( 67 getAllByName(dnsResolver, network, host, TYPE_A, FLAG_NO_CACHE_LOOKUP, 68 timeout))); 69 } catch (UnknownHostException e) { 70 // Might happen if the host is v6-only, still need to return AAAA answers 71 } 72 if (result.size() == 0) { 73 throw new UnknownHostException(host); 74 } 75 return result.toArray(new InetAddress[0]); 76 } 77 78 /** 79 * Return dns query result based on the given QueryType(TYPE_A, TYPE_AAAA) or TYPE_ADDRCONFIG. 80 * Used for probing in NetworkMonitor. 81 */ 82 @NonNull getAllByName(@onNull final DnsResolver dnsResolver, @NonNull final Network network, @NonNull final String host, int type, int flag, int timeoutMs)83 public static InetAddress[] getAllByName(@NonNull final DnsResolver dnsResolver, 84 @NonNull final Network network, @NonNull final String host, int type, int flag, 85 int timeoutMs) throws UnknownHostException { 86 final CountDownLatch latch = new CountDownLatch(1); 87 final AtomicReference<List<InetAddress>> resultRef = new AtomicReference<>(); 88 89 final DnsResolver.Callback<List<InetAddress>> callback = 90 new DnsResolver.Callback<List<InetAddress>>() { 91 @Override 92 public void onAnswer(List<InetAddress> answer, int rcode) { 93 if (rcode == 0) { 94 resultRef.set(answer); 95 } 96 latch.countDown(); 97 } 98 99 @Override 100 public void onError(@NonNull DnsResolver.DnsException e) { 101 Log.d(TAG, "DNS error resolving " + host + ": " + e.getMessage()); 102 latch.countDown(); 103 } 104 }; 105 final int oldTag = TrafficStats.getAndSetThreadStatsTag( 106 TrafficStatsConstants.TAG_SYSTEM_PROBE); 107 108 if (type == TYPE_ADDRCONFIG) { 109 dnsResolver.query(network, host, flag, r -> r.run(), null /* cancellationSignal */, 110 callback); 111 } else { 112 dnsResolver.query(network, host, type, flag, r -> r.run(), 113 null /* cancellationSignal */, callback); 114 } 115 116 TrafficStats.setThreadStatsTag(oldTag); 117 118 try { 119 latch.await(timeoutMs, TimeUnit.MILLISECONDS); 120 } catch (InterruptedException e) { 121 } 122 123 final List<InetAddress> result = resultRef.get(); 124 if (result == null || result.size() == 0) { 125 throw new UnknownHostException(host); 126 } 127 128 return result.toArray(new InetAddress[0]); 129 } 130 } 131