blob: d2464590e99e86d9567202ba35d14e48ec6ab743 [file] [log] [blame]
/*
* Copyright (c) 2011, 2012, 2015 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
* Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
*/
package org.eclipse.net4j.internal.tcp.ssl;
import org.eclipse.net4j.internal.tcp.TCPConnector;
import org.eclipse.net4j.internal.tcp.bundle.OM;
import org.eclipse.net4j.tcp.ITCPSelector;
import org.eclipse.net4j.tcp.ssl.SSLUtil;
import org.eclipse.net4j.util.concurrent.ConcurrencyUtil;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
/**
* SSLConnector responses to perform tasks same as TCPConnector but it attached the SSL functionality into read and
* write method.
*
* @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
* @author Caspar De Groot (No Magic Asia Ltd.)
* @since 4.0
*/
public class SSLConnector extends TCPConnector
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, SSLConnector.class);
private SSLEngineManager sslEngineManager;
@Override
public String getProtocolString()
{
return "ssl://";
}
@Override
public void handleConnect(ITCPSelector selector, SocketChannel channel)
{
super.handleConnect(selector, channel);
if (!sslEngineManager.isHandshakeComplete() && isClient())
{
getConfig().getReceiveExecutor().execute(createHandShakeTask(channel));
}
}
@Override
public void handleRegistration(ITCPSelector selector, SocketChannel socketChannel)
{
super.handleRegistration(selector, socketChannel);
if (!sslEngineManager.isHandshakeComplete() && isServer())
{
getConfig().getReceiveExecutor().execute(createHandShakeTask(socketChannel));
}
}
@Override
public void handleRead(ITCPSelector selector, SocketChannel socketChannel)
{
waitForHandShakeFinish();
super.handleRead(selector, socketChannel);
checkRehandShake(socketChannel);
// Handle the left data from reading multiple data at once time.
while (sslEngineManager.getPacketRecvBuf().position() > 0)
{
super.handleRead(selector, socketChannel);
checkRehandShake(socketChannel);
}
}
@Override
public void handleWrite(ITCPSelector selector, SocketChannel socketChannel)
{
waitForHandShakeFinish();
super.handleWrite(selector, socketChannel);
checkRehandShake(socketChannel);
}
@Override
protected void doActivate() throws Exception
{
try
{
boolean isClient = isClient();
String host = getHost();
int port = getPort();
sslEngineManager = new SSLEngineManager(isClient, host, port, getConfig().getReceiveExecutor());
// Set the buffer provider of the config instance in order to replace
// BufferFactory instance with SSLBufferFactory instance.
getConfig().setBufferProvider(new SSLBufferFactory(sslEngineManager));
}
catch (Exception ex)
{
if (TRACER.isEnabled())
{
TRACER.trace("Cannot activate the ssl engine.", ex); //$NON-NLS-1$
}
throw ex;
}
super.doActivate();
}
@Override
protected void doDeactivate() throws Exception
{
try
{
sslEngineManager.close();
}
catch (Exception ex)
{
if (TRACER.isEnabled())
{
TRACER.trace("Cannot deactivate the ssl engine.", ex); //$NON-NLS-1$
}
}
finally
{
super.doDeactivate();
}
}
/**
* Toggles between OP_READ and OP_WRITE
* <p>
* (Having both OP_READ and OP_WRITE interests on a socketChannel is not a good idea when the channel is used for
* TLS/SSL communications.)
*/
@Override
protected void doOrderWriteInterest(boolean on)
{
ITCPSelector selector = getSelector();
SelectionKey selectionKey = getSelectionKey();
if (on)
{
selector.orderReadInterest(selectionKey, isClient(), false);
selector.orderWriteInterest(selectionKey, isClient(), true);
}
else
{
// Note: order is different from above!
selector.orderWriteInterest(selectionKey, isClient(), false);
selector.orderReadInterest(selectionKey, isClient(), true);
}
}
private void checkRehandShake(SocketChannel socketChannel)
{
if (!isClosed())
{
try
{
sslEngineManager.checkRehandShake(socketChannel);
}
catch (Exception ex)
{
deactivateAsync();
}
}
}
private void waitForHandShakeFinish()
{
// Wait until handshake finished. If handshake finish it will not enter this loop.
while (!sslEngineManager.isHandshakeComplete())
{
if (isNegotiating())
{
ConcurrencyUtil.sleep(SSLUtil.getHandShakeWaitTime());
}
else if (!isNegotiating() && !isActive())
{
// Prevent sleeping and reading forever.
break;
}
else
{
Thread.yield();
}
}
if (!isNegotiating() && !isActive())
{
try
{
deactivateAsync();
}
catch (Exception ex)
{
OM.LOG.warn(ex);
}
}
}
private Runnable createHandShakeTask(SocketChannel channel)
{
final SocketChannel socket = channel;
Runnable task = new Runnable()
{
public void run()
{
try
{
sslEngineManager.checkInitialHandshake(socket);
}
catch (Exception ex)
{
if (TRACER.isEnabled())
{
TRACER.trace("ssl cannot handshake.", ex); //$NON-NLS-1$
}
deferredActivate(false);
}
}
};
return task;
}
}