mirror of
https://github.com/snltty/linker.git
synced 2025-09-27 05:25:57 +08:00
傻瓜共享版
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,5 +5,6 @@ obj
|
|||||||
node_modules
|
node_modules
|
||||||
/public/*
|
/public/*
|
||||||
/x64/*
|
/x64/*
|
||||||
|
linker.share.win
|
||||||
|
|
||||||
TestResults
|
TestResults
|
@@ -26,6 +26,7 @@ namespace linker.ics
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
publicCon.EnableSharing(tagSHARINGCONNECTIONTYPE.ICSSHARINGTYPE_PUBLIC);
|
publicCon.EnableSharing(tagSHARINGCONNECTIONTYPE.ICSSHARINGTYPE_PUBLIC);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
BIN
src/linker.install.win/dist/linker简易安装程序.exe
vendored
BIN
src/linker.install.win/dist/linker简易安装程序.exe
vendored
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 48 KiB |
Binary file not shown.
Before Width: | Height: | Size: 48 KiB |
@@ -1,199 +0,0 @@
|
|||||||
import ctypes
|
|
||||||
import os
|
|
||||||
import zipfile
|
|
||||||
import requests
|
|
||||||
import tkinter as tk
|
|
||||||
from tkinter import filedialog, messagebox, ttk
|
|
||||||
from PIL import Image, ImageTk
|
|
||||||
import winshell
|
|
||||||
from win32com.client import Dispatch
|
|
||||||
import threading
|
|
||||||
import webbrowser
|
|
||||||
import pythoncom
|
|
||||||
from io import BytesIO
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
class InstallerApp:
|
|
||||||
def __init__(self, root):
|
|
||||||
self.root = root
|
|
||||||
self.root.title("Linker 安装程序")
|
|
||||||
self.root.geometry("400x400")
|
|
||||||
self.root.resizable(False, False) # 禁止调整窗口大小
|
|
||||||
|
|
||||||
# 检查管理员权限
|
|
||||||
if not self.is_admin():
|
|
||||||
messagebox.showerror("权限不足", "当前用户没有管理员权限,请以管理员身份运行程序。")
|
|
||||||
self.root.quit()
|
|
||||||
|
|
||||||
# 默认安装路径
|
|
||||||
self.default_install_dir = r"C:\Program Files (x86)\linker"
|
|
||||||
self.install_dir = self.default_install_dir
|
|
||||||
|
|
||||||
# 设置背景色
|
|
||||||
self.root.config(bg="#f4f4f4")
|
|
||||||
|
|
||||||
# 加载 Logo
|
|
||||||
self.load_logo()
|
|
||||||
|
|
||||||
# 快速安装按钮
|
|
||||||
self.btn_install = tk.Button(self.root, text="快速安装", command=self.start_installation,
|
|
||||||
bg="#007BFF", fg="white", font=("Arial", 14), width=20, height=2)
|
|
||||||
self.btn_install.pack(pady=10)
|
|
||||||
|
|
||||||
# 安装目录选择按钮
|
|
||||||
self.btn_select_path = tk.Button(self.root, text="选择安装目录", command=self.select_install_path,
|
|
||||||
bg="#007BFF", fg="white", font=("Arial", 10), width=20)
|
|
||||||
self.btn_select_path.pack(pady=10)
|
|
||||||
|
|
||||||
# 协议复选框
|
|
||||||
self.agree_var = tk.IntVar()
|
|
||||||
self.agree_checkbox = tk.Checkbutton(self.root, text="我已阅读并同意", variable=self.agree_var,
|
|
||||||
bg="#f4f4f4", font=("Arial", 10))
|
|
||||||
self.agree_checkbox.pack(pady=10)
|
|
||||||
|
|
||||||
# 用户协议链接
|
|
||||||
self.terms_label = tk.Label(self.root, text="《用户许可协议》", fg="blue", cursor="hand2", bg="#f4f4f4")
|
|
||||||
self.terms_label.pack(pady=5)
|
|
||||||
self.terms_label.bind("<Button-1>", self.open_terms) # 点击跳转到协议网页
|
|
||||||
|
|
||||||
# 安装进度条
|
|
||||||
self.progress = ttk.Progressbar(self.root, orient="horizontal", length=300, mode="determinate")
|
|
||||||
self.progress.pack(pady=10)
|
|
||||||
|
|
||||||
# 状态标签
|
|
||||||
self.label_status = tk.Label(self.root, text="", bg="#f4f4f4", font=("Arial", 10))
|
|
||||||
self.label_status.pack(pady=5)
|
|
||||||
|
|
||||||
def is_admin(self):
|
|
||||||
"""检查当前程序是否以管理员身份运行"""
|
|
||||||
try:
|
|
||||||
return ctypes.windll.shell32.IsUserAnAdmin() != 0
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def load_logo(self):
|
|
||||||
"""加载 logo(本地优先,失败后从网络加载)"""
|
|
||||||
if getattr(sys, 'frozen', False):
|
|
||||||
base_path = sys._MEIPASS # PyInstaller 运行环境
|
|
||||||
else:
|
|
||||||
base_path = os.path.dirname(__file__) # 普通 Python 运行环境
|
|
||||||
|
|
||||||
local_logo_path = os.path.join(base_path, 'img', 'logo.png')
|
|
||||||
remote_logo_url = "https://linker-doc.snltty.com/img/logo.png"
|
|
||||||
|
|
||||||
try:
|
|
||||||
if os.path.exists(local_logo_path):
|
|
||||||
self.logo = Image.open(local_logo_path)
|
|
||||||
else:
|
|
||||||
response = requests.get(remote_logo_url, timeout=10)
|
|
||||||
response.raise_for_status()
|
|
||||||
img_data = BytesIO(response.content)
|
|
||||||
self.logo = Image.open(img_data)
|
|
||||||
|
|
||||||
# 调整大小
|
|
||||||
self.logo = self.logo.resize((100, 100), Image.Resampling.LANCZOS)
|
|
||||||
self.logo = ImageTk.PhotoImage(self.logo)
|
|
||||||
self.logo_label = tk.Label(self.root, image=self.logo, bg="#f4f4f4")
|
|
||||||
self.logo_label.pack(pady=20) # 确保 Logo 正确显示
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"加载 logo 失败: {e}")
|
|
||||||
self.logo = None
|
|
||||||
|
|
||||||
def select_install_path(self):
|
|
||||||
"""选择安装路径"""
|
|
||||||
self.install_dir = filedialog.askdirectory(initialdir=self.default_install_dir, title="选择安装目录")
|
|
||||||
if not self.install_dir:
|
|
||||||
self.install_dir = self.default_install_dir
|
|
||||||
|
|
||||||
def open_terms(self, event):
|
|
||||||
"""打开用户许可协议"""
|
|
||||||
webbrowser.open("https://linker-doc.snltty.com/docs/1%E3%80%81%E9%A6%96%E9%A1%B5")
|
|
||||||
|
|
||||||
def start_installation(self):
|
|
||||||
"""开始安装"""
|
|
||||||
if not self.agree_var.get():
|
|
||||||
messagebox.showerror("错误", "请先勾选‘我已阅读并同意《用户许可协议》’")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.progress["value"] = 0
|
|
||||||
self.btn_install.config(state=tk.DISABLED)
|
|
||||||
|
|
||||||
# 启动安装
|
|
||||||
threading.Thread(target=self.install_process, daemon=True).start()
|
|
||||||
|
|
||||||
def install_process(self):
|
|
||||||
"""执行安装流程"""
|
|
||||||
zip_path = os.path.join(self.install_dir, "linker.zip")
|
|
||||||
|
|
||||||
if not os.path.exists(self.install_dir):
|
|
||||||
os.makedirs(self.install_dir)
|
|
||||||
|
|
||||||
self.update_status("正在下载 ZIP 文件...")
|
|
||||||
self.download_zip("https://static.qbcode.cn/downloads/linker/v1.6.9/linker-win-x64.zip", zip_path)
|
|
||||||
self.progress["value"] = 30
|
|
||||||
|
|
||||||
self.update_status("正在解压文件...")
|
|
||||||
extract_folder = self.extract_zip(zip_path, self.install_dir)
|
|
||||||
self.progress["value"] = 70
|
|
||||||
|
|
||||||
os.remove(zip_path)
|
|
||||||
|
|
||||||
linker_exe = self.find_linker_exe(extract_folder)
|
|
||||||
if linker_exe:
|
|
||||||
self.update_status("创建快捷方式...")
|
|
||||||
pythoncom.CoInitialize()
|
|
||||||
self.create_shortcut(linker_exe, "linker")
|
|
||||||
self.progress["value"] = 100
|
|
||||||
messagebox.showinfo("完成", "安装完成!")
|
|
||||||
else:
|
|
||||||
messagebox.showerror("错误", "linker.tray.win.exe 未找到,安装失败!")
|
|
||||||
|
|
||||||
self.btn_install.config(state=tk.NORMAL)
|
|
||||||
|
|
||||||
def download_zip(self, url, save_path):
|
|
||||||
"""下载 ZIP 文件"""
|
|
||||||
try:
|
|
||||||
response = requests.get(url, stream=True)
|
|
||||||
response.raise_for_status()
|
|
||||||
with open(save_path, 'wb') as file:
|
|
||||||
for chunk in response.iter_content(1024):
|
|
||||||
file.write(chunk)
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
messagebox.showerror("下载错误", f"下载失败: {e}")
|
|
||||||
self.btn_install.config(state=tk.NORMAL)
|
|
||||||
|
|
||||||
def extract_zip(self, zip_path, extract_to):
|
|
||||||
"""解压 ZIP 文件"""
|
|
||||||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
|
||||||
zip_ref.extractall(extract_to)
|
|
||||||
return extract_to
|
|
||||||
|
|
||||||
def find_linker_exe(self, extract_folder):
|
|
||||||
"""寻找 linker.tray.win.exe"""
|
|
||||||
for root, dirs, files in os.walk(extract_folder):
|
|
||||||
if "linker.tray.win.exe" in files:
|
|
||||||
return os.path.join(root, "linker.tray.win.exe")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def create_shortcut(self, target_path, shortcut_name):
|
|
||||||
"""创建快捷方式"""
|
|
||||||
desktop = winshell.desktop()
|
|
||||||
shortcut_path = os.path.join(desktop, f"{shortcut_name}.lnk")
|
|
||||||
shell = Dispatch('WScript.Shell')
|
|
||||||
shortcut = shell.CreateShortcut(shortcut_path)
|
|
||||||
shortcut.TargetPath = target_path
|
|
||||||
shortcut.WorkingDirectory = os.path.dirname(target_path)
|
|
||||||
shortcut.Save()
|
|
||||||
|
|
||||||
def update_status(self, message):
|
|
||||||
"""更新状态标签"""
|
|
||||||
self.label_status.config(text=message)
|
|
||||||
self.root.update_idletasks()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
root = tk.Tk()
|
|
||||||
app = InstallerApp(root)
|
|
||||||
root.mainloop()
|
|
@@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
added_files = [
|
|
||||||
('img/logo.png','img/')
|
|
||||||
]
|
|
||||||
|
|
||||||
# 配置打包参数
|
|
||||||
a = Analysis(
|
|
||||||
['main1.0.py'],
|
|
||||||
pathex=['D:/py/linker/.venv/Lib/site-packages'],
|
|
||||||
binaries=[],
|
|
||||||
datas=added_files ,
|
|
||||||
hiddenimports=['requests', 'pywin32', 'winshell', 'tkinterweb'],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[],
|
|
||||||
noarchive=False,
|
|
||||||
optimize=0,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure)
|
|
||||||
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='linker简易安装程序',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=False,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon='D:\py\linker\src\linker.install.win\img\linker.ico'
|
|
||||||
)
|
|
||||||
|
|
Binary file not shown.
@@ -1,4 +0,0 @@
|
|||||||
加载所需要的模块
|
|
||||||
pip install -r requirements.txt
|
|
||||||
打包
|
|
||||||
pyinstaller .\main1.0.spec
|
|
@@ -189,7 +189,7 @@ namespace linker.messenger.entry
|
|||||||
if ((modules & ExcludeModule.Socks5) != ExcludeModule.Socks5)
|
if ((modules & ExcludeModule.Socks5) != ExcludeModule.Socks5)
|
||||||
serviceProvider.UseSocks5Client();
|
serviceProvider.UseSocks5Client();
|
||||||
if ((modules & ExcludeModule.Tuntap) != ExcludeModule.Tuntap)
|
if ((modules & ExcludeModule.Tuntap) != ExcludeModule.Tuntap)
|
||||||
serviceProvider.UseTuntapClient();
|
serviceProvider.UseTuntapClient(configDic);
|
||||||
if ((modules & ExcludeModule.Updater) != ExcludeModule.Updater)
|
if ((modules & ExcludeModule.Updater) != ExcludeModule.Updater)
|
||||||
serviceProvider.UseUpdaterClient();
|
serviceProvider.UseUpdaterClient();
|
||||||
serviceProvider.UseExRoute().UseAccessClient().UseDecenterClient().UsePcpClient().UseRelayClient().UseSyncClient().UseTunnelClient().UseFlowClient();
|
serviceProvider.UseExRoute().UseAccessClient().UseDecenterClient().UsePcpClient().UseRelayClient().UseSyncClient().UseTunnelClient().UseFlowClient();
|
||||||
|
@@ -73,7 +73,7 @@ namespace linker.messenger.flow.messenger
|
|||||||
sForwardFlow.Update();
|
sForwardFlow.Update();
|
||||||
SForwardFlowRequestInfo info = serializer.Deserialize<SForwardFlowRequestInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
SForwardFlowRequestInfo info = serializer.Deserialize<SForwardFlowRequestInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
|
|
||||||
if (sForwardServerStore.SecretKey == info.SecretKey)
|
if (sForwardServerStore.ValidateSecretKey(info.SecretKey))
|
||||||
{
|
{
|
||||||
info.GroupId = string.Empty;
|
info.GroupId = string.Empty;
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ namespace linker.messenger.flow.messenger
|
|||||||
{
|
{
|
||||||
relayFlow.Update();
|
relayFlow.Update();
|
||||||
RelayFlowRequestInfo info = serializer.Deserialize<RelayFlowRequestInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
RelayFlowRequestInfo info = serializer.Deserialize<RelayFlowRequestInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
if (relayServerStore.SecretKey == info.SecretKey)
|
if (relayServerStore.ValidateSecretKey(info.SecretKey))
|
||||||
{
|
{
|
||||||
info.GroupId = string.Empty;
|
info.GroupId = string.Empty;
|
||||||
}
|
}
|
||||||
@@ -152,7 +152,7 @@ namespace linker.messenger.flow.messenger
|
|||||||
|
|
||||||
private DateTime start = DateTime.Now;
|
private DateTime start = DateTime.Now;
|
||||||
|
|
||||||
public FlowClientMessenger( MessengerFlow messengerFlow,ISerializer serializer)
|
public FlowClientMessenger(MessengerFlow messengerFlow, ISerializer serializer)
|
||||||
{
|
{
|
||||||
this.messengerFlow = messengerFlow;
|
this.messengerFlow = messengerFlow;
|
||||||
this.serializer = serializer;
|
this.serializer = serializer;
|
||||||
|
@@ -292,7 +292,7 @@ namespace linker.messenger.relay.messenger
|
|||||||
public async Task UpdateNodeForward(IConnection connection)
|
public async Task UpdateNodeForward(IConnection connection)
|
||||||
{
|
{
|
||||||
RelayServerNodeUpdateWrapInfo info = serializer.Deserialize<RelayServerNodeUpdateWrapInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
RelayServerNodeUpdateWrapInfo info = serializer.Deserialize<RelayServerNodeUpdateWrapInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
if (info.SecretKey == relayServerStore.SecretKey)
|
if (relayServerStore.ValidateSecretKey(info.SecretKey))
|
||||||
{
|
{
|
||||||
await relayServerTransfer.UpdateNodeReport(info.Info).ConfigureAwait(false);
|
await relayServerTransfer.UpdateNodeReport(info.Info).ConfigureAwait(false);
|
||||||
connection.Write(Helper.TrueArray);
|
connection.Write(Helper.TrueArray);
|
||||||
@@ -337,7 +337,7 @@ namespace linker.messenger.relay.messenger
|
|||||||
public void AccessCdkey(IConnection connection)
|
public void AccessCdkey(IConnection connection)
|
||||||
{
|
{
|
||||||
string secretKey = serializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
|
string secretKey = serializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
connection.Write(relayServerStore.SecretKey == secretKey ? Helper.TrueArray : Helper.FalseArray);
|
connection.Write(relayServerStore.ValidateSecretKey(secretKey) ? Helper.TrueArray : Helper.FalseArray);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加CDKEY
|
/// 添加CDKEY
|
||||||
@@ -352,7 +352,7 @@ namespace linker.messenger.relay.messenger
|
|||||||
connection.Write(Helper.FalseArray);
|
connection.Write(Helper.FalseArray);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (relayServerStore.SecretKey != info.SecretKey)
|
if (relayServerStore.ValidateSecretKey(info.SecretKey))
|
||||||
{
|
{
|
||||||
connection.Write(Helper.FalseArray);
|
connection.Write(Helper.FalseArray);
|
||||||
return;
|
return;
|
||||||
@@ -376,7 +376,7 @@ namespace linker.messenger.relay.messenger
|
|||||||
connection.Write(Helper.FalseArray);
|
connection.Write(Helper.FalseArray);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (relayServerStore.SecretKey == info.SecretKey)
|
if (relayServerStore.ValidateSecretKey(info.SecretKey))
|
||||||
{
|
{
|
||||||
await relayServerCdkeyStore.Del(info.Id).ConfigureAwait(false);
|
await relayServerCdkeyStore.Del(info.Id).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -401,7 +401,7 @@ namespace linker.messenger.relay.messenger
|
|||||||
connection.Write(serializer.Serialize(new RelayServerCdkeyPageResultInfo { }));
|
connection.Write(serializer.Serialize(new RelayServerCdkeyPageResultInfo { }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (relayServerStore.SecretKey != info.SecretKey && string.IsNullOrWhiteSpace(info.UserId))
|
if (relayServerStore.ValidateSecretKey(info.SecretKey) && string.IsNullOrWhiteSpace(info.UserId))
|
||||||
{
|
{
|
||||||
connection.Write(serializer.Serialize(new RelayServerCdkeyPageResultInfo { }));
|
connection.Write(serializer.Serialize(new RelayServerCdkeyPageResultInfo { }));
|
||||||
return;
|
return;
|
||||||
@@ -427,7 +427,7 @@ namespace linker.messenger.relay.messenger
|
|||||||
connection.Write(serializer.Serialize(new RelayServerCdkeyTestResultInfo { }));
|
connection.Write(serializer.Serialize(new RelayServerCdkeyTestResultInfo { }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (relayServerStore.SecretKey != info.SecretKey)
|
if (relayServerStore.ValidateSecretKey(info.SecretKey))
|
||||||
{
|
{
|
||||||
connection.Write(serializer.Serialize(new RelayServerCdkeyTestResultInfo { }));
|
connection.Write(serializer.Serialize(new RelayServerCdkeyTestResultInfo { }));
|
||||||
return;
|
return;
|
||||||
|
@@ -2,10 +2,7 @@
|
|||||||
{
|
{
|
||||||
public interface IRelayServerStore
|
public interface IRelayServerStore
|
||||||
{
|
{
|
||||||
/// <summary>
|
public bool ValidateSecretKey(string secretKey);
|
||||||
/// 中继密钥
|
|
||||||
/// </summary>
|
|
||||||
public string SecretKey { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置中继密钥
|
/// 设置中继密钥
|
||||||
|
@@ -15,7 +15,7 @@ namespace linker.messenger.relay.server.validator
|
|||||||
|
|
||||||
public async Task<string> Validate(linker.messenger.relay.client.transport.RelayInfo relayInfo, SignCacheInfo fromMachine, SignCacheInfo toMachine)
|
public async Task<string> Validate(linker.messenger.relay.client.transport.RelayInfo relayInfo, SignCacheInfo fromMachine, SignCacheInfo toMachine)
|
||||||
{
|
{
|
||||||
if (relayInfo.SecretKey != relayServerStore.SecretKey)
|
if (relayServerStore.ValidateSecretKey(relayInfo.SecretKey) == false)
|
||||||
{
|
{
|
||||||
return $"SecretKey validate fail";
|
return $"SecretKey validate fail";
|
||||||
}
|
}
|
||||||
|
@@ -2,10 +2,6 @@
|
|||||||
{
|
{
|
||||||
public interface ISForwardServerStore
|
public interface ISForwardServerStore
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 穿透密钥
|
|
||||||
/// </summary>
|
|
||||||
public string SecretKey { get; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 缓冲区大小
|
/// 缓冲区大小
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -19,6 +15,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int[] TunnelPortRange { get; }
|
public int[] TunnelPortRange { get; }
|
||||||
|
|
||||||
|
public bool ValidateSecretKey(string key);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 穿透密钥
|
/// 穿透密钥
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -17,7 +17,7 @@ namespace linker.messenger.sforward.server.validator
|
|||||||
|
|
||||||
public async Task<string> Validate(SignCacheInfo signCacheInfo, SForwardAddInfo sForwardAddInfo)
|
public async Task<string> Validate(SignCacheInfo signCacheInfo, SForwardAddInfo sForwardAddInfo)
|
||||||
{
|
{
|
||||||
if (sForwardServerStore.SecretKey != sForwardAddInfo.SecretKey)
|
if (sForwardServerStore.ValidateSecretKey(sForwardAddInfo.SecretKey))
|
||||||
{
|
{
|
||||||
return $"sforward secretKey 【{sForwardAddInfo.SecretKey}】 valid fail";
|
return $"sforward secretKey 【{sForwardAddInfo.SecretKey}】 valid fail";
|
||||||
}
|
}
|
||||||
|
@@ -7,12 +7,9 @@ namespace linker.messenger.signin
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISignInServerStore : IStore<SignCacheInfo>
|
public interface ISignInServerStore : IStore<SignCacheInfo>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 信标密钥
|
|
||||||
/// </summary>
|
|
||||||
public string SecretKey { get; }
|
|
||||||
|
|
||||||
public int CleanDays { get; }
|
public int CleanDays { get; }
|
||||||
|
|
||||||
|
public bool ValidateSecretKey(string key);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置信标密钥
|
/// 设置信标密钥
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -51,12 +51,10 @@
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<string> Validate(SignInfo signInfo, SignCacheInfo cache)
|
public async Task<string> Validate(SignInfo signInfo, SignCacheInfo cache)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(signInServerStore.SecretKey) == false)
|
signInfo.Args.TryGetValue("signin-secretkey", out string secretkey);
|
||||||
|
if (signInServerStore.ValidateSecretKey(secretkey) == false)
|
||||||
{
|
{
|
||||||
if (signInfo.Args.TryGetValue("signin-secretkey", out string secretkey) == false || secretkey != signInServerStore.SecretKey)
|
return $"server secretkey validate fail";
|
||||||
{
|
|
||||||
return $"server secretkey validate fail";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await Task.CompletedTask.ConfigureAwait(false);
|
await Task.CompletedTask.ConfigureAwait(false);
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
@@ -4,13 +4,17 @@ namespace linker.messenger.store.file.relay
|
|||||||
{
|
{
|
||||||
public sealed class RelayServerStore : IRelayServerStore
|
public sealed class RelayServerStore : IRelayServerStore
|
||||||
{
|
{
|
||||||
public string SecretKey => config.Data.Server.Relay.SecretKey;
|
|
||||||
private readonly FileConfig config;
|
private readonly FileConfig config;
|
||||||
public RelayServerStore(FileConfig config)
|
public RelayServerStore(FileConfig config)
|
||||||
{
|
{
|
||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ValidateSecretKey(string secretKey)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(config.Data.Server.Relay.SecretKey) || config.Data.Server.Relay.SecretKey == secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetSecretKey(string secretKey)
|
public void SetSecretKey(string secretKey)
|
||||||
{
|
{
|
||||||
config.Data.Server.Relay.SecretKey = secretKey;
|
config.Data.Server.Relay.SecretKey = secretKey;
|
||||||
@@ -21,5 +25,7 @@ namespace linker.messenger.store.file.relay
|
|||||||
config.Data.Update();
|
config.Data.Update();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,8 +4,6 @@ namespace linker.messenger.store.file.sforward
|
|||||||
{
|
{
|
||||||
public sealed class SForwardServerStore : ISForwardServerStore
|
public sealed class SForwardServerStore : ISForwardServerStore
|
||||||
{
|
{
|
||||||
public string SecretKey => fileConfig.Data.Server.SForward.SecretKey;
|
|
||||||
|
|
||||||
public byte BufferSize => fileConfig.Data.Server.SForward.BufferSize;
|
public byte BufferSize => fileConfig.Data.Server.SForward.BufferSize;
|
||||||
|
|
||||||
public int WebPort => fileConfig.Data.Server.SForward.WebPort;
|
public int WebPort => fileConfig.Data.Server.SForward.WebPort;
|
||||||
@@ -18,6 +16,10 @@ namespace linker.messenger.store.file.sforward
|
|||||||
this.fileConfig = fileConfig;
|
this.fileConfig = fileConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ValidateSecretKey(string key)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(fileConfig.Data.Server.SForward.SecretKey) || fileConfig.Data.Server.SForward.SecretKey == key;
|
||||||
|
}
|
||||||
public bool SetSecretKey(string key)
|
public bool SetSecretKey(string key)
|
||||||
{
|
{
|
||||||
fileConfig.Data.Server.SForward.SecretKey = key;
|
fileConfig.Data.Server.SForward.SecretKey = key;
|
||||||
|
@@ -5,7 +5,6 @@ namespace linker.messenger.store.file.signIn
|
|||||||
{
|
{
|
||||||
public sealed class SignInServerStore : ISignInServerStore
|
public sealed class SignInServerStore : ISignInServerStore
|
||||||
{
|
{
|
||||||
public string SecretKey => fileConfig.Data.Server.SignIn.SecretKey;
|
|
||||||
public int CleanDays => fileConfig.Data.Server.SignIn.CleanDays;
|
public int CleanDays => fileConfig.Data.Server.SignIn.CleanDays;
|
||||||
|
|
||||||
private readonly Storefactory dBfactory;
|
private readonly Storefactory dBfactory;
|
||||||
@@ -17,6 +16,11 @@ namespace linker.messenger.store.file.signIn
|
|||||||
liteCollection = dBfactory.GetCollection<SignCacheInfo>("signs");
|
liteCollection = dBfactory.GetCollection<SignCacheInfo>("signs");
|
||||||
this.fileConfig = fileConfig;
|
this.fileConfig = fileConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ValidateSecretKey(string key)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(fileConfig.Data.Server.SignIn.SecretKey) || fileConfig.Data.Server.SignIn.SecretKey == key;
|
||||||
|
}
|
||||||
public void SetSecretKey(string secretKey)
|
public void SetSecretKey(string secretKey)
|
||||||
{
|
{
|
||||||
fileConfig.Data.Server.SignIn.SecretKey = secretKey;
|
fileConfig.Data.Server.SignIn.SecretKey = secretKey;
|
||||||
|
@@ -5,14 +5,16 @@ namespace linker.messenger.store.file.updater
|
|||||||
{
|
{
|
||||||
public sealed class UpdaterServerStore : IUpdaterServerStore
|
public sealed class UpdaterServerStore : IUpdaterServerStore
|
||||||
{
|
{
|
||||||
public string SecretKey => fileConfig.Data.Server.Updater.SecretKey;
|
|
||||||
|
|
||||||
private readonly FileConfig fileConfig;
|
private readonly FileConfig fileConfig;
|
||||||
public UpdaterServerStore(FileConfig fileConfig)
|
public UpdaterServerStore(FileConfig fileConfig)
|
||||||
{
|
{
|
||||||
this.fileConfig = fileConfig;
|
this.fileConfig = fileConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ValidateSecretKey(string key)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(fileConfig.Data.Server.Updater.SecretKey) || fileConfig.Data.Server.Updater.SecretKey == key;
|
||||||
|
}
|
||||||
public void SetSecretKey(string key)
|
public void SetSecretKey(string key)
|
||||||
{
|
{
|
||||||
fileConfig.Data.Server.Updater.SecretKey = key;
|
fileConfig.Data.Server.Updater.SecretKey = key;
|
||||||
|
@@ -1,11 +1,17 @@
|
|||||||
using linker.messenger.api;
|
using linker.libs;
|
||||||
|
using linker.libs.extends;
|
||||||
|
using linker.messenger.api;
|
||||||
using linker.messenger.decenter;
|
using linker.messenger.decenter;
|
||||||
using linker.messenger.exroute;
|
using linker.messenger.exroute;
|
||||||
|
using linker.messenger.signin;
|
||||||
using linker.messenger.tunnel;
|
using linker.messenger.tunnel;
|
||||||
using linker.messenger.tuntap.lease;
|
using linker.messenger.tuntap.lease;
|
||||||
using linker.messenger.tuntap.messenger;
|
using linker.messenger.tuntap.messenger;
|
||||||
using linker.tun;
|
using linker.tun;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
namespace linker.messenger.tuntap
|
namespace linker.messenger.tuntap
|
||||||
{
|
{
|
||||||
public static class Entry
|
public static class Entry
|
||||||
@@ -35,7 +41,7 @@ namespace linker.messenger.tuntap
|
|||||||
|
|
||||||
return serviceCollection;
|
return serviceCollection;
|
||||||
}
|
}
|
||||||
public static ServiceProvider UseTuntapClient(this ServiceProvider serviceProvider)
|
public static ServiceProvider UseTuntapClient(this ServiceProvider serviceProvider, Dictionary<string, string> configDic)
|
||||||
{
|
{
|
||||||
TuntapProxy tuntapProxy = serviceProvider.GetService<TuntapProxy>();
|
TuntapProxy tuntapProxy = serviceProvider.GetService<TuntapProxy>();
|
||||||
TuntapTransfer tuntapTransfer = serviceProvider.GetService<TuntapTransfer>();
|
TuntapTransfer tuntapTransfer = serviceProvider.GetService<TuntapTransfer>();
|
||||||
@@ -54,15 +60,63 @@ namespace linker.messenger.tuntap
|
|||||||
IApiServer apiServer = serviceProvider.GetService<IApiServer>();
|
IApiServer apiServer = serviceProvider.GetService<IApiServer>();
|
||||||
apiServer.AddPlugins(new List<libs.api.IApiController> { serviceProvider.GetService<TuntapApiController>() });
|
apiServer.AddPlugins(new List<libs.api.IApiController> { serviceProvider.GetService<TuntapApiController>() });
|
||||||
|
|
||||||
ExRouteTransfer exRouteTransfer= serviceProvider.GetService<ExRouteTransfer>();
|
ExRouteTransfer exRouteTransfer = serviceProvider.GetService<ExRouteTransfer>();
|
||||||
exRouteTransfer.AddExRoutes(new List<IExRoute> { serviceProvider.GetService<TuntapExRoute>() });
|
exRouteTransfer.AddExRoutes(new List<IExRoute> { serviceProvider.GetService<TuntapExRoute>() });
|
||||||
|
|
||||||
TunnelClientExcludeIPTransfer tunnelClientExcludeIPTransfer = serviceProvider.GetService<TunnelClientExcludeIPTransfer>();
|
TunnelClientExcludeIPTransfer tunnelClientExcludeIPTransfer = serviceProvider.GetService<TunnelClientExcludeIPTransfer>();
|
||||||
tunnelClientExcludeIPTransfer.AddTunnelExcludeIPs(new List<ITunnelClientExcludeIP> { serviceProvider.GetService<TuntapTunnelExcludeIP>() });
|
tunnelClientExcludeIPTransfer.AddTunnelExcludeIPs(new List<ITunnelClientExcludeIP> { serviceProvider.GetService<TuntapTunnelExcludeIP>() });
|
||||||
|
|
||||||
DecenterClientTransfer decenterClientTransfer= serviceProvider.GetService<DecenterClientTransfer>();
|
DecenterClientTransfer decenterClientTransfer = serviceProvider.GetService<DecenterClientTransfer>();
|
||||||
decenterClientTransfer.AddDecenters(new List<IDecenter> { serviceProvider.GetService<TuntapDecenter>() });
|
decenterClientTransfer.AddDecenters(new List<IDecenter> { serviceProvider.GetService<TuntapDecenter>() });
|
||||||
|
|
||||||
|
if (configDic.TryGetValue("Tuntap", out string base64))
|
||||||
|
{
|
||||||
|
ITuntapClientStore tuntapClientStore = serviceProvider.GetService<ITuntapClientStore>();
|
||||||
|
ILeaseClientStore leaseClientStore = serviceProvider.GetService<ILeaseClientStore>();
|
||||||
|
ISignInClientStore signInClientStore = serviceProvider.GetService<ISignInClientStore>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JsonElement doc = JsonDocument.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(base64))).RootElement;
|
||||||
|
if (doc.TryGetProperty("IP", out JsonElement ip))
|
||||||
|
{
|
||||||
|
tuntapClientStore.Info.IP = IPAddress.Parse(ip.GetString());
|
||||||
|
}
|
||||||
|
if (doc.TryGetProperty("PrefixLength", out JsonElement prefixLength))
|
||||||
|
{
|
||||||
|
tuntapClientStore.Info.PrefixLength = prefixLength.GetByte();
|
||||||
|
}
|
||||||
|
if (doc.TryGetProperty("Lans", out JsonElement lans))
|
||||||
|
{
|
||||||
|
tuntapClientStore.Info.Lans = lans.GetString().DeJson<List<TuntapLanInfo>>();
|
||||||
|
}
|
||||||
|
if (doc.TryGetProperty("Name", out JsonElement name))
|
||||||
|
{
|
||||||
|
tuntapClientStore.Info.Name = name.GetString();
|
||||||
|
}
|
||||||
|
if (doc.TryGetProperty("Running", out JsonElement running))
|
||||||
|
{
|
||||||
|
tuntapClientStore.Info.Running = running.GetBoolean();
|
||||||
|
}
|
||||||
|
if (doc.TryGetProperty("Switch", out JsonElement _switch))
|
||||||
|
{
|
||||||
|
tuntapClientStore.Info.Switch = (TuntapSwitch)_switch.GetInt32();
|
||||||
|
}
|
||||||
|
if (doc.TryGetProperty("Forwards", out JsonElement forwards))
|
||||||
|
{
|
||||||
|
tuntapClientStore.Info.Forwards = forwards.GetString().DeJson<List<TuntapForwardInfo>>();
|
||||||
|
}
|
||||||
|
if (doc.TryGetProperty("Lease", out JsonElement lease))
|
||||||
|
{
|
||||||
|
leaseClientStore.Set(signInClientStore.Group.Id, lease.GetString().DeJson<LeaseInfo>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.Instance.Error(ex);
|
||||||
|
}
|
||||||
|
tuntapClientStore.Confirm();
|
||||||
|
}
|
||||||
|
|
||||||
return serviceProvider;
|
return serviceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,10 +2,8 @@
|
|||||||
{
|
{
|
||||||
public interface IUpdaterServerStore
|
public interface IUpdaterServerStore
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 更新密钥
|
public bool ValidateSecretKey(string key);
|
||||||
/// </summary>
|
|
||||||
public string SecretKey { get; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置更新密钥
|
/// 设置更新密钥
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -132,7 +132,7 @@ namespace linker.messenger.updater
|
|||||||
MachineId = string.Empty,
|
MachineId = string.Empty,
|
||||||
Current = info.Current,
|
Current = info.Current,
|
||||||
Length = info.Length,
|
Length = info.Length,
|
||||||
Status = info.Status,
|
Status = info.Status,
|
||||||
Version = info.Version
|
Version = info.Version
|
||||||
};
|
};
|
||||||
connection.Write(serializer.Serialize(result));
|
connection.Write(serializer.Serialize(result));
|
||||||
@@ -151,7 +151,7 @@ namespace linker.messenger.updater
|
|||||||
public void ConfirmServer(IConnection connection)
|
public void ConfirmServer(IConnection connection)
|
||||||
{
|
{
|
||||||
UpdaterConfirmServerInfo confirm = serializer.Deserialize<UpdaterConfirmServerInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
UpdaterConfirmServerInfo confirm = serializer.Deserialize<UpdaterConfirmServerInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
if (updaterServerStore.SecretKey == confirm.SecretKey)
|
if (updaterServerStore.ValidateSecretKey(confirm.SecretKey))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(confirm.Version))
|
if (string.IsNullOrWhiteSpace(confirm.Version))
|
||||||
{
|
{
|
||||||
@@ -172,7 +172,7 @@ namespace linker.messenger.updater
|
|||||||
public void ExitServer(IConnection connection)
|
public void ExitServer(IConnection connection)
|
||||||
{
|
{
|
||||||
UpdaterConfirmServerInfo confirm = serializer.Deserialize<UpdaterConfirmServerInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
UpdaterConfirmServerInfo confirm = serializer.Deserialize<UpdaterConfirmServerInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
if (updaterServerStore.SecretKey == confirm.SecretKey)
|
if (updaterServerStore.ValidateSecretKey(confirm.SecretKey))
|
||||||
{
|
{
|
||||||
Environment.Exit(1);
|
Environment.Exit(1);
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ namespace linker.messenger.updater
|
|||||||
}
|
}
|
||||||
|
|
||||||
//需要密钥
|
//需要密钥
|
||||||
if (confirm.All && updaterServerStore.SecretKey != confirm.SecretKey)
|
if (confirm.All && updaterServerStore.ValidateSecretKey(confirm.SecretKey) == false)
|
||||||
{
|
{
|
||||||
connection.Write(Helper.FalseArray);
|
connection.Write(Helper.FalseArray);
|
||||||
return;
|
return;
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<project ver="10" name="linker.share.win" libEmbed="true" icon="res\favicon.ico" ui="win" output="linker.share.win.exe" CompanyName="snltty" FileDescription="linker.share.win" LegalCopyright="Copyright (C) snltty 2025" ProductName="linker.share.win" InternalName="linker.share.win" FileVersion="0.0.0.1" ProductVersion="0.0.0.1" publishDir="/dist/" dstrip="false">
|
<project ver="10" name="linker.share.win" libEmbed="true" icon="res\favicon.ico" ui="win" output="linker.share.win.exe" CompanyName="snltty" FileDescription="linker.share.win" LegalCopyright="Copyright (C) snltty 2025" ProductName="linker.share.win" InternalName="linker.share.win" FileVersion="0.0.0.3" ProductVersion="0.0.0.3" publishDir="/dist/" dstrip="false">
|
||||||
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
|
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
|
||||||
<folder name="资源文件" path="res" embed="true" local="false" ignored="false">
|
<folder name="资源文件" path="res" embed="true" local="false" ignored="false">
|
||||||
<file name="connect.aardio" path="res\connect.aardio" comment="res\connect.aardio"/>
|
<file name="connect.aardio" path="res\connect.aardio" comment="res\connect.aardio"/>
|
||||||
<file name="share.aardio" path="res\share.aardio" comment="res\share.aardio"/>
|
<file name="share.aardio" path="res\share.aardio" comment="res\share.aardio"/>
|
||||||
|
<file name="info.aardio" path="res\info.aardio" comment="res\info.aardio"/>
|
||||||
</folder>
|
</folder>
|
||||||
<folder name="窗体文件" path="dlg" comment="目录" embed="true"/>
|
<folder name="窗体文件" path="dlg" comment="目录" embed="true"/>
|
||||||
</project>
|
</project>
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
//RUNAS//
|
//RUNAS//
|
||||||
import win.ui;
|
import win.ui;
|
||||||
/*DSG{{*/
|
/*DSG{{*/
|
||||||
mainForm = win.form(text="linker.share.win";right=250;bottom=291;border="thin";max=false;topmost=1)
|
mainForm = win.form(text="linker局域网共享";right=352;bottom=312;border="thin";max=false;topmost=1)
|
||||||
mainForm.add(
|
mainForm.add(
|
||||||
mainTab={cls="tab";left=1;top=-2;right=250;bottom=254;aw=1;edge=1;z=1}
|
mainTab={cls="tab";left=8;top=1;right=346;bottom=306;z=1}
|
||||||
)
|
)
|
||||||
/*}}*/
|
/*}}*/
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ if(!atom){
|
|||||||
|
|
||||||
mainForm.mainTab.loadForm("\res\connect.aardio");
|
mainForm.mainTab.loadForm("\res\connect.aardio");
|
||||||
mainForm.mainTab.loadForm("\res\share.aardio");
|
mainForm.mainTab.loadForm("\res\share.aardio");
|
||||||
|
mainForm.mainTab.loadForm("\res\info.aardio");
|
||||||
|
|
||||||
mainForm.show();
|
mainForm.show();
|
||||||
return win.loopMessage();
|
return win.loopMessage();
|
@@ -1,7 +1,16 @@
|
|||||||
import win.ui;
|
import win.ui;
|
||||||
/*DSG{{*/
|
/*DSG{{*/
|
||||||
var winform = win.form(text="连接共享";right=759;bottom=469)
|
var winform = win.form(text="连接共享";right=330;bottom=276)
|
||||||
winform.add()
|
winform.add(
|
||||||
|
button={cls="button";text="删除";left=223;top=10;right=269;bottom=31;z=3};
|
||||||
|
button2={cls="button";text="新增";left=275;top=10;right=321;bottom=31;z=4};
|
||||||
|
button3={cls="button";text="开启连接";left=120;top=234;right=208;bottom=269;z=8};
|
||||||
|
checkbox={cls="checkbox";text="自动开启连接";left=10;top=242;right=105;bottom=261;z=7};
|
||||||
|
combobox={cls="combobox";left=67;top=11;right=217;bottom=32;edge=1;items={};mode="dropdown";z=2};
|
||||||
|
groupbox={cls="groupbox";text="在线的";left=10;top=37;right=321;bottom=228;edge=1;z=5};
|
||||||
|
listview2={cls="listview";left=17;top=56;right=314;bottom=221;edge=1;z=6};
|
||||||
|
static={cls="static";text="选择共享";left=10;top=12;right=67;bottom=29;transparent=1;z=1}
|
||||||
|
)
|
||||||
/*}}*/
|
/*}}*/
|
||||||
|
|
||||||
winform.show();
|
winform.show();
|
||||||
|
@@ -1,13 +1,286 @@
|
|||||||
import win.ui;
|
import win.ui;
|
||||||
/*DSG{{*/
|
/*DSG{{*/
|
||||||
var winform = win.form(text="发出共享";right=243;bottom=254)
|
var winform = win.form(text="发出共享";right=330;bottom=283)
|
||||||
winform.add(
|
winform.add(
|
||||||
button={cls="button";text="生成";left=195;top=12;right=234;bottom=35;z=3};
|
btnCopy={cls="button";text="复制共享密钥";left=229;top=240;right=321;bottom=267;z=16};
|
||||||
edit={cls="edit";text="Edit";left=44;top=14;right=189;bottom=35;edge=1;z=1};
|
btnNewSecretkey={cls="button";text="新ID";left=276;top=41;right=321;bottom=64;z=14};
|
||||||
static={cls="static";text="密钥";left=11;top=16;right=48;bottom=35;transparent=1;z=2}
|
btnSave={cls="button";text="保存";left=276;top=12;right=321;bottom=35;z=19};
|
||||||
|
btnStart={cls="button";text="开启共享";left=122;top=237;right=210;bottom=272;z=15};
|
||||||
|
cbLan={cls="combobox";left=80;top=102;right=205;bottom=123;edge=1;items={};mode="dropdown";z=4};
|
||||||
|
cbLanPrefixlength={cls="combobox";left=248;top=102;right=314;bottom=123;edge=1;items={};mode="dropdown";z=7};
|
||||||
|
cbRoutePrefixlength={cls="combobox";left=248;top=131;right=314;bottom=152;edge=1;items={};mode="dropdown";z=9};
|
||||||
|
ckAuto={cls="checkbox";text="自动开启共享";left=11;top=246;right=115;bottom=265;z=17};
|
||||||
|
groupbox={cls="groupbox";text="共享网段";left=11;top=75;right=321;bottom=228;edge=1;z=3};
|
||||||
|
static={cls="static";text="服务器";left=11;top=16;right=54;bottom=33;transparent=1;z=2};
|
||||||
|
static3={cls="static";text="内网网段";left=19;top=105;right=72;bottom=122;transparent=1;z=5};
|
||||||
|
static4={cls="static";text="掩码";left=216;top=105;right=247;bottom=119;transparent=1;z=6};
|
||||||
|
static5={cls="static";text="掩码";left=216;top=135;right=247;bottom=152;transparent=1;z=8};
|
||||||
|
static6={cls="static";text="映射路由";left=19;top=134;right=72;bottom=154;transparent=1;z=11};
|
||||||
|
static9={cls="static";text="唯一ID";left=11;top=46;right=52;bottom=63;transparent=1;z=13};
|
||||||
|
textMsg={cls="edit";left=19;top=163;right=314;bottom=219;autohscroll=false;edge=1;multiline=1;readonly=1;z=18};
|
||||||
|
textRoute={cls="ipaddress";left=80;top=132;right=205;bottom=153;edge=1;z=10};
|
||||||
|
textSecretkey={cls="edit";left=58;top=42;right=273;bottom=63;edge=1;z=12};
|
||||||
|
textServer={cls="edit";left=58;top=13;right=273;bottom=34;edge=1;z=1}
|
||||||
)
|
)
|
||||||
/*}}*/
|
/*}}*/
|
||||||
|
|
||||||
|
import dotNet;
|
||||||
|
compiler = dotNet.createCompiler("C#");
|
||||||
|
compiler.Source = /******
|
||||||
|
using System;
|
||||||
|
namespace LinkerLibsSpace
|
||||||
|
{
|
||||||
|
public class LinkerLibs
|
||||||
|
{
|
||||||
|
public string GetGuid()
|
||||||
|
{
|
||||||
|
return Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
******/
|
||||||
|
assembly = compiler.CompileOrFail();
|
||||||
|
assembly.import("LinkerLibsSpace");
|
||||||
|
winform.linkerLibs = LinkerLibsSpace.LinkerLibs();
|
||||||
|
|
||||||
|
|
||||||
|
if(mainForm != null)
|
||||||
|
{
|
||||||
|
winform.mainForm = mainForm;
|
||||||
|
winform.mainForm.shareForm =winform;
|
||||||
|
}
|
||||||
|
|
||||||
|
import crypt.aes;
|
||||||
|
import crypt.bin;
|
||||||
|
import web.json;
|
||||||
|
winform.aes = crypt.aes();
|
||||||
|
winform.aes.setPassword("BC7F0C7C-6A1E-4792-A17F-0008D300C076");
|
||||||
|
winform.shareSettingTab = {
|
||||||
|
"server"="",
|
||||||
|
"secretkey" = winform.linkerLibs.GetGuid(),
|
||||||
|
"lan"="",
|
||||||
|
"lanPrefixlength"="24",
|
||||||
|
"route"="192.168.188.0",
|
||||||
|
"routePrefixlength"="24",
|
||||||
|
"auto"=false
|
||||||
|
}
|
||||||
|
winform.loadShareSetting = function()
|
||||||
|
{
|
||||||
|
configStr = string.load("~/configs/share.json");
|
||||||
|
if(configStr)
|
||||||
|
{
|
||||||
|
json = web.json.parse(winform.aes.decrypt(configStr));
|
||||||
|
winform.shareSettingTab.server = json.server;
|
||||||
|
winform.shareSettingTab.secretkey = json.secretkey;
|
||||||
|
winform.shareSettingTab.lan = json.lan;
|
||||||
|
winform.shareSettingTab.lanPrefixlength = json.lanPrefixlength;
|
||||||
|
winform.shareSettingTab.route = json.route;
|
||||||
|
winform.shareSettingTab.routePrefixlength = json.routePrefixlength;
|
||||||
|
winform.shareSettingTab.auto = json.auto;
|
||||||
|
}
|
||||||
|
winform.btnSave.oncommand = function()
|
||||||
|
{
|
||||||
|
winform.saveShareSetting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
winform.saveShareSetting = function()
|
||||||
|
{
|
||||||
|
import console;
|
||||||
|
winform.shareSettingTab.server = winform.textServer.text;
|
||||||
|
winform.shareSettingTab.secretkey = winform.textSecretkey.text;
|
||||||
|
winform.shareSettingTab.lan = winform.cbLan.selText;
|
||||||
|
winform.shareSettingTab.lanPrefixlength = winform.cbLanPrefixlength.selText;
|
||||||
|
winform.shareSettingTab.route = winform.textRoute.text;
|
||||||
|
winform.shareSettingTab.routePrefixlength = winform.cbRoutePrefixlength.selText;
|
||||||
|
winform.shareSettingTab.auto = winform.ckAuto.checked;
|
||||||
|
config = winform.aes.encrypt(web.json.stringify(winform.shareSettingTab));
|
||||||
|
import fsys;
|
||||||
|
try{fsys.delete("~/configs/share.json");}catch(e){}
|
||||||
|
string.save("~/configs/share.json",config ,true);
|
||||||
|
win.msgbox("已保存配置",,0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
winform.initLans = function()
|
||||||
|
{
|
||||||
|
import com.wmi;
|
||||||
|
|
||||||
|
winform.prefixlengthDefault = "24";
|
||||||
|
winform.prefixlengthTab = {32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16};
|
||||||
|
winform.cbLanPrefixlength.items = winform.prefixlengthTab;
|
||||||
|
winform.cbLanPrefixlength.selText = winform.shareSettingTab.lanPrefixlength;
|
||||||
|
winform.cbLanPrefixlength.onEditChange = function(){
|
||||||
|
winform.cbLanPrefixlength.selText = winform.prefixlengthDefault;
|
||||||
|
}
|
||||||
|
winform.cbRoutePrefixlength.items = winform.prefixlengthTab;
|
||||||
|
winform.cbRoutePrefixlength.selText = winform.shareSettingTab.routePrefixlength;
|
||||||
|
winform.cbRoutePrefixlength.onEditChange = function(){
|
||||||
|
winform.cbRoutePrefixlength.selText = winform.prefixlengthDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
lansTab = {};
|
||||||
|
for item in com.wmi.eachProperties("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=true") {
|
||||||
|
table.push(lansTab,item.IPAddress[1]);
|
||||||
|
}
|
||||||
|
winform.cbLan.items = lansTab;
|
||||||
|
|
||||||
|
if(#winform.shareSettingTab.lan)winform.cbLan.selText = winform.shareSettingTab.lan
|
||||||
|
else winform.cbLan.selText = lansTab[1];
|
||||||
|
winform.cbLan.onEditChange = function(){
|
||||||
|
winform.cbLan.selText = lansTab[1];
|
||||||
|
}
|
||||||
|
winform.textRoute.text = winform.shareSettingTab.route;
|
||||||
|
|
||||||
|
function setText()
|
||||||
|
{
|
||||||
|
winform.textMsg.text = "对方使用【"+winform.textRoute.text+"/"+winform.cbRoutePrefixlength.selText+"】访问你的【"+winform.cbLan.selText+"/"+winform.cbLanPrefixlength.selText+"】,这能有效的解决网段冲突,和隐藏你的真实内网网段";
|
||||||
|
}
|
||||||
|
winform.cbLan.onListChange = function(){
|
||||||
|
setText();
|
||||||
|
}
|
||||||
|
winform.cbLanPrefixlength.onListChange = function(){
|
||||||
|
setText();
|
||||||
|
}
|
||||||
|
winform.cbRoutePrefixlength.onListChange = function(){
|
||||||
|
setText();
|
||||||
|
}
|
||||||
|
winform.textRoute.onChange = function(){
|
||||||
|
if(owner.onModified)owner.onModified(true);
|
||||||
|
setText();
|
||||||
|
}
|
||||||
|
setText();
|
||||||
|
}
|
||||||
|
|
||||||
|
winform.initSecretkey = function()
|
||||||
|
{
|
||||||
|
winform.textSecretkey.text = winform.shareSettingTab.secretkey;
|
||||||
|
winform.btnNewSecretkey.oncommand = function(){
|
||||||
|
result = win.msgbox("旧的共享密钥将不可用,确认使用一个新ID吗",,0x01/*MB_YESNOCANCEL*/ );
|
||||||
|
if(result == 1)
|
||||||
|
{
|
||||||
|
winform.textSecretkey.text = winform.linkerLibs.GetGuid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
winform.initAuto = function()
|
||||||
|
{
|
||||||
|
winform.ckAuto.checked = winform.shareSettingTab.auto;
|
||||||
|
}
|
||||||
|
winform.initServer = function()
|
||||||
|
{
|
||||||
|
winform.textServer.text = winform.shareSettingTab.server;
|
||||||
|
}
|
||||||
|
|
||||||
|
winform.getShareKey = function()
|
||||||
|
{
|
||||||
|
json = {
|
||||||
|
"Client"={
|
||||||
|
CApi = {ApiPort:18003,ApiPassword="snltty",WebPort:0},
|
||||||
|
Servers={{Name="linker",Host=winform.textServer.text}},
|
||||||
|
Groups={{Name="Linker",Id="Linker",Password=winform.textSecretkey.text}}
|
||||||
|
},
|
||||||
|
"Common"= { Modes={"client"},Install=true },
|
||||||
|
"Tuntap" = {
|
||||||
|
Running=true,
|
||||||
|
Lans={
|
||||||
|
{
|
||||||
|
IP= winform.cbLan.selText,
|
||||||
|
PrefixLength = winform.cbLanPrefixlength.selText,
|
||||||
|
MapIP = winform.textRoute.text,
|
||||||
|
MapPrefixLength = winform.cbRoutePrefixlength.selText
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Lease={
|
||||||
|
IP="10.18.0.0",
|
||||||
|
PrefixLength=16,
|
||||||
|
Name="linkers"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!#json.Client.Servers[1].Host)
|
||||||
|
{
|
||||||
|
winform.textServer.showErrorTip("必填","请填写服务器")
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(!#json.Client.Groups[1].Password)
|
||||||
|
{
|
||||||
|
winform.textSecretkey.showErrorTip("必选","请填写唯一id")
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(!#json.Tuntap.Lans[1].IP)
|
||||||
|
{
|
||||||
|
winform.cbLan.showErrorTip("必选","请选择一个局域网IP")
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
winform.initSHareKey = function()
|
||||||
|
{
|
||||||
|
winform.btnCopy.oncommand = function()
|
||||||
|
{
|
||||||
|
import win.clip;
|
||||||
|
json = winform.getShareKey();
|
||||||
|
if(json)
|
||||||
|
{
|
||||||
|
encodeText = winform.aes.encrypt(web.json.stringify(json));
|
||||||
|
base64Text = crypt.bin.encodeBase64(encodeText);
|
||||||
|
win.clip.write(base64Text);
|
||||||
|
win.msgbox("已复制到剪贴板",,0x01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
winform.startProc = null;
|
||||||
|
winform.runStart = function()
|
||||||
|
{
|
||||||
|
import process.popen;
|
||||||
|
import console;
|
||||||
|
|
||||||
|
winform.btnStart.text = "操作中..";
|
||||||
|
if(winform.startProc)
|
||||||
|
{
|
||||||
|
winform.startProc.terminate();
|
||||||
|
winform.startProc = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json = winform.getShareKey();
|
||||||
|
if(json)
|
||||||
|
{
|
||||||
|
params = {
|
||||||
|
"--config-client",
|
||||||
|
crypt.bin.encodeBase64(web.json.stringify(json.Client))
|
||||||
|
"--config-common",
|
||||||
|
crypt.bin.encodeBase64(web.json.stringify(json.Common))
|
||||||
|
"--config-tuntap",
|
||||||
|
crypt.bin.encodeBase64(web.json.stringify(json.Tuntap))
|
||||||
|
}
|
||||||
|
winform.startProc = process.popen("~/linker.exe",process.joinArguments(params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
winform.btnStart.text = winform.startProc ? "关闭共享" : "开启共享";
|
||||||
|
}
|
||||||
|
winform.initStart = function()
|
||||||
|
{
|
||||||
|
if(winform.ckAuto.checked)
|
||||||
|
{
|
||||||
|
winform.runStart();
|
||||||
|
}
|
||||||
|
winform.btnStart.oncommand = function()
|
||||||
|
{
|
||||||
|
winform.runStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
winform.loadShareSetting();
|
||||||
|
winform.initLans();
|
||||||
|
winform.initSecretkey();
|
||||||
|
winform.initAuto();
|
||||||
|
winform.initServer();
|
||||||
|
winform.initSHareKey();
|
||||||
|
winform.initStart();
|
||||||
|
|
||||||
winform.show();
|
winform.show();
|
||||||
win.loopMessage();
|
win.loopMessage();
|
||||||
return winform;
|
return winform;
|
||||||
|
@@ -2,19 +2,23 @@
|
|||||||
using linker.libs.extends;
|
using linker.libs.extends;
|
||||||
using Microsoft.Win32.SafeHandles;
|
using Microsoft.Win32.SafeHandles;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace linker.tun
|
namespace linker.tun
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// osx网卡实现,未测试
|
||||||
|
/// </summary>
|
||||||
internal sealed class LinkerOsxTunDevice : ILinkerTunDevice
|
internal sealed class LinkerOsxTunDevice : ILinkerTunDevice
|
||||||
{
|
{
|
||||||
|
|
||||||
private string name = string.Empty;
|
private string name = string.Empty;
|
||||||
public string Name => name;
|
public string Name => name;
|
||||||
public bool Running => fs != null;
|
public bool Running => safeFileHandle != null;
|
||||||
public bool AppNat => false;
|
public bool AppNat => false;
|
||||||
|
|
||||||
private FileStream fs = null;
|
private FileStream fsRead = null;
|
||||||
|
private FileStream fsWrite = null;
|
||||||
private IPAddress address;
|
private IPAddress address;
|
||||||
private byte prefixLength = 24;
|
private byte prefixLength = 24;
|
||||||
private SafeFileHandle safeFileHandle;
|
private SafeFileHandle safeFileHandle;
|
||||||
@@ -28,42 +32,79 @@ namespace linker.tun
|
|||||||
|
|
||||||
public bool Setup(string name, IPAddress address, byte prefixLength, out string error)
|
public bool Setup(string name, IPAddress address, byte prefixLength, out string error)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = "utun0";
|
||||||
error = string.Empty;
|
error = string.Empty;
|
||||||
|
|
||||||
interfaceOsx = GetOsxInterfaceNum();
|
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.prefixLength = prefixLength;
|
this.prefixLength = prefixLength;
|
||||||
|
|
||||||
safeFileHandle = File.OpenHandle($"/dev/{Name}", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, FileOptions.Asynchronous);
|
IntPtr arg = Marshal.AllocHGlobal(4);
|
||||||
fs = new FileStream(safeFileHandle, FileAccess.ReadWrite, 1500);
|
Marshal.WriteInt32(arg, 0);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
interfaceOsx = GetOsxInterfaceNum();
|
||||||
|
safeFileHandle = File.OpenHandle($"/dev/utun", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, FileOptions.Asynchronous);
|
||||||
|
|
||||||
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(prefixLength));
|
int ret = OsxAPI.Ioctl(safeFileHandle, arg);
|
||||||
CommandHelper.Osx(string.Empty, new string[] {
|
if (ret < 0)
|
||||||
$"route delete -net {network}/{prefixLength} {address}",
|
{
|
||||||
$"ifconfig {Name} {address} {address} up",
|
Shutdown();
|
||||||
$"route add -net {network}/{prefixLength} {address}",
|
error = $"open utun failed: {Marshal.GetLastWin32Error()}";
|
||||||
});
|
return false;
|
||||||
return true;
|
}
|
||||||
|
this.name = $"utun{Marshal.ReadInt32(arg)}";
|
||||||
|
|
||||||
|
fsRead = new FileStream(safeFileHandle, FileAccess.Read, 65 * 1024, true);
|
||||||
|
fsWrite = new FileStream(safeFileHandle, FileAccess.Write, 65 * 1024, true);
|
||||||
|
|
||||||
|
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(prefixLength));
|
||||||
|
|
||||||
|
CommandHelper.Osx(string.Empty, new string[] {
|
||||||
|
$"route delete -net {network}/{prefixLength} {address}",
|
||||||
|
$"ifconfig {Name} {address} {address} up",
|
||||||
|
$"route add -net {network}/{prefixLength} {address}",
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Shutdown();
|
||||||
|
error = $"open utun failed: {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(arg);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Shutdown()
|
public void Shutdown()
|
||||||
{
|
{
|
||||||
if (fs != null)
|
try
|
||||||
{
|
{
|
||||||
safeFileHandle?.Dispose();
|
|
||||||
|
|
||||||
interfaceOsx = string.Empty;
|
interfaceOsx = string.Empty;
|
||||||
fs.Close();
|
|
||||||
fs.Dispose();
|
safeFileHandle?.Dispose();
|
||||||
fs = null;
|
safeFileHandle = null;
|
||||||
|
|
||||||
|
try { fsRead?.Flush(); } catch (Exception) { }
|
||||||
|
try { fsRead?.Close(); fsRead?.Dispose(); } catch (Exception) { }
|
||||||
|
fsRead = null;
|
||||||
|
|
||||||
|
try { fsWrite?.Flush(); } catch (Exception) { }
|
||||||
|
try { fsWrite?.Close(); fsWrite?.Dispose(); } catch (Exception) { }
|
||||||
|
fsWrite = null;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(this.prefixLength));
|
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(this.prefixLength));
|
||||||
CommandHelper.Osx(string.Empty, new string[] { $"route delete -net {network}/{prefixLength} {address}" });
|
CommandHelper.Osx(string.Empty, new string[] { $"route delete -net {network}/{prefixLength} {address}" });
|
||||||
}
|
}
|
||||||
public void Refresh()
|
public void Refresh()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRoute(LinkerTunDeviceRouteItem[] ips)
|
public void AddRoute(LinkerTunDeviceRouteItem[] ips)
|
||||||
@@ -93,7 +134,7 @@ namespace linker.tun
|
|||||||
|
|
||||||
public void SetMtu(int value)
|
public void SetMtu(int value)
|
||||||
{
|
{
|
||||||
CommandHelper.Osx(string.Empty, new string[] { $"ifconfig {Name} mtu {value}" });
|
CommandHelper.Osx(string.Empty, new string[] { $"ifconfig {Name} mtu {value} up" });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSystemNat(out string error)
|
public void SetSystemNat(out string error)
|
||||||
@@ -134,34 +175,28 @@ namespace linker.tun
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private byte[] buffer = new byte[2 * 1024];
|
private byte[] buffer = new byte[65 * 1024];
|
||||||
|
private readonly object writeLockObj = new object();
|
||||||
public byte[] Read(out int length)
|
public byte[] Read(out int length)
|
||||||
{
|
{
|
||||||
length = 0;
|
length = 0;
|
||||||
try
|
if (safeFileHandle == null) return Helper.EmptyArray;
|
||||||
{
|
|
||||||
length = fs.Read(buffer.AsSpan(4));
|
length = fsRead.Read(buffer.AsSpan(4));
|
||||||
length.ToBytes(buffer);
|
length.ToBytes(buffer);
|
||||||
length += 4;
|
length += 4;
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return Helper.EmptyArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Write(ReadOnlyMemory<byte> buffer)
|
public bool Write(ReadOnlyMemory<byte> buffer)
|
||||||
{
|
{
|
||||||
try
|
if (safeFileHandle == null) return true;
|
||||||
|
lock (writeLockObj)
|
||||||
{
|
{
|
||||||
fs.Write(buffer.Span);
|
fsWrite.Write(buffer.Span);
|
||||||
|
fsWrite.Flush();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetOsxInterfaceNum()
|
private string GetOsxInterfaceNum()
|
||||||
@@ -187,7 +222,6 @@ namespace linker.tun
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<bool> CheckAvailable(bool order = false)
|
public async Task<bool> CheckAvailable(bool order = false)
|
||||||
{
|
{
|
||||||
return await Task.FromResult(true).ConfigureAwait(false);
|
return await Task.FromResult(true).ConfigureAwait(false);
|
||||||
|
23
src/linker.tun/OsxTun.cs
Normal file
23
src/linker.tun/OsxTun.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace linker.tun
|
||||||
|
{
|
||||||
|
internal static class OsxAPI
|
||||||
|
{
|
||||||
|
|
||||||
|
// 定义 macOS 的 ioctl 命令(来自 <net/if_utun.h>)
|
||||||
|
private const uint UTUN_CONTROL = 0x80000000; // 'u' << 24
|
||||||
|
private const uint UTUN_CTRL_SET_IFNAME = (UTUN_CONTROL | 1);
|
||||||
|
|
||||||
|
// P/Invoke 声明
|
||||||
|
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
|
||||||
|
private static extern int Ioctl(SafeFileHandle fd, uint request, IntPtr arg);
|
||||||
|
|
||||||
|
public static int Ioctl(SafeFileHandle fd, IntPtr arg)
|
||||||
|
{
|
||||||
|
return Ioctl(fd, UTUN_CTRL_SET_IFNAME, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -34,7 +34,7 @@ namespace linker.tunnel.transport
|
|||||||
|
|
||||||
public bool DisableSSL => false;
|
public bool DisableSSL => false;
|
||||||
|
|
||||||
public byte Order => 3;
|
public byte Order => 5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接成功
|
/// 连接成功
|
||||||
|
@@ -32,7 +32,7 @@ namespace linker.tunnel.transport
|
|||||||
|
|
||||||
public bool DisableSSL => false;
|
public bool DisableSSL => false;
|
||||||
|
|
||||||
public byte Order => 2;
|
public byte Order => 4;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接成功
|
/// 连接成功
|
||||||
|
@@ -35,7 +35,7 @@ namespace linker.tunnel.transport
|
|||||||
|
|
||||||
public bool DisableSSL => false;
|
public bool DisableSSL => false;
|
||||||
|
|
||||||
public byte Order => 5;
|
public byte Order => 2;
|
||||||
|
|
||||||
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
|
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
|
||||||
|
|
||||||
|
@@ -38,7 +38,7 @@ namespace linker.tunnel.transport
|
|||||||
|
|
||||||
public bool DisableSSL => false;
|
public bool DisableSSL => false;
|
||||||
|
|
||||||
public byte Order => 1;
|
public byte Order => 3;
|
||||||
|
|
||||||
|
|
||||||
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
|
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
|
||||||
|
@@ -33,7 +33,7 @@ namespace linker.tunnel.transport
|
|||||||
|
|
||||||
public bool DisableSSL => false;
|
public bool DisableSSL => false;
|
||||||
|
|
||||||
public byte Order => 4;
|
public byte Order => 1;
|
||||||
|
|
||||||
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
|
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
|
||||||
|
|
||||||
|
@@ -88,6 +88,11 @@ namespace linker
|
|||||||
configDic.Add("Common", args[i + 1]);
|
configDic.Add("Common", args[i + 1]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
else if (args[i] == "--config-tuntap")
|
||||||
|
{
|
||||||
|
configDic.Add("Tuntap", args[i + 1]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return configDic;
|
return configDic;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
v1.7.6
|
v1.7.6
|
||||||
2025-04-27 11:23:46
|
2025-04-28 16:09:25
|
||||||
1. 一些优化
|
1. 一些优化
|
||||||
2. 安卓APP勉强能用,支持分身,下拉刷新,在线升级
|
2. 安卓APP勉强能用,支持分身,下拉刷新,在线升级
|
||||||
5. 如果你设备很多,请尝试升级其中一个成功重启后再升级其它
|
5. 如果你设备很多,请尝试升级其中一个成功重启后再升级其它
|
Reference in New Issue
Block a user