All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.netconf.callhome.protocol.tls.NetconfCallHomeTlsServer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2020 Pantheon Technologies, s.r.o. 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
 */
package org.opendaylight.netconf.callhome.protocol.tls;

import static java.util.Objects.requireNonNull;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslHandler;
import java.net.InetSocketAddress;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.util.Optional;
import org.opendaylight.netconf.callhome.protocol.CallHomeNetconfSubsystemListener;
import org.opendaylight.netconf.client.SslHandlerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class NetconfCallHomeTlsServer {
    private static final Logger LOG = LoggerFactory.getLogger(NetconfCallHomeTlsServer.class);

    private final String host;
    private final Integer port;
    private final Integer timeout;
    private final Integer maxConnections;
    private final SslHandlerFactory sslHandlerFactory;
    private final CallHomeNetconfSubsystemListener subsystemListener;
    private final EventLoopGroup bossGroup;
    private final EventLoopGroup workerGroup;
    private final TlsAllowedDevicesMonitor allowedDevicesMonitor;

    private ChannelFuture cf;

    NetconfCallHomeTlsServer(final String host, final Integer port, final Integer timeout, final Integer maxConnections,
                             final SslHandlerFactory sslHandlerFactory,
                             final CallHomeNetconfSubsystemListener subsystemListener,
                             final EventLoopGroup bossGroup, final EventLoopGroup workerGroup,
                             final TlsAllowedDevicesMonitor allowedDevicesMonitor) {
        this.host = requireNonNull(host);
        this.port = requireNonNull(port);
        this.timeout = requireNonNull(timeout);
        this.maxConnections = requireNonNull(maxConnections);
        this.sslHandlerFactory = requireNonNull(sslHandlerFactory);
        this.subsystemListener = requireNonNull(subsystemListener);
        this.bossGroup = requireNonNull(bossGroup);
        this.workerGroup = requireNonNull(workerGroup);
        this.allowedDevicesMonitor = requireNonNull(allowedDevicesMonitor);
    }

    public void start() {
        final ChannelFuture bindFuture = new ServerBootstrap()
            .group(bossGroup, workerGroup)
            // FIXME: a case against using globals: we really would like to use epoll() here
            .channel(NioServerSocketChannel.class)
            .localAddress(new InetSocketAddress(host, port))
            .childOption(ChannelOption.SO_KEEPALIVE, true)
            .childOption(ChannelOption.SO_BACKLOG, maxConnections)
            .childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout)
            .childHandler(new TlsAuthChannelInitializer(sslHandlerFactory, future -> {
                if (future.isSuccess()) {
                    LOG.debug("SSL handshake completed successfully, accepting connection...");
                    final Channel channel = future.get();
                    // If the ssl handshake was successful it is expected that session contains peer certificate(s)
                    final Certificate cert = channel.pipeline().get(SslHandler.class).engine().getSession()
                        .getPeerCertificates()[0];
                    final PublicKey publicKey = cert.getPublicKey();
                    final Optional deviceId = allowedDevicesMonitor.findDeviceIdByPublicKey(publicKey);
                    if (deviceId.isEmpty()) {
                        LOG.error("Unable to identify connected device by provided certificate");
                        channel.close();
                    } else {
                        final CallHomeTlsSessionContext tlsSessionContext = new CallHomeTlsSessionContext(
                            deviceId.orElseThrow(), channel, sslHandlerFactory, subsystemListener);
                        tlsSessionContext.openNetconfChannel(channel);
                    }
                } else {
                    LOG.debug("SSL handshake failed, rejecting connection...");
                    future.get().close();
                }
            }))
            .bind();
        bindFuture.addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                LOG.debug("Call-Home TLS server bind completed");
            } else {
                LOG.error("Call-Home TLS server bind failed", future.cause());
            }
            cf = future.channel().closeFuture().addListener(f -> stop());
        });
    }

    public void stop() {
        LOG.debug("Stopping the Call-Home TLS server...");
        try {
            if (cf != null && cf.channel().isOpen()) {
                cf.channel().close().sync();
            }
        } catch (final InterruptedException e) {
            LOG.error("Error during shutdown of the Call-Home TLS server", e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy