diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/NetworkBindingConstants.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/NetworkBindingConstants.java index ac7078a809c..2f2424e6627 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/NetworkBindingConstants.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/NetworkBindingConstants.java @@ -28,7 +28,7 @@ import org.openhab.core.thing.ThingTypeUID; @NonNullByDefault public class NetworkBindingConstants { - public static final String BINDING_ID = "network"; + private static final String BINDING_ID = "network"; // List of all Thing Type UIDs public static final ThingTypeUID BACKWARDS_COMPATIBLE_DEVICE = new ThingTypeUID(BINDING_ID, "device"); diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/PresenceDetection.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/PresenceDetection.java index 2e3f702fb5e..83b6ea9a462 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/PresenceDetection.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/PresenceDetection.java @@ -29,6 +29,7 @@ import java.util.function.Consumer; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.network.internal.dhcp.DHCPListenService; +import org.openhab.binding.network.internal.dhcp.DHCPPacketListenerServer; import org.openhab.binding.network.internal.dhcp.IPRequestReceivedCallback; import org.openhab.binding.network.internal.toberemoved.cache.ExpiringCacheAsync; import org.openhab.binding.network.internal.utils.NetworkUtils; @@ -49,8 +50,7 @@ import org.slf4j.LoggerFactory; @NonNullByDefault public class PresenceDetection implements IPRequestReceivedCallback { - public static final double NOT_REACHABLE = -1; - public static final int DESTINATION_TTL = 300 * 1000; // in ms, 300 s + private static final int DESTINATION_TTL = 300 * 1000; // in ms, 300 s NetworkUtils networkUtils = new NetworkUtils(); private final Logger logger = LoggerFactory.getLogger(PresenceDetection.class); @@ -59,7 +59,7 @@ public class PresenceDetection implements IPRequestReceivedCallback { private boolean useDHCPsniffing = false; private String ipPingState = "Disabled"; protected String arpPingUtilPath = ""; - protected ArpPingUtilEnum arpPingMethod = ArpPingUtilEnum.DISABLED; + private ArpPingUtilEnum arpPingMethod = ArpPingUtilEnum.DISABLED; protected @Nullable IpPingMethodEnum pingMethod = null; private boolean iosDevice; private Set tcpPorts = new HashSet<>(); @@ -72,7 +72,7 @@ public class PresenceDetection implements IPRequestReceivedCallback { private @NonNullByDefault({}) ExpiringCache<@Nullable InetAddress> destination; private @Nullable InetAddress cachedDestination = null; - public boolean preferResponseTimeAsLatency; + private boolean preferResponseTimeAsLatency; /// State variables (cannot be final because of test dependency injections) ExpiringCacheAsync cache; @@ -80,7 +80,7 @@ public class PresenceDetection implements IPRequestReceivedCallback { private @Nullable ScheduledFuture refreshJob; protected @Nullable ExecutorService executorService; private String dhcpState = "off"; - Integer currentCheck = 0; + private Integer currentCheck = 0; int detectionChecks; public PresenceDetection(final PresenceDetectionListener updateListener, int cacheDeviceStateTimeInMS) @@ -112,12 +112,13 @@ public class PresenceDetection implements IPRequestReceivedCallback { this.destination = new ExpiringCache<>(DESTINATION_TTL, () -> { try { InetAddress destinationAddress = InetAddress.getByName(hostname); - if (!destinationAddress.equals(cachedDestination)) { + InetAddress cached = cachedDestination; + if (!destinationAddress.equals(cached)) { logger.trace("host name resolved to other address, (re-)setup presence detection"); setUseArpPing(true, destinationAddress); if (useDHCPsniffing) { - if (cachedDestination != null) { - disableDHCPListen(cachedDestination); + if (cached != null) { + disableDHCPListen(cached); } enableDHCPListen(destinationAddress); } @@ -126,8 +127,9 @@ public class PresenceDetection implements IPRequestReceivedCallback { return destinationAddress; } catch (UnknownHostException e) { logger.trace("hostname resolution failed"); - if (cachedDestination != null) { - disableDHCPListen(cachedDestination); + InetAddress cached = cachedDestination; + if (cached != null) { + disableDHCPListen(cached); cachedDestination = null; } return null; @@ -594,8 +596,9 @@ public class PresenceDetection implements IPRequestReceivedCallback { future.cancel(true); refreshJob = null; } - if (cachedDestination != null) { - disableDHCPListen(cachedDestination); + InetAddress cached = cachedDestination; + if (cached != null) { + disableDHCPListen(cached); } } @@ -608,22 +611,18 @@ public class PresenceDetection implements IPRequestReceivedCallback { */ private void enableDHCPListen(InetAddress destinationAddress) { try { - if (DHCPListenService.register(destinationAddress.getHostAddress(), this).isUseUnprevilegedPort()) { - dhcpState = "No access right for port 67. Bound to port 6767 instead. Port forwarding necessary!"; - } else { - dhcpState = "Running normally"; - } + DHCPPacketListenerServer listener = DHCPListenService.register(destinationAddress.getHostAddress(), this); + dhcpState = String.format("Bound to port %d - %s", listener.getCurrentPort(), + (listener.usingPrivilegedPort() ? "Running normally" : "Port forwarding necessary !")); } catch (SocketException e) { - logger.warn("Cannot use DHCP sniffing.", e); + dhcpState = String.format("Cannot use DHCP sniffing: %s", e.getMessage()); + logger.warn("{}", dhcpState); useDHCPsniffing = false; - dhcpState = "Cannot use DHCP sniffing: " + e.getLocalizedMessage(); } } - private void disableDHCPListen(@Nullable InetAddress destinationAddress) { - if (destinationAddress != null) { - DHCPListenService.unregister(destinationAddress.getHostAddress()); - dhcpState = "off"; - } + private void disableDHCPListen(InetAddress destinationAddress) { + DHCPListenService.unregister(destinationAddress.getHostAddress()); + dhcpState = "off"; } } diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/WakeOnLanPacketSender.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/WakeOnLanPacketSender.java index c84c58638d0..e637da761ba 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/WakeOnLanPacketSender.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/WakeOnLanPacketSender.java @@ -44,10 +44,10 @@ public class WakeOnLanPacketSender { // Wake-on-LAN magic packet constants static final int PREFIX_BYTE_SIZE = 6; - static final int MAC_REPETITIONS = 16; + private static final int MAC_REPETITIONS = 16; static final int MAC_BYTE_SIZE = 6; static final int MAGIC_PACKET_BYTE_SIZE = PREFIX_BYTE_SIZE + MAC_REPETITIONS * MAC_BYTE_SIZE; - static final String[] MAC_SEPARATORS = new String[] { ":", "-" }; + private static final String[] MAC_SEPARATORS = new String[] { ":", "-" }; private final Logger logger = LoggerFactory.getLogger(WakeOnLanPacketSender.class); diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPListenService.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPListenService.java index 5e069f608ed..75286f287a3 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPListenService.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPListenService.java @@ -34,10 +34,9 @@ import org.slf4j.LoggerFactory; @NonNullByDefault public class DHCPListenService { static @Nullable DHCPPacketListenerServer instance; - static Map registeredListeners = new TreeMap<>(); - static Logger logger = LoggerFactory.getLogger(DHCPListenService.class); + private static Map registeredListeners = new TreeMap<>(); + private static Logger logger = LoggerFactory.getLogger(DHCPListenService.class); - @SuppressWarnings({ "null", "unused" }) public static synchronized DHCPPacketListenerServer register(String hostAddress, IPRequestReceivedCallback dhcpListener) throws SocketException { DHCPPacketListenerServer instance = DHCPListenService.instance; diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPPacket.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPPacket.java index ac011190e2d..62c5e8c9a11 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPPacket.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPPacket.java @@ -207,7 +207,6 @@ class DHCPPacket { * * @return option type, of null if not present. */ - @SuppressWarnings({ "null", "unused" }) public @Nullable Byte getDHCPMessageType() { byte[] opt = options.get(DHO_DHCP_MESSAGE_TYPE); if (opt == null) { @@ -223,7 +222,6 @@ class DHCPPacket { /** * Returns the requested IP address of a BOOTREQUEST packet. */ - @SuppressWarnings({ "null", "unused" }) public @Nullable InetAddress getRequestedIPAddress() throws IllegalArgumentException, UnknownHostException { byte[] opt = options.get(DHO_DHCP_REQUESTED_ADDRESS); if (opt == null) { diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPPacketListenerServer.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPPacketListenerServer.java index 2f8b8df2e72..28198cde0cc 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPPacketListenerServer.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/dhcp/DHCPPacketListenerServer.java @@ -13,7 +13,6 @@ package org.openhab.binding.network.internal.dhcp; import java.io.IOException; -import java.net.BindException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; @@ -36,25 +35,27 @@ import org.slf4j.LoggerFactory; */ @NonNullByDefault public class DHCPPacketListenerServer extends Thread { + private static final int PRIVILEGED_PORT = 67; + private static final int UNPRIVILEGED_PORT = 6767; private byte[] buffer = new byte[1024]; - protected @Nullable DatagramSocket dsocket; + private @Nullable DatagramSocket dsocket; private DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - boolean willbeclosed = false; - Logger logger = LoggerFactory.getLogger(DHCPPacketListenerServer.class); - private boolean useUnprevilegedPort = false; private final IPRequestReceivedCallback listener; + private final Logger logger = LoggerFactory.getLogger(DHCPPacketListenerServer.class); + private boolean willbeclosed = false; + private int currentPort = PRIVILEGED_PORT; - DHCPPacketListenerServer(IPRequestReceivedCallback listener) throws SocketException, BindException { + DHCPPacketListenerServer(IPRequestReceivedCallback listener) throws SocketException { this.listener = listener; try { - bindSocketTo(67); + bindSocketTo(currentPort); } catch (SocketException e) { - useUnprevilegedPort = true; - bindSocketTo(6767); + currentPort = UNPRIVILEGED_PORT; + bindSocketTo(currentPort); } } - protected void bindSocketTo(int port) throws SocketException { + private void bindSocketTo(int port) throws SocketException { DatagramSocket dsocket = new DatagramSocket(null); dsocket.setReuseAddress(true); dsocket.setBroadcast(true); @@ -112,10 +113,9 @@ public class DHCPPacketListenerServer extends Thread { return dsocket; } - // Return true if the instance couldn't bind to port 67 and used port 6767 instead - // to listen to DHCP traffic (port forwarding necessary). - public boolean isUseUnprevilegedPort() { - return useUnprevilegedPort; + // Return true if the instance is using port 67 to listen to DHCP traffic (no port forwarding necessary). + public boolean usingPrivilegedPort() { + return currentPort == PRIVILEGED_PORT; } /** @@ -137,4 +137,8 @@ public class DHCPPacketListenerServer extends Thread { dsocket = null; } } + + public int getCurrentPort() { + return currentPort; + } } diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/toberemoved/cache/ExpiringCacheAsync.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/toberemoved/cache/ExpiringCacheAsync.java index 4b138b079e2..81668fac1e0 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/toberemoved/cache/ExpiringCacheAsync.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/toberemoved/cache/ExpiringCacheAsync.java @@ -31,12 +31,12 @@ import org.eclipse.jdt.annotation.Nullable; */ @NonNullByDefault public class ExpiringCacheAsync { - final long expiry; - ExpiringCacheUpdate cacheUpdater; + private final long expiry; + private ExpiringCacheUpdate cacheUpdater; long expiresAt = 0; - boolean refreshRequested = false; - V value; - final List> waitingCacheCallbacks = new LinkedList<>(); + private boolean refreshRequested = false; + private V value; + private final List> waitingCacheCallbacks = new LinkedList<>(); /** * Implement the requestCacheUpdate method which will be called when the cache @@ -114,7 +114,7 @@ public class ExpiringCacheAsync { * * @return the new value */ - public void refreshValue(Consumer callback) { + private void refreshValue(Consumer callback) { waitingCacheCallbacks.add(callback); if (refreshRequested) { return; diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java index d657721807b..a48598a7c3a 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java @@ -28,16 +28,17 @@ import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; +import java.net.UnknownHostException; import java.time.Duration; +import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import org.apache.commons.lang3.SystemUtils; -import org.apache.commons.net.util.SubnetUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.io.net.exec.ExecUtil; @@ -68,6 +69,35 @@ public class NetworkUtils { .collect(Collectors.toSet()); } + /** + * Gets every IPv4 address on the network defined by its cidr + * + * @return The collected IPv4 Addresses + */ + private List getIPAddresses(CidrAddress adr) { + List result = new ArrayList<>(); + byte[] octets = adr.getAddress().getAddress(); + final int addressCount = (1 << (32 - adr.getPrefix())) - 2; + final int ipMask = 0xFFFFFFFF << (32 - adr.getPrefix()); + octets[0] &= ipMask >> 24; + octets[1] &= ipMask >> 16; + octets[2] &= ipMask >> 8; + octets[3] &= ipMask; + try { + final CidrAddress baseIp = new CidrAddress(InetAddress.getByAddress(octets), (short) adr.getPrefix()); + for (int i = 1; i <= addressCount; i++) { + int octet = i & ~ipMask; + byte[] segments = baseIp.getAddress().getAddress(); + segments[2] += (octet >> 8); + segments[3] += octet; + result.add(InetAddress.getByAddress(segments).getHostAddress()); + } + } catch (UnknownHostException e) { + logger.debug("Could not build net ip address.", e); + } + return result; + } + /** * Get a set of all interface names. * @@ -108,7 +138,7 @@ public class NetworkUtils { * @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 getNetworkIPs(Set interfaceIPs, int maximumPerInterface) { + private Set getNetworkIPs(Set interfaceIPs, int maximumPerInterface) { LinkedHashSet networkIPs = new LinkedHashSet<>(); short minCidrPrefixLength = 8; // historic Class A network, addresses = 16777214 @@ -132,14 +162,13 @@ public class NetworkUtils { cidrNotation = new CidrAddress(cidrNotation.getAddress(), minCidrPrefixLength); } - SubnetUtils utils = new SubnetUtils(cidrNotation.toString()); - String[] addresses = utils.getInfo().getAllAddresses(); - int len = addresses.length; + List addresses = getIPAddresses(cidrNotation); + int len = addresses.size(); if (maximumPerInterface != 0 && maximumPerInterface < len) { len = maximumPerInterface; } for (int i = 0; i < len; i++) { - networkIPs.add(addresses[i]); + networkIPs.add(addresses.get(i)); } } @@ -173,16 +202,22 @@ public class NetworkUtils { * works JavaPing is returned. */ public IpPingMethodEnum determinePingMethod() { + String os = System.getProperty("os.name"); 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 + if (os == null) { return IpPingMethodEnum.JAVA_PING; + } else { + os = os.toLowerCase(); + if (os.indexOf("win") >= 0) { + method = IpPingMethodEnum.WINDOWS_PING; + } else if (os.indexOf("mac") >= 0) { + method = IpPingMethodEnum.MAC_OS_PING; + } else if (os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0 || os.indexOf("aix") >= 0) { + 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 {