blob: acc9b944b744efd414c025cea2c9b2d5fe274fe3 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.io;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.annotation.Stress;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
{
private static SslContextFactory __sslCtxFactory=new SslContextFactory();
private static ByteBufferPool __byteBufferPool = new MappedByteBufferPool();
@BeforeClass
public static void initSslEngine() throws Exception
{
File keystore = MavenTestingUtils.getTestResourceFile("keystore");
__sslCtxFactory.setKeyStorePath(keystore.getAbsolutePath());
__sslCtxFactory.setKeyStorePassword("storepwd");
__sslCtxFactory.setKeyManagerPassword("keypwd");
__sslCtxFactory.setEndpointIdentificationAlgorithm("");
__sslCtxFactory.start();
}
@Override
protected Socket newClient() throws IOException
{
SSLSocket socket = __sslCtxFactory.newSslSocket();
socket.connect(_connector.socket().getLocalSocketAddress());
return socket;
}
@Override
protected Connection newConnection(SocketChannel channel, EndPoint endpoint)
{
SSLEngine engine = __sslCtxFactory.newSSLEngine();
engine.setUseClientMode(false);
SslConnection sslConnection = new SslConnection(__byteBufferPool, _threadPool, endpoint, engine);
sslConnection.setRenegotiationAllowed(__sslCtxFactory.isRenegotiationAllowed());
sslConnection.setRenegotiationLimit(__sslCtxFactory.getRenegotiationLimit());
Connection appConnection = super.newConnection(channel,sslConnection.getDecryptedEndPoint());
sslConnection.getDecryptedEndPoint().setConnection(appConnection);
return sslConnection;
}
@Test
@Override
public void testEcho() throws Exception
{
super.testEcho();
}
@Ignore // SSL does not do half closes
@Override
public void testShutdown() throws Exception
{
}
@Test
public void testTcpClose() throws Exception
{
// This test replaces SSLSocket() with a very manual SSL client
// so we can close TCP underneath SSL.
SocketChannel client = SocketChannel.open(_connector.socket().getLocalSocketAddress());
client.socket().setSoTimeout(500);
SocketChannel server = _connector.accept();
server.configureBlocking(false);
_manager.accept(server);
SSLEngine engine = __sslCtxFactory.newSSLEngine();
engine.setUseClientMode(true);
engine.beginHandshake();
ByteBuffer appOut = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize());
ByteBuffer sslOut = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()*2);
ByteBuffer appIn = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize());
ByteBuffer sslIn = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()*2);
boolean debug=false;
if (debug) System.err.println(engine.getHandshakeStatus());
int loop=20;
while (engine.getHandshakeStatus()!=HandshakeStatus.NOT_HANDSHAKING)
{
if (--loop==0)
throw new IllegalStateException();
if (engine.getHandshakeStatus()==HandshakeStatus.NEED_WRAP)
{
if (debug) System.err.printf("sslOut %d-%d-%d%n",sslOut.position(),sslOut.limit(),sslOut.capacity());
if (debug) System.err.printf("appOut %d-%d-%d%n",appOut.position(),appOut.limit(),appOut.capacity());
SSLEngineResult result =engine.wrap(appOut,sslOut);
if (debug) System.err.println(result);
sslOut.flip();
int flushed=client.write(sslOut);
if (debug) System.err.println("out="+flushed);
sslOut.clear();
}
if (engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
{
if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity());
if (sslIn.position()==0)
{
int filled=client.read(sslIn);
if (debug) System.err.println("in="+filled);
}
sslIn.flip();
if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity());
SSLEngineResult result =engine.unwrap(sslIn,appIn);
if (debug) System.err.println(result);
if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity());
if (sslIn.hasRemaining())
sslIn.compact();
else
sslIn.clear();
if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity());
}
if (engine.getHandshakeStatus()==HandshakeStatus.NEED_TASK)
{
Runnable task;
while ((task=engine.getDelegatedTask())!=null)
task.run();
if (debug) System.err.println(engine.getHandshakeStatus());
}
}
if (debug) System.err.println("\nSay Hello");
// write a message
appOut.put("HelloWorld".getBytes(StandardCharsets.UTF_8));
appOut.flip();
SSLEngineResult result =engine.wrap(appOut,sslOut);
if (debug) System.err.println(result);
sslOut.flip();
int flushed=client.write(sslOut);
if (debug) System.err.println("out="+flushed);
sslOut.clear();
appOut.clear();
// read the response
int filled=client.read(sslIn);
if (debug) System.err.println("in="+filled);
sslIn.flip();
result =engine.unwrap(sslIn,appIn);
if (debug) System.err.println(result);
if (sslIn.hasRemaining())
sslIn.compact();
else
sslIn.clear();
appIn.flip();
String reply= new String(appIn.array(),appIn.arrayOffset(),appIn.remaining());
appIn.clear();
Assert.assertEquals("HelloWorld",reply);
if (debug) System.err.println("Shutting down output");
client.socket().shutdownOutput();
filled=client.read(sslIn);
if (debug) System.err.println("in="+filled);
if (filled>=0)
{
// this is the old behaviour.
sslIn.flip();
try
{
// Since the client closed abruptly, the server is sending a close alert with a failure
engine.unwrap(sslIn, appIn);
Assert.fail();
}
catch (SSLException x)
{
// Expected
}
}
sslIn.clear();
filled=client.read(sslIn);
Assert.assertEquals(-1,filled);
Thread.sleep(100); // TODO This should not be needed
Assert.assertFalse(server.isOpen());
}
@Test
@Override
public void testWriteBlocked() throws Exception
{
super.testWriteBlocked();
}
@Override
public void testReadBlocked() throws Exception
{
super.testReadBlocked();
}
@Override
public void testIdle() throws Exception
{
super.testIdle();
}
@Test
@Override
@Stress("Requires a relatively idle (network wise) environment")
public void testStress() throws Exception
{
super.testStress();
}
@Test
public void checkSslEngineBehaviour() throws Exception
{
SSLEngine server = __sslCtxFactory.newSSLEngine();
SSLEngine client = __sslCtxFactory.newSSLEngine();
ByteBuffer netC2S = ByteBuffer.allocate(server.getSession().getPacketBufferSize());
ByteBuffer netS2C = ByteBuffer.allocate(server.getSession().getPacketBufferSize());
ByteBuffer serverIn = ByteBuffer.allocate(server.getSession().getApplicationBufferSize());
ByteBuffer serverOut = ByteBuffer.allocate(server.getSession().getApplicationBufferSize());
ByteBuffer clientIn = ByteBuffer.allocate(client.getSession().getApplicationBufferSize());
SSLEngineResult result;
// start the client
client.setUseClientMode(true);
client.beginHandshake();
Assert.assertEquals(HandshakeStatus.NEED_WRAP,client.getHandshakeStatus());
// what if we try an unwrap?
netS2C.flip();
result=client.unwrap(netS2C,clientIn);
// unwrap is a noop
assertEquals(SSLEngineResult.Status.OK,result.getStatus());
assertEquals(0,result.bytesConsumed());
assertEquals(0,result.bytesProduced());
assertEquals(HandshakeStatus.NEED_WRAP,result.getHandshakeStatus());
netS2C.clear();
// do the needed WRAP of empty buffer
result=client.wrap(BufferUtil.EMPTY_BUFFER,netC2S);
// unwrap is a noop
assertEquals(SSLEngineResult.Status.OK,result.getStatus());
assertEquals(0,result.bytesConsumed());
assertThat(result.bytesProduced(),greaterThan(0));
assertEquals(HandshakeStatus.NEED_UNWRAP,result.getHandshakeStatus());
netC2S.flip();
assertEquals(netC2S.remaining(),result.bytesProduced());
// start the server
server.setUseClientMode(false);
server.beginHandshake();
Assert.assertEquals(HandshakeStatus.NEED_UNWRAP,server.getHandshakeStatus());
// what if we try a needless wrap?
serverOut.put(BufferUtil.toBuffer("Hello World"));
serverOut.flip();
result=server.wrap(serverOut,netS2C);
// wrap is a noop
assertEquals(SSLEngineResult.Status.OK,result.getStatus());
assertEquals(0,result.bytesConsumed());
assertEquals(0,result.bytesProduced());
assertEquals(HandshakeStatus.NEED_UNWRAP,result.getHandshakeStatus());
// Do the needed unwrap, to an empty buffer
result=server.unwrap(netC2S,BufferUtil.EMPTY_BUFFER);
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW,result.getStatus());
assertEquals(0,result.bytesConsumed());
assertEquals(0,result.bytesProduced());
assertEquals(HandshakeStatus.NEED_UNWRAP,result.getHandshakeStatus());
// Do the needed unwrap, to a full buffer
serverIn.position(serverIn.limit());
result=server.unwrap(netC2S,serverIn);
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW,result.getStatus());
assertEquals(0,result.bytesConsumed());
assertEquals(0,result.bytesProduced());
assertEquals(HandshakeStatus.NEED_UNWRAP,result.getHandshakeStatus());
// Do the needed unwrap, to an empty buffer
serverIn.clear();
result=server.unwrap(netC2S,serverIn);
assertEquals(SSLEngineResult.Status.OK,result.getStatus());
assertThat(result.bytesConsumed(),greaterThan(0));
assertEquals(0,result.bytesProduced());
assertEquals(HandshakeStatus.NEED_TASK,result.getHandshakeStatus());
server.getDelegatedTask().run();
assertEquals(HandshakeStatus.NEED_WRAP,server.getHandshakeStatus());
}
}