8171077: Use getaddrinfo/getnameinfo in Windows Inet4AddresImpl native code
authorclanger
Tue, 20 Dec 2016 10:49:50 +0100
changeset 42776 bd88c3c48915
parent 42775 a45fa7082c81
child 42777 a94fc33e9866
child 43524 b2e2caf26552
8171077: Use getaddrinfo/getnameinfo in Windows Inet4AddresImpl native code Reviewed-by: chegar
jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c
--- a/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c	Mon Dec 19 17:09:10 2016 -0800
+++ b/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c	Tue Dec 20 10:49:50 2016 +0100
@@ -113,24 +113,22 @@
  * Class:     java_net_Inet4AddressImpl
  * Method:    lookupAllHostAddr
  * Signature: (Ljava/lang/String;)[[B
- *
- * This is almost shared code
  */
-
 JNIEXPORT jobjectArray JNICALL
 Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
-                                                jstring host) {
+                                                 jstring host) {
+    jobjectArray ret = NULL;
     const char *hostname;
-    struct hostent *hp;
+    int error = 0;
     unsigned int addr[4];
-
-    jobjectArray ret = NULL;
+    struct addrinfo hints, *res = NULL, *resNew = NULL, *last = NULL,
+        *iterator;
 
     initInetAddressIDs(env);
     JNU_CHECK_EXCEPTION_RETURN(env, NULL);
 
     if (IS_NULL(host)) {
-        JNU_ThrowNullPointerException(env, "host argument");
+        JNU_ThrowNullPointerException(env, "host argument is null");
         return NULL;
     }
     hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
@@ -166,9 +164,9 @@
         /*
          * Return an byte array with the populated address.
          */
-        address = (addr[3]<<24) & 0xff000000;
-        address |= (addr[2]<<16) & 0xff0000;
-        address |= (addr[1]<<8) & 0xff00;
+        address = (addr[3] << 24) & 0xff000000;
+        address |= (addr[2] << 16) & 0xff0000;
+        address |= (addr[1] << 8) & 0xff00;
         address |= addr[0];
 
         ret = (*env)->NewObjectArray(env, 1, ia_class, NULL);
@@ -179,58 +177,95 @@
 
         iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
         if (IS_NULL(iaObj)) {
-          ret = NULL;
-          goto cleanupAndReturn;
+            ret = NULL;
+            goto cleanupAndReturn;
         }
         setInetAddress_addr(env, iaObj, ntohl(address));
         (*env)->SetObjectArrayElement(env, ret, 0, iaObj);
-        JNU_ReleaseStringPlatformChars(env, host, hostname);
-        return ret;
+        goto cleanupAndReturn;
     }
 
-    /*
-     * Perform the lookup
-     */
-    if ((hp = gethostbyname((char*)hostname)) != NULL) {
-        struct in_addr **addrp = (struct in_addr **) hp->h_addr_list;
-        int len = sizeof(struct in_addr);
+    // try once, with our static buffer
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = AI_CANONNAME;
+    hints.ai_family = AF_INET;
+
+    error = getaddrinfo(hostname, NULL, &hints, &res);
+
+    if (error) {
+        NET_ThrowByNameWithLastError(env, "java/net/UnknownHostException",
+                                     hostname);
+        goto cleanupAndReturn;
+    } else {
         int i = 0;
+        iterator = res;
+        while (iterator != NULL) {
+            // skip duplicates
+            int skip = 0;
+            struct addrinfo *iteratorNew = resNew;
+            while (iteratorNew != NULL) {
+                struct sockaddr_in *addr1, *addr2;
+                addr1 = (struct sockaddr_in *)iterator->ai_addr;
+                addr2 = (struct sockaddr_in *)iteratorNew->ai_addr;
+                if (addr1->sin_addr.s_addr == addr2->sin_addr.s_addr) {
+                    skip = 1;
+                    break;
+                }
+                iteratorNew = iteratorNew->ai_next;
+            }
 
-        while (*addrp != (struct in_addr *) 0) {
-            i++;
-            addrp++;
+            if (!skip) {
+                struct addrinfo *next
+                    = (struct addrinfo *)malloc(sizeof(struct addrinfo));
+                if (!next) {
+                    JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
+                    ret = NULL;
+                    goto cleanupAndReturn;
+                }
+                memcpy(next, iterator, sizeof(struct addrinfo));
+                next->ai_next = NULL;
+                if (resNew == NULL) {
+                    resNew = next;
+                } else {
+                    last->ai_next = next;
+                }
+                last = next;
+                i++;
+            }
+            iterator = iterator->ai_next;
         }
 
+        // allocate array - at this point i contains the number of addresses
         ret = (*env)->NewObjectArray(env, i, ia_class, NULL);
-
         if (IS_NULL(ret)) {
             goto cleanupAndReturn;
         }
 
-        addrp = (struct in_addr **) hp->h_addr_list;
         i = 0;
-        while (*addrp != (struct in_addr *) 0) {
-          jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
-          if (IS_NULL(iaObj)) {
-            ret = NULL;
-            goto cleanupAndReturn;
-          }
-          setInetAddress_addr(env, iaObj, ntohl((*addrp)->s_addr));
-          setInetAddress_hostName(env, iaObj, host);
-          (*env)->SetObjectArrayElement(env, ret, i, iaObj);
-          addrp++;
-          i++;
+        iterator = resNew;
+        while (iterator != NULL) {
+            jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
+            if (IS_NULL(iaObj)) {
+                ret = NULL;
+                goto cleanupAndReturn;
+            }
+            setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in *)
+                                (iterator->ai_addr))->sin_addr.s_addr));
+            setInetAddress_hostName(env, iaObj, host);
+            (*env)->SetObjectArrayElement(env, ret, i++, iaObj);
+            iterator = iterator->ai_next;
         }
-    } else if (WSAGetLastError() == WSATRY_AGAIN) {
-        NET_ThrowByNameWithLastError(env,
-                                     JNU_JAVANETPKG "UnknownHostException",
-                                     hostname);
-    } else {
-        JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
     }
-
 cleanupAndReturn:
     JNU_ReleaseStringPlatformChars(env, host, hostname);
+    while (resNew != NULL) {
+        last = resNew;
+        resNew = resNew->ai_next;
+        free(last);
+    }
+    if (res != NULL) {
+        freeaddrinfo(res);
+    }
     return ret;
 }
 
@@ -238,30 +273,42 @@
  * Class:     java_net_Inet4AddressImpl
  * Method:    getHostByAddr
  * Signature: (I)Ljava/lang/String;
+ *
+ * Theoretically the UnknownHostException could be enriched with gai error
+ * information. But as it is silently ignored anyway, there's no need for this.
+ * It's only important that either a valid hostname is returned or an
+ * UnknownHostException is thrown.
  */
 JNIEXPORT jstring JNICALL
 Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
-                                            jbyteArray addrArray) {
-    struct hostent *hp;
+                                             jbyteArray addrArray) {
+    jstring ret = NULL;
+    char host[NI_MAXHOST + 1];
     jbyte caddr[4];
     jint addr;
+    struct sockaddr_in sa;
+
+    // construct a sockaddr_in structure
+    memset((char *)&sa, 0, sizeof(struct sockaddr_in));
     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
-    addr = ((caddr[0]<<24) & 0xff000000);
-    addr |= ((caddr[1] <<16) & 0xff0000);
-    addr |= ((caddr[2] <<8) & 0xff00);
+    addr = ((caddr[0] << 24) & 0xff000000);
+    addr |= ((caddr[1] << 16) & 0xff0000);
+    addr |= ((caddr[2] << 8) & 0xff00);
     addr |= (caddr[3] & 0xff);
-    addr = htonl(addr);
+    sa.sin_addr.s_addr = htonl(addr);
+    sa.sin_family = AF_INET;
 
-    hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
-    if (hp == NULL) {
-        JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 0);
-        return NULL;
+    if (getnameinfo((struct sockaddr *)&sa, sizeof(struct sockaddr_in),
+                    host, NI_MAXHOST, NULL, 0, NI_NAMEREQD)) {
+        JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);
+    } else {
+        ret = (*env)->NewStringUTF(env, host);
+        if (ret == NULL) {
+            JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);
+        }
     }
-    if (hp->h_name == NULL) { /* Deal with bug in Windows XP */
-        JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 0);
-        return NULL;
-    }
-    return JNU_NewStringPlatform(env, hp->h_name);
+
+    return ret;
 }
 
 static jboolean