8233435: (dc) DatagramChannel should allow IPv6 socket join IPv4 multicast groups (macOS, win)

Reviewed-by: dfuchs
This commit is contained in:
Alan Bateman 2019-11-09 11:48:37 +00:00
parent 690b960c8f
commit 090dc51a4e
6 changed files with 185 additions and 66 deletions

View file

@ -239,6 +239,40 @@ class DatagramChannelImpl
}
}
/**
* Returns the protocol family to specify to set/getSocketOption for the
* given socket option.
*/
private ProtocolFamily familyFor(SocketOption<?> name) {
assert Thread.holdsLock(stateLock);
// unspecified (most options)
if (SocketOptionRegistry.findOption(name, Net.UNSPEC) != null)
return Net.UNSPEC;
// IPv4 socket
if (family == StandardProtocolFamily.INET)
return StandardProtocolFamily.INET;
// IPv6 socket that is unbound
if (localAddress == null)
return StandardProtocolFamily.INET6;
// IPv6 socket bound to wildcard or IPv6 address
InetAddress address = localAddress.getAddress();
if (address.isAnyLocalAddress() || (address instanceof Inet6Address))
return StandardProtocolFamily.INET6;
// IPv6 socket bound to IPv4 address
if (Net.canUseIPv6OptionsWithIPv4LocalAddress()) {
// IPV6_XXX options can be used
return StandardProtocolFamily.INET6;
} else {
// IPV6_XXX options cannot be used
return StandardProtocolFamily.INET;
}
}
@Override
public <T> DatagramChannel setOption(SocketOption<T> name, T value)
throws IOException
@ -252,14 +286,7 @@ class DatagramChannelImpl
synchronized (stateLock) {
ensureOpen();
if (name == StandardSocketOptions.IP_TOS ||
name == StandardSocketOptions.IP_MULTICAST_TTL ||
name == StandardSocketOptions.IP_MULTICAST_LOOP)
{
// options are protocol dependent
Net.setSocketOption(fd, family, name, value);
return this;
}
ProtocolFamily family = familyFor(name);
if (name == StandardSocketOptions.IP_MULTICAST_IF) {
NetworkInterface interf = (NetworkInterface)value;
@ -285,7 +312,7 @@ class DatagramChannelImpl
}
// remaining options don't need any special handling
Net.setSocketOption(fd, Net.UNSPEC, name, value);
Net.setSocketOption(fd, family, name, value);
return this;
}
}
@ -302,12 +329,7 @@ class DatagramChannelImpl
synchronized (stateLock) {
ensureOpen();
if (name == StandardSocketOptions.IP_TOS ||
name == StandardSocketOptions.IP_MULTICAST_TTL ||
name == StandardSocketOptions.IP_MULTICAST_LOOP)
{
return (T) Net.getSocketOption(fd, family, name);
}
ProtocolFamily family = familyFor(name);
if (name == StandardSocketOptions.IP_MULTICAST_IF) {
if (family == StandardProtocolFamily.INET) {
@ -333,11 +355,11 @@ class DatagramChannelImpl
}
if (name == StandardSocketOptions.SO_REUSEADDR && reuseAddressEmulated) {
return (T)Boolean.valueOf(isReuseAddress);
return (T) Boolean.valueOf(isReuseAddress);
}
// no special handling
return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
return (T) Net.getSocketOption(fd, family, name);
}
}

View file

@ -121,6 +121,14 @@ public class Net {
return canJoin6WithIPv4Group0();
}
/**
* Tells whether IPV6_XXX socket options should be used on an IPv6 socket
* that is bound to an IPv4 address.
*/
static boolean canUseIPv6OptionsWithIPv4LocalAddress() {
return canUseIPv6OptionsWithIPv4LocalAddress0();
}
public static InetSocketAddress checkAddress(SocketAddress sa) {
if (sa == null)
throw new NullPointerException();
@ -434,6 +442,8 @@ public class Net {
private static native boolean canJoin6WithIPv4Group0();
private static native boolean canUseIPv6OptionsWithIPv4LocalAddress0();
static FileDescriptor socket(boolean stream) throws IOException {
return socket(UNSPEC, stream);
}

View file

@ -158,24 +158,34 @@ Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) {
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_Net_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl)
{
#if defined(__APPLE__) || defined(_AIX)
/* for now IPv6 sockets cannot join IPv4 multicast groups */
return JNI_FALSE;
#else
#if defined(__linux__) || defined(__APPLE__) || defined(__solaris__)
/* IPv6 sockets can join IPv4 multicast groups */
return JNI_TRUE;
#else
/* IPv6 sockets cannot join IPv4 multicast groups */
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_Net_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl)
{
#ifdef __solaris__
#if defined(__APPLE__) || defined(__solaris__)
/* IPV6_ADD_MEMBERSHIP can be used to join IPv4 multicast groups */
return JNI_TRUE;
#else
/* IPV6_ADD_MEMBERSHIP cannot be used to join IPv4 multicast groups */
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_Net_canUseIPv6OptionsWithIPv4LocalAddress0(JNIEnv* env, jclass cl)
{
/* IPV6_XXX socket options can be used on IPv6 sockets bound to IPv4 address */
return JNI_TRUE;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6,
jboolean stream, jboolean reuse, jboolean ignored)

View file

@ -127,12 +127,21 @@ Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) {
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_Net_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl)
{
return JNI_FALSE;
/* IPv6 sockets can join IPv4 multicast groups */
return JNI_TRUE;
}
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_Net_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl)
{
/* IPV6_ADD_MEMBERSHIP cannot be used to join IPv4 multicast groups */
return JNI_FALSE;
}
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_Net_canUseIPv6OptionsWithIPv4LocalAddress0(JNIEnv* env, jclass cl)
{
/* IPV6_XXX socket options cannot be used on IPv6 sockets bound to IPv4 address */
return JNI_FALSE;
}
@ -279,7 +288,7 @@ Java_sun_nio_ch_Net_localPort(JNIEnv *env, jclass clazz, jobject fdo)
SOCKETADDRESS sa;
int sa_len = sizeof(sa);
if (getsockname(fdval(env, fdo), &sa.sa, &sa_len) < 0) {
if (getsockname(fdval(env, fdo), &sa.sa, &sa_len) == SOCKET_ERROR) {
int error = WSAGetLastError();
if (error == WSAEINVAL) {
return 0;
@ -297,7 +306,7 @@ Java_sun_nio_ch_Net_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo)
int sa_len = sizeof(sa);
int port;
if (getsockname(fdval(env, fdo), &sa.sa, &sa_len) < 0) {
if (getsockname(fdval(env, fdo), &sa.sa, &sa_len) == SOCKET_ERROR) {
NET_ThrowNew(env, WSAGetLastError(), "getsockname");
return NULL;
}
@ -310,7 +319,7 @@ Java_sun_nio_ch_Net_remotePort(JNIEnv *env, jclass clazz, jobject fdo)
SOCKETADDRESS sa;
int sa_len = sizeof(sa);
if (getpeername(fdval(env, fdo), &sa.sa, &sa_len) < 0) {
if (getpeername(fdval(env, fdo), &sa.sa, &sa_len) == SOCKET_ERROR) {
int error = WSAGetLastError();
if (error == WSAEINVAL) {
return 0;
@ -328,7 +337,7 @@ Java_sun_nio_ch_Net_remoteInetAddress(JNIEnv *env, jclass clazz, jobject fdo)
int sa_len = sizeof(sa);
int port;
if (getpeername(fdval(env, fdo), &sa.sa, &sa_len) < 0) {
if (getpeername(fdval(env, fdo), &sa.sa, &sa_len) == SOCKET_ERROR) {
NET_ThrowNew(env, WSAGetLastError(), "getsockname");
return NULL;
}
@ -366,7 +375,7 @@ Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo,
} else {
n = getsockopt(fdval(env, fdo), level, opt, arg, &arglen);
}
if (n < 0) {
if (n == SOCKET_ERROR) {
handleSocketError(env, WSAGetLastError());
return IOS_THROWN;
}
@ -410,7 +419,7 @@ Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo,
} else {
n = setsockopt(fdval(env, fdo), level, opt, parg, arglen);
}
if (n < 0)
if (n == SOCKET_ERROR)
handleSocketError(env, WSAGetLastError());
}
@ -439,7 +448,7 @@ Java_sun_nio_ch_Net_joinOrDrop4(JNIEnv *env, jobject this, jboolean join, jobjec
}
n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, optval, optlen);
if (n < 0) {
if (n == SOCKET_ERROR) {
if (join && (WSAGetLastError() == WSAENOPROTOOPT))
return IOS_UNAVAILABLE;
handleSocketError(env, WSAGetLastError());
@ -461,7 +470,7 @@ Java_sun_nio_ch_Net_blockOrUnblock4(JNIEnv *env, jobject this, jboolean block, j
n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt,
(void*)&mreq_source, sizeof(mreq_source));
if (n < 0) {
if (n == SOCKET_ERROR) {
if (block && (WSAGetLastError() == WSAENOPROTOOPT))
return IOS_UNAVAILABLE;
handleSocketError(env, WSAGetLastError());
@ -516,8 +525,8 @@ Java_sun_nio_ch_Net_joinOrDrop6(JNIEnv *env, jobject this, jboolean join, jobjec
n = setGroupSourceReqOption(env, fdo, opt, group, index, source);
}
if (n < 0) {
handleSocketError(env, errno);
if (n == SOCKET_ERROR) {
handleSocketError(env, WSAGetLastError());
}
return 0;
}
@ -528,8 +537,8 @@ Java_sun_nio_ch_Net_blockOrUnblock6(JNIEnv *env, jobject this, jboolean block, j
{
int opt = (block) ? MCAST_BLOCK_SOURCE : MCAST_UNBLOCK_SOURCE;
int n = setGroupSourceReqOption(env, fdo, opt, group, index, source);
if (n < 0) {
handleSocketError(env, errno);
if (n == SOCKET_ERROR) {
handleSocketError(env, WSAGetLastError());
}
return 0;
}
@ -545,7 +554,7 @@ Java_sun_nio_ch_Net_setInterface4(JNIEnv* env, jobject this, jobject fdo, jint i
n = setsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF,
(void*)&(in.s_addr), arglen);
if (n < 0) {
if (n == SOCKET_ERROR) {
handleSocketError(env, WSAGetLastError());
}
}
@ -558,7 +567,7 @@ Java_sun_nio_ch_Net_getInterface4(JNIEnv* env, jobject this, jobject fdo)
int n;
n = getsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, (void*)&in, &arglen);
if (n < 0) {
if (n == SOCKET_ERROR) {
handleSocketError(env, WSAGetLastError());
return IOS_THROWN;
}
@ -568,27 +577,27 @@ Java_sun_nio_ch_Net_getInterface4(JNIEnv* env, jobject this, jobject fdo)
JNIEXPORT void JNICALL
Java_sun_nio_ch_Net_setInterface6(JNIEnv* env, jobject this, jobject fdo, jint index)
{
int value = (jint)index;
DWORD value = (jint)index;
int arglen = sizeof(value);
int n;
n = setsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF,
(void*)&(index), arglen);
if (n < 0) {
handleSocketError(env, errno);
if (n == SOCKET_ERROR) {
handleSocketError(env, WSAGetLastError());
}
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Net_getInterface6(JNIEnv* env, jobject this, jobject fdo)
{
int index;
DWORD index;
int arglen = sizeof(index);
int n;
n = getsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, (void*)&index, &arglen);
if (n < 0) {
handleSocketError(env, errno);
if (n == SOCKET_ERROR) {
handleSocketError(env, WSAGetLastError());
return -1;
}
return (jint)index;