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 } }