diff --git a/make/java/nio/mapfile-bsd b/make/java/nio/mapfile-bsd index 4093a0023294053677cf34065521d41688c3860d..e5a92d93f180db61cbf535cbe11aa8bc58fc0c99 100644 --- a/make/java/nio/mapfile-bsd +++ b/make/java/nio/mapfile-bsd @@ -109,6 +109,7 @@ SUNWprivate_1.1 { 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 --git a/make/java/nio/mapfile-linux b/make/java/nio/mapfile-linux index 92c7d318894ded0b9951a7c69f7253ca4d8eb9aa..e85bafae76dbdfb13ee00fe3fcade659d1009506 100644 --- a/make/java/nio/mapfile-linux +++ b/make/java/nio/mapfile-linux @@ -117,6 +117,7 @@ SUNWprivate_1.1 { 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 --git a/make/java/nio/mapfile-solaris b/make/java/nio/mapfile-solaris index 98deb510dcb1ba4452603afc91586bec8f61f8a6..3a610edd9e74f7ff2e533e16ca8710116b158cb2 100644 --- a/make/java/nio/mapfile-solaris +++ b/make/java/nio/mapfile-solaris @@ -105,6 +105,7 @@ SUNWprivate_1.1 { 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 --git a/makefiles/mapfiles/libnio/mapfile-linux b/makefiles/mapfiles/libnio/mapfile-linux index 92c7d318894ded0b9951a7c69f7253ca4d8eb9aa..e85bafae76dbdfb13ee00fe3fcade659d1009506 100644 --- a/makefiles/mapfiles/libnio/mapfile-linux +++ b/makefiles/mapfiles/libnio/mapfile-linux @@ -117,6 +117,7 @@ SUNWprivate_1.1 { 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 --git a/makefiles/mapfiles/libnio/mapfile-macosx b/makefiles/mapfiles/libnio/mapfile-macosx index 4093a0023294053677cf34065521d41688c3860d..e5a92d93f180db61cbf535cbe11aa8bc58fc0c99 100644 --- a/makefiles/mapfiles/libnio/mapfile-macosx +++ b/makefiles/mapfiles/libnio/mapfile-macosx @@ -109,6 +109,7 @@ SUNWprivate_1.1 { 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 --git a/makefiles/mapfiles/libnio/mapfile-solaris b/makefiles/mapfiles/libnio/mapfile-solaris index 98deb510dcb1ba4452603afc91586bec8f61f8a6..3a610edd9e74f7ff2e533e16ca8710116b158cb2 100644 --- a/makefiles/mapfiles/libnio/mapfile-solaris +++ b/makefiles/mapfiles/libnio/mapfile-solaris @@ -105,6 +105,7 @@ SUNWprivate_1.1 { 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 --git a/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java b/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java index 729faf8d1b808f04e343c410f9012f367cab13ab..09965831d5b13b4daa317dd49833372d53705090 100644 --- a/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java +++ b/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java @@ -63,6 +63,8 @@ abstract class AsynchronousServerSocketChannelImpl // 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 @@ abstract class AsynchronousServerSocketChannelImpl 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 @@ abstract class AsynchronousServerSocketChannelImpl 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 --git a/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java b/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java index 6ba52273a6de7b49c08cfaafad9ae16b325d1203..52c7ab28c00cf7e5208f937f0fbc56c1e1127828 100644 --- a/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java +++ b/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java @@ -79,6 +79,9 @@ abstract class AsynchronousSocketChannelImpl 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 @@ abstract class AsynchronousSocketChannelImpl 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 @@ abstract class AsynchronousSocketChannelImpl 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 --git a/src/share/classes/sun/nio/ch/DatagramChannelImpl.java b/src/share/classes/sun/nio/ch/DatagramChannelImpl.java index 4e41a9aa1b80bd715e023c842f6873596481e535..9ecdd43f25ccf255bb655a8563dfb08ba1e01cf9 100644 --- a/src/share/classes/sun/nio/ch/DatagramChannelImpl.java +++ b/src/share/classes/sun/nio/ch/DatagramChannelImpl.java @@ -94,6 +94,12 @@ class DatagramChannelImpl // 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 @@ class DatagramChannelImpl } 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 @@ class DatagramChannelImpl } } + if (name == StandardSocketOptions.SO_REUSEADDR && + reuseAddressEmulated) + { + return (T)Boolean.valueOf(isReuseAddress); + } + // no special handling return (T) Net.getSocketOption(fd, Net.UNSPEC, name); } diff --git a/src/share/classes/sun/nio/ch/Net.java b/src/share/classes/sun/nio/ch/Net.java index 486cc838debd958dc73a44ffb1b3e0d2763ca220..bb0e93bda3934730887e88643fef23a9c0d2eee3 100644 --- a/src/share/classes/sun/nio/ch/Net.java +++ b/src/share/classes/sun/nio/ch/Net.java @@ -44,6 +44,34 @@ public class Net { } }; + // 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; @@ -60,6 +88,13 @@ public class Net { return isIPv6Available; } + /** + * Returns true if exclusive binding is on + */ + static boolean useExclusiveBind() { + return exclusiveBind; + } + /** * Tells whether IPv6 sockets can join IPv4 multicast groups */ @@ -308,6 +343,12 @@ public class Net { 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 @@ public class Net { { 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 --git a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java index 728d865b8784b901f07f788a3617404225dd0e3d..9bba345bafe953c2e6e3dfa9ebff4da2ce1f2edd 100644 --- a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java +++ b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java @@ -74,6 +74,9 @@ class ServerSocketChannelImpl // 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 @@ class ServerSocketChannelImpl 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 @@ class ServerSocketChannelImpl 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 --git a/src/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/share/classes/sun/nio/ch/SocketChannelImpl.java index 22be8fb6efdd2a946f0d0e525af7ade55896b20b..c3daa58d5a600de6c606ad9a52c22902c09c0e5a 100644 --- a/src/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -70,6 +70,9 @@ class SocketChannelImpl // -- 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 @@ class SocketChannelImpl 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 @@ class SocketChannelImpl 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 --git a/src/solaris/native/sun/nio/ch/Net.c b/src/solaris/native/sun/nio/ch/Net.c index f37fe88b18e5c4801f9d19596bb5904bd49c771f..0163fb6dc9f2bef14c7eccf0255305b3d87879a1 100644 --- a/src/solaris/native/sun/nio/ch/Net.c +++ b/src/solaris/native/sun/nio/ch/Net.c @@ -124,6 +124,11 @@ Java_sun_nio_ch_Net_isIPv6Available0(JNIEnv* env, jclass cl) 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 @@ Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, } 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 --git a/src/windows/classes/java/net/DefaultDatagramSocketImplFactory.java b/src/windows/classes/java/net/DefaultDatagramSocketImplFactory.java index 43ce6f27744005690d6a630ae24968882bccdad2..2d319fa5561714b0de7a8782b400ee8231040cd6 100644 --- a/src/windows/classes/java/net/DefaultDatagramSocketImplFactory.java +++ b/src/windows/classes/java/net/DefaultDatagramSocketImplFactory.java @@ -56,24 +56,45 @@ class DefaultDatagramSocketImplFactory /* 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 @@ class DefaultDatagramSocketImplFactory 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 --git a/src/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java b/src/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java index 089278b66c7f59d1acf509ed69220c521c8453a9..f94afd1788198d733d885f4759f61879b206bc72 100644 --- a/src/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java +++ b/src/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java @@ -45,6 +45,22 @@ class DualStackPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl { 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 @@ class DualStackPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl 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 @@ class DualStackPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl fdAccess.set(fd, -1); } + @SuppressWarnings("fallthrough") protected void socketSetOption(int opt, Object val) throws SocketException { int nativefd = checkAndReturnNativeFD(); @@ -153,6 +170,13 @@ class DualStackPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl 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 @@ class DualStackPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl 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 @@ class DualStackPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl 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 --git a/src/windows/classes/java/net/DualStackPlainSocketImpl.java b/src/windows/classes/java/net/DualStackPlainSocketImpl.java index 54121b463409941f360c6ee69bfcb6ea6b696426..6163dd32d42d04a1f78f2297f482d4020b5d5ee3 100644 --- a/src/windows/classes/java/net/DualStackPlainSocketImpl.java +++ b/src/windows/classes/java/net/DualStackPlainSocketImpl.java @@ -42,10 +42,20 @@ class DualStackPlainSocketImpl extends AbstractPlainSocketImpl { static JavaIOFileDescriptorAccess fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess(); - public DualStackPlainSocketImpl() {} - public DualStackPlainSocketImpl(FileDescriptor fd) { + // true if this socket is exclusively bound + private final boolean exclusiveBind; + + // emulates SO_REUSEADDR when exclusiveBind is true + private boolean isReuseAddress; + + 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 @@ class DualStackPlainSocketImpl extends AbstractPlainSocketImpl 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 @@ class DualStackPlainSocketImpl extends AbstractPlainSocketImpl 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 @@ class DualStackPlainSocketImpl extends AbstractPlainSocketImpl switch(opt) { case TCP_NODELAY : case SO_OOBINLINE : - case SO_KEEPALIVE : case SO_REUSEADDR : + if (exclusiveBind) { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = on; + return; + } + case SO_KEEPALIVE : optionValue = on ? 1 : 0; break; case SO_SNDBUF : @@ -207,6 +224,10 @@ class DualStackPlainSocketImpl extends AbstractPlainSocketImpl 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 @@ class DualStackPlainSocketImpl extends AbstractPlainSocketImpl 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 --git a/src/windows/classes/java/net/PlainSocketImpl.java b/src/windows/classes/java/net/PlainSocketImpl.java index bcb01f1846be53d69602b0695ea8da1b728f7cd7..1b6287d47f5f38f8f0b5ef8082026acfebc7c888 100644 --- a/src/windows/classes/java/net/PlainSocketImpl.java +++ b/src/windows/classes/java/net/PlainSocketImpl.java @@ -54,6 +54,12 @@ class PlainSocketImpl extends AbstractPlainSocketImpl /* 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 @@ class PlainSocketImpl extends AbstractPlainSocketImpl 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 @@ class PlainSocketImpl extends AbstractPlainSocketImpl // (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 @@ class PlainSocketImpl extends AbstractPlainSocketImpl */ PlainSocketImpl() { if (useDualStackImpl) { - impl = new DualStackPlainSocketImpl(); + impl = new DualStackPlainSocketImpl(exclusiveBind); } else { - impl = new TwoStacksPlainSocketImpl(); + impl = new TwoStacksPlainSocketImpl(exclusiveBind); } } @@ -90,9 +105,9 @@ class PlainSocketImpl extends AbstractPlainSocketImpl */ 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 --git a/src/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java b/src/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java index 2d89d601869460847b915196f963312ae5975df9..b1672907dcbff171bdd4ed1047003a3036bcd5ac 100644 --- a/src/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java +++ b/src/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java @@ -66,6 +66,22 @@ class TwoStacksPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl 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 @@ class TwoStacksPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl } } + @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 @@ class TwoStacksPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl } 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 @@ class TwoStacksPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl /* 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 @@ class TwoStacksPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl 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 --git a/src/windows/classes/java/net/TwoStacksPlainSocketImpl.java b/src/windows/classes/java/net/TwoStacksPlainSocketImpl.java index 6ca25ec6021333c863da5fcf51033d6a3608b928..1f2b68d97f638aadf884f8fa257e988bc79c0a0b 100644 --- a/src/windows/classes/java/net/TwoStacksPlainSocketImpl.java +++ b/src/windows/classes/java/net/TwoStacksPlainSocketImpl.java @@ -66,14 +66,23 @@ class TwoStacksPlainSocketImpl extends AbstractPlainSocketImpl */ 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 @@ class TwoStacksPlainSocketImpl extends AbstractPlainSocketImpl 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 @@ class TwoStacksPlainSocketImpl extends AbstractPlainSocketImpl } } + @Override void reset() throws IOException { if (fd != null || fd1 != null) { socketClose(); @@ -167,6 +197,7 @@ class TwoStacksPlainSocketImpl extends AbstractPlainSocketImpl /* * 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 @@ class TwoStacksPlainSocketImpl extends AbstractPlainSocketImpl 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 @@ class TwoStacksPlainSocketImpl extends AbstractPlainSocketImpl 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 --git a/src/windows/native/java/net/DualStackPlainDatagramSocketImpl.c b/src/windows/native/java/net/DualStackPlainDatagramSocketImpl.c index cdd969cef793a003ebca75a89a672f960671a26c..1c72465f11211c78057f04ffeacc7b264b755711 100644 --- a/src/windows/native/java/net/DualStackPlainDatagramSocketImpl.c +++ b/src/windows/native/java/net/DualStackPlainDatagramSocketImpl.c @@ -113,7 +113,7 @@ JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketCrea * 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 @@ JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketBind &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 --git a/src/windows/native/java/net/DualStackPlainSocketImpl.c b/src/windows/native/java/net/DualStackPlainSocketImpl.c index 5f22e887d00d661d40f735cd188ad0b89ebd3062..2c796edab1bd2595e21fdbb3c9c79c6c775c27ed 100644 --- a/src/windows/native/java/net/DualStackPlainSocketImpl.c +++ b/src/windows/native/java/net/DualStackPlainSocketImpl.c @@ -82,7 +82,9 @@ JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_socket0 * 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 @@ JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_bind0 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 --git a/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c b/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c index e81f408b3c4707aad20188126724f1f44edcb593..c18066e31323fc251a3283381a8c80b09dd3d426 100644 --- a/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c +++ b/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c @@ -421,7 +421,8 @@ Java_java_net_TwoStacksPlainDatagramSocketImpl_init(JNIEnv *env, jclass cls) { 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 @@ Java_java_net_TwoStacksPlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this, 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 @@ Java_java_net_TwoStacksPlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this, 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 @@ static void setMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, /* * 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 --git a/src/windows/native/java/net/TwoStacksPlainSocketImpl.c b/src/windows/native/java/net/TwoStacksPlainSocketImpl.c index 73a799f6055fde4162d8578ae8b33cde4beb55bf..6823ddc7c7c538100d0ce9ee3d958fc4c5877541 100644 --- a/src/windows/native/java/net/TwoStacksPlainSocketImpl.c +++ b/src/windows/native/java/net/TwoStacksPlainSocketImpl.c @@ -394,7 +394,8 @@ Java_java_net_TwoStacksPlainSocketImpl_socketConnect(JNIEnv *env, jobject this, */ 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 @@ Java_java_net_TwoStacksPlainSocketImpl_socketBind(JNIEnv *env, jobject this, (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 @@ Java_java_net_TwoStacksPlainSocketImpl_socketBind(JNIEnv *env, jobject this, } } } 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 @@ Java_java_net_TwoStacksPlainSocketImpl_socketClose0(JNIEnv *env, jobject this, * * * 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 --git a/src/windows/native/java/net/net_util_md.c b/src/windows/native/java/net/net_util_md.c index 99629ba85ac01b44f8e03c848241df3b6520097e..4e5cb50b0bc0f25e095204c7a9a643b410efcb93 100644 --- a/src/windows/native/java/net/net_util_md.c +++ b/src/windows/native/java/net/net_util_md.c @@ -387,12 +387,24 @@ NET_SetSockOpt(int s, int level, int optname, const void *optval, 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) { @@ -455,16 +467,33 @@ NET_GetSockOpt(int s, int level, int optname, void *optval, return rv; } +/* + * 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 @@ NET_Bind(int s, struct sockaddr *him, int len) 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 @@ void dumpAddr (char *str, void *addr) { */ 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 @@ NET_BindV6(struct ipv6bind* b) { 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 @@ NET_BindV6(struct ipv6bind* b) { 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 @@ NET_BindV6(struct ipv6bind* b) { 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 @@ NET_BindV6(struct ipv6bind* b) { } 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 @@ NET_BindV6(struct ipv6bind* b) { /* 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 @@ NET_BindV6(struct ipv6bind* b) { } 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 --git a/src/windows/native/java/net/net_util_md.h b/src/windows/native/java/net/net_util_md.h index 50a275534bfc55429c2f7c6a002d65a0414d2427..6964d9dc3b5daa7d8781716c18d8559f5c78ecd6 100644 --- a/src/windows/native/java/net/net_util_md.h +++ b/src/windows/native/java/net/net_util_md.h @@ -311,7 +311,7 @@ void NET_ThrowSocketException(JNIEnv *env, char* msg); */ 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 @@ JNIEXPORT int JNICALL NET_BindV6(struct ipv6bind* b); 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 --git a/src/windows/native/sun/nio/ch/Net.c b/src/windows/native/sun/nio/ch/Net.c index 8d515e88471b17bee98d28ab28139c1c99d99ec9..733405e6f8004a32192f90b9583aedf0a97f92c9 100644 --- a/src/windows/native/sun/nio/ch/Net.c +++ b/src/windows/native/sun/nio/ch/Net.c @@ -101,6 +101,18 @@ Java_sun_nio_ch_Net_isIPv6Available0(JNIEnv* env, jclass cl) 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 @@ Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, } 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 @@ Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jboolean preferIPv6, 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"); }