Added ability to get LAN address

connection_type_change
Robinson 2021-04-06 01:27:22 +02:00
parent 1cc7ac128b
commit 2164890bd0
1 changed files with 112 additions and 6 deletions

View File

@ -1,12 +1,7 @@
package dorkbox.netUtil
import dorkbox.netUtil.Common.logger
import java.net.Inet4Address
import java.net.Inet6Address
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.NetworkInterface
import java.net.SocketException
import java.net.*
/**
* A class that holds a number of network-related constants, also from:
@ -131,6 +126,117 @@ object IP {
LOCALHOST = loopbackAddr!!
}
/**
* The LAN address of the machine, to the best of our ability
*/
fun lanAddress(): InetAddress {
/**
* Returns an `InetAddress` object encapsulating what is most likely the machine's LAN IP address.
*
*
* This method is intended for use as a replacement of JDK method `InetAddress.getLocalHost`, because
* that method is ambiguous on Linux systems. Linux systems enumerate the loopback network interface the same
* way as regular LAN network interfaces, but the JDK `InetAddress.getLocalHost` method does not
* specify the algorithm used to select the address returned under such circumstances, and will often return the
* loopback address, which is not valid for network communication. Details
* [here](http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037).
*
*
* This method will scan all IP addresses on all network interfaces on the host machine to determine the IP address
* most likely to be the machine's LAN address. If the machine has multiple IP addresses, this method will prefer
* a site-local IP address (e.g. 192.168.x.x or 10.10.x.x, usually IPv4) if the machine has one (and will return the
* first site-local address if the machine has more than one), but if the machine does not hold a site-local
* address, this method will return simply the first non-loopback address found (IPv4 or IPv6).
*
*
* If this method cannot find a non-loopback address using this selection algorithm, it will fall back to
* calling and returning the result of JDK method `InetAddress.getLocalHost`.
*
*
* @throws UnknownHostException If the LAN address of the machine cannot be found.
*
* From: https://issues.apache.org/jira/browse/JCS-40
*/
val likelyAddress = mutableListOf<InetAddress>()
val candidates = mutableListOf<InetAddress>()
try {
// Iterate all NICs (network interface cards)...
val ifaces = NetworkInterface.getNetworkInterfaces()
while (ifaces.hasMoreElements()) {
val iface = ifaces.nextElement()
// Iterate all IP addresses assigned to each card...
val inetAddrs = iface.inetAddresses
while (inetAddrs.hasMoreElements()) {
val inetAddr = inetAddrs.nextElement()
if (!inetAddr.isLoopbackAddress) {
if (inetAddr.isSiteLocalAddress) {
// Found non-loopback site-local address. Return it immediately...
likelyAddress.add(inetAddr)
} else {
// Found non-loopback address, but not necessarily site-local.
// Store it as a candidate to be returned if site-local address is not subsequently found...
candidates.add(inetAddr)
}
}
}
}
} catch (e: Exception) {
// ignored
}
if (likelyAddress.size == 1) {
return likelyAddress.first()
}
// we have MORE than 1 likely address, but we DO NOT know which one is used for 0.0.0.0 traffic.
// we **COULD** parse out the gateway information from the routes for each interface... but that is a huge amount of work.
// it's MUCH easier to see if open a connection to 1.1.1.1 and get the interface this traffic was on, and use that interface IP address
runCatching {
Socket().use {
it.connect(InetSocketAddress("1.1.1.1", 80))
return it.localAddress
}
}.onFailure {
Common.logger.error("Unable to determine outbound traffic local address. Using alternate logic instead.", it)
}
// there was an error doing this! (it's possible that outbound traffic is not allowed
if (IPv6.isPreferred) {
val ipv6 = likelyAddress.filterIsInstance<Inet6Address>()
if (ipv6.isNotEmpty()) {
return ipv6.first()
}
} else if (IPv4.isPreferred) {
val ipv4 = likelyAddress.filterIsInstance<Inet4Address>()
if (ipv4.isNotEmpty()) {
return ipv4.first()
}
}
// we STILL don't have something. Possibly that we have no likely addresses, but we
// found some other non-loopback address.
// The machine might have a non-site-local address assigned to its NIC (or it might be running
// IPv6 which deprecates the "site-local" concept).
// Return this non-loopback candidate address...
if (IPv6.isPreferred) {
val ipv6 = candidates.filterIsInstance<Inet6Address>()
if (ipv6.isNotEmpty()) {
return ipv6.first()
}
} else if (IPv4.isPreferred) {
val ipv4 = candidates.filterIsInstance<Inet4Address>()
if (ipv4.isNotEmpty()) {
return ipv4.first()
}
}
// At this point, we did not find a non-loopback address.
// Fall back to returning whatever InetAddress.getLocalHost() returns...
return InetAddress.getLocalHost()
?: throw UnknownHostException("The JDK InetAddress.getLocalHost() method unexpectedly returned null.")
}
/**
* Creates an byte[] based on an ipAddressString. No error handling is performed here.
*/