org.eclipse.californium.elements.tcp.TlsClientConnector.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.californium.elements.tcp.TlsClientConnector.java

Source

/*******************************************************************************
 * Copyright (c) 2016, 2017 Amazon Web Services and others.
 * <p>
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * <p>
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.html.
 * <p>
 * Contributors:
 * Joe Magerramov (Amazon Web Services) - CoAP over TCP support.
 * Achim Kraus (Bosch Software Innovations GmbH) - create "remote aware" SSLEngine
 * Achim Kraus (Bosch Software Innovations GmbH) - introduce protocol,
 *                                                 remove scheme
 * Achim Kraus (Bosch Software Innovations GmbH) - delay sending message after complete
 *                                                 TLS handshake.
 * Bosch Software Innovations GmbH - migrate to SLF4J
 * Achim Kraus (Bosch Software Innovations GmbH) - add handshake timeout
 * Achim Kraus (Bosch Software Innovations GmbH) - change exception type to
 *                                                 IllegalStateException
 ******************************************************************************/
package org.eclipse.californium.elements.tcp;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.CancellationException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;

import org.eclipse.californium.elements.EndpointContext;
import org.eclipse.californium.elements.EndpointContextMatcher;
import org.eclipse.californium.elements.RawData;
import org.eclipse.californium.elements.TlsEndpointContext;

import io.netty.channel.Channel;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

/**
 * A TLS client connector that establishes outbound TLS connections.
 */
public class TlsClientConnector extends TcpClientConnector {

    private static final int DEFAULT_HANDSHAKE_TIMEOUT_MILLIS = 10000;

    /**
     * Context to be used to for connections.
     */
    private final SSLContext sslContext;
    /**
     * Handshake timeout in milliseconds.
     */
    private final int handshakeTimeoutMillis;

    /**
     * Creates TLS client connector with custom SSL context. Useful for using
     * client keys, or custom trust stores. The context must be initialized by
     * the caller.
     * 
     * @param sslContext ssl context
     * @param numberOfThreads number of thread used by connector
     * @param connectTimeoutMillis tcp connect timeout in milliseconds
     * @param idleTimeout idle timeout in seconds to close unused connection.
     */
    public TlsClientConnector(SSLContext sslContext, int numberOfThreads, int connectTimeoutMillis,
            int idleTimeout) {
        this(sslContext, numberOfThreads, connectTimeoutMillis, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS, idleTimeout);
    }

    /**
     * Creates TLS client connector with custom SSL context. Useful for using
     * client keys, or custom trust stores. The context must be initialized by
     * the caller.
     * 
     * @param sslContext ssl context
     * @param numberOfThreads number of thread used by connector
     * @param connectTimeoutMillis tcp connect timeout in milliseconds
     * @param handshakeTimeoutMillis handshake timeout in milliseconds
     * @param idleTimeout idle timeout in seconds to close unused connection
     */
    public TlsClientConnector(SSLContext sslContext, int numberOfThreads, int connectTimeoutMillis,
            int handshakeTimeoutMillis, int idleTimeout) {
        super(numberOfThreads, connectTimeoutMillis, idleTimeout, new TlsContextUtil(true));
        this.sslContext = sslContext;
        this.handshakeTimeoutMillis = handshakeTimeoutMillis;
    }

    /**
     * {@inheritDoc}
     * 
     * Delay message sending after TLS handshake is completed.
     */
    @Override
    protected void send(final Channel channel, final EndpointContextMatcher endpointMatcher, final RawData msg) {
        final SslHandler sslHandler = channel.pipeline().get(SslHandler.class);
        if (sslHandler == null) {
            msg.onError(new IllegalStateException("Missing SslHandler"));
        } else {
            /*
             * Trigger handshake.
             */
            Future<Channel> handshakeFuture = sslHandler.handshakeFuture();
            handshakeFuture.addListener(new GenericFutureListener<Future<Channel>>() {

                @Override
                public void operationComplete(Future<Channel> future) throws Exception {
                    if (future.isSuccess()) {
                        EndpointContext context = contextUtil.buildEndpointContext(channel);
                        if (context == null || context.get(TlsEndpointContext.KEY_SESSION_ID) == null) {
                            msg.onError(new IllegalStateException("Missing TlsEndpointContext " + context));
                            return;
                        }
                        /*
                         * Handshake succeeded! 
                         * Call super.send() to actually send the message.
                         */
                        TlsClientConnector.super.send(future.getNow(), endpointMatcher, msg);
                    } else if (future.isCancelled()) {
                        msg.onError(new CancellationException());
                    } else {
                        msg.onError(future.cause());
                    }
                }
            });
        }
    }

    @Override
    protected void onNewChannelCreated(SocketAddress remote, Channel ch) {
        SSLEngine sslEngine = createSllEngine(remote);
        sslEngine.setUseClientMode(true);
        SslHandler sslHandler = new SslHandler(sslEngine);
        sslHandler.setHandshakeTimeoutMillis(handshakeTimeoutMillis);
        ch.pipeline().addFirst(sslHandler);
    }

    @Override
    public String getProtocol() {
        return "TLS";
    }

    /**
     * Create SSL engine for remote socket address.
     * 
     * @param remoteAddress for SSL engine
     * @return created SSL engine
     */
    private SSLEngine createSllEngine(SocketAddress remoteAddress) {
        if (remoteAddress instanceof InetSocketAddress) {
            InetSocketAddress remote = (InetSocketAddress) remoteAddress;
            LOGGER.info("Connection to inet {}", remote);
            return sslContext.createSSLEngine(remote.getAddress().getHostAddress(), remote.getPort());
        } else {
            LOGGER.info("Connection to {}", remoteAddress);
            return sslContext.createSSLEngine();
        }
    }
}