diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastIPv6MappedTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastIPv6MappedTest.java new file mode 100644 index 0000000000..8abc9ca6fb --- /dev/null +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastIPv6MappedTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019 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.testsuite.transport.socket; + +import io.netty.util.NetUtil; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +public class DatagramUnicastIPv6MappedTest extends DatagramUnicastIPv6Test { + + @Override + protected SocketAddress newSocketAddress() { + return new InetSocketAddress(0); + } + + @Override + protected InetSocketAddress sendToAddress(InetSocketAddress serverAddress) { + InetAddress addr = serverAddress.getAddress(); + if (addr.isAnyLocalAddress()) { + return new InetSocketAddress(NetUtil.LOCALHOST4, serverAddress.getPort()); + } + return serverAddress; + } +} diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastIPv6Test.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastIPv6Test.java new file mode 100644 index 0000000000..5346fc095a --- /dev/null +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastIPv6Test.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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.testsuite.transport.socket; + +import io.netty.channel.socket.InternetProtocolFamily; +import org.junit.Assume; +import org.junit.BeforeClass; + +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.nio.channels.Channel; +import java.nio.channels.spi.SelectorProvider; + +public class DatagramUnicastIPv6Test extends DatagramUnicastTest { + + @BeforeClass + public static void assumeIpv6Supported() { + try { + Channel channel = SelectorProvider.provider().openDatagramChannel(StandardProtocolFamily.INET6); + channel.close(); + } catch (UnsupportedOperationException e) { + Assume.assumeNoException("IPv6 not supported", e); + } catch (IOException ignore) { + // Ignore + } + } + @Override + protected InternetProtocolFamily internetProtocolFamily() { + return InternetProtocolFamily.IPv6; + } +} diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java index 817b75e128..9bd8f38b38 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java @@ -27,8 +27,11 @@ import io.netty.channel.ChannelOption; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramPacket; +import io.netty.util.NetUtil; import org.junit.Test; +import java.net.Inet6Address; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.channels.NotYetConnectedException; @@ -36,6 +39,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.*; @@ -171,30 +175,27 @@ public class DatagramUnicastTest extends AbstractDatagramTest { } final CountDownLatch latch = new CountDownLatch(count); - sc = setupServerChannel(sb, bytes, sender, latch, false); + AtomicReference errorRef = new AtomicReference(); + sc = setupServerChannel(sb, bytes, sender, latch, errorRef, false); - InetSocketAddress addr = (InetSocketAddress) sc.localAddress(); + InetSocketAddress addr = sendToAddress((InetSocketAddress) sc.localAddress()); + List futures = new ArrayList(count); for (int i = 0; i < count; i++) { - switch (wrapType) { - case DUP: - cc.write(new DatagramPacket(buf.retainedDuplicate(), addr)); - break; - case SLICE: - cc.write(new DatagramPacket(buf.retainedSlice(), addr)); - break; - case READ_ONLY: - cc.write(new DatagramPacket(buf.retain().asReadOnly(), addr)); - break; - case NONE: - cc.write(new DatagramPacket(buf.retain(), addr)); - break; - default: - throw new Error("unknown wrap type: " + wrapType); - } + futures.add(write(cc, buf, addr, wrapType)); } // release as we used buf.retain() before cc.flush(); - assertTrue(latch.await(10, TimeUnit.SECONDS)); + + for (ChannelFuture future: futures) { + future.sync(); + } + if (!latch.await(10, TimeUnit.SECONDS)) { + Throwable error = errorRef.get(); + if (error != null) { + throw error; + } + fail(); + } } finally { // release as we used buf.retain() before buf.release(); @@ -204,6 +205,20 @@ public class DatagramUnicastTest extends AbstractDatagramTest { } } + private static ChannelFuture write(Channel cc, ByteBuf buf, InetSocketAddress remote, WrapType wrapType) { + switch (wrapType) { + case DUP: + return cc.write(new DatagramPacket(buf.retainedDuplicate(), remote)); + case SLICE: + return cc.write(new DatagramPacket(buf.retainedSlice(), remote)); + case READ_ONLY: + return cc.write(new DatagramPacket(buf.retain().asReadOnly(), remote)); + case NONE: + return cc.write(new DatagramPacket(buf.retain(), remote)); + default: + throw new Error("unknown wrap type: " + wrapType); + } + } private void testSimpleSendWithConnect(Bootstrap sb, Bootstrap cb, ByteBuf buf, final byte[] bytes, int count) throws Throwable { try { @@ -218,20 +233,32 @@ public class DatagramUnicastTest extends AbstractDatagramTest { private void testSimpleSendWithConnect0(Bootstrap sb, Bootstrap cb, ByteBuf buf, final byte[] bytes, int count, WrapType wrapType) throws Throwable { final CountDownLatch clientLatch = new CountDownLatch(count); - + final AtomicReference clientErrorRef = new AtomicReference(); cb.handler(new SimpleChannelInboundHandler() { @Override public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { - ByteBuf buf = msg.content(); - assertEquals(bytes.length, buf.readableBytes()); - for (int i = 0; i < bytes.length; i++) { - assertEquals(bytes[i], buf.getByte(buf.readerIndex() + i)); + try { + ByteBuf buf = msg.content(); + assertEquals(bytes.length, buf.readableBytes()); + for (int i = 0; i < bytes.length; i++) { + assertEquals(bytes[i], buf.getByte(buf.readerIndex() + i)); + } + + InetSocketAddress localAddress = (InetSocketAddress) ctx.channel().localAddress(); + if (localAddress.getAddress().isAnyLocalAddress()) { + assertEquals(localAddress.getPort(), msg.recipient().getPort()); + } else { + // Test that the channel's localAddress is equal to the message's recipient + assertEquals(localAddress, msg.recipient()); + } + } finally { + clientLatch.countDown(); } + } - // Test that the channel's localAddress is equal to the message's recipient - assertEquals(ctx.channel().localAddress(), msg.recipient()); - - clientLatch.countDown(); + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + clientErrorRef.compareAndSet(null, cause); } }); @@ -239,10 +266,11 @@ public class DatagramUnicastTest extends AbstractDatagramTest { DatagramChannel cc = null; try { final CountDownLatch latch = new CountDownLatch(count); + final AtomicReference errorRef = new AtomicReference(); cc = (DatagramChannel) cb.bind(newSocketAddress()).sync().channel(); - sc = setupServerChannel(sb, bytes, cc.localAddress(), latch, true); + sc = setupServerChannel(sb, bytes, cc.localAddress(), latch, errorRef, true); - cc.connect(sc.localAddress()).syncUninterruptibly(); + cc.connect(sendToAddress((InetSocketAddress) sc.localAddress())).syncUninterruptibly(); List futures = new ArrayList(); for (int i = 0; i < count; i++) { @@ -254,8 +282,20 @@ public class DatagramUnicastTest extends AbstractDatagramTest { future.sync(); } - assertTrue(latch.await(10, TimeUnit.SECONDS)); - assertTrue(clientLatch.await(10, TimeUnit.SECONDS)); + if (!latch.await(10, TimeUnit.SECONDS)) { + Throwable cause = errorRef.get(); + if (cause != null) { + throw cause; + } + fail(); + } + if (!clientLatch.await(10, TimeUnit.SECONDS)) { + Throwable cause = clientErrorRef.get(); + if (cause != null) { + throw cause; + } + fail(); + } assertTrue(cc.isConnected()); // Test what happens when we call disconnect() @@ -292,7 +332,8 @@ public class DatagramUnicastTest extends AbstractDatagramTest { @SuppressWarnings("deprecation") private Channel setupServerChannel(Bootstrap sb, final byte[] bytes, final SocketAddress sender, - final CountDownLatch latch, final boolean echo) + final CountDownLatch latch, final AtomicReference errorRef, + final boolean echo) throws Throwable { sb.handler(new ChannelInitializer() { @Override @@ -300,25 +341,38 @@ public class DatagramUnicastTest extends AbstractDatagramTest { ch.pipeline().addLast(new SimpleChannelInboundHandler() { @Override public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { - if (sender == null) { - assertNotNull(msg.sender()); - } else { - assertEquals(sender, msg.sender()); - } + try { + if (sender == null) { + assertNotNull(msg.sender()); + } else { + InetSocketAddress senderAddress = (InetSocketAddress) sender; + if (senderAddress.getAddress().isAnyLocalAddress()) { + assertEquals(senderAddress.getPort(), msg.sender().getPort()); + } else { + assertEquals(sender, msg.sender()); + } + } - ByteBuf buf = msg.content(); - assertEquals(bytes.length, buf.readableBytes()); - for (int i = 0; i < bytes.length; i++) { - assertEquals(bytes[i], buf.getByte(buf.readerIndex() + i)); - } + ByteBuf buf = msg.content(); + assertEquals(bytes.length, buf.readableBytes()); + for (int i = 0; i < bytes.length; i++) { + assertEquals(bytes[i], buf.getByte(buf.readerIndex() + i)); + } - // Test that the channel's localAddress is equal to the message's recipient - assertEquals(ctx.channel().localAddress(), msg.recipient()); + // Test that the channel's localAddress is equal to the message's recipient + assertEquals(ctx.channel().localAddress(), msg.recipient()); - if (echo) { - ctx.writeAndFlush(new DatagramPacket(buf.retainedDuplicate(), msg.sender())); + if (echo) { + ctx.writeAndFlush(new DatagramPacket(buf.retainedDuplicate(), msg.sender())); + } + } finally { + latch.countDown(); } - latch.countDown(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + errorRef.compareAndSet(null, cause); } }); } @@ -331,4 +385,15 @@ public class DatagramUnicastTest extends AbstractDatagramTest { channel.close().sync(); } } + + protected InetSocketAddress sendToAddress(InetSocketAddress serverAddress) { + InetAddress addr = serverAddress.getAddress(); + if (addr.isAnyLocalAddress()) { + if (addr instanceof Inet6Address) { + return new InetSocketAddress(NetUtil.LOCALHOST6, serverAddress.getPort()); + } + return new InetSocketAddress(NetUtil.LOCALHOST4, serverAddress.getPort()); + } + return serverAddress; + } } diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramUnicastIPv6MappedTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramUnicastIPv6MappedTest.java new file mode 100644 index 0000000000..fd2a8f37a0 --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramUnicastIPv6MappedTest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 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.bootstrap.Bootstrap; +import io.netty.testsuite.transport.TestsuitePermutation.BootstrapComboFactory; +import io.netty.testsuite.transport.socket.DatagramUnicastIPv6MappedTest; + +import java.util.List; + +public class EpollDatagramUnicastIPv6MappedTest extends DatagramUnicastIPv6MappedTest { + @Override + protected List> newFactories() { + return EpollSocketTestPermutation.INSTANCE.datagram(internetProtocolFamily()); + } +} diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramUnicastIPv6Test.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramUnicastIPv6Test.java new file mode 100644 index 0000000000..29e0a51e6a --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramUnicastIPv6Test.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 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.bootstrap.Bootstrap; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.DatagramUnicastIPv6Test; + +import java.util.List; + +public class EpollDatagramUnicastIPv6Test extends DatagramUnicastIPv6Test { + @Override + protected List> newFactories() { + return EpollSocketTestPermutation.INSTANCE.datagram(internetProtocolFamily()); + } +} diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDatagramUnicastIPv6MappedTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDatagramUnicastIPv6MappedTest.java new file mode 100644 index 0000000000..a216d4e34a --- /dev/null +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDatagramUnicastIPv6MappedTest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 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.kqueue; + +import io.netty.bootstrap.Bootstrap; +import io.netty.testsuite.transport.TestsuitePermutation.BootstrapComboFactory; +import io.netty.testsuite.transport.socket.DatagramUnicastIPv6MappedTest; + +import java.util.List; + +public class KQueueDatagramUnicastIPv6MappedTest extends DatagramUnicastIPv6MappedTest { + @Override + protected List> newFactories() { + return KQueueSocketTestPermutation.INSTANCE.datagram(internetProtocolFamily()); + } +} diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDatagramUnicastIPv6Test.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDatagramUnicastIPv6Test.java new file mode 100644 index 0000000000..226fc3b647 --- /dev/null +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDatagramUnicastIPv6Test.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019 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.kqueue; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.socket.InternetProtocolFamily; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.DatagramUnicastIPv6Test; +import io.netty.testsuite.transport.socket.DatagramUnicastTest; + +import java.util.List; + +public class KQueueDatagramUnicastIPv6Test extends DatagramUnicastIPv6Test { + @Override + protected List> newFactories() { + return KQueueSocketTestPermutation.INSTANCE.datagram(internetProtocolFamily()); + } +} diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.c b/transport-native-unix-common/src/main/c/netty_unix_socket.c index db4b2914ae..2f817159b2 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_socket.c +++ b/transport-native-unix-common/src/main/c/netty_unix_socket.c @@ -103,7 +103,8 @@ static jobject createDatagramSocketAddress(JNIEnv* env, const struct sockaddr_st } else { offset = 0; } - (*env)->SetByteArrayRegion(env, addressBytes, offset, ipLength, (jbyte*) &s->sin6_addr.s6_addr); + jbyte* addr = (jbyte*) &s->sin6_addr.s6_addr; + (*env)->SetByteArrayRegion(env, addressBytes, 0, ipLength, addr + offset); } jobject obj = (*env)->NewObject(env, datagramSocketAddressClass, datagramSocketAddrMethodId, addressBytes, scopeId, port, len, local); if ((*env)->ExceptionCheck(env) == JNI_TRUE) {