提交 37cd8bfe 编写于 作者: C Chuansheng Lu 提交者: 云矅

[CoreClasslib] Fixed slow DNS resolution bug

Summary:
- Port fix https://aone.alibaba-inc.com/project/355606/issue/8443788 to dragonwell
- Switch implementation of java/net/Inet4AddressImpl.lookupAllHostAddr based on Linux glibc versions.

Test Plan: jdk/test/net/

Reviewers: 井桐

Reviewed By: 井桐

Differential Revision: https://aone.alibaba-inc.com/code/D849274
上级 bb1f71c8
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -51,6 +51,49 @@
#define HAS_GLIBC_GETHOSTBY_R 1
#endif
static jclass ni_iacls;
static jclass ni_ia4cls;
static jmethodID ni_ia4ctrID;
#if defined(__GLIBC__)
#include <gnu/libc-version.h>
static int glibc_major_version = 0;
static int glibc_minor_version = 0;
static void initialize_glibc_version() {
const char* ver_str = gnu_get_libc_version();
// version string is in format of "2.6"
char *p = NULL;
glibc_major_version = strtol(ver_str, &p, 10);
++p;
glibc_minor_version = strtol(p, NULL, 10);
}
#endif // __GLIBC__
static jboolean initializeInetClasses(JNIEnv *env)
{
static int initialized = 0;
if (!initialized) {
#if defined(__GLIBC__)
initialize_glibc_version();
#endif
ni_iacls = (*env)->FindClass(env, "java/net/InetAddress");
CHECK_NULL_RETURN(ni_iacls, JNI_FALSE);
ni_iacls = (*env)->NewGlobalRef(env, ni_iacls);
CHECK_NULL_RETURN(ni_iacls, JNI_FALSE);
ni_ia4cls = (*env)->FindClass(env, "java/net/Inet4Address");
CHECK_NULL_RETURN(ni_ia4cls, JNI_FALSE);
ni_ia4cls = (*env)->NewGlobalRef(env, ni_ia4cls);
CHECK_NULL_RETURN(ni_ia4cls, JNI_FALSE);
ni_ia4ctrID = (*env)->GetMethodID(env, ni_ia4cls, "<init>", "()V");
CHECK_NULL_RETURN(ni_ia4ctrID, JNI_FALSE);
initialized = 1;
}
return JNI_TRUE;
}
#if defined(_ALLBSD_SOURCE) && !defined(HAS_GLIBC_GETHOSTBY_R)
extern jobjectArray lookupIfLocalhost(JNIEnv *env, const char *hostname, jboolean includeV6);
......@@ -367,20 +410,120 @@ Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
return (*env)->NewStringUTF(env, hostname);
}
/*
* Find an internet address for a given hostname. Note that this
* code only works for addresses of type INET. The translation
* of %d.%d.%d.%d to an address (int) occurs in java now, so the
* String "host" shouldn't *ever* be a %d.%d.%d.%d string
*
* Class: java_net_Inet4AddressImpl
* Method: lookupAllHostAddr
* Signature: (Ljava/lang/String;)[[B
*/
#ifdef __GLIBC__
JNIEXPORT jobjectArray JNICALL
Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
jstring host) {
/* the initial size of our hostent buffers */
#define HENT_BUF_SIZE 1024
#define BIG_HENT_BUF_SIZE 10240 /* a jumbo-sized one */
// The implementation code was copied from JDK7u respository
// http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/58e586f18da6/src/solaris/native/java/net/Inet4AddressImpl.c
static jobjectArray
lookupAllHostAddrs_gethostbyname(JNIEnv *env, jobject this, jstring host) {
const char *hostname;
jobjectArray ret = 0;
struct hostent res, *hp = 0;
// this buffer must be pointer-aligned so is declared
// with pointer type
char *buf[HENT_BUF_SIZE/(sizeof (char *))];
/* temporary buffer, on the off chance we need to expand */
char *tmp = NULL;
int h_error = 0;
if (!initializeInetClasses(env))
return NULL;
if (IS_NULL(host)) {
JNU_ThrowNullPointerException(env, "host is null");
return 0;
}
hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
CHECK_NULL_RETURN(hostname, NULL);
#ifdef __solaris__
/*
* Workaround for Solaris bug 4160367 - if a hostname contains a
* white space then 0.0.0.0 is returned
*/
if (isspace((unsigned char)hostname[0])) {
JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
(char *)hostname);
JNU_ReleaseStringPlatformChars(env, host, hostname);
return NULL;
}
#endif
/* Try once, with our static buffer. */
#ifdef HAS_GLIBC_GETHOSTBY_R
gethostbyname_r(hostname, &res, (char*)buf, sizeof(buf), &hp, &h_error);
#else
hp = gethostbyname_r(hostname, &res, (char*)buf, sizeof(buf), &h_error);
#endif
/* With the re-entrant system calls, it's possible that the buffer
* we pass to it is not large enough to hold an exceptionally
* large DNS entry. This is signaled by errno->ERANGE. We try once
* more, with a very big size.
*/
if (hp == NULL && errno == ERANGE) {
if ((tmp = (char*)malloc(BIG_HENT_BUF_SIZE))) {
#ifdef HAS_GLIBC_GETHOSTBY_R
gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE,
&hp, &h_error);
#else
hp = gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE,
&h_error);
#endif
}
}
if (hp != NULL) {
struct in_addr **addrp = (struct in_addr **) hp->h_addr_list;
int i = 0;
while (*addrp != (struct in_addr *) 0) {
i++;
addrp++;
}
ret = (*env)->NewObjectArray(env, i, ni_iacls, NULL);
if (IS_NULL(ret)) {
/* we may have memory to free at the end of this */
goto cleanupAndReturn;
}
addrp = (struct in_addr **) hp->h_addr_list;
i = 0;
while (*addrp) {
jobject iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID);
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++;
}
} else {
JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
(char *)hostname);
ret = NULL;
}
cleanupAndReturn:
JNU_ReleaseStringPlatformChars(env, host, hostname);
if (tmp != NULL) {
free(tmp);
}
return ret;
}
#endif //__GLIBC__
// lookupAllHostAddr impl uses getaddrinfo
static jobjectArray
lookupAllHostAddrs_getaddrinfo(JNIEnv *env, jobject this, jstring host) {
const char *hostname;
jobjectArray ret = 0;
int retLen = 0;
......@@ -508,6 +651,35 @@ Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
return ret;
}
/*
* Find an internet address for a given hostname. Note that this
* code only works for addresses of type INET. The translation
* of %d.%d.%d.%d to an address (int) occurs in java now, so the
* String "host" shouldn't *ever* be a %d.%d.%d.%d string
*
* Class: java_net_Inet4AddressImpl
* Method: lookupAllHostAddr
* Signature: (Ljava/lang/String;)[[B
*/
JNIEXPORT jobjectArray JNICALL
Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
jstring host) {
if (!initializeInetClasses(env)) {
return NULL;
}
#if defined(__GLIBC__)
if (glibc_major_version >= 2 && glibc_minor_version >= 12) {
return lookupAllHostAddrs_getaddrinfo(env, this, host);
} else {
return lookupAllHostAddrs_gethostbyname(env, this, host);
}
#else
return lookupAllHostAddrs_getaddrinfo(env, this, host);
#endif
}
/*
* Class: java_net_Inet4AddressImpl
* Method: getHostByAddr
......
#
# Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Alibaba designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# @test LookUpAllAddrsTest.sh
# @summary test calling InetAddress.getAllByName() without triggering DNS reverse resolution
# @run shell/timeout=30 LookUpAllAddrsTest.sh
#
# NOTE: this test only works on RPM based Linux distributions
set -x
if [ "${TESTSRC}" = "" ]
then
TESTSRC=${PWD}
echo "TESTSRC not set. Using "${TESTSRC}" as default"
fi
echo "TESTSRC=${TESTSRC}"
FS=/
JAVA=${TESTJAVA}${FS}bin${FS}java
JAVAC=${TESTJAVA}${FS}bin${FS}javac
TEST_CLASS=T
TEST_SOURCE=${TEST_CLASS}.java
# pre-condition checking
which gdb
if [ $? != 0 ]
then
echo "Cannot find GDB, please install it!"
exit 1
fi
which rpm
if [ $? != 0 ]
then
echo Cannot find tool 'rpm', unsupported platform
exit 1
fi
GLIBC_VER_MAJOR=$(rpm -qa | sort | uniq | perl -e 'for (<>) { if ($_ =~ /^glibc-(\d+)\.(\d+)\D\S+/ ) { print "$1\n"; exit; } }')
GLIBC_VER_MINOR=$(rpm -qa | sort | uniq | perl -e 'for (<>) { if ($_ =~ /^glibc-(\d+)\.(\d+)\D\S+/ ) { print "$2\n"; exit; } }')
# Prepare class
cat >> $TEST_SOURCE << EOF
import java.net.*;
import java.util.*;
public class T {
static {
System.setProperty("sun.net.inetaddr.ttl", "0");
System.setProperty("sun.net.inetaddr.negative.ttl", "0");
}
public static void main(String[] args) throws InterruptedException, UnknownHostException {
System.out.println("Test begin!");
for (int i = 0; i < 10; ++i) {
long t = System.currentTimeMillis();
InetAddress[] addresses = InetAddress.getAllByName("tbapi.alipay.com");
System.out.println("COST:" + (System.currentTimeMillis() - t) + " " + Arrays.toString(addresses));
}
}
}
EOF
# GDB command file to verify the invocation path
cat >> debug.gdb << EOF
# needed options
handle SIGSEGV nostop pass noprint
handle SIGPIPE nostop pass noprint
set breakpoint pending on
# set up breakpoints to print callflow info
break getaddrinfo
command
print "#####getaddrinfo"
continue
end
break gethostbyname_r
command
print "#####gethostbyname_r"
continue
end
# start execution and exit
run
quit
EOF
# Do compilation
${JAVAC} -g $TEST_SOURCE
if [ $? != '0' ]
then
printf "Failed to compile $TEST_SOURCE"
exit 1
fi
# Run test
if [ $GLIBC_VER_MAJOR -ge 2 ] && [ $GLIBC_VER_MINOR -ge 12 ]
then
# GLIBC_VER >= 2.12, using getaddrinfo
if [ "x" == "x`gdb --command=debug.gdb --arg $JAVA $TEST_CLASS 2>&1 | grep '#####getaddrinfo'`" ]
then
echo "Failed!"
exit 1
fi
if [ "x" != "x`gdb --command=debug.gdb --arg $JAVA $TEST_CLASS 2>&1 | grep '#####gethostbyname_r'`" ]
then
echo "Failed!"
exit 1
fi
else
# GLIBC_VER < 2.12, using gethostbyname_r
if [ "x" != "x`gdb --command=debug.gdb --arg $JAVA $TEST_CLASS 2>&1 | grep '#####getaddrinfo'`" ]
then
echo "Failed!"
exit 1
fi
if [ "x" == "x`gdb --command=debug.gdb --arg $JAVA $TEST_CLASS 2>&1 | grep '#####gethostbyname_r'`" ]
then
echo "Failed!"
exit 1
fi
fi
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册