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