mirror of
https://github.com/netchx/netch.git
synced 2026-03-18 18:13:21 +08:00
done
This commit is contained in:
37
Netch/Controllers/DNSController.cs
Normal file
37
Netch/Controllers/DNSController.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class DNSController
|
||||
{
|
||||
public static DNS.Server.DnsServer Server = new DNS.Server.DnsServer(new Resolver());
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = Server.Listen(new IPEndPoint(IPAddress.IPv6Any, 53));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
Server.Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Netch/Controllers/HTTPController.cs
Normal file
92
Netch/Controllers/HTTPController.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class HTTPController
|
||||
{
|
||||
/// <summary>
|
||||
/// 实例
|
||||
/// </summary>
|
||||
public PrivoxyController pPrivoxyController = new PrivoxyController();
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public bool Start(Models.Server server, Models.Mode mode)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (server.Type == "Socks5")
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(server.Username) && !String.IsNullOrWhiteSpace(server.Password))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPrivoxyController.Start(server, mode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pPrivoxyController.Start(server, mode);
|
||||
}
|
||||
|
||||
if (mode.Type != 5)
|
||||
{
|
||||
// HTTP 系统代理模式,启动系统代理
|
||||
using (var registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true))
|
||||
{
|
||||
registry.SetValue("ProxyEnable", 1);
|
||||
registry.SetValue("ProxyServer", $"127.0.0.1:{Global.Settings.HTTPLocalPort}");
|
||||
|
||||
Win32Native.InternetSetOption(IntPtr.Zero, 39, IntPtr.Zero, 0);
|
||||
Win32Native.InternetSetOption(IntPtr.Zero, 37, IntPtr.Zero, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
pPrivoxyController.Stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
}
|
||||
|
||||
using (var registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true))
|
||||
{
|
||||
registry.SetValue("ProxyEnable", 0);
|
||||
registry.DeleteValue("ProxyServer", false);
|
||||
|
||||
Win32Native.InternetSetOption(IntPtr.Zero, 39, IntPtr.Zero, 0);
|
||||
Win32Native.InternetSetOption(IntPtr.Zero, 37, IntPtr.Zero, 0);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
220
Netch/Controllers/MainController.cs
Normal file
220
Netch/Controllers/MainController.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class MainController
|
||||
{
|
||||
public static Process GetProcess()
|
||||
{
|
||||
var process = new Process();
|
||||
process.StartInfo.WorkingDirectory = String.Format("{0}\\bin", Directory.GetCurrentDirectory());
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
process.StartInfo.RedirectStandardError = true;
|
||||
process.StartInfo.RedirectStandardInput = true;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.EnableRaisingEvents = true;
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SS 控制器
|
||||
/// </summary>
|
||||
public SSController pSSController = null;
|
||||
|
||||
/// <summary>
|
||||
/// SSR 控制器
|
||||
/// </summary>
|
||||
public SSRController pSSRController = null;
|
||||
|
||||
/// <summary>
|
||||
/// V2Ray 控制器
|
||||
/// </summary>
|
||||
public VMessController pVMessController = null;
|
||||
|
||||
/// <summary>
|
||||
/// NF 控制器
|
||||
/// </summary>
|
||||
public NFController pNFController = null;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 控制器
|
||||
/// </summary>
|
||||
public HTTPController pHTTPController = null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// TUN/TAP 控制器
|
||||
/// </summary>
|
||||
public TUNTAPController pTUNTAPController = null;
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public bool Start(Models.Server server, Models.Mode mode)
|
||||
{
|
||||
var result = false;
|
||||
switch (server.Type)
|
||||
{
|
||||
case "Socks5":
|
||||
if (mode.Type == 4)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case "SS":
|
||||
KillProcess("Shadowsocks");
|
||||
if (pSSController == null)
|
||||
{
|
||||
pSSController = new SSController();
|
||||
}
|
||||
result = pSSController.Start(server, mode);
|
||||
break;
|
||||
case "SSR":
|
||||
KillProcess("ShadowsocksR");
|
||||
if (pSSRController == null)
|
||||
{
|
||||
pSSRController = new SSRController();
|
||||
}
|
||||
result = pSSRController.Start(server, mode);
|
||||
break;
|
||||
case "VMess":
|
||||
KillProcess("v2ray");
|
||||
if (pVMessController == null)
|
||||
{
|
||||
pVMessController = new VMessController();
|
||||
}
|
||||
result = pVMessController.Start(server, mode);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (mode.Type == 0)
|
||||
{
|
||||
if (pNFController == null)
|
||||
{
|
||||
pNFController = new NFController();
|
||||
}
|
||||
// 进程代理模式,启动 NF 控制器
|
||||
result = pNFController.Start(server, mode);
|
||||
}
|
||||
else if (mode.Type == 1)
|
||||
{
|
||||
if (pTUNTAPController == null)
|
||||
{
|
||||
pTUNTAPController = new TUNTAPController();
|
||||
}
|
||||
// TUN/TAP 黑名单代理模式,启动 TUN/TAP 控制器
|
||||
result = pTUNTAPController.Start(server, mode);
|
||||
}
|
||||
else if (mode.Type == 2)
|
||||
{
|
||||
if (pTUNTAPController == null)
|
||||
{
|
||||
pTUNTAPController = new TUNTAPController();
|
||||
}
|
||||
// TUN/TAP 白名单代理模式,启动 TUN/TAP 控制器
|
||||
result = pTUNTAPController.Start(server, mode);
|
||||
}
|
||||
else if (mode.Type == 3 || mode.Type == 5)
|
||||
{
|
||||
if (pHTTPController == null)
|
||||
{
|
||||
pHTTPController = new HTTPController();
|
||||
}
|
||||
// HTTP 系统代理和 Socks5 和 HTTP 代理模式,启动 HTTP 控制器
|
||||
result = pHTTPController.Start(server, mode);
|
||||
}
|
||||
else if (mode.Type == 4)
|
||||
{
|
||||
// Socks5 代理模式,不需要启动额外的控制器
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (pSSController != null)
|
||||
{
|
||||
pSSController.Stop();
|
||||
}
|
||||
else if (pSSRController != null)
|
||||
{
|
||||
pSSRController.Stop();
|
||||
}
|
||||
else if (pVMessController != null)
|
||||
{
|
||||
pVMessController.Stop();
|
||||
}
|
||||
|
||||
if (pNFController != null)
|
||||
{
|
||||
pNFController.Stop();
|
||||
}
|
||||
else if (pTUNTAPController != null)
|
||||
{
|
||||
pTUNTAPController.Stop();
|
||||
}
|
||||
else if (pHTTPController != null)
|
||||
{
|
||||
pHTTPController.Stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void KillProcess(String name) {
|
||||
Process[] processes = Process.GetProcessesByName(name);
|
||||
foreach (Process p in processes)
|
||||
{
|
||||
if (IsChildProcess(p, name))
|
||||
{
|
||||
p.Kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsChildProcess(Process process,string name)
|
||||
{
|
||||
bool result;
|
||||
try
|
||||
{
|
||||
string FileName = (Directory.GetCurrentDirectory() + "\\bin\\" + name + ".exe").ToLower();
|
||||
string procFileName = process.MainModule.FileName.ToLower();
|
||||
result = FileName.Equals(procFileName, StringComparison.Ordinal);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.Message);
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
265
Netch/Controllers/NFController.cs
Normal file
265
Netch/Controllers/NFController.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class NFController
|
||||
{
|
||||
/// <summary>
|
||||
/// 流量变动事件
|
||||
/// </summary>
|
||||
public event BandwidthUpdateHandler OnBandwidthUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// 流量变动处理器
|
||||
/// </summary>
|
||||
/// <param name="upload">上传</param>
|
||||
/// <param name="download">下载</param>
|
||||
public delegate void BandwidthUpdateHandler(long upload, Int64 download);
|
||||
|
||||
/// <summary>
|
||||
/// 进程实例
|
||||
/// </summary>
|
||||
public Process Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态
|
||||
/// </summary>
|
||||
public Models.State State = Models.State.Waiting;
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否成功</returns>
|
||||
public bool Start(Models.Server server, Models.Mode mode)
|
||||
{
|
||||
if (!File.Exists("bin\\Redirector.exe"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 生成驱动文件路径
|
||||
var driver = String.Format("{0}\\drivers\\netfilter2.sys", Environment.SystemDirectory);
|
||||
|
||||
// 检查驱动是否存在
|
||||
if (!File.Exists(driver))
|
||||
{
|
||||
// 生成系统版本
|
||||
var version = $"{Environment.OSVersion.Version.Major.ToString()}.{Environment.OSVersion.Version.Minor.ToString()}";
|
||||
|
||||
// 检查系统版本并复制对应驱动
|
||||
try
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case "10.0":
|
||||
File.Copy("bin\\Win-10.sys", driver);
|
||||
Utils.Logging.Info("已复制 Win10 驱动");
|
||||
break;
|
||||
case "6.3":
|
||||
case "6.2":
|
||||
File.Copy("bin\\Win-8.sys", driver);
|
||||
Utils.Logging.Info("已复制 Win8 驱动");
|
||||
break;
|
||||
case "6.1":
|
||||
case "6.0":
|
||||
File.Copy("bin\\Win-7.sys", driver);
|
||||
Utils.Logging.Info("已复制 Win7 驱动");
|
||||
break;
|
||||
default:
|
||||
Utils.Logging.Info($"不支持的系统版本:{version}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info("复制驱动文件失败");
|
||||
Utils.Logging.Info(e.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 注册驱动文件
|
||||
var result = nfapinet.NFAPI.nf_registerDriver("netfilter2");
|
||||
if (result != nfapinet.NF_STATUS.NF_STATUS_SUCCESS)
|
||||
{
|
||||
Utils.Logging.Info($"注册驱动失败,返回值:{result}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var service = new ServiceController("netfilter2");
|
||||
if (service.Status == ServiceControllerStatus.Stopped)
|
||||
{
|
||||
service.Start();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
|
||||
var result = nfapinet.NFAPI.nf_registerDriver("netfilter2");
|
||||
if (result != nfapinet.NF_STATUS.NF_STATUS_SUCCESS)
|
||||
{
|
||||
Utils.Logging.Info($"注册驱动失败,返回值:{result}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var processes = "";
|
||||
foreach (var proc in mode.Rule)
|
||||
{
|
||||
processes += proc;
|
||||
processes += ",";
|
||||
}
|
||||
processes = processes.Substring(0, processes.Length - 1);
|
||||
|
||||
Instance = MainController.GetProcess();
|
||||
Instance.StartInfo.FileName = "bin\\Redirector.exe";
|
||||
|
||||
var FallBackArg = "";
|
||||
|
||||
if (server.Type != "Socks5")
|
||||
{
|
||||
FallBackArg = $"-r 127.0.0.1:{Global.Settings.Socks5LocalPort} -p \"{processes}\"";
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var result = Utils.DNS.Lookup(server.Hostname);
|
||||
if (result == null)
|
||||
{
|
||||
Utils.Logging.Info("无法解析服务器 IP 地址");
|
||||
return false;
|
||||
}
|
||||
|
||||
FallBackArg = $"-r {result.ToString()}:{server.Port} -p \"{processes}\"";
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(server.Username) && !String.IsNullOrWhiteSpace(server.Password))
|
||||
{
|
||||
FallBackArg += $" -username \"{server.Username}\" -password \"{server.Password}\"";
|
||||
}
|
||||
}
|
||||
|
||||
Instance.StartInfo.Arguments = FallBackArg + $" -t {Global.Settings.RedirectorTCPPort}";
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
State = Models.State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
|
||||
var IsFallback = false;
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == Models.State.Started)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (State == Models.State.Stopped)
|
||||
{
|
||||
if (!IsFallback)
|
||||
{
|
||||
IsFallback = true;
|
||||
Stop();
|
||||
Utils.Logging.Info($"尝试去除 \"-t {Global.Settings.RedirectorTCPPort}\" 参数后启动 \"bin\\Redirector.exe\"");
|
||||
Instance.StartInfo.Arguments = FallBackArg;
|
||||
Utils.Logging.Info($"当前 \"bin\\Redirector.exe\" 启动参数为 \"{Instance.StartInfo.Arguments}\"");
|
||||
Global.Settings.RedirectorTCPPort = 2800;
|
||||
Instance.CancelOutputRead();
|
||||
Instance.CancelErrorRead();
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
State = Models.State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.Logging.Info("NF 进程启动失败");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Utils.Logging.Info("NF 进程启动超时");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Instance != null && !Instance.HasExited)
|
||||
{
|
||||
Instance.Kill();
|
||||
Instance.WaitForExit();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(e.Data))
|
||||
{
|
||||
File.AppendAllText("logging\\redirector.log", String.Format("{0}\r\n", e.Data));
|
||||
|
||||
if (State == Models.State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
}
|
||||
else if (e.Data.Contains("Started"))
|
||||
{
|
||||
State = Models.State.Started;
|
||||
}
|
||||
else if (e.Data.Contains("Failed") || e.Data.Contains("Unable"))
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
}
|
||||
}
|
||||
else if (State == Models.State.Started)
|
||||
{
|
||||
if (e.Data.StartsWith("[Application][Bandwidth]"))
|
||||
{
|
||||
var splited = e.Data.Replace("[Application][Bandwidth]", "").Trim().Split(',');
|
||||
if (splited.Length == 2)
|
||||
{
|
||||
var uploadSplited = splited[0].Split(':');
|
||||
var downloadSplited = splited[1].Split(':');
|
||||
|
||||
if (uploadSplited.Length == 2 && downloadSplited.Length == 2)
|
||||
{
|
||||
if (long.TryParse(uploadSplited[1], out long upload) && long.TryParse(downloadSplited[1], out long download))
|
||||
{
|
||||
Task.Run(() => OnBandwidthUpdated(upload, download));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Netch/Controllers/PrivoxyController.cs
Normal file
84
Netch/Controllers/PrivoxyController.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class PrivoxyController
|
||||
{
|
||||
/// <summary>
|
||||
/// 进程实例
|
||||
/// </summary>
|
||||
public Process Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public bool Start(Models.Server server, Models.Mode mode)
|
||||
{
|
||||
foreach (var proc in Process.GetProcessesByName("Privoxy"))
|
||||
{
|
||||
try
|
||||
{
|
||||
proc.Kill();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 跳过
|
||||
}
|
||||
}
|
||||
|
||||
if (!File.Exists("bin\\Privoxy.exe") || !File.Exists("bin\\default.conf"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (server.Type != "Socks5")
|
||||
{
|
||||
File.WriteAllText("data\\privoxy.conf", File.ReadAllText("bin\\default.conf").Replace("_BIND_PORT_", Global.Settings.HTTPLocalPort.ToString()).Replace("_DEST_PORT_", Global.Settings.Socks5LocalPort.ToString()).Replace("0.0.0.0", Global.Settings.LocalAddress));
|
||||
}
|
||||
else
|
||||
{
|
||||
File.WriteAllText("data\\privoxy.conf", File.ReadAllText("bin\\default.conf").Replace("_BIND_PORT_", Global.Settings.HTTPLocalPort.ToString()).Replace("_DEST_PORT_", server.Port.ToString()).Replace("s 0.0.0.0", $"s {Global.Settings.LocalAddress}").Replace("/ 127.0.0.1", $"/ {server.Hostname}"));
|
||||
}
|
||||
|
||||
|
||||
Instance = new Process()
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
FileName = String.Format("{0}\\bin\\Privoxy.exe", Directory.GetCurrentDirectory()),
|
||||
Arguments = "..\\data\\privoxy.conf",
|
||||
WorkingDirectory = String.Format("{0}\\bin", Directory.GetCurrentDirectory()),
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
Instance.Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Instance != null && !Instance.HasExited)
|
||||
{
|
||||
Instance.Kill();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 跳过
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
122
Netch/Controllers/SSController.cs
Normal file
122
Netch/Controllers/SSController.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class SSController
|
||||
{
|
||||
/// <summary>
|
||||
/// 进程实例
|
||||
/// </summary>
|
||||
public Process Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态
|
||||
/// </summary>
|
||||
public Models.State State = Models.State.Waiting;
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public bool Start(Models.Server server, Models.Mode mode)
|
||||
{
|
||||
if (!File.Exists("bin\\Shadowsocks.exe"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Instance = MainController.GetProcess();
|
||||
Instance.StartInfo.FileName = "bin\\Shadowsocks.exe";
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(server.Plugin) && !String.IsNullOrWhiteSpace(server.PluginOption))
|
||||
{
|
||||
Instance.StartInfo.Arguments = $"-s {server.Hostname} -p {server.Port} -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u --plugin {server.Plugin} --plugin-opts \"{server.PluginOption}\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance.StartInfo.Arguments = $"-s {server.Hostname} -p {server.Port} -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u";
|
||||
}
|
||||
|
||||
if (mode.BypassChina)
|
||||
{
|
||||
Instance.StartInfo.Arguments += " --acl default.acl";
|
||||
}
|
||||
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
|
||||
State = Models.State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == Models.State.Started)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (State == Models.State.Stopped)
|
||||
{
|
||||
Utils.Logging.Info("SS 进程启动失败");
|
||||
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Utils.Logging.Info("SS 进程启动超时");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Instance != null && !Instance.HasExited)
|
||||
{
|
||||
Instance.Kill();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(e.Data))
|
||||
{
|
||||
// File.AppendAllText("logging\\shadowsocks.log", $"{e.Data}\r\n");
|
||||
|
||||
if (State == Models.State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
}
|
||||
else if (e.Data.Contains("listening at"))
|
||||
{
|
||||
State = Models.State.Started;
|
||||
}
|
||||
else if (e.Data.Contains("Invalid config path") || e.Data.Contains("usage") || e.Data.Contains("plugin service exit unexpectedly"))
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
136
Netch/Controllers/SSRController.cs
Normal file
136
Netch/Controllers/SSRController.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class SSRController
|
||||
{
|
||||
/// <summary>
|
||||
/// 进程实例
|
||||
/// </summary>
|
||||
public Process Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态
|
||||
/// </summary>
|
||||
public Models.State State = Models.State.Waiting;
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public bool Start(Models.Server server, Models.Mode mode)
|
||||
{
|
||||
if (!File.Exists("bin\\ShadowsocksR.exe"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Instance = MainController.GetProcess();
|
||||
Instance.StartInfo.FileName = "bin\\ShadowsocksR.exe";
|
||||
Instance.StartInfo.Arguments = $"-s {server.Hostname} -p {server.Port} -k \"{server.Password}\" -m {server.EncryptMethod}";
|
||||
|
||||
if (!String.IsNullOrEmpty(server.Protocol))
|
||||
{
|
||||
Instance.StartInfo.Arguments += $" -O {server.Protocol}";
|
||||
|
||||
if (!String.IsNullOrEmpty(server.ProtocolParam))
|
||||
{
|
||||
Instance.StartInfo.Arguments += $" -G \"{server.ProtocolParam}\"";
|
||||
}
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(server.OBFS))
|
||||
{
|
||||
Instance.StartInfo.Arguments += $" -o {server.OBFS}";
|
||||
|
||||
if (!String.IsNullOrEmpty(server.OBFSParam))
|
||||
{
|
||||
Instance.StartInfo.Arguments += $" -g \"{server.OBFSParam}\"";
|
||||
}
|
||||
}
|
||||
|
||||
Instance.StartInfo.Arguments += $" -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -u";
|
||||
|
||||
if (mode.BypassChina)
|
||||
{
|
||||
Instance.StartInfo.Arguments += " --acl default.acl";
|
||||
}
|
||||
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
|
||||
State = Models.State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == Models.State.Started)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (State == Models.State.Stopped)
|
||||
{
|
||||
Utils.Logging.Info("SSR 进程启动失败");
|
||||
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Utils.Logging.Info("SSR 进程启动超时");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Instance != null && !Instance.HasExited)
|
||||
{
|
||||
Instance.Kill();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(e.Data))
|
||||
{
|
||||
File.AppendAllText("logging\\shadowsocksr.log", $"{e.Data}\r\n");
|
||||
|
||||
if (State == Models.State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
}
|
||||
else if (e.Data.Contains("listening at"))
|
||||
{
|
||||
State = Models.State.Started;
|
||||
}
|
||||
else if (e.Data.Contains("Invalid config path") || e.Data.Contains("usage"))
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
368
Netch/Controllers/TUNTAPController.cs
Normal file
368
Netch/Controllers/TUNTAPController.cs
Normal file
@@ -0,0 +1,368 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class TUNTAPController
|
||||
{
|
||||
/// <summary>
|
||||
/// 进程实例(tun2socks)
|
||||
/// </summary>
|
||||
public Process Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态
|
||||
/// </summary>
|
||||
public Models.State State = Models.State.Waiting;
|
||||
|
||||
/// <summary>
|
||||
/// 服务器 IP 地址
|
||||
/// </summary>
|
||||
public IPAddress[] ServerAddresses = new IPAddress[0];
|
||||
|
||||
/// <summary>
|
||||
/// 保存传入的规则
|
||||
/// </summary>
|
||||
public Models.Server SavedServer = new Models.Server();
|
||||
public Models.Mode SavedMode = new Models.Mode();
|
||||
|
||||
/// <summary>
|
||||
/// 本地 DNS 服务控制器
|
||||
/// </summary>
|
||||
public DNSController pDNSController = new DNSController();
|
||||
|
||||
/// <summary>
|
||||
/// 配置 TUNTAP 适配器
|
||||
/// </summary>
|
||||
public bool Configure()
|
||||
{
|
||||
// 查询服务器 IP 地址
|
||||
var destination = Dns.GetHostAddressesAsync(SavedServer.Hostname);
|
||||
if (destination.Wait(1000))
|
||||
{
|
||||
if (destination.Result.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ServerAddresses = destination.Result;
|
||||
}
|
||||
|
||||
// 搜索出口
|
||||
return Utils.Configuration.SearchOutbounds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置绕行规则
|
||||
/// </summary>
|
||||
public bool SetupBypass()
|
||||
{
|
||||
// 让服务器 IP 走直连
|
||||
foreach (var address in ServerAddresses)
|
||||
{
|
||||
if (!IPAddress.IsLoopback(address))
|
||||
{
|
||||
NativeMethods.CreateRoute(address.ToString(), 32, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理模式的绕过中国
|
||||
if (SavedMode.BypassChina)
|
||||
{
|
||||
using (var sr = new StringReader(Encoding.UTF8.GetString(Properties.Resources.CNIP)))
|
||||
{
|
||||
string text;
|
||||
|
||||
while ((text = sr.ReadLine()) != null)
|
||||
{
|
||||
var info = text.Split('/');
|
||||
|
||||
NativeMethods.CreateRoute(info[0], int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理全局绕过 IP
|
||||
foreach (var ip in Global.Settings.BypassIPs)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
var address = IPAddress.Parse(info[0]);
|
||||
|
||||
if (!IPAddress.IsLoopback(address))
|
||||
{
|
||||
NativeMethods.CreateRoute(address.ToString(), int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
|
||||
if (SavedMode.Type == 2) // 处理仅规则内走直连
|
||||
{
|
||||
// 创建默认路由
|
||||
if (!NativeMethods.CreateRoute("0.0.0.0", 0, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index, 10))
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
|
||||
foreach (var address in ServerAddresses)
|
||||
{
|
||||
NativeMethods.DeleteRoute(address.ToString(), 32, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var ip in SavedMode.Rule)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
|
||||
if (info.Length == 2)
|
||||
{
|
||||
if (int.TryParse(info[1], out var prefix))
|
||||
{
|
||||
NativeMethods.CreateRoute(info[0], prefix, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (SavedMode.Type == 1) // 处理仅规则内走代理
|
||||
{
|
||||
foreach (var ip in SavedMode.Rule)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
|
||||
if (info.Length == 2)
|
||||
{
|
||||
if (int.TryParse(info[1], out var prefix))
|
||||
{
|
||||
NativeMethods.CreateRoute(info[0], prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 清除绕行规则
|
||||
/// </summary>
|
||||
public bool ClearBypass()
|
||||
{
|
||||
if (SavedMode.Type == 2)
|
||||
{
|
||||
NativeMethods.DeleteRoute("0.0.0.0", 0, Global.Settings.TUNTAP.Gateway.ToString(), Global.TUNTAP.Index, 10);
|
||||
|
||||
foreach (var ip in SavedMode.Rule)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
|
||||
if (info.Length == 2)
|
||||
{
|
||||
if (int.TryParse(info[1], out var prefix))
|
||||
{
|
||||
NativeMethods.DeleteRoute(info[0], prefix, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (SavedMode.Type == 1)
|
||||
{
|
||||
foreach (var ip in SavedMode.Rule)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
|
||||
if (info.Length == 2)
|
||||
{
|
||||
if (int.TryParse(info[1], out var prefix))
|
||||
{
|
||||
NativeMethods.DeleteRoute(info[0], prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var ip in Global.Settings.BypassIPs)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
var address = IPAddress.Parse(info[0]);
|
||||
|
||||
if (!IPAddress.IsLoopback(address))
|
||||
{
|
||||
NativeMethods.DeleteRoute(address.ToString(), int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
|
||||
if (SavedMode.BypassChina)
|
||||
{
|
||||
using (var sr = new StringReader(Encoding.UTF8.GetString(Properties.Resources.CNIP)))
|
||||
{
|
||||
string text;
|
||||
|
||||
while ((text = sr.ReadLine()) != null)
|
||||
{
|
||||
var info = text.Split('/');
|
||||
|
||||
NativeMethods.DeleteRoute(info[0], int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var address in ServerAddresses)
|
||||
{
|
||||
if (!IPAddress.IsLoopback(address))
|
||||
{
|
||||
NativeMethods.DeleteRoute(address.ToString(), 32, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">配置</param>
|
||||
/// <returns>是否成功</returns>
|
||||
public bool Start(Models.Server server, Models.Mode mode)
|
||||
{
|
||||
foreach (var proc in Process.GetProcessesByName("tun2socks"))
|
||||
{
|
||||
try
|
||||
{
|
||||
proc.Kill();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 跳过
|
||||
}
|
||||
}
|
||||
|
||||
if (!File.Exists("bin\\tun2socks.exe"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (File.Exists("logging\\tun2socks.log"))
|
||||
{
|
||||
File.Delete("logging\\tun2socks.log");
|
||||
}
|
||||
|
||||
SavedMode = mode;
|
||||
SavedServer = server;
|
||||
|
||||
if (!Configure())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetupBypass();
|
||||
|
||||
Instance = new Process();
|
||||
Instance.StartInfo.WorkingDirectory = String.Format("{0}\\bin", Directory.GetCurrentDirectory());
|
||||
Instance.StartInfo.FileName = String.Format("{0}\\bin\\tun2socks.exe", Directory.GetCurrentDirectory());
|
||||
|
||||
string dns;
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
dns = "";
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += value;
|
||||
dns += ',';
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
dns = dns.Substring(0, dns.Length - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pDNSController.Start();
|
||||
dns = "127.0.0.1";
|
||||
}
|
||||
|
||||
if (server.Type == "Socks5")
|
||||
{
|
||||
Instance.StartInfo.Arguments = String.Format("-proxyServer {0}:{1} -tunAddr {2} -tunMask {3} -tunGw {4} -tunDns {5} -tunName \"{6}\"", server.Hostname, server.Port, Global.Settings.TUNTAP.Address, Global.Settings.TUNTAP.Netmask, Global.Settings.TUNTAP.Gateway, dns, Global.TUNTAP.Adapter.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance.StartInfo.Arguments = String.Format("-proxyServer 127.0.0.1:{0} -tunAddr {1} -tunMask {2} -tunGw {3} -tunDns {4} -tunName \"{5}\"", Global.Settings.Socks5LocalPort, Global.Settings.TUNTAP.Address, Global.Settings.TUNTAP.Netmask, Global.Settings.TUNTAP.Gateway, dns, Global.TUNTAP.Adapter.Name);
|
||||
}
|
||||
|
||||
Instance.StartInfo.CreateNoWindow = true;
|
||||
Instance.StartInfo.RedirectStandardError = true;
|
||||
Instance.StartInfo.RedirectStandardInput = true;
|
||||
Instance.StartInfo.RedirectStandardOutput = true;
|
||||
Instance.StartInfo.UseShellExecute = false;
|
||||
Instance.EnableRaisingEvents = true;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
|
||||
State = Models.State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginErrorReadLine();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.PriorityClass = ProcessPriorityClass.RealTime;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == Models.State.Started)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (State == Models.State.Stopped)
|
||||
{
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Instance != null && !Instance.HasExited)
|
||||
{
|
||||
Instance.Kill();
|
||||
}
|
||||
|
||||
pDNSController.Stop();
|
||||
ClearBypass();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(e.Data))
|
||||
{
|
||||
File.AppendAllText("logging\\tun2socks.log", String.Format("{0}\r\n", e.Data.Trim()));
|
||||
|
||||
if (State == Models.State.Starting)
|
||||
{
|
||||
if (e.Data.Contains("Running"))
|
||||
{
|
||||
State = Models.State.Started;
|
||||
}
|
||||
else if (e.Data.Contains("failed") || e.Data.Contains("invalid vconfig file"))
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
Netch/Controllers/UpdateChecker.cs
Normal file
87
Netch/Controllers/UpdateChecker.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using Netch.Models.GitHubRelease;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class UpdateChecker
|
||||
{
|
||||
private const string DefaultUserAgent = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36";
|
||||
|
||||
private const int DefaultGetTimeout = 30000;
|
||||
|
||||
private const string Owner = @"NetchX";
|
||||
private const string Repo = @"Netch";
|
||||
|
||||
public string LatestVersionNumber;
|
||||
public string LatestVersionUrl;
|
||||
|
||||
public event EventHandler NewVersionFound;
|
||||
public event EventHandler NewVersionFoundFailed;
|
||||
public event EventHandler NewVersionNotFound;
|
||||
|
||||
public const string Name = @"Netch";
|
||||
public const string Copyright = @"Copyright © 2019";
|
||||
public const string Version = @"1.3.4";
|
||||
|
||||
public async void Check(bool notifyNoFound, bool isPreRelease)
|
||||
{
|
||||
try
|
||||
{
|
||||
var updater = new GitHubRelease(Owner, Repo);
|
||||
var url = updater.AllReleaseUrl;
|
||||
|
||||
var json = await GetAsync(url, true);
|
||||
|
||||
var releases = JsonConvert.DeserializeObject<List<Release>>(json);
|
||||
var latestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
|
||||
if (VersionUtil.CompareVersion(latestRelease.tag_name, Version) > 0)
|
||||
{
|
||||
LatestVersionNumber = latestRelease.tag_name;
|
||||
LatestVersionUrl = latestRelease.html_url;
|
||||
NewVersionFound?.Invoke(this, new EventArgs());
|
||||
}
|
||||
else
|
||||
{
|
||||
LatestVersionNumber = latestRelease.tag_name;
|
||||
if (notifyNoFound)
|
||||
{
|
||||
NewVersionNotFound?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e.Message);
|
||||
if (notifyNoFound)
|
||||
{
|
||||
NewVersionFoundFailed?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> GetAsync(string url, bool useProxy, string userAgent = @"", double timeout = DefaultGetTimeout)
|
||||
{
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
UseProxy = useProxy
|
||||
};
|
||||
var httpClient = new HttpClient(httpClientHandler)
|
||||
{
|
||||
Timeout = TimeSpan.FromMilliseconds(timeout)
|
||||
};
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.Add(@"User-Agent", string.IsNullOrWhiteSpace(userAgent) ? DefaultUserAgent : userAgent);
|
||||
|
||||
var response = await httpClient.SendAsync(request);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
var resultStr = await response.Content.ReadAsStringAsync();
|
||||
return resultStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
250
Netch/Controllers/VMessController.cs
Normal file
250
Netch/Controllers/VMessController.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class VMessController
|
||||
{
|
||||
/// <summary>
|
||||
/// 进程实例
|
||||
/// </summary>
|
||||
public Process Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态
|
||||
/// </summary>
|
||||
public Models.State State = Models.State.Waiting;
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public bool Start(Models.Server server, Models.Mode mode)
|
||||
{
|
||||
if (!File.Exists("bin\\v2ray.exe") || !File.Exists("bin\\v2ctl.exe"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
File.WriteAllText("data\\last.json", Newtonsoft.Json.JsonConvert.SerializeObject(new Models.Information.VMess.Config()
|
||||
{
|
||||
inbounds = new List<Models.Information.VMess.Inbounds>()
|
||||
{
|
||||
new Models.Information.VMess.Inbounds()
|
||||
{
|
||||
settings = new Models.Information.VMess.InboundSettings(),
|
||||
port = Global.Settings.Socks5LocalPort,
|
||||
listen = Global.Settings.LocalAddress
|
||||
}
|
||||
},
|
||||
outbounds = new List<Models.Information.VMess.Outbounds>()
|
||||
{
|
||||
new Models.Information.VMess.Outbounds()
|
||||
{
|
||||
settings = new Models.Information.VMess.OutboundSettings()
|
||||
{
|
||||
vnext = new List<Models.Information.VMess.VNext>()
|
||||
{
|
||||
new Models.Information.VMess.VNext()
|
||||
{
|
||||
address = server.Hostname,
|
||||
port = server.Port,
|
||||
users = new List<Models.Information.VMess.User>
|
||||
{
|
||||
new Models.Information.VMess.User()
|
||||
{
|
||||
id = server.UserID,
|
||||
alterId = server.AlterID,
|
||||
security = server.EncryptMethod
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = new Models.Information.VMess.StreamSettings()
|
||||
{
|
||||
network = server.TransferProtocol,
|
||||
security = server.TLSSecure == true ? "tls" : "",
|
||||
wsSettings = server.TransferProtocol == "ws" ? new Models.Information.VMess.WebSocketSettings()
|
||||
{
|
||||
path = server.Path == "" ? "/" : server.Path,
|
||||
headers = new Models.Information.VMess.WSHeaders()
|
||||
{
|
||||
Host = server.Host == "" ? server.Hostname : server.Host
|
||||
}
|
||||
} : null,
|
||||
tcpSettings = server.FakeType == "http" ? new Models.Information.VMess.TCPSettings()
|
||||
{
|
||||
header = new Models.Information.VMess.TCPHeaders()
|
||||
{
|
||||
type = server.FakeType,
|
||||
request = new Models.Information.VMess.TCPRequest()
|
||||
{
|
||||
path = server.Path == "" ? "/" : server.Path,
|
||||
headers = new Models.Information.VMess.TCPRequestHeaders()
|
||||
{
|
||||
Host = server.Host == "" ? server.Hostname : server.Host
|
||||
}
|
||||
}
|
||||
}
|
||||
} : null,
|
||||
kcpSettings = server.TransferProtocol == "kcp" ? new Models.Information.VMess.KCPSettings()
|
||||
{
|
||||
header = new Models.Information.VMess.TCPHeaders()
|
||||
{
|
||||
type = server.FakeType
|
||||
}
|
||||
} : null,
|
||||
quicSettings = server.TransferProtocol == "quic" ? new Models.Information.VMess.QUICSettings()
|
||||
{
|
||||
security = server.QUICSecure,
|
||||
key = server.QUICSecret,
|
||||
header = new Models.Information.VMess.TCPHeaders()
|
||||
{
|
||||
type = server.FakeType
|
||||
}
|
||||
} : null,
|
||||
httpSettings = server.TransferProtocol == "h2" ? new Models.Information.VMess.HTTPSettings()
|
||||
{
|
||||
host = server.Host == "" ? server.Hostname : server.Host,
|
||||
path = server.Path == "" ? "/" : server.Path
|
||||
} : null,
|
||||
tlsSettings = new Models.Information.VMess.TLSSettings()
|
||||
},
|
||||
mux = new Models.Information.VMess.OutboundMux()
|
||||
},
|
||||
new Models.Information.VMess.Outbounds()
|
||||
{
|
||||
tag = "direct",
|
||||
protocol = "freedom",
|
||||
settings = null,
|
||||
streamSettings = null,
|
||||
mux = null
|
||||
}
|
||||
},
|
||||
routing = new Models.Information.VMess.Routing()
|
||||
{
|
||||
rules = new List<Models.Information.VMess.RoutingRules>()
|
||||
{
|
||||
mode.BypassChina == true ? new Models.Information.VMess.RoutingRules()
|
||||
{
|
||||
type = "field",
|
||||
ip = new List<string>
|
||||
{
|
||||
"geoip:cn",
|
||||
"geoip:private"
|
||||
|
||||
},
|
||||
domain = new List<string>
|
||||
{
|
||||
"geosite:cn"
|
||||
},
|
||||
outboundTag = "direct"
|
||||
} : new Models.Information.VMess.RoutingRules()
|
||||
{
|
||||
type = "field",
|
||||
ip = new List<string>
|
||||
{
|
||||
"geoip:private"
|
||||
},
|
||||
outboundTag = "direct"
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// 清理上一次的日志文件,防止淤积占用磁盘空间
|
||||
if (Directory.Exists("logging"))
|
||||
{
|
||||
if (File.Exists("logging\\v2ray.log"))
|
||||
{
|
||||
File.Delete("logging\\v2ray.log");
|
||||
}
|
||||
}
|
||||
|
||||
Instance = MainController.GetProcess();
|
||||
Instance.StartInfo.FileName = "bin\\v2ray.exe";
|
||||
Instance.StartInfo.Arguments = "-config ..\\data\\last.json";
|
||||
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
|
||||
State = Models.State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == Models.State.Started)
|
||||
{
|
||||
if (File.Exists("data\\last.json"))
|
||||
{
|
||||
File.Delete("data\\last.json");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (State == Models.State.Stopped)
|
||||
{
|
||||
Utils.Logging.Info("V2Ray 进程启动失败");
|
||||
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Utils.Logging.Info("V2Ray 进程启动超时");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Instance != null && !Instance.HasExited)
|
||||
{
|
||||
Instance.Kill();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Logging.Info(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(e.Data))
|
||||
{
|
||||
File.AppendAllText("logging\\v2ray.log", $"{e.Data}\r\n");
|
||||
|
||||
if (State == Models.State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
}
|
||||
else if (e.Data.Contains("started"))
|
||||
{
|
||||
State = Models.State.Started;
|
||||
}
|
||||
else if (e.Data.Contains("config file not readable") || e.Data.Contains("failed to"))
|
||||
{
|
||||
State = Models.State.Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user