Home | History | Annotate | Download | only in net
      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