mirror of
https://github.com/snltty/linker.git
synced 2025-09-27 05:25:57 +08:00
172
This commit is contained in:
2
.github/workflows/dotnet.yml
vendored
2
.github/workflows/dotnet.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
|||||||
release_name: v1.7.2.${{ steps.date.outputs.today }}
|
release_name: v1.7.2.${{ steps.date.outputs.today }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
body: "1. 内网穿透的计划任务\r\n2. 一些修复和优化\r\n3. 优化自动分配IP\r\n4. 优化网卡,排除不明数据包\r\n5. 优化管理UI,适配移动端"
|
body: "1. 内网穿透的计划任务\r\n2. 一些修复和优化\r\n3. 优化自动分配IP\r\n4. 优化网卡,排除不明数据包\r\n5. 优化管理UI,适配移动端\r\n6. 虚拟网卡点对网IP映射"
|
||||||
- name: publish projects
|
- name: publish projects
|
||||||
run: ./publish.bat "C:\\Android\\android-sdk"
|
run: ./publish.bat "C:\\Android\\android-sdk"
|
||||||
- name: upload-win-x86-oss
|
- name: upload-win-x86-oss
|
||||||
|
@@ -460,14 +460,22 @@ namespace linker.app
|
|||||||
|
|
||||||
public sealed class WebServerFileReader : IWebServerFileReader
|
public sealed class WebServerFileReader : IWebServerFileReader
|
||||||
{
|
{
|
||||||
DateTime lastModified = DateTime.Now;
|
ReceiveDataBuffer receiveDataBuffer = new ReceiveDataBuffer();
|
||||||
public byte[] Read(string root,string fileName, out DateTime lastModified)
|
byte[] buffer = new byte[4 * 1024];
|
||||||
|
public byte[] Read(string root, string fileName, out DateTime lastModified)
|
||||||
{
|
{
|
||||||
lastModified = this.lastModified;
|
lastModified = DateTime.Now;
|
||||||
fileName = Path.Join("public/web", fileName);
|
fileName = Path.Join("public/web", fileName);
|
||||||
using Stream fileStream = FileSystem.Current.OpenAppPackageFileAsync(fileName).Result;
|
using Stream fileStream = FileSystem.Current.OpenAppPackageFileAsync(fileName).Result;
|
||||||
using StreamReader reader = new StreamReader(fileStream);
|
int length = 0;
|
||||||
return reader.ReadToEnd().ToBytes();
|
while ((length = fileStream.Read(buffer, 0, buffer.Length)) != 0)
|
||||||
|
{
|
||||||
|
receiveDataBuffer.AddRange(buffer, 0, length);
|
||||||
|
}
|
||||||
|
byte[] result = receiveDataBuffer.Data.ToArray();
|
||||||
|
receiveDataBuffer.Clear();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -41,7 +41,6 @@ namespace linker.libs
|
|||||||
{
|
{
|
||||||
return FindValue(NetworkHelper.ToValue(ip), out value);
|
return FindValue(NetworkHelper.ToValue(ip), out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FindValue(uint ip, out T value)
|
public bool FindValue(uint ip, out T value)
|
||||||
{
|
{
|
||||||
if (ip2value.TryGetValue(ip, out value))
|
if (ip2value.TryGetValue(ip, out value))
|
||||||
|
@@ -57,22 +57,41 @@ namespace linker.libs.web
|
|||||||
//默认页面
|
//默认页面
|
||||||
if (path == "/") path = "index.html";
|
if (path == "/") path = "index.html";
|
||||||
|
|
||||||
|
Memory<byte> memory = Helper.EmptyArray;
|
||||||
|
DateTime last = DateTime.Now;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte[] bytes = fileReader.Read(root,path, out DateTime last);
|
memory = fileReader.Read(root, path, out last);
|
||||||
|
if (memory.Length > 0)
|
||||||
|
{
|
||||||
|
response.ContentLength64 = memory.Length;
|
||||||
|
response.ContentType = GetContentType(path);
|
||||||
|
if (OperatingSystem.IsAndroid())
|
||||||
|
{
|
||||||
|
response.Headers.Set("Last-Modified", last.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Headers.Set("Last-Modified", last.ToString());
|
||||||
|
}
|
||||||
|
response.OutputStream.Write(memory.Span);
|
||||||
|
response.OutputStream.Flush();
|
||||||
|
response.OutputStream.Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(ex + $"");
|
||||||
response.ContentLength64 = bytes.Length;
|
response.ContentLength64 = bytes.Length;
|
||||||
response.ContentType = GetContentType(path);
|
response.ContentType = "text/plain; charset=utf-8";
|
||||||
response.Headers.Set("Last-Modified", last.ToString());
|
|
||||||
|
|
||||||
response.OutputStream.Write(bytes, 0, bytes.Length);
|
response.OutputStream.Write(bytes, 0, bytes.Length);
|
||||||
response.OutputStream.Flush();
|
response.OutputStream.Flush();
|
||||||
response.OutputStream.Close();
|
response.OutputStream.Close();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@@ -115,9 +134,9 @@ namespace linker.libs.web
|
|||||||
{
|
{
|
||||||
public byte[] Read(string root, string fileName, out DateTime lastModified);
|
public byte[] Read(string root, string fileName, out DateTime lastModified);
|
||||||
}
|
}
|
||||||
public sealed class WebServerFileReader: IWebServerFileReader
|
public sealed class WebServerFileReader : IWebServerFileReader
|
||||||
{
|
{
|
||||||
public byte[] Read(string root,string fileName, out DateTime lastModified)
|
public byte[] Read(string root, string fileName, out DateTime lastModified)
|
||||||
{
|
{
|
||||||
fileName = Path.Join(root, fileName);
|
fileName = Path.Join(root, fileName);
|
||||||
lastModified = File.GetLastWriteTimeUtc(fileName);
|
lastModified = File.GetLastWriteTimeUtc(fileName);
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
using linker.libs;
|
using linker.libs;
|
||||||
using linker.libs.timer;
|
using linker.libs.timer;
|
||||||
using linker.messenger.signin;
|
using linker.messenger.signin;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace linker.messenger.decenter
|
namespace linker.messenger.decenter
|
||||||
{
|
{
|
||||||
@@ -72,6 +71,7 @@ namespace linker.messenger.decenter
|
|||||||
{
|
{
|
||||||
sync.AddData(decenterSyncInfo.Data);
|
sync.AddData(decenterSyncInfo.Data);
|
||||||
sync.DataVersion.Increment();
|
sync.DataVersion.Increment();
|
||||||
|
versionMultipleManager.Increment(sync.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -173,7 +173,7 @@ namespace linker.messenger.serializer.memorypack
|
|||||||
|
|
||||||
[MemoryPackConstructor]
|
[MemoryPackConstructor]
|
||||||
SerializableTuntapInfo(string machineId, TuntapStatus status, IPAddress ip, byte prefixLength, string name,
|
SerializableTuntapInfo(string machineId, TuntapStatus status, IPAddress ip, byte prefixLength, string name,
|
||||||
List<TuntapLanInfo> lans, IPAddress wan, string setupError, string natError, string systemInfo, List<TuntapForwardInfo> forwards, TuntapSwitch Switch)
|
List<TuntapLanInfo> lans, IPAddress wan, string setupError, string natError, string systemInfo, List<TuntapForwardInfo> forwards, TuntapSwitch Switch)
|
||||||
{
|
{
|
||||||
var info = new TuntapInfo
|
var info = new TuntapInfo
|
||||||
{
|
{
|
||||||
@@ -437,8 +437,14 @@ namespace linker.messenger.serializer.memorypack
|
|||||||
[MemoryPackInclude]
|
[MemoryPackInclude]
|
||||||
string Error => info.Error;
|
string Error => info.Error;
|
||||||
|
|
||||||
|
[MemoryPackInclude, MemoryPackAllowSerialize]
|
||||||
|
IPAddress MapIP => info.MapIP;
|
||||||
|
|
||||||
|
[MemoryPackInclude, MemoryPackAllowSerialize]
|
||||||
|
byte MapPrefixLength => info.MapPrefixLength;
|
||||||
|
|
||||||
[MemoryPackConstructor]
|
[MemoryPackConstructor]
|
||||||
SerializableTuntapLanInfo(IPAddress ip, byte prefixLength, bool disabled, bool exists, string error)
|
SerializableTuntapLanInfo(IPAddress ip, byte prefixLength, bool disabled, bool exists, string error, IPAddress mapip, byte mapprefixLength)
|
||||||
{
|
{
|
||||||
var info = new TuntapLanInfo
|
var info = new TuntapLanInfo
|
||||||
{
|
{
|
||||||
@@ -446,7 +452,9 @@ namespace linker.messenger.serializer.memorypack
|
|||||||
Exists = exists,
|
Exists = exists,
|
||||||
IP = ip,
|
IP = ip,
|
||||||
PrefixLength = prefixLength,
|
PrefixLength = prefixLength,
|
||||||
Error = error
|
Error = error,
|
||||||
|
MapIP = mapip,
|
||||||
|
MapPrefixLength = mapprefixLength,
|
||||||
};
|
};
|
||||||
this.info = info;
|
this.info = info;
|
||||||
}
|
}
|
||||||
|
@@ -106,7 +106,7 @@ namespace linker.messenger.store.file
|
|||||||
LoggerHelper.Instance.Info("use store file");
|
LoggerHelper.Instance.Info("use store file");
|
||||||
|
|
||||||
FileConfig fileConfig = serviceProvider.GetService<FileConfig>();
|
FileConfig fileConfig = serviceProvider.GetService<FileConfig>();
|
||||||
fileConfig.Initialize(configDic);
|
fileConfig.Save(configDic);
|
||||||
RunningConfig runningConfig = serviceProvider.GetService<RunningConfig>();
|
RunningConfig runningConfig = serviceProvider.GetService<RunningConfig>();
|
||||||
|
|
||||||
IApiServer apiServer = serviceProvider.GetService<IApiServer>();
|
IApiServer apiServer = serviceProvider.GetService<IApiServer>();
|
||||||
|
@@ -5,9 +5,14 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using linker.libs.timer;
|
using linker.libs.timer;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace linker.messenger.store.file
|
namespace linker.messenger.store.file
|
||||||
{
|
{
|
||||||
|
public sealed class FileConfigInitParams
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
public sealed class FileConfig
|
public sealed class FileConfig
|
||||||
{
|
{
|
||||||
private SemaphoreSlim slim = new SemaphoreSlim(1);
|
private SemaphoreSlim slim = new SemaphoreSlim(1);
|
||||||
@@ -18,12 +23,9 @@ namespace linker.messenger.store.file
|
|||||||
public ConfigInfo Data { get; private set; } = new ConfigInfo();
|
public ConfigInfo Data { get; private set; } = new ConfigInfo();
|
||||||
|
|
||||||
public FileConfig()
|
public FileConfig()
|
||||||
{
|
|
||||||
}
|
|
||||||
public void Initialize(Dictionary<string, string> dic)
|
|
||||||
{
|
{
|
||||||
Init();
|
Init();
|
||||||
Load(dic);
|
Load();
|
||||||
Save();
|
Save();
|
||||||
SaveTask();
|
SaveTask();
|
||||||
}
|
}
|
||||||
@@ -42,14 +44,14 @@ namespace linker.messenger.store.file
|
|||||||
object property = item.GetValue(Data);
|
object property = item.GetValue(Data);
|
||||||
fsDic.Add(item.Name.ToLower(), new FileReadWrite
|
fsDic.Add(item.Name.ToLower(), new FileReadWrite
|
||||||
{
|
{
|
||||||
Path = Path.Combine(Helper.currentDirectory,configPath, $"{item.Name.ToLower()}.json"),
|
Path = Path.Combine(Helper.currentDirectory, configPath, $"{item.Name.ToLower()}.json"),
|
||||||
Property = item,
|
Property = item,
|
||||||
PropertyObject = property,
|
PropertyObject = property,
|
||||||
PropertyMethod = (IConfig)property,
|
PropertyMethod = (IConfig)property,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void Load(Dictionary<string, string> dic)
|
private void Load()
|
||||||
{
|
{
|
||||||
slim.Wait();
|
slim.Wait();
|
||||||
try
|
try
|
||||||
@@ -68,10 +70,6 @@ namespace linker.messenger.store.file
|
|||||||
{
|
{
|
||||||
text = File.ReadAllText(item.Value.Path, encoding: System.Text.Encoding.UTF8);
|
text = File.ReadAllText(item.Value.Path, encoding: System.Text.Encoding.UTF8);
|
||||||
}
|
}
|
||||||
else if (dic != null && dic.TryGetValue(item.Value.Property.Name, out string base64))
|
|
||||||
{
|
|
||||||
text = Encoding.UTF8.GetString(Convert.FromBase64String(base64));
|
|
||||||
}
|
|
||||||
if (string.IsNullOrWhiteSpace(text))
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
{
|
{
|
||||||
LoggerHelper.Instance.Error($"{item.Value.Path} empty");
|
LoggerHelper.Instance.Error($"{item.Value.Path} empty");
|
||||||
@@ -96,7 +94,7 @@ namespace linker.messenger.store.file
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public void Save()
|
public void Save(Dictionary<string, string> dic = null)
|
||||||
{
|
{
|
||||||
slim.Wait();
|
slim.Wait();
|
||||||
try
|
try
|
||||||
@@ -111,6 +109,11 @@ namespace linker.messenger.store.file
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
string text = item.Value.PropertyMethod.Serialize(item.Value.Property.GetValue(Data));
|
string text = item.Value.PropertyMethod.Serialize(item.Value.Property.GetValue(Data));
|
||||||
|
if (dic != null && dic.TryGetValue(item.Value.Property.Name, out string base64))
|
||||||
|
{
|
||||||
|
string text2 = item.Value.PropertyMethod.Serialize(item.Value.PropertyMethod.Deserialize(Encoding.UTF8.GetString(Convert.FromBase64String(base64))));
|
||||||
|
text = MergeJson(text, text2);
|
||||||
|
}
|
||||||
File.WriteAllText($"{item.Value.Path}.temp", text, encoding: System.Text.Encoding.UTF8);
|
File.WriteAllText($"{item.Value.Path}.temp", text, encoding: System.Text.Encoding.UTF8);
|
||||||
File.Move($"{item.Value.Path}.temp", item.Value.Path, true);
|
File.Move($"{item.Value.Path}.temp", item.Value.Path, true);
|
||||||
}
|
}
|
||||||
@@ -128,7 +131,10 @@ namespace linker.messenger.store.file
|
|||||||
{
|
{
|
||||||
slim.Release();
|
slim.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void SaveTask()
|
private void SaveTask()
|
||||||
{
|
{
|
||||||
TimerHelper.SetIntervalLong(() =>
|
TimerHelper.SetIntervalLong(() =>
|
||||||
@@ -140,6 +146,61 @@ namespace linker.messenger.store.file
|
|||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string MergeJson(string json1, string json2)
|
||||||
|
{
|
||||||
|
using var doc1 = JsonDocument.Parse(json1);
|
||||||
|
using var doc2 = JsonDocument.Parse(json2);
|
||||||
|
|
||||||
|
var output = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
foreach (var property in doc1.RootElement.EnumerateObject())
|
||||||
|
{
|
||||||
|
output[property.Name] = GetValue(property.Value);
|
||||||
|
}
|
||||||
|
foreach (var property in doc2.RootElement.EnumerateObject())
|
||||||
|
{
|
||||||
|
output[property.Name] = GetValue(property.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.ToJson();
|
||||||
|
|
||||||
|
object GetValue(JsonElement element)
|
||||||
|
{
|
||||||
|
return element.ValueKind switch
|
||||||
|
{
|
||||||
|
JsonValueKind.String => element.GetString(),
|
||||||
|
JsonValueKind.Number => element.GetDecimal(),
|
||||||
|
JsonValueKind.True => true,
|
||||||
|
JsonValueKind.False => false,
|
||||||
|
JsonValueKind.Null => null,
|
||||||
|
JsonValueKind.Object => GetObjectValue(element),
|
||||||
|
JsonValueKind.Array => GetArrayValue(element),
|
||||||
|
JsonValueKind.Undefined => null,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, object> GetObjectValue(JsonElement element)
|
||||||
|
{
|
||||||
|
var dict = new Dictionary<string, object>();
|
||||||
|
foreach (var prop in element.EnumerateObject())
|
||||||
|
{
|
||||||
|
dict[prop.Name] = GetValue(prop.Value);
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<object> GetArrayValue(JsonElement element)
|
||||||
|
{
|
||||||
|
var list = new List<object>();
|
||||||
|
foreach (var item in element.EnumerateArray())
|
||||||
|
{
|
||||||
|
list.Add(GetValue(item));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class FileReadWrite
|
public sealed class FileReadWrite
|
||||||
@@ -190,10 +251,13 @@ namespace linker.messenger.store.file
|
|||||||
}
|
}
|
||||||
public object Deserialize(string text)
|
public object Deserialize(string text)
|
||||||
{
|
{
|
||||||
if (text.Contains("ApiPassword"))
|
try
|
||||||
{
|
{
|
||||||
return text.DeJson<ConfigClientInfo>();
|
return text.DeJson<ConfigClientInfo>();
|
||||||
}
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
return Encoding.UTF8.GetString(crypto.Decode(Convert.FromBase64String(text)).ToArray()).DeJson<ConfigClientInfo>();
|
return Encoding.UTF8.GetString(crypto.Decode(Convert.FromBase64String(text)).ToArray()).DeJson<ConfigClientInfo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using linker.messenger.tuntap;
|
using linker.messenger.signin;
|
||||||
|
using linker.messenger.tuntap;
|
||||||
using linker.messenger.tuntap.lease;
|
using linker.messenger.tuntap.lease;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
@@ -12,4 +13,9 @@ namespace linker.messenger.store.file
|
|||||||
public TuntapConfigInfo Tuntap { get; set; } = new TuntapConfigInfo();
|
public TuntapConfigInfo Tuntap { get; set; } = new TuntapConfigInfo();
|
||||||
public ConcurrentDictionary<string, LeaseInfo> Leases { get; set; } = new ConcurrentDictionary<string, LeaseInfo>();
|
public ConcurrentDictionary<string, LeaseInfo> Leases { get; set; } = new ConcurrentDictionary<string, LeaseInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public partial class ConfigServerInfo
|
||||||
|
{
|
||||||
|
public TuntapConfigServerInfo Tuntap { get; set; } = new TuntapConfigServerInfo();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,16 +1,22 @@
|
|||||||
using linker.messenger.tuntap.lease;
|
using linker.messenger.tuntap;
|
||||||
|
using linker.messenger.tuntap.lease;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
namespace linker.messenger.store.file.tuntap
|
namespace linker.messenger.store.file.tuntap
|
||||||
{
|
{
|
||||||
public sealed class LeaseServerStore : ILeaseServerStore
|
public sealed class LeaseServerStore : ILeaseServerStore
|
||||||
{
|
{
|
||||||
|
public TuntapLeaseConfigServerInfo Info => fileConfig.Data.Server.Tuntap.Lease;
|
||||||
|
|
||||||
private readonly Storefactory dBfactory;
|
private readonly Storefactory dBfactory;
|
||||||
private readonly ILiteCollection<LeaseCacheInfo> liteCollection;
|
private readonly ILiteCollection<LeaseCacheInfo> liteCollection;
|
||||||
public LeaseServerStore(Storefactory dBfactory)
|
private readonly FileConfig fileConfig;
|
||||||
|
public LeaseServerStore(FileConfig fileConfig,Storefactory dBfactory)
|
||||||
{
|
{
|
||||||
|
this.fileConfig = fileConfig;
|
||||||
this.dBfactory = dBfactory;
|
this.dBfactory = dBfactory;
|
||||||
liteCollection = dBfactory.GetCollection<LeaseCacheInfo>("dhcp");
|
liteCollection = dBfactory.GetCollection<LeaseCacheInfo>("dhcp");
|
||||||
|
|
||||||
}
|
}
|
||||||
public bool Add(LeaseCacheInfo info)
|
public bool Add(LeaseCacheInfo info)
|
||||||
{
|
{
|
||||||
|
@@ -341,11 +341,14 @@ namespace linker.messenger.tuntap
|
|||||||
|
|
||||||
public sealed partial class TuntapLanInfo
|
public sealed partial class TuntapLanInfo
|
||||||
{
|
{
|
||||||
public IPAddress IP { get; set; }
|
public IPAddress IP { get; set; } = IPAddress.Any;
|
||||||
public byte PrefixLength { get; set; } = 24;
|
public byte PrefixLength { get; set; } = 24;
|
||||||
public bool Disabled { get; set; }
|
public bool Disabled { get; set; }
|
||||||
public bool Exists { get; set; }
|
public bool Exists { get; set; }
|
||||||
public string Error { get; set; } = string.Empty;
|
public string Error { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public IPAddress MapIP { get; set; } = IPAddress.Any;
|
||||||
|
public byte MapPrefixLength { get; set; } = 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum TuntapStatus : byte
|
public enum TuntapStatus : byte
|
||||||
@@ -396,5 +399,16 @@ namespace linker.messenger.tuntap
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
InterfaceOrder = 128,
|
InterfaceOrder = 128,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public sealed class TuntapConfigServerInfo
|
||||||
|
{
|
||||||
|
public TuntapLeaseConfigServerInfo Lease { get; set; } = new TuntapLeaseConfigServerInfo();
|
||||||
|
}
|
||||||
|
public sealed class TuntapLeaseConfigServerInfo
|
||||||
|
{
|
||||||
|
public int IPDays { get; set; } = 7;
|
||||||
|
public int NetworkDays { get; set; } = 30;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,6 +5,8 @@ using linker.messenger.exroute;
|
|||||||
using linker.messenger.signin;
|
using linker.messenger.signin;
|
||||||
using linker.tun;
|
using linker.tun;
|
||||||
using linker.tunnel.connection;
|
using linker.tunnel.connection;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace linker.messenger.tuntap
|
namespace linker.messenger.tuntap
|
||||||
{
|
{
|
||||||
@@ -53,6 +55,7 @@ namespace linker.messenger.tuntap
|
|||||||
};
|
};
|
||||||
tuntapTransfer.OnSetupSuccess += () =>
|
tuntapTransfer.OnSetupSuccess += () =>
|
||||||
{
|
{
|
||||||
|
SetMaps();
|
||||||
AddForward();
|
AddForward();
|
||||||
};
|
};
|
||||||
tuntapTransfer.OnShutdownBefore += () =>
|
tuntapTransfer.OnShutdownBefore += () =>
|
||||||
@@ -70,7 +73,10 @@ namespace linker.messenger.tuntap
|
|||||||
};
|
};
|
||||||
|
|
||||||
//配置有更新,去同步一下
|
//配置有更新,去同步一下
|
||||||
tuntapConfigTransfer.OnUpdate += () => { AddForward(); _ = CheckDevice(); tuntapDecenter.Refresh(); };
|
tuntapConfigTransfer.OnUpdate += () =>
|
||||||
|
{
|
||||||
|
SetMaps(); AddForward(); _ = CheckDevice(); tuntapDecenter.Refresh();
|
||||||
|
};
|
||||||
|
|
||||||
//隧道回调
|
//隧道回调
|
||||||
tuntapProxy.Callback = this;
|
tuntapProxy.Callback = this;
|
||||||
@@ -123,7 +129,7 @@ namespace linker.messenger.tuntap
|
|||||||
{
|
{
|
||||||
tuntapTransfer.Write(buffer);
|
tuntapTransfer.Write(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 重启网卡
|
/// 重启网卡
|
||||||
@@ -147,6 +153,13 @@ namespace linker.messenger.tuntap
|
|||||||
tuntapTransfer.Shutdown();
|
tuntapTransfer.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetMaps()
|
||||||
|
{
|
||||||
|
var maps = tuntapConfigTransfer.Info.Lans
|
||||||
|
.Where(c => c.MapIP.Equals(IPAddress.Any) == false && c.Disabled == false)
|
||||||
|
.Select(c => new LanMapInfo { IP = c.IP, ToIP = c.MapIP, PrefixLength = c.MapPrefixLength }).ToArray();
|
||||||
|
tuntapTransfer.SetMap(maps);
|
||||||
|
}
|
||||||
// <summary>
|
// <summary>
|
||||||
/// 添加端口转发
|
/// 添加端口转发
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -83,7 +83,7 @@ namespace linker.messenger.tuntap
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task InputPacket(LinkerTunDevicPacket packet)
|
public async Task InputPacket(LinkerTunDevicPacket packet)
|
||||||
{
|
{
|
||||||
//LoggerHelper.Instance.Warning($"tuntap read to {new IPEndPoint(new IPAddress(packet.DistIPAddress.Span), packet.DistPort)} {packet.Length}");
|
//LoggerHelper.Instance.Warning($"tuntap read {new IPEndPoint(new IPAddress(packet.SourceIPAddress.Span), packet.SourcePort)}->{new IPEndPoint(new IPAddress(packet.DistIPAddress.Span), packet.DistPort)}->{packet.Length}");
|
||||||
//IPV4广播组播、IPV6 多播
|
//IPV4广播组播、IPV6 多播
|
||||||
if (packet.IPV4Broadcast || packet.IPV6Multicast)
|
if (packet.IPV4Broadcast || packet.IPV6Multicast)
|
||||||
{
|
{
|
||||||
|
@@ -174,6 +174,13 @@ namespace linker.messenger.tuntap
|
|||||||
{
|
{
|
||||||
linkerTunDeviceAdapter.DelRoute(ips);
|
linkerTunDeviceAdapter.DelRoute(ips);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SetMap(LanMapInfo[] maps)
|
||||||
|
{
|
||||||
|
linkerTunDeviceAdapter.SetMap(maps);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> CheckAvailable(bool order = false)
|
public async Task<bool> CheckAvailable(bool order = false)
|
||||||
{
|
{
|
||||||
return await linkerTunDeviceAdapter.CheckAvailable(order).ConfigureAwait(false);
|
return await linkerTunDeviceAdapter.CheckAvailable(order).ConfigureAwait(false);
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
{
|
{
|
||||||
public interface ILeaseServerStore
|
public interface ILeaseServerStore
|
||||||
{
|
{
|
||||||
|
public TuntapLeaseConfigServerInfo Info { get; }
|
||||||
|
|
||||||
public List<LeaseCacheInfo> Get();
|
public List<LeaseCacheInfo> Get();
|
||||||
public bool Add(LeaseCacheInfo info);
|
public bool Add(LeaseCacheInfo info);
|
||||||
public bool Update(LeaseCacheInfo info);
|
public bool Update(LeaseCacheInfo info);
|
||||||
|
@@ -194,7 +194,9 @@ namespace linker.messenger.tuntap.lease
|
|||||||
//空闲的IP
|
//空闲的IP
|
||||||
IEnumerable<int> idleIPs = Enumerable.Range((int)firstIPValue, (int)length + 1).Except(cache.Users.Select(c => (int)c.IP));
|
IEnumerable<int> idleIPs = Enumerable.Range((int)firstIPValue, (int)length + 1).Except(cache.Users.Select(c => (int)c.IP));
|
||||||
//过期的IP
|
//过期的IP
|
||||||
IEnumerable<int> expireIPs = cache.Users.Where(c => (DateTime.Now - c.LastTime).TotalDays > 7).Select(c => (int)c.IP);
|
IEnumerable<int> expireIPs = cache.Users
|
||||||
|
.Where(c => (DateTime.Now - c.LastTime).TotalDays > leaseServerStore.Info.IPDays)
|
||||||
|
.OrderBy(c => c.LastTime).Select(c => (int)c.IP);
|
||||||
|
|
||||||
uint newIPValue = (uint)idleIPs.FirstOrDefault();
|
uint newIPValue = (uint)idleIPs.FirstOrDefault();
|
||||||
//没找到空闲的,但是有其它超时的,抢一个
|
//没找到空闲的,但是有其它超时的,抢一个
|
||||||
@@ -240,7 +242,7 @@ namespace linker.messenger.tuntap.lease
|
|||||||
{
|
{
|
||||||
DateTime now = DateTime.Now;
|
DateTime now = DateTime.Now;
|
||||||
|
|
||||||
var items = caches.Values.Where(c => (now - c.LastTime).TotalDays > 30).ToList();
|
var items = caches.Values.Where(c => (now - c.LastTime).TotalDays > leaseServerStore.Info.NetworkDays).ToList();
|
||||||
if (items.Count > 0)
|
if (items.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.239" ProductVersion="0.0.0.239" publishDir="/dist/" dstrip="false" local="false" ignored="false">
|
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.240" ProductVersion="0.0.0.240" publishDir="/dist/" dstrip="false" local="false" ignored="false">
|
||||||
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
|
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
|
||||||
<folder name="资源文件" path="res" embed="true" local="false" ignored="false">
|
<folder name="资源文件" path="res" embed="true" local="false" ignored="false">
|
||||||
<file name="favicon.ico" path="res\favicon.ico" comment="res\favicon.ico"/>
|
<file name="favicon.ico" path="res\favicon.ico" comment="res\favicon.ico"/>
|
||||||
|
BIN
src/linker.tray.win/dist/linker.tray.win.exe
vendored
BIN
src/linker.tray.win/dist/linker.tray.win.exe
vendored
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
.el-radio-group[data-v-7061404c]{margin-right:.6rem}.wrap[data-v-7061404c]{padding-bottom:1rem}.el-form-item[data-v-2bef0d8e]{margin-bottom:1rem}.el-input-number--small[data-v-2bef0d8e]{width:10rem!important}.el-form-item[data-v-3d96703d]{margin-bottom:1rem}.el-input-number--small[data-v-3d96703d]{width:10rem!important}.head .search>div[data-v-5d11d068]{margin-right:1rem}.page[data-v-5d11d068]{padding:2rem 0;display:inline-block}.el-form-item[data-v-5d11d068]{margin-bottom:1rem}.el-input-number--small[data-v-5d11d068]{width:10rem!important}.head .search>div[data-v-22d5523e]{margin-right:1rem}.page[data-v-22d5523e]{padding:2rem 0;display:inline-block}.el-form-item[data-v-22d5523e]{margin-bottom:1rem}.el-input-number--small[data-v-22d5523e]{width:10rem!important}.el-form-item[data-v-c2557c92]{margin-bottom:1rem}.el-input-number--small[data-v-c2557c92]{width:10rem!important}.blue[data-v-5b81e49d]{color:#409eff}a.a-edit[data-v-5b81e49d]{margin-left:1rem}a.a-edit .el-icon[data-v-5b81e49d]{vertical-align:middle}.servers-wrap[data-v-597f32d0]{padding:1rem;font-size:1.3rem;color:#555}.servers-wrap a[data-v-597f32d0]{color:#333}
|
.el-radio-group[data-v-7061404c]{margin-right:.6rem}.wrap[data-v-7061404c]{padding-bottom:1rem}.el-form-item[data-v-2bef0d8e]{margin-bottom:1rem}.el-input-number--small[data-v-2bef0d8e]{width:10rem!important}.el-form-item[data-v-3d96703d]{margin-bottom:1rem}.el-input-number--small[data-v-3d96703d]{width:10rem!important}.head .search>div[data-v-5d11d068]{margin-right:1rem}.page[data-v-5d11d068]{padding:2rem 0;display:inline-block}.el-form-item[data-v-5d11d068]{margin-bottom:1rem}.el-input-number--small[data-v-5d11d068]{width:10rem!important}.head .search>div[data-v-22d5523e]{margin-right:1rem}.page[data-v-22d5523e]{padding:2rem 0;display:inline-block}.el-form-item[data-v-22d5523e]{margin-bottom:1rem}.el-input-number--small[data-v-22d5523e]{width:10rem!important}.el-form-item[data-v-c2557c92]{margin-bottom:1rem}.el-input-number--small[data-v-c2557c92]{width:10rem!important}.blue[data-v-21fcf68e]{color:#409eff}a.a-edit[data-v-21fcf68e]{margin-left:1rem}a.a-edit .el-icon[data-v-21fcf68e]{vertical-align:middle}.servers-wrap[data-v-597f32d0]{padding:1rem;font-size:1.3rem;color:#555}.servers-wrap a[data-v-597f32d0]{color:#333}
|
@@ -1 +1 @@
|
|||||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.cfba5739.js"></script><script defer="defer" src="js/app.70d76e49.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.3aab4747.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.cfba5739.js"></script><script defer="defer" src="js/app.cfe18975.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.3aab4747.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
1
src/linker.tray.win/web/js/161.81578611.js
Normal file
1
src/linker.tray.win/web/js/161.81578611.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@@ -48,8 +48,8 @@ namespace linker.tun
|
|||||||
Shutdown();
|
Shutdown();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
fsRead = new FileStream(safeFileHandle, FileAccess.Read, 32 * 1024, true);
|
fsRead = new FileStream(safeFileHandle, FileAccess.Read, 65 * 1024, true);
|
||||||
fsWrite = new FileStream(safeFileHandle, FileAccess.Write, 32 * 1024, true);
|
fsWrite = new FileStream(safeFileHandle, FileAccess.Write, 65 * 1024, true);
|
||||||
|
|
||||||
interfaceLinux = GetLinuxInterfaceNum();
|
interfaceLinux = GetLinuxInterfaceNum();
|
||||||
return true;
|
return true;
|
||||||
@@ -310,7 +310,7 @@ namespace linker.tun
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private readonly byte[] buffer = new byte[8 * 1024];
|
private readonly byte[] buffer = new byte[65 * 1024];
|
||||||
private readonly object writeLockObj = new object();
|
private readonly object writeLockObj = new object();
|
||||||
public byte[] Read(out int length)
|
public byte[] Read(out int length)
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
using linker.libs;
|
using linker.libs;
|
||||||
using linker.libs.timer;
|
using linker.libs.timer;
|
||||||
|
using System;
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Frozen;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
namespace linker.tun
|
namespace linker.tun
|
||||||
@@ -20,6 +25,10 @@ namespace linker.tun
|
|||||||
public string NatError => natError;
|
public string NatError => natError;
|
||||||
|
|
||||||
|
|
||||||
|
private FrozenDictionary<uint, uint> mapDic = new Dictionary<uint, uint>().ToFrozenDictionary();
|
||||||
|
private uint[] masks = Array.Empty<uint>();
|
||||||
|
|
||||||
|
|
||||||
private OperatingManager operatingManager = new OperatingManager();
|
private OperatingManager operatingManager = new OperatingManager();
|
||||||
public LinkerTunDeviceStatus Status
|
public LinkerTunDeviceStatus Status
|
||||||
{
|
{
|
||||||
@@ -224,6 +233,8 @@ namespace linker.tun
|
|||||||
byte[] buffer = linkerTunDevice.Read(out int length);
|
byte[] buffer = linkerTunDevice.Read(out int length);
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
{
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
LoggerHelper.Instance.Warning($"tuntap read buffer 0");
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -249,14 +260,58 @@ namespace linker.tun
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buffer"></param>
|
/// <param name="buffer"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool Write(ReadOnlyMemory<byte> buffer)
|
public unsafe bool Write(ReadOnlyMemory<byte> buffer)
|
||||||
{
|
{
|
||||||
if (linkerTunDevice != null && Status == LinkerTunDeviceStatus.Running)
|
if (linkerTunDevice != null && Status == LinkerTunDeviceStatus.Running)
|
||||||
{
|
{
|
||||||
|
MapToRealIP(buffer);
|
||||||
return linkerTunDevice.Write(buffer);
|
return linkerTunDevice.Write(buffer);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
private unsafe void MapToRealIP(ReadOnlyMemory<byte> buffer)
|
||||||
|
{
|
||||||
|
//只支持映射IPV4
|
||||||
|
if ((byte)(buffer.Span[0] >> 4 & 0b1111) != 4) return;
|
||||||
|
//映射表不为空
|
||||||
|
if (masks.Length == 0 || mapDic.Count == 0) return;
|
||||||
|
|
||||||
|
uint dist = BinaryPrimitives.ReadUInt32BigEndian(buffer.Span.Slice(16, 4));
|
||||||
|
for (int i = 0; i < masks.Length; i++)
|
||||||
|
{
|
||||||
|
//目标IP网络号存在映射表中,找到映射后的真实网络号,替换网络号得到最终真实的IP
|
||||||
|
if (mapDic.TryGetValue(dist & masks[i], out uint realNetwork))
|
||||||
|
{
|
||||||
|
//将原本的目标IP修改为映射的IP
|
||||||
|
fixed (byte* ptr = buffer.Span)
|
||||||
|
{
|
||||||
|
//修改目标IP
|
||||||
|
*(uint*)(ptr + 16) = BinaryPrimitives.ReverseEndianness(realNetwork | (dist & ~masks[i]));
|
||||||
|
//重新计算IP头校验和
|
||||||
|
*(ushort*)(ptr + 10) = 0;
|
||||||
|
*(ushort*)(ptr + 10) = Checksum((ushort*)ptr, (byte)((*ptr & 0b1111) * 4));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置IP映射列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="maps"></param>
|
||||||
|
public void SetMap(LanMapInfo[] maps)
|
||||||
|
{
|
||||||
|
if (maps == null || maps.Length == 0)
|
||||||
|
{
|
||||||
|
mapDic = new Dictionary<uint, uint>().ToFrozenDictionary();
|
||||||
|
masks = Array.Empty<uint>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapDic = maps.ToFrozenDictionary(x => NetworkHelper.ToNetworkValue(x.IP, x.PrefixLength), x => NetworkHelper.ToNetworkValue(x.ToIP, x.PrefixLength));
|
||||||
|
masks = maps.Select(x => NetworkHelper.ToPrefixValue(x.PrefixLength)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 计算校验和
|
/// 计算校验和
|
||||||
@@ -290,4 +345,11 @@ namespace linker.tun
|
|||||||
return await linkerTunDevice.CheckAvailable(order);
|
return await linkerTunDevice.CheckAvailable(order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class LanMapInfo
|
||||||
|
{
|
||||||
|
public IPAddress IP { get; set; }
|
||||||
|
public IPAddress ToIP { get; set; }
|
||||||
|
public byte PrefixLength { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="state.show" :close-on-click-modal="false" append-to=".app-wrap"
|
<el-dialog v-model="state.show" :close-on-click-modal="false" append-to=".app-wrap"
|
||||||
:title="`设置[${state.machineName}]组网`" top="1vh" width="760">
|
:title="`设置[${state.machineName}]组网`" top="1vh" width="780">
|
||||||
<div>
|
<div>
|
||||||
<el-form ref="ruleFormRef" :model="state.ruleForm" :rules="state.rules" label-width="8rem">
|
<el-form ref="ruleFormRef" :model="state.ruleForm" :rules="state.rules" label-width="8rem">
|
||||||
<el-form-item label="网卡名" prop="Name">
|
<el-form-item label="网卡名" prop="Name">
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<el-input @change="handlePrefixLengthChange" v-model="state.ruleForm.PrefixLength" style="width:4rem" />
|
<el-input @change="handlePrefixLengthChange" v-model="state.ruleForm.PrefixLength" style="width:4rem" />
|
||||||
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="">
|
<el-form-item label="" class="mgb-0">
|
||||||
<el-checkbox class="mgr-1" v-model="state.ruleForm.ShowDelay" label="显示延迟" size="large" />
|
<el-checkbox class="mgr-1" v-model="state.ruleForm.ShowDelay" label="显示延迟" size="large" />
|
||||||
<el-checkbox class="mgr-1" v-model="state.ruleForm.AutoConnect" label="自动连接" size="large" />
|
<el-checkbox class="mgr-1" v-model="state.ruleForm.AutoConnect" label="自动连接" size="large" />
|
||||||
<el-checkbox class="mgr-1" v-model="state.ruleForm.Multicast" label="禁用广播" size="large" />
|
<el-checkbox class="mgr-1" v-model="state.ruleForm.Multicast" label="禁用广播" size="large" />
|
||||||
@@ -145,6 +145,5 @@ export default {
|
|||||||
.upgrade-wrap{
|
.upgrade-wrap{
|
||||||
border:1px solid #ddd;
|
border:1px solid #ddd;
|
||||||
margin-bottom:2rem
|
margin-bottom:2rem
|
||||||
padding:0 0 1rem 0;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@@ -5,7 +5,7 @@
|
|||||||
<span class="green" v-if="state.testing">、testing</span>
|
<span class="green" v-if="state.testing">、testing</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<el-table stripe :data="state.forwards" border size="small" width="100%" height="300px" @cell-dblclick="handleCellClick">
|
<el-table stripe :data="state.forwards" border size="small" width="100%" height="200px" @cell-dblclick="handleCellClick">
|
||||||
<el-table-column prop="ListenPort" label="源端口" width="60">
|
<el-table-column prop="ListenPort" label="源端口" width="60">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<template v-if="scope.row.ListenPortEditing">
|
<template v-if="scope.row.ListenPortEditing">
|
||||||
@@ -186,7 +186,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.wrap{
|
.wrap{
|
||||||
padding-right:1rem;
|
padding:0 1rem 1rem 0;
|
||||||
}
|
}
|
||||||
.remark{
|
.remark{
|
||||||
white-space: nowrap; /* 文本不换行 */
|
white-space: nowrap; /* 文本不换行 */
|
||||||
|
@@ -4,27 +4,77 @@
|
|||||||
<span class="yellow">填写局域网IP,使用NAT转发</span>
|
<span class="yellow">填写局域网IP,使用NAT转发</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<template v-for="(item, index) in state.lans" :key="index">
|
<el-table stripe :data="state.lans" border size="small" width="100%" height="200px" @cell-dblclick="handleCellClick">
|
||||||
<div class="flex" style="margin-bottom:.6rem">
|
<el-table-column prop="IP" label="路由IP" width="120">
|
||||||
<div >
|
<template #default="scope">
|
||||||
<el-input v-model="item.IP" style="width:14rem" />
|
<template v-if="scope.row.IPEditing">
|
||||||
<span>/</span>
|
<el-input autofocus size="small" v-model="scope.row.IP"
|
||||||
<el-input @change="handleMaskChange(index)" v-model="item.PrefixLength"
|
@blur="handleEditBlur(scope.row, 'IP')"></el-input>
|
||||||
style="width:4rem" />
|
</template>
|
||||||
</div>
|
<template v-else>
|
||||||
<div class="pdl-10">
|
<strong v-if="scope.row.Error" :title="scope.row.Error" class="red">{{ scope.row.IP }}</strong>
|
||||||
<el-checkbox v-model="item.Disabled" label="禁用记录" style="vertical-align: middle;"/>
|
<span v-else>{{ scope.row.IP }}</span>
|
||||||
</div>
|
</template>
|
||||||
<div class="pdl-10">
|
</template>
|
||||||
<el-button type="danger" @click="handleDel(index)" size="small"><el-icon>
|
</el-table-column>
|
||||||
<Delete />
|
<el-table-column prop="PrefixLength" label="路由掩码" width="80">
|
||||||
</el-icon></el-button>
|
<template #default="scope">
|
||||||
<el-button type="primary" @click="handleAdd(index)" size="small"><el-icon>
|
<template v-if="scope.row.PrefixLengthEditing">
|
||||||
<Plus />
|
<el-input autofocus size="small" v-model="scope.row.PrefixLength"
|
||||||
</el-icon></el-button>
|
@blur="handleEditBlur(scope.row, 'PrefixLength')"></el-input>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
<template v-else>
|
||||||
</template>
|
<strong v-if="scope.row.Error" :title="scope.row.Error" class="red">{{ scope.row.PrefixLength }}</strong>
|
||||||
|
<span v-else>{{ scope.row.PrefixLength }}</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="MapIP" label="目标IP" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<template v-if="scope.row.MapIPEditing">
|
||||||
|
<el-input autofocus size="small" v-model="scope.row.MapIP"
|
||||||
|
@blur="handleEditBlur(scope.row, 'MapIP')"></el-input>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<strong v-if="scope.row.Error" :title="scope.row.Error" class="red">{{ scope.row.MapIP }}</strong>
|
||||||
|
<span v-else>{{ scope.row.MapIP }}</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="MapPrefixLength" label="目标掩码" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<template v-if="scope.row.MapPrefixLengthEditing">
|
||||||
|
<el-input autofocus size="small" v-model="scope.row.MapPrefixLength"
|
||||||
|
@blur="handleEditBlur(scope.row, 'MapPrefixLength')"></el-input>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<strong v-if="scope.row.Error" :title="scope.row.Error" class="red">{{ scope.row.MapPrefixLength }}</strong>
|
||||||
|
<span v-else>{{ scope.row.MapPrefixLength }}</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="Disabled" label="禁用">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-checkbox v-model="scope.row.Disabled" label="禁用记录"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="Oper" label="操作" width="110">
|
||||||
|
<template #default="scope">
|
||||||
|
<div>
|
||||||
|
<el-popconfirm title="删除不可逆,是否确认?" @confirm="handleDel(scope.$index)">
|
||||||
|
<template #reference>
|
||||||
|
<el-button type="danger" size="small">
|
||||||
|
<el-icon><Delete /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
<el-button type="primary" size="small" @click="handleAdd(scope.$index)">
|
||||||
|
<el-icon><Plus /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -43,15 +93,33 @@ export default {
|
|||||||
lans: tuntap.value.current.Lans.slice(0)
|
lans: tuntap.value.current.Lans.slice(0)
|
||||||
});
|
});
|
||||||
if (state.lans.length == 0) {
|
if (state.lans.length == 0) {
|
||||||
state.lans.push({ IP: '0.0.0.0', PrefixLength: 24 });
|
state.lans.push({ IP: '0.0.0.0', PrefixLength: 24,MapIP:'0.0.0.0',MapPrefixLength:24 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMaskChange = (index) => {
|
const handleCellClick = (row, column) => {
|
||||||
var value = +state.lans[index].PrefixLength;
|
handleEdit(row, column.property);
|
||||||
if (value > 32 || value < 0 || isNaN(value)) {
|
}
|
||||||
value = 24;
|
const handleEdit = (row, p) => {
|
||||||
|
state.lans.forEach(c => {
|
||||||
|
c[`IPEditing`] = false;
|
||||||
|
c[`PrefixLengthEditing`] = false;
|
||||||
|
c[`MapIPEditing`] = false;
|
||||||
|
c[`MapPrefixLengthEditing`] = false;
|
||||||
|
})
|
||||||
|
row[`${p}Editing`] = true;
|
||||||
|
row[`__editing`] = true;
|
||||||
|
}
|
||||||
|
const handleEditBlur = (row, p) => {
|
||||||
|
row[`${p}Editing`] = false;
|
||||||
|
row[`__editing`] = false;
|
||||||
|
|
||||||
|
if(p == 'PrefixLength' || p == 'MapPrefixLength'){
|
||||||
|
var value = +row[p];
|
||||||
|
if (value > 32 || value < 0 || isNaN(value)) {
|
||||||
|
value = 24;
|
||||||
|
}
|
||||||
|
row[p] = value;
|
||||||
}
|
}
|
||||||
state.lans[index].PrefixLength = value;
|
|
||||||
}
|
}
|
||||||
const handleDel = (index) => {
|
const handleDel = (index) => {
|
||||||
state.lans.splice(index, 1);
|
state.lans.splice(index, 1);
|
||||||
@@ -68,13 +136,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
state,handleMaskChange,handleDel,handleAdd,getData
|
state,handleDel,handleAdd,getData,handleCellClick,handleEditBlur
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.wrap{
|
.wrap{
|
||||||
padding-right:1rem;
|
padding:0 1rem 1rem 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@@ -2,9 +2,6 @@
|
|||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using linker.messenger.entry;
|
using linker.messenger.entry;
|
||||||
using linker.tunnel;
|
|
||||||
using linker.messenger.relay.client;
|
|
||||||
using linker.messenger.signin;
|
|
||||||
|
|
||||||
namespace linker
|
namespace linker
|
||||||
{
|
{
|
||||||
@@ -27,10 +24,6 @@ namespace linker
|
|||||||
//ThreadPool.SetMinThreads(1024, 1024);
|
//ThreadPool.SetMinThreads(1024, 1024);
|
||||||
//ThreadPool.SetMaxThreads(65535, 65535);
|
//ThreadPool.SetMaxThreads(65535, 65535);
|
||||||
|
|
||||||
//LoggerHelper.Instance.Warning(Process.GetCurrentProcess().MainModule.FileName);
|
|
||||||
//LoggerHelper.Instance.Warning(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
|
|
||||||
//LoggerHelper.Instance.Warning(Directory.GetCurrentDirectory());
|
|
||||||
|
|
||||||
string serviceDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
|
string serviceDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
|
||||||
Directory.SetCurrentDirectory(serviceDirectory);
|
Directory.SetCurrentDirectory(serviceDirectory);
|
||||||
|
|
||||||
@@ -59,6 +52,7 @@ namespace linker
|
|||||||
|
|
||||||
LinkerMessengerEntry.Initialize();
|
LinkerMessengerEntry.Initialize();
|
||||||
LinkerMessengerEntry.Build();
|
LinkerMessengerEntry.Build();
|
||||||
|
|
||||||
LinkerMessengerEntry.Setup(ExcludeModule.None, configDic);
|
LinkerMessengerEntry.Setup(ExcludeModule.None, configDic);
|
||||||
|
|
||||||
LoggerHelper.Instance.Warning($"current version : {VersionHelper.version}");
|
LoggerHelper.Instance.Warning($"current version : {VersionHelper.version}");
|
||||||
|
@@ -24,7 +24,8 @@
|
|||||||
2. 一些修复和优化
|
2. 一些修复和优化
|
||||||
3. 优化自动分配IP
|
3. 优化自动分配IP
|
||||||
4. 优化网卡,排除不明数据包
|
4. 优化网卡,排除不明数据包
|
||||||
5. 优化管理UI,适配移动端</Description>
|
5. 优化管理UI,适配移动端
|
||||||
|
6. 虚拟网卡点对网IP映射</Description>
|
||||||
<Copyright>snltty</Copyright>
|
<Copyright>snltty</Copyright>
|
||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
v1.7.2
|
v1.7.2
|
||||||
2025-04-11 17:28:49
|
2025-04-13 15:50:43
|
||||||
1. 内网穿透的计划任务
|
1. 内网穿透的计划任务
|
||||||
2. 一些修复和优化
|
2. 一些修复和优化
|
||||||
3. 优化自动分配IP
|
3. 优化自动分配IP
|
||||||
4. 优化网卡,排除不明数据包
|
4. 优化网卡,排除不明数据包
|
||||||
5. 优化管理UI,适配移动端
|
5. 优化管理UI,适配移动端
|
||||||
|
6. 虚拟网卡点对网IP映射
|
Reference in New Issue
Block a user