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 package jdk.test.lib.net;
  25 
  26 import java.io.ByteArrayOutputStream;
  27 import java.io.IOException;
  28 import java.io.PrintStream;
  29 import java.io.UncheckedIOException;
  30 import java.net.InetAddress;
  31 import java.net.InetSocketAddress;
  32 import java.net.ServerSocket;
  33 import java.net.Socket;
  34 import java.net.SocketException;
  35 import java.net.UnknownHostException;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedActionException;
  38 import java.security.PrivilegedExceptionAction;
  39 import java.util.concurrent.Callable;
  40 import jtreg.SkippedException;
  41 
  42 /**
  43  * Determines Internet Protocol version support at the TCP socket level.
  44  */
  45 public class IPSupport {
  46 
  47     private static final boolean hasIPv4;
  48     private static final boolean hasIPv6;
  49     private static final boolean preferIPv4Stack;
  50 
  51     static {
  52         try {
  53             InetAddress loopbackIPv4 = InetAddress.getByAddress(
  54                     new byte[] {0x7F, 0x00, 0x00, 0x01});
  55 
  56             InetAddress loopbackIPv6 = InetAddress.getByAddress(
  57                     new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  58                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01});
  59 
  60             hasIPv4 = runPrivilegedAction(() -> hasAddress(loopbackIPv4));
  61             hasIPv6 = runPrivilegedAction(() -> hasAddress(loopbackIPv6));
  62         } catch (UnknownHostException e) {
  63             throw new AssertionError(e);
  64         }
  65         preferIPv4Stack = runPrivilegedAction(() -> Boolean.parseBoolean(
  66             System.getProperty("java.net.preferIPv4Stack")));
  67         if (!preferIPv4Stack && !hasIPv4 && !hasIPv6) {
  68             throw new AssertionError("IPv4 and IPv6 both not available and java.net.preferIPv4Stack is not true");
  69         }
  70     }
  71 
  72     private static boolean hasAddress(InetAddress address) {
  73         try (Socket socket = new Socket()) {
  74             socket.bind(new InetSocketAddress(address, 0));
  75             return true;
  76         } catch (SocketException se) {
  77             return false;
  78         } catch (IOException e) {
  79             throw new UncheckedIOException(e);
  80         }
  81     }
  82 
  83     private static <T> T runPrivilegedAction(Callable<T> callable) {
  84         try {
  85             PrivilegedExceptionAction<T> pa = () -> callable.call();
  86             return AccessController.doPrivileged(pa);
  87         } catch (PrivilegedActionException pae) {
  88             throw new UncheckedIOException((IOException) pae.getCause());
  89         }
  90     }
  91 
  92     private IPSupport() { }
  93 
  94     /**
  95      * Whether or not IPv4 is supported.
  96      */
  97     public static final boolean hasIPv4() {
  98         return hasIPv4;
  99     }
 100 
 101     /**
 102      * Whether or not IPv6 is supported.
 103      */
 104     public static final boolean hasIPv6() {
 105         return hasIPv6;
 106     }
 107 
 108     /**
 109      * Whether or not the "java.net.preferIPv4Stack" system property is set.
 110      */
 111     public static final boolean preferIPv4Stack() {
 112         return preferIPv4Stack;
 113     }
 114 
 115 
 116     /**
 117      * Whether or not the current networking configuration is valid or not.
 118      *
 119      * If preferIPv4Stack is true but there is no IPv4 support, the configuration is invalid.
 120      */
 121     public static final boolean currentConfigurationIsValid() {
 122         return hasIPv4() || hasIPv6();
 123     }
 124 
 125     /**
 126      * Ensures that the platform supports the ability to create a
 127      * minimally-operational socket whose protocol is either one of IPv4
 128      * or IPv6.
 129      *
 130      * <p> A minimally-operation socket is one that can be created and
 131      * bound to an IP-specific loopback address. IP support is
 132      * considered non-operational if a socket cannot be bound to either
 133      * one of, an IPv4 loopback address, or the IPv6 loopback address.
 134      *
 135      * @throws SkippedException if the current networking configuration
 136      *         is non-operational
 137      */
 138     public static void throwSkippedExceptionIfNonOperational() throws SkippedException {
 139         if (!currentConfigurationIsValid()) {
 140             ByteArrayOutputStream os = new ByteArrayOutputStream();
 141             PrintStream ps = new PrintStream(os);
 142             ps.println("Invalid networking configuration");
 143             printPlatformSupport(ps);
 144             throw new SkippedException(os.toString());
 145         }
 146     }
 147 
 148     /**
 149      * Prints the platform supported configurations.
 150      */
 151     public static void printPlatformSupport(PrintStream out) {
 152         out.println("IPSupport - IPv4: " + hasIPv4());
 153         out.println("IPSupport - IPv6: " + hasIPv6());
 154         out.println("preferIPv4Stack: " + preferIPv4Stack());
 155     }
 156 
 157 }