• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 1999-2004 Brian Wellington (bwelling@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@meudal.net">Yannick Meudal</a>
31  * @author <a href="mailto:arnt@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 {
refresh()43 	refresh();
44 }
45 
46 public
ResolverConfig()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
addServer(String server, List list)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
addSearch(String search, List list)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
parseNdots(String token)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
configureFromLists(List lserver, List lsearch)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
configureNdots(int lndots)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
findProperty()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
findSunJVM()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
findResolvConf(String file)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
findUnix()274 findUnix() {
275 	findResolvConf("/etc/resolv.conf");
276 }
277 
278 private void
findNetware()279 findNetware() {
280 	findResolvConf("sys:/etc/resolv.cfg");
281 }
282 
283 /**
284  * Parses the output of winipcfg or ipconfig.
285  */
286 private void
findWin(InputStream in, Locale locale)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
findWin(InputStream in)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
find95()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
findNT()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
findAndroid()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 []
servers()463 servers() {
464 	return servers;
465 }
466 
467 /** Returns the first located server */
468 public String
server()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 []
searchPath()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
ndots()488 ndots() {
489 	if (ndots < 0)
490 		return 1;
491 	return ndots;
492 }
493 
494 /** Gets the current configuration */
495 public static synchronized ResolverConfig
getCurrentConfig()496 getCurrentConfig() {
497 	return currentConfig;
498 }
499 
500 /** Gets the current configuration */
501 public static void
refresh()502 refresh() {
503 	ResolverConfig newConfig = new ResolverConfig();
504 	synchronized (ResolverConfig.class) {
505 		currentConfig = newConfig;
506 	}
507 }
508 
509 }
510