1 /* 2 * Copyright (c) 2012, 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 // SunJSSE does not support dynamic system properties, no way to re-use 26 // system properties in samevm/agentvm mode. 27 // 28 29 /* 30 * @test 31 * @bug 7068321 32 * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server 33 * @library ../SSLEngine ../templates 34 * @modules java.base/sun.misc 35 * @build SSLEngineService SSLCapabilities SSLExplorer 36 * @run main/othervm SSLEngineExplorerMatchedSNI www.example.com 37 * www\.example\.com 38 * @run main/othervm SSLEngineExplorerMatchedSNI www.example.com 39 * www\.example\.(com|org) 40 * @run main/othervm SSLEngineExplorerMatchedSNI example.com 41 * (.*\.)*example\.(com|org) 42 * @run main/othervm SSLEngineExplorerMatchedSNI www.example.com 43 * (.*\.)*example\.(com|org) 44 * @run main/othervm SSLEngineExplorerMatchedSNI www.us.example.com 45 * (.*\.)*example\.(com|org) 46 */ 47 48 import javax.net.ssl.*; 49 import java.nio.*; 50 import java.net.*; 51 import java.util.*; 52 import java.nio.channels.*; 53 54 public class SSLEngineExplorerMatchedSNI extends SSLEngineService { 55 56 /* 57 * ============================================================= 58 * Set the various variables needed for the tests, then 59 * specify what tests to run on each side. 60 */ 61 62 /* 63 * Should we run the client or server in a separate thread? 64 * Both sides can throw exceptions, but do you have a preference 65 * as to which side should be the main thread. 66 */ 67 static boolean separateServerThread = false; 68 69 // Is the server ready to serve? 70 volatile static boolean serverReady = false; 71 72 /* 73 * Turn on SSL debugging? 74 */ 75 static boolean debug = false; 76 77 /* 78 * Define the server side of the test. 79 * 80 * If the server prematurely exits, serverReady will be set to true 81 * to avoid infinite hangs. 82 */ 83 void doServerSide() throws Exception { 84 85 // create SSLEngine. 86 SSLEngine ssle = createSSLEngine(false); 87 88 // Create a server socket channel. 89 InetSocketAddress isa = 90 new InetSocketAddress(InetAddress.getLocalHost(), serverPort); 91 ServerSocketChannel ssc = ServerSocketChannel.open(); 92 ssc.socket().bind(isa); 93 serverPort = ssc.socket().getLocalPort(); 94 95 // Signal Client, we're ready for his connect. 96 serverReady = true; 97 98 // Accept a socket channel. 99 SocketChannel sc = ssc.accept(); 100 101 // Complete connection. 102 while (!sc.finishConnect()) { 103 Thread.sleep(50); 104 // waiting for the connection completed. 105 } 106 107 ByteBuffer buffer = ByteBuffer.allocate(0xFF); 108 int position = 0; 109 SSLCapabilities capabilities = null; 110 111 // Read the header of TLS record 112 buffer.limit(SSLExplorer.RECORD_HEADER_SIZE); 113 while (position < SSLExplorer.RECORD_HEADER_SIZE) { 114 int n = sc.read(buffer); 115 if (n < 0) { 116 throw new Exception("unexpected end of stream!"); 117 } 118 position += n; 119 } 120 buffer.flip(); 121 122 int recordLength = SSLExplorer.getRequiredSize(buffer); 123 if (buffer.capacity() < recordLength) { 124 ByteBuffer oldBuffer = buffer; 125 buffer = ByteBuffer.allocate(recordLength); 126 buffer.put(oldBuffer); 127 } 128 129 buffer.position(SSLExplorer.RECORD_HEADER_SIZE); 130 buffer.limit(buffer.capacity()); 131 while (position < recordLength) { 132 int n = sc.read(buffer); 133 if (n < 0) { 134 throw new Exception("unexpected end of stream!"); 135 } 136 position += n; 137 } 138 buffer.flip(); 139 140 capabilities = SSLExplorer.explore(buffer); 141 if (capabilities != null) { 142 System.out.println("Record version: " + 143 capabilities.getRecordVersion()); 144 System.out.println("Hello version: " + 145 capabilities.getHelloVersion()); 146 } 147 148 // enable server name indication checking 149 SNIMatcher matcher = SNIHostName.createSNIMatcher( 150 serverAcceptableHostname); 151 Collection<SNIMatcher> matchers = new ArrayList<>(1); 152 matchers.add(matcher); 153 SSLParameters params = ssle.getSSLParameters(); 154 params.setSNIMatchers(matchers); 155 ssle.setSSLParameters(params); 156 157 // handshaking 158 handshaking(ssle, sc, buffer); 159 160 // receive application data 161 receive(ssle, sc); 162 163 // send out application data 164 deliver(ssle, sc); 165 166 // check server name indication 167 ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession(); 168 checkCapabilities(capabilities, session); 169 170 // close the socket channel. 171 sc.close(); 172 ssc.close(); 173 } 174 175 /* 176 * Define the client side of the test. 177 * 178 * If the server prematurely exits, serverReady will be set to true 179 * to avoid infinite hangs. 180 */ 181 void doClientSide() throws Exception { 182 // create SSLEngine. 183 SSLEngine ssle = createSSLEngine(true); 184 185 /* 186 * Wait for server to get started. 187 */ 188 while (!serverReady) { 189 Thread.sleep(50); 190 } 191 192 // Create a non-blocking socket channel. 193 SocketChannel sc = SocketChannel.open(); 194 sc.configureBlocking(false); 195 InetSocketAddress isa = 196 new InetSocketAddress(InetAddress.getLocalHost(), serverPort); 197 sc.connect(isa); 198 199 // Complete connection. 200 while (!sc.finishConnect() ) { 201 Thread.sleep(50); 202 // waiting for the connection completed. 203 } 204 205 SNIHostName serverName = new SNIHostName(clientRequestedHostname); 206 List<SNIServerName> serverNames = new ArrayList<>(1); 207 serverNames.add(serverName); 208 SSLParameters params = ssle.getSSLParameters(); 209 params.setServerNames(serverNames); 210 ssle.setSSLParameters(params); 211 212 // handshaking 213 handshaking(ssle, sc, null); 214 215 // send out application data 216 deliver(ssle, sc); 217 218 // receive application data 219 receive(ssle, sc); 220 221 // check server name indication 222 ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession(); 223 checkSNIInSession(session); 224 225 // close the socket channel. 226 sc.close(); 227 } 228 229 void checkCapabilities(SSLCapabilities capabilities, 230 ExtendedSSLSession session) throws Exception { 231 List<SNIServerName> sessionSNI = session.getRequestedServerNames(); 232 if (!sessionSNI.equals(capabilities.getServerNames())) { 233 for (SNIServerName sni : sessionSNI) { 234 System.out.println("SNI in session is " + sni); 235 } 236 237 List<SNIServerName> capaSNI = capabilities.getServerNames(); 238 for (SNIServerName sni : capaSNI) { 239 System.out.println("SNI in session is " + sni); 240 } 241 242 throw new Exception( 243 "server name indication does not match capabilities"); 244 } 245 246 checkSNIInSession(session); 247 } 248 249 void checkSNIInSession(ExtendedSSLSession session) throws Exception { 250 List<SNIServerName> sessionSNI = session.getRequestedServerNames(); 251 if (sessionSNI.isEmpty()) { 252 throw new Exception( 253 "unexpected empty request server name indication"); 254 } 255 256 if (sessionSNI.size() != 1) { 257 throw new Exception( 258 "unexpected request server name indication"); 259 } 260 261 SNIServerName serverName = sessionSNI.get(0); 262 if (!(serverName instanceof SNIHostName)) { 263 throw new Exception( 264 "unexpected instance of request server name indication"); 265 } 266 267 String hostname = ((SNIHostName)serverName).getAsciiName(); 268 if (!clientRequestedHostname.equalsIgnoreCase(hostname)) { 269 throw new Exception( 270 "unexpected request server name indication value"); 271 } 272 } 273 274 private static String clientRequestedHostname; 275 private static String serverAcceptableHostname; 276 277 private static void parseArguments(String[] args) { 278 clientRequestedHostname = args[0]; 279 serverAcceptableHostname = args[1]; 280 } 281 282 /* 283 * ============================================================= 284 * The remainder is just support stuff 285 */ 286 volatile Exception serverException = null; 287 volatile Exception clientException = null; 288 289 // use any free port by default 290 volatile int serverPort = 0; 291 292 public static void main(String args[]) throws Exception { 293 if (debug) 294 System.setProperty("javax.net.debug", "all"); 295 296 /* 297 * Get the customized arguments. 298 */ 299 parseArguments(args); 300 301 new SSLEngineExplorerMatchedSNI(); 302 } 303 304 Thread clientThread = null; 305 Thread serverThread = null; 306 307 /* 308 * Primary constructor, used to drive remainder of the test. 309 * 310 * Fork off the other side, then do your work. 311 */ 312 SSLEngineExplorerMatchedSNI() throws Exception { 313 super("../etc"); 314 315 if (separateServerThread) { 316 startServer(true); 317 startClient(false); 318 } else { 319 startClient(true); 320 startServer(false); 321 } 322 323 /* 324 * Wait for other side to close down. 325 */ 326 if (separateServerThread) { 327 serverThread.join(); 328 } else { 329 clientThread.join(); 330 } 331 332 /* 333 * When we get here, the test is pretty much over. 334 * 335 * If the main thread excepted, that propagates back 336 * immediately. If the other thread threw an exception, we 337 * should report back. 338 */ 339 if (serverException != null) { 340 System.out.print("Server Exception:"); 341 throw serverException; 342 } 343 if (clientException != null) { 344 System.out.print("Client Exception:"); 345 throw clientException; 346 } 347 } 348 349 void startServer(boolean newThread) throws Exception { 350 if (newThread) { 351 serverThread = new Thread() { 352 public void run() { 353 try { 354 doServerSide(); 355 } catch (Exception e) { 356 /* 357 * Our server thread just died. 358 * 359 * Release the client, if not active already... 360 */ 361 System.err.println("Server died..."); 362 System.err.println(e); 363 serverReady = true; 364 serverException = e; 365 } 366 } 367 }; 368 serverThread.start(); 369 } else { 370 doServerSide(); 371 } 372 } 373 374 void startClient(boolean newThread) throws Exception { 375 if (newThread) { 376 clientThread = new Thread() { 377 public void run() { 378 try { 379 doClientSide(); 380 } catch (Exception e) { 381 /* 382 * Our client thread just died. 383 */ 384 System.err.println("Client died..."); 385 clientException = e; 386 } 387 } 388 }; 389 clientThread.start(); 390 } else { 391 doClientSide(); 392 } 393 } 394 }