This is an automated email from the ASF dual-hosted git repository. andy pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/jena.git
commit 7ad441cf5b45bcb99e0dbbf159361ea5067877ce Author: Andy Seaborne <[email protected]> AuthorDate: Sat Nov 30 09:03:05 2024 +0000 Find non-loopback IP address --- .../main/java/org/apache/jena/atlas/net/Host.java | 154 +++++++++++++++++++++ .../test/java/org/apache/jena/atlas/TC_Atlas.java | 12 +- .../jena/atlas/{TC_Atlas.java => net/TS_Net.java} | 25 +--- .../java/org/apache/jena/atlas/net/TestHost.java | 60 ++++++++ 4 files changed, 224 insertions(+), 27 deletions(-) diff --git a/jena-base/src/main/java/org/apache/jena/atlas/net/Host.java b/jena-base/src/main/java/org/apache/jena/atlas/net/Host.java new file mode 100644 index 0000000000..1028472c30 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/net/Host.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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 org.apache.jena.atlas.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.Enumeration; + +import org.apache.jena.atlas.RuntimeIOException; +import org.apache.jena.atlas.io.IOX; + +public class Host { + /** + * Returns a host address, for what is most likely the machine's LAN IP address + * (not a loopback address, 127.0.0.x or ::1). If there is no other choice, it returns + * <code>a local host address</code>. + */ + public static String getHostAddress() { + try { + InetAddress addr = getLocalHostLANAddress$(); + if ( addr == null ) + return null; + return addr.getHostAddress(); + } catch ( UnknownHostException ex) { + return null; + } + } + + /** + * Returns an <code>InetAddress</code> object encapsulating what is most likely the machine's LAN IP address + * (not a loopback address, 127.0.0.x or ::1). If there is no other choice, it returns + * <code>InetAddress.getLocalHost</code>. + * Throw a {@link RuntimeIOException runtime IO exception} if no address choice found. + */ + public static InetAddress getLocalHostLANAddress() { + try { + return getLocalHostLANAddress$(); + } catch (IOException ioException) { + throw IOX.exception(ioException); + } + } + + /** + * Returns an <code>InetAddress</code> object encapsulating what is most likely the machine's LAN IP address. + * <p/> + * This method is intended for use as a replacement of JDK method <code>InetAddress.getLocalHost</code>, 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 <code>InetAddress.getLocalHost</code> 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 + * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037">here</a>. + * <p/> + * 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). + * <p/> + * 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 <code>InetAddress.getLocalHost</code>. + * <p/> + * + * @throws UnknownHostException If the LAN address of the machine cannot be found. + */ + private static InetAddress getLocalHostLANAddress$() throws UnknownHostException { + // This code comes from: + // https://issues.apache.org/jira/browse/JCS-40 + try { + InetAddress candidateAddress = null; + // Iterate all NICs (network interface cards)... + for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements();) { + NetworkInterface iface = ifaces.nextElement(); + // Iterate all IP addresses assigned to each card... + for (Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements();) { + InetAddress inetAddr = inetAddrs.nextElement(); + if (!inetAddr.isLoopbackAddress()) { + if (inetAddr.isSiteLocalAddress()) { + // Found non-loopback site-local address. Return it immediately... + return inetAddr; + } + if (candidateAddress == null) { + // 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... + candidateAddress = inetAddr; + // Note that we don't repeatedly assign non-loopback non-site-local addresses as candidates, + // only the first. For subsequent iterations, candidate will be non-null. + } + } + } + } + if (candidateAddress != null) { + // We did not find a site-local address, but we found some other non-loopback address. + // Server 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... + return candidateAddress; + } + // At this point, we did not find a non-loopback address. + // Fall back to returning whatever InetAddress.getLocalHost() returns... + InetAddress jdkSuppliedAddress = InetAddress.getLocalHost(); + if (jdkSuppliedAddress == null) { + throw new UnknownHostException("The JDK InetAddress.getLocalHost() method unexpectedly returned null."); + } + return jdkSuppliedAddress; + } + catch (UnknownHostException e) { throw e; } + catch (Exception e) { + UnknownHostException unknownHostException = new UnknownHostException("Failed to determine LAN address: " + e); + unknownHostException.initCause(e); + throw unknownHostException; + } + } + + // @formatter:off +// public static void main(String ... arg) throws UnknownHostException { +// try { +// for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements();) { +// NetworkInterface iface = ifaces.nextElement(); +// // Iterate all IP addresses assigned to each card... +// for (Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements();) { +// InetAddress inetAddr = inetAddrs.nextElement(); +// System.out.println("IP Address : '" +inetAddr.getHostAddress()+"'"); +// } +// } +// System.out.println(); +// +// InetAddress inetAddr = getLocalHostLANAddress(); +// //InetAddress localhost = InetAddress.getLocalHost(); +// System.out.println("System IP Address : '" +inetAddr.getHostAddress()+"'"); +// } catch (Exception ex) { +// ex.printStackTrace(); +// System.exit(0); +// } +// } + // @formatter:on +} diff --git a/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java b/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java index 1f3ddc5167..d2d0ae08f7 100644 --- a/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java +++ b/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java @@ -18,13 +18,15 @@ package org.apache.jena.atlas; +import org.junit.runner.RunWith ; +import org.junit.runners.Suite ; + import org.apache.jena.atlas.io.TS_IO ; import org.apache.jena.atlas.iterator.TS_Iterator ; import org.apache.jena.atlas.lib.TS_Lib ; import org.apache.jena.atlas.lib.persistent.TS_Persistent; import org.apache.jena.atlas.lib.tuple.TS_Tuple ; -import org.junit.runner.RunWith ; -import org.junit.runners.Suite ; +import org.apache.jena.atlas.net.TS_Net; @RunWith(Suite.class) @Suite.SuiteClasses( { @@ -34,11 +36,7 @@ import org.junit.runners.Suite ; , TS_Iterator.class , TS_IO.class , TS_Persistent.class -// , TS_Event.class -// , TS_JSON.class -// , TS_Data.class -// , TS_Web.class -// , TestCSVParser.class + , TS_Net.class }) public class TC_Atlas diff --git a/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java b/jena-base/src/test/java/org/apache/jena/atlas/net/TS_Net.java similarity index 63% copy from jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java copy to jena-base/src/test/java/org/apache/jena/atlas/net/TS_Net.java index 1f3ddc5167..1f2a65d94a 100644 --- a/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java +++ b/jena-base/src/test/java/org/apache/jena/atlas/net/TS_Net.java @@ -16,30 +16,15 @@ * limitations under the License. */ -package org.apache.jena.atlas; +package org.apache.jena.atlas.net; + -import org.apache.jena.atlas.io.TS_IO ; -import org.apache.jena.atlas.iterator.TS_Iterator ; -import org.apache.jena.atlas.lib.TS_Lib ; -import org.apache.jena.atlas.lib.persistent.TS_Persistent; -import org.apache.jena.atlas.lib.tuple.TS_Tuple ; import org.junit.runner.RunWith ; import org.junit.runners.Suite ; + @RunWith(Suite.class) @Suite.SuiteClasses( { - // Library - TS_Lib.class - , TS_Tuple.class - , TS_Iterator.class - , TS_IO.class - , TS_Persistent.class -// , TS_Event.class -// , TS_JSON.class -// , TS_Data.class -// , TS_Web.class -// , TestCSVParser.class + TestHost.class }) - -public class TC_Atlas -{} +public class TS_Net {} diff --git a/jena-base/src/test/java/org/apache/jena/atlas/net/TestHost.java b/jena-base/src/test/java/org/apache/jena/atlas/net/TestHost.java new file mode 100644 index 0000000000..4cd94688d7 --- /dev/null +++ b/jena-base/src/test/java/org/apache/jena/atlas/net/TestHost.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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 org.apache.jena.atlas.net; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.junit.Test; + +import org.apache.jena.atlas.RuntimeIOException; + +public class TestHost { + @Test public void host_localNonLoopback() { + // Assumes the machine has some kind of IP networking. + try { + InetAddress addr = Host.getLocalHostLANAddress(); + assertNotNull(addr); + assertFalse(addr.isLoopbackAddress()); + } catch ( RuntimeIOException ex) { + Throwable x = ex.getCause(); + if ( x instanceof UnknownHostException ) + return; + throw ex; + } + } + + @Test public void host_localNonLoopbackAddress() { + // Assumes the machine has some kind of IP networking. + try { + // InetAddress.toString is "host/address" + String addr = Host.getHostAddress(); + assertNotNull(addr); + assertFalse(addr.contains("/")); + } catch ( RuntimeIOException ex) { + Throwable x = ex.getCause(); + if ( x instanceof UnknownHostException ) + return; + throw ex; + } + } +}
