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 24092d03c9..c8f036ad27 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 @@ -173,6 +173,68 @@ jobject createInetSocketAddress(JNIEnv * env, struct sockaddr_storage addr) { return socketAddr; } +jbyteArray createInetSocketAddressArray(JNIEnv * env, struct sockaddr_storage addr) { + int port; + if (addr.ss_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + port = ntohs(s->sin_port); + + // Encode address and port into the array + unsigned char a[8]; + a[0] = s->sin_addr.s_addr >> 24; + a[1] = s->sin_addr.s_addr >> 16; + a[2] = s->sin_addr.s_addr >> 8; + a[3] = s->sin_addr.s_addr; + a[4] = port >> 24; + a[5] = port >> 16; + a[6] = port >> 8; + a[7] = port; + + jbyteArray bArray = (*env)->NewByteArray(env, 8); + (*env)->SetByteArrayRegion(env, bArray, 0, 8, (jbyte*) &a); + + return bArray; + } else { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + port = ntohs(s->sin6_port); + + if (s->sin6_addr.s6_addr[0] == 0x00 && s->sin6_addr.s6_addr[1] == 0x00 && s->sin6_addr.s6_addr[2] == 0x00 && s->sin6_addr.s6_addr[3] == 0x00 && s->sin6_addr.s6_addr[4] == 0x00 + && s->sin6_addr.s6_addr[5] == 0x00 && s->sin6_addr.s6_addr[6] == 0x00 && s->sin6_addr.s6_addr[7] == 0x00 && s->sin6_addr.s6_addr[8] == 0x00 && s->sin6_addr.s6_addr[9] == 0x00 + && s->sin6_addr.s6_addr[10] == 0xff && s->sin6_addr.s6_addr[11] == 0xff) { + // IPv4-mapped-on-IPv6 + // Encode port into the array and write it into the jbyteArray + unsigned char a[4]; + a[0] = port >> 24; + a[1] = port >> 16; + a[2] = port >> 8; + a[3] = port; + + jbyteArray bArray = (*env)->NewByteArray(env, 8); + // we only need the last 4 bytes for mapped address + (*env)->SetByteArrayRegion(env, bArray, 0, 4, (jbyte*) &(s->sin6_addr.s6_addr[12])); + (*env)->SetByteArrayRegion(env, bArray, 4, 4, (jbyte*) &a); + + return bArray; + } else { + // Encode scopeid and port into the array + unsigned char a[8]; + a[0] = s->sin6_scope_id >> 24; + a[1] = s->sin6_scope_id >> 16; + a[2] = s->sin6_scope_id >> 8; + a[3] = s->sin6_scope_id; + a[4] = port >> 24; + a[5] = port >> 16; + a[6] = port >> 8; + a[7] = port; + + jbyteArray bArray = (*env)->NewByteArray(env, 24); + (*env)->SetByteArrayRegion(env, bArray, 0, 16, (jbyte*) &(s->sin6_addr.s6_addr)); + (*env)->SetByteArrayRegion(env, bArray, 16, 8, (jbyte*) &a); + return bArray; + } + } +} + jobject createDatagramSocketAddress(JNIEnv * env, struct sockaddr_storage addr, int len) { char ipstr[INET6_ADDRSTRLEN]; int port; @@ -1138,7 +1200,7 @@ JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv *env return res; } -JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_remoteAddress(JNIEnv * env, jclass clazz, jint fd) { +JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv * env, jclass clazz, jint fd) { socklen_t len; struct sockaddr_storage addr; @@ -1146,10 +1208,10 @@ JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_remoteAddress(JNIEn if (getpeername(fd, (struct sockaddr*)&addr, &len) == -1) { return NULL; } - return createInetSocketAddress(env, addr); + return createInetSocketAddressArray(env, addr); } -JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_localAddress(JNIEnv * env, jclass clazz, jint fd) { +JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv * env, jclass clazz, jint fd) { socklen_t len; struct sockaddr_storage addr; @@ -1157,7 +1219,7 @@ JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_localAddress(JNIEnv if (getsockname(fd, (struct sockaddr*)&addr, &len) == -1) { return NULL; } - return createInetSocketAddress(env, addr); + return createInetSocketAddressArray(env, addr); } JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv * env, jclass clazz, jint fd, jint optval) { diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h index 92f6ee627b..949ae092b0 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h @@ -70,8 +70,8 @@ jboolean Java_io_netty_channel_epoll_Native_connect(JNIEnv * env, jclass clazz, jboolean Java_io_netty_channel_epoll_Native_finishConnect(JNIEnv * env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_accept(JNIEnv * env, jclass clazz, jint fd); jlong Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv *env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len); -jobject Java_io_netty_channel_epoll_Native_remoteAddress(JNIEnv * env, jclass clazz, jint fd); -jobject Java_io_netty_channel_epoll_Native_localAddress(JNIEnv * env, jclass clazz, jint fd); +jbyteArray Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv * env, jclass clazz, jint fd); +jbyteArray Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv * env, jclass clazz, jint fd); void Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv * env, jclass clazz, jint fd, jint optval); void Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv * env, jclass clazz, jint fd, jint optval); void Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv *env, jclass clazz, jint fd, jint optval); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java index 9b6894ee4f..6c661c8788 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.Locale; @@ -200,8 +201,61 @@ final class Native { public static native boolean connect(int fd, byte[] address, int scopeId, int port) throws IOException; public static native boolean finishConnect(int fd) throws IOException; - public static native InetSocketAddress remoteAddress(int fd); - public static native InetSocketAddress localAddress(int fd); + public static InetSocketAddress remoteAddress(int fd) { + byte[] addr = remoteAddress0(fd); + return address(addr); + } + + public static InetSocketAddress localAddress(int fd) { + byte[] addr = localAddress0(fd); + return address(addr); + } + + static InetSocketAddress address(byte[] addr) { + int len = addr.length; + // The last 4 bytes are always the port + final int port = decodeInt(addr, len - 4); + final InetAddress address; + + try { + switch (len) { + // 8 bytes: + // - 4 == ipaddress + // - 4 == port + case 8: + byte[] ipv4 = new byte[4]; + System.arraycopy(addr, 0, ipv4, 0, 4); + address = InetAddress.getByAddress(ipv4); + break; + + // 24 bytes: + // - 16 == ipaddress + // - 4 == scopeId + // - 4 == port + case 24: + byte[] ipv6 = new byte[16]; + System.arraycopy(addr, 0, ipv6, 0, 16); + int scopeId = decodeInt(addr, len - 8); + address = Inet6Address.getByAddress(null, ipv6, scopeId); + break; + default: + throw new Error(); + } + return new InetSocketAddress(address, port); + } catch (UnknownHostException e) { + throw new Error("Should never happen", e); + } + } + + private static int decodeInt(byte[] addr, int index) { + return (addr[index] & 0xff) << 24 | + (addr[index + 1] & 0xff) << 16 | + (addr[index + 2] & 0xff) << 8 | + addr[index + 3] & 0xff; + } + + private static native byte[] remoteAddress0(int fd); + private static native byte[] localAddress0(int fd); public static native int accept(int fd) throws IOException; public static native void shutdown(int fd, boolean read, boolean write) throws IOException; diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/NativeTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/NativeTest.java new file mode 100644 index 0000000000..a99da2d234 --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/NativeTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.epoll; + +import io.netty.util.NetUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.net.Inet6Address; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; + +public class NativeTest { + + @Test + public void testAddressIpv4() throws Exception { + InetSocketAddress inetAddress = new InetSocketAddress(NetUtil.LOCALHOST4, 9999); + byte[] bytes = new byte[8]; + ByteBuffer buffer = ByteBuffer.wrap(bytes); + buffer.put(inetAddress.getAddress().getAddress()); + buffer.putInt(inetAddress.getPort()); + Assert.assertEquals(inetAddress, Native.address(buffer.array())); + } + + @Test + public void testAddressIpv6() throws Exception { + Inet6Address address = NetUtil.LOCALHOST6; + InetSocketAddress inetAddress = new InetSocketAddress(address, 9999); + byte[] bytes = new byte[24]; + ByteBuffer buffer = ByteBuffer.wrap(bytes); + buffer.put(address.getAddress()); + buffer.putInt(address.getScopeId()); + buffer.putInt(inetAddress.getPort()); + Assert.assertEquals(inetAddress, Native.address(buffer.array())); + } +}