All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.wizard.common.utils.NetworkAddressHelper Maven / Gradle / Ivy

package org.bidib.wizard.common.utils;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NetworkAddressHelper {

    private static final Logger LOG = LoggerFactory.getLogger(NetworkAddressHelper.class);

    // An implementation can honor these if it wants (the default does)
    public static final String SYSTEM_PROPERTY_NET_IFACES = "org.bidib.jbidibc.discovery.network.useInterfaces";

    public static final String SYSTEM_PROPERTY_NET_ADDRESSES = "org.bidib.jbidibc.discovery.network.useAddresses";

    public static final String SYSTEM_PROPERTY_NET_LOCAL_ADDRESS =
        "org.bidib.jbidibc.discovery.network.useLocalAddress";

    final protected Set useInterfaces = new HashSet<>();

    final protected Set useAddresses = new HashSet<>();

    final protected List networkInterfaces = new ArrayList<>();

    final protected List bindAddresses = new ArrayList<>();

    public NetworkAddressHelper() {

        String useInterfacesString = System.getProperty(SYSTEM_PROPERTY_NET_IFACES);
        if (useInterfacesString != null) {
            String[] userInterfacesStrings = useInterfacesString.split(",");
            useInterfaces.addAll(Arrays.asList(userInterfacesStrings));
        }

        String useAddressesString = System.getProperty(SYSTEM_PROPERTY_NET_ADDRESSES);
        if (useAddressesString != null) {
            String[] useAddressesStrings = useAddressesString.split(",");
            useAddresses.addAll(Arrays.asList(useAddressesStrings));
        }

        discoverNetworkInterfaces();
        discoverBindAddresses();

        if ((networkInterfaces.size() == 0 || bindAddresses.size() == 0)) {
            LOG.warn("No usable network interface or addresses found");
            if (requiresNetworkInterface()) {
                throw new IllegalStateException("Could not discover any usable network interfaces and/or addresses");
            }
        }
    }

    /**
     * @return true (the default) if a MissingNetworkInterfaceException should be thrown
     */
    protected boolean requiresNetworkInterface() {
        return true;
    }

    // @Override
    public Iterator getNetworkInterfaces() {
        return new Synchronized(networkInterfaces) {
            @Override
            protected void synchronizedRemove(int index) {
                synchronized (networkInterfaces) {
                    networkInterfaces.remove(index);
                }
            }
        };
    }

    public InetAddress[] getBindAddresses() {

        return bindAddresses.toArray(new InetAddress[0]);
    }

    // @Override
    public boolean hasUsableNetwork() {
        return networkInterfaces.size() > 0 && bindAddresses.size() > 0;
    }

    protected void discoverNetworkInterfaces() throws InitializationException {
        try {

            Enumeration interfaceEnumeration = NetworkInterface.getNetworkInterfaces();
            for (NetworkInterface iface : Collections.list(interfaceEnumeration)) {
                // displayInterfaceInformation(iface);

                LOG.trace("Analyzing network interface: {}", iface.getDisplayName());
                if (isUsableNetworkInterface(iface, useInterfaces)) {
                    LOG
                        .debug("Discovered usable network interface: {}, name: {}", iface.getDisplayName(),
                            iface.getName());

                    synchronized (networkInterfaces) {
                        networkInterfaces.add(iface);
                    }
                }
                else {
                    LOG.trace("Ignoring non-usable network interface: " + iface.getDisplayName());
                }
            }

        }
        catch (Exception ex) {
            throw new InitializationException("Could not not analyze local network interfaces: " + ex, ex);
        }
    }

    /**
     * Validation of every discovered network interface.
     * 

* Override this method to customize which network interfaces are used. *

*

* The given implementation ignores interfaces which are *

*
    *
  • loopback (yes, we do not bind to lo0)
  • *
  • down
  • *
  • have no bound IP addresses
  • *
  • named "vmnet*" (OS X VMWare does not properly stop interfaces when it quits)
  • *
  • named "vnic*" (OS X Parallels interfaces should be ignored as well)
  • *
  • named "vboxnet*" (OS X Virtual Box interfaces should be ignored as well)
  • *
  • named "*virtual*" (VirtualBox interfaces, for example
  • *
  • named "ppp*"
  • *
* * @param iface * The interface to validate. * @return True if the given interface matches all validation criteria. * @throws Exception * If any validation test failed with an un-recoverable error. */ public static boolean isUsableNetworkInterface(NetworkInterface iface, final Set useInterfaces) { LOG.trace("Current network interface, displayName: {}, name: {}", iface.getDisplayName(), iface.getName()); try { if (!iface.isUp()) { LOG.trace("Skipping network interface (down): " + iface.getDisplayName()); return false; } if (getInetAddresses(iface).size() == 0) { LOG.trace("Skipping network interface without bound IP addresses: " + iface.getDisplayName()); return false; } if (iface.getName().toLowerCase(Locale.ROOT).startsWith("vmnet") || (iface.getDisplayName() != null && iface.getDisplayName().toLowerCase(Locale.ROOT).contains("vmnet"))) { LOG.trace("Skipping network interface (VMWare): " + iface.getDisplayName()); return false; } if (iface.getName().toLowerCase(Locale.ROOT).startsWith("vnic")) { LOG.trace("Skipping network interface (Parallels): " + iface.getDisplayName()); return false; } if (iface.getName().toLowerCase(Locale.ROOT).startsWith("vboxnet") || (iface.getDisplayName() != null && iface.getDisplayName().toLowerCase(Locale.ROOT).startsWith("virtual"))) { LOG.trace("Skipping network interface (Virtual Box): " + iface.getDisplayName()); return false; } if (iface.getDisplayName() != null && iface.getDisplayName().toLowerCase(Locale.ROOT).startsWith("hyper-v")) { LOG.trace("Skipping network interface (Hyper-V): {}", iface.getDisplayName()); return false; } if (iface.getName().toLowerCase(Locale.ROOT).contains("tap-windows") || (iface.getDisplayName() != null && iface.getDisplayName().toLowerCase(Locale.ROOT).contains("tap-windows"))) { LOG.trace("Skipping network interface (named '*tap-windows*'): " + iface.getDisplayName()); return false; } if (iface.getName().toLowerCase(Locale.ROOT).contains("virtual")) { LOG.trace("Skipping network interface (named '*virtual*'): " + iface.getDisplayName()); return false; } if (iface.getName().toLowerCase(Locale.ROOT).startsWith("ppp")) { LOG.trace("Skipping network interface (PPP): " + iface.getDisplayName()); return false; } if (iface.isLoopback()) { LOG.trace("Skipping network interface (ignoring loopback): " + iface.getDisplayName()); return false; } if (useInterfaces != null && useInterfaces.size() > 0 && !useInterfaces.contains(iface.getName())) { LOG .trace("Skipping unwanted network interface (-D" + SYSTEM_PROPERTY_NET_IFACES + "): " + iface.getName()); return false; } if (!iface.supportsMulticast()) { LOG.warn("Network interface may not be multicast capable: " + iface.getDisplayName()); } return true; } catch (SocketException ex) { LOG.warn("Detect if network interface is usable failed.", ex); return false; } } protected static List getInetAddresses(NetworkInterface networkInterface) { return Collections.list(networkInterface.getInetAddresses()); } protected void discoverBindAddresses() throws InitializationException { try { synchronized (networkInterfaces) { Iterator it = networkInterfaces.iterator(); while (it.hasNext()) { NetworkInterface networkInterface = it.next(); LOG.trace("Discovering addresses of interface: " + networkInterface.getDisplayName()); int usableAddresses = 0; for (InetAddress inetAddress : getInetAddresses(networkInterface)) { if (inetAddress == null) { LOG.warn("Network has a null address: " + networkInterface.getDisplayName()); continue; } if (isUsableAddress(networkInterface, inetAddress)) { LOG.debug("Discovered usable network interface address: " + inetAddress.getHostAddress()); usableAddresses++; synchronized (bindAddresses) { bindAddresses.add(inetAddress); } } else { LOG.trace("Ignoring non-usable network interface address: " + inetAddress.getHostAddress()); } } if (usableAddresses == 0) { LOG .trace("Network interface has no usable addresses, removing: " + networkInterface.getDisplayName()); it.remove(); } } } } catch (Exception ex) { throw new InitializationException("Could not not analyze local network interfaces: " + ex, ex); } } /** * Validation of every discovered local address. *

* Override this method to customize which network addresses are used. *

*

* The given implementation ignores addresses which are *

*
    *
  • not IPv4
  • *
  • the local loopback (yes, we ignore 127.0.0.1)
  • *
* * @param networkInterface * The interface to validate. * @param address * The address of this interface to validate. * @return True if the given address matches all validation criteria. */ protected boolean isUsableAddress(NetworkInterface networkInterface, InetAddress address) { // if (!(address instanceof Inet4Address)) { // LOG.trace("Skipping unsupported non-IPv4 address: " + address); // return false; // } if ((address instanceof Inet6Address) && address.isLinkLocalAddress()) { LOG.info("Skipping link local ipv6 address: " + address); return false; } if (address.isAnyLocalAddress()) { LOG.info("Skipping any local address: " + address); return false; } if (address.isLoopbackAddress()) { LOG.trace("Skipping loopback address: " + address); return false; } if (useAddresses.size() > 0 && !useAddresses.contains(address.getHostAddress())) { LOG.trace("Skipping unwanted address: " + address); return false; } return true; } /************************************************************************* * Get the current IP address of the computer. This will return the first address of the first network interface * that is a "real" IP address of the given type. * * @param cl * the type of address we are searching for. * @return the address as string or "" if not found. * @throws UnknownHostException ************************************************************************/ public static InetAddress getLocalAddressOfType(Class cl) throws UnknownHostException { try { String overrideIP = System.getProperty(SYSTEM_PROPERTY_NET_LOCAL_ADDRESS); if (StringUtils.isNotBlank(overrideIP)) { return InetAddress.getByName(overrideIP.trim()); } Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); List localAddresses = new ArrayList<>(); while (interfaces.hasMoreElements()) { NetworkInterface current = interfaces.nextElement(); LOG.debug("Current network interface: {}", current); if (!isUsableNetworkInterface(current, Collections.emptySet())) { continue; } Enumeration addresses = current.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress adr = addresses.nextElement(); if (cl.isInstance(adr)) { localAddresses.add(adr); } } } if (localAddresses.isEmpty()) { InetAddress localAddress = InetAddress.getLocalHost(); LOG.debug("No local addresses detected, resolved to: {}", localAddress); return localAddress; } else { InetAddress localAddress = localAddresses.get(0); if (localAddresses.size() > 1) { // if we have multiple addresses we prefer the localHost address if (Inet4Address.class.equals(cl)) { InetAddress localHost = Inet4Address.getLocalHost(); if (localAddresses.contains(localHost)) { localAddress = localHost; LOG.debug("Found local IPv4 in local addresses, resolved to: {}", localAddress); } } else if (Inet6Address.class.equals(cl)) { InetAddress localHost = Inet6Address.getLocalHost(); if (localAddresses.contains(localHost)) { localAddress = localHost; LOG.debug("Found local IPv6 in local addresses, resolved to: {}", localAddress); } } } LOG.debug("Local address resolved to: {}", localAddress); return localAddress; } } catch (Exception e) { LOG.debug("Local address not resolvable."); return InetAddress.getLocalHost(); } } /** * Wraps a collection and provides stable iteration with thread-safe removal. *

* Internally uses the iterator of a CopyOnWriteArrayList, when remove() is called, * delegates to {@link #synchronizedRemove(int)}. *

*/ static public abstract class Synchronized implements Iterator { final Iterator wrapped; int nextIndex = 0; boolean removedCurrent = false; public Synchronized(Collection collection) { this.wrapped = new CopyOnWriteArrayList(collection).iterator(); } @Override public boolean hasNext() { return wrapped.hasNext(); } @Override public E next() { removedCurrent = false; nextIndex++; return wrapped.next(); } @Override public void remove() { if (nextIndex == 0) { throw new IllegalStateException("Call next() first"); } if (removedCurrent) { throw new IllegalStateException("Already removed current, call next()"); } synchronizedRemove(nextIndex - 1); removedCurrent = true; } /** * Must remove the element at the given index from the original collection in a thread-safe fashion. */ abstract protected void synchronizedRemove(int index); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy