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 ---