test/jdk/java/net/MulticastSocket/SetLoopbackOption.java
changeset 59224 55fdee124e89
equal deleted inserted replaced
59223:f16e4154dd7b 59224:55fdee124e89
       
     1 /*
       
     2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @bug 8233296
       
    27  * @summary Check that MulticastSocket::setOption and MulticastSocket::getOption
       
    28  *          return the correct result for StandardSocketOptions.IP_MULTICAST_LOOP.
       
    29  *          The test sets a DatagramSocketImplFactory and needs to run in /othervm
       
    30  *          mode.
       
    31  * @run testng/othervm SetLoopbackOption
       
    32  * @run testng/othervm -Djava.net.preferIPv4Stack=true SetLoopbackOption
       
    33  * @run testng/othervm -Djava.net.preferIPv6Addresses=true SetLoopbackOption
       
    34  */
       
    35 
       
    36 import java.io.FileDescriptor;
       
    37 import java.io.IOException;
       
    38 import java.net.DatagramPacket;
       
    39 import java.net.DatagramSocket;
       
    40 import java.net.DatagramSocketImpl;
       
    41 import java.net.DatagramSocketImplFactory;
       
    42 import java.net.InetAddress;
       
    43 import java.net.InetSocketAddress;
       
    44 import java.net.MulticastSocket;
       
    45 import java.net.NetworkInterface;
       
    46 import java.net.SocketAddress;
       
    47 import java.net.SocketException;
       
    48 import java.net.SocketOption;
       
    49 import java.net.SocketOptions;
       
    50 import java.net.StandardSocketOptions;
       
    51 import java.util.HashMap;
       
    52 import java.util.Map;
       
    53 import java.util.Set;
       
    54 
       
    55 import org.testng.annotations.Test;
       
    56 import static org.testng.Assert.*;
       
    57 
       
    58 import static java.lang.System.out;
       
    59 
       
    60 public class SetLoopbackOption {
       
    61 
       
    62     final InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
       
    63 
       
    64     @Test
       
    65     public void run() throws Exception {
       
    66         var bindAddress = new InetSocketAddress(loopbackAddress, 0);
       
    67         try (MulticastSocket sock = new MulticastSocket(null)) {
       
    68             out.println("Testing unbound socket");
       
    69             test(sock, null);
       
    70             out.printf("\nBinding socket to %s and testing again%n", bindAddress);
       
    71             sock.bind(bindAddress);
       
    72             test(sock, null);
       
    73         }
       
    74         TestDatagramSocketImplFactory factory = new TestDatagramSocketImplFactory();
       
    75         DatagramSocket.setDatagramSocketImplFactory(factory);
       
    76         try (MulticastSocket sock = new MulticastSocket(null)) {
       
    77             out.println("\nTesting unbound socket with custom impl");
       
    78             TestDatagramSocketImpl impl = factory.last;
       
    79             test(sock, impl);
       
    80             out.printf("\nBinding socket to %s and testing again%n", bindAddress);
       
    81             sock.bind(new InetSocketAddress(loopbackAddress, 0));
       
    82             test(sock, impl);
       
    83         }
       
    84     }
       
    85 
       
    86     private void test(MulticastSocket sock, TestDatagramSocketImpl impl) throws Exception {
       
    87         out.println("Testing with " + sock.getClass() + (impl == null ? "" : ", " + impl.getClass()));
       
    88         var op = StandardSocketOptions.IP_MULTICAST_LOOP;
       
    89         var opId = SocketOptions.IP_MULTICAST_LOOP;
       
    90         boolean enable = sock.getOption(op);
       
    91         assertTrue(enable, "Initial Value for " + op);
       
    92         boolean disable = sock.getLoopbackMode();
       
    93         assertFalse(disable, "Initial Value for getLoopbackMode()");
       
    94         if (impl != null) {
       
    95             assertFalse((Boolean)impl.getOption(opId));
       
    96             assertTrue((Boolean)impl.getOption(op));
       
    97         }
       
    98 
       
    99         out.println("Setting " + op + " to " + false);
       
   100         if (impl != null) {
       
   101             // allows setOption(SocketOption, Object) to be called
       
   102             impl.allowAllSetOptions(true);
       
   103         }
       
   104         sock.setOption(op, false);
       
   105         enable = sock.getOption(op);
       
   106         assertFalse(enable, "Value for " + op);
       
   107         disable = sock.getLoopbackMode();
       
   108         assertTrue(disable, "Value for getLoopbackMode()");
       
   109         if (impl != null) {
       
   110             assertTrue((Boolean)impl.getOption(opId));
       
   111             assertFalse((Boolean)impl.getOption(op));
       
   112         }
       
   113         out.println("Setting " + op + " to " + true);
       
   114         sock.setOption(op, true);
       
   115         enable = sock.getOption(op);
       
   116         assertTrue(enable, "Value for " + op);
       
   117         disable = sock.getLoopbackMode();
       
   118         assertFalse(disable, "Value for getLoopbackMode()");
       
   119         if (impl != null) {
       
   120             assertFalse((Boolean)impl.getOption(opId));
       
   121             assertTrue((Boolean)impl.getOption(op));
       
   122         }
       
   123 
       
   124         out.println("Calling setLoopbackMode(true)");
       
   125         if (impl != null) {
       
   126             // for backward compatibility reason, setLoopbackMode
       
   127             // should call setOption(int, Object), not setOption(SocketOption, Object)
       
   128             // Make sure that an exception is thrown if the latter is ever called.
       
   129             impl.allowAllSetOptions(false);
       
   130         }
       
   131         sock.setLoopbackMode(true);
       
   132         enable = sock.getOption(op);
       
   133         assertFalse(enable, "Value for " + op);
       
   134         disable = sock.getLoopbackMode();
       
   135         assertTrue(disable, "Value for getLoopbackMode()");
       
   136         if (impl != null) {
       
   137             assertTrue((Boolean)impl.getOption(opId));
       
   138             assertFalse((Boolean)impl.getOption(op));
       
   139         }
       
   140         out.println("Calling setLoopbackMode(false)");
       
   141         sock.setLoopbackMode(false);
       
   142         enable = sock.getOption(op);
       
   143         assertTrue(enable, "Value for " + op);
       
   144         disable = sock.getLoopbackMode();
       
   145         assertFalse(disable, "Value for getLoopbackMode()");
       
   146         if (impl != null) {
       
   147             assertFalse((Boolean)impl.getOption(opId));
       
   148             assertTrue((Boolean)impl.getOption(op));
       
   149         }
       
   150     }
       
   151 
       
   152     // Used to attempt to control what is called/passed to the impl.
       
   153     static class TestDatagramSocketImplFactory implements DatagramSocketImplFactory {
       
   154         TestDatagramSocketImpl last;
       
   155         public synchronized DatagramSocketImpl createDatagramSocketImpl() {
       
   156             TestDatagramSocketImpl last = this.last;
       
   157             if (last == null) {
       
   158                 return (last = this.last = new TestDatagramSocketImpl());
       
   159             } else {
       
   160                 throw new AssertionError("Only one instance should be created");
       
   161             }
       
   162         }
       
   163     }
       
   164 
       
   165     // Used to attempt to control what is called/passed to the impl.
       
   166     static class TestDatagramSocketImpl extends DatagramSocketImpl {
       
   167         InetAddress address;
       
   168         private boolean allowAllSetOptions;
       
   169 
       
   170         @Override
       
   171         protected void create() throws SocketException {
       
   172             legacyOptions.put(SocketOptions.IP_MULTICAST_LOOP, false);
       
   173             options.put(StandardSocketOptions.IP_MULTICAST_LOOP, true);
       
   174         }
       
   175 
       
   176         final Map<Integer, Object> legacyOptions = new HashMap<>();
       
   177         final Map<SocketOption<?>, Object> options = new HashMap<>();
       
   178 
       
   179         static <T> T shouldNotComeHere() {
       
   180             throw new AssertionError("should not come here");
       
   181         }
       
   182 
       
   183         @Override
       
   184         protected void bind(int lport, InetAddress laddr) throws SocketException {
       
   185             this.localPort = (lport == 0 ? 6789 : lport);
       
   186             this.address = laddr;
       
   187         }
       
   188 
       
   189         @Override
       
   190         protected void send(DatagramPacket p) throws IOException {
       
   191             shouldNotComeHere();
       
   192         }
       
   193 
       
   194         @Override
       
   195         protected int peek(InetAddress i) throws IOException {
       
   196             return shouldNotComeHere();
       
   197         }
       
   198 
       
   199         @Override
       
   200         protected int peekData(DatagramPacket p) throws IOException {
       
   201             return shouldNotComeHere();
       
   202         }
       
   203 
       
   204         @Override
       
   205         protected void receive(DatagramPacket p) throws IOException {
       
   206             shouldNotComeHere();
       
   207         }
       
   208 
       
   209         @Override
       
   210         protected void setTTL(byte ttl) throws IOException {
       
   211             shouldNotComeHere();
       
   212         }
       
   213 
       
   214         @Override
       
   215         protected byte getTTL() throws IOException {
       
   216             return shouldNotComeHere();
       
   217         }
       
   218 
       
   219         @Override
       
   220         protected void setTimeToLive(int ttl) throws IOException {
       
   221             shouldNotComeHere();
       
   222         }
       
   223 
       
   224         @Override
       
   225         protected int getTimeToLive() throws IOException {
       
   226             return shouldNotComeHere();
       
   227         }
       
   228 
       
   229         @Override
       
   230         protected void join(InetAddress inetaddr) throws IOException {
       
   231             shouldNotComeHere();
       
   232         }
       
   233 
       
   234         @Override
       
   235         protected void leave(InetAddress inetaddr) throws IOException {
       
   236             shouldNotComeHere();
       
   237         }
       
   238 
       
   239         @Override
       
   240         protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)
       
   241                 throws IOException {
       
   242             shouldNotComeHere();
       
   243         }
       
   244 
       
   245         @Override
       
   246         protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)
       
   247                 throws IOException {
       
   248             shouldNotComeHere();
       
   249         }
       
   250 
       
   251         @Override
       
   252         protected void close() {
       
   253 
       
   254         }
       
   255 
       
   256         @Override
       
   257         public void setOption(int optID, Object value) throws SocketException {
       
   258             legacyOptions.put(optID, value);
       
   259             if (optID == SocketOptions.IP_MULTICAST_LOOP) {
       
   260                 boolean disable = (Boolean) value;
       
   261                 options.put(StandardSocketOptions.IP_MULTICAST_LOOP, !disable);
       
   262             }
       
   263         }
       
   264 
       
   265         @Override
       
   266         public Object getOption(int optID) throws SocketException {
       
   267             return legacyOptions.get(optID);
       
   268         }
       
   269 
       
   270         @Override
       
   271         protected Set<SocketOption<?>> supportedOptions() {
       
   272             return Set.of(StandardSocketOptions.IP_MULTICAST_LOOP);
       
   273         }
       
   274 
       
   275         @Override
       
   276         protected void connect(InetAddress address, int port) throws SocketException {
       
   277             shouldNotComeHere();
       
   278         }
       
   279 
       
   280         @Override
       
   281         protected void disconnect() {
       
   282             shouldNotComeHere();
       
   283         }
       
   284 
       
   285         @Override
       
   286         protected FileDescriptor getFileDescriptor() {
       
   287             return super.getFileDescriptor();
       
   288         }
       
   289 
       
   290         @Override
       
   291         protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
       
   292             if (!allowAllSetOptions) shouldNotComeHere();
       
   293             options.put(name, value);
       
   294             if (name.equals(StandardSocketOptions.IP_MULTICAST_LOOP)) {
       
   295                 boolean enable = (Boolean)value;
       
   296                 legacyOptions.put(SocketOptions.IP_MULTICAST_LOOP, !enable);
       
   297             }
       
   298         }
       
   299 
       
   300         @Override
       
   301         protected <T> T getOption(SocketOption<T> name) throws IOException {
       
   302             return (T) options.get(name);
       
   303         }
       
   304 
       
   305         public void allowAllSetOptions(boolean allow) {
       
   306             this.allowAllSetOptions = allow;
       
   307         }
       
   308     }
       
   309 
       
   310     public static void main (String args[]) throws Exception {
       
   311         new SetLoopbackOption().run();
       
   312     }
       
   313 }