confused screaming

This commit is contained in:
2026-02-05 15:34:59 +01:00
parent a9536ac780
commit c4223d45e4
16 changed files with 323 additions and 237 deletions

11
PVHelpers/INetEndPoint.cs Normal file
View File

@@ -0,0 +1,11 @@
using System.Net;
namespace PVHelpers
{
public interface INetEndPoint
{
public IPEndPoint EndPoint { get; }
public IPAddress Address { get; }
public Int32 Port { get; }
}
}

View File

@@ -0,0 +1,20 @@
using System.Net;
namespace PVHelpers
{
public class NetEndPointFromHostname(String hostname, Int32 port) : INetEndPoint
{
public String Hostname { get; } = hostname;
public IPEndPoint EndPoint => new(this.Address, this.Port);
public IPAddress Address
{
get
{
IPHostEntry entry = Dns.GetHostEntry(this.Hostname);
return entry.AddressList[0];
}
}
public Int32 Port { get; } = port;
}
}

View File

@@ -0,0 +1,18 @@
using System.Net;
namespace PVHelpers
{
public class NetEndPointFromIP : INetEndPoint
{
public IPEndPoint EndPoint { get; }
public IPAddress Address => this.EndPoint.Address;
public Int32 Port => this.EndPoint.Port;
public NetEndPointFromIP(IPEndPoint endpoint)
{
this.EndPoint = endpoint;
}
public NetEndPointFromIP(IPAddress address, Int32 port) : this(new(address, port)) { }
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -3,5 +3,7 @@
<Platform Project="x64" /> <Platform Project="x64" />
<Deploy /> <Deploy />
</Project> </Project>
<Project Path="PVHelpers/PVHelpers.csproj" Id="eec5debc-f993-42e0-9962-4744b18bd0c7" />
<Project Path="RemoteFrameBuffer/RemoteFrameBuffer.csproj" Id="8e7296d6-c427-4a91-ae0b-131bd7b3ba1a" />
<Project Path="YetAnotherDummy/YetAnotherDummy.csproj" /> <Project Path="YetAnotherDummy/YetAnotherDummy.csproj" />
</Solution> </Solution>

View File

@@ -0,0 +1,7 @@
namespace RemoteFrameBuffer.Client
{
public interface IRemoteFrameBufferClientStreamProvider
{
public Stream GetRemoteFrameBufferClientStream();
}
}

View File

@@ -0,0 +1,7 @@
namespace RemoteFrameBuffer.Client
{
public interface IRemoteFramebufferClientProtocolFactory
{
public abstract RemoteFrameBufferClientProtocol Construct(Stream s);
}
}

View File

@@ -0,0 +1,18 @@
using RemoteFrameBuffer.Common;
namespace RemoteFrameBuffer.Client
{
public abstract class RemoteFrameBufferClientProtocol
{
protected readonly Stream vncStream;
public abstract RfbProtoVersion Version { get; }
public RemoteFrameBufferClientProtocol(Stream s)
{
this.vncStream = s;
}
public abstract void Handshake();
public abstract void Initialization();
}
}

View File

@@ -0,0 +1,10 @@
namespace RemoteFrameBuffer.Client
{
public class RemoteFrameBufferClientProtocolFactory<T> : IRemoteFramebufferClientProtocolFactory where T : RemoteFrameBufferClientProtocol
{
public RemoteFrameBufferClientProtocol Construct(Stream s)
{
return (RemoteFrameBufferClientProtocol)Activator.CreateInstance(typeof(T), s)!;
}
}
}

View File

@@ -0,0 +1,22 @@
using RemoteFrameBuffer.Common;
namespace RemoteFrameBuffer.Client
{
public class RemoteFrameBufferClientProtocol_3_3 : RemoteFrameBufferClientProtocol
{
public override RfbProtoVersion Version => new(3, 3);
public RemoteFrameBufferClientProtocol_3_3(Stream s) : base(s)
{
}
public override void Handshake()
{
}
public override void Initialization()
{
}
}
}

View File

@@ -0,0 +1,29 @@
using PVHelpers;
using System.Net;
using System.Net.Sockets;
namespace RemoteFrameBuffer.Client
{
public class RemoteFrameBufferTcpClientStreamProvider : IRemoteFrameBufferClientStreamProvider
{
private readonly INetEndPoint remoteEndPoint;
private readonly IPEndPoint localEndPoint;
public RemoteFrameBufferTcpClientStreamProvider(INetEndPoint remoteEndPoint, IPEndPoint? localEndPoint)
{
this.remoteEndPoint = remoteEndPoint;
this.localEndPoint = localEndPoint ?? new(IPAddress.Any, 0);
}
public Stream GetRemoteFrameBufferClientStream()
{
TcpClient tcpClient = new TcpClient(this.localEndPoint);
tcpClient.Connect(this.remoteEndPoint.EndPoint);
return tcpClient.GetStream();
}
public RemoteFrameBufferTcpClientStreamProvider(String hostname, Int16 port, IPEndPoint? localEndPoint = null) : this(new NetEndPointFromHostname(hostname, port), localEndPoint) { }
public RemoteFrameBufferTcpClientStreamProvider(IPAddress address, Int32 port, IPEndPoint? localEndPoint = null) : this(new NetEndPointFromIP(address, port), localEndPoint) { }
public RemoteFrameBufferTcpClientStreamProvider(IPEndPoint remoteEndPoint, IPEndPoint? localEndPoint = null) : this(new NetEndPointFromIP(remoteEndPoint), localEndPoint) { }
}
}

View File

@@ -0,0 +1,110 @@
using RemoteFrameBuffer.Client;
using RemoteFrameBuffer.Common;
using System.Text;
using System.Text.RegularExpressions;
namespace RemoteFrameBuffer
{
public partial class RemoteFramebufferClient
{
private static Dictionary<RfbProtoVersion, IRemoteFramebufferClientProtocolFactory> _protocolHandlers;
private readonly IRemoteFrameBufferClientStreamProvider _socketProvider;
private Task? backgroundWorker;
private CancellationTokenSource cancellationTokenSource = new();
static RemoteFramebufferClient()
{
_protocolHandlers = new() {
{ new RfbProtoVersion(3, 3), new RemoteFrameBufferClientProtocolFactory<RemoteFrameBufferClientProtocol_3_3>() },
};
}
public RemoteFramebufferClient(IRemoteFrameBufferClientStreamProvider socketProvider)
{
this._socketProvider = socketProvider;
}
public void Start()
{
if (this.backgroundWorker == null)
{
Console.Error.WriteLine("VNC client starting");
this.backgroundWorker = new Task(this.Worker, this.cancellationTokenSource.Token);
this.backgroundWorker.ContinueWith(this.WorkerStopped);
this.backgroundWorker.Start();
}
}
public void Stop()
{
if (this.backgroundWorker != null)
{
this.cancellationTokenSource.Cancel();
this.backgroundWorker.Wait();
}
}
private void Worker()
{
Int32 failcount = 0;
while (!this.cancellationTokenSource.Token.IsCancellationRequested)
{
Console.Error.WriteLine("Attempting to connect");
Stream s = this._socketProvider.GetRemoteFrameBufferClientStream();
using (s)
{
Byte[] buf = new Byte[12];
CancellationTokenSource readTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
CancellationTokenSource readTokenSource = CancellationTokenSource.CreateLinkedTokenSource(this.cancellationTokenSource.Token, readTimeout.Token);
try
{
s.ReadExactlyAsync(buf, 0, buf.Length, readTokenSource.Token).AsTask().Wait();
} catch (AggregateException e)// TODO: go through InnerExceptions and only throw if we are left with unhandled ones
{
if (e.InnerException is TaskCanceledException)
{
if (failcount++ >= 5)
{
break;
}
Task.Delay(1000).Wait();
continue;
} else
{
throw new Exception("Something broke. Yeah, I know, very useful message.", e);
}
}
failcount = 0;
String protover = Encoding.ASCII.GetString(buf);
Console.Error.Write($"Server protocol: {protover}");
Match m = _rfbVersionRegex().Match(protover);
if (!m.Success)
{
throw new Exception("Cannot parse protocol version");
}
RfbProtoVersion serverVersion = new(Int16.Parse(m.Groups["major"].Value), Int16.Parse(m.Groups["minor"].Value));
RfbProtoVersion maxProto = _protocolHandlers.Keys.Where((ph) => ph <= serverVersion).Max();
RemoteFrameBufferClientProtocol proto = _protocolHandlers[maxProto].Construct(s);
Console.Error.WriteLine($"Using client protocol {proto.Version.Major}.{proto.Version.Minor}");
proto.Handshake();
proto.Initialization();
}
}
}
private void WorkerStopped(Task t)
{
Console.Error.WriteLine("VNC client exited");
this.backgroundWorker = null;
if (!this.cancellationTokenSource.TryReset())
{
this.cancellationTokenSource = new();
}
}
[GeneratedRegex(@"^RFB (?'major'\d{3}).(?'minor'\d{3})$")]
private static partial Regex _rfbVersionRegex();
}
}

View File

@@ -0,0 +1,40 @@
namespace RemoteFrameBuffer.Common
{
public struct RfbProtoVersion(Int16 major, Int16 minor)
{
public Int16 Major = major;
public Int16 Minor = minor;
public static Boolean operator ==(RfbProtoVersion a, RfbProtoVersion b)
{
return (a.Major == b.Major) && (a.Minor == b.Minor);
}
public static Boolean operator !=(RfbProtoVersion a, RfbProtoVersion b)
{
return !(a == b);
}
public static Boolean operator <(RfbProtoVersion a, RfbProtoVersion b)
{
if (a.Major == b.Major)
{
return a.Minor < b.Minor;
} else
{
return a.Major < b.Major;
}
}
public static Boolean operator >(RfbProtoVersion a, RfbProtoVersion b)
{
return b < a;
}
public static Boolean operator <=(RfbProtoVersion a, RfbProtoVersion b)
{
return b < a;
}
public static Boolean operator >=(RfbProtoVersion a, RfbProtoVersion b)
{
return a < b;
}
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PVHelpers\PVHelpers.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,247 +1,13 @@
// See https://aka.ms/new-console-template for more information // See https://aka.ms/new-console-template for more information
using RemoteFrameBuffer;
using RemoteFrameBuffer.Client;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;
using static RemoteFrameBufferClientProtocol;
IPAddress tessia = new([192,168,16,253]); IPAddress tessia = new([192,168,16,253]);
IRemoteFrameBufferClientStreamProvider tessia6001vnc = new IRemoteFrameBufferClientStreamProvider.RemoteFrameBufferTcpClientStreamProvider(tessia, 5901); IRemoteFrameBufferClientStreamProvider tessia6001vnc = new RemoteFrameBufferTcpClientStreamProvider(tessia, 5901);
RemoteFramebufferClient client = new(tessia6001vnc); RemoteFramebufferClient client = new(tessia6001vnc);
client.Start(); client.Start();
Console.ReadLine(); Console.ReadLine();
client.Stop(); client.Stop();
public interface INetEndPoint
{
public IPEndPoint EndPoint { get; }
public IPAddress Address { get; }
public Int32 Port { get; }
public class NetEndPointFromIP : INetEndPoint
{
public IPEndPoint EndPoint { get; }
public IPAddress Address => this.EndPoint.Address;
public Int32 Port => this.EndPoint.Port;
public NetEndPointFromIP(IPEndPoint endpoint)
{
this.EndPoint = endpoint;
}
public NetEndPointFromIP(IPAddress address, Int32 port) : this(new(address, port)) { }
}
public class NetEndPointFromHostname(String hostname, Int32 port) : INetEndPoint
{
public String Hostname { get; } = hostname;
public IPEndPoint EndPoint => new(this.Address, this.Port);
public IPAddress Address {
get
{
IPHostEntry entry = Dns.GetHostEntry(this.Hostname);
return entry.AddressList[0];
}
}
public Int32 Port { get; } = port;
}
}
public interface IRemoteFrameBufferClientStreamProvider
{
public Stream GetRemoteFrameBufferClientStream();
public class RemoteFrameBufferTcpClientStreamProvider : IRemoteFrameBufferClientStreamProvider
{
private readonly INetEndPoint remoteEndPoint;
private readonly IPEndPoint localEndPoint;
public RemoteFrameBufferTcpClientStreamProvider(INetEndPoint remoteEndPoint, IPEndPoint? localEndPoint)
{
this.remoteEndPoint = remoteEndPoint;
this.localEndPoint = localEndPoint ?? new(IPAddress.Any, 0);
}
public Stream GetRemoteFrameBufferClientStream()
{
TcpClient tcpClient = new TcpClient(this.localEndPoint);
tcpClient.Connect(this.remoteEndPoint.EndPoint);
return tcpClient.GetStream();
}
public RemoteFrameBufferTcpClientStreamProvider(String hostname, Int16 port, IPEndPoint? localEndPoint = null) : this(new INetEndPoint.NetEndPointFromHostname(hostname, port), localEndPoint) { }
public RemoteFrameBufferTcpClientStreamProvider(IPAddress address, Int32 port, IPEndPoint? localEndPoint = null) : this(new INetEndPoint.NetEndPointFromIP(address, port), localEndPoint) { }
public RemoteFrameBufferTcpClientStreamProvider(IPEndPoint remoteEndPoint, IPEndPoint? localEndPoint = null) : this(new INetEndPoint.NetEndPointFromIP(remoteEndPoint), localEndPoint) { }
}
}
public partial class RemoteFramebufferClient
{
private static Dictionary<RfbProtoVersion, RemoteFrameBufferClientProtocol.IRemoteFramebufferClientProtocolFactory> _protocolHandlers;
private readonly IRemoteFrameBufferClientStreamProvider _socketProvider;
private Task? backgroundWorker;
private CancellationTokenSource cancellationTokenSource = new();
static RemoteFramebufferClient() {
_protocolHandlers = new() {
{ new RfbProtoVersion(3, 3), new RemoteFrameBufferClientProtocolFactory<RemoteFrameBufferClientProtocol.RemoteFrameBufferClientProtocol_3_3>() },
};
}
public RemoteFramebufferClient(IRemoteFrameBufferClientStreamProvider socketProvider)
{
this._socketProvider = socketProvider;
}
public void Start()
{
if (this.backgroundWorker == null)
{
Console.Error.WriteLine("VNC client starting");
this.backgroundWorker = new Task(this.Worker, this.cancellationTokenSource.Token);
this.backgroundWorker.ContinueWith(this.WorkerStopped);
this.backgroundWorker.Start();
}
}
public void Stop()
{
if (this.backgroundWorker != null)
{
this.cancellationTokenSource.Cancel();
this.backgroundWorker.Wait();
}
}
private void Worker()
{
Int32 failcount = 0;
while (!this.cancellationTokenSource.Token.IsCancellationRequested)
{
Console.Error.WriteLine("Attempting to connect");
Stream s = this._socketProvider.GetRemoteFrameBufferClientStream();
using (s)
{
Byte[] buf = new Byte[12];
CancellationTokenSource readTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
CancellationTokenSource readTokenSource = CancellationTokenSource.CreateLinkedTokenSource(this.cancellationTokenSource.Token, readTimeout.Token);
try
{
s.ReadExactlyAsync(buf, 0, buf.Length, readTokenSource.Token).AsTask().Wait();
}catch (AggregateException e)// TODO: go through InnerExceptions and only throw if we are left with unhandled ones
{
if (e.InnerException is TaskCanceledException)
{
if (failcount++ >= 5)
{
break;
}
Task.Delay(1000).Wait();
continue;
}else
{
throw new Exception("Something broke. Yeah, I know, very useful message.", e);
}
}
failcount = 0;
String protover = Encoding.ASCII.GetString(buf);
Console.Error.Write($"Server protocol: {protover}");
Match m = _rfbVersionRegex().Match(protover);
if (!m.Success)
{
throw new Exception("Cannot parse protocol version");
}
RfbProtoVersion serverVersion = new(Int16.Parse(m.Groups["major"].Value), Int16.Parse(m.Groups["minor"].Value));
RfbProtoVersion maxProto = _protocolHandlers.Keys.Where((ph) => ph <= serverVersion).Max();
RemoteFrameBufferClientProtocol proto = _protocolHandlers[maxProto].Construct(s);
Console.Error.WriteLine($"Using client protocol {proto.Version.Major}.{proto.Version.Minor}");
}
}
}
private void WorkerStopped(Task t)
{
Console.Error.WriteLine("VNC client exited");
this.backgroundWorker = null;
if (!this.cancellationTokenSource.TryReset())
{
this.cancellationTokenSource = new();
}
}
[GeneratedRegex(@"^RFB (?'major'\d{3}).(?'minor'\d{3})$")]
private static partial Regex _rfbVersionRegex();
}
public struct RfbProtoVersion(Int16 major, Int16 minor)
{
public Int16 Major = major;
public Int16 Minor = minor;
public static Boolean operator ==(RfbProtoVersion a, RfbProtoVersion b)
{
return (a.Major == b.Major) && (a.Minor == b.Minor);
}
public static Boolean operator !=(RfbProtoVersion a, RfbProtoVersion b)
{
return !(a == b);
}
public static Boolean operator <(RfbProtoVersion a, RfbProtoVersion b)
{
if (a.Major == b.Major)
{
return a.Minor < b.Minor;
} else
{
return a.Major < b.Major;
}
}
public static Boolean operator >(RfbProtoVersion a, RfbProtoVersion b)
{
return b < a;
}
public static Boolean operator <=(RfbProtoVersion a, RfbProtoVersion b)
{
return b < a;
}
public static Boolean operator >=(RfbProtoVersion a, RfbProtoVersion b)
{
return a < b;
}
}
public abstract class RemoteFrameBufferClientProtocol
{
protected readonly Stream vncStream;
public abstract RfbProtoVersion Version { get; }
public RemoteFrameBufferClientProtocol(Stream s)
{
this.vncStream = s;
}
public interface IRemoteFramebufferClientProtocolFactory {
public abstract RemoteFrameBufferClientProtocol Construct(Stream s);
}
public class RemoteFrameBufferClientProtocolFactory<T> : IRemoteFramebufferClientProtocolFactory where T : RemoteFrameBufferClientProtocol
{
public RemoteFrameBufferClientProtocol Construct(Stream s)
{
return (RemoteFrameBufferClientProtocol)Activator.CreateInstance(typeof(T), s)!;
}
}
public class RemoteFrameBufferClientProtocol_3_3 : RemoteFrameBufferClientProtocol
{
public override RfbProtoVersion Version => new(3,3);
public RemoteFrameBufferClientProtocol_3_3(Stream s) : base(s)
{
}
}
}

View File

@@ -7,4 +7,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RemoteFrameBuffer\RemoteFrameBuffer.csproj" />
</ItemGroup>
</Project> </Project>