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

io.hekate.network.address.AddressPattern Maven / Gradle / Ivy

/*
 * Copyright 2022 The Hekate Project
 *
 * The Hekate Project licenses this file to you 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 io.hekate.network.address;

import io.hekate.core.HekateException;
import io.hekate.core.internal.util.AddressUtils;
import io.hekate.network.NetworkServiceFactory;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Pattern-based implementation of {@link AddressSelector} interface.
 *
 * 

* This implementation of {@link AddressSelector} interface scans through all available network interfaces of the local host and uses * the pattern matching logic in order to decide which address should be selected. *

* *

* The following patterns can be specified: *

* *
    *
  • any - any non-loopback address
  • *
  • any-ip4 - any IPv4 non-loopback address
  • *
  • any-ip6 - any IPv6 non-loopback address
  • * *
  • ip~regex - any IP address that matches the specified regular expression
  • *
  • ip4~regex - any IPv4 address that matches the specified regular expression
  • *
  • ip6~regex - any IPv6 address that matches the specified regular expression
  • * *
  • !ip~regex - any IP address that does NOT match the specified regular expression
  • *
  • !ip4~regex - any IPv4 address that does NOT match the specified regular expression
  • *
  • !ip6~regex - any IPv6 address that does NOT match the specified regular expression
  • * *
  • net~regex - any IP address of a network interface whose {@link NetworkInterface#getName() name} matches the specified * regular expression
  • *
  • net4~regex - IPv4 address of a network interface whose {@link NetworkInterface#getName() name} matches the specified * regular expression
  • *
  • net6~regex - IPv6 address of a network interface whose {@link NetworkInterface#getName() name} matches the specified * regular expression
  • * *
  • !net~regex - any IP address of a network interface whose {@link NetworkInterface#getName() name} does NOT match the * specified regular expression
  • *
  • !net4~regex - IPv4 address of a network interface whose {@link NetworkInterface#getName() name} does NOT match the * specified regular expression
  • *
  • !net6~regex - IPv6 address of a network interface whose {@link NetworkInterface#getName() name} does NOT match the * specified regular expression
  • * *
  • ...all other values will be treated as a directly specified address (see {@link InetAddress#getByName(String)})
  • *
* *

* If several addresses match the specified pattern, the first one will be selected (order is not guaranteed, but preference will be given * to non-{@link NetworkInterface#isPointToPoint() P2P} addresses). *

* * @see AddressSelector * @see NetworkServiceFactory#setHostSelector(AddressSelector) */ public class AddressPattern implements AddressSelector { private static final Logger log = LoggerFactory.getLogger(AddressPattern.class); private static final boolean DEBUG = log.isDebugEnabled(); private final AddressPatternOpts opts; /** * Constructs a new instance with {@code 'any'} pattern. */ public AddressPattern() { this(null); } /** * Constructs a new instance. * * @param pattern Pattern (see the descriptions of this class for the list of supported patterns). */ public AddressPattern(String pattern) { this.opts = AddressPatternOpts.parse(pattern); } /** * Returns the host pattern as string. * * @return Host pattern. */ public String pattern() { return opts.toString(); } @Override public InetAddress select() throws HekateException { try { if (opts.exactAddress() != null) { if (DEBUG) { log.debug("Using the exact address [{}]", opts); } return InetAddress.getByName(opts.exactAddress()); } if (DEBUG) { log.debug("Trying to resolve address [{}]", opts); } Pattern niIncludes = regex(opts.interfaceMatch()); Pattern niExcludes = regex(opts.interfaceNotMatch()); Pattern addrIncludes = regex(opts.ipMatch()); Pattern addrExcludes = regex(opts.ipNotMatch()); List nis = networkInterfaces(); InetAddress p2pAddr = null; String p2pNiName = null; for (NetworkInterface ni : nis) { if (!ni.isUp() || ni.isLoopback()) { continue; } String niName = ni.getName(); if (matches(true, niName, niIncludes) && matches(false, niName, niExcludes)) { Enumeration addresses = ni.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if (DEBUG) { log.debug("Trying address {}", address); } if (checkAddress(addrIncludes, addrExcludes, niName, address)) { if (ni.isPointToPoint()) { // If this is a P2P address then store it as a selected address in case if there are no other addresses. if (p2pAddr == null) { p2pAddr = address; p2pNiName = niName; } } else { if (DEBUG) { log.debug("Resolved address [interface={}, address={}]", niName, address); } // Returns the selected address. return address; } } } } else { if (DEBUG) { log.debug("Skipped network interface that doesn't match name pattern [name={}]", niName); } } } if (DEBUG) { log.debug("Resolved Point-to-Point address [interface={}, address={}]", p2pNiName, p2pAddr); } // Returns the first selected P2P address if we couldn't find any alternatives. return p2pAddr; } catch (IOException e) { throw new HekateException("Failed to resolve node address [" + opts + ']', e); } } // Package level for testing purposes. AddressPatternOpts opts() { return opts; } // Package level for testing purposes. List networkInterfaces() throws SocketException { return AddressUtils.activeNetworks(); } private boolean checkAddress(Pattern addrIncludes, Pattern addrExcludes, String niName, InetAddress address) { if (address.isLinkLocalAddress()) { if (DEBUG) { log.debug("Skipped link local address [interface={}, address={}]", niName, address); } } else if (!ipVersionMatch(address)) { if (DEBUG) { log.debug("Skipped address that doesn't match IP protocol version [interface={}, address={}]", niName, address); } } else { String host = address.getHostAddress(); if (matches(true, host, addrIncludes) && matches(false, host, addrExcludes)) { return true; } else { if (DEBUG) { log.debug("Skipped address that doesn't match host pattern [interface={}, address={}]", niName, host); } } } return false; } private boolean ipVersionMatch(InetAddress addr) { if (opts.ipVersion() != null) { switch (opts.ipVersion()) { case V4: { return addr instanceof Inet4Address; } case V6: { return addr instanceof Inet6Address; } default: { throw new IllegalStateException("Unexpected IP version type: " + opts.ipVersion()); } } } return true; } private boolean matches(boolean shouldMatch, String str, Pattern pattern) { return pattern == null || pattern.matcher(str).matches() == shouldMatch; } private Pattern regex(String pattern) { if (pattern != null) { pattern = pattern.trim(); if (!pattern.isEmpty()) { return Pattern.compile(pattern); } } return null; } @Override public String toString() { return opts.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy