SslHandler.setHandshakeTimeout*(...) should also been enforced on the server side.
Motivation: We should also enforce the handshake timeout on the server-side to allow closing connections which will not finish the handshake in an expected amount of time. Modifications: - Enforce the timeout on the server and client side - Add unit test. Result: Fixes [#7230].
This commit is contained in:
parent
dcb828f02f
commit
521e87984d
@ -1536,11 +1536,15 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
|
public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
|
|
||||||
if (ctx.channel().isActive() && engine.getUseClientMode()) {
|
if (ctx.channel().isActive()) {
|
||||||
// Begin the initial handshake.
|
if (engine.getUseClientMode()) {
|
||||||
// channelActive() event has been fired already, which means this.channelActive() will
|
// Begin the initial handshake.
|
||||||
// not be invoked. We have to initialize here instead.
|
// channelActive() event has been fired already, which means this.channelActive() will
|
||||||
handshake(null);
|
// not be invoked. We have to initialize here instead.
|
||||||
|
handshake(null);
|
||||||
|
} else {
|
||||||
|
applyHandshakeTimeout(null);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// channelActive() event has not been fired yet. this.channelOpen() will be invoked
|
// channelActive() event has not been fired yet. this.channelOpen() will be invoked
|
||||||
// and initialization will occur there.
|
// and initialization will occur there.
|
||||||
@ -1635,17 +1639,21 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
} finally {
|
} finally {
|
||||||
forceFlush(ctx);
|
forceFlush(ctx);
|
||||||
}
|
}
|
||||||
|
applyHandshakeTimeout(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyHandshakeTimeout(Promise<Channel> p) {
|
||||||
|
final Promise<Channel> promise = p == null ? handshakePromise : p;
|
||||||
// Set timeout if necessary.
|
// Set timeout if necessary.
|
||||||
final long handshakeTimeoutMillis = this.handshakeTimeoutMillis;
|
final long handshakeTimeoutMillis = this.handshakeTimeoutMillis;
|
||||||
if (handshakeTimeoutMillis <= 0 || p.isDone()) {
|
if (handshakeTimeoutMillis <= 0 || promise.isDone()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ScheduledFuture<?> timeoutFuture = ctx.executor().schedule(new Runnable() {
|
final ScheduledFuture<?> timeoutFuture = ctx.executor().schedule(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (p.isDone()) {
|
if (promise.isDone()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
notifyHandshakeFailure(HANDSHAKE_TIMED_OUT);
|
notifyHandshakeFailure(HANDSHAKE_TIMED_OUT);
|
||||||
@ -1653,7 +1661,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
|
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
// Cancel the handshake timeout when handshake is finished.
|
// Cancel the handshake timeout when handshake is finished.
|
||||||
p.addListener(new FutureListener<Channel>() {
|
promise.addListener(new FutureListener<Channel>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(Future<Channel> f) throws Exception {
|
public void operationComplete(Future<Channel> f) throws Exception {
|
||||||
timeoutFuture.cancel(false);
|
timeoutFuture.cancel(false);
|
||||||
@ -1671,9 +1679,13 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
|
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
|
||||||
if (!startTls && engine.getUseClientMode()) {
|
if (!startTls) {
|
||||||
// Begin the initial handshake
|
if (engine.getUseClientMode()) {
|
||||||
handshake(null);
|
// Begin the initial handshake.
|
||||||
|
handshake(null);
|
||||||
|
} else {
|
||||||
|
applyHandshakeTimeout(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.fireChannelActive();
|
ctx.fireChannelActive();
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,36 @@ import static org.junit.Assume.assumeTrue;
|
|||||||
|
|
||||||
public class SslHandlerTest {
|
public class SslHandlerTest {
|
||||||
|
|
||||||
|
@Test(expected = SSLException.class, timeout = 3000)
|
||||||
|
public void testClientHandshakeTimeout() throws Exception {
|
||||||
|
testHandshakeTimeout(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = SSLException.class, timeout = 3000)
|
||||||
|
public void testServerHandshakeTimeout() throws Exception {
|
||||||
|
testHandshakeTimeout(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testHandshakeTimeout(boolean client) throws Exception {
|
||||||
|
SSLEngine engine = SSLContext.getDefault().createSSLEngine();
|
||||||
|
engine.setUseClientMode(client);
|
||||||
|
SslHandler handler = new SslHandler(engine);
|
||||||
|
handler.setHandshakeTimeoutMillis(1);
|
||||||
|
|
||||||
|
EmbeddedChannel ch = new EmbeddedChannel(handler);
|
||||||
|
try {
|
||||||
|
while (!handler.handshakeFuture().isDone()) {
|
||||||
|
Thread.sleep(10);
|
||||||
|
// We need to run all pending tasks as the handshake timeout is scheduled on the EventLoop.
|
||||||
|
ch.runPendingTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.handshakeFuture().syncUninterruptibly();
|
||||||
|
} finally {
|
||||||
|
ch.finishAndReleaseAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTruncatedPacket() throws Exception {
|
public void testTruncatedPacket() throws Exception {
|
||||||
SSLEngine engine = SSLContext.getDefault().createSSLEngine();
|
SSLEngine engine = SSLContext.getDefault().createSSLEngine();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user