mirror of
https://github.com/netchx/netch.git
synced 2026-03-14 17:43:18 +08:00
381 lines
12 KiB
C#
381 lines
12 KiB
C#
using System;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Net;
|
||
using System.Text;
|
||
using System.Threading;
|
||
|
||
using Netch.Utils;
|
||
|
||
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>
|
||
/// 配置 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) // 处理仅规则内走直连
|
||
{
|
||
// 将 TUN/TAP 网卡权重放到最高
|
||
var instance = new Process
|
||
{
|
||
StartInfo =
|
||
{
|
||
FileName = "netsh",
|
||
Arguments = string.Format("interface ip set interface {0} metric=0", Global.TUNTAP.Index),
|
||
WindowStyle = ProcessWindowStyle.Hidden,
|
||
UseShellExecute = true,
|
||
CreateNoWindow = true
|
||
}
|
||
};
|
||
instance.Start();
|
||
|
||
// 创建默认路由
|
||
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, 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());
|
||
var adapterName = TUNTAP.GetName(Global.TUNTAP.ComponentID);
|
||
|
||
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
|
||
{
|
||
dns = "1.1.1.1,1.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, adapterName);
|
||
}
|
||
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, adapterName);
|
||
}
|
||
|
||
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 (var 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();
|
||
//修复点击停止按钮后再启动,DNS服务没监听的BUG
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|