From 28f8ca7ba1d7bd2cfabf85035966af397b7e59e9 Mon Sep 17 00:00:00 2001 From: shizukunacho <101909939+shizukunacho@users.noreply.github.com> Date: Fri, 13 Nov 2026 14:32:18 +0800 Subject: [PATCH] fix: import SIP008 subscriptions with numeric ports (#10575) --- .../Fmt/ShadowsocksFmtTests.cs | 32 +++++++++++++++++++ .../ServiceLib/Handler/Fmt/ShadowsocksFmt.cs | 4 +-- v2rayN/ServiceLib/Models/Dto/SsSIP008.cs | 3 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/v2rayN/ServiceLib.Tests/Fmt/ShadowsocksFmtTests.cs b/v2rayN/ServiceLib.Tests/Fmt/ShadowsocksFmtTests.cs index f43cc73e..f57a28a9 100644 --- a/v2rayN/ServiceLib.Tests/Fmt/ShadowsocksFmtTests.cs +++ b/v2rayN/ServiceLib.Tests/Fmt/ShadowsocksFmtTests.cs @@ -2,6 +2,38 @@ namespace ServiceLib.Tests.Fmt; public class ShadowsocksFmtTests { + [Test] + [Arguments("8388", true)] + [Arguments("\"8388\"", true)] + [Arguments("8388", false)] + [Arguments("\"8388\"", false)] + public async Task ResolveSip008_ObjectAndArrayDocuments_ShouldPreserveServerFields(string port, bool useObject) + { + var server = $$""" + { + "id": "27b8a625-4f4b-4428-9f0f-8a2317db7c79", + "remarks": "SIP008 server", + "server": "example.com", + "server_port": {{port}}, + "password": "example", + "method": "chacha20-ietf-poly1305" + } + """; + var document = useObject + ? $$"""{"version": 1, "servers": [{{server}}]}""" + : $"[{server}]"; + + var resolved = ShadowsocksFmt.ResolveSip008(document); + + await resolved.Should().NotBeNull(); + await resolved!.Should().HaveCount(1); + await resolved[0].Remarks.Should().BeEqualTo("SIP008 server"); + await resolved[0].Address.Should().BeEqualTo("example.com"); + await resolved[0].Port.Should().BeEqualTo(8388); + await resolved[0].Password.Should().BeEqualTo("example"); + await resolved[0].GetProtocolExtra().SsMethod.Should().BeEqualTo("chacha20-ietf-poly1305"); + } + // SIP002 user info that is not base64 is "method:password", percent-encoded once. [Test] [Arguments("ss://2022-blake3-aes-128-gcm:p%2541ss@1.2.3.4:8388#demo", "p%41ss")] diff --git a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs index a003540a..dd101223 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs @@ -282,7 +282,7 @@ public class ShadowsocksFmt : BaseFmt { //SsSIP008 var lstSsServer = JsonUtils.Deserialize>(result); - if (lstSsServer?.Count <= 0) + if (lstSsServer == null || lstSsServer.Count == 0) { var ssSIP008 = JsonUtils.Deserialize(result); if (ssSIP008?.servers?.Count > 0) @@ -301,7 +301,7 @@ public class ShadowsocksFmt : BaseFmt Remarks = it.remarks, Password = it.password, Address = it.server, - Port = it.server_port.ToInt() + Port = it.server_port }; ssItem.SetProtocolExtra(new ProtocolExtraItem() { SsMethod = it.method }); lst.Add(ssItem); diff --git a/v2rayN/ServiceLib/Models/Dto/SsSIP008.cs b/v2rayN/ServiceLib/Models/Dto/SsSIP008.cs index 73b7bcf8..32951d7a 100644 --- a/v2rayN/ServiceLib/Models/Dto/SsSIP008.cs +++ b/v2rayN/ServiceLib/Models/Dto/SsSIP008.cs @@ -9,7 +9,8 @@ public class SsServer { public string? remarks { get; set; } public string? server { get; set; } - public string? server_port { get; set; } + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)] + public int server_port { get; set; } public string? method { get; set; } public string? password { get; set; } public string? plugin { get; set; }