7170730: Improve Windows network stack support.
Summary: Enable exclusive binding of ports on Windows
Reviewed-by: alanb, chegar, ahgross
--- 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;
--- 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;
--- 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;
--- 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;
--- 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;
--- 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;
--- 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();
--- 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();
--- 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);
}
--- 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<String>() {
+ @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;
--- 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);
}
--- 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) :
--- 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;
--- 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<Object>() {
- 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<Object>() {
+ 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);
}
}
}
--- 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;
--- 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)
--- 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<Object>() {
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);
}
}
--- 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;
--- 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;
--- 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) {
--- 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");
--- 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;
--- 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;
--- 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) {
--- 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
--- 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");
}