# HG changeset patch # User khazra # Date 1363293975 25200 # Node ID fa6bd09921048f0a1840c20ef810829ce9639265 # Parent be617b8c442745c047901c271e2894a03b9ccd9f 7170730: Improve Windows network stack support. Summary: Enable exclusive binding of ports on Windows Reviewed-by: alanb, chegar, ahgross diff -r be617b8c4427 -r fa6bd0992104 jdk/make/java/nio/mapfile-bsd --- a/jdk/make/java/nio/mapfile-bsd Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/make/java/nio/mapfile-bsd Thu Mar 14 13:46:15 2013 -0700 @@ -109,6 +109,7 @@ Java_sun_nio_ch_Net_getInterface6; Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_Net_poll; + Java_sun_nio_ch_Net_isExclusiveBindAvailable; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; diff -r be617b8c4427 -r fa6bd0992104 jdk/make/java/nio/mapfile-linux --- a/jdk/make/java/nio/mapfile-linux Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/make/java/nio/mapfile-linux Thu Mar 14 13:46:15 2013 -0700 @@ -117,6 +117,7 @@ Java_sun_nio_ch_Net_getInterface6; Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_Net_poll; + Java_sun_nio_ch_Net_isExclusiveBindAvailable; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; diff -r be617b8c4427 -r fa6bd0992104 jdk/make/java/nio/mapfile-solaris --- a/jdk/make/java/nio/mapfile-solaris Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/make/java/nio/mapfile-solaris Thu Mar 14 13:46:15 2013 -0700 @@ -105,6 +105,7 @@ Java_sun_nio_ch_Net_getInterface6; Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_Net_poll; + Java_sun_nio_ch_Net_isExclusiveBindAvailable; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; diff -r be617b8c4427 -r fa6bd0992104 jdk/makefiles/mapfiles/libnio/mapfile-linux --- a/jdk/makefiles/mapfiles/libnio/mapfile-linux Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/makefiles/mapfiles/libnio/mapfile-linux Thu Mar 14 13:46:15 2013 -0700 @@ -117,6 +117,7 @@ Java_sun_nio_ch_Net_getInterface6; Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_Net_poll; + Java_sun_nio_ch_Net_isExclusiveBindAvailable; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; diff -r be617b8c4427 -r fa6bd0992104 jdk/makefiles/mapfiles/libnio/mapfile-macosx --- a/jdk/makefiles/mapfiles/libnio/mapfile-macosx Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/makefiles/mapfiles/libnio/mapfile-macosx Thu Mar 14 13:46:15 2013 -0700 @@ -109,6 +109,7 @@ Java_sun_nio_ch_Net_getInterface6; Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_Net_poll; + Java_sun_nio_ch_Net_isExclusiveBindAvailable; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; diff -r be617b8c4427 -r fa6bd0992104 jdk/makefiles/mapfiles/libnio/mapfile-solaris --- a/jdk/makefiles/mapfiles/libnio/mapfile-solaris Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/makefiles/mapfiles/libnio/mapfile-solaris Thu Mar 14 13:46:15 2013 -0700 @@ -105,6 +105,7 @@ Java_sun_nio_ch_Net_getInterface6; Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_Net_poll; + Java_sun_nio_ch_Net_isExclusiveBindAvailable; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; diff -r be617b8c4427 -r fa6bd0992104 jdk/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java --- a/jdk/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -63,6 +63,8 @@ // set true when accept operation is cancelled private volatile boolean acceptKilled; + // set true when exclusive binding is on and SO_REUSEADDR is emulated + private boolean isReuseAddress; AsynchronousServerSocketChannelImpl(AsynchronousChannelGroupImpl group) { super(group.provider()); @@ -186,7 +188,14 @@ try { begin(); - Net.setSocketOption(fd, Net.UNSPEC, name, value); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = (Boolean)value; + } else { + Net.setSocketOption(fd, Net.UNSPEC, name, value); + } return this; } finally { end(); @@ -203,6 +212,12 @@ try { begin(); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + return (T)Boolean.valueOf(isReuseAddress); + } return (T) Net.getSocketOption(fd, Net.UNSPEC, name); } finally { end(); diff -r be617b8c4427 -r fa6bd0992104 jdk/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java --- a/jdk/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -79,6 +79,9 @@ private final ReadWriteLock closeLock = new ReentrantReadWriteLock(); private volatile boolean open = true; + // set true when exclusive binding is on and SO_REUSEADDR is emulated + private boolean isReuseAddress; + AsynchronousSocketChannelImpl(AsynchronousChannelGroupImpl group) throws IOException { @@ -455,7 +458,14 @@ begin(); if (writeShutdown) throw new IOException("Connection has been shutdown for writing"); - Net.setSocketOption(fd, Net.UNSPEC, name, value); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = (Boolean)value; + } else { + Net.setSocketOption(fd, Net.UNSPEC, name, value); + } return this; } finally { end(); @@ -472,6 +482,12 @@ try { begin(); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + return (T)Boolean.valueOf(isReuseAddress); + } return (T) Net.getSocketOption(fd, Net.UNSPEC, name); } finally { end(); diff -r be617b8c4427 -r fa6bd0992104 jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java --- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -94,6 +94,12 @@ // Multicast support private MembershipRegistry registry; + // set true when socket is bound and SO_REUSEADDRESS is emulated + private boolean reuseAddressEmulated; + + // set true/false when socket is already bound and SO_REUSEADDR is emulated + private boolean isReuseAddress; + // -- End of fields protected by stateLock @@ -222,6 +228,12 @@ } return this; } + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind() && localAddress != null) + { + reuseAddressEmulated = true; + this.isReuseAddress = (Boolean)value; + } // remaining options don't need any special handling Net.setSocketOption(fd, Net.UNSPEC, name, value); @@ -280,6 +292,12 @@ } } + if (name == StandardSocketOptions.SO_REUSEADDR && + reuseAddressEmulated) + { + return (T)Boolean.valueOf(isReuseAddress); + } + // no special handling return (T) Net.getSocketOption(fd, Net.UNSPEC, name); } diff -r be617b8c4427 -r fa6bd0992104 jdk/src/share/classes/sun/nio/ch/Net.java --- a/jdk/src/share/classes/sun/nio/ch/Net.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/Net.java Thu Mar 14 13:46:15 2013 -0700 @@ -44,6 +44,34 @@ } }; + // set to true if exclusive binding is on for Windows + private static final boolean exclusiveBind; + + static { + int availLevel = isExclusiveBindAvailable(); + if (availLevel >= 0) { + String exclBindProp = + java.security.AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public String run() { + return System.getProperty( + "sun.net.useExclusiveBind"); + } + }); + if (exclBindProp != null) { + exclusiveBind = exclBindProp.length() == 0 ? + true : Boolean.parseBoolean(exclBindProp); + } else if (availLevel == 1) { + exclusiveBind = true; + } else { + exclusiveBind = false; + } + } else { + exclusiveBind = false; + } + } + // -- Miscellaneous utilities -- private static volatile boolean checkedIPv6 = false; @@ -61,6 +89,13 @@ } /** + * Returns true if exclusive binding is on + */ + static boolean useExclusiveBind() { + return exclusiveBind; + } + + /** * Tells whether IPv6 sockets can join IPv4 multicast groups */ static boolean canIPv6SocketJoinIPv4Group() { @@ -308,6 +343,12 @@ private static native boolean isIPv6Available0(); + /* + * Returns 1 for Windows versions that support exclusive binding by default, 0 + * for those that do not, and -1 for Solaris/Linux/Mac OS + */ + private static native int isExclusiveBindAvailable(); + private static native boolean canIPv6SocketJoinIPv4Group0(); private static native boolean canJoin6WithIPv4Group0(); @@ -341,11 +382,12 @@ { boolean preferIPv6 = isIPv6Available() && (family != StandardProtocolFamily.INET); - bind0(preferIPv6, fd, addr, port); + bind0(fd, preferIPv6, exclusiveBind, addr, port); } - private static native void bind0(boolean preferIPv6, FileDescriptor fd, - InetAddress addr, int port) + private static native void bind0(FileDescriptor fd, boolean preferIPv6, + boolean useExclBind, InetAddress addr, + int port) throws IOException; static native void listen(FileDescriptor fd, int backlog) throws IOException; diff -r be617b8c4427 -r fa6bd0992104 jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java --- a/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -74,6 +74,9 @@ // Binding private SocketAddress localAddress; // null => unbound + // set true when exclusive binding is on and SO_REUSEADDR is emulated + private boolean isReuseAddress; + // Our socket adaptor, if any ServerSocket socket; @@ -125,13 +128,18 @@ throw new NullPointerException(); if (!supportedOptions().contains(name)) throw new UnsupportedOperationException("'" + name + "' not supported"); - synchronized (stateLock) { if (!isOpen()) throw new ClosedChannelException(); - - // no options that require special handling - Net.setSocketOption(fd, Net.UNSPEC, name, value); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = (Boolean)value; + } else { + // no options that require special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + } return this; } } @@ -149,7 +157,12 @@ synchronized (stateLock) { if (!isOpen()) throw new ClosedChannelException(); - + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + return (T)Boolean.valueOf(isReuseAddress); + } // no options that require special handling return (T) Net.getSocketOption(fd, Net.UNSPEC, name); } diff -r be617b8c4427 -r fa6bd0992104 jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java --- a/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -70,6 +70,9 @@ // -- The following fields are protected by stateLock + // set true when exclusive binding is on and SO_REUSEADDR is emulated + private boolean isReuseAddress; + // State, increases monotonically private static final int ST_UNINITIALIZED = -1; private static final int ST_UNCONNECTED = 0; @@ -174,6 +177,12 @@ if (!Net.isIPv6Available()) Net.setSocketOption(fd, StandardProtocolFamily.INET, name, value); return this; + } else if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = (Boolean)value; + return this; } // no options that require special handling @@ -196,6 +205,13 @@ if (!isOpen()) throw new ClosedChannelException(); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + return (T)Boolean.valueOf(isReuseAddress); + } + // special handling for IP_TOS: always return 0 when IPv6 if (name == StandardSocketOptions.IP_TOS) { return (Net.isIPv6Available()) ? (T) Integer.valueOf(0) : diff -r be617b8c4427 -r fa6bd0992104 jdk/src/solaris/native/sun/nio/ch/Net.c --- a/jdk/src/solaris/native/sun/nio/ch/Net.c Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/solaris/native/sun/nio/ch/Net.c Thu Mar 14 13:46:15 2013 -0700 @@ -124,6 +124,11 @@ return (ipv6_available()) ? JNI_TRUE : JNI_FALSE; } +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) { + return -1; +} + JNIEXPORT jboolean JNICALL Java_sun_nio_ch_Net_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl) { @@ -206,8 +211,8 @@ } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jboolean preferIPv6, - jobject fdo, jobject iao, int port) +Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6, + jboolean useExclBind, jobject iao, int port) { SOCKADDR sa; int sa_len = SOCKADDR_LEN; diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/classes/java/net/DefaultDatagramSocketImplFactory.java --- a/jdk/src/windows/classes/java/net/DefaultDatagramSocketImplFactory.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/classes/java/net/DefaultDatagramSocketImplFactory.java Thu Mar 14 13:46:15 2013 -0700 @@ -56,24 +56,45 @@ /* If the version supports a dual stack TCP implementation */ private static boolean useDualStackImpl = false; + /* sun.net.useExclusiveBind */ + private static String exclBindProp; + + /* True if exclusive binding is on for Windows */ + private static boolean exclusiveBind = true; + + static { // Determine Windows Version. - java.security.AccessController.doPrivileged( new PrivilegedAction() { - public Object run() { - version = 0; - try { - version = Float.parseFloat(System.getProperties().getProperty("os.version")); - preferIPv4Stack = Boolean.parseBoolean( - System.getProperties().getProperty("java.net.preferIPv4Stack")); - } catch (NumberFormatException e ) { - assert false : e; + java.security.AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + version = 0; + try { + version = Float.parseFloat(System.getProperties() + .getProperty("os.version")); + preferIPv4Stack = Boolean.parseBoolean( + System.getProperties() + .getProperty( + "java.net.preferIPv4Stack")); + exclBindProp = System.getProperty( + "sun.net.useExclusiveBind"); + } catch (NumberFormatException e ) { + assert false : e; + } + return null; // nothing to return } - return null; // nothing to return - } }); + }); // (version >= 6.0) implies Vista or greater. if (version >= 6.0 && !preferIPv4Stack) { - useDualStackImpl = true; + useDualStackImpl = true; + } + if (exclBindProp != null) { + // sun.net.useExclusiveBind is true + exclusiveBind = exclBindProp.length() == 0 ? true + : Boolean.parseBoolean(exclBindProp); + } else if (version < 6.0) { + exclusiveBind = false; } // impl.prefix @@ -105,10 +126,12 @@ throw new SocketException("can't instantiate DatagramSocketImpl"); } } else { + if (isMulticast) + exclusiveBind = false; if (useDualStackImpl && !isMulticast) - return new DualStackPlainDatagramSocketImpl(); + return new DualStackPlainDatagramSocketImpl(exclusiveBind); else - return new TwoStacksPlainDatagramSocketImpl(); + return new TwoStacksPlainDatagramSocketImpl(exclusiveBind); } } } diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java --- a/jdk/src/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -45,6 +45,22 @@ { static JavaIOFileDescriptorAccess fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess(); + // true if this socket is exclusively bound + private final boolean exclusiveBind; + + /* + * Set to true if SO_REUSEADDR is set after the socket is bound to + * indicate SO_REUSEADDR is being emulated + */ + private boolean reuseAddressEmulated; + + // emulates SO_REUSEADDR when exclusiveBind is true and socket is bound + private boolean isReuseAddress; + + DualStackPlainDatagramSocketImpl(boolean exclBind) { + exclusiveBind = exclBind; + } + protected void datagramSocketCreate() throws SocketException { if (fd == null) throw new SocketException("Socket closed"); @@ -61,7 +77,7 @@ if (laddr == null) throw new NullPointerException("argument address"); - socketBind(nativefd, laddr, lport); + socketBind(nativefd, laddr, lport, exclusiveBind); if (lport == 0) { localPort = socketLocalPort(nativefd); } else { @@ -141,6 +157,7 @@ fdAccess.set(fd, -1); } + @SuppressWarnings("fallthrough") protected void socketSetOption(int opt, Object val) throws SocketException { int nativefd = checkAndReturnNativeFD(); @@ -153,6 +170,13 @@ optionValue = ((Integer)val).intValue(); break; case SO_REUSEADDR : + if (exclusiveBind && localPort != 0) { + // socket already bound, emulate SO_REUSEADDR + reuseAddressEmulated = true; + isReuseAddress = (Boolean)val; + return; + } + //Intentional fallthrough case SO_BROADCAST : optionValue = ((Boolean)val).booleanValue() ? 1 : 0; break; @@ -170,6 +194,8 @@ if (opt == SO_BINDADDR) { return socketLocalAddress(nativefd); } + if (opt == SO_REUSEADDR && reuseAddressEmulated) + return isReuseAddress; int value = socketGetIntOption(nativefd, opt); Object returnValue = null; @@ -238,8 +264,8 @@ private static native int socketCreate(boolean v6Only); - private static native void socketBind(int fd, InetAddress localAddress, int localport) - throws SocketException; + private static native void socketBind(int fd, InetAddress localAddress, + int localport, boolean exclBind) throws SocketException; private static native void socketConnect(int fd, InetAddress address, int port) throws SocketException; diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/classes/java/net/DualStackPlainSocketImpl.java --- a/jdk/src/windows/classes/java/net/DualStackPlainSocketImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/classes/java/net/DualStackPlainSocketImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -42,10 +42,20 @@ { static JavaIOFileDescriptorAccess fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess(); - public DualStackPlainSocketImpl() {} + + // true if this socket is exclusively bound + private final boolean exclusiveBind; + + // emulates SO_REUSEADDR when exclusiveBind is true + private boolean isReuseAddress; - public DualStackPlainSocketImpl(FileDescriptor fd) { + public DualStackPlainSocketImpl(boolean exclBind) { + exclusiveBind = exclBind; + } + + public DualStackPlainSocketImpl(FileDescriptor fd, boolean exclBind) { this.fd = fd; + exclusiveBind = exclBind; } void socketCreate(boolean stream) throws IOException { @@ -93,7 +103,7 @@ if (address == null) throw new NullPointerException("inet address argument is null."); - bind0(nativefd, address, port); + bind0(nativefd, address, port, exclusiveBind); if (port == 0) { localport = localPort0(nativefd); } else { @@ -162,6 +172,8 @@ shutdown0(nativefd, howto); } + // Intentional fallthrough after SO_REUSEADDR + @SuppressWarnings("fallthrough") void socketSetOption(int opt, boolean on, Object value) throws SocketException { int nativefd = checkAndReturnNativeFD(); @@ -175,8 +187,13 @@ switch(opt) { case TCP_NODELAY : case SO_OOBINLINE : + case SO_REUSEADDR : + if (exclusiveBind) { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = on; + return; + } case SO_KEEPALIVE : - case SO_REUSEADDR : optionValue = on ? 1 : 0; break; case SO_SNDBUF : @@ -207,6 +224,10 @@ return 0; // return value doesn't matter. } + // SO_REUSEADDR emulated when using exclusive bind + if (opt == SO_REUSEADDR && exclusiveBind) + return isReuseAddress? 1 : -1; + int value = getIntOption(nativefd, opt); switch (opt) { @@ -243,7 +264,8 @@ static native int socket0(boolean stream, boolean v6Only) throws IOException; - static native void bind0(int fd, InetAddress localAddress, int localport) + static native void bind0(int fd, InetAddress localAddress, int localport, + boolean exclBind) throws IOException; static native int connect0(int fd, InetAddress remote, int remotePort) diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/classes/java/net/PlainSocketImpl.java --- a/jdk/src/windows/classes/java/net/PlainSocketImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/classes/java/net/PlainSocketImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -54,6 +54,12 @@ /* If the version supports a dual stack TCP implementation */ private static boolean useDualStackImpl = false; + /* sun.net.useExclusiveBind */ + private static String exclBindProp; + + /* True if exclusive binding is on for Windows */ + private static boolean exclusiveBind = true; + static { java.security.AccessController.doPrivileged( new PrivilegedAction() { public Object run() { @@ -62,6 +68,7 @@ version = Float.parseFloat(System.getProperties().getProperty("os.version")); preferIPv4Stack = Boolean.parseBoolean( System.getProperties().getProperty("java.net.preferIPv4Stack")); + exclBindProp = System.getProperty("sun.net.useExclusiveBind"); } catch (NumberFormatException e ) { assert false : e; } @@ -70,7 +77,15 @@ // (version >= 6.0) implies Vista or greater. if (version >= 6.0 && !preferIPv4Stack) { - useDualStackImpl = true; + useDualStackImpl = true; + } + + if (exclBindProp != null) { + // sun.net.useExclusiveBind is true + exclusiveBind = exclBindProp.length() == 0 ? true + : Boolean.parseBoolean(exclBindProp); + } else if (version < 6.0) { + exclusiveBind = false; } } @@ -79,9 +94,9 @@ */ PlainSocketImpl() { if (useDualStackImpl) { - impl = new DualStackPlainSocketImpl(); + impl = new DualStackPlainSocketImpl(exclusiveBind); } else { - impl = new TwoStacksPlainSocketImpl(); + impl = new TwoStacksPlainSocketImpl(exclusiveBind); } } @@ -90,9 +105,9 @@ */ PlainSocketImpl(FileDescriptor fd) { if (useDualStackImpl) { - impl = new DualStackPlainSocketImpl(fd); + impl = new DualStackPlainSocketImpl(fd, exclusiveBind); } else { - impl = new TwoStacksPlainSocketImpl(fd); + impl = new TwoStacksPlainSocketImpl(fd, exclusiveBind); } } diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java --- a/jdk/src/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -66,6 +66,22 @@ init(); } + // true if this socket is exclusively bound + private final boolean exclusiveBind; + + /* + * Set to true if SO_REUSEADDR is set after the socket is bound to + * indicate SO_REUSEADDR is being emulated + */ + private boolean reuseAddressEmulated; + + // emulates SO_REUSEADDR when exclusiveBind is true and socket is bound + private boolean isReuseAddress; + + TwoStacksPlainDatagramSocketImpl(boolean exclBind) { + exclusiveBind = exclBind; + } + protected synchronized void create() throws SocketException { fd1 = new FileDescriptor(); try { @@ -84,6 +100,14 @@ } } + @Override + protected synchronized void bind0(int lport, InetAddress laddr) + throws SocketException + { + bind0(lport, laddr, exclusiveBind); + + } + protected synchronized void receive(DatagramPacket p) throws IOException { try { @@ -104,8 +128,24 @@ } int family = connectedAddress == null ? -1 : connectedAddress.holder().getFamily(); return socketLocalAddress(family); - } else + } else if (optID == SO_REUSEADDR && reuseAddressEmulated) { + return isReuseAddress; + } else { return super.getOption(optID); + } + } + + protected void socketSetOption(int opt, Object val) + throws SocketException + { + if (opt == SO_REUSEADDR && exclusiveBind && localPort != 0) { + // socket already bound, emulate + reuseAddressEmulated = true; + isReuseAddress = (Boolean)val; + } else { + socketNativeSetOption(opt, val); + } + } protected boolean isClosed() { @@ -123,7 +163,8 @@ /* Native methods */ - protected synchronized native void bind0(int lport, InetAddress laddr) + protected synchronized native void bind0(int lport, InetAddress laddr, + boolean exclBind) throws SocketException; protected native void send(DatagramPacket p) throws IOException; @@ -155,7 +196,7 @@ protected native void datagramSocketClose(); - protected native void socketSetOption(int opt, Object val) + protected native void socketNativeSetOption(int opt, Object val) throws SocketException; protected native Object socketGetOption(int opt) throws SocketException; diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/classes/java/net/TwoStacksPlainSocketImpl.java --- a/jdk/src/windows/classes/java/net/TwoStacksPlainSocketImpl.java Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/classes/java/net/TwoStacksPlainSocketImpl.java Thu Mar 14 13:46:15 2013 -0700 @@ -66,14 +66,23 @@ */ private int lastfd = -1; + // true if this socket is exclusively bound + private final boolean exclusiveBind; + + // emulates SO_REUSEADDR when exclusiveBind is true + private boolean isReuseAddress; + static { initProto(); } - public TwoStacksPlainSocketImpl() {} + public TwoStacksPlainSocketImpl(boolean exclBind) { + exclusiveBind = exclBind; + } - public TwoStacksPlainSocketImpl(FileDescriptor fd) { + public TwoStacksPlainSocketImpl(FileDescriptor fd, boolean exclBind) { this.fd = fd; + exclusiveBind = exclBind; } /** @@ -116,13 +125,33 @@ InetAddressContainer in = new InetAddressContainer(); socketGetOption(opt, in); return in.addr; + } else if (opt == SO_REUSEADDR && exclusiveBind) { + // SO_REUSEADDR emulated when using exclusive bind + return isReuseAddress; } else return super.getOption(opt); } + @Override + void socketBind(InetAddress address, int port) throws IOException { + socketBind(address, port, exclusiveBind); + } + + @Override + void socketSetOption(int opt, boolean on, Object value) + throws SocketException + { + // SO_REUSEADDR emulated when using exclusive bind + if (opt == SO_REUSEADDR && exclusiveBind) + isReuseAddress = on; + else + socketNativeSetOption(opt, on, value); + } + /** * Closes the socket. */ + @Override protected void close() throws IOException { synchronized(fdLock) { if (fd != null || fd1 != null) { @@ -155,6 +184,7 @@ } } + @Override void reset() throws IOException { if (fd != null || fd1 != null) { socketClose(); @@ -167,6 +197,7 @@ /* * Return true if already closed or close is pending */ + @Override public boolean isClosedOrPending() { /* * Lock on fdLock to ensure that we wait if a @@ -190,7 +221,7 @@ native void socketConnect(InetAddress address, int port, int timeout) throws IOException; - native void socketBind(InetAddress address, int port) + native void socketBind(InetAddress address, int port, boolean exclBind) throws IOException; native void socketListen(int count) throws IOException; @@ -203,7 +234,7 @@ native void socketShutdown(int howto) throws IOException; - native void socketSetOption(int cmd, boolean on, Object value) + native void socketNativeSetOption(int cmd, boolean on, Object value) throws SocketException; native int socketGetOption(int opt, Object iaContainerObj) throws SocketException; diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/native/java/net/DualStackPlainDatagramSocketImpl.c --- a/jdk/src/windows/native/java/net/DualStackPlainDatagramSocketImpl.c Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/native/java/net/DualStackPlainDatagramSocketImpl.c Thu Mar 14 13:46:15 2013 -0700 @@ -113,7 +113,7 @@ * Signature: (ILjava/net/InetAddress;I)V */ JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketBind - (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) { + (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port, jboolean exclBind) { SOCKETADDRESS sa; int rv; int sa_len = sizeof(sa); @@ -122,8 +122,7 @@ &sa_len, JNI_TRUE) != 0) { return; } - - rv = bind(fd, (struct sockaddr *)&sa, sa_len); + rv = NET_WinBind(fd, (struct sockaddr *)&sa, sa_len, exclBind); if (rv == SOCKET_ERROR) { if (WSAGetLastError() == WSAEACCES) { diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/native/java/net/DualStackPlainSocketImpl.c --- a/jdk/src/windows/native/java/net/DualStackPlainSocketImpl.c Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/native/java/net/DualStackPlainSocketImpl.c Thu Mar 14 13:46:15 2013 -0700 @@ -82,7 +82,9 @@ * Signature: (ILjava/net/InetAddress;I)V */ JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_bind0 - (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) { + (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port, + jboolean exclBind) +{ SOCKETADDRESS sa; int rv; int sa_len = sizeof(sa); @@ -92,7 +94,7 @@ return; } - rv = NET_Bind(fd, (struct sockaddr *)&sa, sa_len); + rv = NET_WinBind(fd, (struct sockaddr *)&sa, sa_len, exclBind); if (rv == SOCKET_ERROR) NET_ThrowNew(env, WSAGetLastError(), "JVM_Bind"); diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c --- a/jdk/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c Thu Mar 14 13:46:15 2013 -0700 @@ -421,7 +421,8 @@ JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this, - jint port, jobject addressObj) { + jint port, jobject addressObj, + jboolean exclBind) { jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); @@ -464,7 +465,7 @@ v6bind.addr = &lcladdr; v6bind.ipv4_fd = fd; v6bind.ipv6_fd = fd1; - if (NET_BindV6(&v6bind) != -1) { + if (NET_BindV6(&v6bind, exclBind) != -1) { /* check if the fds have changed */ if (v6bind.ipv4_fd != fd) { fd = v6bind.ipv4_fd; @@ -491,7 +492,7 @@ return; } } else { - if (bind(fd, (struct sockaddr *)&lcladdr, lcladdrlen) == -1) { + if (NET_WinBind(fd, (struct sockaddr *)&lcladdr, lcladdrlen, exclBind) == -1) { if (WSAGetLastError() == WSAEACCES) { WSASetLastError(WSAEADDRINUSE); } @@ -1776,11 +1777,11 @@ /* * Class: java_net_TwoStacksPlainDatagramSocketImpl - * Method: socketSetOption + * Method: socketNativeSetOption * Signature: (ILjava/lang/Object;)V */ JNIEXPORT void JNICALL -Java_java_net_TwoStacksPlainDatagramSocketImpl_socketSetOption(JNIEnv *env,jobject this, +Java_java_net_TwoStacksPlainDatagramSocketImpl_socketNativeSetOption(JNIEnv *env,jobject this, jint opt,jobject value) { int fd=-1, fd1=-1; diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/native/java/net/TwoStacksPlainSocketImpl.c --- a/jdk/src/windows/native/java/net/TwoStacksPlainSocketImpl.c Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/native/java/net/TwoStacksPlainSocketImpl.c Thu Mar 14 13:46:15 2013 -0700 @@ -394,7 +394,8 @@ */ JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_socketBind(JNIEnv *env, jobject this, - jobject iaObj, jint localport) { + jobject iaObj, jint localport, + jboolean exclBind) { /* fdObj is the FileDescriptor field on this */ jobject fdObj, fd1Obj; @@ -438,13 +439,12 @@ (struct sockaddr *)&him, &len, JNI_FALSE) != 0) { return; } - if (ipv6_supported) { struct ipv6bind v6bind; v6bind.addr = &him; v6bind.ipv4_fd = fd; v6bind.ipv6_fd = fd1; - rv = NET_BindV6(&v6bind); + rv = NET_BindV6(&v6bind, exclBind); if (rv != -1) { /* check if the fds have changed */ if (v6bind.ipv4_fd != fd) { @@ -469,7 +469,7 @@ } } } else { - rv = NET_Bind(fd, (struct sockaddr *)&him, len); + rv = NET_WinBind(fd, (struct sockaddr *)&him, len, exclBind); } if (rv == -1) { @@ -835,11 +835,12 @@ * * * Class: java_net_TwoStacksPlainSocketImpl - * Method: socketSetOption + * Method: socketNativeSetOption * Signature: (IZLjava/lang/Object;)V */ JNIEXPORT void JNICALL -Java_java_net_TwoStacksPlainSocketImpl_socketSetOption(JNIEnv *env, jobject this, +Java_java_net_TwoStacksPlainSocketImpl_socketNativeSetOption(JNIEnv *env, + jobject this, jint cmd, jboolean on, jobject value) { int fd, fd1; diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/native/java/net/net_util_md.c --- a/jdk/src/windows/native/java/net/net_util_md.c Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/native/java/net/net_util_md.c Thu Mar 14 13:46:15 2013 -0700 @@ -387,12 +387,24 @@ int optlen) { int rv; + int parg; + int plen = sizeof(parg); if (level == IPPROTO_IP && optname == IP_TOS) { int *tos = (int *)optval; *tos &= (IPTOS_TOS_MASK | IPTOS_PREC_MASK); } + if (optname == SO_REUSEADDR) { + /* + * Do not set SO_REUSEADDE if SO_EXCLUSIVEADDUSE is already set + */ + rv = NET_GetSockOpt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&parg, &plen); + if (rv == 0 && parg == 1) { + return rv; + } + } + rv = setsockopt(s, level, optname, optval, optlen); if (rv == SOCKET_ERROR) { @@ -456,15 +468,32 @@ } /* + * Sets SO_ECLUSIVEADDRUSE if SO_REUSEADDR is not already set. + */ +void setExclusiveBind(int fd) { + int parg; + int plen = sizeof(parg); + int rv = 0; + rv = NET_GetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&parg, &plen); + if (rv == 0 && parg == 0) { + parg = 1; + rv = NET_SetSockOpt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&parg, plen); + } +} + +/* * Wrapper for bind winsock call - transparent converts an * error related to binding to a port that has exclusive access * into an error indicating the port is in use (facilitates * better error reporting). + * + * Should be only called by the wrapper method NET_WinBind */ JNIEXPORT int JNICALL NET_Bind(int s, struct sockaddr *him, int len) { - int rv = bind(s, him, len); + int rv; + rv = bind(s, him, len); if (rv == SOCKET_ERROR) { /* @@ -479,6 +508,18 @@ return rv; } +/* + * Wrapper for NET_Bind call. Sets SO_EXCLUSIVEADDRUSE + * if required, and then calls NET_BIND + */ +JNIEXPORT int JNICALL +NET_WinBind(int s, struct sockaddr *him, int len, jboolean exclBind) +{ + if (exclBind == JNI_TRUE) + setExclusiveBind(s); + return NET_Bind(s, him, len); +} + JNIEXPORT int JNICALL NET_SocketClose(int fd) { struct linger l; @@ -625,7 +666,7 @@ */ JNIEXPORT int JNICALL -NET_BindV6(struct ipv6bind* b) { +NET_BindV6(struct ipv6bind* b, jboolean exclBind) { int fd=-1, ofd=-1, rv, len; /* need to defer close until new sockets created */ int close_fd=-1, close_ofd=-1; @@ -638,8 +679,8 @@ if (family == AF_INET && (b->addr->him4.sin_addr.s_addr != INADDR_ANY)) { /* bind to v4 only */ int ret; - ret = NET_Bind ((int)b->ipv4_fd, (struct sockaddr *)b->addr, - sizeof (struct sockaddr_in)); + ret = NET_WinBind ((int)b->ipv4_fd, (struct sockaddr *)b->addr, + sizeof (struct sockaddr_in), exclBind); if (ret == SOCKET_ERROR) { CLOSE_SOCKETS_AND_RETURN; } @@ -650,8 +691,8 @@ if (family == AF_INET6 && (!IN6_IS_ADDR_ANY(&b->addr->him6.sin6_addr))) { /* bind to v6 only */ int ret; - ret = NET_Bind ((int)b->ipv6_fd, (struct sockaddr *)b->addr, - sizeof (struct SOCKADDR_IN6)); + ret = NET_WinBind ((int)b->ipv6_fd, (struct sockaddr *)b->addr, + sizeof (struct SOCKADDR_IN6), exclBind); if (ret == SOCKET_ERROR) { CLOSE_SOCKETS_AND_RETURN; } @@ -680,7 +721,7 @@ oaddr.him4.sin_addr.s_addr = INADDR_ANY; } - rv = NET_Bind (fd, (struct sockaddr *)b->addr, SOCKETADDRESS_LEN(b->addr)); + rv = NET_WinBind(fd, (struct sockaddr *)b->addr, SOCKETADDRESS_LEN(b->addr), exclBind); if (rv == SOCKET_ERROR) { CLOSE_SOCKETS_AND_RETURN; } @@ -692,8 +733,8 @@ } bound_port = GET_PORT (b->addr); SET_PORT (&oaddr, bound_port); - if ((rv=NET_Bind (ofd, (struct sockaddr *) &oaddr, - SOCKETADDRESS_LEN (&oaddr))) == SOCKET_ERROR) { + if ((rv=NET_WinBind (ofd, (struct sockaddr *) &oaddr, + SOCKETADDRESS_LEN (&oaddr), exclBind)) == SOCKET_ERROR) { int retries; int sotype, arglen=sizeof(sotype); @@ -729,7 +770,8 @@ /* bind random port on first socket */ SET_PORT (&oaddr, 0); - rv = NET_Bind (ofd, (struct sockaddr *)&oaddr, SOCKETADDRESS_LEN(&oaddr)); + rv = NET_WinBind (ofd, (struct sockaddr *)&oaddr, SOCKETADDRESS_LEN(&oaddr), + exclBind); if (rv == SOCKET_ERROR) { CLOSE_SOCKETS_AND_RETURN; } @@ -745,7 +787,8 @@ } bound_port = GET_PORT (&oaddr); SET_PORT (b->addr, bound_port); - rv = NET_Bind (fd, (struct sockaddr *)b->addr, SOCKETADDRESS_LEN(b->addr)); + rv = NET_WinBind (fd, (struct sockaddr *)b->addr, SOCKETADDRESS_LEN(b->addr), + exclBind); if (rv != SOCKET_ERROR) { if (family == AF_INET) { diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/native/java/net/net_util_md.h --- a/jdk/src/windows/native/java/net/net_util_md.h Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/native/java/net/net_util_md.h Thu Mar 14 13:46:15 2013 -0700 @@ -311,7 +311,7 @@ */ JNIEXPORT int JNICALL NET_Timeout2(int fd, int fd1, long timeout, int *fdret); -JNIEXPORT int JNICALL NET_BindV6(struct ipv6bind* b); +JNIEXPORT int JNICALL NET_BindV6(struct ipv6bind* b, jboolean exclBind); #define NET_WAIT_READ 0x01 #define NET_WAIT_WRITE 0x02 @@ -319,6 +319,9 @@ extern jint NET_Wait(JNIEnv *env, jint fd, jint flags, jint timeout); +JNIEXPORT int JNICALL NET_WinBind(int s, struct sockaddr *him, int len, + jboolean exclBind); + /* XP versions of the native routines */ JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0_XP diff -r be617b8c4427 -r fa6bd0992104 jdk/src/windows/native/sun/nio/ch/Net.c --- a/jdk/src/windows/native/sun/nio/ch/Net.c Thu Mar 14 14:45:55 2013 +0100 +++ b/jdk/src/windows/native/sun/nio/ch/Net.c Thu Mar 14 13:46:15 2013 -0700 @@ -101,6 +101,18 @@ return JNI_FALSE; } +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) { + OSVERSIONINFO ver; + int version; + ver.dwOSVersionInfoSize = sizeof(ver); + GetVersionEx(&ver); + version = ver.dwMajorVersion * 10 + ver.dwMinorVersion; + //if os <= xp exclusive binding is off by default + return version >= 60 ? 1 : 0; +} + + JNIEXPORT jboolean JNICALL Java_sun_nio_ch_Net_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl) { @@ -144,8 +156,8 @@ } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jboolean preferIPv6, - jobject fdo, jobject iao, jint port) +Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6, + jboolean isExclBind, jobject iao, jint port) { SOCKETADDRESS sa; int rv; @@ -155,7 +167,7 @@ return; } - rv = NET_Bind(fdval(env, fdo), (struct sockaddr *)&sa, sa_len); + rv = NET_WinBind(fdval(env, fdo), (struct sockaddr *)&sa, sa_len, isExclBind); if (rv == SOCKET_ERROR) NET_ThrowNew(env, WSAGetLastError(), "bind"); }