/* * Licensed 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 net.grinder.util; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.python.google.common.net.InetAddresses; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import java.io.IOException; import java.net.*; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import static java.net.NetworkInterface.getNetworkInterfaces; import static org.ngrinder.common.util.ExceptionUtils.processException; import static org.ngrinder.common.util.NoOp.noOp; /** * Common network utility. This contains very careful implementation to detect current machine's ip. * There are the following cases which block to get the appropriate ip. * <p/> * <ul> * <li>If there are VM in the same machine</li> * <li>If /etc/hosts are not very well specified</li> * </ul> * * @author JunHo Yoon * @author Mavlarn * @since 3.0 */ public abstract class NetworkUtils { private static final Logger LOGGER = LoggerFactory.getLogger(NetworkUtils.class); public static String DEFAULT_LOCAL_HOST_ADDRESS = getLocalHostAddress(); public static String DEFAULT_LOCAL_HOST_NAME = getLocalHostName(); public static List<InetAddress> DEFAULT_LOCAL_ADDRESSES = getAllLocalNonLoopbackAddresses(false); /** * Get the local host address, try to get actual IP. * * @return ip form of host address */ public static String getLocalHostAddress() { InetAddress localHost = null; try { localHost = InetAddress.getLocalHost(); } catch (Exception e) { LOGGER.error("Error while get localhost address", e); } if (localHost != null && !localHost.isLoopbackAddress()) { return localHost.getHostAddress(); } return getLocalHostAddress("www.google.com", 80); } /** * Get local address by connecting to a server. * * @param byConnecting the server address to connect. * @param port the port to connect * @return IP address local IP address */ static String getLocalHostAddress(String byConnecting, int port) { InetAddress addr = getLocalInetAddress(byConnecting, port); if (addr != null) { return addr.getHostAddress(); } else { // It's final... return "127.0.0.1"; } } /** * Get local host name by connecting to a server. * * @param byConnecting the server address to connect. * @param port the port to connect * @return localhost name. if fails, return "localhost" */ static String getLocalHostName(String byConnecting, int port) { InetAddress addr = getLocalInetAddress(byConnecting, port); if (addr != null) { return addr.getHostName(); } else { return "localhost"; } } static InetAddress getLocalInetAddress(String byConnecting, int port) { InetAddress addr = getAddressWithSocket(byConnecting, port); if (addr == null) { addr = getAddressWithSocket("www.baidu.com", 80); } if (addr == null) { try { addr = getFirstNonLoopbackAddress(true, false); } catch (SocketException e2) { addr = null; } } return addr; } public static InetAddress getAddressWithSocket(String byConnecting, int port) { Socket s = new Socket(); try { if (tryConnection(byConnecting, port, s)) { return s.getLocalAddress(); } } finally { IOUtils.closeQuietly(s); } return null; } public static boolean tryConnection(String byConnecting, int port, Socket socket) { try { socket.connect(new InetSocketAddress(byConnecting, port), 2000); // 2 seconds timeout } catch (Exception e) { return false; } return true; } static InetAddress getFirstNonLoopbackAddress(boolean preferIpv4, boolean preferIPv6) throws SocketException { Enumeration<?> en = getNetworkInterfaces(); while (en.hasMoreElements()) { NetworkInterface i = (NetworkInterface) en.nextElement(); if (!i.isUp()) { continue; } for (Enumeration<?> en2 = i.getInetAddresses(); en2.hasMoreElements(); ) { InetAddress addr = (InetAddress) en2.nextElement(); if (!addr.isLoopbackAddress()) { if (addr instanceof Inet4Address) { if (preferIPv6) { continue; } return addr; } if (addr instanceof Inet6Address) { if (preferIpv4) { continue; } return addr; } } } } return null; } /** * Get local host name. On some platform, InetAddress.getLocalHost().getHostName() will return * "localhost". If the /etc/hosts file is not set properly, it will return "localhost" or throw * exception. So, at this circumstance, we will get the address by connecting a network address. * * @return local host name */ public static String getLocalHostName() { String hostName = null; try { hostName = InetAddress.getLocalHost().getHostName(); } catch (Exception e) { LOGGER.error("Error while get localhost name", e); } if (hostName != null && !"localhost".equals(hostName)) { return hostName; } return getLocalHostName("www.google.com", 80); } /** * Get the IP addresses from host name. * * @param host host * @return {@link InetAddress} array */ public static InetAddress[] getIpsFromHost(String host) { try { return InetAddress.getAllByName(host); } catch (UnknownHostException e) { LOGGER.error("Error while get localhost name for {}", host, e); return new InetAddress[]{}; } } /** * Get the available ports. * * @param size port size * @param from port number starting from * @return port list */ public static List<Integer> getAvailablePorts(String ip, int size, int from, int limit) { List<Integer> ports = new ArrayList<Integer>(size); int freeSocket; InetAddress inetAddress = null; if (StringUtils.isNotBlank(ip)) { try { inetAddress = InetAddress.getByName(ip); } catch (Exception e) { noOp(); } } for (int i = 0; i < size; i++) { freeSocket = checkPortAvailability(inetAddress, from, limit); ports.add(freeSocket); from = freeSocket + 1; } return ports; } /** * Get a available port greater than the given port. * * @param scanStartPort port scan from * @return min port available from scanStartPort */ public static int checkPortAvailability(InetAddress inetAddress, int scanStartPort, int limit) { while (true) { if (checkExactPortAvailability(inetAddress, scanStartPort)) { return scanStartPort; } if (scanStartPort++ > limit) { throw processException("no port is available"); } } } /** * Check if the given port is available. * * @param inetAddress address to be bound * @param port port to be checked * @return true if available */ private static boolean checkExactPortAvailability(InetAddress inetAddress, int port) { ServerSocket socket = null; try { if (inetAddress == null) { socket = new ServerSocket(port); } else { socket = new ServerSocket(port, 1, inetAddress); } return true; } catch (IOException e) { return false; } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { // FALL THROUGH noOp(); } } } } /** * Check if the current machine support IP6 * * @return true if the IP6 is supported. */ public static boolean isIP6Supported() { final Enumeration<NetworkInterface> networkInterfaces; try { networkInterfaces = getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { final NetworkInterface networkInterface = networkInterfaces.nextElement(); if (networkInterface.isUp() && !networkInterface.isLoopback() && !networkInterface.isPointToPoint()) { final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses(); while (inetAddresses.hasMoreElements()) { final InetAddress inetAddress = inetAddresses.nextElement(); if (inetAddress instanceof Inet6Address) { return true; } } } } } catch (SocketException e) { LOGGER.error("Error while resolving non look back local addresses.", e); } return false; } /** * Get the all IP binding address. * * @return [::] if IP6 is supported, "0.0.0.0" otherwise. */ public static String getAllPBindingAddress() { return isIP6Supported() ? "[::]" : "0.0.0.0"; } public static class IPPortPair { private InetAddress ip; private final int port; public IPPortPair(String ip, int port) { try { this.ip = InetAddress.getByName(ip); } catch (UnknownHostException e) { LOGGER.error("{} is not accessible ip"); } this.port = port; } public boolean isValid() { return ip != null; } public int getPort() { return port; } public String getIP() { return ip.getHostAddress(); } public String getFormattedIP() { if (isIP6()) { return "[" + ip.getHostAddress() + "]"; } else { return ip.getHostAddress(); } } @Override public String toString() { return getFormattedIP() + ":" + this.port; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) { return false; } IPPortPair that = (IPPortPair) o; return port == that.port && !(ip != null ? !ip.equals(that.ip) : that.ip != null); } @Override public int hashCode() { int result = ip != null ? ip.hashCode() : 0; result = 31 * result + port; return result; } public boolean isLocalHost() { if (ip == null) { return false; } if (ip.isAnyLocalAddress() || ip.isLoopbackAddress() || ip.isLinkLocalAddress()) { return true; } try { return NetworkInterface.getByInetAddress(ip) != null; } catch (SocketException e) { return false; } } public boolean isIP6() { return ip instanceof Inet6Address; } } /** * Convert the given string to ip and port pair. * <p/> * This supports IP6 and IP4. * <p/> * <ul> * <li>127.0.0.1:30 ==> 127.0.0.1 and 30</li> * <li>2001:0:9d38:90d7:469:1f94:f5bf:cf5d:30 ==> 2001:0:9d38:90d7:469:1f94:f5bf:cf5d and 30</li> * <li>[2001:0:9d38:90d7:469:1f94:f5bf:cf5d]:30 ==> 2001:0:9d38:90d7:469:1f94:f5bf:cf5d and 30</li> * </ul> * * @param ipPortString textual representation of ip and port pair * @param defaultPort default port used when port is invisible. * @return ip and port pair */ public static IPPortPair convertIPAndPortPair(String ipPortString, int defaultPort) { // If it's the scoped IP6 address ipPortString = removeScopedMarkerFromIP(ipPortString); if (InetAddresses.isInetAddress(ipPortString)) { return new IPPortPair(ipPortString, defaultPort); } final int i = ipPortString.lastIndexOf(":"); String ipPart = ipPortString; int portPart = defaultPort; if (i != -1) { portPart = NumberUtils.toInt(ipPortString.substring(i + 1)); ipPart = ipPortString.substring(0, i); } return new IPPortPair(getIP(ipPart), portPart); } public static String removeScopedMarkerFromIP(String ip) { if (StringUtils.isNotEmpty(ip) && ip.contains("%")) { ip = ip.substring(0, ip.lastIndexOf("%")); } return ip; } /** * Get IP form the given string. * <p/> * If the given ipOrHost is host name, it tries to turn it into IP. * If the host name is not available, it returns 127.0.0.1 instead. * ff * * @param ipOrHost textual representation of ip or host name * @return ip */ public static String getIP(String ipOrHost) { String ip = ipOrHost; if (InetAddresses.isInetAddress(ip)) { return ip; } try { ip = InetAddress.getByName(ipOrHost).getHostAddress(); } catch (UnknownHostException e) { ip = "127.0.0.1"; LOGGER.error("Error while resolving {} to IP. Use {} instead.", ipOrHost, ip); LOGGER.debug("Details : ", e); } return ip; } private static List<InetAddress> getAllLocalNonLoopbackAddresses(boolean onlyIPv4) { List<InetAddress> addresses = new ArrayList<InetAddress>(); final Enumeration<NetworkInterface> networkInterfaces; try { networkInterfaces = getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { final NetworkInterface networkInterface = networkInterfaces.nextElement(); if (networkInterface.isUp()) { final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses(); while (inetAddresses.hasMoreElements()) { final InetAddress inetAddress = inetAddresses.nextElement(); if (onlyIPv4 && inetAddress instanceof Inet6Address) { continue; } if (!inetAddress.isLoopbackAddress()) { addresses.add(inetAddress); } } } } } catch (SocketException e) { LOGGER.error("Error while resolving non look back local addresses.", e); } return addresses; } public static List<String> getDnsServers() throws NamingException { Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory"); DirContext ctx = null; List<String> dnsServers = new ArrayList<String>(); try { ctx = new InitialDirContext(env); String dnsString = (String) ctx.getEnvironment().get("java.naming.provider.url"); for (String each : dnsString.split(" ")) { dnsServers.add(each.replace("dns://", "")); } } catch (Exception e) { NoOp.noOp(); } finally { if (ctx != null) { ctx.close(); } } return dnsServers; } }