1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 package java.net; 27 import android.system.ErrnoException; 28 import android.system.GaiException; 29 import android.system.StructAddrinfo; 30 import android.system.IcmpHeaders; 31 32 import dalvik.system.BlockGuard; 33 34 import libcore.io.IoBridge; 35 import libcore.io.Libcore; 36 37 import java.io.FileDescriptor; 38 import java.io.IOException; 39 import libcore.net.InetAddressUtils; 40 41 import static android.system.OsConstants.AF_INET; 42 import static android.system.OsConstants.AF_INET6; 43 import static android.system.OsConstants.AF_UNSPEC; 44 import static android.system.OsConstants.AI_ADDRCONFIG; 45 import static android.system.OsConstants.EACCES; 46 import static android.system.OsConstants.ECONNREFUSED; 47 import static android.system.OsConstants.EPERM; 48 import static android.system.OsConstants.NI_NAMEREQD; 49 import static android.system.OsConstants.ICMP6_ECHO_REPLY; 50 import static android.system.OsConstants.ICMP_ECHOREPLY; 51 import static android.system.OsConstants.IPPROTO_ICMP; 52 import static android.system.OsConstants.IPPROTO_ICMPV6; 53 import static android.system.OsConstants.SOCK_DGRAM; 54 import static android.system.OsConstants.SOCK_STREAM; 55 56 // Android-note: Android-specific behavior and Linux-based implementation 57 // http://b/36933260 Implement root-less ICMP for isReachable() 58 // http://b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os. 59 // http://b/25861497 Add BlockGuard checks. 60 // http://b/26700324 Fix odd dependency chains of the static InetAddress. 61 // anyLocalAddress() Let anyLocalAddress() always return an IPv6 address. 62 // Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. 63 // Rewrote hostname lookup methods on top of Libcore.os. Merge implementation from InetAddress 64 // and remove native methods in this class 65 /* 66 * Package private implementation of InetAddressImpl for dual 67 * IPv4/IPv6 stack. {@code #anyLocalAddress()} will always return an IPv6 address. 68 * 69 * @since 1.4 70 */ 71 72 class Inet6AddressImpl implements InetAddressImpl { 73 74 // @GuardedBy(Inet6AddressImpl.class) 75 private static InetAddress anyLocalAddress; 76 // @GuardedBy(Inet6AddressImpl.class) 77 private static InetAddress[] loopbackAddresses; 78 79 private static final AddressCache addressCache = new AddressCache(); 80 81 // BEGIN Android-changed: Rewrote hostname lookup methods on top of Libcore.os. 82 /* 83 public native String getLocalHostName() throws UnknownHostException; 84 public native InetAddress[] 85 lookupAllHostAddr(String hostname) throws UnknownHostException; 86 public native String getHostByAddr(byte[] addr) throws UnknownHostException; 87 private native boolean isReachable0(byte[] addr, int scope, int timeout, byte[] inf, int ttl, int if_scope) throws IOException; 88 */ 89 @Override lookupAllHostAddr(String host, int netId)90 public InetAddress[] lookupAllHostAddr(String host, int netId) throws UnknownHostException { 91 if (host == null || host.isEmpty()) { 92 // Android-changed: Return both the Inet4 and Inet6 loopback addresses 93 // when host == null or empty. 94 return loopbackAddresses(); 95 } 96 97 // Is it a numeric address? 98 InetAddress result = InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(host); 99 if (result != null) { 100 return new InetAddress[] { result }; 101 } 102 103 return lookupHostByName(host, netId); 104 } 105 106 /** 107 * Resolves a hostname to its IP addresses using a cache. 108 * 109 * @param host the hostname to resolve. 110 * @param netId the network to perform resolution upon. 111 * @return the IP addresses of the host. 112 */ lookupHostByName(String host, int netId)113 private static InetAddress[] lookupHostByName(String host, int netId) 114 throws UnknownHostException { 115 BlockGuard.getThreadPolicy().onNetwork(); 116 // Do we have a result cached? 117 Object cachedResult = addressCache.get(host, netId); 118 if (cachedResult != null) { 119 if (cachedResult instanceof InetAddress[]) { 120 // A cached positive result. 121 return (InetAddress[]) cachedResult; 122 } else { 123 // A cached negative result. 124 throw new UnknownHostException((String) cachedResult); 125 } 126 } 127 try { 128 StructAddrinfo hints = new StructAddrinfo(); 129 hints.ai_flags = AI_ADDRCONFIG; 130 hints.ai_family = AF_UNSPEC; 131 // If we don't specify a socket type, every address will appear twice, once 132 // for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family 133 // anyway, just pick one. 134 hints.ai_socktype = SOCK_STREAM; 135 InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId); 136 // TODO: should getaddrinfo set the hostname of the InetAddresses it returns? 137 for (InetAddress address : addresses) { 138 address.holder().hostName = host; 139 address.holder().originalHostName = host; 140 } 141 addressCache.put(host, netId, addresses); 142 return addresses; 143 } catch (GaiException gaiException) { 144 // If the failure appears to have been a lack of INTERNET permission, throw a clear 145 // SecurityException to aid in debugging this common mistake. 146 // http://code.google.com/p/android/issues/detail?id=15722 147 if (gaiException.getCause() instanceof ErrnoException) { 148 int errno = ((ErrnoException) gaiException.getCause()).errno; 149 if (errno == EACCES || errno == EPERM) { 150 throw new SecurityException("Permission denied (missing INTERNET permission?)", gaiException); 151 } 152 } 153 // Otherwise, throw an UnknownHostException. 154 String detailMessage = "Unable to resolve host \"" + host + "\": " + Libcore.os.gai_strerror(gaiException.error); 155 addressCache.putUnknownHost(host, netId, detailMessage); 156 throw gaiException.rethrowAsUnknownHostException(detailMessage); 157 } 158 } 159 160 @Override getHostByAddr(byte[] addr)161 public String getHostByAddr(byte[] addr) throws UnknownHostException { 162 BlockGuard.getThreadPolicy().onNetwork(); 163 164 return getHostByAddr0(addr); 165 } 166 167 @Override clearAddressCache()168 public void clearAddressCache() { 169 addressCache.clear(); 170 } 171 // END Android-changed: Rewrote hostname lookup methods on top of Libcore.os. 172 173 @Override isReachable(InetAddress addr, int timeout, NetworkInterface netif, int ttl)174 public boolean isReachable(InetAddress addr, int timeout, NetworkInterface netif, int ttl) throws IOException { 175 // Android-changed: rewritten on the top of IoBridge and Libcore.os. 176 InetAddress sourceAddr = null; 177 if (netif != null) { 178 /* 179 * Let's make sure we bind to an address of the proper family. 180 * Which means same family as addr because at this point it could 181 * be either an IPv6 address or an IPv4 address (case of a dual 182 * stack system). 183 */ 184 java.util.Enumeration<InetAddress> it = netif.getInetAddresses(); 185 InetAddress inetaddr = null; 186 while (it.hasMoreElements()) { 187 inetaddr = it.nextElement(); 188 if (inetaddr.getClass().isInstance(addr)) { 189 sourceAddr = inetaddr; 190 break; 191 } 192 } 193 194 if (sourceAddr == null) { 195 // Interface doesn't support the address family of 196 // the destination 197 return false; 198 } 199 } 200 201 // Android-changed: http://b/36933260 Implement root-less ICMP for isReachable(). 202 /* 203 if (addr instanceof Inet6Address) 204 scope = ((Inet6Address) addr).getScopeId(); 205 return isReachable0(addr.getAddress(), scope, timeout, ifaddr, ttl, netif_scope); 206 */ 207 // Try ICMP first 208 if (icmpEcho(addr, timeout, sourceAddr, ttl)) { 209 return true; 210 } 211 212 // No good, let's fall back to TCP 213 return tcpEcho(addr, timeout, sourceAddr, ttl); 214 } 215 216 // BEGIN Android-added: http://b/36933260 Implement root-less ICMP for isReachable(). tcpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl)217 private boolean tcpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl) 218 throws IOException { 219 FileDescriptor fd = null; 220 try { 221 fd = IoBridge.socket(AF_INET6, SOCK_STREAM, 0); 222 if (ttl > 0) { 223 IoBridge.setSocketOption(fd, IoBridge.JAVA_IP_TTL, ttl); 224 } 225 if (sourceAddr != null) { 226 IoBridge.bind(fd, sourceAddr, 0); 227 } 228 IoBridge.connect(fd, addr, 7 /* Echo-protocol port */, timeout); 229 return true; 230 } catch (IOException e) { 231 // Connection refused by remote (ECONNREFUSED) implies reachable. Otherwise silently 232 // ignore the exception and return false. 233 Throwable cause = e.getCause(); 234 return cause instanceof ErrnoException 235 && ((ErrnoException) cause).errno == ECONNREFUSED; 236 } finally { 237 IoBridge.closeAndSignalBlockedThreads(fd); 238 } 239 } 240 icmpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl)241 protected boolean icmpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl) 242 throws IOException { 243 244 FileDescriptor fd = null; 245 try { 246 boolean isIPv4 = addr instanceof Inet4Address; 247 int domain = isIPv4 ? AF_INET : AF_INET6; 248 int icmpProto = isIPv4 ? IPPROTO_ICMP : IPPROTO_ICMPV6; 249 fd = IoBridge.socket(domain, SOCK_DGRAM, icmpProto); 250 251 if (ttl > 0) { 252 IoBridge.setSocketOption(fd, IoBridge.JAVA_IP_TTL, ttl); 253 } 254 if (sourceAddr != null) { 255 IoBridge.bind(fd, sourceAddr, 0); 256 } 257 258 byte[] packet; 259 260 // ICMP is unreliable, try sending requests every second until timeout. 261 for (int to = timeout, seq = 1; to > 0; ++seq) { 262 int sockTo = to >= 1000 ? 1000 : to; 263 264 IoBridge.setSocketOption(fd, SocketOptions.SO_TIMEOUT, sockTo); 265 266 packet = IcmpHeaders.createIcmpEchoHdr(isIPv4, seq); 267 IoBridge.sendto(fd, packet, 0, packet.length, 0, addr, 0); 268 final int icmpId = IoBridge.getLocalInetSocketAddress(fd).getPort(); 269 270 byte[] received = new byte[packet.length]; 271 DatagramPacket receivedPacket = new DatagramPacket(received, packet.length); 272 int size = IoBridge 273 .recvfrom(true, fd, received, 0, received.length, 0, receivedPacket, false); 274 if (size == packet.length) { 275 byte expectedType = isIPv4 ? (byte) ICMP_ECHOREPLY 276 : (byte) ICMP6_ECHO_REPLY; 277 if (receivedPacket.getAddress().equals(addr) 278 && received[0] == expectedType 279 && received[4] == (byte) (icmpId >> 8) 280 && received[5] == (byte) icmpId) { 281 int receivedSequence = ((received[6] & 0xff) << 8) + (received[7] & 0xff); 282 if (receivedSequence <= seq) { 283 return true; 284 } 285 } 286 } 287 to -= sockTo; 288 } 289 } catch (IOException e) { 290 // Silently ignore and fall back. 291 } finally { 292 if (fd != null) { 293 try { 294 Libcore.os.close(fd); 295 } catch (ErrnoException e) { } 296 } 297 } 298 299 return false; 300 } 301 // END Android-added: http://b/36933260 Implement root-less ICMP for isReachable(). 302 303 // BEGIN Android-changed: Let anyLocalAddress() always return an IPv6 address. 304 @Override anyLocalAddress()305 public InetAddress anyLocalAddress() { 306 synchronized (Inet6AddressImpl.class) { 307 // We avoid initializing anyLocalAddress during <clinit> to avoid issues 308 // caused by the dependency chains of these classes. InetAddress depends on 309 // InetAddressImpl, but Inet6Address & Inet4Address are its subclasses. 310 // Also see {@code loopbackAddresses). http://b/26700324 311 if (anyLocalAddress == null) { 312 Inet6Address anyAddress = new Inet6Address(); 313 anyAddress.holder().hostName = "::"; 314 anyLocalAddress = anyAddress; 315 } 316 317 return anyLocalAddress; 318 } 319 } 320 // END Android-changed: Let anyLocalAddress() always return an IPv6 address. 321 322 // BEGIN Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. 323 @Override loopbackAddresses()324 public InetAddress[] loopbackAddresses() { 325 synchronized (Inet6AddressImpl.class) { 326 // We avoid initializing anyLocalAddress during <clinit> to avoid issues 327 // caused by the dependency chains of these classes. InetAddress depends on 328 // InetAddressImpl, but Inet6Address & Inet4Address are its subclasses. 329 // Also see {@code anyLocalAddress). 330 if (loopbackAddresses == null) { 331 loopbackAddresses = new InetAddress[]{Inet6Address.LOOPBACK, Inet4Address.LOOPBACK}; 332 } 333 334 return loopbackAddresses; 335 } 336 } 337 // END Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. 338 339 // BEGIN Android-changed: b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os. getHostByAddr0(byte[] addr)340 private String getHostByAddr0(byte[] addr) throws UnknownHostException { 341 // Android-changed: Rewritten on the top of Libcore.os 342 InetAddress hostaddr = InetAddress.getByAddress(addr); 343 try { 344 return Libcore.os.getnameinfo(hostaddr, NI_NAMEREQD); 345 } catch (GaiException e) { 346 UnknownHostException uhe = new UnknownHostException(hostaddr.toString()); 347 uhe.initCause(e); 348 throw uhe; 349 } 350 } 351 // END Android-changed: b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os. 352 } 353