This commit is contained in:
snltty
2025-03-11 21:12:45 +08:00
parent 56e5957fcf
commit 244ebe99c3
34 changed files with 484 additions and 298 deletions

View File

@@ -35,7 +35,7 @@ jobs:
release_name: v1.6.9.${{ steps.date.outputs.today }} release_name: v1.6.9.${{ steps.date.outputs.today }}
draft: false draft: false
prerelease: false prerelease: false
body: "1. 优化linux下路由跟踪问题\r\n2. 优化linux下获取本机IP问题\r\n3. 增加ICS让win7+、win server2008+支持NAT\r\n4. 增加中继cdkey使用cdkey解锁公开中继节点的带宽、流量、连接数限制\r\n5. 增加内外穿透定时开关功能\r\n6. 优化管理页面连接接口的体验\r\n7. 优化一些UI体验去除同步页面将同步功能放至各个实际的位置\r\n8. 优化端口转发,让不同分组间可以使用相同端口\r\n9. 其它一些修复优化" body: "1. 优化linux下路由跟踪问题,提高启动速度\r\n2. 优化linux下获取本机IP问题,提升虚拟网卡稳定性\r\n3. 增加ICS让win7+、win server2008+支持NAT\r\n4. 增加中继cdkey使用cdkey解锁公开中继节点的带宽、流量、连接数限制\r\n5. 增加内外穿透定时开关功能\r\n6. 优化管理页面连接接口的体验\r\n7. 优化一些UI体验去除同步页面将同步功能放至各个实际的位置\r\n8. 优化端口转发,让不同分组间可以使用相同端口\r\n9. 从命令行参数初始化配置\r\n10. 优化打洞IPV6支持的更多了\r\n11. 其它一些修复优化"
- name: publish projects - name: publish projects
run: ./publish.bat run: ./publish.bat
- name: upload-win-x86-oss - name: upload-win-x86-oss
@@ -218,46 +218,6 @@ jobs:
asset_path: ./public/publish-zip/linker-linux-musl-arm64.zip asset_path: ./public/publish-zip/linker-linux-musl-arm64.zip
asset_name: linker-linux-musl-arm64.zip asset_name: linker-linux-musl-arm64.zip
asset_content_type: application/zip asset_content_type: application/zip
- name: upload-osx-x64-oss
id: upload-osx-x64-oss
uses: tvrcgo/oss-action@v0.1.1
with:
region: oss-cn-shenzhen
key-id: ${{ secrets.ALIYUN_OSS_ID }}
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
bucket: ide-qbcode
asset-path: ./public/publish-zip/linker-osx-x64.zip
target-path: /downloads/linker/v1.6.9/linker-osx-x64.zip
- name: upload-osx-x64
id: upload-osx-x64
uses: actions/upload-release-asset@master
env:
GITHUB_TOKEN: ${{ secrets.ACTIONS_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./public/publish-zip/linker-osx-x64.zip
asset_name: linker-osx-x64.zip
asset_content_type: application/zip
- name: upload-osx-arm64-oss
id: upload-osx-arm64-oss
uses: tvrcgo/oss-action@v0.1.1
with:
region: oss-cn-shenzhen
key-id: ${{ secrets.ALIYUN_OSS_ID }}
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
bucket: ide-qbcode
asset-path: ./public/publish-zip/linker-osx-arm64.zip
target-path: /downloads/linker/v1.6.9/linker-osx-arm64.zip
- name: upload-osx-arm64
id: upload-osx-arm64
uses: actions/upload-release-asset@master
env:
GITHUB_TOKEN: ${{ secrets.ACTIONS_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./public/publish-zip/linker-osx-arm64.zip
asset_name: linker-osx-arm64.zip
asset_content_type: application/zip
- name: upload-version-oss - name: upload-version-oss
id: upload-version-oss id: upload-version-oss
uses: tvrcgo/oss-action@v0.1.1 uses: tvrcgo/oss-action@v0.1.1

View File

@@ -50,8 +50,7 @@ function writeUpload(data, tagName) {
const platforms = { const platforms = {
'win': ['x86', 'x64', 'arm64'], 'win': ['x86', 'x64', 'arm64'],
'linux': ['x64', 'arm', 'arm64'], 'linux': ['x64', 'arm', 'arm64'],
'linux-musl': ['x64', 'arm', 'arm64'], 'linux-musl': ['x64', 'arm', 'arm64']
'osx': ['x64', 'arm64'],
}; };
for (let plat in platforms) { for (let plat in platforms) {
let archs = platforms[plat]; let archs = platforms[plat];

View File

@@ -30,7 +30,7 @@ del /f .\public\extends\win-arm64\linker-win-arm64\linker.ics.pdb
msbuild "src\\linker.ics\\linker.ics.csproj" -p:Configuration=Release -p:OutputPath=../../public/extends/win-x86/linker-win-x86 msbuild "src\\linker.ics\\linker.ics.csproj" -p:Configuration=Release -p:OutputPath=../../public/extends/win-x86/linker-win-x86
del /f .\public\extends\win-x86\linker-win-x86\linker.ics.pdb del /f .\public\extends\win-x86\linker-win-x86\linker.ics.pdb
for %%r in (win-x86,win-x64,win-arm64,linux-x64,linux-arm,linux-arm64,linux-musl-x64,linux-musl-arm,linux-musl-arm64,osx-x64,osx-arm64) do ( for %%r in (win-x86,win-x64,win-arm64,linux-x64,linux-arm,linux-arm64,linux-musl-x64,linux-musl-arm,linux-musl-arm64) do (
dotnet publish ./src/linker -c release -f net8.0 -o ./public/publish/%%r/linker-%%r -r %%r -p:PublishSingleFile=true --self-contained true -p:TrimMode=partial -p:TieredPGO=true -p:DebugType=full -p:EventSourceSupport=false -p:DebugSymbols=true -p:EnableCompressionInSingleFile=true -p:DebuggerSupport=false -p:EnableUnsafeBinaryFormatterSerialization=false -p:EnableUnsafeUTF7Encoding=false -p:HttpActivityPropagationSupport=false -p:InvariantGlobalization=true -p:MetadataUpdaterSupport=false -p:UseSystemResourceKeys=true -p:MetricsSupport=false -p:StackTraceSupport=false -p:XmlResolverIsNetworkingEnabledByDefault=false dotnet publish ./src/linker -c release -f net8.0 -o ./public/publish/%%r/linker-%%r -r %%r -p:PublishSingleFile=true --self-contained true -p:TrimMode=partial -p:TieredPGO=true -p:DebugType=full -p:EventSourceSupport=false -p:DebugSymbols=true -p:EnableCompressionInSingleFile=true -p:DebuggerSupport=false -p:EnableUnsafeBinaryFormatterSerialization=false -p:EnableUnsafeUTF7Encoding=false -p:HttpActivityPropagationSupport=false -p:InvariantGlobalization=true -p:MetadataUpdaterSupport=false -p:UseSystemResourceKeys=true -p:MetricsSupport=false -p:StackTraceSupport=false -p:XmlResolverIsNetworkingEnabledByDefault=false
echo F|xcopy "public\\extends\\%%r\\linker-%%r\\*" "public\\publish\\%%r\\linker-%%r\\*" /s /f /h /y echo F|xcopy "public\\extends\\%%r\\linker-%%r\\*" "public\\publish\\%%r\\linker-%%r\\*" /s /f /h /y

View File

@@ -1,4 +1,8 @@
namespace linker.app using Microsoft.Maui.Controls.PlatformConfiguration;
using System.Diagnostics;
using System.Net.NetworkInformation;
namespace linker.app
{ {
public partial class MainPage : ContentPage public partial class MainPage : ContentPage
{ {
@@ -19,6 +23,7 @@
CounterBtn.Text = $"Clicked {count} times"; CounterBtn.Text = $"Clicked {count} times";
SemanticScreenReader.Announce(CounterBtn.Text); SemanticScreenReader.Announce(CounterBtn.Text);
} }
} }

View File

@@ -4,6 +4,7 @@ using Android.Content;
using Android.Content.PM; using Android.Content.PM;
using Android.Health.Connect.DataTypes.Units; using Android.Health.Connect.DataTypes.Units;
using Android.Net; using Android.Net;
using Android.Nfc;
using Android.OS; using Android.OS;
using Android.Views; using Android.Views;
using AndroidX.Core.App; using AndroidX.Core.App;
@@ -35,6 +36,8 @@ namespace linker.app
StartActivityForResult(intent, 0); StartActivityForResult(intent, 0);
} }
StartForegroundService(new Intent(this, typeof(VpnServiceLinker))); StartForegroundService(new Intent(this, typeof(VpnServiceLinker)));
} }
protected override void OnStart() protected override void OnStart()
@@ -77,6 +80,13 @@ namespace linker.app
if (vpnInterface != null) if (vpnInterface != null)
{ {
Android.Util.Log.Error(TAG, "==============================================VPN is connected"); Android.Util.Log.Error(TAG, "==============================================VPN is connected");
/*
Task.Run(async () =>
{
});
*/
/* /*
Task.Run(async () => Task.Run(async () =>
{ {

View File

@@ -7,6 +7,7 @@ sidebar_position: 3
## 1、公共配置文件客户端服务端都有的 ## 1、公共配置文件客户端服务端都有的
:::tip[服务端运行流程] :::tip[服务端运行流程]
common.json
``` ```
{ {
//运行在哪个模式下,多个模式可同时存在 //运行在哪个模式下,多个模式可同时存在
@@ -41,24 +42,20 @@ sidebar_position: 3
4. 修改server.json可以去在线生成一些GUID作为各个功能的密钥 4. 修改server.json可以去在线生成一些GUID作为各个功能的密钥
5. 再次运行程序 5. 再次运行程序
common.json
```
{
"Modes": ["server"]
}
```
server.json server.json
``` ```
{ {
//中继 //中继
"Relay": { "Relay": {
//中继密钥,客户端密钥不准确时无法使用本中继 //中继密钥,客户端密钥不准确时无法使用本中继
"SecretKey": "" "SecretKey": "",
"Cdkey": {
//cdkey 加密密钥
"SecretKey": "snltty"
},
}, },
//信标服务器端口 //信标服务器端口
"ServicePort": 1802, "ServicePort": 1802,
//内网穿透配置 //内网穿透配置
"SForward": { "SForward": {
//内网穿透密钥 //内网穿透密钥
@@ -82,6 +79,14 @@ server.json
} }
} }
``` ```
action.json
```
{
"SignInActionUrl": "", //登入信标的验证接口
"RelayActionUrl": "", //中继验证接口
"SForwardActionUrl": ""//服务器穿透的验证接口
}
```
::: :::
## 3、客户端使用web初始化 ## 3、客户端使用web初始化
@@ -93,6 +98,63 @@ server.json
![](./img/mode.png) ![](./img/mode.png)
![](./img/client.png) ![](./img/client.png)
![](./img/save.png) ![](./img/save.png)
对应配置文件 client.json
```
{
"Name": "A", //客户端名
"Access": 4503599627370495, //权限值,可以看`src/linker.messenger.api/Config.cs`里的`AccessValue`枚举
"CApi": {
"ApiPort": 1803, //api接口
"ApiPassword": "snltty", //api密码
"WebPort": 1804, //web端口
"WebRoot": "./web/" //web根目录
},
"Servers": [
{
"Name": "Linker", //信标服务器名称
"Host": "127.0.0.1:1802", //信标服务器地址
"SecretKey": null, //信标密钥
"UserId": "8225e9d4-0ac7-4d76-9946-c4fe04ad4696" //用户标识,多个客户端可相同
}
],
"Groups": [
{
"Name": "snltty", //分组名
"Id": "snltty", //分组id
"Password": "snltty" //分组密码
}
],
"Relay": {
"Servers": [
{
"SecretKey": "snltty", //中继密钥
"Disabled": false, //是否禁用
"SSL": true //启用ssl
}
]
},
"SForward": {
"SecretKey": "snltty" //服务器穿透密钥
},
"Updater": {
"SecretKey": "snltty" //更新密钥
},
"Action": {
"Args": {} //自定义验证的参数
}
}
```
::: :::
## 4、使用参数初始化
:::tip[v1.7.0+]
1. 如果你使用第三方程序启动linker`仅配置文件不存在时初始化,后续不会覆盖`,可以传入参数进行初始化,`client.json``server.json``action.json``common.json`
2. 像这样不填写的字段将以默认值生成json的双引号需要转义json里不能有空格
```
linker.exe --config-client {\"a\":\"1\"} --config-server {\"a\":\"1\"} --config-action {\"a\":\"1\"} --config-common {\"a\":\"1\"}
```
:::

View File

@@ -0,0 +1,149 @@
---
sidebar_position: 9
---
# 8.9、中继CDKEY
:::tip[v1.7.0+]
在哪里发卡都可以可以自己开发卡密商城只要最后按一定的字段和加密方式生成cdkey就可以导入使用
:::
### 1、CDKEY参数字段
:::tip[说明]
```
{
"GB":100, //流量GB
"Speed":100, //带宽 Mbps
"Time":"1-0-0 0:0:0", //持续时间 年-月-日 时:分:秒
"WidgetUserId":"2b9c9fef-c342-c968-9dc0-c15e3bc23646",//用户标识
"OrderId":"ACG1234567890", //订单号
"Contact":"111@qq.com", //联系方式
"CostPrice":1, //成本元 double
"Price":1, //原价元 double
"UserPrice":1, //会员价元 double
"PayPrice":1, //支付金额元 double
"Count":1, //数量,导入后,会 GB*Count
"Type":"Relay", //类型 固定 Relay
}
```
:::
### 2、加密方式AES-128-ECB
:::tip[说明]
1. C#样例
```
AesCrypto crypto = new AesCrypto("加密密钥", System.Security.Cryptography.PaddingMode.PKCS7);
//加密
byte[] bytes = crypto.Encode(jsonString);
string cdkey = Convert.ToBase64String(bytes);
public sealed class AesCrypto : ISymmetricCrypto
{
private ICryptoTransform encryptoTransform;
private ICryptoTransform decryptoTransform;
public string Password { get; set; }
public AesCrypto(string password, PaddingMode mode = PaddingMode.ANSIX923)
{
Password = password;
using Aes aes = Aes.Create();
aes.Padding = mode;
(aes.Key, aes.IV) = GenerateKeyAndIV(password);
encryptoTransform = aes.CreateEncryptor(aes.Key, aes.IV);
decryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV);
}
public byte[] Encode(byte[] buffer)
{
return Encode(buffer, 0, buffer.Length);
}
public byte[] Encode(byte[] buffer, int offset, int length)
{
return encryptoTransform.TransformFinalBlock(buffer, offset, length);
}
public Memory<byte> Decode(byte[] buffer)
{
return Decode(buffer, 0, buffer.Length);
}
public Memory<byte> Decode(byte[] buffer, int offset, int length)
{
return decryptoTransform.TransformFinalBlock(buffer, offset, length);
}
public void Dispose()
{
encryptoTransform.Dispose();
decryptoTransform.Dispose();
}
private (byte[] Key, byte[] IV) GenerateKeyAndIV(string password)
{
byte[] key = new byte[16];
byte[] iv = new byte[16];
using SHA384 sha = SHA384.Create();
byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(password));
Array.Copy(hash, 0, key, 0, key.Length);
Array.Copy(hash, key.Length, iv, 0, iv.Length);
return (Key: key, IV: iv);
}
}
```
2. PHP样例
```
//加密
$aesCrypto = new AesCrypto("加密密钥");
$cdkey = base64_encode($aesCrypto->encode($jsonString));
class AesCrypto
{
private $key;
private $iv;
private $password;
public function __construct($password)
{
$this->password = $password;
$this->initAes();
}
private function initAes()
{
$keyAndIV = $this->generateKeyAndIV($this->password);
$this->key = $keyAndIV['key'];
$this->iv = $keyAndIV['iv'];
}
public function encode($data)
{
return $this->encodeWithOffset($data, 0, strlen($data));
}
public function encodeWithOffset($data, $offset, $length)
{
$data = substr($data, $offset, $length);
return openssl_encrypt($data, 'AES-128-CBC', $this->key, OPENSSL_RAW_DATA, $this->iv);
}
public function decode($data)
{
return $this->decodeWithOffset($data, 0, strlen($data));
}
public function decodeWithOffset($data, $offset, $length)
{
$data = substr($data, $offset, $length);
return openssl_decrypt($data, 'AES-128-CBC', $this->key, OPENSSL_RAW_DATA, $this->iv);
}
private function generateKeyAndIV($password)
{
$hash = hash('sha384', $password, true);
$key = substr($hash, 0, 16);
$iv = substr($hash, 16, 16);
return ['key' => $key, 'iv' => $iv];
}
}
```
:::

View File

@@ -1,7 +1,7 @@
--- ---
sidebar_position: 10 sidebar_position: 9
--- ---
# 10、公益赞助 # 9、公益赞助
![Docusaurus Plushie](./img/qr.jpg) ![Docusaurus Plushie](./img/qr.jpg)

View File

@@ -1,74 +0,0 @@
---
sidebar_position: 9
---
# 9、收费项目
<table width="100%">
<thead>
<tr>
<th>Linker</th>
<th>2设备内</th>
<th>2-5设备</th>
<th>5-10设备</th>
<th>10设备以上</th>
<th>权益</th>
</tr>
</thead>
<tbody>
<tr>
<td>使用</td>
<td>免费</td>
<td>免费</td>
<td>免费</td>
<td>免费</td>
<td>/</td>
</tr>
<tr>
<td>开源集成</td>
<td>免费</td>
<td>免费</td>
<td>免费</td>
<td>免费</td>
<td>/</td>
</tr>
<tr>
<td>安装配置维护/次</td>
<td>20/次</td>
<td>50/次</td>
<td>100/次</td>
<td>10/台/次</td>
<td>私有化部署、配置、BUG修复</td>
</tr>
<tr>
<td>安装配置维护/年</td>
<td>99/年</td>
<td>239/年</td>
<td>399/年</td>
<td>29/台/年</td>
<td>私有化部署、配置、BUG修复、技术解答</td>
</tr>
<tr>
<td>商业集成</td>
<td colspan="5" align="center">详谈,技术解答,原理讲解,二开指导</td>
</tr>
<tr>
<td>功能定制</td>
<td colspan="5" align="center">详谈,技术解答,原理讲解,二开指导</td>
</tr>
</tbody>
</table>
<table width="100%">
<thead>
<tr>
<th>证书</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>3个月泛域名证书</td>
<td align="center">9.9/个</td>
</tr>
</tbody>
</table>

View File

@@ -73,6 +73,10 @@ namespace linker.libs
} }
Enqueue(new LoggerModel { Type = LoggerTypes.WARNING, Content = content }); Enqueue(new LoggerModel { Type = LoggerTypes.WARNING, Content = content });
} }
public void Warning(Exception ex)
{
Enqueue(new LoggerModel { Type = LoggerTypes.WARNING, Content = ex + "" });
}
public void Error(string content, params object[] args) public void Error(string content, params object[] args)
{ {
if (args != null && args.Length > 0) if (args != null && args.Length > 0)

View File

@@ -42,6 +42,15 @@ namespace linker.libs
public static class NetworkHelper public static class NetworkHelper
{ {
public static IPEndPoint TransEndpointFamily(IPEndPoint ep)
{
if (ep.Address.AddressFamily == AddressFamily.InterNetworkV6 && ep.Address.IsIPv4MappedToIPv6)
{
return new IPEndPoint(new IPAddress(ep.Address.GetAddressBytes()[^4..]), ep.Port);
}
return ep;
}
/// <summary> /// <summary>
/// 域名解析 /// 域名解析
@@ -217,7 +226,9 @@ namespace linker.libs
{ {
return GetIP() return GetIP()
.Where(c => c.AddressFamily == AddressFamily.InterNetworkV6) .Where(c => c.AddressFamily == AddressFamily.InterNetworkV6)
.Where(c => c.GetAddressBytes().AsSpan(0, 8).SequenceEqual(ipv6LocalBytes) == false).Distinct().ToArray(); ; .Where(c => c.GetAddressBytes().AsSpan(0, 8).SequenceEqual(ipv6LocalBytes) == false)
.Where(c=>c.Equals(IPAddress.IPv6Loopback) == false)
.Distinct().ToArray();
} }
public static IPAddress[] GetIPV4() public static IPAddress[] GetIPV4()
{ {

View File

@@ -18,6 +18,7 @@ using linker.messenger.updater;
using linker.messenger.store.file; using linker.messenger.store.file;
using linker.messenger.serializer.memorypack; using linker.messenger.serializer.memorypack;
using linker.libs; using linker.libs;
using linker.libs.extends;
namespace linker.messenger.entry namespace linker.messenger.entry
{ {
@@ -29,6 +30,11 @@ namespace linker.messenger.entry
private static OperatingManager builded = new OperatingManager(); private static OperatingManager builded = new OperatingManager();
private static OperatingManager setuped = new OperatingManager(); private static OperatingManager setuped = new OperatingManager();
public static void InputJsonConfig(string str)
{
Console.WriteLine(str.DeJson<ConfigClientInfo>().ToJsonFormat());
}
/// <summary> /// <summary>
/// 开始初始化 /// 开始初始化
/// </summary> /// </summary>
@@ -76,7 +82,7 @@ namespace linker.messenger.entry
.AddTuntapClient().AddTuntapServer() .AddTuntapClient().AddTuntapServer()
//更新 //更新
.AddUpdaterClient().AddUpdaterServer() .AddUpdaterClient().AddUpdaterServer()
//信标 //信标
.AddMessenger() .AddMessenger()
//流量统计 //流量统计
@@ -131,7 +137,7 @@ namespace linker.messenger.entry
/// 开始运行 /// 开始运行
/// </summary> /// </summary>
/// <param name="modules">排除哪些模块,默认无</param> /// <param name="modules">排除哪些模块,默认无</param>
public static void Setup(ExcludeModule modules = ExcludeModule.None) public static void Setup(ExcludeModule modules = ExcludeModule.None, Dictionary<string, string> configDic = null)
{ {
if (setuped.StartOperation() == false) return; if (setuped.StartOperation() == false) return;
@@ -139,7 +145,7 @@ namespace linker.messenger.entry
serviceProvider.UseMessenger(); serviceProvider.UseMessenger();
if ((modules & ExcludeModule.StoreFile) != ExcludeModule.StoreFile) if ((modules & ExcludeModule.StoreFile) != ExcludeModule.StoreFile)
serviceProvider.UseStoreFile(); serviceProvider.UseStoreFile(configDic);
if ((modules & ExcludeModule.SerializerMemoryPack) != ExcludeModule.SerializerMemoryPack) if ((modules & ExcludeModule.SerializerMemoryPack) != ExcludeModule.SerializerMemoryPack)
serviceProvider.UseSerializerMemoryPack(); serviceProvider.UseSerializerMemoryPack();

View File

@@ -96,7 +96,7 @@ namespace linker.messenger.relay.client.transport
Stream = sslStream, Stream = sslStream,
Socket = socket, Socket = socket,
Mode = TunnelMode.Client, Mode = TunnelMode.Client,
IPEndPoint = socket.RemoteEndPoint as IPEndPoint, IPEndPoint = NetworkHelper.TransEndpointFamily(socket.RemoteEndPoint as IPEndPoint),
TransactionId = relayInfo.TransactionId, TransactionId = relayInfo.TransactionId,
TransportName = Name, TransportName = Name,
Type = TunnelType.Relay, Type = TunnelType.Relay,
@@ -286,7 +286,7 @@ namespace linker.messenger.relay.client.transport
Stream = sslStream, Stream = sslStream,
Socket = socket, Socket = socket,
Mode = TunnelMode.Server, Mode = TunnelMode.Server,
IPEndPoint = socket.RemoteEndPoint as IPEndPoint, IPEndPoint = NetworkHelper.TransEndpointFamily(socket.RemoteEndPoint as IPEndPoint),
TransactionId = relayInfo.TransactionId, TransactionId = relayInfo.TransactionId,
TransportName = Name, TransportName = Name,
Type = TunnelType.Relay, Type = TunnelType.Relay,

View File

@@ -6,6 +6,7 @@ using linker.messenger.relay.server;
using linker.messenger.signin; using linker.messenger.signin;
using linker.messenger.relay.server.validator; using linker.messenger.relay.server.validator;
using linker.libs.extends; using linker.libs.extends;
using System.Collections.Generic;
namespace linker.messenger.relay.messenger namespace linker.messenger.relay.messenger
{ {
@@ -73,6 +74,7 @@ namespace linker.messenger.relay.messenger
await RelayTest(connection, info, (validated) => await RelayTest(connection, info, (validated) =>
{ {
List<RelayServerNodeReportInfo> list = relayServerTransfer.GetNodes(validated).Select(c => (RelayServerNodeReportInfo)c).ToList(); List<RelayServerNodeReportInfo> list = relayServerTransfer.GetNodes(validated).Select(c => (RelayServerNodeReportInfo)c).ToList();
return serializer.Serialize(list); return serializer.Serialize(list);
}); });
} }
@@ -264,10 +266,6 @@ namespace linker.messenger.relay.messenger
public void NodeReport(IConnection connection) public void NodeReport(IConnection connection)
{ {
RelayServerNodeReportInfo170 info = serializer.Deserialize<RelayServerNodeReportInfo170>(connection.ReceiveRequestWrap.Payload.Span); RelayServerNodeReportInfo170 info = serializer.Deserialize<RelayServerNodeReportInfo170>(connection.ReceiveRequestWrap.Payload.Span);
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Debug($"relay node report : {info.ToJson()}");
}
relayServerTransfer.SetNodeReport(connection, info); relayServerTransfer.SetNodeReport(connection, info);
} }
/// <summary> /// <summary>
@@ -279,10 +277,7 @@ namespace linker.messenger.relay.messenger
public void UpdateNode(IConnection connection) public void UpdateNode(IConnection connection)
{ {
RelayServerNodeUpdateInfo info = serializer.Deserialize<RelayServerNodeUpdateInfo>(connection.ReceiveRequestWrap.Payload.Span); RelayServerNodeUpdateInfo info = serializer.Deserialize<RelayServerNodeUpdateInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (relayServerNodeTransfer.Id == info.Id) relayServerNodeTransfer.UpdateNode(info);
{
relayServerNodeTransfer.UpdateNode(info);
}
} }
/// <summary> /// <summary>
/// 更新节点转发 /// 更新节点转发

View File

@@ -45,8 +45,6 @@ namespace linker.messenger.relay.server
public sealed class RelayServerNodeInfo public sealed class RelayServerNodeInfo
{ {
public const string MASTER_NODE_ID = "824777CF-2804-83FE-DE71-69B7B7D3BBA7";
private string id = Guid.NewGuid().ToString().ToUpper(); private string id = Guid.NewGuid().ToString().ToUpper();
public string Id public string Id
{ {

View File

@@ -1,4 +1,5 @@
using linker.libs; using linker.libs;
using linker.libs.extends;
using linker.messenger.relay.messenger; using linker.messenger.relay.messenger;
using linker.messenger.relay.server.caching; using linker.messenger.relay.server.caching;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -66,17 +67,23 @@ namespace linker.messenger.relay.server
{ {
try try
{ {
if (info.Id == RelayServerNodeInfo.MASTER_NODE_ID)
{ if (info.EndPoint.Address.Equals(IPAddress.Any))
info.EndPoint = new IPEndPoint(IPAddress.Any, 0);
}
else if (info.EndPoint.Address.Equals(IPAddress.Any))
{ {
info.EndPoint.Address = connection.Address.Address; info.EndPoint.Address = connection.Address.Address;
} }
if (info.EndPoint.Address.Equals(IPAddress.Loopback))
{
info.EndPoint = new IPEndPoint(IPAddress.Any, 0);
}
info.LastTicks = Environment.TickCount64; info.LastTicks = Environment.TickCount64;
info.Connection = connection; info.Connection = connection;
reports.AddOrUpdate(info.Id, info, (a, b) => info); reports.AddOrUpdate(info.Id, info, (a, b) => info);
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Debug($"relay node report : {info.ToJson()}");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -92,8 +99,6 @@ namespace linker.messenger.relay.server
/// <param name="info"></param> /// <param name="info"></param>
public async Task UpdateNodeReport(RelayServerNodeUpdateInfo info) public async Task UpdateNodeReport(RelayServerNodeUpdateInfo info)
{ {
if (RelayServerNodeInfo.MASTER_NODE_ID == info.Id) return;
if (reports.TryGetValue(info.Id, out RelayServerNodeReportInfo170 cache)) if (reports.TryGetValue(info.Id, out RelayServerNodeReportInfo170 cache))
{ {
await messengerSender.SendOnly(new MessageRequestWrap await messengerSender.SendOnly(new MessageRequestWrap
@@ -143,7 +148,7 @@ namespace linker.messenger.relay.server
/// </summary> /// </summary>
/// <param name="relayTrafficUpdateInfo"></param> /// <param name="relayTrafficUpdateInfo"></param>
/// <returns></returns> /// <returns></returns>
public void AddTraffic(Dictionary<long,long> dic) public void AddTraffic(Dictionary<long, long> dic)
{ {
if (dic.Count > 0) if (dic.Count > 0)
trafficQueue.Enqueue(dic); trafficQueue.Enqueue(dic);

View File

@@ -13,11 +13,13 @@ namespace linker.messenger.relay.server
/// </summary> /// </summary>
public class RelayServerNodeTransfer public class RelayServerNodeTransfer
{ {
public string Id => relayServerNodeStore.Node.Id; /// <summary>
/// 配置了就用配置的,每配置就用一个默认的
/// </summary>
public RelayServerNodeInfo node = null;
private uint connectionNum = 0; private uint connectionNum = 0;
private IConnection localConnection; private IConnection connection;
private IConnection remoteConnection;
private long bytes = 0; private long bytes = 0;
private long lastBytes = 0; private long lastBytes = 0;
@@ -38,7 +40,23 @@ namespace linker.messenger.relay.server
this.messengerResolver = messengerResolver; this.messengerResolver = messengerResolver;
this.messengerSender = messengerSender; this.messengerSender = messengerSender;
limitTotal.SetLimit((uint)Math.Ceiling((relayServerNodeStore.Node.MaxBandwidthTotal * 1024 * 1024) / 8.0)); node = string.IsNullOrWhiteSpace(relayServerNodeStore.Node.MasterHost) ? new RelayServerNodeInfo
{
Id = "824777CF-2804-83FE-DE71-69B7B7D3BBA7",
Host = new IPEndPoint(IPAddress.Any, relayServerNodeStore.ServicePort).ToString(),
MasterHost = new IPEndPoint(IPAddress.Loopback, relayServerNodeStore.ServicePort).ToString(),
MasterSecretKey = relayServerMasterStore.Master.SecretKey,
MaxBandwidth = 0,
MaxConnection = 0,
MaxBandwidthTotal = 0,
MaxGbTotal = 0,
MaxGbTotalLastBytes = 0,
MaxGbTotalMonth = 0,
Name = "default",
Public = false,
} : relayServerNodeStore.Node;
limitTotal.SetLimit((uint)Math.Ceiling((node.MaxBandwidthTotal * 1024 * 1024) / 8.0));
TrafficTask(); TrafficTask();
ReportTask(); ReportTask();
@@ -46,12 +64,10 @@ namespace linker.messenger.relay.server
} }
public async ValueTask<RelayCacheInfo> TryGetRelayCache(string key, string nodeid) public async ValueTask<RelayCacheInfo> TryGetRelayCache(string key)
{ {
try try
{ {
IConnection connection = relayServerNodeStore.Node.Id == RelayServerNodeInfo.MASTER_NODE_ID ? localConnection : remoteConnection;
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{ {
Connection = connection, Connection = connection,
@@ -73,7 +89,8 @@ namespace linker.messenger.relay.server
public void UpdateNode(RelayServerNodeUpdateInfo info) public void UpdateNode(RelayServerNodeUpdateInfo info)
{ {
relayServerNodeStore.UpdateInfo(info); if (info.Id == node.Id)
relayServerNodeStore.UpdateInfo(info);
} }
/// <summary> /// <summary>
@@ -84,6 +101,7 @@ namespace linker.messenger.relay.server
{ {
//已认证的没有流量限制 //已认证的没有流量限制
if (relayCache.Validated) return true; if (relayCache.Validated) return true;
if (node.Public == false) return false;
//流量卡有的,就能继续用 //流量卡有的,就能继续用
if (relayCache.Cdkey.Any(c => c.LastBytes > 0)) return true; if (relayCache.Cdkey.Any(c => c.LastBytes > 0)) return true;
@@ -95,9 +113,9 @@ namespace linker.messenger.relay.server
/// <returns></returns> /// <returns></returns>
private bool ValidateConnection(RelayCacheInfo relayCache) private bool ValidateConnection(RelayCacheInfo relayCache)
{ {
bool res = relayServerNodeStore.Node.MaxConnection == 0 || relayServerNodeStore.Node.MaxConnection * 2 > connectionNum; bool res = node.MaxConnection == 0 || node.MaxConnection * 2 > connectionNum;
if (res == false && LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) if (res == false && LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Debug($"relay ValidateConnection false,{connectionNum}/{relayServerNodeStore.Node.MaxConnection * 2}"); LoggerHelper.Instance.Debug($"relay ValidateConnection false,{connectionNum}/{node.MaxConnection * 2}");
return res; return res;
} }
@@ -107,11 +125,11 @@ namespace linker.messenger.relay.server
/// <returns></returns> /// <returns></returns>
private bool ValidateBytes(RelayCacheInfo relayCache) private bool ValidateBytes(RelayCacheInfo relayCache)
{ {
bool res = relayServerNodeStore.Node.MaxGbTotal == 0 bool res = node.MaxGbTotal == 0
|| (relayServerNodeStore.Node.MaxGbTotal > 0 && relayServerNodeStore.Node.MaxGbTotalLastBytes > 0); || (node.MaxGbTotal > 0 && node.MaxGbTotalLastBytes > 0);
if (res == false && LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) if (res == false && LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Debug($"relay ValidateBytes false,{relayServerNodeStore.Node.MaxGbTotalLastBytes}bytes/{relayServerNodeStore.Node.MaxGbTotal}gb"); LoggerHelper.Instance.Debug($"relay ValidateBytes false,{node.MaxGbTotalLastBytes}bytes/{node.MaxGbTotal}gb");
return res; return res;
} }
@@ -181,14 +199,14 @@ namespace linker.messenger.relay.server
//验证过的,不消耗流量 //验证过的,不消耗流量
if (cache.Cache.Validated) return true; if (cache.Cache.Validated) return true;
//节点无流量限制的,不消耗流量 //节点无流量限制的,不消耗流量
if (relayServerNodeStore.Node.MaxGbTotal == 0) return true; if (node.MaxGbTotal == 0) return true;
Interlocked.Add(ref cache.Sendt, length); Interlocked.Add(ref cache.Sendt, length);
var current = cache.CurrentCdkey; var current = cache.CurrentCdkey;
if (current != null) return current.LastBytes > 0; if (current != null) return current.LastBytes > 0;
return relayServerNodeStore.Node.MaxGbTotalLastBytes > 0; return node.MaxGbTotalLastBytes > 0;
} }
/// <summary> /// <summary>
@@ -198,7 +216,7 @@ namespace linker.messenger.relay.server
private void SetLimit(RelayTrafficCacheInfo relayCache) private void SetLimit(RelayTrafficCacheInfo relayCache)
{ {
//无限制 //无限制
if (relayCache.Cache.Validated || relayServerNodeStore.Node.MaxBandwidth == 0) if (relayCache.Cache.Validated || node.MaxBandwidth == 0)
{ {
relayCache.Limit.SetLimit(0); relayCache.Limit.SetLimit(0);
return; return;
@@ -206,14 +224,14 @@ namespace linker.messenger.relay.server
RelayServerCdkeyInfo currentCdkey = relayCache.Cache.Cdkey.Where(c => c.LastBytes > 0).OrderByDescending(c => c.Bandwidth).FirstOrDefault(); RelayServerCdkeyInfo currentCdkey = relayCache.Cache.Cdkey.Where(c => c.LastBytes > 0).OrderByDescending(c => c.Bandwidth).FirstOrDefault();
//有cdkey且带宽大于节点带宽就用cdkey的带宽 //有cdkey且带宽大于节点带宽就用cdkey的带宽
if (currentCdkey != null && (currentCdkey.Bandwidth == 0 || currentCdkey.Bandwidth >= relayServerNodeStore.Node.MaxBandwidth || relayServerNodeStore.Node.MaxGbTotalLastBytes == 0)) if (currentCdkey != null && (currentCdkey.Bandwidth == 0 || currentCdkey.Bandwidth >= node.MaxBandwidth || node.MaxGbTotalLastBytes == 0))
{ {
relayCache.CurrentCdkey = currentCdkey; relayCache.CurrentCdkey = currentCdkey;
relayCache.Limit.SetLimit((uint)Math.Ceiling((relayCache.CurrentCdkey.Bandwidth * 1024 * 1024) / 8.0)); relayCache.Limit.SetLimit((uint)Math.Ceiling((relayCache.CurrentCdkey.Bandwidth * 1024 * 1024) / 8.0));
return; return;
} }
relayCache.CurrentCdkey = null; relayCache.CurrentCdkey = null;
relayCache.Limit.SetLimit((uint)Math.Ceiling((relayServerNodeStore.Node.MaxBandwidth * 1024 * 1024) / 8.0)); relayCache.Limit.SetLimit((uint)Math.Ceiling((node.MaxBandwidth * 1024 * 1024) / 8.0));
} }
/// <summary> /// <summary>
@@ -236,18 +254,20 @@ namespace linker.messenger.relay.server
} }
private void ResetNodeBytes() private void ResetNodeBytes()
{ {
if (node.MaxGbTotal == 0) return;
foreach (var cache in trafficDict.Values.Where(c => c.CurrentCdkey == null)) foreach (var cache in trafficDict.Values.Where(c => c.CurrentCdkey == null))
{ {
long length = Interlocked.Exchange(ref cache.Sendt, 0); long length = Interlocked.Exchange(ref cache.Sendt, 0);
if (relayServerNodeStore.Node.MaxGbTotalLastBytes >= length) if (node.MaxGbTotalLastBytes >= length)
relayServerNodeStore.SetMaxGbTotalLastBytes(relayServerNodeStore.Node.MaxGbTotalLastBytes - length); relayServerNodeStore.SetMaxGbTotalLastBytes(node.MaxGbTotalLastBytes - length);
else relayServerNodeStore.SetMaxGbTotalLastBytes(0); else relayServerNodeStore.SetMaxGbTotalLastBytes(0);
} }
if (relayServerNodeStore.Node.MaxGbTotalMonth != DateTime.Now.Month) if (node.MaxGbTotalMonth != DateTime.Now.Month)
{ {
relayServerNodeStore.SetMaxGbTotalMonth(DateTime.Now.Month); relayServerNodeStore.SetMaxGbTotalMonth(DateTime.Now.Month);
relayServerNodeStore.SetMaxGbTotalLastBytes((long)(relayServerNodeStore.Node.MaxGbTotal * 1024 * 1024 * 1024)); relayServerNodeStore.SetMaxGbTotalLastBytes((long)(node.MaxGbTotal * 1024 * 1024 * 1024));
} }
relayServerNodeStore.Confirm(); relayServerNodeStore.Confirm();
} }
@@ -259,14 +279,12 @@ namespace linker.messenger.relay.server
bool result = await messengerSender.SendOnly(new MessageRequestWrap bool result = await messengerSender.SendOnly(new MessageRequestWrap
{ {
Connection = relayServerNodeStore.Node.Id == RelayServerNodeInfo.MASTER_NODE_ID ? localConnection : remoteConnection, Connection = connection,
MessengerId = (ushort)RelayMessengerIds.TrafficReport, MessengerId = (ushort)RelayMessengerIds.TrafficReport,
Payload = serializer.Serialize(new RelayTrafficUpdateInfo Payload = serializer.Serialize(new RelayTrafficUpdateInfo
{ {
Dic = id2sent, Dic = id2sent,
SecretKey = relayServerNodeStore.Node.Id == RelayServerNodeInfo.MASTER_NODE_ID SecretKey = relayServerMasterStore.Master.SecretKey
? relayServerMasterStore.Master.SecretKey
: relayServerNodeStore.Node.MasterSecretKey
}), }),
Timeout = 4000 Timeout = 4000
}); });
@@ -307,67 +325,39 @@ namespace linker.messenger.relay.server
{ {
TimerHelper.SetIntervalLong(async () => TimerHelper.SetIntervalLong(async () =>
{ {
IEnumerable<RelayServerNodeInfo> nodes = new List<RelayServerNodeInfo>
{
//默认报告给自己,作为本服务器的一个默认中继节点
new RelayServerNodeInfo{
Id = RelayServerNodeInfo.MASTER_NODE_ID,
Host = new IPEndPoint(IPAddress.Any, relayServerNodeStore.ServicePort).ToString(),
MasterHost = new IPEndPoint(IPAddress.Loopback, relayServerNodeStore.ServicePort).ToString(),
MasterSecretKey = relayServerMasterStore.Master.SecretKey,
MaxBandwidth = 0,
MaxConnection = 0,
MaxBandwidthTotal=0,
MaxGbTotal=0,
MaxGbTotalLastBytes=0,
MaxGbTotalMonth=0,
Name = "default",
Public = false
},
//配置的中继节点
relayServerNodeStore.Node
}.Where(c => string.IsNullOrWhiteSpace(c.MasterHost) == false && string.IsNullOrWhiteSpace(c.MasterSecretKey) == false)
.Where(c => string.IsNullOrWhiteSpace(c.Name) == false && string.IsNullOrWhiteSpace(c.Id) == false);
double diff = (bytes - lastBytes) * 8 / 1024.0 / 1024.0; double diff = (bytes - lastBytes) * 8 / 1024.0 / 1024.0;
lastBytes = bytes; lastBytes = bytes;
foreach (var node in nodes) try
{ {
try IPEndPoint endPoint = await NetworkHelper.GetEndPointAsync(node.Host, relayServerNodeStore.ServicePort) ?? new IPEndPoint(IPAddress.Any, relayServerNodeStore.ServicePort);
RelayServerNodeReportInfo170 relayNodeReportInfo = new RelayServerNodeReportInfo170
{ {
IConnection connection = node.Id == RelayServerNodeInfo.MASTER_NODE_ID ? localConnection : remoteConnection; Id = node.Id,
IPEndPoint endPoint = await NetworkHelper.GetEndPointAsync(node.Host, relayServerNodeStore.ServicePort) ?? new IPEndPoint(IPAddress.Any, relayServerNodeStore.ServicePort); Name = node.Name,
Public = node.Public,
RelayServerNodeReportInfo170 relayNodeReportInfo = new RelayServerNodeReportInfo170 MaxBandwidth = node.MaxBandwidth,
{ BandwidthRatio = Math.Round(diff / 5, 2),
Id = node.Id, MaxBandwidthTotal = node.MaxBandwidthTotal,
Name = node.Name, MaxGbTotal = node.MaxGbTotal,
Public = node.Public, MaxGbTotalLastBytes = node.MaxGbTotalLastBytes,
MaxBandwidth = node.MaxBandwidth, MaxConnection = node.MaxConnection,
BandwidthRatio = Math.Round(diff / 5, 2), ConnectionRatio = Math.Round(connectionNum / 2.0),
MaxBandwidthTotal = node.MaxBandwidthTotal, EndPoint = endPoint,
MaxGbTotal = node.MaxGbTotal, Url = node.Url
MaxGbTotalLastBytes = node.MaxGbTotalLastBytes, };
MaxConnection = node.MaxConnection, await messengerSender.SendOnly(new MessageRequestWrap
ConnectionRatio = Math.Round(connectionNum / 2.0),
EndPoint = endPoint,
Url = node.Url
};
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = connection,
MessengerId = (ushort)RelayMessengerIds.NodeReport,
Payload = serializer.Serialize(relayNodeReportInfo)
});
}
catch (Exception ex)
{ {
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) Connection = connection,
{ MessengerId = (ushort)RelayMessengerIds.NodeReport,
LoggerHelper.Instance.Error($"relay report : {ex}"); Payload = serializer.Serialize(relayNodeReportInfo)
} });
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error($"relay report : {ex}");
} }
} }
return true; return true;
@@ -378,13 +368,9 @@ namespace linker.messenger.relay.server
{ {
TimerHelper.SetIntervalLong(async () => TimerHelper.SetIntervalLong(async () =>
{ {
if ((remoteConnection == null || remoteConnection.Connected == false) && string.IsNullOrWhiteSpace(relayServerNodeStore.Node.MasterHost) == false) if (connection == null || connection.Connected == false)
{ {
remoteConnection = await SignIn(relayServerNodeStore.Node.MasterHost, relayServerNodeStore.Node.MasterSecretKey).ConfigureAwait(false); connection = await SignIn(node.MasterHost, node.MasterSecretKey).ConfigureAwait(false);
}
if (localConnection == null || localConnection.Connected == false)
{
localConnection = await SignIn(new IPEndPoint(IPAddress.Loopback, relayServerNodeStore.ServicePort).ToString(), relayServerMasterStore.Master.SecretKey).ConfigureAwait(false);
} }
return true; return true;
}, 3000); }, 3000);
@@ -401,6 +387,10 @@ namespace linker.messenger.relay.server
IPEndPoint remote = await NetworkHelper.GetEndPointAsync(host, 1802); IPEndPoint remote = await NetworkHelper.GetEndPointAsync(host, 1802);
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Warning($"relay node sign in to {remote}");
}
Socket socket = new Socket(remote.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); Socket socket = new Socket(remote.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.KeepAlive(); socket.KeepAlive();

View File

@@ -53,7 +53,7 @@ namespace linker.messenger.relay.server
//ask 是发起端来的那key就是 发起端->目标端, answer的目标和来源会交换所以转换一下 //ask 是发起端来的那key就是 发起端->目标端, answer的目标和来源会交换所以转换一下
string key = relayMessage.Type == RelayMessengerType.Ask ? $"{relayMessage.FromId}->{relayMessage.ToId}->{relayMessage.FlowId}" : $"{relayMessage.ToId}->{relayMessage.FromId}->{relayMessage.FlowId}"; string key = relayMessage.Type == RelayMessengerType.Ask ? $"{relayMessage.FromId}->{relayMessage.ToId}->{relayMessage.FlowId}" : $"{relayMessage.ToId}->{relayMessage.FromId}->{relayMessage.FlowId}";
//获取缓存 //获取缓存
RelayCacheInfo relayCache = await relayServerNodeTransfer.TryGetRelayCache(key, relayMessage.NodeId); RelayCacheInfo relayCache = await relayServerNodeTransfer.TryGetRelayCache(key);
if (relayCache == null) if (relayCache == null)
{ {
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)

View File

@@ -100,11 +100,12 @@ namespace linker.messenger.store.file
return serviceCollection; return serviceCollection;
} }
public static ServiceProvider UseStoreFile(this ServiceProvider serviceProvider) public static ServiceProvider UseStoreFile(this ServiceProvider serviceProvider,Dictionary<string,string> configDic)
{ {
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);
RunningConfig runningConfig = serviceProvider.GetService<RunningConfig>(); RunningConfig runningConfig = serviceProvider.GetService<RunningConfig>();
IApiServer apiServer = serviceProvider.GetService<IApiServer>(); IApiServer apiServer = serviceProvider.GetService<IApiServer>();

View File

@@ -18,9 +18,12 @@ 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(); Load(dic);
Save(); Save();
SaveTask(); SaveTask();
} }
@@ -47,7 +50,7 @@ namespace linker.messenger.store.file
}); });
} }
} }
private void Load() private void Load(Dictionary<string, string> dic)
{ {
slim.Wait(); slim.Wait();
try try
@@ -61,13 +64,15 @@ namespace linker.messenger.store.file
LoggerHelper.Instance.Error($"{item.Value.Property.Name} not found"); LoggerHelper.Instance.Error($"{item.Value.Property.Name} not found");
continue; continue;
} }
if (File.Exists(item.Value.Path) == false) string text = string.Empty;
if (File.Exists(item.Value.Path))
{ {
LoggerHelper.Instance.Error($"{item.Value.Path} not exists"); text = File.ReadAllText(item.Value.Path, encoding: System.Text.Encoding.UTF8);
continue; }
else if (dic != null && dic.ContainsKey(item.Value.Property.Name))
{
text = dic[item.Value.Property.Name];
} }
string text = File.ReadAllText(item.Value.Path, encoding: System.Text.Encoding.UTF8);
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
{ {
LoggerHelper.Instance.Error($"{item.Value.Path} empty"); LoggerHelper.Instance.Error($"{item.Value.Path} empty");
@@ -232,5 +237,5 @@ namespace linker.messenger.store.file
} }
} }
} }

View File

@@ -4,6 +4,7 @@ using linker.messenger.signin;
using linker.tunnel; using linker.tunnel;
using System.Net; using System.Net;
using System.Net.Quic; using System.Net.Quic;
using System.Text.Json.Nodes;
namespace linker.messenger.tunnel namespace linker.messenger.tunnel
{ {
@@ -45,23 +46,55 @@ namespace linker.messenger.tunnel
LoggerHelper.Instance.Warning($"tunnel route level:{Info.RouteLevel}"); LoggerHelper.Instance.Warning($"tunnel route level:{Info.RouteLevel}");
} }
private async Task GetIsp()
{
try
{
using HttpClient httpClient = new HttpClient();
string str = await httpClient.GetStringAsync($"http://ip-api.com/json").WaitAsync(TimeSpan.FromMilliseconds(3000));
if (string.IsNullOrWhiteSpace(str) == false)
{
TunnelNetInfo net = str.DeJson<TunnelNetInfo>();
Info.Net.Isp = net.Isp;
Info.Net.As = net.As;
Info.Net.Org = net.Org;
Info.Net.Region = net.Region;
Info.Net.RegionName = net.RegionName;
Info.Net.Country = net.Country;
Info.Net.CountryCode = net.CountryCode;
}
}
catch (Exception ex)
{
LoggerHelper.Instance.Warning(ex);
}
}
private async Task GetPosition()
{
try
{
using HttpClient httpClient = new HttpClient();
string str = await httpClient.GetStringAsync($"https://api.myip.la/en?json").WaitAsync(TimeSpan.FromMilliseconds(5000));
if (string.IsNullOrWhiteSpace(str) == false)
{
JsonNode json = JsonObject.Parse(str);
Info.Net.City = json["location"]["city"].ToString();
Info.Net.Lat = double.Parse(json["location"]["latitude"].ToString());
Info.Net.Lon = double.Parse(json["location"]["longitude"].ToString());
}
}
catch (Exception ex)
{
LoggerHelper.Instance.Warning(ex);
}
}
private async Task GetNet() private async Task GetNet()
{ {
if (string.IsNullOrWhiteSpace(Info.Net.City)) if (string.IsNullOrWhiteSpace(Info.Net.City))
{ {
try await Task.WhenAll(GetIsp(), GetPosition());
{
using HttpClient httpClient = new HttpClient();
string str = await httpClient.GetStringAsync($"http://ip-api.com/json").WaitAsync(TimeSpan.FromMilliseconds(3000));
if (string.IsNullOrWhiteSpace(str) == false)
{
Info.Net = str.DeJson<TunnelNetInfo>();
}
}
catch (Exception)
{
}
} }
} }

View File

@@ -252,17 +252,8 @@ namespace linker.messenger
SourceStream = stream; SourceStream = stream;
SourceNetworkStream = networkStream; SourceNetworkStream = networkStream;
SourceSocket = socket; SourceSocket = socket;
if (remote.Address.AddressFamily == AddressFamily.InterNetworkV6 && remote.Address.IsIPv4MappedToIPv6) Address = NetworkHelper.TransEndpointFamily(remote) ;
{ LocalAddress = NetworkHelper.TransEndpointFamily(local);
remote = new IPEndPoint(new IPAddress(remote.Address.GetAddressBytes()[^4..]), remote.Port);
}
Address = remote;
if (local.Address.AddressFamily == AddressFamily.InterNetworkV6 && local.Address.IsIPv4MappedToIPv6)
{
local = new IPEndPoint(new IPAddress(local.Address.GetAddressBytes()[^4..]), local.Port);
}
LocalAddress = local;
} }

View File

@@ -93,7 +93,7 @@ namespace linker.tunnel.connection
private async Task ProcessWrite() private async Task ProcessWrite()
{ {
byte[] buffer = new byte[65 * 1024]; byte[] buffer = new byte[65 * 1024];
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0); IPEndPoint ep = new IPEndPoint(IPEndPoint.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0);
try try
{ {
while (cancellationTokenSource.IsCancellationRequested == false) while (cancellationTokenSource.IsCancellationRequested == false)

View File

@@ -230,7 +230,7 @@ namespace linker.tunnel.transport
RemoteUdp = remoteUdp, RemoteUdp = remoteUdp,
Stream = quicStream, Stream = quicStream,
Connection = connection, Connection = connection,
IPEndPoint = remoteEP, IPEndPoint = NetworkHelper.TransEndpointFamily(remoteEP),
TransactionId = tunnelTransportInfo.TransactionId, TransactionId = tunnelTransportInfo.TransactionId,
TransactionTag = tunnelTransportInfo.TransactionTag, TransactionTag = tunnelTransportInfo.TransactionTag,
RemoteMachineId = tunnelTransportInfo.Remote.MachineId, RemoteMachineId = tunnelTransportInfo.Remote.MachineId,
@@ -626,7 +626,7 @@ namespace linker.tunnel.transport
TransactionId = state.TransactionId, TransactionId = state.TransactionId,
TransactionTag = state.TransactionTag, TransactionTag = state.TransactionTag,
TransportName = state.TransportName, TransportName = state.TransportName,
IPEndPoint = remoteEP, IPEndPoint = NetworkHelper.TransEndpointFamily(remoteEP),
Label = string.Empty, Label = string.Empty,
BufferSize = state.BufferSize, BufferSize = state.BufferSize,
}; };

View File

@@ -185,7 +185,7 @@ namespace linker.tunnel.transport
{ {
Stream = sslStream, Stream = sslStream,
Socket = targetSocket, Socket = targetSocket,
IPEndPoint = targetSocket.RemoteEndPoint as IPEndPoint, IPEndPoint = NetworkHelper.TransEndpointFamily(targetSocket.RemoteEndPoint as IPEndPoint),
TransactionId = tunnelTransportInfo.TransactionId, TransactionId = tunnelTransportInfo.TransactionId,
TransactionTag = tunnelTransportInfo.TransactionTag, TransactionTag = tunnelTransportInfo.TransactionTag,
RemoteMachineId = tunnelTransportInfo.Remote.MachineId, RemoteMachineId = tunnelTransportInfo.Remote.MachineId,
@@ -291,7 +291,7 @@ namespace linker.tunnel.transport
TransactionId = _state.TransactionId, TransactionId = _state.TransactionId,
TransactionTag = _state.TransactionTag, TransactionTag = _state.TransactionTag,
TransportName = _state.TransportName, TransportName = _state.TransportName,
IPEndPoint = socket.RemoteEndPoint as IPEndPoint, IPEndPoint = NetworkHelper.TransEndpointFamily(socket.RemoteEndPoint as IPEndPoint),
Label = string.Empty, Label = string.Empty,
SSL = _state.SSL, SSL = _state.SSL,
BufferSize = _state.BufferSize, BufferSize = _state.BufferSize,

View File

@@ -178,7 +178,7 @@ namespace linker.tunnel.transport
{ {
Stream = sslStream, Stream = sslStream,
Socket = socket, Socket = socket,
IPEndPoint = socket.RemoteEndPoint as IPEndPoint, IPEndPoint = NetworkHelper.TransEndpointFamily(socket.RemoteEndPoint as IPEndPoint),
TransactionId = state.TransactionId, TransactionId = state.TransactionId,
TransactionTag = state.TransactionTag, TransactionTag = state.TransactionTag,
RemoteMachineId = state.Remote.MachineId, RemoteMachineId = state.Remote.MachineId,
@@ -238,7 +238,7 @@ namespace linker.tunnel.transport
TransactionId = state.TransactionId, TransactionId = state.TransactionId,
TransactionTag = state.TransactionTag, TransactionTag = state.TransactionTag,
TransportName = state.TransportName, TransportName = state.TransportName,
IPEndPoint = socket.RemoteEndPoint as IPEndPoint, IPEndPoint = NetworkHelper.TransEndpointFamily(socket.RemoteEndPoint as IPEndPoint),
Label = string.Empty, Label = string.Empty,
SSL = state.SSL, SSL = state.SSL,
BufferSize = state.BufferSize, BufferSize = state.BufferSize,

View File

@@ -277,7 +277,7 @@ namespace linker.tunnel.transport
TransactionId = tunnelTransportInfo.TransactionId, TransactionId = tunnelTransportInfo.TransactionId,
TransactionTag = tunnelTransportInfo.TransactionTag, TransactionTag = tunnelTransportInfo.TransactionTag,
TransportName = tunnelTransportInfo.TransportName, TransportName = tunnelTransportInfo.TransportName,
IPEndPoint = socket.RemoteEndPoint as IPEndPoint, IPEndPoint = NetworkHelper.TransEndpointFamily(socket.RemoteEndPoint as IPEndPoint),
Label = string.Empty, Label = string.Empty,
SSL = tunnelTransportInfo.SSL, SSL = tunnelTransportInfo.SSL,
BufferSize = tunnelTransportInfo.BufferSize, BufferSize = tunnelTransportInfo.BufferSize,
@@ -338,7 +338,7 @@ namespace linker.tunnel.transport
{ {
Stream = sslStream, Stream = sslStream,
Socket = targetSocket, Socket = targetSocket,
IPEndPoint = targetSocket.RemoteEndPoint as IPEndPoint, IPEndPoint = NetworkHelper.TransEndpointFamily(targetSocket.RemoteEndPoint as IPEndPoint),
TransactionId = tunnelTransportInfo.TransactionId, TransactionId = tunnelTransportInfo.TransactionId,
TransactionTag = tunnelTransportInfo.TransactionTag, TransactionTag = tunnelTransportInfo.TransactionTag,
RemoteMachineId = tunnelTransportInfo.Remote.MachineId, RemoteMachineId = tunnelTransportInfo.Remote.MachineId,

View File

@@ -179,7 +179,7 @@ namespace linker.tunnel.transport
return new TunnelConnectionUdp return new TunnelConnectionUdp
{ {
UdpClient = remoteUdp, UdpClient = remoteUdp,
IPEndPoint = remoteEP, IPEndPoint = NetworkHelper.TransEndpointFamily( remoteEP),
TransactionId = tunnelTransportInfo.TransactionId, TransactionId = tunnelTransportInfo.TransactionId,
TransactionTag = tunnelTransportInfo.TransactionTag, TransactionTag = tunnelTransportInfo.TransactionTag,
RemoteMachineId = tunnelTransportInfo.Remote.MachineId, RemoteMachineId = tunnelTransportInfo.Remote.MachineId,
@@ -224,7 +224,7 @@ namespace linker.tunnel.transport
TimerHelper.Async(async () => TimerHelper.Async(async () =>
{ {
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
SocketReceiveFromResult result = await socket.ReceiveFromAsync(buffer, new IPEndPoint(IPAddress.Any, 0)).ConfigureAwait(false); SocketReceiveFromResult result = await socket.ReceiveFromAsync(buffer, new IPEndPoint(IPAddress.IPv6Any, 0)).ConfigureAwait(false);
await socket.SendToAsync(endBytes, result.RemoteEndPoint); await socket.SendToAsync(endBytes, result.RemoteEndPoint);
tcs.SetResult(result.RemoteEndPoint as IPEndPoint); tcs.SetResult(result.RemoteEndPoint as IPEndPoint);
}); });
@@ -408,7 +408,7 @@ namespace linker.tunnel.transport
TransactionId = state.TransactionId, TransactionId = state.TransactionId,
TransactionTag = state.TransactionTag, TransactionTag = state.TransactionTag,
TransportName = state.TransportName, TransportName = state.TransportName,
IPEndPoint = remoteEP, IPEndPoint = NetworkHelper.TransEndpointFamily(remoteEP),
Label = string.Empty, Label = string.Empty,
Receive = true, Receive = true,
SSL = state.SSL, SSL = state.SSL,

View File

@@ -270,7 +270,7 @@ namespace linker.tunnel.transport
TransactionId = tunnelTransportInfo.TransactionId, TransactionId = tunnelTransportInfo.TransactionId,
TransactionTag = tunnelTransportInfo.TransactionTag, TransactionTag = tunnelTransportInfo.TransactionTag,
TransportName = tunnelTransportInfo.TransportName, TransportName = tunnelTransportInfo.TransportName,
IPEndPoint = state.RemoteEndPoint, IPEndPoint = NetworkHelper.TransEndpointFamily(state.RemoteEndPoint),
Label = string.Empty, Label = string.Empty,
BufferSize = tunnelTransportInfo.BufferSize, BufferSize = tunnelTransportInfo.BufferSize,
Receive = false, Receive = false,
@@ -323,14 +323,14 @@ namespace linker.tunnel.transport
} }
await targetSocket.SendToAsync($"{flagTexts}-{tunnelTransportInfo.Local.MachineId}-{tunnelTransportInfo.FlowId}".ToBytes(), ep).ConfigureAwait(false); await targetSocket.SendToAsync($"{flagTexts}-{tunnelTransportInfo.Local.MachineId}-{tunnelTransportInfo.FlowId}".ToBytes(), ep).ConfigureAwait(false);
await targetSocket.ReceiveFromAsync(new byte[1024], new IPEndPoint(IPAddress.IPv6Any, 0)).WaitAsync(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); await targetSocket.ReceiveFromAsync(new byte[1024], new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0)).WaitAsync(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false);
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Debug($"{Name} connect to {tunnelTransportInfo.Remote.MachineId}->{tunnelTransportInfo.Remote.MachineName} {ep} success"); LoggerHelper.Instance.Debug($"{Name} connect to {tunnelTransportInfo.Remote.MachineId}->{tunnelTransportInfo.Remote.MachineName} {ep} success");
TunnelConnectionUdp result = new TunnelConnectionUdp TunnelConnectionUdp result = new TunnelConnectionUdp
{ {
IPEndPoint = ep, IPEndPoint = NetworkHelper.TransEndpointFamily(ep),
TransactionId = tunnelTransportInfo.TransactionId, TransactionId = tunnelTransportInfo.TransactionId,
TransactionTag = tunnelTransportInfo.TransactionTag, TransactionTag = tunnelTransportInfo.TransactionTag,
RemoteMachineId = tunnelTransportInfo.Remote.MachineId, RemoteMachineId = tunnelTransportInfo.Remote.MachineId,

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -17,6 +17,7 @@ export const provideTuntap = () => {
const systems = { const systems = {
linux: ['debian', 'ubuntu', 'alpine', 'rocky', 'centos', 'fedora', 'archlinux'], linux: ['debian', 'ubuntu', 'alpine', 'rocky', 'centos', 'fedora', 'archlinux'],
armbian: ['armbian'],
openwrt: ['openwrt'], openwrt: ['openwrt'],
ubuntu: ['ubuntu'], ubuntu: ['ubuntu'],
windows: ['windows'], windows: ['windows'],

View File

@@ -54,9 +54,11 @@ namespace linker
public static void Run(string[] args) public static void Run(string[] args)
{ {
Dictionary<string, string> configDic = ParseArgs(args);
LinkerMessengerEntry.Initialize(); LinkerMessengerEntry.Initialize();
LinkerMessengerEntry.Build(); LinkerMessengerEntry.Build();
LinkerMessengerEntry.Setup(ExcludeModule.None); LinkerMessengerEntry.Setup(ExcludeModule.None, configDic);
LoggerHelper.Instance.Warning($"current version : {VersionHelper.version}"); LoggerHelper.Instance.Warning($"current version : {VersionHelper.version}");
@@ -66,6 +68,34 @@ namespace linker
GCHelper.FlushMemory(); GCHelper.FlushMemory();
} }
private static Dictionary<string, string> ParseArgs(string[] args)
{
Dictionary<string, string> configDic = new Dictionary<string, string>();
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "--config-cient")
{
configDic.Add("Client", args[i+1]);
i++;
}else if (args[i] == "--config-server")
{
configDic.Add("Server", args[i + 1]);
i++;
}
else if (args[i] == "--config-action")
{
configDic.Add("Action", args[i + 1]);
i++;
}
else if (args[i] == "--config-common")
{
configDic.Add("Common", args[i + 1]);
i++;
}
}
return configDic;
}
} }
} }

View File

@@ -20,15 +20,17 @@
<Title>linker</Title> <Title>linker</Title>
<Authors>snltty</Authors> <Authors>snltty</Authors>
<Company>snltty</Company> <Company>snltty</Company>
<Description>1. 优化linux下路由跟踪问题 <Description>1. 优化linux下路由跟踪问题,提高启动速度
2. 优化linux下获取本机IP问题 2. 优化linux下获取本机IP问题,提升虚拟网卡稳定性
3. 增加ICS让win7+、win server2008+支持NAT 3. 增加ICS让win7+、win server2008+支持NAT
4. 增加中继cdkey使用cdkey解锁公开中继节点的带宽、流量、连接数限制 4. 增加中继cdkey使用cdkey解锁公开中继节点的带宽、流量、连接数限制
5. 增加内外穿透定时开关功能 5. 增加内外穿透定时开关功能
6. 优化管理页面连接接口的体验 6. 优化管理页面连接接口的体验
7. 优化一些UI体验去除同步页面将同步功能放至各个实际的位置 7. 优化一些UI体验去除同步页面将同步功能放至各个实际的位置
8. 优化端口转发,让不同分组间可以使用相同端口 8. 优化端口转发,让不同分组间可以使用相同端口
9. 其它一些修复优化</Description> 9. 从命令行参数初始化配置
10. 优化打洞IPV6支持的更多了
11. 其它一些修复优化</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>

View File

@@ -1,11 +1,13 @@
v1.6.9 v1.6.9
2025-03-10 17:55:39 2025-03-11 21:12:44
1. 优化linux下路由跟踪问题 1. 优化linux下路由跟踪问题,提高启动速度
2. 优化linux下获取本机IP问题 2. 优化linux下获取本机IP问题,提升虚拟网卡稳定性
3. 增加ICS让win7+、win server2008+支持NAT 3. 增加ICS让win7+、win server2008+支持NAT
4. 增加中继cdkey使用cdkey解锁公开中继节点的带宽、流量、连接数限制 4. 增加中继cdkey使用cdkey解锁公开中继节点的带宽、流量、连接数限制
5. 增加内外穿透定时开关功能 5. 增加内外穿透定时开关功能
6. 优化管理页面连接接口的体验 6. 优化管理页面连接接口的体验
7. 优化一些UI体验去除同步页面将同步功能放至各个实际的位置 7. 优化一些UI体验去除同步页面将同步功能放至各个实际的位置
8. 优化端口转发,让不同分组间可以使用相同端口 8. 优化端口转发,让不同分组间可以使用相同端口
9. 其它一些修复优化 9. 从命令行参数初始化配置
10. 优化打洞IPV6支持的更多了
11. 其它一些修复优化