Skip to content

Commit

Permalink
feat(client-dotnet): implement C# NOW-proto client (#14)
Browse files Browse the repository at this point in the history
This PR includes the following:
- NOW-proto C# client implementation
- NowProto core protocol library bugfixes

---------

Co-authored-by: Marc-André Moreau <[email protected]>
  • Loading branch information
pacmancoder and Marc-André Moreau authored Feb 10, 2025
1 parent 3affff3 commit 4074684
Show file tree
Hide file tree
Showing 71 changed files with 2,030 additions and 99 deletions.
63 changes: 49 additions & 14 deletions NowProto.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,65 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nugets", "nugets", "{BD6154A5-8825-4023-A6AE-CE9FCC4B073B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Devolutions.NowProto", "nugets\Devolutions.NowProto\Devolutions.NowProto.csproj", "{2EB02FFB-DC42-4217-8520-93F06E17BFC4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devolutions.NowProto", "nugets\Devolutions.NowProto\Devolutions.NowProto.csproj", "{2EB02FFB-DC42-4217-8520-93F06E17BFC4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Devolutions.NowProto.Tests", "nugets\Devolutions.NowProto.Tests\Devolutions.NowProto.Tests.csproj", "{34912761-E8CB-4D88-A4B4-365F6DB88985}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devolutions.NowProto.Tests", "nugets\Devolutions.NowProto.Tests\Devolutions.NowProto.Tests.csproj", "{34912761-E8CB-4D88-A4B4-365F6DB88985}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devolutions.NowClient", "nugets\Devolutions.NowClient\Devolutions.NowClient.csproj", "{4692658E-C138-4A40-A10C-7E55D38D250B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|ARM64.Build.0 = Debug|ARM64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|x64.ActiveCfg = Debug|x64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|x64.Build.0 = Debug|x64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|x86.ActiveCfg = Debug|x86
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|x86.Build.0 = Debug|x86
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|ARM64.ActiveCfg = Release|ARM64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|ARM64.Build.0 = Release|ARM64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|x64.ActiveCfg = Release|x64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|x64.Build.0 = Release|x64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|x86.ActiveCfg = Release|x86
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|x86.Build.0 = Release|x86
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|ARM64.ActiveCfg = Debug|ARM64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|ARM64.Build.0 = Debug|ARM64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|x64.ActiveCfg = Debug|x64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|x64.Build.0 = Debug|x64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|x86.ActiveCfg = Debug|x86
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|x86.Build.0 = Debug|x86
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|ARM64.ActiveCfg = Release|ARM64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|ARM64.Build.0 = Release|ARM64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|x64.ActiveCfg = Release|x64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|x64.Build.0 = Release|x64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|x86.ActiveCfg = Release|x86
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|x86.Build.0 = Release|x86
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|ARM64.ActiveCfg = Debug|ARM64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|ARM64.Build.0 = Debug|ARM64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|x64.ActiveCfg = Debug|x64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|x64.Build.0 = Debug|x64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|x86.ActiveCfg = Debug|x86
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|x86.Build.0 = Debug|x86
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|ARM64.ActiveCfg = Release|ARM64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|ARM64.Build.0 = Release|ARM64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|x64.ActiveCfg = Release|x64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|x64.Build.0 = Release|x64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|x86.ActiveCfg = Release|x86
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|Any CPU.Build.0 = Release|Any CPU
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{2EB02FFB-DC42-4217-8520-93F06E17BFC4} = {BD6154A5-8825-4023-A6AE-CE9FCC4B073B}
{34912761-E8CB-4D88-A4B4-365F6DB88985} = {BD6154A5-8825-4023-A6AE-CE9FCC4B073B}
{4692658E-C138-4A40-A10C-7E55D38D250B} = {BD6154A5-8825-4023-A6AE-CE9FCC4B073B}
EndGlobalSection
EndGlobal
60 changes: 60 additions & 0 deletions nugets/Devolutions.NowClient/AExecParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Threading.Channels;

using Devolutions.NowClient.Worker;

namespace Devolutions.NowClient
{
/// <summary>
/// Common remote execution parameters for all execution styles.
/// </summary>
public abstract class AExecParams
{
/// <summary>
/// Callback for stdout data collection.
/// </summary>
public StdoutHandler OnStdout
{
set
{
_stdoutHandler = value;
}
}

/// <summary>
/// Callback for stderr data collection.
/// </summary>
public StderrHandler OnStderr
{
set
{
_stderrHandler = value;
}
}

/// <summary>
/// Callback to be called when the execution session starts on the remote host.
/// </summary>
public StartedHandler OnStarted
{
set
{
_startedHandler = value;
}
}

internal ExecSession ToExecSession(uint sessionId, ChannelWriter<IClientCommand> commandWriter)
{
return new ExecSession(
sessionId,
commandWriter,
_stdoutHandler,
_stderrHandler,
_startedHandler
);
}

private StdoutHandler? _stdoutHandler;
private StderrHandler? _stderrHandler;
private StartedHandler? _startedHandler;
}
}
14 changes: 14 additions & 0 deletions nugets/Devolutions.NowClient/Devolutions.NowClient.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x86;x64;ARM64</Platforms>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Devolutions.NowProto\Devolutions.NowProto.csproj" />
</ItemGroup>

</Project>
34 changes: 34 additions & 0 deletions nugets/Devolutions.NowClient/ExecBatchParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Devolutions.NowProto.Messages;

namespace Devolutions.NowClient
{
/// <summary>
/// Batch (Windows CMD) remote execution session parameters.
/// </summary>
/// <param name="command">Command/script to execute.</param>
public class ExecBatchParams(string command) : AExecParams
{
/// <summary>
/// Set the working directory for the command/script.
/// </summary>
public ExecBatchParams Directory(string directory)
{
_directory = directory;
return this;
}

internal NowMsgExecBatch ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecBatch.Builder(sessionId, command);

if (_directory != null)
{
builder.Directory(_directory);
}

return builder.Build();
}

private string? _directory = null;
}
}
50 changes: 50 additions & 0 deletions nugets/Devolutions.NowClient/ExecProcessParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Devolutions.NowProto.Messages;

namespace Devolutions.NowClient
{
/// <summary>
/// Plain process execution session parameters (e.g. CreateProcessW on Windows hosts).
/// </summary>
/// <param name="filename"></param>
public class ExecProcessParams(string filename) : AExecParams
{
/// <summary>
/// Set the command line parameters for the process.
/// </summary>
public ExecProcessParams Parameters(string parameters)
{
_parameters = parameters;
return this;
}

/// <summary>
/// Set the working directory for the process.
/// </summary>
public ExecProcessParams Directory(string directory)
{
_directory = directory;
return this;
}

internal NowMsgExecProcess ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecProcess.Builder(sessionId, filename);

if (_parameters != null)
{
builder.Parameters(_parameters);
}

if (_directory != null)
{
builder.Directory(_directory);
}

return builder.Build();
}


private string? _parameters = null;
private string? _directory = null;
}
}
140 changes: 140 additions & 0 deletions nugets/Devolutions.NowClient/ExecPwshParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using Devolutions.NowProto.Messages;

namespace Devolutions.NowClient
{
/// <summary>
/// Powershell 7 and higher (pwsh) remote execution session parameters.
/// </summary>
/// <param name="command">PowerShell command/script to execute.</param>
public class ExecPwshParams(string command) : AExecParams
{
/// <summary>
/// Set the working directory for the command/script.
/// </summary>
public ExecPwshParams Directory(string directory)
{
_directory = directory;
return this;
}

/// <summary>
/// Set the configuration name for the PowerShell session. (-ConfigurationName)
/// </summary>
public ExecPwshParams ConfigurationName(string configurationName)
{
_configurationName = configurationName;
return this;
}

/// <summary>
/// Set the execution policy for the PowerShell session. (-ExecutionPolicy)
/// </summary>
public ExecPwshParams ExecutionPolicy(string executionPolicy)
{
_executionPolicy = executionPolicy;
return this;
}

/// <summary>
/// Set the apartment state for the PowerShell session. (-Sta/-Mta)
/// </summary>
public ExecPwshParams ApartmentState(NowMsgExecPwsh.ApartmentStateKind apartmentState)
{
_apartmentState = apartmentState;
return this;
}

/// <summary>
/// Disable the PowerShell logo display. (-NoLogo)
/// </summary>
public ExecPwshParams NoLogo()
{
_noLogo = true;
return this;
}

/// <summary>
/// Do not close the PowerShell session after the command/script execution. (-NoExit)
/// </summary>
public ExecPwshParams NoExit()
{
_noExit = true;
return this;
}

/// <summary>
/// Do not load the PowerShell profile. (-NoProfile)
/// </summary>
public ExecPwshParams NoProfile()
{
_noProfile = true;
return this;
}

/// <summary>
/// Run the PowerShell session in non-interactive mode. (-NonInteractive)
/// </summary>
public ExecPwshParams NonInteractive()
{
_nonInteractive = true;
return this;
}

internal NowMsgExecPwsh ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecPwsh.Builder(sessionId, command);

if (_directory != null)
{
builder.Directory(_directory);
}

if (_configurationName != null)
{
builder.ConfigurationName(_configurationName);
}

if (_executionPolicy != null)
{
builder.ExecutionPolicy(_executionPolicy);
}

if (_apartmentState != null)
{
builder.ApartmentState(_apartmentState.Value);
}

if (_noLogo)
{
builder.SetNoLogo();
}

if (_noExit)
{
builder.SetNoExit();
}

if (_noProfile)
{
builder.SetNoProfile();
}

if (_nonInteractive)
{
builder.SetNonInteractive();
}

return builder.Build();
}


private string? _configurationName = null;
private string? _executionPolicy = null;
private string? _directory = null;
private NowMsgExecPwsh.ApartmentStateKind? _apartmentState = null;
private bool _noLogo = false;
private bool _noExit = false;
private bool _noProfile = false;
private bool _nonInteractive = false;
}
}
Loading

0 comments on commit 4074684

Please sign in to comment.