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