Inet6Address.java revision 051128862ae7c5c031b8ddb763848ed264a63746
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
18package java.net;
19
20import java.io.IOException;
21import java.io.ObjectInputStream;
22import java.io.ObjectOutputStream;
23import java.io.ObjectStreamField;
24import java.util.Enumeration;
25
26import org.apache.harmony.luni.util.Inet6Util;
27import org.apache.harmony.luni.util.Msg;
28
29/**
30 * This class represents a 128 bit long IPv6 address.
31 */
32public final class Inet6Address extends InetAddress {
33
34    private static final long serialVersionUID = 6880410070516793377L;
35
36    static final InetAddress ANY = new Inet6Address(new byte[]
37            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
38    static final InetAddress LOOPBACK = new Inet6Address(new byte[]
39            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, "localhost"); //$NON-NLS-1$
40
41    int scope_id;
42
43    boolean scope_id_set;
44
45    boolean scope_ifname_set;
46
47    String ifname;
48
49    /*
50     * scoped interface.
51     */
52    transient NetworkInterface scopedIf;
53
54    Inet6Address(byte address[]) {
55        family = AF_INET6;
56        ipaddress = address;
57        scope_id = 0;
58    }
59
60    Inet6Address(byte address[], String name) {
61        family = AF_INET6;
62        hostName = name;
63        ipaddress = address;
64        scope_id = 0;
65    }
66
67    /**
68     * Constructs an {@code InetAddress} representing the {@code address} and
69     * {@code name} and {@code scope_id}.
70     *
71     * @param address
72     *            the network address.
73     * @param name
74     *            the name associated with the address.
75     * @param scope_id
76     *            the scope id for link- or site-local addresses.
77     */
78    Inet6Address(byte address[], String name, int scope_id) {
79        family = AF_INET6;
80        hostName = name;
81        ipaddress = address;
82        this.scope_id = scope_id;
83        if (scope_id != 0) {
84            scope_id_set = true;
85        }
86    }
87
88    /**
89     * Constructs an IPv6 address according to the given {@code host}, {@code
90     * addr} and {@code scope_id}.
91     *
92     * @param host
93     *            the host name associated with the address.
94     * @param addr
95     *            the network address.
96     * @param scope_id
97     *            the scope id for link- or site-local addresses.
98     * @return the Inet6Address instance representing the IP address.
99     * @throws UnknownHostException
100     *             if the address is null or has an invalid length.
101     */
102    public static Inet6Address getByAddress(String host, byte[] addr,
103            int scope_id) throws UnknownHostException {
104        if (null == addr || 16 != addr.length) {
105            // KA020=Illegal IPv6 address
106            throw new UnknownHostException(Msg.getString("KA020")); //$NON-NLS-1$
107        }
108        if (scope_id < 0) {
109            scope_id = 0;
110        }
111        return new Inet6Address(addr, host, scope_id);
112    }
113
114    /**
115     * Gets an IPv6 address instance according to the given {@code host},
116     * {@code addr} and {@code nif}. {@code scope_id} is set according to the
117     * given {@code nif} and the {@code addr} type (for example site-local or
118     * link-local).
119     *
120     * @param host
121     *            the hostname associated with the address.
122     * @param addr
123     *            the network address.
124     * @param nif
125     *            the network interface that this address is associated with.
126     * @return the Inet6Address instance representing the IP address.
127     * @throws UnknownHostException
128     *             if the address is {@code null} or has an invalid length or
129     *             the interface doesn't have a numeric scope id for the given
130     *             address type.
131     */
132    public static Inet6Address getByAddress(String host, byte[] addr,
133            NetworkInterface nif) throws UnknownHostException {
134
135        Inet6Address address = Inet6Address.getByAddress(host, addr, 0);
136
137        // if nif is null, nothing needs to be set.
138        if (null == nif) {
139            return address;
140        }
141
142        // find the first address which matches the type addr,
143        // then set the scope_id, ifname and scopedIf.
144        Enumeration<InetAddress> addressList = nif.getInetAddresses();
145        while (addressList.hasMoreElements()) {
146            InetAddress ia = addressList.nextElement();
147            if (ia.getAddress().length == 16) {
148                Inet6Address v6ia = (Inet6Address) ia;
149                boolean isSameType = v6ia.compareLocalType(address);
150                if (isSameType) {
151                    address.scope_id_set = true;
152                    address.scope_id = v6ia.scope_id;
153                    address.scope_ifname_set = true;
154                    address.ifname = nif.getName();
155                    address.scopedIf = nif;
156                    break;
157                }
158            }
159        }
160        // if no address matches the type of addr, throws an
161        // UnknownHostException.
162        if (!address.scope_id_set) {
163            // KA021=Scope id is not found for the given address
164            throw new UnknownHostException(Msg.getString("KA021")); //$NON-NLS-1$
165        }
166        return address;
167    }
168
169    /**
170     * Returns {@code true} if one of following cases applies:
171     * <p>
172     * <ol>
173     *  <li>both addresses are site local</li>
174     *  <li>both addresses are link local</li>
175     *  <li>{@code ia} is neither site local nor link local</li>
176     * </ol>
177     */
178    private boolean compareLocalType(Inet6Address ia) {
179        if (ia.isSiteLocalAddress() && isSiteLocalAddress()) {
180            return true;
181        }
182        if (ia.isLinkLocalAddress() && isLinkLocalAddress()) {
183            return true;
184        }
185        if (!ia.isSiteLocalAddress() && !ia.isLinkLocalAddress()) {
186            return true;
187        }
188        return false;
189    }
190
191    /**
192     * Constructs an {@code InetAddress} representing the {@code address} and
193     * {@code scope_id}.
194     *
195     * @param address
196     *            the network address.
197     * @param scope_id
198     *            the scope id for link- or site-local addresses.
199     */
200    Inet6Address(byte address[], int scope_id) {
201        ipaddress = address;
202        this.scope_id = scope_id;
203        if (scope_id != 0) {
204            scope_id_set = true;
205        }
206    }
207
208    /**
209     * Returns whether this address is an IP multicast address or not. Valid
210     * IPv6 multicast addresses are binary prefixed with 11111111 or FF (hex).
211     *
212     * @return {@code true} if this address is in the multicast group, {@code
213     *         false} otherwise.
214     */
215    @Override
216    public boolean isMulticastAddress() {
217        // Multicast addresses are prefixed with 11111111 (255)
218        return ipaddress[0] == -1;
219    }
220
221    /**
222     * Returns whether this address is a unspecified wildcard address "::" or
223     * not.
224     *
225     * @return {@code true} if this instance represents a wildcard address,
226     *         {@code false} otherwise.
227     */
228    @Override
229    public boolean isAnyLocalAddress() {
230        for (int i = 0; i < ipaddress.length; i++) {
231            if (ipaddress[i] != 0) {
232                return false;
233            }
234        }
235        return true;
236    }
237
238    /**
239     * Returns whether this address is the loopback address or not. The only
240     * valid IPv6 loopback address is "::1".
241     *
242     * @return {@code true} if this instance represents the loopback address,
243     *         {@code false} otherwise.
244     */
245    @Override
246    public boolean isLoopbackAddress() {
247
248        // The last word must be 1
249        if (ipaddress[15] != 1) {
250            return false;
251        }
252
253        // All other words must be 0
254        for (int i = 0; i < 15; i++) {
255            if (ipaddress[i] != 0) {
256                return false;
257            }
258        }
259
260        return true;
261    }
262
263    /**
264     * Returns whether this address is a link-local address or not. A valid IPv6
265     * link-local address is prefixed with 1111111010.
266     *
267     * @return {@code true} if this instance represents a link-local address,
268     *         {@code false} otherwise.
269     */
270    @Override
271    public boolean isLinkLocalAddress() {
272
273        // the first 10 bits need to be 1111111010 (1018)
274        return (ipaddress[0] == -2) && ((ipaddress[1] & 255) >>> 6) == 2;
275    }
276
277    /**
278     * Returns whether this address is a site-local address or not. A valid IPv6
279     * site-local address is prefixed with 1111111011.
280     *
281     * @return {@code true} if this instance represents a site-local address,
282     *         {@code false} otherwise.
283     */
284    @Override
285    public boolean isSiteLocalAddress() {
286
287        // the first 10 bits need to be 1111111011 (1019)
288        return (ipaddress[0] == -2) && ((ipaddress[1] & 255) >>> 6) == 3;
289    }
290
291    /**
292     * Returns whether this address is a global multicast address or not. A
293     * valid IPv6 global multicast address is 11111111xxxx1110 or FF0E hex.
294     *
295     * @return {@code true} if this instance represents a global multicast
296     *         address, {@code false} otherwise.
297     */
298    @Override
299    public boolean isMCGlobal() {
300        // the first byte should be 0xFF and the lower 4 bits
301        // of the second byte should be 0xE
302        return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 14;
303    }
304
305    /**
306     * Returns whether this address is a node-local multicast address or not. A
307     * valid IPv6 node-local multicast address is prefixed with
308     * 11111111xxxx0001.
309     *
310     * @return {@code true} if this instance represents a node-local multicast
311     *         address, {@code false} otherwise.
312     */
313    @Override
314    public boolean isMCNodeLocal() {
315        // the first byte should be 0xFF and the lower 4 bits
316        // of the second byte should be 0x1
317        return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 1;
318    }
319
320    /**
321     * Returns whether this address is a link-local multicast address or not. A
322     * valid IPv6 link-local multicast address is prefixed with
323     * 11111111xxxx0010.
324     *
325     * @return {@code true} if this instance represents a link-local multicast
326     *         address, {@code false} otherwise.
327     */
328    @Override
329    public boolean isMCLinkLocal() {
330        // the first byte should be 0xFF and the lower 4 bits
331        // of the second byte should be 0x2
332        return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 2;
333    }
334
335    /**
336     * Returns whether this address is a site-local multicast address or not. A
337     * valid IPv6 site-local multicast address is prefixed with
338     * 11111111xxxx0101.
339     *
340     * @return {@code true} if this instance represents a site-local multicast
341     *         address, {@code false} otherwise.
342     */
343    @Override
344    public boolean isMCSiteLocal() {
345        // the first byte should be 0xFF and the lower 4 bits
346        // of the second byte should be 0x5
347        return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 5;
348    }
349
350    /**
351     * Returns whether this address is a organization-local multicast address or
352     * not. A valid IPv6 org-local multicast address is prefixed with
353     * 11111111xxxx1000.
354     *
355     * @return {@code true} if this instance represents a org-local multicast
356     *         address, {@code false} otherwise.
357     */
358    @Override
359    public boolean isMCOrgLocal() {
360        // the first byte should be 0xFF and the lower 4 bits
361        // of the second byte should be 0x8
362        return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 8;
363    }
364
365    /**
366     * Gets the textual representation of this IP address.
367     *
368     * @return the as a dotted string formatted IP address.
369     */
370    @Override
371    public String getHostAddress() {
372        return Inet6Util.createIPAddrStringFromByteArray(ipaddress);
373    }
374
375    /**
376     * Gets the scope id as a number if this address is linked to an interface.
377     * Otherwise returns {@code 0}.
378     *
379     * @return the scope_id of this address or 0 when not linked with an
380     *         interface.
381     */
382    public int getScopeId() {
383        if (scope_id_set) {
384            return scope_id;
385        }
386        return 0;
387    }
388
389    /**
390     * Gets the network interface if this address is instanced with a scoped
391     * network interface. Otherwise returns {@code null}.
392     *
393     * @return the scoped network interface of this address.
394     */
395    public NetworkInterface getScopedInterface() {
396        if (scope_ifname_set) {
397            return scopedIf;
398        }
399        return null;
400    }
401
402    // BEGIN android-removed
403    // public int hashCode() {}
404    // END android-removed
405
406    // BEGIN android-removed
407    // public boolean equals(Object obj) {}
408    // END android-removed
409
410    /**
411     * Returns whether this address is IPv4 compatible or not. An IPv4
412     * compatible address is prefixed with 96 bits of 0's. The last 32-bits are
413     * varied corresponding with the 32-bit IPv4 address space.
414     *
415     * @return {@code true} if this instance represents an IPv4 compatible
416     *         address, {@code false} otherwise.
417     */
418    public boolean isIPv4CompatibleAddress() {
419        for (int i = 0; i < 12; i++) {
420            if (ipaddress[i] != 0) {
421                return false;
422            }
423        }
424        return true;
425    }
426
427    private static final ObjectStreamField[] serialPersistentFields = {
428            new ObjectStreamField("ipaddress", new byte[0].getClass()), //$NON-NLS-1$
429            new ObjectStreamField("scope_id", Integer.TYPE), //$NON-NLS-1$
430            new ObjectStreamField("scope_id_set", Boolean.TYPE), //$NON-NLS-1$
431            new ObjectStreamField("scope_ifname_set", Boolean.TYPE), //$NON-NLS-1$
432            new ObjectStreamField("ifname", String.class), }; //$NON-NLS-1$
433
434    private void writeObject(ObjectOutputStream stream) throws IOException {
435        ObjectOutputStream.PutField fields = stream.putFields();
436        if (ipaddress == null) {
437            fields.put("ipaddress", null); //$NON-NLS-1$
438        } else {
439            fields.put("ipaddress", ipaddress); //$NON-NLS-1$
440        }
441
442        fields.put("scope_id", scope_id); //$NON-NLS-1$
443        fields.put("scope_id_set", scope_id_set); //$NON-NLS-1$
444        fields.put("scope_ifname_set", scope_ifname_set); //$NON-NLS-1$
445        fields.put("ifname", ifname); //$NON-NLS-1$
446        stream.writeFields();
447    }
448
449    private void readObject(ObjectInputStream stream) throws IOException,
450            ClassNotFoundException {
451        ObjectInputStream.GetField fields = stream.readFields();
452        ipaddress = (byte[]) fields.get("ipaddress", null); //$NON-NLS-1$
453        scope_id = fields.get("scope_id", 0); //$NON-NLS-1$
454        scope_id_set = fields.get("scope_id_set", false); //$NON-NLS-1$
455        ifname = (String) fields.get("ifname", null); //$NON-NLS-1$
456        scope_ifname_set = fields.get("scope_ifname_set", false); //$NON-NLS-1$
457        if (scope_ifname_set && null != ifname) {
458            scopedIf = NetworkInterface.getByName(ifname);
459        }
460    }
461
462    /**
463     * Returns a string containing a concise, human-readable description of this
464     * IP address.
465     *
466     * @return the description, as host/address.
467     */
468    @Override
469    public String toString() {
470        if (ifname != null) {
471            return super.toString() + "%" + ifname; //$NON-NLS-1$
472        }
473        if (scope_id != 0) {
474            return super.toString() + "%" + scope_id; //$NON-NLS-1$
475        }
476        return super.toString();
477    }
478}
479