• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.net.util;
18 
19 import static android.system.OsConstants.AF_INET;
20 import static android.system.OsConstants.AF_INET6;
21 import static android.system.OsConstants.IPPROTO_UDP;
22 import static android.system.OsConstants.SOCK_DGRAM;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.net.InetAddresses;
27 import android.net.Network;
28 import android.system.ErrnoException;
29 import android.system.Os;
30 import android.util.Log;
31 
32 import com.android.internal.util.BitUtils;
33 
34 import libcore.io.IoUtils;
35 
36 import java.io.FileDescriptor;
37 import java.io.IOException;
38 import java.net.Inet4Address;
39 import java.net.Inet6Address;
40 import java.net.InetAddress;
41 import java.net.InetSocketAddress;
42 import java.net.SocketAddress;
43 import java.util.ArrayList;
44 import java.util.Collections;
45 import java.util.Comparator;
46 import java.util.List;
47 
48 /**
49  * @hide
50  */
51 public class DnsUtils {
52     private static final String TAG = "DnsUtils";
53     private static final int CHAR_BIT = 8;
54     public static final int IPV6_ADDR_SCOPE_NODELOCAL = 0x01;
55     public static final int IPV6_ADDR_SCOPE_LINKLOCAL = 0x02;
56     public static final int IPV6_ADDR_SCOPE_SITELOCAL = 0x05;
57     public static final int IPV6_ADDR_SCOPE_GLOBAL = 0x0e;
58     private static final Comparator<SortableAddress> sRfc6724Comparator = new Rfc6724Comparator();
59 
60     /**
61      * Comparator to sort SortableAddress in Rfc6724 style.
62      */
63     public static class Rfc6724Comparator implements Comparator<SortableAddress> {
64         // This function matches the behaviour of _rfc6724_compare in the native resolver.
65         @Override
compare(SortableAddress span1, SortableAddress span2)66         public int compare(SortableAddress span1, SortableAddress span2) {
67             // Rule 1: Avoid unusable destinations.
68             if (span1.hasSrcAddr != span2.hasSrcAddr) {
69                 return span2.hasSrcAddr - span1.hasSrcAddr;
70             }
71 
72             // Rule 2: Prefer matching scope.
73             if (span1.scopeMatch != span2.scopeMatch) {
74                 return span2.scopeMatch - span1.scopeMatch;
75             }
76 
77             // TODO: Implement rule 3: Avoid deprecated addresses.
78             // TODO: Implement rule 4: Prefer home addresses.
79 
80             // Rule 5: Prefer matching label.
81             if (span1.labelMatch != span2.labelMatch) {
82                 return span2.labelMatch - span1.labelMatch;
83             }
84 
85             // Rule 6: Prefer higher precedence.
86             if (span1.precedence != span2.precedence) {
87                 return span2.precedence - span1.precedence;
88             }
89 
90             // TODO: Implement rule 7: Prefer native transport.
91 
92             // Rule 8: Prefer smaller scope.
93             if (span1.scope != span2.scope) {
94                 return span1.scope - span2.scope;
95             }
96 
97             // Rule 9: Use longest matching prefix. IPv6 only.
98             if (span1.prefixMatchLen != span2.prefixMatchLen) {
99                 return span2.prefixMatchLen - span1.prefixMatchLen;
100             }
101 
102             // Rule 10: Leave the order unchanged. Collections.sort is a stable sort.
103             return 0;
104         }
105     }
106 
107     /**
108      * Class used to sort with RFC 6724
109      */
110     public static class SortableAddress {
111         public final int label;
112         public final int labelMatch;
113         public final int scope;
114         public final int scopeMatch;
115         public final int precedence;
116         public final int prefixMatchLen;
117         public final int hasSrcAddr;
118         public final InetAddress address;
119 
SortableAddress(@onNull InetAddress addr, @Nullable InetAddress srcAddr)120         public SortableAddress(@NonNull InetAddress addr, @Nullable InetAddress srcAddr) {
121             address = addr;
122             hasSrcAddr = (srcAddr != null) ? 1 : 0;
123             label = findLabel(addr);
124             scope = findScope(addr);
125             precedence = findPrecedence(addr);
126             labelMatch = ((srcAddr != null) && (label == findLabel(srcAddr))) ? 1 : 0;
127             scopeMatch = ((srcAddr != null) && (scope == findScope(srcAddr))) ? 1 : 0;
128             if (isIpv6Address(addr) && isIpv6Address(srcAddr)) {
129                 prefixMatchLen = compareIpv6PrefixMatchLen(srcAddr, addr);
130             } else {
131                 prefixMatchLen = 0;
132             }
133         }
134     }
135 
136     /**
137      * Sort the given address list in RFC6724 order.
138      * Will leave the list unchanged if an error occurs.
139      *
140      * This function matches the behaviour of _rfc6724_sort in the native resolver.
141      */
rfc6724Sort(@ullable Network network, @NonNull List<InetAddress> answers)142     public static @NonNull List<InetAddress> rfc6724Sort(@Nullable Network network,
143             @NonNull List<InetAddress> answers) {
144         List<SortableAddress> sortableAnswerList = new ArrayList<>();
145         answers.forEach(addr -> sortableAnswerList.add(
146                 new SortableAddress(addr, findSrcAddress(network, addr))));
147 
148         Collections.sort(sortableAnswerList, sRfc6724Comparator);
149 
150         final List<InetAddress> sortedAnswers = new ArrayList<>();
151         sortableAnswerList.forEach(ans -> sortedAnswers.add(ans.address));
152 
153         return sortedAnswers;
154     }
155 
findSrcAddress(@ullable Network network, @NonNull InetAddress addr)156     private static @Nullable InetAddress findSrcAddress(@Nullable Network network,
157             @NonNull InetAddress addr) {
158         final int domain;
159         if (isIpv4Address(addr)) {
160             domain = AF_INET;
161         } else if (isIpv6Address(addr)) {
162             domain = AF_INET6;
163         } else {
164             return null;
165         }
166         final FileDescriptor socket;
167         try {
168             socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
169         } catch (ErrnoException e) {
170             Log.e(TAG, "findSrcAddress:" + e.toString());
171             return null;
172         }
173         try {
174             if (network != null) network.bindSocket(socket);
175             Os.connect(socket, new InetSocketAddress(addr, 0));
176             return ((InetSocketAddress) Os.getsockname(socket)).getAddress();
177         } catch (IOException | ErrnoException e) {
178             return null;
179         } finally {
180             IoUtils.closeQuietly(socket);
181         }
182     }
183 
184     /**
185      * Get the label for a given IPv4/IPv6 address.
186      * RFC 6724, section 2.1.
187      *
188      * Note that Java will return an IPv4-mapped address as an IPv4 address.
189      */
findLabel(@onNull InetAddress addr)190     private static int findLabel(@NonNull InetAddress addr) {
191         if (isIpv4Address(addr)) {
192             return 4;
193         } else if (isIpv6Address(addr)) {
194             if (addr.isLoopbackAddress()) {
195                 return 0;
196             } else if (isIpv6Address6To4(addr)) {
197                 return 2;
198             } else if (isIpv6AddressTeredo(addr)) {
199                 return 5;
200             } else if (isIpv6AddressULA(addr)) {
201                 return 13;
202             } else if (((Inet6Address) addr).isIPv4CompatibleAddress()) {
203                 return 3;
204             } else if (addr.isSiteLocalAddress()) {
205                 return 11;
206             } else if (isIpv6Address6Bone(addr)) {
207                 return 12;
208             } else {
209                 // All other IPv6 addresses, including global unicast addresses.
210                 return 1;
211             }
212         } else {
213             // This should never happen.
214             return 1;
215         }
216     }
217 
isIpv6Address(@ullable InetAddress addr)218     private static boolean isIpv6Address(@Nullable InetAddress addr) {
219         return addr instanceof Inet6Address;
220     }
221 
isIpv4Address(@ullable InetAddress addr)222     private static boolean isIpv4Address(@Nullable InetAddress addr) {
223         return addr instanceof Inet4Address;
224     }
225 
isIpv6Address6To4(@onNull InetAddress addr)226     private static boolean isIpv6Address6To4(@NonNull InetAddress addr) {
227         if (!isIpv6Address(addr)) return false;
228         final byte[] byteAddr = addr.getAddress();
229         return byteAddr[0] == 0x20 && byteAddr[1] == 0x02;
230     }
231 
isIpv6AddressTeredo(@onNull InetAddress addr)232     private static boolean isIpv6AddressTeredo(@NonNull InetAddress addr) {
233         if (!isIpv6Address(addr)) return false;
234         final byte[] byteAddr = addr.getAddress();
235         return byteAddr[0] == 0x20 && byteAddr[1] == 0x01 && byteAddr[2] == 0x00
236                 && byteAddr[3] == 0x00;
237     }
238 
isIpv6AddressULA(@onNull InetAddress addr)239     private static boolean isIpv6AddressULA(@NonNull InetAddress addr) {
240         return isIpv6Address(addr) && (addr.getAddress()[0] & 0xfe) == 0xfc;
241     }
242 
isIpv6Address6Bone(@onNull InetAddress addr)243     private static boolean isIpv6Address6Bone(@NonNull InetAddress addr) {
244         if (!isIpv6Address(addr)) return false;
245         final byte[] byteAddr = addr.getAddress();
246         return byteAddr[0] == 0x3f && byteAddr[1] == (byte) 0xfe;
247     }
248 
getIpv6MulticastScope(@onNull InetAddress addr)249     private static int getIpv6MulticastScope(@NonNull InetAddress addr) {
250         return !isIpv6Address(addr) ? 0 : (addr.getAddress()[1] & 0x0f);
251     }
252 
findScope(@onNull InetAddress addr)253     private static int findScope(@NonNull InetAddress addr) {
254         if (isIpv6Address(addr)) {
255             if (addr.isMulticastAddress()) {
256                 return getIpv6MulticastScope(addr);
257             } else if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
258                 /**
259                  * RFC 4291 section 2.5.3 says loopback is to be treated as having
260                  * link-local scope.
261                  */
262                 return IPV6_ADDR_SCOPE_LINKLOCAL;
263             } else if (addr.isSiteLocalAddress()) {
264                 return IPV6_ADDR_SCOPE_SITELOCAL;
265             } else {
266                 return IPV6_ADDR_SCOPE_GLOBAL;
267             }
268         } else if (isIpv4Address(addr)) {
269             if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
270                 return IPV6_ADDR_SCOPE_LINKLOCAL;
271             } else {
272                 /**
273                  * RFC 6724 section 3.2. Other IPv4 addresses, including private addresses
274                  * and shared addresses (100.64.0.0/10), are assigned global scope.
275                  */
276                 return IPV6_ADDR_SCOPE_GLOBAL;
277             }
278         } else {
279             /**
280              * This should never happen.
281              * Return a scope with low priority as a last resort.
282              */
283             return IPV6_ADDR_SCOPE_NODELOCAL;
284         }
285     }
286 
287     /**
288      * Get the precedence for a given IPv4/IPv6 address.
289      * RFC 6724, section 2.1.
290      *
291      * Note that Java will return an IPv4-mapped address as an IPv4 address.
292      */
findPrecedence(@onNull InetAddress addr)293     private static int findPrecedence(@NonNull InetAddress addr) {
294         if (isIpv4Address(addr)) {
295             return 35;
296         } else if (isIpv6Address(addr)) {
297             if (addr.isLoopbackAddress()) {
298                 return 50;
299             } else if (isIpv6Address6To4(addr)) {
300                 return 30;
301             } else if (isIpv6AddressTeredo(addr)) {
302                 return 5;
303             } else if (isIpv6AddressULA(addr)) {
304                 return 3;
305             } else if (((Inet6Address) addr).isIPv4CompatibleAddress() || addr.isSiteLocalAddress()
306                     || isIpv6Address6Bone(addr)) {
307                 return 1;
308             } else {
309                 // All other IPv6 addresses, including global unicast addresses.
310                 return 40;
311             }
312         } else {
313             return 1;
314         }
315     }
316 
317     /**
318      * Find number of matching initial bits between the two addresses.
319      */
compareIpv6PrefixMatchLen(@onNull InetAddress srcAddr, @NonNull InetAddress dstAddr)320     private static int compareIpv6PrefixMatchLen(@NonNull InetAddress srcAddr,
321             @NonNull InetAddress dstAddr) {
322         final byte[] srcByte = srcAddr.getAddress();
323         final byte[] dstByte = dstAddr.getAddress();
324 
325         // This should never happen.
326         if (srcByte.length != dstByte.length) return 0;
327 
328         for (int i = 0; i < dstByte.length; ++i) {
329             if (srcByte[i] == dstByte[i]) {
330                 continue;
331             }
332             int x = BitUtils.uint8(srcByte[i]) ^ BitUtils.uint8(dstByte[i]);
333             return i * CHAR_BIT + (Integer.numberOfLeadingZeros(x) - 24);  // Java ints are 32 bits
334         }
335         return dstByte.length * CHAR_BIT;
336     }
337 
338     /**
339      * Check if given network has Ipv4 capability
340      * This function matches the behaviour of have_ipv4 in the native resolver.
341      */
haveIpv4(@ullable Network network)342     public static boolean haveIpv4(@Nullable Network network) {
343         final SocketAddress addrIpv4 =
344                 new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0);
345         return checkConnectivity(network, AF_INET, addrIpv4);
346     }
347 
348     /**
349      * Check if given network has Ipv6 capability
350      * This function matches the behaviour of have_ipv6 in the native resolver.
351      */
haveIpv6(@ullable Network network)352     public static boolean haveIpv6(@Nullable Network network) {
353         final SocketAddress addrIpv6 =
354                 new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0);
355         return checkConnectivity(network, AF_INET6, addrIpv6);
356     }
357 
checkConnectivity(@ullable Network network, int domain, @NonNull SocketAddress addr)358     private static boolean checkConnectivity(@Nullable Network network,
359             int domain, @NonNull SocketAddress addr) {
360         final FileDescriptor socket;
361         try {
362             socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
363         } catch (ErrnoException e) {
364             return false;
365         }
366         try {
367             if (network != null) network.bindSocket(socket);
368             Os.connect(socket, addr);
369         } catch (IOException | ErrnoException e) {
370             return false;
371         } finally {
372             IoUtils.closeQuietly(socket);
373         }
374         return true;
375     }
376 }
377