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