diff --git a/v2rayN/ServiceLib.Tests/Fmt/SocksFmtTests.cs b/v2rayN/ServiceLib.Tests/Fmt/SocksFmtTests.cs new file mode 100644 index 00000000..33182d38 --- /dev/null +++ b/v2rayN/ServiceLib.Tests/Fmt/SocksFmtTests.cs @@ -0,0 +1,40 @@ +namespace ServiceLib.Tests.Fmt; + +public class SocksFmtTests +{ + // The legacy form base64-encodes the whole "user:pass@host:port". + [Test] + [Arguments("socks://YWRtaW46YWRtaW5AMS4yLjMuNDoxMDgw#demo", "admin", "admin", "1.2.3.4", 1080)] + [Arguments("socks://dXNlcjpwYXNzQDEuMi4zLjQ6MTA4MA==#demo", "user", "pass", "1.2.3.4", 1080)] + [Arguments("socks://dXNlcjpxd2VydHk/QDEuMi4zLjQ6MTA4MA==#demo", "user", "qwerty?", "1.2.3.4", 1080)] + [Arguments("socks://dXNlcjpxd2VydHk_QDEuMi4zLjQ6MTA4MA#demo", "user", "qwerty?", "1.2.3.4", 1080)] + public async Task ResolveConfig_LegacyBase64_ShouldDecodeServerAndCredentials( + string shareUri, string username, string password, string address, int port) + { + var resolved = FmtHandler.ResolveConfig(shareUri, out var msg); + + await resolved.Should().NotBeNull().Because($"uri: {shareUri}, msg: {msg}"); + await resolved!.ConfigType.Should().BeEqualTo(EConfigType.SOCKS); + await resolved.Address.Should().BeEqualTo(address); + await resolved.Port.Should().BeEqualTo(port); + await resolved.Username.Should().BeEqualTo(username); + await resolved.Password.Should().BeEqualTo(password); + await resolved.Remarks.Should().BeEqualTo("demo"); + } + + [Test] + [Arguments("socks://YWRtaW46YWRtaW4@1.2.3.4:1080#demo", "admin", "admin")] + [Arguments("socks://admin:admin@1.2.3.4:1080#demo", "admin", "admin")] + [Arguments("socks://1.2.3.4:1080#demo", "", "")] + public async Task ResolveConfig_HostAndPort_ShouldKeepParsingIt(string shareUri, string username, string password) + { + var resolved = FmtHandler.ResolveConfig(shareUri, out var msg); + + await resolved.Should().NotBeNull().Because($"uri: {shareUri}, msg: {msg}"); + await resolved!.Address.Should().BeEqualTo("1.2.3.4"); + await resolved.Port.Should().BeEqualTo(1080); + await resolved.Username.Should().BeEqualTo(username); + await resolved.Password.Should().BeEqualTo(password); + await resolved.Remarks.Should().BeEqualTo("demo"); + } +} diff --git a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs index 7edd0939..74ec6cdd 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs @@ -86,7 +86,10 @@ public class SocksFmt : BaseFmt private static ProfileItem? ResolveSocksNew(string result) { var parsedUrl = Utils.TryUri(result); - if (parsedUrl == null) + // A legacy socks://base64(user:pass@host:port) link has no port of its own, but System.Uri + // accepts many of them, reading the base64 as the host (and whatever follows a '/' as the + // path). Leave those to ResolveSocks. + if (parsedUrl == null || parsedUrl.Port == -1) { return null; }