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