confused screaming
This commit is contained in:
11
PVHelpers/INetEndPoint.cs
Normal file
11
PVHelpers/INetEndPoint.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
20
PVHelpers/NetEndPointFromHostname.cs
Normal file
20
PVHelpers/NetEndPointFromHostname.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
PVHelpers/NetEndPointFromIP.cs
Normal file
18
PVHelpers/NetEndPointFromIP.cs
Normal 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)) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
9
PVHelpers/PVHelpers.csproj
Normal file
9
PVHelpers/PVHelpers.csproj
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace RemoteFrameBuffer.Client
|
||||||
|
{
|
||||||
|
public interface IRemoteFrameBufferClientStreamProvider
|
||||||
|
{
|
||||||
|
public Stream GetRemoteFrameBufferClientStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace RemoteFrameBuffer.Client
|
||||||
|
{
|
||||||
|
public interface IRemoteFramebufferClientProtocolFactory
|
||||||
|
{
|
||||||
|
public abstract RemoteFrameBufferClientProtocol Construct(Stream s);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocol.cs
Normal file
18
RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocol.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
110
RemoteFrameBuffer/Client/RemoteFramebufferClient.cs
Normal file
110
RemoteFrameBuffer/Client/RemoteFramebufferClient.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
40
RemoteFrameBuffer/Common/RfbProtoVersion.cs
Normal file
40
RemoteFrameBuffer/Common/RfbProtoVersion.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
RemoteFrameBuffer/RemoteFrameBuffer.csproj
Normal file
13
RemoteFrameBuffer/RemoteFrameBuffer.csproj
Normal 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>
|
||||||
@@ -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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,4 +7,8 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\RemoteFrameBuffer\RemoteFrameBuffer.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user