IronRDP/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs
Benoît Cortier 712da42ded
fix: inject socket local address for the client addr (#759)
We used to inject the resolved target server address, but that is not
what is expected. Server typically ignores this field so this was not a
problem up until now.
2025-04-21 09:08:50 +00:00

193 lines
7.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace Devolutions.IronRdp.ConnectExample
{
class Program
{
static async Task Main(string[] args)
{
Log.InitWithEnv();
var arguments = ParseArguments(args);
if (arguments == null)
{
return;
}
var serverName = arguments["--serverName"];
var username = arguments["--username"];
var password = arguments["--password"];
var domain = arguments["--domain"];
try
{
var (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;
while (keepLooping)
{
var readPduTask = framed.ReadPdu();
Action? action = null;
byte[]? payload = null;
if (readPduTask == await Task.WhenAny(readPduTask, Task.Delay(2000)))
{
var pduReadTask = await readPduTask;
action = pduReadTask.Item1;
payload = pduReadTask.Item2;
Console.WriteLine($"Action: {action}");
}
else
{
Console.WriteLine("Timeout");
break;
}
var outputIterator = activeState.Process(decodedImage, action, payload);
while (!outputIterator.IsEmpty())
{
var output = outputIterator.Next()!; // outputIterator.Next() is not null since outputIterator.IsEmpty() is false
Console.WriteLine($"Output type: {output.GetType()}");
if (output.GetEnumType() == ActiveStageOutputType.Terminate)
{
Console.WriteLine("Connection terminated.");
keepLooping = false;
}
if (output.GetEnumType() == ActiveStageOutputType.ResponseFrame)
{
var responseFrame = output.GetResponseFrame()!;
byte[] responseFrameBytes = new byte[responseFrame.GetSize()];
responseFrame.Fill(responseFrameBytes);
await framed.Write(responseFrameBytes);
}
}
}
saveImage(decodedImage, "output.png");
}
catch (Exception e)
{
Console.WriteLine($"An error occurred: {e.Message}\n\nStackTrace:\n{e.StackTrace}");
}
}
private static void saveImage(DecodedImage decodedImage, string v)
{
int width = decodedImage.GetWidth();
int height = decodedImage.GetHeight();
var data = decodedImage.GetData();
var bytes = new byte[data.GetSize()];
data.Fill(bytes);
using Image<Rgba32> image = new Image<Rgba32>(width, height);
// Well mutate this struct instead of creating a new one for performance reasons.
Rgba32 color = new Rgba32(0, 0, 0);
for (int col = 0; col < width; ++col)
{
for (int row = 0; row < height; ++row)
{
var idx = (row * width + col) * 4;
color.R = bytes[idx];
color.G = bytes[idx + 1];
color.B = bytes[idx + 2];
image[col, row] = color;
}
}
// Save the image as bitmap.
image.Save("./output.bmp");
}
static Dictionary<string, string>? ParseArguments(string[] args)
{
if (args.Length == 0 || Array.Exists(args, arg => arg == "--help"))
{
PrintHelp();
return null;
}
var arguments = new Dictionary<string, string>();
string? lastKey = null;
foreach (var arg in args)
{
if (arg.StartsWith("--"))
{
if (lastKey != null)
{
Console.WriteLine($"Error: Missing value for {lastKey}.");
PrintHelp();
return null;
}
if (!IsValidArgument(arg))
{
Console.WriteLine($"Error: Unknown argument {arg}.");
PrintHelp();
return null;
}
lastKey = arg;
}
else
{
if (lastKey == null)
{
Console.WriteLine("Error: Value without a preceding flag.");
PrintHelp();
return null;
}
arguments[lastKey] = arg;
lastKey = null;
}
}
if (lastKey != null)
{
Console.WriteLine($"Error: Missing value for {lastKey}.");
PrintHelp();
return null;
}
return arguments;
}
static bool IsValidArgument(string argument)
{
var validArguments = new List<string> { "--serverName", "--username", "--password", "--domain" };
return validArguments.Contains(argument);
}
static void PrintHelp()
{
Console.WriteLine("Usage: dotnet run -- [OPTIONS]");
Console.WriteLine("Options:");
Console.WriteLine(" --serverName <serverName> The name of the server to connect to.");
Console.WriteLine(" --username <username> The username for connection.");
Console.WriteLine(" --password <password> The password for connection.");
Console.WriteLine(" --domain <domain> The domain of the server.");
Console.WriteLine(" --help Show this message and exit.");
}
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);
configBuilder.SetDesktopSize((ushort)height, (ushort)width);
configBuilder.SetClientName("IronRdp");
configBuilder.SetClientDir("C:\\");
configBuilder.SetPerformanceFlags(PerformanceFlags.NewDefault());
return configBuilder.Build();
}
}
}