1 /*
  2  * Copyright (c) 2019, 2021, 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.NetworkInterface;
 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     private static final boolean preferIPv6Addresses;
 51 
 52     static {
 53         try {
 54             InetAddress loopbackIPv4 = InetAddress.getByAddress(
 55                     new byte[] {0x7F, 0x00, 0x00, 0x01});
 56 
 57             InetAddress loopbackIPv6 = InetAddress.getByAddress(
 58                     new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 59                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01});
 60 
 61             hasIPv4 = runPrivilegedAction(() -> hasAddress(loopbackIPv4));
 62             hasIPv6 = runPrivilegedAction(() -> hasAddress(loopbackIPv6));
 63         } catch (UnknownHostException e) {
 64             throw new AssertionError(e);
 65         }
 66         preferIPv4Stack = runPrivilegedAction(() -> Boolean.parseBoolean(
 67             System.getProperty("java.net.preferIPv4Stack")));
 68         preferIPv6Addresses = runPrivilegedAction(() -> Boolean.parseBoolean(
 69             System.getProperty("java.net.preferIPv6Addresses")));
 70         if (!preferIPv4Stack && !hasIPv4 && !hasIPv6) {
 71             throw new AssertionError("IPv4 and IPv6 both not available and java.net.preferIPv4Stack is not true");
 72         }
 73     }
 74 
 75     private static boolean hasAddress(InetAddress address) {
 76         try (Socket socket = new Socket()) {
 77             socket.bind(new InetSocketAddress(address, 0));
 78             return true;
 79         } catch (SocketException se) {
 80             try {
 81                 return NetworkInterface.networkInterfaces()
 82                         .flatMap(NetworkInterface::inetAddresses)
 83                         .map(InetAddress::getClass)
 84                         .filter(clz -> clz.equals(address.getClass()))
 85                         .findAny().isPresent();
 86             } catch (SocketException se2) {
 87                 return false;
 88             }
 89         } catch (IOException e) {
 90             throw new UncheckedIOException(e);
 91         }
 92     }
 93 
 94     private static <T> T runPrivilegedAction(Callable<T> callable) {
 95         try {
 96             PrivilegedExceptionAction<T> pa = () -> callable.call();
 97             return AccessController.doPrivileged(pa);
 98         } catch (PrivilegedActionException pae) {
 99             throw new UncheckedIOException((IOException) pae.getCause());
100         }
101     }
102 
103     private IPSupport() { }
104 
105     /**
106      * Whether or not IPv4 is supported.
107      */
108     public static final boolean hasIPv4() {
109         return hasIPv4;
110     }
111 
112     /**
113      * Whether or not IPv6 is supported.
114      */
115     public static final boolean hasIPv6() {
116         return hasIPv6;
117     }
118 
119     /**
120      * Whether or not the "java.net.preferIPv4Stack" system property is set.
121      */
122     public static final boolean preferIPv4Stack() {
123         return preferIPv4Stack;
124     }
125 
126     /**
127      * Whether or not the "java.net.preferIPv6Addresses" system property is set.
128      */
129     public static final boolean preferIPv6Addresses() {
130         return preferIPv6Addresses;
131     }
132 
133 
134     /**
135      * Whether or not the current networking configuration is valid or not.
136      *
137      * If preferIPv4Stack is true but there is no IPv4 support, the configuration is invalid.
138      */
139     public static final boolean currentConfigurationIsValid() {
140         return hasIPv4() || hasIPv6();
141     }
142 
143     /**
144      * Ensures that the platform supports the ability to create a
145      * minimally-operational socket whose protocol is either one of IPv4
146      * or IPv6.
147      *
148      * <p> A minimally-operation socket is one that can be created and
149      * bound to an IP-specific loopback address. IP support is
150      * considered non-operational if a socket cannot be bound to either
151      * one of, an IPv4 loopback address, or the IPv6 loopback address.
152      *
153      * @throws SkippedException if the current networking configuration
154      *         is non-operational
155      */
156     public static void throwSkippedExceptionIfNonOperational() throws SkippedException {
157         if (!currentConfigurationIsValid()) {
158             ByteArrayOutputStream os = new ByteArrayOutputStream();
159             PrintStream ps = new PrintStream(os);
160             ps.println("Invalid networking configuration");
161             printPlatformSupport(ps);
162             throw new SkippedException(os.toString());
163         }
164     }
165 
166     /**
167      * Prints the platform supported configurations.
168      */
169     public static void printPlatformSupport(PrintStream out) {
170         out.println("IPSupport - IPv4: " + hasIPv4());
171         out.println("IPSupport - IPv6: " + hasIPv6());
172         out.println("preferIPv4Stack: " + preferIPv4Stack());
173         out.println("preferIPv6Addresses: " + preferIPv6Addresses());
174     }
175 
176 }
--- EOF ---