mirror of
https://github.com/snltty/linker.git
synced 2025-12-24 12:38:04 +08:00
增加打洞协议,自定义认证参数管理,增加防火墙同步
This commit is contained in:
@@ -12,8 +12,9 @@ sidebar_position: 2
|
||||
|
||||
:::tip[1、情况1,你的设备支持NAT转发时]
|
||||
|
||||
1. linux,已经自动添加NAT转发(在`OpenWrt`,需要在`防火墙 - 区域设置`中将`转发`设置为`接受`)
|
||||
2. windows,优先使用系统`NetNat`,`NetNat`失败则启用`内置SNAT`,但是性能应该没有`NetNat`好
|
||||
1. linux,已经自动添加NAT转发
|
||||
2. linux软路由docker,在`OpenWrt、群晖`或者其它软路由系统,可能需要在宿主机允许IP转发,可以在UI设置,也可以尝试`sysctl -w net.ipv4.ip_forward=1`允许IP转发,然后`iptables -t nat -A POSTROUTING -s 10.18.18.0/24 -j MASQUERADE`然后添加NAT,10.18.18.0/24是你的虚拟网卡网段
|
||||
3. windows,优先使用系统`NetNat`,`NetNat`失败则启用`内置SNAT`,但是性能应该没有`NetNat`好
|
||||
1. 由于`内置SNAT`依赖`WinDivert驱动`,如果报错`Windows 无法验证此文件的数字签名`什么的,可以尝试以下两种解决办法
|
||||
2. 使用`管理员身份运行cmd`执行以下两条命令,然后重启系统
|
||||
```
|
||||
@@ -30,8 +31,6 @@ sidebar_position: 2
|
||||
:::
|
||||
|
||||
|
||||
|
||||
|
||||
:::tip[2、情况2,你的设备无法使用NAT转发时]
|
||||
|
||||
1. 你的设备无法使用NAT转发(一般出现在低版本windows下,win10以下),那你只能使用端口转发功能来访问你当前设备局域网下的其它设备
|
||||
|
||||
@@ -1,30 +1,70 @@
|
||||
using linker.libs.extends;
|
||||
using linker.libs;
|
||||
using linker.libs.extends;
|
||||
using linker.libs.web;
|
||||
using linker.messenger.api;
|
||||
using linker.messenger.signin;
|
||||
using linker.tunnel.transport;
|
||||
|
||||
namespace linker.messenger.action
|
||||
{
|
||||
public sealed class ActionApiController : IApiController
|
||||
{
|
||||
private readonly IActionClientStore actionStore;
|
||||
|
||||
public ActionApiController(IActionClientStore actionStore)
|
||||
private readonly ActionTransfer actionTransfer;
|
||||
private readonly SignInClientState signInClientState;
|
||||
private readonly ISignInClientStore signInClientStore;
|
||||
private readonly IMessengerSender messengerSender;
|
||||
private readonly ISerializer serializer;
|
||||
public ActionApiController(ActionTransfer actionTransfer, SignInClientState signInClientState, ISignInClientStore signInClientStore,
|
||||
IMessengerSender messengerSender, ISerializer serializer)
|
||||
{
|
||||
this.actionStore = actionStore;
|
||||
this.actionTransfer = actionTransfer;
|
||||
this.signInClientState = signInClientState;
|
||||
this.signInClientStore = signInClientStore;
|
||||
this.messengerSender = messengerSender;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
|
||||
[Access(AccessValue.Action)]
|
||||
public bool SetArgs(ApiControllerParamsInfo param)
|
||||
{
|
||||
actionStore.SetActionArg(param.Content);
|
||||
return actionStore.Confirm();
|
||||
return actionTransfer.SetActionDynamicArg(param.Content);
|
||||
}
|
||||
[Access(AccessValue.Action)]
|
||||
public bool SetServerArgs(ApiControllerParamsInfo param)
|
||||
public async Task<string> GetServerArgs(ApiControllerParamsInfo param)
|
||||
{
|
||||
actionStore.SetActionArgs(param.Content.DeJson<Dictionary<string, string>>());
|
||||
return actionStore.Confirm();
|
||||
if (param.Content == signInClientStore.Id || string.IsNullOrWhiteSpace(param.Content))
|
||||
{
|
||||
return actionTransfer.GetActionStaticArg();
|
||||
}
|
||||
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
|
||||
{
|
||||
Connection = signInClientState.Connection,
|
||||
MessengerId = (ushort)ActionMessengerIds.GetForward,
|
||||
Payload = serializer.Serialize(param.Content)
|
||||
}).ConfigureAwait(false);
|
||||
if (resp.Code == MessageResponeCodes.OK && resp.Data.Length > 0)
|
||||
{
|
||||
return serializer.Deserialize<string>(resp.Data.Span);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
[Access(AccessValue.Action)]
|
||||
public async Task<bool> SetServerArgs(ApiControllerParamsInfo param)
|
||||
{
|
||||
KeyValuePair<string, string> keyValue = param.Content.DeJson<KeyValuePair<string, string>>();
|
||||
|
||||
if (keyValue.Key == signInClientStore.Id || string.IsNullOrWhiteSpace(keyValue.Key))
|
||||
{
|
||||
return actionTransfer.SetActionStaticArg(keyValue.Value);
|
||||
}
|
||||
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
|
||||
{
|
||||
Connection = signInClientState.Connection,
|
||||
MessengerId = (ushort)ActionMessengerIds.SetForward,
|
||||
Payload = serializer.Serialize(keyValue)
|
||||
}).ConfigureAwait(false);
|
||||
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
95
src/linker.messenger.action/ActionMessenger.cs
Normal file
95
src/linker.messenger.action/ActionMessenger.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using linker.libs;
|
||||
using linker.messenger.signin;
|
||||
|
||||
namespace linker.messenger.action
|
||||
{
|
||||
public class ActionClientMessenger : IMessenger
|
||||
{
|
||||
private readonly IMessengerSender messengerSender;
|
||||
private readonly ISerializer serializer;
|
||||
private readonly ActionTransfer actionTransfer;
|
||||
|
||||
public ActionClientMessenger(IMessengerSender messengerSender, ISerializer serializer, ActionTransfer actionTransfer)
|
||||
{
|
||||
this.messengerSender = messengerSender;
|
||||
this.serializer = serializer;
|
||||
this.actionTransfer = actionTransfer;
|
||||
}
|
||||
[MessengerId((ushort)ActionMessengerIds.Get)]
|
||||
public void Get(IConnection connection)
|
||||
{
|
||||
connection.Write(serializer.Serialize(actionTransfer.GetActionStaticArg()));
|
||||
}
|
||||
[MessengerId((ushort)ActionMessengerIds.Set)]
|
||||
public void Set(IConnection connection)
|
||||
{
|
||||
actionTransfer.SetActionStaticArg(serializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span));
|
||||
}
|
||||
}
|
||||
|
||||
public class ActionServerMessenger : IMessenger
|
||||
{
|
||||
private readonly IMessengerSender messengerSender;
|
||||
private readonly SignInServerCaching signCaching;
|
||||
private readonly ISerializer serializer;
|
||||
|
||||
public ActionServerMessenger(IMessengerSender messengerSender, SignInServerCaching signCaching, ISerializer serializer)
|
||||
{
|
||||
this.messengerSender = messengerSender;
|
||||
this.signCaching = signCaching;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
[MessengerId((ushort)ActionMessengerIds.GetForward)]
|
||||
public void GetForward(IConnection connection)
|
||||
{
|
||||
string machineid = serializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
|
||||
if (signCaching.TryGet(connection.Id, machineid, out SignCacheInfo from, out SignCacheInfo to))
|
||||
{
|
||||
uint requestid = connection.ReceiveRequestWrap.RequestId;
|
||||
_ = messengerSender.SendReply(new MessageRequestWrap
|
||||
{
|
||||
Connection = to.Connection,
|
||||
MessengerId = (ushort)ActionMessengerIds.Get
|
||||
}).ContinueWith(async (result) =>
|
||||
{
|
||||
if (result.Result.Code == MessageResponeCodes.OK && result.Result.Data.Length > 0)
|
||||
{
|
||||
await messengerSender.ReplyOnly(new MessageResponseWrap
|
||||
{
|
||||
Connection = connection,
|
||||
Payload = result.Result.Data,
|
||||
RequestId = requestid,
|
||||
}, (ushort)ActionMessengerIds.GetForward).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
[MessengerId((ushort)ActionMessengerIds.SetForward)]
|
||||
public void SetForward(IConnection connection)
|
||||
{
|
||||
KeyValuePair<string, string> info = serializer.Deserialize<KeyValuePair<string,string>>(connection.ReceiveRequestWrap.Payload.Span);
|
||||
if (signCaching.TryGet(connection.Id, info.Key, out SignCacheInfo from, out SignCacheInfo to))
|
||||
{
|
||||
uint requestid = connection.ReceiveRequestWrap.RequestId;
|
||||
_ = messengerSender.SendReply(new MessageRequestWrap
|
||||
{
|
||||
Connection = to.Connection,
|
||||
MessengerId = (ushort)ActionMessengerIds.Set,
|
||||
Payload = serializer.Serialize(info.Value)
|
||||
}).ContinueWith(async (result) =>
|
||||
{
|
||||
if (result.Result.Code == MessageResponeCodes.OK && result.Result.Data.Length > 0)
|
||||
{
|
||||
await messengerSender.ReplyOnly(new MessageResponseWrap
|
||||
{
|
||||
Connection = connection,
|
||||
Payload = result.Result.Data,
|
||||
RequestId = requestid,
|
||||
}, (ushort)ActionMessengerIds.SetForward).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/linker.messenger.action/ActionMessengerIds.cs
Normal file
14
src/linker.messenger.action/ActionMessengerIds.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace linker.messenger.action
|
||||
{
|
||||
public enum ActionMessengerIds : ushort
|
||||
{
|
||||
_ = 3600,
|
||||
|
||||
Get = 3601,
|
||||
GetForward = 3602,
|
||||
Set = 3603,
|
||||
SetForward = 3604,
|
||||
|
||||
None = 3699
|
||||
}
|
||||
}
|
||||
33
src/linker.messenger.action/ActionSync.cs
Normal file
33
src/linker.messenger.action/ActionSync.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using linker.libs;
|
||||
using linker.messenger.sync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace linker.messenger.action
|
||||
{
|
||||
public sealed class ActionSync : ISync
|
||||
{
|
||||
public string Name => "ActionStatic";
|
||||
|
||||
private readonly ActionTransfer actionTransfer;
|
||||
private readonly ISerializer serializer;
|
||||
public ActionSync(ActionTransfer actionTransfer, ISerializer serializer)
|
||||
{
|
||||
this.actionTransfer = actionTransfer;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public Memory<byte> GetData()
|
||||
{
|
||||
return serializer.Serialize(actionTransfer.GetActionStaticArg());
|
||||
}
|
||||
|
||||
public void SetData(Memory<byte> data)
|
||||
{
|
||||
actionTransfer.SetActionStaticArg(serializer.Deserialize<string>(data.Span));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,17 @@
|
||||
using System.Net.Http.Json;
|
||||
using linker.messenger.signin;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
namespace linker.messenger.action
|
||||
{
|
||||
public sealed class ActionTransfer
|
||||
{
|
||||
private readonly ISignInClientStore signInClientStore;
|
||||
private readonly IActionClientStore actionClientStore;
|
||||
public ActionTransfer(ISignInClientStore signInClientStore, IActionClientStore actionClientStore)
|
||||
{
|
||||
this.signInClientStore = signInClientStore;
|
||||
this.actionClientStore = actionClientStore;
|
||||
}
|
||||
public async Task<string> ExcuteActions(string actionJson, string url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url)) return string.Empty;
|
||||
@@ -31,5 +39,24 @@ namespace linker.messenger.action
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public bool SetActionDynamicArg(string value)
|
||||
{
|
||||
actionClientStore.SetActionDynamicArg(value);
|
||||
return actionClientStore.Confirm();
|
||||
}
|
||||
public bool SetActionStaticArg(string value)
|
||||
{
|
||||
actionClientStore.SetActionStaticArg(signInClientStore.Server.Host, value);
|
||||
return actionClientStore.Confirm();
|
||||
}
|
||||
public string GetActionStaticArg()
|
||||
{
|
||||
return actionClientStore.GetActionStaticArg(signInClientStore.Server.Host);
|
||||
}
|
||||
public bool TryAddActionArg(Dictionary<string, string> args)
|
||||
{
|
||||
return actionClientStore.TryAddActionArg(signInClientStore.Server.Host, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using linker.messenger.action;
|
||||
using linker.messenger.relay.server.validator;
|
||||
using linker.messenger.sforward.server.validator;
|
||||
using linker.messenger.signin.args;
|
||||
using linker.messenger.sync;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
namespace linker.messenger.api
|
||||
{
|
||||
@@ -14,6 +15,11 @@ namespace linker.messenger.api
|
||||
|
||||
serviceCollection.AddSingleton<ActionTransfer>();
|
||||
serviceCollection.AddSingleton<SignInArgsAction>();
|
||||
|
||||
|
||||
serviceCollection.AddSingleton<ActionClientMessenger>();
|
||||
|
||||
serviceCollection.AddSingleton<ActionSync>();
|
||||
return serviceCollection;
|
||||
}
|
||||
public static ServiceProvider UseActionClient(this ServiceProvider serviceProvider)
|
||||
@@ -24,6 +30,12 @@ namespace linker.messenger.api
|
||||
SignInArgsTransfer signInArgsTransfer = serviceProvider.GetService<SignInArgsTransfer>();
|
||||
signInArgsTransfer.AddArgs(new List<ISignInArgs> { serviceProvider.GetService<SignInArgsAction>() });
|
||||
|
||||
IMessengerResolver messengerResolver = serviceProvider.GetService<IMessengerResolver>();
|
||||
messengerResolver.AddMessenger(new List<IMessenger> { serviceProvider.GetService<ActionClientMessenger>() });
|
||||
|
||||
SyncTreansfer syncTransfer = serviceProvider.GetService<SyncTreansfer>();
|
||||
syncTransfer.AddSyncs(new List<ISync> { serviceProvider.GetService<ActionSync>() });
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
|
||||
@@ -33,6 +45,8 @@ namespace linker.messenger.api
|
||||
serviceCollection.AddSingleton<SignInArgsAction>();
|
||||
serviceCollection.AddSingleton<RelayValidatorAction>();
|
||||
serviceCollection.AddSingleton<SForwardValidatorAction>();
|
||||
|
||||
serviceCollection.AddSingleton<ActionServerMessenger>();
|
||||
return serviceCollection;
|
||||
}
|
||||
public static ServiceProvider UseActionServer(this ServiceProvider serviceProvider)
|
||||
@@ -45,6 +59,10 @@ namespace linker.messenger.api
|
||||
|
||||
SForwardValidatorTransfer sForwardValidatorTransfer = serviceProvider.GetService<SForwardValidatorTransfer>();
|
||||
sForwardValidatorTransfer.AddValidators(new List<ISForwardValidator> { serviceProvider.GetService<SForwardValidatorAction>() });
|
||||
|
||||
IMessengerResolver messengerResolver = serviceProvider.GetService<IMessengerResolver>();
|
||||
messengerResolver.AddMessenger(new List<IMessenger> { serviceProvider.GetService<ActionServerMessenger>() });
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
namespace linker.messenger.action
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace linker.messenger.action
|
||||
{
|
||||
public sealed class ActionInfo
|
||||
{
|
||||
public string Arg { get; set; } = string.Empty;
|
||||
public Dictionary<string, string> Args { get; set; } = new Dictionary<string, string>();
|
||||
public ConcurrentDictionary<string, string> Args { get; set; } = new ConcurrentDictionary<string, string>();
|
||||
}
|
||||
public interface IActionClientStore
|
||||
{
|
||||
/// <summary>
|
||||
/// 设置动态验证参数,优先使用
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void SetActionArg(string action);
|
||||
/// <param name="value"></param>
|
||||
public void SetActionDynamicArg(string value);
|
||||
/// <summary>
|
||||
/// 设置静态验证参数,动态参数为空时使用
|
||||
/// </summary>
|
||||
/// <param name="actions">action参数列表,host->arg,不同的服务器不同的参数</param>
|
||||
public void SetActionArgs(Dictionary<string, string> actions);
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
public void SetActionStaticArg(string key,string value);
|
||||
/// <summary>
|
||||
/// 从配置里获取验证参数,添加到args
|
||||
/// 获取静态参数
|
||||
/// </summary>
|
||||
/// <param name="host">当前服务器地址</param>
|
||||
/// <param name="args">一个字典</param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryAddActionArg(string host, Dictionary<string, string> args);
|
||||
public string GetActionStaticArg(string key);
|
||||
|
||||
/// <summary>
|
||||
/// 网args里添加指定key的值
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryAddActionArg(string key, Dictionary<string, string> args);
|
||||
/// <summary>
|
||||
/// 提交更新
|
||||
/// </summary>
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace linker.messenger.api
|
||||
[AccessDisplay("修改打洞协议")]
|
||||
Transport = 28,
|
||||
|
||||
[AccessDisplay("修改验证参数")]
|
||||
[AccessDisplay("修改本机验证参数")]
|
||||
Action = 29,
|
||||
|
||||
[AccessDisplay("查看内网穿透流量")]
|
||||
@@ -149,6 +149,9 @@ namespace linker.messenger.api
|
||||
WakeupSelf = 46,
|
||||
[AccessDisplay("唤醒所有设备")]
|
||||
WakeupOther = 47,
|
||||
|
||||
[AccessDisplay("修改所有验证参数")]
|
||||
ActionOther = 48,
|
||||
}
|
||||
|
||||
public sealed class AccessTextInfo
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
using linker.libs.web;
|
||||
using linker.messenger.sync;
|
||||
using linker.snat;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@@ -13,6 +14,7 @@ namespace linker.messenger.firewall
|
||||
serviceCollection.AddSingleton<FirewallClientMessenger>();
|
||||
serviceCollection.AddSingleton<FirewallTransfer>();
|
||||
serviceCollection.AddSingleton<FirewallApiController>();
|
||||
serviceCollection.AddSingleton<FirewallSync>();
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
@@ -26,6 +28,9 @@ namespace linker.messenger.firewall
|
||||
linker.messenger.api.IWebServer apiServer = serviceProvider.GetService<linker.messenger.api.IWebServer>();
|
||||
apiServer.AddPlugins(new List<IApiController> { serviceProvider.GetService<FirewallApiController>() });
|
||||
|
||||
SyncTreansfer syncTransfer = serviceProvider.GetService<SyncTreansfer>();
|
||||
syncTransfer.AddSyncs(new List<ISync> { serviceProvider.GetService<FirewallSync>() });
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace linker.messenger.firewall
|
||||
if (accessStore.HasAccess(AccessValue.FirewallSelf) == false) return new FirewallListInfo();
|
||||
return firewallTransfer.Get(info.Data);
|
||||
}
|
||||
if (accessStore.HasAccess(AccessValue.FirewallOther) == false) return new FirewallListInfo ();
|
||||
if (accessStore.HasAccess(AccessValue.FirewallOther) == false) return new FirewallListInfo();
|
||||
|
||||
var resp = await messengerSender.SendReply(new MessageRequestWrap
|
||||
{
|
||||
@@ -121,5 +121,15 @@ namespace linker.messenger.firewall
|
||||
Payload = serializer.Serialize(info)
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
/// <summary>
|
||||
/// 选中
|
||||
/// </summary>
|
||||
/// <param name="param"></param>
|
||||
/// <returns></returns>
|
||||
public bool Check(ApiControllerParamsInfo param)
|
||||
{
|
||||
FirewallCheckInfo info = param.Content.DeJson<FirewallCheckInfo>();
|
||||
return firewallTransfer.Check(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
src/linker.messenger.firewall/FirewallSync.cs
Normal file
36
src/linker.messenger.firewall/FirewallSync.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using linker.libs;
|
||||
using linker.messenger.sync;
|
||||
|
||||
namespace linker.messenger.firewall
|
||||
{
|
||||
public sealed class FirewallSync : ISync
|
||||
{
|
||||
public string Name => "Firewall";
|
||||
|
||||
private readonly FirewallTransfer firewallTransfer;
|
||||
private readonly ISerializer serializer;
|
||||
public FirewallSync(FirewallTransfer firewallTransfer, ISerializer serializer)
|
||||
{
|
||||
this.firewallTransfer = firewallTransfer;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
public Memory<byte> GetData()
|
||||
{
|
||||
return serializer.Serialize(firewallTransfer.Get().Where(c => c.Checked).ToList());
|
||||
}
|
||||
|
||||
public void SetData(Memory<byte> data)
|
||||
{
|
||||
List<string> ids = firewallTransfer.Get().Select(c => c.Id).ToList();
|
||||
|
||||
List<FirewallRuleInfo> list = serializer.Deserialize<List<FirewallRuleInfo>>(data.Span);
|
||||
foreach (FirewallRuleInfo rule in list)
|
||||
{
|
||||
rule.Id = string.Empty;
|
||||
}
|
||||
firewallTransfer.Add(list);
|
||||
|
||||
firewallTransfer.Remove(ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,15 @@ namespace linker.messenger.firewall
|
||||
BuildRules();
|
||||
return true;
|
||||
}
|
||||
public bool Check(FirewallCheckInfo info)
|
||||
{
|
||||
return firewallClientStore.Check(info);
|
||||
}
|
||||
|
||||
public List<FirewallRuleInfo> Get()
|
||||
{
|
||||
return firewallClientStore.GetAll().ToList();
|
||||
}
|
||||
public FirewallListInfo Get(FirewallSearchInfo info)
|
||||
{
|
||||
return new FirewallListInfo
|
||||
@@ -50,12 +58,29 @@ namespace linker.messenger.firewall
|
||||
BuildRules();
|
||||
return true;
|
||||
}
|
||||
public bool Add(List<FirewallRuleInfo> infos)
|
||||
{
|
||||
foreach (var item in infos)
|
||||
{
|
||||
item.GroupId = signInClientStore.Group.Id;
|
||||
}
|
||||
|
||||
firewallClientStore.Add(infos);
|
||||
BuildRules();
|
||||
return true;
|
||||
}
|
||||
public bool Remove(string id)
|
||||
{
|
||||
firewallClientStore.Remove(id);
|
||||
BuildRules();
|
||||
return true;
|
||||
}
|
||||
public bool Remove(List<string> ids)
|
||||
{
|
||||
firewallClientStore.Remove(ids);
|
||||
BuildRules();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,14 @@ namespace linker.messenger.firewall
|
||||
public LinkerFirewallState State { get; }
|
||||
public void SetState(LinkerFirewallState state);
|
||||
|
||||
public IEnumerable<FirewallRuleInfo> GetAll();
|
||||
public IEnumerable<FirewallRuleInfo> GetAll(FirewallSearchInfo searchInfo);
|
||||
public IEnumerable<FirewallRuleInfo> GetEnabled(string groupId);
|
||||
public bool Add(FirewallRuleInfo rule);
|
||||
public bool Add(List<FirewallRuleInfo> rules);
|
||||
public bool Remove(string id);
|
||||
public bool Remove(List<string> ids);
|
||||
public bool Check(FirewallCheckInfo info);
|
||||
}
|
||||
|
||||
public sealed class FirewallRuleInfo : linker.snat.LinkerFirewallRuleInfo
|
||||
@@ -21,6 +25,7 @@ namespace linker.messenger.firewall
|
||||
public bool Disabled { get; set; }
|
||||
public int OrderBy { get; set; }
|
||||
public string Remark { get; set; }
|
||||
public bool Checked { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -59,4 +64,15 @@ namespace linker.messenger.firewall
|
||||
public string MachineId { get; set; }
|
||||
public LinkerFirewallState State { get; set; }
|
||||
}
|
||||
|
||||
public sealed partial class FirewallCheckForwardInfo
|
||||
{
|
||||
public string MachineId { get; set; }
|
||||
public FirewallCheckInfo Data { get; set; }
|
||||
}
|
||||
public sealed partial class FirewallCheckInfo
|
||||
{
|
||||
public List<string> Ids { get; set; }
|
||||
public bool IsChecked { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\linker.messenger.api\linker.messenger.api.csproj" />
|
||||
<ProjectReference Include="..\linker.messenger.signin\linker.messenger.signin.csproj" />
|
||||
<ProjectReference Include="..\linker.messenger.sync\linker.messenger.sync.csproj" />
|
||||
<ProjectReference Include="..\linker.messenger\linker.messenger.csproj" />
|
||||
<ProjectReference Include="..\linker.snat\linker.snat.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace linker.messenger.serializer.memorypack
|
||||
MemoryPackFormatterProvider.Register(new TunnelSetRouteLevelInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new TunnelInterfaceInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new TunnelNetInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new TunnelTransportItemSetInfoFormatter());
|
||||
|
||||
|
||||
MemoryPackFormatterProvider.Register(new DecenterSyncInfoFormatter());
|
||||
@@ -66,7 +67,7 @@ namespace linker.messenger.serializer.memorypack
|
||||
MemoryPackFormatterProvider.Register(new RelayAskResultInfo170Formatter());
|
||||
MemoryPackFormatterProvider.Register(new RelayCacheInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new RelayMessageInfoFormatter());
|
||||
|
||||
|
||||
MemoryPackFormatterProvider.Register(new RelayTrafficUpdateInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new RelayServerNodeUpdateInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new RelayServerNodeUpdateWrapInfoFormatter());
|
||||
@@ -143,6 +144,8 @@ namespace linker.messenger.serializer.memorypack
|
||||
MemoryPackFormatterProvider.Register(new FirewallAddForwardInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new FirewallRemoveForwardInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new FirewallStateForwardInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new FirewallCheckInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new FirewallCheckForwardInfoFormatter());
|
||||
|
||||
MemoryPackFormatterProvider.Register(new WakeupInfoFormatter());
|
||||
MemoryPackFormatterProvider.Register(new WakeupSearchInfoFormatter());
|
||||
|
||||
@@ -441,4 +441,112 @@ namespace linker.messenger.serializer.memorypack
|
||||
value = wrapped.info;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MemoryPackable]
|
||||
public readonly partial struct SerializableFirewallCheckInfo
|
||||
{
|
||||
[MemoryPackIgnore]
|
||||
public readonly FirewallCheckInfo info;
|
||||
|
||||
[MemoryPackInclude]
|
||||
List<string> Ids => info.Ids;
|
||||
|
||||
[MemoryPackInclude, MemoryPackAllowSerialize]
|
||||
bool IsChecked => info.IsChecked;
|
||||
|
||||
[MemoryPackConstructor]
|
||||
SerializableFirewallCheckInfo(List<string> ids, bool isChecked)
|
||||
{
|
||||
this.info = new FirewallCheckInfo
|
||||
{
|
||||
Ids = ids,
|
||||
IsChecked = isChecked
|
||||
};
|
||||
}
|
||||
|
||||
public SerializableFirewallCheckInfo(FirewallCheckInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
}
|
||||
public class FirewallCheckInfoFormatter : MemoryPackFormatter<FirewallCheckInfo>
|
||||
{
|
||||
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref FirewallCheckInfo value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNullObjectHeader();
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WritePackable(new SerializableFirewallCheckInfo(value));
|
||||
}
|
||||
|
||||
public override void Deserialize(ref MemoryPackReader reader, scoped ref FirewallCheckInfo value)
|
||||
{
|
||||
if (reader.PeekIsNull())
|
||||
{
|
||||
reader.Advance(1); // skip null block
|
||||
value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var wrapped = reader.ReadPackable<SerializableFirewallCheckInfo>();
|
||||
value = wrapped.info;
|
||||
}
|
||||
}
|
||||
[MemoryPackable]
|
||||
public readonly partial struct SerializableFirewallCheckForwardInfo
|
||||
{
|
||||
[MemoryPackIgnore]
|
||||
public readonly FirewallCheckForwardInfo info;
|
||||
|
||||
[MemoryPackInclude]
|
||||
string MachineId => info.MachineId;
|
||||
|
||||
[MemoryPackInclude, MemoryPackAllowSerialize]
|
||||
FirewallCheckInfo Data => info.Data;
|
||||
|
||||
[MemoryPackConstructor]
|
||||
SerializableFirewallCheckForwardInfo(string machineId, FirewallCheckInfo data)
|
||||
{
|
||||
this.info = new FirewallCheckForwardInfo
|
||||
{
|
||||
MachineId = machineId,
|
||||
Data = data
|
||||
};
|
||||
}
|
||||
|
||||
public SerializableFirewallCheckForwardInfo(FirewallCheckForwardInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
}
|
||||
public class FirewallCheckForwardInfoFormatter : MemoryPackFormatter<FirewallCheckForwardInfo>
|
||||
{
|
||||
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref FirewallCheckForwardInfo value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNullObjectHeader();
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WritePackable(new SerializableFirewallCheckForwardInfo(value));
|
||||
}
|
||||
|
||||
public override void Deserialize(ref MemoryPackReader reader, scoped ref FirewallCheckForwardInfo value)
|
||||
{
|
||||
if (reader.PeekIsNull())
|
||||
{
|
||||
reader.Advance(1); // skip null block
|
||||
value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var wrapped = reader.ReadPackable<SerializableFirewallCheckForwardInfo>();
|
||||
value = wrapped.info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,4 +615,54 @@ namespace linker.messenger.serializer.memorypack
|
||||
value = wrapped.info;
|
||||
}
|
||||
}
|
||||
[MemoryPackable]
|
||||
public readonly partial struct SerializableTunnelTransportItemSetInfo
|
||||
{
|
||||
[MemoryPackIgnore]
|
||||
public readonly TunnelTransportItemSetInfo info;
|
||||
|
||||
[MemoryPackInclude]
|
||||
string MachineId => info.MachineId;
|
||||
|
||||
[MemoryPackInclude]
|
||||
List<TunnelTransportItemInfo> Data => info.Data;
|
||||
|
||||
[MemoryPackConstructor]
|
||||
SerializableTunnelTransportItemSetInfo(string machineId, List<TunnelTransportItemInfo> data)
|
||||
{
|
||||
var info = new TunnelTransportItemSetInfo { MachineId = machineId, Data = data };
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public SerializableTunnelTransportItemSetInfo(TunnelTransportItemSetInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
}
|
||||
public class TunnelTransportItemSetInfoFormatter : MemoryPackFormatter<TunnelTransportItemSetInfo>
|
||||
{
|
||||
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref TunnelTransportItemSetInfo value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNullObjectHeader();
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WritePackable(new SerializableTunnelTransportItemSetInfo(value));
|
||||
}
|
||||
|
||||
public override void Deserialize(ref MemoryPackReader reader, scoped ref TunnelTransportItemSetInfo value)
|
||||
{
|
||||
if (reader.PeekIsNull())
|
||||
{
|
||||
reader.Advance(1); // skip null block
|
||||
value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var wrapped = reader.ReadPackable<SerializableTunnelTransportItemSetInfo>();
|
||||
value = wrapped.info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using linker.messenger.action;
|
||||
using linker.libs.extends;
|
||||
using linker.messenger.action;
|
||||
namespace linker.messenger.store.file.action
|
||||
{
|
||||
public sealed class ActionClientStore : IActionClientStore
|
||||
@@ -11,15 +12,25 @@ namespace linker.messenger.store.file.action
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public void SetActionArg(string action)
|
||||
public void SetActionDynamicArg(string value)
|
||||
{
|
||||
config.Data.Client.Action.Arg = action;
|
||||
config.Data.Client.Action.Arg = value;
|
||||
}
|
||||
public void SetActionArgs(Dictionary<string, string> actions)
|
||||
|
||||
public void SetActionStaticArg(string key, string value)
|
||||
{
|
||||
config.Data.Client.Action.Args = actions;
|
||||
|
||||
config.Data.Client.Action.Args.AddOrUpdate(key, value, (a, b) => value);
|
||||
}
|
||||
|
||||
public string GetActionStaticArg(string key)
|
||||
{
|
||||
if (config.Data.Client.Action.Args.TryGetValue(key, out string arg))
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public bool TryAddActionArg(string host, Dictionary<string, string> args)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(config.Data.Client.Action.Arg) == false)
|
||||
@@ -37,6 +48,7 @@ namespace linker.messenger.store.file.action
|
||||
config.Data.Update();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,10 @@ namespace linker.messenger.store.file.firewall
|
||||
runningConfig.Data.Update();
|
||||
}
|
||||
|
||||
public IEnumerable<FirewallRuleInfo> GetAll()
|
||||
{
|
||||
return liteCollection.FindAll();
|
||||
}
|
||||
public IEnumerable<FirewallRuleInfo> GetAll(FirewallSearchInfo info)
|
||||
{
|
||||
IEnumerable<FirewallRuleInfo> list = liteCollection.FindAll()
|
||||
@@ -77,10 +81,50 @@ namespace linker.messenger.store.file.firewall
|
||||
}, c => c.Id == rule.Id) > 0;
|
||||
}
|
||||
}
|
||||
public bool Add(List<FirewallRuleInfo> rules)
|
||||
{
|
||||
foreach (var rule in rules)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(rule.Id))
|
||||
{
|
||||
rule.Id = ObjectId.NewObjectId().ToString();
|
||||
liteCollection.Insert(rule);
|
||||
}
|
||||
else
|
||||
{
|
||||
liteCollection.UpdateMany(p => new FirewallRuleInfo
|
||||
{
|
||||
SrcId = rule.SrcId,
|
||||
SrcName = rule.SrcName,
|
||||
GroupId = rule.GroupId,
|
||||
DstCIDR = rule.DstCIDR,
|
||||
DstPort = rule.DstPort,
|
||||
Protocol = rule.Protocol,
|
||||
Action = rule.Action,
|
||||
OrderBy = rule.OrderBy,
|
||||
Disabled = rule.Disabled,
|
||||
Remark = rule.Remark
|
||||
}, c => c.Id == rule.Id);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Remove(string id)
|
||||
{
|
||||
return liteCollection.Delete(id);
|
||||
}
|
||||
public bool Remove(List<string> ids)
|
||||
{
|
||||
return liteCollection.DeleteMany(c => ids.Contains(c.Id)) > 0;
|
||||
}
|
||||
|
||||
public bool Check(FirewallCheckInfo info)
|
||||
{
|
||||
return liteCollection.UpdateMany(p => new FirewallRuleInfo
|
||||
{
|
||||
Checked = info.IsChecked
|
||||
}, c => info.Ids.Contains(c.Id)) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Net;
|
||||
using linker.tunnel.transport;
|
||||
using System.Net;
|
||||
|
||||
namespace linker.messenger.tunnel
|
||||
{
|
||||
@@ -72,4 +73,11 @@ namespace linker.messenger.tunnel
|
||||
|
||||
public TunnelNetInfo Net { get; set; } = new TunnelNetInfo();
|
||||
}
|
||||
|
||||
public sealed class TunnelTransportItemSetInfo
|
||||
{
|
||||
public string MachineId { get; set; }
|
||||
public List<TunnelTransportItemInfo> Data { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -120,7 +120,24 @@ namespace linker.messenger.tunnel
|
||||
/// <returns></returns>
|
||||
public async Task<List<TunnelTransportItemInfo>> GetTransports(ApiControllerParamsInfo param)
|
||||
{
|
||||
return await tunnelClientStore.GetTunnelTransports().ConfigureAwait(false);
|
||||
if (param.Content == signInClientStore.Id || string.IsNullOrWhiteSpace(param.Content))
|
||||
{
|
||||
return await tunnelClientStore.GetTunnelTransports().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
|
||||
{
|
||||
Connection = signInClientState.Connection,
|
||||
MessengerId = (ushort)TunnelMessengerIds.TransportGetForward,
|
||||
Payload = serializer.Serialize(param.Content)
|
||||
}).ConfigureAwait(false);
|
||||
if (resp.Code == MessageResponeCodes.OK && resp.Data.Length > 0)
|
||||
{
|
||||
return serializer.Deserialize<List<TunnelTransportItemInfo>>(resp.Data.Span);
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置打洞协议
|
||||
@@ -130,12 +147,21 @@ namespace linker.messenger.tunnel
|
||||
[Access(AccessValue.Transport)]
|
||||
public async Task<bool> SetTransports(ApiControllerParamsInfo param)
|
||||
{
|
||||
List<TunnelTransportItemInfo> info = param.Content.DeJson<List<TunnelTransportItemInfo>>();
|
||||
await tunnelClientStore.SetTunnelTransports(info).ConfigureAwait(false);
|
||||
return true;
|
||||
TunnelTransportItemSetInfo info = param.Content.DeJson<TunnelTransportItemSetInfo>();
|
||||
if (info.MachineId == signInClientStore.Id || string.IsNullOrWhiteSpace(info.MachineId))
|
||||
{
|
||||
await tunnelClientStore.SetTunnelTransports(info.Data).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
|
||||
{
|
||||
Connection = signInClientState.Connection,
|
||||
MessengerId = (ushort)TunnelMessengerIds.TransportSetForward,
|
||||
Payload = serializer.Serialize(info)
|
||||
}).ConfigureAwait(false);
|
||||
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
|
||||
}
|
||||
|
||||
|
||||
public async Task<TunnelLocalNetworkInfo> GetNetwork(ApiControllerParamsInfo param)
|
||||
{
|
||||
if (param.Content == signInClientStore.Id)
|
||||
|
||||
@@ -104,6 +104,17 @@ namespace linker.messenger.tunnel
|
||||
{
|
||||
connection.Write(serializer.Serialize(tunnelNetworkTransfer.GetLocalNetwork()));
|
||||
}
|
||||
|
||||
[MessengerId((ushort)TunnelMessengerIds.TransportGet)]
|
||||
public void TransportGet(IConnection connection)
|
||||
{
|
||||
connection.Write(serializer.Serialize(tunnelClientStore.GetTunnelTransports()));
|
||||
}
|
||||
[MessengerId((ushort)TunnelMessengerIds.TransportSet)]
|
||||
public void TransportSet(IConnection connection)
|
||||
{
|
||||
tunnelClientStore.SetTunnelTransports(serializer.Deserialize<List<TunnelTransportItemInfo>>(connection.ReceiveRequestWrap.Payload.Span));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -247,5 +258,58 @@ namespace linker.messenger.tunnel
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MessengerId((ushort)TunnelMessengerIds.TransportGetForward)]
|
||||
public void TransportGetForward(IConnection connection)
|
||||
{
|
||||
string machineid = serializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
|
||||
if (signCaching.TryGet(connection.Id, machineid, out SignCacheInfo from, out SignCacheInfo to))
|
||||
{
|
||||
uint requestid = connection.ReceiveRequestWrap.RequestId;
|
||||
_ = messengerSender.SendReply(new MessageRequestWrap
|
||||
{
|
||||
Connection = to.Connection,
|
||||
MessengerId = (ushort)TunnelMessengerIds.TransportGet
|
||||
}).ContinueWith(async (result) =>
|
||||
{
|
||||
if (result.Result.Code == MessageResponeCodes.OK && result.Result.Data.Length > 0)
|
||||
{
|
||||
await messengerSender.ReplyOnly(new MessageResponseWrap
|
||||
{
|
||||
Connection = connection,
|
||||
Payload = result.Result.Data,
|
||||
RequestId = requestid,
|
||||
}, (ushort)TunnelMessengerIds.TransportGetForward).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
[MessengerId((ushort)TunnelMessengerIds.TransportSetForward)]
|
||||
public void TransportSetForward(IConnection connection)
|
||||
{
|
||||
TunnelTransportItemSetInfo info = serializer.Deserialize<TunnelTransportItemSetInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||
if (signCaching.TryGet(connection.Id, info.MachineId, out SignCacheInfo from, out SignCacheInfo to))
|
||||
{
|
||||
uint requestid = connection.ReceiveRequestWrap.RequestId;
|
||||
_ = messengerSender.SendReply(new MessageRequestWrap
|
||||
{
|
||||
Connection = to.Connection,
|
||||
MessengerId = (ushort)TunnelMessengerIds.TransportSet,
|
||||
Payload = serializer.Serialize(info.Data)
|
||||
}).ContinueWith(async (result) =>
|
||||
{
|
||||
if (result.Result.Code == MessageResponeCodes.OK && result.Result.Data.Length > 0)
|
||||
{
|
||||
await messengerSender.ReplyOnly(new MessageResponseWrap
|
||||
{
|
||||
Connection = connection,
|
||||
Payload = result.Result.Data,
|
||||
RequestId = requestid,
|
||||
}, (ushort)TunnelMessengerIds.TransportSetForward).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
Network = 2013,
|
||||
NetworkForward = 2014,
|
||||
|
||||
TransportGet = 2015,
|
||||
TransportGetForward = 2016,
|
||||
TransportSet = 2017,
|
||||
TransportSetForward = 2018,
|
||||
|
||||
None = 2099
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using linker.libs;
|
||||
using linker.libs.extends;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -35,6 +33,7 @@ namespace linker.snat
|
||||
|
||||
mapDic = maps.ToFrozenDictionary(x => NetworkHelper.ToNetworkValue(x.FakeIP, x.PrefixLength), x => NetworkHelper.ToNetworkValue(x.RealIP, x.PrefixLength));
|
||||
masks = maps.Select(x => NetworkHelper.ToPrefixValue(x.PrefixLength)).ToArray();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,6 +93,7 @@ namespace linker.snat
|
||||
if (packet.Span[19] == 255) return;
|
||||
|
||||
uint fakeDist = NetworkHelper.ToValue(packet.Span.Slice(16, 4));
|
||||
|
||||
for (int i = 0; i < masks.Length; i++)
|
||||
{
|
||||
//目标IP网络号存在映射表中,找到映射后的真实网络号,替换网络号得到最终真实的IP
|
||||
|
||||
@@ -88,6 +88,8 @@ namespace linker.snat
|
||||
error = string.Empty;
|
||||
try
|
||||
{
|
||||
CommandHelper.Windows(string.Empty, ["reg add \"HKLM\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\" /v IPEnableRouter /t REG_DWORD /d 1 /f"]);
|
||||
|
||||
Shutdown();
|
||||
|
||||
srcIp = NetworkHelper.ToValue(info.Src);
|
||||
@@ -117,7 +119,9 @@ namespace linker.snat
|
||||
private static string BuildFilter(AddrInfo[] dsts)
|
||||
{
|
||||
IEnumerable<string> ipRanges = dsts.Select(c => $"(ip.SrcAddr >= {c.NetworkIP} and ip.SrcAddr <= {c.BroadcastIP})");
|
||||
return $"inbound and ({string.Join(" or ", ipRanges)})";
|
||||
string filter = $"inbound and ({string.Join(" or ", ipRanges)})";
|
||||
//Console.WriteLine($"filter:{filter}");
|
||||
return filter;
|
||||
}
|
||||
/// <summary>
|
||||
/// 开始接收数据包
|
||||
@@ -211,10 +215,13 @@ namespace linker.snat
|
||||
};
|
||||
if (result == false) return false;
|
||||
|
||||
//Console.WriteLine($"snat inject :{p.IPv4Hdr->SrcAddr}->{p.IPv4Hdr->DstAddr} 替换为 {interfaceAddr}->{p.IPv4Hdr->DstAddr}");
|
||||
//改写源地址为网卡地址
|
||||
p.IPv4Hdr->SrcAddr = interfaceAddr;
|
||||
|
||||
}
|
||||
WinDivert.CalcChecksums(p.Packet.Span, ref addr, 0);
|
||||
|
||||
winDivert.SendEx(p.Packet.Span, new ReadOnlySpan<WinDivertAddress>(ref addr));
|
||||
}
|
||||
}
|
||||
@@ -260,6 +267,8 @@ namespace linker.snat
|
||||
natMapInfo.LastTime = Environment.TickCount64;
|
||||
natMapInfo.Timeout = 15 * 1000;
|
||||
|
||||
//Console.WriteLine($"snat inject icmp:{*ptr0}->{identifier0},{*ptr1}->{identifier1}");
|
||||
|
||||
//改写为新的标识符
|
||||
*ptr0 = identifier0;
|
||||
*ptr1 = identifier1;
|
||||
@@ -286,10 +295,13 @@ namespace linker.snat
|
||||
ValueTuple<uint, ushort, uint, ushort, ProtocolType> key = (p.IPv4Hdr->DstAddr.Raw, *ptr0, p.IPv4Hdr->SrcAddr.Raw, *ptr1, ProtocolType.Icmp);
|
||||
if (natMap.TryGetValue(key, out NatMapInfo natMapInfo))
|
||||
{
|
||||
//Console.WriteLine($"snat recv icmp:{*ptr0}->{natMapInfo.Identifier0},{*ptr1}->{natMapInfo.Identifier1}");
|
||||
//改回原来的标识符
|
||||
*ptr0 = natMapInfo.Identifier0;
|
||||
*ptr1 = natMapInfo.Identifier1;
|
||||
//Console.WriteLine($"icmp recv:{p.IPv4Hdr->SrcAddr}->{p.IPv4Hdr->DstAddr} 替换为 {p.IPv4Hdr->SrcAddr}->{natMapInfo.SrcAddr}");
|
||||
p.IPv4Hdr->DstAddr = natMapInfo.SrcAddr;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
@@ -645,6 +657,7 @@ namespace linker.snat
|
||||
uint value = NetworkHelper.ToValue(Address);
|
||||
uint network = NetworkHelper.ToNetworkValue(value, NetworkHelper.ToValue(IPv4Mask));
|
||||
network2ipMap.TryAdd(network, (value, IPv4Addr.Parse(Address.ToString())));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,9 +166,13 @@ namespace linker.tunnel.connection
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
LoggerHelper.Instance.Error(Encoding.UTF8.GetString(memory.Span));
|
||||
LoggerHelper.Instance.Error(string.Join(",", memory.ToArray()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { sendWebsocketMsg } from './request'
|
||||
|
||||
export const setArgs = (args) => {
|
||||
return sendWebsocketMsg('action/SetServerArgs', args);
|
||||
|
||||
export const getArgs = (machineid) => {
|
||||
return sendWebsocketMsg('action/GetServerArgs', machineid);
|
||||
}
|
||||
export const setArgs = (keyvalue) => {
|
||||
return sendWebsocketMsg('action/SetServerArgs', keyvalue);
|
||||
}
|
||||
@@ -11,4 +11,7 @@ export const removeFirewall = (data) => {
|
||||
}
|
||||
export const stateFirewall = (data) => {
|
||||
return sendWebsocketMsg('firewall/state', data);
|
||||
}
|
||||
export const checkFirewall = (data) => {
|
||||
return sendWebsocketMsg('firewall/check', data);
|
||||
}
|
||||
@@ -17,8 +17,8 @@ export const setTunnelRouteLevel = (data) => {
|
||||
return sendWebsocketMsg('tunnel/SetRouteLevel', data);
|
||||
}
|
||||
|
||||
export const getTunnelTransports = () => {
|
||||
return sendWebsocketMsg('tunnel/GetTransports');
|
||||
export const getTunnelTransports = (machineid) => {
|
||||
return sendWebsocketMsg('tunnel/GetTransports',machineid);
|
||||
}
|
||||
export const setTunnelTransports = (data) => {
|
||||
return sendWebsocketMsg('tunnel/SetTransports', data);
|
||||
|
||||
@@ -254,6 +254,8 @@ export default {
|
||||
'server.asyncUpdaterSecretKey': 'Update secretKey',
|
||||
'server.asyncTunnelTransports': 'Tunnel transports',
|
||||
'server.asyncSignInUserId': 'User Id',
|
||||
'server.asyncActionStatic': 'Action args',
|
||||
'server.asyncFirewall': 'Firewall selected item',
|
||||
|
||||
'firewall.rule': 'Firewall rule',
|
||||
'firewall.srcName': 'Src Device',
|
||||
|
||||
@@ -344,6 +344,8 @@ export default {
|
||||
'server.asyncUpdaterSecretKey': '更新密钥',
|
||||
'server.asyncTunnelTransports': '打洞协议',
|
||||
'server.asyncSignInUserId': '用户唯一标识',
|
||||
'server.asyncActionStatic': '自定义验证参数',
|
||||
'server.asyncFirewall': '防火墙选中项',
|
||||
|
||||
'firewall.rule': '防火墙协议',
|
||||
'firewall.srcName': '源设备',
|
||||
|
||||
73
src/linker.web/src/views/full/action/Action.vue
Normal file
73
src/linker.web/src/views/full/action/Action.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<span class="flex-1">{{$t('action.text')}}</span>
|
||||
<Sync name="ActionStatic" v-if="state.isSelf"></Sync>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<el-input v-model="state.data" :rows="10" type="textarea" resize="none" @change="handleSave" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="t-c">
|
||||
<el-button type="success" @click="handleSave">{{$t('common.confirm')}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-card>
|
||||
</template>
|
||||
<script>
|
||||
import { getArgs, setArgs } from '@/apis/action';
|
||||
import { injectGlobalData } from '@/provide';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { computed, onMounted, reactive } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Sync from '../sync/Index.vue'
|
||||
export default {
|
||||
props:['machineId'],
|
||||
components: { Sync },
|
||||
setup(props) {
|
||||
const { t } = useI18n();
|
||||
const globalData = injectGlobalData();
|
||||
const state = reactive({
|
||||
data: '',
|
||||
machineId: props.machineId || globalData.value.config.Client.Id,
|
||||
isSelf:computed(()=>{
|
||||
return state.machineId == globalData.value.config.Client.Id;
|
||||
})
|
||||
});
|
||||
const loadData = () => {
|
||||
getArgs(state.machineId).then((res) => {
|
||||
state.data = res;
|
||||
});
|
||||
}
|
||||
const handleSave = () => {
|
||||
try {
|
||||
if (state.data && typeof (JSON.parse(state.data)) != 'object') {
|
||||
ElMessage.error(t('action.jsonError'));
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(t('action.jsonError'));
|
||||
return;
|
||||
}
|
||||
setArgs({
|
||||
Key: state.machineId,
|
||||
Value: state.data
|
||||
}).then(() => {
|
||||
ElMessage.success(t('common.oper'));
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
ElMessage.error(t('common.operFail'));
|
||||
});
|
||||
}
|
||||
onMounted(()=>{
|
||||
loadData();
|
||||
})
|
||||
|
||||
return { state, handleSave }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="stylus" scoped>
|
||||
</style>
|
||||
@@ -1,52 +1,19 @@
|
||||
<template>
|
||||
<div class="action-wrap">
|
||||
<el-card shadow="never">
|
||||
<template #header>{{$t('action.text')}}</template>
|
||||
<div>
|
||||
<el-input v-model="state.list" :rows="10" type="textarea" resize="none" @change="handleSave" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="t-c">
|
||||
<el-button type="success" @click="handleSave">{{$t('common.confirm')}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-card>
|
||||
<Action :machineId="state.machineId"></Action>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { setArgs } from '@/apis/action';
|
||||
import { injectGlobalData } from '@/provide';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Action from './Action.vue';
|
||||
export default {
|
||||
props:['machineId'],
|
||||
components:{Action},
|
||||
setup(props) {
|
||||
const { t } = useI18n();
|
||||
const globalData = injectGlobalData();
|
||||
const state = reactive({
|
||||
list: globalData.value.config.Client.Action.Args[globalData.value.config.Client.Server.Host] || ''
|
||||
machineId: props.machineId,
|
||||
});
|
||||
const handleSave = () => {
|
||||
try {
|
||||
if (state.list && typeof (JSON.parse(state.list)) != 'object') {
|
||||
ElMessage.error(t('action.jsonError'));
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(t('action.jsonError'));
|
||||
return;
|
||||
}
|
||||
const json = JSON.parse(JSON.stringify(globalData.value.config.Client.Action.Args));
|
||||
json[globalData.value.config.Client.Server.Host] = state.list;
|
||||
setArgs(json).then(() => {
|
||||
ElMessage.success(t('common.oper'));
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
ElMessage.error(t('common.operFail'));
|
||||
});;
|
||||
}
|
||||
|
||||
return { state, handleSave }
|
||||
return { state }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
</el-table>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="state.showNodes" :title="$t('server.relayTitle')" width="760" top="2vh">
|
||||
<el-dialog v-model="state.showNodes" :title="$t('server.relayTitle')" width="98%" top="2vh">
|
||||
<div>
|
||||
<el-table :data="state.nodes" size="small" border height="600">
|
||||
<el-table-column property="Name" :label="$t('server.relayName')">
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
<OperRoutes v-if="oper.showRoutes" v-model="oper.showRoutes" ></OperRoutes>
|
||||
<OperFirewall v-if="oper.showFirewall" v-model="oper.showFirewall" ></OperFirewall>
|
||||
<OperWakeup v-if="oper.showWakeup" v-model="oper.showWakeup" ></OperWakeup>
|
||||
<OperTransport v-if="oper.showTransport" v-model="oper.showTransport" ></OperTransport>
|
||||
<OperAction v-if="oper.showAction" v-model="oper.showAction" ></OperAction>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@@ -85,6 +87,8 @@ import { provideOper } from './oper'
|
||||
import OperRoutes from './OperRoutes.vue'
|
||||
import OperFirewall from './OperFirewall.vue'
|
||||
import OperWakeup from './OperWakeup.vue'
|
||||
import OperTransport from './OperTransport.vue'
|
||||
import OperAction from './OperAction.vue'
|
||||
|
||||
|
||||
export default {
|
||||
@@ -98,7 +102,7 @@ export default {
|
||||
Forward,ForwardEdit,
|
||||
SForwardEdit ,UpdaterConfirm,
|
||||
Stopwatch,
|
||||
Oper,OperRoutes,OperFirewall,OperWakeup
|
||||
Oper,OperRoutes,OperFirewall,OperWakeup ,OperTransport,OperAction
|
||||
},
|
||||
setup(props) {
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
|
||||
<el-dropdown-item v-if="scope.row.isSelf && hasWakeupSelf" @click="handleWakeup(scope.row.MachineId,scope.row.MachineName)"><el-icon><VideoPlay /></el-icon>唤醒</el-dropdown-item>
|
||||
<el-dropdown-item v-else-if="hasWakeupOther" @click="handleWakeup(scope.row.MachineId,scope.row.MachineName)"><el-icon><VideoPlay /></el-icon>唤醒</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item v-if="hasTransport" @click="handleTransport(scope.row.MachineId,scope.row.MachineName)"><el-icon><Orange /></el-icon>打洞协议</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item v-if="scope.row.isSelf && hasActionSelf" @click="handleAction(scope.row.MachineId,scope.row.MachineName)"><el-icon><Lock /></el-icon>验证参数</el-dropdown-item>
|
||||
<el-dropdown-item v-else-if="hasActionOther" @click="handleAction(scope.row.MachineId,scope.row.MachineName)"><el-icon><Lock /></el-icon>验证参数</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@@ -36,18 +41,17 @@
|
||||
import { signInDel } from '@/apis/signin';
|
||||
import { exit } from '@/apis/updater';
|
||||
import { injectGlobalData } from '@/provide';
|
||||
import { Delete,SwitchButton,ArrowDown, Flag,HelpFilled,Platform,Paperclip,CircleCheck,VideoPlay } from '@element-plus/icons-vue'
|
||||
import { Delete,SwitchButton,ArrowDown, Flag,HelpFilled,Platform,Paperclip,CircleCheck,VideoPlay,Orange,Lock } from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { computed } from 'vue';
|
||||
import { useAccess } from './access';
|
||||
import { setApiPassword } from '@/apis/access';
|
||||
import { useFlow } from './flow';
|
||||
import { useTuntap } from './tuntap';
|
||||
import { useOper } from './oper';
|
||||
|
||||
export default {
|
||||
emits:['refresh','access'],
|
||||
components:{Delete,SwitchButton,ArrowDown,Flag,HelpFilled,Platform,Paperclip,CircleCheck,VideoPlay},
|
||||
components:{Delete,SwitchButton,ArrowDown,Flag,HelpFilled,Platform,Paperclip,CircleCheck,VideoPlay,Orange,Lock},
|
||||
setup (props,{emit}) {
|
||||
|
||||
const globalData = injectGlobalData();
|
||||
@@ -68,6 +72,10 @@ export default {
|
||||
|
||||
const hasWakeupSelf = computed(()=>globalData.value.hasAccess('WakeupSelf'));
|
||||
const hasWakeupOther = computed(()=>globalData.value.hasAccess('WakeupOther'));
|
||||
const hasTransport = computed(()=>globalData.value.hasAccess('Transport'));
|
||||
|
||||
const hasActionSelf = computed(()=>globalData.value.hasAccess('Action'));
|
||||
const hasActionOther = computed(()=>globalData.value.hasAccess('ActionOther'));
|
||||
|
||||
const flow = useFlow();
|
||||
const oper = useOper();
|
||||
@@ -145,10 +153,21 @@ export default {
|
||||
oper.value.device.name = name;
|
||||
oper.value.showWakeup = true;
|
||||
}
|
||||
const handleTransport = (id,name)=>{
|
||||
oper.value.device.id = id;
|
||||
oper.value.device.name = name;
|
||||
oper.value.showTransport = true;
|
||||
}
|
||||
const handleAction = (id,name)=>{
|
||||
oper.value.device.id = id;
|
||||
oper.value.device.name = name;
|
||||
oper.value.showAction = true;
|
||||
}
|
||||
|
||||
return {accessList,handleDel,handleExit,hasReboot,hasRemove,hasAccess,handleShowAccess,handleAccess,
|
||||
hasApiPassword,hasApiPasswordOther,handleApiPassword,handleStopwatch,handleRoutes,
|
||||
hasFirewallSelf,hasFirewallOther,handleFirewall,hasWakeupSelf,hasWakeupOther,handleWakeup
|
||||
hasFirewallSelf,hasFirewallOther,handleFirewall,hasWakeupSelf,hasWakeupOther,handleWakeup,
|
||||
hasTransport,handleTransport,hasActionSelf,hasActionOther,handleAction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
src/linker.web/src/views/full/devices/OperAction.vue
Normal file
42
src/linker.web/src/views/full/devices/OperAction.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<el-dialog v-model="state.show" append-to=".app-wrap" :title="`[${state.machineName}]上的验证参数`" top="1vh" width="98%">
|
||||
<div>
|
||||
<Action :machineId="state.machineId"></Action>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script>
|
||||
import { reactive, watch } from 'vue';
|
||||
import Action from '../action/Action.vue'
|
||||
import { useOper } from './oper';
|
||||
export default {
|
||||
props: ['modelValue'],
|
||||
emits: ['update:modelValue'],
|
||||
components: {
|
||||
Action
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const oper = useOper();
|
||||
|
||||
const state = reactive({
|
||||
show: true,
|
||||
machineId: oper.value.device.id,
|
||||
machineName: oper.value.device.name
|
||||
});
|
||||
watch(() => state.show, (val) => {
|
||||
if (!val) {
|
||||
setTimeout(() => {
|
||||
emit('update:modelValue', val);
|
||||
emit('change')
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
return {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-dialog v-model="state.show" append-to=".app-wrap" :title="`[${state.machineName}]上的防火墙`" top="1vh" width="760">
|
||||
<el-dialog v-model="state.show" append-to=".app-wrap" :title="`[${state.machineName}]上的防火墙`" top="1vh" width="98%">
|
||||
<div>
|
||||
<Firewall :machineId="state.machineId"></Firewall>
|
||||
</div>
|
||||
|
||||
42
src/linker.web/src/views/full/devices/OperTransport.vue
Normal file
42
src/linker.web/src/views/full/devices/OperTransport.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<el-dialog v-model="state.show" append-to=".app-wrap" :title="`[${state.machineName}]上的打洞协议`" top="1vh" width="98%">
|
||||
<div>
|
||||
<Transport :machineId="state.machineId"></Transport>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script>
|
||||
import { reactive, watch } from 'vue';
|
||||
import Transport from '../transport/Transport.vue'
|
||||
import { useOper } from './oper';
|
||||
export default {
|
||||
props: ['modelValue'],
|
||||
emits: ['update:modelValue'],
|
||||
components: {
|
||||
Transport
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const oper = useOper();
|
||||
|
||||
const state = reactive({
|
||||
show: true,
|
||||
machineId: oper.value.device.id,
|
||||
machineName: oper.value.device.name
|
||||
});
|
||||
watch(() => state.show, (val) => {
|
||||
if (!val) {
|
||||
setTimeout(() => {
|
||||
emit('update:modelValue', val);
|
||||
emit('change')
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
return {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-dialog v-model="state.show" append-to=".app-wrap" :title="`[${state.machineName}]上的唤醒`" top="1vh" width="760">
|
||||
<el-dialog v-model="state.show" append-to=".app-wrap" :title="`[${state.machineName}]上的唤醒`" top="1vh" width="98%">
|
||||
<div>
|
||||
<Wakeup :machineId="state.machineId"></Wakeup>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<PlanList ref="planDom" :machineid="machineId" category="sforward" :handles="state.handles">
|
||||
<el-dialog v-model="state.show" @open="handleOnShowList" append-to=".app-wrap" :title="`【${machineName}】的内网穿透`" top="2vh" width="760">
|
||||
<el-dialog v-model="state.show" @open="handleOnShowList" append-to=".app-wrap" :title="`【${machineName}】的内网穿透`" top="2vh" width="98%">
|
||||
<div>
|
||||
<div class="t-c head">
|
||||
<el-button type="success" size="small" @click="handleAdd" :loading="state.loading">添加</el-button>
|
||||
|
||||
@@ -6,6 +6,8 @@ export const provideOper = () => {
|
||||
showRoutes:false,
|
||||
showFirewall:false,
|
||||
showWakeup:false,
|
||||
showTransport:false,
|
||||
showAction:false,
|
||||
});
|
||||
provide(operSymbol, oper);
|
||||
return {
|
||||
|
||||
@@ -30,12 +30,23 @@
|
||||
<el-button type="success" size="small" :loading="state.loading" @click="handleAdd()">+</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="mgt-1" v-if="state.isSelf">
|
||||
<Sync name="Firewall"></Sync>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body flex-1 relative">
|
||||
<el-table class="firewall" stripe border :data="state.data" size="small" :height="`${state.height}px`" :row-class-name="tableRowClassName">
|
||||
<el-table-column prop="SrcName" :label="$t('firewall.srcName')" >
|
||||
<el-table-column prop="Checked" width="30" v-if="state.isSelf" >
|
||||
<template #header>
|
||||
<el-checkbox size="small" v-model="state.checkAll" :indeterminate="state.checkAllIndeterminate" @change="handleCheckAllChange"></el-checkbox>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row.Checked" size="small" @change="handleChecked(scope.row)"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="SrcName" :label="$t('firewall.srcName')" >
|
||||
<template v-slot="scope">
|
||||
<div class="ellipsis" :title="scope.row.SrcName">{{ scope.row.SrcName }}</div>
|
||||
</template>
|
||||
@@ -45,11 +56,11 @@
|
||||
<el-table-column prop="Protocol" :label="$t('firewall.protocol')" width="70">
|
||||
<template v-slot="scope">{{handleShowProtocol(scope.row.Protocol)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="Action" :label="$t('firewall.action')" width="50">
|
||||
<el-table-column prop="Action" :label="$t('firewall.action')" width="56">
|
||||
<template v-slot="scope">{{handleShowAction(scope.row.Action)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="OrderBy" :label="$t('firewall.orderby')" width="60"></el-table-column>
|
||||
<el-table-column prop="Disabled" :label="$t('firewall.disabled')" width="70">
|
||||
<el-table-column prop="OrderBy" :label="$t('firewall.orderby')" width="56"></el-table-column>
|
||||
<el-table-column prop="Disabled" :label="$t('firewall.disabled')" width="66">
|
||||
<template v-slot="scope">
|
||||
<div>
|
||||
<el-switch v-model="scope.row.Disabled" size="small"
|
||||
@@ -63,7 +74,7 @@
|
||||
<div class="ellipsis" :title="scope.row.Remark">{{ scope.row.Remark }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="80" fixed="right">
|
||||
<el-table-column width="60" fixed="right">
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<el-switch v-model="state.state" size="small" :title="$t('firewall.switch')"
|
||||
@@ -73,14 +84,15 @@
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<a href="javascript:void(0);" class="a-line mgr-1" @click="handleAdd(scope.row)">{{$t('firewall.edit')}}</a>
|
||||
<el-popconfirm
|
||||
:confirm-button-text="$t('common.confirm')" :cancel-button-text="$t('common.cancel')"
|
||||
:title="$t('firewall.delConfirm')" @confirm="handleDel(scope.row)">
|
||||
<template #reference>
|
||||
<a href="javascript:void(0);" class="a-line">{{$t('firewall.del')}}</a>
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">{{$t('common.option')}}<el-icon><ArrowDown /></el-icon></span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="handleAdd(scope.row)">{{$t('firewall.edit')}}</el-dropdown-item>
|
||||
<el-dropdown-item @click="handleDel(scope.row)">{{$t('firewall.del')}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -93,13 +105,15 @@
|
||||
import { reactive,computed, ref } from '@vue/reactivity'
|
||||
import { onMounted, provide } from '@vue/runtime-core'
|
||||
import { injectGlobalData } from '@/provide'
|
||||
import { addFirewall, getFirewall, removeFirewall, stateFirewall } from '@/apis/firewall';
|
||||
import { addFirewall, checkFirewall, getFirewall, removeFirewall, stateFirewall } from '@/apis/firewall';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {ArrowDown} from'@element-plus/icons-vue'
|
||||
import Add from './Add.vue';
|
||||
import Sync from '../sync/Index.vue'
|
||||
export default {
|
||||
props: ['machineId','machineName'],
|
||||
components:{Add},
|
||||
components:{Add,Sync,ArrowDown},
|
||||
setup(props,{emit}) {
|
||||
|
||||
const {t} = useI18n();
|
||||
@@ -107,6 +121,8 @@ export default {
|
||||
const globalData = injectGlobalData();
|
||||
const state = reactive({
|
||||
loading: true,
|
||||
checkAll:false,
|
||||
checkAllIndeterminate:false,
|
||||
search:{
|
||||
MachineId:props.machineId || globalData.value.config.Client.Id,
|
||||
Data:{
|
||||
@@ -135,7 +151,10 @@ export default {
|
||||
data:[],
|
||||
state:1,
|
||||
height:computed(()=>globalData.value.height - 140),
|
||||
showAdd:false
|
||||
showAdd:false,
|
||||
isSelf:computed(()=>{
|
||||
return state.search.MachineId == globalData.value.config.Client.Id;
|
||||
})
|
||||
})
|
||||
const loadData = () => {
|
||||
state.loading = true;
|
||||
@@ -143,12 +162,40 @@ export default {
|
||||
state.loading = false;
|
||||
state.data = res.List;
|
||||
state.state = res.State;
|
||||
checkAllStatus();
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
state.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
const checkAllStatus = ()=>{
|
||||
state.checkAll = state.data.some(item=>item.Checked);
|
||||
state.checkAllIndeterminate = state.checkAll && state.data.every(item=>item.Checked) == false;
|
||||
}
|
||||
const handleCheckAllChange = (value)=>{
|
||||
state.data.forEach(c=>{
|
||||
c.Checked = value;
|
||||
});
|
||||
checkAllStatus();
|
||||
|
||||
const ids = state.data.map(item=>item.Id);
|
||||
if(ids.length > 0){
|
||||
_checkFirewall(ids,value);
|
||||
}
|
||||
}
|
||||
const handleChecked = (value)=>{
|
||||
checkAllStatus();
|
||||
_checkFirewall([value.Id],value.Checked);
|
||||
}
|
||||
const _checkFirewall = (ids,value)=>{
|
||||
state.loading = true;
|
||||
checkFirewall({
|
||||
Ids:ids,
|
||||
IsChecked:value
|
||||
}).then(()=>{state.loading = false;}).catch(()=>{state.loading = false;});
|
||||
}
|
||||
|
||||
const handleSetState = ()=>{
|
||||
state.loading = true;
|
||||
stateFirewall({
|
||||
@@ -164,11 +211,17 @@ export default {
|
||||
});
|
||||
}
|
||||
const handleDel = (row) => {
|
||||
state.loading = true;
|
||||
removeFirewall({
|
||||
MachineId:state.search.MachineId,
|
||||
Id:row.Id
|
||||
}).then(()=>{loadData(); state.loading = false;}).catch(()=>{state.loading = false;});
|
||||
ElMessageBox.confirm(t('firewall.delConfirm'), t('common.confirm'), {
|
||||
confirmButtonText: t('common.confirm'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
state.loading = true;
|
||||
removeFirewall({
|
||||
MachineId:state.search.MachineId,
|
||||
Id:row.Id
|
||||
}).then(()=>{loadData(); state.loading = false;}).catch(()=>{state.loading = false})
|
||||
}).catch(()=>{});
|
||||
}
|
||||
const handleDsiabled = (row)=>{
|
||||
state.loading = true;
|
||||
@@ -223,7 +276,8 @@ export default {
|
||||
|
||||
return {
|
||||
state, loadData, tableRowClassName,handleSetState,handleAdd,handleDel,
|
||||
handleShowProtocol,handleShowAction,handleDsiabled
|
||||
handleShowProtocol,handleShowAction,handleDsiabled,
|
||||
handleCheckAllChange,handleChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,4 +300,8 @@ export default {
|
||||
color: #c83f08;
|
||||
}
|
||||
}
|
||||
.el-dropdown-link{
|
||||
font-size:1.2rem;
|
||||
padding-top:.3rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="formDom" :model="state.form" :rules="state.rules" label-width="12rem">
|
||||
<el-form-item label="" label-width="0">
|
||||
<div class="t-c w-100">端口为0则不监听</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="" label-width="0">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
<el-dialog v-model="state.show" :title="$t('server.relayTitle')" width="760" top="2vh">
|
||||
<el-dialog v-model="state.show" :title="$t('server.relayTitle')" width="98%" top="2vh">
|
||||
<div>
|
||||
<el-table :data="state.nodes" size="small" border height="500">
|
||||
<el-table-column property="Name" :label="$t('server.relayName')">
|
||||
|
||||
@@ -1,93 +1,27 @@
|
||||
<template>
|
||||
<div class="transport-wrap">
|
||||
<el-table stripe :data="state.list" border size="small" width="100%" :height="`${state.height}px`" >
|
||||
<el-table-column prop="Name" :label="$t('status.tunnelName')" width="120"></el-table-column>
|
||||
<el-table-column prop="Label" :label="$t('status.tunnelLabel')"></el-table-column>
|
||||
<el-table-column prop="ProtocolType" :label="$t('status.tunnelProtocol')" width="60"></el-table-column>
|
||||
<el-table-column prop="BufferSize" :label="$t('status.tunnelBuffer')" width="100">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.BufferSize" placeholder="Select" size="small" @change="handleSave">
|
||||
<el-option v-for="(item,index) in state.bufferSize" :key="index" :label="item" :value="index"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="Reverse" :label="$t('status.tunnelReverse')" width="64">
|
||||
<template #default="scope">
|
||||
<el-switch :disabled="scope.row.DisableReverse" v-model="scope.row.Reverse" @change="handleSave" inline-prompt :active-text="$t('status.tunnelYes')" :inactive-text="$t('status.tunnelNo')" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="SSL" :label="$t('status.tunnelSSL')" width="60">
|
||||
<template #default="scope">
|
||||
<el-switch :disabled="scope.row.DisableSSL" v-model="scope.row.SSL" @change="handleSave" inline-prompt :active-text="$t('status.tunnelYes')" :inactive-text="$t('status.tunnelNo')" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="Disabled" :label="$t('status.tunnelDisanbled')" width="64">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.Disabled" @change="handleSave" inline-prompt :active-text="$t('status.tunnelYes')" :inactive-text="$t('status.tunnelNo')" style="--el-switch-on-color: red; --el-switch-off-color: #ddd" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="Order" :label="$t('status.tunnelSort')" width="104" fixed="right">
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<strong>{{ $t('status.tunnelSort') }}</strong><span class="flex-1"></span><Sync name="TunnelTransports"></Sync>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<el-input-number v-model="scope.row.Order" :min="1" :max="255" @change="handleOrderChange" size="small" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<Transport :machineId="state.machineId" :height="state.height"></Transport>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { setTunnelTransports } from '@/apis/tunnel';
|
||||
import { computed, reactive } from 'vue';
|
||||
import Transport from './Transport.vue'
|
||||
import { injectGlobalData } from '@/provide';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { computed,reactive, watch } from 'vue'
|
||||
import { Delete,Plus,Top,Bottom } from '@element-plus/icons-vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Sync from '../sync/Index.vue'
|
||||
export default {
|
||||
label:'打洞协议',
|
||||
name:'transports',
|
||||
order:2,
|
||||
components:{Delete,Plus,Top,Bottom,Sync},
|
||||
setup(props) {
|
||||
const {t} = useI18n();
|
||||
props:['machineId'],
|
||||
components:{Transport},
|
||||
setup(props) {
|
||||
const globalData = injectGlobalData();
|
||||
const state = reactive({
|
||||
list:globalData.value.config.Client.Tunnel.Transports.sort((a,b)=>a.Order - b.Order),
|
||||
height: computed(()=>globalData.value.height-20),
|
||||
bufferSize:globalData.value.bufferSize
|
||||
});
|
||||
watch(()=>globalData.value.config.Client.Tunnel.Transports,()=>{
|
||||
state.list = globalData.value.config.Client.Tunnel.Transports.sort((a,b)=>a.Order - b.Order);
|
||||
});
|
||||
|
||||
const handleOrderChange = ()=>{
|
||||
handleSave(state.list);
|
||||
}
|
||||
const handleSave = ()=>{
|
||||
state.list = state.list.slice().sort((a,b)=>a.Order - b.Order);
|
||||
setTunnelTransports(state.list).then(()=>{
|
||||
ElMessage.success(t('common.oper'));
|
||||
}).catch((err)=>{
|
||||
console.log(err);
|
||||
ElMessage.error(t('common.operFail'));
|
||||
});
|
||||
}
|
||||
|
||||
return {state,handleOrderChange,handleSave}
|
||||
machineId:props.machineId,
|
||||
height:computed(()=>globalData.value.height-20)
|
||||
})
|
||||
return {state}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="stylus" scoped>
|
||||
.transport-wrap{
|
||||
padding:1rem
|
||||
font-size:1.3rem;
|
||||
color:#555;
|
||||
a{color:#333;}
|
||||
}
|
||||
</style>
|
||||
96
src/linker.web/src/views/full/transport/Transport.vue
Normal file
96
src/linker.web/src/views/full/transport/Transport.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<el-table stripe :data="state.list" border size="small" width="100%" :height="`${state.height}px`" >
|
||||
<el-table-column prop="Name" :label="$t('status.tunnelName')" width="120"></el-table-column>
|
||||
<el-table-column prop="Label" :label="$t('status.tunnelLabel')"></el-table-column>
|
||||
<el-table-column prop="ProtocolType" :label="$t('status.tunnelProtocol')" width="60"></el-table-column>
|
||||
<el-table-column prop="BufferSize" :label="$t('status.tunnelBuffer')" width="100">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.BufferSize" placeholder="Select" size="small" @change="handleSave">
|
||||
<el-option v-for="(item,index) in state.bufferSize" :key="index" :label="item" :value="index"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="Reverse" :label="$t('status.tunnelReverse')" width="64">
|
||||
<template #default="scope">
|
||||
<el-switch :disabled="scope.row.DisableReverse" v-model="scope.row.Reverse" @change="handleSave" inline-prompt :active-text="$t('status.tunnelYes')" :inactive-text="$t('status.tunnelNo')" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="SSL" :label="$t('status.tunnelSSL')" width="60">
|
||||
<template #default="scope">
|
||||
<el-switch :disabled="scope.row.DisableSSL" v-model="scope.row.SSL" @change="handleSave" inline-prompt :active-text="$t('status.tunnelYes')" :inactive-text="$t('status.tunnelNo')" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="Disabled" :label="$t('status.tunnelDisanbled')" width="64">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.Disabled" @change="handleSave" inline-prompt :active-text="$t('status.tunnelYes')" :inactive-text="$t('status.tunnelNo')" style="--el-switch-on-color: red; --el-switch-off-color: #ddd" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="Order" :label="$t('status.tunnelSort')" width="104" fixed="right">
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<strong>{{ $t('status.tunnelSort') }}</strong><span class="flex-1"></span><Sync name="TunnelTransports" v-if="state.isSelf"></Sync>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<el-input-number v-model="scope.row.Order" :min="1" :max="255" @change="handleOrderChange" size="small" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<script>
|
||||
import { getTunnelTransports, setTunnelTransports } from '@/apis/tunnel';
|
||||
import { injectGlobalData } from '@/provide';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { computed,onMounted,reactive, watch } from 'vue'
|
||||
import { Delete,Plus,Top,Bottom } from '@element-plus/icons-vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Sync from '../sync/Index.vue'
|
||||
export default {
|
||||
props:['machineId','height'],
|
||||
components:{Delete,Plus,Top,Bottom,Sync},
|
||||
setup(props) {
|
||||
const {t} = useI18n();
|
||||
const globalData = injectGlobalData();
|
||||
const state = reactive({
|
||||
list:[],
|
||||
height:computed(()=>props.height),
|
||||
bufferSize:globalData.value.bufferSize,
|
||||
machineid:props.machineId || globalData.value.config.Client.Id,
|
||||
isSelf:computed(()=>{
|
||||
return state.machineid === globalData.value.config.Client.Id;
|
||||
})
|
||||
});
|
||||
|
||||
const getData = ()=>{
|
||||
getTunnelTransports(state.machineid).then((res)=>{
|
||||
state.list = res.sort((a,b)=>a.Order - b.Order);
|
||||
});
|
||||
}
|
||||
|
||||
const handleOrderChange = ()=>{
|
||||
handleSave(state.list);
|
||||
}
|
||||
const handleSave = ()=>{
|
||||
state.list = state.list.slice().sort((a,b)=>a.Order - b.Order);
|
||||
setTunnelTransports({
|
||||
machineid:state.machineid,
|
||||
data:state.list
|
||||
}).then(()=>{
|
||||
ElMessage.success(t('common.oper'));
|
||||
}).catch((err)=>{
|
||||
console.log(err);
|
||||
ElMessage.error(t('common.operFail'));
|
||||
});
|
||||
}
|
||||
onMounted(()=>{
|
||||
getData();
|
||||
});
|
||||
|
||||
return {state,handleOrderChange,handleSave}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="stylus" scoped>
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
v1.8.3
|
||||
2025-06-14 20:50:30
|
||||
2025-06-16 17:35:00
|
||||
1. 一些累计更新
|
||||
2. 修复socks5,解决CPU爆满问题,增加本地域名解析,支持HTTP代理
|
||||
3. 优化唤醒模块
|
||||
|
||||
Reference in New Issue
Block a user