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