| // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) |
| |
| package org.xbill.DNS; |
| |
| import java.io.*; |
| import java.lang.reflect.*; |
| import java.util.*; |
| |
| /** |
| * A class that tries to locate name servers and the search path to |
| * be appended to unqualified names. |
| * |
| * The following are attempted, in order, until one succeeds. |
| * <UL> |
| * <LI>The properties 'dns.server' and 'dns.search' (comma delimited lists) |
| * are checked. The servers can either be IP addresses or hostnames |
| * (which are resolved using Java's built in DNS support). |
| * <LI>The sun.net.dns.ResolverConfiguration class is queried. |
| * <LI>On Unix, /etc/resolv.conf is parsed. |
| * <LI>On Windows, ipconfig/winipcfg is called and its output parsed. This |
| * may fail for non-English versions on Windows. |
| * <LI>"localhost" is used as the nameserver, and the search path is empty. |
| * </UL> |
| * |
| * These routines will be called internally when creating Resolvers/Lookups |
| * without explicitly specifying server names, and can also be called |
| * directly if desired. |
| * |
| * @author Brian Wellington |
| * @author <a href="mailto:yannick@meudal.net">Yannick Meudal</a> |
| * @author <a href="mailto:arnt@gulbrandsen.priv.no">Arnt Gulbrandsen</a> |
| */ |
| |
| public class ResolverConfig { |
| |
| private String [] servers = null; |
| private Name [] searchlist = null; |
| private int ndots = -1; |
| |
| private static ResolverConfig currentConfig; |
| |
| static { |
| refresh(); |
| } |
| |
| public |
| ResolverConfig() { |
| if (findProperty()) |
| return; |
| if (findSunJVM()) |
| return; |
| if (servers == null || searchlist == null) { |
| String OS = System.getProperty("os.name"); |
| String vendor = System.getProperty("java.vendor"); |
| if (OS.indexOf("Windows") != -1) { |
| if (OS.indexOf("95") != -1 || |
| OS.indexOf("98") != -1 || |
| OS.indexOf("ME") != -1) |
| find95(); |
| else |
| findNT(); |
| } else if (OS.indexOf("NetWare") != -1) { |
| findNetware(); |
| } else if (vendor.indexOf("Android") != -1) { |
| findAndroid(); |
| } else { |
| findUnix(); |
| } |
| } |
| } |
| |
| private void |
| addServer(String server, List list) { |
| if (list.contains(server)) |
| return; |
| if (Options.check("verbose")) |
| System.out.println("adding server " + server); |
| list.add(server); |
| } |
| |
| private void |
| addSearch(String search, List list) { |
| Name name; |
| if (Options.check("verbose")) |
| System.out.println("adding search " + search); |
| try { |
| name = Name.fromString(search, Name.root); |
| } |
| catch (TextParseException e) { |
| return; |
| } |
| if (list.contains(name)) |
| return; |
| list.add(name); |
| } |
| |
| private int |
| parseNdots(String token) { |
| token = token.substring(6); |
| try { |
| int ndots = Integer.parseInt(token); |
| if (ndots >= 0) { |
| if (Options.check("verbose")) |
| System.out.println("setting ndots " + token); |
| return ndots; |
| } |
| } |
| catch (NumberFormatException e) { |
| } |
| return -1; |
| } |
| |
| private void |
| configureFromLists(List lserver, List lsearch) { |
| if (servers == null && lserver.size() > 0) |
| servers = (String []) lserver.toArray(new String[0]); |
| if (searchlist == null && lsearch.size() > 0) |
| searchlist = (Name []) lsearch.toArray(new Name[0]); |
| } |
| |
| private void |
| configureNdots(int lndots) { |
| if (ndots < 0 && lndots > 0) |
| ndots = lndots; |
| } |
| |
| /** |
| * Looks in the system properties to find servers and a search path. |
| * Servers are defined by dns.server=server1,server2... |
| * The search path is defined by dns.search=domain1,domain2... |
| */ |
| private boolean |
| findProperty() { |
| String prop; |
| List lserver = new ArrayList(0); |
| List lsearch = new ArrayList(0); |
| StringTokenizer st; |
| |
| prop = System.getProperty("dns.server"); |
| if (prop != null) { |
| st = new StringTokenizer(prop, ","); |
| while (st.hasMoreTokens()) |
| addServer(st.nextToken(), lserver); |
| } |
| |
| prop = System.getProperty("dns.search"); |
| if (prop != null) { |
| st = new StringTokenizer(prop, ","); |
| while (st.hasMoreTokens()) |
| addSearch(st.nextToken(), lsearch); |
| } |
| configureFromLists(lserver, lsearch); |
| return (servers != null && searchlist != null); |
| } |
| |
| /** |
| * Uses the undocumented Sun DNS implementation to determine the configuration. |
| * This doesn't work or even compile with all JVMs (gcj, for example). |
| */ |
| private boolean |
| findSunJVM() { |
| List lserver = new ArrayList(0); |
| List lserver_tmp; |
| List lsearch = new ArrayList(0); |
| List lsearch_tmp; |
| |
| try { |
| Class [] noClasses = new Class[0]; |
| Object [] noObjects = new Object[0]; |
| String resConfName = "sun.net.dns.ResolverConfiguration"; |
| Class resConfClass = Class.forName(resConfName); |
| Object resConf; |
| |
| // ResolverConfiguration resConf = ResolverConfiguration.open(); |
| Method open = resConfClass.getDeclaredMethod("open", noClasses); |
| resConf = open.invoke(null, noObjects); |
| |
| // lserver_tmp = resConf.nameservers(); |
| Method nameservers = resConfClass.getMethod("nameservers", |
| noClasses); |
| lserver_tmp = (List) nameservers.invoke(resConf, noObjects); |
| |
| // lsearch_tmp = resConf.searchlist(); |
| Method searchlist = resConfClass.getMethod("searchlist", |
| noClasses); |
| lsearch_tmp = (List) searchlist.invoke(resConf, noObjects); |
| } |
| catch (Exception e) { |
| return false; |
| } |
| |
| if (lserver_tmp.size() == 0) |
| return false; |
| |
| if (lserver_tmp.size() > 0) { |
| Iterator it = lserver_tmp.iterator(); |
| while (it.hasNext()) |
| addServer((String) it.next(), lserver); |
| } |
| |
| if (lsearch_tmp.size() > 0) { |
| Iterator it = lsearch_tmp.iterator(); |
| while (it.hasNext()) |
| addSearch((String) it.next(), lsearch); |
| } |
| configureFromLists(lserver, lsearch); |
| return true; |
| } |
| |
| /** |
| * Looks in /etc/resolv.conf to find servers and a search path. |
| * "nameserver" lines specify servers. "domain" and "search" lines |
| * define the search path. |
| */ |
| private void |
| findResolvConf(String file) { |
| InputStream in = null; |
| try { |
| in = new FileInputStream(file); |
| } |
| catch (FileNotFoundException e) { |
| return; |
| } |
| InputStreamReader isr = new InputStreamReader(in); |
| BufferedReader br = new BufferedReader(isr); |
| List lserver = new ArrayList(0); |
| List lsearch = new ArrayList(0); |
| int lndots = -1; |
| try { |
| String line; |
| while ((line = br.readLine()) != null) { |
| if (line.startsWith("nameserver")) { |
| StringTokenizer st = new StringTokenizer(line); |
| st.nextToken(); /* skip nameserver */ |
| addServer(st.nextToken(), lserver); |
| } |
| else if (line.startsWith("domain")) { |
| StringTokenizer st = new StringTokenizer(line); |
| st.nextToken(); /* skip domain */ |
| if (!st.hasMoreTokens()) |
| continue; |
| if (lsearch.isEmpty()) |
| addSearch(st.nextToken(), lsearch); |
| } |
| else if (line.startsWith("search")) { |
| if (!lsearch.isEmpty()) |
| lsearch.clear(); |
| StringTokenizer st = new StringTokenizer(line); |
| st.nextToken(); /* skip search */ |
| while (st.hasMoreTokens()) |
| addSearch(st.nextToken(), lsearch); |
| } |
| else if(line.startsWith("options")) { |
| StringTokenizer st = new StringTokenizer(line); |
| st.nextToken(); /* skip options */ |
| while (st.hasMoreTokens()) { |
| String token = st.nextToken(); |
| if (token.startsWith("ndots:")) { |
| lndots = parseNdots(token); |
| } |
| } |
| } |
| } |
| br.close(); |
| } |
| catch (IOException e) { |
| } |
| |
| configureFromLists(lserver, lsearch); |
| configureNdots(lndots); |
| } |
| |
| private void |
| findUnix() { |
| findResolvConf("/etc/resolv.conf"); |
| } |
| |
| private void |
| findNetware() { |
| findResolvConf("sys:/etc/resolv.cfg"); |
| } |
| |
| /** |
| * Parses the output of winipcfg or ipconfig. |
| */ |
| private void |
| findWin(InputStream in, Locale locale) { |
| String packageName = ResolverConfig.class.getPackage().getName(); |
| String resPackageName = packageName + ".windows.DNSServer"; |
| ResourceBundle res; |
| if (locale != null) |
| res = ResourceBundle.getBundle(resPackageName, locale); |
| else |
| res = ResourceBundle.getBundle(resPackageName); |
| |
| String host_name = res.getString("host_name"); |
| String primary_dns_suffix = res.getString("primary_dns_suffix"); |
| String dns_suffix = res.getString("dns_suffix"); |
| String dns_servers = res.getString("dns_servers"); |
| |
| BufferedReader br = new BufferedReader(new InputStreamReader(in)); |
| try { |
| List lserver = new ArrayList(); |
| List lsearch = new ArrayList(); |
| String line = null; |
| boolean readingServers = false; |
| boolean readingSearches = false; |
| while ((line = br.readLine()) != null) { |
| StringTokenizer st = new StringTokenizer(line); |
| if (!st.hasMoreTokens()) { |
| readingServers = false; |
| readingSearches = false; |
| continue; |
| } |
| String s = st.nextToken(); |
| if (line.indexOf(":") != -1) { |
| readingServers = false; |
| readingSearches = false; |
| } |
| |
| if (line.indexOf(host_name) != -1) { |
| while (st.hasMoreTokens()) |
| s = st.nextToken(); |
| Name name; |
| try { |
| name = Name.fromString(s, null); |
| } |
| catch (TextParseException e) { |
| continue; |
| } |
| if (name.labels() == 1) |
| continue; |
| addSearch(s, lsearch); |
| } else if (line.indexOf(primary_dns_suffix) != -1) { |
| while (st.hasMoreTokens()) |
| s = st.nextToken(); |
| if (s.equals(":")) |
| continue; |
| addSearch(s, lsearch); |
| readingSearches = true; |
| } else if (readingSearches || |
| line.indexOf(dns_suffix) != -1) |
| { |
| while (st.hasMoreTokens()) |
| s = st.nextToken(); |
| if (s.equals(":")) |
| continue; |
| addSearch(s, lsearch); |
| readingSearches = true; |
| } else if (readingServers || |
| line.indexOf(dns_servers) != -1) |
| { |
| while (st.hasMoreTokens()) |
| s = st.nextToken(); |
| if (s.equals(":")) |
| continue; |
| addServer(s, lserver); |
| readingServers = true; |
| } |
| } |
| |
| configureFromLists(lserver, lsearch); |
| } |
| catch (IOException e) { |
| } |
| return; |
| } |
| |
| private void |
| findWin(InputStream in) { |
| String property = "org.xbill.DNS.windows.parse.buffer"; |
| final int defaultBufSize = 8 * 1024; |
| int bufSize = Integer.getInteger(property, defaultBufSize).intValue(); |
| BufferedInputStream b = new BufferedInputStream(in, bufSize); |
| b.mark(bufSize); |
| findWin(b, null); |
| if (servers == null) { |
| try { |
| b.reset(); |
| } |
| catch (IOException e) { |
| return; |
| } |
| findWin(b, new Locale("", "")); |
| } |
| } |
| |
| /** |
| * Calls winipcfg and parses the result to find servers and a search path. |
| */ |
| private void |
| find95() { |
| String s = "winipcfg.out"; |
| try { |
| Process p; |
| p = Runtime.getRuntime().exec("winipcfg /all /batch " + s); |
| p.waitFor(); |
| File f = new File(s); |
| findWin(new FileInputStream(f)); |
| new File(s).delete(); |
| } |
| catch (Exception e) { |
| return; |
| } |
| } |
| |
| /** |
| * Calls ipconfig and parses the result to find servers and a search path. |
| */ |
| private void |
| findNT() { |
| try { |
| Process p; |
| p = Runtime.getRuntime().exec("ipconfig /all"); |
| findWin(p.getInputStream()); |
| p.destroy(); |
| } |
| catch (Exception e) { |
| return; |
| } |
| } |
| |
| /** |
| * Parses the output of getprop, which is the only way to get DNS |
| * info on Android. getprop might disappear in future releases, so |
| * this code comes with a use-by date. |
| */ |
| private void |
| findAndroid() { |
| // This originally looked for all lines containing .dns; but |
| // http://code.google.com/p/android/issues/detail?id=2207#c73 |
| // indicates that net.dns* should always be the active nameservers, so |
| // we use those. |
| String re1 = "^\\d+(\\.\\d+){3}$"; |
| String re2 = "^[0-9a-f]+(:[0-9a-f]*)+:[0-9a-f]+$"; |
| try { |
| ArrayList lserver = new ArrayList(); |
| ArrayList lsearch = new ArrayList(); |
| String line; |
| Process p = Runtime.getRuntime().exec("getprop"); |
| InputStream in = p.getInputStream(); |
| InputStreamReader isr = new InputStreamReader(in); |
| BufferedReader br = new BufferedReader(isr); |
| while ((line = br.readLine()) != null ) { |
| StringTokenizer t = new StringTokenizer(line, ":"); |
| String name = t.nextToken(); |
| if (name.indexOf( "net.dns" ) > -1) { |
| String v = t.nextToken(); |
| v = v.replaceAll("[ \\[\\]]", ""); |
| if ((v.matches(re1) || v.matches(re2)) && |
| !lserver.contains(v)) |
| lserver.add(v); |
| } |
| } |
| configureFromLists(lserver, lsearch); |
| } catch ( Exception e ) { |
| // ignore resolutely |
| } |
| } |
| |
| /** Returns all located servers */ |
| public String [] |
| servers() { |
| return servers; |
| } |
| |
| /** Returns the first located server */ |
| public String |
| server() { |
| if (servers == null) |
| return null; |
| return servers[0]; |
| } |
| |
| /** Returns all entries in the located search path */ |
| public Name [] |
| searchPath() { |
| return searchlist; |
| } |
| |
| /** |
| * Returns the located ndots value, or the default (1) if not configured. |
| * Note that ndots can only be configured in a resolv.conf file, and will only |
| * take effect if ResolverConfig uses resolv.conf directly (that is, if the |
| * JVM does not include the sun.net.dns.ResolverConfiguration class). |
| */ |
| public int |
| ndots() { |
| if (ndots < 0) |
| return 1; |
| return ndots; |
| } |
| |
| /** Gets the current configuration */ |
| public static synchronized ResolverConfig |
| getCurrentConfig() { |
| return currentConfig; |
| } |
| |
| /** Gets the current configuration */ |
| public static void |
| refresh() { |
| ResolverConfig newConfig = new ResolverConfig(); |
| synchronized (ResolverConfig.class) { |
| currentConfig = newConfig; |
| } |
| } |
| |
| } |