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         final ArrayList<SortableAddress> sortableAnswerList = new ArrayList<>();
145         for (InetAddress addr : answers) {
146             sortableAnswerList.add(new SortableAddress(addr, findSrcAddress(network, addr)));
147         }
148 
149         Collections.sort(sortableAnswerList, sRfc6724Comparator);
150 
151         final List<InetAddress> sortedAnswers = new ArrayList<>();
152         for (SortableAddress ans : sortableAnswerList) {
153             sortedAnswers.add(ans.address);
154         }
155 
156         return sortedAnswers;
157     }
158 
findSrcAddress(@ullable Network network, @NonNull InetAddress addr)159     private static @Nullable InetAddress findSrcAddress(@Nullable Network network,
160             @NonNull InetAddress addr) {
161         final int domain;
162         if (isIpv4Address(addr)) {
163             domain = AF_INET;
164         } else if (isIpv6Address(addr)) {
165             domain = AF_INET6;
166         } else {
167             return null;
168         }
169         final FileDescriptor socket;
170         try {
171             socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
172         } catch (ErrnoException e) {
173             Log.e(TAG, "findSrcAddress:" + e.toString());
174             return null;
175         }
176         try {
177             if (network != null) network.bindSocket(socket);
178             Os.connect(socket, new InetSocketAddress(addr, 0));
179             return ((InetSocketAddress) Os.getsockname(socket)).getAddress();
180         } catch (IOException | ErrnoException e) {
181             return null;
182         } finally {
183             IoUtils.closeQuietly(socket);
184         }
185     }
186 
187     /**
188      * Get the label for a given IPv4/IPv6 address.
189      * RFC 6724, section 2.1.
190      *
191      * Note that Java will return an IPv4-mapped address as an IPv4 address.
192      */
findLabel(@onNull InetAddress addr)193     private static int findLabel(@NonNull InetAddress addr) {
194         if (isIpv4Address(addr)) {
195             return 4;
196         } else if (isIpv6Address(addr)) {
197             if (addr.isLoopbackAddress()) {
198                 return 0;
199             } else if (isIpv6Address6To4(addr)) {
200                 return 2;
201             } else if (isIpv6AddressTeredo(addr)) {
202                 return 5;
203             } else if (isIpv6AddressULA(addr)) {
204                 return 13;
205             } else if (((Inet6Address) addr).isIPv4CompatibleAddress()) {
206                 return 3;
207             } else if (addr.isSiteLocalAddress()) {
208                 return 11;
209             } else if (isIpv6Address6Bone(addr)) {
210                 return 12;
211             } else {
212                 // All other IPv6 addresses, including global unicast addresses.
213                 return 1;
214             }
215         } else {
216             // This should never happen.
217             return 1;
218         }
219     }
220 
isIpv6Address(@ullable InetAddress addr)221     private static boolean isIpv6Address(@Nullable InetAddress addr) {
222         return addr instanceof Inet6Address;
223     }
224 
isIpv4Address(@ullable InetAddress addr)225     private static boolean isIpv4Address(@Nullable InetAddress addr) {
226         return addr instanceof Inet4Address;
227     }
228 
isIpv6Address6To4(@onNull InetAddress addr)229     private static boolean isIpv6Address6To4(@NonNull InetAddress addr) {
230         if (!isIpv6Address(addr)) return false;
231         final byte[] byteAddr = addr.getAddress();
232         return byteAddr[0] == 0x20 && byteAddr[1] == 0x02;
233     }
234 
isIpv6AddressTeredo(@onNull InetAddress addr)235     private static boolean isIpv6AddressTeredo(@NonNull InetAddress addr) {
236         if (!isIpv6Address(addr)) return false;
237         final byte[] byteAddr = addr.getAddress();
238         return byteAddr[0] == 0x20 && byteAddr[1] == 0x01 && byteAddr[2] == 0x00
239                 && byteAddr[3] == 0x00;
240     }
241 
isIpv6AddressULA(@onNull InetAddress addr)242     private static boolean isIpv6AddressULA(@NonNull InetAddress addr) {
243         return isIpv6Address(addr) && (addr.getAddress()[0] & 0xfe) == 0xfc;
244     }
245 
isIpv6Address6Bone(@onNull InetAddress addr)246     private static boolean isIpv6Address6Bone(@NonNull InetAddress addr) {
247         if (!isIpv6Address(addr)) return false;
248         final byte[] byteAddr = addr.getAddress();
249         return byteAddr[0] == 0x3f && byteAddr[1] == (byte) 0xfe;
250     }
251 
getIpv6MulticastScope(@onNull InetAddress addr)252     private static int getIpv6MulticastScope(@NonNull InetAddress addr) {
253         return !isIpv6Address(addr) ? 0 : (addr.getAddress()[1] & 0x0f);
254     }
255 
findScope(@onNull InetAddress addr)256     private static int findScope(@NonNull InetAddress addr) {
257         if (isIpv6Address(addr)) {
258             if (addr.isMulticastAddress()) {
259                 return getIpv6MulticastScope(addr);
260             } else if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
261                 /**
262                  * RFC 4291 section 2.5.3 says loopback is to be treated as having
263                  * link-local scope.
264                  */
265                 return IPV6_ADDR_SCOPE_LINKLOCAL;
266             } else if (addr.isSiteLocalAddress()) {
267                 return IPV6_ADDR_SCOPE_SITELOCAL;
268             } else {
269                 return IPV6_ADDR_SCOPE_GLOBAL;
270             }
271         } else if (isIpv4Address(addr)) {
272             if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
273                 return IPV6_ADDR_SCOPE_LINKLOCAL;
274             } else {
275                 /**
276                  * RFC 6724 section 3.2. Other IPv4 addresses, including private addresses
277                  * and shared addresses (100.64.0.0/10), are assigned global scope.
278                  */
279                 return IPV6_ADDR_SCOPE_GLOBAL;
280             }
281         } else {
282             /**
283              * This should never happen.
284              * Return a scope with low priority as a last resort.
285              */
286             return IPV6_ADDR_SCOPE_NODELOCAL;
287         }
288     }
289 
290     /**
291      * Get the precedence for a given IPv4/IPv6 address.
292      * RFC 6724, section 2.1.
293      *
294      * Note that Java will return an IPv4-mapped address as an IPv4 address.
295      */
findPrecedence(@onNull InetAddress addr)296     private static int findPrecedence(@NonNull InetAddress addr) {
297         if (isIpv4Address(addr)) {
298             return 35;
299         } else if (isIpv6Address(addr)) {
300             if (addr.isLoopbackAddress()) {
301                 return 50;
302             } else if (isIpv6Address6To4(addr)) {
303                 return 30;
304             } else if (isIpv6AddressTeredo(addr)) {
305                 return 5;
306             } else if (isIpv6AddressULA(addr)) {
307                 return 3;
308             } else if (((Inet6Address) addr).isIPv4CompatibleAddress() || addr.isSiteLocalAddress()
309                     || isIpv6Address6Bone(addr)) {
310                 return 1;
311             } else {
312                 // All other IPv6 addresses, including global unicast addresses.
313                 return 40;
314             }
315         } else {
316             return 1;
317         }
318     }
319 
320     /**
321      * Find number of matching initial bits between the two addresses.
322      */
compareIpv6PrefixMatchLen(@onNull InetAddress srcAddr, @NonNull InetAddress dstAddr)323     private static int compareIpv6PrefixMatchLen(@NonNull InetAddress srcAddr,
324             @NonNull InetAddress dstAddr) {
325         final byte[] srcByte = srcAddr.getAddress();
326         final byte[] dstByte = dstAddr.getAddress();
327 
328         // This should never happen.
329         if (srcByte.length != dstByte.length) return 0;
330 
331         for (int i = 0; i < dstByte.length; ++i) {
332             if (srcByte[i] == dstByte[i]) {
333                 continue;
334             }
335             int x = BitUtils.uint8(srcByte[i]) ^ BitUtils.uint8(dstByte[i]);
336             return i * CHAR_BIT + (Integer.numberOfLeadingZeros(x) - 24);  // Java ints are 32 bits
337         }
338         return dstByte.length * CHAR_BIT;
339     }
340 
341     /**
342      * Check if given network has Ipv4 capability
343      * This function matches the behaviour of have_ipv4 in the native resolver.
344      */
haveIpv4(@ullable Network network)345     public static boolean haveIpv4(@Nullable Network network) {
346         final SocketAddress addrIpv4 =
347                 new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0);
348         return checkConnectivity(network, AF_INET, addrIpv4);
349     }
350 
351     /**
352      * Check if given network has Ipv6 capability
353      * This function matches the behaviour of have_ipv6 in the native resolver.
354      */
haveIpv6(@ullable Network network)355     public static boolean haveIpv6(@Nullable Network network) {
356         final SocketAddress addrIpv6 =
357                 new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0);
358         return checkConnectivity(network, AF_INET6, addrIpv6);
359     }
360 
checkConnectivity(@ullable Network network, int domain, @NonNull SocketAddress addr)361     private static boolean checkConnectivity(@Nullable Network network,
362             int domain, @NonNull SocketAddress addr) {
363         final FileDescriptor socket;
364         try {
365             socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
366         } catch (ErrnoException e) {
367             return false;
368         }
369         try {
370             if (network != null) network.bindSocket(socket);
371             Os.connect(socket, addr);
372         } catch (IOException | ErrnoException e) {
373             return false;
374         } finally {
375             IoUtils.closeQuietly(socket);
376         }
377         return true;
378     }
379 }
380