diff --git a/PVHelpers/INetEndPoint.cs b/PVHelpers/INetEndPoint.cs new file mode 100644 index 0000000..33556c8 --- /dev/null +++ b/PVHelpers/INetEndPoint.cs @@ -0,0 +1,11 @@ +using System.Net; + +namespace PVHelpers +{ + public interface INetEndPoint + { + public IPEndPoint EndPoint { get; } + public IPAddress Address { get; } + public Int32 Port { get; } + } +} diff --git a/PVHelpers/NetEndPointFromHostname.cs b/PVHelpers/NetEndPointFromHostname.cs new file mode 100644 index 0000000..c91d741 --- /dev/null +++ b/PVHelpers/NetEndPointFromHostname.cs @@ -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; + } +} diff --git a/PVHelpers/NetEndPointFromIP.cs b/PVHelpers/NetEndPointFromIP.cs new file mode 100644 index 0000000..fae087e --- /dev/null +++ b/PVHelpers/NetEndPointFromIP.cs @@ -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)) { } + } +} diff --git a/PVHelpers/PVHelpers.csproj b/PVHelpers/PVHelpers.csproj new file mode 100644 index 0000000..b760144 --- /dev/null +++ b/PVHelpers/PVHelpers.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/PVabel2026.slnx b/PVabel2026.slnx index 55050b0..bb5fa7a 100644 --- a/PVabel2026.slnx +++ b/PVabel2026.slnx @@ -3,5 +3,7 @@ + + diff --git a/RemoteFrameBuffer/Client/IRemoteFrameBufferClientStreamProvider.cs b/RemoteFrameBuffer/Client/IRemoteFrameBufferClientStreamProvider.cs new file mode 100644 index 0000000..a7c1d3a --- /dev/null +++ b/RemoteFrameBuffer/Client/IRemoteFrameBufferClientStreamProvider.cs @@ -0,0 +1,7 @@ +namespace RemoteFrameBuffer.Client +{ + public interface IRemoteFrameBufferClientStreamProvider + { + public Stream GetRemoteFrameBufferClientStream(); + } +} diff --git a/RemoteFrameBuffer/Client/IRemoteFramebufferClientProtocolFactory.cs b/RemoteFrameBuffer/Client/IRemoteFramebufferClientProtocolFactory.cs new file mode 100644 index 0000000..c1aadde --- /dev/null +++ b/RemoteFrameBuffer/Client/IRemoteFramebufferClientProtocolFactory.cs @@ -0,0 +1,7 @@ +namespace RemoteFrameBuffer.Client +{ + public interface IRemoteFramebufferClientProtocolFactory + { + public abstract RemoteFrameBufferClientProtocol Construct(Stream s); + } +} diff --git a/RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocol.cs b/RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocol.cs new file mode 100644 index 0000000..d0f3b13 --- /dev/null +++ b/RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocol.cs @@ -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(); + } +} diff --git a/RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocolFactory.cs b/RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocolFactory.cs new file mode 100644 index 0000000..a120ba0 --- /dev/null +++ b/RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocolFactory.cs @@ -0,0 +1,10 @@ +namespace RemoteFrameBuffer.Client +{ + public class RemoteFrameBufferClientProtocolFactory : IRemoteFramebufferClientProtocolFactory where T : RemoteFrameBufferClientProtocol + { + public RemoteFrameBufferClientProtocol Construct(Stream s) + { + return (RemoteFrameBufferClientProtocol)Activator.CreateInstance(typeof(T), s)!; + } + } +} diff --git a/RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocol_3_3.cs b/RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocol_3_3.cs new file mode 100644 index 0000000..eba03ce --- /dev/null +++ b/RemoteFrameBuffer/Client/RemoteFrameBufferClientProtocol_3_3.cs @@ -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() + { + + } + } +} diff --git a/RemoteFrameBuffer/Client/RemoteFrameBufferTcpClientStreamProvider.cs b/RemoteFrameBuffer/Client/RemoteFrameBufferTcpClientStreamProvider.cs new file mode 100644 index 0000000..5914b7b --- /dev/null +++ b/RemoteFrameBuffer/Client/RemoteFrameBufferTcpClientStreamProvider.cs @@ -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) { } + } +} diff --git a/RemoteFrameBuffer/Client/RemoteFramebufferClient.cs b/RemoteFrameBuffer/Client/RemoteFramebufferClient.cs new file mode 100644 index 0000000..0e108e9 --- /dev/null +++ b/RemoteFrameBuffer/Client/RemoteFramebufferClient.cs @@ -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 _protocolHandlers; + + private readonly IRemoteFrameBufferClientStreamProvider _socketProvider; + private Task? backgroundWorker; + private CancellationTokenSource cancellationTokenSource = new(); + + static RemoteFramebufferClient() + { + _protocolHandlers = new() { + { new RfbProtoVersion(3, 3), new RemoteFrameBufferClientProtocolFactory() }, + }; + } + + 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(); + } +} diff --git a/RemoteFrameBuffer/Common/RfbProtoVersion.cs b/RemoteFrameBuffer/Common/RfbProtoVersion.cs new file mode 100644 index 0000000..92f6585 --- /dev/null +++ b/RemoteFrameBuffer/Common/RfbProtoVersion.cs @@ -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; + } + } +} diff --git a/RemoteFrameBuffer/RemoteFrameBuffer.csproj b/RemoteFrameBuffer/RemoteFrameBuffer.csproj new file mode 100644 index 0000000..0bb265e --- /dev/null +++ b/RemoteFrameBuffer/RemoteFrameBuffer.csproj @@ -0,0 +1,13 @@ + + + + net10.0 + enable + enable + + + + + + + diff --git a/YetAnotherDummy/Program.cs b/YetAnotherDummy/Program.cs index 002baad..5ed75f1 100644 --- a/YetAnotherDummy/Program.cs +++ b/YetAnotherDummy/Program.cs @@ -1,247 +1,13 @@ // See https://aka.ms/new-console-template for more information +using RemoteFrameBuffer; +using RemoteFrameBuffer.Client; 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]); -IRemoteFrameBufferClientStreamProvider tessia6001vnc = new IRemoteFrameBufferClientStreamProvider.RemoteFrameBufferTcpClientStreamProvider(tessia, 5901); +IRemoteFrameBufferClientStreamProvider tessia6001vnc = new RemoteFrameBufferTcpClientStreamProvider(tessia, 5901); RemoteFramebufferClient client = new(tessia6001vnc); client.Start(); Console.ReadLine(); 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 _protocolHandlers; - - private readonly IRemoteFrameBufferClientStreamProvider _socketProvider; - private Task? backgroundWorker; - private CancellationTokenSource cancellationTokenSource = new(); - - static RemoteFramebufferClient() { - _protocolHandlers = new() { - { new RfbProtoVersion(3, 3), new RemoteFrameBufferClientProtocolFactory() }, - }; - } - - 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 : 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) - { - } - } -} \ No newline at end of file diff --git a/YetAnotherDummy/YetAnotherDummy.csproj b/YetAnotherDummy/YetAnotherDummy.csproj index ed9781c..5dd89bf 100644 --- a/YetAnotherDummy/YetAnotherDummy.csproj +++ b/YetAnotherDummy/YetAnotherDummy.csproj @@ -7,4 +7,8 @@ enable + + + +