| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.net; |
| |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.ObjectStreamField; |
| import java.util.Enumeration; |
| |
| /** |
| * An IPv6 address. See {@link InetAddress}. |
| */ |
| public final class Inet6Address extends InetAddress { |
| |
| private static final long serialVersionUID = 6880410070516793377L; |
| |
| private static final int AF_INET6 = 10; |
| |
| static final InetAddress ANY = new Inet6Address(new byte[] |
| {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); |
| static final InetAddress LOOPBACK = new Inet6Address(new byte[] |
| {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, "localhost"); |
| |
| int scope_id; |
| |
| boolean scope_id_set; |
| |
| boolean scope_ifname_set; |
| |
| String ifname; |
| |
| /* |
| * scoped interface. |
| */ |
| transient NetworkInterface scopedIf; |
| |
| Inet6Address(byte[] address) { |
| family = AF_INET6; |
| ipaddress = address; |
| scope_id = 0; |
| } |
| |
| Inet6Address(byte[] address, String name) { |
| family = AF_INET6; |
| hostName = name; |
| ipaddress = address; |
| scope_id = 0; |
| } |
| |
| /** |
| * Constructs an {@code InetAddress} representing the {@code address} and |
| * {@code name} and {@code scope_id}. |
| * |
| * @param address |
| * the network address. |
| * @param name |
| * the name associated with the address. |
| * @param scope_id |
| * the scope id for link- or site-local addresses. |
| */ |
| Inet6Address(byte[] address, String name, int scope_id) { |
| family = AF_INET6; |
| hostName = name; |
| ipaddress = address; |
| this.scope_id = scope_id; |
| if (scope_id != 0) { |
| scope_id_set = true; |
| } |
| } |
| |
| /** |
| * Constructs an IPv6 address according to the given {@code host}, {@code |
| * addr} and {@code scope_id}. |
| * |
| * @param host |
| * the host name associated with the address. |
| * @param addr |
| * the network address. |
| * @param scope_id |
| * the scope id for link- or site-local addresses. |
| * @return the Inet6Address instance representing the IP address. |
| * @throws UnknownHostException |
| * if the address is null or has an invalid length. |
| */ |
| public static Inet6Address getByAddress(String host, byte[] addr, |
| int scope_id) throws UnknownHostException { |
| if (addr == null || addr.length != 16) { |
| throw new UnknownHostException("Illegal IPv6 address"); |
| } |
| if (scope_id < 0) { |
| scope_id = 0; |
| } |
| return new Inet6Address(addr, host, scope_id); |
| } |
| |
| /** |
| * Gets an IPv6 address instance according to the given {@code host}, |
| * {@code addr} and {@code nif}. {@code scope_id} is set according to the |
| * given {@code nif} and the {@code addr} type (for example site-local or |
| * link-local). |
| * |
| * @param host |
| * the hostname associated with the address. |
| * @param addr |
| * the network address. |
| * @param nif |
| * the network interface that this address is associated with. |
| * @return the Inet6Address instance representing the IP address. |
| * @throws UnknownHostException |
| * if the address is {@code null} or has an invalid length or |
| * the interface doesn't have a numeric scope id for the given |
| * address type. |
| */ |
| public static Inet6Address getByAddress(String host, byte[] addr, |
| NetworkInterface nif) throws UnknownHostException { |
| |
| Inet6Address address = Inet6Address.getByAddress(host, addr, 0); |
| |
| // if nif is null, nothing needs to be set. |
| if (null == nif) { |
| return address; |
| } |
| |
| // find the first address which matches the type addr, |
| // then set the scope_id, ifname and scopedIf. |
| Enumeration<InetAddress> addressList = nif.getInetAddresses(); |
| while (addressList.hasMoreElements()) { |
| InetAddress ia = addressList.nextElement(); |
| if (ia.getAddress().length == 16) { |
| Inet6Address v6ia = (Inet6Address) ia; |
| boolean isSameType = v6ia.compareLocalType(address); |
| if (isSameType) { |
| address.scope_id_set = true; |
| address.scope_id = v6ia.scope_id; |
| address.scope_ifname_set = true; |
| address.ifname = nif.getName(); |
| address.scopedIf = nif; |
| break; |
| } |
| } |
| } |
| // if no address matches the type of addr, throws an |
| // UnknownHostException. |
| if (!address.scope_id_set) { |
| throw new UnknownHostException("Scope id not found for the given address"); |
| } |
| return address; |
| } |
| |
| /** |
| * Returns {@code true} if one of following cases applies: |
| * <p> |
| * <ol> |
| * <li>both addresses are site local</li> |
| * <li>both addresses are link local</li> |
| * <li>{@code ia} is neither site local nor link local</li> |
| * </ol> |
| */ |
| private boolean compareLocalType(Inet6Address ia) { |
| if (ia.isSiteLocalAddress() && isSiteLocalAddress()) { |
| return true; |
| } |
| if (ia.isLinkLocalAddress() && isLinkLocalAddress()) { |
| return true; |
| } |
| if (!ia.isSiteLocalAddress() && !ia.isLinkLocalAddress()) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Constructs an {@code InetAddress} representing the {@code address} and |
| * {@code scope_id}. |
| * |
| * @param address |
| * the network address. |
| * @param scope_id |
| * the scope id for link- or site-local addresses. |
| */ |
| Inet6Address(byte[] address, int scope_id) { |
| ipaddress = address; |
| this.scope_id = scope_id; |
| if (scope_id != 0) { |
| scope_id_set = true; |
| } |
| } |
| |
| /** |
| * Returns whether this address is an IP multicast address or not. Valid |
| * IPv6 multicast addresses are binary prefixed with 11111111 or FF (hex). |
| * |
| * @return {@code true} if this address is in the multicast group, {@code |
| * false} otherwise. |
| */ |
| @Override |
| public boolean isMulticastAddress() { |
| // Multicast addresses are prefixed with 11111111 (255) |
| return ipaddress[0] == -1; |
| } |
| |
| /** |
| * Returns whether this address is a unspecified wildcard address "::" or |
| * not. |
| * |
| * @return {@code true} if this instance represents a wildcard address, |
| * {@code false} otherwise. |
| */ |
| @Override |
| public boolean isAnyLocalAddress() { |
| for (int i = 0; i < ipaddress.length; i++) { |
| if (ipaddress[i] != 0) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Returns whether this address is the loopback address or not. The only |
| * valid IPv6 loopback address is "::1". |
| * |
| * @return {@code true} if this instance represents the loopback address, |
| * {@code false} otherwise. |
| */ |
| @Override |
| public boolean isLoopbackAddress() { |
| |
| // The last word must be 1 |
| if (ipaddress[15] != 1) { |
| return false; |
| } |
| |
| // All other words must be 0 |
| for (int i = 0; i < 15; i++) { |
| if (ipaddress[i] != 0) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Returns whether this address is a link-local address or not. A valid IPv6 |
| * link-local address is prefixed with 1111111010. |
| * |
| * @return {@code true} if this instance represents a link-local address, |
| * {@code false} otherwise. |
| */ |
| @Override |
| public boolean isLinkLocalAddress() { |
| |
| // the first 10 bits need to be 1111111010 (1018) |
| return (ipaddress[0] == -2) && ((ipaddress[1] & 255) >>> 6) == 2; |
| } |
| |
| /** |
| * Returns whether this address is a site-local address or not. A valid IPv6 |
| * site-local address is prefixed with 1111111011. |
| * |
| * @return {@code true} if this instance represents a site-local address, |
| * {@code false} otherwise. |
| */ |
| @Override |
| public boolean isSiteLocalAddress() { |
| |
| // the first 10 bits need to be 1111111011 (1019) |
| return (ipaddress[0] == -2) && ((ipaddress[1] & 255) >>> 6) == 3; |
| } |
| |
| /** |
| * Returns whether this address is a global multicast address or not. A |
| * valid IPv6 global multicast address is 11111111xxxx1110 or FF0E hex. |
| * |
| * @return {@code true} if this instance represents a global multicast |
| * address, {@code false} otherwise. |
| */ |
| @Override |
| public boolean isMCGlobal() { |
| // the first byte should be 0xFF and the lower 4 bits |
| // of the second byte should be 0xE |
| return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 14; |
| } |
| |
| /** |
| * Returns whether this address is a node-local multicast address or not. A |
| * valid IPv6 node-local multicast address is prefixed with |
| * 11111111xxxx0001. |
| * |
| * @return {@code true} if this instance represents a node-local multicast |
| * address, {@code false} otherwise. |
| */ |
| @Override |
| public boolean isMCNodeLocal() { |
| // the first byte should be 0xFF and the lower 4 bits |
| // of the second byte should be 0x1 |
| return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 1; |
| } |
| |
| /** |
| * Returns whether this address is a link-local multicast address or not. A |
| * valid IPv6 link-local multicast address is prefixed with |
| * 11111111xxxx0010. |
| * |
| * @return {@code true} if this instance represents a link-local multicast |
| * address, {@code false} otherwise. |
| */ |
| @Override |
| public boolean isMCLinkLocal() { |
| // the first byte should be 0xFF and the lower 4 bits |
| // of the second byte should be 0x2 |
| return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 2; |
| } |
| |
| /** |
| * Returns whether this address is a site-local multicast address or not. A |
| * valid IPv6 site-local multicast address is prefixed with |
| * 11111111xxxx0101. |
| * |
| * @return {@code true} if this instance represents a site-local multicast |
| * address, {@code false} otherwise. |
| */ |
| @Override |
| public boolean isMCSiteLocal() { |
| // the first byte should be 0xFF and the lower 4 bits |
| // of the second byte should be 0x5 |
| return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 5; |
| } |
| |
| /** |
| * Returns whether this address is a organization-local multicast address or |
| * not. A valid IPv6 org-local multicast address is prefixed with |
| * 11111111xxxx1000. |
| * |
| * @return {@code true} if this instance represents a org-local multicast |
| * address, {@code false} otherwise. |
| */ |
| @Override |
| public boolean isMCOrgLocal() { |
| // the first byte should be 0xFF and the lower 4 bits |
| // of the second byte should be 0x8 |
| return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 8; |
| } |
| |
| // BEGIN android-removed |
| // public String getHostAddress() { |
| // } |
| // END android-removed |
| |
| /** |
| * Gets the scope id as a number if this address is linked to an interface. |
| * Otherwise returns {@code 0}. |
| * |
| * @return the scope_id of this address or 0 when not linked with an |
| * interface. |
| */ |
| public int getScopeId() { |
| if (scope_id_set) { |
| return scope_id; |
| } |
| return 0; |
| } |
| |
| /** |
| * Gets the network interface if this address is instanced with a scoped |
| * network interface. Otherwise returns {@code null}. |
| * |
| * @return the scoped network interface of this address. |
| */ |
| public NetworkInterface getScopedInterface() { |
| if (scope_ifname_set) { |
| return scopedIf; |
| } |
| return null; |
| } |
| |
| // BEGIN android-removed |
| // public int hashCode() {} |
| // END android-removed |
| |
| // BEGIN android-removed |
| // public boolean equals(Object obj) {} |
| // END android-removed |
| |
| /** |
| * Returns whether this address is IPv4 compatible or not. An IPv4 |
| * compatible address is prefixed with 96 bits of 0's. The last 32-bits are |
| * varied corresponding with the 32-bit IPv4 address space. |
| * |
| * @return {@code true} if this instance represents an IPv4 compatible |
| * address, {@code false} otherwise. |
| */ |
| public boolean isIPv4CompatibleAddress() { |
| for (int i = 0; i < 12; i++) { |
| if (ipaddress[i] != 0) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static final ObjectStreamField[] serialPersistentFields = { |
| new ObjectStreamField("ipaddress", new byte[0].getClass()), |
| new ObjectStreamField("scope_id", Integer.TYPE), |
| new ObjectStreamField("scope_id_set", Boolean.TYPE), |
| new ObjectStreamField("scope_ifname_set", Boolean.TYPE), |
| new ObjectStreamField("ifname", String.class), }; |
| |
| private void writeObject(ObjectOutputStream stream) throws IOException { |
| ObjectOutputStream.PutField fields = stream.putFields(); |
| if (ipaddress == null) { |
| fields.put("ipaddress", null); |
| } else { |
| fields.put("ipaddress", ipaddress); |
| } |
| |
| fields.put("scope_id", scope_id); |
| fields.put("scope_id_set", scope_id_set); |
| fields.put("scope_ifname_set", scope_ifname_set); |
| fields.put("ifname", ifname); |
| stream.writeFields(); |
| } |
| |
| private void readObject(ObjectInputStream stream) throws IOException, |
| ClassNotFoundException { |
| ObjectInputStream.GetField fields = stream.readFields(); |
| ipaddress = (byte[]) fields.get("ipaddress", null); |
| scope_id = fields.get("scope_id", 0); |
| scope_id_set = fields.get("scope_id_set", false); |
| ifname = (String) fields.get("ifname", null); |
| scope_ifname_set = fields.get("scope_ifname_set", false); |
| if (scope_ifname_set && null != ifname) { |
| scopedIf = NetworkInterface.getByName(ifname); |
| } |
| } |
| |
| /** |
| * Returns a string containing a concise, human-readable description of this |
| * IP address. |
| * |
| * @return the description, as host/address. |
| */ |
| @Override |
| public String toString() { |
| if (ifname != null) { |
| return super.toString() + "%" + ifname; |
| } |
| if (scope_id != 0) { |
| return super.toString() + "%" + scope_id; |
| } |
| return super.toString(); |
| } |
| } |