/* * 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; } }