2 * Copyright (c) 2020 Pantheon Technologies, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.callhome.protocol.tls;
10 import static java.util.Objects.requireNonNull;
12 import io.netty.channel.Channel;
13 import io.netty.handler.ssl.SslHandler;
14 import io.netty.util.HashedWheelTimer;
15 import io.netty.util.concurrent.GlobalEventExecutor;
16 import io.netty.util.concurrent.Promise;
17 import java.net.SocketAddress;
18 import java.security.PublicKey;
19 import java.security.cert.Certificate;
20 import java.util.Optional;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.atomic.AtomicBoolean;
23 import javax.net.ssl.SSLPeerUnverifiedException;
24 import org.opendaylight.netconf.callhome.protocol.CallHomeNetconfSubsystemListener;
25 import org.opendaylight.netconf.callhome.protocol.CallHomeProtocolSessionContext;
26 import org.opendaylight.netconf.callhome.protocol.TransportType;
27 import org.opendaylight.netconf.client.NetconfClientSession;
28 import org.opendaylight.netconf.client.NetconfClientSessionListener;
29 import org.opendaylight.netconf.client.NetconfClientSessionNegotiatorFactory;
30 import org.opendaylight.netconf.client.SslHandlerFactory;
31 import org.opendaylight.netconf.client.TlsClientChannelInitializer;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
35 final class CallHomeTlsSessionContext implements CallHomeProtocolSessionContext {
36 private static final Logger LOG = LoggerFactory.getLogger(CallHomeTlsSessionContext.class);
38 private final AtomicBoolean activated = new AtomicBoolean();
39 private final SslHandlerFactory sslHandlerFactory;
40 private final CallHomeNetconfSubsystemListener subsystemListener;
41 private final String deviceId;
42 private final Channel channel;
43 private final PublicKey publicKey;
44 private final SocketAddress socketAddress;
46 CallHomeTlsSessionContext(final String deviceId, final Channel channel, final SslHandlerFactory sslHandlerFactory,
47 final CallHomeNetconfSubsystemListener subsystemListener) {
48 this.channel = requireNonNull(channel, "channel");
49 this.deviceId = deviceId;
50 this.socketAddress = channel.remoteAddress();
51 this.publicKey = createPublicKey(channel);
52 this.sslHandlerFactory = requireNonNull(sslHandlerFactory, "sslHandlerFactory");
53 this.subsystemListener = subsystemListener;
56 private static Promise<NetconfClientSession> newSessionPromise() {
57 return GlobalEventExecutor.INSTANCE.newPromise();
60 void openNetconfChannel(final Channel ch) {
61 LOG.debug("Opening NETCONF Subsystem on TLS connection {}", deviceId);
62 subsystemListener.onNetconfSubsystemOpened(this, listener -> doActivate(ch, listener));
66 public void terminate() {
70 private Promise<NetconfClientSession> doActivate(final Channel ch, final NetconfClientSessionListener listener) {
71 final Promise<NetconfClientSession> activationPromise = newSessionPromise();
72 if (!activated.compareAndExchange(false, true)) {
73 return activationPromise.setFailure(new IllegalStateException("Session (channel) already activated."));
76 LOG.info("Activating Netconf channel for {} with {}", getRemoteAddress(), listener);
77 final NetconfClientSessionNegotiatorFactory negotiatorFactory = new NetconfClientSessionNegotiatorFactory(
78 new HashedWheelTimer(), Optional.empty(), TimeUnit.SECONDS.toMillis(5));
79 final TlsClientChannelInitializer tlsClientChannelInitializer = new TlsClientChannelInitializer(
80 sslHandlerFactory, negotiatorFactory, listener);
81 tlsClientChannelInitializer.initialize(ch, activationPromise);
82 return activationPromise;
86 public PublicKey getRemoteServerKey() {
91 public SocketAddress getRemoteAddress() {
96 public String getSessionId() {
101 public TransportType getTransportType() {
102 return TransportType.TLS;
105 private static PublicKey createPublicKey(final Channel ch) {
106 final SslHandler sslHandler = ch.pipeline().get(SslHandler.class);
107 final Certificate[] certificates;
109 certificates = sslHandler.engine().getSession().getPeerCertificates();
110 } catch (SSLPeerUnverifiedException e) {
111 LOG.error("Peer certificate was not established during the handshake", e);
112 throw new IllegalStateException("No peer certificate present", e);
114 return certificates[0].getPublicKey();