From a51f078e3b7a456b8f47a74c6e370399a43a47c1 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 23 Oct 2024 23:32:32 +0800 Subject: fix(secret): fix a bunch of bugs in sing. --- .../Crupest.SecretTool/Crupest.SecretTool.csproj | 6 + .../Crupest.SecretTool/Program.cs | 19 ++- .../Crupest.SecretTool/Crupest.SecretTool/Proxy.cs | 2 +- .../Crupest.SecretTool/Routing.cs | 2 +- .../Crupest.SecretTool/SingConfigJsonObjects.cs | 2 +- .../Crupest.SecretTool/ToolConfig.cs | 155 ++++++++++++--------- .../Crupest.SecretTool/sing-config.json.template | 24 +--- .../Crupest.SecretTool/sing-inbounds-mobile.json | 11 ++ .../Crupest.SecretTool/sing-inbounds-pc.json | 14 ++ tools/Crupest.SecretTool/build-secret.ps1 | 25 ++++ 10 files changed, 168 insertions(+), 92 deletions(-) create mode 100644 tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json create mode 100644 tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json create mode 100644 tools/Crupest.SecretTool/build-secret.ps1 (limited to 'tools') diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj b/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj index 1e011b1..2502e74 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj @@ -23,6 +23,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs index 310143d..18b1ac0 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs @@ -64,18 +64,22 @@ public static class Program { if (args.Length != 0) { - if (args.Length != 1) - { - throw new Exception("Invalid command line arguments."); - } var verb = args[0].ToLower(); if (verb == "download-geodata" || verb == "dg") { + if (args.Length != 1) + { + throw new Exception("Invalid command line arguments. download-geodata requires no arguments."); + } GeoDataManager.Instance.Download(CrupestSecretToolDirectory, false); return; } else if (verb == "generate-surge-rule-set" || verb == "gsr") { + if (args.Length != 1) + { + throw new Exception("Invalid command line arguments. download-geodata requires no arguments."); + } SurgeConfigGenerator.GenerateTo( CrupestSecretToolDirectory, Path.Join(CrupestSecretToolDirectory, SurgeRuleSetChinaOutputFileName), @@ -86,7 +90,12 @@ public static class Program } else if (verb == "generate-sing-config" || verb == "gs") { - var config = ToolConfig.FromDirectoryForSing(CrupestSecretToolDirectory, true, true); + if (args.Length != 2 || args[1].ToLower() is not ("pc" or "mobile")) + { + throw new Exception("Invalid command line arguments. generate-sing-config requires 1 argument. The argument must be either 'pc' or 'mobile'."); + } + + var config = SingToolConfig.FromDirectory(CrupestSecretToolDirectory, args[1].ToLower() == "mobile", true, true); Console.Out.WriteLine(config.ToSingConfigString()); return; } diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs index ddbbde8..d2703ba 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs @@ -48,7 +48,7 @@ public class VmessProxy(string host, int port, string userId, string path, strin public override SingConfigJsonObjects.OutboundBase ToJsonObjectSing() { return new SingConfigJsonObjects.VmessOutbound(Tag, Host, Port, UserId, - Transport: new SingConfigJsonObjects.V2rayWebsocketTransport(Path), + Transport: new SingConfigJsonObjects.V2rayWebsocketTransport(Path, new Dictionary { { "Host", Host } }), Tls: new SingConfigJsonObjects.OutboundTls(true)); } diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs index 9c247a2..fdf1b93 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs @@ -108,7 +108,7 @@ public record Routing(List Rules) : IV4ConfigObject, ISingConfigObj public SingConfigJsonObjects.Route ToJsonObjectSing() { - List ruleJsonObjects = []; + List ruleJsonObjects = [ new SingConfigJsonObjects.RouteRule(Outbound: "dns-out", Protocol: "dns")]; ruleJsonObjects.AddRange(RoutingRule.GroupByOutboundTag(Rules).Values.Select(RoutingRule.ListToJsonObjectSing)); return new SingConfigJsonObjects.Route(ruleJsonObjects); } diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs index 6af0cd1..56b5563 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs @@ -12,7 +12,7 @@ public static class SingConfigJsonObjects V2rayTransportBase? Transport = null, OutboundTls? Tls = null): OutboundBase(Tag, "vmess"); public record RouteRule(List? Domain = null, List? DomainSuffix = null, List? DomainKeyword = null, - List? DomainRegex = null, List? IpCidr = null, List? SourceIpCidr = null, + List? DomainRegex = null, List? IpCidr = null, List? SourceIpCidr = null, string? Protocol = null, List? Port = null, List? SourcePort = null, List? PortRange = null, List? SourcePortRange = null, string? Network = null, List? Inbound = null, string? Outbound = null) : IObject; diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs index 534bd65..809fba1 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs @@ -13,9 +13,9 @@ public interface ISingConfigObject object ToJsonObjectSing(); } -public class ToolConfig(Template template, List proxies, Routing router, StaticHosts? hosts) +public class ToolConfigBase(Template template, List proxies, Routing router) { - private class JsonInterfaceConverter : JsonConverter + protected class JsonInterfaceConverter : JsonConverter { public override Interface Read( ref Utf8JsonReader reader, @@ -34,20 +34,24 @@ public class ToolConfig(Template template, List proxies, Routing router, } } - - public const string ConfigTemplateFileName = "config.json.template"; public const string VmessConfigFileName = "vmess.txt"; public const string ProxyConfigFileName = "proxy.txt"; - public const string HostsConfigFileName = "hosts.txt"; - public const string SingConfigTemplateFileName = "sing-config.json.template"; + public Template Template { get; set; } = template; + public List Proxies { get; set; } = proxies; + public Routing Routing { get; set; } = router; +} + +public class ToolConfig(Template template, List proxies, Routing router, StaticHosts? hosts) : ToolConfigBase(template, proxies, router) +{ + public const string ConfigTemplateFileName = "config.json.template"; + public const string HostsConfigFileName = "hosts.txt"; public static List RequiredConfigFileNames { get; } = [ConfigTemplateFileName, VmessConfigFileName, ProxyConfigFileName]; public static List ConfigFileNames { get; } = [ConfigTemplateFileName, VmessConfigFileName, ProxyConfigFileName, HostsConfigFileName]; private const string ProxyAnchor = "PROXY_ANCHOR"; private const string RoutingAnchor = "ROUTING_ANCHOR"; - private const string SingRouteAnchor = "ROUTE_ANCHOR"; private const string HostsAnchor = "HOSTS_ANCHOR"; public const string AddCnAttributeToGeositeEnvironmentVariable = "CRUPEST_V2RAY_GEOSITE_USE_CN"; @@ -58,45 +62,8 @@ public class ToolConfig(Template template, List proxies, Routing router, _ => true }; - public Template Template { get; set; } = template; - public List Proxies { get; set; } = proxies; - public Routing Routing { get; set; } = router; public StaticHosts Hosts { get; set; } = hosts is null ? new StaticHosts([]) : hosts; - public string ToSingConfigString(bool pretty = true) - { - var jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, - DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - }); - // TODO: Make interface converter generic. - jsonOptions.Converters.Add(new JsonInterfaceConverter()); - jsonOptions.Converters.Add(new JsonInterfaceConverter()); - - var templateValues = new Dictionary - { - [ProxyAnchor] = string.Join(',', Proxies.Select(p => JsonSerializer.Serialize(p.ToJsonObjectSing(), jsonOptions))), - [SingRouteAnchor] = JsonSerializer.Serialize(Routing.ToJsonObjectSing(), jsonOptions), - }; - - var configString = Template.Generate(templateValues); - - if (pretty) - { - var jsonOptionsPretty = new JsonSerializerOptions(jsonOptions) - { - WriteIndented = true, - }; - return JsonSerializer.Serialize(JsonSerializer.Deserialize(configString, jsonOptionsPretty), jsonOptionsPretty); - } - else - { - return configString; - } - } - public string ToJsonStringV4(string domainStrategy = "IpOnDemand", bool directGeositeCn = true, bool pretty = true) { var jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions @@ -178,9 +145,76 @@ public class ToolConfig(Template template, List proxies, Routing router, } } - public static ToolConfig FromFilesForSing(string templatePath, string vmessPath, string proxyPath, bool clean, bool silent) + public static ToolConfig FromDirectory(string directory) { - foreach (var path in new List([templatePath, vmessPath, proxyPath])) + return FromFiles( + Path.Join(directory, ConfigTemplateFileName), + Path.Join(directory, VmessConfigFileName), + Path.Join(directory, ProxyConfigFileName), + Path.Join(directory, HostsConfigFileName) + ); + } + + public static void FromDirectoryAndWriteToFile(string directory, string outputPath) + { + var config = FromDirectory(directory); + File.WriteAllText(outputPath, config.ToJsonStringV4()); + } +} + +public class SingToolConfig(Template template, List proxies, Routing router, string inboundsString) : ToolConfigBase(template, proxies, router) +{ + + public const string ConfigTemplateFileName = "sing-config.json.template"; + public const string ConfigInboundsPcFileName = "sing-inbounds-pc.json"; + public const string ConfigInboundsMobileFileName = "sing-inbounds-mobile.json"; + + public static List RequiredConfigFileNames { get; } = [ConfigTemplateFileName, VmessConfigFileName, ProxyConfigFileName, ConfigInboundsMobileFileName, ConfigInboundsPcFileName]; + + private const string ProxyAnchor = "PROXY_ANCHOR"; + private const string RouteAnchor = "ROUTE_ANCHOR"; + private const string InboundsAnchor = "INBOUNDS_ANCHOR"; + + public string InboundsString { get; } = inboundsString; + + public string ToSingConfigString(bool pretty = true) + { + var jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, + DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }); + // TODO: Make interface converter generic. + jsonOptions.Converters.Add(new JsonInterfaceConverter()); + jsonOptions.Converters.Add(new JsonInterfaceConverter()); + + var templateValues = new Dictionary + { + [ProxyAnchor] = string.Join(',', Proxies.Select(p => JsonSerializer.Serialize(p.ToJsonObjectSing(), jsonOptions))), + [RouteAnchor] = JsonSerializer.Serialize(Routing.ToJsonObjectSing(), jsonOptions), + [InboundsAnchor] = InboundsString + }; + + var configString = Template.Generate(templateValues); + + if (pretty) + { + var jsonOptionsPretty = new JsonSerializerOptions(jsonOptions) + { + WriteIndented = true, + }; + return JsonSerializer.Serialize(JsonSerializer.Deserialize(configString, jsonOptionsPretty), jsonOptionsPretty); + } + else + { + return configString; + } + } + + public static SingToolConfig FromFiles(string templatePath, string vmessPath, string proxyPath, string inboundsPath, bool clean, bool silent) + { + foreach (var path in new List([templatePath, vmessPath, proxyPath, inboundsPath])) { if (!File.Exists(path)) { @@ -191,7 +225,7 @@ public class ToolConfig(Template template, List proxies, Routing router, var geoSiteData = GeoDataManager.Instance.GetOrCreateGeoSiteData(clean, silent); ProxyFile proxyFile = new(proxyPath); - string templateString, vmessString; + string templateString, vmessString, inboundsString; string file = ""; try @@ -200,6 +234,8 @@ public class ToolConfig(Template template, List proxies, Routing router, templateString = File.ReadAllText(templatePath); file = vmessPath; vmessString = File.ReadAllText(vmessPath); + file = inboundsPath; + inboundsString = File.ReadAllText(inboundsPath); } catch (Exception e) { @@ -211,10 +247,10 @@ public class ToolConfig(Template template, List proxies, Routing router, file = templatePath; var template = new Template(templateString); file = vmessPath; - var vmess = VmessProxy.CreateFromConfigString(vmessString, "proxy"); + var vmess = VmessProxy.CreateFromConfigString(vmessString, "proxy-out"); file = proxyPath; - var routing = Routing.FromProxyFileForSing(proxyFile, geoSiteData, "proxy", "direct"); - return new ToolConfig(template, [vmess], routing, null); + var routing = Routing.FromProxyFileForSing(proxyFile, geoSiteData, "proxy-out", "direct-out"); + return new SingToolConfig(template, [vmess], routing, inboundsString); } catch (Exception e) { @@ -222,29 +258,14 @@ public class ToolConfig(Template template, List proxies, Routing router, } } - public static ToolConfig FromDirectory(string directory) + public static SingToolConfig FromDirectory(string directory, bool isMobile, bool clean, bool silent) { return FromFiles( Path.Join(directory, ConfigTemplateFileName), Path.Join(directory, VmessConfigFileName), Path.Join(directory, ProxyConfigFileName), - Path.Join(directory, HostsConfigFileName) - ); - } - - public static ToolConfig FromDirectoryForSing(string directory, bool clean, bool silent) - { - return FromFilesForSing( - Path.Join(directory, SingConfigTemplateFileName), - Path.Join(directory, VmessConfigFileName), - Path.Join(directory, ProxyConfigFileName), + isMobile ? Path.Join(directory, ConfigInboundsMobileFileName) : Path.Join(directory, ConfigInboundsPcFileName), clean, silent ); } - - public static void FromDirectoryAndWriteToFile(string directory, string outputPath) - { - var config = FromDirectory(directory); - File.WriteAllText(outputPath, config.ToJsonStringV4()); - } } diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template index 429bd1d..d7e55a0 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template @@ -23,31 +23,21 @@ "tag": "google", "address": "8.8.8.8" } - ] }, - "inbounds": [ - { - "tag": "http-in", - "type": "http", - "listen": "127.0.0.1", - "listen_port": 3080 - }, - { - "tag": "socks-in", - "type": "socks", - "listen": "127.0.0.1", - "listen_port": 3081 - } - ], + "inbounds": ${INBOUNDS_ANCHOR}, "outbounds": [ { "type": "direct", - "tag": "direct" + "tag": "direct-out" }, { "type": "block", - "tag": "block" + "tag": "block-out" + }, + { + "tag": "dns-out", + "type": "dns" }, ${PROXY_ANCHOR} ], diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json new file mode 100644 index 0000000..5038c40 --- /dev/null +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json @@ -0,0 +1,11 @@ +[ + { + "tag": "tun-in", + "type": "tun", + "auto_route": true, + "strict_route": true, + "address": [ "172.23.0.1/30", "fdfe:acbd:9876::1/126"], + "sniff": true, + "sniff_override_destination": true + } +] diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json new file mode 100644 index 0000000..956d751 --- /dev/null +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json @@ -0,0 +1,14 @@ +[ + { + "tag": "http-in", + "type": "http", + "listen": "127.0.0.1", + "listen_port": 3080 + }, + { + "tag": "socks-in", + "type": "socks", + "listen": "127.0.0.1", + "listen_port": 3081 + } +] \ No newline at end of file diff --git a/tools/Crupest.SecretTool/build-secret.ps1 b/tools/Crupest.SecretTool/build-secret.ps1 new file mode 100644 index 0000000..8aa7987 --- /dev/null +++ b/tools/Crupest.SecretTool/build-secret.ps1 @@ -0,0 +1,25 @@ +if ($args.Count -ne 1 || $args[0] -notmatch "^win-x64|linux-x64|osx-x64$") +{ + Write-Error "You must specify exactly one argument, the build target (win-x64 | linux-x64 | osx-x64)." + exit 1 +} + +Write-Output "Secret dir: $PSScriptRoot" + +Write-Output "Check dotnet..." +dotnet --version +if ($LASTEXITCODE -ne 0) +{ + Write-Error "dotnet not found." + exit 2 +} + +Write-Output "Enter `"secret`" dir..." +Push-Location $PSScriptRoot + +Write-Output "Begin to build..." +dotnet publish Crupest.SecretTool -c Release -o "$secret_dir/publish" --sc -r $args[0] + +Pop-Location + +Write-Host "Finish!" -ForegroundColor Green -- cgit v1.2.3