1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package java.net;
19 
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.io.ObjectStreamField;
24 import java.util.Arrays;
25 import java.util.Enumeration;
26 import static android.system.OsConstants.*;
27 
28 /**
29  * An IPv6 address. See {@link InetAddress}.
30  */
31 public final class Inet6Address extends InetAddress {
32 
33     private static final long serialVersionUID = 6880410070516793377L;
34 
35     /**
36      * @hide
37      */
38     public static final InetAddress ANY =
39             new Inet6Address(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, null, 0);
40 
41     /**
42      * @hide
43      */
44     public static final InetAddress LOOPBACK =
45             new Inet6Address(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
46                     "localhost", 0);
47 
48     private boolean scope_id_set;
49     private int scope_id;
50 
51     private boolean scope_ifname_set;
52     private String ifname;
53 
54     /**
55      * Constructs an {@code InetAddress} representing the {@code address} and
56      * {@code name} and {@code scope_id}.
57      *
58      * @param address
59      *            the network address.
60      * @param name
61      *            the name associated with the address.
62      * @param scope_id
63      *            the scope id for link- or site-local addresses.
64      */
Inet6Address(byte[] ipaddress, String hostName, int scope_id)65     Inet6Address(byte[] ipaddress, String hostName, int scope_id) {
66         super(AF_INET6, ipaddress, hostName);
67         this.scope_id = scope_id;
68         this.scope_id_set = (scope_id != 0);
69     }
70 
71     /**
72      * Constructs an IPv6 address according to the given {@code host}, {@code
73      * addr} and {@code scope_id}.
74      *
75      * @param host
76      *            the host name associated with the address.
77      * @param addr
78      *            the network address.
79      * @param scope_id
80      *            the scope id for link- or site-local addresses.
81      * @return the Inet6Address instance representing the IP address.
82      * @throws UnknownHostException
83      *             if the address is null or has an invalid length.
84      */
getByAddress(String host, byte[] addr, int scope_id)85     public static Inet6Address getByAddress(String host, byte[] addr, int scope_id)
86             throws UnknownHostException {
87         if (addr == null || addr.length != 16) {
88             throw new UnknownHostException("Not an IPv6 address: " + Arrays.toString(addr));
89         }
90         if (scope_id < 0) {
91             scope_id = 0;
92         }
93         // TODO: should we clone 'addr'?
94         return new Inet6Address(addr, host, scope_id);
95     }
96 
97     /**
98      * Gets an IPv6 address instance according to the given {@code host},
99      * {@code addr} and {@code nif}. {@code scope_id} is set according to the
100      * given {@code nif} and the {@code addr} type (for example site-local or
101      * link-local).
102      *
103      * @param host
104      *            the hostname associated with the address.
105      * @param addr
106      *            the network address.
107      * @param nif
108      *            the network interface that this address is associated with.
109      * @return the Inet6Address instance representing the IP address.
110      * @throws UnknownHostException
111      *             if the address is {@code null} or has an invalid length or
112      *             the interface doesn't have a numeric scope id for the given
113      *             address type.
114      */
getByAddress(String host, byte[] addr, NetworkInterface nif)115     public static Inet6Address getByAddress(String host, byte[] addr,
116             NetworkInterface nif) throws UnknownHostException {
117 
118         Inet6Address address = Inet6Address.getByAddress(host, addr, 0);
119 
120         // if nif is null, nothing needs to be set.
121         if (nif == null) {
122             return address;
123         }
124 
125         // find the first address which matches the type addr,
126         // then set the scope_id and ifname.
127         Enumeration<InetAddress> addressList = nif.getInetAddresses();
128         while (addressList.hasMoreElements()) {
129             InetAddress ia = addressList.nextElement();
130             if (ia.getAddress().length == 16) {
131                 Inet6Address v6ia = (Inet6Address) ia;
132                 boolean isSameType = v6ia.compareLocalType(address);
133                 if (isSameType) {
134                     address.scope_id_set = true;
135                     address.scope_id = v6ia.scope_id;
136                     address.scope_ifname_set = true;
137                     address.ifname = nif.getName();
138                     break;
139                 }
140             }
141         }
142         // if no address matches the type of addr, throws an
143         // UnknownHostException.
144         if (!address.scope_id_set) {
145             throw new UnknownHostException("Scope id not found for address: " + Arrays.toString(addr));
146         }
147         return address;
148     }
149 
150     /**
151      * Returns {@code true} if one of following cases applies:
152      * <p>
153      * <ol>
154      *  <li>both addresses are site local</li>
155      *  <li>both addresses are link local</li>
156      *  <li>{@code ia} is neither site local nor link local</li>
157      * </ol>
158      */
compareLocalType(Inet6Address ia)159     private boolean compareLocalType(Inet6Address ia) {
160         if (ia.isSiteLocalAddress() && isSiteLocalAddress()) {
161             return true;
162         }
163         if (ia.isLinkLocalAddress() && isLinkLocalAddress()) {
164             return true;
165         }
166         if (!ia.isSiteLocalAddress() && !ia.isLinkLocalAddress()) {
167             return true;
168         }
169         return false;
170     }
171 
isAnyLocalAddress()172     @Override public boolean isAnyLocalAddress() {
173         return Arrays.equals(ipaddress, Inet6Address.ANY.ipaddress);
174     }
175 
176     /**
177      * Returns whether this IPv6 address is an IPv4-compatible address or not.
178      * An IPv4-compatible address has the prefix {@code ::/96} and is a deprecated
179      * and no-longer used equivalent of the modern IPv4-mapped IPv6 addresses.
180      */
isIPv4CompatibleAddress()181     public boolean isIPv4CompatibleAddress() {
182         for (int i = 0; i < 12; i++) {
183             if (ipaddress[i] != 0) {
184                 return false;
185             }
186         }
187         return true;
188     }
189 
isLinkLocalAddress()190     @Override public boolean isLinkLocalAddress() {
191         return ((ipaddress[0] & 0xff) == 0xfe) && ((ipaddress[1] & 0xc0) == 0x80); // fe80:/10
192     }
193 
isLoopbackAddress()194     @Override public boolean isLoopbackAddress() {
195         return Arrays.equals(ipaddress, Inet6Address.LOOPBACK.ipaddress);
196     }
197 
isMCGlobal()198     @Override public boolean isMCGlobal() {
199         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x0e); // ffxe:/16
200     }
201 
isMCLinkLocal()202     @Override public boolean isMCLinkLocal() {
203         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x02); // ffx2:/16
204     }
205 
isMCNodeLocal()206     @Override public boolean isMCNodeLocal() {
207         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x01); // ffx1:/16
208     }
209 
isMCOrgLocal()210     @Override public boolean isMCOrgLocal() {
211         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x08); // ffx8:/16
212     }
213 
isMCSiteLocal()214     @Override public boolean isMCSiteLocal() {
215         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x05); // ffx5:/16
216     }
217 
isMulticastAddress()218     @Override public boolean isMulticastAddress() {
219         return ((ipaddress[0] & 0xff) == 0xff); // ff::/8
220     }
221 
isSiteLocalAddress()222     @Override public boolean isSiteLocalAddress() {
223         return ((ipaddress[0] & 0xff) == 0xfe) && ((ipaddress[1] & 0xc0) == 0xc0); // fec0:/10
224     }
225 
226     /**
227      * Returns the scope id if this address is scoped to an interface, 0 otherwise.
228      */
getScopeId()229     public int getScopeId() {
230         return scope_id_set ? scope_id : 0;
231     }
232 
233     /**
234      * Returns the network interface if this address is instanced with a scoped
235      * network interface, null otherwise.
236      */
getScopedInterface()237     public NetworkInterface getScopedInterface() {
238         try {
239             return (scope_ifname_set && ifname != null) ? NetworkInterface.getByName(ifname) : null;
240         } catch (SocketException ex) {
241             return null;
242         }
243     }
244 
245     private static final ObjectStreamField[] serialPersistentFields = {
246         new ObjectStreamField("ipaddress", byte[].class),
247         new ObjectStreamField("scope_id", int.class),
248         new ObjectStreamField("scope_id_set", boolean.class),
249         new ObjectStreamField("scope_ifname_set", boolean.class),
250         new ObjectStreamField("ifname", String.class),
251     };
252 
writeObject(ObjectOutputStream stream)253     private void writeObject(ObjectOutputStream stream) throws IOException {
254         ObjectOutputStream.PutField fields = stream.putFields();
255         if (ipaddress == null) {
256             fields.put("ipaddress", null);
257         } else {
258             fields.put("ipaddress", ipaddress);
259         }
260 
261         fields.put("scope_id", scope_id);
262         fields.put("scope_id_set", scope_id_set);
263         fields.put("scope_ifname_set", scope_ifname_set);
264         fields.put("ifname", ifname);
265         stream.writeFields();
266     }
267 
readObject(ObjectInputStream stream)268     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
269         ObjectInputStream.GetField fields = stream.readFields();
270         ipaddress = (byte[]) fields.get("ipaddress", null);
271         scope_id = fields.get("scope_id", 0);
272         scope_id_set = fields.get("scope_id_set", false);
273         ifname = (String) fields.get("ifname", null);
274         scope_ifname_set = fields.get("scope_ifname_set", false);
275     }
276 
toString()277     @Override public String toString() {
278         if (ifname != null) {
279             return super.toString() + "%" + ifname;
280         }
281         if (scope_id != 0) {
282             return super.toString() + "%" + scope_id;
283         }
284         return super.toString();
285     }
286 }
287