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 }