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 if (!networkInterface.supportsMulticast())
202 {
203 log.trace("found network interface [{0}]: no multicast support", networkInterface::getDisplayName);
204 continue;
205 }
206 final Enumeration<InetAddress> addressesFromNetworkInterface = networkInterface.getInetAddresses();
207 while (addressesFromNetworkInterface.hasMoreElements())
208 {
209 final InetAddress inetAddress = addressesFromNetworkInterface.nextElement();
210 log.trace("found network interface [{0}]: address: {1}, site local: {2}, any local {3}, link local {4}, loopback {5}, multicast {6}",
211 networkInterface::getDisplayName, inetAddress::getHostAddress, inetAddress::isSiteLocalAddress,
212 inetAddress::isAnyLocalAddress, inetAddress::isLinkLocalAddress, inetAddress::isLoopbackAddress,
213 inetAddress::isMulticastAddress);
214
215 if (inetAddress.isSiteLocalAddress()
216 && !inetAddress.isAnyLocalAddress()
217 && !inetAddress.isLinkLocalAddress()
218 && !inetAddress.isLoopbackAddress()
219 && !inetAddress.isMulticastAddress())
220 {
221 return networkInterface;
222 }
223 }
224 }
225
226 return null;
227 }
228 }