1use sfio_rustls_config::ClientNameVerification;
2use std::path::Path;
3use std::sync::Arc;
4
5use tokio_rustls::rustls;
6
7use crate::app::{ConnectStrategy, Listener};
8use crate::link::LinkErrorMode;
9use crate::outstation::task::OutstationTask;
10use crate::outstation::{
11 ControlHandler, OutstationApplication, OutstationConfig, OutstationHandle,
12 OutstationInformation,
13};
14use crate::tcp::client::ClientTask;
15use crate::tcp::tls::{CertificateMode, MinTlsVersion, TlsClientConfig, TlsError};
16use crate::tcp::{ClientState, ConnectOptions, EndpointList, PostConnectionHandler};
17use crate::util::phys::{PhysAddr, PhysLayer};
18use crate::util::session::{Enabled, Session};
19use tokio::net::TcpStream;
20use tokio_rustls::rustls::pki_types::ServerName;
21
22use crate::link::reader::LinkModes;
23use tracing::Instrument;
24
25#[allow(clippy::too_many_arguments)]
30pub fn spawn_outstation_tls_client(
31 link_error_mode: LinkErrorMode,
32 endpoints: EndpointList,
33 connect_strategy: ConnectStrategy,
34 connect_options: ConnectOptions,
35 config: OutstationConfig,
36 application: Box<dyn OutstationApplication>,
37 information: Box<dyn OutstationInformation>,
38 control_handler: Box<dyn ControlHandler>,
39 listener: Box<dyn Listener<ClientState>>,
40 tls_config: TlsClientConfig,
41) -> OutstationHandle {
42 let main_addr = endpoints.main_addr().to_string();
43 let (task, handle) = OutstationTask::create(
44 Enabled::No,
45 LinkModes::stream(link_error_mode),
46 config,
47 PhysAddr::None,
48 application,
49 information,
50 control_handler,
51 );
52 let session = Session::outstation(task);
53 let mut client = ClientTask::new(
54 session,
55 endpoints,
56 connect_strategy,
57 connect_options,
58 PostConnectionHandler::Tls(tls_config),
59 listener,
60 );
61
62 let future = async move {
63 client
64 .run()
65 .instrument(tracing::info_span!("dnp3-outstation-tls-client", "endpoint" = ?main_addr))
66 .await;
67 };
68 tokio::spawn(future);
69 handle
70}
71
72pub struct TlsServerConfig {
74 config: Arc<rustls::ServerConfig>,
75}
76
77impl TlsServerConfig {
78 #[deprecated(
80 since = "1.4.1",
81 note = "Please use `full_pki` or `self_signed` instead"
82 )]
83 pub fn new(
84 client_subject_name: &str,
85 peer_cert_path: &Path,
86 local_cert_path: &Path,
87 private_key_path: &Path,
88 password: Option<&str>,
89 min_tls_version: MinTlsVersion,
90 certificate_mode: CertificateMode,
91 ) -> Result<Self, TlsError> {
92 match certificate_mode {
93 CertificateMode::AuthorityBased => Self::full_pki(
94 Some(client_subject_name.to_string()),
95 peer_cert_path,
96 local_cert_path,
97 private_key_path,
98 password,
99 min_tls_version,
100 ),
101 CertificateMode::SelfSigned => Self::self_signed(
102 peer_cert_path,
103 local_cert_path,
104 private_key_path,
105 password,
106 min_tls_version,
107 ),
108 }
109 }
110
111 pub fn full_pki(
120 client_subject_name: Option<String>,
121 peer_cert_path: &Path,
122 local_cert_path: &Path,
123 private_key_path: &Path,
124 password: Option<&str>,
125 min_tls_version: MinTlsVersion,
126 ) -> Result<Self, TlsError> {
127 let name_verification = match client_subject_name {
128 None => ClientNameVerification::None,
129 Some(name) => {
130 let name: ServerName<'static> = name.try_into()?;
131 ClientNameVerification::SanOrCommonName(name)
132 }
133 };
134
135 let config = sfio_rustls_config::server::authority(
136 min_tls_version.into(),
137 name_verification,
138 peer_cert_path,
139 local_cert_path,
140 private_key_path,
141 password,
142 )?;
143
144 Ok(Self {
145 config: Arc::new(config),
146 })
147 }
148
149 pub fn self_signed(
158 peer_cert_path: &Path,
159 local_cert_path: &Path,
160 private_key_path: &Path,
161 password: Option<&str>,
162 min_tls_version: MinTlsVersion,
163 ) -> Result<Self, TlsError> {
164 let config = sfio_rustls_config::server::self_signed(
165 min_tls_version.into(),
166 peer_cert_path,
167 local_cert_path,
168 private_key_path,
169 password,
170 )?;
171
172 Ok(Self {
173 config: Arc::new(config),
174 })
175 }
176
177 pub(crate) async fn handle_connection(
178 &mut self,
179 socket: TcpStream,
180 ) -> Result<PhysLayer, String> {
181 let connector = tokio_rustls::TlsAcceptor::from(self.config.clone());
182 match connector.accept(socket).await {
183 Err(err) => Err(format!("failed to establish TLS session: {err}")),
184 Ok(stream) => Ok(PhysLayer::Tls(Box::new(tokio_rustls::TlsStream::from(
185 stream,
186 )))),
187 }
188 }
189}