8233307: MulticastSocket getOption(IP_MULTICAST_IF) returns interface when not set
Summary: The MulticastSocket method getOption has been changed to conform to the behavior described in StandardSocketOptions.IP_MULTICAST_IF.
Reviewed-by: chegar, dfuchs
--- a/src/java.base/share/classes/java/net/MulticastSocket.java Thu Nov 28 18:30:18 2019 +0100
+++ b/src/java.base/share/classes/java/net/MulticastSocket.java Thu Nov 28 16:49:52 2019 +0000
@@ -576,7 +576,7 @@
public NetworkInterface getNetworkInterface() throws SocketException {
NetworkInterface ni
= (NetworkInterface)getImpl().getOption(SocketOptions.IP_MULTICAST_IF2);
- if ((ni.getIndex() == 0) || (ni.getIndex() == -1)) {
+ if (ni == null) {
InetAddress[] addrs = new InetAddress[1];
addrs[0] = InetAddress.anyLocalAddress();
return new NetworkInterface(addrs[0].getHostName(), 0, addrs);
--- a/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Thu Nov 28 18:30:18 2019 +0100
+++ b/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Thu Nov 28 16:49:52 2019 +0000
@@ -1494,25 +1494,7 @@
if (ni) {
return ni;
}
-
- /*
- * The address doesn't appear to be bound at any known
- * NetworkInterface. Therefore we construct a NetworkInterface
- * with this address.
- */
- ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
- CHECK_NULL_RETURN(ni, NULL);
-
- (*env)->SetIntField(env, ni, ni_indexID, -1);
- addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
- CHECK_NULL_RETURN(addrArray, NULL);
- (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
- (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
- ni_name = (*env)->NewStringUTF(env, "");
- if (ni_name != NULL) {
- (*env)->SetObjectField(env, ni, ni_nameID, ni_name);
- }
- return ni;
+ return NULL;
}
@@ -1619,19 +1601,6 @@
if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
return addr;
}
-
- ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
- CHECK_NULL_RETURN(ni, NULL);
- (*env)->SetIntField(env, ni, ni_indexID, -1);
- addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL);
- CHECK_NULL_RETURN(addrArray, NULL);
- (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
- (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
- ni_name = (*env)->NewStringUTF(env, "");
- if (ni_name != NULL) {
- (*env)->SetObjectField(env, ni, ni_nameID, ni_name);
- }
- return ni;
}
return NULL;
}
--- a/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c Thu Nov 28 18:30:18 2019 +0100
+++ b/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c Thu Nov 28 16:49:52 2019 +0000
@@ -1691,7 +1691,6 @@
static jfieldID ni_indexID;
static jfieldID ni_addrsID;
- jobjectArray addrArray;
jobject addr;
jobject ni;
@@ -1749,19 +1748,7 @@
if (ni) {
return ni;
}
- if (ipv4Mode) {
- ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
- CHECK_NULL_RETURN(ni, NULL);
-
- (*env)->SetIntField(env, ni, ni_indexID, -1);
- addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
- CHECK_NULL_RETURN(addrArray, NULL);
- (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
- (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
- } else {
- ni = NULL;
- }
- return ni;
+ return NULL;
}
/*
@@ -1898,26 +1885,6 @@
return netObject;
}
}
-
- /*
- * Multicast to any address - return anyLocalAddress
- * or a NetworkInterface with addrs[0] set to anyLocalAddress
- */
-
- addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID,
- NULL);
- if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
- return addr;
- }
-
- ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
- CHECK_NULL_RETURN(ni, NULL);
- (*env)->SetIntField(env, ni, ni_indexID, -1);
- addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL);
- CHECK_NULL_RETURN(addrArray, NULL);
- (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
- (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
- return ni;
}
return NULL;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/NetworkInterface/NoSetNetworkInterface.java Thu Nov 28 16:49:52 2019 +0000
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8233307
+ * @library /test/lib
+ * @run main/othervm NoSetNetworkInterface
+ * @run main/othervm -Djava.net.preferIPv4Stack=true NoSetNetworkInterface
+ * @run main/othervm -Djava.net.preferIPv6Addresses=true NoSetNetworkInterface
+ * @summary Check that methods that are used to set and get the NetworkInterface
+ * for a MulticastSocket work as expected. This test also checks that getOption
+ * returns null correctly when a NetworkInterface has not been set
+ */
+
+import jdk.test.lib.NetworkConfiguration;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.net.NetworkInterface;
+import java.net.StandardSocketOptions;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+public class NoSetNetworkInterface {
+ public static void main(String[] args) throws Exception {
+
+ NetworkConfiguration nc = NetworkConfiguration.probe();
+
+ // check set and get methods work as expected
+ nc.multicastInterfaces(true).forEach(ni -> {
+ checkSetInterface(ni);
+ checkSetNetworkInterface(ni);
+ checkSetOption(ni);
+ });
+
+ // Check that dummy NetworkInterface is returned when not set
+ checkDummyNetworkInterface();
+ }
+
+ public static void checkSetInterface(NetworkInterface ni) {
+ try (MulticastSocket ms = new MulticastSocket()) {
+ Optional<InetAddress> iAddr = ni.inetAddresses()
+ .filter(Predicate.not(InetAddress::isAnyLocalAddress))
+ .findFirst();
+ if (iAddr.isPresent()) {
+ ms.setInterface(iAddr.get());
+ checkForCorrectNetworkInterface("setInterface", ms, ni);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public static void checkSetNetworkInterface(NetworkInterface ni) {
+ try (MulticastSocket ms = new MulticastSocket()) {
+ ms.setNetworkInterface(ni);
+ checkForCorrectNetworkInterface("setNetworkInterface", ms, ni);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public static void checkSetOption(NetworkInterface ni) {
+ try (MulticastSocket ms = new MulticastSocket()) {
+ ms.setOption(StandardSocketOptions.IP_MULTICAST_IF, ni);
+ checkForCorrectNetworkInterface("setOption", ms, ni);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public static void checkForCorrectNetworkInterface(String setterMethod,
+ MulticastSocket ms,
+ NetworkInterface ni) throws IOException {
+
+ // getInterface
+ InetAddress testAddr = ms.getInterface();
+ if (!ni.inetAddresses().anyMatch(i -> i.equals(testAddr))) {
+ throw new RuntimeException(setterMethod + " != getInterface");
+ }
+
+ // getNetworkInterface
+ if (!ni.equals(ms.getNetworkInterface())) {
+ throw new RuntimeException(setterMethod + " != getNetworkInterface");
+ }
+
+ // getOption
+ if (!ni.equals(ms.getOption(StandardSocketOptions.IP_MULTICAST_IF))) {
+ throw new RuntimeException(setterMethod + " != getOption");
+ }
+ }
+
+ public static void checkDummyNetworkInterface() throws IOException {
+
+ try(MulticastSocket ms = new MulticastSocket()) {
+
+ // getOption with no Network Interface set
+ NetworkInterface n0 = ms.getOption(StandardSocketOptions.IP_MULTICAST_IF);
+ if (n0 != null) {
+ throw new RuntimeException("NetworkInterface should be null");
+ }
+
+ // getNetworkInterface with no Network Interface set
+ NetworkInterface n1 = ms.getNetworkInterface();
+ if (n1 == null) {
+ throw new RuntimeException("getNetworkInterface() should not return null");
+ } else if (!((n1.getName().equals("0.0.0.0") || n1.getName().equals("::"))
+ && (n1.getIndex() == 0)
+ && (n1.inetAddresses().count() == 1))) {
+
+ throw new RuntimeException("Dummy NetworkInterface not returned as expected");
+ }
+
+ // getInterface with no Network Interface set
+ InetAddress iaddr = ms.getInterface();
+ if (iaddr == null) {
+ throw new RuntimeException("getInterface() should not return null");
+ } else if (!iaddr.isAnyLocalAddress()) {
+ throw new RuntimeException("getInterface() should return anyLocalAddress");
+ }
+ }
+ }
+}
+