/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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.undertow.protocols.ssl;

import io.undertow.server.DefaultByteBufferPool;
import org.xnio.ChannelListener;
import org.xnio.ChannelListeners;
import org.xnio.FutureResult;
import org.xnio.IoFuture;
import org.xnio.IoUtils;
import org.xnio.Option;
import org.xnio.OptionMap;
import io.undertow.connector.ByteBufferPool;
import org.xnio.StreamConnection;
import org.xnio.Xnio;
import org.xnio.XnioExecutor;
import org.xnio.XnioIoThread;
import org.xnio.XnioWorker;
import org.xnio.channels.AcceptingChannel;
import org.xnio.channels.AssembledConnectedSslStreamChannel;
import org.xnio.channels.BoundChannel;
import org.xnio.channels.ConnectedSslStreamChannel;
import org.xnio.channels.ConnectedStreamChannel;
import org.xnio.ssl.JsseSslUtils;
import org.xnio.ssl.JsseXnioSsl;
import org.xnio.ssl.SslConnection;
import org.xnio.ssl.XnioSsl;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.concurrent.TimeUnit;

import static org.xnio.IoUtils.safeClose;

Author:Stuart Douglas
/** * @author Stuart Douglas */
public class UndertowXnioSsl extends XnioSsl { private static final ByteBufferPool DEFAULT_BUFFER_POOL = new DefaultByteBufferPool(true, 17 * 1024, -1, 12); private final ByteBufferPool bufferPool; private volatile SSLContext sslContext;
Construct a new instance.
Params:
  • xnio – the XNIO instance to associate with
  • optionMap – the options for this provider
Throws:
/** * Construct a new instance. * * @param xnio the XNIO instance to associate with * @param optionMap the options for this provider * @throws java.security.NoSuchProviderException if the given SSL provider is not found * @throws java.security.NoSuchAlgorithmException if the given SSL algorithm is not supported * @throws java.security.KeyManagementException if the SSL context could not be initialized */
public UndertowXnioSsl(final Xnio xnio, final OptionMap optionMap) throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException { this(xnio, optionMap, DEFAULT_BUFFER_POOL, JsseSslUtils.createSSLContext(optionMap)); }
Construct a new instance. @param xnio the XNIO instance to associate with
Params:
  • optionMap – the options for this provider
  • sslContext – the SSL context to use for this instance
/** * Construct a new instance. * @param xnio the XNIO instance to associate with * @param optionMap the options for this provider * @param sslContext the SSL context to use for this instance */
public UndertowXnioSsl(final Xnio xnio, final OptionMap optionMap, final SSLContext sslContext) { this(xnio, optionMap, DEFAULT_BUFFER_POOL, sslContext); }
Construct a new instance.
Params:
  • xnio – the XNIO instance to associate with
  • optionMap – the options for this provider
  • bufferPool –
Throws:
/** * Construct a new instance. * * @param xnio the XNIO instance to associate with * @param optionMap the options for this provider * @param bufferPool * @throws java.security.NoSuchProviderException if the given SSL provider is not found * @throws java.security.NoSuchAlgorithmException if the given SSL algorithm is not supported * @throws java.security.KeyManagementException if the SSL context could not be initialized */
public UndertowXnioSsl(final Xnio xnio, final OptionMap optionMap, ByteBufferPool bufferPool) throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException { this(xnio, optionMap, bufferPool, JsseSslUtils.createSSLContext(optionMap)); }
Construct a new instance. @param xnio the XNIO instance to associate with
Params:
  • optionMap – the options for this provider
  • bufferPool –
  • sslContext – the SSL context to use for this instance
/** * Construct a new instance. * @param xnio the XNIO instance to associate with * @param optionMap the options for this provider * @param bufferPool * @param sslContext the SSL context to use for this instance */
public UndertowXnioSsl(final Xnio xnio, final OptionMap optionMap, ByteBufferPool bufferPool, final SSLContext sslContext) { super(xnio, sslContext, optionMap); this.bufferPool = bufferPool; this.sslContext = sslContext; }
Get the JSSE SSL context for this provider instance.
Returns:the SSL context
/** * Get the JSSE SSL context for this provider instance. * * @return the SSL context */
@SuppressWarnings("unused") public SSLContext getSslContext() { return sslContext; }
Get the SSL engine for a given connection.
Returns:the SSL engine
/** * Get the SSL engine for a given connection. * * @return the SSL engine */
public static SSLEngine getSslEngine(SslConnection connection) { if (connection instanceof UndertowSslConnection) { return ((UndertowSslConnection) connection).getSSLEngine(); } else { return JsseXnioSsl.getSslEngine(connection); } } public static SslConduit getSslConduit(SslConnection connection) { return ((UndertowSslConnection) connection).getSslConduit(); } @SuppressWarnings("deprecation") public IoFuture<ConnectedSslStreamChannel> connectSsl(final XnioWorker worker, final InetSocketAddress bindAddress, final InetSocketAddress destination, final ChannelListener<? super ConnectedSslStreamChannel> openListener, final ChannelListener<? super BoundChannel> bindListener, final OptionMap optionMap) { final FutureResult<ConnectedSslStreamChannel> futureResult = new FutureResult<>(IoUtils.directExecutor()); final IoFuture<SslConnection> futureSslConnection = openSslConnection(worker, bindAddress, destination, new ChannelListener<SslConnection>() { public void handleEvent(final SslConnection sslConnection) { final ConnectedSslStreamChannel assembledChannel = new AssembledConnectedSslStreamChannel(sslConnection, sslConnection.getSourceChannel(), sslConnection.getSinkChannel()); if (!futureResult.setResult(assembledChannel)) { safeClose(assembledChannel); } else { ChannelListeners.invokeChannelListener(assembledChannel, openListener); } } }, bindListener, optionMap).addNotifier(new IoFuture.HandlingNotifier<SslConnection, FutureResult<ConnectedSslStreamChannel>>() { public void handleCancelled(final FutureResult<ConnectedSslStreamChannel> result) { result.setCancelled(); } public void handleFailed(final IOException exception, final FutureResult<ConnectedSslStreamChannel> result) { result.setException(exception); } }, futureResult); futureResult.getIoFuture().addNotifier(new IoFuture.HandlingNotifier<ConnectedStreamChannel, IoFuture<SslConnection>>() { public void handleCancelled(final IoFuture<SslConnection> result) { result.cancel(); } }, futureSslConnection); futureResult.addCancelHandler(futureSslConnection); return futureResult.getIoFuture(); } public IoFuture<SslConnection> openSslConnection(final XnioWorker worker, final InetSocketAddress bindAddress, final InetSocketAddress destination, final ChannelListener<? super SslConnection> openListener, final ChannelListener<? super BoundChannel> bindListener, final OptionMap optionMap) { final FutureResult<SslConnection> futureResult = new FutureResult<>(worker); final IoFuture<StreamConnection> connection = worker.openStreamConnection(bindAddress, destination, new StreamConnectionChannelListener(optionMap, destination, futureResult, openListener), bindListener, optionMap); return setupSslConnection(futureResult, connection); } @Override public IoFuture<SslConnection> openSslConnection(final XnioIoThread ioThread, final InetSocketAddress bindAddress, final InetSocketAddress destination, final ChannelListener<? super SslConnection> openListener, final ChannelListener<? super BoundChannel> bindListener, final OptionMap optionMap) { final FutureResult<SslConnection> futureResult = new FutureResult<>(ioThread); final IoFuture<StreamConnection> connection = ioThread.openStreamConnection(bindAddress, destination, new StreamConnectionChannelListener(optionMap, destination, futureResult, openListener), bindListener, optionMap); return setupSslConnection(futureResult, connection); } public SslConnection wrapExistingConnection(StreamConnection connection, OptionMap optionMap) { return new UndertowSslConnection(connection, JsseSslUtils.createSSLEngine(sslContext, optionMap, (InetSocketAddress) connection.getPeerAddress()), bufferPool); } private IoFuture<SslConnection> setupSslConnection(FutureResult<SslConnection> futureResult, IoFuture<StreamConnection> connection) { connection.addNotifier(new IoFuture.HandlingNotifier<StreamConnection, FutureResult<SslConnection>>() { public void handleCancelled(final FutureResult<SslConnection> attachment) { attachment.setCancelled(); } public void handleFailed(final IOException exception, final FutureResult<SslConnection> attachment) { attachment.setException(exception); } }, futureResult); futureResult.addCancelHandler(connection); return futureResult.getIoFuture(); } @SuppressWarnings("deprecation") public AcceptingChannel<ConnectedSslStreamChannel> createSslTcpServer(final XnioWorker worker, final InetSocketAddress bindAddress, final ChannelListener<? super AcceptingChannel<ConnectedSslStreamChannel>> acceptListener, final OptionMap optionMap) throws IOException { final AcceptingChannel<SslConnection> server = createSslConnectionServer(worker, bindAddress, null, optionMap); final AcceptingChannel<ConnectedSslStreamChannel> acceptingChannel = new AcceptingChannel<ConnectedSslStreamChannel>() { public ConnectedSslStreamChannel accept() throws IOException { final SslConnection connection = server.accept(); return connection == null ? null : new AssembledConnectedSslStreamChannel(connection, connection.getSourceChannel(), connection.getSinkChannel()); } public ChannelListener.Setter<? extends AcceptingChannel<ConnectedSslStreamChannel>> getAcceptSetter() { return ChannelListeners.getDelegatingSetter(server.getAcceptSetter(), this); } public ChannelListener.Setter<? extends AcceptingChannel<ConnectedSslStreamChannel>> getCloseSetter() { return ChannelListeners.getDelegatingSetter(server.getCloseSetter(), this); } public SocketAddress getLocalAddress() { return server.getLocalAddress(); } public <A extends SocketAddress> A getLocalAddress(final Class<A> type) { return server.getLocalAddress(type); } public void suspendAccepts() { server.suspendAccepts(); } public void resumeAccepts() { server.resumeAccepts(); } public boolean isAcceptResumed() { return server.isAcceptResumed(); } public void wakeupAccepts() { server.wakeupAccepts(); } public void awaitAcceptable() throws IOException { server.awaitAcceptable(); } public void awaitAcceptable(final long time, final TimeUnit timeUnit) throws IOException { server.awaitAcceptable(time, timeUnit); } public XnioWorker getWorker() { return server.getWorker(); } @Deprecated public XnioExecutor getAcceptThread() { return server.getAcceptThread(); } public XnioIoThread getIoThread() { return server.getIoThread(); } public void close() throws IOException { server.close(); } public boolean isOpen() { return server.isOpen(); } public boolean supportsOption(final Option<?> option) { return server.supportsOption(option); } public <T> T getOption(final Option<T> option) throws IOException { return server.getOption(option); } public <T> T setOption(final Option<T> option, final T value) throws IllegalArgumentException, IOException { return server.setOption(option, value); } }; acceptingChannel.getAcceptSetter().set(acceptListener); return acceptingChannel; }
Updates the SSLContext that is in use. All new connections will use this new context, however established connections will not be affected.
Params:
  • context – The new context
/** * Updates the SSLContext that is in use. All new connections will use this new context, however established connections * will not be affected. * * @param context The new context */
public void updateSSLContext(SSLContext context) { this.sslContext = context; } public AcceptingChannel<SslConnection> createSslConnectionServer(final XnioWorker worker, final InetSocketAddress bindAddress, final ChannelListener<? super AcceptingChannel<SslConnection>> acceptListener, final OptionMap optionMap) throws IOException { final UndertowAcceptingSslChannel server = new UndertowAcceptingSslChannel(this, worker.createStreamConnectionServer(bindAddress, null, optionMap), optionMap, bufferPool, false); if (acceptListener != null) server.getAcceptSetter().set(acceptListener); return server; } private class StreamConnectionChannelListener implements ChannelListener<StreamConnection> { private final OptionMap optionMap; private final InetSocketAddress destination; private final FutureResult<SslConnection> futureResult; private final ChannelListener<? super SslConnection> openListener; StreamConnectionChannelListener(OptionMap optionMap, InetSocketAddress destination, FutureResult<SslConnection> futureResult, ChannelListener<? super SslConnection> openListener) { this.optionMap = optionMap; this.destination = destination; this.futureResult = futureResult; this.openListener = openListener; } public void handleEvent(final StreamConnection connection) { try { final SslConnection wrappedConnection = new UndertowSslConnection(connection, JsseSslUtils.createSSLEngine(sslContext, optionMap, destination), bufferPool); if (!futureResult.setResult(wrappedConnection)) { IoUtils.safeClose(connection); } else { ChannelListeners.invokeChannelListener(wrappedConnection, openListener); } } catch (Throwable e) { futureResult.setException(new IOException(e)); } } } }