From fadbf7df52873b8071660f9f867c3b1ff6e34679 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 6 Mar 2015 13:05:43 +0100 Subject: [PATCH] Respect -Djava.net.preferIPv4Stack when using epoll transport Motivation: On a system where ipv4 and ipv6 are supported a user may want to use -Djava.net.preferIPv4Stack=true to restrict it to use ipv4 only. This is currently ignored with the epoll transport. Modifications: Respect java.net.preferIPv4Stack system property. Result: -Djava.net.preferIPv4Stack=true will have the effect the user is looking for. --- .../src/main/java/io/netty/util/NetUtil.java | 12 +++++++ .../main/c/io_netty_channel_epoll_Native.c | 34 +++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/io/netty/util/NetUtil.java b/common/src/main/java/io/netty/util/NetUtil.java index 9f63bade48..00958c45b2 100644 --- a/common/src/main/java/io/netty/util/NetUtil.java +++ b/common/src/main/java/io/netty/util/NetUtil.java @@ -114,6 +114,11 @@ public final class NetUtil { */ private static final int IPV4_SEPARATORS = 3; + /** + * {@code true} if ipv4 should be used on a system that supports ipv4 and ipv6. + */ + private static final boolean IPV4_PREFERRED = Boolean.getBoolean("java.net.preferIPv4Stack"); + /** * The logger being used by this class */ @@ -259,6 +264,13 @@ public final class NetUtil { SOMAXCONN = somaxconn; } + /** + * Returns {@code true} if ipv4 should be prefered on a system that supports ipv4 and ipv6. + */ + public static boolean isIpV4StackPreferred() { + return IPV4_PREFERRED; + } + /** * Creates an byte[] based on an ipAddressString. No error handling is * performed here. diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c index d1d5b98108..aa755c234a 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c @@ -84,6 +84,8 @@ jmethodID closedChannelExceptionMethodId = NULL; jclass inetSocketAddressClass = NULL; jclass datagramSocketAddressClass = NULL; jclass nativeDatagramPacketClass = NULL; +jclass netUtilClass = NULL; +jmethodID netUtilClassIpv4PreferredMethodId = NULL; static int socketType; static const char* ip4prefix = "::ffff:"; @@ -305,7 +307,13 @@ static int init_sockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jpo return 0; } -static int socket_type() { +static int socket_type(JNIEnv* env) { + jboolean ipv4Preferred = (*env)->CallStaticBooleanMethod(env, netUtilClass, netUtilClassIpv4PreferredMethodId); + + if (ipv4Preferred) { + // User asked to use ipv4 explicitly. + return AF_INET; + } int fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0); if (fd == -1) { if (errno == EAFNOSUPPORT) { @@ -343,6 +351,24 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; } else { + jclass localNetUtilClass = (*env)->FindClass(env, "io/netty/util/NetUtil" ); + if (localNetUtilClass == NULL) { + // pending exception... + return JNI_ERR; + } + netUtilClass = (jclass) (*env)->NewGlobalRef(env, localNetUtilClass); + if (netUtilClass == NULL) { + // out-of-memory! + throwOutOfMemoryError(env); + return JNI_ERR; + } + + netUtilClassIpv4PreferredMethodId = (*env)->GetStaticMethodID(env, netUtilClass, "isIpV4StackPreferred", "()Z" ); + if (netUtilClassIpv4PreferredMethodId == NULL) { + // position method was not found.. something is wrong so bail out + throwRuntimeException(env, "failed to get method ID: NetUild.isIpV4StackPreferred()"); + return JNI_ERR; + } // cache classes that are used within other jni methods for performance reasons jclass localClosedChannelExceptionClass = (*env)->FindClass(env, "java/nio/channels/ClosedChannelException"); if (localClosedChannelExceptionClass == NULL) { @@ -509,7 +535,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { throwRuntimeException(env, "failed to get method ID: InetSocketAddress.(String, int)"); return JNI_ERR; } - socketType = socket_type(); + socketType = socket_type(env); datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "", "(Ljava/lang/String;II)V"); if (datagramSocketAddrMethodId == NULL) { @@ -575,6 +601,9 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) { if (datagramSocketAddressClass != NULL) { (*env)->DeleteGlobalRef(env, datagramSocketAddressClass); } + if (netUtilClass != NULL) { + (*env)->DeleteGlobalRef(env, netUtilClass); + } } } @@ -952,7 +981,6 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_shutdown0(JNIEnv* env, } static inline jint socket0(JNIEnv* env, jclass clazz, int type) { - // TODO: Maybe also respect -Djava.net.preferIPv4Stack=true int fd = socket(socketType, type | SOCK_NONBLOCK, 0); if (fd == -1) { return -errno;