Update package versions and enhance IXuiPanelGateway interface
- Updated the version of `ThreeXui.Net` to 1.0.2 in `Directory.Packages.props`. - Enhanced the `IXuiPanelGateway` interface to include detailed documentation on the new `ForcedFingerprint` and `ForcedPacketEncoding` parameters for the `BuildConnectionStringAsync` method, clarifying their roles in client application interactions. - Refactored `XuiPanelGateway` to implement the new parameters, ensuring compatibility with client requirements for TLS fingerprinting and packet encoding based on transport type. - Updated architecture documentation to reflect changes in connection string handling and the implications for client applications.
This commit is contained in:
@@ -1,46 +1,46 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="FluentValidation" Version="12.1.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Extensions" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.11" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.9" />
|
||||
<PackageVersion
|
||||
Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore"
|
||||
Version="10.0.9"
|
||||
/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.OpenApi" Version="2.9.0" />
|
||||
<PackageVersion Include="Scalar.AspNetCore" Version="2.16.7" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageVersion>
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.9" />
|
||||
<PackageVersion
|
||||
Include="Microsoft.Extensions.DependencyInjection.Abstractions"
|
||||
Version="10.0.9"
|
||||
/>
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.19.1" />
|
||||
<PackageVersion Include="Telegram.Bot" Version="22.10.1" />
|
||||
<PackageVersion Include="ThreeXui.Net" Version="1.0.1" />
|
||||
<!-- Тестирование (M8) -->
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||
<PackageVersion Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.9" />
|
||||
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.2.0" />
|
||||
<!-- InMemory, не Sqlite/Npgsql — модель использует Postgres-специфичные типы (uuid[], jsonb),
|
||||
не имеющие реляционных аналогов; InMemory игнорирует HasColumnType и не требует их маппинга. -->
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.9" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="FluentValidation" Version="12.1.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Extensions" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.11" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.9" />
|
||||
<PackageVersion
|
||||
Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore"
|
||||
Version="10.0.9"
|
||||
/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.OpenApi" Version="2.9.0" />
|
||||
<PackageVersion Include="Scalar.AspNetCore" Version="2.16.7" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageVersion>
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.9" />
|
||||
<PackageVersion
|
||||
Include="Microsoft.Extensions.DependencyInjection.Abstractions"
|
||||
Version="10.0.9"
|
||||
/>
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.19.1" />
|
||||
<PackageVersion Include="Telegram.Bot" Version="22.10.1" />
|
||||
<PackageVersion Include="ThreeXui.Net" Version="1.0.2" />
|
||||
<!-- Тестирование (M8) -->
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||
<PackageVersion Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.9" />
|
||||
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.2.0" />
|
||||
<!-- InMemory, не Sqlite/Npgsql — модель использует Postgres-специфичные типы (uuid[], jsonb),
|
||||
не имеющие реляционных аналогов; InMemory игнорирует HasColumnType и не требует их маппинга. -->
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.9" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -95,6 +95,13 @@ public interface IXuiPanelGateway
|
||||
CancellationToken cancellationToken
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Реализация (<c>XuiPanelGateway</c>) передаёт в ThreeXui.Net (1.0.2+)
|
||||
/// <c>ForcedFingerprint: "firefox"</c> (клиентские приложения выдают TLS-fingerprint панели,
|
||||
/// если fp не задан явно в tls/reality-ссылке) и для транспорта <c>xhttp</c> —
|
||||
/// <c>ForcedPacketEncoding: "xudp"</c> (без него часть клиентов не может корректно релеить UDP
|
||||
/// поверх xhttp); для остальных транспортов <c>ForcedPacketEncoding</c> не передаётся.
|
||||
/// </summary>
|
||||
Task<Result<string>> BuildConnectionStringAsync(
|
||||
Node node,
|
||||
Inbound inbound,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PnvPanel.Application.Common.Interfaces;
|
||||
using PnvPanel.Application.Common.Models;
|
||||
@@ -239,12 +237,12 @@ internal sealed class XuiPanelGateway(
|
||||
inbound.Port,
|
||||
publicHost,
|
||||
node.BaseAddress.ToString(),
|
||||
remoteInbound
|
||||
remoteInbound,
|
||||
ForcedFingerprint: ForcedFingerprint,
|
||||
ForcedPacketEncoding: IsXhttpTransport(remoteInbound.StreamSettings) ? "xudp" : null
|
||||
);
|
||||
|
||||
return Result.Success(
|
||||
ForceFingerprint(builder.Build(request), inbound.Protocol, ForcedFingerprint)
|
||||
);
|
||||
return Result.Success(builder.Build(request));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -259,65 +257,31 @@ internal sealed class XuiPanelGateway(
|
||||
|
||||
/// <summary>
|
||||
/// Клиентские приложения выдают TLS-fingerprint панели, если fp не задан явно в
|
||||
/// share-ссылке — подставляем "firefox" поверх того, что вернул ThreeXui.Net
|
||||
/// (сам параметр берётся из streamSettings ноды, per-nod настройки не даём).
|
||||
/// Применимо только к tls/reality-ссылкам (vless/trojan/vmess); shadowsocks TLS
|
||||
/// не использует.
|
||||
/// share-ссылке — подставляем "firefox" через <see cref="XuiConnectionStringRequest.ForcedFingerprint"/>
|
||||
/// (ThreeXui.Net 1.0.2+; применяется библиотекой только к tls/reality-ссылкам, игнорируется иначе).
|
||||
/// </summary>
|
||||
private const string ForcedFingerprint = "firefox";
|
||||
|
||||
private static string ForceFingerprint(string link, VpnProtocol protocol, string fingerprint) =>
|
||||
protocol switch
|
||||
{
|
||||
VpnProtocol.Vless or VpnProtocol.Trojan => ForceQueryFingerprint(link, fingerprint),
|
||||
VpnProtocol.Vmess => ForceVmessFingerprint(link, fingerprint),
|
||||
_ => link,
|
||||
};
|
||||
|
||||
private static string ForceQueryFingerprint(string link, string fingerprint)
|
||||
/// <summary>
|
||||
/// packetEncoding=xudp через <see cref="XuiConnectionStringRequest.ForcedPacketEncoding"/> нужен
|
||||
/// только транспорту xhttp — остальные транспорты его не используют, добавлять не за чем.
|
||||
/// </summary>
|
||||
private static bool IsXhttpTransport(string? streamSettingsJson)
|
||||
{
|
||||
var hashIndex = link.IndexOf('#');
|
||||
var head = hashIndex >= 0 ? link[..hashIndex] : link;
|
||||
var fragment = hashIndex >= 0 ? link[hashIndex..] : string.Empty;
|
||||
|
||||
var queryIndex = head.IndexOf('?');
|
||||
if (queryIndex < 0)
|
||||
return link;
|
||||
|
||||
var baseUri = head[..queryIndex];
|
||||
var query = head[(queryIndex + 1)..];
|
||||
if (!query.Contains("security=tls") && !query.Contains("security=reality"))
|
||||
return link;
|
||||
|
||||
var parts = query
|
||||
.Split('&', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(p => !p.StartsWith("fp=", StringComparison.Ordinal))
|
||||
.Append($"fp={fingerprint}");
|
||||
|
||||
return $"{baseUri}?{string.Join('&', parts)}{fragment}";
|
||||
}
|
||||
|
||||
private static string ForceVmessFingerprint(string link, string fingerprint)
|
||||
{
|
||||
const string prefix = "vmess://";
|
||||
if (!link.StartsWith(prefix, StringComparison.Ordinal))
|
||||
return link;
|
||||
if (string.IsNullOrWhiteSpace(streamSettingsJson))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var json = Encoding.UTF8.GetString(Convert.FromBase64String(link[prefix.Length..]));
|
||||
var node = JsonNode.Parse(json)?.AsObject();
|
||||
if (node is null || string.IsNullOrEmpty(node["tls"]?.GetValue<string>()))
|
||||
return link;
|
||||
|
||||
node["fp"] = fingerprint;
|
||||
var updatedJson = node.ToJsonString();
|
||||
return prefix + Convert.ToBase64String(Encoding.UTF8.GetBytes(updatedJson));
|
||||
using var doc = JsonDocument.Parse(streamSettingsJson);
|
||||
return doc.RootElement.ValueKind == JsonValueKind.Object
|
||||
&& doc.RootElement.TryGetProperty("network", out var network)
|
||||
&& network.ValueKind == JsonValueKind.String
|
||||
&& string.Equals(network.GetString(), "xhttp", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (JsonException)
|
||||
{
|
||||
// Неожиданный формат vmess-пейлоада — возвращаем ссылку как есть.
|
||||
return link;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user