Skip to content

Commit 712da42

Browse files
authored
fix: inject socket local address for the client addr (#759)
We used to inject the resolved target server address, but that is not what is expected. Server typically ignores this field so this was not a problem up until now.
1 parent ec1832b commit 712da42

14 files changed

Lines changed: 124 additions & 111 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ironrdp-client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ semver = "1"
7777
raw-window-handle = "0.6"
7878
uuid = { version = "1.16" }
7979
x509-cert = { version = "0.2", default-features = false, features = ["std"] }
80+
url = "2"
8081

8182
[target.'cfg(windows)'.dependencies]
8283
windows = { version = "0.61", features = ["Win32_Foundation"] }

crates/ironrdp-client/src/config.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use ironrdp::connector::{self, Credentials};
88
use ironrdp::pdu::rdp::capability_sets::MajorPlatformType;
99
use ironrdp::pdu::rdp::client_info::PerformanceFlags;
1010
use tap::prelude::*;
11+
use url::Url;
1112

1213
const DEFAULT_WIDTH: u16 = 1920;
1314
const DEFAULT_HEIGHT: u16 = 1080;
@@ -131,7 +132,7 @@ impl From<&Destination> for connector::ServerName {
131132

132133
#[derive(Clone, Debug)]
133134
pub struct RDCleanPathConfig {
134-
pub url: String,
135+
pub url: Url,
135136
pub auth_token: String,
136137
}
137138

@@ -161,7 +162,7 @@ struct Args {
161162

162163
/// Proxy URL to connect to for the RDCleanPath
163164
#[clap(long, requires("rdcleanpath_token"))]
164-
rdcleanpath_url: Option<String>,
165+
rdcleanpath_url: Option<Url>,
165166

166167
/// Authentication token to insert in the RDCleanPath packet
167168
#[clap(long, requires("rdcleanpath_url"))]

crates/ironrdp-client/src/rdp.rs

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,17 @@ async fn connect(
121121
) -> ConnectorResult<(ConnectionResult, UpgradedFramed)> {
122122
let dest = format!("{}:{}", config.destination.name(), config.destination.port());
123123

124-
let stream = TcpStream::connect(dest)
124+
let socket = TcpStream::connect(dest)
125125
.await
126126
.map_err(|e| connector::custom_err!("TCP connect", e))?;
127127

128-
let server_addr = stream
129-
.peer_addr()
130-
.map_err(|e| connector::custom_err!("Peer address", e))?;
128+
let client_addr = socket
129+
.local_addr()
130+
.map_err(|e| connector::custom_err!("get socket local address", e))?;
131131

132-
let mut framed = ironrdp_tokio::TokioFramed::new(stream);
132+
let mut framed = ironrdp_tokio::TokioFramed::new(socket);
133133

134-
let mut connector = connector::ClientConnector::new(config.connector.clone())
135-
.with_client_addr(server_addr)
134+
let mut connector = connector::ClientConnector::new(config.connector.clone(), client_addr)
136135
.with_static_channel(
137136
ironrdp::dvc::DrdynvcClient::new().with_dynamic_channel(DisplayControlClient::new(|_| Ok(Vec::new()))),
138137
)
@@ -184,15 +183,34 @@ async fn connect_ws(
184183
rdcleanpath: &RDCleanPathConfig,
185184
cliprdr_factory: Option<&(dyn CliprdrBackendFactory + Send)>,
186185
) -> ConnectorResult<(ConnectionResult, UpgradedFramed)> {
187-
let (ws, _) = tokio_tungstenite::connect_async(&rdcleanpath.url)
186+
let hostname = rdcleanpath
187+
.url
188+
.host_str()
189+
.ok_or_else(|| connector::general_err!("host missing from the URL"))?;
190+
191+
let port = rdcleanpath.url.port_or_known_default().unwrap_or(443);
192+
193+
let socket = TcpStream::connect((hostname, port))
194+
.await
195+
.map_err(|e| connector::custom_err!("TCP connect", e))?;
196+
197+
socket
198+
.set_nodelay(true)
199+
.map_err(|e| connector::custom_err!("set TCP_NODELAY", e))?;
200+
201+
let client_addr = socket
202+
.local_addr()
203+
.map_err(|e| connector::custom_err!("get socket local address", e))?;
204+
205+
let (ws, _) = tokio_tungstenite::client_async_tls(rdcleanpath.url.as_str(), socket)
188206
.await
189207
.map_err(|e| connector::custom_err!("WS connect", e))?;
190208

191209
let ws = crate::ws::websocket_compat(ws);
192210

193211
let mut framed = ironrdp_tokio::TokioFramed::new(ws);
194212

195-
let mut connector = connector::ClientConnector::new(config.connector.clone())
213+
let mut connector = connector::ClientConnector::new(config.connector.clone(), client_addr)
196214
.with_static_channel(
197215
ironrdp::dvc::DrdynvcClient::new().with_dynamic_channel(DisplayControlClient::new(|_| Ok(Vec::new()))),
198216
)
@@ -312,7 +330,7 @@ where
312330

313331
debug!(message = ?rdcleanpath_res, "Received RDCleanPath PDU");
314332

315-
let (x224_connection_response, server_cert_chain, server_addr) = match rdcleanpath_res
333+
let (x224_connection_response, server_cert_chain) = match rdcleanpath_res
316334
.into_enum()
317335
.map_err(|e| connector::custom_err!("invalid RDCleanPath PDU", e))?
318336
{
@@ -324,19 +342,13 @@ where
324342
ironrdp_rdcleanpath::RDCleanPath::Response {
325343
x224_connection_response,
326344
server_cert_chain,
327-
server_addr,
328-
} => (x224_connection_response, server_cert_chain, server_addr),
345+
server_addr: _,
346+
} => (x224_connection_response, server_cert_chain),
329347
ironrdp_rdcleanpath::RDCleanPath::Err(error) => {
330348
return Err(connector::custom_err!("received an RDCleanPath error", error));
331349
}
332350
};
333351

334-
let server_addr = server_addr
335-
.parse()
336-
.map_err(|e| connector::custom_err!("failed to parse server address sent by proxy", e))?;
337-
338-
connector.attach_client_addr(server_addr);
339-
340352
let connector::ClientConnectorState::ConnectionInitiationWaitConfirm { .. } = connector.state else {
341353
return Err(connector::general_err!("invalid connector state (wait confirm)"));
342354
};

crates/ironrdp-connector/src/connection.rs

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -123,32 +123,21 @@ impl State for ClientConnectorState {
123123
pub struct ClientConnector {
124124
pub config: Config,
125125
pub state: ClientConnectorState,
126-
pub client_addr: Option<SocketAddr>,
126+
/// The client address to be used in the Client Info PDU.
127+
pub client_addr: SocketAddr,
127128
pub static_channels: StaticChannelSet,
128129
}
129130

130131
impl ClientConnector {
131-
pub fn new(config: Config) -> Self {
132+
pub fn new(config: Config, client_addr: SocketAddr) -> Self {
132133
Self {
133134
config,
134135
state: ClientConnectorState::ConnectionInitiationSendRequest,
135-
client_addr: None,
136+
client_addr,
136137
static_channels: StaticChannelSet::new(),
137138
}
138139
}
139140

140-
/// Sets the client address to be used in the Client Info PDU.
141-
#[must_use]
142-
pub fn with_client_addr(mut self, addr: SocketAddr) -> Self {
143-
self.client_addr = Some(addr);
144-
self
145-
}
146-
147-
/// Sets the client address to be used in the Client Info PDU.
148-
pub fn attach_client_addr(&mut self, addr: SocketAddr) {
149-
self.client_addr = Some(addr);
150-
}
151-
152141
#[must_use]
153142
pub fn with_static_channel<T>(mut self, channel: T) -> Self
154143
where
@@ -448,12 +437,7 @@ impl Sequence for ClientConnector {
448437
} => {
449438
debug!("Secure Settings Exchange");
450439

451-
let client_addr = self
452-
.client_addr
453-
.as_ref()
454-
.ok_or_else(|| general_err!("client address is missing"))?;
455-
456-
let client_info = create_client_info_pdu(&self.config, client_addr);
440+
let client_info = create_client_info_pdu(&self.config, &self.client_addr);
457441

458442
debug!(message = ?client_info, "Send");
459443

crates/ironrdp-testsuite-extra/tests/tests.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,11 @@ where
195195
let client = tokio::task::spawn_local(async move {
196196
let (tx, rx) = oneshot::channel();
197197
ev.send(ServerEvent::GetLocalAddr(tx)).unwrap();
198-
let addr = rx.await.unwrap().unwrap();
199-
let tcp_stream = TcpStream::connect(addr).await.expect("TCP connect");
198+
let server_addr = rx.await.unwrap().unwrap();
199+
let tcp_stream = TcpStream::connect(server_addr).await.expect("TCP connect");
200+
let client_addr = tcp_stream.local_addr().expect("local_addr");
200201
let mut framed = ironrdp_tokio::TokioFramed::new(tcp_stream);
201-
let mut connector = connector::ClientConnector::new(client_config).with_client_addr(addr);
202+
let mut connector = connector::ClientConnector::new(client_config, client_addr);
202203
let should_upgrade = ironrdp_async::connect_begin(&mut framed, &mut connector)
203204
.await
204205
.expect("begin connection");

crates/ironrdp-web/src/session.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use core::cell::RefCell;
55
use core::num::NonZeroU32;
66
use core::time::Duration;
77
use std::borrow::Cow;
8+
use std::net::{Ipv4Addr, SocketAddrV4};
89
use std::rc::Rc;
910

1011
use anyhow::Context as _;
@@ -919,7 +920,10 @@ async fn connect(
919920
) -> Result<(connector::ConnectionResult, WebSocket), IronError> {
920921
let mut framed = ironrdp_futures::LocalFuturesFramed::new(ws);
921922

922-
let mut connector = ClientConnector::new(config);
923+
// In web browser environments, we do not have an easy access to the local address of the socket.
924+
let dummy_client_addr = std::net::SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 33899));
925+
926+
let mut connector = ClientConnector::new(config, dummy_client_addr);
923927

924928
if let Some(clipboard_backend) = clipboard_backend {
925929
connector.attach_static_channel(CliprdrClient::new(Box::new(clipboard_backend)));
@@ -1031,16 +1035,16 @@ where
10311035

10321036
debug!(message = ?rdcleanpath_res, "Received RDCleanPath PDU");
10331037

1034-
let (x224_connection_response, server_cert_chain, server_addr) =
1038+
let (x224_connection_response, server_cert_chain) =
10351039
match rdcleanpath_res.into_enum().context("invalid RDCleanPath PDU")? {
10361040
ironrdp_rdcleanpath::RDCleanPath::Request { .. } => {
10371041
return Err(anyhow::Error::msg("received an unexpected RDCleanPath type (request)").into());
10381042
}
10391043
ironrdp_rdcleanpath::RDCleanPath::Response {
10401044
x224_connection_response,
10411045
server_cert_chain,
1042-
server_addr,
1043-
} => (x224_connection_response, server_cert_chain, server_addr),
1046+
server_addr: _,
1047+
} => (x224_connection_response, server_cert_chain),
10441048
ironrdp_rdcleanpath::RDCleanPath::Err(error) => {
10451049
return Err(
10461050
IronError::from(anyhow::Error::new(error).context("received an RDCleanPath error"))
@@ -1049,12 +1053,6 @@ where
10491053
}
10501054
};
10511055

1052-
let server_addr = server_addr
1053-
.parse()
1054-
.context("failed to parse server address sent by proxy")?;
1055-
1056-
connector.attach_client_addr(server_addr);
1057-
10581056
let connector::ClientConnectorState::ConnectionInitiationWaitConfirm { .. } = connector.state else {
10591057
return Err(anyhow::Error::msg("invalid connector state (wait confirm)").into());
10601058
};

crates/ironrdp/examples/screenshot.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,11 @@ fn connect(
237237
.set_read_timeout(Some(Duration::from_secs(3)))
238238
.expect("set_read_timeout call failed");
239239

240+
let client_addr = tcp_stream.local_addr().context("get socket local address")?;
241+
240242
let mut framed = ironrdp_blocking::Framed::new(tcp_stream);
241243

242-
let mut connector = connector::ClientConnector::new(config).with_client_addr(server_addr);
244+
let mut connector = connector::ClientConnector::new(config, client_addr);
243245

244246
let should_upgrade = ironrdp_blocking::connect_begin(&mut framed, &mut connector).context("begin connection")?;
245247

ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static async Task Main(string[] args)
3232
var readPduTask = framed.ReadPdu();
3333
Action? action = null;
3434
byte[]? payload = null;
35-
if (readPduTask == await Task.WhenAny(readPduTask, Task.Delay(1000)))
35+
if (readPduTask == await Task.WhenAny(readPduTask, Task.Delay(2000)))
3636
{
3737
var pduReadTask = await readPduTask;
3838
action = pduReadTask.Item1;

ffi/dotnet/Devolutions.IronRdp/Generated/ClientConnector.cs

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,45 +37,31 @@ public unsafe ClientConnector(Raw.ClientConnector* handle)
3737
_inner = handle;
3838
}
3939

40+
/// <exception cref="IronRdpException"></exception>
4041
/// <returns>
4142
/// A <c>ClientConnector</c> allocated on Rust side.
4243
/// </returns>
43-
public static ClientConnector New(Config config)
44+
public static ClientConnector New(Config config, string clientAddr)
4445
{
4546
unsafe
4647
{
48+
byte[] clientAddrBuf = DiplomatUtils.StringToUtf8(clientAddr);
49+
nuint clientAddrBufLength = (nuint)clientAddrBuf.Length;
4750
Raw.Config* configRaw;
4851
configRaw = config.AsFFI();
4952
if (configRaw == null)
5053
{
5154
throw new ObjectDisposedException("Config");
5255
}
53-
Raw.ClientConnector* retVal = Raw.ClientConnector.New(configRaw);
54-
return new ClientConnector(retVal);
55-
}
56-
}
57-
58-
/// <summary>
59-
/// Must use
60-
/// </summary>
61-
/// <exception cref="IronRdpException"></exception>
62-
public void WithClientAddr(string clientAddr)
63-
{
64-
unsafe
65-
{
66-
if (_inner == null)
67-
{
68-
throw new ObjectDisposedException("ClientConnector");
69-
}
70-
byte[] clientAddrBuf = DiplomatUtils.StringToUtf8(clientAddr);
71-
nuint clientAddrBufLength = (nuint)clientAddrBuf.Length;
7256
fixed (byte* clientAddrBufPtr = clientAddrBuf)
7357
{
74-
Raw.ConnectorFfiResultVoidBoxIronRdpError result = Raw.ClientConnector.WithClientAddr(_inner, clientAddrBufPtr, clientAddrBufLength);
58+
Raw.ConnectorFfiResultBoxClientConnectorBoxIronRdpError result = Raw.ClientConnector.New(configRaw, clientAddrBufPtr, clientAddrBufLength);
7559
if (!result.isOk)
7660
{
7761
throw new IronRdpException(new IronRdpError(result.Err));
7862
}
63+
Raw.ClientConnector* retVal = result.Ok;
64+
return new ClientConnector(retVal);
7965
}
8066
}
8167
}

0 commit comments

Comments
 (0)