/*******************************************************************************
* Copyright (c) 2017 Rogue Wave Software Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Rogue Wave Software Inc. - initial implementation
*******************************************************************************/
package org.eclipse.php.internal.core.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
/**
* Simple utility class for obtaining & managing workstation network addresses.
*
* @author Bartlomiej Laczkowski
*/
public class NetworkUtil {
private static class DNSJob extends Job {
private String host;
private Inet4Address address;
public DNSJob(String host) {
super(""); //$NON-NLS-1$
this.host = host;
setSystem(true);
setUser(false);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
setAddress((Inet4Address) Inet4Address.getByName(host));
} catch (UnknownHostException e) {
// ignore
}
return Status.OK_STATUS;
}
synchronized Inet4Address getAddress() {
return address;
}
synchronized void setAddress(Inet4Address address) {
this.address = address;
}
}
private static Inet4Address CLASS_A_NETWORK;
private static Inet4Address CLASS_B_NETWORK;
private static Inet4Address CLASS_C_NETWORK;
private static Inet4Address LOOPBACK_NETWORK;
public static Inet4Address LOCALHOST;
static {
try {
CLASS_A_NETWORK = (Inet4Address) InetAddress.getByName("10.0.0.0"); //$NON-NLS-1$
CLASS_B_NETWORK = (Inet4Address) InetAddress.getByName("172.16.0.0"); //$NON-NLS-1$
CLASS_C_NETWORK = (Inet4Address) InetAddress.getByName("192.168.0.0"); //$NON-NLS-1$
LOOPBACK_NETWORK = (Inet4Address) InetAddress.getByName("127.0.0.0"); //$NON-NLS-1$
LOCALHOST = (Inet4Address) InetAddress.getByName("127.0.0.1"); //$NON-NLS-1$
} catch (UnknownHostException e) {
// cannot occur in this particular case because all IPs are valid
}
}
private static final String[] WHAT_IS_MY_IP_CALLBACKS = new String[] { "http://checkip.amazonaws.com", //$NON-NLS-1$
"http://icanhazip.com", //$NON-NLS-1$
"http://www.trackip.net/ip" //$NON-NLS-1$
};
private static final int WHAT_IS_MY_IP_CONNECTION_TIMEOUT = 1000;
private static final int WHAT_IS_MY_IP_READ_TIMEOUT = 5000;
public static final int TYPE_PUBLIC = 0x01;
public static final int TYPE_PRIVATE = 0x02;
public static final int TYPE_LOOPBACK = 0x04;
private static final Pattern IPV4_PATTERN = Pattern
.compile("^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
private NetworkUtil() {
// Private constructor - utility class
}
/**
* Returns all of the workstation network addresses
*
* @return all available network addresses
*/
public static List<Inet4Address> getAllAddresses() {
final List<Inet4Address> descriptors = new ArrayList<Inet4Address>();
Inet4Address publicAddress = getPublicAddress();
if (publicAddress != null)
descriptors.add(getPublicAddress());
descriptors.addAll(getPrivateAddresses());
descriptors.add(LOCALHOST);
return descriptors;
}
/**
* Tries to obtain workstation public IP address.
*
* @return workstation public IP address or <code>null</code> if can not be
* obtained
*/
public static Inet4Address getPublicAddress() {
for (String callback : WHAT_IS_MY_IP_CALLBACKS) {
BufferedReader bufferedReader = null;
try {
URL whatismyip = new URL(callback);
URLConnection connection = whatismyip.openConnection();
connection.setConnectTimeout(WHAT_IS_MY_IP_CONNECTION_TIMEOUT);
connection.setReadTimeout(WHAT_IS_MY_IP_READ_TIMEOUT);
bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String publicIP = bufferedReader.readLine();
return getByName(publicIP, 2000);
} catch (Exception e) {
// ignore
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
// ignore
}
}
}
}
return null;
}
/**
* Tries to obtain LAN private addresses.
*
* @return LAN private addresses
*/
public static List<Inet4Address> getPrivateAddresses() {
List<Inet4Address> descriptors = new ArrayList<Inet4Address>();
try {
// Add localhost first
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
// Skip VMware's
if (networkInterface.getDisplayName().contains("VMware")) { //$NON-NLS-1$
continue;
}
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) {
descriptors.add((Inet4Address) inetAddress);
}
}
}
} catch (Exception e) {
// in this case continue with already detected hosts
}
return descriptors;
}
/**
* Returns type of address (e.g. local, private, public).
*
* @param address
* @return type of address
*/
public static int getType(Inet4Address address) {
if (address.isLoopbackAddress()) {
return TYPE_LOOPBACK;
} else if (isPrivateClassA(address) || isPrivateClassB(address) || isPrivateClassC(address)) {
return TYPE_PRIVATE;
} else {
return TYPE_PUBLIC;
}
}
/**
* Does the same what {@link InetAddress#getByName(String)} but with
* possibility to provide custom timeout.
*
* @param host
* @param timeout
* @return IP address of a host
*/
public static Inet4Address getByName(String host, int timeout) {
DNSJob resolver = new DNSJob(host);
resolver.schedule();
try {
resolver.join(timeout, new NullProgressMonitor());
} catch (OperationCanceledException e) {
// ignore
} catch (InterruptedException e) {
// ignore
}
return resolver.getAddress();
}
/**
* Checks if specified address is in the range of class A private network
* (RFC 1918) with 10.0.0.0/8 mask.
*
* @param address
* @return <code>true</code> if it is in the range; otherwise return
* <code>false</code>
*/
public static boolean isPrivateClassA(Inet4Address address) {
return isInRange(address, CLASS_A_NETWORK, 8);
}
/**
* Checks if specified address is in the range of class B private network
* (RFC 1918) with 172.16.0.0/12 mask.
*
* @param address
* @return <code>true</code> if it is in the range; otherwise return
* <code>false</code>
*/
public static boolean isPrivateClassB(Inet4Address address) {
return isInRange(address, CLASS_B_NETWORK, 12);
}
/**
* Checks if specified address is in the range of class C private network
* (RFC 1918) with 192.168.0.0/16 mask.
*
* @param address
* @return <code>true</code> if it is in the range; otherwise return
* <code>false</code>
*/
public static boolean isPrivateClassC(Inet4Address address) {
return isInRange(address, CLASS_C_NETWORK, 16);
}
/**
* Checks if specified address is in the range of class A private network
* (RFC 1918) with 127.0.0.0/8 mask.
*
* @param address
* @return <code>true</code> if it is in the range; otherwise return
* <code>false</code>
*/
public static boolean isLoopbackAddress(Inet4Address address) {
return isInRange(address, LOOPBACK_NETWORK, 8);
}
/**
* Checks if both of provided addresses belong to the same private network
* class range.
*
* @param address1
* @param address2
* @return <code>true</code> if both addresses belong to the same class
* range, <code>false</code> otherwise
*/
public static boolean isSamePrivateClass(Inet4Address address1, Inet4Address address2) {
if ((isPrivateClassA(address1) && isPrivateClassA(address2))
|| (isPrivateClassB(address1) && isPrivateClassB(address2))
|| (isPrivateClassC(address1) && isPrivateClassC(address2)))
return true;
return false;
}
/**
* Checks if provided addresses are in the same sub-network with the use of
* given mask.
*
* @param address1
* @param address2
* @param mask
* @return <code>true</code> if both addresses belong to the same
* sub-network, <code>false</code> otherwise
*/
public static boolean isSameNetwork(Inet4Address address1, Inet4Address address2, int mask) {
int maskValue = 0xFFFFFFF << (32 - mask);
int address1Value = getAddress(address1.getAddress());
int address2Value = getAddress(address2.getAddress());
return (address1Value & maskValue) == (address2Value & maskValue);
}
/**
* Checks if provided address is IPv4 address.
*
* @param input
* @return <code>true</code> if address is IPv4, <code>false</code>
* otherwise
*/
public static boolean isIPv4Address(final String input) {
return IPV4_PATTERN.matcher(input).matches();
}
private static boolean isInRange(Inet4Address address, Inet4Address subnet, int mask) {
int maskValue = 0xFFFFFFF << (32 - mask);
int subnetValue = getAddress(subnet.getAddress());
int addressValue = getAddress(address.getAddress());
return (subnetValue & subnetValue) == (addressValue & maskValue);
}
private static int getAddress(byte[] bytesAddress) {
return ((((int) bytesAddress[0]) & 0xFF) << 24) | ((((int) bytesAddress[1]) & 0xFF) << 16)
| ((((int) bytesAddress[2]) & 0xFF) << 8) | ((((int) bytesAddress[3]) & 0xFF) << 0);
}
}