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