This is an automated email from the ASF dual-hosted git repository. matthiasblaesing pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push: new 0d32553 [NETBEANS-3652] PAC: myIpAddress() returns "127.0.0.1" when executed in Travis env new 631618d Merge pull request #1835 from lbruun/NETBEANS-3652-ImprovedLocalIpDiscovery 0d32553 is described below commit 0d3255303c8707d49dda19992c8cda41416b430e Author: Lars Bruun-Hansen <lbr...@apache.org> AuthorDate: Wed Jan 1 10:07:04 2020 +0100 [NETBEANS-3652] PAC: myIpAddress() returns "127.0.0.1" when executed in Travis env Improved local IP address discovery: An additional method is added to the multiple ways of finding own host's IP: UDP. In some environments this will be able to find the IP where the other methods fail. However, discovering the IP of the local host remains an imprecise discipline. Also, the getMostLikelyLocalInetAddresses() method is now more precise about filtering out loopback addresses. Returning the loopback address from this method should really, really be a last resort. --- .travis.yml | 2 +- .../core/network/utils/IpAddressUtils.java | 18 +++ .../core/network/utils/LocalAddressUtils.java | 135 +++++++++++++++++++-- 3 files changed, 147 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7c84b97..13008df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -120,7 +120,7 @@ matrix: - hide-logs.sh ant $OPTS -f platform/core.kit test - hide-logs.sh ant $OPTS -f platform/core.multiview test - hide-logs.sh ant $OPTS -f platform/core.netigso test - #- hide-logs.sh ant $OPTS -f platform/core.network test + - hide-logs.sh ant $OPTS -f platform/core.network test - hide-logs.sh ant $OPTS -f platform/core.osgi test - hide-logs.sh ant $OPTS -f platform/core.output2 test - hide-logs.sh ant $OPTS -f platform/core.startup test diff --git a/platform/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java b/platform/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java index 7c745f5..cded068 100644 --- a/platform/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java +++ b/platform/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java @@ -26,6 +26,7 @@ import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -427,6 +428,23 @@ public class IpAddressUtils { } /** + * Removes loopback addresses from the provided list. + * @param addresses + */ + public static void removeLoopback(List<InetAddress> addresses) { + if (addresses == null) { + return; + } + Iterator<InetAddress> iterator = addresses.iterator(); + while (iterator.hasNext()) { + InetAddress a = iterator.next(); + if (a.isLoopbackAddress()) { + iterator.remove(); + } + } + } + + /** * Sorts a list of IP addresses. * * @param addresses IP addresses diff --git a/platform/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java b/platform/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java index 68aa14f..92b739e 100644 --- a/platform/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java +++ b/platform/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java @@ -19,6 +19,8 @@ package org.netbeans.core.network.utils; +import java.net.DatagramSocket; +import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InterfaceAddress; @@ -78,19 +80,42 @@ import org.openide.util.RequestProcessor; */ public class LocalAddressUtils { private static final Logger LOG = Logger.getLogger(LocalAddressUtils.class.getName()); - private static final RequestProcessor RP = new RequestProcessor("LocalNetworkAddressFinder", 3); + private static final RequestProcessor RP = new RequestProcessor("LocalNetworkAddressFinder", 4); + + // Create some static InetAddress'es. This is done in a somewhat convoluted + // way, but one which guarantees that no DNS lookup will be performed. private static final byte[] LOOPBACK_IPV4_RAW = new byte[]{0x7f,0x00,0x00,0x01}; private static final byte[] LOOPBACK_IPV6_RAW = new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + + + // 8.8.8.8 -- which incidentially is Google's DNS server, but we don't ever connect + private static final byte[] SOMEADDR_IPV4_RAW = new byte[]{ + (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80}; + + // 2001:4860:4860::8888 -- which incidentially is Google's DNS server, but we don't ever connect + private static final byte[] SOMEADDR_IPV6_RAW = new byte[]{ + 0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0x88, (byte)0x88}; + private static InetAddress LOOPBACK_IPV4; private static InetAddress LOOPBACK_IPV6; + private static InetAddress SOMEADDR_IPV4; + private static InetAddress SOMEADDR_IPV6; static { try { LOOPBACK_IPV4 = InetAddress.getByAddress("local-ipv4-dummy", LOOPBACK_IPV4_RAW); LOOPBACK_IPV6 = InetAddress.getByAddress("local-ipv6-dummy", LOOPBACK_IPV6_RAW); + SOMEADDR_IPV4 = InetAddress.getByAddress("some-ipv4-dummy", SOMEADDR_IPV4_RAW); + SOMEADDR_IPV6 = InetAddress.getByAddress("some-ipv6-dummy", SOMEADDR_IPV6_RAW); } catch (UnknownHostException ex) { + // Cannot happen by definition + LOOPBACK_IPV4 = null; + LOOPBACK_IPV6 = null; + SOMEADDR_IPV4 = null; + SOMEADDR_IPV6 = null; } } @@ -99,6 +124,7 @@ public class LocalAddressUtils { private static Future<InetAddress> fut1; private static Future<InetAddress[]> fut2; private static Future<List<InetAddress>> fut3; + private static Future<List<InetAddress>> fut4; private static final Callable<InetAddress> C1 = () -> InetAddress.getLocalHost(); private static final Callable<InetAddress[]> C2 = () -> { @@ -111,6 +137,37 @@ public class LocalAddressUtils { }; private static final Callable<List<InetAddress>> C3 = () -> getLocalNetworkInterfaceAddr(); + private static final Callable<List<InetAddress>> C4 = () -> { + // See https://stackoverflow.com/a/38342964 + // Note that no actual connection is ever made below. (UDP is connection-less!) + // We just need the Sockets API to populate the local address + // of the socket. We do this by pretending that we want to send + // some UDP packages, but we never actually do so. + + List<InetAddress> list = new ArrayList<>(2); + + // IPv4 attempt + try (final DatagramSocket socket = new DatagramSocket()) { + socket.connect(SOMEADDR_IPV4, 10002); // doesn¨t need to be reachable .. and port it irrelevant + InetAddress addr = socket.getLocalAddress(); + if (addr != null && (addr instanceof Inet4Address) && (!addr.isAnyLocalAddress() && (!addr.isLoopbackAddress()))) { + list.add(addr); + } + } catch (SecurityException | SocketException ex) { + } + // IPv6 attempt + try (final DatagramSocket socket = new DatagramSocket()) { + socket.connect(SOMEADDR_IPV6, 10002); // doesn¨t need to be reachable .. and port it irrelevant + InetAddress addr = socket.getLocalAddress(); + if (addr != null && (addr instanceof Inet6Address) && (!addr.isAnyLocalAddress() && (!addr.isLoopbackAddress()))) { + list.add(addr); + } + } catch (SecurityException | SocketException ex) { + } + + return list; + }; + static { refreshNetworkInfo(false); } @@ -128,7 +185,7 @@ public class LocalAddressUtils { */ public static void warmUp() { // Does nothing. The refreshNetworkInfo task will be fired when the class - // is loaded by the classloaded. + // is loaded by the classloader. } /** @@ -145,11 +202,13 @@ public class LocalAddressUtils { fut1 = RP.submit(C1); fut2 = RP.submit(C2); fut3 = RP.submit(C3); + fut4 = RP.submit(C4); if (await) { try { fut1.get(); fut2.get(); fut3.get(); + fut4.get(); } catch (InterruptedException | ExecutionException ex) { } } @@ -213,7 +272,14 @@ public class LocalAddressUtils { refreshNetworkInfo(false); } try { - return fut2.get(); + InetAddress[] arr = fut2.get(); + if (arr == null || arr.length == 0) { + return new InetAddress[0]; + } else { + List<InetAddress> list = Arrays.asList(arr); + List<InetAddress> filteredList = IpAddressUtilsFilter.filterInetAddresses(list, ipTypePref); + return filteredList.toArray(new InetAddress[filteredList.size()]); + } } catch (ExecutionException ex) { if (ex.getCause() instanceof UnknownHostException) { throw (UnknownHostException) ex.getCause(); @@ -273,6 +339,45 @@ public class LocalAddressUtils { } /** + * Returns the address of the local host, found by using UDP method. + * + * <p> + * This method works by creating a 'connected' UDP socket. Upon the connect + * operation, the Berkeley sockets API will populate the local endpoint + * (own host) according to the host's routing information. Hence we can + * use this for finding the IP of the local host. Note, that there is + * never any actual UDP connection created. We only use the socket to see how + * its metadata has been populated. + * + * <p> + * This method is known not to work on Mac OSX. It will most likely + * return an empty list if on Mac OSX. + * + * <p> + * This method returns a cached result and is therefore likely not to block + * unless this is the first time this class is being referenced. + * + * + * @param ipTypePref IP protocol filter + * @see #getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference) + * @return list of IP addresses, either of length 1 or length 0, never null. + */ + public static @NonNull List<InetAddress> getDatagramLocalInetAddress(IpTypePreference ipTypePref) { + synchronized (LOCK) { + if (fut4 == null) { + refreshNetworkInfo(false); + } + try { + return IpAddressUtilsFilter.filterInetAddresses(fut4.get(), ipTypePref); + } catch (ExecutionException ex) { + throw new RuntimeException(ex.getCause()); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + } + } + + /** * Returns the host's IP addresses. Or rather the IP addresses most likely * to be the ones the host is known by. This method is much more likely * to return a correct result than the JDKs {@link InetAddress#getLocalHost()}, @@ -306,6 +411,11 @@ public class LocalAddressUtils { * </li> * * <li> + * Use the result from {@link #getDatagramLocalInetAddress(IpAddressUtils.IpTypePreference) getDatagramLocalInetAddress() } if it matches the + * {@code ipTypePref} filter. + * </li> + * + * <li> * Finally, if everything else fails, return the result of * {@link #getLoopbackAddress(IpAddressUtils.IpTypePreference) * getLoopbackAddress()}. @@ -323,8 +433,11 @@ public class LocalAddressUtils { */ public static @NonNull InetAddress[] getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference ipTypePref) { List<InetAddress> filteredList = getPrioritizedLocalHostAddresses(ipTypePref); + IpAddressUtils.removeLoopback(filteredList); + try { List<InetAddress> localHostAddresses = Arrays.asList(getLocalHostAddresses(ipTypePref)); + IpAddressUtils.removeLoopback(localHostAddresses); if (localHostAddresses != null && !localHostAddresses.isEmpty()) { List<InetAddress> tmpList = new ArrayList<>(5); @@ -333,14 +446,16 @@ public class LocalAddressUtils { tmpList.add(addr); } } - + // #1 if (!tmpList.isEmpty()) { return tmpList.toArray(new InetAddress[tmpList.size()]); } // #2 - return localHostAddresses.toArray(new InetAddress[localHostAddresses.size()]); + if (!localHostAddresses.isEmpty()) { + return localHostAddresses.toArray(new InetAddress[localHostAddresses.size()]); + } } } catch (UnknownHostException ex) { } @@ -354,13 +469,19 @@ public class LocalAddressUtils { // #4 try { InetAddress addr = IpAddressUtilsFilter.pickInetAddress(Collections.singletonList(getLocalHost()), ipTypePref); - if (addr != null) { + if (addr != null && (!addr.isAnyLocalAddress()) && (!addr.isLoopbackAddress())) { return new InetAddress[]{addr}; } } catch (UnknownHostException ex) { } - // #5 - last resort + // #5 - nearly last resort + List<InetAddress> datagramLocalInetAddress = getDatagramLocalInetAddress(ipTypePref); + if (datagramLocalInetAddress != null && (!datagramLocalInetAddress.isEmpty())) { + return datagramLocalInetAddress.toArray(new InetAddress[datagramLocalInetAddress.size()]); + } + + // #6 - last resort return new InetAddress[]{getLoopbackAddress(ipTypePref)}; } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists