# HG changeset patch # User igerasim # Date 1523643675 25200 # Fri Apr 13 11:21:15 2018 -0700 # Node ID f6ca2602a503093f2a6749553623ef45034d666d # Parent 4902927396523f324766b4c461ed6c99fd9aa5d3 [mq]: 8201510-Merge-TwoStacksPlainSocketImpl-into-DualStackPlainSocketImpl diff --git a/src/java.base/windows/classes/java/net/DualStackPlainSocketImpl.java b/src/java.base/windows/classes/java/net/DualStackPlainSocketImpl.java --- a/src/java.base/windows/classes/java/net/DualStackPlainSocketImpl.java +++ b/src/java.base/windows/classes/java/net/DualStackPlainSocketImpl.java @@ -26,14 +26,18 @@ import java.io.IOException; import java.io.FileDescriptor; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.security.action.GetPropertyAction; import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.JavaIOFileDescriptorAccess; /** - * This class defines the plain SocketImpl that is used on Windows platforms - * greater or equal to Windows Vista. These platforms have a dual - * layer TCP/IP stack and can handle both IPv4 and IPV6 through a - * single file descriptor. + * This class defines the plain SocketImpl. + * When java.net.preferIPv4Stack system property is set to true, it uses + * IPv4-only socket. + * When java.net.preferIPv4Stack is set to false, it handles both IPv4 + * and IPv6 through a single file descriptor. * * @author Chris Hegarty */ @@ -43,30 +47,35 @@ private static final JavaIOFileDescriptorAccess fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess(); - // true if this socket is exclusively bound - private final boolean exclusiveBind; + private static final boolean preferIPv4Stack = + Boolean.parseBoolean(AccessController.doPrivileged( + new GetPropertyAction("java.net.preferIPv4Stack", "false"))); - // emulates SO_REUSEADDR when exclusiveBind is true + private static final boolean useExclusiveBind = + Boolean.parseBoolean(AccessController.doPrivileged( + new GetPropertyAction("sun.net.useExclusiveBind", "true"))); + + // emulates SO_REUSEADDR when useExclusiveBind is true private boolean isReuseAddress; - public DualStackPlainSocketImpl(boolean exclBind) { - exclusiveBind = exclBind; + public DualStackPlainSocketImpl() { } - public DualStackPlainSocketImpl(FileDescriptor fd, boolean exclBind) { + public DualStackPlainSocketImpl(FileDescriptor fd) { this.fd = fd; - exclusiveBind = exclBind; } + @Override void socketCreate(boolean stream) throws IOException { if (fd == null) throw new SocketException("Socket closed"); - int newfd = socket0(stream, false /*v6 Only*/); + int newfd = socket0(stream); fdAccess.set(fd, newfd); } + @Override void socketConnect(InetAddress address, int port, int timeout) throws IOException { int nativefd = checkAndReturnNativeFD(); @@ -74,6 +83,9 @@ if (address == null) throw new NullPointerException("inet address argument is null."); + if (preferIPv4Stack && !(address instanceof Inet4Address)) + throw new SocketException("Protocol family not supported"); + int connectResult; if (timeout <= 0) { connectResult = connect0(nativefd, address, port); @@ -97,13 +109,17 @@ localport = localPort0(nativefd); } + @Override void socketBind(InetAddress address, int port) throws IOException { int nativefd = checkAndReturnNativeFD(); if (address == null) throw new NullPointerException("inet address argument is null."); - bind0(nativefd, address, port, exclusiveBind); + if (preferIPv4Stack && !(address instanceof Inet4Address)) + throw new SocketException("Protocol family not supported"); + + bind0(nativefd, address, port, useExclusiveBind); if (port == 0) { localport = localPort0(nativefd); } else { @@ -113,12 +129,14 @@ this.address = address; } + @Override void socketListen(int backlog) throws IOException { int nativefd = checkAndReturnNativeFD(); listen0(nativefd, backlog); } + @Override void socketAccept(SocketImpl s) throws IOException { int nativefd = checkAndReturnNativeFD(); @@ -148,13 +166,17 @@ s.port = isa.getPort(); s.address = isa.getAddress(); s.localport = localport; + if (preferIPv4Stack && !(s.address instanceof Inet4Address)) + throw new SocketException("Protocol family not supported"); } + @Override int socketAvailable() throws IOException { int nativefd = checkAndReturnNativeFD(); return available0(nativefd); } + @Override void socketClose0(boolean useDeferredClose/*unused*/) throws IOException { if (fd == null) throw new SocketException("Socket closed"); @@ -167,6 +189,7 @@ close0(nativefd); } + @Override void socketShutdown(int howto) throws IOException { int nativefd = checkAndReturnNativeFD(); shutdown0(nativefd, howto); @@ -174,41 +197,51 @@ // Intentional fallthrough after SO_REUSEADDR @SuppressWarnings("fallthrough") + @Override void socketSetOption(int opt, boolean on, Object value) throws SocketException { - int nativefd = checkAndReturnNativeFD(); - if (opt == SO_TIMEOUT) { // timeout implemented through select. - return; - } // SO_REUSEPORT is not supported on Windows. if (opt == SO_REUSEPORT) { throw new UnsupportedOperationException("unsupported option"); } + int nativefd = checkAndReturnNativeFD(); + + if (opt == SO_TIMEOUT) { + if (preferIPv4Stack) { + // Don't enable the socket option on ServerSocket as it's + // meaningless (we don't receive on a ServerSocket). + if (serverSocket == null) { + setSoTimeout0(nativefd, ((Integer)value).intValue()); + } + } // else timeout is implemented through select. + return; + } + int optionValue = 0; switch(opt) { - case SO_REUSEADDR : - if (exclusiveBind) { + case SO_REUSEADDR: + if (useExclusiveBind) { // SO_REUSEADDR emulated when using exclusive bind isReuseAddress = on; return; } // intentional fallthrough - case TCP_NODELAY : - case SO_OOBINLINE : - case SO_KEEPALIVE : + case TCP_NODELAY: + case SO_OOBINLINE: + case SO_KEEPALIVE: optionValue = on ? 1 : 0; break; - case SO_SNDBUF : - case SO_RCVBUF : - case IP_TOS : + case SO_SNDBUF: + case SO_RCVBUF: + case IP_TOS: optionValue = ((Integer)value).intValue(); break; - case SO_LINGER : + case SO_LINGER: if (on) { - optionValue = ((Integer)value).intValue(); + optionValue = ((Integer)value).intValue(); } else { optionValue = -1; } @@ -220,7 +253,15 @@ setIntOption(nativefd, opt, optionValue); } - int socketGetOption(int opt, Object iaContainerObj) throws SocketException { + @Override + int socketGetOption(int opt, Object iaContainerObj) + throws SocketException { + + // SO_REUSEPORT is not supported on Windows. + if (opt == SO_REUSEPORT) { + throw new UnsupportedOperationException("unsupported option"); + } + int nativefd = checkAndReturnNativeFD(); // SO_BINDADDR is not a socket option. @@ -228,27 +269,24 @@ localAddress(nativefd, (InetAddressContainer)iaContainerObj); return 0; // return value doesn't matter. } - // SO_REUSEPORT is not supported on Windows. - if (opt == SO_REUSEPORT) { - throw new UnsupportedOperationException("unsupported option"); - } // SO_REUSEADDR emulated when using exclusive bind - if (opt == SO_REUSEADDR && exclusiveBind) - return isReuseAddress? 1 : -1; + if (opt == SO_REUSEADDR && useExclusiveBind) + return isReuseAddress ? 1 : -1; int value = getIntOption(nativefd, opt); switch (opt) { - case TCP_NODELAY : - case SO_OOBINLINE : - case SO_KEEPALIVE : - case SO_REUSEADDR : + case TCP_NODELAY: + case SO_OOBINLINE: + case SO_KEEPALIVE: + case SO_REUSEADDR: return (value == 0) ? -1 : 1; } return value; } + @Override void socketSendUrgentData(int data) throws IOException { int nativefd = checkAndReturnNativeFD(); sendOOB(nativefd, data); @@ -271,7 +309,7 @@ static native void initIDs(); - static native int socket0(boolean stream, boolean v6Only) throws IOException; + static native int socket0(boolean stream) throws IOException; static native void bind0(int fd, InetAddress localAddress, int localport, boolean exclBind) @@ -300,6 +338,8 @@ static native void setIntOption(int fd, int cmd, int optionValue) throws SocketException; + static native void setSoTimeout0(int fd, int timeout) throws SocketException; + static native int getIntOption(int fd, int cmd) throws SocketException; static native void sendOOB(int fd, int data) throws IOException; diff --git a/src/java.base/windows/classes/java/net/PlainSocketImpl.java b/src/java.base/windows/classes/java/net/PlainSocketImpl.java --- a/src/java.base/windows/classes/java/net/PlainSocketImpl.java +++ b/src/java.base/windows/classes/java/net/PlainSocketImpl.java @@ -25,20 +25,13 @@ package java.net; import java.io.*; -import java.security.AccessController; -import java.security.PrivilegedAction; -import sun.security.action.GetPropertyAction; /* * This class PlainSocketImpl simply delegates to the appropriate real * SocketImpl. We do this because PlainSocketImpl is already extended * by SocksSocketImpl. *

- * There are two possibilities for the real SocketImpl, - * TwoStacksPlainSocketImpl or DualStackPlainSocketImpl. We use - * DualStackPlainSocketImpl on systems that have a dual stack - * TCP implementation. Otherwise we create an instance of - * TwoStacksPlainSocketImpl and delegate to it. + * There is one possibility for the real SocketImpl: DualStackPlainSocketImpl. * * @author Chris Hegarty */ @@ -46,44 +39,18 @@ class PlainSocketImpl extends AbstractPlainSocketImpl { private AbstractPlainSocketImpl impl; - /* java.net.preferIPv4Stack */ - private static final boolean preferIPv4Stack; - - /* True if exclusive binding is on for Windows */ - private static final boolean exclusiveBind; - - static { - preferIPv4Stack = Boolean.parseBoolean( - AccessController.doPrivileged( - new GetPropertyAction("java.net.preferIPv4Stack"))); - - String exclBindProp = AccessController.doPrivileged( - new GetPropertyAction("sun.net.useExclusiveBind", "")); - exclusiveBind = (exclBindProp.isEmpty()) - ? true - : Boolean.parseBoolean(exclBindProp); - } - /** * Constructs an empty instance. */ PlainSocketImpl() { - if (!preferIPv4Stack) { - impl = new DualStackPlainSocketImpl(exclusiveBind); - } else { - impl = new TwoStacksPlainSocketImpl(exclusiveBind); - } + impl = new DualStackPlainSocketImpl(); } /** * Constructs an instance with the given file descriptor. */ PlainSocketImpl(FileDescriptor fd) { - if (!preferIPv4Stack) { - impl = new DualStackPlainSocketImpl(fd, exclusiveBind); - } else { - impl = new TwoStacksPlainSocketImpl(fd, exclusiveBind); - } + impl = new DualStackPlainSocketImpl(fd); } // Override methods in SocketImpl that access impl's fields. @@ -148,18 +115,10 @@ } public void setOption(int opt, Object val) throws SocketException { - if (opt == SocketOptions.SO_REUSEPORT) { - // SO_REUSEPORT is not supported on Windows. - throw new UnsupportedOperationException("unsupported option"); - } impl.setOption(opt, val); } public Object getOption(int opt) throws SocketException { - if (opt == SocketOptions.SO_REUSEPORT) { - // SO_REUSEPORT is not supported on Windows. - throw new UnsupportedOperationException("unsupported option"); - } return impl.getOption(opt); } @@ -271,8 +230,8 @@ // Override methods in AbstractPlainSocketImpl that need to be implemented. - void socketCreate(boolean isServer) throws IOException { - impl.socketCreate(isServer); + void socketCreate(boolean stream) throws IOException { + impl.socketCreate(stream); } void socketConnect(InetAddress address, int port, int timeout) @@ -307,18 +266,10 @@ void socketSetOption(int cmd, boolean on, Object value) throws SocketException { - if (cmd == SocketOptions.SO_REUSEPORT) { - // SO_REUSEPORT is not supported on Windows. - throw new UnsupportedOperationException("unsupported option"); - } impl.socketSetOption(cmd, on, value); } int socketGetOption(int opt, Object iaContainerObj) throws SocketException { - if (opt == SocketOptions.SO_REUSEPORT) { - // SO_REUSEPORT is not supported on Windows. - throw new UnsupportedOperationException("unsupported option"); - } return impl.socketGetOption(opt, iaContainerObj); } diff --git a/src/java.base/windows/classes/java/net/TwoStacksPlainSocketImpl.java b/src/java.base/windows/classes/java/net/TwoStacksPlainSocketImpl.java deleted file mode 100644 --- a/src/java.base/windows/classes/java/net/TwoStacksPlainSocketImpl.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (c) 2007, 2018, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle 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. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package java.net; - -import java.io.IOException; -import java.io.FileDescriptor; -import sun.net.ResourceManager; -import jdk.internal.misc.SharedSecrets; -import jdk.internal.misc.JavaIOFileDescriptorAccess; - -/* - * This class defines the plain SocketImpl that is used when - * the System property java.net.preferIPv4Stack is set to true. - * - * @author Chris Hegarty - */ - -class TwoStacksPlainSocketImpl extends AbstractPlainSocketImpl { - - private static final JavaIOFileDescriptorAccess fdAccess = - SharedSecrets.getJavaIOFileDescriptorAccess(); - - // true if this socket is exclusively bound - private final boolean exclusiveBind; - - // emulates SO_REUSEADDR when exclusiveBind is true - private boolean isReuseAddress; - - public TwoStacksPlainSocketImpl(boolean exclBind) { - exclusiveBind = exclBind; - } - - public TwoStacksPlainSocketImpl(FileDescriptor fd, boolean exclBind) { - this.fd = fd; - exclusiveBind = exclBind; - } - - void socketCreate(boolean stream) throws IOException { - if (fd == null) - throw new SocketException("Socket closed"); - - int newfd = socket0(stream, false /*v6 Only*/); - - fdAccess.set(fd, newfd); - } - - @Override - void socketConnect(InetAddress address, int port, int timeout) - throws IOException { - int nativefd = checkAndReturnNativeFD(); - - if (address == null) - throw new NullPointerException("inet address argument is null."); - - int connectResult; - if (timeout <= 0) { - connectResult = connect0(nativefd, address, port); - } else { - configureBlocking(nativefd, false); - try { - connectResult = connect0(nativefd, address, port); - if (connectResult == WOULDBLOCK) { - waitForConnect(nativefd, timeout); - } - } finally { - configureBlocking(nativefd, true); - } - } - /* - * We need to set the local port field. If bind was called - * previous to the connect (by the client) then localport field - * will already be set. - */ - if (localport == 0) - localport = localPort0(nativefd); - } - - @Override - void socketBind(InetAddress address, int port) throws IOException { - int nativefd = checkAndReturnNativeFD(); - - if (address == null) - throw new NullPointerException("inet address argument is null."); - - bind0(nativefd, address, port, exclusiveBind); - if (port == 0) { - localport = localPort0(nativefd); - } else { - localport = port; - } - - this.address = address; - } - - @Override - void socketListen(int backlog) throws IOException { - int nativefd = checkAndReturnNativeFD(); - - listen0(nativefd, backlog); - } - - @Override - void socketAccept(SocketImpl s) throws IOException { - int nativefd = checkAndReturnNativeFD(); - - if (s == null) - throw new NullPointerException("socket is null"); - - int newfd = -1; - InetSocketAddress[] isaa = new InetSocketAddress[1]; - if (timeout <= 0) { - newfd = accept0(nativefd, isaa); - } else { - configureBlocking(nativefd, false); - try { - waitForNewConnection(nativefd, timeout); - newfd = accept0(nativefd, isaa); - if (newfd != -1) { - configureBlocking(newfd, true); - } - } finally { - configureBlocking(nativefd, true); - } - } - /* Update (SocketImpl)s' fd */ - fdAccess.set(s.fd, newfd); - /* Update socketImpls remote port, address and localport */ - InetSocketAddress isa = isaa[0]; - s.port = isa.getPort(); - s.address = isa.getAddress(); - s.localport = localport; - } - - @Override - int socketAvailable() throws IOException { - int nativefd = checkAndReturnNativeFD(); - return available0(nativefd); - } - - @Override - void socketClose0(boolean useDeferredClose/*unused*/) throws IOException { - if (fd == null) - throw new SocketException("Socket closed"); - - if (!fd.valid()) - return; - - final int nativefd = fdAccess.get(fd); - fdAccess.set(fd, -1); - close0(nativefd); - } - - @Override - void socketShutdown(int howto) throws IOException { - int nativefd = checkAndReturnNativeFD(); - shutdown0(nativefd, howto); - } - - // Intentional fallthrough after SO_REUSEADDR - @SuppressWarnings("fallthrough") - @Override - void socketSetOption(int opt, boolean on, Object value) - throws SocketException { - int nativefd = checkAndReturnNativeFD(); - - if (opt == SO_TIMEOUT) { - // Don't enable the socket option on ServerSocket as it's - // meaningless (we don't receive on a ServerSocket). - if (serverSocket == null) { - setSoTimeout0(nativefd, ((Integer)value).intValue()); - } - return; - } - // SO_REUSEPORT is not supported on Windows. - if (opt == SO_REUSEPORT) { - throw new UnsupportedOperationException("unsupported option"); - } - - int optionValue = 0; - - switch(opt) { - case SO_REUSEADDR : - if (exclusiveBind) { - // SO_REUSEADDR emulated when using exclusive bind - isReuseAddress = on; - return; - } - // intentional fallthrough - case TCP_NODELAY : - case SO_OOBINLINE : - case SO_KEEPALIVE : - optionValue = on ? 1 : 0; - break; - case SO_SNDBUF : - case SO_RCVBUF : - case IP_TOS : - optionValue = ((Integer)value).intValue(); - break; - case SO_LINGER : - if (on) { - optionValue = ((Integer)value).intValue(); - } else { - optionValue = -1; - } - break; - default :/* shouldn't get here */ - throw new SocketException("Option not supported"); - } - - setIntOption(nativefd, opt, optionValue); - } - - @Override - int socketGetOption(int opt, Object iaContainerObj) throws SocketException { - int nativefd = checkAndReturnNativeFD(); - - // SO_BINDADDR is not a socket option. - if (opt == SO_BINDADDR) { - localAddress(nativefd, (InetAddressContainer)iaContainerObj); - return 0; // return value doesn't matter. - } - // SO_REUSEPORT is not supported on Windows. - if (opt == SO_REUSEPORT) { - throw new UnsupportedOperationException("unsupported option"); - } - - // SO_REUSEADDR emulated when using exclusive bind - if (opt == SO_REUSEADDR && exclusiveBind) - return isReuseAddress? 1 : -1; - - int value = getIntOption(nativefd, opt); - - switch (opt) { - case TCP_NODELAY : - case SO_OOBINLINE : - case SO_KEEPALIVE : - case SO_REUSEADDR : - return (value == 0) ? -1 : 1; - } - return value; - } - - @Override - void socketSendUrgentData(int data) throws IOException { - int nativefd = checkAndReturnNativeFD(); - sendOOB(nativefd, data); - } - - private int checkAndReturnNativeFD() throws SocketException { - if (fd == null || !fd.valid()) - throw new SocketException("Socket closed"); - - return fdAccess.get(fd); - } - - static final int WOULDBLOCK = -2; // Nothing available (non-blocking) - - static { - initIDs(); - } - - /* Native methods */ - - static native void initIDs(); - - static native int socket0(boolean stream, boolean v6Only) throws IOException; - - static native void bind0(int fd, InetAddress localAddress, int localport, - boolean exclBind) - throws IOException; - - static native int connect0(int fd, InetAddress remote, int remotePort) - throws IOException; - - static native void waitForConnect(int fd, int timeout) throws IOException; - - static native int localPort0(int fd) throws IOException; - - static native void localAddress(int fd, InetAddressContainer in) throws SocketException; - - static native void listen0(int fd, int backlog) throws IOException; - - static native int accept0(int fd, InetSocketAddress[] isaa) throws IOException; - - static native void waitForNewConnection(int fd, int timeout) throws IOException; - - static native int available0(int fd) throws IOException; - - static native void close0(int fd) throws IOException; - - static native void shutdown0(int fd, int howto) throws IOException; - - static native void setIntOption(int fd, int cmd, int optionValue) throws SocketException; - - static native void setSoTimeout0(int fd, int timeout) throws SocketException; - - static native int getIntOption(int fd, int cmd) throws SocketException; - - static native void sendOOB(int fd, int data) throws IOException; - - static native void configureBlocking(int fd, boolean blocking) throws IOException; -} diff --git a/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c b/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c --- a/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c +++ b/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2018, 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 @@ -27,8 +27,8 @@ #include "java_net_DualStackPlainSocketImpl.h" #include "java_net_SocketOptions.h" -#define SET_BLOCKING 0 -#define SET_NONBLOCKING 1 +#define SET_BLOCKING 0 +#define SET_NONBLOCKING 1 static jclass isa_class; /* java.net.InetSocketAddress */ static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */ @@ -60,22 +60,28 @@ * Signature: (ZZ)I */ JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_socket0 - (JNIEnv *env, jclass clazz, jboolean stream, jboolean v6Only /*unused*/) { + (JNIEnv *env, jclass clazz, jboolean stream) { int fd, rv, opt=0; + int type = (stream ? SOCK_STREAM : SOCK_DGRAM); + int domain = ipv6_available() ? AF_INET6 : AF_INET; - fd = NET_Socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); + fd = NET_Socket(domain, type, 0); + if (fd == INVALID_SOCKET) { NET_ThrowNew(env, WSAGetLastError(), "create"); return -1; } - rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt)); - if (rv == SOCKET_ERROR) { - NET_ThrowNew(env, WSAGetLastError(), "create"); + if (domain == AF_INET6) { + rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, + sizeof(opt)); + if (rv == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "create"); + closesocket(fd); + return -1; + } } - SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE); - return fd; } @@ -90,10 +96,11 @@ { SOCKETADDRESS sa; int rv, sa_len = 0; + jboolean v4MappedAddress = ipv6_available() ? JNI_TRUE : JNI_FALSE; if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, - &sa_len, JNI_TRUE) != 0) { - return; + &sa_len, v4MappedAddress) != 0) { + return; } rv = NET_WinBind(fd, &sa, sa_len, exclBind); @@ -111,10 +118,11 @@ (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) { SOCKETADDRESS sa; int rv, sa_len = 0; + jboolean v4MappedAddress = ipv6_available() ? JNI_TRUE : JNI_FALSE; if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, - &sa_len, JNI_TRUE) != 0) { - return -1; + &sa_len, v4MappedAddress) != 0) { + return -1; } rv = connect(fd, &sa.sa, sa_len); @@ -124,7 +132,8 @@ return java_net_DualStackPlainSocketImpl_WOULDBLOCK; } else if (err == WSAEADDRNOTAVAIL) { JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException", - "connect: Address is invalid on local machine, or port is not valid on remote machine"); + "connect: Address is invalid on local machine," + " or port is not valid on remote machine"); } else { NET_ThrowNew(env, err, "connect"); } @@ -200,6 +209,10 @@ if (rv == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Unable to establish connection"); + } else if (!ipv6_available() && rv == WSAEADDRNOTAVAIL) { + JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException", + "connect: Address is invalid on local machine," + " or port is not valid on remote machine"); } else { NET_ThrowNew(env, rv, "connect"); } @@ -284,13 +297,7 @@ newfd = accept(fd, &sa.sa, &len); if (newfd == INVALID_SOCKET) { - if (WSAGetLastError() == -2) { - JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", - "operation interrupted"); - } else { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "socket closed"); - } + NET_ThrowNew(env, WSAGetLastError(), "accept failed"); return -1; } @@ -298,6 +305,10 @@ ia = NET_SockaddrToInetAddress(env, &sa, &port); isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); + if (isa == NULL) { + closesocket(newfd); + return -1; + } (*env)->SetObjectArrayElement(env, isaa, 0, isa); return newfd; @@ -402,6 +413,51 @@ /* * Class: java_net_DualStackPlainSocketImpl + * Method: setSoTimeout0 + * Signature: (II)V + */ +JNIEXPORT void JNICALL +Java_java_net_DualStackPlainSocketImpl_setSoTimeout0 + (JNIEnv *env, jclass clazz, jint fd, jint timeout) +{ + /* + * SO_TIMEOUT is the socket option used to specify the timeout + * for ServerSocket.accept and Socket.getInputStream().read. + * It does not typically map to a native level socket option. + * For Windows we special-case this and use the SOL_SOCKET/SO_RCVTIMEO + * socket option to specify a receive timeout on the socket. This + * receive timeout is applicable to Socket only and the socket + * option should not be set on ServerSocket. + */ + + /* + * SO_RCVTIMEO is only supported on Microsoft's implementation + * of Windows Sockets so if WSAENOPROTOOPT returned then + * reset flag and timeout will be implemented using + * select() -- see SocketInputStream.socketRead. + */ + if (isRcvTimeoutSupported) { + /* + * Disable SO_RCVTIMEO if timeout is <= 5 second. + */ + if (timeout <= 5000) { + timeout = 0; + } + + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, + sizeof(timeout)) < 0) { + int err = WSAGetLastError(); + if (err == WSAENOPROTOOPT) { + isRcvTimeoutSupported = JNI_FALSE; + } else { + NET_ThrowNew(env, err, "setsockopt SO_RCVTIMEO"); + } + } + } +} + +/* + * Class: java_net_DualStackPlainSocketImpl * Method: getIntOption * Signature: (II)I */ @@ -466,7 +522,7 @@ int result; if (blocking == JNI_TRUE) { - arg = SET_BLOCKING; // 0 + arg = SET_BLOCKING; // 0 } else { arg = SET_NONBLOCKING; // 1 } diff --git a/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c b/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c deleted file mode 100644 --- a/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Copyright (c) 1997, 2018, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle 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. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -#include "net_util.h" - -#include "java_net_TwoStacksPlainSocketImpl.h" -#include "java_net_SocketOptions.h" -#include "java_net_InetAddress.h" - -#define SET_BLOCKING 0 -#define SET_NONBLOCKING 1 - -static jclass isa_class; /* java.net.InetSocketAddress */ -static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */ - -/************************************************************************ - * TwoStacksPlainSocketImpl - */ - -/* - * The initIDs function is called whenever TwoStacksPlainSocketImpl is - * loaded, to cache fieldIds for efficiency. This is called everytime - * the Java class is loaded. - * - * Class: java_net_TwoStacksPlainSocketImpl - * Method: initIDs - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_initIDs - (JNIEnv *env, jclass clazz) { - - jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); - CHECK_NULL(cls); - isa_class = (*env)->NewGlobalRef(env, cls); - CHECK_NULL(isa_class); - isa_ctorID = (*env)->GetMethodID(env, cls, "", - "(Ljava/net/InetAddress;I)V"); - CHECK_NULL(isa_ctorID); -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: socket0 - * Signature: (ZZ)I - */ -JNIEXPORT jint JNICALL Java_java_net_TwoStacksPlainSocketImpl_socket0 - (JNIEnv *env, jclass clazz, jboolean stream, jboolean v6Only /*unused*/) { - int fd; - - fd = socket(AF_INET, (stream ? SOCK_STREAM: SOCK_DGRAM), 0); - if (fd == INVALID_SOCKET) { - NET_ThrowNew(env, WSAGetLastError(), "create"); - return -1; - } - - SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE); - - return fd; -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: bind0 - * Signature: (ILjava/net/InetAddress;I)V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_bind0 - (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port, - jboolean exclBind) -{ - SOCKETADDRESS sa; - int rv, sa_len = 0; - /* family is an int field of iaObj */ - int family; - - family = getInetAddress_family(env, iaObj); - if (family != java_net_InetAddress_IPv4) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "Protocol family not supported"); - return; - } - - if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, - &sa_len, JNI_FALSE) != 0) { - return; - } - - rv = NET_WinBind(fd, &sa, sa_len, exclBind); - - if (rv == SOCKET_ERROR) - NET_ThrowNew(env, WSAGetLastError(), "NET_Bind"); -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: connect0 - * Signature: (ILjava/net/InetAddress;I)I - */ -JNIEXPORT jint JNICALL Java_java_net_TwoStacksPlainSocketImpl_connect0 - (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) { - SOCKETADDRESS sa; - int rv, sa_len = 0; - int family; - - if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, - &sa_len, JNI_FALSE) != 0) { - return -1; - } - - family = sa.sa.sa_family; - if (family != AF_INET) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "Protocol family not supported"); - return -1; - } - - rv = connect(fd, &sa.sa, sa_len); - if (rv == SOCKET_ERROR) { - int err = WSAGetLastError(); - if (err == WSAEWOULDBLOCK) { - return java_net_TwoStacksPlainSocketImpl_WOULDBLOCK; - } else if (err == WSAEADDRNOTAVAIL) { - JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException", - "connect: Address is invalid on local machine, or port is not valid on remote machine"); - } else { - NET_ThrowNew(env, err, "connect"); - } - return -1; // return value not important. - } - return rv; -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: waitForConnect - * Signature: (II)V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_waitForConnect - (JNIEnv *env, jclass clazz, jint fd, jint timeout) { - int rv, retry; - int optlen = sizeof(rv); - fd_set wr, ex; - struct timeval t; - - FD_ZERO(&wr); - FD_ZERO(&ex); - FD_SET(fd, &wr); - FD_SET(fd, &ex); - t.tv_sec = timeout / 1000; - t.tv_usec = (timeout % 1000) * 1000; - - /* - * Wait for timeout, connection established or - * connection failed. - */ - rv = select(fd+1, 0, &wr, &ex, &t); - - /* - * Timeout before connection is established/failed so - * we throw exception and shutdown input/output to prevent - * socket from being used. - * The socket should be closed immediately by the caller. - */ - if (rv == 0) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", - "connect timed out"); - shutdown( fd, SD_BOTH ); - return; - } - /* - * Socket is writable or error occurred. On some Windows editions - * the socket will appear writable when the connect fails so we - * check for error rather than writable. - */ - if (!FD_ISSET(fd, &ex)) { - return; /* connection established */ - } - - /* - * Connection failed. The logic here is designed to work around - * bug on Windows NT whereby using getsockopt to obtain the - * last error (SO_ERROR) indicates there is no error. The workaround - * on NT is to allow winsock to be scheduled and this is done by - * yielding and retrying. As yielding is problematic in heavy - * load conditions we attempt up to 3 times to get the error reason. - */ - for (retry=0; retry<3; retry++) { - NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, - (char*)&rv, &optlen); - if (rv) { - break; - } - Sleep(0); - } - - if (rv == 0) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "Unable to establish connection"); - } else if (rv == WSAEADDRNOTAVAIL) { - JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException", - "connect: Address is invalid on local machine," - " or port is not valid on remote machine"); - } else { - NET_ThrowNew(env, rv, "connect"); - } -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: localPort0 - * Signature: (I)I - */ -JNIEXPORT jint JNICALL Java_java_net_TwoStacksPlainSocketImpl_localPort0 - (JNIEnv *env, jclass clazz, jint fd) { - SOCKETADDRESS sa; - int len = sizeof(sa); - - if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) { - if (WSAGetLastError() == WSAENOTSOCK) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "Socket closed"); - } else { - NET_ThrowNew(env, WSAGetLastError(), "getsockname failed"); - } - return -1; - } - return (int) ntohs((u_short)GET_PORT(&sa)); -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: localAddress - * Signature: (ILjava/net/InetAddressContainer;)V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_localAddress - (JNIEnv *env, jclass clazz, jint fd, jobject iaContainerObj) { - int port; - SOCKETADDRESS sa; - int len = sizeof(sa); - jobject iaObj; - jclass iaContainerClass; - jfieldID iaFieldID; - - if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) { - NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name"); - return; - } - iaObj = NET_SockaddrToInetAddress(env, &sa, &port); - CHECK_NULL(iaObj); - - iaContainerClass = (*env)->GetObjectClass(env, iaContainerObj); - iaFieldID = (*env)->GetFieldID(env, iaContainerClass, "addr", "Ljava/net/InetAddress;"); - CHECK_NULL(iaFieldID); - (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj); -} - - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: listen0 - * Signature: (II)V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_listen0 - (JNIEnv *env, jclass clazz, jint fd, jint backlog) { - if (listen(fd, backlog) == SOCKET_ERROR) { - NET_ThrowNew(env, WSAGetLastError(), "listen failed"); - } -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: accept0 - * Signature: (I[Ljava/net/InetSocketAddress;)I - */ -JNIEXPORT jint JNICALL Java_java_net_TwoStacksPlainSocketImpl_accept0 - (JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) { - int newfd, port=0; - jobject isa; - jobject ia; - SOCKETADDRESS sa; - int len = sizeof(sa); - - memset((char *)&sa, 0, len); - newfd = accept(fd, &sa.sa, &len); - - if (newfd < 0) { - if (newfd == -2) { - JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", - "operation interrupted"); - } else { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "socket closed"); - } - return -1; - } - - SetHandleInformation((HANDLE)(UINT_PTR)newfd, HANDLE_FLAG_INHERIT, 0); - - if (sa.sa.sa_family != AF_INET) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "Protocol family not supported"); - NET_SocketClose(newfd); - return -1; - } - - ia = NET_SockaddrToInetAddress(env, &sa, &port); - isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); - (*env)->SetObjectArrayElement(env, isaa, 0, isa); - - return newfd; -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: waitForNewConnection - * Signature: (II)V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_waitForNewConnection - (JNIEnv *env, jclass clazz, jint fd, jint timeout) { - int rv; - - rv = NET_Timeout(fd, timeout); - if (rv == 0) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", - "Accept timed out"); - } else if (rv == -1) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); - } else if (rv == -2) { - JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", - "operation interrupted"); - } -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: available0 - * Signature: (I)I - */ -JNIEXPORT jint JNICALL Java_java_net_TwoStacksPlainSocketImpl_available0 - (JNIEnv *env, jclass clazz, jint fd) { - jint available = -1; - - if ((ioctlsocket(fd, FIONREAD, &available)) == SOCKET_ERROR) { - NET_ThrowNew(env, WSAGetLastError(), "socket available"); - } - - return available; -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: close0 - * Signature: (I)V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_close0 - (JNIEnv *env, jclass clazz, jint fd) { - NET_SocketClose(fd); -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: shutdown0 - * Signature: (II)V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_shutdown0 - (JNIEnv *env, jclass clazz, jint fd, jint howto) { - shutdown(fd, howto); -} - - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: setIntOption - * Signature: (III)V - */ -JNIEXPORT void JNICALL -Java_java_net_TwoStacksPlainSocketImpl_setIntOption - (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) -{ - int level = 0, opt = 0; - struct linger linger = {0, 0}; - char *parg; - int arglen; - - if (NET_MapSocketOption(cmd, &level, &opt) < 0) { - JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); - return; - } - - if (opt == java_net_SocketOptions_SO_LINGER) { - parg = (char *)&linger; - arglen = sizeof(linger); - if (value >= 0) { - linger.l_onoff = 1; - linger.l_linger = (unsigned short)value; - } else { - linger.l_onoff = 0; - linger.l_linger = 0; - } - } else { - parg = (char *)&value; - arglen = sizeof(value); - } - - if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) { - NET_ThrowNew(env, WSAGetLastError(), "setsockopt"); - } -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: setSoTimeout0 - * Signature: (II)V - */ -JNIEXPORT void JNICALL -Java_java_net_TwoStacksPlainSocketImpl_setSoTimeout0 - (JNIEnv *env, jclass clazz, jint fd, jint timeout) -{ - /* - * SO_TIMEOUT is the socket option used to specify the timeout - * for ServerSocket.accept and Socket.getInputStream().read. - * It does not typically map to a native level socket option. - * For Windows we special-case this and use the SOL_SOCKET/SO_RCVTIMEO - * socket option to specify a receive timeout on the socket. This - * receive timeout is applicable to Socket only and the socket - * option should not be set on ServerSocket. - */ - - /* - * SO_RCVTIMEO is only supported on Microsoft's implementation - * of Windows Sockets so if WSAENOPROTOOPT returned then - * reset flag and timeout will be implemented using - * select() -- see SocketInputStream.socketRead. - */ - if (isRcvTimeoutSupported) { - /* - * Disable SO_RCVTIMEO if timeout is <= 5 second. - */ - if (timeout <= 5000) { - timeout = 0; - } - - if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, - sizeof(timeout)) < 0) { - int err = WSAGetLastError(); - if (err == WSAENOPROTOOPT) { - isRcvTimeoutSupported = JNI_FALSE; - } else { - NET_ThrowNew(env, err, "setsockopt SO_RCVTIMEO"); - } - } - } -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: getIntOption - * Signature: (II)I - */ -JNIEXPORT jint JNICALL Java_java_net_TwoStacksPlainSocketImpl_getIntOption - (JNIEnv *env, jclass clazz, jint fd, jint cmd) -{ - int level = 0, opt = 0; - int result=0; - struct linger linger = {0, 0}; - char *arg; - int arglen; - - if (NET_MapSocketOption(cmd, &level, &opt) < 0) { - JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); - return -1; - } - - if (opt == java_net_SocketOptions_SO_LINGER) { - arg = (char *)&linger; - arglen = sizeof(linger); - } else { - arg = (char *)&result; - arglen = sizeof(result); - } - - if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) { - NET_ThrowNew(env, WSAGetLastError(), "getsockopt"); - return -1; - } - - if (opt == java_net_SocketOptions_SO_LINGER) - return linger.l_onoff ? linger.l_linger : -1; - else - return result; -} - - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: sendOOB - * Signature: (II)V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_sendOOB - (JNIEnv *env, jclass clazz, jint fd, jint data) { - jint n; - unsigned char d = (unsigned char) data & 0xff; - - n = send(fd, (char *)&data, 1, MSG_OOB); - if (n == SOCKET_ERROR) { - NET_ThrowNew(env, WSAGetLastError(), "send"); - } -} - -/* - * Class: java_net_TwoStacksPlainSocketImpl - * Method: configureBlocking - * Signature: (IZ)V - */ -JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_configureBlocking - (JNIEnv *env, jclass clazz, jint fd, jboolean blocking) { - u_long arg; - int result; - - if (blocking == JNI_TRUE) { - arg = SET_BLOCKING; // 0 - } else { - arg = SET_NONBLOCKING; // 1 - } - - result = ioctlsocket(fd, FIONBIO, &arg); - if (result == SOCKET_ERROR) { - NET_ThrowNew(env, WSAGetLastError(), "configureBlocking"); - } -} diff --git a/test/jdk/java/net/Socket/RejectIPv6.java b/test/jdk/java/net/Socket/RejectIPv6.java new file mode 100644 --- /dev/null +++ b/test/jdk/java/net/Socket/RejectIPv6.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8201510 + * @summary Make sure IPv6 addresses are rejected when the System option + * java.net.preferIPv4Stack is set to true + * @run main/othervm -Djava.net.preferIPv4Stack=true RejectIPv6 + */ + +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; + +public class RejectIPv6 { + + public static void main(String [] argv) throws Throwable { + ServerSocket serverSocket = new ServerSocket(0); + serverSocket.setSoTimeout(1000); + int serverPort = serverSocket.getLocalPort(); + Socket clientSocket = new Socket(); + + test("bind", () -> clientSocket.bind( + new InetSocketAddress("::1", 0))); + + test("connect", () -> clientSocket.connect( + new InetSocketAddress("::1", serverPort), 1000)); + } + + static void test(String msg, CodeToTest codeToTest) throws Throwable { + Thread client = new Thread(() -> + { + try { + codeToTest.run(); + throw new RuntimeException(msg + + " failed to reject IPv6 address"); + } catch (SocketException ok) { + } catch (Exception exc) { + throw new RuntimeException("unexpected", exc); + } + }); + client.start(); + client.join(); + } + + interface CodeToTest { + void run() throws Exception; + } +}