Refresh IETF client/server models
[netconf.git] / netconf / callhome-server / src / main / java / org / opendaylight / netconf / callhome / server / tls / CallHomeTlsServer.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netconf.callhome.server.tls;
9
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.netconf.client.NetconfClientSessionNegotiatorFactory.DEFAULT_CLIENT_CAPABILITIES;
12
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.rev231228.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.rev231228.TcpServerGrouping;
31 import org.opendaylight.yangtools.yang.common.Uint16;
32
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;
37
38     private final CallHomeTlsSessionContextManager contextManager;
39     private final TLSClient client;
40
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));
55         try {
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);
60         }
61     }
62
63     @Override
64     public void close() throws Exception {
65         contextManager.close();
66         client.shutdown().get();
67     }
68
69     public static Builder builder() {
70         return new Builder();
71     }
72
73     public static final class Builder {
74
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;
84
85         private Builder() {
86             // on purpose
87         }
88
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);
97         }
98
99         public Builder withSessionContextManager(final CallHomeTlsSessionContextManager newContextManager) {
100             this.contextManager = newContextManager;
101             return this;
102         }
103
104         public Builder withAuthProvider(final CallHomeTlsAuthProvider newAuthProvider) {
105             this.authProvider = newAuthProvider;
106             return this;
107         }
108
109         public Builder withStatusRecorder(final CallHomeStatusRecorder newStatusRecorder) {
110             this.statusRecorder = newStatusRecorder;
111             return this;
112         }
113
114         public Builder withAddress(final InetAddress newAddress) {
115             this.address = newAddress;
116             return this;
117         }
118
119         public Builder withPort(final int newPort) {
120             this.port = newPort;
121             return this;
122         }
123
124         public Builder withMaxConnections(final int newMaxConnections) {
125             this.maxConnections = newMaxConnections;
126             return this;
127         }
128
129         public Builder withTimeout(final int newTimeoutMillis) {
130             this.timeoutMillis = newTimeoutMillis;
131             return this;
132         }
133
134         public Builder withBootstrapFactory(final BootstrapFactory newBootstrapFactory) {
135             this.bootstrapFactory = newBootstrapFactory;
136             return this;
137         }
138
139         public Builder withNegotiationFactory(final NetconfClientSessionNegotiatorFactory newNegotiationFactory) {
140             this.negotiationFactory = newNegotiationFactory;
141             return this;
142         }
143     }
144
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();
150     }
151
152     private static BootstrapFactory defaultBootstrapFactory() {
153         return new BootstrapFactory("tls-call-home-server", 0);
154     }
155
156     private static NetconfClientSessionNegotiatorFactory defaultNegotiationFactory() {
157         return new NetconfClientSessionNegotiatorFactory(new HashedWheelTimer(),
158             Optional.empty(), DEFAULT_TIMEOUT_MILLIS, DEFAULT_CLIENT_CAPABILITIES);
159     }
160 }