/* * Copyright (C) 2014 The Android Open Source Project * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.net; import android.system.ErrnoException; import android.system.GaiException; import android.system.StructAddrinfo; import android.system.StructIcmpHdr; import dalvik.system.BlockGuard; import libcore.io.IoBridge; import libcore.io.Libcore; import java.io.FileDescriptor; import java.io.IOException; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.AF_UNSPEC; import static android.system.OsConstants.AI_ADDRCONFIG; import static android.system.OsConstants.EACCES; import static android.system.OsConstants.ECONNREFUSED; import static android.system.OsConstants.NI_NAMEREQD; import static android.system.OsConstants.ICMP6_ECHO_REPLY; import static android.system.OsConstants.ICMP_ECHOREPLY; import static android.system.OsConstants.IPPROTO_ICMP; import static android.system.OsConstants.IPPROTO_ICMPV6; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_STREAM; // Android-note: Android-specific behavior and Linux-based implementation // http://b/36933260 Implement root-less ICMP for isReachable() // http://b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os. // http://b/25861497 Add BlockGuard checks. // http://b/26700324 Fix odd dependency chains of the static InetAddress. // anyLocalAddress() Let anyLocalAddress() always return an IPv6 address. // Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. // Rewrote hostname lookup methods on top of Libcore.os. Merge implementation from InetAddress // and remove native methods in this class /* * Package private implementation of InetAddressImpl for dual * IPv4/IPv6 stack. {@code #anyLocalAddress()} will always return an IPv6 address. * * @since 1.4 */ class Inet6AddressImpl implements InetAddressImpl { // @GuardedBy(Inet6AddressImpl.class) private static InetAddress anyLocalAddress; // @GuardedBy(Inet6AddressImpl.class) private static InetAddress[] loopbackAddresses; private static final AddressCache addressCache = new AddressCache(); // BEGIN Android-changed: Rewrote hostname lookup methods on top of Libcore.os. /* public native String getLocalHostName() throws UnknownHostException; public native InetAddress[] lookupAllHostAddr(String hostname) throws UnknownHostException; public native String getHostByAddr(byte[] addr) throws UnknownHostException; private native boolean isReachable0(byte[] addr, int scope, int timeout, byte[] inf, int ttl, int if_scope) throws IOException; */ @Override public InetAddress[] lookupAllHostAddr(String host, int netId) throws UnknownHostException { if (host == null || host.isEmpty()) { // Android-changed: Return both the Inet4 and Inet6 loopback addresses // when host == null or empty. return loopbackAddresses(); } // Is it a numeric address? InetAddress result = InetAddress.parseNumericAddressNoThrow(host); if (result != null) { result = InetAddress.disallowDeprecatedFormats(host, result); if (result == null) { throw new UnknownHostException("Deprecated IPv4 address format: " + host); } return new InetAddress[] { result }; } return lookupHostByName(host, netId); } /** * Resolves a hostname to its IP addresses using a cache. * * @param host the hostname to resolve. * @param netId the network to perform resolution upon. * @return the IP addresses of the host. */ private static InetAddress[] lookupHostByName(String host, int netId) throws UnknownHostException { BlockGuard.getThreadPolicy().onNetwork(); // Do we have a result cached? Object cachedResult = addressCache.get(host, netId); if (cachedResult != null) { if (cachedResult instanceof InetAddress[]) { // A cached positive result. return (InetAddress[]) cachedResult; } else { // A cached negative result. throw new UnknownHostException((String) cachedResult); } } try { StructAddrinfo hints = new StructAddrinfo(); hints.ai_flags = AI_ADDRCONFIG; hints.ai_family = AF_UNSPEC; // If we don't specify a socket type, every address will appear twice, once // for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family // anyway, just pick one. hints.ai_socktype = SOCK_STREAM; InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId); // TODO: should getaddrinfo set the hostname of the InetAddresses it returns? for (InetAddress address : addresses) { address.holder().hostName = host; address.holder().originalHostName = host; } addressCache.put(host, netId, addresses); return addresses; } catch (GaiException gaiException) { // If the failure appears to have been a lack of INTERNET permission, throw a clear // SecurityException to aid in debugging this common mistake. // http://code.google.com/p/android/issues/detail?id=15722 if (gaiException.getCause() instanceof ErrnoException) { if (((ErrnoException) gaiException.getCause()).errno == EACCES) { throw new SecurityException("Permission denied (missing INTERNET permission?)", gaiException); } } // Otherwise, throw an UnknownHostException. String detailMessage = "Unable to resolve host \"" + host + "\": " + Libcore.os.gai_strerror(gaiException.error); addressCache.putUnknownHost(host, netId, detailMessage); throw gaiException.rethrowAsUnknownHostException(detailMessage); } } @Override public String getHostByAddr(byte[] addr) throws UnknownHostException { BlockGuard.getThreadPolicy().onNetwork(); return getHostByAddr0(addr); } @Override public void clearAddressCache() { addressCache.clear(); } // END Android-changed: Rewrote hostname lookup methods on top of Libcore.os. @Override public boolean isReachable(InetAddress addr, int timeout, NetworkInterface netif, int ttl) throws IOException { // Android-changed: rewritten on the top of IoBridge and Libcore.os. InetAddress sourceAddr = null; if (netif != null) { /* * Let's make sure we bind to an address of the proper family. * Which means same family as addr because at this point it could * be either an IPv6 address or an IPv4 address (case of a dual * stack system). */ java.util.Enumeration it = netif.getInetAddresses(); InetAddress inetaddr = null; while (it.hasMoreElements()) { inetaddr = it.nextElement(); if (inetaddr.getClass().isInstance(addr)) { sourceAddr = inetaddr; break; } } if (sourceAddr == null) { // Interface doesn't support the address family of // the destination return false; } } // Android-changed: http://b/36933260 Implement root-less ICMP for isReachable(). /* if (addr instanceof Inet6Address) scope = ((Inet6Address) addr).getScopeId(); return isReachable0(addr.getAddress(), scope, timeout, ifaddr, ttl, netif_scope); */ // Try ICMP first if (icmpEcho(addr, timeout, sourceAddr, ttl)) { return true; } // No good, let's fall back to TCP return tcpEcho(addr, timeout, sourceAddr, ttl); } // BEGIN Android-added: http://b/36933260 Implement root-less ICMP for isReachable(). private boolean tcpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl) throws IOException { FileDescriptor fd = null; try { fd = IoBridge.socket(AF_INET6, SOCK_STREAM, 0); if (ttl > 0) { IoBridge.setSocketOption(fd, IoBridge.JAVA_IP_TTL, ttl); } if (sourceAddr != null) { IoBridge.bind(fd, sourceAddr, 0); } IoBridge.connect(fd, addr, 7 /* Echo-protocol port */, timeout); return true; } catch (IOException e) { // Connection refused by remote (ECONNREFUSED) implies reachable. Otherwise silently // ignore the exception and return false. Throwable cause = e.getCause(); return cause instanceof ErrnoException && ((ErrnoException) cause).errno == ECONNREFUSED; } finally { IoBridge.closeAndSignalBlockedThreads(fd); } } protected boolean icmpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl) throws IOException { FileDescriptor fd = null; try { boolean isIPv4 = addr instanceof Inet4Address; int domain = isIPv4 ? AF_INET : AF_INET6; int icmpProto = isIPv4 ? IPPROTO_ICMP : IPPROTO_ICMPV6; fd = IoBridge.socket(domain, SOCK_DGRAM, icmpProto); if (ttl > 0) { IoBridge.setSocketOption(fd, IoBridge.JAVA_IP_TTL, ttl); } if (sourceAddr != null) { IoBridge.bind(fd, sourceAddr, 0); } byte[] packet; // ICMP is unreliable, try sending requests every second until timeout. for (int to = timeout, seq = 0; to > 0; ++seq) { int sockTo = to >= 1000 ? 1000 : to; IoBridge.setSocketOption(fd, SocketOptions.SO_TIMEOUT, sockTo); packet = StructIcmpHdr.IcmpEchoHdr(isIPv4, seq).getBytes(); IoBridge.sendto(fd, packet, 0, packet.length, 0, addr, 0); final int icmpId = IoBridge.getLocalInetSocketAddress(fd).getPort(); byte[] received = new byte[packet.length]; DatagramPacket receivedPacket = new DatagramPacket(received, packet.length); int size = IoBridge .recvfrom(true, fd, received, 0, received.length, 0, receivedPacket, false); if (size == packet.length) { byte expectedType = isIPv4 ? (byte) ICMP_ECHOREPLY : (byte) ICMP6_ECHO_REPLY; if (receivedPacket.getAddress().equals(addr) && received[0] == expectedType && received[4] == (byte) (icmpId >> 8) && received[5] == (byte) icmpId && received[6] == (byte) (seq >> 8) && received[7] == (byte) seq) { // This is the packet we're expecting. return true; } } to -= sockTo; } } catch (IOException e) { // Silently ignore and fall back. } finally { if (fd != null) { try { Libcore.os.close(fd); } catch (ErrnoException e) { } } } return false; } // END Android-added: http://b/36933260 Implement root-less ICMP for isReachable(). // BEGIN Android-changed: Let anyLocalAddress() always return an IPv6 address. @Override public InetAddress anyLocalAddress() { synchronized (Inet6AddressImpl.class) { // We avoid initializing anyLocalAddress during to avoid issues // caused by the dependency chains of these classes. InetAddress depends on // InetAddressImpl, but Inet6Address & Inet4Address are its subclasses. // Also see {@code loopbackAddresses). http://b/26700324 if (anyLocalAddress == null) { Inet6Address anyAddress = new Inet6Address(); anyAddress.holder().hostName = "::"; anyLocalAddress = anyAddress; } return anyLocalAddress; } } // END Android-changed: Let anyLocalAddress() always return an IPv6 address. // BEGIN Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. @Override public InetAddress[] loopbackAddresses() { synchronized (Inet6AddressImpl.class) { // We avoid initializing anyLocalAddress during to avoid issues // caused by the dependency chains of these classes. InetAddress depends on // InetAddressImpl, but Inet6Address & Inet4Address are its subclasses. // Also see {@code anyLocalAddress). if (loopbackAddresses == null) { loopbackAddresses = new InetAddress[]{Inet6Address.LOOPBACK, Inet4Address.LOOPBACK}; } return loopbackAddresses; } } // END Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. // BEGIN Android-changed: b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os. private String getHostByAddr0(byte[] addr) throws UnknownHostException { // Android-changed: Rewritten on the top of Libcore.os InetAddress hostaddr = InetAddress.getByAddress(addr); try { return Libcore.os.getnameinfo(hostaddr, NI_NAMEREQD); } catch (GaiException e) { UnknownHostException uhe = new UnknownHostException(hostaddr.toString()); uhe.initCause(e); throw uhe; } } // END Android-changed: b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os. }