• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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