Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InetAddress.getLocalHost().getHostName() different behavior between JDK 11 and JDK 8

I wrote a simple java program to basically run:

System.out.println(InetAddress.getLocalHost().getHostName());

If I compile it and run it on Java 1.7.231 or 1.8.221 On RHEL 7.7, it returns the FQDN (computer.domain.com), but ON THE SAME SERVER, compile it in RHEL JDK 11.0.2 it returns only the server name.

As I understand it should do a reverse DNS lookup (basically a hostname -f) but with JDK 11 the behavior is definitely different. Any idea why is this happening?

like image 389
Andy Knipp Avatar asked May 19 '20 18:05

Andy Knipp


People also ask

What is the use of getLocalHost () method in Java?

Java InetAddress getLocalHost() method The getLocalHost() method of Java InetAddress class returns the instance of InetAddress containing local host name and address. In this, firstly the host name is retrieved from the system, then that name is resolved into InetAddress.

What is an InetAddress?

The InetAddress is Java's representation of an IP address. Instances of this class are used together with UDP DatagramSockets and normal Socket's and ServerSocket's.

Which best describes INET address class?

Class InetAddress. This class represents an Internet Protocol (IP) address. An IP address is either a 32-bit or 128-bit unsigned number used by IP, a lower-level protocol on which protocols like UDP and TCP are built.


Video Answer


3 Answers

This might be the same problem as reported here: InetAddress.getLocalhost() does not give same result in java7 and java8.

It boils down to a change in the JDK:

Since: http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/81987765cb81 was pushed, we call getaddrinfo / getnameinfo to get a local host name instead of the older (obseleted) gethostbyname_r/gethostbyaddr_r calls.

The newer calls respect the localhosts /etc/nsswitch.conf configuration files. In the case of this machine that file tells these calls to look in files before referencing other naming services.

Since the /etc/hosts file contains an explicit mapping for this hostname / IP combination, that is what is returned.

In the older JDK's the gethostbyname_r actually ignored the local machines settings and immediately delegated to the naming service.

like image 135
Bastien Jansen Avatar answered Oct 16 '22 23:10

Bastien Jansen


Under the hood, in order to obtain the localhost name the SDK perform a native invocation to the underlying operating system.

The C function which is involved is getLocalHostName. For both IP version 4 and 6 you can find the appropriate implementation: basically it is the same source code with minimal changes to take into consideration if you are using IP version 6.

Let's assume for instance the code for IP version 4.

For Java 11, the corresponding native code is implemented in Inet4AddressImpl.c. This is how getLocalHostname is implemented:

/*
 * Class:     java_net_Inet4AddressImpl
 * Method:    getLocalHostName
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL
Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
    char hostname[NI_MAXHOST + 1];


    hostname[0] = '\0';
    if (gethostname(hostname, sizeof(hostname)) != 0) {
        strcpy(hostname, "localhost");
    } else {
#if defined(__solaris__)
        // try to resolve hostname via nameservice
        // if it is known but getnameinfo fails, hostname will still be the
        // value from gethostname
        struct addrinfo hints, *res;


        // make sure string is null-terminated
        hostname[NI_MAXHOST] = '\0';
        memset(&hints, 0, sizeof(hints));
        hints.ai_flags = AI_CANONNAME;
        hints.ai_family = AF_INET;


        if (getaddrinfo(hostname, NULL, &hints, &res) == 0) {
            getnameinfo(res->ai_addr, res->ai_addrlen, hostname, sizeof(hostname),
                        NULL, 0, NI_NAMEREQD);
            freeaddrinfo(res);
        }
#else
        // make sure string is null-terminated
        hostname[NI_MAXHOST] = '\0';
#endif
    }
    return (*env)->NewStringUTF(env, hostname);
}

As you can see, when using something different from Solaris, it seems that the code only relies on gethostname to obtain the required value. This restriction was introduced in this commit in the context of this bug.

Here you can see the analogous IP 4 version native source code implementation for Java 8.

In that source code you can find several differences with the previous one for Java 11.

First, the code is divided in two sections depending on whether the following definition applies:

#if defined(__GLIBC__) || (defined(__FreeBSD__) && (__FreeBSD_version >= 601104))
#define HAS_GLIBC_GETHOSTBY_R   1
#endif




#if defined(_ALLBSD_SOURCE) && !defined(HAS_GLIBC_GETHOSTBY_R)

...

#else /* defined(_ALLBSD_SOURCE) && !defined(HAS_GLIBC_GETHOSTBY_R) */

...

and the implementation provided for getLocalHostName is different if the condition applies or not.

In my opinion, in the case of Redhat the condition does not apply and, as a consequence, the following code is the one used at runtime:

/************************************************************************
 * Inet4AddressImpl
 */


/*
 * Class:     java_net_Inet4AddressImpl
 * Method:    getLocalHostName
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL
Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
    char hostname[NI_MAXHOST+1];


    hostname[0] = '\0';
    if (JVM_GetHostName(hostname, sizeof(hostname))) {
        /* Something went wrong, maybe networking is not setup? */
        strcpy(hostname, "localhost");
    } else {
        struct addrinfo hints, *res;
        int error;


        hostname[NI_MAXHOST] = '\0';
        memset(&hints, 0, sizeof(hints));
        hints.ai_flags = AI_CANONNAME;
        hints.ai_family = AF_INET;


        error = getaddrinfo(hostname, NULL, &hints, &res);


        if (error == 0) {/* host is known to name service */
            getnameinfo(res->ai_addr,
                        res->ai_addrlen,
                        hostname,
                        NI_MAXHOST,
                        NULL,
                        0,
                        NI_NAMEREQD);


            /* if getnameinfo fails hostname is still the value
               from gethostname */


            freeaddrinfo(res);
        }
    }
    return (*env)->NewStringUTF(env, hostname);
}

As you can see, this last implementation call gethostname in first place as well, although indirectly, using JVM_GetHostName, wrapped in C++ code:

JVM_LEAF(int, JVM_GetHostName(char* name, int namelen))
  JVMWrapper("JVM_GetHostName");
  return os::get_host_name(name, namelen);
JVM_END

Depending on the actual OS, os::get_host_name will translate to different functions. For linux it will invoke gethostname:

inline int os::get_host_name(char* name, int namelen) {
  return ::gethostname(name, namelen);
}

If the call to gethostname succeeds, getaddrinfo is invoked with the host name returned by gethostname. If in turn, this last call succeeds, getnameinfo is invoked, with the address returned by getaddrinfo to get the final hostname.

In a certain way it seems strange to me, I feel I'm missing something, but these differences can be very likely the cause of the different behavior you experienced; the hypothesis can be tested using the provided native code and debugging the results obtained for your system.

like image 32
jccampanero Avatar answered Oct 17 '22 01:10

jccampanero


This answer from oracle documentation may help you :

On Red Hat Linux installations InetAddress.getLocalHost() may return an InetAddress corresponding to the loopback address (127.0.0.1). This arises because the default installation creates an association in /etc/hosts between the hostname of the machine and the loopback address. To ensure that InetAddress.getLocalHost() returns the actual host address, update the /etc/hosts file or the name service configuration file (/etc/nsswitch.conf) to query dns or nis before searching hosts.

Link : https://docs.oracle.com/javase/7/docs/technotes/guides/idl/jidlFAQ.html

Similar bug on JDK 1.7 https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7166687

like image 1
Oussama ZAGHDOUD Avatar answered Oct 17 '22 00:10

Oussama ZAGHDOUD