1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.net; 20 21 import org.apache.commons.logging.Log; 22 import org.apache.commons.logging.LogFactory; 23 import org.apache.hadoop.classification.InterfaceAudience; 24 import org.apache.hadoop.classification.InterfaceStability; 25 26 import java.net.InetAddress; 27 import java.net.NetworkInterface; 28 import java.net.SocketException; 29 import java.net.UnknownHostException; 30 import java.util.Collections; 31 import java.util.Enumeration; 32 import java.util.LinkedHashSet; 33 import java.util.Vector; 34 35 import javax.naming.NamingException; 36 import javax.naming.directory.Attributes; 37 import javax.naming.directory.DirContext; 38 import javax.naming.directory.InitialDirContext; 39 40 /** 41 * 42 * A class that provides direct and reverse lookup functionalities, allowing 43 * the querying of specific network interfaces or nameservers. 44 * 45 * 46 */ 47 @InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"}) 48 @InterfaceStability.Unstable 49 public class DNS { 50 51 private static final Log LOG = LogFactory.getLog(DNS.class); 52 53 /** 54 * The cached hostname -initially null. 55 */ 56 57 private static final String cachedHostname = resolveLocalHostname(); 58 private static final String cachedHostAddress = resolveLocalHostIPAddress(); 59 private static final String LOCALHOST = "localhost"; 60 61 /** 62 * Returns the hostname associated with the specified IP address by the 63 * provided nameserver. 64 * 65 * Loopback addresses 66 * @param hostIp The address to reverse lookup 67 * @param ns The host name of a reachable DNS server 68 * @return The host name associated with the provided IP 69 * @throws NamingException If a NamingException is encountered 70 */ reverseDns(InetAddress hostIp, String ns)71 public static String reverseDns(InetAddress hostIp, String ns) 72 throws NamingException { 73 // 74 // Builds the reverse IP lookup form 75 // This is formed by reversing the IP numbers and appending in-addr.arpa 76 // 77 String[] parts = hostIp.getHostAddress().split("\\."); 78 String reverseIP = parts[3] + "." + parts[2] + "." + parts[1] + "." 79 + parts[0] + ".in-addr.arpa"; 80 81 DirContext ictx = new InitialDirContext(); 82 Attributes attribute; 83 try { 84 attribute = ictx.getAttributes("dns://" // Use "dns:///" if the default 85 + ((ns == null) ? "" : ns) + 86 // nameserver is to be used 87 "/" + reverseIP, new String[] { "PTR" }); 88 } finally { 89 ictx.close(); 90 } 91 92 String hostname = attribute.get("PTR").get().toString(); 93 int hostnameLength = hostname.length(); 94 if (hostname.charAt(hostnameLength - 1) == '.') { 95 hostname = hostname.substring(0, hostnameLength - 1); 96 } 97 return hostname; 98 } 99 100 /** 101 * @return NetworkInterface for the given subinterface name (eg eth0:0) 102 * or null if no interface with the given name can be found 103 */ getSubinterface(String strInterface)104 private static NetworkInterface getSubinterface(String strInterface) 105 throws SocketException { 106 Enumeration<NetworkInterface> nifs = 107 NetworkInterface.getNetworkInterfaces(); 108 109 while (nifs.hasMoreElements()) { 110 Enumeration<NetworkInterface> subNifs = 111 nifs.nextElement().getSubInterfaces(); 112 113 while (subNifs.hasMoreElements()) { 114 NetworkInterface nif = subNifs.nextElement(); 115 if (nif.getName().equals(strInterface)) { 116 return nif; 117 } 118 } 119 } 120 return null; 121 } 122 123 /** 124 * @param nif network interface to get addresses for 125 * @return set containing addresses for each subinterface of nif, 126 * see below for the rationale for using an ordered set 127 */ getSubinterfaceInetAddrs( NetworkInterface nif)128 private static LinkedHashSet<InetAddress> getSubinterfaceInetAddrs( 129 NetworkInterface nif) { 130 LinkedHashSet<InetAddress> addrs = new LinkedHashSet<InetAddress>(); 131 Enumeration<NetworkInterface> subNifs = nif.getSubInterfaces(); 132 while (subNifs.hasMoreElements()) { 133 NetworkInterface subNif = subNifs.nextElement(); 134 addrs.addAll(Collections.list(subNif.getInetAddresses())); 135 } 136 return addrs; 137 } 138 139 /** 140 * Like {@link DNS#getIPs(String, boolean), but returns all 141 * IPs associated with the given interface and its subinterfaces. 142 */ getIPs(String strInterface)143 public static String[] getIPs(String strInterface) 144 throws UnknownHostException { 145 return getIPs(strInterface, true); 146 } 147 148 /** 149 * Returns all the IPs associated with the provided interface, if any, in 150 * textual form. 151 * 152 * @param strInterface 153 * The name of the network interface or sub-interface to query 154 * (eg eth0 or eth0:0) or the string "default" 155 * @param returnSubinterfaces 156 * Whether to return IPs associated with subinterfaces of 157 * the given interface 158 * @return A string vector of all the IPs associated with the provided 159 * interface. The local host IP is returned if the interface 160 * name "default" is specified or there is an I/O error looking 161 * for the given interface. 162 * @throws UnknownHostException 163 * If the given interface is invalid 164 * 165 */ getIPs(String strInterface, boolean returnSubinterfaces)166 public static String[] getIPs(String strInterface, 167 boolean returnSubinterfaces) throws UnknownHostException { 168 if ("default".equals(strInterface)) { 169 return new String[] { cachedHostAddress }; 170 } 171 NetworkInterface netIf; 172 try { 173 netIf = NetworkInterface.getByName(strInterface); 174 if (netIf == null) { 175 netIf = getSubinterface(strInterface); 176 } 177 } catch (SocketException e) { 178 LOG.warn("I/O error finding interface " + strInterface + 179 ": " + e.getMessage()); 180 return new String[] { cachedHostAddress }; 181 } 182 if (netIf == null) { 183 throw new UnknownHostException("No such interface " + strInterface); 184 } 185 186 // NB: Using a LinkedHashSet to preserve the order for callers 187 // that depend on a particular element being 1st in the array. 188 // For example, getDefaultIP always returns the first element. 189 LinkedHashSet<InetAddress> allAddrs = new LinkedHashSet<InetAddress>(); 190 allAddrs.addAll(Collections.list(netIf.getInetAddresses())); 191 if (!returnSubinterfaces) { 192 allAddrs.removeAll(getSubinterfaceInetAddrs(netIf)); 193 } 194 195 String ips[] = new String[allAddrs.size()]; 196 int i = 0; 197 for (InetAddress addr : allAddrs) { 198 ips[i++] = addr.getHostAddress(); 199 } 200 return ips; 201 } 202 203 204 /** 205 * Returns the first available IP address associated with the provided 206 * network interface or the local host IP if "default" is given. 207 * 208 * @param strInterface 209 * The name of the network interface or subinterface to query 210 * (e.g. eth0 or eth0:0) or the string "default" 211 * @return The IP address in text form, the local host IP is returned 212 * if the interface name "default" is specified 213 * @throws UnknownHostException 214 * If the given interface is invalid 215 */ getDefaultIP(String strInterface)216 public static String getDefaultIP(String strInterface) 217 throws UnknownHostException { 218 String[] ips = getIPs(strInterface); 219 return ips[0]; 220 } 221 222 /** 223 * Returns all the host names associated by the provided nameserver with the 224 * address bound to the specified network interface 225 * 226 * @param strInterface 227 * The name of the network interface or subinterface to query 228 * (e.g. eth0 or eth0:0) 229 * @param nameserver 230 * The DNS host name 231 * @return A string vector of all host names associated with the IPs tied to 232 * the specified interface 233 * @throws UnknownHostException if the given interface is invalid 234 */ getHosts(String strInterface, String nameserver)235 public static String[] getHosts(String strInterface, String nameserver) 236 throws UnknownHostException { 237 String[] ips = getIPs(strInterface); 238 Vector<String> hosts = new Vector<String>(); 239 for (int ctr = 0; ctr < ips.length; ctr++) { 240 try { 241 hosts.add(reverseDns(InetAddress.getByName(ips[ctr]), 242 nameserver)); 243 } catch (UnknownHostException ignored) { 244 } catch (NamingException ignored) { 245 } 246 } 247 if (hosts.isEmpty()) { 248 LOG.warn("Unable to determine hostname for interface " + strInterface); 249 return new String[] { cachedHostname }; 250 } else { 251 return hosts.toArray(new String[hosts.size()]); 252 } 253 } 254 255 256 /** 257 * Determine the local hostname; retrieving it from cache if it is known 258 * If we cannot determine our host name, return "localhost" 259 * @return the local hostname or "localhost" 260 */ resolveLocalHostname()261 private static String resolveLocalHostname() { 262 String localhost; 263 try { 264 localhost = InetAddress.getLocalHost().getCanonicalHostName(); 265 } catch (UnknownHostException e) { 266 LOG.warn("Unable to determine local hostname " 267 + "-falling back to \"" + LOCALHOST + "\"", e); 268 localhost = LOCALHOST; 269 } 270 return localhost; 271 } 272 273 274 /** 275 * Get the IPAddress of the local host as a string. 276 * This will be a loop back value if the local host address cannot be 277 * determined. 278 * If the loopback address of "localhost" does not resolve, then the system's 279 * network is in such a state that nothing is going to work. A message is 280 * logged at the error level and a null pointer returned, a pointer 281 * which will trigger failures later on the application 282 * @return the IPAddress of the local host or null for a serious problem. 283 */ resolveLocalHostIPAddress()284 private static String resolveLocalHostIPAddress() { 285 String address; 286 try { 287 address = InetAddress.getLocalHost().getHostAddress(); 288 } catch (UnknownHostException e) { 289 LOG.warn("Unable to determine address of the host" 290 + "-falling back to \"" + LOCALHOST + "\" address", e); 291 try { 292 address = InetAddress.getByName(LOCALHOST).getHostAddress(); 293 } catch (UnknownHostException noLocalHostAddressException) { 294 //at this point, deep trouble 295 LOG.error("Unable to determine local loopback address " 296 + "of \"" + LOCALHOST + "\" " + 297 "-this system's network configuration is unsupported", e); 298 address = null; 299 } 300 } 301 return address; 302 } 303 304 /** 305 * Returns all the host names associated by the default nameserver with the 306 * address bound to the specified network interface 307 * 308 * @param strInterface 309 * The name of the network interface to query (e.g. eth0) 310 * @return The list of host names associated with IPs bound to the network 311 * interface 312 * @throws UnknownHostException 313 * If one is encountered while querying the default interface 314 * 315 */ getHosts(String strInterface)316 public static String[] getHosts(String strInterface) 317 throws UnknownHostException { 318 return getHosts(strInterface, null); 319 } 320 321 /** 322 * Returns the default (first) host name associated by the provided 323 * nameserver with the address bound to the specified network interface 324 * 325 * @param strInterface 326 * The name of the network interface to query (e.g. eth0) 327 * @param nameserver 328 * The DNS host name 329 * @return The default host names associated with IPs bound to the network 330 * interface 331 * @throws UnknownHostException 332 * If one is encountered while querying the default interface 333 */ getDefaultHost(String strInterface, String nameserver)334 public static String getDefaultHost(String strInterface, String nameserver) 335 throws UnknownHostException { 336 if ("default".equals(strInterface)) { 337 return cachedHostname; 338 } 339 340 if ("default".equals(nameserver)) { 341 return getDefaultHost(strInterface); 342 } 343 344 String[] hosts = getHosts(strInterface, nameserver); 345 return hosts[0]; 346 } 347 348 /** 349 * Returns the default (first) host name associated by the default 350 * nameserver with the address bound to the specified network interface 351 * 352 * @param strInterface 353 * The name of the network interface to query (e.g. eth0). 354 * Must not be null. 355 * @return The default host name associated with IPs bound to the network 356 * interface 357 * @throws UnknownHostException 358 * If one is encountered while querying the default interface 359 */ getDefaultHost(String strInterface)360 public static String getDefaultHost(String strInterface) 361 throws UnknownHostException { 362 return getDefaultHost(strInterface, null); 363 } 364 365 } 366