001package org.apache.commons.jcs3.utils.net; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.net.InetAddress; 023import java.net.NetworkInterface; 024import java.net.SocketException; 025import java.net.UnknownHostException; 026import java.util.ArrayList; 027import java.util.Enumeration; 028/* 029 * or more contributor license agreements. See the NOTICE file 030 * distributed with this work for additional information 031 * regarding copyright ownership. The ASF licenses this file 032 * to you under the Apache License, Version 2.0 (the 033 * "License"); you may not use this file except in compliance 034 * with the License. You may obtain a copy of the License at 035 * 036 * http://www.apache.org/licenses/LICENSE-2.0 037 * 038 * Unless required by applicable law or agreed to in writing, 039 * software distributed under the License is distributed on an 040 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 041 * KIND, either express or implied. See the License for the 042 * specific language governing permissions and limitations 043 * under the License. 044 */ 045import java.util.List; 046 047import org.apache.commons.jcs3.log.Log; 048import org.apache.commons.jcs3.log.LogManager; 049 050/** 051 * Simple utility for getting the local host name. 052 */ 053public class HostNameUtil 054{ 055 /** The logger. */ 056 private static final Log log = LogManager.getLog( HostNameUtil.class ); 057 058 /** 059 * Gets the address for the local machine. 060 * <p> 061 * @return InetAddress.getLocalHost().getHostAddress() 062 * @throws UnknownHostException 063 */ 064 public static String getLocalHostAddress() throws UnknownHostException 065 { 066 final String hostAddress = getLocalHostLANAddress().getHostAddress(); 067 log.debug( "hostAddress = [{0}]", hostAddress ); 068 return hostAddress; 069 } 070 071 /** 072 * Returns an <code>InetAddress</code> object encapsulating what is most likely the machine's 073 * LAN IP address. 074 * <p> 075 * This method is intended for use as a replacement of JDK method 076 * <code>InetAddress.getLocalHost</code>, because that method is ambiguous on Linux systems. 077 * Linux systems enumerate the loopback network interface the same way as regular LAN network 078 * interfaces, but the JDK <code>InetAddress.getLocalHost</code> method does not specify the 079 * algorithm used to select the address returned under such circumstances, and will often return 080 * the loopback address, which is not valid for network communication. Details <a 081 * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037">here</a>. 082 * <p> 083 * This method will scan all IP addresses on all network interfaces on the host machine to 084 * determine the IP address most likely to be the machine's LAN address. If the machine has 085 * multiple IP addresses, this method will prefer a site-local IP address (e.g. 192.168.x.x or 086 * 10.10.x.x, usually IPv4) if the machine has one (and will return the first site-local address 087 * if the machine has more than one), but if the machine does not hold a site-local address, 088 * this method will return simply the first non-loopback address found (IPv4 or IPv6).</p> 089 * <p> 090 * If this method cannot find a non-loopback address using this selection algorithm, it will 091 * fall back to calling and returning the result of JDK method 092 * <code>InetAddress.getLocalHost</code>. 093 * <p> 094 * <a href="http://issues.apache.org/jira/browse/JCS-40">JIR ISSUE JCS-40</a> 095 * <p> 096 * @return InetAddress 097 * @throws UnknownHostException If the LAN address of the machine cannot be found. 098 * @since 3.1 099 */ 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}