Java tutorial
/** * Copyright (c) 2010-2017 by the respective copyright holders. * * 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 */ package org.openhab.binding.network.internal.utils; import java.io.IOException; import java.net.ConnectException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.NoRouteToHostException; import java.net.PortUnreachableException; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.apache.commons.net.util.SubnetUtils; import org.eclipse.smarthome.io.net.exec.ExecUtil; /** * Network utility functions for pinging and for determining all interfaces and assigned IP addresses. * * @author David Graeff <david.graeff@web.de> */ public class NetworkUtils { /** * Gets every IPv4 Address on each Interface except the loopback * The Address format is ip/subnet * * @return The collected IPv4 Addresses */ public Set<String> getInterfaceIPs() { Set<String> interfaceIPs = new HashSet<>(); Enumeration<NetworkInterface> interfaces; try { interfaces = NetworkInterface.getNetworkInterfaces(); } catch (SocketException ignored) { // If we are not allowed to enumerate, we return an empty result set. return interfaceIPs; } // For each interface ... for (Enumeration<NetworkInterface> en = interfaces; en.hasMoreElements();) { NetworkInterface networkInterface = en.nextElement(); boolean isLoopback = true; try { isLoopback = networkInterface.isLoopback(); } catch (SocketException ignored) { } if (!isLoopback) { // .. and for each address ... for (Iterator<InterfaceAddress> it = networkInterface.getInterfaceAddresses().iterator(); it .hasNext();) { // ... get IP and Subnet InterfaceAddress interfaceAddress = it.next(); interfaceIPs.add(interfaceAddress.getAddress().getHostAddress() + "/" + interfaceAddress.getNetworkPrefixLength()); } } } return interfaceIPs; } /** * Get a set of all interface names. * * @return Set of interface names */ public Set<String> getInterfaceNames() { Set<String> result = new HashSet<>(); try { // For each interface ... for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en .hasMoreElements();) { NetworkInterface networkInterface = en.nextElement(); if (!networkInterface.isLoopback()) { result.add(networkInterface.getName()); } } } catch (SocketException ignored) { // If we are not allowed to enumerate, we return an empty result set. } return result; } /** * Determines every IP which can be assigned on all available interfaces * * @param maximumPerInterface The maximum of IP addresses per interface or 0 to get all. * @return Every single IP which can be assigned on the Networks the computer is connected to */ public Set<String> getNetworkIPs(int maximumPerInterface) { return getNetworkIPs(getInterfaceIPs(), maximumPerInterface); } /** * Takes the interfaceIPs and fetches every IP which can be assigned on their network * * @param networkIPs The IPs which are assigned to the Network Interfaces * @param maximumPerInterface The maximum of IP addresses per interface or 0 to get all. * @return Every single IP which can be assigned on the Networks the computer is connected to */ public Set<String> getNetworkIPs(Set<String> interfaceIPs, int maximumPerInterface) { LinkedHashSet<String> networkIPs = new LinkedHashSet<>(); for (String string : interfaceIPs) { try { // gets every ip which can be assigned on the given network SubnetUtils utils = new SubnetUtils(string); String[] addresses = utils.getInfo().getAllAddresses(); int len = addresses.length; if (maximumPerInterface != 0 && maximumPerInterface < len) { len = maximumPerInterface; } for (int i = 0; i < len; i++) { networkIPs.add(addresses[i]); } } catch (Exception ex) { } } return networkIPs; } /** * Try to establish a tcp connection to the given port. Returns false if a timeout occurred * or the connection was denied. * * @param host The IP or hostname * @param port The tcp port. Must be not 0. * @param timeout Timeout in ms * @param logger A slf4j logger instance to log IOException * @return * @throws IOException */ public boolean servicePing(String host, int port, int timeout) throws IOException { SocketAddress socketAddress = new InetSocketAddress(host, port); try (Socket socket = new Socket()) { socket.connect(socketAddress, timeout); return true; } catch (NoRouteToHostException ignored) { return false; } catch (SocketTimeoutException ignored) { return false; } catch (ConnectException e) { // Connection refused, there is a device on the other end though return true; } } /** * Return the working method for the native system ping. If no native ping * works JavaPing is returned. */ public IpPingMethodEnum determinePingMethod() { IpPingMethodEnum method; if (SystemUtils.IS_OS_WINDOWS) { method = IpPingMethodEnum.WINDOWS_PING; } else if (SystemUtils.IS_OS_MAC) { method = IpPingMethodEnum.MAC_OS_PING; } else if (SystemUtils.IS_OS_UNIX) { method = IpPingMethodEnum.IPUTILS_LINUX_PING; } else { // We cannot estimate the command line for any other operating system and just return false return IpPingMethodEnum.JAVA_PING; } try { if (nativePing(method, "127.0.0.1", 1000)) { return method; } } catch (IOException ignored) { } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Reset interrupt flag } return IpPingMethodEnum.JAVA_PING; } /** * Return true if the external arp ping utility (arping) is available and executable on the given path. */ public ArpPingUtilEnum determineNativeARPpingMethod(String arpToolPath) { String result = ExecUtil.executeCommandLineAndWaitResponse(arpToolPath, 100); if (StringUtils.isBlank(result)) { return null; } else if (result.contains("Thomas Habets")) { if (result.contains("-w sec Specify a timeout")) { return ArpPingUtilEnum.THOMAS_HABERT_ARPING; } else { return ArpPingUtilEnum.THOMAS_HABERT_ARPING_WITHOUT_TIMEOUT; } } else if (result.contains("-w timeout")) { return ArpPingUtilEnum.IPUTILS_ARPING; } return ArpPingUtilEnum.UNKNOWN_TOOL; } public enum IpPingMethodEnum { JAVA_PING, WINDOWS_PING, IPUTILS_LINUX_PING, MAC_OS_PING } /** * Use the native ping utility of the operating system to detect device presence. * * @param hostname The DNS name, IPv4 or IPv6 address. Must not be null. * @param timeoutInMS Timeout in milliseconds. Be aware that DNS resolution is not part of this timeout. * @return Returns true if the device responded * @throws IOException The ping command could probably not be found */ public boolean nativePing(IpPingMethodEnum method, String hostname, int timeoutInMS) throws IOException, InterruptedException { Process proc; // Yes, all supported operating systems have their own ping utility with a different command line switch (method) { case IPUTILS_LINUX_PING: proc = new ProcessBuilder("ping", "-w", String.valueOf(timeoutInMS / 1000), "-c", "1", hostname) .start(); break; case MAC_OS_PING: proc = new ProcessBuilder("ping", "-t", String.valueOf(timeoutInMS / 1000), "-c", "1", hostname) .start(); break; case WINDOWS_PING: proc = new ProcessBuilder("ping", "-w", String.valueOf(timeoutInMS), "-n", "1", hostname).start(); break; case JAVA_PING: default: // We cannot estimate the command line for any other operating system and just return false return false; } // The return code is 0 for a successful ping. 1 if device didn't respond and 2 if there is another error like // network interface not ready. return proc.waitFor() == 0; } public enum ArpPingUtilEnum { UNKNOWN_TOOL, IPUTILS_ARPING, THOMAS_HABERT_ARPING, THOMAS_HABERT_ARPING_WITHOUT_TIMEOUT } /** * Execute the arping tool to perform an ARP ping (only for IPv4 addresses). * There exist two different arping utils with the same name unfortunatelly. * * iputils arping which is sometimes preinstalled on fedora/ubuntu and the * * https://github.com/ThomasHabets/arping which also works on Windows and MacOS. * * @param arpUtilPath The arping absolute path including filename. Example: "arping" or "/usr/bin/arping" or * "C:\something\arping.exe" * @param interfaceName An interface name, on linux for example "wlp58s0", shown by ifconfig. Must not be null. * @param ipV4address The ipV4 address. Must not be null. * @param timeoutInMS A timeout in milliseconds * @return Return true if the device responded * @throws IOException The ping command could probably not be found */ public boolean nativeARPPing(ArpPingUtilEnum arpingTool, String arpUtilPath, String interfaceName, String ipV4address, int timeoutInMS) throws IOException, InterruptedException { if (arpUtilPath == null || arpingTool == null || arpingTool == ArpPingUtilEnum.UNKNOWN_TOOL) { return false; } Process proc; if (arpingTool == ArpPingUtilEnum.THOMAS_HABERT_ARPING_WITHOUT_TIMEOUT) { proc = new ProcessBuilder(arpUtilPath, "-c", "1", "-I", interfaceName, ipV4address).start(); } else { proc = new ProcessBuilder(arpUtilPath, "-w", String.valueOf(timeoutInMS / 1000), "-c", "1", "-I", interfaceName, ipV4address).start(); } // The return code is 0 for a successful ping. 1 if device didn't respond and 2 if there is another error like // network interface not ready. return proc.waitFor() == 0; } /** * iOS devices are in a deep sleep mode, where they only listen to UDP traffic on port 5353 (Bonjour service * discovery). A packet on port 5353 will wake up the network stack to respond to ARP pings at least. * * @throws IOException */ public void wakeUpIOS(InetAddress address) throws IOException { try (DatagramSocket s = new DatagramSocket()) { byte[] buffer = new byte[0]; s.send(new DatagramPacket(buffer, buffer.length, address, 5353)); } catch (PortUnreachableException ignored) { // We ignore the port unreachable error } } }