mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-12-23 12:26:46 +00:00
fix(ironrdp-async)!: use static dispatch for NetworkClient trait (#1043)
- Rename `AsyncNetworkClient` to `NetworkClient` - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch using generics (`&mut N where N: NetworkClient`) - Reorder `connect_finalize` parameters for consistency across crates
This commit is contained in:
parent
cca323adab
commit
bca6d190a8
12 changed files with 123 additions and 146 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use ironrdp_async::AsyncNetworkClient;
|
||||
use ironrdp_async::NetworkClient;
|
||||
use ironrdp_connector::sspi::credssp::{
|
||||
CredSspServer, CredentialsProxy, ServerError, ServerMode, ServerState, TsRequest,
|
||||
};
|
||||
|
|
@ -71,7 +71,7 @@ impl CredentialsProxy for CredentialsProxyImpl<'_> {
|
|||
|
||||
pub(crate) async fn resolve_generator(
|
||||
generator: &mut CredsspProcessGenerator<'_>,
|
||||
network_client: &mut dyn AsyncNetworkClient,
|
||||
network_client: &mut impl NetworkClient,
|
||||
) -> Result<ServerState, ServerError> {
|
||||
let mut state = generator.start();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#![cfg_attr(doc, doc = include_str!("../README.md"))]
|
||||
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
|
||||
|
||||
use ironrdp_async::{single_sequence_step, AsyncNetworkClient, Framed, FramedRead, FramedWrite, StreamWrapper};
|
||||
use ironrdp_async::{single_sequence_step, Framed, FramedRead, FramedWrite, NetworkClient, StreamWrapper};
|
||||
use ironrdp_connector::sspi::credssp::EarlyUserAuthResult;
|
||||
use ironrdp_connector::sspi::{AuthIdentity, KerberosServerConfig, Username};
|
||||
use ironrdp_connector::{custom_err, general_err, ConnectorResult, ServerName};
|
||||
|
|
@ -51,16 +51,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn accept_credssp<S>(
|
||||
pub async fn accept_credssp<S, N>(
|
||||
framed: &mut Framed<S>,
|
||||
acceptor: &mut Acceptor,
|
||||
network_client: &mut N,
|
||||
client_computer_name: ServerName,
|
||||
public_key: Vec<u8>,
|
||||
kerberos_config: Option<KerberosServerConfig>,
|
||||
network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
let mut buf = WriteBuf::new();
|
||||
|
||||
|
|
@ -68,11 +69,11 @@ where
|
|||
perform_credssp_step(
|
||||
framed,
|
||||
acceptor,
|
||||
network_client,
|
||||
&mut buf,
|
||||
client_computer_name,
|
||||
public_key,
|
||||
kerberos_config,
|
||||
network_client,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
|
|
@ -98,34 +99,73 @@ where
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all, ret)]
|
||||
async fn perform_credssp_step<S>(
|
||||
async fn perform_credssp_step<S, N>(
|
||||
framed: &mut Framed<S>,
|
||||
acceptor: &mut Acceptor,
|
||||
network_client: &mut N,
|
||||
buf: &mut WriteBuf,
|
||||
client_computer_name: ServerName,
|
||||
public_key: Vec<u8>,
|
||||
kerberos_config: Option<KerberosServerConfig>,
|
||||
network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
assert!(acceptor.should_perform_credssp());
|
||||
let AcceptorState::Credssp { protocol, .. } = acceptor.state else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
async fn credssp_loop<S>(
|
||||
let result = credssp_loop(
|
||||
framed,
|
||||
acceptor,
|
||||
network_client,
|
||||
buf,
|
||||
client_computer_name,
|
||||
public_key,
|
||||
kerberos_config,
|
||||
)
|
||||
.await;
|
||||
|
||||
if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) {
|
||||
trace!(?result, "HYBRID_EX");
|
||||
|
||||
let result = if result.is_ok() {
|
||||
EarlyUserAuthResult::Success
|
||||
} else {
|
||||
EarlyUserAuthResult::AccessDenied
|
||||
};
|
||||
|
||||
buf.clear();
|
||||
result
|
||||
.to_buffer(&mut *buf)
|
||||
.map_err(|e| ironrdp_connector::custom_err!("to_buffer", e))?;
|
||||
let response = &buf[..result.buffer_len()];
|
||||
framed
|
||||
.write_all(response)
|
||||
.await
|
||||
.map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
|
||||
}
|
||||
|
||||
result?;
|
||||
|
||||
acceptor.mark_credssp_as_done();
|
||||
|
||||
return Ok(());
|
||||
|
||||
async fn credssp_loop<S, N>(
|
||||
framed: &mut Framed<S>,
|
||||
acceptor: &mut Acceptor,
|
||||
network_client: &mut N,
|
||||
buf: &mut WriteBuf,
|
||||
client_computer_name: ServerName,
|
||||
public_key: Vec<u8>,
|
||||
kerberos_config: Option<KerberosServerConfig>,
|
||||
mut network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
let creds = acceptor
|
||||
.creds
|
||||
|
|
@ -164,12 +204,7 @@ where
|
|||
|
||||
let result = {
|
||||
let mut generator = sequence.process_ts_request(ts_request);
|
||||
|
||||
if let Some(network_client_ref) = network_client.as_deref_mut() {
|
||||
resolve_generator(&mut generator, network_client_ref).await
|
||||
} else {
|
||||
generator.resolve_to_result()
|
||||
}
|
||||
resolve_generator(&mut generator, network_client).await
|
||||
}; // drop generator
|
||||
|
||||
buf.clear();
|
||||
|
|
@ -184,43 +219,7 @@ where
|
|||
.map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let result = credssp_loop(
|
||||
framed,
|
||||
acceptor,
|
||||
buf,
|
||||
client_computer_name,
|
||||
public_key,
|
||||
kerberos_config,
|
||||
network_client,
|
||||
)
|
||||
.await;
|
||||
|
||||
if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) {
|
||||
trace!(?result, "HYBRID_EX");
|
||||
|
||||
let result = if result.is_ok() {
|
||||
EarlyUserAuthResult::Success
|
||||
} else {
|
||||
EarlyUserAuthResult::AccessDenied
|
||||
};
|
||||
|
||||
buf.clear();
|
||||
result
|
||||
.to_buffer(&mut *buf)
|
||||
.map_err(|e| ironrdp_connector::custom_err!("to_buffer", e))?;
|
||||
let response = &buf[..result.buffer_len()];
|
||||
framed
|
||||
.write_all(response)
|
||||
.await
|
||||
.map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
|
||||
}
|
||||
|
||||
result?;
|
||||
|
||||
acceptor.mark_credssp_as_done();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ use ironrdp_connector::credssp::{CredsspProcessGenerator, CredsspSequence, Kerbe
|
|||
use ironrdp_connector::sspi::credssp::ClientState;
|
||||
use ironrdp_connector::sspi::generator::GeneratorState;
|
||||
use ironrdp_connector::{
|
||||
custom_err, general_err, ClientConnector, ClientConnectorState, ConnectionResult, ConnectorError, ConnectorResult,
|
||||
ServerName, State as _,
|
||||
general_err, ClientConnector, ClientConnectorState, ConnectionResult, ConnectorError, ConnectorResult, ServerName,
|
||||
State as _,
|
||||
};
|
||||
use ironrdp_core::WriteBuf;
|
||||
use tracing::{debug, info, instrument, trace};
|
||||
|
||||
use crate::framed::{Framed, FramedRead, FramedWrite};
|
||||
use crate::{single_sequence_step, AsyncNetworkClient};
|
||||
use crate::{single_sequence_step, NetworkClient};
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct ShouldUpgrade;
|
||||
|
|
@ -49,28 +49,29 @@ pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector) -> Up
|
|||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn connect_finalize<S>(
|
||||
pub async fn connect_finalize<S, N>(
|
||||
_: Upgraded,
|
||||
framed: &mut Framed<S>,
|
||||
mut connector: ClientConnector,
|
||||
framed: &mut Framed<S>,
|
||||
network_client: &mut N,
|
||||
server_name: ServerName,
|
||||
server_public_key: Vec<u8>,
|
||||
network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
kerberos_config: Option<KerberosConfig>,
|
||||
) -> ConnectorResult<ConnectionResult>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
let mut buf = WriteBuf::new();
|
||||
|
||||
if connector.should_perform_credssp() {
|
||||
perform_credssp_step(
|
||||
framed,
|
||||
&mut connector,
|
||||
framed,
|
||||
network_client,
|
||||
&mut buf,
|
||||
server_name,
|
||||
server_public_key,
|
||||
network_client,
|
||||
kerberos_config,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -91,7 +92,7 @@ where
|
|||
|
||||
async fn resolve_generator(
|
||||
generator: &mut CredsspProcessGenerator<'_>,
|
||||
network_client: &mut dyn AsyncNetworkClient,
|
||||
network_client: &mut impl NetworkClient,
|
||||
) -> ConnectorResult<ClientState> {
|
||||
let mut state = generator.start();
|
||||
|
||||
|
|
@ -110,17 +111,18 @@ async fn resolve_generator(
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
async fn perform_credssp_step<S>(
|
||||
framed: &mut Framed<S>,
|
||||
async fn perform_credssp_step<S, N>(
|
||||
connector: &mut ClientConnector,
|
||||
framed: &mut Framed<S>,
|
||||
network_client: &mut N,
|
||||
buf: &mut WriteBuf,
|
||||
server_name: ServerName,
|
||||
server_public_key: Vec<u8>,
|
||||
mut network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
kerberos_config: Option<KerberosConfig>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
assert!(connector.should_perform_credssp());
|
||||
|
||||
|
|
@ -141,15 +143,8 @@ where
|
|||
loop {
|
||||
let client_state = {
|
||||
let mut generator = sequence.process_ts_request(ts_request);
|
||||
|
||||
if let Some(network_client_ref) = network_client.as_deref_mut() {
|
||||
trace!("resolving network");
|
||||
resolve_generator(&mut generator, network_client_ref).await?
|
||||
} else {
|
||||
generator
|
||||
.resolve_to_result()
|
||||
.map_err(|e| custom_err!("resolve without network client", e))?
|
||||
}
|
||||
trace!("resolving network");
|
||||
resolve_generator(&mut generator, network_client).await?
|
||||
}; // drop generator
|
||||
|
||||
buf.clear();
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
#![cfg_attr(doc, doc = include_str!("../README.md"))]
|
||||
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
|
||||
|
||||
use core::future::Future;
|
||||
|
||||
pub use bytes;
|
||||
|
||||
mod connector;
|
||||
mod framed;
|
||||
mod session;
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
|
||||
use ironrdp_connector::sspi::generator::NetworkRequest;
|
||||
use ironrdp_connector::ConnectorResult;
|
||||
|
||||
|
|
@ -17,9 +16,6 @@ pub use self::connector::*;
|
|||
pub use self::framed::*;
|
||||
// pub use self::session::*;
|
||||
|
||||
pub trait AsyncNetworkClient {
|
||||
fn send<'a>(
|
||||
&'a mut self,
|
||||
network_request: &'a NetworkRequest,
|
||||
) -> Pin<Box<dyn Future<Output = ConnectorResult<Vec<u8>>> + 'a>>;
|
||||
pub trait NetworkClient {
|
||||
fn send(&mut self, network_request: &NetworkRequest) -> impl Future<Output = ConnectorResult<Vec<u8>>>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,11 +53,11 @@ pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector) -> Up
|
|||
#[instrument(skip_all)]
|
||||
pub fn connect_finalize<S>(
|
||||
_: Upgraded,
|
||||
framed: &mut Framed<S>,
|
||||
mut connector: ClientConnector,
|
||||
framed: &mut Framed<S>,
|
||||
network_client: &mut impl NetworkClient,
|
||||
server_name: ServerName,
|
||||
server_public_key: Vec<u8>,
|
||||
network_client: &mut impl NetworkClient,
|
||||
kerberos_config: Option<KerberosConfig>,
|
||||
) -> ConnectorResult<ConnectionResult>
|
||||
where
|
||||
|
|
@ -69,12 +69,12 @@ where
|
|||
|
||||
if connector.should_perform_credssp() {
|
||||
perform_credssp_step(
|
||||
framed,
|
||||
&mut connector,
|
||||
framed,
|
||||
network_client,
|
||||
&mut buf,
|
||||
server_name,
|
||||
server_public_key,
|
||||
network_client,
|
||||
kerberos_config,
|
||||
)?;
|
||||
}
|
||||
|
|
@ -118,12 +118,12 @@ fn resolve_generator(
|
|||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
fn perform_credssp_step<S>(
|
||||
framed: &mut Framed<S>,
|
||||
connector: &mut ClientConnector,
|
||||
framed: &mut Framed<S>,
|
||||
network_client: &mut impl NetworkClient,
|
||||
buf: &mut WriteBuf,
|
||||
server_name: ServerName,
|
||||
server_public_key: Vec<u8>,
|
||||
network_client: &mut impl NetworkClient,
|
||||
kerberos_config: Option<KerberosConfig>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
|
|
|
|||
|
|
@ -240,11 +240,11 @@ async fn connect(
|
|||
|
||||
let connection_result = ironrdp_tokio::connect_finalize(
|
||||
upgraded,
|
||||
&mut upgraded_framed,
|
||||
connector,
|
||||
&mut upgraded_framed,
|
||||
&mut ReqwestNetworkClient::new(),
|
||||
(&config.destination).into(),
|
||||
server_public_key,
|
||||
Some(&mut ReqwestNetworkClient::new()),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -326,11 +326,11 @@ async fn connect_ws(
|
|||
|
||||
let connection_result = ironrdp_tokio::connect_finalize(
|
||||
upgraded,
|
||||
&mut framed,
|
||||
connector,
|
||||
&mut framed,
|
||||
&mut ReqwestNetworkClient::new(),
|
||||
(&config.destination).into(),
|
||||
server_public_key,
|
||||
Some(&mut ReqwestNetworkClient::new()),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
|
|
|||
|
|
@ -349,10 +349,10 @@ impl RdpServer {
|
|||
ironrdp_acceptor::accept_credssp(
|
||||
&mut framed,
|
||||
&mut acceptor,
|
||||
&mut ironrdp_tokio::reqwest::ReqwestNetworkClient::new(),
|
||||
client_name.into(),
|
||||
pub_key.clone(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,12 +212,12 @@ where
|
|||
let mut upgraded_framed = ironrdp_tokio::TokioFramed::new(upgraded_stream);
|
||||
let connection_result = ironrdp_async::connect_finalize(
|
||||
upgraded,
|
||||
&mut upgraded_framed,
|
||||
connector,
|
||||
&mut upgraded_framed,
|
||||
&mut ironrdp_tokio::reqwest::ReqwestNetworkClient::new(),
|
||||
"localhost".into(),
|
||||
server_public_key,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("finalize connection");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use core::future::Future;
|
||||
use core::net::{IpAddr, Ipv4Addr};
|
||||
use core::pin::Pin;
|
||||
|
||||
use ironrdp_connector::{custom_err, general_err, ConnectorResult};
|
||||
use reqwest::Client;
|
||||
|
|
@ -9,18 +7,15 @@ use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
|
|||
use tokio::net::{TcpStream, UdpSocket};
|
||||
use url::Url;
|
||||
|
||||
use crate::AsyncNetworkClient;
|
||||
use crate::NetworkClient;
|
||||
|
||||
pub struct ReqwestNetworkClient {
|
||||
client: Option<Client>,
|
||||
}
|
||||
|
||||
impl AsyncNetworkClient for ReqwestNetworkClient {
|
||||
fn send<'a>(
|
||||
&'a mut self,
|
||||
network_request: &'a sspi::generator::NetworkRequest,
|
||||
) -> Pin<Box<dyn Future<Output = ConnectorResult<Vec<u8>>> + 'a>> {
|
||||
Box::pin(ReqwestNetworkClient::send_request(self, network_request))
|
||||
impl NetworkClient for ReqwestNetworkClient {
|
||||
async fn send(&mut self, network_request: &sspi::generator::NetworkRequest) -> ConnectorResult<Vec<u8>> {
|
||||
ReqwestNetworkClient::send_request(self, network_request).await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,53 +1,45 @@
|
|||
use core::pin::Pin;
|
||||
|
||||
use futures_util::Future;
|
||||
use ironrdp::connector::sspi::generator::NetworkRequest;
|
||||
use ironrdp::connector::sspi::network_client::NetworkProtocol;
|
||||
use ironrdp::connector::{custom_err, reason_err, ConnectorResult};
|
||||
use ironrdp_futures::AsyncNetworkClient;
|
||||
use ironrdp_futures::NetworkClient;
|
||||
use tracing::debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct WasmNetworkClient;
|
||||
|
||||
impl AsyncNetworkClient for WasmNetworkClient {
|
||||
fn send<'a>(
|
||||
&'a mut self,
|
||||
network_request: &'a NetworkRequest,
|
||||
) -> Pin<Box<dyn Future<Output = ConnectorResult<Vec<u8>>> + 'a>> {
|
||||
Box::pin(async move {
|
||||
debug!(?network_request.protocol, ?network_request.url);
|
||||
impl NetworkClient for WasmNetworkClient {
|
||||
async fn send(&mut self, network_request: &NetworkRequest) -> ConnectorResult<Vec<u8>> {
|
||||
debug!(?network_request.protocol, ?network_request.url);
|
||||
|
||||
match &network_request.protocol {
|
||||
NetworkProtocol::Http | NetworkProtocol::Https => {
|
||||
let body = js_sys::Uint8Array::from(network_request.data.as_slice());
|
||||
match &network_request.protocol {
|
||||
NetworkProtocol::Http | NetworkProtocol::Https => {
|
||||
let body = js_sys::Uint8Array::from(network_request.data.as_slice());
|
||||
|
||||
let response = gloo_net::http::Request::post(network_request.url.as_str())
|
||||
.header("keep-alive", "true")
|
||||
.body(body)
|
||||
.map_err(|e| custom_err!("failed to send KDC request", e))?
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| custom_err!("failed to send KDC request", e))?;
|
||||
let response = gloo_net::http::Request::post(network_request.url.as_str())
|
||||
.header("keep-alive", "true")
|
||||
.body(body)
|
||||
.map_err(|e| custom_err!("failed to send KDC request", e))?
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| custom_err!("failed to send KDC request", e))?;
|
||||
|
||||
if !response.ok() {
|
||||
return Err(reason_err!(
|
||||
"KdcProxy",
|
||||
"HTTP status error ({} {})",
|
||||
response.status(),
|
||||
response.status_text(),
|
||||
));
|
||||
}
|
||||
|
||||
let body = response
|
||||
.binary()
|
||||
.await
|
||||
.map_err(|e| custom_err!("failed to retrieve HTTP response", e))?;
|
||||
|
||||
Ok(body)
|
||||
if !response.ok() {
|
||||
return Err(reason_err!(
|
||||
"KdcProxy",
|
||||
"HTTP status error ({} {})",
|
||||
response.status(),
|
||||
response.status_text(),
|
||||
));
|
||||
}
|
||||
unsupported => Err(reason_err!("CredSSP", "unsupported protocol: {unsupported:?}")),
|
||||
|
||||
let body = response
|
||||
.binary()
|
||||
.await
|
||||
.map_err(|e| custom_err!("failed to retrieve HTTP response", e))?;
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
})
|
||||
unsupported => Err(reason_err!("CredSSP", "unsupported protocol: {unsupported:?}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -979,11 +979,11 @@ async fn connect(
|
|||
|
||||
let connection_result = ironrdp_futures::connect_finalize(
|
||||
upgraded,
|
||||
&mut framed,
|
||||
connector,
|
||||
&mut framed,
|
||||
&mut WasmNetworkClient,
|
||||
(&destination).into(),
|
||||
server_public_key,
|
||||
Some(&mut WasmNetworkClient),
|
||||
url::Url::parse(kdc_proxy_url.unwrap_or_default().as_str()) // if kdc_proxy_url does not exit, give url parser a empty string, it will fail anyway and map to a None
|
||||
.ok()
|
||||
.map(|url| KerberosConfig {
|
||||
|
|
|
|||
|
|
@ -258,11 +258,11 @@ fn connect(
|
|||
let mut network_client = ReqwestNetworkClient;
|
||||
let connection_result = ironrdp_blocking::connect_finalize(
|
||||
upgraded,
|
||||
&mut upgraded_framed,
|
||||
connector,
|
||||
&mut upgraded_framed,
|
||||
&mut network_client,
|
||||
server_name.into(),
|
||||
server_public_key,
|
||||
&mut network_client,
|
||||
None,
|
||||
)
|
||||
.context("finalize connection")?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue