diff --git a/MelonVPNCore/DaemonMessaging.cs b/MelonVPNCore/DaemonMessaging.cs new file mode 100644 index 0000000..3ded4c9 --- /dev/null +++ b/MelonVPNCore/DaemonMessaging.cs @@ -0,0 +1,341 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Collections.Generic; + +namespace MelonVPNCore +{ + public class DaemonClient : IDisposable + { + + public ClientResponseState SendDataMessage(DataMessage msg) => SendCustomMessage(Messages.GetMessage(msg)); + private Socket _sock; + public static int timeout = 5000; + private Thread _recvThread; + public bool shouldBeConnected = false; + public event EventHandler messageReceived; + public event EventHandler connectionError; + + public DaemonClient() { } + public DaemonClient(Socket sockIn) + { + if (sockIn is null) throw new ArgumentNullException(nameof(sockIn)); + } + + public void start() + { + try + { + if (_sock is null) + { + IPHostEntry host = Dns.GetHostEntry("localhost"); + IPAddress ipAddress = host.AddressList[0]; + IPEndPoint remoteEndPoint = new IPEndPoint(ipAddress, DaemonServer.port); + _sock = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + shouldBeConnected = true; + _sock.Connect(remoteEndPoint); + } + else + { + shouldBeConnected = true; + } + _sock.ReceiveTimeout = timeout; + _sock.SendTimeout = timeout; + _recvThread = new Thread(recvThreader) + { + IsBackground = true + }; + _recvThread.Start(); + } + catch (Exception e) + { + shouldBeConnected = false; + Console.WriteLine(e); + _sock = null; + if (!(_recvThread is null)) + { + if (_recvThread.IsAlive) _recvThread.Abort(); + _recvThread = null; + } + throw new InvalidOperationException("Daemon Client Init Failed."); + } + } + + public ClientResponseState SendCustomMessage(string msg) + { + try + { + if (msg == Messages.EOF || msg == "") return ClientResponseState.Blank; + byte[] bytes = Encoding.ASCII.GetBytes(msg); + _sock.Send(bytes); + shouldBeConnected = _sock.Connected; + return ClientResponseState.Sent; + } + catch (Exception e) + { + Console.WriteLine(e); + shouldBeConnected = _sock.Connected; + connectionError?.Invoke(this, e); + return ClientResponseState.Error; + } + } + + public bool poll() + { + this.SendDataMessage(DataMessage.Blank); + return shouldBeConnected; + } + + public void recvThreader() + { + while (shouldBeConnected) + { + try + { + string data = null; + byte[] bytes = null; + + while (shouldBeConnected) + { + bytes = new byte[1024]; + int bytesRec = _sock.Receive(bytes); + data += Encoding.ASCII.GetString(bytes, 0, bytesRec); + shouldBeConnected = _sock.Connected; + if (data.IndexOf(Messages.EOF, StringComparison.CurrentCulture) > -1) break; + } + messageReceived?.Invoke(this, data); + } catch (SocketException e) + { + Console.WriteLine(e); + shouldBeConnected = _sock.Connected; + connectionError?.Invoke(this, e); + } catch (Exception e) + { + Console.WriteLine(e); + shouldBeConnected = _sock.Connected; + } + } + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + shouldBeConnected = false; + try + { + _sock.Shutdown(SocketShutdown.Both); + } catch (SocketException e) + { + Console.WriteLine(e); + } + if (!(_recvThread is null)) + { + if (_recvThread.IsAlive) _recvThread.Abort(); + _recvThread = null; + } + _sock.Close(); + } + _sock = null; + _recvThread = null; + disposedValue = true; + } + } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + } + #endregion + } + + public class DaemonServer : IDisposable + { + public static int port = 22037; + private Socket _sock; + private Thread _lThread; + private bool _stayListening = false; + public event EventHandler clientConnected; + + public void start() + { + try + { + IPHostEntry host = Dns.GetHostEntry("localhost"); + IPAddress ipAddress = host.AddressList[0]; + IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port); + _sock = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _stayListening = true; + _sock.Bind(localEndPoint); + _sock.Listen(64); + _lThread = new Thread(lThreader) + { + IsBackground = true + }; + _lThread.Start(); + } + catch (Exception e) + { + _stayListening = false; + Console.WriteLine(e); + _sock = null; + if (!(_lThread is null)) + { + if (_lThread.IsAlive) _lThread.Abort(); + _lThread = null; + } + throw new InvalidOperationException("Daemon Server Init Failed."); + } + } + + public void lThreader() + { + while (_stayListening) + { + try + { + DaemonClient dc = new DaemonClient(_sock.Accept()); + clientConnected?.Invoke(this,dc); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _stayListening = false; + if (!(_lThread is null)) + { + if (_lThread.IsAlive) _lThread.Abort(); + _lThread = null; + } + _sock.Close(); + } + _sock = null; + _lThread = null; + disposedValue = true; + } + } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + } + #endregion + + } + + public class DaemonClientMultiplexor : IDisposable + { + public ClientResponseState SendDataMessage(DataMessage msg) => SendCustomMessage(Messages.GetMessage(msg)); + private List plexed = new List(); + private object slockplexed = new object(); + private object slockcon = new object(); + private object slockrecv = new object(); + public event EventHandler clientMessageReceived; + public event EventHandler clientConnectComplete; + public bool startClients = true; + + public DaemonClientMultiplexor(DaemonServer dsIn) + { + if (dsIn is null) throw new ArgumentNullException(nameof(dsIn)); + dsIn.clientConnected += DsIn_ClientConnected; + } + + void DsIn_ClientConnected(object sender, DaemonClient e) + { + lock (slockplexed) plexed.Add(e); + e.messageReceived += E_MessageReceived; + e.connectionError += E_ConnectionError; + if (startClients) e.start(); + lock(slockcon) clientConnectComplete?.Invoke(this, e); + } + + void E_MessageReceived(object sender, string e) + { + lock(slockrecv) clientMessageReceived?.Invoke(this, e); + } + + void E_ConnectionError(object sender, Exception e) + { + var dc = (DaemonClient)sender; + if (! dc.shouldBeConnected) { dc.Dispose(); lock (slockplexed) plexed.Remove(dc); } + } + + public ClientResponseState SendCustomMessage(string msg) + { + var toret = ClientResponseState.Sent; + lock (slockplexed) { + foreach (DaemonClient c in plexed) + { + try + { + if (c.SendCustomMessage(msg) != ClientResponseState.Sent) toret = ClientResponseState.Error; + if (!c.shouldBeConnected) { c.Dispose(); lock (slockplexed) plexed.Remove(c); } + } catch (Exception e) + { + Console.WriteLine(e); + } + } + } + return toret; + } + + public void disconnectAllClients() + { + lock (slockplexed) foreach (DaemonClient c in plexed) c.Dispose(); + lock (slockplexed) plexed.Clear(); + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + lock(slockplexed) foreach (DaemonClient c in plexed) c.Dispose(); + lock (slockplexed) plexed.Clear(); + } + + plexed = null; + slockcon = null; + slockrecv = null; + slockplexed = null; + disposedValue = true; + } + } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + } + #endregion + + } +} diff --git a/MelonVPNCore/MelonVPNCore.csproj b/MelonVPNCore/MelonVPNCore.csproj index 40b76c1..46ba005 100644 --- a/MelonVPNCore/MelonVPNCore.csproj +++ b/MelonVPNCore/MelonVPNCore.csproj @@ -36,6 +36,7 @@ +