Home | History | Annotate | Download | only in net
      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      */
     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      */
     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      */
    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      */
    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 
    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      */
    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 
    190     @Override public boolean isLinkLocalAddress() {
    191         return ((ipaddress[0] & 0xff) == 0xfe) && ((ipaddress[1] & 0xc0) == 0x80); // fe80:/10
    192     }
    193 
    194     @Override public boolean isLoopbackAddress() {
    195         return Arrays.equals(ipaddress, Inet6Address.LOOPBACK.ipaddress);
    196     }
    197 
    198     @Override public boolean isMCGlobal() {
    199         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x0e); // ffxe:/16
    200     }
    201 
    202     @Override public boolean isMCLinkLocal() {
    203         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x02); // ffx2:/16
    204     }
    205 
    206     @Override public boolean isMCNodeLocal() {
    207         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x01); // ffx1:/16
    208     }
    209 
    210     @Override public boolean isMCOrgLocal() {
    211         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x08); // ffx8:/16
    212     }
    213 
    214     @Override public boolean isMCSiteLocal() {
    215         return ((ipaddress[0] & 0xff) == 0xff) && ((ipaddress[1] & 0x0f) == 0x05); // ffx5:/16
    216     }
    217 
    218     @Override public boolean isMulticastAddress() {
    219         return ((ipaddress[0] & 0xff) == 0xff); // ff::/8
    220     }
    221 
    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      */
    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      */
    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 
    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 
    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 
    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