7df012884f
Motivation Per javadoc in 4.1.x SimpleChannelInboundHandler: "Please keep in mind that channelRead0(ChannelHandlerContext, I) will be renamed to messageReceived(ChannelHandlerContext, I) in 5.0." Modifications Rename aforementioned method and all references/overrides. Result Method is renamed.
273 lines
10 KiB
Java
273 lines
10 KiB
Java
/*
|
|
* Copyright 2015 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
package io.netty.testsuite.transport.socket;
|
|
|
|
import io.netty.bootstrap.Bootstrap;
|
|
import io.netty.bootstrap.ServerBootstrap;
|
|
import io.netty.buffer.ByteBuf;
|
|
import io.netty.buffer.ByteBufAllocator;
|
|
import io.netty.channel.Channel;
|
|
import io.netty.channel.ChannelHandler.Sharable;
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
import io.netty.channel.ChannelInitializer;
|
|
import io.netty.channel.SimpleChannelInboundHandler;
|
|
import io.netty.handler.codec.DecoderException;
|
|
import io.netty.handler.ssl.OpenSsl;
|
|
import io.netty.handler.ssl.SslContext;
|
|
import io.netty.handler.ssl.SslContextBuilder;
|
|
import io.netty.handler.ssl.SslHandler;
|
|
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
|
|
import io.netty.handler.ssl.SslProvider;
|
|
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
import io.netty.util.concurrent.Future;
|
|
import io.netty.util.internal.logging.InternalLogger;
|
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
import org.junit.Assume;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.junit.runners.Parameterized;
|
|
import org.junit.runners.Parameterized.Parameters;
|
|
|
|
import java.io.File;
|
|
import java.nio.channels.ClosedChannelException;
|
|
import java.security.cert.CertificateException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
import javax.net.ssl.SSLHandshakeException;
|
|
|
|
import static org.junit.Assert.assertSame;
|
|
import static org.junit.Assert.assertTrue;
|
|
import static org.junit.Assert.fail;
|
|
|
|
@RunWith(Parameterized.class)
|
|
public class SocketSslClientRenegotiateTest extends AbstractSocketTest {
|
|
|
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(
|
|
SocketSslClientRenegotiateTest.class);
|
|
private static final File CERT_FILE;
|
|
private static final File KEY_FILE;
|
|
|
|
static {
|
|
SelfSignedCertificate ssc;
|
|
try {
|
|
ssc = new SelfSignedCertificate();
|
|
} catch (CertificateException e) {
|
|
throw new Error(e);
|
|
}
|
|
CERT_FILE = ssc.certificate();
|
|
KEY_FILE = ssc.privateKey();
|
|
}
|
|
|
|
@Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}, delegate = {2}")
|
|
public static Collection<Object[]> data() throws Exception {
|
|
List<SslContext> serverContexts = new ArrayList<>();
|
|
List<SslContext> clientContexts = new ArrayList<>();
|
|
clientContexts.add(
|
|
SslContextBuilder.forClient()
|
|
.trustManager(CERT_FILE)
|
|
.sslProvider(SslProvider.JDK)
|
|
.build()
|
|
);
|
|
|
|
boolean hasOpenSsl = OpenSsl.isAvailable();
|
|
if (hasOpenSsl) {
|
|
serverContexts.add(
|
|
SslContextBuilder.forServer(CERT_FILE, KEY_FILE)
|
|
.sslProvider(SslProvider.OPENSSL)
|
|
.build()
|
|
);
|
|
} else {
|
|
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
|
|
}
|
|
|
|
List<Object[]> params = new ArrayList<>();
|
|
for (SslContext sc: serverContexts) {
|
|
for (SslContext cc: clientContexts) {
|
|
for (int i = 0; i < 32; i++) {
|
|
params.add(new Object[] { sc, cc, true});
|
|
params.add(new Object[] { sc, cc, false});
|
|
}
|
|
}
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
private final SslContext serverCtx;
|
|
private final SslContext clientCtx;
|
|
private final boolean delegate;
|
|
|
|
private final AtomicReference<Throwable> clientException = new AtomicReference<>();
|
|
private final AtomicReference<Throwable> serverException = new AtomicReference<>();
|
|
|
|
private volatile Channel clientChannel;
|
|
private volatile Channel serverChannel;
|
|
|
|
private volatile SslHandler clientSslHandler;
|
|
private volatile SslHandler serverSslHandler;
|
|
|
|
private final TestHandler clientHandler = new TestHandler(clientException);
|
|
|
|
private final TestHandler serverHandler = new TestHandler(serverException);
|
|
|
|
public SocketSslClientRenegotiateTest(
|
|
SslContext serverCtx, SslContext clientCtx, boolean delegate) {
|
|
this.serverCtx = serverCtx;
|
|
this.clientCtx = clientCtx;
|
|
this.delegate = delegate;
|
|
}
|
|
|
|
@Test(timeout = 30000)
|
|
public void testSslRenegotiationRejected() throws Throwable {
|
|
// BoringSSL does not support renegotiation intentionally.
|
|
Assume.assumeFalse("BoringSSL".equals(OpenSsl.versionString()));
|
|
Assume.assumeTrue(OpenSsl.isAvailable());
|
|
run();
|
|
}
|
|
|
|
private static SslHandler newSslHandler(SslContext sslCtx, ByteBufAllocator allocator, Executor executor) {
|
|
if (executor == null) {
|
|
return sslCtx.newHandler(allocator);
|
|
} else {
|
|
return sslCtx.newHandler(allocator, executor);
|
|
}
|
|
}
|
|
|
|
public void testSslRenegotiationRejected(ServerBootstrap sb, Bootstrap cb) throws Throwable {
|
|
reset();
|
|
|
|
final ExecutorService executorService = delegate ? Executors.newCachedThreadPool() : null;
|
|
|
|
try {
|
|
sb.childHandler(new ChannelInitializer<Channel>() {
|
|
@Override
|
|
@SuppressWarnings("deprecation")
|
|
public void initChannel(Channel sch) throws Exception {
|
|
serverChannel = sch;
|
|
serverSslHandler = newSslHandler(serverCtx, sch.alloc(), executorService);
|
|
// As we test renegotiation we should use a protocol that support it.
|
|
serverSslHandler.engine().setEnabledProtocols(new String[]{"TLSv1.2"});
|
|
sch.pipeline().addLast("ssl", serverSslHandler);
|
|
sch.pipeline().addLast("handler", serverHandler);
|
|
}
|
|
});
|
|
|
|
cb.handler(new ChannelInitializer<Channel>() {
|
|
@Override
|
|
@SuppressWarnings("deprecation")
|
|
public void initChannel(Channel sch) throws Exception {
|
|
clientChannel = sch;
|
|
clientSslHandler = newSslHandler(clientCtx, sch.alloc(), executorService);
|
|
// As we test renegotiation we should use a protocol that support it.
|
|
clientSslHandler.engine().setEnabledProtocols(new String[]{"TLSv1.2"});
|
|
sch.pipeline().addLast("ssl", clientSslHandler);
|
|
sch.pipeline().addLast("handler", clientHandler);
|
|
}
|
|
});
|
|
|
|
Channel sc = sb.bind().sync().channel();
|
|
cb.connect(sc.localAddress()).sync();
|
|
|
|
Future<Channel> clientHandshakeFuture = clientSslHandler.handshakeFuture();
|
|
clientHandshakeFuture.sync();
|
|
|
|
String renegotiation = clientSslHandler.engine().getEnabledCipherSuites()[0];
|
|
// Use the first previous enabled ciphersuite and try to renegotiate.
|
|
clientSslHandler.engine().setEnabledCipherSuites(new String[]{renegotiation});
|
|
clientSslHandler.renegotiate().await();
|
|
serverChannel.close().awaitUninterruptibly();
|
|
clientChannel.close().awaitUninterruptibly();
|
|
sc.close().awaitUninterruptibly();
|
|
try {
|
|
if (serverException.get() != null) {
|
|
throw serverException.get();
|
|
}
|
|
fail();
|
|
} catch (DecoderException e) {
|
|
assertTrue(e.getCause() instanceof SSLHandshakeException);
|
|
}
|
|
if (clientException.get() != null) {
|
|
throw clientException.get();
|
|
}
|
|
} finally {
|
|
if (executorService != null) {
|
|
executorService.shutdown();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void reset() {
|
|
clientException.set(null);
|
|
serverException.set(null);
|
|
clientHandler.handshakeCounter = 0;
|
|
serverHandler.handshakeCounter = 0;
|
|
clientChannel = null;
|
|
serverChannel = null;
|
|
|
|
clientSslHandler = null;
|
|
serverSslHandler = null;
|
|
}
|
|
|
|
@Sharable
|
|
private static final class TestHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
|
|
|
protected final AtomicReference<Throwable> exception;
|
|
private int handshakeCounter;
|
|
|
|
TestHandler(AtomicReference<Throwable> exception) {
|
|
this.exception = exception;
|
|
}
|
|
|
|
@Override
|
|
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
|
|
ctx.flush();
|
|
}
|
|
|
|
@Override
|
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
exception.compareAndSet(null, cause);
|
|
ctx.close();
|
|
}
|
|
|
|
@Override
|
|
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
|
if (evt instanceof SslHandshakeCompletionEvent) {
|
|
SslHandshakeCompletionEvent handshakeEvt = (SslHandshakeCompletionEvent) evt;
|
|
if (handshakeCounter == 0) {
|
|
handshakeCounter++;
|
|
if (handshakeEvt.cause() != null) {
|
|
logger.warn("Handshake failed:", handshakeEvt.cause());
|
|
}
|
|
assertSame(SslHandshakeCompletionEvent.SUCCESS, evt);
|
|
} else {
|
|
if (ctx.channel().parent() == null) {
|
|
assertTrue(handshakeEvt.cause() instanceof ClosedChannelException);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void messageReceived(ChannelHandlerContext ctx, ByteBuf in) throws Exception { }
|
|
}
|
|
}
|