diff --git a/Cargo.lock b/Cargo.lock
index 207d6cb36..7cfbfaf10 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1525,6 +1525,7 @@ dependencies = [
"ironrdp",
"ironrdp-cliprdr-native",
"ironrdp-core",
+ "ironrdp-rdcleanpath",
"sspi",
"thiserror 1.0.69",
"tracing",
diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml
index b265a4f48..9c6b902b3 100644
--- a/ffi/Cargo.toml
+++ b/ffi/Cargo.toml
@@ -17,6 +17,7 @@ diplomat-runtime = "0.7"
ironrdp = { path = "../crates/ironrdp", features = ["session", "connector", "dvc", "svc", "rdpdr", "rdpsnd", "graphics", "input", "cliprdr", "displaycontrol"] }
ironrdp-cliprdr-native.path = "../crates/ironrdp-cliprdr-native"
ironrdp-core = { path = "../crates/ironrdp-core", features = ["alloc"] }
+ironrdp-rdcleanpath = { path = "../crates/ironrdp-rdcleanpath" }
sspi = { version = "0.16", features = ["network_client"] }
thiserror = "1"
tracing = { version = "0.1", features = ["log"] }
diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.cs
new file mode 100644
index 000000000..3455481a5
--- /dev/null
+++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdCleanPathPdu.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 RdCleanPathPdu
+{
+ private const string NativeLib = "DevolutionsIronRdp";
+
+ [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_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/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/RawRdcleanpathFfiResultBoxServerCertChainBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxServerCertChainBoxIronRdpError.cs
new file mode 100644
index 000000000..c78b2d558
--- /dev/null
+++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxServerCertChainBoxIronRdpError.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 RdcleanpathFfiResultBoxServerCertChainBoxIronRdpError
+{
+ [StructLayout(LayoutKind.Explicit)]
+ private unsafe struct InnerUnion
+ {
+ [FieldOffset(0)]
+ internal ServerCertChain* ok;
+ [FieldOffset(0)]
+ internal IronRdpError* err;
+ }
+
+ private InnerUnion _inner;
+
+ [MarshalAs(UnmanagedType.U1)]
+ public bool isOk;
+
+ public unsafe ServerCertChain* 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/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/RawServerCertChain.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawServerCertChain.cs
new file mode 100644
index 000000000..bd3bf3913
--- /dev/null
+++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawServerCertChain.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 ServerCertChain
+{
+ private const string NativeLib = "DevolutionsIronRdp";
+
+ [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 = "ServerCertChain_get_vecu8", ExactSpelling = true)]
+ public static unsafe extern UtilsFfiResultBoxVecU8BoxIronRdpError GetVecu8(ServerCertChain* 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 = "ServerCertChain_destroy", ExactSpelling = true)]
+ public static unsafe extern void Destroy(ServerCertChain* self);
+}
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/RdCleanPathPdu.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs
new file mode 100644
index 000000000..7b4f816b6
--- /dev/null
+++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RdCleanPathPdu.cs
@@ -0,0 +1,225 @@
+// 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;
+
+ 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.
+ ///
+ ///
+ /// 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 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 VecU8 allocated on Rust side.
+ ///
+ 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
+ {
+ if (_inner == null)
+ {
+ throw new ObjectDisposedException("RdCleanPathPdu");
+ }
+ DiplomatWriteable writeable = new DiplomatWriteable();
+ Raw.RdcleanpathFfiResultVoidBoxIronRdpError result = Raw.RdCleanPathPdu.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.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/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/ServerCertChain.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/ServerCertChain.cs
new file mode 100644
index 000000000..835bf2341
--- /dev/null
+++ b/ffi/dotnet/Devolutions.IronRdp/Generated/ServerCertChain.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 ServerCertChain: IDisposable
+{
+ private unsafe Raw.ServerCertChain* _inner;
+
+ public nuint Len
+ {
+ get
+ {
+ return GetLen();
+ }
+ }
+
+ ///
+ /// 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).
+ ///
+ /// 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 ServerCertChain(Raw.ServerCertChain* handle)
+ {
+ _inner = handle;
+ }
+
+ public nuint GetLen()
+ {
+ unsafe
+ {
+ if (_inner == null)
+ {
+ throw new ObjectDisposedException("ServerCertChain");
+ }
+ nuint retVal = Raw.ServerCertChain.GetLen(_inner);
+ return retVal;
+ }
+ }
+
+ ///
+ ///
+ /// A VecU8 allocated on Rust side.
+ ///
+ public VecU8 GetVecu8(nuint index)
+ {
+ unsafe
+ {
+ if (_inner == null)
+ {
+ throw new ObjectDisposedException("ServerCertChain");
+ }
+ Raw.UtilsFfiResultBoxVecU8BoxIronRdpError result = Raw.ServerCertChain.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("ServerCertChain");
+ }
+ Raw.UtilsFfiResultBoxBytesSliceBoxIronRdpError result = Raw.ServerCertChain.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.ServerCertChain* AsFFI()
+ {
+ return _inner;
+ }
+
+ ///
+ /// Destroys the underlying object immediately.
+ ///
+ public void Dispose()
+ {
+ unsafe
+ {
+ if (_inner == null)
+ {
+ return;
+ }
+
+ Raw.ServerCertChain.Destroy(_inner);
+ _inner = null;
+
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ ~ServerCertChain()
+ {
+ Dispose();
+ }
+}
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/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..34bedc11b
--- /dev/null
+++ b/ffi/src/rdcleanpath.rs
@@ -0,0 +1,135 @@
+#[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