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