From 30ddbf42e398d21b43e1d8f53784195e26c77a80 Mon Sep 17 00:00:00 2001 From: irving ou Date: Fri, 4 Jul 2025 21:20:10 -0400 Subject: [PATCH 1/8] feat(ffi): expose API for RDCleanPath --- Cargo.lock | 1 + crates/ironrdp/Cargo.toml | 2 + crates/ironrdp/src/lib.rs | 4 + ffi/Cargo.toml | 2 +- .../MainWindow.axaml.cs | 61 ++++-- .../Devolutions.IronRdp.ConnectExample.csproj | 15 ++ .../Program.cs | 75 ++++++-- .../Properties/Resources.Designer.cs | 63 ++++++ .../Properties/Resources.resx | 101 ++++++++++ .../Properties/launchSettings.json | 12 ++ .../Generated/OptionalString.cs | 105 ++++++++++ .../Generated/RawOptionalString.cs | 31 +++ .../Generated/RawRdCleanPath.cs | 27 +++ .../Generated/RawRdCleanPathPdu.cs | 36 ++++ .../Generated/RawRdCleanPathResponse.cs | 30 +++ .../Generated/RawRdCleanPathType.cs | 19 ++ ...hFfiResultBoxRdCleanPathBoxIronRdpError.cs | 46 +++++ ...iResultBoxRdCleanPathPduBoxIronRdpError.cs | 46 +++++ ...ltBoxRdCleanPathResponseBoxIronRdpError.cs | 46 +++++ ...eanpathFfiResultBoxVecU8BoxIronRdpError.cs | 46 +++++ ...FfiResultRdCleanPathTypeBoxIronRdpError.cs | 46 +++++ ...RdcleanpathFfiResultVoidBoxIronRdpError.cs | 36 ++++ ...lsFfiResultBoxBytesSliceBoxIronRdpError.cs | 46 +++++ ...awUtilsFfiResultBoxVecU8BoxIronRdpError.cs | 46 +++++ .../Generated/RawVecVecU8.cs | 30 +++ .../Generated/RdCleanPath.cs | 115 +++++++++++ .../Generated/RdCleanPathPdu.cs | 181 ++++++++++++++++++ .../Generated/RdCleanPathResponse.cs | 157 +++++++++++++++ .../Generated/RdCleanPathType.cs | 19 ++ .../Devolutions.IronRdp/Generated/VecVecU8.cs | 128 +++++++++++++ .../Devolutions.IronRdp/src/Connection.cs | 119 ++++++++++-- ffi/dotnet/Devolutions.IronRdp/src/Framed.cs | 11 +- .../Devolutions.IronRdp/src/RdcleanPath.cs | 14 ++ .../src/WebsocketStream.cs | 64 +++++++ ffi/dotnet/IronRdp.sln.DotSettings | 2 + ffi/src/lib.rs | 1 + ffi/src/rdcleanpath.rs | 159 +++++++++++++++ ffi/src/utils/mod.rs | 46 +++++ 38 files changed, 1932 insertions(+), 56 deletions(-) create mode 100644 ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/Resources.Designer.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/Resources.resx create mode 100644 ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/launchSettings.json create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/OptionalString.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawOptionalString.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPath.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathResponse.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathType.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxVecU8BoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultVoidBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawUtilsFfiResultBoxBytesSliceBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawUtilsFfiResultBoxVecU8BoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawVecVecU8.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPath.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathResponse.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathType.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/VecVecU8.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs create mode 100644 ffi/dotnet/IronRdp.sln.DotSettings create mode 100644 ffi/src/rdcleanpath.rs diff --git a/Cargo.lock b/Cargo.lock index 9500a47f9..b2625f35a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2352,6 +2352,7 @@ dependencies = [ "ironrdp-graphics", "ironrdp-input", "ironrdp-pdu", + "ironrdp-rdcleanpath", "ironrdp-rdpdr", "ironrdp-rdpsnd", "ironrdp-server", diff --git a/crates/ironrdp/Cargo.toml b/crates/ironrdp/Cargo.toml index e68c98d66..7bff94d95 100644 --- a/crates/ironrdp/Cargo.toml +++ b/crates/ironrdp/Cargo.toml @@ -31,6 +31,7 @@ dvc = ["dep:ironrdp-dvc"] rdpdr = ["dep:ironrdp-rdpdr"] rdpsnd = ["dep:ironrdp-rdpsnd"] displaycontrol = ["dep:ironrdp-displaycontrol"] +rdcleanpath = ["dep:ironrdp-rdcleanpath"] # Internal (PRIVATE!) features used to aid testing. # Don't rely on these whatsoever. They may disappear at any time. __bench = ["ironrdp-server/__bench"] @@ -50,6 +51,7 @@ ironrdp-dvc = { path = "../ironrdp-dvc", version = "0.3", optional = true } # pu ironrdp-rdpdr = { path = "../ironrdp-rdpdr", version = "0.3", optional = true } # public ironrdp-rdpsnd = { path = "../ironrdp-rdpsnd", version = "0.5", optional = true } # public ironrdp-displaycontrol = { path = "../ironrdp-displaycontrol", version = "0.3", optional = true } # public +ironrdp-rdcleanpath = { path = "../ironrdp-rdcleanpath", version = "0.1", optional = true } # public [dev-dependencies] ironrdp-blocking = { path = "../ironrdp-blocking", version = "0.5.0" } diff --git a/crates/ironrdp/src/lib.rs b/crates/ironrdp/src/lib.rs index 11a3bdd8c..5f94902c0 100644 --- a/crates/ironrdp/src/lib.rs +++ b/crates/ironrdp/src/lib.rs @@ -63,3 +63,7 @@ pub use ironrdp_session as session; #[cfg(feature = "svc")] #[doc(inline)] pub use ironrdp_svc as svc; + +#[cfg(feature = "rdcleanpath")] +#[doc(inline)] +pub use ironrdp_rdcleanpath as rdclean_path; \ No newline at end of file diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index fbefb2e76..0c31901a9 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] diplomat = "0.7" diplomat-runtime = "0.7" -ironrdp = { path = "../crates/ironrdp", features = ["session", "connector", "dvc", "svc", "rdpdr", "rdpsnd", "graphics", "input", "cliprdr", "displaycontrol"] } +ironrdp = { path = "../crates/ironrdp", features = ["session", "connector", "dvc", "svc", "rdpdr", "rdpsnd", "graphics", "input", "cliprdr", "displaycontrol","rdcleanpath"] } ironrdp-cliprdr-native.path = "../crates/ironrdp-cliprdr-native" ironrdp-core = { path = "../crates/ironrdp-core", features = ["alloc"] } sspi = { version = "0.15", features = ["network_client"] } diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index 747f19545..15e56c5f9 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -7,6 +7,7 @@ using System; using System.ComponentModel; using System.Diagnostics; +using System.IO; using System.Net.Security; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -22,7 +23,7 @@ public partial class MainWindow : Window readonly InputDatabase? _inputDatabase = InputDatabase.New(); ActiveStage? _activeStage; DecodedImage? _decodedImage; - Framed? _framed; + Framed? _framed; WinCliprdr? _cliprdr; private readonly RendererModel _renderModel; private Image? _imageControl; @@ -79,8 +80,10 @@ private void OnOpened(object? sender, EventArgs e) var password = Environment.GetEnvironmentVariable("IRONRDP_PASSWORD"); var domain = Environment.GetEnvironmentVariable("IRONRDP_DOMAIN"); var server = Environment.GetEnvironmentVariable("IRONRDP_SERVER"); + var wsProxy = Environment.GetEnvironmentVariable("IRONRDP_PROXY"); + var wsProxyToken = Environment.GetEnvironmentVariable("IRONRDP_PROXY_TOKEN"); - if (username == null || password == null || domain == null || server == null) + if (username == null || password == null || server == null) { var errorMessage = "Please set the IRONRDP_USERNAME, IRONRDP_PASSWORD, IRONRDP_DOMAIN, and RONRDP_SERVER environment variables"; @@ -106,15 +109,41 @@ private void OnOpened(object? sender, EventArgs e) BeforeConnectSetup(); Task.Run(async () => { - var (res, framed) = await Connection.Connect(config, server, factory); - this._decodedImage = DecodedImage.New(PixelFormat.RgbA32, res.GetDesktopSize().GetWidth(), - res.GetDesktopSize().GetHeight()); - this._activeStage = ActiveStage.New(res); - this._framed = framed; - ReadPduAndProcessActiveStage(); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + try + { + + ConnectionResult res; + Framed framed; + //wsProxy = null; + if (wsProxy != null && wsProxyToken != null) + { + Debug.WriteLine("Connecting via WebSocket proxy"); + (res, framed) = await Connection.ConnectWs( + config, + new RdcleanPathConfig(new Uri(wsProxy), wsProxyToken), + server, + factory); + } + else + { + Debug.WriteLine("Connecting directly to server"); + (res, framed) = await Connection.Connect(config, server, factory); + } + Debug.WriteLine("Connection success"); + this._decodedImage = DecodedImage.New(PixelFormat.RgbA32, res.GetDesktopSize().GetWidth(), + res.GetDesktopSize().GetHeight()); + this._activeStage = ActiveStage.New(res); + this._framed = framed; + ReadPduAndProcessActiveStage(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + HandleClipboardEvents(); + } + } + catch (Exception e) { - HandleClipboardEvents(); + Debug.WriteLine(e); + this.Close(); } }); } @@ -260,12 +289,17 @@ private void HandleClipboardEvents() }); } - private static Config BuildConfig(string username, string password, string domain, int width, int height) + private static Config BuildConfig(string username, string password, string? domain, int width, int height) { ConfigBuilder configBuilder = ConfigBuilder.New(); configBuilder.WithUsernameAndPassword(username, password); - configBuilder.SetDomain(domain); + if (domain != null) + { + configBuilder.SetDomain(domain); + } + configBuilder.SetEnableCredssp(true); + configBuilder.SetEnableTls(true); configBuilder.SetDesktopSize((ushort)height, (ushort)width); configBuilder.SetClientName("IronRdp"); configBuilder.SetClientDir("C:\\"); @@ -395,6 +429,7 @@ private async Task HandleActiveStageOutput(ActiveStageOutputIterator outpu var output = outputIterator .Next()!; // outputIterator.Next() is not null since outputIterator.IsEmpty() is false + Debug.WriteLine($"Output type: {output.GetType()}, Output enum type : {output.GetEnumType()}"); if (output.GetEnumType() == ActiveStageOutputType.Terminate) { return false; @@ -418,7 +453,7 @@ private async Task HandleActiveStageOutput(ActiveStageOutputIterator outpu var writeBuf = WriteBuf.New(); while (true) { - await Connection.SingleSequenceStep(activationSequence, writeBuf,_framed!); + await Connection.SingleSequenceStep(activationSequence, writeBuf, _framed!); if (activationSequence.GetState().GetType() != ConnectionActivationStateType.Finalized) continue; diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.csproj b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.csproj index 1ef474af5..b0fc96a3b 100644 --- a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.csproj +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.csproj @@ -19,4 +19,19 @@ + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs index b685455ca..05468ccb6 100644 --- a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs @@ -1,4 +1,5 @@ -using SixLabors.ImageSharp; +using System.Diagnostics; +using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; namespace Devolutions.IronRdp.ConnectExample @@ -19,11 +20,30 @@ static async Task Main(string[] args) var serverName = arguments["--serverName"]; var username = arguments["--username"]; var password = arguments["--password"]; - var domain = arguments["--domain"]; + arguments.TryGetValue("--domain", out var domain); + arguments.TryGetValue("--proxy", out var wsProxy); + arguments.TryGetValue("--proxyToken", out var wsProxyToken); try { - var (res, framed) = await Connection.Connect(buildConfig(serverName, username, password, domain, 1980, 1080), serverName, null); + ConnectionResult res; + Framed framed; + if (wsProxyToken != null && wsProxy != null) + { + (res, framed) = await Connection.ConnectWs( + buildConfig(serverName, username, password, domain, 1980, 1080), + new RdcleanPathConfig(new Uri(wsProxy), wsProxyToken), + serverName, + null); + } + else + { + (res, framed) = await Connection.Connect( + buildConfig(serverName, username, password, domain, 1980, 1080), + serverName, + null); + } + var decodedImage = DecodedImage.New(PixelFormat.RgbA32, res.GetDesktopSize().GetWidth(), res.GetDesktopSize().GetHeight()); var activeState = ActiveStage.New(res); var keepLooping = true; @@ -37,11 +57,11 @@ static async Task Main(string[] args) var pduReadTask = await readPduTask; action = pduReadTask.Item1; payload = pduReadTask.Item2; - Console.WriteLine($"Action: {action}"); + Debug.WriteLine($"Action: {action}"); } else { - Console.WriteLine("Timeout"); + Debug.WriteLine("Timeout"); break; } var outputIterator = activeState.Process(decodedImage, action, payload); @@ -49,10 +69,10 @@ static async Task Main(string[] args) while (!outputIterator.IsEmpty()) { var output = outputIterator.Next()!; // outputIterator.Next() is not null since outputIterator.IsEmpty() is false - Console.WriteLine($"Output type: {output.GetType()}"); + Debug.WriteLine($"Output type: {output.GetType()}, Output enum type : {output.GetEnumType()}"); if (output.GetEnumType() == ActiveStageOutputType.Terminate) { - Console.WriteLine("Connection terminated."); + Debug.WriteLine("Connection terminated."); keepLooping = false; } @@ -66,8 +86,7 @@ static async Task Main(string[] args) } } - saveImage(decodedImage, "output.png"); - + saveImage(decodedImage, "C:\\dev\\IronRDP\\output.bmp"); } catch (Exception e) { @@ -104,7 +123,7 @@ private static void saveImage(DecodedImage decodedImage, string v) } // Save the image as bitmap. - image.Save("./output.bmp"); + image.Save(v); } static Dictionary? ParseArguments(string[] args) @@ -134,6 +153,10 @@ private static void saveImage(DecodedImage decodedImage, string v) return null; } lastKey = arg; + } + else if (arg == "\\" || arg == "//") + { + } else { @@ -160,7 +183,15 @@ private static void saveImage(DecodedImage decodedImage, string v) static bool IsValidArgument(string argument) { - var validArguments = new List { "--serverName", "--username", "--password", "--domain" }; + var validArguments = new List + { + "--serverName", + "--username", + "--password", + "--domain", + "--proxy", + "--proxyToken" + }; return validArguments.Contains(argument); } @@ -168,19 +199,26 @@ static void PrintHelp() { Console.WriteLine("Usage: dotnet run -- [OPTIONS]"); Console.WriteLine("Options:"); - Console.WriteLine(" --serverName The name of the server to connect to."); - Console.WriteLine(" --username The username for connection."); - Console.WriteLine(" --password The password for connection."); - Console.WriteLine(" --domain The domain of the server."); - Console.WriteLine(" --help Show this message and exit."); + Console.WriteLine(" --serverName The name of the server to connect to."); + Console.WriteLine(" --username The username for connection."); + Console.WriteLine(" --password The password for connection."); + Console.WriteLine(" --domain The domain of the server."); + Console.WriteLine(" --proxy WebSocket proxy URL."); + Console.WriteLine(" --proxyToken Authentication token for the proxy."); + Console.WriteLine(" --help Show this message and exit."); } - private static Config buildConfig(string servername, string username, string password, string domain, int width, int height) + private static Config buildConfig(string servername, string username, string password, string? domain, int width, int height) { ConfigBuilder configBuilder = ConfigBuilder.New(); configBuilder.WithUsernameAndPassword(username, password); - configBuilder.SetDomain(domain); + if (domain != null) + { + configBuilder.SetDomain(domain); + } + configBuilder.SetEnableCredssp(true); + configBuilder.SetEnableTls(true); configBuilder.SetDesktopSize((ushort)height, (ushort)width); configBuilder.SetClientName("IronRdp"); configBuilder.SetClientDir("C:\\"); @@ -188,6 +226,5 @@ private static Config buildConfig(string servername, string username, string pas return configBuilder.Build(); } - } } diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/Resources.Designer.cs b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/Resources.Designer.cs new file mode 100644 index 000000000..eb168c12f --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Devolutions.IronRdp.ConnectExample.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Devolutions.IronRdp.ConnectExample.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/Resources.resx b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/Resources.resx new file mode 100644 index 000000000..4fdb1b6af --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/Resources.resx @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/launchSettings.json b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/launchSettings.json new file mode 100644 index 000000000..227775bb6 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Devolutions.IronRdp.ConnectExample": { + "commandName": "Project", + "commandLineArgs": "--serverName 10.10.0.3 --username Administrator --password DevoLabs123! \\\r\n--proxy ws://localhost:7171/jet/rdp \\\r\n--proxyToken eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkFTU09DSUFUSU9OIn0.eyJkc3RfaHN0IjoiMTAuMTAuMC4zOjMzODkiLCJkc3RfcHdkIjoiRGV2b0xhYnMxMjMhIiwiZHN0X3VzciI6IkFkbWluaXN0cmF0b3IiLCJleHAiOjE3NTEzMjcyOTAsImpldF9haWQiOiIyZjQyNmY2Yy01OThiLTRhYzktOTllNS00NzcxNzY4OWI2ZmUiLCJqZXRfYXAiOiJyZHAiLCJqZXRfY20iOiJmd2QiLCJqZXRfcmVjIjoibm9uZSIsImp0aSI6IjM2NjgzZTU5LWQ5NjUtNDkyNS1hMmNkLTBlNWNmZjUzNDhlZiIsIm5iZiI6MTc1MTMyNjM5MCwicHJ4X3B3ZCI6IkRldm9sYWJzMTIzISIsInByeF91c3IiOiJBZG1pbmlzdHJhdG9yQGFkLml0LWhlbHAubmluamEifQ.mfTBuXqFlqpL6zm6L798s7Q-aowPtC1HRtQt_IiXLiY3ToWLEckMRctr5dO_IZKxOMxI5bcK6dW3PgEL5XXksPesDHT8Ykt1rAmFtivCqL-iy68sGfUIkk8IDlbV5aRTrYXVXGPI7Ofj1XrKN3snvEUq95w8CYXRP3b5Yp-3vauM64oxGyGK9rlVmg3xoIuIthGGkMFWvRGYwLbzdNs8DcW2h_0Q4Yt2ZeXURPmO0SRTy1Kt7WOcNTsq6auXA1Hu6nTmVYTE0zERTUnWJF_W7iKQ4ggWY7KerAr03_SVxuxr9sI8Ah-Vx4FTv9VI9b5soo2vE8MLL6kMLpfABbHv_A\r\n", + "environmentVariables": { + "IRONRDP_LOG": "trace", + "IRONRDP_LOG_PATH": "C:\\\\dev\\\\IronRDP\\\\ironrdp-dotnet-ws.log" + } + } + } +} \ No newline at end of file diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/OptionalString.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/OptionalString.cs new file mode 100644 index 000000000..a48a411e2 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/OptionalString.cs @@ -0,0 +1,105 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class OptionalString: IDisposable +{ + private unsafe Raw.OptionalString* _inner; + + /// + /// Creates a managed OptionalString from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe OptionalString(Raw.OptionalString* handle) + { + _inner = handle; + } + + public bool IsSome() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("OptionalString"); + } + bool retVal = Raw.OptionalString.IsSome(_inner); + return retVal; + } + } + + /// + /// A OptionalString allocated on Rust side. + /// + public static OptionalString New(string value) + { + unsafe + { + byte[] valueBuf = DiplomatUtils.StringToUtf8(value); + nuint valueBufLength = (nuint)valueBuf.Length; + fixed (byte* valueBufPtr = valueBuf) + { + Raw.OptionalString* retVal = Raw.OptionalString.New(valueBufPtr, valueBufLength); + return new OptionalString(retVal); + } + } + } + + /// + /// A OptionalString allocated on Rust side. + /// + public static OptionalString NewEmpty() + { + unsafe + { + Raw.OptionalString* retVal = Raw.OptionalString.NewEmpty(); + return new OptionalString(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.OptionalString* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.OptionalString.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~OptionalString() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawOptionalString.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawOptionalString.cs new file mode 100644 index 000000000..53a16ba19 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawOptionalString.cs @@ -0,0 +1,31 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct OptionalString +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OptionalString_is_some", ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.U1)] + public static unsafe extern bool IsSome(OptionalString* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OptionalString_new", ExactSpelling = true)] + public static unsafe extern OptionalString* New(byte* value, nuint valueSz); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OptionalString_new_empty", ExactSpelling = true)] + public static unsafe extern OptionalString* NewEmpty(); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OptionalString_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(OptionalString* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPath.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPath.cs new file mode 100644 index 000000000..6a547e85c --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPath.cs @@ -0,0 +1,27 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdCleanPath +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPath_get_type", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError GetType(RdCleanPath* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPath_to_response", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError ToResponse(RdCleanPath* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPath_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(RdCleanPath* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs new file mode 100644 index 000000000..24ab39004 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs @@ -0,0 +1,36 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdCleanPathPdu +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_new_request", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError NewRequest(VecU8* x224Pdu, byte* destination, nuint destinationSz, byte* proxyAuth, nuint proxyAuthSz, OptionalString* pcb); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_to_der", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxVecU8BoxIronRdpError ToDer(RdCleanPathPdu* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_get_hint", ExactSpelling = true)] + public static unsafe extern PduHint* GetHint(); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_from_der", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError FromDer(byte* der, nuint derSz); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_into_enum", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError IntoEnum(RdCleanPathPdu* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(RdCleanPathPdu* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathResponse.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathResponse.cs new file mode 100644 index 000000000..87e410098 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathResponse.cs @@ -0,0 +1,30 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdCleanPathResponse +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathResponse_get_x224_connection_response", ExactSpelling = true)] + public static unsafe extern VecU8* GetX224ConnectionResponse(RdCleanPathResponse* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathResponse_get_server_cert_chain", ExactSpelling = true)] + public static unsafe extern VecVecU8* GetServerCertChain(RdCleanPathResponse* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathResponse_get_server_addr", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultVoidBoxIronRdpError GetServerAddr(RdCleanPathResponse* self, DiplomatWriteable* serverAddr); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathResponse_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(RdCleanPathResponse* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathType.cs new file mode 100644 index 000000000..03b830227 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathType.cs @@ -0,0 +1,19 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +public enum RdCleanPathType +{ + Request = 0, + Response = 1, + Error = 2, +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError.cs new file mode 100644 index 000000000..8579eff42 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal RdCleanPath* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe RdCleanPath* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError.cs new file mode 100644 index 000000000..74bf82a09 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal RdCleanPathPdu* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe RdCleanPathPdu* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError.cs new file mode 100644 index 000000000..888ed4f73 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal RdCleanPathResponse* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe RdCleanPathResponse* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxVecU8BoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxVecU8BoxIronRdpError.cs new file mode 100644 index 000000000..7cce2e6ad --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxVecU8BoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultBoxVecU8BoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal VecU8* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe VecU8* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError.cs new file mode 100644 index 000000000..155e0db03 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal RdCleanPathType ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe RdCleanPathType Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultVoidBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultVoidBoxIronRdpError.cs new file mode 100644 index 000000000..c1e085512 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultVoidBoxIronRdpError.cs @@ -0,0 +1,36 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultVoidBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawUtilsFfiResultBoxBytesSliceBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawUtilsFfiResultBoxBytesSliceBoxIronRdpError.cs new file mode 100644 index 000000000..5527183b6 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawUtilsFfiResultBoxBytesSliceBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct UtilsFfiResultBoxBytesSliceBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal BytesSlice* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe BytesSlice* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawUtilsFfiResultBoxVecU8BoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawUtilsFfiResultBoxVecU8BoxIronRdpError.cs new file mode 100644 index 000000000..823dd2517 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawUtilsFfiResultBoxVecU8BoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct UtilsFfiResultBoxVecU8BoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal VecU8* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe VecU8* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawVecVecU8.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawVecVecU8.cs new file mode 100644 index 000000000..b98525e64 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawVecVecU8.cs @@ -0,0 +1,30 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct VecVecU8 +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "VecVecU8_get_len", ExactSpelling = true)] + public static unsafe extern nuint GetLen(VecVecU8* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "VecVecU8_get_vecu8", ExactSpelling = true)] + public static unsafe extern UtilsFfiResultBoxVecU8BoxIronRdpError GetVecu8(VecVecU8* self, nuint index); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "VecVecU8_get_slice", ExactSpelling = true)] + public static unsafe extern UtilsFfiResultBoxBytesSliceBoxIronRdpError GetSlice(VecVecU8* self, nuint index); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "VecVecU8_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(VecVecU8* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPath.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPath.cs new file mode 100644 index 000000000..df90355c3 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPath.cs @@ -0,0 +1,115 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class RdCleanPath: IDisposable +{ + private unsafe Raw.RdCleanPath* _inner; + + public RdCleanPathType Type + { + get + { + return GetType(); + } + } + + /// + /// Creates a managed RdCleanPath from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe RdCleanPath(Raw.RdCleanPath* handle) + { + _inner = handle; + } + + /// + /// + /// A RdCleanPathType allocated on C# side. + /// + public RdCleanPathType GetType() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPath"); + } + Raw.RdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError result = Raw.RdCleanPath.GetType(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.RdCleanPathType retVal = result.Ok; + return (RdCleanPathType)retVal; + } + } + + /// + /// + /// A RdCleanPathResponse allocated on Rust side. + /// + public RdCleanPathResponse ToResponse() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPath"); + } + Raw.RdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError result = Raw.RdCleanPath.ToResponse(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.RdCleanPathResponse* retVal = result.Ok; + return new RdCleanPathResponse(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.RdCleanPath* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.RdCleanPath.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~RdCleanPath() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs new file mode 100644 index 000000000..4490e416c --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs @@ -0,0 +1,181 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class RdCleanPathPdu: IDisposable +{ + private unsafe Raw.RdCleanPathPdu* _inner; + + /// + /// Creates a managed RdCleanPathPdu from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe RdCleanPathPdu(Raw.RdCleanPathPdu* handle) + { + _inner = handle; + } + + /// + /// + /// A RdCleanPathPdu allocated on Rust side. + /// + public static RdCleanPathPdu NewRequest(VecU8 x224Pdu, string destination, string proxyAuth, OptionalString pcb) + { + unsafe + { + byte[] destinationBuf = DiplomatUtils.StringToUtf8(destination); + byte[] proxyAuthBuf = DiplomatUtils.StringToUtf8(proxyAuth); + nuint destinationBufLength = (nuint)destinationBuf.Length; + nuint proxyAuthBufLength = (nuint)proxyAuthBuf.Length; + Raw.VecU8* x224PduRaw; + x224PduRaw = x224Pdu.AsFFI(); + if (x224PduRaw == null) + { + throw new ObjectDisposedException("VecU8"); + } + Raw.OptionalString* pcbRaw; + pcbRaw = pcb.AsFFI(); + if (pcbRaw == null) + { + throw new ObjectDisposedException("OptionalString"); + } + fixed (byte* destinationBufPtr = destinationBuf) + { + fixed (byte* proxyAuthBufPtr = proxyAuthBuf) + { + Raw.RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError result = Raw.RdCleanPathPdu.NewRequest(x224PduRaw, destinationBufPtr, destinationBufLength, proxyAuthBufPtr, proxyAuthBufLength, pcbRaw); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.RdCleanPathPdu* retVal = result.Ok; + return new RdCleanPathPdu(retVal); + } + } + } + } + + /// + /// + /// A VecU8 allocated on Rust side. + /// + public VecU8 ToDer() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathPdu"); + } + Raw.RdcleanpathFfiResultBoxVecU8BoxIronRdpError result = Raw.RdCleanPathPdu.ToDer(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.VecU8* retVal = result.Ok; + return new VecU8(retVal); + } + } + + /// + /// A PduHint allocated on Rust side. + /// + public static PduHint GetHint() + { + unsafe + { + Raw.PduHint* retVal = Raw.RdCleanPathPdu.GetHint(); + return new PduHint(retVal); + } + } + + /// + /// + /// A RdCleanPathPdu allocated on Rust side. + /// + public static RdCleanPathPdu FromDer(byte[] der) + { + unsafe + { + nuint derLength = (nuint)der.Length; + fixed (byte* derPtr = der) + { + Raw.RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError result = Raw.RdCleanPathPdu.FromDer(derPtr, derLength); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.RdCleanPathPdu* retVal = result.Ok; + return new RdCleanPathPdu(retVal); + } + } + } + + /// + /// + /// A RdCleanPath allocated on Rust side. + /// + public RdCleanPath IntoEnum() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathPdu"); + } + Raw.RdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError result = Raw.RdCleanPathPdu.IntoEnum(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.RdCleanPath* retVal = result.Ok; + return new RdCleanPath(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.RdCleanPathPdu* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.RdCleanPathPdu.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~RdCleanPathPdu() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathResponse.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathResponse.cs new file mode 100644 index 000000000..539b23fc5 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathResponse.cs @@ -0,0 +1,157 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class RdCleanPathResponse: IDisposable +{ + private unsafe Raw.RdCleanPathResponse* _inner; + + public string ServerAddr + { + get + { + return GetServerAddr(); + } + } + + public VecVecU8 ServerCertChain + { + get + { + return GetServerCertChain(); + } + } + + public VecU8 X224ConnectionResponse + { + get + { + return GetX224ConnectionResponse(); + } + } + + /// + /// Creates a managed RdCleanPathResponse from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe RdCleanPathResponse(Raw.RdCleanPathResponse* handle) + { + _inner = handle; + } + + /// + /// A VecU8 allocated on Rust side. + /// + public VecU8 GetX224ConnectionResponse() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathResponse"); + } + Raw.VecU8* retVal = Raw.RdCleanPathResponse.GetX224ConnectionResponse(_inner); + return new VecU8(retVal); + } + } + + /// + /// A VecVecU8 allocated on Rust side. + /// + public VecVecU8 GetServerCertChain() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathResponse"); + } + Raw.VecVecU8* retVal = Raw.RdCleanPathResponse.GetServerCertChain(_inner); + return new VecVecU8(retVal); + } + } + + /// + public void GetServerAddr(DiplomatWriteable serverAddr) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathResponse"); + } + Raw.RdcleanpathFfiResultVoidBoxIronRdpError result = Raw.RdCleanPathResponse.GetServerAddr(_inner, &serverAddr); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + } + } + + /// + public string GetServerAddr() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathResponse"); + } + DiplomatWriteable writeable = new DiplomatWriteable(); + Raw.RdcleanpathFfiResultVoidBoxIronRdpError result = Raw.RdCleanPathResponse.GetServerAddr(_inner, &writeable); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + string retVal = writeable.ToUnicode(); + writeable.Dispose(); + return retVal; + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.RdCleanPathResponse* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.RdCleanPathResponse.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~RdCleanPathResponse() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathType.cs new file mode 100644 index 000000000..a8ac2f1a4 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathType.cs @@ -0,0 +1,19 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public enum RdCleanPathType +{ + Request = 0, + Response = 1, + Error = 2, +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/VecVecU8.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/VecVecU8.cs new file mode 100644 index 000000000..f3dc53612 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/VecVecU8.cs @@ -0,0 +1,128 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class VecVecU8: IDisposable +{ + private unsafe Raw.VecVecU8* _inner; + + public nuint Len + { + get + { + return GetLen(); + } + } + + /// + /// Creates a managed VecVecU8 from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe VecVecU8(Raw.VecVecU8* handle) + { + _inner = handle; + } + + public nuint GetLen() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("VecVecU8"); + } + nuint retVal = Raw.VecVecU8.GetLen(_inner); + return retVal; + } + } + + /// + /// + /// A VecU8 allocated on Rust side. + /// + public VecU8 GetVecu8(nuint index) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("VecVecU8"); + } + Raw.UtilsFfiResultBoxVecU8BoxIronRdpError result = Raw.VecVecU8.GetVecu8(_inner, index); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.VecU8* retVal = result.Ok; + return new VecU8(retVal); + } + } + + /// + /// + /// A BytesSlice allocated on Rust side. + /// + public BytesSlice GetSlice(nuint index) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("VecVecU8"); + } + Raw.UtilsFfiResultBoxBytesSliceBoxIronRdpError result = Raw.VecVecU8.GetSlice(_inner, index); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.BytesSlice* retVal = result.Ok; + return new BytesSlice(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.VecVecU8* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.VecVecU8.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~VecVecU8() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs index ed638b934..c0d6d4098 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs @@ -1,21 +1,24 @@ +using System.Diagnostics; +using Devolutions.IronRdp.src; using System.Net; using System.Net.Security; using System.Net.Sockets; +using System.Net.WebSockets; +using System.Security.Cryptography.X509Certificates; namespace Devolutions.IronRdp; public static class Connection { - public static async Task<(ConnectionResult, Framed)> Connect(Config config, string serverName, + public static async Task<(ConnectionResult, Framed)> Connect(Config config, string serverName, CliprdrBackendFactory? factory, int port = 3389) { var client = await CreateTcpConnection(serverName, port); - string clientAddr = client.Client.LocalEndPoint.ToString(); - Console.WriteLine(clientAddr); + var clientAddr = client.Client.LocalEndPoint?.ToString(); var framed = new Framed(client.GetStream()); - var connector = ClientConnector.New(config, clientAddr); + var connector = ClientConnector.New(config, clientAddr!); connector.WithDynamicChannelDisplayControl(); @@ -26,13 +29,96 @@ public static class Connection } await ConnectBegin(framed, connector); - var (serverPublicKey, framedSsl) = await SecurityUpgrade(framed, connector); + (var serverPublicKey,Framed framedSsl) = await SecurityUpgrade(framed, connector); var result = await ConnectFinalize(serverName, connector, serverPublicKey, framedSsl); return (result, framedSsl); } - private static async Task<(byte[], Framed)> SecurityUpgrade(Framed framed, + public static async Task<(ConnectionResult, Framed)> ConnectWs(Config config, RdcleanPathConfig rdcleanPathConfig, string serverName, CliprdrBackendFactory? factory, int port = 3389, CancellationToken token = default) + { + var websocket = new ClientWebSocket(); + + await websocket.ConnectAsync(rdcleanPathConfig.Uri, token); + var stream = new WebsocketStream(websocket); + var framed = new Framed(stream); + + var client = await CreateTcpConnection(serverName, port); + var clientAddr = client.Client.LocalEndPoint?.ToString(); + + var connector = ClientConnector.New(config, clientAddr!); + + connector.WithDynamicChannelDisplayControl(); + + if (factory != null) + { + var cliprdr = factory.BuildCliprdr(); + connector.AttachStaticCliprdr(cliprdr); + } + + string destination = serverName + ":" + port; + + var serverPublicKey = await ConnectRdCleanPath(framed, connector, destination, rdcleanPathConfig.AuthToken); + + var result = await ConnectFinalize(serverName, connector, serverPublicKey, framed); + + + return (result, framed); + } + + private static async Task ConnectRdCleanPath( + Framed framed, + ClientConnector connector, + string destination, + string proxyAuth + ) + where S : Stream + { + var writeBuf = WriteBuf.New(); + connector.StepNoInput(writeBuf); + var x224Pdu = writeBuf.GetFilled(); + + var rdcleanPathReq = RdCleanPathPdu.NewRequest( + x224Pdu, + destination, + proxyAuth, + OptionalString.NewEmpty() + ); + + var rdcleanPathPdu = rdcleanPathReq.ToDer(); + var bytes = Utils.VecU8ToByte(rdcleanPathPdu); + await framed.Write(bytes); + + var pduHint = RdCleanPathPdu.GetHint(); + + var pdu = await framed.ReadByHint(pduHint); + var rdcleanPathResponseDer = RdCleanPathPdu.FromDer(pdu); + var rdcleanPath = rdcleanPathResponseDer.IntoEnum(); + if (rdcleanPath.GetType() != RdCleanPathType.Response) + { + throw new IronRdpLibException(IronRdpLibExceptionType.ConnectionFailed, + "RdCleanPath response is not a response type"); + } + + var rdCleanPathResponse = rdcleanPath.ToResponse(); + + var x224ConnectionResponse = rdCleanPathResponse.GetX224ConnectionResponse(); + var serverCertChain = rdCleanPathResponse.GetServerCertChain(); + + writeBuf.Clear(); + connector.Step(Utils.VecU8ToByte(x224ConnectionResponse), writeBuf); + + var serverCertVec = serverCertChain.GetVecu8(0); + var serverCertByte = Utils.VecU8ToByte(serverCertVec); + var serverCert = new X509Certificate2(serverCertByte); + var serverPublicKey = serverCert.GetPublicKey(); + + connector.MarkSecurityUpgradeAsDone(); + + return serverPublicKey; + } + + private static async Task<(byte[], Framed)> SecurityUpgrade(Framed framed, ClientConnector connector) { var (streamRequireUpgrade, _) = framed.GetInner(); @@ -47,7 +133,7 @@ await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions() AllowTlsResume = false }); var serverPublicKey = await promise.Task; - Framed framedSsl = new(sslStream); + Framed framedSsl = new(sslStream); connector.MarkSecurityUpgradeAsDone(); return (serverPublicKey, framedSsl); @@ -63,18 +149,18 @@ private static async Task ConnectBegin(Framed framed, ClientConne } - private static async Task ConnectFinalize(string serverName, ClientConnector connector, - byte[] serverPubKey, Framed framedSsl) + private static async Task ConnectFinalize(string serverName, ClientConnector connector, + byte[] serverPubKey, Framed upgradedFramed) where S : System.IO.Stream { var writeBuf2 = WriteBuf.New(); if (connector.ShouldPerformCredssp()) { - await PerformCredsspSteps(connector, serverName, writeBuf2, framedSsl, serverPubKey); + await PerformCredsspSteps(connector, serverName, writeBuf2, upgradedFramed, serverPubKey); } while (!connector.GetDynState().IsTerminal()) { - await SingleSequenceStep(connector, writeBuf2, framedSsl); + await SingleSequenceStep(connector, writeBuf2, upgradedFramed); } ClientConnectorState state = connector.ConsumeAndCastToClientConnectorState(); @@ -83,14 +169,12 @@ private static async Task ConnectFinalize(string serverName, C { return state.GetConnectedResult(); } - else - { - throw new IronRdpLibException(IronRdpLibExceptionType.ConnectionFailed, "Connection failed"); - } + + throw new IronRdpLibException(IronRdpLibExceptionType.ConnectionFailed, "Connection failed"); } - private static async Task PerformCredsspSteps(ClientConnector connector, string serverName, WriteBuf writeBuf, - Framed framedSsl, byte[] serverpubkey) + private static async Task PerformCredsspSteps(ClientConnector connector, string serverName, WriteBuf writeBuf, + Framed framedSsl, byte[] serverpubkey) where S : Stream { var credsspSequenceInitResult = CredsspSequence.Init(connector, serverName, serverpubkey, null); var credsspSequence = credsspSequenceInitResult.GetCredsspSequence(); @@ -227,6 +311,7 @@ static async Task CreateTcpConnection(String servername, int port) return client; } + } public static class Utils diff --git a/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs b/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs index f03530f3d..cde759d58 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Runtime.InteropServices; namespace Devolutions.IronRdp; @@ -32,13 +33,11 @@ public Framed(TS stream) var action = pduInfo.GetAction(); return (action, frame); } - else + + var len = await this.Read(); + if (len == 0) { - var len = await this.Read(); - if (len == 0) - { - throw new IronRdpLibException(IronRdpLibExceptionType.EndOfFile, "EOF on ReadPdu"); - } + throw new IronRdpLibException(IronRdpLibExceptionType.EndOfFile, "EOF on ReadPdu"); } } } diff --git a/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs b/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs new file mode 100644 index 000000000..18a466686 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs @@ -0,0 +1,14 @@ +using System; + +public class RdcleanPathConfig +{ + public Uri Uri { get; private set; } + + public string AuthToken { get; private set; } + + public RdcleanPathConfig(Uri url, string authToken) + { + Uri = url; + AuthToken = authToken; + } +} \ No newline at end of file diff --git a/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs b/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs new file mode 100644 index 000000000..01a75193b --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs @@ -0,0 +1,64 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Devolutions.IronRdp.src +{ + public class WebsocketStream : Stream + { + private readonly ClientWebSocket _webSocket; + + public WebsocketStream(ClientWebSocket webSocket) + { + _webSocket = webSocket; + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => true; + public override long Length => throw new NotSupportedException(); + public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + var result = await _webSocket.ReceiveAsync( + new ArraySegment(buffer, offset, count), cancellationToken); + + if (result.CloseStatus.HasValue) // remote sent a CLOSE frame + { + return 0; // treat as end-of-stream + } + + return result.Count; + } + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (count == 0) + { + // Note: this is particularly important, if we send a zero-length frame, + // somehow Gateway will raise TLS issue during the proxy. + return; // Nothing to write + } + + await _webSocket.SendAsync( + new ArraySegment(buffer, offset, count), + WebSocketMessageType.Binary, + true, + cancellationToken); + } + + public override void Flush() + { + } + + // Not supported + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + public override void SetLength(long value) => throw new NotSupportedException(); + public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + } +} diff --git a/ffi/dotnet/IronRdp.sln.DotSettings b/ffi/dotnet/IronRdp.sln.DotSettings new file mode 100644 index 000000000..24b62fa6a --- /dev/null +++ b/ffi/dotnet/IronRdp.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index 407847ce4..44009cd5a 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -11,6 +11,7 @@ pub mod graphics; pub mod input; pub mod log; pub mod pdu; +pub mod rdcleanpath; pub mod session; pub mod svc; pub mod utils; diff --git a/ffi/src/rdcleanpath.rs b/ffi/src/rdcleanpath.rs new file mode 100644 index 000000000..1604c5437 --- /dev/null +++ b/ffi/src/rdcleanpath.rs @@ -0,0 +1,159 @@ +#[derive(Clone, Copy, Debug)] +struct RDCleanPathHint; + +const RDCLEANPATH_HINT: RDCleanPathHint = RDCleanPathHint; + +impl ironrdp::pdu::PduHint for RDCleanPathHint { + fn find_size(&self, bytes: &[u8]) -> ironrdp::core::DecodeResult> { + match ironrdp::rdclean_path::RDCleanPathPdu::detect(bytes) { + ironrdp::rdclean_path::DetectionResult::Detected { total_length, .. } => Ok(Some((true, total_length))), + ironrdp::rdclean_path::DetectionResult::NotEnoughBytes => Ok(None), + ironrdp::rdclean_path::DetectionResult::Failed => Err(ironrdp::core::other_err!( + "RDCleanPathHint", + "detection failed (invalid PDU)" + )), + } + } +} + +#[diplomat::bridge] +pub mod ffi { + use diplomat_runtime::DiplomatWriteable; + use ironrdp::rdclean_path::der::asn1::OctetString; + use core::fmt::Write; + + use crate::error::ffi::{IronRdpError, IronRdpErrorKind}; + use crate::error::ValueConsumedError; + use crate::utils::ffi::{OptionalString, VecU8, VecVecU8}; + + #[diplomat::opaque] + pub struct RdCleanPathPdu(pub Option); + + impl RdCleanPathPdu { + pub fn new_request( + x224_pdu: &VecU8, + destination: &str, + proxy_auth: &str, + pcb: &OptionalString, + ) -> Result, Box> { + let x224_pdu = &x224_pdu.0; + let destination = destination.to_owned(); + let proxy_auth = proxy_auth.to_owned(); + + let cleanpath_pdu = ironrdp::rdclean_path::RDCleanPathPdu::new_request( + x224_pdu.to_owned(), + destination, + proxy_auth, + pcb.into(), + ) + .map_err(|_| IronRdpErrorKind::EncodeError)?; + + Ok(Box::new(RdCleanPathPdu(Some(cleanpath_pdu)))) + } + + pub fn to_der(&self) -> Result, Box> { + let Some(pdu) = self.0.as_ref() else { + return Err(ValueConsumedError::for_item("RdCleanPathPdu").into()); + }; + + let der = pdu.to_der().map_err(|_| IronRdpErrorKind::EncodeError)?; + Ok(Box::new(VecU8(der))) + } + + pub fn get_hint<'a>() -> Box> { + Box::new(crate::connector::ffi::PduHint(&super::RDCLEANPATH_HINT)) + } + + pub fn from_der(der: &[u8]) -> Result, Box> { + let pdu = + ironrdp::rdclean_path::RDCleanPathPdu::from_der(der).map_err(|_| IronRdpErrorKind::DecodeError)?; + Ok(Box::new(RdCleanPathPdu(Some(pdu)))) + } + + pub fn into_enum(&mut self) -> Result, Box> { + let Some(pdu) = self.0.take() else { + return Err(ValueConsumedError::for_item("RdCleanPathPdu").into()); + }; + + let rdclean_path = pdu + .into_enum() + .map(|rd_clean_path| Box::new(RdCleanPath(Some(rd_clean_path)))) + .map_err(|_| IronRdpErrorKind::EncodeError)?; + + Ok(rdclean_path) + } + } + + #[diplomat::opaque] + pub struct RdCleanPath(pub Option); + + #[diplomat::opaque] + pub struct RdCleanPathResponse { + x224_connection_response: OctetString, + server_cert_chain: Vec, + server_addr: String, + } + + impl RdCleanPathResponse { + pub fn get_x224_connection_response(&self) -> Box { + VecU8::from_bytes(self.x224_connection_response.as_bytes()) + } + + pub fn get_server_cert_chain(&self) -> Box { + let vecs = self + .server_cert_chain + .iter() + .map(|cert| cert.as_bytes().to_vec()) + .collect::>(); + + Box::new(VecVecU8(vecs)) + } + + pub fn get_server_addr(&self, server_addr: &mut DiplomatWriteable) -> Result<(), Box> { + write!(server_addr, "{}", self.server_addr).map_err(|_| IronRdpErrorKind::IO)?; + + Ok(()) + } + } + + pub enum RdCleanPathType { + Request, + Response, + Error, + } + + impl RdCleanPath { + pub fn get_type(&self) -> Result> { + let value = self + .0 + .as_ref() + .ok_or_else(|| ValueConsumedError::for_item("RdCleanPath"))?; + + Ok(match value { + ironrdp::rdclean_path::RDCleanPath::Request { .. } => RdCleanPathType::Request, + ironrdp::rdclean_path::RDCleanPath::Response { .. } => RdCleanPathType::Response, + ironrdp::rdclean_path::RDCleanPath::Err(_) => RdCleanPathType::Error, + }) + } + + pub fn to_response(&mut self) -> Result, Box> { + let value = self + .0 + .take() + .ok_or_else(|| ValueConsumedError::for_item("RdCleanPath"))?; + + match value { + ironrdp::rdclean_path::RDCleanPath::Response { + x224_connection_response, + server_cert_chain, + server_addr, + } => Ok(Box::new(RdCleanPathResponse { + x224_connection_response, + server_cert_chain, + server_addr, + })), + _ => Err(IronRdpErrorKind::IncorrectEnumType.into()), + } + } + } +} diff --git a/ffi/src/utils/mod.rs b/ffi/src/utils/mod.rs index 672c5653e..839c8a9d3 100644 --- a/ffi/src/utils/mod.rs +++ b/ffi/src/utils/mod.rs @@ -3,6 +3,29 @@ pub mod ffi { use crate::error::ffi::IronRdpError; + #[diplomat::opaque] + pub struct VecVecU8(pub Vec>); + + impl VecVecU8 { + pub fn get_len(&self) -> usize { + self.0.len() + } + + pub fn get_vecu8(&self, index: usize) -> Result, Box> { + if index >= self.0.len() { + return Err("index out of bounds".into()); + } + Ok(Box::new(VecU8(self.0[index].clone()))) + } + + pub fn get_slice<'a>(&'a self, index: usize) -> Result>, Box> { + if index >= self.0.len() { + return Err("index out of bounds".into()); + } + Ok(Box::new(BytesSlice(&self.0[index]))) + } + } + #[diplomat::opaque] pub struct VecU8(pub Vec); @@ -79,4 +102,27 @@ pub mod ffi { self.0.ok_or_else(|| "value is None".into()) } } + + #[diplomat::opaque] + pub struct OptionalString(pub(crate) Option); + + impl OptionalString { + pub fn is_some(&self) -> bool { + self.0.is_some() + } + + pub fn new(value: &str) -> Box { + Box::new(OptionalString(Some(value.to_owned()))) + } + + pub fn new_empty() -> Box { + Box::new(OptionalString(None)) + } + } +} + +impl From<&ffi::OptionalString> for Option { + fn from(value: &ffi::OptionalString) -> Self { + value.0.clone() + } } From 66717d0b3a239edbefe80358e8033dd4f5295519 Mon Sep 17 00:00:00 2001 From: irving ou Date: Fri, 4 Jul 2025 21:20:34 -0400 Subject: [PATCH 2/8] ci --- ffi/src/rdcleanpath.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/src/rdcleanpath.rs b/ffi/src/rdcleanpath.rs index 1604c5437..a138b516c 100644 --- a/ffi/src/rdcleanpath.rs +++ b/ffi/src/rdcleanpath.rs @@ -18,9 +18,9 @@ impl ironrdp::pdu::PduHint for RDCleanPathHint { #[diplomat::bridge] pub mod ffi { + use core::fmt::Write; use diplomat_runtime::DiplomatWriteable; use ironrdp::rdclean_path::der::asn1::OctetString; - use core::fmt::Write; use crate::error::ffi::{IronRdpError, IronRdpErrorKind}; use crate::error::ValueConsumedError; From d009173484e66a38cb9ca2fa082a24abb0d5186b Mon Sep 17 00:00:00 2001 From: irving ou Date: Sat, 5 Jul 2025 18:20:28 -0400 Subject: [PATCH 3/8] review fix --- .../Generated/IronRdpErrorKind.cs | 1 + .../Generated/RawIronRdpErrorKind.cs | 1 + .../Generated/RawRdCleanPath.cs | 27 --- .../Generated/RawRdCleanPathPdu.cs | 13 +- .../Generated/RawRdCleanPathRequestBuilder.cs | 39 ++++ .../Generated/RawRdCleanPathResponse.cs | 30 ---- .../Generated/RawRdCleanPathType.cs | 19 -- ...hFfiResultBoxRdCleanPathBoxIronRdpError.cs | 46 ----- ...ltBoxRdCleanPathResponseBoxIronRdpError.cs | 46 ----- ...esultBoxServerCertChainBoxIronRdpError.cs} | 6 +- .../{RawVecVecU8.cs => RawServerCertChain.cs} | 18 +- .../Generated/RdCleanPath.cs | 115 ------------ .../Generated/RdCleanPathPdu.cs | 134 +++++++++----- .../Generated/RdCleanPathRequestBuilder.cs | 166 +++++++++++++++++ .../Generated/RdCleanPathResponse.cs | 157 ---------------- .../Generated/RdCleanPathType.cs | 19 -- .../{VecVecU8.cs => ServerCertChain.cs} | 26 +-- .../Devolutions.IronRdp/src/Connection.cs | 29 ++- ffi/src/error.rs | 2 + ffi/src/rdcleanpath.rs | 167 ++++++++---------- ffi/src/utils/mod.rs | 4 +- 21 files changed, 418 insertions(+), 647 deletions(-) delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPath.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathRequestBuilder.cs delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathResponse.cs delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathType.cs delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError.cs delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError.cs rename ffi/dotnet/Devolutions.IronRdp/Generated/{RawRdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError.cs => RawRdcleanpathFfiResultBoxServerCertChainBoxIronRdpError.cs} (82%) rename ffi/dotnet/Devolutions.IronRdp/Generated/{RawVecVecU8.cs => RawServerCertChain.cs} (56%) delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPath.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathRequestBuilder.cs delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathResponse.cs delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathType.cs rename ffi/dotnet/Devolutions.IronRdp/Generated/{VecVecU8.cs => ServerCertChain.cs} (77%) diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/IronRdpErrorKind.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/IronRdpErrorKind.cs index 0f9ec353e..2efd9328f 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/IronRdpErrorKind.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/IronRdpErrorKind.cs @@ -24,4 +24,5 @@ public enum IronRdpErrorKind IncorrectEnumType = 8, Clipboard = 9, WrongOS = 10, + MissingRequiredField = 11, } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawIronRdpErrorKind.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawIronRdpErrorKind.cs index 1e86b93a1..9a146e447 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawIronRdpErrorKind.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawIronRdpErrorKind.cs @@ -24,4 +24,5 @@ public enum IronRdpErrorKind IncorrectEnumType = 8, Clipboard = 9, WrongOS = 10, + MissingRequiredField = 11, } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPath.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPath.cs deleted file mode 100644 index 6a547e85c..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPath.cs +++ /dev/null @@ -1,27 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp.Raw; - -#nullable enable - -[StructLayout(LayoutKind.Sequential)] -public partial struct RdCleanPath -{ - private const string NativeLib = "DevolutionsIronRdp"; - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPath_get_type", ExactSpelling = true)] - public static unsafe extern RdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError GetType(RdCleanPath* self); - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPath_to_response", ExactSpelling = true)] - public static unsafe extern RdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError ToResponse(RdCleanPath* self); - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPath_destroy", ExactSpelling = true)] - public static unsafe extern void Destroy(RdCleanPath* self); -} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs index 24ab39004..3455481a5 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs @@ -16,9 +16,6 @@ public partial struct RdCleanPathPdu { private const string NativeLib = "DevolutionsIronRdp"; - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_new_request", ExactSpelling = true)] - public static unsafe extern RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError NewRequest(VecU8* x224Pdu, byte* destination, nuint destinationSz, byte* proxyAuth, nuint proxyAuthSz, OptionalString* pcb); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_to_der", ExactSpelling = true)] public static unsafe extern RdcleanpathFfiResultBoxVecU8BoxIronRdpError ToDer(RdCleanPathPdu* self); @@ -28,8 +25,14 @@ public partial struct RdCleanPathPdu [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_from_der", ExactSpelling = true)] public static unsafe extern RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError FromDer(byte* der, nuint derSz); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_into_enum", ExactSpelling = true)] - public static unsafe extern RdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError IntoEnum(RdCleanPathPdu* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_get_x224_connection_pdu", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxVecU8BoxIronRdpError GetX224ConnectionPdu(RdCleanPathPdu* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_get_server_cert_chain", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxServerCertChainBoxIronRdpError GetServerCertChain(RdCleanPathPdu* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_get_server_addr", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultVoidBoxIronRdpError GetServerAddr(RdCleanPathPdu* self, DiplomatWriteable* serverAddr); [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathPdu_destroy", ExactSpelling = true)] public static unsafe extern void Destroy(RdCleanPathPdu* self); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathRequestBuilder.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathRequestBuilder.cs new file mode 100644 index 000000000..d1f3f5f2a --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathRequestBuilder.cs @@ -0,0 +1,39 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdCleanPathRequestBuilder +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathRequestBuilder_new", ExactSpelling = true)] + public static unsafe extern RdCleanPathRequestBuilder* New(); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathRequestBuilder_with_x224_pdu", ExactSpelling = true)] + public static unsafe extern void WithX224Pdu(RdCleanPathRequestBuilder* self, VecU8* x224Pdu); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathRequestBuilder_with_destination", ExactSpelling = true)] + public static unsafe extern void WithDestination(RdCleanPathRequestBuilder* self, byte* destination, nuint destinationSz); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathRequestBuilder_with_proxy_auth", ExactSpelling = true)] + public static unsafe extern void WithProxyAuth(RdCleanPathRequestBuilder* self, byte* proxyAuth, nuint proxyAuthSz); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathRequestBuilder_with_pcb", ExactSpelling = true)] + public static unsafe extern void WithPcb(RdCleanPathRequestBuilder* self, byte* pcb, nuint pcbSz); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathRequestBuilder_build", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError Build(RdCleanPathRequestBuilder* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathRequestBuilder_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(RdCleanPathRequestBuilder* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathResponse.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathResponse.cs deleted file mode 100644 index 87e410098..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathResponse.cs +++ /dev/null @@ -1,30 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp.Raw; - -#nullable enable - -[StructLayout(LayoutKind.Sequential)] -public partial struct RdCleanPathResponse -{ - private const string NativeLib = "DevolutionsIronRdp"; - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathResponse_get_x224_connection_response", ExactSpelling = true)] - public static unsafe extern VecU8* GetX224ConnectionResponse(RdCleanPathResponse* self); - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathResponse_get_server_cert_chain", ExactSpelling = true)] - public static unsafe extern VecVecU8* GetServerCertChain(RdCleanPathResponse* self); - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathResponse_get_server_addr", ExactSpelling = true)] - public static unsafe extern RdcleanpathFfiResultVoidBoxIronRdpError GetServerAddr(RdCleanPathResponse* self, DiplomatWriteable* serverAddr); - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RdCleanPathResponse_destroy", ExactSpelling = true)] - public static unsafe extern void Destroy(RdCleanPathResponse* self); -} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathType.cs deleted file mode 100644 index 03b830227..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathType.cs +++ /dev/null @@ -1,19 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp.Raw; - -#nullable enable - -public enum RdCleanPathType -{ - Request = 0, - Response = 1, - Error = 2, -} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError.cs deleted file mode 100644 index 8579eff42..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError.cs +++ /dev/null @@ -1,46 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp.Raw; - -#nullable enable - -[StructLayout(LayoutKind.Sequential)] -public partial struct RdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError -{ - [StructLayout(LayoutKind.Explicit)] - private unsafe struct InnerUnion - { - [FieldOffset(0)] - internal RdCleanPath* ok; - [FieldOffset(0)] - internal IronRdpError* err; - } - - private InnerUnion _inner; - - [MarshalAs(UnmanagedType.U1)] - public bool isOk; - - public unsafe RdCleanPath* Ok - { - get - { - return _inner.ok; - } - } - - public unsafe IronRdpError* Err - { - get - { - return _inner.err; - } - } -} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError.cs deleted file mode 100644 index 888ed4f73..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError.cs +++ /dev/null @@ -1,46 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp.Raw; - -#nullable enable - -[StructLayout(LayoutKind.Sequential)] -public partial struct RdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError -{ - [StructLayout(LayoutKind.Explicit)] - private unsafe struct InnerUnion - { - [FieldOffset(0)] - internal RdCleanPathResponse* ok; - [FieldOffset(0)] - internal IronRdpError* err; - } - - private InnerUnion _inner; - - [MarshalAs(UnmanagedType.U1)] - public bool isOk; - - public unsafe RdCleanPathResponse* Ok - { - get - { - return _inner.ok; - } - } - - public unsafe IronRdpError* Err - { - get - { - return _inner.err; - } - } -} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxServerCertChainBoxIronRdpError.cs similarity index 82% rename from ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError.cs rename to ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxServerCertChainBoxIronRdpError.cs index 155e0db03..c78b2d558 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxServerCertChainBoxIronRdpError.cs @@ -12,13 +12,13 @@ namespace Devolutions.IronRdp.Raw; #nullable enable [StructLayout(LayoutKind.Sequential)] -public partial struct RdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError +public partial struct RdcleanpathFfiResultBoxServerCertChainBoxIronRdpError { [StructLayout(LayoutKind.Explicit)] private unsafe struct InnerUnion { [FieldOffset(0)] - internal RdCleanPathType ok; + internal ServerCertChain* ok; [FieldOffset(0)] internal IronRdpError* err; } @@ -28,7 +28,7 @@ private unsafe struct InnerUnion [MarshalAs(UnmanagedType.U1)] public bool isOk; - public unsafe RdCleanPathType Ok + public unsafe ServerCertChain* Ok { get { diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawVecVecU8.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawServerCertChain.cs similarity index 56% rename from ffi/dotnet/Devolutions.IronRdp/Generated/RawVecVecU8.cs rename to ffi/dotnet/Devolutions.IronRdp/Generated/RawServerCertChain.cs index b98525e64..bd3bf3913 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawVecVecU8.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawServerCertChain.cs @@ -12,19 +12,19 @@ namespace Devolutions.IronRdp.Raw; #nullable enable [StructLayout(LayoutKind.Sequential)] -public partial struct VecVecU8 +public partial struct ServerCertChain { private const string NativeLib = "DevolutionsIronRdp"; - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "VecVecU8_get_len", ExactSpelling = true)] - public static unsafe extern nuint GetLen(VecVecU8* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ServerCertChain_get_len", ExactSpelling = true)] + public static unsafe extern nuint GetLen(ServerCertChain* self); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "VecVecU8_get_vecu8", ExactSpelling = true)] - public static unsafe extern UtilsFfiResultBoxVecU8BoxIronRdpError GetVecu8(VecVecU8* self, nuint index); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ServerCertChain_get_vecu8", ExactSpelling = true)] + public static unsafe extern UtilsFfiResultBoxVecU8BoxIronRdpError GetVecu8(ServerCertChain* self, nuint index); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "VecVecU8_get_slice", ExactSpelling = true)] - public static unsafe extern UtilsFfiResultBoxBytesSliceBoxIronRdpError GetSlice(VecVecU8* self, nuint index); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ServerCertChain_get_slice", ExactSpelling = true)] + public static unsafe extern UtilsFfiResultBoxBytesSliceBoxIronRdpError GetSlice(ServerCertChain* self, nuint index); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "VecVecU8_destroy", ExactSpelling = true)] - public static unsafe extern void Destroy(VecVecU8* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ServerCertChain_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(ServerCertChain* self); } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPath.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPath.cs deleted file mode 100644 index df90355c3..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPath.cs +++ /dev/null @@ -1,115 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp; - -#nullable enable - -public partial class RdCleanPath: IDisposable -{ - private unsafe Raw.RdCleanPath* _inner; - - public RdCleanPathType Type - { - get - { - return GetType(); - } - } - - /// - /// Creates a managed RdCleanPath from a raw handle. - /// - /// - /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). - ///
- /// This constructor assumes the raw struct is allocated on Rust side. - /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. - ///
- public unsafe RdCleanPath(Raw.RdCleanPath* handle) - { - _inner = handle; - } - - /// - /// - /// A RdCleanPathType allocated on C# side. - /// - public RdCleanPathType GetType() - { - unsafe - { - if (_inner == null) - { - throw new ObjectDisposedException("RdCleanPath"); - } - Raw.RdcleanpathFfiResultRdCleanPathTypeBoxIronRdpError result = Raw.RdCleanPath.GetType(_inner); - if (!result.isOk) - { - throw new IronRdpException(new IronRdpError(result.Err)); - } - Raw.RdCleanPathType retVal = result.Ok; - return (RdCleanPathType)retVal; - } - } - - /// - /// - /// A RdCleanPathResponse allocated on Rust side. - /// - public RdCleanPathResponse ToResponse() - { - unsafe - { - if (_inner == null) - { - throw new ObjectDisposedException("RdCleanPath"); - } - Raw.RdcleanpathFfiResultBoxRdCleanPathResponseBoxIronRdpError result = Raw.RdCleanPath.ToResponse(_inner); - if (!result.isOk) - { - throw new IronRdpException(new IronRdpError(result.Err)); - } - Raw.RdCleanPathResponse* retVal = result.Ok; - return new RdCleanPathResponse(retVal); - } - } - - /// - /// Returns the underlying raw handle. - /// - public unsafe Raw.RdCleanPath* AsFFI() - { - return _inner; - } - - /// - /// Destroys the underlying object immediately. - /// - public void Dispose() - { - unsafe - { - if (_inner == null) - { - return; - } - - Raw.RdCleanPath.Destroy(_inner); - _inner = null; - - GC.SuppressFinalize(this); - } - } - - ~RdCleanPath() - { - Dispose(); - } -} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs index 4490e416c..7b4f816b6 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs @@ -15,6 +15,30 @@ public partial class RdCleanPathPdu: IDisposable { private unsafe Raw.RdCleanPathPdu* _inner; + public string ServerAddr + { + get + { + return GetServerAddr(); + } + } + + public ServerCertChain ServerCertChain + { + get + { + return GetServerCertChain(); + } + } + + public VecU8 X224ConnectionPdu + { + get + { + return GetX224ConnectionPdu(); + } + } + /// /// Creates a managed RdCleanPathPdu from a raw handle. /// @@ -29,46 +53,6 @@ public unsafe RdCleanPathPdu(Raw.RdCleanPathPdu* handle) _inner = handle; } - /// - /// - /// A RdCleanPathPdu allocated on Rust side. - /// - public static RdCleanPathPdu NewRequest(VecU8 x224Pdu, string destination, string proxyAuth, OptionalString pcb) - { - unsafe - { - byte[] destinationBuf = DiplomatUtils.StringToUtf8(destination); - byte[] proxyAuthBuf = DiplomatUtils.StringToUtf8(proxyAuth); - nuint destinationBufLength = (nuint)destinationBuf.Length; - nuint proxyAuthBufLength = (nuint)proxyAuthBuf.Length; - Raw.VecU8* x224PduRaw; - x224PduRaw = x224Pdu.AsFFI(); - if (x224PduRaw == null) - { - throw new ObjectDisposedException("VecU8"); - } - Raw.OptionalString* pcbRaw; - pcbRaw = pcb.AsFFI(); - if (pcbRaw == null) - { - throw new ObjectDisposedException("OptionalString"); - } - fixed (byte* destinationBufPtr = destinationBuf) - { - fixed (byte* proxyAuthBufPtr = proxyAuthBuf) - { - Raw.RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError result = Raw.RdCleanPathPdu.NewRequest(x224PduRaw, destinationBufPtr, destinationBufLength, proxyAuthBufPtr, proxyAuthBufLength, pcbRaw); - if (!result.isOk) - { - throw new IronRdpException(new IronRdpError(result.Err)); - } - Raw.RdCleanPathPdu* retVal = result.Ok; - return new RdCleanPathPdu(retVal); - } - } - } - } - /// /// /// A VecU8 allocated on Rust side. @@ -127,9 +111,67 @@ public static RdCleanPathPdu FromDer(byte[] der) /// /// - /// A RdCleanPath allocated on Rust side. + /// A VecU8 allocated on Rust side. /// - public RdCleanPath IntoEnum() + public VecU8 GetX224ConnectionPdu() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathPdu"); + } + Raw.RdcleanpathFfiResultBoxVecU8BoxIronRdpError result = Raw.RdCleanPathPdu.GetX224ConnectionPdu(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.VecU8* retVal = result.Ok; + return new VecU8(retVal); + } + } + + /// + /// + /// A ServerCertChain allocated on Rust side. + /// + public ServerCertChain GetServerCertChain() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathPdu"); + } + Raw.RdcleanpathFfiResultBoxServerCertChainBoxIronRdpError result = Raw.RdCleanPathPdu.GetServerCertChain(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.ServerCertChain* retVal = result.Ok; + return new ServerCertChain(retVal); + } + } + + /// + public void GetServerAddr(DiplomatWriteable serverAddr) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathPdu"); + } + Raw.RdcleanpathFfiResultVoidBoxIronRdpError result = Raw.RdCleanPathPdu.GetServerAddr(_inner, &serverAddr); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + } + } + + /// + public string GetServerAddr() { unsafe { @@ -137,13 +179,15 @@ public RdCleanPath IntoEnum() { throw new ObjectDisposedException("RdCleanPathPdu"); } - Raw.RdcleanpathFfiResultBoxRdCleanPathBoxIronRdpError result = Raw.RdCleanPathPdu.IntoEnum(_inner); + DiplomatWriteable writeable = new DiplomatWriteable(); + Raw.RdcleanpathFfiResultVoidBoxIronRdpError result = Raw.RdCleanPathPdu.GetServerAddr(_inner, &writeable); if (!result.isOk) { throw new IronRdpException(new IronRdpError(result.Err)); } - Raw.RdCleanPath* retVal = result.Ok; - return new RdCleanPath(retVal); + string retVal = writeable.ToUnicode(); + writeable.Dispose(); + return retVal; } } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathRequestBuilder.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathRequestBuilder.cs new file mode 100644 index 000000000..cb0df5499 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathRequestBuilder.cs @@ -0,0 +1,166 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class RdCleanPathRequestBuilder: IDisposable +{ + private unsafe Raw.RdCleanPathRequestBuilder* _inner; + + /// + /// Creates a managed RdCleanPathRequestBuilder from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe RdCleanPathRequestBuilder(Raw.RdCleanPathRequestBuilder* handle) + { + _inner = handle; + } + + /// + /// A RdCleanPathRequestBuilder allocated on Rust side. + /// + public static RdCleanPathRequestBuilder New() + { + unsafe + { + Raw.RdCleanPathRequestBuilder* retVal = Raw.RdCleanPathRequestBuilder.New(); + return new RdCleanPathRequestBuilder(retVal); + } + } + + public void WithX224Pdu(VecU8 x224Pdu) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathRequestBuilder"); + } + Raw.VecU8* x224PduRaw; + x224PduRaw = x224Pdu.AsFFI(); + if (x224PduRaw == null) + { + throw new ObjectDisposedException("VecU8"); + } + Raw.RdCleanPathRequestBuilder.WithX224Pdu(_inner, x224PduRaw); + } + } + + public void WithDestination(string destination) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathRequestBuilder"); + } + byte[] destinationBuf = DiplomatUtils.StringToUtf8(destination); + nuint destinationBufLength = (nuint)destinationBuf.Length; + fixed (byte* destinationBufPtr = destinationBuf) + { + Raw.RdCleanPathRequestBuilder.WithDestination(_inner, destinationBufPtr, destinationBufLength); + } + } + } + + public void WithProxyAuth(string proxyAuth) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathRequestBuilder"); + } + byte[] proxyAuthBuf = DiplomatUtils.StringToUtf8(proxyAuth); + nuint proxyAuthBufLength = (nuint)proxyAuthBuf.Length; + fixed (byte* proxyAuthBufPtr = proxyAuthBuf) + { + Raw.RdCleanPathRequestBuilder.WithProxyAuth(_inner, proxyAuthBufPtr, proxyAuthBufLength); + } + } + } + + public void WithPcb(string pcb) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathRequestBuilder"); + } + byte[] pcbBuf = DiplomatUtils.StringToUtf8(pcb); + nuint pcbBufLength = (nuint)pcbBuf.Length; + fixed (byte* pcbBufPtr = pcbBuf) + { + Raw.RdCleanPathRequestBuilder.WithPcb(_inner, pcbBufPtr, pcbBufLength); + } + } + } + + /// + /// + /// A RdCleanPathPdu allocated on Rust side. + /// + public RdCleanPathPdu Build() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RdCleanPathRequestBuilder"); + } + Raw.RdcleanpathFfiResultBoxRdCleanPathPduBoxIronRdpError result = Raw.RdCleanPathRequestBuilder.Build(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.RdCleanPathPdu* retVal = result.Ok; + return new RdCleanPathPdu(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.RdCleanPathRequestBuilder* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.RdCleanPathRequestBuilder.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~RdCleanPathRequestBuilder() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathResponse.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathResponse.cs deleted file mode 100644 index 539b23fc5..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathResponse.cs +++ /dev/null @@ -1,157 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp; - -#nullable enable - -public partial class RdCleanPathResponse: IDisposable -{ - private unsafe Raw.RdCleanPathResponse* _inner; - - public string ServerAddr - { - get - { - return GetServerAddr(); - } - } - - public VecVecU8 ServerCertChain - { - get - { - return GetServerCertChain(); - } - } - - public VecU8 X224ConnectionResponse - { - get - { - return GetX224ConnectionResponse(); - } - } - - /// - /// Creates a managed RdCleanPathResponse from a raw handle. - /// - /// - /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). - ///
- /// This constructor assumes the raw struct is allocated on Rust side. - /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. - ///
- public unsafe RdCleanPathResponse(Raw.RdCleanPathResponse* handle) - { - _inner = handle; - } - - /// - /// A VecU8 allocated on Rust side. - /// - public VecU8 GetX224ConnectionResponse() - { - unsafe - { - if (_inner == null) - { - throw new ObjectDisposedException("RdCleanPathResponse"); - } - Raw.VecU8* retVal = Raw.RdCleanPathResponse.GetX224ConnectionResponse(_inner); - return new VecU8(retVal); - } - } - - /// - /// A VecVecU8 allocated on Rust side. - /// - public VecVecU8 GetServerCertChain() - { - unsafe - { - if (_inner == null) - { - throw new ObjectDisposedException("RdCleanPathResponse"); - } - Raw.VecVecU8* retVal = Raw.RdCleanPathResponse.GetServerCertChain(_inner); - return new VecVecU8(retVal); - } - } - - /// - public void GetServerAddr(DiplomatWriteable serverAddr) - { - unsafe - { - if (_inner == null) - { - throw new ObjectDisposedException("RdCleanPathResponse"); - } - Raw.RdcleanpathFfiResultVoidBoxIronRdpError result = Raw.RdCleanPathResponse.GetServerAddr(_inner, &serverAddr); - if (!result.isOk) - { - throw new IronRdpException(new IronRdpError(result.Err)); - } - } - } - - /// - public string GetServerAddr() - { - unsafe - { - if (_inner == null) - { - throw new ObjectDisposedException("RdCleanPathResponse"); - } - DiplomatWriteable writeable = new DiplomatWriteable(); - Raw.RdcleanpathFfiResultVoidBoxIronRdpError result = Raw.RdCleanPathResponse.GetServerAddr(_inner, &writeable); - if (!result.isOk) - { - throw new IronRdpException(new IronRdpError(result.Err)); - } - string retVal = writeable.ToUnicode(); - writeable.Dispose(); - return retVal; - } - } - - /// - /// Returns the underlying raw handle. - /// - public unsafe Raw.RdCleanPathResponse* AsFFI() - { - return _inner; - } - - /// - /// Destroys the underlying object immediately. - /// - public void Dispose() - { - unsafe - { - if (_inner == null) - { - return; - } - - Raw.RdCleanPathResponse.Destroy(_inner); - _inner = null; - - GC.SuppressFinalize(this); - } - } - - ~RdCleanPathResponse() - { - Dispose(); - } -} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathType.cs deleted file mode 100644 index a8ac2f1a4..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathType.cs +++ /dev/null @@ -1,19 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp; - -#nullable enable - -public enum RdCleanPathType -{ - Request = 0, - Response = 1, - Error = 2, -} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/VecVecU8.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/ServerCertChain.cs similarity index 77% rename from ffi/dotnet/Devolutions.IronRdp/Generated/VecVecU8.cs rename to ffi/dotnet/Devolutions.IronRdp/Generated/ServerCertChain.cs index f3dc53612..835bf2341 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/VecVecU8.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/ServerCertChain.cs @@ -11,9 +11,9 @@ namespace Devolutions.IronRdp; #nullable enable -public partial class VecVecU8: IDisposable +public partial class ServerCertChain: IDisposable { - private unsafe Raw.VecVecU8* _inner; + private unsafe Raw.ServerCertChain* _inner; public nuint Len { @@ -24,7 +24,7 @@ public nuint Len } /// - /// Creates a managed VecVecU8 from a raw handle. + /// Creates a managed ServerCertChain from a raw handle. /// /// /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). @@ -32,7 +32,7 @@ public nuint Len /// This constructor assumes the raw struct is allocated on Rust side. /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. /// - public unsafe VecVecU8(Raw.VecVecU8* handle) + public unsafe ServerCertChain(Raw.ServerCertChain* handle) { _inner = handle; } @@ -43,9 +43,9 @@ public nuint GetLen() { if (_inner == null) { - throw new ObjectDisposedException("VecVecU8"); + throw new ObjectDisposedException("ServerCertChain"); } - nuint retVal = Raw.VecVecU8.GetLen(_inner); + nuint retVal = Raw.ServerCertChain.GetLen(_inner); return retVal; } } @@ -60,9 +60,9 @@ public VecU8 GetVecu8(nuint index) { if (_inner == null) { - throw new ObjectDisposedException("VecVecU8"); + throw new ObjectDisposedException("ServerCertChain"); } - Raw.UtilsFfiResultBoxVecU8BoxIronRdpError result = Raw.VecVecU8.GetVecu8(_inner, index); + Raw.UtilsFfiResultBoxVecU8BoxIronRdpError result = Raw.ServerCertChain.GetVecu8(_inner, index); if (!result.isOk) { throw new IronRdpException(new IronRdpError(result.Err)); @@ -82,9 +82,9 @@ public BytesSlice GetSlice(nuint index) { if (_inner == null) { - throw new ObjectDisposedException("VecVecU8"); + throw new ObjectDisposedException("ServerCertChain"); } - Raw.UtilsFfiResultBoxBytesSliceBoxIronRdpError result = Raw.VecVecU8.GetSlice(_inner, index); + Raw.UtilsFfiResultBoxBytesSliceBoxIronRdpError result = Raw.ServerCertChain.GetSlice(_inner, index); if (!result.isOk) { throw new IronRdpException(new IronRdpError(result.Err)); @@ -97,7 +97,7 @@ public BytesSlice GetSlice(nuint index) /// /// Returns the underlying raw handle. /// - public unsafe Raw.VecVecU8* AsFFI() + public unsafe Raw.ServerCertChain* AsFFI() { return _inner; } @@ -114,14 +114,14 @@ public void Dispose() return; } - Raw.VecVecU8.Destroy(_inner); + Raw.ServerCertChain.Destroy(_inner); _inner = null; GC.SuppressFinalize(this); } } - ~VecVecU8() + ~ServerCertChain() { Dispose(); } diff --git a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs index c0d6d4098..13520dcb8 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs @@ -78,33 +78,30 @@ string proxyAuth connector.StepNoInput(writeBuf); var x224Pdu = writeBuf.GetFilled(); - var rdcleanPathReq = RdCleanPathPdu.NewRequest( - x224Pdu, - destination, - proxyAuth, - OptionalString.NewEmpty() - ); + // Create RdCleanPathRequest + var rdCleanPathReqBuilder = RdCleanPathRequestBuilder.New(); + rdCleanPathReqBuilder.WithX224Pdu(x224Pdu); + rdCleanPathReqBuilder.WithDestination(destination); + rdCleanPathReqBuilder.WithProxyAuth(proxyAuth); + var rdcleanPathReq = rdCleanPathReqBuilder.Build(); + + // Send RdCleanPathRequest var rdcleanPathPdu = rdcleanPathReq.ToDer(); var bytes = Utils.VecU8ToByte(rdcleanPathPdu); await framed.Write(bytes); + + // Read RdCleanPathResponse var pduHint = RdCleanPathPdu.GetHint(); var pdu = await framed.ReadByHint(pduHint); - var rdcleanPathResponseDer = RdCleanPathPdu.FromDer(pdu); - var rdcleanPath = rdcleanPathResponseDer.IntoEnum(); - if (rdcleanPath.GetType() != RdCleanPathType.Response) - { - throw new IronRdpLibException(IronRdpLibExceptionType.ConnectionFailed, - "RdCleanPath response is not a response type"); - } - - var rdCleanPathResponse = rdcleanPath.ToResponse(); + var rdCleanPathResponse = RdCleanPathPdu.FromDer(pdu); - var x224ConnectionResponse = rdCleanPathResponse.GetX224ConnectionResponse(); + var x224ConnectionResponse = rdCleanPathResponse.GetX224ConnectionPdu(); var serverCertChain = rdCleanPathResponse.GetServerCertChain(); + // Handle X224 connection response writeBuf.Clear(); connector.Step(Utils.VecU8ToByte(x224ConnectionResponse), writeBuf); diff --git a/ffi/src/error.rs b/ffi/src/error.rs index 9312ba45f..ffdc6c41f 100644 --- a/ffi/src/error.rs +++ b/ffi/src/error.rs @@ -139,6 +139,8 @@ pub mod ffi { Clipboard, #[error("wrong platform error")] WrongOS, + #[error("Missing required field")] + MissingRequiredField, } /// Stringified Picky error along with an error kind. diff --git a/ffi/src/rdcleanpath.rs b/ffi/src/rdcleanpath.rs index a138b516c..963d2e44c 100644 --- a/ffi/src/rdcleanpath.rs +++ b/ffi/src/rdcleanpath.rs @@ -18,45 +18,72 @@ impl ironrdp::pdu::PduHint for RDCleanPathHint { #[diplomat::bridge] pub mod ffi { - use core::fmt::Write; - use diplomat_runtime::DiplomatWriteable; - use ironrdp::rdclean_path::der::asn1::OctetString; use crate::error::ffi::{IronRdpError, IronRdpErrorKind}; use crate::error::ValueConsumedError; - use crate::utils::ffi::{OptionalString, VecU8, VecVecU8}; + use crate::utils::ffi::{ServerCertChain, VecU8}; + use core::fmt::Write; #[diplomat::opaque] - pub struct RdCleanPathPdu(pub Option); + pub struct RdCleanPathPdu(pub ironrdp::rdclean_path::RDCleanPathPdu); - impl RdCleanPathPdu { - pub fn new_request( - x224_pdu: &VecU8, - destination: &str, - proxy_auth: &str, - pcb: &OptionalString, - ) -> Result, Box> { - let x224_pdu = &x224_pdu.0; - let destination = destination.to_owned(); - let proxy_auth = proxy_auth.to_owned(); - - let cleanpath_pdu = ironrdp::rdclean_path::RDCleanPathPdu::new_request( - x224_pdu.to_owned(), + #[diplomat::opaque] + pub struct RdCleanPathRequestBuilder { + x224_pdu: Option>, + destination: Option, + proxy_auth: Option, + pcb: Option, + } + + impl RdCleanPathRequestBuilder { + pub fn new() -> Box { + Box::new(RdCleanPathRequestBuilder { + x224_pdu: None, + destination: None, + proxy_auth: None, + pcb: None, + }) + } + + pub fn with_x224_pdu(&mut self, x224_pdu: &VecU8) { + self.x224_pdu = Some(x224_pdu.0.clone()); + } + + pub fn with_destination(&mut self, destination: &str) { + self.destination = Some(destination.to_owned()); + } + + pub fn with_proxy_auth(&mut self, proxy_auth: &str) { + self.proxy_auth = Some(proxy_auth.to_owned()); + } + + pub fn with_pcb(&mut self, pcb: &str) { + self.pcb = Some(pcb.to_owned()); + } + + pub fn build(&self) -> Result, Box> { + let RdCleanPathRequestBuilder { + x224_pdu, destination, proxy_auth, - pcb.into(), + pcb, + } = self; + + let request = ironrdp::rdclean_path::RDCleanPathPdu::new_request( + x224_pdu.to_owned().ok_or(IronRdpErrorKind::MissingRequiredField)?, + destination.to_owned().ok_or(IronRdpErrorKind::MissingRequiredField)?, + proxy_auth.to_owned().ok_or(IronRdpErrorKind::MissingRequiredField)?, + pcb.to_owned(), ) .map_err(|_| IronRdpErrorKind::EncodeError)?; - Ok(Box::new(RdCleanPathPdu(Some(cleanpath_pdu)))) + Ok(Box::new(RdCleanPathPdu(request))) } + } + impl RdCleanPathPdu { pub fn to_der(&self) -> Result, Box> { - let Some(pdu) = self.0.as_ref() else { - return Err(ValueConsumedError::for_item("RdCleanPathPdu").into()); - }; - - let der = pdu.to_der().map_err(|_| IronRdpErrorKind::EncodeError)?; + let der = self.0.to_der().map_err(|_| IronRdpErrorKind::EncodeError)?; Ok(Box::new(VecU8(der))) } @@ -67,93 +94,43 @@ pub mod ffi { pub fn from_der(der: &[u8]) -> Result, Box> { let pdu = ironrdp::rdclean_path::RDCleanPathPdu::from_der(der).map_err(|_| IronRdpErrorKind::DecodeError)?; - Ok(Box::new(RdCleanPathPdu(Some(pdu)))) + Ok(Box::new(RdCleanPathPdu(pdu))) } - pub fn into_enum(&mut self) -> Result, Box> { - let Some(pdu) = self.0.take() else { + pub fn get_x224_connection_pdu(&self) -> Result, Box> { + let Some(x224_pdu_response) = self.0.x224_connection_pdu.as_ref() else { return Err(ValueConsumedError::for_item("RdCleanPathPdu").into()); }; - let rdclean_path = pdu - .into_enum() - .map(|rd_clean_path| Box::new(RdCleanPath(Some(rd_clean_path)))) - .map_err(|_| IronRdpErrorKind::EncodeError)?; + let result = x224_pdu_response.as_bytes().to_vec(); - Ok(rdclean_path) + Ok(Box::new(VecU8(result))) } - } - - #[diplomat::opaque] - pub struct RdCleanPath(pub Option); - #[diplomat::opaque] - pub struct RdCleanPathResponse { - x224_connection_response: OctetString, - server_cert_chain: Vec, - server_addr: String, - } - - impl RdCleanPathResponse { - pub fn get_x224_connection_response(&self) -> Box { - VecU8::from_bytes(self.x224_connection_response.as_bytes()) - } + pub fn get_server_cert_chain(&self) -> Result, Box> { + let Some(server_cert_chain) = self.0.server_cert_chain.as_ref() else { + return Err(ValueConsumedError::for_item("ServerCertChain").into()); + }; - pub fn get_server_cert_chain(&self) -> Box { - let vecs = self - .server_cert_chain + let vecs = server_cert_chain .iter() .map(|cert| cert.as_bytes().to_vec()) .collect::>(); - Box::new(VecVecU8(vecs)) + Ok(Box::new(ServerCertChain(vecs))) } - pub fn get_server_addr(&self, server_addr: &mut DiplomatWriteable) -> Result<(), Box> { - write!(server_addr, "{}", self.server_addr).map_err(|_| IronRdpErrorKind::IO)?; - - Ok(()) - } - } - - pub enum RdCleanPathType { - Request, - Response, - Error, - } + pub fn get_server_addr( + &self, + server_addr: &mut diplomat_runtime::DiplomatWriteable, + ) -> Result<(), Box> { + let Some(server_addr_str) = self.0.server_addr.as_ref() else { + return Err(ValueConsumedError::for_item("server_addr").into()); + }; - impl RdCleanPath { - pub fn get_type(&self) -> Result> { - let value = self - .0 - .as_ref() - .ok_or_else(|| ValueConsumedError::for_item("RdCleanPath"))?; - - Ok(match value { - ironrdp::rdclean_path::RDCleanPath::Request { .. } => RdCleanPathType::Request, - ironrdp::rdclean_path::RDCleanPath::Response { .. } => RdCleanPathType::Response, - ironrdp::rdclean_path::RDCleanPath::Err(_) => RdCleanPathType::Error, - }) - } + write!(server_addr, "{server_addr_str}").map_err(|_| IronRdpErrorKind::IO)?; - pub fn to_response(&mut self) -> Result, Box> { - let value = self - .0 - .take() - .ok_or_else(|| ValueConsumedError::for_item("RdCleanPath"))?; - - match value { - ironrdp::rdclean_path::RDCleanPath::Response { - x224_connection_response, - server_cert_chain, - server_addr, - } => Ok(Box::new(RdCleanPathResponse { - x224_connection_response, - server_cert_chain, - server_addr, - })), - _ => Err(IronRdpErrorKind::IncorrectEnumType.into()), - } + Ok(()) } } } diff --git a/ffi/src/utils/mod.rs b/ffi/src/utils/mod.rs index 839c8a9d3..a6fa8cdc1 100644 --- a/ffi/src/utils/mod.rs +++ b/ffi/src/utils/mod.rs @@ -4,9 +4,9 @@ pub mod ffi { use crate::error::ffi::IronRdpError; #[diplomat::opaque] - pub struct VecVecU8(pub Vec>); + pub struct ServerCertChain(pub Vec>); - impl VecVecU8 { + impl ServerCertChain { pub fn get_len(&self) -> usize { self.0.len() } From 0e9e069d59111c4040c7df80f964ebe309e66bc1 Mon Sep 17 00:00:00 2001 From: irving ou Date: Sat, 5 Jul 2025 18:22:04 -0400 Subject: [PATCH 4/8] review fix --- ffi/dotnet/IronRdp.sln.DotSettings | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 ffi/dotnet/IronRdp.sln.DotSettings diff --git a/ffi/dotnet/IronRdp.sln.DotSettings b/ffi/dotnet/IronRdp.sln.DotSettings deleted file mode 100644 index 24b62fa6a..000000000 --- a/ffi/dotnet/IronRdp.sln.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file From 7fde0b3ac6fe19d1152a4b6276d897638c17aa9e Mon Sep 17 00:00:00 2001 From: irving ou Date: Sat, 5 Jul 2025 18:25:21 -0400 Subject: [PATCH 5/8] review fix --- .../Properties/launchSettings.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/launchSettings.json diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/launchSettings.json b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/launchSettings.json deleted file mode 100644 index 227775bb6..000000000 --- a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Properties/launchSettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "profiles": { - "Devolutions.IronRdp.ConnectExample": { - "commandName": "Project", - "commandLineArgs": "--serverName 10.10.0.3 --username Administrator --password DevoLabs123! \\\r\n--proxy ws://localhost:7171/jet/rdp \\\r\n--proxyToken eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkFTU09DSUFUSU9OIn0.eyJkc3RfaHN0IjoiMTAuMTAuMC4zOjMzODkiLCJkc3RfcHdkIjoiRGV2b0xhYnMxMjMhIiwiZHN0X3VzciI6IkFkbWluaXN0cmF0b3IiLCJleHAiOjE3NTEzMjcyOTAsImpldF9haWQiOiIyZjQyNmY2Yy01OThiLTRhYzktOTllNS00NzcxNzY4OWI2ZmUiLCJqZXRfYXAiOiJyZHAiLCJqZXRfY20iOiJmd2QiLCJqZXRfcmVjIjoibm9uZSIsImp0aSI6IjM2NjgzZTU5LWQ5NjUtNDkyNS1hMmNkLTBlNWNmZjUzNDhlZiIsIm5iZiI6MTc1MTMyNjM5MCwicHJ4X3B3ZCI6IkRldm9sYWJzMTIzISIsInByeF91c3IiOiJBZG1pbmlzdHJhdG9yQGFkLml0LWhlbHAubmluamEifQ.mfTBuXqFlqpL6zm6L798s7Q-aowPtC1HRtQt_IiXLiY3ToWLEckMRctr5dO_IZKxOMxI5bcK6dW3PgEL5XXksPesDHT8Ykt1rAmFtivCqL-iy68sGfUIkk8IDlbV5aRTrYXVXGPI7Ofj1XrKN3snvEUq95w8CYXRP3b5Yp-3vauM64oxGyGK9rlVmg3xoIuIthGGkMFWvRGYwLbzdNs8DcW2h_0Q4Yt2ZeXURPmO0SRTy1Kt7WOcNTsq6auXA1Hu6nTmVYTE0zERTUnWJF_W7iKQ4ggWY7KerAr03_SVxuxr9sI8Ah-Vx4FTv9VI9b5soo2vE8MLL6kMLpfABbHv_A\r\n", - "environmentVariables": { - "IRONRDP_LOG": "trace", - "IRONRDP_LOG_PATH": "C:\\\\dev\\\\IronRDP\\\\ironrdp-dotnet-ws.log" - } - } - } -} \ No newline at end of file From 83da59a85ecbe36d6bd1d10861ae6140f7b93d19 Mon Sep 17 00:00:00 2001 From: irving ou Date: Sat, 5 Jul 2025 18:37:14 -0400 Subject: [PATCH 6/8] review fix --- ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs b/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs index 18a466686..d01b5e88c 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs @@ -2,13 +2,13 @@ public class RdcleanPathConfig { - public Uri Uri { get; private set; } + public Uri GatewayUri { get; private set; } public string AuthToken { get; private set; } public RdcleanPathConfig(Uri url, string authToken) { - Uri = url; + GatewayUri = url; AuthToken = authToken; } } \ No newline at end of file From b2f731cf68ecb719eaeb66022f619fe3fd88c22b Mon Sep 17 00:00:00 2001 From: irving ou Date: Sat, 5 Jul 2025 18:42:26 -0400 Subject: [PATCH 7/8] review fix --- .../Generated/OptionalString.cs | 105 ------------------ .../Generated/RawOptionalString.cs | 31 ------ .../src/WebsocketStream.cs | 3 + ffi/src/utils/mod.rs | 23 ---- 4 files changed, 3 insertions(+), 159 deletions(-) delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/OptionalString.cs delete mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawOptionalString.cs diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/OptionalString.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/OptionalString.cs deleted file mode 100644 index a48a411e2..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/OptionalString.cs +++ /dev/null @@ -1,105 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp; - -#nullable enable - -public partial class OptionalString: IDisposable -{ - private unsafe Raw.OptionalString* _inner; - - /// - /// Creates a managed OptionalString from a raw handle. - /// - /// - /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). - ///
- /// This constructor assumes the raw struct is allocated on Rust side. - /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. - ///
- public unsafe OptionalString(Raw.OptionalString* handle) - { - _inner = handle; - } - - public bool IsSome() - { - unsafe - { - if (_inner == null) - { - throw new ObjectDisposedException("OptionalString"); - } - bool retVal = Raw.OptionalString.IsSome(_inner); - return retVal; - } - } - - /// - /// A OptionalString allocated on Rust side. - /// - public static OptionalString New(string value) - { - unsafe - { - byte[] valueBuf = DiplomatUtils.StringToUtf8(value); - nuint valueBufLength = (nuint)valueBuf.Length; - fixed (byte* valueBufPtr = valueBuf) - { - Raw.OptionalString* retVal = Raw.OptionalString.New(valueBufPtr, valueBufLength); - return new OptionalString(retVal); - } - } - } - - /// - /// A OptionalString allocated on Rust side. - /// - public static OptionalString NewEmpty() - { - unsafe - { - Raw.OptionalString* retVal = Raw.OptionalString.NewEmpty(); - return new OptionalString(retVal); - } - } - - /// - /// Returns the underlying raw handle. - /// - public unsafe Raw.OptionalString* AsFFI() - { - return _inner; - } - - /// - /// Destroys the underlying object immediately. - /// - public void Dispose() - { - unsafe - { - if (_inner == null) - { - return; - } - - Raw.OptionalString.Destroy(_inner); - _inner = null; - - GC.SuppressFinalize(this); - } - } - - ~OptionalString() - { - Dispose(); - } -} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawOptionalString.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawOptionalString.cs deleted file mode 100644 index 53a16ba19..000000000 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawOptionalString.cs +++ /dev/null @@ -1,31 +0,0 @@ -// by Diplomat - -#pragma warning disable 0105 -using System; -using System.Runtime.InteropServices; - -using Devolutions.IronRdp.Diplomat; -#pragma warning restore 0105 - -namespace Devolutions.IronRdp.Raw; - -#nullable enable - -[StructLayout(LayoutKind.Sequential)] -public partial struct OptionalString -{ - private const string NativeLib = "DevolutionsIronRdp"; - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OptionalString_is_some", ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.U1)] - public static unsafe extern bool IsSome(OptionalString* self); - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OptionalString_new", ExactSpelling = true)] - public static unsafe extern OptionalString* New(byte* value, nuint valueSz); - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OptionalString_new_empty", ExactSpelling = true)] - public static unsafe extern OptionalString* NewEmpty(); - - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OptionalString_destroy", ExactSpelling = true)] - public static unsafe extern void Destroy(OptionalString* self); -} diff --git a/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs b/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs index 01a75193b..419669183 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs @@ -53,6 +53,9 @@ await _webSocket.SendAsync( public override void Flush() { + // No need. the third parameter of SendAsync is set to true, which means the frame is sent immediately. + // Also, this method is not called in practice ever somehow. + // However, since it's not blocking any functionality, we can leave it empty. } // Not supported diff --git a/ffi/src/utils/mod.rs b/ffi/src/utils/mod.rs index a6fa8cdc1..b60e40284 100644 --- a/ffi/src/utils/mod.rs +++ b/ffi/src/utils/mod.rs @@ -102,27 +102,4 @@ pub mod ffi { self.0.ok_or_else(|| "value is None".into()) } } - - #[diplomat::opaque] - pub struct OptionalString(pub(crate) Option); - - impl OptionalString { - pub fn is_some(&self) -> bool { - self.0.is_some() - } - - pub fn new(value: &str) -> Box { - Box::new(OptionalString(Some(value.to_owned()))) - } - - pub fn new_empty() -> Box { - Box::new(OptionalString(None)) - } - } -} - -impl From<&ffi::OptionalString> for Option { - fn from(value: &ffi::OptionalString) -> Self { - value.0.clone() - } } From e453b4d5ad8ac33f846b22732ea55ff3e52f9145 Mon Sep 17 00:00:00 2001 From: irving ou Date: Sat, 5 Jul 2025 18:44:22 -0400 Subject: [PATCH 8/8] review fix --- .../Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs | 2 +- ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs | 2 +- ffi/dotnet/Devolutions.IronRdp/src/Connection.cs | 6 +++--- ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index 15e56c5f9..e910a5738 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -120,7 +120,7 @@ private void OnOpened(object? sender, EventArgs e) Debug.WriteLine("Connecting via WebSocket proxy"); (res, framed) = await Connection.ConnectWs( config, - new RdcleanPathConfig(new Uri(wsProxy), wsProxyToken), + new RdCleanPathConfig(new Uri(wsProxy), wsProxyToken), server, factory); } diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs index 05468ccb6..8c83fd95e 100644 --- a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs @@ -32,7 +32,7 @@ static async Task Main(string[] args) { (res, framed) = await Connection.ConnectWs( buildConfig(serverName, username, password, domain, 1980, 1080), - new RdcleanPathConfig(new Uri(wsProxy), wsProxyToken), + new RdCleanPathConfig(new Uri(wsProxy), wsProxyToken), serverName, null); } diff --git a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs index 13520dcb8..31bbe3caa 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs @@ -35,11 +35,11 @@ public static class Connection return (result, framedSsl); } - public static async Task<(ConnectionResult, Framed)> ConnectWs(Config config, RdcleanPathConfig rdcleanPathConfig, string serverName, CliprdrBackendFactory? factory, int port = 3389, CancellationToken token = default) + public static async Task<(ConnectionResult, Framed)> ConnectWs(Config config, RdCleanPathConfig rdCleanPathConfig, string serverName, CliprdrBackendFactory? factory, int port = 3389, CancellationToken token = default) { var websocket = new ClientWebSocket(); - await websocket.ConnectAsync(rdcleanPathConfig.Uri, token); + await websocket.ConnectAsync(rdCleanPathConfig.GatewayUri, token); var stream = new WebsocketStream(websocket); var framed = new Framed(stream); @@ -58,7 +58,7 @@ public static class Connection string destination = serverName + ":" + port; - var serverPublicKey = await ConnectRdCleanPath(framed, connector, destination, rdcleanPathConfig.AuthToken); + var serverPublicKey = await ConnectRdCleanPath(framed, connector, destination, rdCleanPathConfig.AuthToken); var result = await ConnectFinalize(serverName, connector, serverPublicKey, framed); diff --git a/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs b/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs index d01b5e88c..49a743704 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/RdcleanPath.cs @@ -1,12 +1,12 @@ -using System; +namespace Devolutions.IronRdp; -public class RdcleanPathConfig +public class RdCleanPathConfig { public Uri GatewayUri { get; private set; } public string AuthToken { get; private set; } - public RdcleanPathConfig(Uri url, string authToken) + public RdCleanPathConfig(Uri url, string authToken) { GatewayUri = url; AuthToken = authToken;