2 * Copyright (c) 2023 PANTHEON.tech 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.server.tls;
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.netconf.client.NetconfClientSessionNegotiatorFactory.DEFAULT_CLIENT_CAPABILITIES;
13 import io.netty.channel.ChannelOption;
14 import io.netty.util.HashedWheelTimer;
15 import java.net.InetAddress;
16 import java.util.Optional;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.TimeUnit;
19 import java.util.concurrent.TimeoutException;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.netconf.callhome.server.CallHomeStatusRecorder;
22 import org.opendaylight.netconf.callhome.server.CallHomeTransportChannelListener;
23 import org.opendaylight.netconf.client.NetconfClientSessionNegotiatorFactory;
24 import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
25 import org.opendaylight.netconf.transport.tcp.BootstrapFactory;
26 import org.opendaylight.netconf.transport.tls.TLSClient;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.client.rev230417.netconf.client.listen.stack.grouping.transport.ssh.ssh.TcpServerParametersBuilder;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.server.rev230417.TcpServerGrouping;
31 import org.opendaylight.yangtools.yang.common.Uint16;
33 public final class CallHomeTlsServer implements AutoCloseable {
34 private static final int DEFAULT_PORT = 4335;
35 private static final Integer DEFAULT_TIMEOUT_MILLIS = 5000;
36 private static final Integer DEFAULT_MAX_CONNECTIONS = 64;
38 private final CallHomeTlsSessionContextManager contextManager;
39 private final TLSClient client;
41 private CallHomeTlsServer(final TcpServerGrouping tcpServerParams,
42 final BootstrapFactory bootstrapFactory,
43 final Integer maxConnections, final Integer timeoutMillis,
44 final NetconfClientSessionNegotiatorFactory negotiationFactory,
45 final CallHomeTlsSessionContextManager contextManager,
46 final CallHomeTlsAuthProvider authProvider,
47 final CallHomeStatusRecorder statusRecorder) {
48 this.contextManager = requireNonNull(contextManager);
49 final var bootstrap = bootstrapFactory.newServerBootstrap()
50 .childOption(ChannelOption.SO_KEEPALIVE, true)
51 .childOption(ChannelOption.SO_BACKLOG, requireNonNull(maxConnections))
52 .childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, requireNonNull(timeoutMillis));
53 final var transportChannelListener = new CallHomeTransportChannelListener(requireNonNull(negotiationFactory),
54 contextManager, requireNonNull(statusRecorder));
56 client = TLSClient.listen(transportChannelListener, bootstrap, tcpServerParams, authProvider)
57 .get(timeoutMillis, TimeUnit.MILLISECONDS);
58 } catch (UnsupportedConfigurationException | InterruptedException | ExecutionException | TimeoutException e) {
59 throw new IllegalStateException("Could not start TLS Call-Home server", e);
64 public void close() throws Exception {
65 contextManager.close();
66 client.shutdown().get();
69 public static Builder builder() {
73 public static final class Builder {
75 private InetAddress address;
76 private int port = DEFAULT_PORT;
77 private BootstrapFactory bootstrapFactory;
78 private NetconfClientSessionNegotiatorFactory negotiationFactory;
79 private Integer maxConnections;
80 private Integer timeoutMillis;
81 private CallHomeTlsAuthProvider authProvider;
82 private CallHomeTlsSessionContextManager contextManager;
83 private CallHomeStatusRecorder statusRecorder;
89 public @NonNull CallHomeTlsServer build() {
90 return new CallHomeTlsServer(
91 toServerParams(address, port),
92 bootstrapFactory == null ? defaultBootstrapFactory() : bootstrapFactory,
93 maxConnections == null ? DEFAULT_MAX_CONNECTIONS : maxConnections,
94 timeoutMillis == null ? DEFAULT_TIMEOUT_MILLIS : timeoutMillis,
95 negotiationFactory == null ? defaultNegotiationFactory() : negotiationFactory,
96 contextManager, authProvider, statusRecorder);
99 public Builder withSessionContextManager(final CallHomeTlsSessionContextManager newContextManager) {
100 this.contextManager = newContextManager;
104 public Builder withAuthProvider(final CallHomeTlsAuthProvider newAuthProvider) {
105 this.authProvider = newAuthProvider;
109 public Builder withStatusRecorder(final CallHomeStatusRecorder newStatusRecorder) {
110 this.statusRecorder = newStatusRecorder;
114 public Builder withAddress(final InetAddress newAddress) {
115 this.address = newAddress;
119 public Builder withPort(final int newPort) {
124 public Builder withMaxConnections(final int newMaxConnections) {
125 this.maxConnections = newMaxConnections;
129 public Builder withTimeout(final int newTimeoutMillis) {
130 this.timeoutMillis = newTimeoutMillis;
134 public Builder withBootstrapFactory(final BootstrapFactory newBootstrapFactory) {
135 this.bootstrapFactory = newBootstrapFactory;
139 public Builder withNegotiationFactory(final NetconfClientSessionNegotiatorFactory newNegotiationFactory) {
140 this.negotiationFactory = newNegotiationFactory;
145 private static TcpServerGrouping toServerParams(final InetAddress address, final int port) {
146 final var ipAddress = IetfInetUtil.ipAddressFor(
147 address == null ? InetAddress.getLoopbackAddress() : address);
148 final var portNumber = new PortNumber(Uint16.valueOf(port < 0 ? DEFAULT_PORT : port));
149 return new TcpServerParametersBuilder().setLocalAddress(ipAddress).setLocalPort(portNumber).build();
152 private static BootstrapFactory defaultBootstrapFactory() {
153 return new BootstrapFactory("tls-call-home-server", 0);
156 private static NetconfClientSessionNegotiatorFactory defaultNegotiationFactory() {
157 return new NetconfClientSessionNegotiatorFactory(new HashedWheelTimer(),
158 Optional.empty(), DEFAULT_TIMEOUT_MILLIS, DEFAULT_CLIENT_CAPABILITIES);