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 com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.common.util.concurrent.SettableFuture;
15 import io.netty.channel.Channel;
16 import io.netty.handler.ssl.SslHandler;
17 import io.netty.util.HashedWheelTimer;
18 import io.netty.util.concurrent.GlobalEventExecutor;
19 import io.netty.util.concurrent.Promise;
20 import java.net.SocketAddress;
21 import java.security.PublicKey;
22 import java.security.cert.Certificate;
23 import java.util.Optional;
24 import java.util.concurrent.TimeUnit;
25 import java.util.concurrent.atomic.AtomicBoolean;
26 import javax.net.ssl.SSLPeerUnverifiedException;
27 import org.opendaylight.netconf.callhome.protocol.CallHomeNetconfSubsystemListener;
28 import org.opendaylight.netconf.callhome.protocol.CallHomeProtocolSessionContext;
29 import org.opendaylight.netconf.client.NetconfClientSession;
30 import org.opendaylight.netconf.client.NetconfClientSessionListener;
31 import org.opendaylight.netconf.client.NetconfClientSessionNegotiatorFactory;
32 import org.opendaylight.netconf.client.SslHandlerFactory;
33 import org.opendaylight.netconf.client.TlsClientChannelInitializer;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev231025.connection.parameters.Protocol.Name;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 final class CallHomeTlsSessionContext implements CallHomeProtocolSessionContext {
39 private static final Logger LOG = LoggerFactory.getLogger(CallHomeTlsSessionContext.class);
41 private final AtomicBoolean activated = new AtomicBoolean();
42 private final SslHandlerFactory sslHandlerFactory;
43 private final CallHomeNetconfSubsystemListener subsystemListener;
44 private final String deviceId;
45 private final Channel channel;
46 private final PublicKey publicKey;
47 private final SocketAddress socketAddress;
49 CallHomeTlsSessionContext(final String deviceId, final Channel channel, final SslHandlerFactory sslHandlerFactory,
50 final CallHomeNetconfSubsystemListener subsystemListener) {
51 this.channel = requireNonNull(channel, "channel");
52 this.deviceId = deviceId;
53 socketAddress = channel.remoteAddress();
54 publicKey = createPublicKey(channel);
55 this.sslHandlerFactory = requireNonNull(sslHandlerFactory, "sslHandlerFactory");
56 this.subsystemListener = subsystemListener;
59 private static Promise<NetconfClientSession> newSessionPromise() {
60 return GlobalEventExecutor.INSTANCE.newPromise();
63 void openNetconfChannel(final Channel ch) {
64 LOG.debug("Opening NETCONF Subsystem on TLS connection {}", deviceId);
65 subsystemListener.onNetconfSubsystemOpened(this, listener -> doActivate(ch, listener));
69 public void terminate() {
73 private ListenableFuture<NetconfClientSession> doActivate(final Channel ch,
74 final NetconfClientSessionListener listener) {
75 final Promise<NetconfClientSession> activationPromise = newSessionPromise();
76 if (activated.compareAndExchange(false, true)) {
77 return Futures.immediateFailedFuture(new IllegalStateException("Session (channel) already activated."));
80 LOG.info("Activating Netconf channel for {} with {}", getRemoteAddress(), listener);
81 final NetconfClientSessionNegotiatorFactory negotiatorFactory = new NetconfClientSessionNegotiatorFactory(
82 new HashedWheelTimer(), Optional.empty(), TimeUnit.SECONDS.toMillis(5));
83 final TlsClientChannelInitializer tlsClientChannelInitializer = new TlsClientChannelInitializer(
84 sslHandlerFactory, negotiatorFactory, listener);
85 tlsClientChannelInitializer.initialize(ch, activationPromise);
86 final SettableFuture<NetconfClientSession> future = SettableFuture.create();
87 activationPromise.addListener(ignored -> {
88 final var cause = activationPromise.cause();
90 future.setException(cause);
92 future.set(activationPromise.getNow());
99 public PublicKey getRemoteServerKey() {
104 public SocketAddress getRemoteAddress() {
105 return socketAddress;
109 public String getSessionId() {
114 public Name getTransportType() {
118 private static PublicKey createPublicKey(final Channel ch) {
119 final SslHandler sslHandler = ch.pipeline().get(SslHandler.class);
120 final Certificate[] certificates;
122 certificates = sslHandler.engine().getSession().getPeerCertificates();
123 } catch (SSLPeerUnverifiedException e) {
124 LOG.error("Peer certificate was not established during the handshake", e);
125 throw new IllegalStateException("No peer certificate present", e);
127 return certificates[0].getPublicKey();