/**************************************************************************
* Copyright (c) 2007, 2009, 2015, 2016 by Chris Gray, KIFFER Ltd. *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of KIFFER Ltd nor the names of other contributors *
* may be used to endorse or promote products derived from this *
* software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL KIFFER LTD OR OTHER CONTRIBUTORS BE LIABLE FOR ANY *
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS *
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING *
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
* POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
/*
** Mika caches DNS lookup results as follows:
** <ul><li>If the <b>security</b> property <tt>inetaddress.cache.ttl</tt>
** is defined, we use this as the cache entry lifetime (in seconds).
** <li>Failing that, if the <b>system</b> property
** <tt>mika.inetaddress.cache.ttl</tt> property is defined,
** we use this instead.
** <li>Otherwise the cache entry lifetime defaults to 86400 seconds
** (24 hours), in keeping with general DNS practice.
** </ul>
** Mika does not implement a negative address cache.
*/
package java.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.Date;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Properties;
public class InetAddress implements Serializable {
private static final long serialVersionUID = 3286316764910316507L;
static final int TYPE_LOOPBACK = 1;
static final int TYPE_IPV4 = 2;
static final int TYPE_IPV6 = 10;
private static InetAddress ownAddress;
/**
** Lifetime of positive_cache entries, in milliseconds (0 means infinity).
*/
static int positive_cache_ttl = GetSecurityProperty.INETADDRESS_CACHE_TTL;
/**
** A subclass of Hashtable used to implement the positive cache.
*/
static class IAddrPositiveCache extends Hashtable {
/**
** Convert the name (key) to lower case; don't overwrite an immortal
** entry with an otherwise identical mortal entry.
*/
public synchronized Object put(String name, Object value) {
String namelc = name.toLowerCase();
IAddrCacheEntry newentry = (IAddrCacheEntry)value;
InetAddress ia = newentry.addr;
IAddrCacheEntry existing = (IAddrCacheEntry)super.get(namelc);
if (existing == null || !existing.addr.equals(ia) || existing.expiryTime < newentry.expiryTime) {
super.put(namelc, value);
}
return existing;
}
/**
** Convert the name to lower case, and if the value indexed has expired
** then remove it and return null.
*/
public synchronized Object get(String name) {
String namelc = name.toLowerCase();
IAddrCacheEntry entry = (IAddrCacheEntry)super.get(namelc);
if (entry == null) {
return null;
}
if (entry.expiryTime < System.currentTimeMillis()) {
super.remove(namelc);
return null;
}
return entry;
}
}
/**
** Inner class which represents an (InetAddress, long expiryTime) pair.
*/
static class IAddrCacheEntry {
InetAddress addr;
long expiryTime;
IAddrCacheEntry(InetAddress addr, long expiryTime) {
this.addr = addr;
this.expiryTime = expiryTime;
}
IAddrCacheEntry(InetAddress addr) {
this(addr, System.currentTimeMillis() + positive_cache_ttl);
}
public String toString() {
return "IAddrCacheEntry with address " + addr + ", expiry time " + new Date(expiryTime).toString();
}
}
/**
** Cache of positive results. Key is the hostname, value is an IAddrCacheEntry.
*/
static final IAddrPositiveCache positive_cache = new IAddrPositiveCache();
static final InetAddress loopbackAddress = new Inet4Address(0x7f000001,"localhost","127.0.0.1", 0);
static final InetAddress allZeroAddress = new Inet4Address(0,"0.0.0.0","0.0.0.0", 0);
static {
try {
ownAddress = new Inet4Address(getLocalName(), 0);
}
catch (UnknownHostException uhe) {
//just to make sure ownAddress will not be null ...
ownAddress = loopbackAddress;
}
IAddrCacheEntry iace1= new IAddrCacheEntry(loopbackAddress, Long.MAX_VALUE);
IAddrCacheEntry iace2= new IAddrCacheEntry(ownAddress, Long.MAX_VALUE);
positive_cache.put("localhost", iace1);
positive_cache.put("127.0.0.1", iace1);
positive_cache.put(getLocalName(), iace2);
positive_cache.put(ownAddress.getHostAddress(), iace2);
Properties hostProperties = new Properties();
try {
InputStream hostpropstream = ClassLoader.getSystemResourceAsStream("mika.hosts");
if (hostpropstream != null) {
hostProperties.load(hostpropstream);
Enumeration names = hostProperties.propertyNames();
while (names.hasMoreElements()) {
String name = (String)names.nextElement();
String addr = hostProperties.getProperty(name);
int intaddr = 0;
int firstpoint = addr.indexOf('.');
int secondpoint = -1;
int thirdpoint = -1;
if (firstpoint >= 0) {
secondpoint = addr.indexOf('.', firstpoint + 1);
}
if (secondpoint >= 0) {
thirdpoint = addr.indexOf('.', secondpoint + 1);
}
if (firstpoint >= 0 && secondpoint >= 0 && thirdpoint >= 0) {
try {
int octet1 = Integer.parseInt(addr.substring(0, firstpoint));
int octet2 = Integer.parseInt(addr.substring(firstpoint + 1, secondpoint));
int octet3 = Integer.parseInt(addr.substring(secondpoint + 1, thirdpoint));
int octet4 = Integer.parseInt(addr.substring(thirdpoint + 1));
if (octet1 >= 0 && octet1 < 256 && octet2 >= 0 && octet2 < 256 && octet3 >= 0 && octet3 < 256 && octet4 >= 0 && octet4 < 256) {
intaddr = ((octet1 * 256 + octet2) * 256 + octet3) * 256 + octet4;
positive_cache.put(name, new IAddrCacheEntry(new Inet4Address(intaddr, name, addr, 0), Long.MAX_VALUE));
}
else {
System.err.println("Error in mika.hosts: octet out of range in ip address: " + addr);
}
}
catch (NumberFormatException nfe) {
System.err.println("Error in mika.hosts: bad digit in ip address: " + addr);
}
}
else {
System.err.println("Error in mika.hosts: missing '.' in ip address: " + addr);
}
}
}
}
catch (IOException e) {}
}
/**
** InetAddress hosts static permission checks to avoid code duplication.
** Since all Socket classes use InetAddress this does NOT unneeded class loading ...
*/
static void permissionCheck(String host) {
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkConnect(host, -1);
}
}
}
static void factoryCheck() {
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkSetFactory();
}
}
}
static void listenCheck(int port) {
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkListen(port);
}
}
}
static void acceptCheck(String host, int port) {
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkAccept(host, port);
}
}
}
static void connectCheck(String host, int port) {
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkConnect(host, port);
}
}
}
/**
** Address family, as dictated by serialized form.
*/
int family;
/**
** IP address in integer form, as dictated by serialized form.
*/
int address;
/**
** Host name, as dictated by serialized form.
*/
String hostName;
/**
* Canonical host name (lazy look-up)
*/
transient String canonicalHostName;
/**
** Dotted-string form of 'address'. Also ised as a lock object for lazily looking up canonicalHostName.
*/
transient String ipAddressString;
/**
** Creates an empty InetAddress used by peek. For all other purposes
** a constructor of Inet4Address or Inet6Address is used.
*/
InetAddress(){}
/**
* Compares this object against the specified object
*/
public boolean equals(Object obj) {
if(!(obj instanceof InetAddress)){
return false;
}
InetAddress other = (InetAddress)obj;
return (this.address == other.address);
}
/**
* Returns the raw IP address of this InetAddress object
*/
public byte[] getAddress() {
byte[] octets = new byte[4];
octets[3] = (byte)address;
octets[2] = (byte)(address>>>8);
octets[1] = (byte)(address>>>16);
octets[0] = (byte)(address>>>24);
return octets;
}
/**
* Determines all the hosts IP addresses, given the hostname;
*/
public static InetAddress[] getAllByName (String host) throws UnknownHostException
{
if (host==null || host.length()==0) {
throw new UnknownHostException();
}
//the check is done in getByName
//permissionCheck(host);
// FOR NOW, lets do the same thing as for getByName
InetAddress[] temp = new InetAddress[1];
temp[0] = getByName(host);
return temp;
}
/**
* Determine the IP address, given the hostname
*/
public static InetAddress getByName(String host) throws UnknownHostException {
if (host==null || host.length() == 0){
return loopbackAddress;
}
try {
permissionCheck(host);
}
catch (IllegalArgumentException iae) {
throw new UnknownHostException(host);
}
return getByNameImpl(host);
}
static InetAddress getByNameImpl(String host) throws UnknownHostException {
synchronized(positive_cache) {
String hostlc = host.toLowerCase();
IAddrCacheEntry cached = (IAddrCacheEntry)positive_cache.get(hostlc);
if(cached != null){
return cached.addr;
}
}
if(host.indexOf(':') != -1) {
return new Inet6Address(host);
}
else {
return new Inet4Address(host, positive_cache_ttl);
}
}
/**
* Returns IP address 'xxx.xxx.xxx.xxx'
*/
public String getHostAddress() {
if(ipAddressString == null){
ipAddressString = intToIPString(address);
}
return ipAddressString;
}
private static String intToIPString(int address){
StringBuffer result = new StringBuffer(16);
result.append((address>>>24 & 0x0ff));
result.append('.');
result.append((address>>>16 & 0x0ff));
result.append('.');
result.append((address>>>8 & 0x0ff));
result.append('.');
result.append((address & 0x0ff));
return result.toString();
}
static InetAddress createInetAddress(int ip){
String ipname = intToIPString(ip);
InetAddress ia = null;
synchronized (positive_cache) {
Object cached = positive_cache.get(ipname);
if(cached != null){
ia = ((IAddrCacheEntry)cached).addr;
}
}
if(ia == null){
try {
return new Inet4Address(ipname, positive_cache_ttl);
} catch(UnknownHostException uhe){
//this exception cannot be thrown on a 'real' IP-string, but to be sure ...
return new Inet4Address(ip,ipname,ipname, 0);
}
}
return ia;
}
/**
* Returns the hostname for this address
*/
public String getHostName() {
if(hostName == null){
if(getHostAddress() != null && lookupName(this)) {
hostName = ipAddressString;
}
else {
positive_cache.put(hostName, new IAddrCacheEntry(this));
}
}
permissionCheck(hostName);
return hostName;
}
/**
* Returns the localhost
*/
public static InetAddress getLocalHost()
throws UnknownHostException
{
try {
permissionCheck(getLocalName());
return ownAddress;
}
catch (SecurityException se) {
return loopbackAddress;
}
}
/**
* returns the hashcode for this IP address
*/
public int hashCode() {
return address;
}
/**
* Checks if the InetAddress is a multicast address
* IP Multicast Addresses (Class D) range from 224.0.0.0 to 239.255.255.255
*/
public boolean isMulticastAddress () {
byte[] ip = getAddress();
return (((ip[0]&0xFF) >223) && ((ip[0]&0xFF)<240));
}
/**
* InetAddress -> String conversion
*/
public String toString () {
String name = "";
if (hostName != null) {
name = hostName;
}
else if (ipAddressString != null && !ipAddressString.equals(intToIPString(address))) {
name = ipAddressString;
}
return name + "/" + getHostAddress();
}
/*
public boolean isIPv6 () {
return family == TYPE_IPV6;
}
*/
/**
* Returns the fully qualified hostname corresponding to this IP address.
*/
public synchronized String getCanonicalHostName() {
if (canonicalHostName != null) {
return canonicalHostName;
}
/*
* Hack to get the most likely IP address for a given hostname.
* We use ping(8) for this because it is available just about anywhere;
* if we used a more "appropriate" tool like nslookup or dig or ...
* then we would need a fall-back for when it is not available.
* TODO: replace with some pukka native code.
*/
try {
final Process p = Runtime.getRuntime().exec("ping -c1 -w1 " + getLocalName());
final String firstLineOfOutput = new BufferedReader(new InputStreamReader(p.getInputStream())).readLine();
final int leftParen = firstLineOfOutput.indexOf("(");
final int rightParen = firstLineOfOutput.indexOf(")");
final String ip = firstLineOfOutput.substring(rightParen + 1, leftParen);
canonicalHostName = null; // TODO
//return getHostByAddrImpl(this).hostName;
} catch (Exception ex) {
return getHostAddress();
}
return canonicalHostName;
}
/**
** should only be called when ipAddressString is not null
*/
private static synchronized native boolean lookupName(InetAddress ia);
/**
** will find an IP address for the name
*/
native void createInetAddress(String hostname) throws UnknownHostException;
/**
** will find the name of this host ...
*/
private static native String getLocalName();
/* Remaining code adapted from Harmony/Android code by Chris Gray 2016 */
/*
* 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.
*/
/**
* Equivalent to {@code getByAddress(null, ipAddress)}. Handy for addresses with
* no associated hostname.
*/
public static InetAddress getByAddress(byte[] ipAddress) throws UnknownHostException {
return getByAddress(null, ipAddress, 0);
}
/**
* Returns an {@code InetAddress} corresponding to the given network-order
* bytes {@code ipAddress} and {@code scopeId}.
*
* <p>For an IPv4 address, the byte array must be of length 4.
* For IPv6, the byte array must be of length 16. Any other length will cause an {@code
* UnknownHostException}.
*
* <p>No reverse lookup is performed. The given {@code hostName} (which may be null) is
* associated with the new {@code InetAddress} with no validation done.
*
* <p>(Note that numeric addresses such as {@code "127.0.0.1"} are names for the
* purposes of this API. Most callers probably want {@link #getAllByName} instead.)
*
* @throws UnknownHostException if {@code ipAddress} is null or the wrong length.
*/
public static InetAddress getByAddress(String hostName, byte[] ipAddress) throws UnknownHostException {
return getByAddress(hostName, ipAddress, 0);
}
private static InetAddress getByAddress(String hostName, byte[] ipAddress, int scopeId) throws UnknownHostException {
if (ipAddress == null) {
throw new UnknownHostException("ipAddress == null");
}
if (ipAddress.length == 4) {
return new Inet4Address((byte[]) ipAddress.clone(), hostName, positive_cache_ttl);
} else if (ipAddress.length == 16) {
final String dotted = (ipAddress[0] & 0xff) + "." + (ipAddress[1] & 0xff) + "." + (ipAddress[2] & 0xff) + "." + (ipAddress[3] & 0xff);
synchronized(positive_cache) {
IAddrCacheEntry cached = (IAddrCacheEntry)positive_cache.get(dotted);
if(cached != null){
return cached.addr;
}
}
// First check to see if the address is an IPv6-mapped
// IPv4 address. If it is, then we can make it a IPv4
// address, otherwise, we'll create an IPv6 address.
if (isIPv4MappedAddress(ipAddress)) {
return new Inet4Address(ipv4MappedToIPv4(ipAddress), hostName, positive_cache_ttl);
} else {
// FIXME hacked to fit Mika's Inet6Address constructor
// return new Inet6Address((byte[]) ipAddress.clone(), hostName, scopeId, positive_cache_ttl);
StringBuffer sb = new StringBuffer(Integer.toHexString((ipAddress[0] & 0xff) * 256 + (ipAddress[1] & 0xff)));
for (int i = 2; i < 16; i += 2) {
sb.append(':');
sb.append(Integer.toHexString((ipAddress[i] & 0xff) * 256 + (ipAddress[i + 1] & 0xff)));
}
return new Inet6Address(sb.toString());
}
} else {
throw badAddressLength(ipAddress);
}
}
private static UnknownHostException badAddressLength(byte[] bytes) throws UnknownHostException {
StringBuffer sb = new StringBuffer("Address is neither 4 or 16 bytes: [");
sb.append(bytes.length == 0 ? "<empty>" : Integer.toString(bytes[0] & 0xff));
for (int i = 1; i < bytes.length; ++i) {
sb.append(", ");
sb.append(bytes[i] & 0xff);
}
sb.append(']');
throw new UnknownHostException("Address is neither 4 or 16 bytes: " + sb);
}
private static boolean isIPv4MappedAddress(byte[] ipAddress) {
// Check if the address matches ::FFFF:d.d.d.d
// The first 10 bytes are 0. The next to are -1 (FF).
// The last 4 bytes are varied.
if (ipAddress == null || ipAddress.length != 16) {
return false;
}
for (int i = 0; i < 10; i++) {
if (ipAddress[i] != 0) {
return false;
}
}
if (ipAddress[10] != -1 || ipAddress[11] != -1) {
return false;
}
return true;
}
private static byte[] ipv4MappedToIPv4(byte[] mappedAddress) {
byte[] ipv4Address = new byte[4];
for (int i = 0; i < 4; i++) {
ipv4Address[i] = mappedAddress[12 + i];
}
return ipv4Address;
}
/**
* Returns whether this is a wildcard address or not. This implementation
* returns always {@code false}.
*
* @return {@code true} if this instance represents a wildcard address,
* {@code false} otherwise.
*/
public boolean isAnyLocalAddress() {
return false;
}
/**
* Returns whether this address is a link-local address or not. This
* implementation returns always {@code false}.
* <p>
* Valid IPv6 link-local addresses are FE80::0 through to
* FEBF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF.
* <p>
* There are no valid IPv4 link-local addresses.
*
* @return {@code true} if this instance represents a link-local address,
* {@code false} otherwise.
*/
public boolean isLinkLocalAddress() {
return false;
}
/**
* Returns whether this address is a loopback address or not. This
* implementation returns always {@code false}. Valid IPv4 loopback
* addresses are 127.d.d.d The only valid IPv6 loopback address is ::1.
*
* @return {@code true} if this instance represents a loopback address,
* {@code false} otherwise.
*/
public boolean isLoopbackAddress() {
return false;
}
/**
* Returns whether this address is a global multicast address or not. This
* implementation returns always {@code false}.
* <p>
* Valid IPv6 link-global multicast addresses are FFxE:/112 where x is a set
* of flags, and the additional 112 bits make up the global multicast
* address space.
* <p>
* Valid IPv4 global multicast addresses are between: 224.0.1.0 to
* 238.255.255.255.
*
* @return {@code true} if this instance represents a global multicast
* address, {@code false} otherwise.
*/
public boolean isMCGlobal() {
return false;
}
/**
* Returns whether this address is a link-local multicast address or not.
* This implementation returns always {@code false}.
* <p>
* Valid IPv6 link-local multicast addresses are FFx2:/112 where x is a set
* of flags, and the additional 112 bits make up the link-local multicast
* address space.
* <p>
* Valid IPv4 link-local addresses are between: 224.0.0.0 to 224.0.0.255
*
* @return {@code true} if this instance represents a link-local multicast
* address, {@code false} otherwise.
*/
public boolean isMCLinkLocal() {
return false;
}
/**
* Returns whether this address is a node-local multicast address or not.
* This implementation returns always {@code false}.
* <p>
* Valid IPv6 node-local multicast addresses are FFx1:/112 where x is a set
* of flags, and the additional 112 bits make up the node-local multicast
* address space.
* <p>
* There are no valid IPv4 node-local multicast addresses.
*
* @return {@code true} if this instance represents a node-local multicast
* address, {@code false} otherwise.
*/
public boolean isMCNodeLocal() {
return false;
}
/**
* Returns whether this address is a organization-local multicast address or
* not. This implementation returns always {@code false}.
* <p>
* Valid IPv6 organization-local multicast addresses are FFx8:/112 where x
* is a set of flags, and the additional 112 bits make up the
* organization-local multicast address space.
* <p>
* Valid IPv4 organization-local addresses are between: 239.192.0.0 to
* 239.251.255.255
*
* @return {@code true} if this instance represents a organization-local
* multicast address, {@code false} otherwise.
*/
public boolean isMCOrgLocal() {
return false;
}
/**
* Returns whether this address is a site-local multicast address or not.
* This implementation returns always {@code false}.
* <p>
* Valid IPv6 site-local multicast addresses are FFx5:/112 where x is a set
* of flags, and the additional 112 bits make up the site-local multicast
* address space.
* <p>
* Valid IPv4 site-local addresses are between: 239.252.0.0 to
* 239.255.255.255
*
* @return {@code true} if this instance represents a site-local multicast
* address, {@code false} otherwise.
*/
public boolean isMCSiteLocal() {
return false;
}
/**
* Returns whether this address is a site-local address or not. This
* implementation returns always {@code false}.
* <p>
* Valid IPv6 site-local addresses are FEC0::0 through to
* FEFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF.
* <p>
* There are no valid IPv4 site-local addresses.
*
* @return {@code true} if this instance represents a site-local address,
* {@code false} otherwise.
*/
public boolean isSiteLocalAddress() {
return false;
}
}