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 java.net.InetAddress;
22 import java.net.NetworkInterface;
23 import java.net.SocketException;
24 import java.net.UnknownHostException;
25 import java.util.Collections;
26 import java.util.Enumeration;
27 import java.util.LinkedHashSet;
28 import java.util.Vector;
29 
30 import javax.naming.NamingException;
31 import javax.naming.directory.Attributes;
32 import javax.naming.directory.DirContext;
33 import javax.naming.directory.InitialDirContext;
34 
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 
38 /**
39  *
40  * A class that provides direct and reverse lookup functionalities, allowing
41  * the querying of specific network interfaces or nameservers.
42  *
43  *
44  */
45 public class DNS {
46 
47   public static final Log LOG = LogFactory.getLog(DNS.class);
48 
49   /**
50    * Returns the hostname associated with the specified IP address by the
51    * provided nameserver.
52    *
53    * @param hostIp
54    *            The address to reverse lookup
55    * @param ns
56    *            The host name of a reachable DNS server
57    * @return The host name associated with the provided IP
58    * @throws NamingException
59    *             If a NamingException is encountered
60    */
reverseDns(InetAddress hostIp, String ns)61   public static String reverseDns(InetAddress hostIp, String ns)
62     throws NamingException {
63     //
64     // Builds the reverse IP lookup form
65     // This is formed by reversing the IP numbers and appending in-addr.arpa
66     //
67     String[] parts = hostIp.getHostAddress().split("\\.");
68     String reverseIP = parts[3] + "." + parts[2] + "." + parts[1] + "."
69       + parts[0] + ".in-addr.arpa";
70 
71     DirContext ictx = new InitialDirContext();
72     Attributes attribute =
73       ictx.getAttributes("dns://"               // Use "dns:///" if the default
74                          + ((ns == null) ? "" : ns) +
75                          // nameserver is to be used
76                          "/" + reverseIP, new String[] { "PTR" });
77     ictx.close();
78 
79     String hostname = attribute.get("PTR").get().toString();
80     int hostnameLength = hostname.length();
81     if (hostname.charAt(hostnameLength - 1) == '.') {
82       hostname = hostname.substring(0, hostnameLength - 1);
83     }
84     return hostname;
85   }
86 
87   /**
88    * @return NetworkInterface for the given subinterface name (eg eth0:0)
89    *    or null if no interface with the given name can be found
90    */
getSubinterface(String strInterface)91   private static NetworkInterface getSubinterface(String strInterface)
92       throws SocketException {
93     Enumeration<NetworkInterface> nifs =
94       NetworkInterface.getNetworkInterfaces();
95 
96     while (nifs.hasMoreElements()) {
97       Enumeration<NetworkInterface> subNifs =
98         nifs.nextElement().getSubInterfaces();
99 
100       while (subNifs.hasMoreElements()) {
101         NetworkInterface nif = subNifs.nextElement();
102         if (nif.getName().equals(strInterface)) {
103           return nif;
104         }
105       }
106     }
107     return null;
108   }
109 
110   /**
111    * @param nif network interface to get addresses for
112    * @return set containing addresses for each subinterface of nif,
113    *    see below for the rationale for using an ordered set
114    */
getSubinterfaceInetAddrs( NetworkInterface nif)115   private static LinkedHashSet<InetAddress> getSubinterfaceInetAddrs(
116       NetworkInterface nif) {
117     LinkedHashSet<InetAddress> addrs = new LinkedHashSet<InetAddress>();
118     Enumeration<NetworkInterface> subNifs = nif.getSubInterfaces();
119     while (subNifs.hasMoreElements()) {
120       NetworkInterface subNif = subNifs.nextElement();
121       addrs.addAll(Collections.list(subNif.getInetAddresses()));
122     }
123     return addrs;
124   }
125 
126   /**
127    * Like {@link DNS#getIPs(String, boolean)}, but returns all
128    * IPs associated with the given interface and its subinterfaces.
129    */
getIPs(String strInterface)130   public static String[] getIPs(String strInterface)
131       throws UnknownHostException {
132     return getIPs(strInterface, true);
133   }
134 
135   /**
136    * Returns all the IPs associated with the provided interface, if any, in
137    * textual form.
138    *
139    * @param strInterface
140    *            The name of the network interface or subinterface to query
141    *            (eg eth0 or eth0:0) or the string "default"
142    * @param returnSubinterfaces
143    *            Whether to return IPs associated with subinterfaces of
144    *            the given interface
145    * @return A string vector of all the IPs associated with the provided
146    *         interface
147    * @throws UnknownHostException
148    *             If an UnknownHostException is encountered in querying the
149    *             default interface or the given interface can not be found
150    *
151    */
getIPs(String strInterface, boolean returnSubinterfaces)152   public static String[] getIPs(String strInterface,
153       boolean returnSubinterfaces) throws UnknownHostException {
154     if ("default".equals(strInterface)) {
155       return new String[] {
156           InetAddress.getLocalHost().getHostAddress()
157       };
158     }
159     NetworkInterface netIf;
160     try {
161       netIf = NetworkInterface.getByName(strInterface);
162       if (netIf == null) {
163         netIf = getSubinterface(strInterface);
164         if (netIf == null) {
165           throw new UnknownHostException("Unknown interface " + strInterface);
166         }
167       }
168     } catch (SocketException e) {
169       LOG.warn("Unable to get IP for interface " + strInterface, e);
170       return new String[] {
171           InetAddress.getLocalHost().getHostAddress()
172       };
173     }
174 
175     // NB: Using a LinkedHashSet to preserve the order for callers
176     // that depend on a particular element being 1st in the array.
177     // For example, getDefaultIP always returns the first element.
178     LinkedHashSet<InetAddress> allAddrs = new LinkedHashSet<InetAddress>();
179     allAddrs.addAll(Collections.list(netIf.getInetAddresses()));
180     if (!returnSubinterfaces) {
181       allAddrs.removeAll(getSubinterfaceInetAddrs(netIf));
182     }
183 
184     String ips[] = new String[allAddrs.size()];
185     int i = 0;
186     for (InetAddress addr : allAddrs) {
187       ips[i++] = addr.getHostAddress();
188     }
189     return ips;
190   }
191 
192   /**
193    * Returns the first available IP address associated with the provided
194    * network interface
195    *
196    * @param strInterface
197    *            The name of the network interface or subinterface to query
198    *            (e.g. eth0 or eth0:0) or the string "default"
199    * @return The IP address in text form
200    * @throws UnknownHostException
201    *             If one is encountered in querying the default interface
202    */
getDefaultIP(String strInterface)203   public static String getDefaultIP(String strInterface)
204     throws UnknownHostException {
205     String[] ips = getIPs(strInterface);
206     return ips[0];
207   }
208 
209   /**
210    * Returns all the host names associated by the provided nameserver with the
211    * address bound to the specified network interface
212    *
213    * @param strInterface
214    *            The name of the network interface or subinterface to query
215    *            (e.g. eth0 or eth0:0) or the string "default"
216    * @param nameserver
217    *            The DNS host name
218    * @return A string vector of all host names associated with the IPs tied to
219    *         the specified interface
220    * @throws UnknownHostException
221    */
getHosts(String strInterface, String nameserver)222   public static String[] getHosts(String strInterface, String nameserver)
223     throws UnknownHostException {
224     String[] ips = getIPs(strInterface);
225     Vector<String> hosts = new Vector<String>();
226     for (int ctr = 0; ctr < ips.length; ctr++)
227       try {
228         hosts.add(reverseDns(InetAddress.getByName(ips[ctr]),
229                              nameserver));
230       } catch (Exception e) {
231       }
232 
233     if (hosts.size() == 0)
234       return new String[] { InetAddress.getLocalHost().getCanonicalHostName() };
235     else
236       return hosts.toArray(new String[] {});
237   }
238 
239   /**
240    * Returns all the host names associated by the default nameserver with the
241    * address bound to the specified network interface
242    *
243    * @param strInterface
244    *            The name of the network interface to query (e.g. eth0)
245    * @return The list of host names associated with IPs bound to the network
246    *         interface
247    * @throws UnknownHostException
248    *             If one is encountered while querying the deault interface
249    *
250    */
getHosts(String strInterface)251   public static String[] getHosts(String strInterface)
252     throws UnknownHostException {
253     return getHosts(strInterface, null);
254   }
255 
256   /**
257    * Returns the default (first) host name associated by the provided
258    * nameserver with the address bound to the specified network interface
259    *
260    * @param strInterface
261    *            The name of the network interface to query (e.g. eth0)
262    * @param nameserver
263    *            The DNS host name
264    * @return The default host names associated with IPs bound to the network
265    *         interface
266    * @throws UnknownHostException
267    *             If one is encountered while querying the deault interface
268    */
getDefaultHost(String strInterface, String nameserver)269   public static String getDefaultHost(String strInterface, String nameserver)
270     throws UnknownHostException {
271     if (strInterface.equals("default"))
272       return InetAddress.getLocalHost().getCanonicalHostName();
273 
274     if (nameserver != null && nameserver.equals("default"))
275       return getDefaultHost(strInterface);
276 
277     String[] hosts = getHosts(strInterface, nameserver);
278     return hosts[0];
279   }
280 
281   /**
282    * Returns the default (first) host name associated by the default
283    * nameserver with the address bound to the specified network interface
284    *
285    * @param strInterface
286    *            The name of the network interface to query (e.g. eth0)
287    * @return The default host name associated with IPs bound to the network
288    *         interface
289    * @throws UnknownHostException
290    *             If one is encountered while querying the deault interface
291    */
getDefaultHost(String strInterface)292   public static String getDefaultHost(String strInterface)
293     throws UnknownHostException {
294     return getDefaultHost(strInterface, null);
295   }
296 
297 }
298