using AspNetTools; using System; using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Runtime.InteropServices; using System.Threading; using System.Web; namespace Wv2h { public class Wv2hModule : IHttpModule, IDisposable { private static object _lock = new object(); private static Process _process; private static IPEndPoint _listening; //TODO: Use pid.lock to hold process private static void Log(string info) { File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "logs.log"), DateTime.Now + " " + info + Environment.NewLine); } public void Init(HttpApplication context) { Log("WsModule Init, OS:" + Environment.OSVersion); AppDomain.CurrentDomain.UnhandledException += (sender, args) => { Log(args.ExceptionObject?.ToString()); }; //start v2ray exe //get it's port var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data"); lock (_lock) { if (_process == null || _process?.HasExited == true) { if (_process == null) { Log("Init process"); } else if (_process?.HasExited == true) { Log("Last Process Exit. Start new process"); } _process = new Process { StartInfo = { UseShellExecute = false, WorkingDirectory = dir, FileName = Path.Combine(dir, ConfigurationManager.AppSettings["exe"]), Arguments = "-config="+ConfigurationManager.AppSettings["cfg"] } }; _process.Start(); var pid = _process.Id; Log("Process started, Pid:" + pid); Thread.Sleep(1000); var connections = ListenScan.GetAllTcpConnections(); var listens = connections.Where(p => p.owningPid == pid).ToArray(); _listening = new IPEndPoint(IPAddress.Loopback, listens.First().LocalPort); Log("Listening on " + _listening); } else { Log("Use exist process"); } } //forward ws to v2 context.PostMapRequestHandler += delegate { var ctx = HttpContext.Current; var req = ctx.Request; if(req.Path.StartsWith("/.")) return; //ACME Log($"{(ctx.IsWebSocketRequest ? "[WS]" : "")} {req.UserHostAddress} {req.HttpMethod} {req.Path} "); WebSocketForwardModule.ForwardCurrentContext( ConfigurationManager.AppSettings["key"], ConfigurationManager.AppSettings["value"], _listening, 1024 * int.Parse(ConfigurationManager.AppSettings["bsk"])); }; } public void Dispose() { Log("WsModule Dispose"); lock (_lock) { if (_process?.HasExited != true) { Log("Killing process " + _process?.Id); _process?.Kill(); Log("Process Killed " + _process?.Id); } } } private static class ListenScan { public enum TCP_TABLE_CLASS : int { TCP_TABLE_BASIC_LISTENER, TCP_TABLE_BASIC_CONNECTIONS, TCP_TABLE_BASIC_ALL, TCP_TABLE_OWNER_PID_LISTENER, TCP_TABLE_OWNER_PID_CONNECTIONS, TCP_TABLE_OWNER_PID_ALL, TCP_TABLE_OWNER_MODULE_LISTENER, TCP_TABLE_OWNER_MODULE_CONNECTIONS, TCP_TABLE_OWNER_MODULE_ALL } [StructLayout(LayoutKind.Sequential)] public struct MIB_TCPROW_OWNER_PID { public uint state; public uint localAddr; public byte localPort1; public byte localPort2; public byte localPort3; public byte localPort4; public uint remoteAddr; public byte remotePort1; public byte remotePort2; public byte remotePort3; public byte remotePort4; public int owningPid; public ushort LocalPort { get { return BitConverter.ToUInt16( new byte[2] { localPort2, localPort1 }, 0); } } public ushort RemotePort { get { return BitConverter.ToUInt16( new byte[2] { remotePort2, remotePort1 }, 0); } } } [StructLayout(LayoutKind.Sequential)] public struct MIB_TCPTABLE_OWNER_PID { public uint dwNumEntries; private MIB_TCPROW_OWNER_PID table; } [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, TCP_TABLE_CLASS tblClass, int reserved); public static MIB_TCPROW_OWNER_PID[] GetAllTcpConnections() { MIB_TCPROW_OWNER_PID[] tTable; int AF_INET = 2; // IP_v4 int buffSize = 0; // how much memory do we need? uint ret = GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0); if (ret != 0 && ret != 122) // 122 insufficient buffer size throw new Exception("bad ret on check " + ret); IntPtr buffTable = Marshal.AllocHGlobal(buffSize); try { ret = GetExtendedTcpTable(buffTable, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0); if (ret != 0) throw new Exception("bad ret " + ret); // get the number of entries in the table MIB_TCPTABLE_OWNER_PID tab = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure( buffTable, typeof(MIB_TCPTABLE_OWNER_PID)); IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries)); tTable = new MIB_TCPROW_OWNER_PID[tab.dwNumEntries]; for (int i = 0; i < tab.dwNumEntries; i++) { MIB_TCPROW_OWNER_PID tcpRow = (MIB_TCPROW_OWNER_PID)Marshal .PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID)); tTable[i] = tcpRow; // next entry rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow)); } } finally { // Free the Memory Marshal.FreeHGlobal(buffTable); } return tTable; } } } }