This commit is contained in:
snltty
2025-03-02 17:55:04 +08:00
parent b49bd135bf
commit d12d4a1b99
49 changed files with 1082 additions and 247 deletions

View File

@@ -35,7 +35,7 @@ jobs:
release_name: v1.6.9.${{ steps.date.outputs.today }}
draft: false
prerelease: false
body: "1. 修复litedb抢锁超时导致客户端登录失败问题\r\n2. 同步信标服务器\r\n3. 其它一些修复优化"
body: "1. 优化linux下路由跟踪问题\r\n2. 优化linux下获取本机IP问题\r\n3. 增加ICS让win7+、win server2008+支持NAT\r\n4. 增加中继卡密\r\n5. 增加内外穿透定时开关功能\r\n6. 优化管理页面连接接口的体验\r\n7. 优化一些UI体验去除同步页面将同步功能放置各个实际的位置\r\n8. 其它一些修复优化"
- name: publish projects
run: ./publish.bat
- name: upload-win-x86-oss

View File

@@ -57,10 +57,11 @@ sidebar_position: 1
:::
:::danger[win10以下]
1. win7 或 win8 可能需要安装一些环境,才能运行
2. <a href="https://aka.ms/vs/16/release/vc_redist.x64.exe" target="_blank">Microsoft Visual C++ 2015-2019 Redistributable </a>
3. <a href="https://www.microsoft.com/download/details.aspx?id=47442" target="_blank">KB3063858 </a>
4. <a href="https://www.microsoft.com/zh-cn/download/details.aspx?id=46148" target="_blank">KB3033929 </a>
1. win7、win8、win server 2008、win server 2008 R2 可能需要安装一些环境,才能运行
2. <a href="https://aka.ms/vs/16/release/vc_redist.x64.exe" target="_blank">Microsoft Visual C++ 2015-2019 Redistributable 运行库</a>
3. <a href="https://www.microsoft.com/download/details.aspx?id=47442" target="_blank">KB3063858 运行库</a>
4. <a href="https://www.microsoft.com/zh-cn/download/details.aspx?id=46148" target="_blank">KB3033929 全球化补丁</a>
5. <a href="https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/thank-you/net462-web-installer" target="_blank">netframework4.6.2 ICS NAT运行库</a>
:::

View File

@@ -1,24 +0,0 @@
---
sidebar_position: 4
---
# 1.1.2、RRAS
:::tip[说明]
1. 操作不当可能会导致网络无法访问,请谨慎操作
2. 有局限性,创建虚拟网卡后,需要重新`“配置并启用路由和远程访问”`
![](./img/rras1.png)
![](./img/rras2.png)
![](./img/rras3.png)
![](./img/rras4.png)
![](./img/rras5.png)
![](./img/rras6.png)
![](./img/rras7.png)
![](./img/rras8.png)
![](./img/rras9.png)
![](./img/rras10.png)
![](./img/rras11.png)
![](./img/rras12.png)
![](./img/rras13.png)
:::

View File

@@ -1,12 +1,11 @@
---
sidebar_position: 5
sidebar_position: 4
---
# 1.1.3、ICS
:::tip[说明]
1. 操作不当可能会导致网络无法访问,请谨慎操作
![](./img/ics1.png)
1. 如果系统没有<a href="https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/thank-you/net462-web-installer" target="_blank">netframework4.6.2</a>,就下载安装一下
2. 需要linker v1.7.0+版本
3. 剩下的交给linker
:::

View File

@@ -1,5 +1,5 @@
---
sidebar_position: 6
sidebar_position: 5
---
# 1.2、网对网

View File

@@ -9,9 +9,9 @@ namespace linker.libs
{
public static string Windows(string arg, string[] commands)
{
return Execute("cmd.exe", arg, commands,out string error);
return Execute("cmd.exe", arg, commands, out string error);
}
public static string Windows(string arg, string[] commands,out string error)
public static string Windows(string arg, string[] commands, out string error)
{
return Execute("cmd.exe", arg, commands, out error);
}
@@ -23,14 +23,14 @@ namespace linker.libs
error = "PowerShell is not installed";
return string.Empty;
}
return Execute("powershell.exe", arg, commands,out error);
return Execute("powershell.exe", arg, commands, out error);
}
public static string Linux(string arg, string[] commands)
{
return Execute("/bin/bash", arg, commands, out string error);
}
public static string Linux(string arg, string[] commands,out string error)
public static string Linux(string arg, string[] commands, out string error)
{
return Execute("/bin/bash", arg, commands, out error);
}
@@ -56,7 +56,7 @@ namespace linker.libs
return proc;
}
public static string Execute(string fileName, string arg, string[] commands,out string error)
public static string Execute(string fileName, string arg, string[] commands, out string error)
{
using Process proc = new Process();
proc.StartInfo.WorkingDirectory = Path.GetFullPath(Path.Join("./"));
@@ -80,12 +80,9 @@ namespace linker.libs
proc.StandardInput.WriteLine("exit");
proc.StandardInput.Close();
string output = proc.StandardOutput.ReadToEnd();
error = proc.StandardError.ReadToEnd();
string output = string.Empty;
if (string.IsNullOrWhiteSpace(error))
{
output = proc.StandardOutput.ReadToEnd();
}
proc.WaitForExit();
proc.Close();
proc.Dispose();

View File

@@ -36,7 +36,7 @@ namespace linker.libs
public bool Equals(IPNetwork other)
{
return ToValue() == other.ToValue();
return ToValue() == other.ToValue();
}
}
@@ -142,7 +142,7 @@ namespace linker.libs
{
result = new List<IPAddress>();
string str = CommandHelper.Linux(string.Empty, new string[] { $"traceroute {server} -4 -m 5" });
string str = CommandHelper.Linux(string.Empty, new string[] { $"traceroute {server} -4 -m 5 -w 1" });
string[] lines = str.Split(Environment.NewLine);
Regex regex = new Regex(@"(\d+\.\d+\.\d+\.\d+)");
@@ -191,33 +191,41 @@ namespace linker.libs
private static byte[] ipv6LocalBytes = new byte[] { 254, 128, 0, 0, 0, 0, 0, 0 };
public static IPAddress[] GetIPV6()
private static IPAddress[] GetIP()
{
try
{
return Dns.GetHostAddresses(Dns.GetHostName())
.Where(c => c.AddressFamily == AddressFamily.InterNetworkV6)
.Where(c => c.GetAddressBytes().AsSpan(0, 8).SequenceEqual(ipv6LocalBytes) == false).Distinct().ToArray();
return Dns.GetHostEntry(Dns.GetHostName()).AddressList;
}
catch (Exception)
{
try
{
return NetworkInterface.GetAllNetworkInterfaces()
.SelectMany(c => c.GetIPProperties().UnicastAddresses)
.Select(c => c.Address)
.ToArray();
}
catch (Exception)
{
}
}
return Array.Empty<IPAddress>();
}
public static IPAddress[] GetIPV6()
{
return GetIP()
.Where(c => c.AddressFamily == AddressFamily.InterNetworkV6)
.Where(c => c.GetAddressBytes().AsSpan(0, 8).SequenceEqual(ipv6LocalBytes) == false).Distinct().ToArray(); ;
}
public static IPAddress[] GetIPV4()
{
try
{
return Dns.GetHostEntry(Dns.GetHostName()).AddressList
.Where(c => c.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
return GetIP()
.Where(c => c.AddressFamily == AddressFamily.InterNetwork)
.Where(c => c.IsIPv4MappedToIPv6 == false)
.Where(c => c.Equals(IPAddress.Loopback) == false)
.Distinct().ToArray();
}
catch (Exception)
{
}
return Array.Empty<IPAddress>();
}
public static byte ToPrefixLength(uint ip)

View File

@@ -38,7 +38,7 @@ namespace linker.libs.api
server = new WebSocketServer();
try
{
server.Start( port);
server.Start(port);
}
catch (Exception ex)
{
@@ -51,6 +51,13 @@ namespace linker.libs.api
{
header.SetHeaderValue(WebsocketHeaderKey.SecWebSocketExtensions, string.Empty);
}
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketProtocol, out string _password1);
LoggerHelper.Instance.Info($"websocket client password {_password1} eq {password}");
}
return res;
};
server.OnOpen = (connection) =>

View File

@@ -313,6 +313,8 @@ namespace linker.libs.websocket
{
token.Connectrion.SendFrameClose(WebSocketFrameInfo.EnumCloseStatus.ExtendsError);
token.Connectrion.Close();
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Error($"websocket opcode error:{token.FrameInfo.Opcode}");
return;
}
}
@@ -356,6 +358,8 @@ namespace linker.libs.websocket
header.StatusCode = HttpStatusCode.MethodNotAllowed;
token.Connectrion.ConnectResponse(header);
token.Connectrion.Close();
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Error("websocket SecWebSocketKey error");
return;
}
@@ -370,6 +374,8 @@ namespace linker.libs.websocket
header.StatusCode = HttpStatusCode.Unauthorized;
token.Connectrion.ConnectResponse(header);
token.Connectrion.Close();
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Error("websocket OnConnecting false");
}
}
@@ -479,6 +485,9 @@ namespace linker.libs.websocket
{
Disposabled = true;
Connectrion.Close();
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Error($"websocket connection clear");
}
}

View File

@@ -136,6 +136,9 @@
[AccessDisplay("重置所有接口密码")]
SetApiPasswordOther = (ulong)1 << 42,
[AccessDisplay("管理中继CDKEY")]
RelayCdkey = (ulong)1 << 43,
Full = ulong.MaxValue >> 64 - 52,
}

View File

@@ -1,9 +1,12 @@
using linker.libs.api;
using linker.libs;
using linker.libs.api;
using linker.libs.extends;
using linker.messenger.api;
using linker.messenger.relay.client;
using linker.messenger.relay.client.transport;
using linker.messenger.relay.messenger;
using linker.messenger.relay.server;
using linker.messenger.signin;
namespace linker.messenger.relay
{
@@ -15,19 +18,23 @@ namespace linker.messenger.relay
private readonly RelayClientTestTransfer relayTestTransfer;
private readonly RelayClientTransfer relayTransfer;
private readonly IRelayClientStore relayClientStore;
private readonly SignInClientState signInClientState;
private readonly IMessengerSender messengerSender;
private readonly ISerializer serializer;
private readonly ISignInClientStore signInClientStore;
public RelayApiController(RelayClientTestTransfer relayTestTransfer, RelayClientTransfer relayTransfer, IRelayClientStore relayClientStore)
public RelayApiController(RelayClientTestTransfer relayTestTransfer, RelayClientTransfer relayTransfer, IRelayClientStore relayClientStore,
SignInClientState signInClientState, IMessengerSender messengerSender, ISerializer serializer, ISignInClientStore signInClientStore)
{
this.relayTestTransfer = relayTestTransfer;
this.relayTransfer = relayTransfer;
this.relayClientStore = relayClientStore;
this.signInClientState = signInClientState;
this.messengerSender = messengerSender;
this.serializer = serializer;
this.signInClientStore = signInClientStore;
}
/// <summary>
/// 设置中继协议
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
[Access(AccessValue.Config)]
public bool SetServers(ApiControllerParamsInfo param)
{
@@ -50,6 +57,90 @@ namespace linker.messenger.relay
return true;
}
public async Task<bool> AccessCdkey(ApiControllerParamsInfo param)
{
var resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
MessengerId = (ushort)RelayMessengerIds.AccessCdkey,
Payload = serializer.Serialize(relayClientStore.Server.SecretKey)
});
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
}
[Access(AccessValue.RelayCdkey)]
public async Task<bool> AddCdkey(ApiControllerParamsInfo param)
{
RelayServerCdkeyInfo info = param.Content.DeJson<RelayServerCdkeyInfo>();
var resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
MessengerId = (ushort)RelayMessengerIds.AddCdkey,
Payload = serializer.Serialize(new RelayServerCdkeyAddInfo
{
Data = info,
SecretKey = relayClientStore.Server.SecretKey
})
});
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
}
[Access(AccessValue.RelayCdkey)]
public async Task<bool> DelCdkey(ApiControllerParamsInfo param)
{
var resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
MessengerId = (ushort)RelayMessengerIds.DelCdkey,
Payload = serializer.Serialize(new RelayServerCdkeyDelInfo
{
Id = param.Content,
SecretKey = relayClientStore.Server.SecretKey
})
});
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
}
[Access(AccessValue.RelayCdkey)]
public async Task<RelayServerCdkeyPageResultInfo> PageCdkey(ApiControllerParamsInfo param)
{
RelayServerCdkeyPageRequestInfo info = param.Content.DeJson<RelayServerCdkeyPageRequestInfo>();
info.SecretKey = relayClientStore.Server.SecretKey;
var resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
MessengerId = (ushort)RelayMessengerIds.PageCdkey,
Payload = serializer.Serialize(info)
});
if (resp.Code == MessageResponeCodes.OK)
{
return serializer.Deserialize<RelayServerCdkeyPageResultInfo>(resp.Data.Span);
}
return new RelayServerCdkeyPageResultInfo();
}
public async Task<RelayServerCdkeyPageResultInfo> MyCdkey(ApiControllerParamsInfo param)
{
RelayServerCdkeyPageRequestInfo info = param.Content.DeJson<RelayServerCdkeyPageRequestInfo>();
info.SecretKey = relayClientStore.Server.SecretKey;
info.UserId = signInClientStore.Server.UserId;
var resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
MessengerId = (ushort)RelayMessengerIds.PageCdkey,
Payload = serializer.Serialize(info)
});
if (resp.Code == MessageResponeCodes.OK)
{
return serializer.Deserialize<RelayServerCdkeyPageResultInfo>(resp.Data.Span);
}
return new RelayServerCdkeyPageResultInfo();
}
}
public sealed class RelayConnectInfo

View File

@@ -18,10 +18,12 @@ namespace linker.messenger.relay.client
private Dictionary<string, List<Action<ITunnelConnection>>> OnConnected { get; } = new Dictionary<string, List<Action<ITunnelConnection>>>();
private readonly IRelayClientStore relayClientStore;
public RelayClientTransfer(IMessengerSender messengerSender,ISerializer serializer,IRelayClientStore relayClientStore,SignInClientState signInClientState,IMessengerStore messengerStore)
private readonly ISignInClientStore signInClientStore;
public RelayClientTransfer(IMessengerSender messengerSender, ISerializer serializer, IRelayClientStore relayClientStore, SignInClientState signInClientState, IMessengerStore messengerStore, ISignInClientStore signInClientStore)
{
this.relayClientStore = relayClientStore;
Transports = new List<IRelayClientTransport> {
this.signInClientStore = signInClientStore;
Transports = new List<IRelayClientTransport> {
new RelayClientTransportSelfHost(messengerSender,serializer,relayClientStore,signInClientState,messengerStore),
};
LoggerHelper.Instance.Info($"load relay transport:{string.Join(",", Transports.Select(c => c.GetType().Name))}");
@@ -87,7 +89,8 @@ namespace linker.messenger.relay.client
TransactionId = transactionId,
TransportName = transport.Name,
SSL = relayClientStore.Server.SSL,
NodeId = nodeId
NodeId = nodeId,
UserId = signInClientStore.Server.UserId,
};
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)

View File

@@ -112,6 +112,11 @@ namespace linker.messenger.relay.client.transport
/// 是否ssl
/// </summary>
public bool SSL { get; set; } = true;
/// <summary>
/// UserId
/// </summary>
public string UserId { get; set; }
}

View File

@@ -45,14 +45,18 @@ namespace linker.messenger.relay.messenger
private readonly RelayServerMasterTransfer relayServerTransfer;
private readonly RelayServerValidatorTransfer relayValidatorTransfer;
private readonly ISerializer serializer;
private readonly IRelayServerCdkeyStore relayServerCdkeyStore;
private readonly IRelayServerStore relayServerStore;
public RelayServerMessenger(IMessengerSender messengerSender, SignInServerCaching signCaching, ISerializer serializer, RelayServerMasterTransfer relayServerTransfer, RelayServerValidatorTransfer relayValidatorTransfer)
public RelayServerMessenger(IMessengerSender messengerSender, SignInServerCaching signCaching, ISerializer serializer, RelayServerMasterTransfer relayServerTransfer, RelayServerValidatorTransfer relayValidatorTransfer, IRelayServerCdkeyStore relayServerCdkeyStore, IRelayServerStore relayServerStore)
{
this.messengerSender = messengerSender;
this.signCaching = signCaching;
this.relayServerTransfer = relayServerTransfer;
this.relayValidatorTransfer = relayValidatorTransfer;
this.serializer = serializer;
this.relayServerCdkeyStore = relayServerCdkeyStore;
this.relayServerStore = relayServerStore;
}
/// <summary>
@@ -89,7 +93,7 @@ namespace linker.messenger.relay.messenger
[MessengerId((ushort)RelayMessengerIds.RelayAsk)]
public async Task RelayAsk(IConnection connection)
{
client.transport.RelayInfo info = serializer.Deserialize<client.transport.RelayInfo>(connection.ReceiveRequestWrap.Payload.Span);
RelayInfo info = serializer.Deserialize<RelayInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cacheFrom) == false || signCaching.TryGet(info.RemoteMachineId, out SignCacheInfo cacheTo) == false || cacheFrom.GroupId != cacheTo.GroupId)
{
connection.Write(serializer.Serialize(new RelayAskResultInfo { }));
@@ -105,9 +109,11 @@ namespace linker.messenger.relay.messenger
string error = await relayValidatorTransfer.Validate(info, cacheFrom, cacheTo);
result.Nodes = relayServerTransfer.GetNodes(string.IsNullOrWhiteSpace(error));
List<RelayServerCdkeyInfo> cdkeys = await relayServerCdkeyStore.Get(info.UserId);
if (result.Nodes.Count > 0)
{
result.FlowingId = relayServerTransfer.AddRelay(cacheFrom.MachineId, cacheFrom.MachineName, cacheTo.MachineId, cacheTo.MachineName, cacheFrom.GroupId);
result.FlowingId = relayServerTransfer.AddRelay(cacheFrom.MachineId, cacheFrom.MachineName, cacheTo.MachineId, cacheTo.MachineName, cacheFrom.GroupId, cdkeys);
}
connection.Write(serializer.Serialize(result));
@@ -167,5 +173,89 @@ namespace linker.messenger.relay.messenger
connection.Write(Helper.FalseArray);
}
}
/// <summary>
/// 检查权限
/// </summary>
/// <param name="connection"></param>
/// <returns></returns>
[MessengerId((ushort)RelayMessengerIds.AccessCdkey)]
public void AccessCdkey(IConnection connection)
{
string secretKey = serializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
connection.Write(relayServerStore.SecretKey == secretKey ? Helper.TrueArray : Helper.FalseArray);
}
/// <summary>
/// 添加CDKEY
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)RelayMessengerIds.AddCdkey)]
public async Task AddCdkey(IConnection connection)
{
RelayServerCdkeyAddInfo info = serializer.Deserialize<RelayServerCdkeyAddInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) == false)
{
connection.Write(Helper.FalseArray);
return;
}
if (relayServerStore.SecretKey != info.SecretKey)
{
connection.Write(Helper.FalseArray);
return;
}
await relayServerCdkeyStore.Add(info.Data);
connection.Write(Helper.TrueArray);
}
/// <summary>
/// 删除Cdkey
/// </summary>
/// <param name="connection"></param>
/// <returns></returns>
[MessengerId((ushort)RelayMessengerIds.DelCdkey)]
public async Task DelCdkey(IConnection connection)
{
RelayServerCdkeyDelInfo info = serializer.Deserialize<RelayServerCdkeyDelInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) == false)
{
connection.Write(Helper.FalseArray);
return;
}
if (relayServerStore.SecretKey != info.SecretKey)
{
connection.Write(Helper.FalseArray);
return;
}
await relayServerCdkeyStore.Del(info.Id);
connection.Write(Helper.TrueArray);
}
/// <summary>
/// 查询CDKEY
/// </summary>
/// <param name="connection"></param>
/// <returns></returns>
[MessengerId((ushort)RelayMessengerIds.PageCdkey)]
public async Task PageCdkey(IConnection connection)
{
RelayServerCdkeyPageRequestInfo info = serializer.Deserialize<RelayServerCdkeyPageRequestInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) == false)
{
connection.Write(serializer.Serialize(new RelayServerCdkeyPageResultInfo { }));
return;
}
if (relayServerStore.SecretKey != info.SecretKey && string.IsNullOrWhiteSpace(info.UserId))
{
connection.Write(serializer.Serialize(new RelayServerCdkeyPageResultInfo { }));
return;
}
var page = await relayServerCdkeyStore.Get(info);
connection.Write(serializer.Serialize(page));
}
}
}

View File

@@ -15,6 +15,11 @@
NodeDelay = 2106,
NodeDelayForward = 2107,
AddCdkey = 2108,
PageCdkey = 2109,
DelCdkey = 2110,
AccessCdkey = 2111,
Max = 2199
}
}

View File

@@ -0,0 +1,104 @@
namespace linker.messenger.relay.server
{
public interface IRelayServerCdkeyStore
{
public Task<bool> Add(RelayServerCdkeyInfo info);
public Task<bool> Del(string id);
/// <summary>
/// 获取有效的CDKEY
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
public Task<List<RelayServerCdkeyInfo>> Get(string userid);
public Task<RelayServerCdkeyPageResultInfo> Get(RelayServerCdkeyPageRequestInfo relayServerCdkeyPageRequestInfo);
}
public sealed partial class RelayServerCdkeyPageRequestInfo
{
public int Page { get; set; }
public int Size { get; set; }
public string Order { get; set; }
public string Sort { get; set; }
public string UserId { get; set; }
public string Remark { get; set; }
public string SecretKey { get; set; }
}
public sealed partial class RelayServerCdkeyPageResultInfo
{
public int Page { get; set; }
public int Size { get; set; }
public int Count { get; set; }
public List<RelayServerCdkeyInfo> List { get; set; }
}
public sealed partial class RelayServerCdkeyAddInfo
{
public string SecretKey { get; set; }
public RelayServerCdkeyInfo Data { get; set; }
}
public sealed partial class RelayServerCdkeyDelInfo
{
public string SecretKey { get; set; }
public string Id { get; set; }
}
/// <summary>
/// 中继CDKEY
/// </summary>
public sealed partial class RelayServerCdkeyInfo
{
public string Id { get; set; }
/// <summary>
/// 用户标识
/// </summary>
public string UserId { get; set; }
/// <summary>
/// KEY
/// </summary>
public string CdKey { get; set; }
/// <summary>
/// 添加时间
/// </summary>
public DateTime AddTime { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime StartTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime EndTime { get; set; }
/// <summary>
/// 允许节点
/// </summary>
public List<string> Nodes { get; set; }
/// <summary>
/// 带宽Mbps
/// </summary>
public double Bandwidth { get; set; }
/// <summary>
/// 流量
/// </summary>
public ulong MaxBytes { get; set; }
/// <summary>
/// 剩余流量
/// </summary>
public ulong LastBytes { get; set; }
/// <summary>
/// 原价
/// </summary>
public double Memory { get; set; }
/// <summary>
/// 支付金额
/// </summary>
public double PayMemory { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
}
}

View File

@@ -26,7 +26,7 @@ namespace linker.messenger.relay.server
}
public ulong AddRelay(string fromid, string fromName, string toid, string toName, string groupid)
public ulong AddRelay(string fromid, string fromName, string toid, string toName, string groupid, List<RelayServerCdkeyInfo> cdkeys)
{
ulong flowingId = Interlocked.Increment(ref relayFlowingId);
@@ -37,7 +37,8 @@ namespace linker.messenger.relay.server
FromName = fromName,
ToId = toid,
ToName = toName,
GroupId = groupid
GroupId = groupid,
Cdkey = cdkeys
};
bool added = relayCaching.TryAdd($"{fromid}->{toid}->{flowingId}", cache, 15000);
if (added == false) return 0;
@@ -81,7 +82,7 @@ namespace linker.messenger.relay.server
}
catch (Exception ex)
{
if(LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}

View File

@@ -256,6 +256,8 @@ namespace linker.messenger.relay.server
public string ToId { get; set; }
public string ToName { get; set; }
public string GroupId { get; set; }
public List<RelayServerCdkeyInfo> Cdkey { get; set; }
}
public sealed class RelayWrapInfo

View File

@@ -25,7 +25,7 @@ namespace linker.messenger.serializer.memorypack
MemoryPackFormatterProvider.Register(new SignInResponseInfoFormatter());
MemoryPackFormatterProvider.Register(new SignInConfigSetNameInfoFormatter());
MemoryPackFormatterProvider.Register(new SignInNamesResponseItemInfoFormatter());
MemoryPackFormatterProvider.Register(new SyncInfoFormatter());
@@ -55,6 +55,12 @@ namespace linker.messenger.serializer.memorypack
MemoryPackFormatterProvider.Register(new RelayAskResultInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayCacheInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayMessageInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyPageRequestInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyPageResultInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyAddInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyDelInfoFormatter());
MemoryPackFormatterProvider.Register(new AccessUpdateInfoFormatter());
MemoryPackFormatterProvider.Register(new AccessInfoFormatter());
@@ -83,7 +89,7 @@ namespace linker.messenger.serializer.memorypack
MemoryPackFormatterProvider.Register(new FlowItemInfoFormatter());
MemoryPackFormatterProvider.Register(new FlowReportNetInfoFormatter());
MemoryPackFormatterProvider.Register(new FlowInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayFlowItemInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayFlowRequestInfoFormatter());

View File

@@ -58,7 +58,7 @@ namespace linker.messenger.serializer.memorypack
}
}
[MemoryPackable]
public readonly partial struct SerializableRelayInfo
@@ -89,11 +89,14 @@ namespace linker.messenger.serializer.memorypack
[MemoryPackInclude]
bool SSL => info.SSL;
[MemoryPackInclude]
string UserId => info.UserId;
[MemoryPackConstructor]
SerializableRelayInfo(string fromMachineId, string fromMachineName,
string remoteMachineId, string remoteMachineName,
string transactionId, string secretKey, string transportName, ulong flowingId,
string nodeId, IPEndPoint server, bool ssl)
string nodeId, IPEndPoint server, bool ssl, string userid)
{
var info = new RelayInfo
{
@@ -107,7 +110,8 @@ namespace linker.messenger.serializer.memorypack
TransactionId = transactionId,
TransportName = transportName,
SecretKey = secretKey,
Server = server
Server = server,
UserId = userid
};
this.info = info;
}
@@ -295,7 +299,6 @@ namespace linker.messenger.serializer.memorypack
[MemoryPackable]
public readonly partial struct SerializableRelayCacheInfo
{
@@ -315,8 +318,11 @@ namespace linker.messenger.serializer.memorypack
[MemoryPackInclude]
string GroupId => info.GroupId;
[MemoryPackInclude, MemoryPackAllowSerialize]
List<RelayServerCdkeyInfo> Cdkey => info.Cdkey;
[MemoryPackConstructor]
SerializableRelayCacheInfo(ulong flowId, string fromId, string fromName, string toId, string toName, string groupId)
SerializableRelayCacheInfo(ulong flowId, string fromId, string fromName, string toId, string toName, string groupId, List<RelayServerCdkeyInfo> cdkey)
{
var info = new RelayCacheInfo
{
@@ -326,6 +332,7 @@ namespace linker.messenger.serializer.memorypack
GroupId = groupId,
ToId = toId,
ToName = toName,
Cdkey = cdkey
};
this.info = info;
}
@@ -427,4 +434,340 @@ namespace linker.messenger.serializer.memorypack
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializableRelayServerCdkeyInfo
{
[MemoryPackIgnore]
public readonly RelayServerCdkeyInfo info;
[MemoryPackInclude]
string Id => info.Id;
[MemoryPackInclude]
string UserId => info.UserId;
[MemoryPackInclude]
string CdKey => info.CdKey;
[MemoryPackInclude]
DateTime AddTime => info.AddTime;
[MemoryPackInclude]
DateTime StartTime => info.StartTime;
[MemoryPackInclude]
DateTime EndTime => info.EndTime;
[MemoryPackInclude]
List<string> Nodes => info.Nodes;
[MemoryPackInclude]
double Bandwidth => info.Bandwidth;
[MemoryPackInclude]
ulong MaxBytes => info.MaxBytes;
[MemoryPackInclude]
ulong LastBytes => info.LastBytes;
[MemoryPackInclude]
double Memory => info.Memory;
[MemoryPackInclude]
double PayMemory => info.PayMemory;
[MemoryPackInclude]
string Remark => info.Remark;
[MemoryPackConstructor]
SerializableRelayServerCdkeyInfo(string id, string userid, string cdKey, DateTime addTime, DateTime startTime, DateTime endTime,
List<string> nodes, double bandwidth, ulong maxBytes, ulong lastBytes, double memory, double payMemory, string remark)
{
var info = new RelayServerCdkeyInfo
{
Id = id,
UserId = userid,
CdKey = cdKey,
AddTime = addTime,
StartTime = startTime,
EndTime = endTime,
Nodes = nodes,
Bandwidth = bandwidth,
MaxBytes = maxBytes,
LastBytes = lastBytes,
Memory = memory,
PayMemory = payMemory,
Remark = remark
};
this.info = info;
}
public SerializableRelayServerCdkeyInfo(RelayServerCdkeyInfo info)
{
this.info = info;
}
}
public class RelayServerCdkeyInfoFormatter : MemoryPackFormatter<RelayServerCdkeyInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref RelayServerCdkeyInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializableRelayServerCdkeyInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref RelayServerCdkeyInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializableRelayServerCdkeyInfo>();
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializableRelayServerCdkeyAddInfo
{
[MemoryPackIgnore]
public readonly RelayServerCdkeyAddInfo info;
[MemoryPackInclude]
string SecretKey => info.SecretKey;
[MemoryPackInclude, MemoryPackAllowSerialize]
RelayServerCdkeyInfo Data => info.Data;
[MemoryPackConstructor]
SerializableRelayServerCdkeyAddInfo(string secretKey, RelayServerCdkeyInfo data)
{
var info = new RelayServerCdkeyAddInfo
{
SecretKey = secretKey,
Data = data
};
this.info = info;
}
public SerializableRelayServerCdkeyAddInfo(RelayServerCdkeyAddInfo info)
{
this.info = info;
}
}
public class RelayServerCdkeyAddInfoFormatter : MemoryPackFormatter<RelayServerCdkeyAddInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref RelayServerCdkeyAddInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializableRelayServerCdkeyAddInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref RelayServerCdkeyAddInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializableRelayServerCdkeyAddInfo>();
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializableRelayServerCdkeyDelInfo
{
[MemoryPackIgnore]
public readonly RelayServerCdkeyDelInfo info;
[MemoryPackInclude]
string SecretKey => info.SecretKey;
[MemoryPackInclude]
string Id => info.Id;
[MemoryPackConstructor]
SerializableRelayServerCdkeyDelInfo(string secretKey, string id)
{
var info = new RelayServerCdkeyDelInfo
{
SecretKey = secretKey,
Id = id
};
this.info = info;
}
public SerializableRelayServerCdkeyDelInfo(RelayServerCdkeyDelInfo info)
{
this.info = info;
}
}
public class RelayServerCdkeyDelInfoFormatter : MemoryPackFormatter<RelayServerCdkeyDelInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref RelayServerCdkeyDelInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializableRelayServerCdkeyDelInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref RelayServerCdkeyDelInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializableRelayServerCdkeyDelInfo>();
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializableRelayServerCdkeyPageRequestInfo
{
[MemoryPackIgnore]
public readonly RelayServerCdkeyPageRequestInfo info;
[MemoryPackInclude]
int Page => info.Page;
[MemoryPackInclude]
int Size => info.Size;
[MemoryPackInclude]
string Order => info.Order;
[MemoryPackInclude]
string Sort => info.Sort;
[MemoryPackInclude]
string UserId => info.UserId;
[MemoryPackInclude]
string Remark => info.Remark;
[MemoryPackInclude]
string SecretKey => info.SecretKey;
[MemoryPackConstructor]
SerializableRelayServerCdkeyPageRequestInfo(int page, int size, string order, string sort, string userid, string remark, string secretKey)
{
var info = new RelayServerCdkeyPageRequestInfo
{
Sort = sort,
Order = order,
Size = size,
Page = page,
UserId = userid,
Remark = remark,
SecretKey = secretKey
};
this.info = info;
}
public SerializableRelayServerCdkeyPageRequestInfo(RelayServerCdkeyPageRequestInfo info)
{
this.info = info;
}
}
public class RelayServerCdkeyPageRequestInfoFormatter : MemoryPackFormatter<RelayServerCdkeyPageRequestInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref RelayServerCdkeyPageRequestInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializableRelayServerCdkeyPageRequestInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref RelayServerCdkeyPageRequestInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializableRelayServerCdkeyPageRequestInfo>();
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializableRelayServerCdkeyPageResultInfo
{
[MemoryPackIgnore]
public readonly RelayServerCdkeyPageResultInfo info;
[MemoryPackInclude]
int Page => info.Page;
[MemoryPackInclude]
int Size => info.Size;
[MemoryPackInclude]
int Count => info.Count;
[MemoryPackInclude]
List<RelayServerCdkeyInfo> List => info.List;
[MemoryPackConstructor]
SerializableRelayServerCdkeyPageResultInfo(int page, int size, int count, List<RelayServerCdkeyInfo> list)
{
var info = new RelayServerCdkeyPageResultInfo
{
Count = count,
List = list,
Size = size,
Page = page
};
this.info = info;
}
public SerializableRelayServerCdkeyPageResultInfo(RelayServerCdkeyPageResultInfo info)
{
this.info = info;
}
}
public class RelayServerCdkeyPageResultInfoFormatter : MemoryPackFormatter<RelayServerCdkeyPageResultInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref RelayServerCdkeyPageResultInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializableRelayServerCdkeyPageResultInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref RelayServerCdkeyPageResultInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializableRelayServerCdkeyPageResultInfo>();
value = wrapped.info;
}
}
}

View File

@@ -42,7 +42,8 @@ namespace linker.messenger.signin
public string Name { get; set; } = string.Empty;
public string Host { get; set; } = string.Empty;
public string SecretKey { get; set; } = string.Empty;
public string UserId { get; set; } = Guid.NewGuid().ToString();
}
public sealed class SignInConfigServerInfo

View File

@@ -45,6 +45,11 @@
/// <param name="secretKey"></param>
public void SetSecretKey(string secretKey);
/// <summary>
/// 设置用户id
/// </summary>
/// <param name="userid"></param>
public void SetUserId(string userid);
/// <summary>
/// 信标服务器
/// </summary>
/// <param name="host"></param>

View File

@@ -59,6 +59,7 @@ namespace linker.messenger.store.file
serviceCollection.AddSingleton<IRelayServerStore, RelayServerStore>();
serviceCollection.AddSingleton<IRelayServerNodeStore, RelayServerNodeStore>();
serviceCollection.AddSingleton<IRelayServerMasterStore, RelayServerMasterStore>();
serviceCollection.AddSingleton<IRelayServerCdkeyStore, RelayServerCdkeyStore>();
serviceCollection.AddSingleton<ITunnelClientStore, TunnelClientStore>();
@@ -66,6 +67,7 @@ namespace linker.messenger.store.file
serviceCollection.AddSingleton<ISignInClientStore, SignInClientStore>();
serviceCollection.AddSingleton<ISignInServerStore, SignInServerStore>();
serviceCollection.AddSingleton<SignInSyncSecretKey>();
serviceCollection.AddSingleton<SignInSyncUserId>();
serviceCollection.AddSingleton<SignInSyncServer>();
serviceCollection.AddSingleton<SignInSyncGroupSecretKey>();
@@ -107,6 +109,7 @@ namespace linker.messenger.store.file
SyncTreansfer syncTreansfer = serviceProvider.GetService<SyncTreansfer>();
syncTreansfer.AddSyncs(new List<ISync> {
serviceProvider.GetService<SignInSyncSecretKey>(),
serviceProvider.GetService<SignInSyncUserId>(),
serviceProvider.GetService<SignInSyncGroupSecretKey>(),
serviceProvider.GetService<SignInSyncServer>(),

View File

@@ -0,0 +1,65 @@
using linker.messenger.relay.server;
using LiteDB;
namespace linker.messenger.store.file.relay
{
public sealed class RelayServerCdkeyStore : IRelayServerCdkeyStore
{
private readonly Storefactory dBfactory;
private readonly ILiteCollection<RelayServerCdkeyInfo> liteCollection;
public RelayServerCdkeyStore(Storefactory dBfactory, FileConfig fileConfig)
{
this.dBfactory = dBfactory;
liteCollection = dBfactory.GetCollection<RelayServerCdkeyInfo>("relayCdkey");
}
public async Task<bool> Add(RelayServerCdkeyInfo info)
{
if (string.IsNullOrWhiteSpace(info.Id))
{
info.Id = ObjectId.NewObjectId().ToString();
liteCollection.Insert(info);
}
else
{
liteCollection.Update(info);
}
return await Task.FromResult(true);
}
public async Task<bool> Del(string id)
{
return await Task.FromResult(liteCollection.Delete(id));
}
public async Task<List<RelayServerCdkeyInfo>> Get(string userid)
{
return await Task.FromResult(liteCollection.Find(x => x.UserId == userid && x.LastBytes > 0 && x.StartTime <= DateTime.Now && x.EndTime < DateTime.Now).ToList());
}
public async Task<RelayServerCdkeyPageResultInfo> Get(RelayServerCdkeyPageRequestInfo relayServerCdkeyPageRequestInfo)
{
ILiteQueryable<RelayServerCdkeyInfo> query = liteCollection.Query();
if (string.IsNullOrWhiteSpace(relayServerCdkeyPageRequestInfo.Order) == false)
{
query = query.OrderBy(relayServerCdkeyPageRequestInfo.Order, relayServerCdkeyPageRequestInfo.Sort == "asc" ? Query.Ascending : Query.Descending);
}
if (string.IsNullOrWhiteSpace(relayServerCdkeyPageRequestInfo.UserId) == false)
{
query = query.Where(x => x.UserId == relayServerCdkeyPageRequestInfo.UserId);
}
if (string.IsNullOrWhiteSpace(relayServerCdkeyPageRequestInfo.Remark) == false)
{
query = query.Where(x => x.Remark.Contains(relayServerCdkeyPageRequestInfo.Remark));
}
return await Task.FromResult(new RelayServerCdkeyPageResultInfo
{
Page = relayServerCdkeyPageRequestInfo.Page,
Size = relayServerCdkeyPageRequestInfo.Size,
Count = query.Count(),
List = query.Skip((relayServerCdkeyPageRequestInfo.Page - 1) * relayServerCdkeyPageRequestInfo.Size).Limit(relayServerCdkeyPageRequestInfo.Size).ToList()
});
}
}
}

View File

@@ -43,6 +43,11 @@ namespace linker.messenger.store.file.signIn
Server.SecretKey = secretKey;
config.Data.Update();
}
public void SetUserId(string userid)
{
Server.UserId = userid;
config.Data.Update();
}
public void SetHost(string host)
{
Server.Host = host;

View File

@@ -46,6 +46,27 @@ namespace linker.messenger.store.file.signIn
signInClientStore.SetSecretKey(serializer.Deserialize<string>(data.Span));
}
}
public sealed class SignInSyncUserId : ISync
{
public string Name => "SignInUserId";
private readonly ISignInClientStore signInClientStore;
private readonly ISerializer serializer;
public SignInSyncUserId(ISignInClientStore signInClientStore, ISerializer serializer)
{
this.signInClientStore = signInClientStore;
this.serializer = serializer;
}
public Memory<byte> GetData()
{
return serializer.Serialize(signInClientStore.Server.UserId);
}
public void SetData(Memory<byte> data)
{
signInClientStore.SetUserId(serializer.Deserialize<string>(data.Span));
}
}
public sealed class SignInSyncGroupSecretKey : ISync
{
public string Name => "GroupSecretKey";

View File

@@ -35,10 +35,13 @@ namespace linker.messenger.tunnel
{
LoggerHelper.Instance.Info($"tunnel route level getting.");
Info.RouteLevel = NetworkHelper.GetRouteLevel(signInClientStore.Server.Host, out List<IPAddress> ips);
Info.RouteIPs = ips.ToArray();
Info.LocalIPs = NetworkHelper.GetIPV6().Concat(NetworkHelper.GetIPV4()).ToArray();
LoggerHelper.Instance.Warning($"route ips:{string.Join(",", ips.Select(c => c.ToString()))}");
LoggerHelper.Instance.Warning($"tunnel local ips :{string.Join(",", Info.LocalIPs.Select(c => c.ToString()))}");
Info.RouteIPs = ips.ToArray();
var ipv6 = NetworkHelper.GetIPV6();
LoggerHelper.Instance.Warning($"tunnel local ip6 :{string.Join(",", ipv6.Select(c => c.ToString()))}");
var ipv4 = NetworkHelper.GetIPV4();
LoggerHelper.Instance.Warning($"tunnel local ip4 :{string.Join(",", ipv4.Select(c => c.ToString()))}");
Info.LocalIPs = ipv6.Concat(ipv4).ToArray();
LoggerHelper.Instance.Warning($"tunnel route level:{Info.RouteLevel}");
}

View File

@@ -22,6 +22,8 @@ namespace linker.tun
private IPAddress address;
private byte prefixLength = 24;
private string defaultInterfaceName = string.Empty;
private CancellationTokenSource tokenSource;
public LinkerWinTunDevice()
@@ -67,6 +69,7 @@ namespace linker.tun
AddIPV6();
GetWindowsInterfaceNum();
GetDefaultInterface();
tokenSource = new CancellationTokenSource();
return true;
@@ -115,7 +118,6 @@ namespace linker.tun
}
}
public void Shutdown()
{
tokenSource?.Cancel();
@@ -137,7 +139,6 @@ namespace linker.tun
interfaceNumber = 0;
}
public void SetMtu(int value)
{
CommandHelper.Windows(string.Empty, new string[] {
@@ -157,20 +158,18 @@ namespace linker.tun
IPAddress network = NetworkHelper.ToNetworkIP(this.address, NetworkHelper.ToPrefixValue(prefixLength));
CommandHelper.PowerShell($"New-NetNat -Name {Name} -InternalIPInterfaceAddressPrefix {network}/{prefixLength}", [], out error);
if (string.IsNullOrWhiteSpace(error) == false)
if (string.IsNullOrWhiteSpace(error))
{
error = "NetNat Not Supported";
string result = CommandHelper.Windows(string.Empty, ["netsh routing"]);
if (result.Contains("netsh routing ip"))
{
error = string.Empty;
}
else
{
error += "RRAS Not Supported";
}
return;
}
CommandHelper.Windows(string.Empty, [$"net start SharedAccess"]);
string result = CommandHelper.Windows(string.Empty, [$"linker.ics.exe {defaultInterfaceName} {Name} enable"]);
if (result.Contains($"enable success"))
{
return;
}
error = "NetNat and ICS not supported";
}
catch (Exception ex)
{
@@ -186,6 +185,7 @@ namespace linker.tun
{
CommandHelper.PowerShell($"start-service WinNat", [], out error);
CommandHelper.PowerShell($"Remove-NetNat -Name {Name} -Confirm:$false", [], out error);
CommandHelper.Windows(string.Empty, [$"linker.ics.exe {Name} disable"]);
}
catch (Exception ex)
{
@@ -327,6 +327,33 @@ namespace linker.tun
}
}
private void GetDefaultInterface()
{
string[] lines = CommandHelper.Windows(string.Empty, new string[] { $"route print" }).Split(Environment.NewLine);
foreach (var item in lines)
{
if (item.Trim().StartsWith("0.0.0.0"))
{
string[] arr = Regex.Replace(item.Trim(), @"\s+", " ").Split(' ');
IPAddress ip = IPAddress.Parse(arr[arr.Length - 2]);
foreach (var inter in NetworkInterface.GetAllNetworkInterfaces())
{
try
{
if (ip.Equals(inter.GetIPProperties().UnicastAddresses.FirstOrDefault(c => c.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).Address))
{
defaultInterfaceName = inter.Name;
return;
}
}
catch (Exception)
{
}
}
}
}
}
private void ClearRegistry()
{

View File

@@ -8,4 +8,7 @@ export const setRelaySubscribe = () => {
}
export const relayConnect = (data) => {
return sendWebsocketMsg('relay/Connect', data);
}
export const relayCdkeyAccess = () => {
return sendWebsocketMsg('relay/AccessCdkey');
}

View File

@@ -119,18 +119,34 @@ a.a-line {
padding-right: 1rem;
}
.m-r-1 {
margin-right: 1rem;
.mgr-1 {
margin-right: 1rem !important;
}
.m-l-1 {
.mgl-1 {
margin-left: 1rem;
}
.m-b-0 {
.mgl-2 {
margin-left: 2rem;
}
.mgl-3 {
margin-left: 3rem;
}
.mgb-0 {
margin-bottom: 0 !important;
}
.mgb-3 {
margin-bottom: 3rem;
}
.mgt-3 {
margin-top: 3rem;
}
table {
border-spacing: 0;
border-collapse: collapse;
@@ -174,6 +190,10 @@ span.split-pad10 {
color: #e68906 !important;
}
.disable {
color: #ddd !important;
}
.gateway {
&.green {
/* background: linear-gradient(270deg, #caff00, green, #0d6d23, #e38a00, green); */

View File

@@ -7,6 +7,7 @@ export default {
'common.operating': 'In operation',
'common.tips': 'Tips',
'common.option': 'Option',
'common.access': 'No access',
'head.home': 'Home',
'head.server': 'Server',
@@ -36,6 +37,8 @@ export default {
'server.messengerText': 'ip:port or domain:port',
'server.messengerSecretKey': 'Messenger SecretKey',
'server.messengerSecretKeyText': 'Messenger SecretKey',
'server.messengerUserId': 'User Id',
'server.messengerUserIdText': 'Your unique identifier,for card key (CDKEY).',
'permission.closed': 'Closed',
'permission.simple': 'Simple',
@@ -120,6 +123,8 @@ export default {
'server.relayPublic': 'Public',
'server.relayOper': 'Oper',
'server.relayUse': 'Use',
'server.relayMyCdkey': 'My CDKEY',
'server.relayCdkey': 'Manager CDKEY',
'server.sforwardSecretKey': 'Server forward secretKey',
'server.sforwardText': 'The server forward can be used when the key is correct',
@@ -148,5 +153,7 @@ export default {
'server.asyncRelaySecretKey': 'Relay secretKey',
'server.asyncSForwardSecretKey': 'Server forward secretKey',
'server.asyncUpdaterSecretKey': 'Update secretKey',
'server.asyncTunnelTransports': 'Tunnel transports'
'server.asyncTunnelTransports': 'Tunnel transports',
'server.asyncSignInUserId': 'User Id',
}

View File

@@ -7,6 +7,7 @@ export default {
'common.operating': '正在操作',
'common.tips': '提示',
'common.option': '选项',
'common.access': '无权限',
'head.home': '首页',
'head.server': '服务器',
@@ -35,6 +36,8 @@ export default {
'server.messengerText': 'ip:端口 或者 域名:端口',
'server.messengerSecretKey': '信标密钥',
'server.messengerSecretKeyText': '密钥正确时可连接服务器',
'server.messengerUserId': '用户id',
'server.messengerUserIdText': '你的唯一标识用于流量卡密CDKEY',
'permission.closed': '禁止通行',
'permission.simple': '简单管理',
@@ -122,6 +125,8 @@ export default {
'server.relayPublic': '公开',
'server.relayOper': '操作',
'server.relayUse': '使用',
'server.relayMyCdkey': '我的CDKEY',
'server.relayCdkey': '管理CDKEY',
'server.sforwardSecretKey': '服务器穿透密钥',
'server.sforwardText': '当密钥正确是可用',
@@ -150,5 +155,6 @@ export default {
'server.asyncRelaySecretKey': '中继密钥',
'server.asyncSForwardSecretKey': '服务器穿透密钥',
'server.asyncUpdaterSecretKey': '更新密钥',
'server.asyncTunnelTransports': '打洞协议'
'server.asyncTunnelTransports': '打洞协议',
'server.asyncSignInUserId': '用户唯一标识',
}

View File

@@ -6,7 +6,7 @@
接口 : <el-input v-model="state.api" style="width:70%"></el-input>
</div>
<div class="pdt-10">
秘钥 : <el-input type="password" v-model="state.psd" style="width:70%"></el-input>
秘钥 : <el-input show-password type="password" v-model="state.psd" style="width:70%"></el-input>
</div>
<div class="pdt-10">
<el-checkbox v-model="state.save" >保存密码</el-checkbox>

View File

@@ -16,7 +16,7 @@
<el-checkbox v-model="state.ruleForm.Multicast" label="禁用广播" size="large" />
<el-checkbox v-model="state.ruleForm.Nat" label="禁用NAT" size="large" />
</el-form-item>
<el-form-item prop="upgrade" class="m-b-0">
<el-form-item prop="upgrade" class="mgb-0">
<el-checkbox v-model="state.ruleForm.Upgrade" label="我很懂,我要使用高级功能(点对网和网对网)" size="large" />
</el-form-item>
<div class="upgrade-wrap" v-if="state.ruleForm.Upgrade">

View File

@@ -34,13 +34,13 @@
<div>
<template v-for="(item1,index) in tuntap.list[item.MachineId].Lans" :key="index">
<template v-if="item1.Disabled">
<div class="flex yellow" title="已禁用">{{ item1.IP }} / {{ item1.PrefixLength }}</div>
<div class="flex disable" title="已禁用">{{ item1.IP }} / {{ item1.PrefixLength }}</div>
</template>
<template v-else-if="item1.Exists">
<div class="flex yellow" title="与其它设备填写IP、或本机局域网IP有冲突">{{ item1.IP }} / {{ item1.PrefixLength }}</div>
</template>
<template v-else>
<div class="flex" title="正常使用" :class="{green:tuntap.list[item.MachineId].running}">{{ item1.IP }} / {{ item1.PrefixLength }}</div>
<div class="flex green" title="正常使用">{{ item1.IP }} / {{ item1.PrefixLength }}</div>
</template>
</template>
</div>

View File

@@ -32,6 +32,11 @@
</template>
</el-table-column>
<el-table-column prop="Oper" :label="$t('server.groupOper')" width="160">
<template #header>
<div class="flex">
<strong>{{ $t('server.groupOper') }}</strong><span class="flex-1"></span><Sync name="GroupSecretKey"></Sync>
</div>
</template>
<template #default="scope">
<div>
<el-popconfirm :title="$t('server.groupDelConfirm')" @confirm="handleDel(scope.$index)">
@@ -60,8 +65,9 @@ import { ElMessage } from 'element-plus';
import { computed, reactive, watch } from 'vue'
import { Delete,Plus,Select } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import Sync from '../sync/Index.vue'
export default {
components:{Delete,Plus,Select },
components:{Delete,Plus,Select,Sync },
setup(props) {
const {t} = useI18n();
const globalData = injectGlobalData();

View File

@@ -5,7 +5,7 @@
<div class="inner">
<div class="head flex">
<div>
<el-select v-model="state.type" @change="loadData" size="small" class="m-r-1" style="width: 6rem;">
<el-select v-model="state.type" @change="loadData" size="small" class="mgr-1" style="width: 6rem;">
<el-option :value="-1" label="all"></el-option>
<el-option :value="0" label="debug"></el-option>
<el-option :value="1" label="info"></el-option>

View File

@@ -1,88 +0,0 @@
<template>
<div :style="{ height: `${state.height}px` }">
<el-card shadow="never">
<template #header>{{ $t('server.asyncText') }}</template>
<div>
<el-checkbox v-model="state.checkAll" :indeterminate="state.isIndeterminate"
@change="handleCheckAllChange">{{
$t('server.asyncCheckAll') }}</el-checkbox>
<el-checkbox-group v-model="state.checkeds" @change="handleCheckedsChange">
<el-row>
<template v-for="name in state.names">
<el-col :span="8">
<el-checkbox :key="name.name" :label="name.label" :value="name.name">{{ name.label
}}</el-checkbox>
</el-col>
</template>
</el-row>
</el-checkbox-group>
</div>
<template #footer>
<div class="t-c">
<el-button type="success" @click="handleSync">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-card>
</div>
</template>
<script>
import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { computed, onMounted, reactive } from 'vue'
import { getSyncNames, setSync } from '@/apis/sync';
import { useI18n } from 'vue-i18n';
export default {
setup(props) {
const { t } = useI18n();
const globalData = injectGlobalData();
const state = reactive({
names: [],
checkAll: false,
isIndeterminate: false,
checkeds: [],
height: computed(() => globalData.value.height - 90),
});
const handleCheckAllChange = (val) => {
state.checkeds = val ? state.names.map(c => c.name) : [];
state.isIndeterminate = false
}
const handleCheckedsChange = (value) => {
const checkedCount = value.length
state.checkAll = checkedCount === state.names.length
state.isIndeterminate = checkedCount > 0 && checkedCount < state.names.length;
}
const labels = {
'SignInServer': t('server.asyncSignInServer'),
'SignInSecretKey': t('server.asyncSignInSecretKey'),
'GroupSecretKey': t('server.asyncGroupSecretKey'),
'RelaySecretKey': t('server.asyncRelaySecretKey'),
'SForwardSecretKey': t('server.asyncSForwardSecretKey'),
'UpdaterSecretKey': t('server.asyncUpdaterSecretKey'),
'TunnelTransports': t('server.asyncTunnelTransports')
}
onMounted(() => {
getSyncNames().then(res => {
state.names = res.map(c => {
return { name: c, label: labels[c] }
});
});
});
const handleSync = () => {
if (state.checkeds.length == 0) {
ElMessage.error(t('server.asyncSelect'));
return;
}
setSync(state.checkeds).then(res => {
ElMessage.success(t('common.oper'));
});
}
return { state, handleCheckAllChange, handleCheckedsChange, handleSync }
}
}
</script>
<style lang="stylus" scoped>
</style>

View File

@@ -1,32 +1,20 @@
<template>
<div class="servers-wrap" >
<el-tabs type="border-card" style="width:100%" v-model="state.tab">
<el-tab-pane :label="$t('server.messenger')" name="signin" v-if="hasConfig">
<SignInServers></SignInServers>
</el-tab-pane>
<el-tab-pane :label="$t('server.sync')" name="async" v-if="hasSync">
<Async></Async>
</el-tab-pane>
</el-tabs>
<SignInServers v-if="hasConfig"></SignInServers>
</div>
</template>
<script>
import { computed, reactive } from 'vue';
import { injectGlobalData } from '@/provide';
import SignInServers from './SignInServers.vue';
import Async from './Async.vue';
export default {
components:{SignInServers,Async},
components:{SignInServers},
setup(props) {
const globalData = injectGlobalData();
const hasConfig = computed(()=>globalData.value.hasAccess('Config'))
const hasSync = computed(()=>globalData.value.hasAccess('Sync'));
const state = reactive({
tab:'signin'
});
const state = reactive({});
return {
state,hasConfig,hasSync
state,hasConfig
}
}
}
@@ -38,9 +26,4 @@ export default {
color:#555;
a{color:#333;}
}
.el-checkbox{
vertical-align:middle;
margin-right:1rem;
}
</style>

View File

@@ -1,11 +1,18 @@
<template>
<el-form-item :label="$t('server.relaySecretKey')">
<el-input type="password" show-password v-model="state.list.SecretKey" maxlength="36" @change="handleSave" />
<el-checkbox v-model="state.list.SSL" :label="$t('server.relaySSL')" size="large" @change="handleSave" />
<el-checkbox v-model="state.list.Disabled" :label="$t('server.relayDisable')" size="large" @change="handleSave" />
<a href="javascript:;" @click="state.show=true" class="delay a-line" :class="{red:state.nodes.length==0,green:state.nodes.length>0}">
{{$t('server.relayNodes')}} : {{state.nodes.length}}
</a>
<div class="flex">
<el-input class="flex-1" type="password" show-password v-model="state.list.SecretKey" maxlength="36" @change="handleSave" />
<Sync class="mgl-1" name="RelaySecretKey"></Sync>
<div class="mgl-1">
<el-checkbox class="mgr-1" v-model="state.list.SSL" :label="$t('server.relaySSL')" @change="handleSave" />
<el-checkbox v-model="state.list.Disabled" :label="$t('server.relayDisable')" @change="handleSave" />
</div>
<a href="javascript:;" @click="state.show=true" class="mgl-1 delay a-line" :class="{red:state.nodes.length==0,green:state.nodes.length>0}">
{{$t('server.relayNodes')}} : {{state.nodes.length}}
</a>
<Relay></Relay>
</div>
</el-form-item>
<el-dialog v-model="state.show" :title="$t('server.relayTitle')" width="760" top="2vh">
@@ -60,7 +67,10 @@ import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { onMounted, onUnmounted, reactive, watch } from 'vue'
import { useI18n } from 'vue-i18n';
import Sync from '../sync/Index.vue'
import Relay from './relay/Index.vue'
export default {
components:{Sync,Relay},
setup(props) {
const {t} = useI18n();
const globalData = injectGlobalData();
@@ -103,5 +113,4 @@ export default {
}
</script>
<style lang="stylus" scoped>
.delay{margin-left:3rem;}
</style>

View File

@@ -2,7 +2,8 @@
<el-form-item :label="$t('server.sforwardSecretKey')">
<div class="flex">
<el-input class="flex-1" type="password" show-password v-model="state.SForwardSecretKey" maxlength="36" @blur="handleChange" />
<span>{{$t('server.sforwardText')}}</span>
<Sync class="mgl-1" name="SForwardSecretKey"></Sync>
<span class="mgl-1">{{$t('server.sforwardText')}}</span>
</div>
</el-form-item>
</template>
@@ -11,7 +12,9 @@ import { getSForwardSecretKey,setSForwardSecretKey } from '@/apis/sforward';
import { ElMessage } from 'element-plus';
import {onMounted, reactive } from 'vue'
import { useI18n } from 'vue-i18n';
import Sync from '../sync/Index.vue'
export default {
components:{Sync},
setup(props) {
const {t} = useI18n();
const state = reactive({

View File

@@ -7,16 +7,25 @@
<el-form-item :label="$t('server.messengerAddr')">
<div class="flex">
<el-input class="flex-1" v-model="state.list.Host" @change="handleSave" />
<span>{{$t('server.messengerText')}}</span>
<Sync class="mgl-1" name="SignInServer"></Sync>
<span class="mgl-1">{{$t('server.messengerText')}}</span>
</div>
</el-form-item>
<el-form-item :label="$t('server.messengerSecretKey')">
<div class="flex">
<el-input class="flex-1" type="password" show-password maxlength="36" v-model="state.list.SecretKey" @change="handleSave" />
<span>{{$t('server.messengerSecretKeyText')}}</span>
<Sync class="mgl-1" name="SignInSecretKey"></Sync>
<span class="mgl-1">{{$t('server.messengerSecretKeyText')}}</span>
</div>
</el-form-item>
<RelayServers></RelayServers>
<el-form-item :label="$t('server.messengerUserId')">
<div class="flex">
<el-input class="flex-1" type="password" show-password maxlength="36" v-model="state.list.UserId" @change="handleSave" />
<Sync class="mgl-1" name="SignInUserId"></Sync>
<span class="mgl-1">{{$t('server.messengerUserIdText')}}</span>
</div>
</el-form-item>
<RelayServers class="mgt-2"></RelayServers>
<SForward></SForward>
<Updater></Updater>
</el-form>
@@ -38,8 +47,9 @@ import SForward from './SForward.vue';
import Updater from './Updater.vue';
import RelayServers from './RelayServers.vue';
import { useI18n } from 'vue-i18n';
import Sync from '../sync/Index.vue'
export default {
components:{SForward,Updater,RelayServers},
components:{SForward,Updater,RelayServers,Sync},
setup(props) {
const {t} = useI18n();
const globalData = injectGlobalData();

View File

@@ -2,10 +2,11 @@
<el-form-item :label="$t('server.updaterSecretKey')">
<div class="flex">
<el-input class="flex-1" type="password" show-password v-model="state.secretKey" maxlength="36" @blur="handleChange"/>
<span>{{$t('server.updaterText')}}</span>
<Sync class="mgl-1" name="UpdaterSecretKey"></Sync>
<span class="mgl-1">{{$t('server.updaterText')}}</span>
</div>
</el-form-item>
<el-form-item :label="$t('server.updaterRate')">
<!-- <el-form-item :label="$t('server.updaterRate')">
<div>
<div>
<el-input-number v-model="state.year" :min="0" :max="99" style="width:12rem" @change="handleSecChange" /> {{$t('server.updaterY') }}
@@ -18,7 +19,7 @@
<el-input-number v-model="state.sec" :min="0" :max="99" style="width:12rem" @change="handleSecChange"/> {{$t('server.updaterS') }}
</div>
</div>
</el-form-item>
</el-form-item> -->
</template>
<script>
import { getSecretKey,setSecretKey, setUpdateInterval } from '@/apis/updater';
@@ -26,7 +27,9 @@ import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { onMounted, reactive } from 'vue'
import { useI18n } from 'vue-i18n';
import Sync from '../sync/Index.vue'
export default {
components:{Sync},
setup(props) {
const {t} = useI18n();
const globalData = injectGlobalData();

View File

@@ -0,0 +1,33 @@
<template>
<a href="javascript:;" class="mgl-1 a-line">{{$t('server.relayMyCdkey')}}</a>
<a v-if="state.showManager && hasRelayCdkey" href="javascript:;" class="mgl-1 a-line">{{$t('server.relayCdkey')}}</a>
</template>
<script>
import { relayCdkeyAccess } from '@/apis/relay';
import { injectGlobalData } from '@/provide';
import { computed, onMounted, reactive } from 'vue';
export default {
setup () {
const globalData = injectGlobalData();
const hasRelayCdkey = computed(()=>globalData.value.hasAccess('RelayCdkey'));
const state = reactive({
showManager:false
});
onMounted(()=>{
relayCdkeyAccess().then(res=>{
state.showManager = res;
}).catch(()=>{})
})
return {state,hasRelayCdkey}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -56,7 +56,6 @@ export default {
const hasExport = computed(()=>globalData.value.hasAccess('Export'));
const onlyNode = computed(()=>globalData.value.config.Client.OnlyNode);
const machineId = computed(()=>globalData.value.config.Client.Id);
console.log(globalData.value.config.Client);
const state = reactive({
show: false,
loading:false,

View File

@@ -0,0 +1,45 @@
<template>
<div>
<el-button class="btn" size="small" @click="handleSync"><el-icon><Share /></el-icon></el-button>
</div>
</template>
<script>
import { setSync } from '@/apis/sync';
import { injectGlobalData } from '@/provide';
import {Share} from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
export default {
props:['name'],
components: {Share},
setup (props) {
const { t } = useI18n();
const globalData = injectGlobalData();
const hasSync = computed(()=>globalData.value.hasAccess('Sync'));
const handleSync = ()=>{
if(!hasSync.value){
ElMessage.success(t('common.access'));
return;
}
ElMessageBox.confirm(`${t('server.sync')} ${t(`server.async${props.name}`)} ? `, t('common.tips'), {
confirmButtonText: t('common.confirm'),
cancelButtonText:t('common.cancel'),
type: 'warning'
}).then(() => {
setSync([props.name]).then(res => {
ElMessage.success(t('common.oper'));
});
}).catch(() => {});
}
return {
handleSync
}
}
}
</script>
<style lang="stylus" scoped>
</style>

View File

@@ -27,6 +27,11 @@
</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" />
@@ -43,11 +48,12 @@ 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},
components:{Delete,Plus,Top,Bottom,Sync},
setup(props) {
const {t} = useI18n();
const globalData = injectGlobalData();

View File

@@ -20,9 +20,14 @@
<Title>linker</Title>
<Authors>snltty</Authors>
<Company>snltty</Company>
<Description>1. 修复litedb抢锁超时导致客户端登录失败问题
2. 同步信标服务器
3. 其它一些修复优化</Description>
<Description>1. 优化linux下路由跟踪问题
2. 优化linux下获取本机IP问题
3. 增加ICS让win7+、win server2008+支持NAT
4. 增加中继卡密
5. 增加内外穿透定时开关功能
6. 优化管理页面连接接口的体验
7. 优化一些UI体验去除同步页面将同步功能放置各个实际的位置
8. 其它一些修复优化</Description>
<Copyright>snltty</Copyright>
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>

View File

@@ -1,5 +1,10 @@
v1.6.9
2025-03-01 02:24:18
1. 修复litedb抢锁超时导致客户端登录失败问题
2. 同步信标服务器
3. 其它一些修复优化
2025-03-02 17:55:03
1. 优化linux下路由跟踪问题
2. 优化linux下获取本机IP问题
3. 增加ICS让win7+、win server2008+支持NAT
4. 增加中继卡密
5. 增加内外穿透定时开关功能
6. 优化管理页面连接接口的体验
7. 优化一些UI体验去除同步页面将同步功能放置各个实际的位置
8. 其它一些修复优化