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