1 /* 2 * Copyright (c) 2015, 2016, 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 // SunJSSE does not support dynamic system properties, no way to re-use 25 // system properties in samevm/agentvm mode. 26 27 /* 28 * @test 29 * @bug 8043758 30 * @summary Datagram Transport Layer Security (DTLS) 31 * @modules java.base/sun.security.util 32 * @run main/othervm DTLSOverDatagram 33 */ 34 35 import java.io.*; 36 import java.nio.*; 37 import java.net.*; 38 import java.util.*; 39 import java.security.*; 40 import java.security.cert.*; 41 import javax.net.ssl.*; 42 import java.util.concurrent.*; 43 44 import sun.security.util.HexDumpEncoder; 45 46 /** 47 * An example to show the way to use SSLEngine in datagram connections. 48 */ 49 public class DTLSOverDatagram { 50 51 private static int MAX_HANDSHAKE_LOOPS = 200; 52 private static int MAX_APP_READ_LOOPS = 60; 53 private static int SOCKET_TIMEOUT = 10 * 1000; // in millis 54 private static int BUFFER_SIZE = 1024; 55 private static int MAXIMUM_PACKET_SIZE = 1024; 56 57 /* 58 * The following is to set up the keystores. 59 */ 60 private static String pathToStores = "../etc"; 61 private static String keyStoreFile = "keystore"; 62 private static String trustStoreFile = "truststore"; 63 private static String passwd = "passphrase"; 64 65 private static String keyFilename = 66 System.getProperty("test.src", ".") + "/" + pathToStores + 67 "/" + keyStoreFile; 68 private static String trustFilename = 69 System.getProperty("test.src", ".") + "/" + pathToStores + 70 "/" + trustStoreFile; 71 private static Exception clientException = null; 72 private static Exception serverException = null; 73 74 private static ByteBuffer serverApp = 75 ByteBuffer.wrap("Hi Client, I'm Server".getBytes()); 76 private static ByteBuffer clientApp = 77 ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); 78 79 /* 80 * ============================================================= 81 * The test case 82 */ 83 public static void main(String[] args) throws Exception { 84 DTLSOverDatagram testCase = new DTLSOverDatagram(); 85 testCase.runTest(testCase); 86 } 87 88 /* 89 * Define the server side of the test. 90 */ 91 void doServerSide(DatagramSocket socket, InetSocketAddress clientSocketAddr) 92 throws Exception { 93 94 // create SSLEngine 95 SSLEngine engine = createSSLEngine(false); 96 97 // handshaking 98 handshake(engine, socket, clientSocketAddr, "Server"); 99 100 // read client application data 101 receiveAppData(engine, socket, clientApp); 102 103 // write server application data 104 deliverAppData(engine, socket, serverApp, clientSocketAddr); 105 } 106 107 /* 108 * Define the client side of the test. 109 */ 110 void doClientSide(DatagramSocket socket, InetSocketAddress serverSocketAddr) 111 throws Exception { 112 113 // create SSLEngine 114 SSLEngine engine = createSSLEngine(true); 115 116 // handshaking 117 handshake(engine, socket, serverSocketAddr, "Client"); 118 119 // write client application data 120 deliverAppData(engine, socket, clientApp, serverSocketAddr); 121 122 // read server application data 123 receiveAppData(engine, socket, serverApp); 124 } 125 126 /* 127 * ============================================================= 128 * The remainder is support stuff for DTLS operations. 129 */ 130 SSLEngine createSSLEngine(boolean isClient) throws Exception { 131 SSLContext context = getDTLSContext(); 132 SSLEngine engine = context.createSSLEngine(); 133 134 SSLParameters paras = engine.getSSLParameters(); 135 paras.setMaximumPacketSize(MAXIMUM_PACKET_SIZE); 136 137 engine.setUseClientMode(isClient); 138 engine.setSSLParameters(paras); 139 140 return engine; 141 } 142 143 // handshake 144 void handshake(SSLEngine engine, DatagramSocket socket, 145 SocketAddress peerAddr, String side) throws Exception { 146 147 boolean endLoops = false; 148 int loops = MAX_HANDSHAKE_LOOPS; 149 engine.beginHandshake(); 150 while (!endLoops && 151 (serverException == null) && (clientException == null)) { 152 153 if (--loops < 0) { 154 throw new RuntimeException( 155 "Too much loops to produce handshake packets"); 156 } 157 158 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); 159 log(side, "=======handshake(" + loops + ", " + hs + ")======="); 160 if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP || 161 hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) { 162 163 log(side, "Receive DTLS records, handshake status is " + hs); 164 165 ByteBuffer iNet; 166 ByteBuffer iApp; 167 if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) { 168 byte[] buf = new byte[BUFFER_SIZE]; 169 DatagramPacket packet = new DatagramPacket(buf, buf.length); 170 try { 171 socket.receive(packet); 172 } catch (SocketTimeoutException ste) { 173 log(side, "Warning: " + ste); 174 175 List<DatagramPacket> packets = new ArrayList<>(); 176 boolean finished = onReceiveTimeout( 177 engine, peerAddr, side, packets); 178 179 for (DatagramPacket p : packets) { 180 socket.send(p); 181 } 182 183 if (finished) { 184 log(side, "Handshake status is FINISHED " 185 + "after calling onReceiveTimeout(), " 186 + "finish the loop"); 187 endLoops = true; 188 } 189 190 log(side, "New handshake status is " 191 + engine.getHandshakeStatus()); 192 193 continue; 194 } 195 196 iNet = ByteBuffer.wrap(buf, 0, packet.getLength()); 197 iApp = ByteBuffer.allocate(BUFFER_SIZE); 198 } else { 199 iNet = ByteBuffer.allocate(0); 200 iApp = ByteBuffer.allocate(BUFFER_SIZE); 201 } 202 203 SSLEngineResult r = engine.unwrap(iNet, iApp); 204 SSLEngineResult.Status rs = r.getStatus(); 205 hs = r.getHandshakeStatus(); 206 if (rs == SSLEngineResult.Status.OK) { 207 // OK 208 } else if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) { 209 log(side, "BUFFER_OVERFLOW, handshake status is " + hs); 210 211 // the client maximum fragment size config does not work? 212 throw new Exception("Buffer overflow: " + 213 "incorrect client maximum fragment size"); 214 } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) { 215 log(side, "BUFFER_UNDERFLOW, handshake status is " + hs); 216 217 // bad packet, or the client maximum fragment size 218 // config does not work? 219 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 220 throw new Exception("Buffer underflow: " + 221 "incorrect client maximum fragment size"); 222 } // otherwise, ignore this packet 223 } else if (rs == SSLEngineResult.Status.CLOSED) { 224 throw new Exception( 225 "SSL engine closed, handshake status is " + hs); 226 } else { 227 throw new Exception("Can't reach here, result is " + rs); 228 } 229 230 if (hs == SSLEngineResult.HandshakeStatus.FINISHED) { 231 log(side, "Handshake status is FINISHED, finish the loop"); 232 endLoops = true; 233 } 234 } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) { 235 List<DatagramPacket> packets = new ArrayList<>(); 236 boolean finished = produceHandshakePackets( 237 engine, peerAddr, side, packets); 238 239 log(side, "Produced " + packets.size() + " packets"); 240 for (DatagramPacket p : packets) { 241 socket.send(p); 242 } 243 244 if (finished) { 245 log(side, "Handshake status is FINISHED " 246 + "after producing handshake packets, " 247 + "finish the loop"); 248 endLoops = true; 249 } 250 } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) { 251 runDelegatedTasks(engine); 252 } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 253 log(side, 254 "Handshake status is NOT_HANDSHAKING, finish the loop"); 255 endLoops = true; 256 } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) { 257 throw new Exception( 258 "Unexpected status, SSLEngine.getHandshakeStatus() " 259 + "shouldn't return FINISHED"); 260 } else { 261 throw new Exception( 262 "Can't reach here, handshake status is " + hs); 263 } 264 } 265 266 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); 267 log(side, "Handshake finished, status is " + hs); 268 269 if (engine.getHandshakeSession() != null) { 270 throw new Exception( 271 "Handshake finished, but handshake session is not null"); 272 } 273 274 SSLSession session = engine.getSession(); 275 if (session == null) { 276 throw new Exception("Handshake finished, but session is null"); 277 } 278 log(side, "Negotiated protocol is " + session.getProtocol()); 279 log(side, "Negotiated cipher suite is " + session.getCipherSuite()); 280 281 // handshake status should be NOT_HANDSHAKING 282 // 283 // According to the spec, SSLEngine.getHandshakeStatus() can't 284 // return FINISHED. 285 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 286 throw new Exception("Unexpected handshake status " + hs); 287 } 288 } 289 290 // deliver application data 291 void deliverAppData(SSLEngine engine, DatagramSocket socket, 292 ByteBuffer appData, SocketAddress peerAddr) throws Exception { 293 294 // Note: have not consider the packet loses 295 List<DatagramPacket> packets = 296 produceApplicationPackets(engine, appData, peerAddr); 297 appData.flip(); 298 for (DatagramPacket p : packets) { 299 socket.send(p); 300 } 301 } 302 303 // receive application data 304 void receiveAppData(SSLEngine engine, 305 DatagramSocket socket, ByteBuffer expectedApp) throws Exception { 306 307 int loops = MAX_APP_READ_LOOPS; 308 while ((serverException == null) && (clientException == null)) { 309 if (--loops < 0) { 310 throw new RuntimeException( 311 "Too much loops to receive application data"); 312 } 313 314 byte[] buf = new byte[BUFFER_SIZE]; 315 DatagramPacket packet = new DatagramPacket(buf, buf.length); 316 socket.receive(packet); 317 ByteBuffer netBuffer = ByteBuffer.wrap(buf, 0, packet.getLength()); 318 ByteBuffer recBuffer = ByteBuffer.allocate(BUFFER_SIZE); 319 SSLEngineResult rs = engine.unwrap(netBuffer, recBuffer); 320 recBuffer.flip(); 321 if (recBuffer.remaining() != 0) { 322 printHex("Received application data", recBuffer); 323 if (!recBuffer.equals(expectedApp)) { 324 System.out.println("Engine status is " + rs); 325 throw new Exception("Not the right application data"); 326 } 327 break; 328 } 329 } 330 } 331 332 // produce handshake packets 333 boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr, 334 String side, List<DatagramPacket> packets) throws Exception { 335 336 boolean endLoops = false; 337 int loops = MAX_HANDSHAKE_LOOPS; 338 while (!endLoops && 339 (serverException == null) && (clientException == null)) { 340 341 if (--loops < 0) { 342 throw new RuntimeException( 343 "Too much loops to produce handshake packets"); 344 } 345 346 ByteBuffer oNet = ByteBuffer.allocate(32768); 347 ByteBuffer oApp = ByteBuffer.allocate(0); 348 SSLEngineResult r = engine.wrap(oApp, oNet); 349 oNet.flip(); 350 351 SSLEngineResult.Status rs = r.getStatus(); 352 SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus(); 353 log(side, "====packet(" + loops + ", " + rs + ", " + hs + ")===="); 354 if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) { 355 // the client maximum fragment size config does not work? 356 throw new Exception("Buffer overflow: " + 357 "incorrect server maximum fragment size"); 358 } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) { 359 log(side, 360 "Produce handshake packets: BUFFER_UNDERFLOW occured"); 361 log(side, 362 "Produce handshake packets: Handshake status: " + hs); 363 // bad packet, or the client maximum fragment size 364 // config does not work? 365 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 366 throw new Exception("Buffer underflow: " + 367 "incorrect server maximum fragment size"); 368 } // otherwise, ignore this packet 369 } else if (rs == SSLEngineResult.Status.CLOSED) { 370 throw new Exception("SSLEngine has closed"); 371 } else if (rs == SSLEngineResult.Status.OK) { 372 // OK 373 } else { 374 throw new Exception("Can't reach here, result is " + rs); 375 } 376 377 // SSLEngineResult.Status.OK: 378 if (oNet.hasRemaining()) { 379 byte[] ba = new byte[oNet.remaining()]; 380 oNet.get(ba); 381 DatagramPacket packet = createHandshakePacket(ba, socketAddr); 382 packets.add(packet); 383 } 384 385 if (hs == SSLEngineResult.HandshakeStatus.FINISHED) { 386 log(side, "Produce handshake packets: " 387 + "Handshake status is FINISHED, finish the loop"); 388 return true; 389 } 390 391 boolean endInnerLoop = false; 392 SSLEngineResult.HandshakeStatus nhs = hs; 393 while (!endInnerLoop) { 394 if (nhs == SSLEngineResult.HandshakeStatus.NEED_TASK) { 395 runDelegatedTasks(engine); 396 } else if (nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP || 397 nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN || 398 nhs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 399 400 endInnerLoop = true; 401 endLoops = true; 402 } else if (nhs == SSLEngineResult.HandshakeStatus.NEED_WRAP) { 403 endInnerLoop = true; 404 } else if (nhs == SSLEngineResult.HandshakeStatus.FINISHED) { 405 throw new Exception( 406 "Unexpected status, SSLEngine.getHandshakeStatus() " 407 + "shouldn't return FINISHED"); 408 } else { 409 throw new Exception("Can't reach here, handshake status is " 410 + nhs); 411 } 412 nhs = engine.getHandshakeStatus(); 413 } 414 } 415 416 return false; 417 } 418 419 DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) { 420 return new DatagramPacket(ba, ba.length, socketAddr); 421 } 422 423 // produce application packets 424 List<DatagramPacket> produceApplicationPackets( 425 SSLEngine engine, ByteBuffer source, 426 SocketAddress socketAddr) throws Exception { 427 428 List<DatagramPacket> packets = new ArrayList<>(); 429 ByteBuffer appNet = ByteBuffer.allocate(32768); 430 SSLEngineResult r = engine.wrap(source, appNet); 431 appNet.flip(); 432 433 SSLEngineResult.Status rs = r.getStatus(); 434 if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) { 435 // the client maximum fragment size config does not work? 436 throw new Exception("Buffer overflow: " + 437 "incorrect server maximum fragment size"); 438 } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) { 439 // unlikely 440 throw new Exception("Buffer underflow during wraping"); 441 } else if (rs == SSLEngineResult.Status.CLOSED) { 442 throw new Exception("SSLEngine has closed"); 443 } else if (rs == SSLEngineResult.Status.OK) { 444 // OK 445 } else { 446 throw new Exception("Can't reach here, result is " + rs); 447 } 448 449 // SSLEngineResult.Status.OK: 450 if (appNet.hasRemaining()) { 451 byte[] ba = new byte[appNet.remaining()]; 452 appNet.get(ba); 453 DatagramPacket packet = 454 new DatagramPacket(ba, ba.length, socketAddr); 455 packets.add(packet); 456 } 457 458 return packets; 459 } 460 461 // Get a datagram packet for the specified handshake type. 462 static DatagramPacket getPacket( 463 List<DatagramPacket> packets, byte handshakeType) { 464 boolean matched = false; 465 for (DatagramPacket packet : packets) { 466 byte[] data = packet.getData(); 467 int offset = packet.getOffset(); 468 int length = packet.getLength(); 469 470 // Normally, this pakcet should be a handshake message 471 // record. However, even if the underlying platform 472 // splits the record more, we don't really worry about 473 // the improper packet loss because DTLS implementation 474 // should be able to handle packet loss properly. 475 // 476 // See RFC 6347 for the detailed format of DTLS records. 477 if (handshakeType == -1) { // ChangeCipherSpec 478 // Is it a ChangeCipherSpec message? 479 matched = (length == 14) && (data[offset] == 0x14); 480 } else if ((length >= 25) && // 25: handshake mini size 481 (data[offset] == 0x16)) { // a handshake message 482 483 // check epoch number for initial handshake only 484 if (data[offset + 3] == 0x00) { // 3,4: epoch 485 if (data[offset + 4] == 0x00) { // plaintext 486 matched = 487 (data[offset + 13] == handshakeType); 488 } else { // cipherext 489 // The 1st ciphertext is a Finished message. 490 // 491 // If it is not proposed to loss the Finished 492 // message, it is not necessary to check the 493 // following packets any mroe as a Finished 494 // message is the last handshake message. 495 matched = (handshakeType == 20); 496 } 497 } 498 } 499 500 if (matched) { 501 return packet; 502 } 503 } 504 505 return null; 506 } 507 508 // run delegated tasks 509 void runDelegatedTasks(SSLEngine engine) throws Exception { 510 Runnable runnable; 511 while ((runnable = engine.getDelegatedTask()) != null) { 512 runnable.run(); 513 } 514 515 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); 516 if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) { 517 throw new Exception("handshake shouldn't need additional tasks"); 518 } 519 } 520 521 // retransmission if timeout 522 boolean onReceiveTimeout(SSLEngine engine, SocketAddress socketAddr, 523 String side, List<DatagramPacket> packets) throws Exception { 524 525 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); 526 if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 527 return false; 528 } else { 529 // retransmission of handshake messages 530 return produceHandshakePackets(engine, socketAddr, side, packets); 531 } 532 } 533 534 // get DTSL context 535 SSLContext getDTLSContext() throws Exception { 536 KeyStore ks = KeyStore.getInstance("JKS"); 537 KeyStore ts = KeyStore.getInstance("JKS"); 538 539 char[] passphrase = "passphrase".toCharArray(); 540 541 try (FileInputStream fis = new FileInputStream(keyFilename)) { 542 ks.load(fis, passphrase); 543 } 544 545 try (FileInputStream fis = new FileInputStream(trustFilename)) { 546 ts.load(fis, passphrase); 547 } 548 549 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 550 kmf.init(ks, passphrase); 551 552 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 553 tmf.init(ts); 554 555 SSLContext sslCtx = SSLContext.getInstance("DTLS"); 556 557 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 558 559 return sslCtx; 560 } 561 562 563 /* 564 * ============================================================= 565 * The remainder is support stuff to kickstart the testing. 566 */ 567 568 // Will the handshaking and application data exchange succeed? 569 public boolean isGoodJob() { 570 return true; 571 } 572 573 public final void runTest(DTLSOverDatagram testCase) throws Exception { 574 try (DatagramSocket serverSocket = new DatagramSocket(); 575 DatagramSocket clientSocket = new DatagramSocket()) { 576 577 serverSocket.setSoTimeout(SOCKET_TIMEOUT); 578 clientSocket.setSoTimeout(SOCKET_TIMEOUT); 579 580 InetSocketAddress serverSocketAddr = new InetSocketAddress( 581 InetAddress.getLocalHost(), serverSocket.getLocalPort()); 582 583 InetSocketAddress clientSocketAddr = new InetSocketAddress( 584 InetAddress.getLocalHost(), clientSocket.getLocalPort()); 585 586 ExecutorService pool = Executors.newFixedThreadPool(2); 587 Future<String> server, client; 588 589 try { 590 server = pool.submit(new ServerCallable( 591 testCase, serverSocket, clientSocketAddr)); 592 client = pool.submit(new ClientCallable( 593 testCase, clientSocket, serverSocketAddr)); 594 } finally { 595 pool.shutdown(); 596 } 597 598 boolean failed = false; 599 600 // wait for client to finish 601 try { 602 System.out.println("Client finished: " + client.get()); 603 } catch (CancellationException | InterruptedException 604 | ExecutionException e) { 605 System.out.println("Exception on client side: "); 606 e.printStackTrace(System.out); 607 failed = true; 608 } 609 610 // wait for server to finish 611 try { 612 System.out.println("Client finished: " + server.get()); 613 } catch (CancellationException | InterruptedException 614 | ExecutionException e) { 615 System.out.println("Exception on server side: "); 616 e.printStackTrace(System.out); 617 failed = true; 618 } 619 620 if (failed) { 621 throw new RuntimeException("Test failed"); 622 } 623 } 624 } 625 626 final static class ServerCallable implements Callable<String> { 627 628 private final DTLSOverDatagram testCase; 629 private final DatagramSocket socket; 630 private final InetSocketAddress clientSocketAddr; 631 632 ServerCallable(DTLSOverDatagram testCase, DatagramSocket socket, 633 InetSocketAddress clientSocketAddr) { 634 635 this.testCase = testCase; 636 this.socket = socket; 637 this.clientSocketAddr = clientSocketAddr; 638 } 639 640 @Override 641 public String call() throws Exception { 642 try { 643 testCase.doServerSide(socket, clientSocketAddr); 644 } catch (Exception e) { 645 System.out.println("Exception in ServerCallable.call():"); 646 e.printStackTrace(System.out); 647 serverException = e; 648 649 if (testCase.isGoodJob()) { 650 throw e; 651 } else { 652 return "Well done, server!"; 653 } 654 } 655 656 if (testCase.isGoodJob()) { 657 return "Well done, server!"; 658 } else { 659 throw new Exception("No expected exception"); 660 } 661 } 662 } 663 664 final static class ClientCallable implements Callable<String> { 665 666 private final DTLSOverDatagram testCase; 667 private final DatagramSocket socket; 668 private final InetSocketAddress serverSocketAddr; 669 670 ClientCallable(DTLSOverDatagram testCase, DatagramSocket socket, 671 InetSocketAddress serverSocketAddr) { 672 673 this.testCase = testCase; 674 this.socket = socket; 675 this.serverSocketAddr = serverSocketAddr; 676 } 677 678 @Override 679 public String call() throws Exception { 680 try { 681 testCase.doClientSide(socket, serverSocketAddr); 682 } catch (Exception e) { 683 System.out.println("Exception in ClientCallable.call():"); 684 e.printStackTrace(System.out); 685 clientException = e; 686 687 if (testCase.isGoodJob()) { 688 throw e; 689 } else { 690 return "Well done, client!"; 691 } 692 } 693 694 if (testCase.isGoodJob()) { 695 return "Well done, client!"; 696 } else { 697 throw new Exception("No expected exception"); 698 } 699 } 700 } 701 702 final static void printHex(String prefix, ByteBuffer bb) { 703 HexDumpEncoder dump = new HexDumpEncoder(); 704 705 synchronized (System.out) { 706 System.out.println(prefix); 707 try { 708 dump.encodeBuffer(bb.slice(), System.out); 709 } catch (Exception e) { 710 // ignore 711 } 712 System.out.flush(); 713 } 714 } 715 716 final static void printHex(String prefix, 717 byte[] bytes, int offset, int length) { 718 719 HexDumpEncoder dump = new HexDumpEncoder(); 720 721 synchronized (System.out) { 722 System.out.println(prefix); 723 try { 724 ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length); 725 dump.encodeBuffer(bb, System.out); 726 } catch (Exception e) { 727 // ignore 728 } 729 System.out.flush(); 730 } 731 } 732 733 static void log(String side, String message) { 734 System.out.println(side + ": " + message); 735 } 736 }