Skip to content

Commit

Permalink
8297798: Timeout with DTLSOverDatagram test template
Browse files Browse the repository at this point in the history
Reviewed-by: mbaesken
Backport-of: 23457a664ce00de33fdb6846ff31f489e6dd2177
  • Loading branch information
GoeLin committed Mar 30, 2024
1 parent 979daf5 commit 0073602
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 153 deletions.
197 changes: 63 additions & 134 deletions test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -33,6 +33,7 @@
* @run main/othervm DTLSOverDatagram
*/

import java.io.IOException;
import java.nio.*;
import java.net.*;
import java.util.*;
Expand All @@ -42,6 +43,7 @@
import jdk.test.lib.security.SSLContextBuilder;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

import jdk.test.lib.hexdump.HexPrinter;

Expand All @@ -50,8 +52,6 @@
*/
public class DTLSOverDatagram {

private static final int MAX_HANDSHAKE_LOOPS = 200;
private static final int MAX_APP_READ_LOOPS = 60;
private static final int SOCKET_TIMEOUT = 10 * 1000; // in millis
private static final int BUFFER_SIZE = 1024;
private static final int MAXIMUM_PACKET_SIZE = 1024;
Expand All @@ -75,8 +75,9 @@ public class DTLSOverDatagram {
private static final ByteBuffer CLIENT_APP =
ByteBuffer.wrap("Hi Server, I'm Client".getBytes());

private static Exception clientException = null;
private static Exception serverException = null;
private final AtomicBoolean exceptionOccurred = new AtomicBoolean(false);

private final CountDownLatch serverStarted = new CountDownLatch(1);
/*
* =============================================================
* The test case
Expand Down Expand Up @@ -148,18 +149,12 @@ void handshake(SSLEngine engine, DatagramSocket socket,
SocketAddress peerAddr, String side) throws Exception {

boolean endLoops = false;
int loops = MAX_HANDSHAKE_LOOPS;
int loops = 0;
engine.beginHandshake();
while (!endLoops &&
(serverException == null) && (clientException == null)) {

if (--loops < 0) {
throw new RuntimeException(
"Too many loops to produce handshake packets");
}
while (!endLoops && !exceptionOccurred.get()) {

SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
log(side, "=======handshake(" + loops + ", " + hs + ")=======");
log(side, "=======handshake(" + ++loops + ", " + hs + ")=======");

switch (hs) {
case NEED_UNWRAP:
Expand Down Expand Up @@ -314,16 +309,10 @@ void deliverAppData(SSLEngine engine, DatagramSocket socket,
void receiveAppData(SSLEngine engine,
DatagramSocket socket, ByteBuffer expectedApp) throws Exception {

int loops = MAX_APP_READ_LOOPS;
while ((serverException == null) && (clientException == null)) {
if (--loops < 0) {
throw new RuntimeException(
"Too much loops to receive application data");
}

while (!exceptionOccurred.get()) {
byte[] buf = new byte[BUFFER_SIZE];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
DatagramPacket packet = readFromSocket(socket, buf);

ByteBuffer netBuffer = ByteBuffer.wrap(buf, 0, packet.getLength());
ByteBuffer recBuffer = ByteBuffer.allocate(BUFFER_SIZE);
SSLEngineResult rs = engine.unwrap(netBuffer, recBuffer);
Expand All @@ -339,19 +328,31 @@ void receiveAppData(SSLEngine engine,
}
}

/*
Some tests failed with receive time-out errors when the client tried to read
from the server. The server thread had exited normally so the read _should_
succeed. So let's try to read a couple of times before giving up.
*/
DatagramPacket readFromSocket(DatagramSocket socket, byte[] buffer) throws IOException {
for (int i = 1 ; i <= 2 ; ++i) {
try {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
return packet;
} catch (SocketTimeoutException exc) {
System.out.println("Attempt " + i + ": Timeout occurred reading from socket.");
}
}
throw new IOException("Did not receive data after 2 attempts.");
}

// produce handshake packets
boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
String side, List<DatagramPacket> packets) throws Exception {

boolean endLoops = false;
int loops = MAX_HANDSHAKE_LOOPS / 2;
while (!endLoops &&
(serverException == null) && (clientException == null)) {

if (--loops < 0) {
throw new RuntimeException(
"Too many loops to produce handshake packets");
}
int loops = 0;
while (!endLoops && !exceptionOccurred.get()) {

ByteBuffer oNet = ByteBuffer.allocate(32768);
ByteBuffer oApp = ByteBuffer.allocate(0);
Expand All @@ -361,7 +362,7 @@ boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
SSLEngineResult.Status rs = r.getStatus();
SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
log(side, "----produce handshake packet(" +
loops + ", " + rs + ", " + hs + ")----");
++loops + ", " + rs + ", " + hs + ")----");

verifySSLEngineResultStatus(r, side);

Expand Down Expand Up @@ -521,10 +522,6 @@ SSLContext getDTLSContext() throws Exception {
* The remainder is support stuff to kickstart the testing.
*/

// Will the handshaking and application data exchange succeed?
public boolean isGoodJob() {
return true;
}

public final void runTest(DTLSOverDatagram testCase) throws Exception {
InetSocketAddress serverSocketAddress = new InetSocketAddress
Expand All @@ -544,119 +541,51 @@ public final void runTest(DTLSOverDatagram testCase) throws Exception {
InetSocketAddress clientSocketAddr = new InetSocketAddress(
InetAddress.getLoopbackAddress(), clientSocket.getLocalPort());

ExecutorService pool = Executors.newFixedThreadPool(2);
Future<String> server, client;
ExecutorService pool = Executors.newFixedThreadPool(1);
Future<Void> server;

try {
server = pool.submit(new ServerCallable(
server = pool.submit(() -> runServer(
testCase, serverSocket, clientSocketAddr));
client = pool.submit(new ClientCallable(
testCase, clientSocket, serverSocketAddr));
} finally {
pool.shutdown();
}

boolean failed = false;

// wait for client to finish
try {
System.out.println("Client finished: " + client.get());
} catch (CancellationException | InterruptedException
| ExecutionException e) {
System.out.println("Exception on client side: ");
e.printStackTrace(System.out);
failed = true;
}

// wait for server to finish
try {
System.out.println("Client finished: " + server.get());
} catch (CancellationException | InterruptedException
| ExecutionException e) {
System.out.println("Exception on server side: ");
e.printStackTrace(System.out);
failed = true;
}
pool.shutdown();

if (failed) {
throw new RuntimeException("Test failed");
}
runClient(testCase, clientSocket, serverSocketAddr);
server.get();
}
}

final static class ServerCallable implements Callable<String> {

private final DTLSOverDatagram testCase;
private final DatagramSocket socket;
private final InetSocketAddress clientSocketAddr;
Void runServer(DTLSOverDatagram testCase, DatagramSocket socket,
InetSocketAddress clientSocketAddr) throws Exception {
try {
serverStarted.countDown();
testCase.doServerSide(socket, clientSocketAddr);

ServerCallable(DTLSOverDatagram testCase, DatagramSocket socket,
InetSocketAddress clientSocketAddr) {
} catch (Exception exc) {
exceptionOccurred.set(true);

this.testCase = testCase;
this.socket = socket;
this.clientSocketAddr = clientSocketAddr;
// log for debugging clarity
System.out.println("Unexpected exception in server");
exc.printStackTrace(System.err);
throw exc;
}

@Override
public String call() throws Exception {
try {
testCase.doServerSide(socket, clientSocketAddr);
} catch (Exception e) {
System.out.println("Exception in ServerCallable.call():");
e.printStackTrace(System.out);
serverException = e;

if (testCase.isGoodJob()) {
throw e;
} else {
return "Well done, server!";
}
}

if (testCase.isGoodJob()) {
return "Well done, server!";
} else {
throw new Exception("No expected exception");
}
}
return null;
}

final static class ClientCallable implements Callable<String> {

private final DTLSOverDatagram testCase;
private final DatagramSocket socket;
private final InetSocketAddress serverSocketAddr;

ClientCallable(DTLSOverDatagram testCase, DatagramSocket socket,
InetSocketAddress serverSocketAddr) {

this.testCase = testCase;
this.socket = socket;
this.serverSocketAddr = serverSocketAddr;
private void runClient(DTLSOverDatagram testCase, DatagramSocket socket,
InetSocketAddress serverSocketAddr) throws Exception {
if(!serverStarted.await(5, TimeUnit.SECONDS)) {
throw new Exception("Server did not start within 5 seconds.");
}

@Override
public String call() throws Exception {
try {
testCase.doClientSide(socket, serverSocketAddr);
} catch (Exception e) {
System.out.println("Exception in ClientCallable.call():");
e.printStackTrace(System.out);
clientException = e;

if (testCase.isGoodJob()) {
throw e;
} else {
return "Well done, client!";
}
}
try {
testCase.doClientSide(socket, serverSocketAddr);
} catch (Exception exc) {
exceptionOccurred.set(true);

if (testCase.isGoodJob()) {
return "Well done, client!";
} else {
throw new Exception("No expected exception");
}
// log for debugging clarity
System.out.println("Unexpected exception in client.");
exc.printStackTrace(System.err);
throw exc;
}
}

Expand Down
33 changes: 14 additions & 19 deletions test/jdk/javax/net/ssl/DTLS/InvalidRecords.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -36,27 +36,32 @@

import java.net.DatagramPacket;
import java.net.SocketAddress;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* Test that if handshake messages are crasged, the handshake would fail
* Test that if handshake messages are changed, the handshake would fail
* because of handshaking hash verification.
*/
public class InvalidRecords extends DTLSOverDatagram {
boolean needInvalidRecords = true;
private static final AtomicBoolean needInvalidRecords = new AtomicBoolean(true);

public static void main(String[] args) throws Exception {
InvalidRecords testCase = new InvalidRecords();
testCase.runTest(testCase);
}

@Override
public boolean isGoodJob() {
return false;
if (needInvalidRecords.get()) {
// if this is true, the createHandshakePacket() method
// was NOT called twice to create ClientHello messages
throw new RuntimeException(
"The invalid handshake packet was not"
+ " rejected as it should have been.");
}
}


@Override
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
if ((ba.length >= 60) &&
if (needInvalidRecords.get() && (ba.length >= 60) &&
(ba[0x00] == (byte)0x16) && (ba[0x0D] == (byte)0x01) &&
(ba[0x3B] == (byte)0x00) && (ba[0x3C] > 0)) {

Expand All @@ -65,18 +70,8 @@ DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
// ba[0x3B]: length of session ID
// ba[0x3C]: length of cookie

if (!needInvalidRecords) {
// The 2nd ClientHello with cookie. The 1st one should be
// rejected as expected.
//
// This may happen if the last few bytes of the packet are
// for supported_version extension.
throw new RuntimeException(
"the crashed handshake message was rejected as expected");
}

// ClientHello with cookie
needInvalidRecords = false;
needInvalidRecords.set(false);
System.out.println("invalidate ClientHello message");
if (ba[ba.length - 1] == (byte)0xFF) {
ba[ba.length - 1] = (byte)0xFE;
Expand Down

1 comment on commit 0073602

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.