What can be simpler than resolving a host name to IP address? Whoever asks this question probably does not fully understand the details of the name resolution process. Want a quiz? ;) Just think about the host called "256.1.2.3". OK, we can go quite far if we continue this way. This article is about getting the control over the name resolutions in Java. For most of the stand-alone desktop applications and Web applications it usually does not cause any troubles. But for the embedded systems using Java and for the servers that cannot be just restarted silently it may create a challenge.

In UNIX-like systems the name resolution is usually performed by the special resolver library (libresolv). This library is used as generic interface to all imaginable name resolution methods - from using /etc/hosts file to DNS/NIS/LDAP services. In some systems you do not need to link this library explicitly - it is used directly by the C library. The latter offers unified way of resolving any host name (or address for reverse resolution) which is independent from the underlying network protocols and address structure, and controlled with the system-wide configuration files (/etc/service.switch, /etc/nsswitch.conf, /etc/resolv.conf etc). So, for example, if your system is using DNS and your application calls gethostbyname() function (use "man gethostbyname" to learn more) to resolve host "foo.bar" the resolver library will first check /etc/hosts file, then query the DNS server for this name, and then, if all this fails, will even try the same host name with all defined domain names.

Why do you need to know all this if you use Java? Because as any UNIX process your JVM uses the same method to resolve the hostnames. When you call InetAddress.getAllByName() JVM finally calls gethostbyname2() (this is an extended version of gethostbyname() that supports more address families). So, effectively, your name resolution logic in Java works almost in the same way as for all other programs.

Why "almost"? Because Java goes a bit further than the regular programs and offer a cache for the name resolution results for the performance reasons. There is a couple of implementation-specific properties that you can use to control it, look at this page for more details. For example, if you want to completely disable the internal cache, you can set "networkaddress.cache.ttl" and "networkaddress.cache.negative.ttl" system properties to 0. There will be a price for it: you may have to go all the way through the resolver library every time you open a network connection (which may be acceptable). What I was never able to understand is why the default value for cache TTL is -1 (cache forever). This is why your application continues to connect to the old IP address when the new one is specified in the zone record for a particular host. Negative TTL of 10 seconds does make sense, but infinite TTL for the successful resolutions does not.

OK, now we have better understanding what happens. However, there is one more aspect of the name resolution - updating the system configuration. Interesting thing about the resolver library in UNIX is that it reads its configuration once upon the first resolution attempt and then never re-reads it. As result, if you change the IP address(es) of the DNS server(s), or the suffix, or any other parameters in /etc/resolv.conf file (the same applies to all resolver configuration files but now we will concentrate on the DNS configuration) the application will not notice the difference. The same applies to all other programs in the system.

There is no standard way to refresh the resolver configuration in Java. However, there is at least two ways to do it from C:

  • Call res_init() function of the resolver library
  • (more elegant way): reset the RES_INIT flag of the global _res.options variable

We will do this simple exercise using JNI. First we need to create a class with a native method:

public class ResolverHelper {
	private  static native void refresh();
}

Compile this class using "javac" and then run "javah ResolverHelper" command. It will generate the C header file called ResolverHelper.h that contains the declaration of our native function. Now we need to create its body. Create ResolverHelper.c file with the following contents:

#include <jni.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>

JNIEXPORT void JNICALL Java_ResolverHelper_refresh
  (JNIEnv *, jclass) {
        _res.options &= ~RES_INIT;
}

Compile this code using your favorite C compiler (look at this great article on your platform. You will get a small shared library that you now have to load in your JVM in order to access the native method of your ResolverHelper class. You need to call System.loadLibrary("ResolverHelper") in order to do it. Note that you do not specify the library file name suffix ("dll", ".so", "dylib" etc) and your library file has to be either copied to JRE_HOME/lib/<platform> or any common shared library location in your system, or, probably the best option - to your application-specific directory that you will add to "java.library.path" system property.

That's it, now we can refresh the DNS configuration any time we want. If the resolv.conf file can be changed externally, you will need a simple notification mechanism to let your Java application know about the change. Or you can just monitor the change time of /etc/resolv.conf file. Or you can refresh it every time you open a network connection (assuming you know that the application does not do it dozens of times per second). There may be a number of options depending on the requirements.

Finally, there is an interesting library called JNA. You can achieve the same goal by using it and without writing any native code (although you will still need some - JNA native library). As usually, there is a number of practical solutions to a problem once you understand it.

  ### References

  1. Java Networking Properties
  2. Excellent article about compiling JNI code for various platforms
  3. Dynamically access native libraries from Java without JNI



blog comments powered by Disqus

Published

22 May 2008

Tags