Home | History | Annotate | Download | only in DNS
      1 // Copyright (c) 1999-2004 Brian Wellington (bwelling (at) xbill.org)
      2 
      3 package org.xbill.DNS;
      4 
      5 import java.io.*;
      6 import java.lang.reflect.*;
      7 import java.util.*;
      8 
      9 /**
     10  * A class that tries to locate name servers and the search path to
     11  * be appended to unqualified names.
     12  *
     13  * The following are attempted, in order, until one succeeds.
     14  * <UL>
     15  *   <LI>The properties 'dns.server' and 'dns.search' (comma delimited lists)
     16  *       are checked.  The servers can either be IP addresses or hostnames
     17  *       (which are resolved using Java's built in DNS support).
     18  *   <LI>The sun.net.dns.ResolverConfiguration class is queried.
     19  *   <LI>On Unix, /etc/resolv.conf is parsed.
     20  *   <LI>On Windows, ipconfig/winipcfg is called and its output parsed.  This
     21  *       may fail for non-English versions on Windows.
     22  *   <LI>"localhost" is used as the nameserver, and the search path is empty.
     23  * </UL>
     24  *
     25  * These routines will be called internally when creating Resolvers/Lookups
     26  * without explicitly specifying server names, and can also be called
     27  * directly if desired.
     28  *
     29  * @author Brian Wellington
     30  * @author <a href="mailto:yannick (at) meudal.net">Yannick Meudal</a>
     31  * @author <a href="mailto:arnt (at) gulbrandsen.priv.no">Arnt Gulbrandsen</a>
     32  */
     33 
     34 public class ResolverConfig {
     35 
     36 private String [] servers = null;
     37 private Name [] searchlist = null;
     38 private int ndots = -1;
     39 
     40 private static ResolverConfig currentConfig;
     41 
     42 static {
     43 	refresh();
     44 }
     45 
     46 public
     47 ResolverConfig() {
     48 	if (findProperty())
     49 		return;
     50 	if (findSunJVM())
     51 		return;
     52 	if (servers == null || searchlist == null) {
     53 		String OS = System.getProperty("os.name");
     54 		String vendor = System.getProperty("java.vendor");
     55 		if (OS.indexOf("Windows") != -1) {
     56 			if (OS.indexOf("95") != -1 ||
     57 			    OS.indexOf("98") != -1 ||
     58 			    OS.indexOf("ME") != -1)
     59 				find95();
     60 			else
     61 				findNT();
     62 		} else if (OS.indexOf("NetWare") != -1) {
     63 			findNetware();
     64 		} else if (vendor.indexOf("Android") != -1) {
     65 			findAndroid();
     66 		} else {
     67 			findUnix();
     68 		}
     69 	}
     70 }
     71 
     72 private void
     73 addServer(String server, List list) {
     74 	if (list.contains(server))
     75 		return;
     76 	if (Options.check("verbose"))
     77 		System.out.println("adding server " + server);
     78 	list.add(server);
     79 }
     80 
     81 private void
     82 addSearch(String search, List list) {
     83 	Name name;
     84 	if (Options.check("verbose"))
     85 		System.out.println("adding search " + search);
     86 	try {
     87 		name = Name.fromString(search, Name.root);
     88 	}
     89 	catch (TextParseException e) {
     90 		return;
     91 	}
     92 	if (list.contains(name))
     93 		return;
     94 	list.add(name);
     95 }
     96 
     97 private int
     98 parseNdots(String token) {
     99 	token = token.substring(6);
    100 	try {
    101 		int ndots = Integer.parseInt(token);
    102 		if (ndots >= 0) {
    103 			if (Options.check("verbose"))
    104 				System.out.println("setting ndots " + token);
    105 			return ndots;
    106 		}
    107 	}
    108 	catch (NumberFormatException e) {
    109 	}
    110 	return -1;
    111 }
    112 
    113 private void
    114 configureFromLists(List lserver, List lsearch) {
    115 	if (servers == null && lserver.size() > 0)
    116 		servers = (String []) lserver.toArray(new String[0]);
    117 	if (searchlist == null && lsearch.size() > 0)
    118 		searchlist = (Name []) lsearch.toArray(new Name[0]);
    119 }
    120 
    121 private void
    122 configureNdots(int lndots) {
    123 	if (ndots < 0 && lndots > 0)
    124 		ndots = lndots;
    125 }
    126 
    127 /**
    128  * Looks in the system properties to find servers and a search path.
    129  * Servers are defined by dns.server=server1,server2...
    130  * The search path is defined by dns.search=domain1,domain2...
    131  */
    132 private boolean
    133 findProperty() {
    134 	String prop;
    135 	List lserver = new ArrayList(0);
    136 	List lsearch = new ArrayList(0);
    137 	StringTokenizer st;
    138 
    139 	prop = System.getProperty("dns.server");
    140 	if (prop != null) {
    141 		st = new StringTokenizer(prop, ",");
    142 		while (st.hasMoreTokens())
    143 			addServer(st.nextToken(), lserver);
    144 	}
    145 
    146 	prop = System.getProperty("dns.search");
    147 	if (prop != null) {
    148 		st = new StringTokenizer(prop, ",");
    149 		while (st.hasMoreTokens())
    150 			addSearch(st.nextToken(), lsearch);
    151 	}
    152 	configureFromLists(lserver, lsearch);
    153 	return (servers != null && searchlist != null);
    154 }
    155 
    156 /**
    157  * Uses the undocumented Sun DNS implementation to determine the configuration.
    158  * This doesn't work or even compile with all JVMs (gcj, for example).
    159  */
    160 private boolean
    161 findSunJVM() {
    162 	List lserver = new ArrayList(0);
    163 	List lserver_tmp;
    164 	List lsearch = new ArrayList(0);
    165 	List lsearch_tmp;
    166 
    167 	try {
    168 		Class [] noClasses = new Class[0];
    169 		Object [] noObjects = new Object[0];
    170 		String resConfName = "sun.net.dns.ResolverConfiguration";
    171 		Class resConfClass = Class.forName(resConfName);
    172 		Object resConf;
    173 
    174 		// ResolverConfiguration resConf = ResolverConfiguration.open();
    175 		Method open = resConfClass.getDeclaredMethod("open", noClasses);
    176 		resConf = open.invoke(null, noObjects);
    177 
    178 		// lserver_tmp = resConf.nameservers();
    179 		Method nameservers = resConfClass.getMethod("nameservers",
    180 							    noClasses);
    181 		lserver_tmp = (List) nameservers.invoke(resConf, noObjects);
    182 
    183 		// lsearch_tmp = resConf.searchlist();
    184 		Method searchlist = resConfClass.getMethod("searchlist",
    185 							    noClasses);
    186 		lsearch_tmp = (List) searchlist.invoke(resConf, noObjects);
    187 	}
    188 	catch (Exception e) {
    189 		return false;
    190 	}
    191 
    192 	if (lserver_tmp.size() == 0)
    193 		return false;
    194 
    195 	if (lserver_tmp.size() > 0) {
    196 		Iterator it = lserver_tmp.iterator();
    197 		while (it.hasNext())
    198 			addServer((String) it.next(), lserver);
    199 	}
    200 
    201 	if (lsearch_tmp.size() > 0) {
    202 		Iterator it = lsearch_tmp.iterator();
    203 		while (it.hasNext())
    204 			addSearch((String) it.next(), lsearch);
    205 	}
    206 	configureFromLists(lserver, lsearch);
    207 	return true;
    208 }
    209 
    210 /**
    211  * Looks in /etc/resolv.conf to find servers and a search path.
    212  * "nameserver" lines specify servers.  "domain" and "search" lines
    213  * define the search path.
    214  */
    215 private void
    216 findResolvConf(String file) {
    217 	InputStream in = null;
    218 	try {
    219 		in = new FileInputStream(file);
    220 	}
    221 	catch (FileNotFoundException e) {
    222 		return;
    223 	}
    224 	InputStreamReader isr = new InputStreamReader(in);
    225 	BufferedReader br = new BufferedReader(isr);
    226 	List lserver = new ArrayList(0);
    227 	List lsearch = new ArrayList(0);
    228 	int lndots = -1;
    229 	try {
    230 		String line;
    231 		while ((line = br.readLine()) != null) {
    232 			if (line.startsWith("nameserver")) {
    233 				StringTokenizer st = new StringTokenizer(line);
    234 				st.nextToken(); /* skip nameserver */
    235 				addServer(st.nextToken(), lserver);
    236 			}
    237 			else if (line.startsWith("domain")) {
    238 				StringTokenizer st = new StringTokenizer(line);
    239 				st.nextToken(); /* skip domain */
    240 				if (!st.hasMoreTokens())
    241 					continue;
    242 				if (lsearch.isEmpty())
    243 					addSearch(st.nextToken(), lsearch);
    244 			}
    245 			else if (line.startsWith("search")) {
    246 				if (!lsearch.isEmpty())
    247 					lsearch.clear();
    248 				StringTokenizer st = new StringTokenizer(line);
    249 				st.nextToken(); /* skip search */
    250 				while (st.hasMoreTokens())
    251 					addSearch(st.nextToken(), lsearch);
    252 			}
    253 			else if(line.startsWith("options")) {
    254 				StringTokenizer st = new StringTokenizer(line);
    255 				st.nextToken(); /* skip options */
    256 				while (st.hasMoreTokens()) {
    257 					String token = st.nextToken();
    258 					if (token.startsWith("ndots:")) {
    259 						lndots = parseNdots(token);
    260 					}
    261 				}
    262 			}
    263 		}
    264 		br.close();
    265 	}
    266 	catch (IOException e) {
    267 	}
    268 
    269 	configureFromLists(lserver, lsearch);
    270 	configureNdots(lndots);
    271 }
    272 
    273 private void
    274 findUnix() {
    275 	findResolvConf("/etc/resolv.conf");
    276 }
    277 
    278 private void
    279 findNetware() {
    280 	findResolvConf("sys:/etc/resolv.cfg");
    281 }
    282 
    283 /**
    284  * Parses the output of winipcfg or ipconfig.
    285  */
    286 private void
    287 findWin(InputStream in, Locale locale) {
    288 	String packageName = ResolverConfig.class.getPackage().getName();
    289 	String resPackageName = packageName + ".windows.DNSServer";
    290 	ResourceBundle res;
    291 	if (locale != null)
    292 		res = ResourceBundle.getBundle(resPackageName, locale);
    293 	else
    294 		res = ResourceBundle.getBundle(resPackageName);
    295 
    296 	String host_name = res.getString("host_name");
    297 	String primary_dns_suffix = res.getString("primary_dns_suffix");
    298 	String dns_suffix = res.getString("dns_suffix");
    299 	String dns_servers = res.getString("dns_servers");
    300 
    301 	BufferedReader br = new BufferedReader(new InputStreamReader(in));
    302 	try {
    303 		List lserver = new ArrayList();
    304 		List lsearch = new ArrayList();
    305 		String line = null;
    306 		boolean readingServers = false;
    307 		boolean readingSearches = false;
    308 		while ((line = br.readLine()) != null) {
    309 			StringTokenizer st = new StringTokenizer(line);
    310 			if (!st.hasMoreTokens()) {
    311 				readingServers = false;
    312 				readingSearches = false;
    313 				continue;
    314 			}
    315 			String s = st.nextToken();
    316 			if (line.indexOf(":") != -1) {
    317 				readingServers = false;
    318 				readingSearches = false;
    319 			}
    320 
    321 			if (line.indexOf(host_name) != -1) {
    322 				while (st.hasMoreTokens())
    323 					s = st.nextToken();
    324 				Name name;
    325 				try {
    326 					name = Name.fromString(s, null);
    327 				}
    328 				catch (TextParseException e) {
    329 					continue;
    330 				}
    331 				if (name.labels() == 1)
    332 					continue;
    333 				addSearch(s, lsearch);
    334 			} else if (line.indexOf(primary_dns_suffix) != -1) {
    335 				while (st.hasMoreTokens())
    336 					s = st.nextToken();
    337 				if (s.equals(":"))
    338 					continue;
    339 				addSearch(s, lsearch);
    340 				readingSearches = true;
    341 			} else if (readingSearches ||
    342 				   line.indexOf(dns_suffix) != -1)
    343 			{
    344 				while (st.hasMoreTokens())
    345 					s = st.nextToken();
    346 				if (s.equals(":"))
    347 					continue;
    348 				addSearch(s, lsearch);
    349 				readingSearches = true;
    350 			} else if (readingServers ||
    351 				   line.indexOf(dns_servers) != -1)
    352 			{
    353 				while (st.hasMoreTokens())
    354 					s = st.nextToken();
    355 				if (s.equals(":"))
    356 					continue;
    357 				addServer(s, lserver);
    358 				readingServers = true;
    359 			}
    360 		}
    361 
    362 		configureFromLists(lserver, lsearch);
    363 	}
    364 	catch (IOException e) {
    365 	}
    366 	return;
    367 }
    368 
    369 private void
    370 findWin(InputStream in) {
    371 	String property = "org.xbill.DNS.windows.parse.buffer";
    372 	final int defaultBufSize = 8 * 1024;
    373 	int bufSize = Integer.getInteger(property, defaultBufSize).intValue();
    374 	BufferedInputStream b = new BufferedInputStream(in, bufSize);
    375 	b.mark(bufSize);
    376 	findWin(b, null);
    377 	if (servers == null) {
    378 		try {
    379 			b.reset();
    380 		}
    381 		catch (IOException e) {
    382 			return;
    383 		}
    384 		findWin(b, new Locale("", ""));
    385 	}
    386 }
    387 
    388 /**
    389  * Calls winipcfg and parses the result to find servers and a search path.
    390  */
    391 private void
    392 find95() {
    393 	String s = "winipcfg.out";
    394 	try {
    395 		Process p;
    396 		p = Runtime.getRuntime().exec("winipcfg /all /batch " + s);
    397 		p.waitFor();
    398 		File f = new File(s);
    399 		findWin(new FileInputStream(f));
    400 		new File(s).delete();
    401 	}
    402 	catch (Exception e) {
    403 		return;
    404 	}
    405 }
    406 
    407 /**
    408  * Calls ipconfig and parses the result to find servers and a search path.
    409  */
    410 private void
    411 findNT() {
    412 	try {
    413 		Process p;
    414 		p = Runtime.getRuntime().exec("ipconfig /all");
    415 		findWin(p.getInputStream());
    416 		p.destroy();
    417 	}
    418 	catch (Exception e) {
    419 		return;
    420 	}
    421 }
    422 
    423 /**
    424  * Parses the output of getprop, which is the only way to get DNS
    425  * info on Android. getprop might disappear in future releases, so
    426  * this code comes with a use-by date.
    427  */
    428 private void
    429 findAndroid() {
    430 	// This originally looked for all lines containing .dns; but
    431 	// http://code.google.com/p/android/issues/detail?id=2207#c73
    432 	// indicates that net.dns* should always be the active nameservers, so
    433 	// we use those.
    434 	String re1 = "^\\d+(\\.\\d+){3}$";
    435 	String re2 = "^[0-9a-f]+(:[0-9a-f]*)+:[0-9a-f]+$";
    436 	try {
    437 		ArrayList lserver = new ArrayList();
    438 		ArrayList lsearch = new ArrayList();
    439 		String line;
    440 		Process p = Runtime.getRuntime().exec("getprop");
    441 		InputStream in = p.getInputStream();
    442 		InputStreamReader isr = new InputStreamReader(in);
    443 		BufferedReader br = new BufferedReader(isr);
    444 		while ((line = br.readLine()) != null ) {
    445 			StringTokenizer t = new StringTokenizer(line, ":");
    446 			String name = t.nextToken();
    447 			if (name.indexOf( "net.dns" ) > -1) {
    448 				String v = t.nextToken();
    449 				v = v.replaceAll("[ \\[\\]]", "");
    450 				if ((v.matches(re1) || v.matches(re2)) &&
    451 				    !lserver.contains(v))
    452 					lserver.add(v);
    453 			}
    454 		}
    455 		configureFromLists(lserver, lsearch);
    456 	} catch ( Exception e ) {
    457 		// ignore resolutely
    458 	}
    459 }
    460 
    461 /** Returns all located servers */
    462 public String []
    463 servers() {
    464 	return servers;
    465 }
    466 
    467 /** Returns the first located server */
    468 public String
    469 server() {
    470 	if (servers == null)
    471 		return null;
    472 	return servers[0];
    473 }
    474 
    475 /** Returns all entries in the located search path */
    476 public Name []
    477 searchPath() {
    478 	return searchlist;
    479 }
    480 
    481 /**
    482  * Returns the located ndots value, or the default (1) if not configured.
    483  * Note that ndots can only be configured in a resolv.conf file, and will only
    484  * take effect if ResolverConfig uses resolv.conf directly (that is, if the
    485  * JVM does not include the sun.net.dns.ResolverConfiguration class).
    486  */
    487 public int
    488 ndots() {
    489 	if (ndots < 0)
    490 		return 1;
    491 	return ndots;
    492 }
    493 
    494 /** Gets the current configuration */
    495 public static synchronized ResolverConfig
    496 getCurrentConfig() {
    497 	return currentConfig;
    498 }
    499 
    500 /** Gets the current configuration */
    501 public static void
    502 refresh() {
    503 	ResolverConfig newConfig = new ResolverConfig();
    504 	synchronized (ResolverConfig.class) {
    505 		currentConfig = newConfig;
    506 	}
    507 }
    508 
    509 }
    510