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