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