using System; using System.Diagnostics; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace MelonVPNCore { public static class DaemonSocketServer { private static Process currentVpnProcess; private static bool shouldBeRunning; private static bool isRestarting; private const int startingTime = 3000; private const int restartDelay = 250; private static DaemonConfig config; public const string daemonConfigPath = "/etc/melon-vpn/daemon.cfg"; private static void SaveConfig() { File.WriteAllText(daemonConfigPath, config.ToJson()); } public static void StartServer() { if (File.Exists(daemonConfigPath)) config = DaemonConfig.FromJson(File.ReadAllText(daemonConfigPath)); else config = new DaemonConfig(); IPHostEntry host = Dns.GetHostEntry("localhost"); IPAddress ipAddress = host.AddressList[0]; IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 22035); try { Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listener.Bind(localEndPoint); listener.Listen(32); string lastClientUpdate = Messages.EOF; while (true) { Socket handler = listener.Accept(); string data = null; byte[] bytes = null; while (true) { bytes = new byte[1024]; int bytesRec = handler.Receive(bytes); data += Encoding.ASCII.GetString(bytes, 0, bytesRec); if (data.IndexOf(Messages.EOF, StringComparison.CurrentCulture) > -1) break; } if (data.StartsWith(Messages.ClientListStartMsg, StringComparison.CurrentCulture) && shouldBeRunning) { lastClientUpdate = data; Client.SendCustomMessage(lastClientUpdate, true); } else if (data == Messages.RestartOnMsg) { Console.WriteLine("Updated shouldRestart to true"); config.ShouldRestart = true; SaveConfig(); Client.SendDataMessage(DataMessage.RestartOn, true); } else if (data == Messages.RestartOffMsg) { Console.WriteLine("Updated shouldRestart to false"); config.ShouldRestart = false; SaveConfig(); Client.SendDataMessage(DataMessage.RestartOff, true); } else if (data == Messages.StatusMsg) { Console.WriteLine("Status requested"); if (IsProcessOnline(currentVpnProcess) || shouldBeRunning) { if (isRestarting) { Console.WriteLine("Sending response: restarting"); Client.SendDataMessage(DataMessage.Restarting, true); Client.SendCustomMessage(lastClientUpdate, true); } else { Console.WriteLine("Sending response: online"); Client.SendDataMessage(DataMessage.Online, true); Client.SendCustomMessage(lastClientUpdate, true); } } else { Console.WriteLine("Sending response: offline"); Client.SendDataMessage(DataMessage.Offline, true); Client.SendCustomMessage(Messages.ClientListEmptyMsg, true); } Client.SendDataMessage(config.ShouldRestart ? DataMessage.RestartOn : DataMessage.RestartOff); } else if (data == Messages.StartMsg) { if (! IsProcessOnline(currentVpnProcess)) { shouldBeRunning = true; Console.WriteLine("Starting VPN"); try { Console.WriteLine("Starting embedded process"); Console.WriteLine("Sending starting reply"); Client.SendDataMessage(DataMessage.Starting, true); if (StartEProcess(true)) { Console.WriteLine("Sending online reply"); Client.SendDataMessage(DataMessage.Online, true); currentVpnProcess.EnableRaisingEvents = true; } else { throw new InvalidOperationException("Client crashed!"); } } catch (Exception e) { shouldBeRunning = false; Console.WriteLine("There was an error. But I fixed it."); Console.WriteLine("It looked like this: " + e); currentVpnProcess.Dispose(); currentVpnProcess = null; Client.SendDataMessage(DataMessage.Error, true); Client.SendCustomMessage(Messages.ClientListEmptyMsg, true); } } else { Console.WriteLine("VPN already started"); } } else if (data == Messages.StopMsg) { shouldBeRunning = false; bool haderr = false; if (IsProcessOnline(currentVpnProcess)) { Console.WriteLine("Stopping VPN"); try { currentVpnProcess.EnableRaisingEvents = false; Console.WriteLine("Stopping embedded process"); currentVpnProcess.Kill(); currentVpnProcess = null; } catch (Exception e) { Console.WriteLine("There was an error. But I fixed it."); Console.WriteLine("It looked like this: " + e); currentVpnProcess = null; Client.SendDataMessage(DataMessage.Error, true); Client.SendCustomMessage(Messages.ClientListEmptyMsg, true); haderr = true; } } else { Console.WriteLine("VPN already stopped"); } if (!haderr) { Console.WriteLine("Sending offline reply"); Client.SendDataMessage(DataMessage.Offline, true); Client.SendCustomMessage(Messages.ClientListEmptyMsg, true); } } Client.SendDataMessage(DataMessage.Blank, true); handler.Shutdown(SocketShutdown.Both); handler.Close(); } } catch (Exception e) { Console.WriteLine(e); } } public static bool IsProcessOnline(Process p) { if (p == null) return false; return !p.HasExited; } static void CurrentVpnProcess_Exited(object sender, EventArgs e) { Console.WriteLine("Restarting embedded process"); currentVpnProcess.Dispose(); currentVpnProcess = null; Thread.Sleep(restartDelay); bool imonline = false; isRestarting = true; while (config.ShouldRestart && shouldBeRunning) { Console.WriteLine("Sending restarting reply"); Client.SendDataMessage(DataMessage.Restarting, true); if (StartEProcess(false)) { Console.WriteLine("Sending online reply"); Client.SendDataMessage(DataMessage.Online, true); currentVpnProcess.EnableRaisingEvents = true; imonline = true; break; } else { currentVpnProcess.Dispose(); currentVpnProcess = null; Thread.Sleep(restartDelay); } } isRestarting = false; if (! imonline) { shouldBeRunning = false; Console.WriteLine("Sending offline reply"); Client.SendDataMessage(DataMessage.Offline, true); Client.SendCustomMessage(Messages.ClientListEmptyMsg, true); } } static bool StartEProcess(bool reraisee) { if (shouldBeRunning) { try { currentVpnProcess = new Process { StartInfo = new ProcessStartInfo("simple-vpn", "client /etc/melon-vpn/client.cfg"), EnableRaisingEvents = false }; currentVpnProcess.Exited += CurrentVpnProcess_Exited; currentVpnProcess.Start(); Thread.Sleep(startingTime); return !currentVpnProcess.HasExited; } catch (Exception e) { //Not really fixed is it? Console.WriteLine("There was an error. But I fixed it."); Console.WriteLine("It looked like this: " + e); if (reraisee) { throw e; } } } return false; } } }