View Javadoc
1   package org.apache.commons.jcs3.utils.net;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.net.InetAddress;
23  import java.net.NetworkInterface;
24  import java.net.SocketException;
25  import java.net.UnknownHostException;
26  import java.util.ArrayList;
27  import java.util.Enumeration;
28  /*
29   * or more contributor license agreements.  See the NOTICE file
30   * distributed with this work for additional information
31   * regarding copyright ownership.  The ASF licenses this file
32   * to you under the Apache License, Version 2.0 (the
33   * "License"); you may not use this file except in compliance
34   * with the License.  You may obtain a copy of the License at
35   *
36   *   http://www.apache.org/licenses/LICENSE-2.0
37   *
38   * Unless required by applicable law or agreed to in writing,
39   * software distributed under the License is distributed on an
40   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41   * KIND, either express or implied.  See the License for the
42   * specific language governing permissions and limitations
43   * under the License.
44   */
45  import java.util.List;
46  
47  import org.apache.commons.jcs3.log.Log;
48  import org.apache.commons.jcs3.log.LogManager;
49  
50  /**
51   * Simple utility for getting the local host name.
52   */
53  public class HostNameUtil
54  {
55      /** The logger. */
56      private static final Log log = LogManager.getLog( HostNameUtil.class );
57  
58      /**
59       * Gets the address for the local machine.
60       * <p>
61       * @return InetAddress.getLocalHost().getHostAddress()
62       * @throws UnknownHostException
63       */
64      public static String getLocalHostAddress() throws UnknownHostException
65      {
66          final String hostAddress = getLocalHostLANAddress().getHostAddress();
67          log.debug( "hostAddress = [{0}]", hostAddress );
68          return hostAddress;
69      }
70  
71      /**
72       * Returns an <code>InetAddress</code> object encapsulating what is most likely the machine's
73       * LAN IP address.
74       * <p>
75       * This method is intended for use as a replacement of JDK method
76       * <code>InetAddress.getLocalHost</code>, because that method is ambiguous on Linux systems.
77       * Linux systems enumerate the loopback network interface the same way as regular LAN network
78       * interfaces, but the JDK <code>InetAddress.getLocalHost</code> method does not specify the
79       * algorithm used to select the address returned under such circumstances, and will often return
80       * the loopback address, which is not valid for network communication. Details <a
81       * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037">here</a>.
82       * <p>
83       * This method will scan all IP addresses on all network interfaces on the host machine to
84       * determine the IP address most likely to be the machine's LAN address. If the machine has
85       * multiple IP addresses, this method will prefer a site-local IP address (e.g. 192.168.x.x or
86       * 10.10.x.x, usually IPv4) if the machine has one (and will return the first site-local address
87       * if the machine has more than one), but if the machine does not hold a site-local address,
88       * this method will return simply the first non-loopback address found (IPv4 or IPv6).</p>
89       * <p>
90       * If this method cannot find a non-loopback address using this selection algorithm, it will
91       * fall back to calling and returning the result of JDK method
92       * <code>InetAddress.getLocalHost</code>.
93       * <p>
94       * <a href="http://issues.apache.org/jira/browse/JCS-40">JIR ISSUE JCS-40</a>
95       * <p>
96       * @return InetAddress
97       * @throws UnknownHostException If the LAN address of the machine cannot be found.
98       * @since 3.1
99       */
100     public static InetAddress getLocalHostLANAddress()
101         throws UnknownHostException
102     {
103         return getLocalHostLANAddresses().stream().findFirst().orElse(null);
104     }
105 
106     /**
107      * Returns all <code>InetAddress</code> objects encapsulating what are most likely the machine's
108      * LAN IP addresses.
109      * <p>
110      * This method will scan all IP addresses on all network interfaces on the host machine to
111      * determine the IP addresses most likely to be the machine's LAN addresses.
112      * <p>
113      * @return List<InetAddress>
114      * @throws UnknownHostException If the LAN address of the machine cannot be found.
115      */
116     public static List<InetAddress> getLocalHostLANAddresses()
117         throws UnknownHostException
118     {
119         List<InetAddress> addresses = new ArrayList<>();
120 
121         try
122         {
123             InetAddress candidateAddress = null;
124             // Iterate all NICs (network interface cards)...
125             final Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
126             while ( ifaces.hasMoreElements() )
127             {
128                 final NetworkInterface iface = ifaces.nextElement();
129 
130                 // Skip loopback interfaces
131                 if (iface.isLoopback() || !iface.isUp())
132                 {
133                     continue;
134                 }
135 
136                 // Iterate all IP addresses assigned to each card...
137                 for ( final Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); )
138                 {
139                     final InetAddress inetAddr = inetAddrs.nextElement();
140                     if ( !inetAddr.isLoopbackAddress() )
141                     {
142                         if (inetAddr.isSiteLocalAddress())
143                         {
144                             // Found non-loopback site-local address.
145                             addresses.add(inetAddr);
146                         }
147                         if ( candidateAddress == null )
148                         {
149                             // Found non-loopback address, but not necessarily site-local.
150                             // Store it as a candidate to be returned if site-local address is not subsequently found...
151                             candidateAddress = inetAddr;
152                             // Note that we don't repeatedly assign non-loopback non-site-local addresses as candidates,
153                             // only the first. For subsequent iterations, candidate will be non-null.
154                         }
155                     }
156                 }
157             }
158             if (candidateAddress != null && addresses.isEmpty())
159             {
160                 // We did not find a site-local address, but we found some other non-loopback address.
161                 // Server might have a non-site-local address assigned to its NIC (or it might be running
162                 // IPv6 which deprecates the "site-local" concept).
163                 addresses.add(candidateAddress);
164             }
165             // At this point, we did not find a non-loopback address.
166             // Fall back to returning whatever InetAddress.getLocalHost() returns...
167             if (addresses.isEmpty())
168             {
169                 final InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
170                 if ( jdkSuppliedAddress == null )
171                 {
172                     throw new UnknownHostException( "The JDK InetAddress.getLocalHost() method unexpectedly returned null." );
173                 }
174                 addresses.add(jdkSuppliedAddress);
175             }
176         }
177         catch ( final SocketException e )
178         {
179             final UnknownHostException unknownHostException = new UnknownHostException( "Failed to determine LAN address: "
180                 + e );
181             unknownHostException.initCause( e );
182             throw unknownHostException;
183         }
184 
185         return addresses;
186     }
187 
188     /**
189      * On systems with multiple network interfaces and mixed IPv6/IPv4 get a valid network
190      * interface for binding to multicast
191      *
192      * @return a network interface suitable for multicast
193      * @throws SocketException if a problem occurs while reading the network interfaces
194      */
195     public static NetworkInterface getMulticastNetworkInterface() throws SocketException
196     {
197         final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
198         while (networkInterfaces.hasMoreElements())
199         {
200             final NetworkInterface networkInterface = networkInterfaces.nextElement();
201             final Enumeration<InetAddress> addressesFromNetworkInterface = networkInterface.getInetAddresses();
202             while (addressesFromNetworkInterface.hasMoreElements())
203             {
204                 final InetAddress inetAddress = addressesFromNetworkInterface.nextElement();
205                 if (inetAddress.isSiteLocalAddress()
206                         && !inetAddress.isAnyLocalAddress()
207                         && !inetAddress.isLinkLocalAddress()
208                         && !inetAddress.isLoopbackAddress()
209                         && !inetAddress.isMulticastAddress())
210                 {
211                     return networkInterface;
212                 }
213             }
214         }
215 
216         return null;
217     }
218 }